]> git.proxmox.com Git - mirror_ovs.git/blame - lib/tnl-ports.c
ofproto-dpif-upcall: Echo HASH attribute back to datapath.
[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 }
7dc18ae9 174 if (!strcmp(type, "gre") || !strcmp(type, "erspan") ||
3b10ceee 175 !strcmp(type, "ip6erspan") || !strcmp(type, "ip6gre")) {
98e3f58e
PS
176 return IPPROTO_GRE;
177 }
178 if (!strcmp(type, "vxlan")) {
179 return IPPROTO_UDP;
180 }
181 return 0;
182}
183
7f9b8504 184void
98e3f58e
PS
185tnl_port_map_insert(odp_port_t port, ovs_be16 tp_port,
186 const char dev_name[], const char type[])
7f9b8504
PS
187{
188 struct tnl_port *p;
189 struct ip_device *ip_dev;
98e3f58e
PS
190 uint8_t nw_proto;
191
192 nw_proto = tnl_type_to_nw_proto(type);
193 if (!nw_proto) {
194 return;
195 }
7f9b8504
PS
196
197 ovs_mutex_lock(&mutex);
198 LIST_FOR_EACH(p, node, &port_list) {
c8025aee 199 if (p->port == port && p->nw_proto == nw_proto) {
e7c9ff0e 200 ovs_refcount_ref(&p->ref_cnt);
201 goto out;
7f9b8504
PS
202 }
203 }
204
205 p = xzalloc(sizeof *p);
206 p->port = port;
98e3f58e
PS
207 p->tp_port = tp_port;
208 p->nw_proto = nw_proto;
7f9b8504 209 ovs_strlcpy(p->dev_name, dev_name, sizeof p->dev_name);
e7c9ff0e 210 ovs_refcount_init(&p->ref_cnt);
417e7e66 211 ovs_list_insert(&port_list, &p->node);
7f9b8504
PS
212
213 LIST_FOR_EACH(ip_dev, node, &addr_list) {
98e3f58e 214 map_insert_ipdev__(ip_dev, p->dev_name, p->port, p->nw_proto, p->tp_port);
7f9b8504
PS
215 }
216
217out:
fccd7c09 218 ovs_mutex_unlock(&mutex);
a36de779
PS
219}
220
221static void
222tnl_port_unref(const struct cls_rule *cr)
223{
224 struct tnl_port_in *p = tnl_port_cast(cr);
225
226 if (cr && ovs_refcount_unref_relaxed(&p->ref_cnt) == 1) {
46ab60bf
BP
227 classifier_remove_assert(&cls, cr);
228 ovsrcu_postpone(tnl_port_free, p);
a36de779
PS
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
c8025aee 258tnl_port_map_delete(odp_port_t 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) {
c8025aee 268 if (p->port == port && p->nw_proto == nw_proto &&
e7c9ff0e 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) {
c8025aee
BN
351 ds_put_format(&ds, "%s (%"PRIu32") ref_cnt=%u\n", p->dev_name, p->port,
352 ovs_refcount_read(&p->ref_cnt));
a36de779 353 }
7f9b8504
PS
354
355out:
356 ovs_mutex_unlock(&mutex);
a36de779
PS
357 unixctl_command_reply(conn, ds_cstr(&ds));
358 ds_destroy(&ds);
359}
360
7f9b8504
PS
361static void
362map_insert_ipdev(struct ip_device *ip_dev)
363{
364 struct tnl_port *p;
365
366 LIST_FOR_EACH(p, node, &port_list) {
98e3f58e 367 map_insert_ipdev__(ip_dev, p->dev_name, p->port, p->nw_proto, p->tp_port);
7f9b8504
PS
368 }
369}
370
371static void
a8704b50
PS
372insert_ipdev__(struct netdev *dev,
373 struct in6_addr *addr, int n_addr)
7f9b8504
PS
374{
375 struct ip_device *ip_dev;
376 enum netdev_flags flags;
7f9b8504 377 int error;
7f9b8504
PS
378
379 error = netdev_get_flags(dev, &flags);
380 if (error || (flags & NETDEV_LOOPBACK)) {
a8704b50 381 goto err;
7f9b8504
PS
382 }
383
384 ip_dev = xzalloc(sizeof *ip_dev);
a8704b50 385 ip_dev->dev = netdev_ref(dev);
7f9b8504
PS
386 ip_dev->change_seq = netdev_get_change_seq(dev);
387 error = netdev_get_etheraddr(ip_dev->dev, &ip_dev->mac);
388 if (error) {
a8704b50 389 goto err_free_ipdev;
7f9b8504 390 }
a8704b50
PS
391 ip_dev->addr = addr;
392 ip_dev->n_addr = n_addr;
7f9b8504 393 ovs_strlcpy(ip_dev->dev_name, netdev_get_name(dev), sizeof ip_dev->dev_name);
417e7e66 394 ovs_list_insert(&addr_list, &ip_dev->node);
7f9b8504 395 map_insert_ipdev(ip_dev);
a8704b50
PS
396 return;
397
398err_free_ipdev:
399 netdev_close(ip_dev->dev);
400 free(ip_dev);
401err:
402 free(addr);
403}
404
405static void
406insert_ipdev(const char dev_name[])
407{
408 struct in6_addr *addr, *mask;
409 struct netdev *dev;
410 int error, n_in6;
411
493e0550 412 error = netdev_open(dev_name, netdev_get_type_from_name(dev_name), &dev);
a8704b50
PS
413 if (error) {
414 return;
415 }
416
417 error = netdev_get_addr_list(dev, &addr, &mask, &n_in6);
418 if (error) {
419 netdev_close(dev);
420 return;
421 }
422 free(mask);
423 insert_ipdev__(dev, addr, n_in6);
424 netdev_close(dev);
7f9b8504
PS
425}
426
427static void
428delete_ipdev(struct ip_device *ip_dev)
429{
430 struct tnl_port *p;
431
432 LIST_FOR_EACH(p, node, &port_list) {
98e3f58e 433 ipdev_map_delete(ip_dev, p->tp_port, p->nw_proto);
7f9b8504
PS
434 }
435
417e7e66 436 ovs_list_remove(&ip_dev->node);
7f9b8504 437 netdev_close(ip_dev->dev);
a8704b50 438 free(ip_dev->addr);
7f9b8504
PS
439 free(ip_dev);
440}
441
442void
443tnl_port_map_insert_ipdev(const char dev_name[])
444{
c465f75f 445 struct ip_device *ip_dev, *next;
7f9b8504
PS
446
447 ovs_mutex_lock(&mutex);
448
c465f75f 449 LIST_FOR_EACH_SAFE(ip_dev, next, node, &addr_list) {
7f9b8504
PS
450 if (!strcmp(netdev_get_name(ip_dev->dev), dev_name)) {
451 if (ip_dev->change_seq == netdev_get_change_seq(ip_dev->dev)) {
452 goto out;
453 }
454 /* Address changed. */
455 delete_ipdev(ip_dev);
7f9b8504
PS
456 }
457 }
458 insert_ipdev(dev_name);
459
460out:
461 ovs_mutex_unlock(&mutex);
462}
463
464void
465tnl_port_map_delete_ipdev(const char dev_name[])
466{
467 struct ip_device *ip_dev, *next;
468
469 ovs_mutex_lock(&mutex);
470 LIST_FOR_EACH_SAFE(ip_dev, next, node, &addr_list) {
471 if (!strcmp(netdev_get_name(ip_dev->dev), dev_name)) {
472 delete_ipdev(ip_dev);
473 }
474 }
475 ovs_mutex_unlock(&mutex);
476}
477
478void
479tnl_port_map_run(void)
480{
c465f75f 481 struct ip_device *ip_dev, *next;
7f9b8504
PS
482
483 ovs_mutex_lock(&mutex);
c465f75f 484 LIST_FOR_EACH_SAFE(ip_dev, next, node, &addr_list) {
7f9b8504
PS
485 char dev_name[IFNAMSIZ];
486
487 if (ip_dev->change_seq == netdev_get_change_seq(ip_dev->dev)) {
488 continue;
489 }
490
491 /* Address changed. */
f9ac0f03 492 ovs_strlcpy_arrays(dev_name, ip_dev->dev_name);
7f9b8504
PS
493 delete_ipdev(ip_dev);
494 insert_ipdev(dev_name);
495 }
496 ovs_mutex_unlock(&mutex);
497}
498
a36de779
PS
499void
500tnl_port_map_init(void)
501{
d70e8c28 502 classifier_init(&cls, flow_segment_u64s);
417e7e66
BW
503 ovs_list_init(&addr_list);
504 ovs_list_init(&port_list);
7f9b8504 505 unixctl_command_register("tnl/ports/show", "-v", 0, 1, tnl_port_show, NULL);
a36de779 506}