Coverage Report

Created: 2026-07-16 06:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openvswitch/lib/dpif-netlink-rtnl.c
Line
Count
Source
1
/*
2
 * Copyright (c) 2017 Red Hat, 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 "dpif-netlink-rtnl.h"
20
21
#include <net/if.h>
22
#include <linux/ip.h>
23
#include <linux/rtnetlink.h>
24
25
#include "dpif-netlink.h"
26
#include "netdev-vport.h"
27
#include "netlink-socket.h"
28
#include "openvswitch/vlog.h"
29
30
VLOG_DEFINE_THIS_MODULE(dpif_netlink_rtnl);
31
32
/* On some older systems, these enums are not defined. */
33
#ifndef IFLA_VXLAN_MAX
34
#define IFLA_VXLAN_MAX 0
35
#endif
36
#if IFLA_VXLAN_MAX < 27
37
0
#define IFLA_VXLAN_LEARNING 7
38
0
#define IFLA_VXLAN_PORT 15
39
0
#define IFLA_VXLAN_UDP_ZERO_CSUM6_RX 20
40
0
#define IFLA_VXLAN_GBP 23
41
0
#define IFLA_VXLAN_COLLECT_METADATA 25
42
0
#define IFLA_VXLAN_GPE 27
43
#endif
44
45
#ifndef IFLA_GRE_MAX
46
#define IFLA_GRE_MAX 0
47
#endif
48
#if IFLA_GRE_MAX < 18
49
0
#define IFLA_GRE_COLLECT_METADATA 18
50
#endif
51
52
#ifndef IFLA_GENEVE_MAX
53
#define IFLA_GENEVE_MAX 0
54
#endif
55
#if IFLA_GENEVE_MAX < 10
56
0
#define IFLA_GENEVE_PORT 5
57
0
#define IFLA_GENEVE_COLLECT_METADATA 6
58
0
#define IFLA_GENEVE_UDP_ZERO_CSUM6_RX 10
59
#endif
60
61
#ifndef IFLA_BAREUDP_MAX
62
#define IFLA_BAREUDP_MAX 0
63
#endif
64
#if IFLA_BAREUDP_MAX < 4
65
0
#define IFLA_BAREUDP_PORT 1
66
0
#define IFLA_BAREUDP_ETHERTYPE 2
67
0
#define IFLA_BAREUDP_SRCPORT_MIN 3
68
0
#define IFLA_BAREUDP_MULTIPROTO_MODE 4
69
#endif
70
71
0
#define BAREUDP_SRCPORT_MIN 49153
72
73
static const struct nl_policy rtlink_policy[] = {
74
    [IFLA_LINKINFO] = { .type = NL_A_NESTED },
75
};
76
static const struct nl_policy linkinfo_policy[] = {
77
    [IFLA_INFO_KIND] = { .type = NL_A_STRING },
78
    [IFLA_INFO_DATA] = { .type = NL_A_NESTED },
79
};
80
static const struct nl_policy vxlan_policy[] = {
81
    [IFLA_VXLAN_COLLECT_METADATA] = { .type = NL_A_U8 },
82
    [IFLA_VXLAN_LEARNING] = { .type = NL_A_U8 },
83
    [IFLA_VXLAN_UDP_ZERO_CSUM6_RX] = { .type = NL_A_U8 },
84
    [IFLA_VXLAN_PORT] = { .type = NL_A_U16 },
85
    [IFLA_VXLAN_GBP] = { .type = NL_A_FLAG, .optional = true },
86
    [IFLA_VXLAN_GPE] = { .type = NL_A_FLAG, .optional = true },
87
};
88
static const struct nl_policy gre_policy[] = {
89
    [IFLA_GRE_COLLECT_METADATA] = { .type = NL_A_FLAG },
90
};
91
static const struct nl_policy geneve_policy[] = {
92
    [IFLA_GENEVE_COLLECT_METADATA] = { .type = NL_A_FLAG },
93
    [IFLA_GENEVE_UDP_ZERO_CSUM6_RX] = { .type = NL_A_U8 },
94
    [IFLA_GENEVE_PORT] = { .type = NL_A_U16 },
95
};
96
static const struct nl_policy bareudp_policy[] = {
97
    [IFLA_BAREUDP_PORT] = { .type = NL_A_U16 },
98
    [IFLA_BAREUDP_ETHERTYPE] = { .type = NL_A_U16 },
99
};
100
101
static const char *
102
vport_type_to_kind(enum ovs_vport_type type,
103
                   const struct netdev_tunnel_config *tnl_cfg)
104
0
{
105
0
    switch (type) {
106
0
    case OVS_VPORT_TYPE_VXLAN:
107
0
        return "vxlan";
108
0
    case OVS_VPORT_TYPE_GRE:
109
0
        if (tnl_cfg->pt_mode == NETDEV_PT_LEGACY_L3) {
110
0
            return "gre";
111
0
        } else if (tnl_cfg->pt_mode == NETDEV_PT_LEGACY_L2) {
112
0
            return "gretap";
113
0
        } else {
114
0
            return NULL;
115
0
        }
116
0
    case OVS_VPORT_TYPE_GENEVE:
117
0
        return "geneve";
118
0
    case OVS_VPORT_TYPE_ERSPAN:
119
0
        return "erspan";
120
0
    case OVS_VPORT_TYPE_IP6ERSPAN:
121
0
        return "ip6erspan";
122
0
    case OVS_VPORT_TYPE_IP6GRE:
123
0
        if (tnl_cfg->pt_mode == NETDEV_PT_LEGACY_L2) {
124
0
            return "ip6gretap";
125
0
        } else if (tnl_cfg->pt_mode == NETDEV_PT_LEGACY_L3) {
126
0
            return NULL;
127
0
        } else {
128
0
            return NULL;
129
0
        }
130
0
    case OVS_VPORT_TYPE_BAREUDP:
131
0
        return "bareudp";
132
0
    case OVS_VPORT_TYPE_NETDEV:
133
0
    case OVS_VPORT_TYPE_INTERNAL:
134
0
    case OVS_VPORT_TYPE_GTPU:
135
0
    case OVS_VPORT_TYPE_SRV6:
136
0
    case OVS_VPORT_TYPE_UNSPEC:
137
0
    case __OVS_VPORT_TYPE_MAX:
138
0
    default:
139
0
        break;
140
0
    }
141
142
0
    return NULL;
143
0
}
144
145
static int
146
rtnl_transact(uint32_t type, uint32_t flags, const char *name,
147
              struct ofpbuf **reply)
148
0
{
149
0
    struct ofpbuf request;
150
0
    int err;
151
152
0
    ofpbuf_init(&request, 0);
153
0
    nl_msg_put_nlmsghdr(&request, 0, type, flags);
154
0
    ofpbuf_put_zeros(&request, sizeof(struct ifinfomsg));
155
0
    nl_msg_put_string(&request, IFLA_IFNAME, name);
156
157
0
    err = nl_transact(NETLINK_ROUTE, &request, reply);
158
0
    ofpbuf_uninit(&request);
159
160
0
    return err;
161
0
}
162
163
static int
164
dpif_netlink_rtnl_destroy(const char *name)
165
0
{
166
0
    return rtnl_transact(RTM_DELLINK, NLM_F_REQUEST | NLM_F_ACK, name, NULL);
167
0
}
168
169
static int
170
dpif_netlink_rtnl_getlink(const char *name, struct ofpbuf **reply)
171
0
{
172
0
    return rtnl_transact(RTM_GETLINK, NLM_F_REQUEST, name, reply);
173
0
}
174
175
static int
176
rtnl_policy_parse(const char *kind, struct ofpbuf *reply,
177
                  const struct nl_policy *policy,
178
                  struct nlattr *tnl_info[],
179
                  size_t policy_size)
180
0
{
181
0
    struct nlattr *linkinfo[ARRAY_SIZE(linkinfo_policy)];
182
0
    struct nlattr *rtlink[ARRAY_SIZE(rtlink_policy)];
183
0
    int error = 0;
184
185
0
    if (!nl_policy_parse(reply, NLMSG_HDRLEN + sizeof(struct ifinfomsg),
186
0
                         rtlink_policy, rtlink, ARRAY_SIZE(rtlink_policy))
187
0
        || !nl_parse_nested(rtlink[IFLA_LINKINFO], linkinfo_policy,
188
0
                            linkinfo, ARRAY_SIZE(linkinfo_policy))
189
0
        || strcmp(nl_attr_get_string(linkinfo[IFLA_INFO_KIND]), kind)
190
0
        || !nl_parse_nested(linkinfo[IFLA_INFO_DATA], policy,
191
0
                            tnl_info, policy_size)) {
192
0
        error = EINVAL;
193
0
    }
194
195
0
    return error;
196
0
}
197
198
static int
199
dpif_netlink_rtnl_vxlan_verify(const struct netdev_tunnel_config *tnl_cfg,
200
                               const char *kind, struct ofpbuf *reply)
201
0
{
202
0
    struct nlattr *vxlan[ARRAY_SIZE(vxlan_policy)];
203
0
    int err;
204
205
0
    err = rtnl_policy_parse(kind, reply, vxlan_policy, vxlan,
206
0
                            ARRAY_SIZE(vxlan_policy));
207
0
    if (!err) {
208
0
        if (0 != nl_attr_get_u8(vxlan[IFLA_VXLAN_LEARNING])
209
0
            || 1 != nl_attr_get_u8(vxlan[IFLA_VXLAN_COLLECT_METADATA])
210
0
            || 1 != nl_attr_get_u8(vxlan[IFLA_VXLAN_UDP_ZERO_CSUM6_RX])
211
0
            || (tnl_cfg->dst_port
212
0
                != nl_attr_get_be16(vxlan[IFLA_VXLAN_PORT]))
213
0
            || (tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GBP)
214
0
                && !nl_attr_get_flag(vxlan[IFLA_VXLAN_GBP]))
215
0
            || (tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GPE)
216
0
                && !nl_attr_get_flag(vxlan[IFLA_VXLAN_GPE]))) {
217
0
            err = EINVAL;
218
0
        }
219
0
    }
220
221
0
    return err;
222
0
}
223
224
static int
225
dpif_netlink_rtnl_gre_verify(const struct netdev_tunnel_config OVS_UNUSED *tnl,
226
                             const char *kind, struct ofpbuf *reply)
227
0
{
228
0
    struct nlattr *gre[ARRAY_SIZE(gre_policy)];
229
0
    int err;
230
231
0
    err = rtnl_policy_parse(kind, reply, gre_policy, gre,
232
0
                            ARRAY_SIZE(gre_policy));
233
0
    if (!err) {
234
0
        if (!nl_attr_get_flag(gre[IFLA_GRE_COLLECT_METADATA])) {
235
0
            err = EINVAL;
236
0
        }
237
0
    }
238
239
0
    return err;
240
0
}
241
242
static int
243
dpif_netlink_rtnl_geneve_verify(const struct netdev_tunnel_config *tnl_cfg,
244
                                const char *kind, struct ofpbuf *reply)
245
0
{
246
0
    struct nlattr *geneve[ARRAY_SIZE(geneve_policy)];
247
0
    int err;
248
249
0
    err = rtnl_policy_parse(kind, reply, geneve_policy, geneve,
250
0
                            ARRAY_SIZE(geneve_policy));
251
0
    if (!err) {
252
0
        if (!nl_attr_get_flag(geneve[IFLA_GENEVE_COLLECT_METADATA])
253
0
            || 1 != nl_attr_get_u8(geneve[IFLA_GENEVE_UDP_ZERO_CSUM6_RX])
254
0
            || (tnl_cfg->dst_port
255
0
                != nl_attr_get_be16(geneve[IFLA_GENEVE_PORT]))) {
256
0
            err = EINVAL;
257
0
        }
258
0
    }
259
260
0
    return err;
261
0
}
262
static int
263
dpif_netlink_rtnl_bareudp_verify(const struct netdev_tunnel_config *tnl_cfg,
264
                                const char *kind, struct ofpbuf *reply)
265
0
{
266
0
    struct nlattr *bareudp[ARRAY_SIZE(bareudp_policy)];
267
0
    int err;
268
269
0
    err = rtnl_policy_parse(kind, reply, bareudp_policy, bareudp,
270
0
                            ARRAY_SIZE(bareudp_policy));
271
0
    if (!err) {
272
0
        if ((tnl_cfg->dst_port != nl_attr_get_be16(bareudp[IFLA_BAREUDP_PORT]))
273
0
            || (tnl_cfg->payload_ethertype
274
0
                != nl_attr_get_be16(bareudp[IFLA_BAREUDP_ETHERTYPE]))) {
275
0
            err = EINVAL;
276
0
        }
277
0
    }
278
0
    return err;
279
0
}
280
281
static int
282
dpif_netlink_rtnl_verify(const struct netdev_tunnel_config *tnl_cfg,
283
                         enum ovs_vport_type type, const char *name)
284
0
{
285
0
    struct ofpbuf *reply;
286
0
    const char *kind;
287
0
    int err;
288
289
0
    kind = vport_type_to_kind(type, tnl_cfg);
290
0
    if (!kind) {
291
0
        return EOPNOTSUPP;
292
0
    }
293
294
0
    err = dpif_netlink_rtnl_getlink(name, &reply);
295
0
    if (err) {
296
0
        return err;
297
0
    }
298
299
0
    switch (type) {
300
0
    case OVS_VPORT_TYPE_VXLAN:
301
0
        err = dpif_netlink_rtnl_vxlan_verify(tnl_cfg, kind, reply);
302
0
        break;
303
0
    case OVS_VPORT_TYPE_GRE:
304
0
    case OVS_VPORT_TYPE_ERSPAN:
305
0
    case OVS_VPORT_TYPE_IP6ERSPAN:
306
0
    case OVS_VPORT_TYPE_IP6GRE:
307
0
        err = dpif_netlink_rtnl_gre_verify(tnl_cfg, kind, reply);
308
0
        break;
309
0
    case OVS_VPORT_TYPE_GENEVE:
310
0
        err = dpif_netlink_rtnl_geneve_verify(tnl_cfg, kind, reply);
311
0
        break;
312
0
    case OVS_VPORT_TYPE_BAREUDP:
313
0
        err = dpif_netlink_rtnl_bareudp_verify(tnl_cfg, kind, reply);
314
0
        break;
315
0
    case OVS_VPORT_TYPE_NETDEV:
316
0
    case OVS_VPORT_TYPE_INTERNAL:
317
0
    case OVS_VPORT_TYPE_GTPU:
318
0
    case OVS_VPORT_TYPE_SRV6:
319
0
    case OVS_VPORT_TYPE_UNSPEC:
320
0
    case __OVS_VPORT_TYPE_MAX:
321
0
    default:
322
0
        OVS_NOT_REACHED();
323
0
    }
324
325
0
    ofpbuf_delete(reply);
326
0
    return err;
327
0
}
328
329
static int
330
rtnl_set_mtu(const char *name, uint32_t mtu, struct ofpbuf *request)
331
0
{
332
0
    ofpbuf_clear(request);
333
0
    nl_msg_put_nlmsghdr(request, 0, RTM_SETLINK,
334
0
                        NLM_F_REQUEST | NLM_F_ACK);
335
0
    ofpbuf_put_zeros(request, sizeof(struct ifinfomsg));
336
0
    nl_msg_put_string(request, IFLA_IFNAME, name);
337
0
    nl_msg_put_u32(request, IFLA_MTU, mtu);
338
339
0
    return nl_transact(NETLINK_ROUTE, request, NULL);
340
0
}
341
342
static int
343
dpif_netlink_rtnl_create(const struct netdev_tunnel_config *tnl_cfg,
344
                         const char *name, enum ovs_vport_type type,
345
                         const char *kind, uint32_t flags)
346
0
{
347
0
    enum {
348
        /* For performance, we want to use the largest MTU that the system
349
         * supports.  Most existing tunnels will accept UINT16_MAX, treating it
350
         * as the actual max MTU, but some do not.  Thus, we use a slightly
351
         * smaller value, that should always be safe yet does not noticeably
352
         * reduce performance. */
353
0
        MAX_MTU = 65000
354
0
    };
355
356
0
    size_t linkinfo_off, infodata_off;
357
0
    struct ifinfomsg *ifinfo;
358
0
    struct ofpbuf request;
359
0
    int err;
360
361
0
    ofpbuf_init(&request, 0);
362
0
    nl_msg_put_nlmsghdr(&request, 0, RTM_NEWLINK, flags);
363
0
    ifinfo = ofpbuf_put_zeros(&request, sizeof(struct ifinfomsg));
364
0
    ifinfo->ifi_change = ifinfo->ifi_flags = IFF_UP;
365
0
    nl_msg_put_string(&request, IFLA_IFNAME, name);
366
0
    nl_msg_put_u32(&request, IFLA_MTU, MAX_MTU);
367
0
    linkinfo_off = nl_msg_start_nested(&request, IFLA_LINKINFO);
368
0
    nl_msg_put_string(&request, IFLA_INFO_KIND, kind);
369
0
    infodata_off = nl_msg_start_nested(&request, IFLA_INFO_DATA);
370
371
    /* tunnel unique info */
372
0
    switch (type) {
373
0
    case OVS_VPORT_TYPE_VXLAN:
374
0
        nl_msg_put_u8(&request, IFLA_VXLAN_LEARNING, 0);
375
0
        nl_msg_put_u8(&request, IFLA_VXLAN_COLLECT_METADATA, 1);
376
0
        nl_msg_put_u8(&request, IFLA_VXLAN_UDP_ZERO_CSUM6_RX, 1);
377
0
        if (tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GBP)) {
378
0
            nl_msg_put_flag(&request, IFLA_VXLAN_GBP);
379
0
        }
380
0
        if (tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GPE)) {
381
0
            nl_msg_put_flag(&request, IFLA_VXLAN_GPE);
382
0
        }
383
0
        nl_msg_put_be16(&request, IFLA_VXLAN_PORT, tnl_cfg->dst_port);
384
0
        break;
385
0
    case OVS_VPORT_TYPE_GRE:
386
0
    case OVS_VPORT_TYPE_ERSPAN:
387
0
    case OVS_VPORT_TYPE_IP6ERSPAN:
388
0
    case OVS_VPORT_TYPE_IP6GRE:
389
0
        nl_msg_put_flag(&request, IFLA_GRE_COLLECT_METADATA);
390
0
        break;
391
0
    case OVS_VPORT_TYPE_GENEVE:
392
0
        nl_msg_put_flag(&request, IFLA_GENEVE_COLLECT_METADATA);
393
0
        nl_msg_put_u8(&request, IFLA_GENEVE_UDP_ZERO_CSUM6_RX, 1);
394
0
        nl_msg_put_be16(&request, IFLA_GENEVE_PORT, tnl_cfg->dst_port);
395
0
        break;
396
0
    case OVS_VPORT_TYPE_BAREUDP:
397
0
        nl_msg_put_be16(&request, IFLA_BAREUDP_ETHERTYPE,
398
0
                        tnl_cfg->payload_ethertype);
399
0
        nl_msg_put_u16(&request, IFLA_BAREUDP_SRCPORT_MIN,
400
0
                       BAREUDP_SRCPORT_MIN);
401
0
        nl_msg_put_be16(&request, IFLA_BAREUDP_PORT, tnl_cfg->dst_port);
402
0
        if (tnl_cfg->exts & (1 << OVS_BAREUDP_EXT_MULTIPROTO_MODE)) {
403
0
            nl_msg_put_flag(&request, IFLA_BAREUDP_MULTIPROTO_MODE);
404
0
        }
405
0
        break;
406
0
    case OVS_VPORT_TYPE_NETDEV:
407
0
    case OVS_VPORT_TYPE_INTERNAL:
408
0
    case OVS_VPORT_TYPE_GTPU:
409
0
    case OVS_VPORT_TYPE_SRV6:
410
0
    case OVS_VPORT_TYPE_UNSPEC:
411
0
    case __OVS_VPORT_TYPE_MAX:
412
0
    default:
413
0
        err = EOPNOTSUPP;
414
0
        goto exit;
415
0
    }
416
417
0
    nl_msg_end_nested(&request, infodata_off);
418
0
    nl_msg_end_nested(&request, linkinfo_off);
419
420
0
    err = nl_transact(NETLINK_ROUTE, &request, NULL);
421
0
    if (!err && (type == OVS_VPORT_TYPE_GRE ||
422
0
                 type == OVS_VPORT_TYPE_IP6GRE)) {
423
        /* Work around a bug in kernel GRE driver, which ignores IFLA_MTU in
424
         * RTM_NEWLINK, by setting the MTU again.  See
425
         * https://bugzilla.redhat.com/show_bug.cgi?id=1488484.
426
         *
427
         * In case of MAX_MTU exceeds hw max MTU, retry a smaller value. */
428
0
        int err2 = rtnl_set_mtu(name, MAX_MTU, &request);
429
0
        if (err2) {
430
0
            err2 = rtnl_set_mtu(name, 1450, &request);
431
0
        }
432
0
        if (err2) {
433
0
            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
434
435
0
            VLOG_WARN_RL(&rl, "setting MTU of tunnel %s failed (%s)",
436
0
                         name, ovs_strerror(err2));
437
0
        }
438
0
    }
439
440
0
exit:
441
0
    ofpbuf_uninit(&request);
442
443
0
    return err;
444
0
}
445
446
int
447
dpif_netlink_rtnl_tunnel_create(struct netdev *netdev)
448
0
{
449
0
    const struct netdev_tunnel_config *tnl_cfg;
450
0
    char namebuf[NETDEV_VPORT_NAME_BUFSIZE];
451
0
    enum ovs_vport_type type;
452
0
    const char *name;
453
0
    const char *kind;
454
0
    uint32_t flags;
455
0
    int err;
456
457
0
    type = netdev_to_ovs_vport_type(netdev_get_type(netdev));
458
0
    tnl_cfg = netdev_get_tunnel_config(netdev);
459
0
    if (!tnl_cfg) {
460
0
        return EOPNOTSUPP;
461
0
    }
462
463
0
    kind = vport_type_to_kind(type, tnl_cfg);
464
0
    if (!kind) {
465
0
        return EOPNOTSUPP;
466
0
    }
467
468
0
    name = netdev_vport_get_dpif_port(netdev, namebuf, sizeof namebuf);
469
0
    flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL;
470
471
0
    err = dpif_netlink_rtnl_create(tnl_cfg, name, type, kind, flags);
472
473
    /* If the device exists, validate and/or attempt to recreate it. */
474
0
    if (err == EEXIST) {
475
0
        err = dpif_netlink_rtnl_verify(tnl_cfg, type, name);
476
0
        if (!err) {
477
0
            return 0;
478
0
        }
479
0
        err = dpif_netlink_rtnl_destroy(name);
480
0
        if (err) {
481
0
            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
482
483
0
            VLOG_WARN_RL(&rl, "RTNL device %s exists and cannot be "
484
0
                         "deleted: %s", name, ovs_strerror(err));
485
0
            return err;
486
0
        }
487
0
        err = dpif_netlink_rtnl_create(tnl_cfg, name, type, kind, flags);
488
0
    }
489
0
    if (err) {
490
0
        return err;
491
0
    }
492
493
0
    err = dpif_netlink_rtnl_verify(tnl_cfg, type, name);
494
0
    if (err) {
495
0
        int err2 = dpif_netlink_rtnl_destroy(name);
496
497
0
        if (err2) {
498
0
            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
499
500
0
            VLOG_WARN_RL(&rl, "Failed to delete device %s during rtnl port "
501
0
                         "creation: %s", name, ovs_strerror(err2));
502
0
        }
503
0
    }
504
505
0
    return err;
506
0
}
507
508
int
509
dpif_netlink_rtnl_tunnel_destroy(const char *name, const char *type)
510
0
{
511
0
    switch (netdev_to_ovs_vport_type(type)) {
512
0
    case OVS_VPORT_TYPE_VXLAN:
513
0
    case OVS_VPORT_TYPE_GRE:
514
0
    case OVS_VPORT_TYPE_GENEVE:
515
0
    case OVS_VPORT_TYPE_ERSPAN:
516
0
    case OVS_VPORT_TYPE_IP6ERSPAN:
517
0
    case OVS_VPORT_TYPE_IP6GRE:
518
0
    case OVS_VPORT_TYPE_BAREUDP:
519
0
        return dpif_netlink_rtnl_destroy(name);
520
0
    case OVS_VPORT_TYPE_NETDEV:
521
0
    case OVS_VPORT_TYPE_INTERNAL:
522
0
    case OVS_VPORT_TYPE_GTPU:
523
0
    case OVS_VPORT_TYPE_SRV6:
524
0
    case OVS_VPORT_TYPE_UNSPEC:
525
0
    case __OVS_VPORT_TYPE_MAX:
526
0
    default:
527
0
        return EOPNOTSUPP;
528
0
    }
529
0
    return 0;
530
0
}