]> git.proxmox.com Git - mirror_ovs.git/blame - lib/ovs-router.c
ovs-tcpdump: Do not import unused "select" module.
[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
44e0c35d 97 cr = classifier_lookup(&cls, OVS_VERSION_MAX, &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;
20dcdc7f 139 bool is_ipv4;
a8704b50
PS
140
141 err = netdev_open(output_bridge, NULL, &dev);
142 if (err) {
143 return err;
144 }
145
146 err = netdev_get_addr_list(dev, &addr6, &mask, &n_in6);
147 if (err) {
148 goto out;
149 }
150
20dcdc7f
TLSC
151 is_ipv4 = IN6_IS_ADDR_V4MAPPED(ip6_dst);
152
a8704b50
PS
153 for (i = 0; i < n_in6; i++) {
154 struct in6_addr a1, a2;
155 int mask_bits;
156
20dcdc7f
TLSC
157 if (is_ipv4 && !IN6_IS_ADDR_V4MAPPED(&addr6[i])) {
158 continue;
159 }
160
a8704b50
PS
161 a1 = ipv6_addr_bitand(ip6_dst, &mask[i]);
162 a2 = ipv6_addr_bitand(&addr6[i], &mask[i]);
163 mask_bits = bitmap_count1(ALIGNED_CAST(const unsigned long *, &mask[i]), 128);
164
165 if (!memcmp(&a1, &a2, sizeof (a1)) && mask_bits > max_plen) {
166 *psrc = addr6[i];
167 max_plen = mask_bits;
168 }
169 }
170 if (max_plen == -1) {
171 err = ENOENT;
172 }
173out:
174 free(addr6);
175 free(mask);
176 netdev_close(dev);
177 return err;
178}
179
180static int
0b8da9ae
TLSC
181ovs_router_insert__(uint8_t priority, const struct in6_addr *ip6_dst,
182 uint8_t plen, const char output_bridge[],
183 const struct in6_addr *gw)
d9b4ebc5
PS
184{
185 const struct cls_rule *cr;
186 struct ovs_router_entry *p;
187 struct match match;
a8704b50 188 int err;
d9b4ebc5 189
0b8da9ae 190 rt_init_match(&match, ip6_dst, plen);
d9b4ebc5
PS
191
192 p = xzalloc(sizeof *p);
8742957c 193 ovs_strlcpy(p->output_bridge, output_bridge, sizeof p->output_bridge);
0b8da9ae
TLSC
194 if (ipv6_addr_is_set(gw)) {
195 p->gw = *gw;
196 }
197 p->nw_addr = match.flow.ipv6_dst;
d9b4ebc5
PS
198 p->plen = plen;
199 p->priority = priority;
a8704b50 200 err = get_src_addr(ip6_dst, output_bridge, &p->src_addr);
4e08f54b
AW
201 if (err && ipv6_addr_is_set(gw)) {
202 err = get_src_addr(gw, output_bridge, &p->src_addr);
203 }
a8704b50 204 if (err) {
7209202e 205 free(p);
a8704b50
PS
206 return err;
207 }
2b7b1427 208 /* Longest prefix matches first. */
bd53aa17 209 cls_rule_init(&p->cr, &match, priority);
d9b4ebc5 210
fccd7c09 211 ovs_mutex_lock(&mutex);
44e0c35d 212 cr = classifier_replace(&cls, &p->cr, OVS_VERSION_MIN, NULL, 0);
fccd7c09
JR
213 ovs_mutex_unlock(&mutex);
214
d9b4ebc5
PS
215 if (cr) {
216 /* An old rule with the same match was displaced. */
217 ovsrcu_postpone(rt_entry_free, ovs_router_entry_cast(cr));
218 }
7f9b8504 219 tnl_port_map_insert_ipdev(output_bridge);
a36de779 220 seq_change(tnl_conf_seq);
a8704b50 221 return 0;
d9b4ebc5
PS
222}
223
224void
0b8da9ae
TLSC
225ovs_router_insert(const struct in6_addr *ip_dst, uint8_t plen,
226 const char output_bridge[], const struct in6_addr *gw)
d9b4ebc5
PS
227{
228 ovs_router_insert__(plen, ip_dst, plen, output_bridge, gw);
229}
230
7f9b8504
PS
231
232static bool
233__rt_entry_delete(const struct cls_rule *cr)
234{
235 struct ovs_router_entry *p = ovs_router_entry_cast(cr);
236
237 tnl_port_map_delete_ipdev(p->output_bridge);
238 /* Remove it. */
239 cr = classifier_remove(&cls, cr);
240 if (cr) {
241 ovsrcu_postpone(rt_entry_free, ovs_router_entry_cast(cr));
242 return true;
243 }
244 return false;
245}
246
d9b4ebc5 247static bool
0b8da9ae 248rt_entry_delete(uint8_t priority, const struct in6_addr *ip6_dst, uint8_t plen)
d9b4ebc5 249{
dfea28b3 250 const struct cls_rule *cr;
d9b4ebc5
PS
251 struct cls_rule rule;
252 struct match match;
7f9b8504 253 bool res = false;
d9b4ebc5 254
0b8da9ae 255 rt_init_match(&match, ip6_dst, plen);
d9b4ebc5 256
bd53aa17 257 cls_rule_init(&rule, &match, priority);
d9b4ebc5
PS
258
259 /* Find the exact rule. */
44e0c35d 260 cr = classifier_find_rule_exactly(&cls, &rule, OVS_VERSION_MAX);
d9b4ebc5 261 if (cr) {
fccd7c09 262 ovs_mutex_lock(&mutex);
7f9b8504 263 res = __rt_entry_delete(cr);
fccd7c09 264 ovs_mutex_unlock(&mutex);
d9b4ebc5 265 }
7f9b8504 266 return res;
d9b4ebc5
PS
267}
268
0b8da9ae
TLSC
269static bool
270scan_ipv6_route(const char *s, struct in6_addr *addr, unsigned int *plen)
271{
e7695092 272 char *error = ipv6_parse_cidr(s, addr, plen);
184dfff0
JP
273 if (error) {
274 free(error);
275 return false;
0b8da9ae 276 }
184dfff0 277 return true;
0b8da9ae
TLSC
278}
279
d9b4ebc5
PS
280static bool
281scan_ipv4_route(const char *s, ovs_be32 *addr, unsigned int *plen)
282{
e7695092
BP
283 char *error = ip_parse_cidr(s, addr, plen);
284 if (error) {
285 free(error);
d9b4ebc5
PS
286 return false;
287 }
e7695092 288 return true;
d9b4ebc5
PS
289}
290
291static void
292ovs_router_add(struct unixctl_conn *conn, int argc,
293 const char *argv[], void *aux OVS_UNUSED)
294{
e7695092 295 ovs_be32 ip;
d9b4ebc5 296 unsigned int plen;
0b8da9ae
TLSC
297 struct in6_addr ip6;
298 struct in6_addr gw6;
a8704b50 299 int err;
d9b4ebc5
PS
300
301 if (scan_ipv4_route(argv[1], &ip, &plen)) {
e7695092
BP
302 ovs_be32 gw = 0;
303 if (argc > 3 && !ip_parse(argv[3], &gw)) {
304 unixctl_command_reply_error(conn, "Invalid gateway");
305 return;
d9b4ebc5 306 }
0b8da9ae
TLSC
307 in6_addr_set_mapped_ipv4(&ip6, ip);
308 in6_addr_set_mapped_ipv4(&gw6, gw);
309 plen += 96;
310 } else if (scan_ipv6_route(argv[1], &ip6, &plen)) {
e7695092
BP
311 gw6 = in6addr_any;
312 if (argc > 3 && !ipv6_parse(argv[3], &gw6)) {
313 unixctl_command_reply_error(conn, "Invalid IPv6 gateway");
314 return;
0b8da9ae 315 }
d9b4ebc5 316 } else {
f2e06274
BP
317 unixctl_command_reply_error(conn, "Invalid parameters");
318 return;
d9b4ebc5 319 }
a8704b50
PS
320 err = ovs_router_insert__(plen + 32, &ip6, plen, argv[2], &gw6);
321 if (err) {
9104895e 322 unixctl_command_reply_error(conn, "Error while inserting route.");
a8704b50
PS
323 } else {
324 unixctl_command_reply(conn, "OK");
325 }
d9b4ebc5
PS
326}
327
328static void
329ovs_router_del(struct unixctl_conn *conn, int argc OVS_UNUSED,
330 const char *argv[], void *aux OVS_UNUSED)
331{
332 ovs_be32 ip;
333 unsigned int plen;
0b8da9ae 334 struct in6_addr ip6;
d9b4ebc5
PS
335
336 if (scan_ipv4_route(argv[1], &ip, &plen)) {
0b8da9ae
TLSC
337 in6_addr_set_mapped_ipv4(&ip6, ip);
338 plen += 96;
339 } else if (!scan_ipv6_route(argv[1], &ip6, &plen)) {
f2e06274
BP
340 unixctl_command_reply_error(conn, "Invalid parameters");
341 return;
d9b4ebc5 342 }
0b8da9ae
TLSC
343 if (rt_entry_delete(plen + 32, &ip6, plen)) {
344 unixctl_command_reply(conn, "OK");
345 seq_change(tnl_conf_seq);
346 } else {
f2e06274 347 unixctl_command_reply_error(conn, "Not found");
0b8da9ae 348 }
d9b4ebc5
PS
349}
350
351static void
352ovs_router_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
353 const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
354{
355 struct ovs_router_entry *rt;
356 struct ds ds = DS_EMPTY_INITIALIZER;
357
358 ds_put_format(&ds, "Route Table:\n");
359 CLS_FOR_EACH(rt, cr, &cls) {
0b8da9ae 360 uint8_t plen;
d9b4ebc5
PS
361 if (rt->priority == rt->plen) {
362 ds_put_format(&ds, "Cached: ");
363 } else {
364 ds_put_format(&ds, "User: ");
365 }
ac6d120f 366 ipv6_format_mapped(&rt->nw_addr, &ds);
0b8da9ae
TLSC
367 plen = rt->plen;
368 if (IN6_IS_ADDR_V4MAPPED(&rt->nw_addr)) {
369 plen -= 96;
370 }
371 ds_put_format(&ds, "/%"PRIu16" dev %s", plen, rt->output_bridge);
372 if (ipv6_addr_is_set(&rt->gw)) {
373 ds_put_format(&ds, " GW ");
ac6d120f 374 ipv6_format_mapped(&rt->gw, &ds);
d9b4ebc5 375 }
a8704b50
PS
376 ds_put_format(&ds, " SRC ");
377 ipv6_format_mapped(&rt->src_addr, &ds);
d9b4ebc5
PS
378 ds_put_format(&ds, "\n");
379 }
380 unixctl_command_reply(conn, ds_cstr(&ds));
381 ds_destroy(&ds);
382}
383
bc786797
YT
384static void
385ovs_router_lookup_cmd(struct unixctl_conn *conn, int argc OVS_UNUSED,
386 const char *argv[], void *aux OVS_UNUSED)
387{
388 ovs_be32 ip;
0b8da9ae 389 struct in6_addr ip6;
bc786797 390 unsigned int plen;
0b8da9ae 391 char iface[IFNAMSIZ];
a8704b50 392 struct in6_addr gw, src;
bc786797
YT
393
394 if (scan_ipv4_route(argv[1], &ip, &plen) && plen == 32) {
0b8da9ae
TLSC
395 in6_addr_set_mapped_ipv4(&ip6, ip);
396 } else if (!(scan_ipv6_route(argv[1], &ip6, &plen) && plen == 128)) {
f2e06274
BP
397 unixctl_command_reply_error(conn, "Invalid parameters");
398 return;
0b8da9ae 399 }
bc786797 400
a8704b50 401 if (ovs_router_lookup(&ip6, iface, &src, &gw)) {
0b8da9ae 402 struct ds ds = DS_EMPTY_INITIALIZER;
a8704b50
PS
403 ds_put_format(&ds, "src ");
404 ipv6_format_mapped(&src, &ds);
0b8da9ae 405 ds_put_format(&ds, "gateway ");
a8704b50 406 ipv6_format_mapped(&gw, &ds);
0b8da9ae
TLSC
407 ds_put_format(&ds, "\ndev %s\n", iface);
408 unixctl_command_reply(conn, ds_cstr(&ds));
409 ds_destroy(&ds);
bc786797 410 } else {
f2e06274 411 unixctl_command_reply_error(conn, "Not found");
bc786797
YT
412 }
413}
414
d9b4ebc5
PS
415void
416ovs_router_flush(void)
417{
418 struct ovs_router_entry *rt;
419
802f84ff
JR
420 ovs_mutex_lock(&mutex);
421 classifier_defer(&cls);
de4ad4a2 422 CLS_FOR_EACH(rt, cr, &cls) {
d9b4ebc5 423 if (rt->priority == rt->plen) {
7f9b8504 424 __rt_entry_delete(&rt->cr);
d9b4ebc5
PS
425 }
426 }
802f84ff
JR
427 classifier_publish(&cls);
428 ovs_mutex_unlock(&mutex);
a36de779 429 seq_change(tnl_conf_seq);
d9b4ebc5
PS
430}
431
432/* May not be called more than once. */
433void
1bc50ef3 434ovs_router_init(void)
d9b4ebc5
PS
435{
436 classifier_init(&cls, NULL);
0b8da9ae 437 unixctl_command_register("ovs/route/add", "ip_addr/prefix_len out_br_name gw", 2, 3,
d9b4ebc5
PS
438 ovs_router_add, NULL);
439 unixctl_command_register("ovs/route/show", "", 0, 0, ovs_router_show, NULL);
0b8da9ae 440 unixctl_command_register("ovs/route/del", "ip_addr/prefix_len", 1, 1, ovs_router_del,
d9b4ebc5 441 NULL);
0b8da9ae 442 unixctl_command_register("ovs/route/lookup", "ip_addr", 1, 1,
bc786797 443 ovs_router_lookup_cmd, NULL);
d9b4ebc5 444}