b3b8a11088ec533e5f933778acbf235b76cc710f
[cascardo/ovs.git] / lib / netdev-vport.c
1 /*
2  * Copyright (c) 2010, 2011, 2012, 2013, 2014 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 "netdev-vport.h"
20
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <sys/socket.h>
24 #include <net/if.h>
25 #include <sys/ioctl.h>
26
27 #include "byte-order.h"
28 #include "daemon.h"
29 #include "dirs.h"
30 #include "dpif.h"
31 #include "hash.h"
32 #include "hmap.h"
33 #include "list.h"
34 #include "netdev-provider.h"
35 #include "ofpbuf.h"
36 #include "ovs-router.h"
37 #include "packets.h"
38 #include "poll-loop.h"
39 #include "route-table.h"
40 #include "shash.h"
41 #include "socket-util.h"
42 #include "vlog.h"
43
44 VLOG_DEFINE_THIS_MODULE(netdev_vport);
45
46 #define GENEVE_DST_PORT 6081
47 #define VXLAN_DST_PORT 4789
48 #define LISP_DST_PORT 4341
49
50 #define DEFAULT_TTL 64
51
52 struct netdev_vport {
53     struct netdev up;
54
55     /* Protects all members below. */
56     struct ovs_mutex mutex;
57
58     uint8_t etheraddr[ETH_ADDR_LEN];
59     struct netdev_stats stats;
60
61     /* Tunnels. */
62     struct netdev_tunnel_config tnl_cfg;
63     char egress_iface[IFNAMSIZ];
64     bool carrier_status;
65
66     /* Patch Ports. */
67     char *peer;
68 };
69
70 struct vport_class {
71     const char *dpif_port;
72     struct netdev_class netdev_class;
73 };
74
75 /* Last read of the route-table's change number. */
76 static uint64_t rt_change_seqno;
77
78 static int netdev_vport_construct(struct netdev *);
79 static int get_patch_config(const struct netdev *netdev, struct smap *args);
80 static int get_tunnel_config(const struct netdev *, struct smap *args);
81 static bool tunnel_check_status_change__(struct netdev_vport *);
82
83 static bool
84 is_vport_class(const struct netdev_class *class)
85 {
86     return class->construct == netdev_vport_construct;
87 }
88
89 bool
90 netdev_vport_is_vport_class(const struct netdev_class *class)
91 {
92     return is_vport_class(class);
93 }
94
95 static const struct vport_class *
96 vport_class_cast(const struct netdev_class *class)
97 {
98     ovs_assert(is_vport_class(class));
99     return CONTAINER_OF(class, struct vport_class, netdev_class);
100 }
101
102 static struct netdev_vport *
103 netdev_vport_cast(const struct netdev *netdev)
104 {
105     ovs_assert(is_vport_class(netdev_get_class(netdev)));
106     return CONTAINER_OF(netdev, struct netdev_vport, up);
107 }
108
109 static const struct netdev_tunnel_config *
110 get_netdev_tunnel_config(const struct netdev *netdev)
111 {
112     return &netdev_vport_cast(netdev)->tnl_cfg;
113 }
114
115 bool
116 netdev_vport_is_patch(const struct netdev *netdev)
117 {
118     const struct netdev_class *class = netdev_get_class(netdev);
119
120     return class->get_config == get_patch_config;
121 }
122
123 bool
124 netdev_vport_is_layer3(const struct netdev *dev)
125 {
126     const char *type = netdev_get_type(dev);
127
128     return (!strcmp("lisp", type));
129 }
130
131 static bool
132 netdev_vport_needs_dst_port(const struct netdev *dev)
133 {
134     const struct netdev_class *class = netdev_get_class(dev);
135     const char *type = netdev_get_type(dev);
136
137     return (class->get_config == get_tunnel_config &&
138             (!strcmp("geneve", type) || !strcmp("vxlan", type) ||
139              !strcmp("lisp", type)));
140 }
141
142 const char *
143 netdev_vport_class_get_dpif_port(const struct netdev_class *class)
144 {
145     return is_vport_class(class) ? vport_class_cast(class)->dpif_port : NULL;
146 }
147
148 const char *
149 netdev_vport_get_dpif_port(const struct netdev *netdev,
150                            char namebuf[], size_t bufsize)
151 {
152     const struct netdev_class *class = netdev_get_class(netdev);
153     const char *dpif_port = netdev_vport_class_get_dpif_port(class);
154
155     if (!dpif_port) {
156         return netdev_get_name(netdev);
157     }
158
159     if (netdev_vport_needs_dst_port(netdev)) {
160         const struct netdev_vport *vport = netdev_vport_cast(netdev);
161
162         /*
163          * Note: IFNAMSIZ is 16 bytes long. Implementations should choose
164          * a dpif port name that is short enough to fit including any
165          * port numbers but assert just in case.
166          */
167         BUILD_ASSERT(NETDEV_VPORT_NAME_BUFSIZE >= IFNAMSIZ);
168         ovs_assert(strlen(dpif_port) + 6 < IFNAMSIZ);
169         snprintf(namebuf, bufsize, "%s_%d", dpif_port,
170                  ntohs(vport->tnl_cfg.dst_port));
171         return namebuf;
172     } else {
173         return dpif_port;
174     }
175 }
176
177 char *
178 netdev_vport_get_dpif_port_strdup(const struct netdev *netdev)
179 {
180     char namebuf[NETDEV_VPORT_NAME_BUFSIZE];
181
182     return xstrdup(netdev_vport_get_dpif_port(netdev, namebuf,
183                                               sizeof namebuf));
184 }
185
186 /* Whenever the route-table change number is incremented,
187  * netdev_vport_route_changed() should be called to update
188  * the corresponding tunnel interface status. */
189 static void
190 netdev_vport_route_changed(void)
191 {
192     struct netdev **vports;
193     size_t i, n_vports;
194
195     vports = netdev_get_vports(&n_vports);
196     for (i = 0; i < n_vports; i++) {
197         struct netdev *netdev_ = vports[i];
198         struct netdev_vport *netdev = netdev_vport_cast(netdev_);
199
200         ovs_mutex_lock(&netdev->mutex);
201         /* Finds all tunnel vports. */
202         if (netdev->tnl_cfg.ip_dst) {
203             if (tunnel_check_status_change__(netdev)) {
204                 netdev_change_seq_changed(netdev_);
205             }
206         }
207         ovs_mutex_unlock(&netdev->mutex);
208
209         netdev_close(netdev_);
210     }
211
212     free(vports);
213 }
214
215 static struct netdev *
216 netdev_vport_alloc(void)
217 {
218     struct netdev_vport *netdev = xzalloc(sizeof *netdev);
219     return &netdev->up;
220 }
221
222 static int
223 netdev_vport_construct(struct netdev *netdev_)
224 {
225     struct netdev_vport *netdev = netdev_vport_cast(netdev_);
226
227     ovs_mutex_init(&netdev->mutex);
228     eth_addr_random(netdev->etheraddr);
229
230     route_table_register();
231
232     return 0;
233 }
234
235 static void
236 netdev_vport_destruct(struct netdev *netdev_)
237 {
238     struct netdev_vport *netdev = netdev_vport_cast(netdev_);
239
240     route_table_unregister();
241     free(netdev->peer);
242     ovs_mutex_destroy(&netdev->mutex);
243 }
244
245 static void
246 netdev_vport_dealloc(struct netdev *netdev_)
247 {
248     struct netdev_vport *netdev = netdev_vport_cast(netdev_);
249     free(netdev);
250 }
251
252 static int
253 netdev_vport_set_etheraddr(struct netdev *netdev_,
254                            const uint8_t mac[ETH_ADDR_LEN])
255 {
256     struct netdev_vport *netdev = netdev_vport_cast(netdev_);
257
258     ovs_mutex_lock(&netdev->mutex);
259     memcpy(netdev->etheraddr, mac, ETH_ADDR_LEN);
260     ovs_mutex_unlock(&netdev->mutex);
261     netdev_change_seq_changed(netdev_);
262
263     return 0;
264 }
265
266 static int
267 netdev_vport_get_etheraddr(const struct netdev *netdev_,
268                            uint8_t mac[ETH_ADDR_LEN])
269 {
270     struct netdev_vport *netdev = netdev_vport_cast(netdev_);
271
272     ovs_mutex_lock(&netdev->mutex);
273     memcpy(mac, netdev->etheraddr, ETH_ADDR_LEN);
274     ovs_mutex_unlock(&netdev->mutex);
275
276     return 0;
277 }
278
279 /* Checks if the tunnel status has changed and returns a boolean.
280  * Updates the tunnel status if it has changed. */
281 static bool
282 tunnel_check_status_change__(struct netdev_vport *netdev)
283     OVS_REQUIRES(netdev->mutex)
284 {
285     char iface[IFNAMSIZ];
286     bool status = false;
287     ovs_be32 route;
288     ovs_be32 gw;
289
290     iface[0] = '\0';
291     route = netdev->tnl_cfg.ip_dst;
292     if (ovs_router_lookup(route, iface, &gw)) {
293         struct netdev *egress_netdev;
294
295         if (!netdev_open(iface, "system", &egress_netdev)) {
296             status = netdev_get_carrier(egress_netdev);
297             netdev_close(egress_netdev);
298         }
299     }
300
301     if (strcmp(netdev->egress_iface, iface)
302         || netdev->carrier_status != status) {
303         ovs_strlcpy(netdev->egress_iface, iface, IFNAMSIZ);
304         netdev->carrier_status = status;
305
306         return true;
307     }
308
309     return false;
310 }
311
312 static int
313 tunnel_get_status(const struct netdev *netdev_, struct smap *smap)
314 {
315     struct netdev_vport *netdev = netdev_vport_cast(netdev_);
316
317     if (netdev->egress_iface[0]) {
318         smap_add(smap, "tunnel_egress_iface", netdev->egress_iface);
319
320         smap_add(smap, "tunnel_egress_iface_carrier",
321                  netdev->carrier_status ? "up" : "down");
322     }
323
324     return 0;
325 }
326
327 static int
328 netdev_vport_update_flags(struct netdev *netdev OVS_UNUSED,
329                           enum netdev_flags off,
330                           enum netdev_flags on OVS_UNUSED,
331                           enum netdev_flags *old_flagsp)
332 {
333     if (off & (NETDEV_UP | NETDEV_PROMISC)) {
334         return EOPNOTSUPP;
335     }
336
337     *old_flagsp = NETDEV_UP | NETDEV_PROMISC;
338     return 0;
339 }
340
341 static void
342 netdev_vport_run(void)
343 {
344     uint64_t seq;
345
346     route_table_run();
347     seq = route_table_get_change_seq();
348     if (rt_change_seqno != seq) {
349         rt_change_seqno = seq;
350         netdev_vport_route_changed();
351     }
352 }
353
354 static void
355 netdev_vport_wait(void)
356 {
357     uint64_t seq;
358
359     route_table_wait();
360     seq = route_table_get_change_seq();
361     if (rt_change_seqno != seq) {
362         poll_immediate_wake();
363     }
364 }
365 \f
366 /* Code specific to tunnel types. */
367
368 static ovs_be64
369 parse_key(const struct smap *args, const char *name,
370           bool *present, bool *flow)
371 {
372     const char *s;
373
374     *present = false;
375     *flow = false;
376
377     s = smap_get(args, name);
378     if (!s) {
379         s = smap_get(args, "key");
380         if (!s) {
381             return 0;
382         }
383     }
384
385     *present = true;
386
387     if (!strcmp(s, "flow")) {
388         *flow = true;
389         return 0;
390     } else {
391         return htonll(strtoull(s, NULL, 0));
392     }
393 }
394
395 static int
396 set_tunnel_config(struct netdev *dev_, const struct smap *args)
397 {
398     struct netdev_vport *dev = netdev_vport_cast(dev_);
399     const char *name = netdev_get_name(dev_);
400     const char *type = netdev_get_type(dev_);
401     bool ipsec_mech_set, needs_dst_port, has_csum;
402     struct netdev_tunnel_config tnl_cfg;
403     struct smap_node *node;
404
405     has_csum = strstr(type, "gre");
406     ipsec_mech_set = false;
407     memset(&tnl_cfg, 0, sizeof tnl_cfg);
408
409     needs_dst_port = netdev_vport_needs_dst_port(dev_);
410     tnl_cfg.ipsec = strstr(type, "ipsec");
411     tnl_cfg.dont_fragment = true;
412
413     SMAP_FOR_EACH (node, args) {
414         if (!strcmp(node->key, "remote_ip")) {
415             struct in_addr in_addr;
416             if (!strcmp(node->value, "flow")) {
417                 tnl_cfg.ip_dst_flow = true;
418                 tnl_cfg.ip_dst = htonl(0);
419             } else if (lookup_ip(node->value, &in_addr)) {
420                 VLOG_WARN("%s: bad %s 'remote_ip'", name, type);
421             } else if (ip_is_multicast(in_addr.s_addr)) {
422                 VLOG_WARN("%s: multicast remote_ip="IP_FMT" not allowed",
423                           name, IP_ARGS(in_addr.s_addr));
424                 return EINVAL;
425             } else {
426                 tnl_cfg.ip_dst = in_addr.s_addr;
427             }
428         } else if (!strcmp(node->key, "local_ip")) {
429             struct in_addr in_addr;
430             if (!strcmp(node->value, "flow")) {
431                 tnl_cfg.ip_src_flow = true;
432                 tnl_cfg.ip_src = htonl(0);
433             } else if (lookup_ip(node->value, &in_addr)) {
434                 VLOG_WARN("%s: bad %s 'local_ip'", name, type);
435             } else {
436                 tnl_cfg.ip_src = in_addr.s_addr;
437             }
438         } else if (!strcmp(node->key, "tos")) {
439             if (!strcmp(node->value, "inherit")) {
440                 tnl_cfg.tos_inherit = true;
441             } else {
442                 char *endptr;
443                 int tos;
444                 tos = strtol(node->value, &endptr, 0);
445                 if (*endptr == '\0' && tos == (tos & IP_DSCP_MASK)) {
446                     tnl_cfg.tos = tos;
447                 } else {
448                     VLOG_WARN("%s: invalid TOS %s", name, node->value);
449                 }
450             }
451         } else if (!strcmp(node->key, "ttl")) {
452             if (!strcmp(node->value, "inherit")) {
453                 tnl_cfg.ttl_inherit = true;
454             } else {
455                 tnl_cfg.ttl = atoi(node->value);
456             }
457         } else if (!strcmp(node->key, "dst_port") && needs_dst_port) {
458             tnl_cfg.dst_port = htons(atoi(node->value));
459         } else if (!strcmp(node->key, "csum") && has_csum) {
460             if (!strcmp(node->value, "true")) {
461                 tnl_cfg.csum = true;
462             }
463         } else if (!strcmp(node->key, "df_default")) {
464             if (!strcmp(node->value, "false")) {
465                 tnl_cfg.dont_fragment = false;
466             }
467         } else if (!strcmp(node->key, "peer_cert") && tnl_cfg.ipsec) {
468             if (smap_get(args, "certificate")) {
469                 ipsec_mech_set = true;
470             } else {
471                 const char *use_ssl_cert;
472
473                 /* If the "use_ssl_cert" is true, then "certificate" and
474                  * "private_key" will be pulled from the SSL table.  The
475                  * use of this option is strongly discouraged, since it
476                  * will like be removed when multiple SSL configurations
477                  * are supported by OVS.
478                  */
479                 use_ssl_cert = smap_get(args, "use_ssl_cert");
480                 if (!use_ssl_cert || strcmp(use_ssl_cert, "true")) {
481                     VLOG_ERR("%s: 'peer_cert' requires 'certificate' argument",
482                              name);
483                     return EINVAL;
484                 }
485                 ipsec_mech_set = true;
486             }
487         } else if (!strcmp(node->key, "psk") && tnl_cfg.ipsec) {
488             ipsec_mech_set = true;
489         } else if (tnl_cfg.ipsec
490                 && (!strcmp(node->key, "certificate")
491                     || !strcmp(node->key, "private_key")
492                     || !strcmp(node->key, "use_ssl_cert"))) {
493             /* Ignore options not used by the netdev. */
494         } else if (!strcmp(node->key, "key") ||
495                    !strcmp(node->key, "in_key") ||
496                    !strcmp(node->key, "out_key")) {
497             /* Handled separately below. */
498         } else {
499             VLOG_WARN("%s: unknown %s argument '%s'", name, type, node->key);
500         }
501     }
502
503     /* Add a default destination port for tunnel ports if none specified. */
504     if (!strcmp(type, "geneve") && !tnl_cfg.dst_port) {
505         tnl_cfg.dst_port = htons(GENEVE_DST_PORT);
506     }
507
508     if (!strcmp(type, "vxlan") && !tnl_cfg.dst_port) {
509         tnl_cfg.dst_port = htons(VXLAN_DST_PORT);
510     }
511
512     if (!strcmp(type, "lisp") && !tnl_cfg.dst_port) {
513         tnl_cfg.dst_port = htons(LISP_DST_PORT);
514     }
515
516     if (tnl_cfg.ipsec) {
517         static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER;
518         static pid_t pid = 0;
519
520 #ifndef _WIN32
521         ovs_mutex_lock(&mutex);
522         if (pid <= 0) {
523             char *file_name = xasprintf("%s/%s", ovs_rundir(),
524                                         "ovs-monitor-ipsec.pid");
525             pid = read_pidfile(file_name);
526             free(file_name);
527         }
528         ovs_mutex_unlock(&mutex);
529 #endif
530
531         if (pid < 0) {
532             VLOG_ERR("%s: IPsec requires the ovs-monitor-ipsec daemon",
533                      name);
534             return EINVAL;
535         }
536
537         if (smap_get(args, "peer_cert") && smap_get(args, "psk")) {
538             VLOG_ERR("%s: cannot define both 'peer_cert' and 'psk'", name);
539             return EINVAL;
540         }
541
542         if (!ipsec_mech_set) {
543             VLOG_ERR("%s: IPsec requires an 'peer_cert' or psk' argument",
544                      name);
545             return EINVAL;
546         }
547     }
548
549     if (!tnl_cfg.ip_dst && !tnl_cfg.ip_dst_flow) {
550         VLOG_ERR("%s: %s type requires valid 'remote_ip' argument",
551                  name, type);
552         return EINVAL;
553     }
554     if (tnl_cfg.ip_src_flow && !tnl_cfg.ip_dst_flow) {
555         VLOG_ERR("%s: %s type requires 'remote_ip=flow' with 'local_ip=flow'",
556                  name, type);
557         return EINVAL;
558     }
559     if (!tnl_cfg.ttl) {
560         tnl_cfg.ttl = DEFAULT_TTL;
561     }
562
563     tnl_cfg.in_key = parse_key(args, "in_key",
564                                &tnl_cfg.in_key_present,
565                                &tnl_cfg.in_key_flow);
566
567     tnl_cfg.out_key = parse_key(args, "out_key",
568                                &tnl_cfg.out_key_present,
569                                &tnl_cfg.out_key_flow);
570
571     ovs_mutex_lock(&dev->mutex);
572     dev->tnl_cfg = tnl_cfg;
573     tunnel_check_status_change__(dev);
574     netdev_change_seq_changed(dev_);
575     ovs_mutex_unlock(&dev->mutex);
576
577     return 0;
578 }
579
580 static int
581 get_tunnel_config(const struct netdev *dev, struct smap *args)
582 {
583     struct netdev_vport *netdev = netdev_vport_cast(dev);
584     struct netdev_tunnel_config tnl_cfg;
585
586     ovs_mutex_lock(&netdev->mutex);
587     tnl_cfg = netdev->tnl_cfg;
588     ovs_mutex_unlock(&netdev->mutex);
589
590     if (tnl_cfg.ip_dst) {
591         smap_add_format(args, "remote_ip", IP_FMT, IP_ARGS(tnl_cfg.ip_dst));
592     } else if (tnl_cfg.ip_dst_flow) {
593         smap_add(args, "remote_ip", "flow");
594     }
595
596     if (tnl_cfg.ip_src) {
597         smap_add_format(args, "local_ip", IP_FMT, IP_ARGS(tnl_cfg.ip_src));
598     } else if (tnl_cfg.ip_src_flow) {
599         smap_add(args, "local_ip", "flow");
600     }
601
602     if (tnl_cfg.in_key_flow && tnl_cfg.out_key_flow) {
603         smap_add(args, "key", "flow");
604     } else if (tnl_cfg.in_key_present && tnl_cfg.out_key_present
605                && tnl_cfg.in_key == tnl_cfg.out_key) {
606         smap_add_format(args, "key", "%"PRIu64, ntohll(tnl_cfg.in_key));
607     } else {
608         if (tnl_cfg.in_key_flow) {
609             smap_add(args, "in_key", "flow");
610         } else if (tnl_cfg.in_key_present) {
611             smap_add_format(args, "in_key", "%"PRIu64,
612                             ntohll(tnl_cfg.in_key));
613         }
614
615         if (tnl_cfg.out_key_flow) {
616             smap_add(args, "out_key", "flow");
617         } else if (tnl_cfg.out_key_present) {
618             smap_add_format(args, "out_key", "%"PRIu64,
619                             ntohll(tnl_cfg.out_key));
620         }
621     }
622
623     if (tnl_cfg.ttl_inherit) {
624         smap_add(args, "ttl", "inherit");
625     } else if (tnl_cfg.ttl != DEFAULT_TTL) {
626         smap_add_format(args, "ttl", "%"PRIu8, tnl_cfg.ttl);
627     }
628
629     if (tnl_cfg.tos_inherit) {
630         smap_add(args, "tos", "inherit");
631     } else if (tnl_cfg.tos) {
632         smap_add_format(args, "tos", "0x%x", tnl_cfg.tos);
633     }
634
635     if (tnl_cfg.dst_port) {
636         uint16_t dst_port = ntohs(tnl_cfg.dst_port);
637         const char *type = netdev_get_type(dev);
638
639         if ((!strcmp("geneve", type) && dst_port != GENEVE_DST_PORT) ||
640             (!strcmp("vxlan", type) && dst_port != VXLAN_DST_PORT) ||
641             (!strcmp("lisp", type) && dst_port != LISP_DST_PORT)) {
642             smap_add_format(args, "dst_port", "%d", dst_port);
643         }
644     }
645
646     if (tnl_cfg.csum) {
647         smap_add(args, "csum", "true");
648     }
649
650     if (!tnl_cfg.dont_fragment) {
651         smap_add(args, "df_default", "false");
652     }
653
654     return 0;
655 }
656 \f
657 /* Code specific to patch ports. */
658
659 /* If 'netdev' is a patch port, returns the name of its peer as a malloc()'d
660  * string that the caller must free.
661  *
662  * If 'netdev' is not a patch port, returns NULL. */
663 char *
664 netdev_vport_patch_peer(const struct netdev *netdev_)
665 {
666     char *peer = NULL;
667
668     if (netdev_vport_is_patch(netdev_)) {
669         struct netdev_vport *netdev = netdev_vport_cast(netdev_);
670
671         ovs_mutex_lock(&netdev->mutex);
672         if (netdev->peer) {
673             peer = xstrdup(netdev->peer);
674         }
675         ovs_mutex_unlock(&netdev->mutex);
676     }
677
678     return peer;
679 }
680
681 void
682 netdev_vport_inc_rx(const struct netdev *netdev,
683                     const struct dpif_flow_stats *stats)
684 {
685     if (is_vport_class(netdev_get_class(netdev))) {
686         struct netdev_vport *dev = netdev_vport_cast(netdev);
687
688         ovs_mutex_lock(&dev->mutex);
689         dev->stats.rx_packets += stats->n_packets;
690         dev->stats.rx_bytes += stats->n_bytes;
691         ovs_mutex_unlock(&dev->mutex);
692     }
693 }
694
695 void
696 netdev_vport_inc_tx(const struct netdev *netdev,
697                     const struct dpif_flow_stats *stats)
698 {
699     if (is_vport_class(netdev_get_class(netdev))) {
700         struct netdev_vport *dev = netdev_vport_cast(netdev);
701
702         ovs_mutex_lock(&dev->mutex);
703         dev->stats.tx_packets += stats->n_packets;
704         dev->stats.tx_bytes += stats->n_bytes;
705         ovs_mutex_unlock(&dev->mutex);
706     }
707 }
708
709 static int
710 get_patch_config(const struct netdev *dev_, struct smap *args)
711 {
712     struct netdev_vport *dev = netdev_vport_cast(dev_);
713
714     ovs_mutex_lock(&dev->mutex);
715     if (dev->peer) {
716         smap_add(args, "peer", dev->peer);
717     }
718     ovs_mutex_unlock(&dev->mutex);
719
720     return 0;
721 }
722
723 static int
724 set_patch_config(struct netdev *dev_, const struct smap *args)
725 {
726     struct netdev_vport *dev = netdev_vport_cast(dev_);
727     const char *name = netdev_get_name(dev_);
728     const char *peer;
729
730     peer = smap_get(args, "peer");
731     if (!peer) {
732         VLOG_ERR("%s: patch type requires valid 'peer' argument", name);
733         return EINVAL;
734     }
735
736     if (smap_count(args) > 1) {
737         VLOG_ERR("%s: patch type takes only a 'peer' argument", name);
738         return EINVAL;
739     }
740
741     if (!strcmp(name, peer)) {
742         VLOG_ERR("%s: patch peer must not be self", name);
743         return EINVAL;
744     }
745
746     ovs_mutex_lock(&dev->mutex);
747     free(dev->peer);
748     dev->peer = xstrdup(peer);
749     netdev_change_seq_changed(dev_);
750     ovs_mutex_unlock(&dev->mutex);
751
752     return 0;
753 }
754
755 static int
756 get_stats(const struct netdev *netdev, struct netdev_stats *stats)
757 {
758     struct netdev_vport *dev = netdev_vport_cast(netdev);
759
760     ovs_mutex_lock(&dev->mutex);
761     *stats = dev->stats;
762     ovs_mutex_unlock(&dev->mutex);
763
764     return 0;
765 }
766 \f
767 #define VPORT_FUNCTIONS(GET_CONFIG, SET_CONFIG,             \
768                         GET_TUNNEL_CONFIG, GET_STATUS)      \
769     NULL,                                                   \
770     netdev_vport_run,                                       \
771     netdev_vport_wait,                                      \
772                                                             \
773     netdev_vport_alloc,                                     \
774     netdev_vport_construct,                                 \
775     netdev_vport_destruct,                                  \
776     netdev_vport_dealloc,                                   \
777     GET_CONFIG,                                             \
778     SET_CONFIG,                                             \
779     GET_TUNNEL_CONFIG,                                      \
780     NULL,                       /* get_numa_id */           \
781     NULL,                       /* set_multiq */            \
782                                                             \
783     NULL,                       /* send */                  \
784     NULL,                       /* send_wait */             \
785                                                             \
786     netdev_vport_set_etheraddr,                             \
787     netdev_vport_get_etheraddr,                             \
788     NULL,                       /* get_mtu */               \
789     NULL,                       /* set_mtu */               \
790     NULL,                       /* get_ifindex */           \
791     NULL,                       /* get_carrier */           \
792     NULL,                       /* get_carrier_resets */    \
793     NULL,                       /* get_miimon */            \
794     get_stats,                                              \
795                                                             \
796     NULL,                       /* get_features */          \
797     NULL,                       /* set_advertisements */    \
798                                                             \
799     NULL,                       /* set_policing */          \
800     NULL,                       /* get_qos_types */         \
801     NULL,                       /* get_qos_capabilities */  \
802     NULL,                       /* get_qos */               \
803     NULL,                       /* set_qos */               \
804     NULL,                       /* get_queue */             \
805     NULL,                       /* set_queue */             \
806     NULL,                       /* delete_queue */          \
807     NULL,                       /* get_queue_stats */       \
808     NULL,                       /* queue_dump_start */      \
809     NULL,                       /* queue_dump_next */       \
810     NULL,                       /* queue_dump_done */       \
811     NULL,                       /* dump_queue_stats */      \
812                                                             \
813     NULL,                       /* get_in4 */               \
814     NULL,                       /* set_in4 */               \
815     NULL,                       /* get_in6 */               \
816     NULL,                       /* add_router */            \
817     NULL,                       /* get_next_hop */          \
818     GET_STATUS,                                             \
819     NULL,                       /* arp_lookup */            \
820                                                             \
821     netdev_vport_update_flags,                              \
822                                                             \
823     NULL,                   /* rx_alloc */                  \
824     NULL,                   /* rx_construct */              \
825     NULL,                   /* rx_destruct */               \
826     NULL,                   /* rx_dealloc */                \
827     NULL,                   /* rx_recv */                   \
828     NULL,                   /* rx_wait */                   \
829     NULL,                   /* rx_drain */
830
831 #define TUNNEL_CLASS(NAME, DPIF_PORT)                       \
832     { DPIF_PORT,                                            \
833         { NAME, VPORT_FUNCTIONS(get_tunnel_config,          \
834                                 set_tunnel_config,          \
835                                 get_netdev_tunnel_config,   \
836                                 tunnel_get_status) }}
837
838 void
839 netdev_vport_tunnel_register(void)
840 {
841     /* The name of the dpif_port should be short enough to accomodate adding
842      * a port number to the end if one is necessary. */
843     static const struct vport_class vport_classes[] = {
844         TUNNEL_CLASS("geneve", "genev_sys"),
845         TUNNEL_CLASS("gre", "gre_sys"),
846         TUNNEL_CLASS("ipsec_gre", "gre_sys"),
847         TUNNEL_CLASS("gre64", "gre64_sys"),
848         TUNNEL_CLASS("ipsec_gre64", "gre64_sys"),
849         TUNNEL_CLASS("vxlan", "vxlan_sys"),
850         TUNNEL_CLASS("lisp", "lisp_sys")
851     };
852     static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
853
854     if (ovsthread_once_start(&once)) {
855         int i;
856
857         for (i = 0; i < ARRAY_SIZE(vport_classes); i++) {
858             netdev_register_provider(&vport_classes[i].netdev_class);
859         }
860         ovsthread_once_done(&once);
861     }
862 }
863
864 void
865 netdev_vport_patch_register(void)
866 {
867     static const struct vport_class patch_class =
868         { NULL,
869             { "patch", VPORT_FUNCTIONS(get_patch_config,
870                                        set_patch_config,
871                                        NULL,
872                                        NULL) }};
873     netdev_register_provider(&patch_class.netdev_class);
874 }