Coverage Report

Created: 2023-03-26 07:41

/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_BAREUDP:
133
0
        return "bareudp";
134
0
    case OVS_VPORT_TYPE_NETDEV:
135
0
    case OVS_VPORT_TYPE_INTERNAL:
136
0
    case OVS_VPORT_TYPE_LISP:
137
0
    case OVS_VPORT_TYPE_STT:
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_LISP:
320
0
    case OVS_VPORT_TYPE_STT:
321
0
    case OVS_VPORT_TYPE_GTPU:
322
0
    case OVS_VPORT_TYPE_UNSPEC:
323
0
    case __OVS_VPORT_TYPE_MAX:
324
0
    default:
325
0
        OVS_NOT_REACHED();
326
0
    }
327
328
0
    ofpbuf_delete(reply);
329
0
    return err;
330
0
}
331
332
static int
333
rtnl_set_mtu(const char *name, uint32_t mtu, struct ofpbuf *request)
334
0
{
335
0
    ofpbuf_clear(request);
336
0
    nl_msg_put_nlmsghdr(request, 0, RTM_SETLINK,
337
0
                        NLM_F_REQUEST | NLM_F_ACK);
338
0
    ofpbuf_put_zeros(request, sizeof(struct ifinfomsg));
339
0
    nl_msg_put_string(request, IFLA_IFNAME, name);
340
0
    nl_msg_put_u32(request, IFLA_MTU, mtu);
341
342
0
    return nl_transact(NETLINK_ROUTE, request, NULL);
343
0
}
344
345
static int
346
dpif_netlink_rtnl_create(const struct netdev_tunnel_config *tnl_cfg,
347
                         const char *name, enum ovs_vport_type type,
348
                         const char *kind, uint32_t flags)
349
0
{
350
0
    enum {
351
        /* For performance, we want to use the largest MTU that the system
352
         * supports.  Most existing tunnels will accept UINT16_MAX, treating it
353
         * as the actual max MTU, but some do not.  Thus, we use a slightly
354
         * smaller value, that should always be safe yet does not noticeably
355
         * reduce performance. */
356
0
        MAX_MTU = 65000
357
0
    };
358
359
0
    size_t linkinfo_off, infodata_off;
360
0
    struct ifinfomsg *ifinfo;
361
0
    struct ofpbuf request;
362
0
    int err;
363
364
0
    ofpbuf_init(&request, 0);
365
0
    nl_msg_put_nlmsghdr(&request, 0, RTM_NEWLINK, flags);
366
0
    ifinfo = ofpbuf_put_zeros(&request, sizeof(struct ifinfomsg));
367
0
    ifinfo->ifi_change = ifinfo->ifi_flags = IFF_UP;
368
0
    nl_msg_put_string(&request, IFLA_IFNAME, name);
369
0
    nl_msg_put_u32(&request, IFLA_MTU, MAX_MTU);
370
0
    linkinfo_off = nl_msg_start_nested(&request, IFLA_LINKINFO);
371
0
    nl_msg_put_string(&request, IFLA_INFO_KIND, kind);
372
0
    infodata_off = nl_msg_start_nested(&request, IFLA_INFO_DATA);
373
374
    /* tunnel unique info */
375
0
    switch (type) {
376
0
    case OVS_VPORT_TYPE_VXLAN:
377
0
        nl_msg_put_u8(&request, IFLA_VXLAN_LEARNING, 0);
378
0
        nl_msg_put_u8(&request, IFLA_VXLAN_COLLECT_METADATA, 1);
379
0
        nl_msg_put_u8(&request, IFLA_VXLAN_UDP_ZERO_CSUM6_RX, 1);
380
0
        if (tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GBP)) {
381
0
            nl_msg_put_flag(&request, IFLA_VXLAN_GBP);
382
0
        }
383
0
        if (tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GPE)) {
384
0
            nl_msg_put_flag(&request, IFLA_VXLAN_GPE);
385
0
        }
386
0
        nl_msg_put_be16(&request, IFLA_VXLAN_PORT, tnl_cfg->dst_port);
387
0
        break;
388
0
    case OVS_VPORT_TYPE_GRE:
389
0
    case OVS_VPORT_TYPE_ERSPAN:
390
0
    case OVS_VPORT_TYPE_IP6ERSPAN:
391
0
    case OVS_VPORT_TYPE_IP6GRE:
392
0
        nl_msg_put_flag(&request, IFLA_GRE_COLLECT_METADATA);
393
0
        break;
394
0
    case OVS_VPORT_TYPE_GENEVE:
395
0
        nl_msg_put_flag(&request, IFLA_GENEVE_COLLECT_METADATA);
396
0
        nl_msg_put_u8(&request, IFLA_GENEVE_UDP_ZERO_CSUM6_RX, 1);
397
0
        nl_msg_put_be16(&request, IFLA_GENEVE_PORT, tnl_cfg->dst_port);
398
0
        break;
399
0
    case OVS_VPORT_TYPE_BAREUDP:
400
0
        nl_msg_put_be16(&request, IFLA_BAREUDP_ETHERTYPE,
401
0
                        tnl_cfg->payload_ethertype);
402
0
        nl_msg_put_u16(&request, IFLA_BAREUDP_SRCPORT_MIN,
403
0
                       BAREUDP_SRCPORT_MIN);
404
0
        nl_msg_put_be16(&request, IFLA_BAREUDP_PORT, tnl_cfg->dst_port);
405
0
        if (tnl_cfg->exts & (1 << OVS_BAREUDP_EXT_MULTIPROTO_MODE)) {
406
0
            nl_msg_put_flag(&request, IFLA_BAREUDP_MULTIPROTO_MODE);
407
0
        }
408
0
        break;
409
0
    case OVS_VPORT_TYPE_NETDEV:
410
0
    case OVS_VPORT_TYPE_INTERNAL:
411
0
    case OVS_VPORT_TYPE_LISP:
412
0
    case OVS_VPORT_TYPE_STT:
413
0
    case OVS_VPORT_TYPE_GTPU:
414
0
    case OVS_VPORT_TYPE_UNSPEC:
415
0
    case __OVS_VPORT_TYPE_MAX:
416
0
    default:
417
0
        err = EOPNOTSUPP;
418
0
        goto exit;
419
0
    }
420
421
0
    nl_msg_end_nested(&request, infodata_off);
422
0
    nl_msg_end_nested(&request, linkinfo_off);
423
424
0
    err = nl_transact(NETLINK_ROUTE, &request, NULL);
425
0
    if (!err && (type == OVS_VPORT_TYPE_GRE ||
426
0
                 type == OVS_VPORT_TYPE_IP6GRE)) {
427
        /* Work around a bug in kernel GRE driver, which ignores IFLA_MTU in
428
         * RTM_NEWLINK, by setting the MTU again.  See
429
         * https://bugzilla.redhat.com/show_bug.cgi?id=1488484.
430
         *
431
         * In case of MAX_MTU exceeds hw max MTU, retry a smaller value. */
432
0
        int err2 = rtnl_set_mtu(name, MAX_MTU, &request);
433
0
        if (err2) {
434
0
            err2 = rtnl_set_mtu(name, 1450, &request);
435
0
        }
436
0
        if (err2) {
437
0
            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
438
439
0
            VLOG_WARN_RL(&rl, "setting MTU of tunnel %s failed (%s)",
440
0
                         name, ovs_strerror(err2));
441
0
        }
442
0
    }
443
444
0
exit:
445
0
    ofpbuf_uninit(&request);
446
447
0
    return err;
448
0
}
449
450
int
451
dpif_netlink_rtnl_port_create(struct netdev *netdev)
452
0
{
453
0
    const struct netdev_tunnel_config *tnl_cfg;
454
0
    char namebuf[NETDEV_VPORT_NAME_BUFSIZE];
455
0
    enum ovs_vport_type type;
456
0
    const char *name;
457
0
    const char *kind;
458
0
    uint32_t flags;
459
0
    int err;
460
461
0
    type = netdev_to_ovs_vport_type(netdev_get_type(netdev));
462
0
    tnl_cfg = netdev_get_tunnel_config(netdev);
463
0
    if (!tnl_cfg) {
464
0
        return EOPNOTSUPP;
465
0
    }
466
467
0
    kind = vport_type_to_kind(type, tnl_cfg);
468
0
    if (!kind) {
469
0
        return EOPNOTSUPP;
470
0
    }
471
472
0
    name = netdev_vport_get_dpif_port(netdev, namebuf, sizeof namebuf);
473
0
    flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL;
474
475
0
    err = dpif_netlink_rtnl_create(tnl_cfg, name, type, kind, flags);
476
477
    /* If the device exists, validate and/or attempt to recreate it. */
478
0
    if (err == EEXIST) {
479
0
        err = dpif_netlink_rtnl_verify(tnl_cfg, type, name);
480
0
        if (!err) {
481
0
            return 0;
482
0
        }
483
0
        err = dpif_netlink_rtnl_destroy(name);
484
0
        if (err) {
485
0
            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
486
487
0
            VLOG_WARN_RL(&rl, "RTNL device %s exists and cannot be "
488
0
                         "deleted: %s", name, ovs_strerror(err));
489
0
            return err;
490
0
        }
491
0
        err = dpif_netlink_rtnl_create(tnl_cfg, name, type, kind, flags);
492
0
    }
493
0
    if (err) {
494
0
        return err;
495
0
    }
496
497
0
    err = dpif_netlink_rtnl_verify(tnl_cfg, type, name);
498
0
    if (err) {
499
0
        int err2 = dpif_netlink_rtnl_destroy(name);
500
501
0
        if (err2) {
502
0
            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
503
504
0
            VLOG_WARN_RL(&rl, "Failed to delete device %s during rtnl port "
505
0
                         "creation: %s", name, ovs_strerror(err2));
506
0
        }
507
0
    }
508
509
0
    return err;
510
0
}
511
512
int
513
dpif_netlink_rtnl_port_destroy(const char *name, const char *type)
514
0
{
515
0
    switch (netdev_to_ovs_vport_type(type)) {
516
0
    case OVS_VPORT_TYPE_VXLAN:
517
0
    case OVS_VPORT_TYPE_GRE:
518
0
    case OVS_VPORT_TYPE_GENEVE:
519
0
    case OVS_VPORT_TYPE_ERSPAN:
520
0
    case OVS_VPORT_TYPE_IP6ERSPAN:
521
0
    case OVS_VPORT_TYPE_IP6GRE:
522
0
    case OVS_VPORT_TYPE_BAREUDP:
523
0
        return dpif_netlink_rtnl_destroy(name);
524
0
    case OVS_VPORT_TYPE_NETDEV:
525
0
    case OVS_VPORT_TYPE_INTERNAL:
526
0
    case OVS_VPORT_TYPE_LISP:
527
0
    case OVS_VPORT_TYPE_STT:
528
0
    case OVS_VPORT_TYPE_GTPU:
529
0
    case OVS_VPORT_TYPE_UNSPEC:
530
0
    case __OVS_VPORT_TYPE_MAX:
531
0
    default:
532
0
        return EOPNOTSUPP;
533
0
    }
534
0
    return 0;
535
0
}
536
537
/**
538
 * Probe for whether the modules are out-of-tree (openvswitch) or in-tree
539
 * (upstream kernel).
540
 *
541
 * We probe for "ovs_geneve" via rtnetlink. As long as this returns something
542
 * other than EOPNOTSUPP we know that the module in use is the out-of-tree one.
543
 * This will be used to determine which netlink interface to use when creating
544
 * ports; rtnetlink or compat/genetlink.
545
 *
546
 * See ovs_tunnels_out_of_tree
547
 */
548
bool
549
dpif_netlink_rtnl_probe_oot_tunnels(void)
550
0
{
551
0
    char namebuf[NETDEV_VPORT_NAME_BUFSIZE];
552
0
    struct netdev *netdev = NULL;
553
0
    bool out_of_tree = false;
554
0
    const char *name;
555
0
    int error;
556
557
0
    error = netdev_open("ovs-system-probe", "geneve", &netdev);
558
0
    if (!error) {
559
0
        struct ofpbuf *reply;
560
0
        const struct netdev_tunnel_config *tnl_cfg;
561
562
0
        tnl_cfg = netdev_get_tunnel_config(netdev);
563
0
        if (!tnl_cfg) {
564
0
            return true;
565
0
        }
566
567
0
        name = netdev_vport_get_dpif_port(netdev, namebuf, sizeof namebuf);
568
569
        /* The geneve module exists when ovs-vswitchd crashes
570
         * and restarts, handle the case here.
571
         */
572
0
        error = dpif_netlink_rtnl_getlink(name, &reply);
573
0
        if (!error) {
574
575
0
            struct nlattr *linkinfo[ARRAY_SIZE(linkinfo_policy)];
576
0
            struct nlattr *rtlink[ARRAY_SIZE(rtlink_policy)];
577
0
            const char *kind;
578
579
0
            if (!nl_policy_parse(reply,
580
0
                                 NLMSG_HDRLEN + sizeof(struct ifinfomsg),
581
0
                                 rtlink_policy, rtlink,
582
0
                                 ARRAY_SIZE(rtlink_policy))
583
0
                || !nl_parse_nested(rtlink[IFLA_LINKINFO], linkinfo_policy,
584
0
                                    linkinfo, ARRAY_SIZE(linkinfo_policy))) {
585
0
                VLOG_ABORT("Error fetching Geneve tunnel device %s "
586
0
                           "linkinfo", name);
587
0
            }
588
589
0
            kind = nl_attr_get_string(linkinfo[IFLA_INFO_KIND]);
590
591
0
            if (!strcmp(kind, "ovs_geneve")) {
592
0
                out_of_tree = true;
593
0
            } else if (!strcmp(kind, "geneve")) {
594
0
                out_of_tree = false;
595
0
            } else {
596
0
                VLOG_ABORT("Geneve tunnel device %s with kind %s"
597
0
                           " not supported", name, kind);
598
0
            }
599
600
0
            ofpbuf_delete(reply);
601
0
            netdev_close(netdev);
602
603
0
            return out_of_tree;
604
0
        }
605
606
0
        error = dpif_netlink_rtnl_create(tnl_cfg, name, OVS_VPORT_TYPE_GENEVE,
607
0
                                         "ovs_geneve",
608
0
                                         (NLM_F_REQUEST | NLM_F_ACK
609
0
                                          | NLM_F_CREATE));
610
0
        if (error != EOPNOTSUPP) {
611
0
            if (!error) {
612
0
                dpif_netlink_rtnl_destroy(name);
613
0
            }
614
0
            out_of_tree = true;
615
0
        }
616
0
        netdev_close(netdev);
617
0
    }
618
619
0
    return out_of_tree;
620
0
}