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