]> git.proxmox.com Git - mirror_ovs.git/blame - lib/netdev-vport.c
cirrus: Use FreeBSD 12.2.
[mirror_ovs.git] / lib / netdev-vport.c
CommitLineData
777ece09 1/*
f9ac0f03 2 * Copyright (c) 2010, 2011, 2012, 2013, 2014, 2017 Nicira, Inc.
68da36fe 3 * Copyright (c) 2016 Red Hat, Inc.
777ece09
JG
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at:
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
6fcfff1b 11 * Unless required by applicable law or agreed to in writing, software
777ece09
JG
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#include <config.h>
2b9d6589
BP
19
20#include "netdev-vport.h"
21
777ece09
JG
22#include <errno.h>
23#include <fcntl.h>
ea83a2fc 24#include <sys/socket.h>
2b9d6589 25#include <net/if.h>
b2befd5b 26#include <sys/types.h>
2456f331 27#include <netinet/in.h>
370e373b 28#include <netinet/ip6.h>
777ece09
JG
29#include <sys/ioctl.h>
30
b9298d3f 31#include "byte-order.h"
5059eff3
JP
32#include "daemon.h"
33#include "dirs.h"
0a740f48 34#include "dpif.h"
aca40d4f
TLSC
35#include "netdev.h"
36#include "netdev-native-tnl.h"
2b9d6589 37#include "netdev-provider.h"
6b241d64 38#include "netdev-vport-private.h"
9fff138e 39#include "openvswitch/dynamic-string.h"
d9b4ebc5 40#include "ovs-router.h"
2b9d6589 41#include "packets.h"
fd016ae3 42#include "openvswitch/poll-loop.h"
a132aa96 43#include "route-table.h"
189de33f 44#include "simap.h"
aca40d4f 45#include "smap.h"
777ece09 46#include "socket-util.h"
a36de779
PS
47#include "unaligned.h"
48#include "unixctl.h"
aca40d4f 49#include "openvswitch/vlog.h"
ebe0e518 50#include "openvswitch/ofp-parse.h"
01b25786
PB
51#ifdef __linux__
52#include "netdev-linux.h"
53#endif
777ece09 54
d98e6007 55VLOG_DEFINE_THIS_MODULE(netdev_vport);
5136ce49 56
c1fc1411 57#define GENEVE_DST_PORT 6081
4f2abb7b 58#define VXLAN_DST_PORT 4789
a6ae068b 59#define LISP_DST_PORT 4341
4237026e 60#define STT_DST_PORT 7471
a6ae068b 61
f431bf7d
EJ
62#define DEFAULT_TTL 64
63
41ca1e0a
AW
64/* Last read of the route-table's change number. */
65static uint64_t rt_change_seqno;
66
86383816 67static int get_patch_config(const struct netdev *netdev, struct smap *args);
b5d57fc8 68static int get_tunnel_config(const struct netdev *, struct smap *args);
41ca1e0a 69static bool tunnel_check_status_change__(struct netdev_vport *);
189de33f
EC
70static void update_vxlan_global_cfg(struct netdev *,
71 struct netdev_tunnel_config *,
72 struct netdev_tunnel_config *);
2b9d6589 73
6b241d64
PS
74struct vport_class {
75 const char *dpif_port;
76 struct netdev_class netdev_class;
189de33f 77 struct simap global_cfg_tracker;
6b241d64 78};
777ece09 79
41ca1e0a
AW
80bool
81netdev_vport_is_vport_class(const struct netdev_class *class)
82{
83 return is_vport_class(class);
84}
85
189de33f 86static struct vport_class *
2b9d6589
BP
87vport_class_cast(const struct netdev_class *class)
88{
cb22974d 89 ovs_assert(is_vport_class(class));
2b9d6589
BP
90 return CONTAINER_OF(class, struct vport_class, netdev_class);
91}
92
f431bf7d 93static const struct netdev_tunnel_config *
b5d57fc8 94get_netdev_tunnel_config(const struct netdev *netdev)
f431bf7d 95{
b5d57fc8 96 return &netdev_vport_cast(netdev)->tnl_cfg;
f431bf7d
EJ
97}
98
0a740f48
EJ
99bool
100netdev_vport_is_patch(const struct netdev *netdev)
101{
b5d57fc8 102 const struct netdev_class *class = netdev_get_class(netdev);
f18a39b7 103
c060c4cf 104 return class->get_config == get_patch_config;
0a740f48
EJ
105}
106
56b11f0b 107static bool
b5d57fc8 108netdev_vport_needs_dst_port(const struct netdev *dev)
56b11f0b 109{
b5d57fc8
BP
110 const struct netdev_class *class = netdev_get_class(dev);
111 const char *type = netdev_get_type(dev);
56b11f0b 112
a6ae068b 113 return (class->get_config == get_tunnel_config &&
c1fc1411 114 (!strcmp("geneve", type) || !strcmp("vxlan", type) ||
3c6d05a0 115 !strcmp("lisp", type) || !strcmp("stt", type) ||
ebe0e518 116 !strcmp("gtpu", type) || !strcmp("bareudp",type)));
56b11f0b
KM
117}
118
94a53842
AW
119const char *
120netdev_vport_class_get_dpif_port(const struct netdev_class *class)
121{
122 return is_vport_class(class) ? vport_class_cast(class)->dpif_port : NULL;
123}
124
de281153 125const char *
3aa30359
BP
126netdev_vport_get_dpif_port(const struct netdev *netdev,
127 char namebuf[], size_t bufsize)
de281153 128{
a5d4fadd
JG
129 const struct netdev_class *class = netdev_get_class(netdev);
130 const char *dpif_port = netdev_vport_class_get_dpif_port(class);
131
132 if (!dpif_port) {
133 return netdev_get_name(netdev);
134 }
135
b5d57fc8
BP
136 if (netdev_vport_needs_dst_port(netdev)) {
137 const struct netdev_vport *vport = netdev_vport_cast(netdev);
56b11f0b
KM
138
139 /*
a5d4fadd
JG
140 * Note: IFNAMSIZ is 16 bytes long. Implementations should choose
141 * a dpif port name that is short enough to fit including any
142 * port numbers but assert just in case.
56b11f0b 143 */
3aa30359 144 BUILD_ASSERT(NETDEV_VPORT_NAME_BUFSIZE >= IFNAMSIZ);
a5d4fadd
JG
145 ovs_assert(strlen(dpif_port) + 6 < IFNAMSIZ);
146 snprintf(namebuf, bufsize, "%s_%d", dpif_port,
56b11f0b 147 ntohs(vport->tnl_cfg.dst_port));
3aa30359 148 return namebuf;
56b11f0b 149 } else {
a5d4fadd 150 return dpif_port;
56b11f0b 151 }
2b9d6589 152}
777ece09 153
41ca1e0a
AW
154/* Whenever the route-table change number is incremented,
155 * netdev_vport_route_changed() should be called to update
156 * the corresponding tunnel interface status. */
157static void
158netdev_vport_route_changed(void)
159{
160 struct netdev **vports;
161 size_t i, n_vports;
162
163 vports = netdev_get_vports(&n_vports);
164 for (i = 0; i < n_vports; i++) {
165 struct netdev *netdev_ = vports[i];
166 struct netdev_vport *netdev = netdev_vport_cast(netdev_);
167
168 ovs_mutex_lock(&netdev->mutex);
169 /* Finds all tunnel vports. */
3ae91c01 170 if (ipv6_addr_is_set(&netdev->tnl_cfg.ipv6_dst)) {
41ca1e0a
AW
171 if (tunnel_check_status_change__(netdev)) {
172 netdev_change_seq_changed(netdev_);
173 }
174 }
41ca1e0a 175 ovs_mutex_unlock(&netdev->mutex);
b2f771ef
BP
176
177 netdev_close(netdev_);
41ca1e0a
AW
178 }
179
180 free(vports);
181}
182
9dc63482
BP
183static struct netdev *
184netdev_vport_alloc(void)
185{
186 struct netdev_vport *netdev = xzalloc(sizeof *netdev);
187 return &netdev->up;
188}
189
6b241d64 190int
9dc63482 191netdev_vport_construct(struct netdev *netdev_)
2b9d6589 192{
69987881
CM
193 const struct netdev_class *class = netdev_get_class(netdev_);
194 const char *dpif_port = netdev_vport_class_get_dpif_port(class);
a36de779 195 struct netdev_vport *dev = netdev_vport_cast(netdev_);
69987881 196 const char *p, *name = netdev_get_name(netdev_);
a36de779 197 const char *type = netdev_get_type(netdev_);
69987881 198 uint16_t port = 0;
6d9e6eb4 199
a36de779 200 ovs_mutex_init(&dev->mutex);
74ff3298 201 eth_addr_random(&dev->etheraddr);
a36de779 202
69987881
CM
203 if (name && dpif_port && (strlen(name) > strlen(dpif_port) + 1) &&
204 (!strncmp(name, dpif_port, strlen(dpif_port)))) {
205 p = name + strlen(dpif_port) + 1;
206 port = atoi(p);
207 }
208
209 /* If a destination port for tunnel ports is specified in the netdev
210 * name, use it instead of the default one. Otherwise, use the default
211 * destination port */
a36de779 212 if (!strcmp(type, "geneve")) {
69987881 213 dev->tnl_cfg.dst_port = port ? htons(port) : htons(GENEVE_DST_PORT);
a36de779 214 } else if (!strcmp(type, "vxlan")) {
69987881 215 dev->tnl_cfg.dst_port = port ? htons(port) : htons(VXLAN_DST_PORT);
189de33f 216 update_vxlan_global_cfg(netdev_, NULL, &dev->tnl_cfg);
a36de779 217 } else if (!strcmp(type, "lisp")) {
69987881 218 dev->tnl_cfg.dst_port = port ? htons(port) : htons(LISP_DST_PORT);
4237026e 219 } else if (!strcmp(type, "stt")) {
69987881 220 dev->tnl_cfg.dst_port = port ? htons(port) : htons(STT_DST_PORT);
3c6d05a0
WT
221 } else if (!strcmp(type, "gtpu")) {
222 dev->tnl_cfg.dst_port = port ? htons(port) : htons(GTPU_DST_PORT);
ebe0e518
MV
223 } else if (!strcmp(type, "bareudp")) {
224 dev->tnl_cfg.dst_port = htons(port);
a36de779 225 }
6d9e6eb4 226
0890056e
PS
227 dev->tnl_cfg.dont_fragment = true;
228 dev->tnl_cfg.ttl = DEFAULT_TTL;
de5cdb90 229 return 0;
777ece09
JG
230}
231
2b9d6589 232static void
9dc63482 233netdev_vport_destruct(struct netdev *netdev_)
2b9d6589 234{
b5d57fc8 235 struct netdev_vport *netdev = netdev_vport_cast(netdev_);
189de33f
EC
236 const char *type = netdev_get_type(netdev_);
237
238 if (!strcmp(type, "vxlan")) {
239 update_vxlan_global_cfg(netdev_, &netdev->tnl_cfg, NULL);
240 }
2b9d6589 241
b5d57fc8 242 free(netdev->peer);
86383816 243 ovs_mutex_destroy(&netdev->mutex);
9dc63482
BP
244}
245
246static void
247netdev_vport_dealloc(struct netdev *netdev_)
248{
249 struct netdev_vport *netdev = netdev_vport_cast(netdev_);
2b9d6589
BP
250 free(netdev);
251}
252
2b9d6589 253static int
74ff3298 254netdev_vport_set_etheraddr(struct netdev *netdev_, const struct eth_addr mac)
777ece09 255{
b5d57fc8 256 struct netdev_vport *netdev = netdev_vport_cast(netdev_);
86383816
BP
257
258 ovs_mutex_lock(&netdev->mutex);
74ff3298 259 netdev->etheraddr = mac;
86383816 260 ovs_mutex_unlock(&netdev->mutex);
3e912ffc 261 netdev_change_seq_changed(netdev_);
86383816 262
35b769cb 263 return 0;
777ece09
JG
264}
265
2b9d6589 266static int
74ff3298 267netdev_vport_get_etheraddr(const struct netdev *netdev_, struct eth_addr *mac)
777ece09 268{
86383816
BP
269 struct netdev_vport *netdev = netdev_vport_cast(netdev_);
270
271 ovs_mutex_lock(&netdev->mutex);
74ff3298 272 *mac = netdev->etheraddr;
86383816
BP
273 ovs_mutex_unlock(&netdev->mutex);
274
35b769cb 275 return 0;
777ece09
JG
276}
277
41ca1e0a
AW
278/* Checks if the tunnel status has changed and returns a boolean.
279 * Updates the tunnel status if it has changed. */
280static bool
281tunnel_check_status_change__(struct netdev_vport *netdev)
282 OVS_REQUIRES(netdev->mutex)
ea763e0e 283{
3dea0874 284 char iface[IFNAMSIZ];
41ca1e0a 285 bool status = false;
3ae91c01
JB
286 struct in6_addr *route;
287 struct in6_addr gw;
ed52ca57 288 uint32_t mark;
ea763e0e 289
41ca1e0a 290 iface[0] = '\0';
3ae91c01 291 route = &netdev->tnl_cfg.ipv6_dst;
ed52ca57
PS
292 mark = netdev->tnl_cfg.egress_pkt_mark;
293 if (ovs_router_lookup(mark, route, iface, NULL, &gw)) {
a404826e
AE
294 struct netdev *egress_netdev;
295
6c607a64 296 if (!netdev_open(iface, NULL, &egress_netdev)) {
41ca1e0a 297 status = netdev_get_carrier(egress_netdev);
a404826e
AE
298 netdev_close(egress_netdev);
299 }
ea763e0e
EJ
300 }
301
41ca1e0a
AW
302 if (strcmp(netdev->egress_iface, iface)
303 || netdev->carrier_status != status) {
f9ac0f03 304 ovs_strlcpy_arrays(netdev->egress_iface, iface);
41ca1e0a
AW
305 netdev->carrier_status = status;
306
307 return true;
308 }
309
310 return false;
311}
312
313static int
314tunnel_get_status(const struct netdev *netdev_, struct smap *smap)
315{
316 struct netdev_vport *netdev = netdev_vport_cast(netdev_);
317
318 if (netdev->egress_iface[0]) {
319 smap_add(smap, "tunnel_egress_iface", netdev->egress_iface);
320
321 smap_add(smap, "tunnel_egress_iface_carrier",
322 netdev->carrier_status ? "up" : "down");
323 }
324
ea763e0e
EJ
325 return 0;
326}
327
2b9d6589 328static int
b5d57fc8
BP
329netdev_vport_update_flags(struct netdev *netdev OVS_UNUSED,
330 enum netdev_flags off,
331 enum netdev_flags on OVS_UNUSED,
332 enum netdev_flags *old_flagsp)
777ece09
JG
333{
334 if (off & (NETDEV_UP | NETDEV_PROMISC)) {
335 return EOPNOTSUPP;
336 }
337
338 *old_flagsp = NETDEV_UP | NETDEV_PROMISC;
339 return 0;
340}
341
ea83a2fc 342static void
1c33f0c3 343netdev_vport_run(const struct netdev_class *netdev_class OVS_UNUSED)
ea83a2fc 344{
41ca1e0a
AW
345 uint64_t seq;
346
a132aa96 347 route_table_run();
41ca1e0a
AW
348 seq = route_table_get_change_seq();
349 if (rt_change_seqno != seq) {
350 rt_change_seqno = seq;
351 netdev_vport_route_changed();
352 }
ea83a2fc
EJ
353}
354
355static void
1c33f0c3 356netdev_vport_wait(const struct netdev_class *netdev_class OVS_UNUSED)
ea83a2fc 357{
41ca1e0a
AW
358 uint64_t seq;
359
a132aa96 360 route_table_wait();
41ca1e0a
AW
361 seq = route_table_get_change_seq();
362 if (rt_change_seqno != seq) {
363 poll_immediate_wake();
364 }
ea83a2fc
EJ
365}
366\f
0a740f48 367/* Code specific to tunnel types. */
2b9d6589 368
f431bf7d
EJ
369static ovs_be64
370parse_key(const struct smap *args, const char *name,
371 bool *present, bool *flow)
c19e6535
BP
372{
373 const char *s;
374
f431bf7d
EJ
375 *present = false;
376 *flow = false;
377
79f1cbe9 378 s = smap_get(args, name);
c19e6535 379 if (!s) {
79f1cbe9 380 s = smap_get(args, "key");
c19e6535 381 if (!s) {
f431bf7d 382 return 0;
c19e6535
BP
383 }
384 }
385
f431bf7d
EJ
386 *present = true;
387
c19e6535 388 if (!strcmp(s, "flow")) {
f431bf7d
EJ
389 *flow = true;
390 return 0;
c19e6535 391 } else {
f431bf7d 392 return htonll(strtoull(s, NULL, 0));
c19e6535
BP
393 }
394}
395
3ae91c01
JB
396static int
397parse_tunnel_ip(const char *value, bool accept_mcast, bool *flow,
398 struct in6_addr *ipv6, uint16_t *protocol)
399{
400 if (!strcmp(value, "flow")) {
401 *flow = true;
402 *protocol = 0;
403 return 0;
404 }
405 if (addr_is_ipv6(value)) {
406 if (lookup_ipv6(value, ipv6)) {
407 return ENOENT;
408 }
409 if (!accept_mcast && ipv6_addr_is_multicast(ipv6)) {
410 return EINVAL;
411 }
412 *protocol = ETH_TYPE_IPV6;
413 } else {
414 struct in_addr ip;
415 if (lookup_ip(value, &ip)) {
416 return ENOENT;
417 }
418 if (!accept_mcast && ip_is_multicast(ip.s_addr)) {
419 return EINVAL;
420 }
421 in6_addr_set_mapped_ipv4(ipv6, ip.s_addr);
422 *protocol = ETH_TYPE_IP;
423 }
424 return 0;
425}
426
875ab130
BP
427enum tunnel_layers {
428 TNL_L2 = 1 << 0, /* 1 if a tunnel type can carry Ethernet traffic. */
429 TNL_L3 = 1 << 1 /* 1 if a tunnel type can carry L3 traffic. */
430};
431static enum tunnel_layers
432tunnel_supported_layers(const char *type,
433 const struct netdev_tunnel_config *tnl_cfg)
434{
435 if (!strcmp(type, "lisp")) {
436 return TNL_L3;
437 } else if (!strcmp(type, "gre")) {
438 return TNL_L2 | TNL_L3;
ea4cb65c
EG
439 } else if (!strcmp(type, "vxlan")
440 && tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GPE)) {
875ab130 441 return TNL_L2 | TNL_L3;
3c6d05a0
WT
442 } else if (!strcmp(type, "gtpu")) {
443 return TNL_L3;
ebe0e518
MV
444 } else if (!strcmp(type, "bareudp")) {
445 return TNL_L3;
875ab130
BP
446 } else {
447 return TNL_L2;
448 }
449}
450static enum netdev_pt_mode
451default_pt_mode(enum tunnel_layers layers)
452{
453 return layers == TNL_L3 ? NETDEV_PT_LEGACY_L3 : NETDEV_PT_LEGACY_L2;
454}
455
189de33f
EC
456static char *
457vxlan_get_port_ext_gbp_str(uint16_t port, bool gbp,
458 char namebuf[], size_t bufsize)
459{
460 snprintf(namebuf, bufsize, "dst_port_%d%s",
461 port, gbp ? "_gbp" : "");
462
463 return namebuf;
464}
465
466static void
467update_vxlan_global_cfg(struct netdev *netdev,
468 struct netdev_tunnel_config *old_cfg,
469 struct netdev_tunnel_config *new_cfg)
470{
471 unsigned int count;
472 char namebuf[20];
473 const char *type = netdev_get_type(netdev);
474 struct vport_class *vclass = vport_class_cast(netdev_get_class(netdev));
475
476 if (strcmp(type, "vxlan") ||
477 (old_cfg != NULL && new_cfg != NULL &&
478 old_cfg->dst_port == new_cfg->dst_port &&
479 old_cfg->exts == new_cfg->exts)) {
480 return;
481 }
482
483 if (old_cfg != NULL) {
484 vxlan_get_port_ext_gbp_str(ntohs(old_cfg->dst_port),
485 old_cfg->exts &
486 (1 << OVS_VXLAN_EXT_GBP),
487 namebuf, sizeof(namebuf));
488
489 count = simap_get(&vclass->global_cfg_tracker, namebuf);
490 if (count != 0) {
491 if (--count) {
492 simap_put(&vclass->global_cfg_tracker, namebuf, count);
493 } else {
494 simap_find_and_delete(&vclass->global_cfg_tracker, namebuf);
495 }
496 }
497 }
498
499 if (new_cfg != NULL) {
500 vxlan_get_port_ext_gbp_str(ntohs(new_cfg->dst_port),
501 new_cfg->exts &
502 (1 << OVS_VXLAN_EXT_GBP),
503 namebuf, sizeof(namebuf));
504
505 simap_increase(&vclass->global_cfg_tracker, namebuf, 1);
506 }
507}
508
509static bool
510is_concomitant_vxlan_tunnel_present(struct netdev_vport *dev,
511 const struct netdev_tunnel_config *tnl_cfg)
512{
513 char namebuf[20];
514 const char *type = netdev_get_type(&dev->up);
515 struct vport_class *vclass = vport_class_cast(netdev_get_class(&dev->up));
516
517 if (strcmp(type, "vxlan")) {
518 return false;
519 }
520
521 if (dev->tnl_cfg.dst_port == tnl_cfg->dst_port &&
522 (dev->tnl_cfg.exts & (1 << OVS_VXLAN_EXT_GBP)) ==
523 (tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GBP))) {
524
525 if (ntohs(dev->tnl_cfg.dst_port) == VXLAN_DST_PORT) {
526 /* Special case where we kept the default port/gbp, only ok if
527 the opposite of the default does not exits */
528 vxlan_get_port_ext_gbp_str(ntohs(tnl_cfg->dst_port),
529 !(tnl_cfg->exts &
530 (1 << OVS_VXLAN_EXT_GBP)),
531 namebuf, sizeof(namebuf));
532
533 if (simap_get(&vclass->global_cfg_tracker, namebuf) > 0) {
534 return true;
535 }
536 }
537 return false;
538 }
539
540 /* Same port: ok if no one is left with the previous configuration */
541 if (dev->tnl_cfg.dst_port == tnl_cfg->dst_port) {
542 vxlan_get_port_ext_gbp_str(ntohs(dev->tnl_cfg.dst_port),
543 dev->tnl_cfg.exts &
544 (1 << OVS_VXLAN_EXT_GBP),
545 namebuf, sizeof(namebuf));
546
547 if (simap_get(&vclass->global_cfg_tracker, namebuf) > 1) {
548 return true;
549 }
550
551 return false;
552 }
553
554 /* Different port: ok if the opposite gbp option does not yet exists */
555 vxlan_get_port_ext_gbp_str(ntohs(tnl_cfg->dst_port),
556 !(tnl_cfg->exts &
557 (1 << OVS_VXLAN_EXT_GBP)),
558 namebuf, sizeof(namebuf));
559
560 if (simap_get(&vclass->global_cfg_tracker, namebuf) > 0) {
561 return true;
562 }
563
564 return false;
565}
566
2b9d6589 567static int
9fff138e 568set_tunnel_config(struct netdev *dev_, const struct smap *args, char **errp)
2b9d6589 569{
b5d57fc8
BP
570 struct netdev_vport *dev = netdev_vport_cast(dev_);
571 const char *name = netdev_get_name(dev_);
572 const char *type = netdev_get_type(dev_);
9fff138e 573 struct ds errors = DS_EMPTY_INITIALIZER;
0ffff497 574 bool needs_dst_port, has_csum, has_seq;
3ae91c01 575 uint16_t dst_proto = 0, src_proto = 0;
f431bf7d 576 struct netdev_tunnel_config tnl_cfg;
79f1cbe9 577 struct smap_node *node;
9fff138e 578 int err;
f431bf7d 579
4752cc0c 580 has_csum = strstr(type, "gre") || strstr(type, "geneve") ||
4237026e 581 strstr(type, "stt") || strstr(type, "vxlan");
0ffff497 582 has_seq = strstr(type, "gre");
f431bf7d 583 memset(&tnl_cfg, 0, sizeof tnl_cfg);
2b9d6589 584
a36de779
PS
585 /* Add a default destination port for tunnel ports if none specified. */
586 if (!strcmp(type, "geneve")) {
587 tnl_cfg.dst_port = htons(GENEVE_DST_PORT);
588 }
589
590 if (!strcmp(type, "vxlan")) {
591 tnl_cfg.dst_port = htons(VXLAN_DST_PORT);
592 }
593
594 if (!strcmp(type, "lisp")) {
595 tnl_cfg.dst_port = htons(LISP_DST_PORT);
596 }
597
4237026e
PS
598 if (!strcmp(type, "stt")) {
599 tnl_cfg.dst_port = htons(STT_DST_PORT);
600 }
601
3c6d05a0
WT
602 if (!strcmp(type, "gtpu")) {
603 tnl_cfg.dst_port = htons(GTPU_DST_PORT);
604 }
605
a6ae068b 606 needs_dst_port = netdev_vport_needs_dst_port(dev_);
f431bf7d 607 tnl_cfg.dont_fragment = true;
e16a28b5 608
79f1cbe9
EJ
609 SMAP_FOR_EACH (node, args) {
610 if (!strcmp(node->key, "remote_ip")) {
3ae91c01
JB
611 err = parse_tunnel_ip(node->value, false, &tnl_cfg.ip_dst_flow,
612 &tnl_cfg.ipv6_dst, &dst_proto);
613 switch (err) {
614 case ENOENT:
9fff138e 615 ds_put_format(&errors, "%s: bad %s 'remote_ip'\n", name, type);
3ae91c01
JB
616 break;
617 case EINVAL:
9fff138e
DDP
618 ds_put_format(&errors,
619 "%s: multicast remote_ip=%s not allowed\n",
620 name, node->value);
621 goto out;
2b9d6589 622 }
79f1cbe9 623 } else if (!strcmp(node->key, "local_ip")) {
3ae91c01
JB
624 err = parse_tunnel_ip(node->value, true, &tnl_cfg.ip_src_flow,
625 &tnl_cfg.ipv6_src, &src_proto);
626 switch (err) {
627 case ENOENT:
9fff138e 628 ds_put_format(&errors, "%s: bad %s 'local_ip'\n", name, type);
3ae91c01 629 break;
2b9d6589 630 }
79f1cbe9
EJ
631 } else if (!strcmp(node->key, "tos")) {
632 if (!strcmp(node->value, "inherit")) {
f431bf7d 633 tnl_cfg.tos_inherit = true;
2b9d6589 634 } else {
3fca7064
PS
635 char *endptr;
636 int tos;
79f1cbe9 637 tos = strtol(node->value, &endptr, 0);
91aff446 638 if (*endptr == '\0' && tos == (tos & IP_DSCP_MASK)) {
f431bf7d 639 tnl_cfg.tos = tos;
91aff446 640 } else {
9fff138e
DDP
641 ds_put_format(&errors, "%s: invalid TOS %s\n", name,
642 node->value);
3fca7064 643 }
2b9d6589 644 }
79f1cbe9
EJ
645 } else if (!strcmp(node->key, "ttl")) {
646 if (!strcmp(node->value, "inherit")) {
f431bf7d 647 tnl_cfg.ttl_inherit = true;
2b9d6589 648 } else {
f431bf7d 649 tnl_cfg.ttl = atoi(node->value);
2b9d6589 650 }
79f827fa 651 } else if (!strcmp(node->key, "dst_port") && needs_dst_port) {
f431bf7d 652 tnl_cfg.dst_port = htons(atoi(node->value));
f431bf7d 653 } else if (!strcmp(node->key, "csum") && has_csum) {
79f1cbe9 654 if (!strcmp(node->value, "true")) {
f431bf7d 655 tnl_cfg.csum = true;
2b9d6589 656 }
0ffff497
WT
657 } else if (!strcmp(node->key, "seq") && has_seq) {
658 if (!strcmp(node->value, "true")) {
659 tnl_cfg.set_seq = true;
660 }
79f1cbe9
EJ
661 } else if (!strcmp(node->key, "df_default")) {
662 if (!strcmp(node->value, "false")) {
f431bf7d 663 tnl_cfg.dont_fragment = false;
66409d1b 664 }
79f1cbe9
EJ
665 } else if (!strcmp(node->key, "key") ||
666 !strcmp(node->key, "in_key") ||
875ab130
BP
667 !strcmp(node->key, "out_key") ||
668 !strcmp(node->key, "packet_type")) {
c19e6535 669 /* Handled separately below. */
875ab130 670 } else if (!strcmp(node->key, "exts") && !strcmp(type, "vxlan")) {
526df7d8
TG
671 char *str = xstrdup(node->value);
672 char *ext, *save_ptr = NULL;
673
674 tnl_cfg.exts = 0;
675
676 ext = strtok_r(str, ",", &save_ptr);
677 while (ext) {
678 if (!strcmp(type, "vxlan") && !strcmp(ext, "gbp")) {
679 tnl_cfg.exts |= (1 << OVS_VXLAN_EXT_GBP);
439f39cb
GS
680 } else if (!strcmp(type, "vxlan") && !strcmp(ext, "gpe")) {
681 tnl_cfg.exts |= (1 << OVS_VXLAN_EXT_GPE);
526df7d8 682 } else {
9fff138e
DDP
683 ds_put_format(&errors, "%s: unknown extension '%s'\n",
684 name, ext);
526df7d8
TG
685 }
686
687 ext = strtok_r(NULL, ",", &save_ptr);
688 }
689
690 free(str);
bf4bbd0d
PS
691 } else if (!strcmp(node->key, "egress_pkt_mark")) {
692 tnl_cfg.egress_pkt_mark = strtoul(node->value, NULL, 10);
693 tnl_cfg.set_egress_pkt_mark = true;
7dc18ae9 694 } else if (!strcmp(node->key, "erspan_idx")) {
068794b4
GR
695 if (!strcmp(node->value, "flow")) {
696 tnl_cfg.erspan_idx_flow = true;
697 } else {
698 tnl_cfg.erspan_idx_flow = false;
699 tnl_cfg.erspan_idx = strtol(node->value, NULL, 16);
700
701 if (tnl_cfg.erspan_idx & ~ERSPAN_IDX_MASK) {
702 ds_put_format(&errors, "%s: invalid erspan index: %s\n",
703 name, node->value);
704 err = EINVAL;
705 goto out;
706 }
7dc18ae9
WT
707 }
708 } else if (!strcmp(node->key, "erspan_ver")) {
068794b4
GR
709 if (!strcmp(node->value, "flow")) {
710 tnl_cfg.erspan_ver_flow = true;
711 tnl_cfg.erspan_idx_flow = true;
712 tnl_cfg.erspan_dir_flow = true;
713 tnl_cfg.erspan_hwid_flow = true;
714 } else {
715 tnl_cfg.erspan_ver_flow = false;
716 tnl_cfg.erspan_ver = atoi(node->value);
717
718 if (tnl_cfg.erspan_ver != 1 && tnl_cfg.erspan_ver != 2) {
719 ds_put_format(&errors, "%s: invalid erspan version: %s\n",
720 name, node->value);
721 err = EINVAL;
722 goto out;
723 }
7dc18ae9
WT
724 }
725 } else if (!strcmp(node->key, "erspan_dir")) {
068794b4
GR
726 if (!strcmp(node->value, "flow")) {
727 tnl_cfg.erspan_dir_flow = true;
728 } else {
729 tnl_cfg.erspan_dir_flow = false;
730 tnl_cfg.erspan_dir = atoi(node->value);
731
732 if (tnl_cfg.erspan_dir != 0 && tnl_cfg.erspan_dir != 1) {
733 ds_put_format(&errors, "%s: invalid erspan direction: %s\n",
734 name, node->value);
735 err = EINVAL;
736 goto out;
737 }
7dc18ae9
WT
738 }
739 } else if (!strcmp(node->key, "erspan_hwid")) {
068794b4
GR
740 if (!strcmp(node->value, "flow")) {
741 tnl_cfg.erspan_hwid_flow = true;
742 } else {
743 tnl_cfg.erspan_hwid_flow = false;
744 tnl_cfg.erspan_hwid = strtol(node->value, NULL, 16);
745
746 if (tnl_cfg.erspan_hwid & ~(ERSPAN_HWID_MASK >> 4)) {
747 ds_put_format(&errors, "%s: invalid erspan hardware ID: %s\n",
748 name, node->value);
749 err = EINVAL;
750 goto out;
751 }
7dc18ae9 752 }
ebe0e518
MV
753 } else if (!strcmp(node->key, "payload_type")) {
754 if (!strcmp(node->value, "mpls")) {
755 tnl_cfg.payload_ethertype = htons(ETH_TYPE_MPLS);
756 tnl_cfg.exts |= (1 << OVS_BAREUDP_EXT_MULTIPROTO_MODE);
757 } else if (!strcmp(node->value, "ip")) {
758 tnl_cfg.payload_ethertype = htons(ETH_TYPE_IP);
759 tnl_cfg.exts |= (1 << OVS_BAREUDP_EXT_MULTIPROTO_MODE);
760 } else {
761 uint16_t payload_ethertype;
762
763 if (str_to_u16(node->value, "payload_type",
764 &payload_ethertype)) {
765 err = EINVAL;
766 goto out;
767 }
768 tnl_cfg.payload_ethertype = htons(payload_ethertype);
769 }
b9c6da7e
MG
770 } else if (!strcmp(node->key, "remote_cert") ||
771 !strcmp(node->key, "remote_name") ||
772 !strcmp(node->key, "psk")) {
773 /* When configuring OVS for IPsec, these keys may be set in the
774 tunnel port's 'options' column. 'ovs-vswitchd' does not directly
775 use them, but they are read by 'ovs-monitor-ipsec'. In order to
776 suppress the "unknown %s argument" warning message below, we
777 handle them here by ignoring them. */
2b9d6589 778 } else {
439f39cb
GS
779 ds_put_format(&errors, "%s: unknown %s argument '%s'\n", name,
780 type, node->key);
2b9d6589
BP
781 }
782 }
783
875ab130
BP
784 enum tunnel_layers layers = tunnel_supported_layers(type, &tnl_cfg);
785 const char *full_type = (strcmp(type, "vxlan") ? type
ea4cb65c 786 : (tnl_cfg.exts & (1 << OVS_VXLAN_EXT_GPE)
3822ea06 787 ? "VXLAN-GPE" : "VXLAN (without GPE)"));
875ab130
BP
788 const char *packet_type = smap_get(args, "packet_type");
789 if (!packet_type) {
790 tnl_cfg.pt_mode = default_pt_mode(layers);
791 } else if (!strcmp(packet_type, "legacy_l2")) {
792 tnl_cfg.pt_mode = NETDEV_PT_LEGACY_L2;
793 if (!(layers & TNL_L2)) {
794 ds_put_format(&errors, "%s: legacy_l2 configured on %s tunnel "
795 "that cannot carry L2 traffic\n",
796 name, full_type);
797 err = EINVAL;
798 goto out;
799 }
800 } else if (!strcmp(packet_type, "legacy_l3")) {
801 tnl_cfg.pt_mode = NETDEV_PT_LEGACY_L3;
802 if (!(layers & TNL_L3)) {
803 ds_put_format(&errors, "%s: legacy_l3 configured on %s tunnel "
804 "that cannot carry L3 traffic\n",
805 name, full_type);
806 err = EINVAL;
807 goto out;
808 }
809 } else if (!strcmp(packet_type, "ptap")) {
810 tnl_cfg.pt_mode = NETDEV_PT_AWARE;
811 } else {
812 ds_put_format(&errors, "%s: unknown packet_type '%s'\n",
813 name, packet_type);
814 err = EINVAL;
815 goto out;
439f39cb
GS
816 }
817
3ae91c01 818 if (!ipv6_addr_is_set(&tnl_cfg.ipv6_dst) && !tnl_cfg.ip_dst_flow) {
9fff138e
DDP
819 ds_put_format(&errors,
820 "%s: %s type requires valid 'remote_ip' argument\n",
821 name, type);
822 err = EINVAL;
823 goto out;
2b9d6589 824 }
0ad90c84 825 if (tnl_cfg.ip_src_flow && !tnl_cfg.ip_dst_flow) {
9fff138e
DDP
826 ds_put_format(&errors,
827 "%s: %s type requires 'remote_ip=flow' "
828 "with 'local_ip=flow'\n",
829 name, type);
830 err = EINVAL;
831 goto out;
0ad90c84 832 }
3ae91c01 833 if (src_proto && dst_proto && src_proto != dst_proto) {
9fff138e
DDP
834 ds_put_format(&errors,
835 "%s: 'remote_ip' and 'local_ip' "
836 "has to be of the same address family\n",
837 name);
838 err = EINVAL;
839 goto out;
3ae91c01 840 }
f431bf7d
EJ
841 if (!tnl_cfg.ttl) {
842 tnl_cfg.ttl = DEFAULT_TTL;
843 }
844
845 tnl_cfg.in_key = parse_key(args, "in_key",
846 &tnl_cfg.in_key_present,
847 &tnl_cfg.in_key_flow);
f431bf7d
EJ
848
849 tnl_cfg.out_key = parse_key(args, "out_key",
850 &tnl_cfg.out_key_present,
851 &tnl_cfg.out_key_flow);
2b9d6589 852
189de33f
EC
853 if (is_concomitant_vxlan_tunnel_present(dev, &tnl_cfg)) {
854 ds_put_format(&errors, "%s: VXLAN-GBP, and non-VXLAN-GBP "
855 "tunnels can't be configured on the same "
856 "dst_port\n",
857 name);
858 err = EEXIST;
859 goto out;
860 }
861 update_vxlan_global_cfg(dev_, &dev->tnl_cfg, &tnl_cfg);
862
86383816 863 ovs_mutex_lock(&dev->mutex);
a1908399
AW
864 if (memcmp(&dev->tnl_cfg, &tnl_cfg, sizeof tnl_cfg)) {
865 dev->tnl_cfg = tnl_cfg;
866 tunnel_check_status_change__(dev);
867 netdev_change_seq_changed(dev_);
868 }
86383816 869 ovs_mutex_unlock(&dev->mutex);
f431bf7d 870
9fff138e
DDP
871 err = 0;
872
873out:
c296d3f8
DDP
874 if (errors.length) {
875 ds_chomp(&errors, '\n');
876 VLOG_WARN("%s", ds_cstr(&errors));
877 if (err) {
878 *errp = ds_steal_cstr(&errors);
879 }
9fff138e
DDP
880 }
881
882 ds_destroy(&errors);
883
884 return err;
c19e6535
BP
885}
886
2b9d6589 887static int
b5d57fc8 888get_tunnel_config(const struct netdev *dev, struct smap *args)
6d9e6eb4 889{
86383816 890 struct netdev_vport *netdev = netdev_vport_cast(dev);
63171f04 891 const char *type = netdev_get_type(dev);
86383816
BP
892 struct netdev_tunnel_config tnl_cfg;
893
894 ovs_mutex_lock(&netdev->mutex);
895 tnl_cfg = netdev->tnl_cfg;
896 ovs_mutex_unlock(&netdev->mutex);
6d9e6eb4 897
3ae91c01
JB
898 if (ipv6_addr_is_set(&tnl_cfg.ipv6_dst)) {
899 smap_add_ipv6(args, "remote_ip", &tnl_cfg.ipv6_dst);
86383816 900 } else if (tnl_cfg.ip_dst_flow) {
0ad90c84 901 smap_add(args, "remote_ip", "flow");
0a740f48
EJ
902 }
903
3ae91c01
JB
904 if (ipv6_addr_is_set(&tnl_cfg.ipv6_src)) {
905 smap_add_ipv6(args, "local_ip", &tnl_cfg.ipv6_src);
86383816 906 } else if (tnl_cfg.ip_src_flow) {
0ad90c84 907 smap_add(args, "local_ip", "flow");
7f804ea5 908 }
c19e6535 909
86383816 910 if (tnl_cfg.in_key_flow && tnl_cfg.out_key_flow) {
6d9e6eb4 911 smap_add(args, "key", "flow");
86383816
BP
912 } else if (tnl_cfg.in_key_present && tnl_cfg.out_key_present
913 && tnl_cfg.in_key == tnl_cfg.out_key) {
914 smap_add_format(args, "key", "%"PRIu64, ntohll(tnl_cfg.in_key));
6d9e6eb4 915 } else {
86383816 916 if (tnl_cfg.in_key_flow) {
b9ad7294 917 smap_add(args, "in_key", "flow");
86383816 918 } else if (tnl_cfg.in_key_present) {
b9ad7294 919 smap_add_format(args, "in_key", "%"PRIu64,
86383816 920 ntohll(tnl_cfg.in_key));
b9ad7294 921 }
6d9e6eb4 922
86383816 923 if (tnl_cfg.out_key_flow) {
b9ad7294 924 smap_add(args, "out_key", "flow");
86383816 925 } else if (tnl_cfg.out_key_present) {
b9ad7294 926 smap_add_format(args, "out_key", "%"PRIu64,
86383816 927 ntohll(tnl_cfg.out_key));
6d9e6eb4
BP
928 }
929 }
930
86383816 931 if (tnl_cfg.ttl_inherit) {
62827e6a 932 smap_add(args, "ttl", "inherit");
86383816
BP
933 } else if (tnl_cfg.ttl != DEFAULT_TTL) {
934 smap_add_format(args, "ttl", "%"PRIu8, tnl_cfg.ttl);
c19e6535
BP
935 }
936
86383816 937 if (tnl_cfg.tos_inherit) {
6d9e6eb4 938 smap_add(args, "tos", "inherit");
86383816
BP
939 } else if (tnl_cfg.tos) {
940 smap_add_format(args, "tos", "0x%x", tnl_cfg.tos);
6d9e6eb4
BP
941 }
942
86383816
BP
943 if (tnl_cfg.dst_port) {
944 uint16_t dst_port = ntohs(tnl_cfg.dst_port);
9eeb949b 945
c1fc1411
JG
946 if ((!strcmp("geneve", type) && dst_port != GENEVE_DST_PORT) ||
947 (!strcmp("vxlan", type) && dst_port != VXLAN_DST_PORT) ||
4237026e 948 (!strcmp("lisp", type) && dst_port != LISP_DST_PORT) ||
3c6d05a0 949 (!strcmp("stt", type) && dst_port != STT_DST_PORT) ||
ebe0e518
MV
950 (!strcmp("gtpu", type) && dst_port != GTPU_DST_PORT) ||
951 !strcmp("bareudp", type)) {
79f827fa
KM
952 smap_add_format(args, "dst_port", "%d", dst_port);
953 }
954 }
955
86383816 956 if (tnl_cfg.csum) {
6d9e6eb4
BP
957 smap_add(args, "csum", "true");
958 }
8a9ff93a 959
0ffff497
WT
960 if (tnl_cfg.set_seq) {
961 smap_add(args, "seq", "true");
962 }
963
875ab130
BP
964 enum tunnel_layers layers = tunnel_supported_layers(type, &tnl_cfg);
965 if (tnl_cfg.pt_mode != default_pt_mode(layers)) {
966 smap_add(args, "packet_type",
967 tnl_cfg.pt_mode == NETDEV_PT_LEGACY_L2 ? "legacy_l2"
968 : tnl_cfg.pt_mode == NETDEV_PT_LEGACY_L3 ? "legacy_l3"
969 : "ptap");
63171f04
JS
970 }
971
86383816 972 if (!tnl_cfg.dont_fragment) {
66409d1b
AE
973 smap_add(args, "df_default", "false");
974 }
6d9e6eb4 975
bf4bbd0d
PS
976 if (tnl_cfg.set_egress_pkt_mark) {
977 smap_add_format(args, "egress_pkt_mark",
978 "%"PRIu32, tnl_cfg.egress_pkt_mark);
979 }
7dc18ae9 980
068794b4
GR
981 if (!strcmp("erspan", type) || !strcmp("ip6erspan", type)) {
982 if (tnl_cfg.erspan_ver_flow) {
983 /* since version number is not determined,
984 * assume print all other as flow
985 */
986 smap_add(args, "erspan_ver", "flow");
987 smap_add(args, "erspan_idx", "flow");
988 smap_add(args, "erspan_dir", "flow");
989 smap_add(args, "erspan_hwid", "flow");
990 } else {
991 smap_add_format(args, "erspan_ver", "%d", tnl_cfg.erspan_ver);
992
993 if (tnl_cfg.erspan_ver == 1) {
994 if (tnl_cfg.erspan_idx_flow) {
995 smap_add(args, "erspan_idx", "flow");
996 } else {
997 smap_add_format(args, "erspan_idx", "0x%x",
998 tnl_cfg.erspan_idx);
999 }
1000 } else if (tnl_cfg.erspan_ver == 2) {
1001 if (tnl_cfg.erspan_dir_flow) {
1002 smap_add(args, "erspan_dir", "flow");
1003 } else {
1004 smap_add_format(args, "erspan_dir", "%d",
1005 tnl_cfg.erspan_dir);
1006 }
1007 if (tnl_cfg.erspan_hwid_flow) {
1008 smap_add(args, "erspan_hwid", "flow");
1009 } else {
1010 smap_add_format(args, "erspan_hwid", "0x%x",
1011 tnl_cfg.erspan_hwid);
1012 }
1013 }
1014 }
7dc18ae9
WT
1015 }
1016
6d9e6eb4
BP
1017 return 0;
1018}
0a740f48
EJ
1019\f
1020/* Code specific to patch ports. */
1021
161b6042
BP
1022/* If 'netdev' is a patch port, returns the name of its peer as a malloc()'d
1023 * string that the caller must free.
1024 *
1025 * If 'netdev' is not a patch port, returns NULL. */
1026char *
1027netdev_vport_patch_peer(const struct netdev *netdev_)
0a740f48 1028{
161b6042
BP
1029 char *peer = NULL;
1030
1031 if (netdev_vport_is_patch(netdev_)) {
1032 struct netdev_vport *netdev = netdev_vport_cast(netdev_);
86383816
BP
1033
1034 ovs_mutex_lock(&netdev->mutex);
161b6042
BP
1035 if (netdev->peer) {
1036 peer = xstrdup(netdev->peer);
1037 }
86383816 1038 ovs_mutex_unlock(&netdev->mutex);
161b6042
BP
1039 }
1040
1041 return peer;
0a740f48
EJ
1042}
1043
1044void
b9ad7294 1045netdev_vport_inc_rx(const struct netdev *netdev,
9e04d6f6 1046 const struct dpif_flow_stats *stats)
0a740f48 1047{
b5d57fc8
BP
1048 if (is_vport_class(netdev_get_class(netdev))) {
1049 struct netdev_vport *dev = netdev_vport_cast(netdev);
86383816
BP
1050
1051 ovs_mutex_lock(&dev->mutex);
0a740f48
EJ
1052 dev->stats.rx_packets += stats->n_packets;
1053 dev->stats.rx_bytes += stats->n_bytes;
86383816 1054 ovs_mutex_unlock(&dev->mutex);
0a740f48
EJ
1055 }
1056}
1057
1058void
b9ad7294
EJ
1059netdev_vport_inc_tx(const struct netdev *netdev,
1060 const struct dpif_flow_stats *stats)
0a740f48 1061{
b5d57fc8
BP
1062 if (is_vport_class(netdev_get_class(netdev))) {
1063 struct netdev_vport *dev = netdev_vport_cast(netdev);
86383816
BP
1064
1065 ovs_mutex_lock(&dev->mutex);
0a740f48
EJ
1066 dev->stats.tx_packets += stats->n_packets;
1067 dev->stats.tx_bytes += stats->n_bytes;
86383816 1068 ovs_mutex_unlock(&dev->mutex);
0a740f48
EJ
1069 }
1070}
1071
1072static int
b5d57fc8 1073get_patch_config(const struct netdev *dev_, struct smap *args)
0a740f48 1074{
b5d57fc8 1075 struct netdev_vport *dev = netdev_vport_cast(dev_);
0a740f48 1076
86383816 1077 ovs_mutex_lock(&dev->mutex);
0a740f48
EJ
1078 if (dev->peer) {
1079 smap_add(args, "peer", dev->peer);
1080 }
86383816
BP
1081 ovs_mutex_unlock(&dev->mutex);
1082
0a740f48
EJ
1083 return 0;
1084}
6d9e6eb4
BP
1085
1086static int
9fff138e 1087set_patch_config(struct netdev *dev_, const struct smap *args, char **errp)
2b9d6589 1088{
b5d57fc8
BP
1089 struct netdev_vport *dev = netdev_vport_cast(dev_);
1090 const char *name = netdev_get_name(dev_);
2b9d6589
BP
1091 const char *peer;
1092
79f1cbe9 1093 peer = smap_get(args, "peer");
2b9d6589 1094 if (!peer) {
9fff138e
DDP
1095 VLOG_ERR_BUF(errp, "%s: patch type requires valid 'peer' argument",
1096 name);
2b9d6589
BP
1097 return EINVAL;
1098 }
1099
79f1cbe9 1100 if (smap_count(args) > 1) {
9fff138e
DDP
1101 VLOG_ERR_BUF(errp, "%s: patch type takes only a 'peer' argument",
1102 name);
2b9d6589
BP
1103 return EINVAL;
1104 }
1105
2b9d6589 1106 if (!strcmp(name, peer)) {
9fff138e 1107 VLOG_ERR_BUF(errp, "%s: patch peer must not be self", name);
2b9d6589
BP
1108 return EINVAL;
1109 }
1110
86383816 1111 ovs_mutex_lock(&dev->mutex);
a1908399
AW
1112 if (!dev->peer || strcmp(dev->peer, peer)) {
1113 free(dev->peer);
1114 dev->peer = xstrdup(peer);
1115 netdev_change_seq_changed(dev_);
1116 }
86383816
BP
1117 ovs_mutex_unlock(&dev->mutex);
1118
2b9d6589
BP
1119 return 0;
1120}
6d9e6eb4
BP
1121
1122static int
89c09c1c 1123netdev_vport_get_stats(const struct netdev *netdev, struct netdev_stats *stats)
0a740f48 1124{
b5d57fc8 1125 struct netdev_vport *dev = netdev_vport_cast(netdev);
86383816
BP
1126
1127 ovs_mutex_lock(&dev->mutex);
d6e3feb5 1128 /* Passing only collected counters */
1129 stats->tx_packets = dev->stats.tx_packets;
1130 stats->tx_bytes = dev->stats.tx_bytes;
1131 stats->rx_packets = dev->stats.rx_packets;
1132 stats->rx_bytes = dev->stats.rx_bytes;
86383816
BP
1133 ovs_mutex_unlock(&dev->mutex);
1134
6d9e6eb4
BP
1135 return 0;
1136}
a36de779 1137
875ab130 1138static enum netdev_pt_mode
89c09c1c 1139netdev_vport_get_pt_mode(const struct netdev *netdev)
875ab130
BP
1140{
1141 struct netdev_vport *dev = netdev_vport_cast(netdev);
1142
1143 return dev->tnl_cfg.pt_mode;
1144}
1145
1146
2b9d6589 1147\f
01b25786
PB
1148#ifdef __linux__
1149static int
5e530e26 1150netdev_vport_get_ifindex(const struct netdev *netdev_)
01b25786
PB
1151{
1152 char buf[NETDEV_VPORT_NAME_BUFSIZE];
1153 const char *name = netdev_vport_get_dpif_port(netdev_, buf, sizeof(buf));
1154
1155 return linux_get_ifindex(name);
1156}
1157
01b25786 1158#define NETDEV_VPORT_GET_IFINDEX netdev_vport_get_ifindex
01b25786
PB
1159#else /* !__linux__ */
1160#define NETDEV_VPORT_GET_IFINDEX NULL
01b25786
PB
1161#endif /* __linux__ */
1162
89c09c1c
BP
1163#define VPORT_FUNCTIONS_COMMON \
1164 .run = netdev_vport_run, \
1165 .wait = netdev_vport_wait, \
1166 .alloc = netdev_vport_alloc, \
1167 .construct = netdev_vport_construct, \
1168 .destruct = netdev_vport_destruct, \
1169 .dealloc = netdev_vport_dealloc, \
1170 .set_etheraddr = netdev_vport_set_etheraddr, \
1171 .get_etheraddr = netdev_vport_get_etheraddr, \
1172 .get_stats = netdev_vport_get_stats, \
1173 .get_pt_mode = netdev_vport_get_pt_mode, \
5fc5c50f 1174 .update_flags = netdev_vport_update_flags
89c09c1c
BP
1175
1176#define TUNNEL_FUNCTIONS_COMMON \
1177 VPORT_FUNCTIONS_COMMON, \
1178 .get_config = get_tunnel_config, \
1179 .set_config = set_tunnel_config, \
1180 .get_tunnel_config = get_netdev_tunnel_config, \
1181 .get_status = tunnel_get_status
db078f85 1182
2b9d6589 1183void
c060c4cf 1184netdev_vport_tunnel_register(void)
2b9d6589 1185{
a5d4fadd
JG
1186 /* The name of the dpif_port should be short enough to accomodate adding
1187 * a port number to the end if one is necessary. */
189de33f 1188 static struct vport_class vport_classes[] = {
89c09c1c
BP
1189 { "genev_sys",
1190 {
1191 TUNNEL_FUNCTIONS_COMMON,
1192 .type = "geneve",
1193 .build_header = netdev_geneve_build_header,
1194 .push_header = netdev_tnl_push_udp_header,
1195 .pop_header = netdev_geneve_pop_header,
1196 .get_ifindex = NETDEV_VPORT_GET_IFINDEX,
189de33f
EC
1197 },
1198 {{NULL, NULL, 0, 0}}
89c09c1c
BP
1199 },
1200 { "gre_sys",
1201 {
1202 TUNNEL_FUNCTIONS_COMMON,
1203 .type = "gre",
1204 .build_header = netdev_gre_build_header,
1205 .push_header = netdev_gre_push_header,
5e63eaa9
EB
1206 .pop_header = netdev_gre_pop_header,
1207 .get_ifindex = NETDEV_VPORT_GET_IFINDEX,
189de33f
EC
1208 },
1209 {{NULL, NULL, 0, 0}}
89c09c1c
BP
1210 },
1211 { "vxlan_sys",
1212 {
1213 TUNNEL_FUNCTIONS_COMMON,
1214 .type = "vxlan",
1215 .build_header = netdev_vxlan_build_header,
1216 .push_header = netdev_tnl_push_udp_header,
1217 .pop_header = netdev_vxlan_pop_header,
1218 .get_ifindex = NETDEV_VPORT_GET_IFINDEX
189de33f
EC
1219 },
1220 {{NULL, NULL, 0, 0}}
89c09c1c
BP
1221 },
1222 { "lisp_sys",
1223 {
1224 TUNNEL_FUNCTIONS_COMMON,
1225 .type = "lisp"
189de33f
EC
1226 },
1227 {{NULL, NULL, 0, 0}}
89c09c1c
BP
1228 },
1229 { "stt_sys",
1230 {
1231 TUNNEL_FUNCTIONS_COMMON,
1232 .type = "stt"
189de33f
EC
1233 },
1234 {{NULL, NULL, 0, 0}}
89c09c1c
BP
1235 },
1236 { "erspan_sys",
1237 {
1238 TUNNEL_FUNCTIONS_COMMON,
1239 .type = "erspan",
1240 .build_header = netdev_erspan_build_header,
1241 .push_header = netdev_erspan_push_header,
1242 .pop_header = netdev_erspan_pop_header
189de33f
EC
1243 },
1244 {{NULL, NULL, 0, 0}}
89c09c1c
BP
1245 },
1246 { "ip6erspan_sys",
1247 {
1248 TUNNEL_FUNCTIONS_COMMON,
1249 .type = "ip6erspan",
1250 .build_header = netdev_erspan_build_header,
1251 .push_header = netdev_erspan_push_header,
1252 .pop_header = netdev_erspan_pop_header
189de33f
EC
1253 },
1254 {{NULL, NULL, 0, 0}}
89c09c1c
BP
1255 },
1256 { "ip6gre_sys",
1257 {
1258 TUNNEL_FUNCTIONS_COMMON,
1259 .type = "ip6gre",
1260 .build_header = netdev_gre_build_header,
1261 .push_header = netdev_gre_push_header,
8732450c
EB
1262 .pop_header = netdev_gre_pop_header,
1263 .get_ifindex = NETDEV_VPORT_GET_IFINDEX,
189de33f
EC
1264 },
1265 {{NULL, NULL, 0, 0}}
89c09c1c 1266 },
3c6d05a0
WT
1267 { "gtpu_sys",
1268 {
1269 TUNNEL_FUNCTIONS_COMMON,
1270 .type = "gtpu",
1271 .build_header = netdev_gtpu_build_header,
1272 .push_header = netdev_gtpu_push_header,
1273 .pop_header = netdev_gtpu_pop_header,
1274 },
1275 {{NULL, NULL, 0, 0}}
1276 },
ebe0e518
MV
1277 { "udp_sys",
1278 {
1279 TUNNEL_FUNCTIONS_COMMON,
1280 .type = "bareudp",
1281 .get_ifindex = NETDEV_VPORT_GET_IFINDEX,
1282 },
1283 {{NULL, NULL, 0, 0}}
1284 },
3c6d05a0 1285
c3827f61 1286 };
86383816 1287 static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
c3827f61 1288
86383816
BP
1289 if (ovsthread_once_start(&once)) {
1290 int i;
c3827f61 1291
7c54c27f 1292 for (i = 0; i < ARRAY_SIZE(vport_classes); i++) {
189de33f 1293 simap_init(&vport_classes[i].global_cfg_tracker);
7c54c27f
BP
1294 netdev_register_provider(&vport_classes[i].netdev_class);
1295 }
a36de779
PS
1296
1297 unixctl_command_register("tnl/egress_port_range", "min max", 0, 2,
6b241d64 1298 netdev_tnl_egress_port_range, NULL);
a36de779 1299
86383816 1300 ovsthread_once_done(&once);
c3827f61 1301 }
2b9d6589 1302}
c060c4cf
EJ
1303
1304void
1305netdev_vport_patch_register(void)
1306{
189de33f 1307 static struct vport_class patch_class = {
89c09c1c
BP
1308 NULL,
1309 { VPORT_FUNCTIONS_COMMON,
1310 .type = "patch",
1311 .get_config = get_patch_config,
1312 .set_config = set_patch_config,
189de33f
EC
1313 },
1314 {{NULL, NULL, 0, 0}}
89c09c1c 1315 };
189de33f 1316 simap_init(&patch_class.global_cfg_tracker);
c060c4cf
EJ
1317 netdev_register_provider(&patch_class.netdev_class);
1318}