2 * Copyright (c) 2014, 2015, 2016, 2017 Nicira, Inc.
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:
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include "ovs-router.h"
21 #include <sys/types.h>
22 #include <netinet/in.h>
23 #include <arpa/inet.h>
26 #include <sys/socket.h>
33 #include "classifier.h"
34 #include "command-line.h"
37 #include "fatal-signal.h"
38 #include "openvswitch/dynamic-string.h"
42 #include "ovs-thread.h"
43 #include "route-table.h"
44 #include "tnl-ports.h"
47 #include "unaligned.h"
48 #include "openvswitch/vlog.h"
50 VLOG_DEFINE_THIS_MODULE(ovs_router
);
52 static struct vlog_rate_limit rl
= VLOG_RATE_LIMIT_INIT(1, 5);
54 static struct ovs_mutex mutex
= OVS_MUTEX_INITIALIZER
;
55 static struct classifier cls
;
57 /* By default, use the system routing table. For system-independent testing,
58 * the unit tests disable using the system routing table. */
59 static bool use_system_routing_table
= true;
61 struct ovs_router_entry
{
63 char output_bridge
[IFNAMSIZ
];
65 struct in6_addr nw_addr
;
66 struct in6_addr src_addr
;
73 static struct ovs_router_entry
*
74 ovs_router_entry_cast(const struct cls_rule
*cr
)
76 return cr
? CONTAINER_OF(cr
, struct ovs_router_entry
, cr
) : NULL
;
79 /* Disables obtaining routes from the system routing table, for testing
82 ovs_router_disable_system_routing_table(void)
84 use_system_routing_table
= false;
88 ovs_router_lookup_fallback(const struct in6_addr
*ip6_dst
, char output_bridge
[],
89 struct in6_addr
*src6
, struct in6_addr
*gw6
)
93 if (!use_system_routing_table
94 || !route_table_fallback_lookup(ip6_dst
, output_bridge
, gw6
)) {
97 if (netdev_get_in4_by_name(output_bridge
, (struct in_addr
*)&src
)) {
101 in6_addr_set_mapped_ipv4(src6
, src
);
107 ovs_router_lookup(uint32_t mark
, const struct in6_addr
*ip6_dst
,
108 char output_bridge
[],
109 struct in6_addr
*src
, struct in6_addr
*gw
)
111 const struct cls_rule
*cr
;
112 struct flow flow
= {.ipv6_dst
= *ip6_dst
, .pkt_mark
= mark
};
114 if (src
&& ipv6_addr_is_set(src
)) {
115 const struct cls_rule
*cr_src
;
116 struct flow flow_src
= {.ipv6_dst
= *src
, .pkt_mark
= mark
};
118 cr_src
= classifier_lookup(&cls
, OVS_VERSION_MAX
, &flow_src
, NULL
);
120 struct ovs_router_entry
*p_src
= ovs_router_entry_cast(cr_src
);
129 cr
= classifier_lookup(&cls
, OVS_VERSION_MAX
, &flow
, NULL
);
131 struct ovs_router_entry
*p
= ovs_router_entry_cast(cr
);
133 ovs_strlcpy(output_bridge
, p
->output_bridge
, IFNAMSIZ
);
135 if (src
&& !ipv6_addr_is_set(src
)) {
140 return ovs_router_lookup_fallback(ip6_dst
, output_bridge
, src
, gw
);
144 rt_entry_free(struct ovs_router_entry
*p
)
146 cls_rule_destroy(&p
->cr
);
150 static void rt_init_match(struct match
*match
, uint32_t mark
,
151 const struct in6_addr
*ip6_dst
,
155 struct in6_addr mask
;
157 mask
= ipv6_create_mask(plen
);
159 dst
= ipv6_addr_bitand(ip6_dst
, &mask
);
160 memset(match
, 0, sizeof *match
);
161 match
->flow
.ipv6_dst
= dst
;
162 match
->wc
.masks
.ipv6_dst
= mask
;
163 match
->wc
.masks
.pkt_mark
= UINT32_MAX
;
164 match
->flow
.pkt_mark
= mark
;
168 get_src_addr(const struct in6_addr
*ip6_dst
,
169 const char output_bridge
[], struct in6_addr
*psrc
)
171 struct in6_addr
*mask
, *addr6
;
172 int err
, n_in6
, i
, max_plen
= -1;
176 err
= netdev_open(output_bridge
, NULL
, &dev
);
181 err
= netdev_get_addr_list(dev
, &addr6
, &mask
, &n_in6
);
186 is_ipv4
= IN6_IS_ADDR_V4MAPPED(ip6_dst
);
188 for (i
= 0; i
< n_in6
; i
++) {
189 struct in6_addr a1
, a2
;
192 if (is_ipv4
&& !IN6_IS_ADDR_V4MAPPED(&addr6
[i
])) {
196 a1
= ipv6_addr_bitand(ip6_dst
, &mask
[i
]);
197 a2
= ipv6_addr_bitand(&addr6
[i
], &mask
[i
]);
198 mask_bits
= bitmap_count1(ALIGNED_CAST(const unsigned long *, &mask
[i
]), 128);
200 if (!memcmp(&a1
, &a2
, sizeof (a1
)) && mask_bits
> max_plen
) {
202 max_plen
= mask_bits
;
205 if (max_plen
== -1) {
216 ovs_router_insert__(uint32_t mark
, uint8_t priority
, bool local
,
217 const struct in6_addr
*ip6_dst
,
218 uint8_t plen
, const char output_bridge
[],
219 const struct in6_addr
*gw
)
221 const struct cls_rule
*cr
;
222 struct ovs_router_entry
*p
;
226 rt_init_match(&match
, mark
, ip6_dst
, plen
);
228 p
= xzalloc(sizeof *p
);
229 ovs_strlcpy(p
->output_bridge
, output_bridge
, sizeof p
->output_bridge
);
230 if (ipv6_addr_is_set(gw
)) {
234 p
->nw_addr
= match
.flow
.ipv6_dst
;
237 p
->priority
= priority
;
238 err
= get_src_addr(ip6_dst
, output_bridge
, &p
->src_addr
);
239 if (err
&& ipv6_addr_is_set(gw
)) {
240 err
= get_src_addr(gw
, output_bridge
, &p
->src_addr
);
243 struct ds ds
= DS_EMPTY_INITIALIZER
;
245 ipv6_format_mapped(ip6_dst
, &ds
);
246 VLOG_DBG_RL(&rl
, "src addr not available for route %s", ds_cstr(&ds
));
251 /* Longest prefix matches first. */
252 cls_rule_init(&p
->cr
, &match
, priority
);
254 ovs_mutex_lock(&mutex
);
255 cr
= classifier_replace(&cls
, &p
->cr
, OVS_VERSION_MIN
, NULL
, 0);
256 ovs_mutex_unlock(&mutex
);
259 /* An old rule with the same match was displaced. */
260 ovsrcu_postpone(rt_entry_free
, ovs_router_entry_cast(cr
));
262 tnl_port_map_insert_ipdev(output_bridge
);
263 seq_change(tnl_conf_seq
);
268 ovs_router_insert(uint32_t mark
, const struct in6_addr
*ip_dst
, uint8_t plen
,
269 bool local
, const char output_bridge
[],
270 const struct in6_addr
*gw
)
272 if (use_system_routing_table
) {
273 uint8_t priority
= local
? plen
+ 64 : plen
;
274 ovs_router_insert__(mark
, priority
, local
, ip_dst
, plen
, output_bridge
, gw
);
279 rt_entry_delete__(const struct cls_rule
*cr
)
281 struct ovs_router_entry
*p
= ovs_router_entry_cast(cr
);
283 tnl_port_map_delete_ipdev(p
->output_bridge
);
284 classifier_remove_assert(&cls
, cr
);
285 ovsrcu_postpone(rt_entry_free
, ovs_router_entry_cast(cr
));
289 rt_entry_delete(uint32_t mark
, uint8_t priority
,
290 const struct in6_addr
*ip6_dst
, uint8_t plen
)
292 const struct cls_rule
*cr
;
293 struct cls_rule rule
;
297 rt_init_match(&match
, mark
, ip6_dst
, plen
);
299 cls_rule_init(&rule
, &match
, priority
);
301 /* Find the exact rule. */
302 cr
= classifier_find_rule_exactly(&cls
, &rule
, OVS_VERSION_MAX
);
304 ovs_mutex_lock(&mutex
);
305 rt_entry_delete__(cr
);
306 ovs_mutex_unlock(&mutex
);
311 cls_rule_destroy(&rule
);
316 scan_ipv6_route(const char *s
, struct in6_addr
*addr
, unsigned int *plen
)
318 char *error
= ipv6_parse_cidr(s
, addr
, plen
);
327 scan_ipv4_route(const char *s
, ovs_be32
*addr
, unsigned int *plen
)
329 char *error
= ip_parse_cidr(s
, addr
, plen
);
338 ovs_router_add(struct unixctl_conn
*conn
, int argc
,
339 const char *argv
[], void *aux OVS_UNUSED
)
341 struct in6_addr gw6
= in6addr_any
;
348 if (scan_ipv4_route(argv
[1], &ip
, &plen
)) {
352 if (!ovs_scan(argv
[3], "pkt_mark=%"SCNi32
, &mark
) &&
353 !ip_parse(argv
[3], &gw
)) {
354 unixctl_command_reply_error(conn
, "Invalid pkt_mark or gateway");
358 in6_addr_set_mapped_ipv4(&ip6
, ip
);
360 in6_addr_set_mapped_ipv4(&gw6
, gw
);
363 } else if (scan_ipv6_route(argv
[1], &ip6
, &plen
)) {
365 if (!ovs_scan(argv
[3], "pkt_mark=%"SCNi32
, &mark
) &&
366 !ipv6_parse(argv
[3], &gw6
)) {
367 unixctl_command_reply_error(conn
, "Invalid pkt_mark or IPv6 gateway");
372 unixctl_command_reply_error(conn
, "Invalid parameters");
376 if (!ovs_scan(argv
[4], "pkt_mark=%"SCNi32
, &mark
)) {
377 unixctl_command_reply_error(conn
, "Invalid pkt_mark");
382 err
= ovs_router_insert__(mark
, plen
+ 32, false, &ip6
, plen
, argv
[2], &gw6
);
384 unixctl_command_reply_error(conn
, "Error while inserting route.");
386 unixctl_command_reply(conn
, "OK");
391 ovs_router_del(struct unixctl_conn
*conn
, int argc OVS_UNUSED
,
392 const char *argv
[], void *aux OVS_UNUSED
)
399 if (scan_ipv4_route(argv
[1], &ip
, &plen
)) {
400 in6_addr_set_mapped_ipv4(&ip6
, ip
);
402 } else if (!scan_ipv6_route(argv
[1], &ip6
, &plen
)) {
403 unixctl_command_reply_error(conn
, "Invalid parameters");
407 if (!ovs_scan(argv
[2], "pkt_mark=%"SCNi32
, &mark
)) {
408 unixctl_command_reply_error(conn
, "Invalid pkt_mark");
413 if (rt_entry_delete(mark
, plen
+ 32, &ip6
, plen
)) {
414 unixctl_command_reply(conn
, "OK");
415 seq_change(tnl_conf_seq
);
417 unixctl_command_reply_error(conn
, "Not found");
422 ovs_router_show(struct unixctl_conn
*conn
, int argc OVS_UNUSED
,
423 const char *argv
[] OVS_UNUSED
, void *aux OVS_UNUSED
)
425 struct ovs_router_entry
*rt
;
426 struct ds ds
= DS_EMPTY_INITIALIZER
;
428 ds_put_format(&ds
, "Route Table:\n");
429 CLS_FOR_EACH(rt
, cr
, &cls
) {
431 if (rt
->priority
== rt
->plen
|| rt
->local
) {
432 ds_put_format(&ds
, "Cached: ");
434 ds_put_format(&ds
, "User: ");
436 ipv6_format_mapped(&rt
->nw_addr
, &ds
);
438 if (IN6_IS_ADDR_V4MAPPED(&rt
->nw_addr
)) {
441 ds_put_format(&ds
, "/%"PRIu8
, plen
);
443 ds_put_format(&ds
, " MARK %"PRIu32
, rt
->mark
);
446 ds_put_format(&ds
, " dev %s", rt
->output_bridge
);
447 if (ipv6_addr_is_set(&rt
->gw
)) {
448 ds_put_format(&ds
, " GW ");
449 ipv6_format_mapped(&rt
->gw
, &ds
);
451 ds_put_format(&ds
, " SRC ");
452 ipv6_format_mapped(&rt
->src_addr
, &ds
);
454 ds_put_format(&ds
, " local");
456 ds_put_format(&ds
, "\n");
458 unixctl_command_reply(conn
, ds_cstr(&ds
));
463 ovs_router_lookup_cmd(struct unixctl_conn
*conn
, int argc
,
464 const char *argv
[], void *aux OVS_UNUSED
)
466 struct in6_addr gw
, src
= in6addr_any
;
467 char iface
[IFNAMSIZ
];
473 if (scan_ipv4_route(argv
[1], &ip
, &plen
) && plen
== 32) {
474 in6_addr_set_mapped_ipv4(&ip6
, ip
);
475 } else if (!(scan_ipv6_route(argv
[1], &ip6
, &plen
) && plen
== 128)) {
476 unixctl_command_reply_error(conn
, "Invalid parameters");
480 if (!ovs_scan(argv
[2], "pkt_mark=%"SCNi32
, &mark
)) {
481 unixctl_command_reply_error(conn
, "Invalid pkt_mark");
485 if (ovs_router_lookup(mark
, &ip6
, iface
, &src
, &gw
)) {
486 struct ds ds
= DS_EMPTY_INITIALIZER
;
488 ds_put_format(&ds
, "src ");
489 ipv6_format_mapped(&src
, &ds
);
490 ds_put_format(&ds
, "\ngateway ");
491 ipv6_format_mapped(&gw
, &ds
);
492 ds_put_format(&ds
, "\ndev %s\n", iface
);
493 unixctl_command_reply(conn
, ds_cstr(&ds
));
496 unixctl_command_reply_error(conn
, "Not found");
501 ovs_router_flush(void)
503 struct ovs_router_entry
*rt
;
505 ovs_mutex_lock(&mutex
);
506 classifier_defer(&cls
);
507 CLS_FOR_EACH(rt
, cr
, &cls
) {
508 if (rt
->priority
== rt
->plen
) {
509 rt_entry_delete__(&rt
->cr
);
512 classifier_publish(&cls
);
513 ovs_mutex_unlock(&mutex
);
514 seq_change(tnl_conf_seq
);
518 ovs_router_flush_handler(void *aux OVS_UNUSED
)
524 ovs_router_init(void)
526 static struct ovsthread_once once
= OVSTHREAD_ONCE_INITIALIZER
;
528 if (ovsthread_once_start(&once
)) {
529 fatal_signal_add_hook(ovs_router_flush_handler
, NULL
, NULL
, true);
530 classifier_init(&cls
, NULL
);
531 unixctl_command_register("ovs/route/add",
532 "ip_addr/prefix_len out_br_name [gw] "
534 2, 4, ovs_router_add
, NULL
);
535 unixctl_command_register("ovs/route/show", "", 0, 0,
536 ovs_router_show
, NULL
);
537 unixctl_command_register("ovs/route/del", "ip_addr/prefix_len "
538 "[pkt_mark=mark]", 1, 2, ovs_router_del
,
540 unixctl_command_register("ovs/route/lookup", "ip_addr "
541 "[pkt_mark=mark]", 1, 2,
542 ovs_router_lookup_cmd
, NULL
);
543 ovsthread_once_done(&once
);