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