Coverage Report

Created: 2026-09-27 06:19

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/open5gs/lib/ipfw/ogs-ipfw.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2019-2024 by Sukchan Lee <acetcom@gmail.com>
3
 *
4
 * This file is part of Open5GS.
5
 *
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU Affero General Public License as published by
8
 * the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
18
 */
19
20
#ifndef IF_NAMESIZE
21
#define IF_NAMESIZE 16
22
#ifndef IFNAMSIZ
23
#define IFNAMSIZ  IF_NAMESIZE
24
#endif
25
#endif
26
27
#include "ipfw2.h"
28
#include "objs/include_e/netinet/ip_fw.h"
29
30
3.63k
#define MAX_NUM_OF_TOKEN 32
31
#define MAX_NUM_OF_RULE_BUFFER 1024
32
33
void compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, void *tstate);
34
35
/* Set by the errx() override in ipfw2.c when a rule fails to parse */
36
extern int ogs_ipfw_parse_error;
37
38
/* Protocol name table shared with add_proto0() in ipfw2.c */
39
extern int ipfw_proto_by_name(const char *name);
40
41
/*
42
 * 3GPP TS 29.212 limits the IPFilterRule carried in a Flow-Description to
43
 *
44
 *     permit out <proto> from <src> [<ports>] to <dst> [<ports>]
45
 *
46
 * compile_rule() below is the full ipfw(8) command line parser.  It also
47
 * accepts hostnames, address sets, lookup tables and the entire option
48
 * keyword space, and -- because errx() no longer exits -- it keeps parsing
49
 * after it has already rejected a token, dereferencing arguments that are
50
 * not there.  A Flow-Description from a PCF, PCRF or AF can therefore crash
51
 * an SMF, UPF or SGW-U:
52
 *
53
 *     permit out ip                              -> SEGV in add_src()
54
 *     permit out ip from 1.2.3.4                 -> SEGV in add_dst()
55
 *     permit out ip from 1.2.3.4 to 1.2.3.4 uid  -> SEGV in compile_rule()
56
 *     permit out ip from table(1) to assigned    -> SEGV in pack_table()
57
 *     permit out 58 from ff02::2/129 to assigned -> exit() from ipv6.c
58
 *
59
 * Check the token stream against the grammar we accept before handing it
60
 * over.  The structure, the addresses and the ports are settled here; an
61
 * unrecognised protocol name is the one thing still left to the parser,
62
 * which rejects it through ogs_ipfw_parse_error below.
63
 */
64
static bool ipfw_parse_number(const char *s, int min, int max, int *value)
65
1.30k
{
66
1.30k
    int v = 0;
67
68
1.30k
    if (!s || !*s)
69
43
        return false;
70
71
3.54k
    for (; *s; s++) {
72
2.41k
        if (*s < '0' || *s > '9')
73
103
            return false;
74
2.31k
        v = v * 10 + (*s - '0');
75
2.31k
        if (v > max)
76
30
            return false;
77
2.31k
    }
78
79
1.12k
    if (v < min)
80
6
        return false;
81
82
1.12k
    if (value)
83
905
        *value = v;
84
85
1.12k
    return true;
86
1.12k
}
87
88
static bool ipfw_is_number(const char *s, int min, int max)
89
141
{
90
141
    return ipfw_parse_number(s, min, max, NULL);
91
141
}
92
93
/*
94
 * "ip", a protocol number, or a name that resolves to one.
95
 *
96
 * The token is rewritten in place as a number so that compile_rule() only
97
 * ever sees "ip" or a decimal, because every other spelling it understands
98
 * ends up meaning "no protocol restriction" once the rule reaches
99
 * ogs_ipfw_rule_t:
100
 *
101
 *   - add_proto0() stores the number in a u_char without a range check, so
102
 *     "256" is truncated to 0 and a UDP filter turns into "ip"; "999"
103
 *     becomes protocol 231.
104
 *   - add_proto() matches "all" with _substrcmp(), which succeeds on a
105
 *     prefix, so "a" and "al" are accepted as "all" -> protocol 0.
106
 *   - "ip4", "ipv4", "ip6" and "ipv6" are family selectors emitted as O_IP4
107
 *     or O_IP6, opcodes the loop below does not read, so the restriction is
108
 *     dropped and protocol 0 is what remains.
109
 *   - a name whose number is 0 -- "hopopt" -- passes add_proto0() unchanged
110
 *     and lands as protocol 0 as well.
111
 *
112
 * Resolve the name through ipfw_proto_by_name(), the table add_proto0()
113
 * itself uses, rather than keeping a second one here.
114
 */
115
static int ipfw_check_proto(char **token, char *buf, int size, int *protop)
116
1.13k
{
117
1.13k
    const char *s = *token;
118
1.13k
    const char *p;
119
1.13k
    int proto;
120
121
1.13k
    if (!s || !*s)
122
0
        return OGS_ERROR;
123
124
    /* the only name carried through: no protocol restriction */
125
1.13k
    if (strcmp(s, "ip") == 0) {
126
8
        *protop = IPPROTO_IP;
127
8
        return OGS_OK;
128
8
    }
129
130
1.12k
    if (*s >= '0' && *s <= '9') {
131
731
        if (!ipfw_parse_number(s, 1, 255, &proto))
132
24
            return OGS_ERROR;
133
731
    } else {
134
3.24k
        for (p = s; *p; p++) {
135
2.88k
            if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') ||
136
712
                (*p >= '0' && *p <= '9') || *p == '-')
137
2.84k
                continue;
138
39
            return OGS_ERROR;
139
2.88k
        }
140
141
        /*
142
         * "all" and the family selectors would resolve to something, or to
143
         * nothing, that no longer means what was written.  Refuse them by
144
         * name so that the operator is told to use "ip" instead.
145
         */
146
358
        if (strcmp(s, "all") == 0 || strcmp(s, "ip4") == 0 ||
147
356
            strcmp(s, "ipv4") == 0 || strcmp(s, "ip6") == 0 ||
148
354
            strcmp(s, "ipv6") == 0)
149
5
            return OGS_ERROR;
150
151
353
        proto = ipfw_proto_by_name(s);
152
353
        if (proto < 1 || proto > 255)
153
351
            return OGS_ERROR;
154
353
    }
155
156
709
    ogs_snprintf(buf, size, "%d", proto);
157
709
    *token = buf;
158
709
    *protop = proto;
159
160
709
    return OGS_OK;
161
1.12k
}
162
163
/*
164
 * "any", "assigned", or a literal IPv4/IPv6 address with optional prefix.
165
 *
166
 * Returns the address family, AF_UNSPEC for the two keywords -- which carry
167
 * no family of their own -- or -1 when the token is not an address at all.
168
 */
169
static int ipfw_addr_family(const char *s)
170
867
{
171
867
    char buf[OGS_ADDRSTRLEN];
172
867
    struct in_addr addr4;
173
867
    struct in6_addr addr6;
174
867
    const char *slash;
175
867
    size_t len;
176
177
867
    if (!s || !*s)
178
0
        return -1;
179
180
867
    if (strcmp(s, "any") == 0 || strcmp(s, "assigned") == 0)
181
69
        return AF_UNSPEC;
182
183
798
    slash = strchr(s, '/');
184
798
    len = slash ? (size_t)(slash - s) : strlen(s);
185
798
    if (len == 0 || len >= sizeof(buf))
186
10
        return -1;
187
788
    memcpy(buf, s, len);
188
788
    buf[len] = '\0';
189
190
788
    if (inet_pton(AF_INET, buf, &addr4) == 1)
191
63
        return (!slash || ipfw_is_number(slash + 1, 0, 32)) ? AF_INET : -1;
192
725
    if (inet_pton(AF_INET6, buf, &addr6) == 1)
193
623
        return (!slash || ipfw_is_number(slash + 1, 0, 128)) ? AF_INET6 : -1;
194
195
102
    return -1;
196
725
}
197
198
/*
199
 * A port number, written in decimal.
200
 *
201
 * fill_newports() reads it with strtol(..., 0), where a leading zero means
202
 * octal, so the two readings have to be kept from diverging: "010" would be
203
 * accepted here as 10 and compiled as 8, and "08" is not a number at all to
204
 * the parser, which then leaves the rule's remaining tokens shifted by one
205
 * and sends "to" through lookup_host() as if it were an address.
206
 */
207
static bool ipfw_parse_port(const char *s, int *value)
208
455
{
209
455
    if (s && s[0] == '0' && s[1] != '\0')
210
25
        return false;
211
212
430
    return ipfw_parse_number(s, 1, 65535, value);
213
455
}
214
215
/*
216
 * A single port or a "low-high" range, 1..65535.
217
 *
218
 * Port 0 must not be accepted: ogs_ipfw_rule_t uses 0 to mean "no port
219
 * condition", so "from 1.2.3.4 0" would silently become "from 1.2.3.4" and
220
 * widen the filter to every port.  fill_newports() stores the two ends of a
221
 * range without comparing them, so a reversed range has to be caught here
222
 * as well.
223
 */
224
static bool ipfw_is_ports(const char *s)
225
325
{
226
325
    char buf[8];
227
325
    const char *dash;
228
325
    size_t len;
229
325
    int low, high;
230
231
325
    if (!s || !*s)
232
0
        return false;
233
234
325
    dash = strchr(s, '-');
235
325
    if (!dash)
236
157
        return ipfw_parse_port(s, NULL);
237
238
168
    len = dash - s;
239
168
    if (len == 0 || len >= sizeof(buf))
240
3
        return false;
241
165
    memcpy(buf, s, len);
242
165
    buf[len] = '\0';
243
244
165
    if (!ipfw_parse_port(buf, &low))
245
32
        return false;
246
133
    if (!ipfw_parse_port(dash + 1, &high))
247
68
        return false;
248
249
65
    return low <= high;
250
133
}
251
252
/*
253
 * av[0] is unused and av[1] is "permit", so the tokens to check are
254
 * av[2] .. av[last-1].  The direction, which the caller has moved to the
255
 * end of the array, is not part of this grammar.
256
 */
257
static int ipfw_check_tokens(
258
        char *av[], int last, char *proto_buf, int proto_buf_size,
259
        char *flow_description)
260
1.13k
{
261
1.13k
    int x = 2;
262
1.13k
    int proto = IPPROTO_IP;
263
1.13k
    int src_family, dst_family;
264
265
1.13k
#define REJECT(reason) \
266
1.13k
    do { \
267
929
        ogs_error("Invalid Flow-Description [%s] : %s", \
268
929
                flow_description, reason); \
269
929
        return OGS_ERROR; \
270
929
    } while (0)
271
1.13k
#define REQUIRE(cond, reason) \
272
5.82k
    do { if (!(cond)) REJECT(reason); } while (0)
273
274
1.13k
    REQUIRE(x < last, "no protocol");
275
1.13k
    REQUIRE(ipfw_check_proto(
276
1.13k
                &av[x], proto_buf, proto_buf_size, &proto) == OGS_OK,
277
1.13k
            "bad protocol");
278
717
    x++;
279
280
717
    REQUIRE(x < last && strcmp(av[x], "from") == 0, "missing 'from'");
281
640
    x++;
282
283
640
    REQUIRE(x < last, "no source address");
284
/*
285
 * Refer to lib/ipfw/ogs-ipfw.h
286
 * Issue #338
287
 *
288
 * A Flow-Description is always written in the downlink orientation, so the
289
 * UE address -- "assigned" -- can only appear after "to".  An uplink flow
290
 * arrives as RX "permit in from <UE> to <REMOTE>", which flow_rx_to_gx()
291
 * has already rewritten into that form before it reaches here.
292
 */
293
639
    if (strcmp(av[x], "assigned") == 0)
294
1
        REJECT("'assigned' is the UE address and is only valid after 'to'");
295
638
    src_family = ipfw_addr_family(av[x]);
296
638
    REQUIRE(src_family >= 0, "bad source address");
297
499
    x++;
298
299
499
    if (x < last && strcmp(av[x], "to") != 0) {
300
288
        REQUIRE(ipfw_is_ports(av[x]), "bad source port");
301
127
        x++;
302
127
    }
303
304
338
    REQUIRE(x < last && strcmp(av[x], "to") == 0, "missing 'to'");
305
230
    x++;
306
307
230
    REQUIRE(x < last, "no destination address");
308
229
    dst_family = ipfw_addr_family(av[x]);
309
229
    REQUIRE(dst_family >= 0, "bad destination address");
310
220
    x++;
311
312
220
    if (x < last) {
313
37
        REQUIRE(ipfw_is_ports(av[x]), "bad destination port");
314
30
        x++;
315
30
    }
316
317
213
    REQUIRE(x == last, "trailing token");
318
319
    /* one packet cannot carry both families */
320
212
    REQUIRE(src_family == AF_UNSPEC || dst_family == AF_UNSPEC ||
321
212
            src_family == dst_family,
322
212
            "source and destination address family differ");
323
324
    /*
325
     * Protocol 41 is IPv6 encapsulation, and the outer addresses of a
326
     * 6in4 tunnel are legitimately IPv4.  add_src() and add_dst(), however,
327
     * treat the number as if it selected the address family: once the
328
     * protocol is IPPROTO_IPV6 they send every address through
329
     * add_srcip6() and add_dstip6(), whatever it looks like.  ipv6.c calls
330
     * the real errx() from err.h -- it never sees the override in
331
     * ipfw2.c -- so an IPv4 literal exits the daemon rather than failing
332
     * the rule.  Refuse the combination until the parser can carry it.
333
     */
334
210
    if (proto == IPPROTO_IPV6)
335
6
        REQUIRE(src_family != AF_INET && dst_family != AF_INET,
336
210
                "IPv4 literal with protocol 41 is unsupported");
337
338
208
#undef REQUIRE
339
208
#undef REJECT
340
341
208
    return OGS_OK;
342
210
}
343
344
345
int ogs_ipfw_compile_rule(ogs_ipfw_rule_t *ipfw_rule, char *flow_description)
346
1.23k
{
347
1.23k
    char *token, *dir;
348
1.23k
    char *saveptr;
349
1.23k
    int i;
350
351
1.23k
    char *av[MAX_NUM_OF_TOKEN];
352
1.23k
    char proto_buf[4];
353
1.23k
  uint32_t rulebuf[MAX_NUM_OF_RULE_BUFFER];
354
1.23k
  int rbufsize;
355
1.23k
  struct ip_fw_rule *rule = (struct ip_fw_rule *)rulebuf;
356
357
1.23k
  int x, l;
358
1.23k
  ipfw_insn *cmd;
359
360
1.23k
    char *description = NULL;
361
362
1.23k
    ogs_assert(ipfw_rule);
363
1.23k
    ogs_assert(flow_description);
364
365
1.23k
  rbufsize = sizeof(rulebuf);
366
1.23k
  memset(rulebuf, 0, rbufsize);
367
368
1.23k
    av[0] = NULL;
369
370
    /* ACTION */
371
1.23k
    description = ogs_strdup(flow_description);
372
1.23k
    ogs_assert(description);
373
374
1.23k
    token = ogs_strtok_r(description, " ", &saveptr);
375
1.23k
    if (!token || strcmp(token, "permit") != 0) {
376
61
        ogs_error("Not begins with reserved keyword : 'permit'");
377
61
        ogs_free(description);
378
61
        return OGS_ERROR;
379
61
    }
380
1.17k
    av[1] = token;
381
382
    /* Save DIRECTION */
383
1.17k
    dir = token = ogs_strtok_r(NULL, " ", &saveptr);
384
1.17k
    if (!token || strcmp(token, "out") != 0) {
385
33
        ogs_error("Not begins with reserved keyword : 'permit out'");
386
33
        ogs_free(description);
387
33
        return OGS_ERROR;
388
33
    }
389
390
    /* ADDR */
391
1.13k
    i = 2;
392
1.13k
    token = ogs_strtok_r(NULL, " ", &saveptr);
393
4.77k
    while ((token != NULL) && (i < (MAX_NUM_OF_TOKEN-2))) {
394
3.63k
        av[i++] = token;
395
3.63k
        token = ogs_strtok_r(NULL, " ", &saveptr);
396
3.63k
    }
397
398
    /* Add DIRECTION */
399
1.13k
    av[i++] = dir;
400
401
1.13k
    av[i] = NULL;
402
403
1.13k
    if (ipfw_check_tokens(av, i-1, proto_buf, sizeof(proto_buf),
404
1.13k
                flow_description) != OGS_OK) {
405
929
        ogs_free(description);
406
929
        return OGS_ERROR;
407
929
    }
408
409
    /* "to assigned" --> "to any" */
410
1.51k
    for (x = 2; av[x] != NULL; x++) {
411
1.30k
        if (strcmp(av[x], "assigned") == 0 && strcmp(av[x-1], "to") == 0) {
412
1
            av[x] = "any";
413
1
            break;
414
1
        }
415
1.30k
    }
416
417
208
    ogs_ipfw_parse_error = 0;
418
208
  compile_rule(av, (uint32_t *)rule, &rbufsize, NULL);
419
420
208
    memset(ipfw_rule, 0, sizeof(ogs_ipfw_rule_t));
421
422
208
    if (ogs_ipfw_parse_error) {
423
0
        ogs_error("Cannot compile Flow-Description [%s]", flow_description);
424
0
        ogs_free(description);
425
0
        return OGS_ERROR;
426
0
    }
427
208
  for (l = rule->act_ofs, cmd = rule->cmd;
428
1.02k
      l > 0 ; l -= F_LEN(cmd) , cmd += F_LEN(cmd)) {
429
814
        uint32_t *a = NULL;
430
814
        uint16_t *p = NULL;
431
814
    switch (cmd->opcode) {
432
201
        case O_PROTO:
433
201
            ipfw_rule->proto = cmd->arg1;
434
201
            break;
435
7
        case O_IP_SRC:
436
11
        case O_IP_SRC_MASK:
437
11
            a = ((ipfw_insn_u32 *)cmd)->d;
438
11
            ipfw_rule->ipv4_src = 1;
439
11
            ipfw_rule->ip.src.addr[0] = a[0];
440
11
            if (cmd->opcode == O_IP_SRC_MASK)
441
4
                ipfw_rule->ip.src.mask[0] = a[1];
442
7
            else
443
7
                ipfw_rule->ip.src.mask[0] = 0xffffffff;
444
11
            break;
445
9
        case O_IP_DST:
446
41
        case O_IP_DST_MASK:
447
41
            a = ((ipfw_insn_u32 *)cmd)->d;
448
41
            ipfw_rule->ipv4_dst = 1;
449
41
            ipfw_rule->ip.dst.addr[0] = a[0];
450
41
            if (cmd->opcode == O_IP_DST_MASK)
451
32
                ipfw_rule->ip.dst.mask[0] = a[1];
452
9
            else
453
9
                ipfw_rule->ip.dst.mask[0] = 0xffffffff;
454
41
            break;
455
127
        case O_IP6_SRC:
456
143
        case O_IP6_SRC_MASK:
457
143
            a = ((ipfw_insn_u32 *)cmd)->d;
458
143
            ipfw_rule->ipv6_src = 1;
459
143
            memcpy(ipfw_rule->ip.src.addr, a, OGS_IPV6_LEN);
460
143
            if (cmd->opcode == O_IP6_SRC_MASK)
461
16
                memcpy(ipfw_rule->ip.src.mask, a+4, OGS_IPV6_LEN);
462
127
            else
463
127
                n2mask((struct in6_addr *)ipfw_rule->ip.src.mask, 128);
464
143
            break;
465
120
        case O_IP6_DST:
466
145
        case O_IP6_DST_MASK:
467
145
            a = ((ipfw_insn_u32 *)cmd)->d;
468
145
            ipfw_rule->ipv6_dst = 1;
469
145
            memcpy(ipfw_rule->ip.dst.addr, a, OGS_IPV6_LEN);
470
145
            if (cmd->opcode == O_IP6_DST_MASK)
471
25
                memcpy(ipfw_rule->ip.dst.mask, a+4, OGS_IPV6_LEN);
472
120
            else
473
120
                n2mask((struct in6_addr *)ipfw_rule->ip.dst.mask, 128);
474
145
            break;
475
29
        case O_IP_SRCPORT:
476
29
            p = ((ipfw_insn_u16 *)cmd)->ports;
477
29
            ipfw_rule->port.src.low = p[0];
478
29
            ipfw_rule->port.src.high = p[1];
479
29
            break;
480
29
        case O_IP_DSTPORT:
481
29
            p = ((ipfw_insn_u16 *)cmd)->ports;
482
29
            ipfw_rule->port.dst.low = p[0];
483
29
            ipfw_rule->port.dst.high = p[1];
484
29
            break;
485
814
        }
486
814
  }
487
488
208
    ogs_free(description);
489
208
    return OGS_OK;
490
208
}
491
492
char *ogs_ipfw_encode_flow_description(ogs_ipfw_rule_t *ipfw_rule)
493
0
{
494
0
    char flow_description[OGS_HUGE_LEN];
495
0
    char *p, *last;
496
0
    char buf[OGS_ADDRSTRLEN];
497
0
    ogs_sockaddr_t sa;
498
0
    int prefixlen = 0;
499
500
0
    p = flow_description;
501
0
    last = flow_description + OGS_HUGE_LEN;
502
503
0
    ogs_assert(ipfw_rule);
504
505
0
    p = ogs_slprintf(p, last, "permit out");
506
507
0
    if (ipfw_rule->proto) {
508
0
        p = ogs_slprintf(p, last, " %d", ipfw_rule->proto);
509
0
    } else {
510
0
        p = ogs_slprintf(p, last, " ip");
511
0
    }
512
513
0
#define IPV4_BITLEN    (OGS_IPV4_LEN * 8)
514
0
#define IPV6_BITLEN    (OGS_IPV6_LEN * 8)
515
516
0
    p = ogs_slprintf(p, last, " from");
517
0
    memset(&sa, 0, sizeof(sa));
518
519
0
    if (ipfw_rule->ipv4_src) {
520
0
        sa.ogs_sa_family = AF_INET;
521
0
        memcpy(&sa.sin.sin_addr,
522
0
                ipfw_rule->ip.src.addr, sizeof(struct in_addr));
523
524
0
        OGS_ADDR(&sa, buf);
525
0
        prefixlen = contigmask(
526
0
                (uint8_t *)ipfw_rule->ip.src.mask, IPV4_BITLEN);
527
528
0
        if (prefixlen < 0) {
529
0
            ogs_error("Invalid mask[%x:%x:%x:%x]",
530
0
                    ipfw_rule->ip.src.mask[0],
531
0
                    ipfw_rule->ip.src.mask[1],
532
0
                    ipfw_rule->ip.src.mask[2],
533
0
                    ipfw_rule->ip.src.mask[3]);
534
0
            return NULL;
535
0
        } else if (prefixlen == 0) {
536
0
            p = ogs_slprintf(p, last, " any");
537
0
        } else if (prefixlen > 0 && prefixlen < IPV4_BITLEN) {
538
0
            p = ogs_slprintf(p, last, " %s/%d", buf, prefixlen);
539
0
        } else if (prefixlen == IPV4_BITLEN) {
540
0
            p = ogs_slprintf(p, last, " %s", buf);
541
0
        } else {
542
0
            ogs_fatal("Invalid prefixlen[%d]", prefixlen);
543
0
            ogs_assert_if_reached();
544
0
        }
545
546
0
    } else if (ipfw_rule->ipv6_src) {
547
0
        sa.ogs_sa_family = AF_INET6;
548
0
        memcpy(&sa.sin6.sin6_addr,
549
0
                ipfw_rule->ip.src.addr, sizeof(struct in6_addr));
550
551
0
        OGS_ADDR(&sa, buf);
552
0
        prefixlen = contigmask(
553
0
                (uint8_t *)ipfw_rule->ip.src.mask, IPV6_BITLEN);
554
555
0
        if (prefixlen < 0) {
556
0
            ogs_error("Invalid mask[%x:%x:%x:%x]",
557
0
                    ipfw_rule->ip.src.mask[0],
558
0
                    ipfw_rule->ip.src.mask[1],
559
0
                    ipfw_rule->ip.src.mask[2],
560
0
                    ipfw_rule->ip.src.mask[3]);
561
0
            return NULL;
562
0
        } else if (prefixlen == 0) {
563
0
            p = ogs_slprintf(p, last, " any");
564
0
        } else if (prefixlen > 0 && prefixlen < IPV6_BITLEN) {
565
0
            p = ogs_slprintf(p, last, " %s/%d", buf, prefixlen);
566
0
        } else if (prefixlen == IPV6_BITLEN) {
567
0
            p = ogs_slprintf(p, last, " %s", buf);
568
0
        } else {
569
0
            ogs_fatal("Invalid prefixlen[%d]", prefixlen);
570
0
            ogs_assert_if_reached();
571
0
        }
572
0
    } else
573
0
        p = ogs_slprintf(p, last, " any");
574
575
0
    if (ipfw_rule->port.src.low == ipfw_rule->port.src.high) {
576
0
        if (ipfw_rule->port.src.low == 0) {
577
            /* Nothing */
578
0
        } else {
579
0
            p = ogs_slprintf(p, last, " %d", ipfw_rule->port.src.low);
580
0
        }
581
0
    } else {
582
0
        p = ogs_slprintf(p, last, " %d-%d",
583
0
                ipfw_rule->port.src.low, ipfw_rule->port.src.high);
584
0
    }
585
586
0
    p = ogs_slprintf(p, last, " to");
587
0
    memset(&sa, 0, sizeof(sa));
588
589
0
    if (ipfw_rule->ipv4_dst) {
590
0
        sa.ogs_sa_family = AF_INET;
591
0
        memcpy(&sa.sin.sin_addr,
592
0
                ipfw_rule->ip.dst.addr, sizeof(struct in_addr));
593
594
0
        OGS_ADDR(&sa, buf);
595
0
        prefixlen = contigmask(
596
0
                (uint8_t *)ipfw_rule->ip.dst.mask, IPV4_BITLEN);
597
598
0
        if (prefixlen < 0) {
599
0
            ogs_error("Invalid mask[%x:%x:%x:%x]",
600
0
                    ipfw_rule->ip.dst.mask[0],
601
0
                    ipfw_rule->ip.dst.mask[1],
602
0
                    ipfw_rule->ip.dst.mask[2],
603
0
                    ipfw_rule->ip.dst.mask[3]);
604
0
            return NULL;
605
0
        } else if (prefixlen == 0) {
606
0
            p = ogs_slprintf(p, last, " assigned");
607
0
        } else if (prefixlen > 0 && prefixlen < IPV4_BITLEN) {
608
0
            p = ogs_slprintf(p, last, " %s/%d", buf, prefixlen);
609
0
        } else if (prefixlen == IPV4_BITLEN) {
610
0
            p = ogs_slprintf(p, last, " %s", buf);
611
0
        } else {
612
0
            ogs_fatal("Invalid prefixlen[%d]", prefixlen);
613
0
            ogs_assert_if_reached();
614
0
        }
615
616
0
    } else if (ipfw_rule->ipv6_dst) {
617
0
        sa.ogs_sa_family = AF_INET6;
618
0
        memcpy(&sa.sin6.sin6_addr,
619
0
                ipfw_rule->ip.dst.addr, sizeof(struct in6_addr));
620
621
0
        OGS_ADDR(&sa, buf);
622
0
        prefixlen = contigmask(
623
0
                (uint8_t *)ipfw_rule->ip.dst.mask, IPV6_BITLEN);
624
625
0
        if (prefixlen < 0) {
626
0
            ogs_error("Invalid mask[%x:%x:%x:%x]",
627
0
                    ipfw_rule->ip.dst.mask[0],
628
0
                    ipfw_rule->ip.dst.mask[1],
629
0
                    ipfw_rule->ip.dst.mask[2],
630
0
                    ipfw_rule->ip.dst.mask[3]);
631
0
            return NULL;
632
0
        } else if (prefixlen == 0) {
633
0
            p = ogs_slprintf(p, last, " assigned");
634
0
        } else if (prefixlen > 0 && prefixlen < IPV6_BITLEN) {
635
0
            p = ogs_slprintf(p, last, " %s/%d", buf, prefixlen);
636
0
        } else if (prefixlen == IPV6_BITLEN) {
637
0
            p = ogs_slprintf(p, last, " %s", buf);
638
0
        } else {
639
0
            ogs_fatal("Invalid prefixlen[%d]", prefixlen);
640
0
            ogs_assert_if_reached();
641
0
        }
642
0
    } else
643
0
        p = ogs_slprintf(p, last, " assigned");
644
645
0
    if (ipfw_rule->port.dst.low == ipfw_rule->port.dst.high) {
646
0
        if (ipfw_rule->port.dst.low == 0) {
647
            /* Nothing */
648
0
        } else {
649
0
            p = ogs_slprintf(p, last, " %d", ipfw_rule->port.dst.low);
650
0
        }
651
0
    } else {
652
0
        p = ogs_slprintf(p, last, " %d-%d",
653
0
                ipfw_rule->port.dst.low, ipfw_rule->port.dst.high);
654
0
    }
655
656
0
    return ogs_strdup(flow_description);
657
0
}
658
659
ogs_ipfw_rule_t *ogs_ipfw_copy_and_swap(
660
        ogs_ipfw_rule_t *dst, ogs_ipfw_rule_t *src)
661
0
{
662
0
    ogs_assert(src);
663
0
    ogs_assert(dst);
664
0
    ogs_assert(src != dst);
665
666
0
    memcpy(dst, src, sizeof(ogs_ipfw_rule_t));
667
668
0
    dst->ipv4_src = src->ipv4_dst;
669
0
    dst->ipv4_dst = src->ipv4_src;
670
0
    dst->ipv6_src = src->ipv6_dst;
671
0
    dst->ipv6_dst = src->ipv6_src;
672
673
0
    memcpy(&dst->ip.src, &src->ip.dst, sizeof(dst->ip.src));
674
0
    memcpy(&dst->ip.dst, &src->ip.src, sizeof(dst->ip.dst));
675
0
    memcpy(&dst->port.src, &src->port.dst, sizeof(dst->port.src));
676
0
    memcpy(&dst->port.dst, &src->port.src, sizeof(dst->port.dst));
677
678
0
    return dst;
679
0
}
680
681
void ogs_ipfw_rule_swap(ogs_ipfw_rule_t *ipfw_rule)
682
0
{
683
0
    ogs_ipfw_rule_t dst;
684
685
0
    ogs_assert(ipfw_rule);
686
687
0
    ogs_ipfw_copy_and_swap(&dst, ipfw_rule);
688
0
    memcpy(ipfw_rule, &dst, sizeof(ogs_ipfw_rule_t));
689
0
}
690
691
void ogs_pf_content_from_ipfw_rule(
692
        uint8_t direction, ogs_pf_content_t *content, ogs_ipfw_rule_t *rule,
693
        bool no_ipv4v6_local_addr_in_packet_filter)
694
0
{
695
0
    int j, len;
696
697
0
    ogs_assert(content);
698
0
    ogs_assert(rule);
699
700
0
    j = 0, len = 0;
701
0
    if (rule->proto) {
702
0
        content->component[j].type =
703
0
            OGS_PACKET_FILTER_PROTOCOL_IDENTIFIER_NEXT_HEADER_TYPE;
704
0
        content->component[j].proto = rule->proto;
705
0
        j++; len += 2;
706
0
    }
707
708
    /*
709
     * As per 3GPP TS 24.008, following Packet filter component type identifier
710
     * are not supported on the LTE pre release-11 UEs:
711
     *
712
     * IPv4 local address type
713
     * IPv6 remote address/prefix length type
714
     * IPv6 local address/prefix length type
715
     *
716
     * And,
717
     * IPv6 remote address/prefix length type and
718
     * IPv6 local address/prefix length type shall be used when both MS and
719
     * Network support Local Address in TFTs.
720
     */
721
722
0
    if (rule->ipv4_src) {
723
0
        switch (direction) {
724
0
        case OGS_FLOW_DOWNLINK_ONLY:
725
0
        case OGS_FLOW_BIDIRECTIONAL:
726
0
            content->component[j].type =
727
0
                OGS_PACKET_FILTER_IPV4_REMOTE_ADDRESS_TYPE;
728
0
            content->component[j].ipv4.addr = rule->ip.src.addr[0];
729
0
            content->component[j].ipv4.mask = rule->ip.src.mask[0];
730
0
            j++; len += 9;
731
0
            break;
732
0
        case OGS_FLOW_UPLINK_ONLY:
733
0
            if (!no_ipv4v6_local_addr_in_packet_filter) {
734
0
                content->component[j].type =
735
0
                    OGS_PACKET_FILTER_IPV4_LOCAL_ADDRESS_TYPE;
736
0
                content->component[j].ipv4.addr = rule->ip.src.addr[0];
737
0
                content->component[j].ipv4.mask = rule->ip.src.mask[0];
738
0
                j++; len += 9;
739
0
            }
740
0
            break;
741
0
        default:
742
0
            ogs_fatal("Unsupported direction [%d]", direction);
743
0
            ogs_assert_if_reached();
744
0
        }
745
0
    }
746
747
0
    if (rule->ipv4_dst) {
748
0
        switch (direction) {
749
0
        case OGS_FLOW_DOWNLINK_ONLY:
750
0
        case OGS_FLOW_BIDIRECTIONAL:
751
0
            if (!no_ipv4v6_local_addr_in_packet_filter) {
752
0
                content->component[j].type =
753
0
                    OGS_PACKET_FILTER_IPV4_LOCAL_ADDRESS_TYPE;
754
0
                content->component[j].ipv4.addr = rule->ip.dst.addr[0];
755
0
                content->component[j].ipv4.mask = rule->ip.dst.mask[0];
756
0
                j++; len += 9;
757
0
            }
758
0
            break;
759
0
        case OGS_FLOW_UPLINK_ONLY:
760
0
            content->component[j].type =
761
0
                OGS_PACKET_FILTER_IPV4_REMOTE_ADDRESS_TYPE;
762
0
            content->component[j].ipv4.addr = rule->ip.dst.addr[0];
763
0
            content->component[j].ipv4.mask = rule->ip.dst.mask[0];
764
0
            j++; len += 9;
765
0
            break;
766
0
        default:
767
0
            ogs_fatal("Unsupported direction [%d]", direction);
768
0
            ogs_assert_if_reached();
769
0
        }
770
0
    }
771
772
0
    if (rule->ipv6_src) {
773
0
        switch (direction) {
774
0
        case OGS_FLOW_DOWNLINK_ONLY:
775
0
        case OGS_FLOW_BIDIRECTIONAL:
776
0
            if (no_ipv4v6_local_addr_in_packet_filter) {
777
0
                content->component[j].type =
778
0
                    OGS_PACKET_FILTER_IPV6_REMOTE_ADDRESS_TYPE;
779
0
                memcpy(content->component[j].ipv6_mask.addr,
780
0
                    rule->ip.src.addr, sizeof rule->ip.src.addr);
781
0
                memcpy(content->component[j].ipv6_mask.mask,
782
0
                        rule->ip.src.mask, sizeof rule->ip.src.mask);
783
0
                j++; len += 33;
784
0
            } else {
785
0
                content->component[j].type =
786
0
                    OGS_PACKET_FILTER_IPV6_REMOTE_ADDRESS_PREFIX_LENGTH_TYPE;
787
0
                memcpy(content->component[j].ipv6.addr,
788
0
                    rule->ip.src.addr, sizeof rule->ip.src.addr);
789
0
                content->component[j].ipv6.prefixlen =
790
0
                    contigmask((uint8_t *)rule->ip.src.mask, 128);
791
0
                j++; len += 18;
792
0
            }
793
0
            break;
794
0
        case OGS_FLOW_UPLINK_ONLY:
795
0
            if (!no_ipv4v6_local_addr_in_packet_filter) {
796
0
                content->component[j].type =
797
0
                    OGS_PACKET_FILTER_IPV6_LOCAL_ADDRESS_PREFIX_LENGTH_TYPE;
798
0
                memcpy(content->component[j].ipv6.addr,
799
0
                        rule->ip.src.addr, sizeof rule->ip.src.addr);
800
0
                content->component[j].ipv6.prefixlen =
801
0
                    contigmask((uint8_t *)rule->ip.src.mask, 128);
802
0
                j++; len += 18;
803
0
            }
804
0
            break;
805
0
        default:
806
0
            ogs_fatal("Unsupported direction [%d]", direction);
807
0
            ogs_assert_if_reached();
808
0
        }
809
0
    }
810
811
0
    if (rule->ipv6_dst) {
812
0
        switch (direction) {
813
0
        case OGS_FLOW_DOWNLINK_ONLY:
814
0
        case OGS_FLOW_BIDIRECTIONAL:
815
0
            if (!no_ipv4v6_local_addr_in_packet_filter) {
816
0
                content->component[j].type =
817
0
                    OGS_PACKET_FILTER_IPV6_LOCAL_ADDRESS_PREFIX_LENGTH_TYPE;
818
0
                memcpy(content->component[j].ipv6.addr,
819
0
                    rule->ip.dst.addr, sizeof rule->ip.dst.addr);
820
0
                content->component[j].ipv6.prefixlen =
821
0
                    contigmask((uint8_t *)rule->ip.dst.mask, 128);
822
0
                j++; len += 18;
823
0
            }
824
0
            break;
825
0
        case OGS_FLOW_UPLINK_ONLY:
826
0
            if (no_ipv4v6_local_addr_in_packet_filter) {
827
0
                content->component[j].type =
828
0
                        OGS_PACKET_FILTER_IPV6_REMOTE_ADDRESS_TYPE;
829
0
                memcpy(content->component[j].ipv6_mask.addr,
830
0
                        rule->ip.dst.addr, sizeof rule->ip.dst.addr);
831
0
                memcpy(content->component[j].ipv6_mask.mask,
832
0
                        rule->ip.dst.mask, sizeof rule->ip.dst.mask);
833
0
                j++; len += 33;
834
0
            } else {
835
0
                content->component[j].type =
836
0
                    OGS_PACKET_FILTER_IPV6_REMOTE_ADDRESS_PREFIX_LENGTH_TYPE;
837
0
                memcpy(content->component[j].ipv6.addr,
838
0
                        rule->ip.dst.addr, sizeof rule->ip.dst.addr);
839
0
                content->component[j].ipv6.prefixlen =
840
0
                    contigmask((uint8_t *)rule->ip.dst.mask, 128);
841
0
                j++; len += 18;
842
0
            }
843
0
            break;
844
0
        default:
845
0
            ogs_fatal("Unsupported direction [%d]", direction);
846
0
            ogs_assert_if_reached();
847
0
        }
848
0
    }
849
850
0
    if (rule->port.src.low) {
851
0
        if (rule->port.src.low == rule->port.src.high) {
852
0
            switch (direction) {
853
0
            case OGS_FLOW_DOWNLINK_ONLY:
854
0
            case OGS_FLOW_BIDIRECTIONAL:
855
0
                content->component[j].type =
856
0
                    OGS_PACKET_FILTER_SINGLE_REMOTE_PORT_TYPE;
857
0
                break;
858
0
            case OGS_FLOW_UPLINK_ONLY:
859
0
                content->component[j].type =
860
0
                    OGS_PACKET_FILTER_SINGLE_LOCAL_PORT_TYPE;
861
0
                break;
862
0
            default:
863
0
                ogs_fatal("Unsupported direction [%d]", direction);
864
0
                ogs_assert_if_reached();
865
0
            }
866
0
            content->component[j].port.low = rule->port.src.low;
867
0
            j++; len += 3;
868
0
        } else {
869
0
            switch (direction) {
870
0
            case OGS_FLOW_DOWNLINK_ONLY:
871
0
            case OGS_FLOW_BIDIRECTIONAL:
872
0
                content->component[j].type =
873
0
                    OGS_PACKET_FILTER_REMOTE_PORT_RANGE_TYPE;
874
0
                break;
875
0
            case OGS_FLOW_UPLINK_ONLY:
876
0
                content->component[j].type =
877
0
                    OGS_PACKET_FILTER_LOCAL_PORT_RANGE_TYPE;
878
0
                break;
879
0
            default:
880
0
                ogs_fatal("Unsupported direction [%d]", direction);
881
0
                ogs_assert_if_reached();
882
0
            }
883
0
            content->component[j].port.low = rule->port.src.low;
884
0
            content->component[j].port.high = rule->port.src.high;
885
0
            j++; len += 5;
886
0
        }
887
0
    }
888
889
0
    if (rule->port.dst.low) {
890
0
        if (rule->port.dst.low == rule->port.dst.high) {
891
0
            switch (direction) {
892
0
            case OGS_FLOW_DOWNLINK_ONLY:
893
0
            case OGS_FLOW_BIDIRECTIONAL:
894
0
                content->component[j].type =
895
0
                    OGS_PACKET_FILTER_SINGLE_LOCAL_PORT_TYPE;
896
0
                break;
897
0
            case OGS_FLOW_UPLINK_ONLY:
898
0
                content->component[j].type =
899
0
                    OGS_PACKET_FILTER_SINGLE_REMOTE_PORT_TYPE;
900
0
                break;
901
0
            default:
902
0
                ogs_fatal("Unsupported direction [%d]", direction);
903
0
                ogs_assert_if_reached();
904
0
            }
905
0
            content->component[j].port.low = rule->port.dst.low;
906
0
            j++; len += 3;
907
0
        } else {
908
0
            switch (direction) {
909
0
            case OGS_FLOW_DOWNLINK_ONLY:
910
0
            case OGS_FLOW_BIDIRECTIONAL:
911
0
                content->component[j].type =
912
0
                    OGS_PACKET_FILTER_LOCAL_PORT_RANGE_TYPE;
913
0
                break;
914
0
            case OGS_FLOW_UPLINK_ONLY:
915
0
                content->component[j].type =
916
0
                    OGS_PACKET_FILTER_REMOTE_PORT_RANGE_TYPE;
917
0
                break;
918
0
            default:
919
0
                ogs_fatal("Unsupported direction [%d]", direction);
920
0
                ogs_assert_if_reached();
921
0
            }
922
0
            content->component[j].port.low = rule->port.dst.low;
923
0
            content->component[j].port.high = rule->port.dst.high;
924
0
            j++; len += 5;
925
0
        }
926
0
    }
927
928
0
    content->num_of_component = j;
929
0
    content->length = len;
930
0
}