Coverage Report

Created: 2026-06-10 06:40

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, sa_family_t family,
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 = family;
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
0
        sa_family_t family = rd->ipv4 ? AF_INET : AF_INET6;
289
290
0
        route_table_dump_one_table(rd->lookup_table, family,
291
0
                                   route_table_handle_msg, NULL);
292
0
        ovs_router_rule_add(rd->prio, rd->invert, false, rd->src_len,
293
0
                            &rd->from_addr, rd->lookup_table, rd->ipv4);
294
0
    }
295
0
}
296
297
static int
298
route_nln_parse(struct ofpbuf *buf, void *change_)
299
0
{
300
0
    const struct nlmsghdr *nlmsg = buf->data;
301
302
0
    if (nlmsg->nlmsg_type == RTM_NEWROUTE ||
303
0
        nlmsg->nlmsg_type == RTM_DELROUTE) {
304
0
        return route_table_parse(buf, change_);
305
0
    } else if (nlmsg->nlmsg_type == RTM_NEWRULE ||
306
0
               nlmsg->nlmsg_type == RTM_DELRULE) {
307
0
        return rule_parse(buf, change_);
308
0
    }
309
310
0
    VLOG_DBG_RL(&rl, "received unsupported rtnetlink route message");
311
0
    return 0;
312
0
}
313
314
/* Return RTNLGRP_IPV4_RULE or RTNLGRP_IPV6_RULE on success, 0 on parse
315
 * error. */
316
static int
317
rule_parse(struct ofpbuf *buf, void *change_)
318
0
{
319
0
    struct route_table_msg *change = change_;
320
0
    bool ipv4 = false;
321
0
    bool parsed;
322
323
0
    static const struct nl_policy policy[] = {
324
0
        [FRA_PRIORITY] = { .type = NL_A_U32, .optional = true },
325
0
        [FRA_SRC] = { .type = NL_A_U32, .optional = true },
326
0
        [FRA_TABLE] = { .type = NL_A_U32, .optional = true },
327
0
    };
328
329
0
    static const struct nl_policy policy6[] = {
330
0
        [FRA_PRIORITY] = { .type = NL_A_U32, .optional = true },
331
0
        [FRA_SRC] = { .type = NL_A_IPV6, .optional = true },
332
0
        [FRA_TABLE] = { .type = NL_A_U32, .optional = true },
333
0
    };
334
335
0
    struct nlattr *attrs[ARRAY_SIZE(policy)];
336
0
    const struct fib_rule_hdr *frh;
337
338
0
    frh = ofpbuf_at(buf, NLMSG_HDRLEN, sizeof *frh);
339
0
    if (!frh || frh->action != FR_ACT_TO_TBL || frh->tos || frh->dst_len) {
340
        /* Unsupported rule. */
341
0
        return 0;
342
0
    }
343
344
0
    if (frh->family == AF_INET) {
345
0
        parsed = nl_policy_parse(buf, NLMSG_HDRLEN +
346
0
                                 sizeof(struct fib_rule_hdr), policy, attrs,
347
0
                                 ARRAY_SIZE(policy));
348
0
        ipv4 = true;
349
0
    } else if (frh->family == AF_INET6) {
350
0
        parsed = nl_policy_parse(buf, NLMSG_HDRLEN +
351
0
                                 sizeof(struct fib_rule_hdr), policy6, attrs,
352
0
                                 ARRAY_SIZE(policy6));
353
0
    } else {
354
0
        VLOG_DBG_RL(&rl, "received non AF_INET rtnetlink route message");
355
0
        return 0;
356
0
    }
357
358
0
    if (parsed) {
359
0
        const struct nlmsghdr *nlmsg;
360
361
0
        nlmsg = buf->data;
362
363
0
        memset(change, 0, sizeof *change);
364
0
        change->relevant = true;
365
0
        change->nlmsg_type = nlmsg->nlmsg_type;
366
0
        change->rud.invert = false;
367
0
        change->rud.src_len = frh->src_len;
368
0
        change->rud.lookup_table = frh->table;
369
0
        change->rud.ipv4 = ipv4;
370
371
0
        if (frh->flags & FIB_RULE_INVERT) {
372
            /* Invert the matching of rule selector. */
373
0
            change->rud.invert = true;
374
0
        }
375
376
0
        if (attrs[FRA_PRIORITY]) {
377
0
            change->rud.prio = nl_attr_get_u32(attrs[FRA_PRIORITY]);
378
0
        }
379
380
0
        if (attrs[FRA_SRC]) {
381
0
            if (ipv4) {
382
0
                ovs_be32 src = nl_attr_get_be32(attrs[FRA_SRC]);
383
0
                in6_addr_set_mapped_ipv4(&change->rud.from_addr, src);
384
0
            } else {
385
0
                change->rud.from_addr = nl_attr_get_in6_addr(attrs[FRA_SRC]);
386
0
            }
387
0
        } else {
388
0
            if (ipv4) {
389
0
                in6_addr_set_mapped_ipv4(&change->rud.from_addr, 0);
390
0
            } else {
391
0
                change->rud.from_addr = in6addr_any;
392
0
            }
393
0
        }
394
395
0
        if (attrs[FRA_TABLE]) {
396
0
            change->rud.lookup_table = nl_attr_get_u32(attrs[FRA_TABLE]);
397
0
        } else {
398
0
            change->relevant = false;
399
0
        }
400
401
0
        if (change->rud.invert && !change->rud.src_len) {
402
0
            change->relevant = false;
403
0
        }
404
0
    } else {
405
0
        VLOG_DBG_RL(&rl, "received unparseable rtnetlink rule message");
406
0
        return 0;
407
0
    }
408
409
    /* Check if there are any additional attributes that aren't supported
410
     * currently by OVS rule-based route lookup. */
411
0
    if (change->relevant) {
412
0
        size_t offset = NLMSG_HDRLEN + sizeof(struct fib_rule_hdr);
413
0
        struct nlattr *nla;
414
0
        size_t left;
415
416
0
        NL_ATTR_FOR_EACH (nla, left, ofpbuf_at(buf, offset, 0),
417
0
                          buf->size - offset) {
418
0
            uint16_t type = nl_attr_type(nla);
419
420
0
            if ((type > FRA_SRC && type < FRA_PRIORITY) ||
421
0
                (type > FRA_PRIORITY && type < FRA_SUPPRESS_PREFIXLEN) ||
422
0
                (type > FRA_TABLE && type < FRA_PROTOCOL) ||
423
0
                type > FRA_PROTOCOL) {
424
0
                change->relevant = false;
425
0
                break;
426
0
            }
427
0
        }
428
0
    }
429
430
    /* Success. */
431
0
    return ipv4 ? RTNLGRP_IPV4_RULE : RTNLGRP_IPV6_RULE;
432
0
}
433
434
/* Returns true if the given route requires nexthop information (output
435
 * interface, nexthop IP, ...).  Returns false for special route types
436
 * that don't need this information. */
437
static bool
438
route_type_needs_nexthop(unsigned char rtmsg_type)
439
0
{
440
0
    switch (rtmsg_type) {
441
0
    case RTN_BLACKHOLE:
442
0
    case RTN_THROW:
443
0
    case RTN_UNREACHABLE:
444
0
    case RTN_PROHIBIT:
445
0
        return false;
446
447
0
    default:
448
0
        return true;
449
0
    }
450
0
}
451
452
static int
453
route_table_parse__(struct ofpbuf *buf, size_t ofs,
454
                    const struct nlmsghdr *nlmsg,
455
                    const struct rtmsg *rtm,
456
                    const struct rtnexthop *rtnh,
457
                    struct route_table_msg *change)
458
0
{
459
0
    bool parsed, ipv4 = false;
460
461
0
    static const struct nl_policy policy[] = {
462
0
        [RTA_DST] = { .type = NL_A_U32, .optional = true  },
463
0
        [RTA_OIF] = { .type = NL_A_U32, .optional = true },
464
0
        [RTA_GATEWAY] = { .type = NL_A_U32, .optional = true },
465
0
        [RTA_MARK] = { .type = NL_A_U32, .optional = true },
466
0
        [RTA_PREFSRC] = { .type = NL_A_U32, .optional = true },
467
0
        [RTA_TABLE] = { .type = NL_A_U32, .optional = true },
468
0
        [RTA_PRIORITY] = { .type = NL_A_U32, .optional = true },
469
0
        [RTA_VIA] = { .type = NL_A_RTA_VIA, .optional = true },
470
0
        [RTA_MULTIPATH] = { .type = NL_A_NESTED, .optional = true },
471
0
    };
472
473
0
    static const struct nl_policy policy6[] = {
474
0
        [RTA_DST] = { .type = NL_A_IPV6, .optional = true },
475
0
        [RTA_OIF] = { .type = NL_A_U32, .optional = true },
476
0
        [RTA_MARK] = { .type = NL_A_U32, .optional = true },
477
0
        [RTA_GATEWAY] = { .type = NL_A_IPV6, .optional = true },
478
0
        [RTA_PREFSRC] = { .type = NL_A_IPV6, .optional = true },
479
0
        [RTA_TABLE] = { .type = NL_A_U32, .optional = true },
480
0
        [RTA_PRIORITY] = { .type = NL_A_U32, .optional = true },
481
0
        [RTA_VIA] = { .type = NL_A_RTA_VIA, .optional = true },
482
0
        [RTA_MULTIPATH] = { .type = NL_A_NESTED, .optional = true },
483
0
    };
484
485
0
    struct nlattr *attrs[ARRAY_SIZE(policy)];
486
487
0
    if (rtm->rtm_family == AF_INET) {
488
0
        parsed = nl_policy_parse(buf, ofs, policy, attrs,
489
0
                                 ARRAY_SIZE(policy));
490
0
        ipv4 = true;
491
0
    } else if (rtm->rtm_family == AF_INET6) {
492
0
        parsed = nl_policy_parse(buf, ofs, policy6, attrs,
493
0
                                 ARRAY_SIZE(policy6));
494
0
    } else {
495
0
        VLOG_DBG_RL(&rl, "received non AF_INET rtnetlink route message");
496
0
        return 0;
497
0
    }
498
499
0
    if (parsed) {
500
0
        struct route_data_nexthop *rdnh = NULL;
501
0
        int rta_oif;      /* Output interface index. */
502
503
0
        memset(change, 0, sizeof *change);
504
505
0
        ovs_list_init(&change->rd.nexthops);
506
0
        rdnh = rtnh ? xzalloc(sizeof *rdnh) : &change->rd.primary_next_hop__;
507
0
        ovs_list_insert(&change->rd.nexthops, &rdnh->nexthop_node);
508
509
0
        rdnh->family = rtm->rtm_family;
510
0
        change->relevant = true;
511
512
0
        if (rtm->rtm_scope == RT_SCOPE_NOWHERE) {
513
0
            change->relevant = false;
514
0
        }
515
516
0
        if (rtm->rtm_type != RTN_UNICAST &&
517
0
            rtm->rtm_type != RTN_LOCAL) {
518
0
            change->relevant = false;
519
0
        }
520
521
0
        change->rd.rta_table_id = rtm->rtm_table;
522
0
        if (attrs[RTA_TABLE]) {
523
0
            change->rd.rta_table_id = nl_attr_get_u32(attrs[RTA_TABLE]);
524
0
        }
525
526
0
        change->nlmsg_type     = nlmsg->nlmsg_type;
527
0
        change->rd.rtm_dst_len = rtm->rtm_dst_len;
528
0
        change->rd.rtm_protocol = rtm->rtm_protocol;
529
0
        change->rd.rtn_local = rtm->rtm_type == RTN_LOCAL;
530
0
        if (attrs[RTA_OIF] && rtnh) {
531
0
            VLOG_DBG_RL(&rl, "unexpected RTA_OIF attribute while parsing "
532
0
                             "nested RTA_MULTIPATH attributes");
533
0
            goto error_out;
534
0
        }
535
0
        if (attrs[RTA_OIF] || rtnh) {
536
0
            rta_oif = rtnh ? rtnh->rtnh_ifindex
537
0
                           : nl_attr_get_u32(attrs[RTA_OIF]);
538
539
0
            if (!if_indextoname(rta_oif, rdnh->ifname)) {
540
0
                int error = errno;
541
542
0
                VLOG_DBG_RL(&rl, "could not find interface name[%u]: %s",
543
0
                            rta_oif, ovs_strerror(error));
544
0
                if (error == ENXIO) {
545
0
                    change->relevant = false;
546
0
                } else {
547
0
                    goto error_out;
548
0
                }
549
0
            }
550
0
        }
551
552
0
        if (attrs[RTA_DST]) {
553
0
            if (ipv4) {
554
0
                ovs_be32 dst;
555
0
                dst = nl_attr_get_be32(attrs[RTA_DST]);
556
0
                in6_addr_set_mapped_ipv4(&change->rd.rta_dst, dst);
557
0
            } else {
558
0
                change->rd.rta_dst = nl_attr_get_in6_addr(attrs[RTA_DST]);
559
0
            }
560
0
        } else if (ipv4) {
561
0
            in6_addr_set_mapped_ipv4(&change->rd.rta_dst, 0);
562
0
        }
563
0
        if (attrs[RTA_PREFSRC]) {
564
0
            if (ipv4) {
565
0
                ovs_be32 prefsrc;
566
0
                prefsrc = nl_attr_get_be32(attrs[RTA_PREFSRC]);
567
0
                in6_addr_set_mapped_ipv4(&change->rd.rta_prefsrc, prefsrc);
568
0
            } else {
569
0
                change->rd.rta_prefsrc =
570
0
                    nl_attr_get_in6_addr(attrs[RTA_PREFSRC]);
571
0
            }
572
0
        }
573
0
        if (attrs[RTA_GATEWAY]) {
574
0
            if (ipv4) {
575
0
                ovs_be32 gw;
576
0
                gw = nl_attr_get_be32(attrs[RTA_GATEWAY]);
577
0
                in6_addr_set_mapped_ipv4(&rdnh->addr, gw);
578
0
            } else {
579
0
                rdnh->addr = nl_attr_get_in6_addr(attrs[RTA_GATEWAY]);
580
0
            }
581
0
        }
582
0
        if (attrs[RTA_MARK]) {
583
0
            change->rd.rta_mark = nl_attr_get_u32(attrs[RTA_MARK]);
584
0
        }
585
0
        if (attrs[RTA_PRIORITY]) {
586
0
            change->rd.rta_priority = nl_attr_get_u32(attrs[RTA_PRIORITY]);
587
0
        }
588
0
        if (attrs[RTA_VIA]) {
589
0
            const struct rtvia *rtvia = nl_attr_get(attrs[RTA_VIA]);
590
0
            ovs_be32 addr;
591
592
0
            if (attrs[RTA_GATEWAY]) {
593
0
                VLOG_DBG_RL(&rl, "route message can not contain both "
594
0
                                 "RTA_GATEWAY and RTA_VIA");
595
0
                goto error_out;
596
0
            }
597
598
0
            rdnh->family = rtvia->rtvia_family;
599
600
0
            switch (rdnh->family) {
601
0
            case AF_INET:
602
0
                if (nl_attr_get_size(attrs[RTA_VIA])
603
0
                        - sizeof *rtvia < sizeof addr) {
604
0
                    VLOG_DBG_RL(&rl, "got short message while parsing RTA_VIA "
605
0
                                     "attribute for family AF_INET");
606
0
                    goto error_out;
607
0
                }
608
0
                memcpy(&addr, rtvia->rtvia_addr, sizeof addr);
609
0
                in6_addr_set_mapped_ipv4(&rdnh->addr, addr);
610
0
                break;
611
612
0
            case AF_INET6:
613
0
                if (nl_attr_get_size(attrs[RTA_VIA])
614
0
                        - sizeof *rtvia < sizeof rdnh->addr) {
615
0
                    VLOG_DBG_RL(&rl, "got short message while parsing RTA_VIA "
616
0
                                     "attribute for family AF_INET6");
617
0
                    goto error_out;
618
0
                }
619
0
                memcpy(&rdnh->addr, rtvia->rtvia_addr, sizeof rdnh->addr);
620
0
                break;
621
622
0
            default:
623
0
                VLOG_DBG_RL(&rl, "unsupported address family, %d, "
624
0
                                 "in via attribute", rdnh->family);
625
0
                goto error_out;
626
0
            }
627
0
        }
628
0
        if (attrs[RTA_MULTIPATH]) {
629
0
            const struct nlattr *nla;
630
0
            size_t left;
631
632
0
            if (rtnh) {
633
0
                VLOG_DBG_RL(&rl, "unexpected nested RTA_MULTIPATH attribute");
634
0
                goto error_out;
635
0
            }
636
637
            /* The change->rd->nexthops list is unconditionally populated with
638
             * a single rdnh entry as we start parsing above.  Multiple
639
             * branches above may access it or jump to error_out, and having it
640
             * on the list is the only way to ensure proper cleanup.
641
             *
642
             * Getting to this point, we know that the above branches has not
643
             * provided next hop information, because information about
644
             * multiple next hops is encoded in the nested attributes after the
645
             * RTA_MULTIPATH attribute.
646
             *
647
             * Before retrieving those we need to remove the empty rdnh entry
648
             * from the list. */
649
0
            route_data_destroy_nexthops__(&change->rd);
650
651
0
            NL_NESTED_FOR_EACH (nla, left, attrs[RTA_MULTIPATH]) {
652
0
                struct route_table_msg mp_change;
653
0
                struct rtnexthop *mp_rtnh;
654
0
                struct ofpbuf mp_buf;
655
656
0
                ofpbuf_use_const(&mp_buf, nla, nla->nla_len);
657
0
                mp_rtnh = ofpbuf_try_pull(&mp_buf, sizeof *mp_rtnh);
658
659
0
                if (!mp_rtnh) {
660
0
                    VLOG_DBG_RL(&rl, "got short message while parsing "
661
0
                                     "multipath attribute");
662
0
                    goto error_out;
663
0
                }
664
665
0
                if (!route_table_parse__(&mp_buf, 0, nlmsg, rtm, mp_rtnh,
666
0
                                         &mp_change)) {
667
0
                    goto error_out;
668
0
                }
669
0
                ovs_list_push_back_all(&change->rd.nexthops,
670
0
                                       &mp_change.rd.nexthops);
671
0
            }
672
0
        }
673
0
        if (route_type_needs_nexthop(rtm->rtm_type)
674
0
            && !attrs[RTA_OIF] && !attrs[RTA_GATEWAY]
675
0
            && !attrs[RTA_VIA] && !attrs[RTA_MULTIPATH]) {
676
0
            VLOG_DBG_RL(&rl, "route message needs an RTA_OIF, RTA_GATEWAY, "
677
0
                             "RTA_VIA or RTA_MULTIPATH attribute");
678
0
            goto error_out;
679
0
        }
680
        /* Add any additional RTA attribute processing before RTA_MULTIPATH. */
681
682
        /* Ensure that the change->rd->nexthops list is cleared in cases when
683
         * the route does not need a next hop. */
684
0
        if (!route_type_needs_nexthop(rtm->rtm_type)) {
685
0
            route_data_destroy_nexthops__(&change->rd);
686
0
        }
687
0
    } else {
688
0
        VLOG_DBG_RL(&rl, "received unparseable rtnetlink route message");
689
0
        goto error_out;
690
0
    }
691
692
    /* Success. */
693
0
    return ipv4 ? RTNLGRP_IPV4_ROUTE : RTNLGRP_IPV6_ROUTE;
694
695
0
error_out:
696
0
    route_data_destroy(&change->rd);
697
0
    return 0;
698
0
}
699
700
/* Parse Netlink message in buf, which is expected to contain a UAPI rtmsg
701
 * header and associated route attributes.
702
 *
703
 * Return RTNLGRP_IPV4_ROUTE or RTNLGRP_IPV6_ROUTE on success, and 0 on a parse
704
 * error.
705
 *
706
 * On success, memory may have been allocated, and it is the caller's
707
 * responsibility to free it with a call to route_data_destroy().
708
 *
709
 * In case of error, any allocated memory will be freed before returning. */
710
int
711
route_table_parse(struct ofpbuf *buf, void *change)
712
0
{
713
0
    struct nlmsghdr *nlmsg;
714
0
    struct rtmsg *rtm;
715
716
0
    nlmsg = ofpbuf_at(buf, 0, NLMSG_HDRLEN);
717
0
    rtm = ofpbuf_at(buf, NLMSG_HDRLEN, sizeof *rtm);
718
719
0
    if (!nlmsg || !rtm) {
720
0
        return 0;
721
0
    }
722
723
0
    return route_table_parse__(buf, NLMSG_HDRLEN + sizeof *rtm,
724
0
                               nlmsg, rtm, NULL, change);
725
0
}
726
727
static void
728
route_table_change(struct route_table_msg *change, void *aux OVS_UNUSED)
729
0
{
730
0
    if (!change
731
0
        || (change->relevant
732
0
            && ovs_router_is_referenced(change->rd.rta_table_id))) {
733
0
        route_table_valid = false;
734
0
    }
735
0
    if (change) {
736
0
        route_data_destroy(&change->rd);
737
0
    }
738
0
}
739
740
static void
741
route_table_handle_msg(const struct route_table_msg *change,
742
                       void *aux OVS_UNUSED, uint32_t table)
743
0
{
744
0
    if (change->relevant && change->nlmsg_type == RTM_NEWROUTE
745
0
            && !ovs_list_is_empty(&change->rd.nexthops)) {
746
0
        const struct route_data *rd = &change->rd;
747
0
        const struct route_data_nexthop *rdnh;
748
749
        /* The ovs-router module currently does not implement lookup or
750
         * storage for routes with multiple next hops.  For backwards
751
         * compatibility, we use the first next hop. */
752
0
        rdnh = CONTAINER_OF(ovs_list_front(&change->rd.nexthops),
753
0
                            const struct route_data_nexthop, nexthop_node);
754
755
0
        ovs_router_insert(table, rd->rta_mark, &rd->rta_dst,
756
0
                          IN6_IS_ADDR_V4MAPPED(&rd->rta_dst)
757
0
                          ? rd->rtm_dst_len + 96 : rd->rtm_dst_len,
758
0
                          false, rdnh->ifname, &rdnh->addr, &rd->rta_prefsrc);
759
0
    }
760
0
}
761
762
static void
763
rules_change(const struct route_table_msg *change OVS_UNUSED,
764
             void *aux OVS_UNUSED)
765
0
{
766
0
    if (!change || change->relevant) {
767
0
        rules_valid = false;
768
0
    }
769
0
}
770
771
static void
772
route_map_clear(void)
773
0
{
774
0
    ovs_router_rules_flush(false);
775
0
    ovs_router_flush(false);
776
0
}
777
778
bool
779
route_table_fallback_lookup(const struct in6_addr *ip6_dst OVS_UNUSED,
780
                            char name[] OVS_UNUSED,
781
                            struct in6_addr *gw6)
782
0
{
783
0
    *gw6 = in6addr_any;
784
0
    return false;
785
0
}
786
787

788
/* name_table . */
789
790
static void
791
name_table_init(void)
792
0
{
793
0
    name_notifier = rtnetlink_notifier_create(name_table_change, NULL);
794
0
}
795
796
797
static void
798
name_table_change(const struct rtnetlink_change *change,
799
                  void *aux OVS_UNUSED)
800
0
{
801
0
    if (change && change->irrelevant) {
802
0
        return;
803
0
    }
804
805
    /* Changes to interface status can cause routing table changes that some
806
     * versions of the linux kernel do not advertise for some reason. */
807
0
    route_table_valid = false;
808
809
0
    if (change && change->nlmsg_type == RTM_DELLINK) {
810
0
        if (change->ifname) {
811
0
            tnl_port_map_delete_ipdev(change->ifname);
812
0
        }
813
0
    }
814
0
}