Coverage Report

Created: 2026-02-26 06:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openvswitch/lib/route-table.c
Line
Count
Source
1
/*
2
 * Copyright (c) 2011, 2012, 2013, 2014, 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 "route-table.h"
20
21
#include <errno.h>
22
#include <sys/types.h>
23
#include <netinet/in.h>
24
#include <arpa/inet.h>
25
#include <sys/socket.h>
26
#include <linux/fib_rules.h>
27
#include <linux/rtnetlink.h>
28
#include <net/if.h>
29
30
#include "coverage.h"
31
#include "hash.h"
32
#include "netdev.h"
33
#include "netlink.h"
34
#include "netlink-notifier.h"
35
#include "netlink-socket.h"
36
#include "openvswitch/list.h"
37
#include "openvswitch/ofpbuf.h"
38
#include "ovs-router.h"
39
#include "packets.h"
40
#include "rtnetlink.h"
41
#include "tnl-ports.h"
42
#include "openvswitch/vlog.h"
43
44
/* Below constants were added in different Linux versions, so define them just
45
 * in case we're building with old headers. (We can't test for it with #ifdef
46
 * because it's an enum.) */
47
0
#define RTA_MARK 16 /* Linux 2.6.36 */
48
0
#define FRA_SUPPRESS_PREFIXLEN 14 /* Linux 3.12 */
49
0
#define FRA_TABLE 15 /* Linux 2.6.19 */
50
0
#define FRA_PROTOCOL 21 /* Linux 4.17 */
51
52
/* Linux 4.1 added RTA_VIA. */
53
#ifndef HAVE_RTA_VIA
54
#define RTA_VIA 18
55
struct rtvia {
56
    sa_family_t rtvia_family;
57
    uint8_t     rtvia_addr[];
58
};
59
#endif
60
61
VLOG_DEFINE_THIS_MODULE(route_table);
62
63
COVERAGE_DEFINE(route_table_dump);
64
65
BUILD_ASSERT_DECL((enum rt_class_t) CLS_DEFAULT == RT_TABLE_DEFAULT);
66
BUILD_ASSERT_DECL((enum rt_class_t) CLS_MAIN == RT_TABLE_MAIN);
67
BUILD_ASSERT_DECL((enum rt_class_t) CLS_LOCAL == RT_TABLE_LOCAL);
68
69
static struct ovs_mutex route_table_mutex = OVS_MUTEX_INITIALIZER;
70
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
71
72
/* Global change number for route-table, which should be incremented
73
 * every time route_table_reset() is called.  */
74
static uint64_t rt_change_seq;
75
76
static struct nln *nln = NULL;
77
static struct route_table_msg nln_rtmsg_change;
78
static struct nln_notifier *route_notifier = NULL;
79
static struct nln_notifier *route6_notifier = NULL;
80
static struct nln_notifier *rule_notifier = NULL;
81
static struct nln_notifier *rule6_notifier = NULL;
82
static struct nln_notifier *name_notifier = NULL;
83
84
static bool route_table_valid = false;
85
static bool rules_valid = false;
86
87
static int route_nln_parse(struct ofpbuf *, void *change);
88
89
static void rule_handle_msg(const struct route_table_msg *);
90
static int rule_parse(struct ofpbuf *, void *change);
91
92
static void route_table_reset(void);
93
static void route_table_handle_msg(const struct route_table_msg *, void *aux,
94
                                   uint32_t table);
95
static void route_table_change(struct route_table_msg *, void *aux);
96
static void rules_change(const struct route_table_msg *, void *aux);
97
static void route_map_clear(void);
98
99
static void name_table_init(void);
100
static void name_table_change(const struct rtnetlink_change *, void *);
101
102
static void
103
route_data_destroy_nexthops__(struct route_data *rd)
104
0
{
105
0
    struct route_data_nexthop *rdnh;
106
107
0
    LIST_FOR_EACH_POP (rdnh, nexthop_node, &rd->nexthops) {
108
0
        if (rdnh && rdnh != &rd->primary_next_hop__) {
109
0
            free(rdnh);
110
0
        }
111
0
    }
112
0
}
113
114
void
115
route_data_destroy(struct route_data *rd)
116
0
{
117
0
    route_data_destroy_nexthops__(rd);
118
0
}
119
120
uint64_t
121
route_table_get_change_seq(void)
122
0
{
123
0
    return rt_change_seq;
124
0
}
125
126
/* Users of the route_table module should register themselves with this
127
 * function before making any other route_table function calls. */
128
void
129
route_table_init(void)
130
    OVS_EXCLUDED(route_table_mutex)
131
0
{
132
0
    ovs_mutex_lock(&route_table_mutex);
133
0
    ovs_assert(!nln);
134
0
    ovs_assert(!route_notifier);
135
0
    ovs_assert(!route6_notifier);
136
0
    ovs_assert(!rule_notifier);
137
0
    ovs_assert(!rule6_notifier);
138
139
0
    ovs_router_init();
140
0
    nln = nln_create(NETLINK_ROUTE, route_nln_parse, &nln_rtmsg_change);
141
142
0
    route_notifier =
143
0
        nln_notifier_create(nln, RTNLGRP_IPV4_ROUTE,
144
0
                            (nln_notify_func *) route_table_change, NULL);
145
0
    route6_notifier =
146
0
        nln_notifier_create(nln, RTNLGRP_IPV6_ROUTE,
147
0
                            (nln_notify_func *) route_table_change, NULL);
148
149
0
    rule_notifier =
150
0
        nln_notifier_create(nln, RTNLGRP_IPV4_RULE,
151
0
                            (nln_notify_func *) rules_change, NULL);
152
0
    rule6_notifier =
153
0
        nln_notifier_create(nln, RTNLGRP_IPV6_RULE,
154
0
                            (nln_notify_func *) rules_change, NULL);
155
156
0
    route_table_reset();
157
0
    name_table_init();
158
159
0
    ovs_mutex_unlock(&route_table_mutex);
160
0
}
161
162
/* Run periodically to update the locally maintained routing table. */
163
void
164
route_table_run(void)
165
    OVS_EXCLUDED(route_table_mutex)
166
0
{
167
0
    ovs_mutex_lock(&route_table_mutex);
168
0
    if (nln) {
169
0
        rtnetlink_run();
170
0
        nln_run(nln);
171
172
0
        if (!route_table_valid || !rules_valid) {
173
0
            route_table_reset();
174
0
        }
175
0
    }
176
0
    ovs_mutex_unlock(&route_table_mutex);
177
0
}
178
179
/* Causes poll_block() to wake up when route_table updates are required. */
180
void
181
route_table_wait(void)
182
    OVS_EXCLUDED(route_table_mutex)
183
0
{
184
0
    ovs_mutex_lock(&route_table_mutex);
185
0
    if (nln) {
186
0
        rtnetlink_wait();
187
0
        nln_wait(nln);
188
0
    }
189
0
    ovs_mutex_unlock(&route_table_mutex);
190
0
}
191
192
bool
193
route_table_dump_one_table(uint32_t id,
194
                           route_table_handle_msg_callback *handle_msg_cb,
195
                           void *aux)
196
0
{
197
0
    uint64_t reply_stub[NL_DUMP_BUFSIZE / 8];
198
0
    struct ofpbuf request, reply, buf;
199
0
    struct rtmsg *rq_msg;
200
0
    bool filtered = true;
201
0
    struct nl_dump dump;
202
203
0
    ofpbuf_init(&request, 0);
204
205
0
    nl_msg_put_nlmsghdr(&request, sizeof *rq_msg, RTM_GETROUTE, NLM_F_REQUEST);
206
207
0
    rq_msg = ofpbuf_put_zeros(&request, sizeof *rq_msg);
208
0
    rq_msg->rtm_family = AF_UNSPEC;
209
210
0
    if (id > UCHAR_MAX) {
211
0
        rq_msg->rtm_table = RT_TABLE_UNSPEC;
212
0
        nl_msg_put_u32(&request, RTA_TABLE, id);
213
0
    } else {
214
0
        rq_msg->rtm_table = id;
215
0
    }
216
217
0
    nl_dump_start(&dump, NETLINK_ROUTE, &request);
218
0
    ofpbuf_uninit(&request);
219
220
0
    ofpbuf_use_stub(&buf, reply_stub, sizeof reply_stub);
221
0
    while (nl_dump_next(&dump, &reply, &buf)) {
222
0
        struct route_table_msg msg;
223
224
0
        if (route_table_parse(&reply, &msg)) {
225
0
            struct nlmsghdr *nlmsghdr = nl_msg_nlmsghdr(&reply);
226
227
            /* Older kernels do not support filtering. */
228
0
            if (!(nlmsghdr->nlmsg_flags & NLM_F_DUMP_FILTERED)) {
229
0
                filtered = false;
230
0
            }
231
0
            handle_msg_cb(&msg, aux, id);
232
0
            route_data_destroy(&msg.rd);
233
0
        }
234
0
    }
235
0
    ofpbuf_uninit(&buf);
236
0
    nl_dump_done(&dump);
237
238
0
    return filtered;
239
0
}
240
241
static void
242
rules_dump(void)
243
0
{
244
0
    uint64_t reply_stub[NL_DUMP_BUFSIZE / 8];
245
0
    struct ofpbuf request, reply, buf;
246
0
    struct fib_rule_hdr *rq_msg;
247
0
    struct nl_dump dump;
248
249
0
    ofpbuf_init(&request, 0);
250
251
0
    nl_msg_put_nlmsghdr(&request, sizeof *rq_msg, RTM_GETRULE, NLM_F_REQUEST);
252
253
0
    rq_msg = ofpbuf_put_zeros(&request, sizeof *rq_msg);
254
0
    rq_msg->family = AF_UNSPEC;
255
256
0
    nl_dump_start(&dump, NETLINK_ROUTE, &request);
257
0
    ofpbuf_uninit(&request);
258
259
0
    ofpbuf_use_stub(&buf, reply_stub, sizeof reply_stub);
260
0
    while (nl_dump_next(&dump, &reply, &buf)) {
261
0
        struct route_table_msg msg;
262
263
0
        if (rule_parse(&reply, &msg)) {
264
0
            rule_handle_msg(&msg);
265
0
        }
266
0
    }
267
0
    ofpbuf_uninit(&buf);
268
0
    nl_dump_done(&dump);
269
0
}
270
271
static void
272
route_table_reset(void)
273
0
{
274
0
    route_map_clear();
275
0
    netdev_get_addrs_list_flush();
276
0
    route_table_valid = true;
277
0
    rules_valid = true;
278
0
    rt_change_seq++;
279
0
    COVERAGE_INC(route_table_dump);
280
0
    rules_dump();
281
0
}
282
283
static void
284
rule_handle_msg(const struct route_table_msg *change)
285
0
{
286
0
    if (change->relevant) {
287
0
        const struct rule_data *rd = &change->rud;
288
289
0
        route_table_dump_one_table(rd->lookup_table, route_table_handle_msg,
290
0
                                   NULL);
291
0
        ovs_router_rule_add(rd->prio, rd->invert, false, rd->src_len,
292
0
                            &rd->from_addr, rd->lookup_table, rd->ipv4);
293
0
    }
294
0
}
295
296
static int
297
route_nln_parse(struct ofpbuf *buf, void *change_)
298
0
{
299
0
    const struct nlmsghdr *nlmsg = buf->data;
300
301
0
    if (nlmsg->nlmsg_type == RTM_NEWROUTE ||
302
0
        nlmsg->nlmsg_type == RTM_DELROUTE) {
303
0
        return route_table_parse(buf, change_);
304
0
    } else if (nlmsg->nlmsg_type == RTM_NEWRULE ||
305
0
               nlmsg->nlmsg_type == RTM_DELRULE) {
306
0
        return rule_parse(buf, change_);
307
0
    }
308
309
0
    VLOG_DBG_RL(&rl, "received unsupported rtnetlink route message");
310
0
    return 0;
311
0
}
312
313
/* Return RTNLGRP_IPV4_RULE or RTNLGRP_IPV6_RULE on success, 0 on parse
314
 * error. */
315
static int
316
rule_parse(struct ofpbuf *buf, void *change_)
317
0
{
318
0
    struct route_table_msg *change = change_;
319
0
    bool ipv4 = false;
320
0
    bool parsed;
321
322
0
    static const struct nl_policy policy[] = {
323
0
        [FRA_PRIORITY] = { .type = NL_A_U32, .optional = true },
324
0
        [FRA_SRC] = { .type = NL_A_U32, .optional = true },
325
0
        [FRA_TABLE] = { .type = NL_A_U32, .optional = true },
326
0
    };
327
328
0
    static const struct nl_policy policy6[] = {
329
0
        [FRA_PRIORITY] = { .type = NL_A_U32, .optional = true },
330
0
        [FRA_SRC] = { .type = NL_A_IPV6, .optional = true },
331
0
        [FRA_TABLE] = { .type = NL_A_U32, .optional = true },
332
0
    };
333
334
0
    struct nlattr *attrs[ARRAY_SIZE(policy)];
335
0
    const struct fib_rule_hdr *frh;
336
337
0
    frh = ofpbuf_at(buf, NLMSG_HDRLEN, sizeof *frh);
338
0
    if (!frh || frh->action != FR_ACT_TO_TBL || frh->tos || frh->dst_len) {
339
        /* Unsupported rule. */
340
0
        return 0;
341
0
    }
342
343
0
    if (frh->family == AF_INET) {
344
0
        parsed = nl_policy_parse(buf, NLMSG_HDRLEN +
345
0
                                 sizeof(struct fib_rule_hdr), policy, attrs,
346
0
                                 ARRAY_SIZE(policy));
347
0
        ipv4 = true;
348
0
    } else if (frh->family == AF_INET6) {
349
0
        parsed = nl_policy_parse(buf, NLMSG_HDRLEN +
350
0
                                 sizeof(struct fib_rule_hdr), policy6, attrs,
351
0
                                 ARRAY_SIZE(policy6));
352
0
    } else {
353
0
        VLOG_DBG_RL(&rl, "received non AF_INET rtnetlink route message");
354
0
        return 0;
355
0
    }
356
357
0
    if (parsed) {
358
0
        const struct nlmsghdr *nlmsg;
359
360
0
        nlmsg = buf->data;
361
362
0
        memset(change, 0, sizeof *change);
363
0
        change->relevant = true;
364
0
        change->nlmsg_type = nlmsg->nlmsg_type;
365
0
        change->rud.invert = false;
366
0
        change->rud.src_len = frh->src_len;
367
0
        change->rud.lookup_table = frh->table;
368
0
        change->rud.ipv4 = ipv4;
369
370
0
        if (frh->flags & FIB_RULE_INVERT) {
371
            /* Invert the matching of rule selector. */
372
0
            change->rud.invert = true;
373
0
        }
374
375
0
        if (attrs[FRA_PRIORITY]) {
376
0
            change->rud.prio = nl_attr_get_u32(attrs[FRA_PRIORITY]);
377
0
        }
378
379
0
        if (attrs[FRA_SRC]) {
380
0
            if (ipv4) {
381
0
                ovs_be32 src = nl_attr_get_be32(attrs[FRA_SRC]);
382
0
                in6_addr_set_mapped_ipv4(&change->rud.from_addr, src);
383
0
            } else {
384
0
                change->rud.from_addr = nl_attr_get_in6_addr(attrs[FRA_SRC]);
385
0
            }
386
0
        } else {
387
0
            if (ipv4) {
388
0
                in6_addr_set_mapped_ipv4(&change->rud.from_addr, 0);
389
0
            } else {
390
0
                change->rud.from_addr = in6addr_any;
391
0
            }
392
0
        }
393
394
0
        if (attrs[FRA_TABLE]) {
395
0
            change->rud.lookup_table = nl_attr_get_u32(attrs[FRA_TABLE]);
396
0
        } else {
397
0
            change->relevant = false;
398
0
        }
399
400
0
        if (change->rud.invert && !change->rud.src_len) {
401
0
            change->relevant = false;
402
0
        }
403
0
    } else {
404
0
        VLOG_DBG_RL(&rl, "received unparseable rtnetlink rule message");
405
0
        return 0;
406
0
    }
407
408
    /* Check if there are any additional attributes that aren't supported
409
     * currently by OVS rule-based route lookup. */
410
0
    if (change->relevant) {
411
0
        size_t offset = NLMSG_HDRLEN + sizeof(struct fib_rule_hdr);
412
0
        struct nlattr *nla;
413
0
        size_t left;
414
415
0
        NL_ATTR_FOR_EACH (nla, left, ofpbuf_at(buf, offset, 0),
416
0
                          buf->size - offset) {
417
0
            uint16_t type = nl_attr_type(nla);
418
419
0
            if ((type > FRA_SRC && type < FRA_PRIORITY) ||
420
0
                (type > FRA_PRIORITY && type < FRA_SUPPRESS_PREFIXLEN) ||
421
0
                (type > FRA_TABLE && type < FRA_PROTOCOL) ||
422
0
                type > FRA_PROTOCOL) {
423
0
                change->relevant = false;
424
0
                break;
425
0
            }
426
0
        }
427
0
    }
428
429
    /* Success. */
430
0
    return ipv4 ? RTNLGRP_IPV4_RULE : RTNLGRP_IPV6_RULE;
431
0
}
432
433
/* Returns true if the given route requires nexthop information (output
434
 * interface, nexthop IP, ...).  Returns false for special route types
435
 * that don't need this information. */
436
static bool
437
route_type_needs_nexthop(unsigned char rtmsg_type)
438
0
{
439
0
    switch (rtmsg_type) {
440
0
    case RTN_BLACKHOLE:
441
0
    case RTN_THROW:
442
0
    case RTN_UNREACHABLE:
443
0
    case RTN_PROHIBIT:
444
0
        return false;
445
446
0
    default:
447
0
        return true;
448
0
    }
449
0
}
450
451
static int
452
route_table_parse__(struct ofpbuf *buf, size_t ofs,
453
                    const struct nlmsghdr *nlmsg,
454
                    const struct rtmsg *rtm,
455
                    const struct rtnexthop *rtnh,
456
                    struct route_table_msg *change)
457
0
{
458
0
    bool parsed, ipv4 = false;
459
460
0
    static const struct nl_policy policy[] = {
461
0
        [RTA_DST] = { .type = NL_A_U32, .optional = true  },
462
0
        [RTA_OIF] = { .type = NL_A_U32, .optional = true },
463
0
        [RTA_GATEWAY] = { .type = NL_A_U32, .optional = true },
464
0
        [RTA_MARK] = { .type = NL_A_U32, .optional = true },
465
0
        [RTA_PREFSRC] = { .type = NL_A_U32, .optional = true },
466
0
        [RTA_TABLE] = { .type = NL_A_U32, .optional = true },
467
0
        [RTA_PRIORITY] = { .type = NL_A_U32, .optional = true },
468
0
        [RTA_VIA] = { .type = NL_A_RTA_VIA, .optional = true },
469
0
        [RTA_MULTIPATH] = { .type = NL_A_NESTED, .optional = true },
470
0
    };
471
472
0
    static const struct nl_policy policy6[] = {
473
0
        [RTA_DST] = { .type = NL_A_IPV6, .optional = true },
474
0
        [RTA_OIF] = { .type = NL_A_U32, .optional = true },
475
0
        [RTA_MARK] = { .type = NL_A_U32, .optional = true },
476
0
        [RTA_GATEWAY] = { .type = NL_A_IPV6, .optional = true },
477
0
        [RTA_PREFSRC] = { .type = NL_A_IPV6, .optional = true },
478
0
        [RTA_TABLE] = { .type = NL_A_U32, .optional = true },
479
0
        [RTA_PRIORITY] = { .type = NL_A_U32, .optional = true },
480
0
        [RTA_VIA] = { .type = NL_A_RTA_VIA, .optional = true },
481
0
        [RTA_MULTIPATH] = { .type = NL_A_NESTED, .optional = true },
482
0
    };
483
484
0
    struct nlattr *attrs[ARRAY_SIZE(policy)];
485
486
0
    if (rtm->rtm_family == AF_INET) {
487
0
        parsed = nl_policy_parse(buf, ofs, policy, attrs,
488
0
                                 ARRAY_SIZE(policy));
489
0
        ipv4 = true;
490
0
    } else if (rtm->rtm_family == AF_INET6) {
491
0
        parsed = nl_policy_parse(buf, ofs, policy6, attrs,
492
0
                                 ARRAY_SIZE(policy6));
493
0
    } else {
494
0
        VLOG_DBG_RL(&rl, "received non AF_INET rtnetlink route message");
495
0
        return 0;
496
0
    }
497
498
0
    if (parsed) {
499
0
        struct route_data_nexthop *rdnh = NULL;
500
0
        int rta_oif;      /* Output interface index. */
501
502
0
        memset(change, 0, sizeof *change);
503
504
0
        ovs_list_init(&change->rd.nexthops);
505
0
        rdnh = rtnh ? xzalloc(sizeof *rdnh) : &change->rd.primary_next_hop__;
506
0
        ovs_list_insert(&change->rd.nexthops, &rdnh->nexthop_node);
507
508
0
        rdnh->family = rtm->rtm_family;
509
0
        change->relevant = true;
510
511
0
        if (rtm->rtm_scope == RT_SCOPE_NOWHERE) {
512
0
            change->relevant = false;
513
0
        }
514
515
0
        if (rtm->rtm_type != RTN_UNICAST &&
516
0
            rtm->rtm_type != RTN_LOCAL) {
517
0
            change->relevant = false;
518
0
        }
519
520
0
        change->rd.rta_table_id = rtm->rtm_table;
521
0
        if (attrs[RTA_TABLE]) {
522
0
            change->rd.rta_table_id = nl_attr_get_u32(attrs[RTA_TABLE]);
523
0
        }
524
525
0
        change->nlmsg_type     = nlmsg->nlmsg_type;
526
0
        change->rd.rtm_dst_len = rtm->rtm_dst_len;
527
0
        change->rd.rtm_protocol = rtm->rtm_protocol;
528
0
        change->rd.rtn_local = rtm->rtm_type == RTN_LOCAL;
529
0
        if (attrs[RTA_OIF] && rtnh) {
530
0
            VLOG_DBG_RL(&rl, "unexpected RTA_OIF attribute while parsing "
531
0
                             "nested RTA_MULTIPATH attributes");
532
0
            goto error_out;
533
0
        }
534
0
        if (attrs[RTA_OIF] || rtnh) {
535
0
            rta_oif = rtnh ? rtnh->rtnh_ifindex
536
0
                           : nl_attr_get_u32(attrs[RTA_OIF]);
537
538
0
            if (!if_indextoname(rta_oif, rdnh->ifname)) {
539
0
                int error = errno;
540
541
0
                VLOG_DBG_RL(&rl, "could not find interface name[%u]: %s",
542
0
                            rta_oif, ovs_strerror(error));
543
0
                if (error == ENXIO) {
544
0
                    change->relevant = false;
545
0
                } else {
546
0
                    goto error_out;
547
0
                }
548
0
            }
549
0
        }
550
551
0
        if (attrs[RTA_DST]) {
552
0
            if (ipv4) {
553
0
                ovs_be32 dst;
554
0
                dst = nl_attr_get_be32(attrs[RTA_DST]);
555
0
                in6_addr_set_mapped_ipv4(&change->rd.rta_dst, dst);
556
0
            } else {
557
0
                change->rd.rta_dst = nl_attr_get_in6_addr(attrs[RTA_DST]);
558
0
            }
559
0
        } else if (ipv4) {
560
0
            in6_addr_set_mapped_ipv4(&change->rd.rta_dst, 0);
561
0
        }
562
0
        if (attrs[RTA_PREFSRC]) {
563
0
            if (ipv4) {
564
0
                ovs_be32 prefsrc;
565
0
                prefsrc = nl_attr_get_be32(attrs[RTA_PREFSRC]);
566
0
                in6_addr_set_mapped_ipv4(&change->rd.rta_prefsrc, prefsrc);
567
0
            } else {
568
0
                change->rd.rta_prefsrc =
569
0
                    nl_attr_get_in6_addr(attrs[RTA_PREFSRC]);
570
0
            }
571
0
        }
572
0
        if (attrs[RTA_GATEWAY]) {
573
0
            if (ipv4) {
574
0
                ovs_be32 gw;
575
0
                gw = nl_attr_get_be32(attrs[RTA_GATEWAY]);
576
0
                in6_addr_set_mapped_ipv4(&rdnh->addr, gw);
577
0
            } else {
578
0
                rdnh->addr = nl_attr_get_in6_addr(attrs[RTA_GATEWAY]);
579
0
            }
580
0
        }
581
0
        if (attrs[RTA_MARK]) {
582
0
            change->rd.rta_mark = nl_attr_get_u32(attrs[RTA_MARK]);
583
0
        }
584
0
        if (attrs[RTA_PRIORITY]) {
585
0
            change->rd.rta_priority = nl_attr_get_u32(attrs[RTA_PRIORITY]);
586
0
        }
587
0
        if (attrs[RTA_VIA]) {
588
0
            const struct rtvia *rtvia = nl_attr_get(attrs[RTA_VIA]);
589
0
            ovs_be32 addr;
590
591
0
            if (attrs[RTA_GATEWAY]) {
592
0
                VLOG_DBG_RL(&rl, "route message can not contain both "
593
0
                                 "RTA_GATEWAY and RTA_VIA");
594
0
                goto error_out;
595
0
            }
596
597
0
            rdnh->family = rtvia->rtvia_family;
598
599
0
            switch (rdnh->family) {
600
0
            case AF_INET:
601
0
                if (nl_attr_get_size(attrs[RTA_VIA])
602
0
                        - sizeof *rtvia < sizeof addr) {
603
0
                    VLOG_DBG_RL(&rl, "got short message while parsing RTA_VIA "
604
0
                                     "attribute for family AF_INET");
605
0
                    goto error_out;
606
0
                }
607
0
                memcpy(&addr, rtvia->rtvia_addr, sizeof addr);
608
0
                in6_addr_set_mapped_ipv4(&rdnh->addr, addr);
609
0
                break;
610
611
0
            case AF_INET6:
612
0
                if (nl_attr_get_size(attrs[RTA_VIA])
613
0
                        - sizeof *rtvia < sizeof rdnh->addr) {
614
0
                    VLOG_DBG_RL(&rl, "got short message while parsing RTA_VIA "
615
0
                                     "attribute for family AF_INET6");
616
0
                    goto error_out;
617
0
                }
618
0
                memcpy(&rdnh->addr, rtvia->rtvia_addr, sizeof rdnh->addr);
619
0
                break;
620
621
0
            default:
622
0
                VLOG_DBG_RL(&rl, "unsupported address family, %d, "
623
0
                                 "in via attribute", rdnh->family);
624
0
                goto error_out;
625
0
            }
626
0
        }
627
0
        if (attrs[RTA_MULTIPATH]) {
628
0
            const struct nlattr *nla;
629
0
            size_t left;
630
631
0
            if (rtnh) {
632
0
                VLOG_DBG_RL(&rl, "unexpected nested RTA_MULTIPATH attribute");
633
0
                goto error_out;
634
0
            }
635
636
            /* The change->rd->nexthops list is unconditionally populated with
637
             * a single rdnh entry as we start parsing above.  Multiple
638
             * branches above may access it or jump to error_out, and having it
639
             * on the list is the only way to ensure proper cleanup.
640
             *
641
             * Getting to this point, we know that the above branches has not
642
             * provided next hop information, because information about
643
             * multiple next hops is encoded in the nested attributes after the
644
             * RTA_MULTIPATH attribute.
645
             *
646
             * Before retrieving those we need to remove the empty rdnh entry
647
             * from the list. */
648
0
            route_data_destroy_nexthops__(&change->rd);
649
650
0
            NL_NESTED_FOR_EACH (nla, left, attrs[RTA_MULTIPATH]) {
651
0
                struct route_table_msg mp_change;
652
0
                struct rtnexthop *mp_rtnh;
653
0
                struct ofpbuf mp_buf;
654
655
0
                ofpbuf_use_const(&mp_buf, nla, nla->nla_len);
656
0
                mp_rtnh = ofpbuf_try_pull(&mp_buf, sizeof *mp_rtnh);
657
658
0
                if (!mp_rtnh) {
659
0
                    VLOG_DBG_RL(&rl, "got short message while parsing "
660
0
                                     "multipath attribute");
661
0
                    goto error_out;
662
0
                }
663
664
0
                if (!route_table_parse__(&mp_buf, 0, nlmsg, rtm, mp_rtnh,
665
0
                                         &mp_change)) {
666
0
                    goto error_out;
667
0
                }
668
0
                ovs_list_push_back_all(&change->rd.nexthops,
669
0
                                       &mp_change.rd.nexthops);
670
0
            }
671
0
        }
672
0
        if (route_type_needs_nexthop(rtm->rtm_type)
673
0
            && !attrs[RTA_OIF] && !attrs[RTA_GATEWAY]
674
0
            && !attrs[RTA_VIA] && !attrs[RTA_MULTIPATH]) {
675
0
            VLOG_DBG_RL(&rl, "route message needs an RTA_OIF, RTA_GATEWAY, "
676
0
                             "RTA_VIA or RTA_MULTIPATH attribute");
677
0
            goto error_out;
678
0
        }
679
        /* Add any additional RTA attribute processing before RTA_MULTIPATH. */
680
681
        /* Ensure that the change->rd->nexthops list is cleared in cases when
682
         * the route does not need a next hop. */
683
0
        if (!route_type_needs_nexthop(rtm->rtm_type)) {
684
0
            route_data_destroy_nexthops__(&change->rd);
685
0
        }
686
0
    } else {
687
0
        VLOG_DBG_RL(&rl, "received unparseable rtnetlink route message");
688
0
        goto error_out;
689
0
    }
690
691
    /* Success. */
692
0
    return ipv4 ? RTNLGRP_IPV4_ROUTE : RTNLGRP_IPV6_ROUTE;
693
694
0
error_out:
695
0
    route_data_destroy(&change->rd);
696
0
    return 0;
697
0
}
698
699
/* Parse Netlink message in buf, which is expected to contain a UAPI rtmsg
700
 * header and associated route attributes.
701
 *
702
 * Return RTNLGRP_IPV4_ROUTE or RTNLGRP_IPV6_ROUTE on success, and 0 on a parse
703
 * error.
704
 *
705
 * On success, memory may have been allocated, and it is the caller's
706
 * responsibility to free it with a call to route_data_destroy().
707
 *
708
 * In case of error, any allocated memory will be freed before returning. */
709
int
710
route_table_parse(struct ofpbuf *buf, void *change)
711
0
{
712
0
    struct nlmsghdr *nlmsg;
713
0
    struct rtmsg *rtm;
714
715
0
    nlmsg = ofpbuf_at(buf, 0, NLMSG_HDRLEN);
716
0
    rtm = ofpbuf_at(buf, NLMSG_HDRLEN, sizeof *rtm);
717
718
0
    if (!nlmsg || !rtm) {
719
0
        return 0;
720
0
    }
721
722
0
    return route_table_parse__(buf, NLMSG_HDRLEN + sizeof *rtm,
723
0
                               nlmsg, rtm, NULL, change);
724
0
}
725
726
static void
727
route_table_change(struct route_table_msg *change, void *aux OVS_UNUSED)
728
0
{
729
0
    if (!change
730
0
        || (change->relevant
731
0
            && ovs_router_is_referenced(change->rd.rta_table_id))) {
732
0
        route_table_valid = false;
733
0
    }
734
0
    if (change) {
735
0
        route_data_destroy(&change->rd);
736
0
    }
737
0
}
738
739
static void
740
route_table_handle_msg(const struct route_table_msg *change,
741
                       void *aux OVS_UNUSED, uint32_t table)
742
0
{
743
0
    if (change->relevant && change->nlmsg_type == RTM_NEWROUTE
744
0
            && !ovs_list_is_empty(&change->rd.nexthops)) {
745
0
        const struct route_data *rd = &change->rd;
746
0
        const struct route_data_nexthop *rdnh;
747
748
        /* The ovs-router module currently does not implement lookup or
749
         * storage for routes with multiple next hops.  For backwards
750
         * compatibility, we use the first next hop. */
751
0
        rdnh = CONTAINER_OF(ovs_list_front(&change->rd.nexthops),
752
0
                            const struct route_data_nexthop, nexthop_node);
753
754
0
        ovs_router_insert(table, rd->rta_mark, &rd->rta_dst,
755
0
                          IN6_IS_ADDR_V4MAPPED(&rd->rta_dst)
756
0
                          ? rd->rtm_dst_len + 96 : rd->rtm_dst_len,
757
0
                          false, rdnh->ifname, &rdnh->addr, &rd->rta_prefsrc);
758
0
    }
759
0
}
760
761
static void
762
rules_change(const struct route_table_msg *change OVS_UNUSED,
763
             void *aux OVS_UNUSED)
764
0
{
765
0
    if (!change || change->relevant) {
766
0
        rules_valid = false;
767
0
    }
768
0
}
769
770
static void
771
route_map_clear(void)
772
0
{
773
0
    ovs_router_rules_flush(false);
774
0
    ovs_router_flush(false);
775
0
}
776
777
bool
778
route_table_fallback_lookup(const struct in6_addr *ip6_dst OVS_UNUSED,
779
                            char name[] OVS_UNUSED,
780
                            struct in6_addr *gw6)
781
0
{
782
0
    *gw6 = in6addr_any;
783
0
    return false;
784
0
}
785
786

787
/* name_table . */
788
789
static void
790
name_table_init(void)
791
0
{
792
0
    name_notifier = rtnetlink_notifier_create(name_table_change, NULL);
793
0
}
794
795
796
static void
797
name_table_change(const struct rtnetlink_change *change,
798
                  void *aux OVS_UNUSED)
799
0
{
800
0
    if (change && change->irrelevant) {
801
0
        return;
802
0
    }
803
804
    /* Changes to interface status can cause routing table changes that some
805
     * versions of the linux kernel do not advertise for some reason. */
806
0
    route_table_valid = false;
807
808
0
    if (change && change->nlmsg_type == RTM_DELLINK) {
809
0
        if (change->ifname) {
810
0
            tnl_port_map_delete_ipdev(change->ifname);
811
0
        }
812
0
    }
813
0
}