]>
Commit | Line | Data |
---|---|---|
d9b4ebc5 | 1 | /* |
fd13c6b5 | 2 | * Copyright (c) 2014, 2015, 2016, 2017 Nicira, Inc. |
d9b4ebc5 PS |
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> | |
1f9575ca TLSC |
18 | |
19 | #include "ovs-router.h" | |
20 | ||
b2befd5b BP |
21 | #include <sys/types.h> |
22 | #include <netinet/in.h> | |
d9b4ebc5 PS |
23 | #include <arpa/inet.h> |
24 | #include <errno.h> | |
25 | #include <inttypes.h> | |
26 | #include <sys/socket.h> | |
27 | #include <net/if.h> | |
d9b4ebc5 PS |
28 | #include <stdarg.h> |
29 | #include <stdlib.h> | |
30 | #include <string.h> | |
31 | #include <unistd.h> | |
32 | ||
33 | #include "classifier.h" | |
34 | #include "command-line.h" | |
35 | #include "compiler.h" | |
36 | #include "dpif.h" | |
74fd3cdb | 37 | #include "fatal-signal.h" |
3e8a2ad1 | 38 | #include "openvswitch/dynamic-string.h" |
d9b4ebc5 PS |
39 | #include "netdev.h" |
40 | #include "packets.h" | |
a36de779 | 41 | #include "seq.h" |
fccd7c09 | 42 | #include "ovs-thread.h" |
88ffdc93 | 43 | #include "route-table.h" |
7f9b8504 | 44 | #include "tnl-ports.h" |
d9b4ebc5 PS |
45 | #include "unixctl.h" |
46 | #include "util.h" | |
a8704b50 | 47 | #include "unaligned.h" |
ed52ca57 PS |
48 | #include "openvswitch/vlog.h" |
49 | ||
50 | VLOG_DEFINE_THIS_MODULE(ovs_router); | |
51 | ||
52 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); | |
d9b4ebc5 | 53 | |
fccd7c09 | 54 | static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER; |
d9b4ebc5 PS |
55 | static struct classifier cls; |
56 | ||
898d7b05 BP |
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; | |
60 | ||
d9b4ebc5 PS |
61 | struct ovs_router_entry { |
62 | struct cls_rule cr; | |
63 | char output_bridge[IFNAMSIZ]; | |
0b8da9ae TLSC |
64 | struct in6_addr gw; |
65 | struct in6_addr nw_addr; | |
a8704b50 | 66 | struct in6_addr src_addr; |
d9b4ebc5 PS |
67 | uint8_t plen; |
68 | uint8_t priority; | |
8e4e4588 | 69 | bool local; |
ed52ca57 | 70 | uint32_t mark; |
d9b4ebc5 PS |
71 | }; |
72 | ||
73 | static struct ovs_router_entry * | |
74 | ovs_router_entry_cast(const struct cls_rule *cr) | |
75 | { | |
53d416af | 76 | return cr ? CONTAINER_OF(cr, struct ovs_router_entry, cr) : NULL; |
d9b4ebc5 PS |
77 | } |
78 | ||
898d7b05 BP |
79 | /* Disables obtaining routes from the system routing table, for testing |
80 | * purposes. */ | |
81 | void | |
82 | ovs_router_disable_system_routing_table(void) | |
83 | { | |
84 | use_system_routing_table = false; | |
85 | } | |
86 | ||
ec6c5379 PS |
87 | static bool |
88 | ovs_router_lookup_fallback(const struct in6_addr *ip6_dst, char output_bridge[], | |
89 | struct in6_addr *src6, struct in6_addr *gw6) | |
90 | { | |
91 | ovs_be32 src; | |
92 | ||
898d7b05 BP |
93 | if (!use_system_routing_table |
94 | || !route_table_fallback_lookup(ip6_dst, output_bridge, gw6)) { | |
ec6c5379 PS |
95 | return false; |
96 | } | |
97 | if (netdev_get_in4_by_name(output_bridge, (struct in_addr *)&src)) { | |
98 | return false; | |
99 | } | |
100 | if (src6) { | |
101 | in6_addr_set_mapped_ipv4(src6, src); | |
102 | } | |
103 | return true; | |
104 | } | |
105 | ||
d9b4ebc5 | 106 | bool |
ed52ca57 PS |
107 | ovs_router_lookup(uint32_t mark, const struct in6_addr *ip6_dst, |
108 | char output_bridge[], | |
a8704b50 | 109 | struct in6_addr *src, struct in6_addr *gw) |
d9b4ebc5 PS |
110 | { |
111 | const struct cls_rule *cr; | |
ed52ca57 | 112 | struct flow flow = {.ipv6_dst = *ip6_dst, .pkt_mark = mark}; |
d9b4ebc5 | 113 | |
8e4e4588 | 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}; | |
117 | ||
118 | cr_src = classifier_lookup(&cls, OVS_VERSION_MAX, &flow_src, NULL); | |
119 | if (cr_src) { | |
120 | struct ovs_router_entry *p_src = ovs_router_entry_cast(cr_src); | |
121 | if (!p_src->local) { | |
122 | return false; | |
123 | } | |
124 | } else { | |
125 | return false; | |
126 | } | |
127 | } | |
128 | ||
44e0c35d | 129 | cr = classifier_lookup(&cls, OVS_VERSION_MAX, &flow, NULL); |
d9b4ebc5 PS |
130 | if (cr) { |
131 | struct ovs_router_entry *p = ovs_router_entry_cast(cr); | |
132 | ||
8742957c | 133 | ovs_strlcpy(output_bridge, p->output_bridge, IFNAMSIZ); |
d9b4ebc5 | 134 | *gw = p->gw; |
8e4e4588 | 135 | if (src && !ipv6_addr_is_set(src)) { |
a8704b50 PS |
136 | *src = p->src_addr; |
137 | } | |
d9b4ebc5 PS |
138 | return true; |
139 | } | |
ec6c5379 | 140 | return ovs_router_lookup_fallback(ip6_dst, output_bridge, src, gw); |
d9b4ebc5 PS |
141 | } |
142 | ||
143 | static void | |
144 | rt_entry_free(struct ovs_router_entry *p) | |
145 | { | |
146 | cls_rule_destroy(&p->cr); | |
147 | free(p); | |
148 | } | |
149 | ||
ed52ca57 PS |
150 | static void rt_init_match(struct match *match, uint32_t mark, |
151 | const struct in6_addr *ip6_dst, | |
0b8da9ae | 152 | uint8_t plen) |
d9b4ebc5 | 153 | { |
0b8da9ae TLSC |
154 | struct in6_addr dst; |
155 | struct in6_addr mask; | |
d9b4ebc5 | 156 | |
0b8da9ae | 157 | mask = ipv6_create_mask(plen); |
d9b4ebc5 | 158 | |
0b8da9ae | 159 | dst = ipv6_addr_bitand(ip6_dst, &mask); |
d9b4ebc5 | 160 | memset(match, 0, sizeof *match); |
0b8da9ae TLSC |
161 | match->flow.ipv6_dst = dst; |
162 | match->wc.masks.ipv6_dst = mask; | |
ed52ca57 PS |
163 | match->wc.masks.pkt_mark = UINT32_MAX; |
164 | match->flow.pkt_mark = mark; | |
d9b4ebc5 PS |
165 | } |
166 | ||
a8704b50 PS |
167 | static int |
168 | get_src_addr(const struct in6_addr *ip6_dst, | |
169 | const char output_bridge[], struct in6_addr *psrc) | |
170 | { | |
171 | struct in6_addr *mask, *addr6; | |
172 | int err, n_in6, i, max_plen = -1; | |
173 | struct netdev *dev; | |
20dcdc7f | 174 | bool is_ipv4; |
a8704b50 PS |
175 | |
176 | err = netdev_open(output_bridge, NULL, &dev); | |
177 | if (err) { | |
178 | return err; | |
179 | } | |
180 | ||
181 | err = netdev_get_addr_list(dev, &addr6, &mask, &n_in6); | |
182 | if (err) { | |
183 | goto out; | |
184 | } | |
185 | ||
20dcdc7f TLSC |
186 | is_ipv4 = IN6_IS_ADDR_V4MAPPED(ip6_dst); |
187 | ||
a8704b50 PS |
188 | for (i = 0; i < n_in6; i++) { |
189 | struct in6_addr a1, a2; | |
190 | int mask_bits; | |
191 | ||
20dcdc7f TLSC |
192 | if (is_ipv4 && !IN6_IS_ADDR_V4MAPPED(&addr6[i])) { |
193 | continue; | |
194 | } | |
195 | ||
a8704b50 PS |
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); | |
199 | ||
200 | if (!memcmp(&a1, &a2, sizeof (a1)) && mask_bits > max_plen) { | |
201 | *psrc = addr6[i]; | |
202 | max_plen = mask_bits; | |
203 | } | |
204 | } | |
205 | if (max_plen == -1) { | |
206 | err = ENOENT; | |
207 | } | |
208 | out: | |
209 | free(addr6); | |
210 | free(mask); | |
211 | netdev_close(dev); | |
212 | return err; | |
213 | } | |
214 | ||
215 | static int | |
8e4e4588 | 216 | ovs_router_insert__(uint32_t mark, uint8_t priority, bool local, |
ed52ca57 | 217 | const struct in6_addr *ip6_dst, |
0b8da9ae TLSC |
218 | uint8_t plen, const char output_bridge[], |
219 | const struct in6_addr *gw) | |
d9b4ebc5 PS |
220 | { |
221 | const struct cls_rule *cr; | |
222 | struct ovs_router_entry *p; | |
223 | struct match match; | |
a8704b50 | 224 | int err; |
d9b4ebc5 | 225 | |
ed52ca57 | 226 | rt_init_match(&match, mark, ip6_dst, plen); |
d9b4ebc5 PS |
227 | |
228 | p = xzalloc(sizeof *p); | |
8742957c | 229 | ovs_strlcpy(p->output_bridge, output_bridge, sizeof p->output_bridge); |
0b8da9ae TLSC |
230 | if (ipv6_addr_is_set(gw)) { |
231 | p->gw = *gw; | |
232 | } | |
ed52ca57 | 233 | p->mark = mark; |
0b8da9ae | 234 | p->nw_addr = match.flow.ipv6_dst; |
d9b4ebc5 | 235 | p->plen = plen; |
8e4e4588 | 236 | p->local = local; |
d9b4ebc5 | 237 | p->priority = priority; |
a8704b50 | 238 | err = get_src_addr(ip6_dst, output_bridge, &p->src_addr); |
4e08f54b AW |
239 | if (err && ipv6_addr_is_set(gw)) { |
240 | err = get_src_addr(gw, output_bridge, &p->src_addr); | |
241 | } | |
a8704b50 | 242 | if (err) { |
ed52ca57 PS |
243 | struct ds ds = DS_EMPTY_INITIALIZER; |
244 | ||
245 | ipv6_format_mapped(ip6_dst, &ds); | |
246 | VLOG_DBG_RL(&rl, "src addr not available for route %s", ds_cstr(&ds)); | |
7209202e | 247 | free(p); |
ed52ca57 | 248 | ds_destroy(&ds); |
a8704b50 PS |
249 | return err; |
250 | } | |
2b7b1427 | 251 | /* Longest prefix matches first. */ |
bd53aa17 | 252 | cls_rule_init(&p->cr, &match, priority); |
d9b4ebc5 | 253 | |
fccd7c09 | 254 | ovs_mutex_lock(&mutex); |
44e0c35d | 255 | cr = classifier_replace(&cls, &p->cr, OVS_VERSION_MIN, NULL, 0); |
fccd7c09 JR |
256 | ovs_mutex_unlock(&mutex); |
257 | ||
d9b4ebc5 PS |
258 | if (cr) { |
259 | /* An old rule with the same match was displaced. */ | |
260 | ovsrcu_postpone(rt_entry_free, ovs_router_entry_cast(cr)); | |
261 | } | |
7f9b8504 | 262 | tnl_port_map_insert_ipdev(output_bridge); |
a36de779 | 263 | seq_change(tnl_conf_seq); |
a8704b50 | 264 | return 0; |
d9b4ebc5 PS |
265 | } |
266 | ||
267 | void | |
ed52ca57 | 268 | ovs_router_insert(uint32_t mark, const struct in6_addr *ip_dst, uint8_t plen, |
8e4e4588 | 269 | bool local, const char output_bridge[], |
270 | const struct in6_addr *gw) | |
d9b4ebc5 | 271 | { |
898d7b05 | 272 | if (use_system_routing_table) { |
8e4e4588 | 273 | uint8_t priority = local ? plen + 64 : plen; |
274 | ovs_router_insert__(mark, priority, local, ip_dst, plen, output_bridge, gw); | |
898d7b05 | 275 | } |
d9b4ebc5 PS |
276 | } |
277 | ||
46ab60bf BP |
278 | static void |
279 | rt_entry_delete__(const struct cls_rule *cr) | |
7f9b8504 PS |
280 | { |
281 | struct ovs_router_entry *p = ovs_router_entry_cast(cr); | |
282 | ||
283 | tnl_port_map_delete_ipdev(p->output_bridge); | |
46ab60bf BP |
284 | classifier_remove_assert(&cls, cr); |
285 | ovsrcu_postpone(rt_entry_free, ovs_router_entry_cast(cr)); | |
7f9b8504 PS |
286 | } |
287 | ||
d9b4ebc5 | 288 | static bool |
ed52ca57 PS |
289 | rt_entry_delete(uint32_t mark, uint8_t priority, |
290 | const struct in6_addr *ip6_dst, uint8_t plen) | |
d9b4ebc5 | 291 | { |
dfea28b3 | 292 | const struct cls_rule *cr; |
d9b4ebc5 PS |
293 | struct cls_rule rule; |
294 | struct match match; | |
7f9b8504 | 295 | bool res = false; |
d9b4ebc5 | 296 | |
ed52ca57 | 297 | rt_init_match(&match, mark, ip6_dst, plen); |
d9b4ebc5 | 298 | |
bd53aa17 | 299 | cls_rule_init(&rule, &match, priority); |
d9b4ebc5 PS |
300 | |
301 | /* Find the exact rule. */ | |
44e0c35d | 302 | cr = classifier_find_rule_exactly(&cls, &rule, OVS_VERSION_MAX); |
d9b4ebc5 | 303 | if (cr) { |
fccd7c09 | 304 | ovs_mutex_lock(&mutex); |
46ab60bf | 305 | rt_entry_delete__(cr); |
fccd7c09 | 306 | ovs_mutex_unlock(&mutex); |
46ab60bf BP |
307 | |
308 | res = true; | |
d9b4ebc5 | 309 | } |
d3328b16 WT |
310 | |
311 | cls_rule_destroy(&rule); | |
7f9b8504 | 312 | return res; |
d9b4ebc5 PS |
313 | } |
314 | ||
0b8da9ae TLSC |
315 | static bool |
316 | scan_ipv6_route(const char *s, struct in6_addr *addr, unsigned int *plen) | |
317 | { | |
e7695092 | 318 | char *error = ipv6_parse_cidr(s, addr, plen); |
184dfff0 JP |
319 | if (error) { |
320 | free(error); | |
321 | return false; | |
0b8da9ae | 322 | } |
184dfff0 | 323 | return true; |
0b8da9ae TLSC |
324 | } |
325 | ||
d9b4ebc5 PS |
326 | static bool |
327 | scan_ipv4_route(const char *s, ovs_be32 *addr, unsigned int *plen) | |
328 | { | |
e7695092 BP |
329 | char *error = ip_parse_cidr(s, addr, plen); |
330 | if (error) { | |
331 | free(error); | |
d9b4ebc5 PS |
332 | return false; |
333 | } | |
e7695092 | 334 | return true; |
d9b4ebc5 PS |
335 | } |
336 | ||
337 | static void | |
338 | ovs_router_add(struct unixctl_conn *conn, int argc, | |
339 | const char *argv[], void *aux OVS_UNUSED) | |
340 | { | |
ed52ca57 | 341 | struct in6_addr gw6 = in6addr_any; |
0b8da9ae | 342 | struct in6_addr ip6; |
ed52ca57 PS |
343 | uint32_t mark = 0; |
344 | unsigned int plen; | |
345 | ovs_be32 ip; | |
a8704b50 | 346 | int err; |
d9b4ebc5 PS |
347 | |
348 | if (scan_ipv4_route(argv[1], &ip, &plen)) { | |
e7695092 | 349 | ovs_be32 gw = 0; |
ed52ca57 PS |
350 | |
351 | if (argc > 3) { | |
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"); | |
355 | return; | |
356 | } | |
d9b4ebc5 | 357 | } |
0b8da9ae | 358 | in6_addr_set_mapped_ipv4(&ip6, ip); |
ed52ca57 PS |
359 | if (gw) { |
360 | in6_addr_set_mapped_ipv4(&gw6, gw); | |
361 | } | |
0b8da9ae TLSC |
362 | plen += 96; |
363 | } else if (scan_ipv6_route(argv[1], &ip6, &plen)) { | |
ed52ca57 PS |
364 | if (argc > 3) { |
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"); | |
368 | return; | |
369 | } | |
0b8da9ae | 370 | } |
d9b4ebc5 | 371 | } else { |
f2e06274 BP |
372 | unixctl_command_reply_error(conn, "Invalid parameters"); |
373 | return; | |
d9b4ebc5 | 374 | } |
ed52ca57 PS |
375 | if (argc > 4) { |
376 | if (!ovs_scan(argv[4], "pkt_mark=%"SCNi32, &mark)) { | |
377 | unixctl_command_reply_error(conn, "Invalid pkt_mark"); | |
378 | return; | |
379 | } | |
380 | } | |
381 | ||
8e4e4588 | 382 | err = ovs_router_insert__(mark, plen + 32, false, &ip6, plen, argv[2], &gw6); |
a8704b50 | 383 | if (err) { |
9104895e | 384 | unixctl_command_reply_error(conn, "Error while inserting route."); |
a8704b50 PS |
385 | } else { |
386 | unixctl_command_reply(conn, "OK"); | |
387 | } | |
d9b4ebc5 PS |
388 | } |
389 | ||
390 | static void | |
391 | ovs_router_del(struct unixctl_conn *conn, int argc OVS_UNUSED, | |
392 | const char *argv[], void *aux OVS_UNUSED) | |
393 | { | |
0b8da9ae | 394 | struct in6_addr ip6; |
ed52ca57 PS |
395 | uint32_t mark = 0; |
396 | unsigned int plen; | |
397 | ovs_be32 ip; | |
d9b4ebc5 PS |
398 | |
399 | if (scan_ipv4_route(argv[1], &ip, &plen)) { | |
0b8da9ae TLSC |
400 | in6_addr_set_mapped_ipv4(&ip6, ip); |
401 | plen += 96; | |
402 | } else if (!scan_ipv6_route(argv[1], &ip6, &plen)) { | |
f2e06274 BP |
403 | unixctl_command_reply_error(conn, "Invalid parameters"); |
404 | return; | |
d9b4ebc5 | 405 | } |
ed52ca57 PS |
406 | if (argc > 2) { |
407 | if (!ovs_scan(argv[2], "pkt_mark=%"SCNi32, &mark)) { | |
408 | unixctl_command_reply_error(conn, "Invalid pkt_mark"); | |
409 | return; | |
410 | } | |
411 | } | |
412 | ||
413 | if (rt_entry_delete(mark, plen + 32, &ip6, plen)) { | |
0b8da9ae TLSC |
414 | unixctl_command_reply(conn, "OK"); |
415 | seq_change(tnl_conf_seq); | |
416 | } else { | |
f2e06274 | 417 | unixctl_command_reply_error(conn, "Not found"); |
0b8da9ae | 418 | } |
d9b4ebc5 PS |
419 | } |
420 | ||
421 | static void | |
422 | ovs_router_show(struct unixctl_conn *conn, int argc OVS_UNUSED, | |
423 | const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) | |
424 | { | |
425 | struct ovs_router_entry *rt; | |
426 | struct ds ds = DS_EMPTY_INITIALIZER; | |
427 | ||
428 | ds_put_format(&ds, "Route Table:\n"); | |
429 | CLS_FOR_EACH(rt, cr, &cls) { | |
0b8da9ae | 430 | uint8_t plen; |
8e4e4588 | 431 | if (rt->priority == rt->plen || rt->local) { |
d9b4ebc5 PS |
432 | ds_put_format(&ds, "Cached: "); |
433 | } else { | |
434 | ds_put_format(&ds, "User: "); | |
435 | } | |
ac6d120f | 436 | ipv6_format_mapped(&rt->nw_addr, &ds); |
0b8da9ae TLSC |
437 | plen = rt->plen; |
438 | if (IN6_IS_ADDR_V4MAPPED(&rt->nw_addr)) { | |
439 | plen -= 96; | |
440 | } | |
fd13c6b5 | 441 | ds_put_format(&ds, "/%"PRIu8, plen); |
ed52ca57 PS |
442 | if (rt->mark) { |
443 | ds_put_format(&ds, " MARK %"PRIu32, rt->mark); | |
444 | } | |
445 | ||
446 | ds_put_format(&ds, " dev %s", rt->output_bridge); | |
0b8da9ae TLSC |
447 | if (ipv6_addr_is_set(&rt->gw)) { |
448 | ds_put_format(&ds, " GW "); | |
ac6d120f | 449 | ipv6_format_mapped(&rt->gw, &ds); |
d9b4ebc5 | 450 | } |
a8704b50 PS |
451 | ds_put_format(&ds, " SRC "); |
452 | ipv6_format_mapped(&rt->src_addr, &ds); | |
8e4e4588 | 453 | if (rt->local) { |
454 | ds_put_format(&ds, " local"); | |
455 | } | |
d9b4ebc5 PS |
456 | ds_put_format(&ds, "\n"); |
457 | } | |
458 | unixctl_command_reply(conn, ds_cstr(&ds)); | |
459 | ds_destroy(&ds); | |
460 | } | |
461 | ||
bc786797 | 462 | static void |
ed52ca57 | 463 | ovs_router_lookup_cmd(struct unixctl_conn *conn, int argc, |
bc786797 YT |
464 | const char *argv[], void *aux OVS_UNUSED) |
465 | { | |
8e4e4588 | 466 | struct in6_addr gw, src = in6addr_any; |
ed52ca57 | 467 | char iface[IFNAMSIZ]; |
0b8da9ae | 468 | struct in6_addr ip6; |
bc786797 | 469 | unsigned int plen; |
ed52ca57 PS |
470 | uint32_t mark = 0; |
471 | ovs_be32 ip; | |
bc786797 YT |
472 | |
473 | if (scan_ipv4_route(argv[1], &ip, &plen) && plen == 32) { | |
0b8da9ae TLSC |
474 | in6_addr_set_mapped_ipv4(&ip6, ip); |
475 | } else if (!(scan_ipv6_route(argv[1], &ip6, &plen) && plen == 128)) { | |
f2e06274 BP |
476 | unixctl_command_reply_error(conn, "Invalid parameters"); |
477 | return; | |
0b8da9ae | 478 | } |
ed52ca57 PS |
479 | if (argc > 2) { |
480 | if (!ovs_scan(argv[2], "pkt_mark=%"SCNi32, &mark)) { | |
481 | unixctl_command_reply_error(conn, "Invalid pkt_mark"); | |
482 | return; | |
483 | } | |
484 | } | |
485 | if (ovs_router_lookup(mark, &ip6, iface, &src, &gw)) { | |
0b8da9ae | 486 | struct ds ds = DS_EMPTY_INITIALIZER; |
ed52ca57 | 487 | |
a8704b50 PS |
488 | ds_put_format(&ds, "src "); |
489 | ipv6_format_mapped(&src, &ds); | |
2ddf1bca | 490 | ds_put_format(&ds, "\ngateway "); |
a8704b50 | 491 | ipv6_format_mapped(&gw, &ds); |
0b8da9ae TLSC |
492 | ds_put_format(&ds, "\ndev %s\n", iface); |
493 | unixctl_command_reply(conn, ds_cstr(&ds)); | |
494 | ds_destroy(&ds); | |
bc786797 | 495 | } else { |
f2e06274 | 496 | unixctl_command_reply_error(conn, "Not found"); |
bc786797 YT |
497 | } |
498 | } | |
499 | ||
d9b4ebc5 PS |
500 | void |
501 | ovs_router_flush(void) | |
502 | { | |
503 | struct ovs_router_entry *rt; | |
504 | ||
802f84ff JR |
505 | ovs_mutex_lock(&mutex); |
506 | classifier_defer(&cls); | |
de4ad4a2 | 507 | CLS_FOR_EACH(rt, cr, &cls) { |
d9b4ebc5 | 508 | if (rt->priority == rt->plen) { |
46ab60bf | 509 | rt_entry_delete__(&rt->cr); |
d9b4ebc5 PS |
510 | } |
511 | } | |
802f84ff JR |
512 | classifier_publish(&cls); |
513 | ovs_mutex_unlock(&mutex); | |
a36de779 | 514 | seq_change(tnl_conf_seq); |
d9b4ebc5 PS |
515 | } |
516 | ||
74fd3cdb FL |
517 | static void |
518 | ovs_router_flush_handler(void *aux OVS_UNUSED) | |
519 | { | |
520 | ovs_router_flush(); | |
521 | } | |
522 | ||
d9b4ebc5 | 523 | void |
1bc50ef3 | 524 | ovs_router_init(void) |
d9b4ebc5 | 525 | { |
74fd3cdb FL |
526 | static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; |
527 | ||
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] " | |
533 | "[pkt_mark=mark]", | |
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, | |
539 | NULL); | |
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); | |
544 | } | |
d9b4ebc5 | 545 | } |