Coverage Report

Created: 2026-01-17 06:55

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openvswitch/lib/ovs-router.c
Line
Count
Source
1
/*
2
 * Copyright (c) 2014, 2015, 2016, 2017 Nicira, Inc.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at:
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
17
#include <config.h>
18
19
#include "ovs-router.h"
20
21
#include <sys/types.h>
22
#include <netinet/in.h>
23
#include <arpa/inet.h>
24
#include <errno.h>
25
#include <inttypes.h>
26
#include <sys/socket.h>
27
#include <net/if.h>
28
#include <stdarg.h>
29
#include <stdlib.h>
30
#include <string.h>
31
#include <unistd.h>
32
33
#include "classifier.h"
34
#include "command-line.h"
35
#include "compiler.h"
36
#include "cmap.h"
37
#include "dpif.h"
38
#include "fatal-signal.h"
39
#include "openvswitch/dynamic-string.h"
40
#include "openvswitch/json.h"
41
#include "netdev.h"
42
#include "packets.h"
43
#include "seq.h"
44
#include "ovs-thread.h"
45
#include "route-table.h"
46
#include "pvector.h"
47
#include "tnl-ports.h"
48
#include "unixctl.h"
49
#include "util.h"
50
#include "unaligned.h"
51
#include "openvswitch/vlog.h"
52
53
VLOG_DEFINE_THIS_MODULE(ovs_router);
54
55
struct clsmap_node {
56
    struct cmap_node cmap_node;
57
    uint32_t table;
58
    struct classifier cls;
59
};
60
61
struct router_rule {
62
    uint32_t prio;
63
    bool invert;
64
    bool ipv4;
65
    bool user;
66
    uint8_t src_prefix;
67
    struct in6_addr from_addr;
68
    uint32_t lookup_table;
69
};
70
71
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
72
73
static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER;
74
static struct cmap clsmap = CMAP_INITIALIZER;
75
static struct pvector rules;
76
77
/* By default, use the system routing table.  For system-independent testing,
78
 * the unit tests disable using the system routing table. */
79
static bool use_system_routing_table = true;
80
81
struct ovs_router_entry {
82
    struct cls_rule cr;
83
    char output_netdev[IFNAMSIZ];
84
    struct in6_addr gw;
85
    struct in6_addr nw_addr;
86
    struct in6_addr src_addr;
87
    uint8_t plen;
88
    uint8_t priority;
89
    bool user;
90
    uint32_t mark;
91
};
92
93
static void rt_entry_delete__(const struct cls_rule *, struct classifier *);
94
95
static struct classifier *
96
cls_find(uint32_t table)
97
0
{
98
0
    struct clsmap_node *node;
99
100
0
    CMAP_FOR_EACH_WITH_HASH (node, cmap_node, hash_int(table, 0), &clsmap) {
101
0
        if (node->table == table) {
102
0
            return &node->cls;
103
0
        }
104
0
    }
105
106
0
    return NULL;
107
0
}
108
109
static struct classifier *
110
cls_create(uint32_t table)
111
    OVS_REQUIRES(mutex)
112
0
{
113
0
    struct clsmap_node *node;
114
115
0
    node = xmalloc(sizeof *node);
116
0
    classifier_init(&node->cls, NULL);
117
0
    node->table = table;
118
0
    cmap_insert(&clsmap, &node->cmap_node, hash_int(table, 0));
119
120
0
    return &node->cls;
121
0
}
122
123
static void
124
cls_flush(struct classifier *cls, bool flush_all)
125
    OVS_REQUIRES(mutex)
126
0
{
127
0
    struct ovs_router_entry *rt;
128
129
0
    classifier_defer(cls);
130
0
    CLS_FOR_EACH (rt, cr, cls) {
131
0
        if (flush_all || !rt->user) {
132
0
            rt_entry_delete__(&rt->cr, cls);
133
0
        }
134
0
    }
135
0
    classifier_publish(cls);
136
0
}
137
138
static struct ovs_router_entry *
139
ovs_router_entry_cast(const struct cls_rule *cr)
140
0
{
141
0
    return cr ? CONTAINER_OF(cr, struct ovs_router_entry, cr) : NULL;
142
0
}
143
144
/* Disables obtaining routes from the system routing table, for testing
145
 * purposes. */
146
void
147
ovs_router_disable_system_routing_table(void)
148
0
{
149
0
    use_system_routing_table = false;
150
0
}
151
152
static bool
153
ovs_router_lookup_fallback(const struct in6_addr *ip6_dst,
154
                           char output_netdev[], struct in6_addr *src6,
155
                           struct in6_addr *gw6)
156
0
{
157
0
    ovs_be32 src;
158
159
0
    if (!use_system_routing_table
160
0
        || !route_table_fallback_lookup(ip6_dst, output_netdev, gw6)) {
161
0
        return false;
162
0
    }
163
0
    if (netdev_get_in4_by_name(output_netdev, (struct in_addr *)&src)) {
164
0
        return false;
165
0
    }
166
0
    if (src6) {
167
0
        in6_addr_set_mapped_ipv4(src6, src);
168
0
    }
169
0
    return true;
170
0
}
171
172
bool
173
ovs_router_lookup(uint32_t mark, const struct in6_addr *ip6_dst,
174
                  char output_netdev[],
175
                  struct in6_addr *src, struct in6_addr *gw)
176
0
{
177
0
    struct flow flow = {.ipv6_dst = *ip6_dst, .pkt_mark = mark};
178
0
    const struct in6_addr *from_src = src;
179
0
    const struct cls_rule *cr = NULL;
180
0
    struct router_rule *rule;
181
182
0
    if (src && ipv6_addr_is_set(src)) {
183
0
        struct flow flow_src = {.ipv6_dst = *src, .pkt_mark = mark};
184
0
        struct classifier *cls_local = cls_find(CLS_LOCAL);
185
0
        const struct cls_rule *cr_src;
186
187
0
        if (!cls_local) {
188
0
            return false;
189
0
        }
190
191
0
        cr_src = classifier_lookup(cls_local, OVS_VERSION_MAX, &flow_src,
192
0
                                   NULL, NULL);
193
0
        if (!cr_src) {
194
0
            return false;
195
0
        }
196
0
    }
197
198
0
    if (!from_src) {
199
0
        if (IN6_IS_ADDR_V4MAPPED(ip6_dst)) {
200
0
            from_src = &in6addr_v4mapped_any;
201
0
        } else {
202
0
            from_src = &in6addr_any;
203
0
        }
204
0
    }
205
206
0
    PVECTOR_FOR_EACH (rule, &rules) {
207
0
        uint8_t plen = rule->ipv4 ? rule->src_prefix + 96 : rule->src_prefix;
208
0
        bool matched;
209
210
0
        if ((IN6_IS_ADDR_V4MAPPED(from_src) && !rule->ipv4) ||
211
0
            (!IN6_IS_ADDR_V4MAPPED(from_src) && rule->ipv4)) {
212
0
            continue;
213
0
        }
214
215
0
        matched = (!rule->src_prefix ||
216
0
                   ipv6_addr_equals_masked(&rule->from_addr, from_src, plen));
217
218
0
        if (rule->invert) {
219
0
            matched = !matched;
220
0
        }
221
222
0
        if (matched) {
223
0
            struct classifier *cls = cls_find(rule->lookup_table);
224
225
0
            if (!cls) {
226
                /* A rule can be added before the table is created. */
227
0
                continue;
228
0
            }
229
0
            cr = classifier_lookup(cls, OVS_VERSION_MAX, &flow, NULL,
230
0
                                   NULL);
231
0
            if (cr) {
232
0
                struct ovs_router_entry *p = ovs_router_entry_cast(cr);
233
                /* Avoid matching mapped IPv4 of a packet against default IPv6
234
                 * route entry.  Either packet dst is IPv6 or both packet and
235
                 * route entry dst are mapped IPv4.
236
                 */
237
0
                if (!IN6_IS_ADDR_V4MAPPED(ip6_dst) ||
238
0
                    IN6_IS_ADDR_V4MAPPED(&p->nw_addr)) {
239
0
                    break;
240
0
                }
241
0
            }
242
0
        }
243
0
    }
244
245
0
    if (cr) {
246
0
        struct ovs_router_entry *p = ovs_router_entry_cast(cr);
247
248
0
        ovs_strlcpy(output_netdev, p->output_netdev, IFNAMSIZ);
249
0
        *gw = p->gw;
250
0
        if (src && !ipv6_addr_is_set(src)) {
251
0
            *src = p->src_addr;
252
0
        }
253
0
        return true;
254
0
    }
255
0
    return ovs_router_lookup_fallback(ip6_dst, output_netdev, src, gw);
256
0
}
257
258
static void
259
rt_entry_free(struct ovs_router_entry *p)
260
0
{
261
0
    cls_rule_destroy(&p->cr);
262
0
    free(p);
263
0
}
264
265
static void rt_init_match(struct match *match, uint32_t mark,
266
                          const struct in6_addr *ip6_dst,
267
                          uint8_t plen)
268
0
{
269
0
    struct in6_addr dst;
270
0
    struct in6_addr mask;
271
272
0
    mask = ipv6_create_mask(plen);
273
274
0
    dst = ipv6_addr_bitand(ip6_dst, &mask);
275
0
    memset(match, 0, sizeof *match);
276
0
    match->flow.ipv6_dst = dst;
277
0
    match->wc.masks.ipv6_dst = mask;
278
0
    match->wc.masks.pkt_mark = UINT32_MAX;
279
0
    match->flow.pkt_mark = mark;
280
0
}
281
282
static int
283
verify_prefsrc(const struct in6_addr *ip6_dst,
284
               const char netdev_name[],
285
               struct in6_addr *prefsrc)
286
0
{
287
0
    struct in6_addr *mask, *addr6;
288
0
    struct netdev *dev;
289
0
    int err, n_in6, i;
290
291
0
    err = netdev_open(netdev_name, NULL, &dev);
292
0
    if (err) {
293
0
        return err;
294
0
    }
295
296
0
    err = netdev_get_addr_list(dev, &addr6, &mask, &n_in6);
297
0
    if (err) {
298
0
        goto out;
299
0
    }
300
301
0
    for (i = 0; i < n_in6; i++) {
302
0
        struct in6_addr a1, a2;
303
0
        a1 = ipv6_addr_bitand(ip6_dst, &mask[i]);
304
0
        a2 = ipv6_addr_bitand(prefsrc, &mask[i]);
305
306
        /* Check that the interface has "prefsrc" and
307
         * it is same broadcast domain with "ip6_dst". */
308
0
        if (IN6_ARE_ADDR_EQUAL(prefsrc, &addr6[i]) &&
309
0
            IN6_ARE_ADDR_EQUAL(&a1, &a2)) {
310
0
            goto out;
311
0
        }
312
0
    }
313
0
    err = ENOENT;
314
315
0
out:
316
0
    free(addr6);
317
0
    free(mask);
318
0
    netdev_close(dev);
319
0
    return err;
320
0
}
321
322
int
323
ovs_router_get_netdev_source_address(const struct in6_addr *ip6_dst,
324
                                     const char netdev_name[],
325
                                     struct in6_addr *psrc)
326
0
{
327
0
    struct in6_addr *mask, *addr6;
328
0
    int err, n_in6, i, max_plen = -1;
329
0
    struct netdev *dev;
330
0
    bool is_ipv4;
331
332
0
    err = netdev_open(netdev_name, NULL, &dev);
333
0
    if (err) {
334
0
        return err;
335
0
    }
336
337
0
    err = netdev_get_addr_list(dev, &addr6, &mask, &n_in6);
338
0
    if (err) {
339
0
        goto out;
340
0
    }
341
342
0
    is_ipv4 = IN6_IS_ADDR_V4MAPPED(ip6_dst);
343
344
0
    for (i = 0; i < n_in6; i++) {
345
0
        struct in6_addr a1, a2;
346
0
        int mask_bits;
347
348
0
        if (is_ipv4 && !IN6_IS_ADDR_V4MAPPED(&addr6[i])) {
349
0
            continue;
350
0
        }
351
352
0
        a1 = ipv6_addr_bitand(ip6_dst, &mask[i]);
353
0
        a2 = ipv6_addr_bitand(&addr6[i], &mask[i]);
354
0
        mask_bits = bitmap_count1(ALIGNED_CAST(const unsigned long *, &mask[i]), 128);
355
356
0
        if (!memcmp(&a1, &a2, sizeof (a1)) && mask_bits > max_plen) {
357
0
            *psrc = addr6[i];
358
0
            max_plen = mask_bits;
359
0
        }
360
0
    }
361
0
    if (max_plen == -1) {
362
0
        err = ENOENT;
363
0
    }
364
0
out:
365
0
    free(addr6);
366
0
    free(mask);
367
0
    netdev_close(dev);
368
0
    return err;
369
0
}
370
371
static int
372
ovs_router_insert__(uint32_t table, uint32_t mark, uint8_t priority,
373
                    bool user, const struct in6_addr *ip6_dst,
374
                    uint8_t plen, const char output_netdev[],
375
                    const struct in6_addr *gw,
376
                    const struct in6_addr *ip6_src)
377
0
{
378
0
    int (*get_src_addr)(const struct in6_addr *ip6_dst,
379
0
                        const char output_netdev[],
380
0
                        struct in6_addr *prefsrc);
381
0
    const struct cls_rule *cr;
382
0
    struct ovs_router_entry *p;
383
0
    struct classifier *cls;
384
0
    struct match match;
385
0
    int err;
386
387
0
    rt_init_match(&match, mark, ip6_dst, plen);
388
389
0
    p = xzalloc(sizeof *p);
390
0
    ovs_strlcpy(p->output_netdev, output_netdev, sizeof p->output_netdev);
391
0
    if (ipv6_addr_is_set(gw)) {
392
0
        p->gw = *gw;
393
0
    }
394
0
    p->mark = mark;
395
0
    p->nw_addr = match.flow.ipv6_dst;
396
0
    p->plen = plen;
397
0
    p->user = user;
398
0
    p->priority = priority;
399
400
0
    if (ipv6_addr_is_set(ip6_src)) {
401
0
        p->src_addr = *ip6_src;
402
0
        get_src_addr = verify_prefsrc;
403
0
    } else {
404
0
        get_src_addr = ovs_router_get_netdev_source_address;
405
0
    }
406
407
0
    err = get_src_addr(ip6_dst, output_netdev, &p->src_addr);
408
0
    if (err && ipv6_addr_is_set(gw)) {
409
0
        err = get_src_addr(gw, output_netdev, &p->src_addr);
410
0
    }
411
0
    if (err) {
412
0
        struct ds ds = DS_EMPTY_INITIALIZER;
413
414
0
        ipv6_format_mapped(ip6_dst, &ds);
415
0
        VLOG_DBG_RL(&rl, "src addr not available for route %s", ds_cstr(&ds));
416
0
        free(p);
417
0
        ds_destroy(&ds);
418
0
        return err;
419
0
    }
420
    /* Longest prefix matches first. */
421
0
    cls_rule_init(&p->cr, &match, priority);
422
423
0
    ovs_mutex_lock(&mutex);
424
0
    cls = cls_find(table);
425
0
    if (!cls) {
426
0
        cls = cls_create(table);
427
0
    }
428
0
    cr = classifier_replace(cls, &p->cr, OVS_VERSION_MIN, NULL, 0);
429
0
    ovs_mutex_unlock(&mutex);
430
431
0
    if (cr) {
432
        /* An old rule with the same match was displaced. */
433
0
        ovsrcu_postpone(rt_entry_free, ovs_router_entry_cast(cr));
434
0
    }
435
0
    tnl_port_map_insert_ipdev(output_netdev);
436
0
    seq_change(tnl_conf_seq);
437
0
    return 0;
438
0
}
439
440
void
441
ovs_router_insert(uint32_t table, uint32_t mark, const struct in6_addr *ip_dst,
442
                  uint8_t plen, bool user, const char output_netdev[],
443
                  const struct in6_addr *gw, const struct in6_addr *prefsrc)
444
0
{
445
0
    if (use_system_routing_table) {
446
0
        ovs_router_insert__(table, mark, plen, user, ip_dst, plen,
447
0
                            output_netdev, gw, prefsrc);
448
0
    }
449
0
}
450
451
/* The same as 'ovs_router_insert', but it adds the route even if updates
452
 * from the system routing table are disabled.  Used for unit tests. */
453
void
454
ovs_router_force_insert(uint32_t table, uint32_t mark,
455
                        const struct in6_addr *ip_dst,
456
                        uint8_t plen, const char output_netdev[],
457
                        const struct in6_addr *gw,
458
                        const struct in6_addr *prefsrc)
459
0
{
460
0
    ovs_router_insert__(table, mark, plen, false, ip_dst, plen, output_netdev,
461
0
                        gw, prefsrc);
462
0
}
463
464
static void
465
rt_entry_delete__(const struct cls_rule *cr, struct classifier *cls)
466
0
{
467
0
    struct ovs_router_entry *p = ovs_router_entry_cast(cr);
468
469
0
    tnl_port_map_delete_ipdev(p->output_netdev);
470
0
    classifier_remove_assert(cls, cr);
471
0
    ovsrcu_postpone(rt_entry_free, ovs_router_entry_cast(cr));
472
0
}
473
474
static bool
475
rt_entry_delete(struct classifier *cls, uint32_t mark, uint8_t priority,
476
                const struct in6_addr *ip6_dst, uint8_t plen)
477
0
{
478
0
    struct classifier *cls_main = cls_find(CLS_MAIN);
479
0
    const struct cls_rule *cr;
480
0
    struct cls_rule rule;
481
0
    struct match match;
482
0
    bool res = false;
483
484
0
    if (!cls_main) {
485
0
        return false;
486
0
    }
487
488
0
    rt_init_match(&match, mark, ip6_dst, plen);
489
490
0
    cls_rule_init(&rule, &match, priority);
491
492
    /* Find the exact rule. */
493
0
    cr = classifier_find_rule_exactly(cls, &rule, OVS_VERSION_MAX);
494
0
    if (cr) {
495
0
        ovs_mutex_lock(&mutex);
496
0
        rt_entry_delete__(cr, cls);
497
0
        ovs_mutex_unlock(&mutex);
498
499
0
        res = true;
500
0
    }
501
502
0
    cls_rule_destroy(&rule);
503
0
    return res;
504
0
}
505
506
static bool
507
scan_ipv6_route(const char *s, struct in6_addr *addr, unsigned int *plen)
508
0
{
509
0
    char *error = ipv6_parse_cidr(s, addr, plen);
510
0
    if (error) {
511
0
        free(error);
512
0
        return false;
513
0
    }
514
0
    return true;
515
0
}
516
517
static bool
518
scan_ipv4_route(const char *s, ovs_be32 *addr, unsigned int *plen)
519
0
{
520
0
    char *error = ip_parse_cidr(s, addr, plen);
521
0
    if (error) {
522
0
        free(error);
523
0
        return false;
524
0
    }
525
0
    return true;
526
0
}
527
528
static void
529
ovs_router_add(struct unixctl_conn *conn, int argc,
530
              const char *argv[], void *aux OVS_UNUSED)
531
0
{
532
0
    struct in6_addr src6 = in6addr_any;
533
0
    struct in6_addr gw6 = in6addr_any;
534
0
    char src6_s[IPV6_SCAN_LEN + 1];
535
0
    uint32_t table = CLS_MAIN;
536
0
    struct in6_addr ip6;
537
0
    uint32_t mark = 0;
538
0
    unsigned int plen;
539
0
    ovs_be32 src = 0;
540
0
    ovs_be32 gw = 0;
541
0
    bool is_ipv6;
542
0
    ovs_be32 ip;
543
0
    int err;
544
0
    int i;
545
546
0
    if (scan_ipv4_route(argv[1], &ip, &plen)) {
547
0
        in6_addr_set_mapped_ipv4(&ip6, ip);
548
0
        plen += 96;
549
0
        is_ipv6 = false;
550
0
    } else if (scan_ipv6_route(argv[1], &ip6, &plen)) {
551
0
        is_ipv6 = true;
552
0
    } else {
553
0
        unixctl_command_reply_error(conn,
554
0
                                    "Invalid 'ip/plen' parameter");
555
0
        return;
556
0
    }
557
558
    /* Parse optional parameters. */
559
0
    for (i = 3; i < argc; i++) {
560
0
        if (ovs_scan(argv[i], "pkt_mark=%"SCNu32, &mark)) {
561
0
            continue;
562
0
        }
563
564
0
        if (is_ipv6) {
565
0
            if (ovs_scan(argv[i], "src="IPV6_SCAN_FMT, src6_s) &&
566
0
                ipv6_parse(src6_s, &src6)) {
567
0
                continue;
568
0
            }
569
0
            if (ipv6_parse(argv[i], &gw6)) {
570
0
                continue;
571
0
            }
572
0
        } else {
573
0
            if (ovs_scan(argv[i], "src="IP_SCAN_FMT, IP_SCAN_ARGS(&src))) {
574
0
                continue;
575
0
            }
576
0
            if (ip_parse(argv[i], &gw)) {
577
0
                continue;
578
0
            }
579
0
        }
580
581
0
        if (ovs_scan(argv[i], "table=%"SCNu32, &table)) {
582
0
            continue;
583
0
        } else if (ovs_scan(argv[i], "table=")) {
584
0
            unixctl_command_reply_error(conn, "Invalid table format");
585
0
            return;
586
0
        }
587
588
0
        unixctl_command_reply_error(conn,
589
0
                                    "Invalid pkt_mark, IP gateway or src_ip");
590
0
        return;
591
0
    }
592
593
0
    if (gw) {
594
0
        in6_addr_set_mapped_ipv4(&gw6, gw);
595
0
    }
596
0
    if (src) {
597
0
        in6_addr_set_mapped_ipv4(&src6, src);
598
0
    }
599
600
0
    err = ovs_router_insert__(table, mark, plen + 32, true, &ip6, plen,
601
0
                              argv[2], &gw6, &src6);
602
0
    if (err) {
603
0
        unixctl_command_reply_error(conn, "Error while inserting route.");
604
0
    } else {
605
0
        unixctl_command_reply(conn, "OK");
606
0
    }
607
0
}
608
609
static void
610
ovs_router_del(struct unixctl_conn *conn, int argc OVS_UNUSED,
611
              const char *argv[], void *aux OVS_UNUSED)
612
0
{
613
0
    struct classifier *cls = cls_find(CLS_MAIN);
614
0
    struct in6_addr ip6;
615
0
    uint32_t mark = 0;
616
0
    unsigned int plen;
617
0
    uint32_t table;
618
0
    ovs_be32 ip;
619
0
    int i;
620
621
0
    if (scan_ipv4_route(argv[1], &ip, &plen)) {
622
0
        in6_addr_set_mapped_ipv4(&ip6, ip);
623
0
        plen += 96;
624
0
    } else if (!scan_ipv6_route(argv[1], &ip6, &plen)) {
625
0
        unixctl_command_reply_error(conn, "Invalid parameters");
626
0
        return;
627
0
    }
628
629
    /* Parse optional parameters. */
630
0
    for (i = 2; i < argc; i++) {
631
0
        if (ovs_scan(argv[i], "pkt_mark=%"SCNu32, &mark)) {
632
0
            continue;
633
0
        }
634
635
0
        if (ovs_scan(argv[i], "table=%"SCNu32, &table)) {
636
0
            cls = cls_find(table);
637
0
            if (!cls) {
638
0
                struct ds ds = DS_EMPTY_INITIALIZER;
639
640
0
                ds_put_format(&ds, "Table %s not found", argv[i]);
641
0
                unixctl_command_reply_error(conn, ds_cstr_ro(&ds));
642
0
                ds_destroy(&ds);
643
0
                return;
644
0
            }
645
0
            continue;
646
0
        } else if (ovs_scan(argv[i], "table=")) {
647
0
            unixctl_command_reply_error(conn, "Invalid table format");
648
0
            return;
649
0
        }
650
651
0
        unixctl_command_reply_error(conn, "Invalid pkt_mark or table");
652
0
        return;
653
0
    }
654
655
0
    if (!cls) {
656
0
        unixctl_command_reply_error(conn, "Table not found");
657
0
        return;
658
0
    }
659
660
0
    if (rt_entry_delete(cls, mark, plen + 32, &ip6, plen)) {
661
0
        unixctl_command_reply(conn, "OK");
662
0
        seq_change(tnl_conf_seq);
663
0
    } else {
664
0
        unixctl_command_reply_error(conn, "Not found");
665
0
    }
666
0
}
667
668
static void
669
ovs_router_show_json(struct json *json_routes, const struct classifier *cls,
670
                     uint32_t table)
671
0
{
672
0
    struct ds ds = DS_EMPTY_INITIALIZER;
673
0
    struct ovs_router_entry *rt;
674
675
0
    if (!cls) {
676
0
        return;
677
0
    }
678
679
0
    CLS_FOR_EACH (rt, cr, cls) {
680
0
        uint8_t plen = rt->plen;
681
0
        struct json *json, *nh;
682
683
0
        json = json_object_create();
684
0
        nh = json_object_create();
685
686
0
        if (IN6_IS_ADDR_V4MAPPED(&rt->nw_addr)) {
687
0
            plen -= 96;
688
0
        }
689
690
0
        json_object_put(json, "table", json_integer_create(table));
691
0
        json_object_put(json, "user", json_boolean_create(rt->user));
692
0
        json_object_put(json, "local",
693
0
                        json_boolean_create(table == CLS_LOCAL && !rt->user));
694
0
        json_object_put(json, "priority", json_integer_create(rt->priority));
695
0
        json_object_put(json, "prefix", json_integer_create(plen));
696
0
        json_object_put_string(nh, "dev", rt->output_netdev);
697
698
0
        ipv6_format_mapped(&rt->nw_addr, &ds);
699
0
        json_object_put_string(json, "dst", ds_cstr_ro(&ds));
700
0
        ds_clear(&ds);
701
702
0
        ipv6_format_mapped(&rt->src_addr, &ds);
703
0
        json_object_put_string(json, "prefsrc", ds_cstr_ro(&ds));
704
0
        ds_clear(&ds);
705
706
0
        if (rt->mark) {
707
0
            json_object_put(json, "mark", json_integer_create(rt->mark));
708
0
        }
709
710
0
        if (ipv6_addr_is_set(&rt->gw)) {
711
0
            ipv6_format_mapped(&rt->gw, &ds);
712
0
            json_object_put_string(nh, "gateway", ds_cstr_ro(&ds));
713
0
            ds_clear(&ds);
714
0
        }
715
716
0
        json_object_put(json, "nexthops", json_array_create_1(nh));
717
0
        json_array_add(json_routes, json);
718
0
    }
719
720
0
    ds_destroy(&ds);
721
0
}
722
723
static bool
724
is_standard_table(uint32_t table_id)
725
0
{
726
0
    return table_id == CLS_DEFAULT
727
0
           || table_id == CLS_MAIN
728
0
           || table_id == CLS_LOCAL;
729
0
}
730
731
static void
732
ovs_router_show_text(struct ds *ds, const struct classifier *cls,
733
                     uint32_t table, bool show_header)
734
0
{
735
0
    struct ovs_router_entry *rt;
736
737
0
    if (show_header) {
738
0
        if (is_standard_table(table)) {
739
0
            ds_put_format(ds, "Route Table:\n");
740
0
        } else {
741
0
            ds_put_format(ds, "Route Table #%"PRIu32":\n", table);
742
0
        }
743
0
    }
744
745
0
    if (!cls) {
746
0
        return;
747
0
    }
748
749
0
    CLS_FOR_EACH (rt, cr, cls) {
750
0
        uint8_t plen;
751
0
        if (rt->user) {
752
0
            ds_put_format(ds, "User: ");
753
0
        } else {
754
0
            ds_put_format(ds, "Cached: ");
755
0
        }
756
0
        ipv6_format_mapped(&rt->nw_addr, ds);
757
0
        plen = rt->plen;
758
0
        if (IN6_IS_ADDR_V4MAPPED(&rt->nw_addr)) {
759
0
            plen -= 96;
760
0
        }
761
0
        ds_put_format(ds, "/%"PRIu8, plen);
762
0
        if (rt->mark) {
763
0
            ds_put_format(ds, " MARK %"PRIu32, rt->mark);
764
0
        }
765
766
0
        ds_put_format(ds, " dev %s", rt->output_netdev);
767
0
        if (ipv6_addr_is_set(&rt->gw)) {
768
0
            ds_put_format(ds, " GW ");
769
0
            ipv6_format_mapped(&rt->gw, ds);
770
0
        }
771
0
        ds_put_format(ds, " SRC ");
772
0
        ipv6_format_mapped(&rt->src_addr, ds);
773
0
        if (table == CLS_LOCAL && !rt->user) {
774
0
            ds_put_format(ds, " local");
775
0
        }
776
0
        if (!is_standard_table(table) && !show_header) {
777
0
            ds_put_format(ds, " table %"PRIu32, table);
778
0
        }
779
0
        ds_put_format(ds, "\n");
780
0
    }
781
0
}
782
783
static void
784
ovs_router_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
785
               const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
786
0
{
787
0
    struct ds ds = DS_EMPTY_INITIALIZER;
788
0
    struct classifier *cls = NULL;
789
0
    uint32_t table = 0;
790
791
0
    if (argc > 1) {
792
0
        if (!strcmp(argv[1], "table=all")) {
793
0
            table = CLS_ALL;
794
0
        } else if (!ovs_scan(argv[1], "table=%"SCNu32, &table)) {
795
0
            unixctl_command_reply_error(conn, "Invalid table format");
796
0
            return;
797
0
        }
798
0
    }
799
800
0
    if (table && table != CLS_ALL) {
801
0
        cls = cls_find(table);
802
0
        if (!cls) {
803
0
            ds_put_format(&ds, "Table '%s' not found", argv[1]);
804
0
            unixctl_command_reply_error(conn, ds_cstr_ro(&ds));
805
0
            ds_destroy(&ds);
806
0
            return;
807
0
        }
808
0
    }
809
810
0
    if (unixctl_command_get_output_format(conn) == UNIXCTL_OUTPUT_FMT_JSON) {
811
0
        struct json *routes = NULL;
812
813
0
        routes = json_array_create_empty();
814
815
0
        if (table == CLS_ALL) {
816
0
            struct clsmap_node *node;
817
818
0
            CMAP_FOR_EACH (node, cmap_node, &clsmap) {
819
0
                ovs_router_show_json(routes, &node->cls, node->table);
820
0
            }
821
0
            ovs_router_show_json(routes, cls_find(CLS_MAIN), CLS_MAIN);
822
0
        } else if (!table) {
823
0
            ovs_router_show_json(routes, cls_find(CLS_LOCAL), CLS_LOCAL);
824
0
            ovs_router_show_json(routes, cls_find(CLS_MAIN), CLS_MAIN);
825
0
            ovs_router_show_json(routes, cls_find(CLS_DEFAULT), CLS_DEFAULT);
826
0
        } else {
827
0
            ovs_router_show_json(routes, cls, table);
828
0
        }
829
830
0
        unixctl_command_reply_json(conn, routes);
831
0
    } else {
832
0
        if (table == CLS_ALL) {
833
0
            struct clsmap_node *node;
834
835
0
            CMAP_FOR_EACH (node, cmap_node, &clsmap) {
836
0
                ovs_router_show_text(&ds, &node->cls, node->table, false);
837
0
            }
838
0
        } else if (!table) {
839
0
            ovs_router_show_text(&ds, cls_find(CLS_LOCAL), CLS_LOCAL, true);
840
0
            ovs_router_show_text(&ds, cls_find(CLS_MAIN), CLS_MAIN, false);
841
0
            ovs_router_show_text(&ds, cls_find(CLS_DEFAULT), CLS_DEFAULT,
842
0
                                 false);
843
0
        } else {
844
0
            ovs_router_show_text(&ds, cls, table, true);
845
0
        }
846
0
        unixctl_command_reply(conn, ds_cstr(&ds));
847
0
        ds_destroy(&ds);
848
0
    }
849
0
}
850
851
static void
852
ovs_router_rules_show_json(struct json *rule_entries, bool ipv6)
853
0
{
854
0
    struct router_rule *rule;
855
0
    struct ds ds;
856
857
0
    PVECTOR_FOR_EACH (rule, &rules) {
858
0
        struct json *entry;
859
860
0
        if (rule->ipv4 == ipv6) {
861
0
            continue;
862
0
        }
863
0
        entry = json_object_create();
864
865
0
        json_object_put(entry, "priority", json_integer_create(rule->prio));
866
0
        json_object_put(entry, "user", json_integer_create(rule->user));
867
0
        json_object_put(entry, "invert", json_boolean_create(rule->invert));
868
0
        json_object_put(entry, "ipv4", json_boolean_create(rule->ipv4));
869
0
        json_object_put(entry, "src-prefix",
870
0
                        json_integer_create(rule->src_prefix));
871
0
        json_object_put(entry, "lookup",
872
0
                        json_integer_create(rule->lookup_table));
873
874
0
        if (rule->src_prefix) {
875
0
            ds_init(&ds);
876
0
            ipv6_format_mapped(&rule->from_addr, &ds);
877
0
            json_object_put_string(entry, "from", ds_cstr_ro(&ds));
878
0
            ds_destroy(&ds);
879
0
        } else {
880
0
            json_object_put_string(entry, "from", "all");
881
0
        }
882
883
0
        json_array_add(rule_entries, entry);
884
0
    }
885
0
}
886
887
static char *
888
standard_table_name(uint32_t table)
889
0
{
890
0
    switch (table) {
891
0
    case CLS_DEFAULT:
892
0
        return "default";
893
0
    case CLS_MAIN:
894
0
        return "main";
895
0
    case CLS_LOCAL:
896
0
        return "local";
897
0
    }
898
899
0
    return NULL;
900
0
}
901
902
static void
903
ovs_router_rules_show_text(struct ds *ds, bool ipv6)
904
0
{
905
0
    struct router_rule *rule;
906
907
0
    PVECTOR_FOR_EACH (rule, &rules) {
908
0
        if (rule->ipv4 == ipv6) {
909
0
            continue;
910
0
        }
911
0
        if (rule->user) {
912
0
            ds_put_format(ds, "User: ");
913
0
        } else {
914
0
            ds_put_format(ds, "Cached: ");
915
0
        }
916
0
        ds_put_format(ds, "%"PRIu32": ", rule->prio);
917
0
        if (rule->invert) {
918
0
            ds_put_format(ds, "not ");
919
0
        }
920
0
        ds_put_format(ds, "from ");
921
0
        if (rule->src_prefix) {
922
0
            ipv6_format_mapped(&rule->from_addr, ds);
923
0
            if (!((IN6_IS_ADDR_V4MAPPED(&rule->from_addr) &&
924
0
                   rule->src_prefix == 32) || rule->src_prefix == 128)) {
925
0
                ds_put_format(ds, "/%"PRIu8" ", rule->src_prefix);
926
0
            }
927
0
        } else {
928
0
            ds_put_cstr(ds, "all");
929
0
        }
930
0
        ds_put_format(ds, " ");
931
0
        if (is_standard_table(rule->lookup_table)) {
932
0
            ds_put_format(ds, "lookup %s\n",
933
0
                          standard_table_name(rule->lookup_table));
934
0
        } else {
935
0
            ds_put_format(ds, "lookup %"PRIu32"\n", rule->lookup_table);
936
0
        }
937
0
    }
938
0
}
939
940
static void
941
ovs_router_rules_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
942
                      const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
943
0
{
944
0
    bool ipv6 = false;
945
946
0
    if (argc > 1 && ovs_scan(argv[1], "-6")) {
947
0
        ipv6 = true;
948
0
    }
949
950
0
    if (unixctl_command_get_output_format(conn) == UNIXCTL_OUTPUT_FMT_JSON) {
951
0
        struct json *entries = json_array_create_empty();
952
953
0
        ovs_router_rules_show_json(entries, ipv6);
954
0
        unixctl_command_reply_json(conn, entries);
955
0
    } else {
956
0
        struct ds ds = DS_EMPTY_INITIALIZER;
957
958
0
        ovs_router_rules_show_text(&ds, ipv6);
959
0
        unixctl_command_reply(conn, ds_cstr(&ds));
960
0
        ds_destroy(&ds);
961
0
    }
962
0
}
963
964
static void
965
ovs_router_rule_add_cmd(struct unixctl_conn *conn, int argc OVS_UNUSED,
966
                        const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
967
0
{
968
0
    unsigned int src_len = 0;
969
0
    struct in6_addr from;
970
0
    bool invert = false;
971
0
    bool ipv4 = true;
972
0
    uint32_t prio = 0;
973
0
    uint32_t table;
974
0
    ovs_be32 ip;
975
0
    int i = 1;
976
977
0
    if (ovs_scan(argv[i], "-6")) {
978
0
        ipv4 = false;
979
0
        i++;
980
0
    }
981
0
    if (ovs_scan(argv[i], "not")) {
982
0
        invert = true;
983
0
        i++;
984
0
    }
985
986
0
    if (ovs_scan(argv[i], "from=all")) {
987
0
        from = in6addr_any;
988
0
    } else if (ovs_scan(argv[i], "from=")) {
989
0
        const char *arg = &argv[i][strlen("from=")];
990
991
0
        if (scan_ipv4_route(arg, &ip, &src_len)) {
992
0
            in6_addr_set_mapped_ipv4(&from, ip);
993
0
            ipv4 = true;
994
0
        } else if (scan_ipv6_route(arg, &from, &src_len)) {
995
0
            ipv4 = false;
996
0
        } else {
997
0
            unixctl_command_reply_error(conn, "Invalid from=ip/plen");
998
0
            return;
999
0
        }
1000
0
    } else {
1001
0
        unixctl_command_reply_error(conn, "Invalid 'from' parameter");
1002
0
        return;
1003
0
    }
1004
0
    if (argc <= ++i) {
1005
0
        unixctl_command_reply_error(conn, "Not enough arguments");
1006
0
        return;
1007
0
    }
1008
1009
0
    if (ovs_scan(argv[i], "prio=%"SCNu32, &prio)) {
1010
0
        if (argc <= ++i) {
1011
0
            unixctl_command_reply_error(conn, "Not enough arguments");
1012
0
            return;
1013
0
        }
1014
0
    }
1015
1016
0
    if (ovs_scan(argv[i], "table=local")) {
1017
0
        table = CLS_LOCAL;
1018
0
    } else if (ovs_scan(argv[i], "table=main")) {
1019
0
        table = CLS_MAIN;
1020
0
    } else if (ovs_scan(argv[i], "table=default")) {
1021
0
        table = CLS_DEFAULT;
1022
0
    } else if (!ovs_scan(argv[i], "table=%"SCNu32, &table)) {
1023
0
        unixctl_command_reply_error(conn, "Invalid 'table' format");
1024
0
        return;
1025
0
    }
1026
1027
0
    ovs_mutex_lock(&mutex);
1028
0
    if (!prio) {
1029
0
        struct router_rule *rule;
1030
0
        uint32_t prev_prio = 0;
1031
1032
0
        PVECTOR_FOR_EACH (rule, &rules) {
1033
0
            if (rule->prio && (!prio || (rule->prio - prev_prio > 1))) {
1034
0
                prio = rule->prio - 1;
1035
0
            }
1036
0
            prev_prio = rule->prio;
1037
0
        }
1038
0
    }
1039
0
    ovs_router_rule_add(prio, invert, true, src_len, &from, table, ipv4);
1040
0
    ovs_mutex_unlock(&mutex);
1041
1042
0
    unixctl_command_reply(conn, "OK");
1043
0
}
1044
1045
static void
1046
ovs_router_rule_del_cmd(struct unixctl_conn *conn, int argc OVS_UNUSED,
1047
                        const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
1048
0
{
1049
0
    unsigned int src_len = 0;
1050
0
    struct in6_addr from;
1051
0
    bool invert = false;
1052
0
    bool ipv4 = true;
1053
0
    uint32_t prio = 0;
1054
0
    uint32_t table;
1055
0
    ovs_be32 ip;
1056
0
    int i = 1;
1057
0
    int err;
1058
1059
0
    if (ovs_scan(argv[i], "-6")) {
1060
0
        ipv4 = false;
1061
0
        i++;
1062
0
    }
1063
0
    if (ovs_scan(argv[i], "not")) {
1064
0
        invert = true;
1065
0
        i++;
1066
0
    }
1067
1068
0
    if (ovs_scan(argv[i], "from=all")) {
1069
0
        from = in6addr_any;
1070
0
    } else if (ovs_scan(argv[i], "from=")) {
1071
0
        const char *arg = &argv[i][strlen("from=")];
1072
1073
0
        if (scan_ipv4_route(arg, &ip, &src_len)) {
1074
0
            in6_addr_set_mapped_ipv4(&from, ip);
1075
0
            ipv4 = true;
1076
0
        } else if (scan_ipv6_route(arg, &from, &src_len)) {
1077
0
            ipv4 = false;
1078
0
        } else {
1079
0
            unixctl_command_reply_error(conn, "Invalid from=ip/plen");
1080
0
            return;
1081
0
        }
1082
0
    } else {
1083
0
        unixctl_command_reply_error(conn, "Invalid 'from' parameter");
1084
0
        return;
1085
0
    }
1086
0
    if (argc <= ++i) {
1087
0
        unixctl_command_reply_error(conn, "Not enough arguments");
1088
0
        return;
1089
0
    }
1090
1091
0
    if (ovs_scan(argv[i], "prio=%"SCNu32, &prio)) {
1092
0
        if (argc <= ++i) {
1093
0
            unixctl_command_reply_error(conn, "Not enough arguments");
1094
0
            return;
1095
0
        }
1096
0
    }
1097
1098
0
    if (ovs_scan(argv[i], "table=local")) {
1099
0
        table = CLS_LOCAL;
1100
0
    } else if (ovs_scan(argv[i], "table=main")) {
1101
0
        table = CLS_MAIN;
1102
0
    } else if (ovs_scan(argv[i], "table=default")) {
1103
0
        table = CLS_DEFAULT;
1104
0
    } else if (!ovs_scan(argv[i], "table=%"SCNu32, &table)) {
1105
0
        unixctl_command_reply_error(conn, "Invalid 'table' format");
1106
0
        return;
1107
0
    }
1108
1109
0
    ovs_mutex_lock(&mutex);
1110
0
    err = ovs_router_rule_del(prio, invert, src_len, &from, table, ipv4);
1111
0
    ovs_mutex_unlock(&mutex);
1112
1113
0
    if (err) {
1114
0
        struct ds ds = DS_EMPTY_INITIALIZER;
1115
1116
0
        ds_put_format(&ds, "Failed to delete router rule: %d (%s)", err,
1117
0
                      ovs_strerror(err));
1118
0
        unixctl_command_reply_error(conn, ds_cstr_ro(&ds));
1119
0
        ds_destroy(&ds);
1120
0
    } else {
1121
0
        unixctl_command_reply(conn, "OK");
1122
0
    }
1123
0
}
1124
1125
static void
1126
ovs_router_lookup_cmd(struct unixctl_conn *conn, int argc,
1127
                      const char *argv[], void *aux OVS_UNUSED)
1128
0
{
1129
0
    struct in6_addr gw, src6 = in6addr_any;
1130
0
    char src6_s[IPV6_SCAN_LEN + 1];
1131
0
    char iface[IFNAMSIZ];
1132
0
    struct in6_addr ip6;
1133
0
    unsigned int plen;
1134
0
    uint32_t mark = 0;
1135
0
    ovs_be32 src = 0;
1136
0
    bool is_ipv6;
1137
0
    ovs_be32 ip;
1138
0
    int i;
1139
1140
0
    if (scan_ipv4_route(argv[1], &ip, &plen) && plen == 32) {
1141
0
        in6_addr_set_mapped_ipv4(&ip6, ip);
1142
0
        is_ipv6 = false;
1143
0
    } else if (scan_ipv6_route(argv[1], &ip6, &plen) && plen == 128) {
1144
0
        is_ipv6 = true;
1145
0
    } else {
1146
0
        unixctl_command_reply_error(conn, "Invalid 'ip/plen' parameter");
1147
0
        return;
1148
0
    }
1149
1150
    /* Parse optional parameters. */
1151
0
    for (i = 2; i < argc; i++) {
1152
0
        if (ovs_scan(argv[i], "pkt_mark=%"SCNu32, &mark)) {
1153
0
            continue;
1154
0
        }
1155
1156
0
        if (is_ipv6) {
1157
0
            if (ovs_scan(argv[i], "src="IPV6_SCAN_FMT, src6_s) &&
1158
0
                ipv6_parse(src6_s, &src6)) {
1159
0
                continue;
1160
0
            }
1161
0
        } else {
1162
0
            if (ovs_scan(argv[i], "src="IP_SCAN_FMT, IP_SCAN_ARGS(&src))) {
1163
0
                continue;
1164
0
            }
1165
0
        }
1166
1167
0
        unixctl_command_reply_error(conn, "Invalid pkt_mark or src");
1168
0
        return;
1169
0
    }
1170
1171
0
    if (src) {
1172
0
        in6_addr_set_mapped_ipv4(&src6, src);
1173
0
    }
1174
1175
0
    if (ovs_router_lookup(mark, &ip6, iface, &src6, &gw)) {
1176
0
        struct ds ds = DS_EMPTY_INITIALIZER;
1177
1178
0
        ds_put_format(&ds, "src ");
1179
0
        ipv6_format_mapped(&src6, &ds);
1180
0
        ds_put_format(&ds, "\ngateway ");
1181
0
        ipv6_format_mapped(&gw, &ds);
1182
0
        ds_put_format(&ds, "\ndev %s\n", iface);
1183
0
        unixctl_command_reply(conn, ds_cstr(&ds));
1184
0
        ds_destroy(&ds);
1185
0
    } else {
1186
0
        unixctl_command_reply_error(conn, "Not found");
1187
0
    }
1188
0
}
1189
1190
static void
1191
clsmap_node_destroy_cb(struct clsmap_node *node)
1192
0
{
1193
0
    classifier_destroy(&node->cls);
1194
0
    ovsrcu_postpone(free, node);
1195
0
}
1196
1197
static void
1198
ovs_router_flush_protected(bool flush_all)
1199
    OVS_REQUIRES(mutex)
1200
0
{
1201
0
    struct clsmap_node *node;
1202
1203
0
    CMAP_FOR_EACH (node, cmap_node, &clsmap) {
1204
0
        cls_flush(&node->cls, flush_all);
1205
0
        if (!node->cls.n_rules) {
1206
0
            cmap_remove(&clsmap, &node->cmap_node, hash_int(node->table, 0));
1207
0
            ovsrcu_postpone(clsmap_node_destroy_cb, node);
1208
0
        }
1209
0
    }
1210
0
    seq_change(tnl_conf_seq);
1211
0
}
1212
1213
void
1214
ovs_router_flush(bool flush_all)
1215
0
{
1216
0
    ovs_mutex_lock(&mutex);
1217
0
    ovs_router_flush_protected(flush_all);
1218
0
    ovs_mutex_unlock(&mutex);
1219
0
}
1220
1221
static void
1222
init_standard_rules(void)
1223
0
{
1224
    /* Add default rules using same priorities as Linux kernel does. */
1225
0
    ovs_router_rule_add(0, false, false, 0,
1226
0
                        &in6addr_v4mapped_any, CLS_LOCAL, true);
1227
0
    ovs_router_rule_add(0x7FFE, false, false, 0,
1228
0
                        &in6addr_v4mapped_any, CLS_MAIN, true);
1229
0
    ovs_router_rule_add(0x7FFF, false, false, 0,
1230
0
                        &in6addr_v4mapped_any, CLS_DEFAULT, true);
1231
1232
0
    ovs_router_rule_add(0, false, false, 0,
1233
0
                        &in6addr_any, CLS_LOCAL, false);
1234
0
    ovs_router_rule_add(0x7FFE, false, false, 0,
1235
0
                        &in6addr_any, CLS_MAIN, false);
1236
0
}
1237
1238
static void
1239
rule_destroy_cb(struct router_rule *rule)
1240
0
{
1241
0
    ovsrcu_postpone(free, rule);
1242
0
}
1243
1244
static void
1245
ovs_router_rules_flush_protected(bool flush_all)
1246
0
{
1247
0
    struct router_rule *rule;
1248
1249
0
    PVECTOR_FOR_EACH (rule, &rules) {
1250
0
        if (flush_all || !rule->user) {
1251
0
            pvector_remove(&rules, rule);
1252
0
            ovsrcu_postpone(rule_destroy_cb, rule);
1253
0
        }
1254
0
    }
1255
0
    pvector_publish(&rules);
1256
0
}
1257
1258
void
1259
ovs_router_rules_flush(bool flush_all)
1260
0
{
1261
0
    ovs_mutex_lock(&mutex);
1262
0
    ovs_router_rules_flush_protected(flush_all);
1263
0
    ovs_mutex_unlock(&mutex);
1264
0
}
1265
1266
static void
1267
ovs_router_flush_handler(void *aux OVS_UNUSED)
1268
0
{
1269
0
    ovs_mutex_lock(&mutex);
1270
0
    ovs_router_rules_flush_protected(true);
1271
0
    ovs_router_flush_protected(true);
1272
0
    pvector_destroy(&rules);
1273
0
    ovs_assert(cmap_is_empty(&clsmap));
1274
0
    cmap_destroy(&clsmap);
1275
0
    cmap_init(&clsmap);
1276
0
    ovs_mutex_unlock(&mutex);
1277
0
}
1278
1279
bool
1280
ovs_router_is_referenced(uint32_t table)
1281
0
{
1282
0
    struct router_rule *rule;
1283
1284
0
    PVECTOR_FOR_EACH (rule, &rules) {
1285
0
        if (rule->lookup_table == table) {
1286
0
            return true;
1287
0
        }
1288
0
    }
1289
0
    return false;
1290
0
}
1291
1292
static int
1293
rule_pvec_prio(uint32_t prio)
1294
0
{
1295
    /* Invert the priority of a pvector entry to reverse the default sorting
1296
     * order (descending) to maintain the standard rules semantic where 0 is
1297
     * the highest priority and UINT_MAX is the lowest.  The mapping is the
1298
     * following:
1299
     *
1300
     *     0        -> INT_MAX
1301
     *     INT_MAX  -> 0
1302
     *     UINT_MAX -> INT_MIN
1303
     */
1304
0
    if (prio <= INT_MAX) {
1305
0
        return -(INT_MIN + (int) prio + 1);
1306
0
    } else {
1307
0
        return -((int) (prio - INT_MAX - 1)) - 1;
1308
0
    }
1309
0
}
1310
1311
void
1312
ovs_router_rule_add(uint32_t prio, bool invert, bool user, uint8_t src_len,
1313
                    const struct in6_addr *from, uint32_t lookup_table,
1314
                    bool ipv4)
1315
    OVS_REQUIRES(mutex)
1316
0
{
1317
0
    struct router_rule *rule = xzalloc(sizeof *rule);
1318
1319
0
    rule->prio = prio;
1320
0
    rule->invert = invert;
1321
0
    rule->user = user;
1322
0
    rule->src_prefix = src_len;
1323
0
    rule->from_addr = *from;
1324
0
    rule->lookup_table = lookup_table;
1325
0
    rule->ipv4 = ipv4;
1326
1327
0
    pvector_insert(&rules, rule, rule_pvec_prio(prio));
1328
0
    pvector_publish(&rules);
1329
0
}
1330
1331
int
1332
ovs_router_rule_del(uint32_t prio, bool invert, uint8_t src_len,
1333
                    const struct in6_addr *from, uint32_t lookup_table,
1334
                    bool ipv4)
1335
    OVS_REQUIRES(mutex)
1336
0
{
1337
0
    struct router_rule *rule;
1338
1339
0
    PVECTOR_FOR_EACH (rule, &rules) {
1340
0
        if (prio && rule->prio > prio) {
1341
0
            break;
1342
0
        }
1343
0
        if (rule->user
1344
0
            && rule->invert == invert
1345
0
            && (!prio || rule->prio == prio)
1346
0
            && rule->ipv4 == ipv4
1347
0
            && rule->src_prefix == src_len
1348
0
            && ipv6_addr_equals(&rule->from_addr, from)
1349
0
            && rule->lookup_table == lookup_table) {
1350
0
            pvector_remove(&rules, rule);
1351
0
            ovsrcu_postpone(rule_destroy_cb, rule);
1352
0
            pvector_publish(&rules);
1353
0
            return 0;
1354
0
        }
1355
0
    }
1356
1357
0
    return -ENOENT;
1358
0
}
1359
1360
void
1361
ovs_router_init(void)
1362
0
{
1363
0
    static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
1364
1365
0
    if (ovsthread_once_start(&once)) {
1366
0
        ovs_mutex_lock(&mutex);
1367
0
        pvector_init(&rules);
1368
0
        init_standard_rules();
1369
0
        ovs_mutex_unlock(&mutex);
1370
0
        fatal_signal_add_hook(ovs_router_flush_handler, NULL, NULL, true);
1371
0
        unixctl_command_register("ovs/route/add",
1372
0
                                 "ip/plen dev [gw] "
1373
0
                                 "[pkt_mark=mark] [src=src_ip] [table=id]",
1374
0
                                 2, 6, ovs_router_add, NULL);
1375
0
        unixctl_command_register("ovs/route/show", "[table=all|id]", 0, 1,
1376
0
                                 ovs_router_show, NULL);
1377
0
        unixctl_command_register("ovs/route/del", "ip/plen "
1378
0
                                 "[pkt_mark=mark] [table=id]", 1, 3,
1379
0
                                 ovs_router_del, NULL);
1380
0
        unixctl_command_register("ovs/route/lookup", "ip_addr "
1381
0
                                 "[pkt_mark=mark] [src=src_ip]", 1, 3,
1382
0
                                 ovs_router_lookup_cmd, NULL);
1383
0
        unixctl_command_register("ovs/route/rule/show", "[-6]", 0, 1,
1384
0
                                 ovs_router_rules_show, NULL);
1385
0
        unixctl_command_register("ovs/route/rule/add",
1386
0
                                 "[-6] [not] from=all|ip/plen [prio=num] "
1387
0
                                 "table=local|main|default|id",
1388
0
                                 2, 5, ovs_router_rule_add_cmd, NULL);
1389
0
        unixctl_command_register("ovs/route/rule/del",
1390
0
                                 "[-6] [not] from=all|ip/plen [prio=num] "
1391
0
                                 "table=local|main|default|id",
1392
                                 2, 5, ovs_router_rule_del_cmd, NULL);
1393
0
        ovsthread_once_done(&once);
1394
0
    }
1395
0
}