]> git.proxmox.com Git - ovs.git/blob - lib/ovs-router.c
ovs-router: add missing newline in lookup output
[ovs.git] / lib / ovs-router.c
1 /*
2 * Copyright (c) 2014, 2015, 2016 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 <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"
36 #include "openvswitch/dynamic-string.h"
37 #include "netdev.h"
38 #include "packets.h"
39 #include "seq.h"
40 #include "ovs-thread.h"
41 #include "route-table.h"
42 #include "tnl-ports.h"
43 #include "unixctl.h"
44 #include "util.h"
45 #include "unaligned.h"
46 #include "unixctl.h"
47 #include "util.h"
48
49 static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER;
50 static struct classifier cls;
51
52 struct ovs_router_entry {
53 struct cls_rule cr;
54 char output_bridge[IFNAMSIZ];
55 struct in6_addr gw;
56 struct in6_addr nw_addr;
57 struct in6_addr src_addr;
58 uint8_t plen;
59 uint8_t priority;
60 };
61
62 static struct ovs_router_entry *
63 ovs_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
72 static bool
73 ovs_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
90 bool
91 ovs_router_lookup(const struct in6_addr *ip6_dst, char output_bridge[],
92 struct in6_addr *src, struct in6_addr *gw)
93 {
94 const struct cls_rule *cr;
95 struct flow flow = {.ipv6_dst = *ip6_dst};
96
97 cr = classifier_lookup(&cls, OVS_VERSION_MAX, &flow, NULL);
98 if (cr) {
99 struct ovs_router_entry *p = ovs_router_entry_cast(cr);
100
101 ovs_strlcpy(output_bridge, p->output_bridge, IFNAMSIZ);
102 *gw = p->gw;
103 if (src) {
104 *src = p->src_addr;
105 }
106 return true;
107 }
108 return ovs_router_lookup_fallback(ip6_dst, output_bridge, src, gw);
109 }
110
111 static void
112 rt_entry_free(struct ovs_router_entry *p)
113 {
114 cls_rule_destroy(&p->cr);
115 free(p);
116 }
117
118 static void rt_init_match(struct match *match, const struct in6_addr *ip6_dst,
119 uint8_t plen)
120 {
121 struct in6_addr dst;
122 struct in6_addr mask;
123
124 mask = ipv6_create_mask(plen);
125
126 dst = ipv6_addr_bitand(ip6_dst, &mask);
127 memset(match, 0, sizeof *match);
128 match->flow.ipv6_dst = dst;
129 match->wc.masks.ipv6_dst = mask;
130 }
131
132 static int
133 get_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 bool is_ipv4;
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
151 is_ipv4 = IN6_IS_ADDR_V4MAPPED(ip6_dst);
152
153 for (i = 0; i < n_in6; i++) {
154 struct in6_addr a1, a2;
155 int mask_bits;
156
157 if (is_ipv4 && !IN6_IS_ADDR_V4MAPPED(&addr6[i])) {
158 continue;
159 }
160
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 }
173 out:
174 free(addr6);
175 free(mask);
176 netdev_close(dev);
177 return err;
178 }
179
180 static int
181 ovs_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)
184 {
185 const struct cls_rule *cr;
186 struct ovs_router_entry *p;
187 struct match match;
188 int err;
189
190 rt_init_match(&match, ip6_dst, plen);
191
192 p = xzalloc(sizeof *p);
193 ovs_strlcpy(p->output_bridge, output_bridge, sizeof p->output_bridge);
194 if (ipv6_addr_is_set(gw)) {
195 p->gw = *gw;
196 }
197 p->nw_addr = match.flow.ipv6_dst;
198 p->plen = plen;
199 p->priority = priority;
200 err = get_src_addr(ip6_dst, output_bridge, &p->src_addr);
201 if (err && ipv6_addr_is_set(gw)) {
202 err = get_src_addr(gw, output_bridge, &p->src_addr);
203 }
204 if (err) {
205 free(p);
206 return err;
207 }
208 /* Longest prefix matches first. */
209 cls_rule_init(&p->cr, &match, priority);
210
211 ovs_mutex_lock(&mutex);
212 cr = classifier_replace(&cls, &p->cr, OVS_VERSION_MIN, NULL, 0);
213 ovs_mutex_unlock(&mutex);
214
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 }
219 tnl_port_map_insert_ipdev(output_bridge);
220 seq_change(tnl_conf_seq);
221 return 0;
222 }
223
224 void
225 ovs_router_insert(const struct in6_addr *ip_dst, uint8_t plen,
226 const char output_bridge[], const struct in6_addr *gw)
227 {
228 ovs_router_insert__(plen, ip_dst, plen, output_bridge, gw);
229 }
230
231
232 static 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
247 static bool
248 rt_entry_delete(uint8_t priority, const struct in6_addr *ip6_dst, uint8_t plen)
249 {
250 const struct cls_rule *cr;
251 struct cls_rule rule;
252 struct match match;
253 bool res = false;
254
255 rt_init_match(&match, ip6_dst, plen);
256
257 cls_rule_init(&rule, &match, priority);
258
259 /* Find the exact rule. */
260 cr = classifier_find_rule_exactly(&cls, &rule, OVS_VERSION_MAX);
261 if (cr) {
262 ovs_mutex_lock(&mutex);
263 res = __rt_entry_delete(cr);
264 ovs_mutex_unlock(&mutex);
265 }
266 return res;
267 }
268
269 static bool
270 scan_ipv6_route(const char *s, struct in6_addr *addr, unsigned int *plen)
271 {
272 char *error = ipv6_parse_cidr(s, addr, plen);
273 if (error) {
274 free(error);
275 return false;
276 }
277 return true;
278 }
279
280 static bool
281 scan_ipv4_route(const char *s, ovs_be32 *addr, unsigned int *plen)
282 {
283 char *error = ip_parse_cidr(s, addr, plen);
284 if (error) {
285 free(error);
286 return false;
287 }
288 return true;
289 }
290
291 static void
292 ovs_router_add(struct unixctl_conn *conn, int argc,
293 const char *argv[], void *aux OVS_UNUSED)
294 {
295 ovs_be32 ip;
296 unsigned int plen;
297 struct in6_addr ip6;
298 struct in6_addr gw6;
299 int err;
300
301 if (scan_ipv4_route(argv[1], &ip, &plen)) {
302 ovs_be32 gw = 0;
303 if (argc > 3 && !ip_parse(argv[3], &gw)) {
304 unixctl_command_reply_error(conn, "Invalid gateway");
305 return;
306 }
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)) {
311 gw6 = in6addr_any;
312 if (argc > 3 && !ipv6_parse(argv[3], &gw6)) {
313 unixctl_command_reply_error(conn, "Invalid IPv6 gateway");
314 return;
315 }
316 } else {
317 unixctl_command_reply_error(conn, "Invalid parameters");
318 return;
319 }
320 err = ovs_router_insert__(plen + 32, &ip6, plen, argv[2], &gw6);
321 if (err) {
322 unixctl_command_reply_error(conn, "Error while inserting route.");
323 } else {
324 unixctl_command_reply(conn, "OK");
325 }
326 }
327
328 static void
329 ovs_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;
334 struct in6_addr ip6;
335
336 if (scan_ipv4_route(argv[1], &ip, &plen)) {
337 in6_addr_set_mapped_ipv4(&ip6, ip);
338 plen += 96;
339 } else if (!scan_ipv6_route(argv[1], &ip6, &plen)) {
340 unixctl_command_reply_error(conn, "Invalid parameters");
341 return;
342 }
343 if (rt_entry_delete(plen + 32, &ip6, plen)) {
344 unixctl_command_reply(conn, "OK");
345 seq_change(tnl_conf_seq);
346 } else {
347 unixctl_command_reply_error(conn, "Not found");
348 }
349 }
350
351 static void
352 ovs_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) {
360 uint8_t plen;
361 if (rt->priority == rt->plen) {
362 ds_put_format(&ds, "Cached: ");
363 } else {
364 ds_put_format(&ds, "User: ");
365 }
366 ipv6_format_mapped(&rt->nw_addr, &ds);
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 ");
374 ipv6_format_mapped(&rt->gw, &ds);
375 }
376 ds_put_format(&ds, " SRC ");
377 ipv6_format_mapped(&rt->src_addr, &ds);
378 ds_put_format(&ds, "\n");
379 }
380 unixctl_command_reply(conn, ds_cstr(&ds));
381 ds_destroy(&ds);
382 }
383
384 static void
385 ovs_router_lookup_cmd(struct unixctl_conn *conn, int argc OVS_UNUSED,
386 const char *argv[], void *aux OVS_UNUSED)
387 {
388 ovs_be32 ip;
389 struct in6_addr ip6;
390 unsigned int plen;
391 char iface[IFNAMSIZ];
392 struct in6_addr gw, src;
393
394 if (scan_ipv4_route(argv[1], &ip, &plen) && plen == 32) {
395 in6_addr_set_mapped_ipv4(&ip6, ip);
396 } else if (!(scan_ipv6_route(argv[1], &ip6, &plen) && plen == 128)) {
397 unixctl_command_reply_error(conn, "Invalid parameters");
398 return;
399 }
400
401 if (ovs_router_lookup(&ip6, iface, &src, &gw)) {
402 struct ds ds = DS_EMPTY_INITIALIZER;
403 ds_put_format(&ds, "src ");
404 ipv6_format_mapped(&src, &ds);
405 ds_put_format(&ds, "\ngateway ");
406 ipv6_format_mapped(&gw, &ds);
407 ds_put_format(&ds, "\ndev %s\n", iface);
408 unixctl_command_reply(conn, ds_cstr(&ds));
409 ds_destroy(&ds);
410 } else {
411 unixctl_command_reply_error(conn, "Not found");
412 }
413 }
414
415 void
416 ovs_router_flush(void)
417 {
418 struct ovs_router_entry *rt;
419
420 ovs_mutex_lock(&mutex);
421 classifier_defer(&cls);
422 CLS_FOR_EACH(rt, cr, &cls) {
423 if (rt->priority == rt->plen) {
424 __rt_entry_delete(&rt->cr);
425 }
426 }
427 classifier_publish(&cls);
428 ovs_mutex_unlock(&mutex);
429 seq_change(tnl_conf_seq);
430 }
431
432 /* May not be called more than once. */
433 void
434 ovs_router_init(void)
435 {
436 classifier_init(&cls, NULL);
437 unixctl_command_register("ovs/route/add", "ip_addr/prefix_len out_br_name gw", 2, 3,
438 ovs_router_add, NULL);
439 unixctl_command_register("ovs/route/show", "", 0, 0, ovs_router_show, NULL);
440 unixctl_command_register("ovs/route/del", "ip_addr/prefix_len", 1, 1, ovs_router_del,
441 NULL);
442 unixctl_command_register("ovs/route/lookup", "ip_addr", 1, 1,
443 ovs_router_lookup_cmd, NULL);
444 }