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