Coverage Report

Created: 2025-07-01 06:50

/src/openvswitch/lib/dpif-netlink-rtnl.c
Line
Count
Source (jump to first uncovered line)
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_GTPU:
131
0
        return NULL;
132
0
    case OVS_VPORT_TYPE_SRV6:
133
0
        return "srv6";
134
0
    case OVS_VPORT_TYPE_BAREUDP:
135
0
        return "bareudp";
136
0
    case OVS_VPORT_TYPE_NETDEV:
137
0
    case OVS_VPORT_TYPE_INTERNAL:
138
0
    case OVS_VPORT_TYPE_UNSPEC:
139
0
    case __OVS_VPORT_TYPE_MAX:
140
0
    default:
141
0
        break;
142
0
    }
143
144
0
    return NULL;
145
0
}
146
147
static int
148
rtnl_transact(uint32_t type, uint32_t flags, const char *name,
149
              struct ofpbuf **reply)
150
0
{
151
0
    struct ofpbuf request;
152
0
    int err;
153
154
0
    ofpbuf_init(&request, 0);
155
0
    nl_msg_put_nlmsghdr(&request, 0, type, flags);
156
0
    ofpbuf_put_zeros(&request, sizeof(struct ifinfomsg));
157
0
    nl_msg_put_string(&request, IFLA_IFNAME, name);
158
159
0
    err = nl_transact(NETLINK_ROUTE, &request, reply);
160
0
    ofpbuf_uninit(&request);
161
162
0
    return err;
163
0
}
164
165
static int
166
dpif_netlink_rtnl_destroy(const char *name)
167
0
{
168
0
    return rtnl_transact(RTM_DELLINK, NLM_F_REQUEST | NLM_F_ACK, name, NULL);
169
0
}
170
171
static int
172
dpif_netlink_rtnl_getlink(const char *name, struct ofpbuf **reply)
173
0
{
174
0
    return rtnl_transact(RTM_GETLINK, NLM_F_REQUEST, name, reply);
175
0
}
176
177
static int
178
rtnl_policy_parse(const char *kind, struct ofpbuf *reply,
179
                  const struct nl_policy *policy,
180
                  struct nlattr *tnl_info[],
181
                  size_t policy_size)
182
0
{
183
0
    struct nlattr *linkinfo[ARRAY_SIZE(linkinfo_policy)];
184
0
    struct nlattr *rtlink[ARRAY_SIZE(rtlink_policy)];
185
0
    int error = 0;
186
187
0
    if (!nl_policy_parse(reply, NLMSG_HDRLEN + sizeof(struct ifinfomsg),
188
0
                         rtlink_policy, rtlink, ARRAY_SIZE(rtlink_policy))
189
0
        || !nl_parse_nested(rtlink[IFLA_LINKINFO], linkinfo_policy,
190
0
                            linkinfo, ARRAY_SIZE(linkinfo_policy))
191
0
        || strcmp(nl_attr_get_string(linkinfo[IFLA_INFO_KIND]), kind)
192
0
        || !nl_parse_nested(linkinfo[IFLA_INFO_DATA], policy,
193
0
                            tnl_info, policy_size)) {
194
0
        error = EINVAL;
195
0
    }
196
197
0
    return error;
198
0
}
199
200
static int
201
dpif_netlink_rtnl_vxlan_verify(const struct netdev_tunnel_config *tnl_cfg,
202
                               const char *kind, struct ofpbuf *reply)
203
0
{
204
0
    struct nlattr *vxlan[ARRAY_SIZE(vxlan_policy)];
205
0
    int err;
206
207
0
    err = rtnl_policy_parse(kind, reply, vxlan_policy, vxlan,
208
0
                            ARRAY_SIZE(vxlan_policy));
209
0
    if (!err) {
210
0
        if (0 != nl_attr_get_u8(vxlan[IFLA_VXLAN_LEARNING])
211
0
            || 1 != nl_attr_get_u8(vxlan[IFLA_VXLAN_COLLECT_METADATA])
212
0
            || 1 != nl_attr_get_u8(vxlan[IFLA_VXLAN_UDP_ZERO_CSUM6_RX])
213
0
            || (tnl_cfg->dst_port
214
0
                != nl_attr_get_be16(vxlan[IFLA_VXLAN_PORT]))
215
0
            || (tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GBP)
216
0
                && !nl_attr_get_flag(vxlan[IFLA_VXLAN_GBP]))
217
0
            || (tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GPE)
218
0
                && !nl_attr_get_flag(vxlan[IFLA_VXLAN_GPE]))) {
219
0
            err = EINVAL;
220
0
        }
221
0
    }
222
223
0
    return err;
224
0
}
225
226
static int
227
dpif_netlink_rtnl_gre_verify(const struct netdev_tunnel_config OVS_UNUSED *tnl,
228
                             const char *kind, struct ofpbuf *reply)
229
0
{
230
0
    struct nlattr *gre[ARRAY_SIZE(gre_policy)];
231
0
    int err;
232
233
0
    err = rtnl_policy_parse(kind, reply, gre_policy, gre,
234
0
                            ARRAY_SIZE(gre_policy));
235
0
    if (!err) {
236
0
        if (!nl_attr_get_flag(gre[IFLA_GRE_COLLECT_METADATA])) {
237
0
            err = EINVAL;
238
0
        }
239
0
    }
240
241
0
    return err;
242
0
}
243
244
static int
245
dpif_netlink_rtnl_geneve_verify(const struct netdev_tunnel_config *tnl_cfg,
246
                                const char *kind, struct ofpbuf *reply)
247
0
{
248
0
    struct nlattr *geneve[ARRAY_SIZE(geneve_policy)];
249
0
    int err;
250
251
0
    err = rtnl_policy_parse(kind, reply, geneve_policy, geneve,
252
0
                            ARRAY_SIZE(geneve_policy));
253
0
    if (!err) {
254
0
        if (!nl_attr_get_flag(geneve[IFLA_GENEVE_COLLECT_METADATA])
255
0
            || 1 != nl_attr_get_u8(geneve[IFLA_GENEVE_UDP_ZERO_CSUM6_RX])
256
0
            || (tnl_cfg->dst_port
257
0
                != nl_attr_get_be16(geneve[IFLA_GENEVE_PORT]))) {
258
0
            err = EINVAL;
259
0
        }
260
0
    }
261
262
0
    return err;
263
0
}
264
static int
265
dpif_netlink_rtnl_bareudp_verify(const struct netdev_tunnel_config *tnl_cfg,
266
                                const char *kind, struct ofpbuf *reply)
267
0
{
268
0
    struct nlattr *bareudp[ARRAY_SIZE(bareudp_policy)];
269
0
    int err;
270
271
0
    err = rtnl_policy_parse(kind, reply, bareudp_policy, bareudp,
272
0
                            ARRAY_SIZE(bareudp_policy));
273
0
    if (!err) {
274
0
        if ((tnl_cfg->dst_port != nl_attr_get_be16(bareudp[IFLA_BAREUDP_PORT]))
275
0
            || (tnl_cfg->payload_ethertype
276
0
                != nl_attr_get_be16(bareudp[IFLA_BAREUDP_ETHERTYPE]))) {
277
0
            err = EINVAL;
278
0
        }
279
0
    }
280
0
    return err;
281
0
}
282
283
static int
284
dpif_netlink_rtnl_verify(const struct netdev_tunnel_config *tnl_cfg,
285
                         enum ovs_vport_type type, const char *name)
286
0
{
287
0
    struct ofpbuf *reply;
288
0
    const char *kind;
289
0
    int err;
290
291
0
    kind = vport_type_to_kind(type, tnl_cfg);
292
0
    if (!kind) {
293
0
        return EOPNOTSUPP;
294
0
    }
295
296
0
    err = dpif_netlink_rtnl_getlink(name, &reply);
297
0
    if (err) {
298
0
        return err;
299
0
    }
300
301
0
    switch (type) {
302
0
    case OVS_VPORT_TYPE_VXLAN:
303
0
        err = dpif_netlink_rtnl_vxlan_verify(tnl_cfg, kind, reply);
304
0
        break;
305
0
    case OVS_VPORT_TYPE_GRE:
306
0
    case OVS_VPORT_TYPE_ERSPAN:
307
0
    case OVS_VPORT_TYPE_IP6ERSPAN:
308
0
    case OVS_VPORT_TYPE_IP6GRE:
309
0
        err = dpif_netlink_rtnl_gre_verify(tnl_cfg, kind, reply);
310
0
        break;
311
0
    case OVS_VPORT_TYPE_GENEVE:
312
0
        err = dpif_netlink_rtnl_geneve_verify(tnl_cfg, kind, reply);
313
0
        break;
314
0
    case OVS_VPORT_TYPE_BAREUDP:
315
0
        err = dpif_netlink_rtnl_bareudp_verify(tnl_cfg, kind, reply);
316
0
        break;
317
0
    case OVS_VPORT_TYPE_NETDEV:
318
0
    case OVS_VPORT_TYPE_INTERNAL:
319
0
    case OVS_VPORT_TYPE_GTPU:
320
0
    case OVS_VPORT_TYPE_SRV6:
321
0
    case OVS_VPORT_TYPE_UNSPEC:
322
0
    case __OVS_VPORT_TYPE_MAX:
323
0
    default:
324
0
        OVS_NOT_REACHED();
325
0
    }
326
327
0
    ofpbuf_delete(reply);
328
0
    return err;
329
0
}
330
331
static int
332
rtnl_set_mtu(const char *name, uint32_t mtu, struct ofpbuf *request)
333
0
{
334
0
    ofpbuf_clear(request);
335
0
    nl_msg_put_nlmsghdr(request, 0, RTM_SETLINK,
336
0
                        NLM_F_REQUEST | NLM_F_ACK);
337
0
    ofpbuf_put_zeros(request, sizeof(struct ifinfomsg));
338
0
    nl_msg_put_string(request, IFLA_IFNAME, name);
339
0
    nl_msg_put_u32(request, IFLA_MTU, mtu);
340
341
0
    return nl_transact(NETLINK_ROUTE, request, NULL);
342
0
}
343
344
static int
345
dpif_netlink_rtnl_create(const struct netdev_tunnel_config *tnl_cfg,
346
                         const char *name, enum ovs_vport_type type,
347
                         const char *kind, uint32_t flags)
348
0
{
349
0
    enum {
350
        /* For performance, we want to use the largest MTU that the system
351
         * supports.  Most existing tunnels will accept UINT16_MAX, treating it
352
         * as the actual max MTU, but some do not.  Thus, we use a slightly
353
         * smaller value, that should always be safe yet does not noticeably
354
         * reduce performance. */
355
0
        MAX_MTU = 65000
356
0
    };
357
358
0
    size_t linkinfo_off, infodata_off;
359
0
    struct ifinfomsg *ifinfo;
360
0
    struct ofpbuf request;
361
0
    int err;
362
363
0
    ofpbuf_init(&request, 0);
364
0
    nl_msg_put_nlmsghdr(&request, 0, RTM_NEWLINK, flags);
365
0
    ifinfo = ofpbuf_put_zeros(&request, sizeof(struct ifinfomsg));
366
0
    ifinfo->ifi_change = ifinfo->ifi_flags = IFF_UP;
367
0
    nl_msg_put_string(&request, IFLA_IFNAME, name);
368
0
    nl_msg_put_u32(&request, IFLA_MTU, MAX_MTU);
369
0
    linkinfo_off = nl_msg_start_nested(&request, IFLA_LINKINFO);
370
0
    nl_msg_put_string(&request, IFLA_INFO_KIND, kind);
371
0
    infodata_off = nl_msg_start_nested(&request, IFLA_INFO_DATA);
372
373
    /* tunnel unique info */
374
0
    switch (type) {
375
0
    case OVS_VPORT_TYPE_VXLAN:
376
0
        nl_msg_put_u8(&request, IFLA_VXLAN_LEARNING, 0);
377
0
        nl_msg_put_u8(&request, IFLA_VXLAN_COLLECT_METADATA, 1);
378
0
        nl_msg_put_u8(&request, IFLA_VXLAN_UDP_ZERO_CSUM6_RX, 1);
379
0
        if (tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GBP)) {
380
0
            nl_msg_put_flag(&request, IFLA_VXLAN_GBP);
381
0
        }
382
0
        if (tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GPE)) {
383
0
            nl_msg_put_flag(&request, IFLA_VXLAN_GPE);
384
0
        }
385
0
        nl_msg_put_be16(&request, IFLA_VXLAN_PORT, tnl_cfg->dst_port);
386
0
        break;
387
0
    case OVS_VPORT_TYPE_GRE:
388
0
    case OVS_VPORT_TYPE_ERSPAN:
389
0
    case OVS_VPORT_TYPE_IP6ERSPAN:
390
0
    case OVS_VPORT_TYPE_IP6GRE:
391
0
        nl_msg_put_flag(&request, IFLA_GRE_COLLECT_METADATA);
392
0
        break;
393
0
    case OVS_VPORT_TYPE_GENEVE:
394
0
        nl_msg_put_flag(&request, IFLA_GENEVE_COLLECT_METADATA);
395
0
        nl_msg_put_u8(&request, IFLA_GENEVE_UDP_ZERO_CSUM6_RX, 1);
396
0
        nl_msg_put_be16(&request, IFLA_GENEVE_PORT, tnl_cfg->dst_port);
397
0
        break;
398
0
    case OVS_VPORT_TYPE_BAREUDP:
399
0
        nl_msg_put_be16(&request, IFLA_BAREUDP_ETHERTYPE,
400
0
                        tnl_cfg->payload_ethertype);
401
0
        nl_msg_put_u16(&request, IFLA_BAREUDP_SRCPORT_MIN,
402
0
                       BAREUDP_SRCPORT_MIN);
403
0
        nl_msg_put_be16(&request, IFLA_BAREUDP_PORT, tnl_cfg->dst_port);
404
0
        if (tnl_cfg->exts & (1 << OVS_BAREUDP_EXT_MULTIPROTO_MODE)) {
405
0
            nl_msg_put_flag(&request, IFLA_BAREUDP_MULTIPROTO_MODE);
406
0
        }
407
0
        break;
408
0
    case OVS_VPORT_TYPE_NETDEV:
409
0
    case OVS_VPORT_TYPE_INTERNAL:
410
0
    case OVS_VPORT_TYPE_GTPU:
411
0
    case OVS_VPORT_TYPE_SRV6:
412
0
    case OVS_VPORT_TYPE_UNSPEC:
413
0
    case __OVS_VPORT_TYPE_MAX:
414
0
    default:
415
0
        err = EOPNOTSUPP;
416
0
        goto exit;
417
0
    }
418
419
0
    nl_msg_end_nested(&request, infodata_off);
420
0
    nl_msg_end_nested(&request, linkinfo_off);
421
422
0
    err = nl_transact(NETLINK_ROUTE, &request, NULL);
423
0
    if (!err && (type == OVS_VPORT_TYPE_GRE ||
424
0
                 type == OVS_VPORT_TYPE_IP6GRE)) {
425
        /* Work around a bug in kernel GRE driver, which ignores IFLA_MTU in
426
         * RTM_NEWLINK, by setting the MTU again.  See
427
         * https://bugzilla.redhat.com/show_bug.cgi?id=1488484.
428
         *
429
         * In case of MAX_MTU exceeds hw max MTU, retry a smaller value. */
430
0
        int err2 = rtnl_set_mtu(name, MAX_MTU, &request);
431
0
        if (err2) {
432
0
            err2 = rtnl_set_mtu(name, 1450, &request);
433
0
        }
434
0
        if (err2) {
435
0
            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
436
437
0
            VLOG_WARN_RL(&rl, "setting MTU of tunnel %s failed (%s)",
438
0
                         name, ovs_strerror(err2));
439
0
        }
440
0
    }
441
442
0
exit:
443
0
    ofpbuf_uninit(&request);
444
445
0
    return err;
446
0
}
447
448
int
449
dpif_netlink_rtnl_port_create(struct netdev *netdev)
450
0
{
451
0
    const struct netdev_tunnel_config *tnl_cfg;
452
0
    char namebuf[NETDEV_VPORT_NAME_BUFSIZE];
453
0
    enum ovs_vport_type type;
454
0
    const char *name;
455
0
    const char *kind;
456
0
    uint32_t flags;
457
0
    int err;
458
459
0
    type = netdev_to_ovs_vport_type(netdev_get_type(netdev));
460
0
    tnl_cfg = netdev_get_tunnel_config(netdev);
461
0
    if (!tnl_cfg) {
462
0
        return EOPNOTSUPP;
463
0
    }
464
465
0
    kind = vport_type_to_kind(type, tnl_cfg);
466
0
    if (!kind) {
467
0
        return EOPNOTSUPP;
468
0
    }
469
470
0
    name = netdev_vport_get_dpif_port(netdev, namebuf, sizeof namebuf);
471
0
    flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL;
472
473
0
    err = dpif_netlink_rtnl_create(tnl_cfg, name, type, kind, flags);
474
475
    /* If the device exists, validate and/or attempt to recreate it. */
476
0
    if (err == EEXIST) {
477
0
        err = dpif_netlink_rtnl_verify(tnl_cfg, type, name);
478
0
        if (!err) {
479
0
            return 0;
480
0
        }
481
0
        err = dpif_netlink_rtnl_destroy(name);
482
0
        if (err) {
483
0
            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
484
485
0
            VLOG_WARN_RL(&rl, "RTNL device %s exists and cannot be "
486
0
                         "deleted: %s", name, ovs_strerror(err));
487
0
            return err;
488
0
        }
489
0
        err = dpif_netlink_rtnl_create(tnl_cfg, name, type, kind, flags);
490
0
    }
491
0
    if (err) {
492
0
        return err;
493
0
    }
494
495
0
    err = dpif_netlink_rtnl_verify(tnl_cfg, type, name);
496
0
    if (err) {
497
0
        int err2 = dpif_netlink_rtnl_destroy(name);
498
499
0
        if (err2) {
500
0
            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
501
502
0
            VLOG_WARN_RL(&rl, "Failed to delete device %s during rtnl port "
503
0
                         "creation: %s", name, ovs_strerror(err2));
504
0
        }
505
0
    }
506
507
0
    return err;
508
0
}
509
510
int
511
dpif_netlink_rtnl_port_destroy(const char *name, const char *type)
512
0
{
513
0
    switch (netdev_to_ovs_vport_type(type)) {
514
0
    case OVS_VPORT_TYPE_VXLAN:
515
0
    case OVS_VPORT_TYPE_GRE:
516
0
    case OVS_VPORT_TYPE_GENEVE:
517
0
    case OVS_VPORT_TYPE_ERSPAN:
518
0
    case OVS_VPORT_TYPE_IP6ERSPAN:
519
0
    case OVS_VPORT_TYPE_IP6GRE:
520
0
    case OVS_VPORT_TYPE_SRV6:
521
0
    case OVS_VPORT_TYPE_BAREUDP:
522
0
        return dpif_netlink_rtnl_destroy(name);
523
0
    case OVS_VPORT_TYPE_NETDEV:
524
0
    case OVS_VPORT_TYPE_INTERNAL:
525
0
    case OVS_VPORT_TYPE_GTPU:
526
0
    case OVS_VPORT_TYPE_UNSPEC:
527
0
    case __OVS_VPORT_TYPE_MAX:
528
0
    default:
529
0
        return EOPNOTSUPP;
530
0
    }
531
0
    return 0;
532
0
}
533
534
/**
535
 * Probe for whether the modules are out-of-tree (openvswitch) or in-tree
536
 * (upstream kernel).
537
 *
538
 * We probe for "ovs_geneve" via rtnetlink. As long as this returns something
539
 * other than EOPNOTSUPP we know that the module in use is the out-of-tree one.
540
 * This will be used to determine which netlink interface to use when creating
541
 * ports; rtnetlink or compat/genetlink.
542
 *
543
 * See ovs_tunnels_out_of_tree
544
 */
545
bool
546
dpif_netlink_rtnl_probe_oot_tunnels(void)
547
0
{
548
0
    char namebuf[NETDEV_VPORT_NAME_BUFSIZE];
549
0
    struct netdev *netdev = NULL;
550
0
    bool out_of_tree = false;
551
0
    const char *name;
552
0
    int error;
553
554
0
    error = netdev_open("ovs-system-probe", "geneve", &netdev);
555
0
    if (!error) {
556
0
        struct ofpbuf *reply;
557
0
        const struct netdev_tunnel_config *tnl_cfg;
558
559
0
        tnl_cfg = netdev_get_tunnel_config(netdev);
560
0
        if (!tnl_cfg) {
561
0
            netdev_close(netdev);
562
0
            return true;
563
0
        }
564
565
0
        name = netdev_vport_get_dpif_port(netdev, namebuf, sizeof namebuf);
566
567
        /* The geneve module exists when ovs-vswitchd crashes
568
         * and restarts, handle the case here.
569
         */
570
0
        error = dpif_netlink_rtnl_getlink(name, &reply);
571
0
        if (!error) {
572
573
0
            struct nlattr *linkinfo[ARRAY_SIZE(linkinfo_policy)];
574
0
            struct nlattr *rtlink[ARRAY_SIZE(rtlink_policy)];
575
0
            const char *kind;
576
577
0
            if (!nl_policy_parse(reply,
578
0
                                 NLMSG_HDRLEN + sizeof(struct ifinfomsg),
579
0
                                 rtlink_policy, rtlink,
580
0
                                 ARRAY_SIZE(rtlink_policy))
581
0
                || !nl_parse_nested(rtlink[IFLA_LINKINFO], linkinfo_policy,
582
0
                                    linkinfo, ARRAY_SIZE(linkinfo_policy))) {
583
0
                VLOG_ABORT("Error fetching Geneve tunnel device %s "
584
0
                           "linkinfo", name);
585
0
            }
586
587
0
            kind = nl_attr_get_string(linkinfo[IFLA_INFO_KIND]);
588
589
0
            if (!strcmp(kind, "ovs_geneve")) {
590
0
                out_of_tree = true;
591
0
            } else if (!strcmp(kind, "geneve")) {
592
0
                out_of_tree = false;
593
0
            } else {
594
0
                VLOG_ABORT("Geneve tunnel device %s with kind %s"
595
0
                           " not supported", name, kind);
596
0
            }
597
598
0
            ofpbuf_delete(reply);
599
0
            netdev_close(netdev);
600
601
0
            return out_of_tree;
602
0
        }
603
604
0
        error = dpif_netlink_rtnl_create(tnl_cfg, name, OVS_VPORT_TYPE_GENEVE,
605
0
                                         "ovs_geneve",
606
0
                                         (NLM_F_REQUEST | NLM_F_ACK
607
0
                                          | NLM_F_CREATE));
608
0
        if (error != EOPNOTSUPP) {
609
0
            if (!error) {
610
0
                dpif_netlink_rtnl_destroy(name);
611
0
            }
612
0
            out_of_tree = true;
613
0
        }
614
0
        netdev_close(netdev);
615
0
    }
616
617
0
    return out_of_tree;
618
0
}