Coverage Report

Created: 2025-07-01 06:50

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