/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 | } |