]> git.proxmox.com Git - mirror_ovs.git/blame - lib/ovs-router.c
utilities/ovs-ctl.in: Allow non-monitoring daemons
[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"
d9b4ebc5 48
fccd7c09 49static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER;
d9b4ebc5
PS
50static struct classifier cls;
51
52struct ovs_router_entry {
53 struct cls_rule cr;
54 char output_bridge[IFNAMSIZ];
0b8da9ae
TLSC
55 struct in6_addr gw;
56 struct in6_addr nw_addr;
a8704b50 57 struct in6_addr src_addr;
d9b4ebc5
PS
58 uint8_t plen;
59 uint8_t priority;
60};
61
62static struct ovs_router_entry *
63ovs_router_entry_cast(const struct cls_rule *cr)
64{
65 if (offsetof(struct ovs_router_entry, cr) == 0) {
66 return CONTAINER_OF(cr, struct ovs_router_entry, cr);
67 } else {
68 return cr ? CONTAINER_OF(cr, struct ovs_router_entry, cr) : NULL;
69 }
70}
71
ec6c5379
PS
72static bool
73ovs_router_lookup_fallback(const struct in6_addr *ip6_dst, char output_bridge[],
74 struct in6_addr *src6, struct in6_addr *gw6)
75{
76 ovs_be32 src;
77
78 if (!route_table_fallback_lookup(ip6_dst, output_bridge, gw6)) {
79 return false;
80 }
81 if (netdev_get_in4_by_name(output_bridge, (struct in_addr *)&src)) {
82 return false;
83 }
84 if (src6) {
85 in6_addr_set_mapped_ipv4(src6, src);
86 }
87 return true;
88}
89
d9b4ebc5 90bool
0b8da9ae 91ovs_router_lookup(const struct in6_addr *ip6_dst, char output_bridge[],
a8704b50 92 struct in6_addr *src, struct in6_addr *gw)
d9b4ebc5
PS
93{
94 const struct cls_rule *cr;
0b8da9ae 95 struct flow flow = {.ipv6_dst = *ip6_dst};
d9b4ebc5 96
2b7b1427 97 cr = classifier_lookup(&cls, CLS_MAX_VERSION, &flow, NULL);
d9b4ebc5
PS
98 if (cr) {
99 struct ovs_router_entry *p = ovs_router_entry_cast(cr);
100
8742957c 101 ovs_strlcpy(output_bridge, p->output_bridge, IFNAMSIZ);
d9b4ebc5 102 *gw = p->gw;
a8704b50
PS
103 if (src) {
104 *src = p->src_addr;
105 }
d9b4ebc5
PS
106 return true;
107 }
ec6c5379 108 return ovs_router_lookup_fallback(ip6_dst, output_bridge, src, gw);
d9b4ebc5
PS
109}
110
111static void
112rt_entry_free(struct ovs_router_entry *p)
113{
114 cls_rule_destroy(&p->cr);
115 free(p);
116}
117
0b8da9ae
TLSC
118static void rt_init_match(struct match *match, const struct in6_addr *ip6_dst,
119 uint8_t plen)
d9b4ebc5 120{
0b8da9ae
TLSC
121 struct in6_addr dst;
122 struct in6_addr mask;
d9b4ebc5 123
0b8da9ae 124 mask = ipv6_create_mask(plen);
d9b4ebc5 125
0b8da9ae 126 dst = ipv6_addr_bitand(ip6_dst, &mask);
d9b4ebc5 127 memset(match, 0, sizeof *match);
0b8da9ae
TLSC
128 match->flow.ipv6_dst = dst;
129 match->wc.masks.ipv6_dst = mask;
d9b4ebc5
PS
130}
131
a8704b50
PS
132static int
133get_src_addr(const struct in6_addr *ip6_dst,
134 const char output_bridge[], struct in6_addr *psrc)
135{
136 struct in6_addr *mask, *addr6;
137 int err, n_in6, i, max_plen = -1;
138 struct netdev *dev;
139
140 err = netdev_open(output_bridge, NULL, &dev);
141 if (err) {
142 return err;
143 }
144
145 err = netdev_get_addr_list(dev, &addr6, &mask, &n_in6);
146 if (err) {
147 goto out;
148 }
149
150 for (i = 0; i < n_in6; i++) {
151 struct in6_addr a1, a2;
152 int mask_bits;
153
154 a1 = ipv6_addr_bitand(ip6_dst, &mask[i]);
155 a2 = ipv6_addr_bitand(&addr6[i], &mask[i]);
156 mask_bits = bitmap_count1(ALIGNED_CAST(const unsigned long *, &mask[i]), 128);
157
158 if (!memcmp(&a1, &a2, sizeof (a1)) && mask_bits > max_plen) {
159 *psrc = addr6[i];
160 max_plen = mask_bits;
161 }
162 }
163 if (max_plen == -1) {
164 err = ENOENT;
165 }
166out:
167 free(addr6);
168 free(mask);
169 netdev_close(dev);
170 return err;
171}
172
173static int
0b8da9ae
TLSC
174ovs_router_insert__(uint8_t priority, const struct in6_addr *ip6_dst,
175 uint8_t plen, const char output_bridge[],
176 const struct in6_addr *gw)
d9b4ebc5
PS
177{
178 const struct cls_rule *cr;
179 struct ovs_router_entry *p;
180 struct match match;
a8704b50 181 int err;
d9b4ebc5 182
0b8da9ae 183 rt_init_match(&match, ip6_dst, plen);
d9b4ebc5
PS
184
185 p = xzalloc(sizeof *p);
8742957c 186 ovs_strlcpy(p->output_bridge, output_bridge, sizeof p->output_bridge);
0b8da9ae
TLSC
187 if (ipv6_addr_is_set(gw)) {
188 p->gw = *gw;
189 }
190 p->nw_addr = match.flow.ipv6_dst;
d9b4ebc5
PS
191 p->plen = plen;
192 p->priority = priority;
a8704b50
PS
193 err = get_src_addr(ip6_dst, output_bridge, &p->src_addr);
194 if (err) {
7209202e 195 free(p);
a8704b50
PS
196 return err;
197 }
2b7b1427 198 /* Longest prefix matches first. */
bd53aa17 199 cls_rule_init(&p->cr, &match, priority);
d9b4ebc5 200
fccd7c09 201 ovs_mutex_lock(&mutex);
bd53aa17 202 cr = classifier_replace(&cls, &p->cr, CLS_MIN_VERSION, NULL, 0);
fccd7c09
JR
203 ovs_mutex_unlock(&mutex);
204
d9b4ebc5
PS
205 if (cr) {
206 /* An old rule with the same match was displaced. */
207 ovsrcu_postpone(rt_entry_free, ovs_router_entry_cast(cr));
208 }
7f9b8504 209 tnl_port_map_insert_ipdev(output_bridge);
a36de779 210 seq_change(tnl_conf_seq);
a8704b50 211 return 0;
d9b4ebc5
PS
212}
213
214void
0b8da9ae
TLSC
215ovs_router_insert(const struct in6_addr *ip_dst, uint8_t plen,
216 const char output_bridge[], const struct in6_addr *gw)
d9b4ebc5
PS
217{
218 ovs_router_insert__(plen, ip_dst, plen, output_bridge, gw);
219}
220
7f9b8504
PS
221
222static bool
223__rt_entry_delete(const struct cls_rule *cr)
224{
225 struct ovs_router_entry *p = ovs_router_entry_cast(cr);
226
227 tnl_port_map_delete_ipdev(p->output_bridge);
228 /* Remove it. */
229 cr = classifier_remove(&cls, cr);
230 if (cr) {
231 ovsrcu_postpone(rt_entry_free, ovs_router_entry_cast(cr));
232 return true;
233 }
234 return false;
235}
236
d9b4ebc5 237static bool
0b8da9ae 238rt_entry_delete(uint8_t priority, const struct in6_addr *ip6_dst, uint8_t plen)
d9b4ebc5 239{
dfea28b3 240 const struct cls_rule *cr;
d9b4ebc5
PS
241 struct cls_rule rule;
242 struct match match;
7f9b8504 243 bool res = false;
d9b4ebc5 244
0b8da9ae 245 rt_init_match(&match, ip6_dst, plen);
d9b4ebc5 246
bd53aa17 247 cls_rule_init(&rule, &match, priority);
d9b4ebc5
PS
248
249 /* Find the exact rule. */
bd53aa17 250 cr = classifier_find_rule_exactly(&cls, &rule, CLS_MAX_VERSION);
d9b4ebc5 251 if (cr) {
fccd7c09 252 ovs_mutex_lock(&mutex);
7f9b8504 253 res = __rt_entry_delete(cr);
fccd7c09 254 ovs_mutex_unlock(&mutex);
d9b4ebc5 255 }
7f9b8504 256 return res;
d9b4ebc5
PS
257}
258
0b8da9ae
TLSC
259static bool
260scan_ipv6_route(const char *s, struct in6_addr *addr, unsigned int *plen)
261{
e7695092 262 char *error = ipv6_parse_cidr(s, addr, plen);
184dfff0
JP
263 if (error) {
264 free(error);
265 return false;
0b8da9ae 266 }
184dfff0 267 return true;
0b8da9ae
TLSC
268}
269
d9b4ebc5
PS
270static bool
271scan_ipv4_route(const char *s, ovs_be32 *addr, unsigned int *plen)
272{
e7695092
BP
273 char *error = ip_parse_cidr(s, addr, plen);
274 if (error) {
275 free(error);
d9b4ebc5
PS
276 return false;
277 }
e7695092 278 return true;
d9b4ebc5
PS
279}
280
281static void
282ovs_router_add(struct unixctl_conn *conn, int argc,
283 const char *argv[], void *aux OVS_UNUSED)
284{
e7695092 285 ovs_be32 ip;
d9b4ebc5 286 unsigned int plen;
0b8da9ae
TLSC
287 struct in6_addr ip6;
288 struct in6_addr gw6;
a8704b50 289 int err;
d9b4ebc5
PS
290
291 if (scan_ipv4_route(argv[1], &ip, &plen)) {
e7695092
BP
292 ovs_be32 gw = 0;
293 if (argc > 3 && !ip_parse(argv[3], &gw)) {
294 unixctl_command_reply_error(conn, "Invalid gateway");
295 return;
d9b4ebc5 296 }
0b8da9ae
TLSC
297 in6_addr_set_mapped_ipv4(&ip6, ip);
298 in6_addr_set_mapped_ipv4(&gw6, gw);
299 plen += 96;
300 } else if (scan_ipv6_route(argv[1], &ip6, &plen)) {
e7695092
BP
301 gw6 = in6addr_any;
302 if (argc > 3 && !ipv6_parse(argv[3], &gw6)) {
303 unixctl_command_reply_error(conn, "Invalid IPv6 gateway");
304 return;
0b8da9ae 305 }
d9b4ebc5 306 } else {
f2e06274
BP
307 unixctl_command_reply_error(conn, "Invalid parameters");
308 return;
d9b4ebc5 309 }
a8704b50
PS
310 err = ovs_router_insert__(plen + 32, &ip6, plen, argv[2], &gw6);
311 if (err) {
312 unixctl_command_reply(conn, "Error while inserting route.");
313 } else {
314 unixctl_command_reply(conn, "OK");
315 }
d9b4ebc5
PS
316}
317
318static void
319ovs_router_del(struct unixctl_conn *conn, int argc OVS_UNUSED,
320 const char *argv[], void *aux OVS_UNUSED)
321{
322 ovs_be32 ip;
323 unsigned int plen;
0b8da9ae 324 struct in6_addr ip6;
d9b4ebc5
PS
325
326 if (scan_ipv4_route(argv[1], &ip, &plen)) {
0b8da9ae
TLSC
327 in6_addr_set_mapped_ipv4(&ip6, ip);
328 plen += 96;
329 } else if (!scan_ipv6_route(argv[1], &ip6, &plen)) {
f2e06274
BP
330 unixctl_command_reply_error(conn, "Invalid parameters");
331 return;
d9b4ebc5 332 }
0b8da9ae
TLSC
333 if (rt_entry_delete(plen + 32, &ip6, plen)) {
334 unixctl_command_reply(conn, "OK");
335 seq_change(tnl_conf_seq);
336 } else {
f2e06274 337 unixctl_command_reply_error(conn, "Not found");
0b8da9ae 338 }
d9b4ebc5
PS
339}
340
341static void
342ovs_router_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
343 const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
344{
345 struct ovs_router_entry *rt;
346 struct ds ds = DS_EMPTY_INITIALIZER;
347
348 ds_put_format(&ds, "Route Table:\n");
349 CLS_FOR_EACH(rt, cr, &cls) {
0b8da9ae 350 uint8_t plen;
d9b4ebc5
PS
351 if (rt->priority == rt->plen) {
352 ds_put_format(&ds, "Cached: ");
353 } else {
354 ds_put_format(&ds, "User: ");
355 }
ac6d120f 356 ipv6_format_mapped(&rt->nw_addr, &ds);
0b8da9ae
TLSC
357 plen = rt->plen;
358 if (IN6_IS_ADDR_V4MAPPED(&rt->nw_addr)) {
359 plen -= 96;
360 }
361 ds_put_format(&ds, "/%"PRIu16" dev %s", plen, rt->output_bridge);
362 if (ipv6_addr_is_set(&rt->gw)) {
363 ds_put_format(&ds, " GW ");
ac6d120f 364 ipv6_format_mapped(&rt->gw, &ds);
d9b4ebc5 365 }
a8704b50
PS
366 ds_put_format(&ds, " SRC ");
367 ipv6_format_mapped(&rt->src_addr, &ds);
d9b4ebc5
PS
368 ds_put_format(&ds, "\n");
369 }
370 unixctl_command_reply(conn, ds_cstr(&ds));
371 ds_destroy(&ds);
372}
373
bc786797
YT
374static void
375ovs_router_lookup_cmd(struct unixctl_conn *conn, int argc OVS_UNUSED,
376 const char *argv[], void *aux OVS_UNUSED)
377{
378 ovs_be32 ip;
0b8da9ae 379 struct in6_addr ip6;
bc786797 380 unsigned int plen;
0b8da9ae 381 char iface[IFNAMSIZ];
a8704b50 382 struct in6_addr gw, src;
bc786797
YT
383
384 if (scan_ipv4_route(argv[1], &ip, &plen) && plen == 32) {
0b8da9ae
TLSC
385 in6_addr_set_mapped_ipv4(&ip6, ip);
386 } else if (!(scan_ipv6_route(argv[1], &ip6, &plen) && plen == 128)) {
f2e06274
BP
387 unixctl_command_reply_error(conn, "Invalid parameters");
388 return;
0b8da9ae 389 }
bc786797 390
a8704b50 391 if (ovs_router_lookup(&ip6, iface, &src, &gw)) {
0b8da9ae 392 struct ds ds = DS_EMPTY_INITIALIZER;
a8704b50
PS
393 ds_put_format(&ds, "src ");
394 ipv6_format_mapped(&src, &ds);
0b8da9ae 395 ds_put_format(&ds, "gateway ");
a8704b50 396 ipv6_format_mapped(&gw, &ds);
0b8da9ae
TLSC
397 ds_put_format(&ds, "\ndev %s\n", iface);
398 unixctl_command_reply(conn, ds_cstr(&ds));
399 ds_destroy(&ds);
bc786797 400 } else {
f2e06274 401 unixctl_command_reply_error(conn, "Not found");
bc786797
YT
402 }
403}
404
d9b4ebc5
PS
405void
406ovs_router_flush(void)
407{
408 struct ovs_router_entry *rt;
409
802f84ff
JR
410 ovs_mutex_lock(&mutex);
411 classifier_defer(&cls);
de4ad4a2 412 CLS_FOR_EACH(rt, cr, &cls) {
d9b4ebc5 413 if (rt->priority == rt->plen) {
7f9b8504 414 __rt_entry_delete(&rt->cr);
d9b4ebc5
PS
415 }
416 }
802f84ff
JR
417 classifier_publish(&cls);
418 ovs_mutex_unlock(&mutex);
a36de779 419 seq_change(tnl_conf_seq);
d9b4ebc5
PS
420}
421
422/* May not be called more than once. */
423void
1bc50ef3 424ovs_router_init(void)
d9b4ebc5
PS
425{
426 classifier_init(&cls, NULL);
0b8da9ae 427 unixctl_command_register("ovs/route/add", "ip_addr/prefix_len out_br_name gw", 2, 3,
d9b4ebc5
PS
428 ovs_router_add, NULL);
429 unixctl_command_register("ovs/route/show", "", 0, 0, ovs_router_show, NULL);
0b8da9ae 430 unixctl_command_register("ovs/route/del", "ip_addr/prefix_len", 1, 1, ovs_router_del,
d9b4ebc5 431 NULL);
0b8da9ae 432 unixctl_command_register("ovs/route/lookup", "ip_addr", 1, 1,
bc786797 433 ovs_router_lookup_cmd, NULL);
d9b4ebc5 434}