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