suricata
respond-reject-libnet11.c
Go to the documentation of this file.
1/* Copyright (C) 2007-2023 Open Information Security Foundation
2 *
3 * You can copy, redistribute or modify this Program under the terms of
4 * the GNU General Public License version 2 as published by the Free
5 * Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * version 2 along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15 * 02110-1301, USA.
16 */
17
18/**
19 * \file
20 *
21 * \author Victor Julien <victor@inliniac.net>
22 * \author William Metcalf <william.metcalf@gmail.com>
23 *
24 * RespondRejectLibnet11 used to send out libnet based
25 * TCP resets and ICMP unreachables.
26 *
27 * \todo calculate TTL base on average from stream tracking
28 * \todo come up with a way for users to specify icmp unreachable type
29 * \todo Possibly default to port unreachable for UDP traffic this seems
30 * to be the default in flexresp and iptables
31 */
32
33#include "suricata-common.h"
34#include "suricata.h"
35
36#include "decode.h"
37#include "decode-ipv4.h"
38#include "decode-tcp.h"
39#include "decode-sctp.h"
40#include "decode-udp.h"
41#include "packet-queue.h"
42#include "threads.h"
43#include "threadvars.h"
44#include "tm-queuehandlers.h"
45#include "tm-threads.h"
46#include "action-globals.h"
47#include "respond-reject.h"
49#include "util-device-private.h"
50
51#ifdef HAVE_LIBNET11
52
53#ifndef HAVE_LIBNET_INIT_CONST
54#define LIBNET_INIT_CAST (char *)
55#else
56#define LIBNET_INIT_CAST
57#endif
58
59/* Globally configured device to use for sending resets in IDS mode. */
60const char *g_reject_dev = NULL;
61uint16_t g_reject_dev_mtu = 0;
62
63/** set to true in main if we're setting caps. We need it here if we're using
64 * reject rules as libnet 1.1 is not compatible with caps. */
65extern bool sc_set_caps;
66
67#include <libnet.h>
68
69thread_local libnet_t *t_c = NULL;
70thread_local int t_inject_mode = -1;
71
72typedef struct Libnet11Packet_ {
73 uint32_t ack, seq;
74 uint16_t window, dsize;
75 uint8_t ttl;
76 uint16_t id;
77 uint32_t flow;
78 uint8_t class;
79 struct libnet_in6_addr src6, dst6;
80 uint32_t src4, dst4;
81 uint16_t sp, dp;
82 uint16_t len;
83 const uint8_t *smac, *dmac;
84} Libnet11Packet;
85
86static inline libnet_t *GetCtx(const Packet *p, int injection_type)
87{
88 /* fast path: use cache ctx */
89 if (t_c)
90 return t_c;
91
92 /* slow path: setup a new ctx */
93 bool store_ctx = false;
94 const char *devname = NULL;
95 extern uint8_t host_mode;
97 if (g_reject_dev != NULL) {
99 injection_type = t_inject_mode = LIBNET_LINK;
100 devname = g_reject_dev;
101 store_ctx = true;
102 } else {
103 devname = p->livedev ? p->livedev->dev : NULL;
104 }
105 }
106
107 char ebuf[LIBNET_ERRBUF_SIZE];
108 libnet_t *c = libnet_init(injection_type, LIBNET_INIT_CAST devname, ebuf);
109 if (c == NULL) {
110 SCLogError("libnet_init failed: %s", ebuf);
111 }
112 if (store_ctx) {
113 t_c = c;
114 }
115 return c;
116}
117
118static inline void ClearCtx(libnet_t *c)
119{
120 if (t_c == c)
121 libnet_clear_packet(c);
122 else
123 libnet_destroy(c);
124}
125
126void FreeCachedCtx(void)
127{
128 if (t_c) {
129 libnet_destroy(t_c);
130 t_c = NULL;
131 }
132}
133
134static inline void SetupTCP(Packet *p, Libnet11Packet *lpacket, enum RejectDirection dir)
135{
136 const TCPHdr *tcph = PacketGetTCP(p);
137 switch (dir) {
138 case REJECT_DIR_SRC:
139 SCLogDebug("sending a tcp reset to src");
140 /* We follow http://tools.ietf.org/html/rfc793#section-3.4 :
141 * If packet has no ACK, the seq number is 0 and the ACK is built
142 * the normal way. If packet has a ACK, the seq of the RST packet
143 * is equal to the ACK of incoming packet and the ACK is build
144 * using packet sequence number and size of the data. */
145 if (TCP_GET_RAW_ACK(tcph) == 0) {
146 lpacket->seq = 0;
147 lpacket->ack = TCP_GET_RAW_SEQ(tcph) + lpacket->dsize + 1;
148 } else {
149 lpacket->seq = TCP_GET_RAW_ACK(tcph);
150 lpacket->ack = TCP_GET_RAW_SEQ(tcph) + lpacket->dsize;
151 }
152
153 lpacket->sp = p->dp;
154 lpacket->dp = p->sp;
155 break;
156 case REJECT_DIR_DST:
157 default:
158 SCLogDebug("sending a tcp reset to dst");
159 lpacket->seq = TCP_GET_RAW_SEQ(tcph);
160 lpacket->ack = TCP_GET_RAW_ACK(tcph);
161
162 lpacket->sp = p->sp;
163 lpacket->dp = p->dp;
164 break;
165 }
166 lpacket->window = TCP_GET_RAW_WINDOW(tcph);
167 //lpacket.seq += lpacket.dsize;
168}
169
170static inline int BuildTCP(libnet_t *c, Libnet11Packet *lpacket)
171{
172 /* build the package */
173 if ((libnet_build_tcp(
174 lpacket->sp, /* source port */
175 lpacket->dp, /* dst port */
176 lpacket->seq, /* seq number */
177 lpacket->ack, /* ack number */
178 TH_RST|TH_ACK, /* flags */
179 lpacket->window, /* window size */
180 0, /* checksum */
181 0, /* urgent flag */
182 LIBNET_TCP_H, /* header length */
183 NULL, /* payload */
184 0, /* payload length */
185 c, /* libnet context */
186 0)) < 0) /* libnet ptag */
187 {
188 SCLogError("libnet_build_tcp %s", libnet_geterror(c));
189 return -1;
190 }
191 return 0;
192}
193
194static inline int BuildIPv4(libnet_t *c, Libnet11Packet *lpacket, const uint8_t proto)
195{
196 if ((libnet_build_ipv4(
197 lpacket->len, /* entire packet length */
198 0, /* tos */
199 lpacket->id, /* ID */
200 0, /* fragmentation flags and offset */
201 lpacket->ttl, /* TTL */
202 proto, /* protocol */
203 0, /* checksum */
204 lpacket->src4, /* source address */
205 lpacket->dst4, /* destination address */
206 NULL, /* pointer to packet data (or NULL) */
207 0, /* payload length */
208 c, /* libnet context pointer */
209 0)) < 0) /* packet id */
210 {
211 SCLogError("libnet_build_ipv4 %s", libnet_geterror(c));
212 return -1;
213 }
214 return 0;
215}
216
217static inline int BuildIPv6(libnet_t *c, Libnet11Packet *lpacket, const uint8_t proto)
218{
219 if ((libnet_build_ipv6(
220 lpacket->class, /* traffic class */
221 lpacket->flow, /* Flow label */
222 lpacket->len, /* payload length */
223 proto, /* next header */
224 lpacket->ttl, /* TTL */
225 lpacket->src6, /* source address */
226 lpacket->dst6, /* destination address */
227 NULL, /* pointer to packet data (or NULL) */
228 0, /* payload length */
229 c, /* libnet context pointer */
230 0)) < 0) /* packet id */
231 {
232 SCLogError("libnet_build_ipv6 %s", libnet_geterror(c));
233 return -1;
234 }
235 return 0;
236}
237
238static inline void SetupEthernet(Packet *p, Libnet11Packet *lpacket, enum RejectDirection dir)
239{
240 const EthernetHdr *ethh = PacketGetEthernet(p);
241 switch (dir) {
242 case REJECT_DIR_SRC:
243 lpacket->smac = ethh->eth_dst;
244 lpacket->dmac = ethh->eth_src;
245 break;
246 case REJECT_DIR_DST:
247 default:
248 lpacket->smac = ethh->eth_src;
249 lpacket->dmac = ethh->eth_dst;
250 break;
251 }
252}
253
254static inline int BuildEthernet(libnet_t *c, Libnet11Packet *lpacket, uint16_t proto)
255{
256 if ((libnet_build_ethernet(lpacket->dmac,lpacket->smac, proto , NULL, 0, c, 0)) < 0) {
257 SCLogError("libnet_build_ethernet %s", libnet_geterror(c));
258 return -1;
259 }
260 return 0;
261}
262
263static inline int BuildEthernetVLAN(libnet_t *c, Libnet11Packet *lpacket, uint16_t proto, uint16_t vlan_id)
264{
265 if (libnet_build_802_1q(lpacket->dmac, lpacket->smac, ETHERTYPE_VLAN, 0, 0, vlan_id, proto,
266 NULL, /* payload */
267 0, /* payload size */
268 c, /* libnet handle */
269 0) < 0) {
270 SCLogError("libnet_build_802_1q %s", libnet_geterror(c));
271 return -1;
272 }
273 return 0;
274}
275
276int RejectSendLibnet11IPv4TCP(ThreadVars *tv, Packet *p, void *data, enum RejectDirection dir)
277{
278 Libnet11Packet lpacket;
279 int result;
280
281 /* fill in struct defaults */
282 lpacket.ttl = 0;
283 lpacket.id = 0;
284 lpacket.flow = 0;
285 lpacket.class = 0;
286
287 if (!PacketIsTCP(p))
288 return 1;
289
290 libnet_t *c = GetCtx(p, LIBNET_RAW4);
291 if (c == NULL)
292 return 1;
293
294 lpacket.len = LIBNET_IPV4_H + LIBNET_TCP_H;
295 lpacket.dsize = p->payload_len;
296
297 switch (dir) {
298 case REJECT_DIR_SRC:
299 lpacket.src4 = GET_IPV4_DST_ADDR_U32(p);
300 lpacket.dst4 = GET_IPV4_SRC_ADDR_U32(p);
301 break;
302 case REJECT_DIR_DST:
303 default:
304 lpacket.src4 = GET_IPV4_SRC_ADDR_U32(p);
305 lpacket.dst4 = GET_IPV4_DST_ADDR_U32(p);
306 break;
307 }
308 /* TODO come up with ttl calc function */
309 lpacket.ttl = 64;
310
311 SetupTCP(p, &lpacket, dir);
312
313 if (BuildTCP(c, &lpacket) < 0)
314 goto cleanup;
315
316 if (BuildIPv4(c, &lpacket, IPPROTO_TCP) < 0)
317 goto cleanup;
318
319 if (t_inject_mode == LIBNET_LINK) {
320 SetupEthernet(p, &lpacket, dir);
321
322 if (p->vlan_idx == 1) {
323 if (BuildEthernetVLAN(c, &lpacket, ETHERNET_TYPE_IP, p->vlan_id[0]) < 0)
324 goto cleanup;
325 } else {
326 if (BuildEthernet(c, &lpacket, ETHERNET_TYPE_IP) < 0)
327 goto cleanup;
328 }
329 }
330
331 result = libnet_write(c);
332 if (result == -1) {
333 SCLogError("libnet_write failed: %s", libnet_geterror(c));
334 goto cleanup;
335 }
336
337cleanup:
338 ClearCtx(c);
339 return 0;
340}
341
342int RejectSendLibnet11IPv4ICMP(ThreadVars *tv, Packet *p, void *data, enum RejectDirection dir)
343{
344 const IPV4Hdr *ip4h = PacketGetIPv4(p);
345 Libnet11Packet lpacket;
346 int result;
347
348 /* fill in struct defaults */
349 lpacket.ttl = 0;
350 lpacket.id = 0;
351 lpacket.flow = 0;
352 lpacket.class = 0;
353 const uint16_t iplen = IPV4_GET_RAW_IPLEN(ip4h);
354 if (g_reject_dev_mtu >= ETHERNET_HEADER_LEN + LIBNET_IPV4_H + 8) {
355 lpacket.len = MIN(g_reject_dev_mtu - ETHERNET_HEADER_LEN, (LIBNET_IPV4_H + iplen));
356 } else {
357 lpacket.len = LIBNET_IPV4_H + MIN(8,iplen); // 8 bytes is the minimum we have to attach
358 }
359 lpacket.dsize = lpacket.len - (LIBNET_IPV4_H + LIBNET_ICMPV4_H);
360
361 libnet_t *c = GetCtx(p, LIBNET_RAW4);
362 if (c == NULL)
363 return 1;
364
365 switch (dir) {
366 case REJECT_DIR_SRC:
367 lpacket.src4 = GET_IPV4_DST_ADDR_U32(p);
368 lpacket.dst4 = GET_IPV4_SRC_ADDR_U32(p);
369 break;
370 case REJECT_DIR_DST:
371 default:
372 lpacket.src4 = GET_IPV4_SRC_ADDR_U32(p);
373 lpacket.dst4 = GET_IPV4_DST_ADDR_U32(p);
374 break;
375 }
376
377 /* TODO come up with ttl calc function */
378 lpacket.ttl = 64;
379
380 /* build the package */
381 if ((libnet_build_icmpv4_unreach(ICMP_DEST_UNREACH, /* type */
382 ICMP_HOST_ANO, /* code */
383 0, /* checksum */
384 (uint8_t *)ip4h, /* payload */
385 lpacket.dsize, /* payload length */
386 c, /* libnet context */
387 0)) < 0) /* libnet ptag */
388 {
389 SCLogError("libnet_build_icmpv4_unreach %s", libnet_geterror(c));
390 goto cleanup;
391 }
392
393 if (BuildIPv4(c, &lpacket, IPPROTO_ICMP) < 0)
394 goto cleanup;
395
396 if (t_inject_mode == LIBNET_LINK) {
397 SetupEthernet(p, &lpacket, dir);
398
399 if (p->vlan_idx == 1) {
400 if (BuildEthernetVLAN(c, &lpacket, ETHERNET_TYPE_IP, p->vlan_id[0]) < 0)
401 goto cleanup;
402 } else {
403 if (BuildEthernet(c, &lpacket, ETHERNET_TYPE_IP) < 0)
404 goto cleanup;
405 }
406 }
407
408 result = libnet_write(c);
409 if (result == -1) {
410 SCLogError("libnet_write_raw_ipv4 failed: %s", libnet_geterror(c));
411 goto cleanup;
412 }
413
414cleanup:
415 ClearCtx(c);
416 return 0;
417}
418
419int RejectSendLibnet11IPv6TCP(ThreadVars *tv, Packet *p, void *data, enum RejectDirection dir)
420{
421 Libnet11Packet lpacket;
422 int result;
423
424 /* fill in struct defaults */
425 lpacket.ttl = 0;
426 lpacket.id = 0;
427 lpacket.flow = 0;
428 lpacket.class = 0;
429
430 if (!PacketIsTCP(p))
431 return 1;
432
433 libnet_t *c = GetCtx(p, LIBNET_RAW6);
434 if (c == NULL)
435 return 1;
436
437 lpacket.len = LIBNET_TCP_H;
438 lpacket.dsize = p->payload_len;
439
440 switch (dir) {
441 case REJECT_DIR_SRC:
442 memcpy(lpacket.src6.libnet_s6_addr, GET_IPV6_DST_ADDR(p), 16);
443 memcpy(lpacket.dst6.libnet_s6_addr, GET_IPV6_SRC_ADDR(p), 16);
444 break;
445 case REJECT_DIR_DST:
446 default:
447 memcpy(lpacket.src6.libnet_s6_addr, GET_IPV6_SRC_ADDR(p), 16);
448 memcpy(lpacket.dst6.libnet_s6_addr, GET_IPV6_DST_ADDR(p), 16);
449 break;
450 }
451 /* TODO come up with ttl calc function */
452 lpacket.ttl = 64;
453
454 SetupTCP(p, &lpacket, dir);
455
456 BuildTCP(c, &lpacket);
457
458 if (BuildIPv6(c, &lpacket, IPPROTO_TCP) < 0)
459 goto cleanup;
460
461 if (t_inject_mode == LIBNET_LINK) {
462 SetupEthernet(p, &lpacket, dir);
463 if (p->vlan_idx == 1) {
464 if (BuildEthernetVLAN(c, &lpacket, ETHERNET_TYPE_IPV6, p->vlan_id[0]) < 0)
465 goto cleanup;
466 } else {
467 if (BuildEthernet(c, &lpacket, ETHERNET_TYPE_IPV6) < 0)
468 goto cleanup;
469 }
470 }
471
472 result = libnet_write(c);
473 if (result == -1) {
474 SCLogError("libnet_write failed: %s", libnet_geterror(c));
475 goto cleanup;
476 }
477
478cleanup:
479 ClearCtx(c);
480 return 0;
481}
482
483#ifdef HAVE_LIBNET_ICMPV6_UNREACH
484int RejectSendLibnet11IPv6ICMP(ThreadVars *tv, Packet *p, void *data, enum RejectDirection dir)
485{
486 const IPV6Hdr *ip6h = PacketGetIPv6(p);
487 Libnet11Packet lpacket;
488 int result;
489
490 /* fill in struct defaults */
491 lpacket.ttl = 0;
492 lpacket.id = 0;
493 lpacket.flow = 0;
494 lpacket.class = 0;
495 const uint16_t iplen = IPV6_GET_RAW_PLEN(ip6h);
496 if (g_reject_dev_mtu >= ETHERNET_HEADER_LEN + IPV6_HEADER_LEN + 8) {
497 lpacket.len = IPV6_HEADER_LEN + MIN(g_reject_dev_mtu - ETHERNET_HEADER_LEN, iplen);
498 } else {
499 lpacket.len = IPV6_HEADER_LEN + MIN(8, iplen);
500 }
501 lpacket.dsize = lpacket.len - LIBNET_ICMPV6_H;
502
503 libnet_t *c = GetCtx(p, LIBNET_RAW6);
504 if (c == NULL)
505 return 1;
506
507 switch (dir) {
508 case REJECT_DIR_SRC:
509 memcpy(lpacket.src6.libnet_s6_addr, GET_IPV6_DST_ADDR(p), 16);
510 memcpy(lpacket.dst6.libnet_s6_addr, GET_IPV6_SRC_ADDR(p), 16);
511 break;
512 case REJECT_DIR_DST:
513 default:
514 memcpy(lpacket.src6.libnet_s6_addr, GET_IPV6_SRC_ADDR(p), 16);
515 memcpy(lpacket.dst6.libnet_s6_addr, GET_IPV6_DST_ADDR(p), 16);
516 break;
517 }
518
519 /* TODO come up with ttl calc function */
520 lpacket.ttl = 64;
521
522 /* build the package */
523 if ((libnet_build_icmpv6_unreach(ICMP6_DST_UNREACH, /* type */
524 ICMP6_DST_UNREACH_ADMIN, /* code */
525 0, /* checksum */
526 (uint8_t *)ip6h, /* payload */
527 lpacket.dsize, /* payload length */
528 c, /* libnet context */
529 0)) < 0) /* libnet ptag */
530 {
531 SCLogError("libnet_build_icmpv6_unreach %s", libnet_geterror(c));
532 goto cleanup;
533 }
534
535 if (BuildIPv6(c, &lpacket, IPPROTO_ICMPV6) < 0)
536 goto cleanup;
537
538 if (t_inject_mode == LIBNET_LINK) {
539 SetupEthernet(p, &lpacket, dir);
540 if (p->vlan_idx == 1) {
541 if (BuildEthernetVLAN(c, &lpacket, ETHERNET_TYPE_IPV6, p->vlan_id[0]) < 0)
542 goto cleanup;
543 } else {
544 if (BuildEthernet(c, &lpacket, ETHERNET_TYPE_IPV6) < 0)
545 goto cleanup;
546 }
547 }
548
549 result = libnet_write(c);
550 if (result == -1) {
551 SCLogError("libnet_write_raw_ipv6 failed: %s", libnet_geterror(c));
552 goto cleanup;
553 }
554
555cleanup:
556 ClearCtx(c);
557 return 0;
558}
559
560#else /* HAVE_LIBNET_ICMPV6_UNREACH */
561
562int RejectSendLibnet11IPv6ICMP(ThreadVars *tv, Packet *p, void *data, enum RejectDirection dir)
563{
564 SCLogError("Libnet ICMPv6 based rejects are disabled."
565 "Usually this means that you don't have a patched libnet installed,"
566 " or configure couldn't find it.");
567 return 0;
568}
569#endif /* HAVE_LIBNET_ICMPV6_UNREACH */
570
571
572#else
573
575{
576 SCLogError("Libnet based rejects are disabled."
577 "Usually this means that you don't have libnet installed,"
578 " or configure couldn't find it.");
579 return 0;
580}
581
583{
584 SCLogError("Libnet based rejects are disabled."
585 "Usually this means that you don't have libnet installed,"
586 " or configure couldn't find it.");
587 return 0;
588}
589
591{
592 SCLogError("Libnet based rejects are disabled."
593 "Usually this means that you don't have libnet installed,"
594 " or configure couldn't find it.");
595 return 0;
596}
597
599{
600 SCLogError("Libnet based rejects are disabled."
601 "Usually this means that you don't have libnet installed,"
602 " or configure couldn't find it.");
603 return 0;
604}
605
607{
608 SCLogDebug("no libhnet support");
609}
610
611#endif /* HAVE_LIBNET11 */
uint8_t len
#define ETHERNET_TYPE_IP
#define ETHERNET_HEADER_LEN
#define ETHERNET_TYPE_IPV6
#define ICMP_HOST_ANO
#define ICMP_DEST_UNREACH
#define ICMP6_DST_UNREACH_ADMIN
#define ICMP6_DST_UNREACH
#define IPV4_GET_RAW_IPLEN(ip4h)
Definition decode-ipv4.h:98
#define IPV6_GET_RAW_PLEN(ip6h)
Definition decode-ipv6.h:66
#define IPV6_HEADER_LEN
Definition decode-ipv6.h:27
#define TCP_GET_RAW_SEQ(tcph)
Definition decode-tcp.h:80
#define TH_ACK
Definition decode-tcp.h:38
#define TH_RST
Definition decode-tcp.h:36
#define TCP_GET_RAW_ACK(tcph)
Definition decode-tcp.h:81
#define TCP_GET_RAW_WINDOW(tcph)
Definition decode-tcp.h:83
uint8_t proto
#define GET_IPV6_DST_ADDR(p)
Definition decode.h:204
#define GET_IPV4_DST_ADDR_U32(p)
Definition decode.h:197
#define GET_IPV6_SRC_ADDR(p)
Definition decode.h:203
#define GET_IPV4_SRC_ADDR_U32(p)
Definition decode.h:196
uint32_t id
bool sc_set_caps
Definition suricata.c:189
ThreadVars * tv
void FreeCachedCtx(void)
int RejectSendLibnet11IPv6TCP(ThreadVars *tv, Packet *p, void *data, enum RejectDirection dir)
int RejectSendLibnet11IPv4TCP(ThreadVars *tv, Packet *p, void *data, enum RejectDirection dir)
int RejectSendLibnet11IPv6ICMP(ThreadVars *tv, Packet *p, void *data, enum RejectDirection dir)
int RejectSendLibnet11IPv4ICMP(ThreadVars *tv, Packet *p, void *data, enum RejectDirection dir)
RejectDirection
@ REJECT_DIR_DST
@ REJECT_DIR_SRC
uint32_t seq
Port sp
Definition decode.h:508
uint16_t vlan_id[VLAN_MAX_LAYERS]
Definition decode.h:528
int datalink
Definition decode.h:639
uint16_t payload_len
Definition decode.h:606
struct LiveDevice_ * livedev
Definition decode.h:618
uint8_t vlan_idx
Definition decode.h:529
Port dp
Definition decode.h:516
Per thread variable structure.
Definition threadvars.h:58
#define MIN(x, y)
uint8_t host_mode
Definition suricata.c:180
#define IS_SURI_HOST_MODE_SNIFFER_ONLY(host_mode)
Definition suricata.h:129
#define SCLogDebug(...)
Definition util-debug.h:275
#define SCLogError(...)
Macro used to log ERROR messages.
Definition util-debug.h:267