Coverage Report

Created: 2025-07-01 06:50

/src/openvswitch/lib/ovs-router.c
Line
Count
Source (jump to first uncovered line)
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 "dpif.h"
37
#include "fatal-signal.h"
38
#include "openvswitch/dynamic-string.h"
39
#include "openvswitch/json.h"
40
#include "netdev.h"
41
#include "packets.h"
42
#include "seq.h"
43
#include "ovs-thread.h"
44
#include "route-table.h"
45
#include "tnl-ports.h"
46
#include "unixctl.h"
47
#include "util.h"
48
#include "unaligned.h"
49
#include "openvswitch/vlog.h"
50
51
VLOG_DEFINE_THIS_MODULE(ovs_router);
52
53
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
54
55
static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER;
56
static struct classifier cls;
57
58
/* By default, use the system routing table.  For system-independent testing,
59
 * the unit tests disable using the system routing table. */
60
static bool use_system_routing_table = true;
61
62
struct ovs_router_entry {
63
    struct cls_rule cr;
64
    char output_netdev[IFNAMSIZ];
65
    struct in6_addr gw;
66
    struct in6_addr nw_addr;
67
    struct in6_addr src_addr;
68
    uint8_t plen;
69
    uint8_t priority;
70
    bool local;
71
    uint32_t mark;
72
};
73
74
static struct ovs_router_entry *
75
ovs_router_entry_cast(const struct cls_rule *cr)
76
0
{
77
0
    return cr ? CONTAINER_OF(cr, struct ovs_router_entry, cr) : NULL;
78
0
}
79
80
/* Disables obtaining routes from the system routing table, for testing
81
 * purposes. */
82
void
83
ovs_router_disable_system_routing_table(void)
84
0
{
85
0
    use_system_routing_table = false;
86
0
}
87
88
static bool
89
ovs_router_lookup_fallback(const struct in6_addr *ip6_dst,
90
                           char output_netdev[], struct in6_addr *src6,
91
                           struct in6_addr *gw6)
92
0
{
93
0
    ovs_be32 src;
94
95
0
    if (!use_system_routing_table
96
0
        || !route_table_fallback_lookup(ip6_dst, output_netdev, gw6)) {
97
0
        return false;
98
0
    }
99
0
    if (netdev_get_in4_by_name(output_netdev, (struct in_addr *)&src)) {
100
0
        return false;
101
0
    }
102
0
    if (src6) {
103
0
        in6_addr_set_mapped_ipv4(src6, src);
104
0
    }
105
0
    return true;
106
0
}
107
108
bool
109
ovs_router_lookup(uint32_t mark, const struct in6_addr *ip6_dst,
110
                  char output_netdev[],
111
                  struct in6_addr *src, struct in6_addr *gw)
112
0
{
113
0
    const struct cls_rule *cr;
114
0
    struct flow flow = {.ipv6_dst = *ip6_dst, .pkt_mark = mark};
115
116
0
    if (src && ipv6_addr_is_set(src)) {
117
0
        const struct cls_rule *cr_src;
118
0
        struct flow flow_src = {.ipv6_dst = *src, .pkt_mark = mark};
119
120
0
        cr_src = classifier_lookup(&cls, OVS_VERSION_MAX, &flow_src, NULL,
121
0
                                   NULL);
122
0
        if (cr_src) {
123
0
            struct ovs_router_entry *p_src = ovs_router_entry_cast(cr_src);
124
0
            if (!p_src->local) {
125
0
                return false;
126
0
            }
127
0
        } else {
128
0
            return false;
129
0
        }
130
0
    }
131
132
0
    cr = classifier_lookup(&cls, OVS_VERSION_MAX, &flow, NULL, NULL);
133
0
    if (cr) {
134
0
        struct ovs_router_entry *p = ovs_router_entry_cast(cr);
135
136
0
        ovs_strlcpy(output_netdev, p->output_netdev, IFNAMSIZ);
137
0
        *gw = p->gw;
138
0
        if (src && !ipv6_addr_is_set(src)) {
139
0
            *src = p->src_addr;
140
0
        }
141
0
        return true;
142
0
    }
143
0
    return ovs_router_lookup_fallback(ip6_dst, output_netdev, src, gw);
144
0
}
145
146
static void
147
rt_entry_free(struct ovs_router_entry *p)
148
0
{
149
0
    cls_rule_destroy(&p->cr);
150
0
    free(p);
151
0
}
152
153
static void rt_init_match(struct match *match, uint32_t mark,
154
                          const struct in6_addr *ip6_dst,
155
                          uint8_t plen)
156
0
{
157
0
    struct in6_addr dst;
158
0
    struct in6_addr mask;
159
160
0
    mask = ipv6_create_mask(plen);
161
162
0
    dst = ipv6_addr_bitand(ip6_dst, &mask);
163
0
    memset(match, 0, sizeof *match);
164
0
    match->flow.ipv6_dst = dst;
165
0
    match->wc.masks.ipv6_dst = mask;
166
0
    match->wc.masks.pkt_mark = UINT32_MAX;
167
0
    match->flow.pkt_mark = mark;
168
0
}
169
170
static int
171
verify_prefsrc(const struct in6_addr *ip6_dst,
172
               const char netdev_name[],
173
               struct in6_addr *prefsrc)
174
0
{
175
0
    struct in6_addr *mask, *addr6;
176
0
    struct netdev *dev;
177
0
    int err, n_in6, i;
178
179
0
    err = netdev_open(netdev_name, NULL, &dev);
180
0
    if (err) {
181
0
        return err;
182
0
    }
183
184
0
    err = netdev_get_addr_list(dev, &addr6, &mask, &n_in6);
185
0
    if (err) {
186
0
        goto out;
187
0
    }
188
189
0
    for (i = 0; i < n_in6; i++) {
190
0
        struct in6_addr a1, a2;
191
0
        a1 = ipv6_addr_bitand(ip6_dst, &mask[i]);
192
0
        a2 = ipv6_addr_bitand(prefsrc, &mask[i]);
193
194
        /* Check that the interface has "prefsrc" and
195
         * it is same broadcast domain with "ip6_dst". */
196
0
        if (IN6_ARE_ADDR_EQUAL(prefsrc, &addr6[i]) &&
197
0
            IN6_ARE_ADDR_EQUAL(&a1, &a2)) {
198
0
            goto out;
199
0
        }
200
0
    }
201
0
    err = ENOENT;
202
203
0
out:
204
0
    free(addr6);
205
0
    free(mask);
206
0
    netdev_close(dev);
207
0
    return err;
208
0
}
209
210
int
211
ovs_router_get_netdev_source_address(const struct in6_addr *ip6_dst,
212
                                     const char netdev_name[],
213
                                     struct in6_addr *psrc)
214
0
{
215
0
    struct in6_addr *mask, *addr6;
216
0
    int err, n_in6, i, max_plen = -1;
217
0
    struct netdev *dev;
218
0
    bool is_ipv4;
219
220
0
    err = netdev_open(netdev_name, NULL, &dev);
221
0
    if (err) {
222
0
        return err;
223
0
    }
224
225
0
    err = netdev_get_addr_list(dev, &addr6, &mask, &n_in6);
226
0
    if (err) {
227
0
        goto out;
228
0
    }
229
230
0
    is_ipv4 = IN6_IS_ADDR_V4MAPPED(ip6_dst);
231
232
0
    for (i = 0; i < n_in6; i++) {
233
0
        struct in6_addr a1, a2;
234
0
        int mask_bits;
235
236
0
        if (is_ipv4 && !IN6_IS_ADDR_V4MAPPED(&addr6[i])) {
237
0
            continue;
238
0
        }
239
240
0
        a1 = ipv6_addr_bitand(ip6_dst, &mask[i]);
241
0
        a2 = ipv6_addr_bitand(&addr6[i], &mask[i]);
242
0
        mask_bits = bitmap_count1(ALIGNED_CAST(const unsigned long *, &mask[i]), 128);
243
244
0
        if (!memcmp(&a1, &a2, sizeof (a1)) && mask_bits > max_plen) {
245
0
            *psrc = addr6[i];
246
0
            max_plen = mask_bits;
247
0
        }
248
0
    }
249
0
    if (max_plen == -1) {
250
0
        err = ENOENT;
251
0
    }
252
0
out:
253
0
    free(addr6);
254
0
    free(mask);
255
0
    netdev_close(dev);
256
0
    return err;
257
0
}
258
259
static int
260
ovs_router_insert__(uint32_t mark, uint8_t priority, bool local,
261
                    const struct in6_addr *ip6_dst,
262
                    uint8_t plen, const char output_netdev[],
263
                    const struct in6_addr *gw,
264
                    const struct in6_addr *ip6_src)
265
0
{
266
0
    int (*get_src_addr)(const struct in6_addr *ip6_dst,
267
0
                        const char output_netdev[],
268
0
                        struct in6_addr *prefsrc);
269
0
    const struct cls_rule *cr;
270
0
    struct ovs_router_entry *p;
271
0
    struct match match;
272
0
    int err;
273
274
0
    rt_init_match(&match, mark, ip6_dst, plen);
275
276
0
    p = xzalloc(sizeof *p);
277
0
    ovs_strlcpy(p->output_netdev, output_netdev, sizeof p->output_netdev);
278
0
    if (ipv6_addr_is_set(gw)) {
279
0
        p->gw = *gw;
280
0
    }
281
0
    p->mark = mark;
282
0
    p->nw_addr = match.flow.ipv6_dst;
283
0
    p->plen = plen;
284
0
    p->local = local;
285
0
    p->priority = priority;
286
287
0
    if (ipv6_addr_is_set(ip6_src)) {
288
0
        p->src_addr = *ip6_src;
289
0
        get_src_addr = verify_prefsrc;
290
0
    } else {
291
0
        get_src_addr = ovs_router_get_netdev_source_address;
292
0
    }
293
294
0
    err = get_src_addr(ip6_dst, output_netdev, &p->src_addr);
295
0
    if (err && ipv6_addr_is_set(gw)) {
296
0
        err = get_src_addr(gw, output_netdev, &p->src_addr);
297
0
    }
298
0
    if (err) {
299
0
        struct ds ds = DS_EMPTY_INITIALIZER;
300
301
0
        ipv6_format_mapped(ip6_dst, &ds);
302
0
        VLOG_DBG_RL(&rl, "src addr not available for route %s", ds_cstr(&ds));
303
0
        free(p);
304
0
        ds_destroy(&ds);
305
0
        return err;
306
0
    }
307
    /* Longest prefix matches first. */
308
0
    cls_rule_init(&p->cr, &match, priority);
309
310
0
    ovs_mutex_lock(&mutex);
311
0
    cr = classifier_replace(&cls, &p->cr, OVS_VERSION_MIN, NULL, 0);
312
0
    ovs_mutex_unlock(&mutex);
313
314
0
    if (cr) {
315
        /* An old rule with the same match was displaced. */
316
0
        ovsrcu_postpone(rt_entry_free, ovs_router_entry_cast(cr));
317
0
    }
318
0
    tnl_port_map_insert_ipdev(output_netdev);
319
0
    seq_change(tnl_conf_seq);
320
0
    return 0;
321
0
}
322
323
void
324
ovs_router_insert(uint32_t mark, const struct in6_addr *ip_dst, uint8_t plen,
325
                  bool local, const char output_netdev[],
326
                  const struct in6_addr *gw, const struct in6_addr *prefsrc)
327
0
{
328
0
    if (use_system_routing_table) {
329
0
        uint8_t priority = local ? plen + 64 : plen;
330
0
        ovs_router_insert__(mark, priority, local, ip_dst, plen,
331
0
                            output_netdev, gw, prefsrc);
332
0
    }
333
0
}
334
335
/* The same as 'ovs_router_insert', but it adds the route even if updates
336
 * from the system routing table are disabled.  Used for unit tests. */
337
void
338
ovs_router_force_insert(uint32_t mark, const struct in6_addr *ip_dst,
339
                        uint8_t plen, bool local, const char output_netdev[],
340
                        const struct in6_addr *gw,
341
                        const struct in6_addr *prefsrc)
342
0
{
343
0
    uint8_t priority = local ? plen + 64 : plen;
344
345
0
    ovs_router_insert__(mark, priority, local, ip_dst, plen,
346
0
                        output_netdev, gw, prefsrc);
347
0
}
348
349
static void
350
rt_entry_delete__(const struct cls_rule *cr)
351
0
{
352
0
    struct ovs_router_entry *p = ovs_router_entry_cast(cr);
353
354
0
    tnl_port_map_delete_ipdev(p->output_netdev);
355
0
    classifier_remove_assert(&cls, cr);
356
0
    ovsrcu_postpone(rt_entry_free, ovs_router_entry_cast(cr));
357
0
}
358
359
static bool
360
rt_entry_delete(uint32_t mark, uint8_t priority,
361
                const struct in6_addr *ip6_dst, uint8_t plen)
362
0
{
363
0
    const struct cls_rule *cr;
364
0
    struct cls_rule rule;
365
0
    struct match match;
366
0
    bool res = false;
367
368
0
    rt_init_match(&match, mark, ip6_dst, plen);
369
370
0
    cls_rule_init(&rule, &match, priority);
371
372
    /* Find the exact rule. */
373
0
    cr = classifier_find_rule_exactly(&cls, &rule, OVS_VERSION_MAX);
374
0
    if (cr) {
375
0
        ovs_mutex_lock(&mutex);
376
0
        rt_entry_delete__(cr);
377
0
        ovs_mutex_unlock(&mutex);
378
379
0
        res = true;
380
0
    }
381
382
0
    cls_rule_destroy(&rule);
383
0
    return res;
384
0
}
385
386
static bool
387
scan_ipv6_route(const char *s, struct in6_addr *addr, unsigned int *plen)
388
0
{
389
0
    char *error = ipv6_parse_cidr(s, addr, plen);
390
0
    if (error) {
391
0
        free(error);
392
0
        return false;
393
0
    }
394
0
    return true;
395
0
}
396
397
static bool
398
scan_ipv4_route(const char *s, ovs_be32 *addr, unsigned int *plen)
399
0
{
400
0
    char *error = ip_parse_cidr(s, addr, plen);
401
0
    if (error) {
402
0
        free(error);
403
0
        return false;
404
0
    }
405
0
    return true;
406
0
}
407
408
static void
409
ovs_router_add(struct unixctl_conn *conn, int argc,
410
              const char *argv[], void *aux OVS_UNUSED)
411
0
{
412
0
    struct in6_addr src6 = in6addr_any;
413
0
    struct in6_addr gw6 = in6addr_any;
414
0
    char src6_s[IPV6_SCAN_LEN + 1];
415
0
    struct in6_addr ip6;
416
0
    uint32_t mark = 0;
417
0
    unsigned int plen;
418
0
    ovs_be32 src = 0;
419
0
    ovs_be32 gw = 0;
420
0
    bool is_ipv6;
421
0
    ovs_be32 ip;
422
0
    int err;
423
0
    int i;
424
425
0
    if (scan_ipv4_route(argv[1], &ip, &plen)) {
426
0
        in6_addr_set_mapped_ipv4(&ip6, ip);
427
0
        plen += 96;
428
0
        is_ipv6 = false;
429
0
    } else if (scan_ipv6_route(argv[1], &ip6, &plen)) {
430
0
        is_ipv6 = true;
431
0
    } else {
432
0
        unixctl_command_reply_error(conn,
433
0
                                    "Invalid 'ip/plen' parameter");
434
0
        return;
435
0
    }
436
437
    /* Parse optional parameters. */
438
0
    for (i = 3; i < argc; i++) {
439
0
        if (ovs_scan(argv[i], "pkt_mark=%"SCNi32, &mark)) {
440
0
            continue;
441
0
        }
442
443
0
        if (is_ipv6) {
444
0
            if (ovs_scan(argv[i], "src="IPV6_SCAN_FMT, src6_s) &&
445
0
                ipv6_parse(src6_s, &src6)) {
446
0
                continue;
447
0
            }
448
0
            if (ipv6_parse(argv[i], &gw6)) {
449
0
                continue;
450
0
            }
451
0
        } else {
452
0
            if (ovs_scan(argv[i], "src="IP_SCAN_FMT, IP_SCAN_ARGS(&src))) {
453
0
                continue;
454
0
            }
455
0
            if (ip_parse(argv[i], &gw)) {
456
0
                continue;
457
0
            }
458
0
        }
459
460
0
        unixctl_command_reply_error(conn,
461
0
                                    "Invalid pkt_mark, IP gateway or src_ip");
462
0
        return;
463
0
    }
464
465
0
    if (gw) {
466
0
        in6_addr_set_mapped_ipv4(&gw6, gw);
467
0
    }
468
0
    if (src) {
469
0
        in6_addr_set_mapped_ipv4(&src6, src);
470
0
    }
471
472
0
    err = ovs_router_insert__(mark, plen + 32, false, &ip6, plen, argv[2],
473
0
                              &gw6, &src6);
474
0
    if (err) {
475
0
        unixctl_command_reply_error(conn, "Error while inserting route.");
476
0
    } else {
477
0
        unixctl_command_reply(conn, "OK");
478
0
    }
479
0
}
480
481
static void
482
ovs_router_del(struct unixctl_conn *conn, int argc OVS_UNUSED,
483
              const char *argv[], void *aux OVS_UNUSED)
484
0
{
485
0
    struct in6_addr ip6;
486
0
    uint32_t mark = 0;
487
0
    unsigned int plen;
488
0
    ovs_be32 ip;
489
490
0
    if (scan_ipv4_route(argv[1], &ip, &plen)) {
491
0
        in6_addr_set_mapped_ipv4(&ip6, ip);
492
0
        plen += 96;
493
0
    } else if (!scan_ipv6_route(argv[1], &ip6, &plen)) {
494
0
        unixctl_command_reply_error(conn, "Invalid parameters");
495
0
        return;
496
0
    }
497
0
    if (argc > 2) {
498
0
        if (!ovs_scan(argv[2], "pkt_mark=%"SCNi32, &mark)) {
499
0
            unixctl_command_reply_error(conn, "Invalid pkt_mark");
500
0
            return;
501
0
        }
502
0
    }
503
504
0
    if (rt_entry_delete(mark, plen + 32, &ip6, plen)) {
505
0
        unixctl_command_reply(conn, "OK");
506
0
        seq_change(tnl_conf_seq);
507
0
    } else {
508
0
        unixctl_command_reply_error(conn, "Not found");
509
0
    }
510
0
}
511
512
static void
513
ovs_router_show_json(struct json **routes)
514
0
{
515
0
    int n_rules = classifier_count(&cls);
516
0
    struct json **json_entries = NULL;
517
0
    struct ovs_router_entry *rt;
518
0
    struct ds ds;
519
0
    int i = 0;
520
521
0
    if (!n_rules) {
522
0
        goto out;
523
0
    }
524
525
0
    json_entries = xmalloc(n_rules * sizeof *json_entries);
526
0
    ds_init(&ds);
527
528
0
    CLS_FOR_EACH (rt, cr, &cls) {
529
0
        bool user = rt->priority != rt->plen && !rt->local;
530
0
        uint8_t plen = rt->plen;
531
0
        struct json *json, *nh;
532
533
0
        if (i >= n_rules) {
534
0
            break;
535
0
        }
536
537
0
        json = json_object_create();
538
0
        nh = json_object_create();
539
540
0
        if (IN6_IS_ADDR_V4MAPPED(&rt->nw_addr)) {
541
0
            plen -= 96;
542
0
        }
543
544
0
        json_object_put(json, "user", json_boolean_create(user));
545
0
        json_object_put(json, "local", json_boolean_create(rt->local));
546
0
        json_object_put(json, "priority", json_integer_create(rt->priority));
547
0
        json_object_put(json, "prefix", json_integer_create(plen));
548
0
        json_object_put_string(nh, "dev", rt->output_netdev);
549
550
0
        ipv6_format_mapped(&rt->nw_addr, &ds);
551
0
        json_object_put_string(json, "dst", ds_cstr_ro(&ds));
552
0
        ds_clear(&ds);
553
554
0
        ipv6_format_mapped(&rt->src_addr, &ds);
555
0
        json_object_put_string(json, "prefsrc", ds_cstr_ro(&ds));
556
0
        ds_clear(&ds);
557
558
0
        if (rt->mark) {
559
0
            json_object_put(json, "mark", json_integer_create(rt->mark));
560
0
        }
561
562
0
        if (ipv6_addr_is_set(&rt->gw)) {
563
0
            ipv6_format_mapped(&rt->gw, &ds);
564
0
            json_object_put_string(nh, "gateway", ds_cstr_ro(&ds));
565
0
            ds_clear(&ds);
566
0
        }
567
568
0
        json_object_put(json, "nexthops", json_array_create_1(nh));
569
0
        json_entries[i++] = json;
570
0
    }
571
572
0
    ds_destroy(&ds);
573
574
0
out:
575
0
    *routes = json_array_create(json_entries, i);
576
0
}
577
578
static void
579
ovs_router_show_text(struct ds *ds)
580
0
{
581
0
    struct ovs_router_entry *rt;
582
583
0
    ds_put_format(ds, "Route Table:\n");
584
0
    CLS_FOR_EACH (rt, cr, &cls) {
585
0
        uint8_t plen;
586
0
        if (rt->priority == rt->plen || rt->local) {
587
0
            ds_put_format(ds, "Cached: ");
588
0
        } else {
589
0
            ds_put_format(ds, "User: ");
590
0
        }
591
0
        ipv6_format_mapped(&rt->nw_addr, ds);
592
0
        plen = rt->plen;
593
0
        if (IN6_IS_ADDR_V4MAPPED(&rt->nw_addr)) {
594
0
            plen -= 96;
595
0
        }
596
0
        ds_put_format(ds, "/%"PRIu8, plen);
597
0
        if (rt->mark) {
598
0
            ds_put_format(ds, " MARK %"PRIu32, rt->mark);
599
0
        }
600
601
0
        ds_put_format(ds, " dev %s", rt->output_netdev);
602
0
        if (ipv6_addr_is_set(&rt->gw)) {
603
0
            ds_put_format(ds, " GW ");
604
0
            ipv6_format_mapped(&rt->gw, ds);
605
0
        }
606
0
        ds_put_format(ds, " SRC ");
607
0
        ipv6_format_mapped(&rt->src_addr, ds);
608
0
        if (rt->local) {
609
0
            ds_put_format(ds, " local");
610
0
        }
611
0
        ds_put_format(ds, "\n");
612
0
    }
613
0
}
614
615
static void
616
ovs_router_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
617
               const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
618
0
{
619
0
    if (unixctl_command_get_output_format(conn) == UNIXCTL_OUTPUT_FMT_JSON) {
620
0
        struct json *routes;
621
622
0
        ovs_router_show_json(&routes);
623
0
        unixctl_command_reply_json(conn, routes);
624
0
    } else {
625
0
        struct ds ds = DS_EMPTY_INITIALIZER;
626
627
0
        ovs_router_show_text(&ds);
628
0
        unixctl_command_reply(conn, ds_cstr(&ds));
629
0
        ds_destroy(&ds);
630
0
    }
631
0
}
632
633
static void
634
ovs_router_lookup_cmd(struct unixctl_conn *conn, int argc,
635
                      const char *argv[], void *aux OVS_UNUSED)
636
0
{
637
0
    struct in6_addr gw, src = in6addr_any;
638
0
    char iface[IFNAMSIZ];
639
0
    struct in6_addr ip6;
640
0
    unsigned int plen;
641
0
    uint32_t mark = 0;
642
0
    ovs_be32 ip;
643
644
0
    if (scan_ipv4_route(argv[1], &ip, &plen) && plen == 32) {
645
0
        in6_addr_set_mapped_ipv4(&ip6, ip);
646
0
    } else if (!(scan_ipv6_route(argv[1], &ip6, &plen) && plen == 128)) {
647
0
        unixctl_command_reply_error(conn, "Invalid parameters");
648
0
        return;
649
0
    }
650
0
    if (argc > 2) {
651
0
        if (!ovs_scan(argv[2], "pkt_mark=%"SCNi32, &mark)) {
652
0
            unixctl_command_reply_error(conn, "Invalid pkt_mark");
653
0
            return;
654
0
        }
655
0
    }
656
0
    if (ovs_router_lookup(mark, &ip6, iface, &src, &gw)) {
657
0
        struct ds ds = DS_EMPTY_INITIALIZER;
658
659
0
        ds_put_format(&ds, "src ");
660
0
        ipv6_format_mapped(&src, &ds);
661
0
        ds_put_format(&ds, "\ngateway ");
662
0
        ipv6_format_mapped(&gw, &ds);
663
0
        ds_put_format(&ds, "\ndev %s\n", iface);
664
0
        unixctl_command_reply(conn, ds_cstr(&ds));
665
0
        ds_destroy(&ds);
666
0
    } else {
667
0
        unixctl_command_reply_error(conn, "Not found");
668
0
    }
669
0
}
670
671
void
672
ovs_router_flush(void)
673
0
{
674
0
    struct ovs_router_entry *rt;
675
676
0
    ovs_mutex_lock(&mutex);
677
0
    classifier_defer(&cls);
678
0
    CLS_FOR_EACH(rt, cr, &cls) {
679
0
        if (rt->priority == rt->plen || rt->local) {
680
0
            rt_entry_delete__(&rt->cr);
681
0
        }
682
0
    }
683
0
    classifier_publish(&cls);
684
0
    ovs_mutex_unlock(&mutex);
685
0
    seq_change(tnl_conf_seq);
686
0
}
687
688
static void
689
ovs_router_flush_handler(void *aux OVS_UNUSED)
690
0
{
691
0
    ovs_router_flush();
692
0
}
693
694
void
695
ovs_router_init(void)
696
0
{
697
0
    static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
698
699
0
    if (ovsthread_once_start(&once)) {
700
0
        fatal_signal_add_hook(ovs_router_flush_handler, NULL, NULL, true);
701
0
        classifier_init(&cls, NULL);
702
0
        unixctl_command_register("ovs/route/add",
703
0
                                 "ip/plen dev [gw] "
704
0
                                 "[pkt_mark=mark] [src=src_ip]",
705
0
                                 2, 5, ovs_router_add, NULL);
706
0
        unixctl_command_register("ovs/route/show", "", 0, 0,
707
0
                                 ovs_router_show, NULL);
708
0
        unixctl_command_register("ovs/route/del", "ip/plen "
709
0
                                 "[pkt_mark=mark]", 1, 2, ovs_router_del,
710
0
                                 NULL);
711
0
        unixctl_command_register("ovs/route/lookup", "ip_addr "
712
0
                                 "[pkt_mark=mark]", 1, 2,
713
0
                                 ovs_router_lookup_cmd, NULL);
714
0
        ovsthread_once_done(&once);
715
0
    }
716
0
}