1 //--------------------------------------------------------------------------
2 // Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
3 // Copyright (C) 2002-2013 Sourcefire, Inc.
4 //
5 // This program is free software; you can redistribute it and/or modify it
6 // under the terms of the GNU General Public License Version 2 as published
7 // by the Free Software Foundation. You may not use, modify or distribute
8 // this program under any other version of the GNU General Public License.
9 //
10 // This program is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License along
16 // with this program; if not, write to the Free Software Foundation, Inc.,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 //--------------------------------------------------------------------------
19 // cd_icmp6.cc author Josh Rosenbaum <jrosenba@cisco.com>
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include <daq.h>
26
27 #include "codecs/codec_module.h"
28 #include "framework/codec.h"
29 #include "log/text_log.h"
30 #include "main/snort_config.h"
31 #include "protocols/icmp6.h"
32 #include "protocols/icmp4.h"
33 #include "utils/util.h"
34
35 #include "checksum.h"
36
37 using namespace snort;
38
39 #define CD_ICMP6_NAME "icmp6"
40 #define CD_ICMP6_HELP "support for Internet control message protocol v6"
41
42 namespace
43 {
44 const PegInfo pegs[]
45 {
46 { CountType::SUM, "bad_icmp6_checksum", "nonzero icmp6 checksums" },
47 { CountType::SUM, "checksum_bypassed", "checksum calculations bypassed" },
48 { CountType::END, nullptr, nullptr }
49 };
50
51 struct Stats
52 {
53 PegCount bad_ip6_cksum;
54 PegCount cksum_bypassed;
55 };
56
57 static THREAD_LOCAL Stats stats;
58
59 static const RuleMap icmp6_rules[] =
60 {
61 { DECODE_ICMP6_HDR_TRUNC, "truncated ICMPv6 header" },
62 { DECODE_ICMP6_TYPE_OTHER, "ICMPv6 type not decoded" },
63 { DECODE_ICMP6_DST_MULTICAST, "ICMPv6 packet to multicast address" },
64 { DECODE_ICMPV6_TOO_BIG_BAD_MTU,
65 "ICMPv6 packet of type 2 (message too big) with MTU field < 1280" },
66 { DECODE_ICMPV6_UNREACHABLE_NON_RFC_2463_CODE,
67 "ICMPv6 packet of type 1 (destination unreachable) with non-RFC 2463 code" },
68 { DECODE_ICMPV6_SOLICITATION_BAD_CODE,
69 "ICMPv6 router solicitation packet with a code not equal to 0" },
70 { DECODE_ICMPV6_ADVERT_BAD_CODE,
71 "ICMPv6 router advertisement packet with a code not equal to 0" },
72 { DECODE_ICMPV6_SOLICITATION_BAD_RESERVED,
73 "ICMPv6 router solicitation packet with the reserved field not equal to 0" },
74 { DECODE_ICMPV6_ADVERT_BAD_REACHABLE,
75 "ICMPv6 router advertisement packet with the reachable time field set > 1 hour" },
76 { DECODE_ICMPV6_UNREACHABLE_NON_RFC_4443_CODE,
77 "ICMPv6 packet of type 1 (destination unreachable) with non-RFC 4443 code" },
78 { DECODE_ICMPV6_NODE_INFO_BAD_CODE,
79 "ICMPv6 node info query/response packet with a code greater than 2" },
80 { DECODE_ICMP6_NOT_IP6, "ICMPv6 not encapsulated in IPv6" },
81 { 0, nullptr }
82 };
83
84 class Icmp6Module : public BaseCodecModule
85 {
86 public:
Icmp6Module()87 Icmp6Module() : BaseCodecModule(CD_ICMP6_NAME, CD_ICMP6_HELP) { }
88
get_rules() const89 const RuleMap* get_rules() const override
90 { return icmp6_rules; }
91
get_pegs() const92 const PegInfo* get_pegs() const override
93 { return pegs; }
94
get_counts() const95 PegCount* get_counts() const override
96 { return (PegCount*)&stats; }
97 };
98
99 class Icmp6Codec : public Codec
100 {
101 public:
Icmp6Codec()102 Icmp6Codec() : Codec(CD_ICMP6_NAME) { }
103
104 void get_protocol_ids(std::vector<ProtocolId>& v) override;
105 bool decode(const RawData&, CodecData&, DecodeData&) override;
106 void update(const ip::IpApi&, const EncodeFlags, uint8_t* raw_pkt,
107 uint16_t lyr_len, uint32_t& updated_len) override;
108 void format(bool reverse, uint8_t* raw_pkt, DecodeData& snort) override;
109 void log(TextLog* const, const uint8_t* pkt, const uint16_t len) override;
110
111 private:
112 bool valid_checksum_from_daq(const RawData&);
113 };
114 } // anonymous namespace
115
get_protocol_ids(std::vector<ProtocolId> & v)116 void Icmp6Codec::get_protocol_ids(std::vector<ProtocolId>& v)
117 { v.emplace_back(ProtocolId::ICMPV6); }
118
valid_checksum_from_daq(const RawData & raw)119 inline bool Icmp6Codec::valid_checksum_from_daq(const RawData& raw)
120 {
121 const DAQ_PktDecodeData_t* pdd =
122 (const DAQ_PktDecodeData_t*) daq_msg_get_meta(raw.daq_msg, DAQ_PKT_META_DECODE_DATA);
123 if (!pdd || !pdd->flags.bits.l4_checksum || !pdd->flags.bits.icmp || !pdd->flags.bits.l4)
124 return false;
125 // Sanity check to make sure we're talking about the same thing if offset is available
126 if (pdd->l4_offset != DAQ_PKT_DECODE_OFFSET_INVALID)
127 {
128 const uint8_t* data = daq_msg_get_data(raw.daq_msg);
129 if (raw.data - data != pdd->l4_offset)
130 return false;
131 }
132 stats.cksum_bypassed++;
133 return true;
134 }
135
decode(const RawData & raw,CodecData & codec,DecodeData & snort)136 bool Icmp6Codec::decode(const RawData& raw, CodecData& codec, DecodeData& snort)
137 {
138 if (raw.len < icmp::ICMP6_HEADER_MIN_LEN)
139 {
140 codec_event(codec, DECODE_ICMP6_HDR_TRUNC);
141 return false;
142 }
143
144 if ( !snort.ip_api.get_ip6h() /* FIXIT-L && verify prior layer == ip6 */ )
145 {
146 codec_event(codec, DECODE_ICMP6_NOT_IP6);
147 return false;
148 }
149
150 const icmp::Icmp6Hdr* const icmp6h = reinterpret_cast<const icmp::Icmp6Hdr*>(raw.data);
151
152 if ( snort::get_network_policy()->icmp_checksums() && !valid_checksum_from_daq(raw))
153 {
154 checksum::Pseudoheader6 ph6;
155 const ip::IP6Hdr* const ip6h = snort.ip_api.get_ip6h();
156 COPY4(ph6.hdr.sip, ip6h->get_src()->u6_addr32);
157 COPY4(ph6.hdr.dip, ip6h->get_dst()->u6_addr32);
158 ph6.hdr.zero = 0;
159 ph6.hdr.protocol = codec.ip6_csum_proto;
160 ph6.hdr.len = htons((uint16_t)raw.len);
161
162 uint16_t csum = checksum::icmp_cksum((const uint16_t*)(icmp6h), raw.len, ph6);
163
164 if (csum && !codec.is_cooked())
165 {
166 stats.bad_ip6_cksum++;
167 snort.decode_flags |= DECODE_ERR_CKSUM_ICMP;
168 return false;
169 }
170 }
171
172 const uint16_t dsize = raw.len - icmp::ICMP6_HEADER_MIN_LEN;
173 uint16_t len;
174
175 switch (icmp6h->type)
176 {
177 case icmp::Icmp6Types::ECHO_REQUEST:
178 case icmp::Icmp6Types::ECHO_REPLY:
179 if (dsize >= sizeof(ICMPHdr::icmp_hun.idseq))
180 {
181 len = icmp::ICMP6_HEADER_NORMAL_LEN;
182
183 if ( snort.ip_api.get_ip6h()->is_dst_multicast() )
184 codec_event(codec, DECODE_ICMP6_DST_MULTICAST);
185 }
186 else
187 {
188 codec_event(codec, DECODE_ICMP_DGRAM_LT_ICMPHDR);
189 return false;
190 }
191 break;
192
193 case icmp::Icmp6Types::PACKET_TOO_BIG:
194 if (dsize >= sizeof(icmp::ICMP6TooBig))
195 {
196 const icmp::ICMP6TooBig* too_big = (const icmp::ICMP6TooBig*)raw.data;
197
198 if (ntohl(too_big->mtu) < 1280)
199 codec_event(codec, DECODE_ICMPV6_TOO_BIG_BAD_MTU);
200
201 len = icmp::ICMP6_HEADER_NORMAL_LEN;
202 codec.next_prot_id = ProtocolId::IP_EMBEDDED_IN_ICMP6;
203 }
204 else
205 {
206 codec_event(codec, DECODE_ICMP_DGRAM_LT_ICMPHDR);
207 return false;
208 }
209 break;
210
211 case icmp::Icmp6Types::TIME_EXCEEDED6:
212 case icmp::Icmp6Types::PARAMETER_PROBLEM:
213 case icmp::Icmp6Types::DESTINATION_UNREACHABLE:
214 if (dsize >= 4)
215 {
216 if (icmp6h->type == icmp::Icmp6Types::DESTINATION_UNREACHABLE)
217 {
218 if (icmp6h->code == icmp::Icmp6Code::UNREACH_INVALID) // UNREACH_INVALID == 2
219 codec_event(codec, DECODE_ICMPV6_UNREACHABLE_NON_RFC_2463_CODE);
220
221 else if (static_cast<uint8_t>(icmp6h->code) > 6)
222 codec_event(codec, DECODE_ICMPV6_UNREACHABLE_NON_RFC_4443_CODE);
223 }
224 len = icmp::ICMP6_HEADER_NORMAL_LEN;
225 codec.next_prot_id = ProtocolId::IP_EMBEDDED_IN_ICMP6;
226 }
227 else
228 {
229 codec_event(codec, DECODE_ICMP_DGRAM_LT_ICMPHDR);
230 return false;
231 }
232 break;
233
234 case icmp::Icmp6Types::ROUTER_ADVERTISEMENT:
235 if (dsize >= (sizeof(icmp::ICMP6RouterAdvertisement) - icmp::ICMP6_HEADER_MIN_LEN))
236 {
237 const icmp::ICMP6RouterAdvertisement* ra = (const icmp::ICMP6RouterAdvertisement*)raw.data;
238
239 if (icmp6h->code != icmp::Icmp6Code::ADVERTISEMENT)
240 codec_event(codec, DECODE_ICMPV6_ADVERT_BAD_CODE);
241
242 if (ntohl(ra->reachable_time) > 3600000)
243 codec_event(codec, DECODE_ICMPV6_ADVERT_BAD_REACHABLE);
244
245 len = icmp::ICMP6_HEADER_MIN_LEN;
246 }
247 else
248 {
249 codec_event(codec, DECODE_ICMP_DGRAM_LT_ICMPHDR);
250 return false;
251 }
252 break;
253
254 case icmp::Icmp6Types::ROUTER_SOLICITATION:
255 if (dsize >= (sizeof(icmp::ICMP6RouterSolicitation) - icmp::ICMP6_HEADER_MIN_LEN))
256 {
257 const icmp::ICMP6RouterSolicitation* rs = (const icmp::ICMP6RouterSolicitation*)raw.data;
258 if (rs->code != 0)
259 codec_event(codec, DECODE_ICMPV6_SOLICITATION_BAD_CODE);
260
261 if (ntohl(rs->reserved) != 0)
262 codec_event(codec, DECODE_ICMPV6_SOLICITATION_BAD_RESERVED);
263
264 len = icmp::ICMP6_HEADER_MIN_LEN;
265 }
266 else
267 {
268 codec_event(codec, DECODE_ICMP_DGRAM_LT_ICMPHDR);
269 return false;
270 }
271 break;
272
273 case icmp::Icmp6Types::NODE_INFORMATION_QUERY:
274 case icmp::Icmp6Types::NODE_INFORMATION_RESPONSE:
275 if (dsize >= (sizeof(icmp::ICMP6NodeInfo) - icmp::ICMP6_HEADER_MIN_LEN))
276 {
277 const icmp::ICMP6NodeInfo* ni = (const icmp::ICMP6NodeInfo*)raw.data;
278 if (ni->code > 2)
279 codec_event(codec, DECODE_ICMPV6_NODE_INFO_BAD_CODE);
280
281 // FIXIT-L add alert for INFO Response, code == 1 || code == 2) with data
282 len = icmp::ICMP6_HEADER_MIN_LEN;
283 }
284 else
285 {
286 codec_event(codec, DECODE_ICMP_DGRAM_LT_ICMPHDR);
287 return false;
288 }
289 break;
290
291 // recognize these so we don't alert but no further checking (yet)
292 case icmp::Icmp6Types::MULTICAST_LISTENER_QUERY:
293 case icmp::Icmp6Types::MULTICAST_LISTENER_REPORT:
294 case icmp::Icmp6Types::MULTICAST_LISTENER_DONE:
295 case icmp::Icmp6Types::NEIGHBOR_SOLICITATION:
296 case icmp::Icmp6Types::NEIGHBOR_ADVERTISEMENT:
297 case icmp::Icmp6Types::REDIRECT6:
298 case icmp::Icmp6Types::INVERSE_NEIGHBOR_DISCOVERY_SOLICITATION:
299 case icmp::Icmp6Types::INVERSE_NEIGHBOR_DISCOVERY_ADVERTISEMENT:
300 case icmp::Icmp6Types::VERSION_2_MULTICAST_LISTENER_REPORT:
301 case icmp::Icmp6Types::HOME_AGENT_ADDRESS_DISCOVERY_REQUEST:
302 case icmp::Icmp6Types::HOME_AGENT_ADDRESS_DISCOVERY_REPLY:
303 case icmp::Icmp6Types::MOBILE_PREFIX_SOLICITATION:
304 case icmp::Icmp6Types::MOBILE_PREFIX_ADVERTISEMENT:
305 case icmp::Icmp6Types::CERTIFICATION_PATH_SOLICITATION:
306 case icmp::Icmp6Types::CERTIFICATION_PATH_ADVERTISEMENT:
307 case icmp::Icmp6Types::MULTICAST_ROUTER_ADVERTISEMENT:
308 case icmp::Icmp6Types::MULTICAST_ROUTER_SOLICITATION:
309 case icmp::Icmp6Types::MULTICAST_ROUTER_TERMINATION:
310 case icmp::Icmp6Types::FMIPV6:
311 case icmp::Icmp6Types::RPL_CONTROL:
312 case icmp::Icmp6Types::ILNPV6_LOCATOR_UPDATE:
313 case icmp::Icmp6Types::DUPLICATE_ADDRESS_REQUEST:
314 case icmp::Icmp6Types::DUPLICATE_ADDRESS_CONFIRMATION:
315 case icmp::Icmp6Types::MPL_CONTROL:
316 len = raw.len;
317 break;
318
319 default:
320 codec_event(codec, DECODE_ICMP6_TYPE_OTHER);
321 len = raw.len;
322 break;
323 }
324
325 codec.lyr_len = len;
326 codec.proto_bits |= PROTO_BIT__ICMP;
327 snort.icmph = reinterpret_cast<const icmp::ICMPHdr*>(icmp6h);
328 snort.set_pkt_type(PktType::ICMP);
329 return true;
330 }
331
332 /******************************************************************
333 ************************* L O G G E R *************************
334 ******************************************************************/
335
log(TextLog * const text_log,const uint8_t * raw_pkt,const uint16_t)336 void Icmp6Codec::log(TextLog* const text_log, const uint8_t* raw_pkt,
337 const uint16_t /*lyr_len*/)
338 {
339 const icmp::Icmp6Hdr* const icmph = reinterpret_cast<const icmp::Icmp6Hdr*>(raw_pkt);
340 TextLog_Print(text_log, "sType:%d Code:%d ", icmph->type, static_cast<uint8_t>(icmph->code));
341 }
342
343 /******************************************************************
344 ************************* E N C O D E R *************************
345 ******************************************************************/
346
347 namespace
348 {
349 struct IcmpHdr
350 {
351 uint8_t type;
352 uint8_t code;
353 uint16_t cksum;
354 uint32_t unused;
355 };
356 } // namespace
357
update(const ip::IpApi & api,const EncodeFlags flags,uint8_t * raw_pkt,uint16_t lyr_len,uint32_t & updated_len)358 void Icmp6Codec::update(const ip::IpApi& api, const EncodeFlags flags,
359 uint8_t* raw_pkt, uint16_t lyr_len, uint32_t& updated_len)
360 {
361 IcmpHdr* h = reinterpret_cast<IcmpHdr*>(raw_pkt);
362 updated_len += lyr_len;
363
364 if ( !(flags & UPD_COOKED) || (flags & UPD_REBUILT_FRAG) )
365 {
366 checksum::Pseudoheader6 ps6;
367 h->cksum = 0;
368
369 memcpy(ps6.hdr.sip, api.get_src()->get_ip6_ptr(), sizeof(ps6.hdr.sip));
370 memcpy(ps6.hdr.dip, api.get_dst()->get_ip6_ptr(), sizeof(ps6.hdr.dip));
371 ps6.hdr.zero = 0;
372 ps6.hdr.protocol = IpProtocol::ICMPV6;
373 ps6.hdr.len = htons((uint16_t)updated_len);
374 h->cksum = checksum::icmp_cksum((uint16_t*)h, updated_len, ps6);
375 }
376 }
377
format(bool,uint8_t * raw_pkt,DecodeData & snort)378 void Icmp6Codec::format(bool /*reverse*/, uint8_t* raw_pkt, DecodeData& snort)
379 {
380 snort.icmph = reinterpret_cast<ICMPHdr*>(raw_pkt);
381 snort.set_pkt_type(PktType::ICMP);
382 }
383
384 //-------------------------------------------------------------------------
385 // api
386 //-------------------------------------------------------------------------
387
mod_ctor()388 static Module* mod_ctor()
389 { return new Icmp6Module; }
390
mod_dtor(Module * m)391 static void mod_dtor(Module* m)
392 { delete m; }
393
ctor(Module *)394 static Codec* ctor(Module*)
395 { return new Icmp6Codec(); }
396
dtor(Codec * cd)397 static void dtor(Codec* cd)
398 { delete cd; }
399
400 static const CodecApi ipv6_api =
401 {
402 {
403 PT_CODEC,
404 sizeof(CodecApi),
405 CDAPI_VERSION,
406 0,
407 API_RESERVED,
408 API_OPTIONS,
409 CD_ICMP6_NAME,
410 CD_ICMP6_HELP,
411 mod_ctor,
412 mod_dtor,
413 },
414 nullptr, // pinit
415 nullptr, // pterm
416 nullptr, // tinit
417 nullptr, // tterm
418 ctor, // ctor
419 dtor, // dtor
420 };
421
422 #ifdef BUILDING_SO
423 SO_PUBLIC const BaseApi* snort_plugins[] =
424 #else
425 const BaseApi* cd_icmp6[] =
426 #endif
427 {
428 &ipv6_api.base,
429 nullptr
430 };
431
432