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