Coverage Report

Created: 2026-10-16 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/open5gs/lib/ipfw/ipv6.c
Line
Count
Source
1
/*
2
 * Copyright (c) 2002-2003 Luigi Rizzo
3
 * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp
4
 * Copyright (c) 1994 Ugen J.S.Antsilevich
5
 *
6
 * Idea and grammar partially left from:
7
 * Copyright (c) 1993 Daniel Boulet
8
 *
9
 * Redistribution and use in source forms, with and without modification,
10
 * are permitted provided that this entire comment appears intact.
11
 *
12
 * Redistribution in binary form may occur without any restrictions.
13
 * Obviously, it would be nice if you gave credit where credit is due
14
 * but requiring it would be too onerous.
15
 *
16
 * This software is provided ``AS IS'' without any warranties of any kind.
17
 *
18
 * NEW command line interface for IP firewall facility
19
 *
20
 * $FreeBSD: head/sbin/ipfw/ipv6.c 270424 2014-08-23 17:37:18Z melifaro $
21
 *
22
 * ipv6 support
23
 */
24
25
#include <sys/types.h>
26
#include <sys/socket.h>
27
28
#include "ipfw2.h"
29
30
#include <err.h>
31
#include <netdb.h>
32
#include <stdio.h>
33
#include <stdlib.h>
34
#include <string.h>
35
#include <sysexits.h>
36
37
#include <net/if.h>
38
#include <netinet/in.h>
39
#include <netinet/in_systm.h>
40
#include <netinet/ip.h>
41
#include <netinet/icmp6.h>
42
#include <netinet/ip_fw.h>
43
#include <arpa/inet.h>
44
45
283
#define CHECK_LENGTH(v, len) do {     \
46
283
  if ((v) < (len))       \
47
283
    errx(EX_DATAERR, "Rule too long"); \
48
283
  } while (0)
49
50
static struct _s_x icmp6codes[] = {
51
      { "no-route",   ICMP6_DST_UNREACH_NOROUTE },
52
      { "admin-prohib",   ICMP6_DST_UNREACH_ADMIN },
53
      { "address",    ICMP6_DST_UNREACH_ADDR },
54
      { "port",     ICMP6_DST_UNREACH_NOPORT },
55
      { NULL, 0 }
56
};
57
58
void
59
fill_unreach6_code(u_short *codep, char *str)
60
0
{
61
0
  int val;
62
0
  char *s;
63
64
0
  val = strtoul(str, &s, 0);
65
0
  if (s == str || *s != '\0' || val >= 0x100)
66
0
    val = match_token(icmp6codes, str);
67
0
  if (val < 0)
68
0
    errx(EX_DATAERR, "unknown ICMPv6 unreachable code ``%s''", str);
69
0
  *codep = val;
70
0
  return;
71
0
}
72
73
void
74
print_unreach6_code(uint16_t code)
75
0
{
76
0
  char const *s = match_value(icmp6codes, code);
77
78
0
  if (s != NULL)
79
0
    printf("unreach6 %s", s);
80
0
  else
81
0
    printf("unreach6 %u", code);
82
0
}
83
84
/*
85
 * Print the ip address contained in a command.
86
 */
87
void
88
print_ip6(struct buf_pr *bp, ipfw_insn_ip6 *cmd, char const *s)
89
0
{
90
0
       struct hostent *he = NULL;
91
0
       int len = F_LEN((ipfw_insn *) cmd) - 1;
92
0
       struct in6_addr *a = &(cmd->addr6);
93
0
       char trad[255];
94
95
0
       bprintf(bp, "%s%s ", cmd->o.len & F_NOT ? " not": "", s);
96
97
0
       if (cmd->o.opcode == O_IP6_SRC_ME || cmd->o.opcode == O_IP6_DST_ME) {
98
0
         bprintf(bp, "me6");
99
0
         return;
100
0
       }
101
0
       if (cmd->o.opcode == O_IP6) {
102
0
         bprintf(bp, " ip6");
103
0
         return;
104
0
       }
105
106
       /*
107
  * len == 4 indicates a single IP, whereas lists of 1 or more
108
  * addr/mask pairs have len = (2n+1). We convert len to n so we
109
  * use that to count the number of entries.
110
  */
111
112
0
       for (len = len / 4; len > 0; len -= 2, a += 2) {
113
0
     int mb = /* mask length */
114
0
         (cmd->o.opcode == O_IP6_SRC || cmd->o.opcode == O_IP6_DST) ?
115
0
         128 : contigmask((uint8_t *)&(a[1]), 128);
116
117
0
     if (mb == 128 && co.do_resolv)
118
0
         he = gethostbyaddr((char *)a, sizeof(*a), AF_INET6);
119
0
     if (he != NULL)      /* resolved to name */
120
0
         bprintf(bp, "%s", he->h_name);
121
0
     else if (mb == 0)    /* any */
122
0
         bprintf(bp, "any");
123
0
     else {   /* numeric IP followed by some kind of mask */
124
0
         if (inet_ntop(AF_INET6,  a, trad, sizeof( trad ) ) == NULL)
125
0
       bprintf(bp, "Error ntop in print_ip6\n");
126
0
         bprintf(bp, "%s",  trad );
127
0
         if (mb < 0)     /* XXX not really legal... */
128
0
       bprintf(bp, ":%s",
129
0
           inet_ntop(AF_INET6, &a[1], trad, sizeof(trad)));
130
0
         else if (mb < 128)
131
0
       bprintf(bp, "/%d", mb);
132
0
     }
133
0
     if (len > 2)
134
0
         bprintf(bp, ",");
135
0
       }
136
0
}
137
138
void
139
fill_icmp6types(ipfw_insn_icmp6 *cmd, char *av, int cblen)
140
0
{
141
0
       uint8_t type;
142
143
0
       CHECK_LENGTH(cblen, F_INSN_SIZE(ipfw_insn_icmp6));
144
145
0
       bzero(cmd, sizeof(*cmd));
146
0
       while (*av) {
147
0
     if (*av == ',')
148
0
         av++;
149
0
     type = strtoul(av, &av, 0);
150
0
     if (*av != ',' && *av != '\0')
151
0
         errx(EX_DATAERR, "invalid ICMP6 type");
152
     /*
153
      * XXX: shouldn't this be 0xFF?  I can't see any reason why
154
      * we shouldn't be able to filter all possiable values
155
      * regardless of the ability of the rest of the kernel to do
156
      * anything useful with them.
157
      */
158
0
     if (type > ICMP6_MAXTYPE)
159
0
         errx(EX_DATAERR, "ICMP6 type out of range");
160
0
     cmd->d[type / 32] |= ( 1 << (type % 32));
161
0
       }
162
0
       cmd->o.opcode = O_ICMP6TYPE;
163
0
       cmd->o.len |= F_INSN_SIZE(ipfw_insn_icmp6);
164
0
}
165
166
167
void
168
print_icmp6types(struct buf_pr *bp, ipfw_insn_u32 *cmd)
169
0
{
170
0
       int i, j;
171
0
       char sep= ' ';
172
173
0
       bprintf(bp, " ip6 icmp6types");
174
0
       for (i = 0; i < 7; i++)
175
0
         for (j=0; j < 32; ++j) {
176
0
           if ( (cmd->d[i] & (1 << (j))) == 0)
177
0
             continue;
178
0
           bprintf(bp, "%c%d", sep, (i*32 + j));
179
0
           sep = ',';
180
0
         }
181
0
}
182
183
void
184
print_flow6id(struct buf_pr *bp, ipfw_insn_u32 *cmd)
185
0
{
186
0
       uint16_t i, limit = cmd->o.arg1;
187
0
       char sep = ',';
188
189
0
       bprintf(bp, " flow-id ");
190
0
       for( i=0; i < limit; ++i) {
191
0
         if (i == limit - 1)
192
0
           sep = ' ';
193
0
         bprintf(bp, "%d%c", cmd->d[i], sep);
194
0
       }
195
0
}
196
197
/* structure and define for the extension header in ipv6 */
198
static struct _s_x ext6hdrcodes[] = {
199
       { "frag",       EXT_FRAGMENT },
200
       { "hopopt",     EXT_HOPOPTS },
201
       { "route",      EXT_ROUTING },
202
       { "dstopt",     EXT_DSTOPTS },
203
       { "ah",   EXT_AH },
204
       { "esp", EXT_ESP },
205
       { "rthdr0",     EXT_RTHDR0 },
206
       { "rthdr2",     EXT_RTHDR2 },
207
       { NULL,   0 }
208
};
209
210
/* fills command for the extension header filtering */
211
int
212
fill_ext6hdr( ipfw_insn *cmd, char *av)
213
0
{
214
0
       int tok;
215
0
       char *s = av;
216
217
0
       cmd->arg1 = 0;
218
219
0
       while(s) {
220
0
     av = strsep( &s, ",") ;
221
0
     tok = match_token(ext6hdrcodes, av);
222
0
     switch (tok) {
223
0
     case EXT_FRAGMENT:
224
0
         cmd->arg1 |= EXT_FRAGMENT;
225
0
         break;
226
227
0
     case EXT_HOPOPTS:
228
0
         cmd->arg1 |= EXT_HOPOPTS;
229
0
         break;
230
231
0
     case EXT_ROUTING:
232
0
         cmd->arg1 |= EXT_ROUTING;
233
0
         break;
234
235
0
     case EXT_DSTOPTS:
236
0
         cmd->arg1 |= EXT_DSTOPTS;
237
0
         break;
238
239
0
     case EXT_AH:
240
0
         cmd->arg1 |= EXT_AH;
241
0
         break;
242
243
0
     case EXT_ESP:
244
0
         cmd->arg1 |= EXT_ESP;
245
0
         break;
246
247
0
     case EXT_RTHDR0:
248
0
         cmd->arg1 |= EXT_RTHDR0;
249
0
         break;
250
251
0
     case EXT_RTHDR2:
252
0
         cmd->arg1 |= EXT_RTHDR2;
253
0
         break;
254
255
0
     default:
256
0
         errx( EX_DATAERR, "invalid option for ipv6 exten header" );
257
0
         break;
258
0
     }
259
0
       }
260
0
       if (cmd->arg1 == 0 )
261
0
     return 0;
262
0
       cmd->opcode = O_EXT_HDR;
263
0
       cmd->len |= F_INSN_SIZE( ipfw_insn );
264
0
       return 1;
265
0
}
266
267
void
268
print_ext6hdr(struct buf_pr *bp, ipfw_insn *cmd )
269
0
{
270
0
       char sep = ' ';
271
272
0
       bprintf(bp, " extension header:");
273
0
       if (cmd->arg1 & EXT_FRAGMENT ) {
274
0
     bprintf(bp, "%cfragmentation", sep);
275
0
     sep = ',';
276
0
       }
277
0
       if (cmd->arg1 & EXT_HOPOPTS ) {
278
0
     bprintf(bp, "%chop options", sep);
279
0
     sep = ',';
280
0
       }
281
0
       if (cmd->arg1 & EXT_ROUTING ) {
282
0
     bprintf(bp, "%crouting options", sep);
283
0
     sep = ',';
284
0
       }
285
0
       if (cmd->arg1 & EXT_RTHDR0 ) {
286
0
     bprintf(bp, "%crthdr0", sep);
287
0
     sep = ',';
288
0
       }
289
0
       if (cmd->arg1 & EXT_RTHDR2 ) {
290
0
     bprintf(bp, "%crthdr2", sep);
291
0
     sep = ',';
292
0
       }
293
0
       if (cmd->arg1 & EXT_DSTOPTS ) {
294
0
     bprintf(bp, "%cdestination options", sep);
295
0
     sep = ',';
296
0
       }
297
0
       if (cmd->arg1 & EXT_AH ) {
298
0
     bprintf(bp, "%cauthentication header", sep);
299
0
     sep = ',';
300
0
       }
301
0
       if (cmd->arg1 & EXT_ESP ) {
302
0
     bprintf(bp, "%cencapsulated security payload", sep);
303
0
       }
304
0
}
305
306
/* Try to find ipv6 address by hostname */
307
static int
308
lookup_host6 (char *host, struct in6_addr *ip6addr)
309
283
{
310
283
  struct hostent *he;
311
312
283
  if (!inet_pton(AF_INET6, host, ip6addr)) {
313
0
    if ((he = gethostbyname2(host, AF_INET6)) == NULL)
314
0
      return(-1);
315
0
    memcpy(ip6addr, he->h_addr_list[0], sizeof( struct in6_addr));
316
0
  }
317
283
  return(0);
318
283
}
319
320
321
/*
322
 * fill the addr and mask fields in the instruction as appropriate from av.
323
 * Update length as appropriate.
324
 * The following formats are allowed:
325
 *     any     matches any IP6. Actually returns an empty instruction.
326
 *     me      returns O_IP6_*_ME
327
 *
328
 *     03f1::234:123:0342   single IP6 addres
329
 *     03f1::234:123:0342/24      address/mask
330
 *     03f1::234:123:0342/24,03f1::234:123:0343/         List of address
331
 *
332
 * Set of address (as in ipv6) not supported because ipv6 address
333
 * are typically random past the initial prefix.
334
 * Return 1 on success, 0 on failure.
335
 */
336
static int
337
fill_ip6(ipfw_insn_ip6 *cmd, char *av, int cblen)
338
287
{
339
287
  int len = 0;
340
287
  struct in6_addr *d = &(cmd->addr6);
341
287
  char *av1;
342
  /*
343
   * Needed for multiple address.
344
   * Note d[1] points to struct in6_add r mask6 of cmd
345
   */
346
347
287
  cmd->o.len &= ~F_LEN_MASK; /* zero len */
348
349
287
  if (strcmp(av, "any") == 0)
350
4
    return (1);
351
352
353
283
  if (strcmp(av, "me") == 0) { /* Set the data for "me" opt*/
354
0
    cmd->o.len |= F_INSN_SIZE(ipfw_insn);
355
0
    return (1);
356
0
  }
357
358
283
  if (strcmp(av, "me6") == 0) { /* Set the data for "me" opt*/
359
0
    cmd->o.len |= F_INSN_SIZE(ipfw_insn);
360
0
    return (1);
361
0
  }
362
363
283
  if (strncmp(av, "table(", 6) == 0) {
364
0
    char *p = strchr(av + 6, ',');
365
0
    uint32_t *dm = ((ipfw_insn_u32 *)cmd)->d;
366
367
0
    if (p)
368
0
      *p++ = '\0';
369
0
    cmd->o.opcode = O_IP_DST_LOOKUP;
370
0
    cmd->o.arg1 = strtoul(av + 6, NULL, 0);
371
0
    if (p) {
372
0
      cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
373
0
      dm[0] = strtoul(p, NULL, 0);
374
0
    } else
375
0
      cmd->o.len |= F_INSN_SIZE(ipfw_insn);
376
0
    return (1);
377
0
  }
378
379
283
  av1 = av = strdup(av);
380
332
  while (av) {
381
    /*
382
     * After the address we can have '/' indicating a mask,
383
     * or ',' indicating another address follows.
384
     */
385
386
283
    char *p;
387
283
    int masklen;
388
283
    char md = '\0';
389
390
283
    CHECK_LENGTH(cblen, 1 + len + 2 * F_INSN_SIZE(struct in6_addr));
391
392
283
    if ((p = strpbrk(av, "/,")) ) {
393
49
      md = *p;  /* save the separator */
394
49
      *p = '\0';  /* terminate address string */
395
49
      p++;    /* and skip past it */
396
49
    }
397
    /* now p points to NULL, mask or next entry */
398
399
    /* lookup stores address in *d as a side effect */
400
283
    if (lookup_host6(av, d) != 0) {
401
      /* XXX: failed. Free memory and go */
402
0
      errx(EX_DATAERR, "bad address \"%s\"", av);
403
0
    }
404
    /* next, look at the mask, if any */
405
283
    masklen = (md == '/') ? atoi(p) : 128;
406
283
    if (masklen > 128 || masklen < 0)
407
0
      errx(EX_DATAERR, "bad width \"%s\''", p);
408
283
    else
409
283
      n2mask(&d[1], masklen);
410
411
283
    APPLY_MASK(d, &d[1])   /* mask base address with mask */
412
283
413
283
    /* find next separator */
414
283
415
283
    if (md == '/') { /* find separator past the mask */
416
49
      p = strpbrk(p, ",");
417
49
      if (p != NULL)
418
0
        p++;
419
49
    }
420
283
    av = p;
421
422
    /* Check this entry */
423
283
    if (masklen == 0) {
424
      /*
425
       * 'any' turns the entire list into a NOP.
426
       * 'not any' never matches, so it is removed from the
427
       * list unless it is the only item, in which case we
428
       * report an error.
429
       */
430
15
      if (cmd->o.len & F_NOT && av == NULL && len == 0)
431
0
        errx(EX_DATAERR, "not any never matches");
432
15
      continue;
433
15
    }
434
435
    /*
436
     * A single IP can be stored alone
437
     */
438
268
    if (masklen == 128 && av == NULL && len == 0) {
439
234
      len = F_INSN_SIZE(struct in6_addr);
440
234
      break;
441
234
    }
442
443
    /* Update length and pointer to arguments */
444
34
    len += F_INSN_SIZE(struct in6_addr)*2;
445
34
    d += 2;
446
34
  } /* end while */
447
448
  /*
449
   * Total length of the command, remember that 1 is the size of
450
   * the base command.
451
   */
452
283
  if (len + 1 > F_LEN_MASK)
453
0
    errx(EX_DATAERR, "address list too long");
454
283
  cmd->o.len |= len+1;
455
283
  free(av1);
456
283
  return (1);
457
283
}
458
459
/*
460
 * fills command for ipv6 flow-id filtering
461
 * note that the 20 bit flow number is stored in a array of u_int32_t
462
 * it's supported lists of flow-id, so in the o.arg1 we store how many
463
 * additional flow-id we want to filter, the basic is 1
464
 */
465
void
466
fill_flow6( ipfw_insn_u32 *cmd, char *av, int cblen)
467
0
{
468
0
  u_int32_t type;  /* Current flow number */
469
0
  u_int16_t nflow = 0;    /* Current flow index */
470
0
  char *s = av;
471
0
  cmd->d[0] = 0;    /* Initializing the base number*/
472
473
0
  while (s) {
474
0
    CHECK_LENGTH(cblen, F_INSN_SIZE(ipfw_insn_u32) + nflow + 1);
475
476
0
    av = strsep( &s, ",") ;
477
0
    type = strtoul(av, &av, 0);
478
0
    if (*av != ',' && *av != '\0')
479
0
      errx(EX_DATAERR, "invalid ipv6 flow number %s", av);
480
0
    if (type > 0xfffff)
481
0
      errx(EX_DATAERR, "flow number out of range %s", av);
482
0
    cmd->d[nflow] |= type;
483
0
    nflow++;
484
0
  }
485
0
  if( nflow > 0 ) {
486
0
    cmd->o.opcode = O_FLOW6ID;
487
0
    cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32) + nflow;
488
0
    cmd->o.arg1 = nflow;
489
0
  }
490
0
  else {
491
0
    errx(EX_DATAERR, "invalid ipv6 flow number %s", av);
492
0
  }
493
0
}
494
495
ipfw_insn *
496
add_srcip6(ipfw_insn *cmd, char *av, int cblen)
497
143
{
498
499
143
  fill_ip6((ipfw_insn_ip6 *)cmd, av, cblen);
500
143
  if (cmd->opcode == O_IP_DST_SET)      /* set */
501
0
    cmd->opcode = O_IP_SRC_SET;
502
143
  else if (cmd->opcode == O_IP_DST_LOOKUP)    /* table */
503
0
    cmd->opcode = O_IP_SRC_LOOKUP;
504
143
  else if (F_LEN(cmd) == 0) {       /* any */
505
141
  } else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) { /* "me" */
506
9
    cmd->opcode = O_IP6_SRC_ME;
507
132
  } else if (F_LEN(cmd) ==
508
132
      (F_INSN_SIZE(struct in6_addr) + F_INSN_SIZE(ipfw_insn))) {
509
    /* single IP, no mask*/
510
118
    cmd->opcode = O_IP6_SRC;
511
118
  } else {         /* addr/mask opt */
512
14
    cmd->opcode = O_IP6_SRC_MASK;
513
14
  }
514
143
  return cmd;
515
143
}
516
517
ipfw_insn *
518
add_dstip6(ipfw_insn *cmd, char *av, int cblen)
519
144
{
520
521
144
  fill_ip6((ipfw_insn_ip6 *)cmd, av, cblen);
522
144
  if (cmd->opcode == O_IP_DST_SET)      /* set */
523
0
    ;
524
144
  else if (cmd->opcode == O_IP_DST_LOOKUP)    /* table */
525
0
    ;
526
144
  else if (F_LEN(cmd) == 0) {       /* any */
527
142
  } else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) { /* "me" */
528
6
    cmd->opcode = O_IP6_DST_ME;
529
136
  } else if (F_LEN(cmd) ==
530
136
      (F_INSN_SIZE(struct in6_addr) + F_INSN_SIZE(ipfw_insn))) {
531
    /* single IP, no mask*/
532
116
    cmd->opcode = O_IP6_DST;
533
116
  } else {         /* addr/mask opt */
534
20
    cmd->opcode = O_IP6_DST_MASK;
535
20
  }
536
144
  return cmd;
537
144
}