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