1 //--------------------------------------------------------------------------
2 // Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
3 //
4 // This program is free software; you can redistribute it and/or modify it
5 // under the terms of the GNU General Public License Version 2 as published
6 // by the Free Software Foundation. You may not use, modify or distribute
7 // this program under any other version of the GNU General Public License.
8 //
9 // This program is distributed in the hope that it will be useful, but
10 // WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License along
15 // with this program; if not, write to the Free Software Foundation, Inc.,
16 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 //--------------------------------------------------------------------------
18 // cd_icmp4_ip.cc author Josh Rosenbaum <jrosenba@cisco.com>
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include "codecs/codec_module.h"
25 #include "framework/codec.h"
26 #include "log/text_log.h"
27 #include "main/snort_config.h"
28 #include "protocols/icmp4.h"
29 #include "protocols/packet_manager.h"
30 #include "protocols/tcp.h"
31 #include "protocols/udp.h"
32
33 using namespace snort;
34
35 namespace
36 {
37 #define ICMP4_IP_NAME "icmp4_ip"
38 #define ICMP4_IP_HELP "support for IP in ICMPv4"
39
40 class Icmp4IpCodec : public Codec
41 {
42 public:
Icmp4IpCodec()43 Icmp4IpCodec() : Codec(ICMP4_IP_NAME) { }
44
45 void get_protocol_ids(std::vector<ProtocolId>&) override;
46 bool decode(const RawData&, CodecData&, DecodeData&) override;
47 void log(TextLog* const, const uint8_t* pkt, const uint16_t len) override;
48 };
49 } // namespace
50
get_protocol_ids(std::vector<ProtocolId> & v)51 void Icmp4IpCodec::get_protocol_ids(std::vector<ProtocolId>& v)
52 { v.emplace_back(ProtocolId::IP_EMBEDDED_IN_ICMP4); }
53
decode(const RawData & raw,CodecData & codec,DecodeData & snort)54 bool Icmp4IpCodec::decode(const RawData& raw, CodecData& codec, DecodeData& snort)
55 {
56 if (raw.len < ip::IP4_HEADER_LEN)
57 {
58 codec_event(codec, DECODE_ICMP_ORIG_IP_TRUNCATED);
59 return false;
60 }
61
62 /* lay the IP struct over the raw data */
63 const ip::IP4Hdr* const ip4h = reinterpret_cast<const ip::IP4Hdr*>(raw.data);
64
65 /*
66 * with datalink DLT_RAW it's impossible to differ ARP datagrams from IP.
67 * So we are just ignoring non IP datagrams
68 */
69 if ((ip4h->ver() != 4) && !snort.ip_api.is_ip6())
70 {
71 codec_event(codec, DECODE_ICMP_ORIG_IP_VER_MISMATCH);
72 return false;
73 }
74
75 const uint16_t hlen = ip4h->hlen();
76
77 if (raw.len < hlen)
78 {
79 codec_event(codec, DECODE_ICMP_ORIG_DGRAM_LT_ORIG_IP);
80 return false;
81 }
82
83 /* set the remaining packet length */
84 if (ip4h->off() == 0)
85 {
86 const uint32_t ip_len = raw.len - hlen;
87
88 /* Original IP payload should be 64 bits */
89 if (ip_len < 8)
90 {
91 codec_event(codec, DECODE_ICMP_ORIG_PAYLOAD_LT_64);
92 return false;
93 }
94 /* ICMP error packets could contain as much of original payload
95 * as possible, but not exceed 576 bytes
96 */
97 else if (snort.ip_api.dgram_len() > 576)
98 {
99 codec_event(codec, DECODE_ICMP_ORIG_PAYLOAD_GT_576);
100 }
101 }
102 else
103 {
104 /* RFC states that only first frag will get an ICMP response */
105 codec_event(codec, DECODE_ICMP_ORIG_IP_WITH_FRAGOFFSET);
106 return false;
107 }
108
109 switch (ip4h->proto())
110 {
111 case IpProtocol::TCP: /* decode the interesting part of the header */
112 codec.proto_bits |= PROTO_BIT__TCP_EMBED_ICMP;
113 break;
114
115 case IpProtocol::UDP:
116 codec.proto_bits |= PROTO_BIT__UDP_EMBED_ICMP;
117 break;
118
119 case IpProtocol::ICMPV4:
120 codec.proto_bits |= PROTO_BIT__ICMP_EMBED_ICMP;
121 break;
122 default:
123 codec.proto_bits |= PROTO_BIT__ICMP_EMBED_OTHER;
124 break;
125 }
126
127 // If you change this, change the buffer and
128 // memcpy length in encode() below !!
129 codec.lyr_len = hlen;
130 return true;
131 }
132
133 struct ip4_addr
134 {
135 union
136 {
137 uint32_t addr32;
138 uint8_t addr8[4];
139 };
140 };
141
log(TextLog * const text_log,const uint8_t * raw_pkt,const uint16_t)142 void Icmp4IpCodec::log(TextLog* const text_log, const uint8_t* raw_pkt,
143 const uint16_t /*lyr_len*/)
144 {
145 const ip::IP4Hdr* const ip4h = reinterpret_cast<const ip::IP4Hdr*>(raw_pkt);
146 TextLog_Puts(text_log, "\n\t**** ORIGINAL DATAGRAM DUMP: ****");
147 TextLog_NewLine(text_log);
148 TextLog_Puts(text_log, "\tIPv4\n\t\t");
149
150 // COPIED DIRECTLY FROM ipv4 CODEC. This is specifically replicated since
151 // the two are not necessarily the same.
152
153 // FIXIT-RC this does NOT obfuscate correctly
154 if (SnortConfig::get_conf()->obfuscate())
155 {
156 TextLog_Print(text_log, "xxx.xxx.xxx.xxx -> xxx.xxx.xxx.xxx");
157 }
158 else
159 {
160 ip4_addr src, dst;
161 src.addr32 = ip4h->get_src();
162 dst.addr32 = ip4h->get_dst();
163
164 TextLog_Print(text_log, "%d.%d.%d.%d -> %d.%d.%d.%d",
165 (int)src.addr8[0], (int)src.addr8[1],
166 (int)src.addr8[2], (int)src.addr8[3],
167 (int)dst.addr8[0], (int)dst.addr8[1],
168 (int)dst.addr8[2], (int)dst.addr8[3]);
169 }
170
171 TextLog_NewLine(text_log);
172 TextLog_Puts(text_log, "\t\t");
173
174 const uint16_t hlen = ip4h->hlen();
175 const uint16_t len = ip4h->len();
176 const uint16_t frag_off = ip4h->off_w_flags();
177
178 TextLog_Print(text_log, "Next:%s(%02X) TTL:%u TOS:0x%X ID:%u IpLen:%u DgmLen:%u",
179 PacketManager::get_proto_name(ip4h->proto()),
180 static_cast<uint8_t>(ip4h->proto()), ip4h->ttl(), ip4h->tos(),
181 ip4h->id(), hlen, len);
182
183 /* print the reserved bit if it's set */
184 if (frag_off & 0x8000)
185 TextLog_Puts(text_log, " RB");
186
187 /* printf more frags/don't frag bits */
188 if (frag_off & 0x4000)
189 TextLog_Puts(text_log, " DF");
190
191 bool mf = false;
192 if (frag_off & 0x2000)
193 {
194 mf = true;
195 TextLog_Puts(text_log, " MF");
196 }
197
198 #if 0
199 // FIXIT-L more ip options fixits
200 /* print IP options */
201 if (p->ip_option_count > 0)
202 {
203 LogIpOptions(text_log, p);
204 }
205 #endif
206
207 if ( mf && (frag_off & 0x1FFF) && ((len - hlen > 0)))
208 {
209 TextLog_NewLine(text_log);
210 TextLog_Puts(text_log, "\t\t");
211 TextLog_Print(text_log, "Frag Offset: 0x%04X Frag Size: 0x%04X",
212 (frag_off & 0x1FFF), (len - hlen));
213 }
214
215 TextLog_NewLine(text_log);
216 TextLog_Putc(text_log, '\t');
217
218 /* EMBEDDED PROTOCOL */
219 switch (ip4h->proto())
220 {
221 case IpProtocol::TCP: /* decode the interesting part of the header */
222 {
223 const tcp::TCPHdr* tcph = reinterpret_cast<const tcp::TCPHdr*>
224 (raw_pkt + hlen);
225 TextLog_Puts(text_log, "TCP\n\t\t");
226 TextLog_Print(text_log, "SrcPort:%u DstPort:%u Seq: 0x%lX "
227 "Ack: 0x%lX Win: 0x%X TcpLen: %d",ntohs(tcph->th_sport),
228 ntohs(tcph->th_dport), (u_long)ntohl(tcph->th_seq),
229 (u_long)ntohl(tcph->th_ack),
230 ntohs(tcph->th_win), tcph->off());
231
232 break;
233 }
234
235 case IpProtocol::UDP:
236 {
237 const udp::UDPHdr* udph = reinterpret_cast<const udp::UDPHdr*>
238 (raw_pkt + hlen);
239 TextLog_Puts(text_log, "UDP\n\t\t");
240 TextLog_Print(text_log, "SourcePort:%d DestPort:%d Len:%d",
241 ntohs(udph->uh_sport), ntohs(udph->uh_dport),
242 ntohs(udph->uh_len) - udp::UDP_HEADER_LEN);
243 break;
244 }
245
246 case IpProtocol::ICMPV4:
247 {
248 const icmp::ICMPHdr* icmph = reinterpret_cast<const icmp::ICMPHdr*>
249 (raw_pkt + hlen);
250
251 TextLog_Puts(text_log, "ICMPv4\n\t\t");
252 TextLog_Print(text_log, "Type:%d Code:%d Csum:%u",
253 icmph->type, icmph->code, ntohs(icmph->csum));
254
255 switch (icmph->type)
256 {
257 case icmp::IcmpType::DEST_UNREACH:
258 case icmp::IcmpType::TIME_EXCEEDED:
259 case icmp::IcmpType::SOURCE_QUENCH:
260 break;
261
262 case icmp::IcmpType::PARAMETERPROB:
263 if (icmph->code == 0)
264 TextLog_Print(text_log, " Ptr: %u", icmph->s_icmp_pptr);
265 break;
266
267 case ICMP_REDIRECT:
268 // FIXIT-L -IPv6 "NOT YET IMPLEMENTED - ICMP printing"
269 break;
270
271 case icmp::IcmpType::ECHO_4:
272 case icmp::IcmpType::ECHOREPLY:
273 case icmp::IcmpType::TIMESTAMP:
274 case icmp::IcmpType::TIMESTAMPREPLY:
275 case icmp::IcmpType::INFO_REQUEST:
276 case icmp::IcmpType::INFO_REPLY:
277 case icmp::IcmpType::ADDRESS:
278 case icmp::IcmpType::ADDRESSREPLY:
279 TextLog_Print(text_log, " Id: %u SeqNo: %u",
280 ntohs(icmph->s_icmp_id), ntohs(icmph->s_icmp_seq));
281 break;
282
283 case icmp::IcmpType::ROUTER_ADVERTISE:
284 TextLog_Print(text_log, " Addrs: %u Size: %u Lifetime: %u",
285 icmph->s_icmp_num_addrs, icmph->s_icmp_wpa,
286 ntohs(icmph->s_icmp_lifetime));
287 break;
288
289 default:
290 break;
291 }
292 break;
293 }
294 default:
295 {
296 TextLog_Print(text_log, "Protocol:%s(%02X)",
297 PacketManager::get_proto_name(ip4h->proto()),
298 static_cast<uint8_t>(ip4h->proto()));
299 break;
300 }
301 }
302 }
303
304 //-------------------------------------------------------------------------
305 // api
306 //-------------------------------------------------------------------------
307
ctor(Module *)308 static Codec* ctor(Module*)
309 { return new Icmp4IpCodec(); }
310
dtor(Codec * cd)311 static void dtor(Codec* cd)
312 { delete cd; }
313
314 static const CodecApi icmp4_ip_api =
315 {
316 {
317 PT_CODEC,
318 sizeof(CodecApi),
319 CDAPI_VERSION,
320 0,
321 API_RESERVED,
322 API_OPTIONS,
323 ICMP4_IP_NAME,
324 ICMP4_IP_HELP,
325 nullptr, // module constructor
326 nullptr // module destructor
327 },
328 nullptr, // g_ctor
329 nullptr, // g_dtor
330 nullptr, // t_ctor
331 nullptr, // t_dtor
332 ctor,
333 dtor,
334 };
335
336 #ifdef BUILDING_SO
337 SO_PUBLIC const BaseApi* snort_plugins[] =
338 #else
339 const BaseApi* cd_icmp4_ip[] =
340 #endif
341 {
342 &icmp4_ip_api.base,
343 nullptr
344 };
345
346