]> git.proxmox.com Git - ovs.git/blob - lib/ovs-router.c
ovs-router: Fix flushing of local routes.
[ovs.git] / lib / ovs-router.c
1 /*
2 * Copyright (c) 2014, 2015, 2016, 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 "ovs-router.h"
20
21 #include <sys/types.h>
22 #include <netinet/in.h>
23 #include <arpa/inet.h>
24 #include <errno.h>
25 #include <inttypes.h>
26 #include <sys/socket.h>
27 #include <net/if.h>
28 #include <stdarg.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32
33 #include "classifier.h"
34 #include "command-line.h"
35 #include "compiler.h"
36 #include "dpif.h"
37 #include "fatal-signal.h"
38 #include "openvswitch/dynamic-string.h"
39 #include "netdev.h"
40 #include "packets.h"
41 #include "seq.h"
42 #include "ovs-thread.h"
43 #include "route-table.h"
44 #include "tnl-ports.h"
45 #include "unixctl.h"
46 #include "util.h"
47 #include "unaligned.h"
48 #include "openvswitch/vlog.h"
49
50 VLOG_DEFINE_THIS_MODULE(ovs_router);
51
52 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
53
54 static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER;
55 static struct classifier cls;
56
57 /* By default, use the system routing table. For system-independent testing,
58 * the unit tests disable using the system routing table. */
59 static bool use_system_routing_table = true;
60
61 struct ovs_router_entry {
62 struct cls_rule cr;
63 char output_bridge[IFNAMSIZ];
64 struct in6_addr gw;
65 struct in6_addr nw_addr;
66 struct in6_addr src_addr;
67 uint8_t plen;
68 uint8_t priority;
69 bool local;
70 uint32_t mark;
71 };
72
73 static struct ovs_router_entry *
74 ovs_router_entry_cast(const struct cls_rule *cr)
75 {
76 return cr ? CONTAINER_OF(cr, struct ovs_router_entry, cr) : NULL;
77 }
78
79 /* Disables obtaining routes from the system routing table, for testing
80 * purposes. */
81 void
82 ovs_router_disable_system_routing_table(void)
83 {
84 use_system_routing_table = false;
85 }
86
87 static bool
88 ovs_router_lookup_fallback(const struct in6_addr *ip6_dst, char output_bridge[],
89 struct in6_addr *src6, struct in6_addr *gw6)
90 {
91 ovs_be32 src;
92
93 if (!use_system_routing_table
94 || !route_table_fallback_lookup(ip6_dst, output_bridge, gw6)) {
95 return false;
96 }
97 if (netdev_get_in4_by_name(output_bridge, (struct in_addr *)&src)) {
98 return false;
99 }
100 if (src6) {
101 in6_addr_set_mapped_ipv4(src6, src);
102 }
103 return true;
104 }
105
106 bool
107 ovs_router_lookup(uint32_t mark, const struct in6_addr *ip6_dst,
108 char output_bridge[],
109 struct in6_addr *src, struct in6_addr *gw)
110 {
111 const struct cls_rule *cr;
112 struct flow flow = {.ipv6_dst = *ip6_dst, .pkt_mark = mark};
113
114 if (src && ipv6_addr_is_set(src)) {
115 const struct cls_rule *cr_src;
116 struct flow flow_src = {.ipv6_dst = *src, .pkt_mark = mark};
117
118 cr_src = classifier_lookup(&cls, OVS_VERSION_MAX, &flow_src, NULL);
119 if (cr_src) {
120 struct ovs_router_entry *p_src = ovs_router_entry_cast(cr_src);
121 if (!p_src->local) {
122 return false;
123 }
124 } else {
125 return false;
126 }
127 }
128
129 cr = classifier_lookup(&cls, OVS_VERSION_MAX, &flow, NULL);
130 if (cr) {
131 struct ovs_router_entry *p = ovs_router_entry_cast(cr);
132
133 ovs_strlcpy(output_bridge, p->output_bridge, IFNAMSIZ);
134 *gw = p->gw;
135 if (src && !ipv6_addr_is_set(src)) {
136 *src = p->src_addr;
137 }
138 return true;
139 }
140 return ovs_router_lookup_fallback(ip6_dst, output_bridge, src, gw);
141 }
142
143 static void
144 rt_entry_free(struct ovs_router_entry *p)
145 {
146 cls_rule_destroy(&p->cr);
147 free(p);
148 }
149
150 static void rt_init_match(struct match *match, uint32_t mark,
151 const struct in6_addr *ip6_dst,
152 uint8_t plen)
153 {
154 struct in6_addr dst;
155 struct in6_addr mask;
156
157 mask = ipv6_create_mask(plen);
158
159 dst = ipv6_addr_bitand(ip6_dst, &mask);
160 memset(match, 0, sizeof *match);
161 match->flow.ipv6_dst = dst;
162 match->wc.masks.ipv6_dst = mask;
163 match->wc.masks.pkt_mark = UINT32_MAX;
164 match->flow.pkt_mark = mark;
165 }
166
167 static int
168 get_src_addr(const struct in6_addr *ip6_dst,
169 const char output_bridge[], struct in6_addr *psrc)
170 {
171 struct in6_addr *mask, *addr6;
172 int err, n_in6, i, max_plen = -1;
173 struct netdev *dev;
174 bool is_ipv4;
175
176 err = netdev_open(output_bridge, NULL, &dev);
177 if (err) {
178 return err;
179 }
180
181 err = netdev_get_addr_list(dev, &addr6, &mask, &n_in6);
182 if (err) {
183 goto out;
184 }
185
186 is_ipv4 = IN6_IS_ADDR_V4MAPPED(ip6_dst);
187
188 for (i = 0; i < n_in6; i++) {
189 struct in6_addr a1, a2;
190 int mask_bits;
191
192 if (is_ipv4 && !IN6_IS_ADDR_V4MAPPED(&addr6[i])) {
193 continue;
194 }
195
196 a1 = ipv6_addr_bitand(ip6_dst, &mask[i]);
197 a2 = ipv6_addr_bitand(&addr6[i], &mask[i]);
198 mask_bits = bitmap_count1(ALIGNED_CAST(const unsigned long *, &mask[i]), 128);
199
200 if (!memcmp(&a1, &a2, sizeof (a1)) && mask_bits > max_plen) {
201 *psrc = addr6[i];
202 max_plen = mask_bits;
203 }
204 }
205 if (max_plen == -1) {
206 err = ENOENT;
207 }
208 out:
209 free(addr6);
210 free(mask);
211 netdev_close(dev);
212 return err;
213 }
214
215 static int
216 ovs_router_insert__(uint32_t mark, uint8_t priority, bool local,
217 const struct in6_addr *ip6_dst,
218 uint8_t plen, const char output_bridge[],
219 const struct in6_addr *gw)
220 {
221 const struct cls_rule *cr;
222 struct ovs_router_entry *p;
223 struct match match;
224 int err;
225
226 rt_init_match(&match, mark, ip6_dst, plen);
227
228 p = xzalloc(sizeof *p);
229 ovs_strlcpy(p->output_bridge, output_bridge, sizeof p->output_bridge);
230 if (ipv6_addr_is_set(gw)) {
231 p->gw = *gw;
232 }
233 p->mark = mark;
234 p->nw_addr = match.flow.ipv6_dst;
235 p->plen = plen;
236 p->local = local;
237 p->priority = priority;
238 err = get_src_addr(ip6_dst, output_bridge, &p->src_addr);
239 if (err && ipv6_addr_is_set(gw)) {
240 err = get_src_addr(gw, output_bridge, &p->src_addr);
241 }
242 if (err) {
243 struct ds ds = DS_EMPTY_INITIALIZER;
244
245 ipv6_format_mapped(ip6_dst, &ds);
246 VLOG_DBG_RL(&rl, "src addr not available for route %s", ds_cstr(&ds));
247 free(p);
248 ds_destroy(&ds);
249 return err;
250 }
251 /* Longest prefix matches first. */
252 cls_rule_init(&p->cr, &match, priority);
253
254 ovs_mutex_lock(&mutex);
255 cr = classifier_replace(&cls, &p->cr, OVS_VERSION_MIN, NULL, 0);
256 ovs_mutex_unlock(&mutex);
257
258 if (cr) {
259 /* An old rule with the same match was displaced. */
260 ovsrcu_postpone(rt_entry_free, ovs_router_entry_cast(cr));
261 }
262 tnl_port_map_insert_ipdev(output_bridge);
263 seq_change(tnl_conf_seq);
264 return 0;
265 }
266
267 void
268 ovs_router_insert(uint32_t mark, const struct in6_addr *ip_dst, uint8_t plen,
269 bool local, const char output_bridge[],
270 const struct in6_addr *gw)
271 {
272 if (use_system_routing_table) {
273 uint8_t priority = local ? plen + 64 : plen;
274 ovs_router_insert__(mark, priority, local, ip_dst, plen, output_bridge, gw);
275 }
276 }
277
278 static void
279 rt_entry_delete__(const struct cls_rule *cr)
280 {
281 struct ovs_router_entry *p = ovs_router_entry_cast(cr);
282
283 tnl_port_map_delete_ipdev(p->output_bridge);
284 classifier_remove_assert(&cls, cr);
285 ovsrcu_postpone(rt_entry_free, ovs_router_entry_cast(cr));
286 }
287
288 static bool
289 rt_entry_delete(uint32_t mark, uint8_t priority,
290 const struct in6_addr *ip6_dst, uint8_t plen)
291 {
292 const struct cls_rule *cr;
293 struct cls_rule rule;
294 struct match match;
295 bool res = false;
296
297 rt_init_match(&match, mark, ip6_dst, plen);
298
299 cls_rule_init(&rule, &match, priority);
300
301 /* Find the exact rule. */
302 cr = classifier_find_rule_exactly(&cls, &rule, OVS_VERSION_MAX);
303 if (cr) {
304 ovs_mutex_lock(&mutex);
305 rt_entry_delete__(cr);
306 ovs_mutex_unlock(&mutex);
307
308 res = true;
309 }
310
311 cls_rule_destroy(&rule);
312 return res;
313 }
314
315 static bool
316 scan_ipv6_route(const char *s, struct in6_addr *addr, unsigned int *plen)
317 {
318 char *error = ipv6_parse_cidr(s, addr, plen);
319 if (error) {
320 free(error);
321 return false;
322 }
323 return true;
324 }
325
326 static bool
327 scan_ipv4_route(const char *s, ovs_be32 *addr, unsigned int *plen)
328 {
329 char *error = ip_parse_cidr(s, addr, plen);
330 if (error) {
331 free(error);
332 return false;
333 }
334 return true;
335 }
336
337 static void
338 ovs_router_add(struct unixctl_conn *conn, int argc,
339 const char *argv[], void *aux OVS_UNUSED)
340 {
341 struct in6_addr gw6 = in6addr_any;
342 struct in6_addr ip6;
343 uint32_t mark = 0;
344 unsigned int plen;
345 ovs_be32 ip;
346 int err;
347
348 if (scan_ipv4_route(argv[1], &ip, &plen)) {
349 ovs_be32 gw = 0;
350
351 if (argc > 3) {
352 if (!ovs_scan(argv[3], "pkt_mark=%"SCNi32, &mark) &&
353 !ip_parse(argv[3], &gw)) {
354 unixctl_command_reply_error(conn, "Invalid pkt_mark or gateway");
355 return;
356 }
357 }
358 in6_addr_set_mapped_ipv4(&ip6, ip);
359 if (gw) {
360 in6_addr_set_mapped_ipv4(&gw6, gw);
361 }
362 plen += 96;
363 } else if (scan_ipv6_route(argv[1], &ip6, &plen)) {
364 if (argc > 3) {
365 if (!ovs_scan(argv[3], "pkt_mark=%"SCNi32, &mark) &&
366 !ipv6_parse(argv[3], &gw6)) {
367 unixctl_command_reply_error(conn, "Invalid pkt_mark or IPv6 gateway");
368 return;
369 }
370 }
371 } else {
372 unixctl_command_reply_error(conn, "Invalid parameters");
373 return;
374 }
375 if (argc > 4) {
376 if (!ovs_scan(argv[4], "pkt_mark=%"SCNi32, &mark)) {
377 unixctl_command_reply_error(conn, "Invalid pkt_mark");
378 return;
379 }
380 }
381
382 err = ovs_router_insert__(mark, plen + 32, false, &ip6, plen, argv[2], &gw6);
383 if (err) {
384 unixctl_command_reply_error(conn, "Error while inserting route.");
385 } else {
386 unixctl_command_reply(conn, "OK");
387 }
388 }
389
390 static void
391 ovs_router_del(struct unixctl_conn *conn, int argc OVS_UNUSED,
392 const char *argv[], void *aux OVS_UNUSED)
393 {
394 struct in6_addr ip6;
395 uint32_t mark = 0;
396 unsigned int plen;
397 ovs_be32 ip;
398
399 if (scan_ipv4_route(argv[1], &ip, &plen)) {
400 in6_addr_set_mapped_ipv4(&ip6, ip);
401 plen += 96;
402 } else if (!scan_ipv6_route(argv[1], &ip6, &plen)) {
403 unixctl_command_reply_error(conn, "Invalid parameters");
404 return;
405 }
406 if (argc > 2) {
407 if (!ovs_scan(argv[2], "pkt_mark=%"SCNi32, &mark)) {
408 unixctl_command_reply_error(conn, "Invalid pkt_mark");
409 return;
410 }
411 }
412
413 if (rt_entry_delete(mark, plen + 32, &ip6, plen)) {
414 unixctl_command_reply(conn, "OK");
415 seq_change(tnl_conf_seq);
416 } else {
417 unixctl_command_reply_error(conn, "Not found");
418 }
419 }
420
421 static void
422 ovs_router_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
423 const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
424 {
425 struct ovs_router_entry *rt;
426 struct ds ds = DS_EMPTY_INITIALIZER;
427
428 ds_put_format(&ds, "Route Table:\n");
429 CLS_FOR_EACH(rt, cr, &cls) {
430 uint8_t plen;
431 if (rt->priority == rt->plen || rt->local) {
432 ds_put_format(&ds, "Cached: ");
433 } else {
434 ds_put_format(&ds, "User: ");
435 }
436 ipv6_format_mapped(&rt->nw_addr, &ds);
437 plen = rt->plen;
438 if (IN6_IS_ADDR_V4MAPPED(&rt->nw_addr)) {
439 plen -= 96;
440 }
441 ds_put_format(&ds, "/%"PRIu8, plen);
442 if (rt->mark) {
443 ds_put_format(&ds, " MARK %"PRIu32, rt->mark);
444 }
445
446 ds_put_format(&ds, " dev %s", rt->output_bridge);
447 if (ipv6_addr_is_set(&rt->gw)) {
448 ds_put_format(&ds, " GW ");
449 ipv6_format_mapped(&rt->gw, &ds);
450 }
451 ds_put_format(&ds, " SRC ");
452 ipv6_format_mapped(&rt->src_addr, &ds);
453 if (rt->local) {
454 ds_put_format(&ds, " local");
455 }
456 ds_put_format(&ds, "\n");
457 }
458 unixctl_command_reply(conn, ds_cstr(&ds));
459 ds_destroy(&ds);
460 }
461
462 static void
463 ovs_router_lookup_cmd(struct unixctl_conn *conn, int argc,
464 const char *argv[], void *aux OVS_UNUSED)
465 {
466 struct in6_addr gw, src = in6addr_any;
467 char iface[IFNAMSIZ];
468 struct in6_addr ip6;
469 unsigned int plen;
470 uint32_t mark = 0;
471 ovs_be32 ip;
472
473 if (scan_ipv4_route(argv[1], &ip, &plen) && plen == 32) {
474 in6_addr_set_mapped_ipv4(&ip6, ip);
475 } else if (!(scan_ipv6_route(argv[1], &ip6, &plen) && plen == 128)) {
476 unixctl_command_reply_error(conn, "Invalid parameters");
477 return;
478 }
479 if (argc > 2) {
480 if (!ovs_scan(argv[2], "pkt_mark=%"SCNi32, &mark)) {
481 unixctl_command_reply_error(conn, "Invalid pkt_mark");
482 return;
483 }
484 }
485 if (ovs_router_lookup(mark, &ip6, iface, &src, &gw)) {
486 struct ds ds = DS_EMPTY_INITIALIZER;
487
488 ds_put_format(&ds, "src ");
489 ipv6_format_mapped(&src, &ds);
490 ds_put_format(&ds, "\ngateway ");
491 ipv6_format_mapped(&gw, &ds);
492 ds_put_format(&ds, "\ndev %s\n", iface);
493 unixctl_command_reply(conn, ds_cstr(&ds));
494 ds_destroy(&ds);
495 } else {
496 unixctl_command_reply_error(conn, "Not found");
497 }
498 }
499
500 void
501 ovs_router_flush(void)
502 {
503 struct ovs_router_entry *rt;
504
505 ovs_mutex_lock(&mutex);
506 classifier_defer(&cls);
507 CLS_FOR_EACH(rt, cr, &cls) {
508 if (rt->priority == rt->plen || rt->local) {
509 rt_entry_delete__(&rt->cr);
510 }
511 }
512 classifier_publish(&cls);
513 ovs_mutex_unlock(&mutex);
514 seq_change(tnl_conf_seq);
515 }
516
517 static void
518 ovs_router_flush_handler(void *aux OVS_UNUSED)
519 {
520 ovs_router_flush();
521 }
522
523 void
524 ovs_router_init(void)
525 {
526 static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
527
528 if (ovsthread_once_start(&once)) {
529 fatal_signal_add_hook(ovs_router_flush_handler, NULL, NULL, true);
530 classifier_init(&cls, NULL);
531 unixctl_command_register("ovs/route/add",
532 "ip_addr/prefix_len out_br_name [gw] "
533 "[pkt_mark=mark]",
534 2, 4, ovs_router_add, NULL);
535 unixctl_command_register("ovs/route/show", "", 0, 0,
536 ovs_router_show, NULL);
537 unixctl_command_register("ovs/route/del", "ip_addr/prefix_len "
538 "[pkt_mark=mark]", 1, 2, ovs_router_del,
539 NULL);
540 unixctl_command_register("ovs/route/lookup", "ip_addr "
541 "[pkt_mark=mark]", 1, 2,
542 ovs_router_lookup_cmd, NULL);
543 ovsthread_once_done(&once);
544 }
545 }