]>
git.proxmox.com Git - ovs.git/blob - lib/ovs-router.c
2 * Copyright (c) 2014, 2015, 2016 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 "openvswitch/dynamic-string.h"
40 #include "ovs-thread.h"
41 #include "route-table.h"
42 #include "tnl-ports.h"
45 #include "unaligned.h"
49 static struct ovs_mutex mutex
= OVS_MUTEX_INITIALIZER
;
50 static struct classifier cls
;
52 struct ovs_router_entry
{
54 char output_bridge
[IFNAMSIZ
];
56 struct in6_addr nw_addr
;
57 struct in6_addr src_addr
;
62 static struct ovs_router_entry
*
63 ovs_router_entry_cast(const struct cls_rule
*cr
)
65 if (offsetof(struct ovs_router_entry
, cr
) == 0) {
66 return CONTAINER_OF(cr
, struct ovs_router_entry
, cr
);
68 return cr
? CONTAINER_OF(cr
, struct ovs_router_entry
, cr
) : NULL
;
73 ovs_router_lookup_fallback(const struct in6_addr
*ip6_dst
, char output_bridge
[],
74 struct in6_addr
*src6
, struct in6_addr
*gw6
)
78 if (!route_table_fallback_lookup(ip6_dst
, output_bridge
, gw6
)) {
81 if (netdev_get_in4_by_name(output_bridge
, (struct in_addr
*)&src
)) {
85 in6_addr_set_mapped_ipv4(src6
, src
);
91 ovs_router_lookup(const struct in6_addr
*ip6_dst
, char output_bridge
[],
92 struct in6_addr
*src
, struct in6_addr
*gw
)
94 const struct cls_rule
*cr
;
95 struct flow flow
= {.ipv6_dst
= *ip6_dst
};
97 cr
= classifier_lookup(&cls
, OVS_VERSION_MAX
, &flow
, NULL
);
99 struct ovs_router_entry
*p
= ovs_router_entry_cast(cr
);
101 ovs_strlcpy(output_bridge
, p
->output_bridge
, IFNAMSIZ
);
108 return ovs_router_lookup_fallback(ip6_dst
, output_bridge
, src
, gw
);
112 rt_entry_free(struct ovs_router_entry
*p
)
114 cls_rule_destroy(&p
->cr
);
118 static void rt_init_match(struct match
*match
, const struct in6_addr
*ip6_dst
,
122 struct in6_addr mask
;
124 mask
= ipv6_create_mask(plen
);
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
;
133 get_src_addr(const struct in6_addr
*ip6_dst
,
134 const char output_bridge
[], struct in6_addr
*psrc
)
136 struct in6_addr
*mask
, *addr6
;
137 int err
, n_in6
, i
, max_plen
= -1;
141 err
= netdev_open(output_bridge
, NULL
, &dev
);
146 err
= netdev_get_addr_list(dev
, &addr6
, &mask
, &n_in6
);
151 is_ipv4
= IN6_IS_ADDR_V4MAPPED(ip6_dst
);
153 for (i
= 0; i
< n_in6
; i
++) {
154 struct in6_addr a1
, a2
;
157 if (is_ipv4
&& !IN6_IS_ADDR_V4MAPPED(&addr6
[i
])) {
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);
165 if (!memcmp(&a1
, &a2
, sizeof (a1
)) && mask_bits
> max_plen
) {
167 max_plen
= mask_bits
;
170 if (max_plen
== -1) {
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
)
185 const struct cls_rule
*cr
;
186 struct ovs_router_entry
*p
;
190 rt_init_match(&match
, ip6_dst
, plen
);
192 p
= xzalloc(sizeof *p
);
193 ovs_strlcpy(p
->output_bridge
, output_bridge
, sizeof p
->output_bridge
);
194 if (ipv6_addr_is_set(gw
)) {
197 p
->nw_addr
= match
.flow
.ipv6_dst
;
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
);
208 /* Longest prefix matches first. */
209 cls_rule_init(&p
->cr
, &match
, priority
);
211 ovs_mutex_lock(&mutex
);
212 cr
= classifier_replace(&cls
, &p
->cr
, OVS_VERSION_MIN
, NULL
, 0);
213 ovs_mutex_unlock(&mutex
);
216 /* An old rule with the same match was displaced. */
217 ovsrcu_postpone(rt_entry_free
, ovs_router_entry_cast(cr
));
219 tnl_port_map_insert_ipdev(output_bridge
);
220 seq_change(tnl_conf_seq
);
225 ovs_router_insert(const struct in6_addr
*ip_dst
, uint8_t plen
,
226 const char output_bridge
[], const struct in6_addr
*gw
)
228 ovs_router_insert__(plen
, ip_dst
, plen
, output_bridge
, gw
);
233 __rt_entry_delete(const struct cls_rule
*cr
)
235 struct ovs_router_entry
*p
= ovs_router_entry_cast(cr
);
237 tnl_port_map_delete_ipdev(p
->output_bridge
);
239 cr
= classifier_remove(&cls
, cr
);
241 ovsrcu_postpone(rt_entry_free
, ovs_router_entry_cast(cr
));
248 rt_entry_delete(uint8_t priority
, const struct in6_addr
*ip6_dst
, uint8_t plen
)
250 const struct cls_rule
*cr
;
251 struct cls_rule rule
;
255 rt_init_match(&match
, ip6_dst
, plen
);
257 cls_rule_init(&rule
, &match
, priority
);
259 /* Find the exact rule. */
260 cr
= classifier_find_rule_exactly(&cls
, &rule
, OVS_VERSION_MAX
);
262 ovs_mutex_lock(&mutex
);
263 res
= __rt_entry_delete(cr
);
264 ovs_mutex_unlock(&mutex
);
270 scan_ipv6_route(const char *s
, struct in6_addr
*addr
, unsigned int *plen
)
272 char *error
= ipv6_parse_cidr(s
, addr
, plen
);
281 scan_ipv4_route(const char *s
, ovs_be32
*addr
, unsigned int *plen
)
283 char *error
= ip_parse_cidr(s
, addr
, plen
);
292 ovs_router_add(struct unixctl_conn
*conn
, int argc
,
293 const char *argv
[], void *aux OVS_UNUSED
)
301 if (scan_ipv4_route(argv
[1], &ip
, &plen
)) {
303 if (argc
> 3 && !ip_parse(argv
[3], &gw
)) {
304 unixctl_command_reply_error(conn
, "Invalid gateway");
307 in6_addr_set_mapped_ipv4(&ip6
, ip
);
308 in6_addr_set_mapped_ipv4(&gw6
, gw
);
310 } else if (scan_ipv6_route(argv
[1], &ip6
, &plen
)) {
312 if (argc
> 3 && !ipv6_parse(argv
[3], &gw6
)) {
313 unixctl_command_reply_error(conn
, "Invalid IPv6 gateway");
317 unixctl_command_reply_error(conn
, "Invalid parameters");
320 err
= ovs_router_insert__(plen
+ 32, &ip6
, plen
, argv
[2], &gw6
);
322 unixctl_command_reply_error(conn
, "Error while inserting route.");
324 unixctl_command_reply(conn
, "OK");
329 ovs_router_del(struct unixctl_conn
*conn
, int argc OVS_UNUSED
,
330 const char *argv
[], void *aux OVS_UNUSED
)
336 if (scan_ipv4_route(argv
[1], &ip
, &plen
)) {
337 in6_addr_set_mapped_ipv4(&ip6
, ip
);
339 } else if (!scan_ipv6_route(argv
[1], &ip6
, &plen
)) {
340 unixctl_command_reply_error(conn
, "Invalid parameters");
343 if (rt_entry_delete(plen
+ 32, &ip6
, plen
)) {
344 unixctl_command_reply(conn
, "OK");
345 seq_change(tnl_conf_seq
);
347 unixctl_command_reply_error(conn
, "Not found");
352 ovs_router_show(struct unixctl_conn
*conn
, int argc OVS_UNUSED
,
353 const char *argv
[] OVS_UNUSED
, void *aux OVS_UNUSED
)
355 struct ovs_router_entry
*rt
;
356 struct ds ds
= DS_EMPTY_INITIALIZER
;
358 ds_put_format(&ds
, "Route Table:\n");
359 CLS_FOR_EACH(rt
, cr
, &cls
) {
361 if (rt
->priority
== rt
->plen
) {
362 ds_put_format(&ds
, "Cached: ");
364 ds_put_format(&ds
, "User: ");
366 ipv6_format_mapped(&rt
->nw_addr
, &ds
);
368 if (IN6_IS_ADDR_V4MAPPED(&rt
->nw_addr
)) {
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
);
376 ds_put_format(&ds
, " SRC ");
377 ipv6_format_mapped(&rt
->src_addr
, &ds
);
378 ds_put_format(&ds
, "\n");
380 unixctl_command_reply(conn
, ds_cstr(&ds
));
385 ovs_router_lookup_cmd(struct unixctl_conn
*conn
, int argc OVS_UNUSED
,
386 const char *argv
[], void *aux OVS_UNUSED
)
391 char iface
[IFNAMSIZ
];
392 struct in6_addr gw
, src
;
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");
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
));
411 unixctl_command_reply_error(conn
, "Not found");
416 ovs_router_flush(void)
418 struct ovs_router_entry
*rt
;
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
);
427 classifier_publish(&cls
);
428 ovs_mutex_unlock(&mutex
);
429 seq_change(tnl_conf_seq
);
432 /* May not be called more than once. */
434 ovs_router_init(void)
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
,
442 unixctl_command_register("ovs/route/lookup", "ip_addr", 1, 1,
443 ovs_router_lookup_cmd
, NULL
);