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