Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2014, 2015 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 "tnl-ports.h"
20 : :
21 : : #include <stddef.h>
22 : : #include <stdint.h>
23 : : #include <string.h>
24 : :
25 : : #include "classifier.h"
26 : : #include "openvswitch/dynamic-string.h"
27 : : #include "hash.h"
28 : : #include "openvswitch/list.h"
29 : : #include "netdev.h"
30 : : #include "openvswitch/ofpbuf.h"
31 : : #include "ovs-thread.h"
32 : : #include "odp-util.h"
33 : : #include "ovs-thread.h"
34 : : #include "unixctl.h"
35 : : #include "util.h"
36 : :
37 : : static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER;
38 : : static struct classifier cls; /* Tunnel ports. */
39 : :
40 : : struct ip_device {
41 : : struct netdev *dev;
42 : : struct eth_addr mac;
43 : : struct in6_addr *addr;
44 : : int n_addr;
45 : : uint64_t change_seq;
46 : : struct ovs_list node;
47 : : char dev_name[IFNAMSIZ];
48 : : };
49 : :
50 : : static struct ovs_list addr_list;
51 : :
52 : : struct tnl_port {
53 : : odp_port_t port;
54 : : ovs_be16 tp_port;
55 : : uint8_t nw_proto;
56 : : char dev_name[IFNAMSIZ];
57 : : struct ovs_list node;
58 : : };
59 : :
60 : : static struct ovs_list port_list;
61 : :
62 : : struct tnl_port_in {
63 : : struct cls_rule cr;
64 : : odp_port_t portno;
65 : : struct ovs_refcount ref_cnt;
66 : : char dev_name[IFNAMSIZ];
67 : : };
68 : :
69 : : static struct tnl_port_in *
70 : 2191 : tnl_port_cast(const struct cls_rule *cr)
71 : : {
72 : : BUILD_ASSERT_DECL(offsetof(struct tnl_port_in, cr) == 0);
73 : :
74 : 2191 : return CONTAINER_OF(cr, struct tnl_port_in, cr);
75 : : }
76 : :
77 : : static void
78 : 535 : tnl_port_free(struct tnl_port_in *p)
79 : : {
80 : 535 : cls_rule_destroy(&p->cr);
81 : 535 : free(p);
82 : 535 : }
83 : :
84 : : static void
85 : 1096 : tnl_port_init_flow(struct flow *flow, struct eth_addr mac,
86 : : struct in6_addr *addr, uint8_t nw_proto, ovs_be16 tp_port)
87 : : {
88 : 1096 : memset(flow, 0, sizeof *flow);
89 : :
90 : 1096 : flow->dl_dst = mac;
91 [ + + ][ + - ]: 1096 : if (IN6_IS_ADDR_V4MAPPED(addr)) {
[ + - ][ + + ]
92 : 754 : flow->dl_type = htons(ETH_TYPE_IP);
93 : 754 : flow->nw_dst = in6_addr_get_mapped_ipv4(addr);
94 : : } else {
95 : 342 : flow->dl_type = htons(ETH_TYPE_IPV6);
96 : 342 : flow->ipv6_dst = *addr;
97 : : }
98 : :
99 : 1096 : flow->nw_proto = nw_proto;
100 : 1096 : flow->tp_dst = tp_port;
101 : 1096 : }
102 : :
103 : : static void
104 : 550 : map_insert(odp_port_t port, struct eth_addr mac, struct in6_addr *addr,
105 : : uint8_t nw_proto, ovs_be16 tp_port, const char dev_name[])
106 : : {
107 : : const struct cls_rule *cr;
108 : : struct tnl_port_in *p;
109 : : struct match match;
110 : :
111 : 550 : memset(&match, 0, sizeof match);
112 : 550 : tnl_port_init_flow(&match.flow, mac, addr, nw_proto, tp_port);
113 : :
114 : : do {
115 : 550 : cr = classifier_lookup(&cls, OVS_VERSION_MAX, &match.flow, NULL);
116 : 550 : p = tnl_port_cast(cr);
117 : : /* Try again if the rule was released before we get the reference. */
118 [ - + ][ # # ]: 550 : } while (p && !ovs_refcount_try_ref_rcu(&p->ref_cnt));
119 : :
120 [ + - ]: 550 : if (!p) {
121 : 550 : p = xzalloc(sizeof *p);
122 : 550 : p->portno = port;
123 : :
124 : 550 : match.wc.masks.dl_type = OVS_BE16_MAX;
125 : 550 : match.wc.masks.nw_proto = 0xff;
126 : : /* XXX: No fragments support. */
127 : 550 : match.wc.masks.nw_frag = FLOW_NW_FRAG_MASK;
128 : :
129 : : /* 'tp_port' is zero for GRE tunnels. In this case it
130 : : * doesn't make sense to match on UDP port numbers. */
131 [ + + ]: 550 : if (tp_port) {
132 : 322 : match.wc.masks.tp_dst = OVS_BE16_MAX;
133 : : }
134 [ + + ][ + - ]: 550 : if (IN6_IS_ADDR_V4MAPPED(addr)) {
[ + - ][ + + ]
135 : 378 : match.wc.masks.nw_dst = OVS_BE32_MAX;
136 : : } else {
137 : 172 : match.wc.masks.ipv6_dst = in6addr_exact;
138 : : }
139 : 550 : match.wc.masks.vlan_tci = OVS_BE16_MAX;
140 : 550 : memset(&match.wc.masks.dl_dst, 0xff, sizeof (struct eth_addr));
141 : :
142 : 550 : cls_rule_init(&p->cr, &match, 0); /* Priority == 0. */
143 : 550 : ovs_refcount_init(&p->ref_cnt);
144 : 550 : ovs_strlcpy(p->dev_name, dev_name, sizeof p->dev_name);
145 : :
146 : 550 : classifier_insert(&cls, &p->cr, OVS_VERSION_MIN, NULL, 0);
147 : : }
148 : 550 : }
149 : :
150 : : static void
151 : 397 : map_insert_ipdev__(struct ip_device *ip_dev, char dev_name[],
152 : : odp_port_t port, uint8_t nw_proto, ovs_be16 tp_port)
153 : : {
154 [ + - ]: 397 : if (ip_dev->n_addr) {
155 : : int i;
156 : :
157 [ + + ]: 947 : for (i = 0; i < ip_dev->n_addr; i++) {
158 : 550 : map_insert(port, ip_dev->mac, &ip_dev->addr[i],
159 : : nw_proto, tp_port, dev_name);
160 : : }
161 : : }
162 : 397 : }
163 : :
164 : : static uint8_t
165 : 714 : tnl_type_to_nw_proto(const char type[])
166 : : {
167 [ + + ]: 714 : if (!strcmp(type, "geneve")) {
168 : 150 : return IPPROTO_UDP;
169 : : }
170 [ + + ]: 564 : if (!strcmp(type, "stt")) {
171 : 4 : return IPPROTO_TCP;
172 : : }
173 [ + + ]: 560 : if (!strcmp(type, "gre")) {
174 : 484 : return IPPROTO_GRE;
175 : : }
176 [ + + ]: 76 : if (!strcmp(type, "vxlan")) {
177 : 54 : return IPPROTO_UDP;
178 : : }
179 : 22 : return 0;
180 : : }
181 : :
182 : : void
183 : 353 : tnl_port_map_insert(odp_port_t port, ovs_be16 tp_port,
184 : : const char dev_name[], const char type[])
185 : : {
186 : : struct tnl_port *p;
187 : : struct ip_device *ip_dev;
188 : : uint8_t nw_proto;
189 : :
190 : 353 : nw_proto = tnl_type_to_nw_proto(type);
191 [ + + ]: 353 : if (!nw_proto) {
192 : 11 : return;
193 : : }
194 : :
195 : 342 : ovs_mutex_lock(&mutex);
196 [ + + ]: 360 : LIST_FOR_EACH(p, node, &port_list) {
197 [ + + ][ + - ]: 246 : if (tp_port == p->tp_port && p->nw_proto == nw_proto) {
198 : 228 : goto out;
199 : : }
200 : : }
201 : :
202 : 114 : p = xzalloc(sizeof *p);
203 : 114 : p->port = port;
204 : 114 : p->tp_port = tp_port;
205 : 114 : p->nw_proto = nw_proto;
206 : 114 : ovs_strlcpy(p->dev_name, dev_name, sizeof p->dev_name);
207 : 114 : ovs_list_insert(&port_list, &p->node);
208 : :
209 [ + + ]: 232 : LIST_FOR_EACH(ip_dev, node, &addr_list) {
210 : 118 : map_insert_ipdev__(ip_dev, p->dev_name, p->port, p->nw_proto, p->tp_port);
211 : : }
212 : :
213 : : out:
214 : 342 : ovs_mutex_unlock(&mutex);
215 : : }
216 : :
217 : : static void
218 : 546 : tnl_port_unref(const struct cls_rule *cr)
219 : : {
220 : 546 : struct tnl_port_in *p = tnl_port_cast(cr);
221 : :
222 [ + - ][ + - ]: 546 : if (cr && ovs_refcount_unref_relaxed(&p->ref_cnt) == 1) {
223 [ + - ]: 546 : if (classifier_remove(&cls, cr)) {
224 : 546 : ovsrcu_postpone(tnl_port_free, p);
225 : : }
226 : : }
227 : 546 : }
228 : :
229 : : static void
230 : 546 : map_delete(struct eth_addr mac, struct in6_addr *addr,
231 : : ovs_be16 tp_port, uint8_t nw_proto)
232 : : {
233 : : const struct cls_rule *cr;
234 : : struct flow flow;
235 : :
236 : 546 : tnl_port_init_flow(&flow, mac, addr, nw_proto, tp_port);
237 : :
238 : 546 : cr = classifier_lookup(&cls, OVS_VERSION_MAX, &flow, NULL);
239 : 546 : tnl_port_unref(cr);
240 : 546 : }
241 : :
242 : : static void
243 : 395 : ipdev_map_delete(struct ip_device *ip_dev, ovs_be16 tp_port, uint8_t nw_proto)
244 : : {
245 [ + - ]: 395 : if (ip_dev->n_addr) {
246 : : int i;
247 : :
248 [ + + ]: 941 : for (i = 0; i < ip_dev->n_addr; i++) {
249 : 546 : map_delete(ip_dev->mac, &ip_dev->addr[i], tp_port, nw_proto);
250 : : }
251 : : }
252 : 395 : }
253 : :
254 : : void
255 : 361 : tnl_port_map_delete(ovs_be16 tp_port, const char type[])
256 : : {
257 : : struct tnl_port *p, *next;
258 : : struct ip_device *ip_dev;
259 : 361 : bool found = false;
260 : : uint8_t nw_proto;
261 : :
262 : 361 : nw_proto = tnl_type_to_nw_proto(type);
263 : :
264 : 361 : ovs_mutex_lock(&mutex);
265 [ + + ][ + + ]: 375 : LIST_FOR_EACH_SAFE(p, next, node, &port_list) {
266 [ + + ][ + - ]: 126 : if (p->tp_port == tp_port && p->nw_proto == nw_proto) {
267 : 112 : ovs_list_remove(&p->node);
268 : 112 : found = true;
269 : 112 : break;
270 : : }
271 : : }
272 : :
273 [ + + ]: 361 : if (!found) {
274 : 249 : goto out;
275 : : }
276 [ + + ]: 242 : LIST_FOR_EACH(ip_dev, node, &addr_list) {
277 : 130 : ipdev_map_delete(ip_dev, p->tp_port, p->nw_proto);
278 : : }
279 : :
280 : 112 : free(p);
281 : : out:
282 : 361 : ovs_mutex_unlock(&mutex);
283 : 361 : }
284 : :
285 : : /* 'flow' is non-const to allow for temporary modifications during the lookup.
286 : : * Any changes are restored before returning. */
287 : : odp_port_t
288 : 1506 : tnl_port_map_lookup(struct flow *flow, struct flow_wildcards *wc)
289 : : {
290 : 1506 : const struct cls_rule *cr = classifier_lookup(&cls, OVS_VERSION_MAX, flow,
291 : : wc);
292 : :
293 [ + + ]: 1506 : return (cr) ? tnl_port_cast(cr)->portno : ODPP_NONE;
294 : : }
295 : :
296 : : static void
297 : 0 : tnl_port_show_v(struct ds *ds)
298 : : {
299 : : const struct tnl_port_in *p;
300 : :
301 [ # # ][ # # ]: 0 : CLS_FOR_EACH(p, cr, &cls) {
302 : : struct odputil_keybuf keybuf;
303 : : struct odputil_keybuf maskbuf;
304 : : struct flow flow;
305 : : const struct nlattr *key, *mask;
306 : : size_t key_len, mask_len;
307 : : struct flow_wildcards wc;
308 : : struct ofpbuf buf;
309 : 0 : struct odp_flow_key_parms odp_parms = {
310 : : .flow = &flow,
311 : : .mask = &wc.masks,
312 : : };
313 : :
314 : 0 : ds_put_format(ds, "%s (%"PRIu32") : ", p->dev_name, p->portno);
315 : 0 : minimask_expand(p->cr.match.mask, &wc);
316 : 0 : miniflow_expand(p->cr.match.flow, &flow);
317 : :
318 : : /* Key. */
319 : 0 : odp_parms.support.recirc = true;
320 : 0 : ofpbuf_use_stack(&buf, &keybuf, sizeof keybuf);
321 : 0 : odp_flow_key_from_flow(&odp_parms, &buf);
322 : 0 : key = buf.data;
323 : 0 : key_len = buf.size;
324 : :
325 : : /* mask*/
326 : 0 : odp_parms.support.recirc = false;
327 : 0 : ofpbuf_use_stack(&buf, &maskbuf, sizeof maskbuf);
328 : 0 : odp_flow_key_from_mask(&odp_parms, &buf);
329 : 0 : mask = buf.data;
330 : 0 : mask_len = buf.size;
331 : :
332 : : /* build string. */
333 : 0 : odp_flow_format(key, key_len, mask, mask_len, NULL, ds, false);
334 : 0 : ds_put_format(ds, "\n");
335 : : }
336 : 0 : }
337 : :
338 : : static void
339 : 2 : tnl_port_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
340 : : const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
341 : : {
342 : 2 : struct ds ds = DS_EMPTY_INITIALIZER;
343 : : struct tnl_port *p;
344 : :
345 : 2 : ds_put_format(&ds, "Listening ports:\n");
346 : 2 : ovs_mutex_lock(&mutex);
347 [ - + ]: 2 : if (argc > 1) {
348 [ # # ]: 0 : if (!strcasecmp(argv[1], "-v")) {
349 : 0 : tnl_port_show_v(&ds);
350 : 0 : goto out;
351 : : }
352 : : }
353 : :
354 [ + + ]: 8 : LIST_FOR_EACH(p, node, &port_list) {
355 : 6 : ds_put_format(&ds, "%s (%"PRIu32")\n", p->dev_name, p->port);
356 : : }
357 : :
358 : : out:
359 : 2 : ovs_mutex_unlock(&mutex);
360 : 2 : unixctl_command_reply(conn, ds_cstr(&ds));
361 : 2 : ds_destroy(&ds);
362 : 2 : }
363 : :
364 : : static void
365 : 4564 : map_insert_ipdev(struct ip_device *ip_dev)
366 : : {
367 : : struct tnl_port *p;
368 : :
369 [ + + ]: 4843 : LIST_FOR_EACH(p, node, &port_list) {
370 : 279 : map_insert_ipdev__(ip_dev, p->dev_name, p->port, p->nw_proto, p->tp_port);
371 : : }
372 : 4564 : }
373 : :
374 : : static void
375 : 14600 : insert_ipdev__(struct netdev *dev,
376 : : struct in6_addr *addr, int n_addr)
377 : : {
378 : : struct ip_device *ip_dev;
379 : : enum netdev_flags flags;
380 : : int error;
381 : :
382 : 14600 : error = netdev_get_flags(dev, &flags);
383 [ + - ][ + + ]: 14600 : if (error || (flags & NETDEV_LOOPBACK)) {
384 : : goto err;
385 : : }
386 : :
387 : 4564 : ip_dev = xzalloc(sizeof *ip_dev);
388 : 4564 : ip_dev->dev = netdev_ref(dev);
389 : 4564 : ip_dev->change_seq = netdev_get_change_seq(dev);
390 : 4564 : error = netdev_get_etheraddr(ip_dev->dev, &ip_dev->mac);
391 [ - + ]: 4564 : if (error) {
392 : 0 : goto err_free_ipdev;
393 : : }
394 : 4564 : ip_dev->addr = addr;
395 : 4564 : ip_dev->n_addr = n_addr;
396 : 4564 : ovs_strlcpy(ip_dev->dev_name, netdev_get_name(dev), sizeof ip_dev->dev_name);
397 : 4564 : ovs_list_insert(&addr_list, &ip_dev->node);
398 : 4564 : map_insert_ipdev(ip_dev);
399 : 4564 : return;
400 : :
401 : : err_free_ipdev:
402 : 0 : netdev_close(ip_dev->dev);
403 : 0 : free(ip_dev);
404 : : err:
405 : 10036 : free(addr);
406 : : }
407 : :
408 : : static void
409 : 14600 : insert_ipdev(const char dev_name[])
410 : : {
411 : : struct in6_addr *addr, *mask;
412 : : struct netdev *dev;
413 : : int error, n_in6;
414 : :
415 : 14600 : error = netdev_open(dev_name, NULL, &dev);
416 [ - + ]: 14600 : if (error) {
417 : 0 : return;
418 : : }
419 : :
420 : 14600 : error = netdev_get_addr_list(dev, &addr, &mask, &n_in6);
421 [ - + ]: 14600 : if (error) {
422 : 0 : netdev_close(dev);
423 : 0 : return;
424 : : }
425 : 14600 : free(mask);
426 : 14600 : insert_ipdev__(dev, addr, n_in6);
427 : 14600 : netdev_close(dev);
428 : : }
429 : :
430 : : static void
431 : 3641 : delete_ipdev(struct ip_device *ip_dev)
432 : : {
433 : : struct tnl_port *p;
434 : :
435 [ + + ]: 3906 : LIST_FOR_EACH(p, node, &port_list) {
436 : 265 : ipdev_map_delete(ip_dev, p->tp_port, p->nw_proto);
437 : : }
438 : :
439 : 3641 : ovs_list_remove(&ip_dev->node);
440 : 3641 : netdev_close(ip_dev->dev);
441 : 3641 : free(ip_dev->addr);
442 : 3641 : free(ip_dev);
443 : 3641 : }
444 : :
445 : : void
446 : 27738 : tnl_port_map_insert_ipdev(const char dev_name[])
447 : : {
448 : : struct ip_device *ip_dev, *next;
449 : :
450 : 27738 : ovs_mutex_lock(&mutex);
451 : :
452 [ + + ][ + + ]: 61134 : LIST_FOR_EACH_SAFE(ip_dev, next, node, &addr_list) {
453 [ + + ]: 46989 : if (!strcmp(netdev_get_name(ip_dev->dev), dev_name)) {
454 [ + + ]: 13678 : if (ip_dev->change_seq == netdev_get_change_seq(ip_dev->dev)) {
455 : 13593 : goto out;
456 : : }
457 : : /* Address changed. */
458 : 85 : delete_ipdev(ip_dev);
459 : : }
460 : : }
461 : 14145 : insert_ipdev(dev_name);
462 : :
463 : : out:
464 : 27738 : ovs_mutex_unlock(&mutex);
465 : 27738 : }
466 : :
467 : : void
468 : 16781 : tnl_port_map_delete_ipdev(const char dev_name[])
469 : : {
470 : : struct ip_device *ip_dev, *next;
471 : :
472 : 16781 : ovs_mutex_lock(&mutex);
473 [ + + ][ + + ]: 40736 : LIST_FOR_EACH_SAFE(ip_dev, next, node, &addr_list) {
474 [ + + ]: 23955 : if (!strcmp(netdev_get_name(ip_dev->dev), dev_name)) {
475 : 3101 : delete_ipdev(ip_dev);
476 : : }
477 : : }
478 : 16781 : ovs_mutex_unlock(&mutex);
479 : 16781 : }
480 : :
481 : : void
482 : 99708 : tnl_port_map_run(void)
483 : : {
484 : : struct ip_device *ip_dev, *next;
485 : :
486 : 99708 : ovs_mutex_lock(&mutex);
487 [ + + ][ + + ]: 201155 : LIST_FOR_EACH_SAFE(ip_dev, next, node, &addr_list) {
488 : : char dev_name[IFNAMSIZ];
489 : :
490 [ + + ]: 101447 : if (ip_dev->change_seq == netdev_get_change_seq(ip_dev->dev)) {
491 : 100992 : continue;
492 : : }
493 : :
494 : : /* Address changed. */
495 : 455 : ovs_strlcpy(dev_name, ip_dev->dev_name, sizeof dev_name);
496 : 455 : delete_ipdev(ip_dev);
497 : 455 : insert_ipdev(dev_name);
498 : : }
499 : 99708 : ovs_mutex_unlock(&mutex);
500 : 99708 : }
501 : :
502 : : void
503 : 684 : tnl_port_map_init(void)
504 : : {
505 : 684 : classifier_init(&cls, flow_segment_u64s);
506 : 684 : ovs_list_init(&addr_list);
507 : 684 : ovs_list_init(&port_list);
508 : 684 : unixctl_command_register("tnl/ports/show", "-v", 0, 1, tnl_port_show, NULL);
509 : 684 : }
|