2017-05-18 16:10:29 -04:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2017 Red Hat, Inc.
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at:
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include "dpif-netlink-rtnl.h"
|
|
|
|
|
|
|
|
#include <net/if.h>
|
|
|
|
#include <linux/ip.h>
|
|
|
|
#include <linux/rtnetlink.h>
|
|
|
|
|
|
|
|
#include "dpif-netlink.h"
|
|
|
|
#include "netdev-vport.h"
|
|
|
|
#include "netlink-socket.h"
|
|
|
|
#include "openvswitch/vlog.h"
|
|
|
|
|
|
|
|
VLOG_DEFINE_THIS_MODULE(dpif_netlink_rtnl);
|
|
|
|
|
2017-05-18 16:10:30 -04:00
|
|
|
/* On some older systems, these enums are not defined. */
|
|
|
|
#ifndef IFLA_VXLAN_MAX
|
|
|
|
#define IFLA_VXLAN_MAX 0
|
|
|
|
#endif
|
2017-07-10 15:39:53 -04:00
|
|
|
#if IFLA_VXLAN_MAX < 27
|
2017-05-18 16:10:30 -04:00
|
|
|
#define IFLA_VXLAN_LEARNING 7
|
|
|
|
#define IFLA_VXLAN_PORT 15
|
|
|
|
#define IFLA_VXLAN_UDP_ZERO_CSUM6_RX 20
|
|
|
|
#define IFLA_VXLAN_GBP 23
|
|
|
|
#define IFLA_VXLAN_COLLECT_METADATA 25
|
2017-07-10 15:39:53 -04:00
|
|
|
#define IFLA_VXLAN_GPE 27
|
2017-05-18 16:10:30 -04:00
|
|
|
#endif
|
|
|
|
|
2017-05-18 16:10:31 -04:00
|
|
|
#ifndef IFLA_GRE_MAX
|
|
|
|
#define IFLA_GRE_MAX 0
|
|
|
|
#endif
|
2018-06-04 13:14:36 -07:00
|
|
|
#if IFLA_GRE_MAX < 18
|
|
|
|
#define IFLA_GRE_COLLECT_METADATA 18
|
2017-05-18 16:10:31 -04:00
|
|
|
#endif
|
|
|
|
|
2017-05-18 16:10:32 -04:00
|
|
|
#ifndef IFLA_GENEVE_MAX
|
|
|
|
#define IFLA_GENEVE_MAX 0
|
|
|
|
#endif
|
|
|
|
#if IFLA_GENEVE_MAX < 10
|
|
|
|
#define IFLA_GENEVE_PORT 5
|
|
|
|
#define IFLA_GENEVE_COLLECT_METADATA 6
|
|
|
|
#define IFLA_GENEVE_UDP_ZERO_CSUM6_RX 10
|
|
|
|
#endif
|
|
|
|
|
2020-12-17 12:48:41 +05:30
|
|
|
#ifndef IFLA_BAREUDP_MAX
|
|
|
|
#define IFLA_BAREUDP_MAX 0
|
|
|
|
#endif
|
|
|
|
#if IFLA_BAREUDP_MAX < 4
|
|
|
|
#define IFLA_BAREUDP_PORT 1
|
|
|
|
#define IFLA_BAREUDP_ETHERTYPE 2
|
|
|
|
#define IFLA_BAREUDP_SRCPORT_MIN 3
|
|
|
|
#define IFLA_BAREUDP_MULTIPROTO_MODE 4
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define BAREUDP_SRCPORT_MIN 49153
|
|
|
|
|
2017-05-18 16:10:29 -04:00
|
|
|
static const struct nl_policy rtlink_policy[] = {
|
|
|
|
[IFLA_LINKINFO] = { .type = NL_A_NESTED },
|
|
|
|
};
|
|
|
|
static const struct nl_policy linkinfo_policy[] = {
|
|
|
|
[IFLA_INFO_KIND] = { .type = NL_A_STRING },
|
|
|
|
[IFLA_INFO_DATA] = { .type = NL_A_NESTED },
|
|
|
|
};
|
2017-05-18 16:10:30 -04:00
|
|
|
static const struct nl_policy vxlan_policy[] = {
|
|
|
|
[IFLA_VXLAN_COLLECT_METADATA] = { .type = NL_A_U8 },
|
|
|
|
[IFLA_VXLAN_LEARNING] = { .type = NL_A_U8 },
|
|
|
|
[IFLA_VXLAN_UDP_ZERO_CSUM6_RX] = { .type = NL_A_U8 },
|
|
|
|
[IFLA_VXLAN_PORT] = { .type = NL_A_U16 },
|
2017-08-01 18:47:18 -04:00
|
|
|
[IFLA_VXLAN_GBP] = { .type = NL_A_FLAG, .optional = true },
|
|
|
|
[IFLA_VXLAN_GPE] = { .type = NL_A_FLAG, .optional = true },
|
2017-05-18 16:10:30 -04:00
|
|
|
};
|
2017-05-18 16:10:31 -04:00
|
|
|
static const struct nl_policy gre_policy[] = {
|
2018-06-04 13:14:36 -07:00
|
|
|
[IFLA_GRE_COLLECT_METADATA] = { .type = NL_A_FLAG },
|
2017-05-18 16:10:31 -04:00
|
|
|
};
|
2017-05-18 16:10:32 -04:00
|
|
|
static const struct nl_policy geneve_policy[] = {
|
|
|
|
[IFLA_GENEVE_COLLECT_METADATA] = { .type = NL_A_FLAG },
|
|
|
|
[IFLA_GENEVE_UDP_ZERO_CSUM6_RX] = { .type = NL_A_U8 },
|
|
|
|
[IFLA_GENEVE_PORT] = { .type = NL_A_U16 },
|
|
|
|
};
|
2020-12-17 12:48:41 +05:30
|
|
|
static const struct nl_policy bareudp_policy[] = {
|
|
|
|
[IFLA_BAREUDP_PORT] = { .type = NL_A_U16 },
|
|
|
|
[IFLA_BAREUDP_ETHERTYPE] = { .type = NL_A_U16 },
|
|
|
|
};
|
2017-05-18 16:10:29 -04:00
|
|
|
|
|
|
|
static const char *
|
2017-07-10 15:39:54 -04:00
|
|
|
vport_type_to_kind(enum ovs_vport_type type,
|
|
|
|
const struct netdev_tunnel_config *tnl_cfg)
|
2017-05-18 16:10:29 -04:00
|
|
|
{
|
|
|
|
switch (type) {
|
|
|
|
case OVS_VPORT_TYPE_VXLAN:
|
|
|
|
return "vxlan";
|
|
|
|
case OVS_VPORT_TYPE_GRE:
|
2017-07-10 15:39:54 -04:00
|
|
|
if (tnl_cfg->pt_mode == NETDEV_PT_LEGACY_L3) {
|
|
|
|
return "gre";
|
|
|
|
} else if (tnl_cfg->pt_mode == NETDEV_PT_LEGACY_L2) {
|
|
|
|
return "gretap";
|
|
|
|
} else {
|
|
|
|
return NULL;
|
|
|
|
}
|
2017-05-18 16:10:29 -04:00
|
|
|
case OVS_VPORT_TYPE_GENEVE:
|
|
|
|
return "geneve";
|
2018-03-21 14:02:25 -07:00
|
|
|
case OVS_VPORT_TYPE_ERSPAN:
|
|
|
|
return "erspan";
|
|
|
|
case OVS_VPORT_TYPE_IP6ERSPAN:
|
|
|
|
return "ip6erspan";
|
|
|
|
case OVS_VPORT_TYPE_IP6GRE:
|
2019-07-01 12:45:22 -07:00
|
|
|
if (tnl_cfg->pt_mode == NETDEV_PT_LEGACY_L2) {
|
|
|
|
return "ip6gretap";
|
|
|
|
} else if (tnl_cfg->pt_mode == NETDEV_PT_LEGACY_L3) {
|
|
|
|
return NULL;
|
|
|
|
} else {
|
|
|
|
return NULL;
|
|
|
|
}
|
2019-11-25 11:19:23 -08:00
|
|
|
case OVS_VPORT_TYPE_GTPU:
|
|
|
|
return NULL;
|
2023-03-29 14:51:17 +09:00
|
|
|
case OVS_VPORT_TYPE_SRV6:
|
|
|
|
return "srv6";
|
2020-12-17 12:48:41 +05:30
|
|
|
case OVS_VPORT_TYPE_BAREUDP:
|
|
|
|
return "bareudp";
|
2017-05-18 16:10:29 -04:00
|
|
|
case OVS_VPORT_TYPE_NETDEV:
|
|
|
|
case OVS_VPORT_TYPE_INTERNAL:
|
|
|
|
case OVS_VPORT_TYPE_UNSPEC:
|
|
|
|
case __OVS_VPORT_TYPE_MAX:
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
rtnl_transact(uint32_t type, uint32_t flags, const char *name,
|
|
|
|
struct ofpbuf **reply)
|
|
|
|
{
|
|
|
|
struct ofpbuf request;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
ofpbuf_init(&request, 0);
|
|
|
|
nl_msg_put_nlmsghdr(&request, 0, type, flags);
|
|
|
|
ofpbuf_put_zeros(&request, sizeof(struct ifinfomsg));
|
|
|
|
nl_msg_put_string(&request, IFLA_IFNAME, name);
|
|
|
|
|
|
|
|
err = nl_transact(NETLINK_ROUTE, &request, reply);
|
|
|
|
ofpbuf_uninit(&request);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
dpif_netlink_rtnl_destroy(const char *name)
|
|
|
|
{
|
|
|
|
return rtnl_transact(RTM_DELLINK, NLM_F_REQUEST | NLM_F_ACK, name, NULL);
|
|
|
|
}
|
|
|
|
|
2017-05-18 16:10:30 -04:00
|
|
|
static int
|
2017-05-18 16:10:29 -04:00
|
|
|
dpif_netlink_rtnl_getlink(const char *name, struct ofpbuf **reply)
|
|
|
|
{
|
|
|
|
return rtnl_transact(RTM_GETLINK, NLM_F_REQUEST, name, reply);
|
|
|
|
}
|
|
|
|
|
2017-05-18 16:10:30 -04:00
|
|
|
static int
|
2017-05-18 16:10:29 -04:00
|
|
|
rtnl_policy_parse(const char *kind, struct ofpbuf *reply,
|
|
|
|
const struct nl_policy *policy,
|
|
|
|
struct nlattr *tnl_info[],
|
|
|
|
size_t policy_size)
|
|
|
|
{
|
|
|
|
struct nlattr *linkinfo[ARRAY_SIZE(linkinfo_policy)];
|
|
|
|
struct nlattr *rtlink[ARRAY_SIZE(rtlink_policy)];
|
|
|
|
int error = 0;
|
|
|
|
|
2017-06-19 19:54:02 +01:00
|
|
|
if (!nl_policy_parse(reply, NLMSG_HDRLEN + sizeof(struct ifinfomsg),
|
2017-05-18 16:10:29 -04:00
|
|
|
rtlink_policy, rtlink, ARRAY_SIZE(rtlink_policy))
|
|
|
|
|| !nl_parse_nested(rtlink[IFLA_LINKINFO], linkinfo_policy,
|
|
|
|
linkinfo, ARRAY_SIZE(linkinfo_policy))
|
|
|
|
|| strcmp(nl_attr_get_string(linkinfo[IFLA_INFO_KIND]), kind)
|
|
|
|
|| !nl_parse_nested(linkinfo[IFLA_INFO_DATA], policy,
|
|
|
|
tnl_info, policy_size)) {
|
|
|
|
error = EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2017-05-18 16:10:30 -04:00
|
|
|
dpif_netlink_rtnl_vxlan_verify(const struct netdev_tunnel_config *tnl_cfg,
|
2017-05-19 13:27:35 -07:00
|
|
|
const char *kind, struct ofpbuf *reply)
|
2017-05-18 16:10:30 -04:00
|
|
|
{
|
2017-05-19 13:27:35 -07:00
|
|
|
struct nlattr *vxlan[ARRAY_SIZE(vxlan_policy)];
|
2017-05-18 16:10:30 -04:00
|
|
|
int err;
|
|
|
|
|
2017-05-19 13:27:35 -07:00
|
|
|
err = rtnl_policy_parse(kind, reply, vxlan_policy, vxlan,
|
|
|
|
ARRAY_SIZE(vxlan_policy));
|
2017-05-18 16:10:30 -04:00
|
|
|
if (!err) {
|
2017-05-19 13:27:35 -07:00
|
|
|
if (0 != nl_attr_get_u8(vxlan[IFLA_VXLAN_LEARNING])
|
|
|
|
|| 1 != nl_attr_get_u8(vxlan[IFLA_VXLAN_COLLECT_METADATA])
|
|
|
|
|| 1 != nl_attr_get_u8(vxlan[IFLA_VXLAN_UDP_ZERO_CSUM6_RX])
|
|
|
|
|| (tnl_cfg->dst_port
|
|
|
|
!= nl_attr_get_be16(vxlan[IFLA_VXLAN_PORT]))
|
|
|
|
|| (tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GBP)
|
2017-07-10 15:39:53 -04:00
|
|
|
&& !nl_attr_get_flag(vxlan[IFLA_VXLAN_GBP]))
|
|
|
|
|| (tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GPE)
|
|
|
|
&& !nl_attr_get_flag(vxlan[IFLA_VXLAN_GPE]))) {
|
2017-05-19 13:27:35 -07:00
|
|
|
err = EINVAL;
|
2017-05-18 16:10:30 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2017-05-18 16:10:31 -04:00
|
|
|
static int
|
|
|
|
dpif_netlink_rtnl_gre_verify(const struct netdev_tunnel_config OVS_UNUSED *tnl,
|
2017-05-19 13:27:35 -07:00
|
|
|
const char *kind, struct ofpbuf *reply)
|
2017-05-18 16:10:31 -04:00
|
|
|
{
|
2017-05-19 13:27:35 -07:00
|
|
|
struct nlattr *gre[ARRAY_SIZE(gre_policy)];
|
2017-05-18 16:10:31 -04:00
|
|
|
int err;
|
|
|
|
|
2017-05-19 13:27:35 -07:00
|
|
|
err = rtnl_policy_parse(kind, reply, gre_policy, gre,
|
|
|
|
ARRAY_SIZE(gre_policy));
|
2017-05-18 16:10:31 -04:00
|
|
|
if (!err) {
|
2018-06-04 13:14:36 -07:00
|
|
|
if (!nl_attr_get_flag(gre[IFLA_GRE_COLLECT_METADATA])) {
|
2017-05-19 13:27:35 -07:00
|
|
|
err = EINVAL;
|
2017-05-18 16:10:31 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
2017-05-18 16:10:30 -04:00
|
|
|
|
2017-05-18 16:10:32 -04:00
|
|
|
static int
|
|
|
|
dpif_netlink_rtnl_geneve_verify(const struct netdev_tunnel_config *tnl_cfg,
|
2017-05-19 13:27:35 -07:00
|
|
|
const char *kind, struct ofpbuf *reply)
|
2017-05-18 16:10:32 -04:00
|
|
|
{
|
2017-05-19 13:27:35 -07:00
|
|
|
struct nlattr *geneve[ARRAY_SIZE(geneve_policy)];
|
2017-05-18 16:10:32 -04:00
|
|
|
int err;
|
|
|
|
|
2017-05-19 13:27:35 -07:00
|
|
|
err = rtnl_policy_parse(kind, reply, geneve_policy, geneve,
|
|
|
|
ARRAY_SIZE(geneve_policy));
|
2017-05-18 16:10:32 -04:00
|
|
|
if (!err) {
|
2017-05-19 13:27:35 -07:00
|
|
|
if (!nl_attr_get_flag(geneve[IFLA_GENEVE_COLLECT_METADATA])
|
|
|
|
|| 1 != nl_attr_get_u8(geneve[IFLA_GENEVE_UDP_ZERO_CSUM6_RX])
|
|
|
|
|| (tnl_cfg->dst_port
|
|
|
|
!= nl_attr_get_be16(geneve[IFLA_GENEVE_PORT]))) {
|
|
|
|
err = EINVAL;
|
2017-05-18 16:10:32 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
2020-12-17 12:48:41 +05:30
|
|
|
static int
|
|
|
|
dpif_netlink_rtnl_bareudp_verify(const struct netdev_tunnel_config *tnl_cfg,
|
|
|
|
const char *kind, struct ofpbuf *reply)
|
|
|
|
{
|
|
|
|
struct nlattr *bareudp[ARRAY_SIZE(bareudp_policy)];
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = rtnl_policy_parse(kind, reply, bareudp_policy, bareudp,
|
|
|
|
ARRAY_SIZE(bareudp_policy));
|
|
|
|
if (!err) {
|
|
|
|
if ((tnl_cfg->dst_port != nl_attr_get_be16(bareudp[IFLA_BAREUDP_PORT]))
|
|
|
|
|| (tnl_cfg->payload_ethertype
|
|
|
|
!= nl_attr_get_be16(bareudp[IFLA_BAREUDP_ETHERTYPE]))) {
|
|
|
|
err = EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return err;
|
|
|
|
}
|
2017-05-18 16:10:32 -04:00
|
|
|
|
2017-05-18 16:10:30 -04:00
|
|
|
static int
|
|
|
|
dpif_netlink_rtnl_verify(const struct netdev_tunnel_config *tnl_cfg,
|
|
|
|
enum ovs_vport_type type, const char *name)
|
2017-05-18 16:10:29 -04:00
|
|
|
{
|
2017-05-19 13:27:35 -07:00
|
|
|
struct ofpbuf *reply;
|
2017-05-18 16:10:29 -04:00
|
|
|
const char *kind;
|
2017-05-19 13:27:35 -07:00
|
|
|
int err;
|
2017-05-18 16:10:29 -04:00
|
|
|
|
2017-07-10 15:39:54 -04:00
|
|
|
kind = vport_type_to_kind(type, tnl_cfg);
|
2017-05-18 16:10:29 -04:00
|
|
|
if (!kind) {
|
|
|
|
return EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
2017-05-19 13:27:35 -07:00
|
|
|
err = dpif_netlink_rtnl_getlink(name, &reply);
|
|
|
|
if (err) {
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2017-05-18 16:10:29 -04:00
|
|
|
switch (type) {
|
|
|
|
case OVS_VPORT_TYPE_VXLAN:
|
2017-05-19 13:27:35 -07:00
|
|
|
err = dpif_netlink_rtnl_vxlan_verify(tnl_cfg, kind, reply);
|
|
|
|
break;
|
2017-05-18 16:10:29 -04:00
|
|
|
case OVS_VPORT_TYPE_GRE:
|
2018-03-21 14:02:25 -07:00
|
|
|
case OVS_VPORT_TYPE_ERSPAN:
|
|
|
|
case OVS_VPORT_TYPE_IP6ERSPAN:
|
|
|
|
case OVS_VPORT_TYPE_IP6GRE:
|
2017-05-19 13:27:35 -07:00
|
|
|
err = dpif_netlink_rtnl_gre_verify(tnl_cfg, kind, reply);
|
|
|
|
break;
|
2017-05-18 16:10:29 -04:00
|
|
|
case OVS_VPORT_TYPE_GENEVE:
|
2017-05-19 13:27:35 -07:00
|
|
|
err = dpif_netlink_rtnl_geneve_verify(tnl_cfg, kind, reply);
|
|
|
|
break;
|
2020-12-17 12:48:41 +05:30
|
|
|
case OVS_VPORT_TYPE_BAREUDP:
|
|
|
|
err = dpif_netlink_rtnl_bareudp_verify(tnl_cfg, kind, reply);
|
|
|
|
break;
|
2017-05-18 16:10:29 -04:00
|
|
|
case OVS_VPORT_TYPE_NETDEV:
|
|
|
|
case OVS_VPORT_TYPE_INTERNAL:
|
2019-11-25 11:19:23 -08:00
|
|
|
case OVS_VPORT_TYPE_GTPU:
|
2023-03-29 14:51:17 +09:00
|
|
|
case OVS_VPORT_TYPE_SRV6:
|
2017-05-18 16:10:29 -04:00
|
|
|
case OVS_VPORT_TYPE_UNSPEC:
|
|
|
|
case __OVS_VPORT_TYPE_MAX:
|
|
|
|
default:
|
2017-05-19 13:27:36 -07:00
|
|
|
OVS_NOT_REACHED();
|
2017-05-18 16:10:29 -04:00
|
|
|
}
|
|
|
|
|
2017-05-19 13:27:35 -07:00
|
|
|
ofpbuf_delete(reply);
|
|
|
|
return err;
|
2017-05-18 16:10:29 -04:00
|
|
|
}
|
|
|
|
|
2018-07-06 08:28:27 -07:00
|
|
|
static int
|
|
|
|
rtnl_set_mtu(const char *name, uint32_t mtu, struct ofpbuf *request)
|
|
|
|
{
|
|
|
|
ofpbuf_clear(request);
|
|
|
|
nl_msg_put_nlmsghdr(request, 0, RTM_SETLINK,
|
|
|
|
NLM_F_REQUEST | NLM_F_ACK);
|
|
|
|
ofpbuf_put_zeros(request, sizeof(struct ifinfomsg));
|
|
|
|
nl_msg_put_string(request, IFLA_IFNAME, name);
|
|
|
|
nl_msg_put_u32(request, IFLA_MTU, mtu);
|
|
|
|
|
|
|
|
return nl_transact(NETLINK_ROUTE, request, NULL);
|
|
|
|
}
|
|
|
|
|
2017-05-18 16:10:30 -04:00
|
|
|
static int
|
|
|
|
dpif_netlink_rtnl_create(const struct netdev_tunnel_config *tnl_cfg,
|
2017-05-18 16:10:29 -04:00
|
|
|
const char *name, enum ovs_vport_type type,
|
|
|
|
const char *kind, uint32_t flags)
|
|
|
|
{
|
2018-01-17 10:02:24 -08:00
|
|
|
enum {
|
|
|
|
/* For performance, we want to use the largest MTU that the system
|
|
|
|
* supports. Most existing tunnels will accept UINT16_MAX, treating it
|
|
|
|
* as the actual max MTU, but some do not. Thus, we use a slightly
|
|
|
|
* smaller value, that should always be safe yet does not noticeably
|
|
|
|
* reduce performance. */
|
|
|
|
MAX_MTU = 65000
|
|
|
|
};
|
|
|
|
|
2017-05-18 16:10:29 -04:00
|
|
|
size_t linkinfo_off, infodata_off;
|
|
|
|
struct ifinfomsg *ifinfo;
|
|
|
|
struct ofpbuf request;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
ofpbuf_init(&request, 0);
|
|
|
|
nl_msg_put_nlmsghdr(&request, 0, RTM_NEWLINK, flags);
|
|
|
|
ifinfo = ofpbuf_put_zeros(&request, sizeof(struct ifinfomsg));
|
|
|
|
ifinfo->ifi_change = ifinfo->ifi_flags = IFF_UP;
|
|
|
|
nl_msg_put_string(&request, IFLA_IFNAME, name);
|
2018-01-17 10:02:24 -08:00
|
|
|
nl_msg_put_u32(&request, IFLA_MTU, MAX_MTU);
|
2017-05-18 16:10:29 -04:00
|
|
|
linkinfo_off = nl_msg_start_nested(&request, IFLA_LINKINFO);
|
|
|
|
nl_msg_put_string(&request, IFLA_INFO_KIND, kind);
|
|
|
|
infodata_off = nl_msg_start_nested(&request, IFLA_INFO_DATA);
|
|
|
|
|
|
|
|
/* tunnel unique info */
|
|
|
|
switch (type) {
|
|
|
|
case OVS_VPORT_TYPE_VXLAN:
|
2017-05-18 16:10:30 -04:00
|
|
|
nl_msg_put_u8(&request, IFLA_VXLAN_LEARNING, 0);
|
|
|
|
nl_msg_put_u8(&request, IFLA_VXLAN_COLLECT_METADATA, 1);
|
|
|
|
nl_msg_put_u8(&request, IFLA_VXLAN_UDP_ZERO_CSUM6_RX, 1);
|
|
|
|
if (tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GBP)) {
|
|
|
|
nl_msg_put_flag(&request, IFLA_VXLAN_GBP);
|
|
|
|
}
|
2017-07-10 15:39:53 -04:00
|
|
|
if (tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GPE)) {
|
|
|
|
nl_msg_put_flag(&request, IFLA_VXLAN_GPE);
|
|
|
|
}
|
2017-05-18 16:10:30 -04:00
|
|
|
nl_msg_put_be16(&request, IFLA_VXLAN_PORT, tnl_cfg->dst_port);
|
|
|
|
break;
|
2017-05-18 16:10:29 -04:00
|
|
|
case OVS_VPORT_TYPE_GRE:
|
2018-03-05 10:11:57 -08:00
|
|
|
case OVS_VPORT_TYPE_ERSPAN:
|
|
|
|
case OVS_VPORT_TYPE_IP6ERSPAN:
|
|
|
|
case OVS_VPORT_TYPE_IP6GRE:
|
2018-06-04 13:14:36 -07:00
|
|
|
nl_msg_put_flag(&request, IFLA_GRE_COLLECT_METADATA);
|
2017-05-18 16:10:31 -04:00
|
|
|
break;
|
2017-05-18 16:10:29 -04:00
|
|
|
case OVS_VPORT_TYPE_GENEVE:
|
2017-05-18 16:10:32 -04:00
|
|
|
nl_msg_put_flag(&request, IFLA_GENEVE_COLLECT_METADATA);
|
|
|
|
nl_msg_put_u8(&request, IFLA_GENEVE_UDP_ZERO_CSUM6_RX, 1);
|
|
|
|
nl_msg_put_be16(&request, IFLA_GENEVE_PORT, tnl_cfg->dst_port);
|
|
|
|
break;
|
2020-12-17 12:48:41 +05:30
|
|
|
case OVS_VPORT_TYPE_BAREUDP:
|
|
|
|
nl_msg_put_be16(&request, IFLA_BAREUDP_ETHERTYPE,
|
|
|
|
tnl_cfg->payload_ethertype);
|
|
|
|
nl_msg_put_u16(&request, IFLA_BAREUDP_SRCPORT_MIN,
|
|
|
|
BAREUDP_SRCPORT_MIN);
|
|
|
|
nl_msg_put_be16(&request, IFLA_BAREUDP_PORT, tnl_cfg->dst_port);
|
|
|
|
if (tnl_cfg->exts & (1 << OVS_BAREUDP_EXT_MULTIPROTO_MODE)) {
|
|
|
|
nl_msg_put_flag(&request, IFLA_BAREUDP_MULTIPROTO_MODE);
|
|
|
|
}
|
|
|
|
break;
|
2017-05-18 16:10:29 -04:00
|
|
|
case OVS_VPORT_TYPE_NETDEV:
|
|
|
|
case OVS_VPORT_TYPE_INTERNAL:
|
2019-11-25 11:19:23 -08:00
|
|
|
case OVS_VPORT_TYPE_GTPU:
|
2023-03-29 14:51:17 +09:00
|
|
|
case OVS_VPORT_TYPE_SRV6:
|
2017-05-18 16:10:29 -04:00
|
|
|
case OVS_VPORT_TYPE_UNSPEC:
|
|
|
|
case __OVS_VPORT_TYPE_MAX:
|
|
|
|
default:
|
|
|
|
err = EOPNOTSUPP;
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
nl_msg_end_nested(&request, infodata_off);
|
|
|
|
nl_msg_end_nested(&request, linkinfo_off);
|
|
|
|
|
|
|
|
err = nl_transact(NETLINK_ROUTE, &request, NULL);
|
2018-05-04 10:14:44 -07:00
|
|
|
if (!err && (type == OVS_VPORT_TYPE_GRE ||
|
|
|
|
type == OVS_VPORT_TYPE_IP6GRE)) {
|
2018-01-17 10:02:25 -08:00
|
|
|
/* Work around a bug in kernel GRE driver, which ignores IFLA_MTU in
|
|
|
|
* RTM_NEWLINK, by setting the MTU again. See
|
2018-07-06 08:28:27 -07:00
|
|
|
* https://bugzilla.redhat.com/show_bug.cgi?id=1488484.
|
|
|
|
*
|
|
|
|
* In case of MAX_MTU exceeds hw max MTU, retry a smaller value. */
|
|
|
|
int err2 = rtnl_set_mtu(name, MAX_MTU, &request);
|
|
|
|
if (err2) {
|
|
|
|
err2 = rtnl_set_mtu(name, 1450, &request);
|
|
|
|
}
|
2018-01-17 10:02:25 -08:00
|
|
|
if (err2) {
|
|
|
|
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
|
|
|
|
|
|
|
|
VLOG_WARN_RL(&rl, "setting MTU of tunnel %s failed (%s)",
|
|
|
|
name, ovs_strerror(err2));
|
|
|
|
}
|
|
|
|
}
|
2017-05-18 16:10:29 -04:00
|
|
|
|
|
|
|
exit:
|
|
|
|
ofpbuf_uninit(&request);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
dpif_netlink_rtnl_port_create(struct netdev *netdev)
|
|
|
|
{
|
|
|
|
const struct netdev_tunnel_config *tnl_cfg;
|
|
|
|
char namebuf[NETDEV_VPORT_NAME_BUFSIZE];
|
|
|
|
enum ovs_vport_type type;
|
|
|
|
const char *name;
|
|
|
|
const char *kind;
|
|
|
|
uint32_t flags;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
type = netdev_to_ovs_vport_type(netdev_get_type(netdev));
|
|
|
|
tnl_cfg = netdev_get_tunnel_config(netdev);
|
|
|
|
if (!tnl_cfg) {
|
2017-07-27 14:40:02 +03:00
|
|
|
return EOPNOTSUPP;
|
2017-05-18 16:10:29 -04:00
|
|
|
}
|
|
|
|
|
2017-07-10 15:39:54 -04:00
|
|
|
kind = vport_type_to_kind(type, tnl_cfg);
|
|
|
|
if (!kind) {
|
|
|
|
return EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
2017-05-18 16:10:29 -04:00
|
|
|
name = netdev_vport_get_dpif_port(netdev, namebuf, sizeof namebuf);
|
|
|
|
flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL;
|
|
|
|
|
|
|
|
err = dpif_netlink_rtnl_create(tnl_cfg, name, type, kind, flags);
|
|
|
|
|
|
|
|
/* If the device exists, validate and/or attempt to recreate it. */
|
|
|
|
if (err == EEXIST) {
|
|
|
|
err = dpif_netlink_rtnl_verify(tnl_cfg, type, name);
|
|
|
|
if (!err) {
|
|
|
|
return 0;
|
|
|
|
}
|
2017-05-19 13:27:34 -07:00
|
|
|
err = dpif_netlink_rtnl_destroy(name);
|
|
|
|
if (err) {
|
|
|
|
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
|
|
|
|
|
|
|
|
VLOG_WARN_RL(&rl, "RTNL device %s exists and cannot be "
|
|
|
|
"deleted: %s", name, ovs_strerror(err));
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
err = dpif_netlink_rtnl_create(tnl_cfg, name, type, kind, flags);
|
2017-05-18 16:10:29 -04:00
|
|
|
}
|
|
|
|
if (err) {
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = dpif_netlink_rtnl_verify(tnl_cfg, type, name);
|
|
|
|
if (err) {
|
|
|
|
int err2 = dpif_netlink_rtnl_destroy(name);
|
|
|
|
|
|
|
|
if (err2) {
|
|
|
|
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
|
|
|
|
|
|
|
|
VLOG_WARN_RL(&rl, "Failed to delete device %s during rtnl port "
|
|
|
|
"creation: %s", name, ovs_strerror(err2));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2017-05-18 16:10:30 -04:00
|
|
|
dpif_netlink_rtnl_port_destroy(const char *name, const char *type)
|
2017-05-18 16:10:29 -04:00
|
|
|
{
|
|
|
|
switch (netdev_to_ovs_vport_type(type)) {
|
|
|
|
case OVS_VPORT_TYPE_VXLAN:
|
|
|
|
case OVS_VPORT_TYPE_GRE:
|
|
|
|
case OVS_VPORT_TYPE_GENEVE:
|
2018-03-21 14:02:25 -07:00
|
|
|
case OVS_VPORT_TYPE_ERSPAN:
|
|
|
|
case OVS_VPORT_TYPE_IP6ERSPAN:
|
|
|
|
case OVS_VPORT_TYPE_IP6GRE:
|
2023-03-29 14:51:17 +09:00
|
|
|
case OVS_VPORT_TYPE_SRV6:
|
2020-12-17 12:48:41 +05:30
|
|
|
case OVS_VPORT_TYPE_BAREUDP:
|
2017-05-18 16:10:32 -04:00
|
|
|
return dpif_netlink_rtnl_destroy(name);
|
2017-05-18 16:10:29 -04:00
|
|
|
case OVS_VPORT_TYPE_NETDEV:
|
|
|
|
case OVS_VPORT_TYPE_INTERNAL:
|
2019-11-25 11:19:23 -08:00
|
|
|
case OVS_VPORT_TYPE_GTPU:
|
2017-05-18 16:10:29 -04:00
|
|
|
case OVS_VPORT_TYPE_UNSPEC:
|
|
|
|
case __OVS_VPORT_TYPE_MAX:
|
|
|
|
default:
|
|
|
|
return EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2017-05-18 16:10:33 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Probe for whether the modules are out-of-tree (openvswitch) or in-tree
|
|
|
|
* (upstream kernel).
|
|
|
|
*
|
|
|
|
* We probe for "ovs_geneve" via rtnetlink. As long as this returns something
|
|
|
|
* other than EOPNOTSUPP we know that the module in use is the out-of-tree one.
|
|
|
|
* This will be used to determine which netlink interface to use when creating
|
|
|
|
* ports; rtnetlink or compat/genetlink.
|
|
|
|
*
|
|
|
|
* See ovs_tunnels_out_of_tree
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
dpif_netlink_rtnl_probe_oot_tunnels(void)
|
|
|
|
{
|
|
|
|
char namebuf[NETDEV_VPORT_NAME_BUFSIZE];
|
|
|
|
struct netdev *netdev = NULL;
|
|
|
|
bool out_of_tree = false;
|
|
|
|
const char *name;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
error = netdev_open("ovs-system-probe", "geneve", &netdev);
|
|
|
|
if (!error) {
|
2017-11-02 12:01:17 -07:00
|
|
|
struct ofpbuf *reply;
|
2017-05-18 16:10:33 -04:00
|
|
|
const struct netdev_tunnel_config *tnl_cfg;
|
|
|
|
|
|
|
|
tnl_cfg = netdev_get_tunnel_config(netdev);
|
|
|
|
if (!tnl_cfg) {
|
2024-07-12 10:30:23 +02:00
|
|
|
netdev_close(netdev);
|
2017-05-18 16:10:33 -04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
name = netdev_vport_get_dpif_port(netdev, namebuf, sizeof namebuf);
|
2017-11-02 12:01:17 -07:00
|
|
|
|
|
|
|
/* The geneve module exists when ovs-vswitchd crashes
|
|
|
|
* and restarts, handle the case here.
|
|
|
|
*/
|
|
|
|
error = dpif_netlink_rtnl_getlink(name, &reply);
|
|
|
|
if (!error) {
|
|
|
|
|
|
|
|
struct nlattr *linkinfo[ARRAY_SIZE(linkinfo_policy)];
|
|
|
|
struct nlattr *rtlink[ARRAY_SIZE(rtlink_policy)];
|
|
|
|
const char *kind;
|
|
|
|
|
|
|
|
if (!nl_policy_parse(reply,
|
|
|
|
NLMSG_HDRLEN + sizeof(struct ifinfomsg),
|
|
|
|
rtlink_policy, rtlink,
|
|
|
|
ARRAY_SIZE(rtlink_policy))
|
|
|
|
|| !nl_parse_nested(rtlink[IFLA_LINKINFO], linkinfo_policy,
|
|
|
|
linkinfo, ARRAY_SIZE(linkinfo_policy))) {
|
|
|
|
VLOG_ABORT("Error fetching Geneve tunnel device %s "
|
|
|
|
"linkinfo", name);
|
|
|
|
}
|
|
|
|
|
|
|
|
kind = nl_attr_get_string(linkinfo[IFLA_INFO_KIND]);
|
|
|
|
|
|
|
|
if (!strcmp(kind, "ovs_geneve")) {
|
|
|
|
out_of_tree = true;
|
|
|
|
} else if (!strcmp(kind, "geneve")) {
|
|
|
|
out_of_tree = false;
|
|
|
|
} else {
|
|
|
|
VLOG_ABORT("Geneve tunnel device %s with kind %s"
|
|
|
|
" not supported", name, kind);
|
|
|
|
}
|
|
|
|
|
|
|
|
ofpbuf_delete(reply);
|
|
|
|
netdev_close(netdev);
|
|
|
|
|
|
|
|
return out_of_tree;
|
|
|
|
}
|
|
|
|
|
2017-05-18 16:10:33 -04:00
|
|
|
error = dpif_netlink_rtnl_create(tnl_cfg, name, OVS_VPORT_TYPE_GENEVE,
|
|
|
|
"ovs_geneve",
|
|
|
|
(NLM_F_REQUEST | NLM_F_ACK
|
|
|
|
| NLM_F_CREATE));
|
|
|
|
if (error != EOPNOTSUPP) {
|
|
|
|
if (!error) {
|
|
|
|
dpif_netlink_rtnl_destroy(name);
|
|
|
|
}
|
|
|
|
out_of_tree = true;
|
|
|
|
}
|
|
|
|
netdev_close(netdev);
|
|
|
|
}
|
|
|
|
|
|
|
|
return out_of_tree;
|
|
|
|
}
|