Coverage Report

Created: 2025-07-01 06:50

/src/openvswitch/lib/route-table.c
Line
Count
Source (jump to first uncovered line)
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/rtnetlink.h>
27
#include <net/if.h>
28
29
#include "coverage.h"
30
#include "hash.h"
31
#include "netdev.h"
32
#include "netlink.h"
33
#include "netlink-notifier.h"
34
#include "netlink-socket.h"
35
#include "openvswitch/list.h"
36
#include "openvswitch/ofpbuf.h"
37
#include "ovs-router.h"
38
#include "packets.h"
39
#include "rtnetlink.h"
40
#include "tnl-ports.h"
41
#include "openvswitch/vlog.h"
42
43
/* Linux 2.6.36 added RTA_MARK, so define it just in case we're building with
44
 * old headers.  (We can't test for it with #ifdef because it's an enum.) */
45
0
#define RTA_MARK 16
46
47
VLOG_DEFINE_THIS_MODULE(route_table);
48
49
COVERAGE_DEFINE(route_table_dump);
50
51
static struct ovs_mutex route_table_mutex = OVS_MUTEX_INITIALIZER;
52
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
53
54
/* Global change number for route-table, which should be incremented
55
 * every time route_table_reset() is called.  */
56
static uint64_t rt_change_seq;
57
58
static struct nln *nln = NULL;
59
static struct route_table_msg nln_rtmsg_change;
60
static struct nln_notifier *route_notifier = NULL;
61
static struct nln_notifier *route6_notifier = NULL;
62
static struct nln_notifier *name_notifier = NULL;
63
64
static bool route_table_valid = false;
65
66
static void route_table_reset(void);
67
static void route_table_handle_msg(const struct route_table_msg *, void *aux);
68
static void route_table_change(struct route_table_msg *, void *aux);
69
static void route_map_clear(void);
70
71
static void name_table_init(void);
72
static void name_table_change(const struct rtnetlink_change *, void *);
73
74
static void
75
route_data_destroy_nexthops__(struct route_data *rd)
76
0
{
77
0
    struct route_data_nexthop *rdnh;
78
79
0
    LIST_FOR_EACH_POP (rdnh, nexthop_node, &rd->nexthops) {
80
0
        if (rdnh && rdnh != &rd->primary_next_hop__) {
81
0
            free(rdnh);
82
0
        }
83
0
    }
84
0
}
85
86
void
87
route_data_destroy(struct route_data *rd)
88
0
{
89
0
    route_data_destroy_nexthops__(rd);
90
0
}
91
92
uint64_t
93
route_table_get_change_seq(void)
94
0
{
95
0
    return rt_change_seq;
96
0
}
97
98
/* Users of the route_table module should register themselves with this
99
 * function before making any other route_table function calls. */
100
void
101
route_table_init(void)
102
    OVS_EXCLUDED(route_table_mutex)
103
0
{
104
0
    ovs_mutex_lock(&route_table_mutex);
105
0
    ovs_assert(!nln);
106
0
    ovs_assert(!route_notifier);
107
0
    ovs_assert(!route6_notifier);
108
109
0
    ovs_router_init();
110
0
    nln = nln_create(NETLINK_ROUTE, route_table_parse, &nln_rtmsg_change);
111
112
0
    route_notifier =
113
0
        nln_notifier_create(nln, RTNLGRP_IPV4_ROUTE,
114
0
                            (nln_notify_func *) route_table_change, NULL);
115
0
    route6_notifier =
116
0
        nln_notifier_create(nln, RTNLGRP_IPV6_ROUTE,
117
0
                            (nln_notify_func *) route_table_change, NULL);
118
119
0
    route_table_reset();
120
0
    name_table_init();
121
122
0
    ovs_mutex_unlock(&route_table_mutex);
123
0
}
124
125
/* Run periodically to update the locally maintained routing table. */
126
void
127
route_table_run(void)
128
    OVS_EXCLUDED(route_table_mutex)
129
0
{
130
0
    ovs_mutex_lock(&route_table_mutex);
131
0
    if (nln) {
132
0
        rtnetlink_run();
133
0
        nln_run(nln);
134
135
0
        if (!route_table_valid) {
136
0
            route_table_reset();
137
0
        }
138
0
    }
139
0
    ovs_mutex_unlock(&route_table_mutex);
140
0
}
141
142
/* Causes poll_block() to wake up when route_table updates are required. */
143
void
144
route_table_wait(void)
145
    OVS_EXCLUDED(route_table_mutex)
146
0
{
147
0
    ovs_mutex_lock(&route_table_mutex);
148
0
    if (nln) {
149
0
        rtnetlink_wait();
150
0
        nln_wait(nln);
151
0
    }
152
0
    ovs_mutex_unlock(&route_table_mutex);
153
0
}
154
155
bool
156
route_table_dump_one_table(uint32_t id,
157
                           route_table_handle_msg_callback *handle_msg_cb,
158
                           void *aux)
159
0
{
160
0
    uint64_t reply_stub[NL_DUMP_BUFSIZE / 8];
161
0
    struct ofpbuf request, reply, buf;
162
0
    struct rtmsg *rq_msg;
163
0
    bool filtered = true;
164
0
    struct nl_dump dump;
165
166
0
    ofpbuf_init(&request, 0);
167
168
0
    nl_msg_put_nlmsghdr(&request, sizeof *rq_msg, RTM_GETROUTE, NLM_F_REQUEST);
169
170
0
    rq_msg = ofpbuf_put_zeros(&request, sizeof *rq_msg);
171
0
    rq_msg->rtm_family = AF_UNSPEC;
172
173
0
    if (id > UCHAR_MAX) {
174
0
        rq_msg->rtm_table = RT_TABLE_UNSPEC;
175
0
        nl_msg_put_u32(&request, RTA_TABLE, id);
176
0
    } else {
177
0
        rq_msg->rtm_table = id;
178
0
    }
179
180
0
    nl_dump_start(&dump, NETLINK_ROUTE, &request);
181
0
    ofpbuf_uninit(&request);
182
183
0
    ofpbuf_use_stub(&buf, reply_stub, sizeof reply_stub);
184
0
    while (nl_dump_next(&dump, &reply, &buf)) {
185
0
        struct route_table_msg msg;
186
187
0
        if (route_table_parse(&reply, &msg)) {
188
0
            struct nlmsghdr *nlmsghdr = nl_msg_nlmsghdr(&reply);
189
190
            /* Older kernels do not support filtering. */
191
0
            if (!(nlmsghdr->nlmsg_flags & NLM_F_DUMP_FILTERED)) {
192
0
                filtered = false;
193
0
            }
194
0
            handle_msg_cb(&msg, aux);
195
0
            route_data_destroy(&msg.rd);
196
0
        }
197
0
    }
198
0
    ofpbuf_uninit(&buf);
199
0
    nl_dump_done(&dump);
200
201
0
    return filtered;
202
0
}
203
204
static void
205
route_table_reset(void)
206
0
{
207
0
    uint32_t tables[] = {
208
0
        RT_TABLE_DEFAULT,
209
0
        RT_TABLE_MAIN,
210
0
        RT_TABLE_LOCAL,
211
0
    };
212
213
0
    route_map_clear();
214
0
    netdev_get_addrs_list_flush();
215
0
    route_table_valid = true;
216
0
    rt_change_seq++;
217
218
0
    COVERAGE_INC(route_table_dump);
219
220
0
    for (size_t i = 0; i < ARRAY_SIZE(tables); i++) {
221
0
        if (!route_table_dump_one_table(tables[i],
222
0
                                        route_table_handle_msg, NULL)) {
223
            /* Got unfiltered reply, no need to dump further. */
224
0
            break;
225
0
        }
226
0
    }
227
0
}
228
229
/* Returns true if the given route requires nexthop information (output
230
 * interface, nexthop IP, ...).  Returns false for special route types
231
 * that don't need this information. */
232
static bool
233
route_type_needs_nexthop(unsigned char rtmsg_type)
234
0
{
235
0
    switch (rtmsg_type) {
236
0
    case RTN_BLACKHOLE:
237
0
    case RTN_THROW:
238
0
    case RTN_UNREACHABLE:
239
0
    case RTN_PROHIBIT:
240
0
        return false;
241
242
0
    default:
243
0
        return true;
244
0
    }
245
0
}
246
247
static int
248
route_table_parse__(struct ofpbuf *buf, size_t ofs,
249
                    const struct nlmsghdr *nlmsg,
250
                    const struct rtmsg *rtm,
251
                    const struct rtnexthop *rtnh,
252
                    struct route_table_msg *change)
253
0
{
254
0
    bool parsed, ipv4 = false;
255
256
0
    static const struct nl_policy policy[] = {
257
0
        [RTA_DST] = { .type = NL_A_U32, .optional = true  },
258
0
        [RTA_OIF] = { .type = NL_A_U32, .optional = true },
259
0
        [RTA_GATEWAY] = { .type = NL_A_U32, .optional = true },
260
0
        [RTA_MARK] = { .type = NL_A_U32, .optional = true },
261
0
        [RTA_PREFSRC] = { .type = NL_A_U32, .optional = true },
262
0
        [RTA_TABLE] = { .type = NL_A_U32, .optional = true },
263
0
        [RTA_PRIORITY] = { .type = NL_A_U32, .optional = true },
264
0
        [RTA_VIA] = { .type = NL_A_RTA_VIA, .optional = true },
265
0
        [RTA_MULTIPATH] = { .type = NL_A_NESTED, .optional = true },
266
0
    };
267
268
0
    static const struct nl_policy policy6[] = {
269
0
        [RTA_DST] = { .type = NL_A_IPV6, .optional = true },
270
0
        [RTA_OIF] = { .type = NL_A_U32, .optional = true },
271
0
        [RTA_MARK] = { .type = NL_A_U32, .optional = true },
272
0
        [RTA_GATEWAY] = { .type = NL_A_IPV6, .optional = true },
273
0
        [RTA_PREFSRC] = { .type = NL_A_IPV6, .optional = true },
274
0
        [RTA_TABLE] = { .type = NL_A_U32, .optional = true },
275
0
        [RTA_PRIORITY] = { .type = NL_A_U32, .optional = true },
276
0
        [RTA_VIA] = { .type = NL_A_RTA_VIA, .optional = true },
277
0
        [RTA_MULTIPATH] = { .type = NL_A_NESTED, .optional = true },
278
0
    };
279
280
0
    struct nlattr *attrs[ARRAY_SIZE(policy)];
281
282
0
    if (rtm->rtm_family == AF_INET) {
283
0
        parsed = nl_policy_parse(buf, ofs, policy, attrs,
284
0
                                 ARRAY_SIZE(policy));
285
0
        ipv4 = true;
286
0
    } else if (rtm->rtm_family == AF_INET6) {
287
0
        parsed = nl_policy_parse(buf, ofs, policy6, attrs,
288
0
                                 ARRAY_SIZE(policy6));
289
0
    } else {
290
0
        VLOG_DBG_RL(&rl, "received non AF_INET rtnetlink route message");
291
0
        return 0;
292
0
    }
293
294
0
    if (parsed) {
295
0
        struct route_data_nexthop *rdnh = NULL;
296
0
        int rta_oif;      /* Output interface index. */
297
298
0
        memset(change, 0, sizeof *change);
299
300
0
        ovs_list_init(&change->rd.nexthops);
301
0
        rdnh = rtnh ? xzalloc(sizeof *rdnh) : &change->rd.primary_next_hop__;
302
0
        ovs_list_insert(&change->rd.nexthops, &rdnh->nexthop_node);
303
304
0
        rdnh->family = rtm->rtm_family;
305
0
        change->relevant = true;
306
307
0
        if (rtm->rtm_scope == RT_SCOPE_NOWHERE) {
308
0
            change->relevant = false;
309
0
        }
310
311
0
        if (rtm->rtm_type != RTN_UNICAST &&
312
0
            rtm->rtm_type != RTN_LOCAL) {
313
0
            change->relevant = false;
314
0
        }
315
316
0
        change->rd.rta_table_id = rtm->rtm_table;
317
0
        if (attrs[RTA_TABLE]) {
318
0
            change->rd.rta_table_id = nl_attr_get_u32(attrs[RTA_TABLE]);
319
0
        }
320
321
0
        change->nlmsg_type     = nlmsg->nlmsg_type;
322
0
        change->rd.rtm_dst_len = rtm->rtm_dst_len;
323
0
        change->rd.rtm_protocol = rtm->rtm_protocol;
324
0
        change->rd.rtn_local = rtm->rtm_type == RTN_LOCAL;
325
0
        if (attrs[RTA_OIF] && rtnh) {
326
0
            VLOG_DBG_RL(&rl, "unexpected RTA_OIF attribute while parsing "
327
0
                             "nested RTA_MULTIPATH attributes");
328
0
            goto error_out;
329
0
        }
330
0
        if (attrs[RTA_OIF] || rtnh) {
331
0
            rta_oif = rtnh ? rtnh->rtnh_ifindex
332
0
                           : nl_attr_get_u32(attrs[RTA_OIF]);
333
334
0
            if (!if_indextoname(rta_oif, rdnh->ifname)) {
335
0
                int error = errno;
336
337
0
                VLOG_DBG_RL(&rl, "could not find interface name[%u]: %s",
338
0
                            rta_oif, ovs_strerror(error));
339
0
                if (error == ENXIO) {
340
0
                    change->relevant = false;
341
0
                } else {
342
0
                    goto error_out;
343
0
                }
344
0
            }
345
0
        }
346
347
0
        if (attrs[RTA_DST]) {
348
0
            if (ipv4) {
349
0
                ovs_be32 dst;
350
0
                dst = nl_attr_get_be32(attrs[RTA_DST]);
351
0
                in6_addr_set_mapped_ipv4(&change->rd.rta_dst, dst);
352
0
            } else {
353
0
                change->rd.rta_dst = nl_attr_get_in6_addr(attrs[RTA_DST]);
354
0
            }
355
0
        } else if (ipv4) {
356
0
            in6_addr_set_mapped_ipv4(&change->rd.rta_dst, 0);
357
0
        }
358
0
        if (attrs[RTA_PREFSRC]) {
359
0
            if (ipv4) {
360
0
                ovs_be32 prefsrc;
361
0
                prefsrc = nl_attr_get_be32(attrs[RTA_PREFSRC]);
362
0
                in6_addr_set_mapped_ipv4(&change->rd.rta_prefsrc, prefsrc);
363
0
            } else {
364
0
                change->rd.rta_prefsrc =
365
0
                    nl_attr_get_in6_addr(attrs[RTA_PREFSRC]);
366
0
            }
367
0
        }
368
0
        if (attrs[RTA_GATEWAY]) {
369
0
            if (ipv4) {
370
0
                ovs_be32 gw;
371
0
                gw = nl_attr_get_be32(attrs[RTA_GATEWAY]);
372
0
                in6_addr_set_mapped_ipv4(&rdnh->addr, gw);
373
0
            } else {
374
0
                rdnh->addr = nl_attr_get_in6_addr(attrs[RTA_GATEWAY]);
375
0
            }
376
0
        }
377
0
        if (attrs[RTA_MARK]) {
378
0
            change->rd.rta_mark = nl_attr_get_u32(attrs[RTA_MARK]);
379
0
        }
380
0
        if (attrs[RTA_PRIORITY]) {
381
0
            change->rd.rta_priority = nl_attr_get_u32(attrs[RTA_PRIORITY]);
382
0
        }
383
0
        if (attrs[RTA_VIA]) {
384
0
            const struct rtvia *rtvia = nl_attr_get(attrs[RTA_VIA]);
385
0
            ovs_be32 addr;
386
387
0
            if (attrs[RTA_GATEWAY]) {
388
0
                VLOG_DBG_RL(&rl, "route message can not contain both "
389
0
                                 "RTA_GATEWAY and RTA_VIA");
390
0
                goto error_out;
391
0
            }
392
393
0
            rdnh->family = rtvia->rtvia_family;
394
395
0
            switch (rdnh->family) {
396
0
            case AF_INET:
397
0
                if (nl_attr_get_size(attrs[RTA_VIA])
398
0
                        - sizeof *rtvia < sizeof addr) {
399
0
                    VLOG_DBG_RL(&rl, "got short message while parsing RTA_VIA "
400
0
                                     "attribute for family AF_INET");
401
0
                    goto error_out;
402
0
                }
403
0
                memcpy(&addr, rtvia->rtvia_addr, sizeof addr);
404
0
                in6_addr_set_mapped_ipv4(&rdnh->addr, addr);
405
0
                break;
406
407
0
            case AF_INET6:
408
0
                if (nl_attr_get_size(attrs[RTA_VIA])
409
0
                        - sizeof *rtvia < sizeof rdnh->addr) {
410
0
                    VLOG_DBG_RL(&rl, "got short message while parsing RTA_VIA "
411
0
                                     "attribute for family AF_INET6");
412
0
                    goto error_out;
413
0
                }
414
0
                memcpy(&rdnh->addr, rtvia->rtvia_addr, sizeof rdnh->addr);
415
0
                break;
416
417
0
            default:
418
0
                VLOG_DBG_RL(&rl, "unsupported address family, %d, "
419
0
                                 "in via attribute", rdnh->family);
420
0
                goto error_out;
421
0
            }
422
0
        }
423
0
        if (attrs[RTA_MULTIPATH]) {
424
0
            const struct nlattr *nla;
425
0
            size_t left;
426
427
0
            if (rtnh) {
428
0
                VLOG_DBG_RL(&rl, "unexpected nested RTA_MULTIPATH attribute");
429
0
                goto error_out;
430
0
            }
431
432
            /* The change->rd->nexthops list is unconditionally populated with
433
             * a single rdnh entry as we start parsing above.  Multiple
434
             * branches above may access it or jump to error_out, and having it
435
             * on the list is the only way to ensure proper cleanup.
436
             *
437
             * Getting to this point, we know that the above branches has not
438
             * provided next hop information, because information about
439
             * multiple next hops is encoded in the nested attributes after the
440
             * RTA_MULTIPATH attribute.
441
             *
442
             * Before retrieving those we need to remove the empty rdnh entry
443
             * from the list. */
444
0
            route_data_destroy_nexthops__(&change->rd);
445
446
0
            NL_NESTED_FOR_EACH (nla, left, attrs[RTA_MULTIPATH]) {
447
0
                struct route_table_msg mp_change;
448
0
                struct rtnexthop *mp_rtnh;
449
0
                struct ofpbuf mp_buf;
450
451
0
                ofpbuf_use_const(&mp_buf, nla, nla->nla_len);
452
0
                mp_rtnh = ofpbuf_try_pull(&mp_buf, sizeof *mp_rtnh);
453
454
0
                if (!mp_rtnh) {
455
0
                    VLOG_DBG_RL(&rl, "got short message while parsing "
456
0
                                     "multipath attribute");
457
0
                    goto error_out;
458
0
                }
459
460
0
                if (!route_table_parse__(&mp_buf, 0, nlmsg, rtm, mp_rtnh,
461
0
                                         &mp_change)) {
462
0
                    goto error_out;
463
0
                }
464
0
                ovs_list_push_back_all(&change->rd.nexthops,
465
0
                                       &mp_change.rd.nexthops);
466
0
            }
467
0
        }
468
0
        if (route_type_needs_nexthop(rtm->rtm_type)
469
0
            && !attrs[RTA_OIF] && !attrs[RTA_GATEWAY]
470
0
            && !attrs[RTA_VIA] && !attrs[RTA_MULTIPATH]) {
471
0
            VLOG_DBG_RL(&rl, "route message needs an RTA_OIF, RTA_GATEWAY, "
472
0
                             "RTA_VIA or RTA_MULTIPATH attribute");
473
0
            goto error_out;
474
0
        }
475
        /* Add any additional RTA attribute processing before RTA_MULTIPATH. */
476
477
        /* Ensure that the change->rd->nexthops list is cleared in cases when
478
         * the route does not need a next hop. */
479
0
        if (!route_type_needs_nexthop(rtm->rtm_type)) {
480
0
            route_data_destroy_nexthops__(&change->rd);
481
0
        }
482
0
    } else {
483
0
        VLOG_DBG_RL(&rl, "received unparseable rtnetlink route message");
484
0
        goto error_out;
485
0
    }
486
487
    /* Success. */
488
0
    return ipv4 ? RTNLGRP_IPV4_ROUTE : RTNLGRP_IPV6_ROUTE;
489
490
0
error_out:
491
0
    route_data_destroy(&change->rd);
492
0
    return 0;
493
0
}
494
495
/* Parse Netlink message in buf, which is expected to contain a UAPI rtmsg
496
 * header and associated route attributes.
497
 *
498
 * Return RTNLGRP_IPV4_ROUTE or RTNLGRP_IPV6_ROUTE on success, and 0 on a parse
499
 * error.
500
 *
501
 * On success, memory may have been allocated, and it is the caller's
502
 * responsibility to free it with a call to route_data_destroy().
503
 *
504
 * In case of error, any allocated memory will be freed before returning. */
505
int
506
route_table_parse(struct ofpbuf *buf, void *change)
507
0
{
508
0
    struct nlmsghdr *nlmsg;
509
0
    struct rtmsg *rtm;
510
511
0
    nlmsg = ofpbuf_at(buf, 0, NLMSG_HDRLEN);
512
0
    rtm = ofpbuf_at(buf, NLMSG_HDRLEN, sizeof *rtm);
513
514
0
    if (!nlmsg || !rtm) {
515
0
        return 0;
516
0
    }
517
518
0
    return route_table_parse__(buf, NLMSG_HDRLEN + sizeof *rtm,
519
0
                               nlmsg, rtm, NULL, change);
520
0
}
521
522
static bool
523
is_standard_table_id(uint32_t table_id)
524
0
{
525
0
    return !table_id
526
0
           || table_id == RT_TABLE_DEFAULT
527
0
           || table_id == RT_TABLE_MAIN
528
0
           || table_id == RT_TABLE_LOCAL;
529
0
}
530
531
static void
532
route_table_change(struct route_table_msg *change, void *aux OVS_UNUSED)
533
0
{
534
0
    if (!change
535
0
        || (change->relevant
536
0
            && is_standard_table_id(change->rd.rta_table_id))) {
537
0
        route_table_valid = false;
538
0
    }
539
0
    if (change) {
540
0
        route_data_destroy(&change->rd);
541
0
    }
542
0
}
543
544
static void
545
route_table_handle_msg(const struct route_table_msg *change,
546
                       void *aux OVS_UNUSED)
547
0
{
548
0
    if (change->relevant && change->nlmsg_type == RTM_NEWROUTE
549
0
            && !ovs_list_is_empty(&change->rd.nexthops)) {
550
0
        const struct route_data *rd = &change->rd;
551
0
        const struct route_data_nexthop *rdnh;
552
553
        /* The ovs-router module currently does not implement lookup or
554
         * storage for routes with multiple next hops.  For backwards
555
         * compatibility, we use the first next hop. */
556
0
        rdnh = CONTAINER_OF(ovs_list_front(&change->rd.nexthops),
557
0
                            const struct route_data_nexthop, nexthop_node);
558
559
0
        ovs_router_insert(rd->rta_mark, &rd->rta_dst,
560
0
                          IN6_IS_ADDR_V4MAPPED(&rd->rta_dst)
561
0
                          ? rd->rtm_dst_len + 96 : rd->rtm_dst_len,
562
0
                          rd->rtn_local, rdnh->ifname, &rdnh->addr,
563
0
                          &rd->rta_prefsrc);
564
0
    }
565
0
}
566
567
static void
568
route_map_clear(void)
569
0
{
570
0
    ovs_router_flush();
571
0
}
572
573
bool
574
route_table_fallback_lookup(const struct in6_addr *ip6_dst OVS_UNUSED,
575
                            char name[] OVS_UNUSED,
576
                            struct in6_addr *gw6)
577
0
{
578
0
    *gw6 = in6addr_any;
579
0
    return false;
580
0
}
581
582

583
/* name_table . */
584
585
static void
586
name_table_init(void)
587
0
{
588
0
    name_notifier = rtnetlink_notifier_create(name_table_change, NULL);
589
0
}
590
591
592
static void
593
name_table_change(const struct rtnetlink_change *change,
594
                  void *aux OVS_UNUSED)
595
0
{
596
0
    if (change && change->irrelevant) {
597
0
        return;
598
0
    }
599
600
    /* Changes to interface status can cause routing table changes that some
601
     * versions of the linux kernel do not advertise for some reason. */
602
0
    route_table_valid = false;
603
604
0
    if (change && change->nlmsg_type == RTM_DELLINK) {
605
0
        if (change->ifname) {
606
0
            tnl_port_map_delete_ipdev(change->ifname);
607
0
        }
608
0
    }
609
0
}