Branch data Line data Source code
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 : 90902 : ovs_router_entry_cast(const struct cls_rule *cr)
64 : : {
65 : : if (offsetof(struct ovs_router_entry, cr) == 0) {
66 : 45451 : 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 : 14 : 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 [ + - ]: 14 : if (!route_table_fallback_lookup(ip6_dst, output_bridge, gw6)) {
79 : 14 : return false;
80 : : }
81 [ # # ]: 0 : if (netdev_get_in4_by_name(output_bridge, (struct in_addr *)&src)) {
82 : 0 : return false;
83 : : }
84 [ # # ]: 0 : if (src6) {
85 : 0 : in6_addr_set_mapped_ipv4(src6, src);
86 : : }
87 : 14 : return true;
88 : : }
89 : :
90 : : bool
91 : 6664 : 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 : 6664 : struct flow flow = {.ipv6_dst = *ip6_dst};
96 : :
97 : 6664 : cr = classifier_lookup(&cls, OVS_VERSION_MAX, &flow, NULL);
98 [ + + ]: 6664 : if (cr) {
99 : 6650 : struct ovs_router_entry *p = ovs_router_entry_cast(cr);
100 : :
101 : 6650 : ovs_strlcpy(output_bridge, p->output_bridge, IFNAMSIZ);
102 : 6650 : *gw = p->gw;
103 [ + + ]: 6650 : if (src) {
104 : 6252 : *src = p->src_addr;
105 : : }
106 : 6650 : return true;
107 : : }
108 : 6664 : return ovs_router_lookup_fallback(ip6_dst, output_bridge, src, gw);
109 : : }
110 : :
111 : : static void
112 : 21978 : rt_entry_free(struct ovs_router_entry *p)
113 : : {
114 : 21978 : cls_rule_destroy(&p->cr);
115 : 21978 : free(p);
116 : 21978 : }
117 : :
118 : 28476 : 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 : 28476 : mask = ipv6_create_mask(plen);
125 : :
126 : 28476 : dst = ipv6_addr_bitand(ip6_dst, &mask);
127 : 28476 : memset(match, 0, sizeof *match);
128 : 28476 : match->flow.ipv6_dst = dst;
129 : 28476 : match->wc.masks.ipv6_dst = mask;
130 : 28476 : }
131 : :
132 : : static int
133 : 28476 : 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 : 28476 : int err, n_in6, i, max_plen = -1;
138 : : struct netdev *dev;
139 : : bool is_ipv4;
140 : :
141 : 28476 : err = netdev_open(output_bridge, NULL, &dev);
142 [ - + ]: 28476 : if (err) {
143 : 0 : return err;
144 : : }
145 : :
146 : 28476 : err = netdev_get_addr_list(dev, &addr6, &mask, &n_in6);
147 [ + + ]: 28476 : if (err) {
148 : 738 : goto out;
149 : : }
150 : :
151 [ + + ][ + - ]: 27738 : is_ipv4 = IN6_IS_ADDR_V4MAPPED(ip6_dst);
[ + + ]
152 : :
153 [ + + ]: 78292 : for (i = 0; i < n_in6; i++) {
154 : : struct in6_addr a1, a2;
155 : : int mask_bits;
156 : :
157 [ + + ][ + + ]: 50554 : if (is_ipv4 && !IN6_IS_ADDR_V4MAPPED(&addr6[i])) {
[ + - ][ + + ]
[ + + ]
158 : 12393 : continue;
159 : : }
160 : :
161 : 38161 : a1 = ipv6_addr_bitand(ip6_dst, &mask[i]);
162 : 38161 : a2 = ipv6_addr_bitand(&addr6[i], &mask[i]);
163 : 38161 : mask_bits = bitmap_count1(ALIGNED_CAST(const unsigned long *, &mask[i]), 128);
164 : :
165 [ + + ][ + + ]: 38161 : if (!memcmp(&a1, &a2, sizeof (a1)) && mask_bits > max_plen) {
166 : 27738 : *psrc = addr6[i];
167 : 38161 : max_plen = mask_bits;
168 : : }
169 : : }
170 [ - + ]: 27738 : if (max_plen == -1) {
171 : 0 : err = ENOENT;
172 : : }
173 : : out:
174 : 28476 : free(addr6);
175 : 28476 : free(mask);
176 : 28476 : netdev_close(dev);
177 : 28476 : return err;
178 : : }
179 : :
180 : : static int
181 : 28476 : 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 : 28476 : rt_init_match(&match, ip6_dst, plen);
191 : :
192 : 28476 : p = xzalloc(sizeof *p);
193 : 28476 : ovs_strlcpy(p->output_bridge, output_bridge, sizeof p->output_bridge);
194 [ + + ]: 28476 : if (ipv6_addr_is_set(gw)) {
195 : 2532 : p->gw = *gw;
196 : : }
197 : 28476 : p->nw_addr = match.flow.ipv6_dst;
198 : 28476 : p->plen = plen;
199 : 28476 : p->priority = priority;
200 : 28476 : err = get_src_addr(ip6_dst, output_bridge, &p->src_addr);
201 [ + + ]: 28476 : if (err) {
202 : 738 : free(p);
203 : 738 : return err;
204 : : }
205 : : /* Longest prefix matches first. */
206 : 27738 : cls_rule_init(&p->cr, &match, priority);
207 : :
208 : 27738 : ovs_mutex_lock(&mutex);
209 : 27738 : cr = classifier_replace(&cls, &p->cr, OVS_VERSION_MIN, NULL, 0);
210 : 27738 : ovs_mutex_unlock(&mutex);
211 : :
212 [ + + ]: 27738 : if (cr) {
213 : : /* An old rule with the same match was displaced. */
214 : 5239 : ovsrcu_postpone(rt_entry_free, ovs_router_entry_cast(cr));
215 : : }
216 : 27738 : tnl_port_map_insert_ipdev(output_bridge);
217 : 27738 : seq_change(tnl_conf_seq);
218 : 28476 : return 0;
219 : : }
220 : :
221 : : void
222 : 28431 : ovs_router_insert(const struct in6_addr *ip_dst, uint8_t plen,
223 : : const char output_bridge[], const struct in6_addr *gw)
224 : : {
225 : 28431 : ovs_router_insert__(plen, ip_dst, plen, output_bridge, gw);
226 : 28431 : }
227 : :
228 : :
229 : : static bool
230 : 16781 : __rt_entry_delete(const struct cls_rule *cr)
231 : : {
232 : 16781 : struct ovs_router_entry *p = ovs_router_entry_cast(cr);
233 : :
234 : 16781 : tnl_port_map_delete_ipdev(p->output_bridge);
235 : : /* Remove it. */
236 : 16781 : cr = classifier_remove(&cls, cr);
237 [ + - ]: 16781 : if (cr) {
238 : 16781 : ovsrcu_postpone(rt_entry_free, ovs_router_entry_cast(cr));
239 : 16781 : return true;
240 : : }
241 : 0 : return false;
242 : : }
243 : :
244 : : static bool
245 : 0 : rt_entry_delete(uint8_t priority, const struct in6_addr *ip6_dst, uint8_t plen)
246 : : {
247 : : const struct cls_rule *cr;
248 : : struct cls_rule rule;
249 : : struct match match;
250 : 0 : bool res = false;
251 : :
252 : 0 : rt_init_match(&match, ip6_dst, plen);
253 : :
254 : 0 : cls_rule_init(&rule, &match, priority);
255 : :
256 : : /* Find the exact rule. */
257 : 0 : cr = classifier_find_rule_exactly(&cls, &rule, OVS_VERSION_MAX);
258 [ # # ]: 0 : if (cr) {
259 : 0 : ovs_mutex_lock(&mutex);
260 : 0 : res = __rt_entry_delete(cr);
261 : 0 : ovs_mutex_unlock(&mutex);
262 : : }
263 : 0 : return res;
264 : : }
265 : :
266 : : static bool
267 : 1 : scan_ipv6_route(const char *s, struct in6_addr *addr, unsigned int *plen)
268 : : {
269 : 1 : char *error = ipv6_parse_cidr(s, addr, plen);
270 [ - + ]: 1 : if (error) {
271 : 0 : free(error);
272 : 0 : return false;
273 : : }
274 : 1 : return true;
275 : : }
276 : :
277 : : static bool
278 : 45 : scan_ipv4_route(const char *s, ovs_be32 *addr, unsigned int *plen)
279 : : {
280 : 45 : char *error = ip_parse_cidr(s, addr, plen);
281 [ + + ]: 45 : if (error) {
282 : 1 : free(error);
283 : 1 : return false;
284 : : }
285 : 44 : return true;
286 : : }
287 : :
288 : : static void
289 : 45 : ovs_router_add(struct unixctl_conn *conn, int argc,
290 : : const char *argv[], void *aux OVS_UNUSED)
291 : : {
292 : : ovs_be32 ip;
293 : : unsigned int plen;
294 : : struct in6_addr ip6;
295 : : struct in6_addr gw6;
296 : : int err;
297 : :
298 [ + + ]: 45 : if (scan_ipv4_route(argv[1], &ip, &plen)) {
299 : 44 : ovs_be32 gw = 0;
300 [ - + ][ # # ]: 44 : if (argc > 3 && !ip_parse(argv[3], &gw)) {
301 : 0 : unixctl_command_reply_error(conn, "Invalid gateway");
302 : 0 : return;
303 : : }
304 : 44 : in6_addr_set_mapped_ipv4(&ip6, ip);
305 : 44 : in6_addr_set_mapped_ipv4(&gw6, gw);
306 : 44 : plen += 96;
307 [ + - ]: 1 : } else if (scan_ipv6_route(argv[1], &ip6, &plen)) {
308 : 1 : gw6 = in6addr_any;
309 [ - + ][ # # ]: 1 : if (argc > 3 && !ipv6_parse(argv[3], &gw6)) {
310 : 0 : unixctl_command_reply_error(conn, "Invalid IPv6 gateway");
311 : 0 : return;
312 : : }
313 : : } else {
314 : 0 : unixctl_command_reply_error(conn, "Invalid parameters");
315 : 0 : return;
316 : : }
317 : 45 : err = ovs_router_insert__(plen + 32, &ip6, plen, argv[2], &gw6);
318 [ - + ]: 45 : if (err) {
319 : 0 : unixctl_command_reply(conn, "Error while inserting route.");
320 : : } else {
321 : 45 : unixctl_command_reply(conn, "OK");
322 : : }
323 : : }
324 : :
325 : : static void
326 : 0 : ovs_router_del(struct unixctl_conn *conn, int argc OVS_UNUSED,
327 : : const char *argv[], void *aux OVS_UNUSED)
328 : : {
329 : : ovs_be32 ip;
330 : : unsigned int plen;
331 : : struct in6_addr ip6;
332 : :
333 [ # # ]: 0 : if (scan_ipv4_route(argv[1], &ip, &plen)) {
334 : 0 : in6_addr_set_mapped_ipv4(&ip6, ip);
335 : 0 : plen += 96;
336 [ # # ]: 0 : } else if (!scan_ipv6_route(argv[1], &ip6, &plen)) {
337 : 0 : unixctl_command_reply_error(conn, "Invalid parameters");
338 : 0 : return;
339 : : }
340 [ # # ]: 0 : if (rt_entry_delete(plen + 32, &ip6, plen)) {
341 : 0 : unixctl_command_reply(conn, "OK");
342 : 0 : seq_change(tnl_conf_seq);
343 : : } else {
344 : 0 : unixctl_command_reply_error(conn, "Not found");
345 : : }
346 : : }
347 : :
348 : : static void
349 : 0 : ovs_router_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
350 : : const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
351 : : {
352 : : struct ovs_router_entry *rt;
353 : 0 : struct ds ds = DS_EMPTY_INITIALIZER;
354 : :
355 : 0 : ds_put_format(&ds, "Route Table:\n");
356 [ # # ][ # # ]: 0 : CLS_FOR_EACH(rt, cr, &cls) {
357 : : uint8_t plen;
358 [ # # ]: 0 : if (rt->priority == rt->plen) {
359 : 0 : ds_put_format(&ds, "Cached: ");
360 : : } else {
361 : 0 : ds_put_format(&ds, "User: ");
362 : : }
363 : 0 : ipv6_format_mapped(&rt->nw_addr, &ds);
364 : 0 : plen = rt->plen;
365 [ # # ][ # # ]: 0 : if (IN6_IS_ADDR_V4MAPPED(&rt->nw_addr)) {
[ # # ][ # # ]
366 : 0 : plen -= 96;
367 : : }
368 : 0 : ds_put_format(&ds, "/%"PRIu16" dev %s", plen, rt->output_bridge);
369 [ # # ]: 0 : if (ipv6_addr_is_set(&rt->gw)) {
370 : 0 : ds_put_format(&ds, " GW ");
371 : 0 : ipv6_format_mapped(&rt->gw, &ds);
372 : : }
373 : 0 : ds_put_format(&ds, " SRC ");
374 : 0 : ipv6_format_mapped(&rt->src_addr, &ds);
375 : 0 : ds_put_format(&ds, "\n");
376 : : }
377 : 0 : unixctl_command_reply(conn, ds_cstr(&ds));
378 : 0 : ds_destroy(&ds);
379 : 0 : }
380 : :
381 : : static void
382 : 0 : ovs_router_lookup_cmd(struct unixctl_conn *conn, int argc OVS_UNUSED,
383 : : const char *argv[], void *aux OVS_UNUSED)
384 : : {
385 : : ovs_be32 ip;
386 : : struct in6_addr ip6;
387 : : unsigned int plen;
388 : : char iface[IFNAMSIZ];
389 : : struct in6_addr gw, src;
390 : :
391 [ # # ][ # # ]: 0 : if (scan_ipv4_route(argv[1], &ip, &plen) && plen == 32) {
392 : 0 : in6_addr_set_mapped_ipv4(&ip6, ip);
393 [ # # ][ # # ]: 0 : } else if (!(scan_ipv6_route(argv[1], &ip6, &plen) && plen == 128)) {
394 : 0 : unixctl_command_reply_error(conn, "Invalid parameters");
395 : 0 : return;
396 : : }
397 : :
398 [ # # ]: 0 : if (ovs_router_lookup(&ip6, iface, &src, &gw)) {
399 : 0 : struct ds ds = DS_EMPTY_INITIALIZER;
400 : 0 : ds_put_format(&ds, "src ");
401 : 0 : ipv6_format_mapped(&src, &ds);
402 : 0 : ds_put_format(&ds, "gateway ");
403 : 0 : ipv6_format_mapped(&gw, &ds);
404 : 0 : ds_put_format(&ds, "\ndev %s\n", iface);
405 : 0 : unixctl_command_reply(conn, ds_cstr(&ds));
406 : 0 : ds_destroy(&ds);
407 : : } else {
408 : 0 : unixctl_command_reply_error(conn, "Not found");
409 : : }
410 : : }
411 : :
412 : : void
413 : 2488 : ovs_router_flush(void)
414 : : {
415 : : struct ovs_router_entry *rt;
416 : :
417 : 2488 : ovs_mutex_lock(&mutex);
418 : 2488 : classifier_defer(&cls);
419 [ + + ][ + + ]: 19269 : CLS_FOR_EACH(rt, cr, &cls) {
420 [ + - ]: 16781 : if (rt->priority == rt->plen) {
421 : 16781 : __rt_entry_delete(&rt->cr);
422 : : }
423 : : }
424 : 2488 : classifier_publish(&cls);
425 : 2488 : ovs_mutex_unlock(&mutex);
426 : 2488 : seq_change(tnl_conf_seq);
427 : 2488 : }
428 : :
429 : : /* May not be called more than once. */
430 : : void
431 : 684 : ovs_router_init(void)
432 : : {
433 : 684 : classifier_init(&cls, NULL);
434 : 684 : unixctl_command_register("ovs/route/add", "ip_addr/prefix_len out_br_name gw", 2, 3,
435 : : ovs_router_add, NULL);
436 : 684 : unixctl_command_register("ovs/route/show", "", 0, 0, ovs_router_show, NULL);
437 : 684 : unixctl_command_register("ovs/route/del", "ip_addr/prefix_len", 1, 1, ovs_router_del,
438 : : NULL);
439 : 684 : unixctl_command_register("ovs/route/lookup", "ip_addr", 1, 1,
440 : : ovs_router_lookup_cmd, NULL);
441 : 684 : }
|