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 <arpa/inet.h>
24 #include <sys/socket.h>
26 #include <netinet/in.h>
32 #include "classifier.h"
33 #include "command-line.h"
36 #include "fatal-signal.h"
37 #include "openvswitch/dynamic-string.h"
41 #include "ovs-thread.h"
42 #include "route-table.h"
43 #include "tnl-ports.h"
46 #include "unaligned.h"
49 #include "openvswitch/vlog.h"
51 VLOG_DEFINE_THIS_MODULE(ovs_router
);
53 static struct vlog_rate_limit rl
= VLOG_RATE_LIMIT_INIT(1, 5);
55 static struct ovs_mutex mutex
= OVS_MUTEX_INITIALIZER
;
56 static struct classifier cls
;
58 struct ovs_router_entry
{
60 char output_bridge
[IFNAMSIZ
];
62 struct in6_addr nw_addr
;
63 struct in6_addr src_addr
;
69 static struct ovs_router_entry
*
70 ovs_router_entry_cast(const struct cls_rule
*cr
)
72 if (offsetof(struct ovs_router_entry
, cr
) == 0) {
73 return CONTAINER_OF(cr
, struct ovs_router_entry
, cr
);
75 return cr
? CONTAINER_OF(cr
, struct ovs_router_entry
, cr
) : NULL
;
80 ovs_router_lookup_fallback(const struct in6_addr
*ip6_dst
, char output_bridge
[],
81 struct in6_addr
*src6
, struct in6_addr
*gw6
)
85 if (!route_table_fallback_lookup(ip6_dst
, output_bridge
, gw6
)) {
88 if (netdev_get_in4_by_name(output_bridge
, (struct in_addr
*)&src
)) {
92 in6_addr_set_mapped_ipv4(src6
, src
);
98 ovs_router_lookup(uint32_t mark
, const struct in6_addr
*ip6_dst
,
100 struct in6_addr
*src
, struct in6_addr
*gw
)
102 const struct cls_rule
*cr
;
103 struct flow flow
= {.ipv6_dst
= *ip6_dst
, .pkt_mark
= mark
};
105 cr
= classifier_lookup(&cls
, OVS_VERSION_MAX
, &flow
, NULL
);
107 struct ovs_router_entry
*p
= ovs_router_entry_cast(cr
);
109 ovs_strlcpy(output_bridge
, p
->output_bridge
, IFNAMSIZ
);
116 return ovs_router_lookup_fallback(ip6_dst
, output_bridge
, src
, gw
);
120 rt_entry_free(struct ovs_router_entry
*p
)
122 cls_rule_destroy(&p
->cr
);
126 static void rt_init_match(struct match
*match
, uint32_t mark
,
127 const struct in6_addr
*ip6_dst
,
131 struct in6_addr mask
;
133 mask
= ipv6_create_mask(plen
);
135 dst
= ipv6_addr_bitand(ip6_dst
, &mask
);
136 memset(match
, 0, sizeof *match
);
137 match
->flow
.ipv6_dst
= dst
;
138 match
->wc
.masks
.ipv6_dst
= mask
;
139 match
->wc
.masks
.pkt_mark
= UINT32_MAX
;
140 match
->flow
.pkt_mark
= mark
;
144 get_src_addr(const struct in6_addr
*ip6_dst
,
145 const char output_bridge
[], struct in6_addr
*psrc
)
147 struct in6_addr
*mask
, *addr6
;
148 int err
, n_in6
, i
, max_plen
= -1;
152 err
= netdev_open(output_bridge
, NULL
, &dev
);
157 err
= netdev_get_addr_list(dev
, &addr6
, &mask
, &n_in6
);
162 is_ipv4
= IN6_IS_ADDR_V4MAPPED(ip6_dst
);
164 for (i
= 0; i
< n_in6
; i
++) {
165 struct in6_addr a1
, a2
;
168 if (is_ipv4
&& !IN6_IS_ADDR_V4MAPPED(&addr6
[i
])) {
172 a1
= ipv6_addr_bitand(ip6_dst
, &mask
[i
]);
173 a2
= ipv6_addr_bitand(&addr6
[i
], &mask
[i
]);
174 mask_bits
= bitmap_count1(ALIGNED_CAST(const unsigned long *, &mask
[i
]), 128);
176 if (!memcmp(&a1
, &a2
, sizeof (a1
)) && mask_bits
> max_plen
) {
178 max_plen
= mask_bits
;
181 if (max_plen
== -1) {
192 ovs_router_insert__(uint32_t mark
, uint8_t priority
,
193 const struct in6_addr
*ip6_dst
,
194 uint8_t plen
, const char output_bridge
[],
195 const struct in6_addr
*gw
)
197 const struct cls_rule
*cr
;
198 struct ovs_router_entry
*p
;
202 rt_init_match(&match
, mark
, ip6_dst
, plen
);
204 p
= xzalloc(sizeof *p
);
205 ovs_strlcpy(p
->output_bridge
, output_bridge
, sizeof p
->output_bridge
);
206 if (ipv6_addr_is_set(gw
)) {
210 p
->nw_addr
= match
.flow
.ipv6_dst
;
212 p
->priority
= priority
;
213 err
= get_src_addr(ip6_dst
, output_bridge
, &p
->src_addr
);
214 if (err
&& ipv6_addr_is_set(gw
)) {
215 err
= get_src_addr(gw
, output_bridge
, &p
->src_addr
);
218 struct ds ds
= DS_EMPTY_INITIALIZER
;
220 ipv6_format_mapped(ip6_dst
, &ds
);
221 VLOG_DBG_RL(&rl
, "src addr not available for route %s", ds_cstr(&ds
));
226 /* Longest prefix matches first. */
227 cls_rule_init(&p
->cr
, &match
, priority
);
229 ovs_mutex_lock(&mutex
);
230 cr
= classifier_replace(&cls
, &p
->cr
, OVS_VERSION_MIN
, NULL
, 0);
231 ovs_mutex_unlock(&mutex
);
234 /* An old rule with the same match was displaced. */
235 ovsrcu_postpone(rt_entry_free
, ovs_router_entry_cast(cr
));
237 tnl_port_map_insert_ipdev(output_bridge
);
238 seq_change(tnl_conf_seq
);
243 ovs_router_insert(uint32_t mark
, const struct in6_addr
*ip_dst
, uint8_t plen
,
244 const char output_bridge
[], const struct in6_addr
*gw
)
246 ovs_router_insert__(mark
, plen
, ip_dst
, plen
, output_bridge
, gw
);
250 __rt_entry_delete(const struct cls_rule
*cr
)
252 struct ovs_router_entry
*p
= ovs_router_entry_cast(cr
);
254 tnl_port_map_delete_ipdev(p
->output_bridge
);
256 cr
= classifier_remove(&cls
, cr
);
258 ovsrcu_postpone(rt_entry_free
, ovs_router_entry_cast(cr
));
265 rt_entry_delete(uint32_t mark
, uint8_t priority
,
266 const struct in6_addr
*ip6_dst
, uint8_t plen
)
268 const struct cls_rule
*cr
;
269 struct cls_rule rule
;
273 rt_init_match(&match
, mark
, ip6_dst
, plen
);
275 cls_rule_init(&rule
, &match
, priority
);
277 /* Find the exact rule. */
278 cr
= classifier_find_rule_exactly(&cls
, &rule
, OVS_VERSION_MAX
);
280 ovs_mutex_lock(&mutex
);
281 res
= __rt_entry_delete(cr
);
282 ovs_mutex_unlock(&mutex
);
285 cls_rule_destroy(&rule
);
290 scan_ipv6_route(const char *s
, struct in6_addr
*addr
, unsigned int *plen
)
292 char *error
= ipv6_parse_cidr(s
, addr
, plen
);
301 scan_ipv4_route(const char *s
, ovs_be32
*addr
, unsigned int *plen
)
303 char *error
= ip_parse_cidr(s
, addr
, plen
);
312 ovs_router_add(struct unixctl_conn
*conn
, int argc
,
313 const char *argv
[], void *aux OVS_UNUSED
)
315 struct in6_addr gw6
= in6addr_any
;
322 if (scan_ipv4_route(argv
[1], &ip
, &plen
)) {
326 if (!ovs_scan(argv
[3], "pkt_mark=%"SCNi32
, &mark
) &&
327 !ip_parse(argv
[3], &gw
)) {
328 unixctl_command_reply_error(conn
, "Invalid pkt_mark or gateway");
332 in6_addr_set_mapped_ipv4(&ip6
, ip
);
334 in6_addr_set_mapped_ipv4(&gw6
, gw
);
337 } else if (scan_ipv6_route(argv
[1], &ip6
, &plen
)) {
339 if (!ovs_scan(argv
[3], "pkt_mark=%"SCNi32
, &mark
) &&
340 !ipv6_parse(argv
[3], &gw6
)) {
341 unixctl_command_reply_error(conn
, "Invalid pkt_mark or IPv6 gateway");
346 unixctl_command_reply_error(conn
, "Invalid parameters");
350 if (!ovs_scan(argv
[4], "pkt_mark=%"SCNi32
, &mark
)) {
351 unixctl_command_reply_error(conn
, "Invalid pkt_mark");
356 err
= ovs_router_insert__(mark
, plen
+ 32, &ip6
, plen
, argv
[2], &gw6
);
358 unixctl_command_reply_error(conn
, "Error while inserting route.");
360 unixctl_command_reply(conn
, "OK");
365 ovs_router_del(struct unixctl_conn
*conn
, int argc OVS_UNUSED
,
366 const char *argv
[], void *aux OVS_UNUSED
)
373 if (scan_ipv4_route(argv
[1], &ip
, &plen
)) {
374 in6_addr_set_mapped_ipv4(&ip6
, ip
);
376 } else if (!scan_ipv6_route(argv
[1], &ip6
, &plen
)) {
377 unixctl_command_reply_error(conn
, "Invalid parameters");
381 if (!ovs_scan(argv
[2], "pkt_mark=%"SCNi32
, &mark
)) {
382 unixctl_command_reply_error(conn
, "Invalid pkt_mark");
387 if (rt_entry_delete(mark
, plen
+ 32, &ip6
, plen
)) {
388 unixctl_command_reply(conn
, "OK");
389 seq_change(tnl_conf_seq
);
391 unixctl_command_reply_error(conn
, "Not found");
396 ovs_router_show(struct unixctl_conn
*conn
, int argc OVS_UNUSED
,
397 const char *argv
[] OVS_UNUSED
, void *aux OVS_UNUSED
)
399 struct ovs_router_entry
*rt
;
400 struct ds ds
= DS_EMPTY_INITIALIZER
;
402 ds_put_format(&ds
, "Route Table:\n");
403 CLS_FOR_EACH(rt
, cr
, &cls
) {
405 if (rt
->priority
== rt
->plen
) {
406 ds_put_format(&ds
, "Cached: ");
408 ds_put_format(&ds
, "User: ");
410 ipv6_format_mapped(&rt
->nw_addr
, &ds
);
412 if (IN6_IS_ADDR_V4MAPPED(&rt
->nw_addr
)) {
415 ds_put_format(&ds
, "/%"PRIu8
, plen
);
417 ds_put_format(&ds
, " MARK %"PRIu32
, rt
->mark
);
420 ds_put_format(&ds
, " dev %s", rt
->output_bridge
);
421 if (ipv6_addr_is_set(&rt
->gw
)) {
422 ds_put_format(&ds
, " GW ");
423 ipv6_format_mapped(&rt
->gw
, &ds
);
425 ds_put_format(&ds
, " SRC ");
426 ipv6_format_mapped(&rt
->src_addr
, &ds
);
427 ds_put_format(&ds
, "\n");
429 unixctl_command_reply(conn
, ds_cstr(&ds
));
434 ovs_router_lookup_cmd(struct unixctl_conn
*conn
, int argc
,
435 const char *argv
[], void *aux OVS_UNUSED
)
437 struct in6_addr gw
, src
;
438 char iface
[IFNAMSIZ
];
444 if (scan_ipv4_route(argv
[1], &ip
, &plen
) && plen
== 32) {
445 in6_addr_set_mapped_ipv4(&ip6
, ip
);
446 } else if (!(scan_ipv6_route(argv
[1], &ip6
, &plen
) && plen
== 128)) {
447 unixctl_command_reply_error(conn
, "Invalid parameters");
451 if (!ovs_scan(argv
[2], "pkt_mark=%"SCNi32
, &mark
)) {
452 unixctl_command_reply_error(conn
, "Invalid pkt_mark");
456 if (ovs_router_lookup(mark
, &ip6
, iface
, &src
, &gw
)) {
457 struct ds ds
= DS_EMPTY_INITIALIZER
;
459 ds_put_format(&ds
, "src ");
460 ipv6_format_mapped(&src
, &ds
);
461 ds_put_format(&ds
, "\ngateway ");
462 ipv6_format_mapped(&gw
, &ds
);
463 ds_put_format(&ds
, "\ndev %s\n", iface
);
464 unixctl_command_reply(conn
, ds_cstr(&ds
));
467 unixctl_command_reply_error(conn
, "Not found");
472 ovs_router_flush(void)
474 struct ovs_router_entry
*rt
;
476 ovs_mutex_lock(&mutex
);
477 classifier_defer(&cls
);
478 CLS_FOR_EACH(rt
, cr
, &cls
) {
479 if (rt
->priority
== rt
->plen
) {
480 __rt_entry_delete(&rt
->cr
);
483 classifier_publish(&cls
);
484 ovs_mutex_unlock(&mutex
);
485 seq_change(tnl_conf_seq
);
489 ovs_router_flush_handler(void *aux OVS_UNUSED
)
495 ovs_router_init(void)
497 static struct ovsthread_once once
= OVSTHREAD_ONCE_INITIALIZER
;
499 if (ovsthread_once_start(&once
)) {
500 fatal_signal_add_hook(ovs_router_flush_handler
, NULL
, NULL
, true);
501 classifier_init(&cls
, NULL
);
502 unixctl_command_register("ovs/route/add",
503 "ip_addr/prefix_len out_br_name [gw] "
505 2, 4, ovs_router_add
, NULL
);
506 unixctl_command_register("ovs/route/show", "", 0, 0,
507 ovs_router_show
, NULL
);
508 unixctl_command_register("ovs/route/del", "ip_addr/prefix_len "
509 "[pkt_mark=mark]", 1, 2, ovs_router_del
,
511 unixctl_command_register("ovs/route/lookup", "ip_addr "
512 "[pkt_mark=mark]", 1, 2,
513 ovs_router_lookup_cmd
, NULL
);
514 ovsthread_once_done(&once
);