Coverage Report

Created: 2026-04-10 06:48

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