Coverage Report

Created: 2026-09-06 07:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata8/src/respond-reject-libnet11.c
Line
Count
Source
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"
48
#include "respond-reject-libnet11.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. */
60
const char *g_reject_dev = NULL;
61
uint16_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. */
65
extern bool sc_set_caps;
66
67
#include <libnet.h>
68
69
thread_local libnet_t *t_c = NULL;
70
thread_local int t_inject_mode = -1;
71
72
typedef 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
86
static 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;
96
    if (IS_SURI_HOST_MODE_SNIFFER_ONLY(host_mode)) {
97
        if (g_reject_dev != NULL) {
98
            if (p->datalink == LINKTYPE_ETHERNET)
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
118
static 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
126
void FreeCachedCtx(void)
127
{
128
    if (t_c) {
129
        libnet_destroy(t_c);
130
        t_c = NULL;
131
    }
132
}
133
134
static 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
170
static 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
194
static 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
217
static 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
238
static 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
254
static 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
263
static 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
276
int 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
337
cleanup:
338
    ClearCtx(c);
339
    return 0;
340
}
341
342
int 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
414
cleanup:
415
    ClearCtx(c);
416
    return 0;
417
}
418
419
int 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
478
cleanup:
479
    ClearCtx(c);
480
    return 0;
481
}
482
483
#ifdef HAVE_LIBNET_ICMPV6_UNREACH
484
int 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
555
cleanup:
556
    ClearCtx(c);
557
    return 0;
558
}
559
560
#else /* HAVE_LIBNET_ICMPV6_UNREACH */
561
562
int 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
574
int RejectSendLibnet11IPv4TCP(ThreadVars *tv, Packet *p, void *data, enum RejectDirection dir)
575
0
{
576
0
    SCLogError("Libnet based rejects are disabled."
577
0
               "Usually this means that you don't have libnet installed,"
578
0
               " or configure couldn't find it.");
579
0
    return 0;
580
0
}
581
582
int RejectSendLibnet11IPv4ICMP(ThreadVars *tv, Packet *p, void *data, enum RejectDirection dir)
583
0
{
584
0
    SCLogError("Libnet based rejects are disabled."
585
0
               "Usually this means that you don't have libnet installed,"
586
0
               " or configure couldn't find it.");
587
0
    return 0;
588
0
}
589
590
int RejectSendLibnet11IPv6TCP(ThreadVars *tv, Packet *p, void *data, enum RejectDirection dir)
591
0
{
592
0
    SCLogError("Libnet based rejects are disabled."
593
0
               "Usually this means that you don't have libnet installed,"
594
0
               " or configure couldn't find it.");
595
0
    return 0;
596
0
}
597
598
int RejectSendLibnet11IPv6ICMP(ThreadVars *tv, Packet *p, void *data, enum RejectDirection dir)
599
0
{
600
0
    SCLogError("Libnet based rejects are disabled."
601
0
               "Usually this means that you don't have libnet installed,"
602
0
               " or configure couldn't find it.");
603
0
    return 0;
604
0
}
605
606
void FreeCachedCtx(void)
607
0
{
608
0
    SCLogDebug("no libhnet support");
609
0
}
610
611
#endif /* HAVE_LIBNET11 */