platform: add ip6gre/ip6gretap tunnels support
Add platform support for IP6GRE and IP6GRETAP tunnels. The former is a virtual tunnel interface for GRE over IPv6 and the latter is the L2 variant. The platform code internally reuses and extends the same structure used by IPv6 tunnels.
This commit is contained in:
@@ -160,6 +160,8 @@ typedef enum {
|
|||||||
NM_LINK_TYPE_GRETAP,
|
NM_LINK_TYPE_GRETAP,
|
||||||
NM_LINK_TYPE_IFB,
|
NM_LINK_TYPE_IFB,
|
||||||
NM_LINK_TYPE_IP6TNL,
|
NM_LINK_TYPE_IP6TNL,
|
||||||
|
NM_LINK_TYPE_IP6GRE,
|
||||||
|
NM_LINK_TYPE_IP6GRETAP,
|
||||||
NM_LINK_TYPE_IPIP,
|
NM_LINK_TYPE_IPIP,
|
||||||
NM_LINK_TYPE_LOOPBACK,
|
NM_LINK_TYPE_LOOPBACK,
|
||||||
NM_LINK_TYPE_MACSEC,
|
NM_LINK_TYPE_MACSEC,
|
||||||
@@ -197,6 +199,8 @@ typedef enum {
|
|||||||
NMP_OBJECT_TYPE_LNK_GRETAP,
|
NMP_OBJECT_TYPE_LNK_GRETAP,
|
||||||
NMP_OBJECT_TYPE_LNK_INFINIBAND,
|
NMP_OBJECT_TYPE_LNK_INFINIBAND,
|
||||||
NMP_OBJECT_TYPE_LNK_IP6TNL,
|
NMP_OBJECT_TYPE_LNK_IP6TNL,
|
||||||
|
NMP_OBJECT_TYPE_LNK_IP6GRE,
|
||||||
|
NMP_OBJECT_TYPE_LNK_IP6GRETAP,
|
||||||
NMP_OBJECT_TYPE_LNK_IPIP,
|
NMP_OBJECT_TYPE_LNK_IPIP,
|
||||||
NMP_OBJECT_TYPE_LNK_MACSEC,
|
NMP_OBJECT_TYPE_LNK_MACSEC,
|
||||||
NMP_OBJECT_TYPE_LNK_MACVLAN,
|
NMP_OBJECT_TYPE_LNK_MACVLAN,
|
||||||
|
@@ -549,6 +549,8 @@ static const LinkDesc linktypes[] = {
|
|||||||
{ NM_LINK_TYPE_GRETAP, "gretap", "gretap", NULL },
|
{ NM_LINK_TYPE_GRETAP, "gretap", "gretap", NULL },
|
||||||
{ NM_LINK_TYPE_IFB, "ifb", "ifb", NULL },
|
{ NM_LINK_TYPE_IFB, "ifb", "ifb", NULL },
|
||||||
{ NM_LINK_TYPE_IP6TNL, "ip6tnl", "ip6tnl", NULL },
|
{ NM_LINK_TYPE_IP6TNL, "ip6tnl", "ip6tnl", NULL },
|
||||||
|
{ NM_LINK_TYPE_IP6GRE, "ip6gre", "ip6gre", NULL },
|
||||||
|
{ NM_LINK_TYPE_IP6GRETAP, "ip6gretap", "ip6gretap", NULL },
|
||||||
{ NM_LINK_TYPE_IPIP, "ipip", "ipip", NULL },
|
{ NM_LINK_TYPE_IPIP, "ipip", "ipip", NULL },
|
||||||
{ NM_LINK_TYPE_LOOPBACK, "loopback", NULL, NULL },
|
{ NM_LINK_TYPE_LOOPBACK, "loopback", NULL, NULL },
|
||||||
{ NM_LINK_TYPE_MACSEC, "macsec", "macsec", NULL },
|
{ NM_LINK_TYPE_MACSEC, "macsec", "macsec", NULL },
|
||||||
@@ -1222,6 +1224,79 @@ _parse_lnk_ip6tnl (const char *kind, struct nlattr *info_data)
|
|||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static NMPObject *
|
||||||
|
_parse_lnk_ip6gre (const char *kind, struct nlattr *info_data)
|
||||||
|
{
|
||||||
|
static const struct nla_policy policy[IFLA_GRE_MAX + 1] = {
|
||||||
|
[IFLA_GRE_LINK] = { .type = NLA_U32 },
|
||||||
|
[IFLA_GRE_IFLAGS] = { .type = NLA_U16 },
|
||||||
|
[IFLA_GRE_OFLAGS] = { .type = NLA_U16 },
|
||||||
|
[IFLA_GRE_IKEY] = { .type = NLA_U32 },
|
||||||
|
[IFLA_GRE_OKEY] = { .type = NLA_U32 },
|
||||||
|
[IFLA_GRE_LOCAL] = { .type = NLA_UNSPEC,
|
||||||
|
.minlen = sizeof (struct in6_addr)},
|
||||||
|
[IFLA_GRE_REMOTE] = { .type = NLA_UNSPEC,
|
||||||
|
.minlen = sizeof (struct in6_addr)},
|
||||||
|
[IFLA_GRE_TTL] = { .type = NLA_U8 },
|
||||||
|
[IFLA_GRE_ENCAP_LIMIT] = { .type = NLA_U8 },
|
||||||
|
[IFLA_GRE_FLOWINFO] = { .type = NLA_U32 },
|
||||||
|
[IFLA_GRE_FLAGS] = { .type = NLA_U32 },
|
||||||
|
};
|
||||||
|
struct nlattr *tb[IFLA_GRE_MAX + 1];
|
||||||
|
int err;
|
||||||
|
NMPObject *obj;
|
||||||
|
NMPlatformLnkIp6Tnl *props;
|
||||||
|
guint32 flowinfo;
|
||||||
|
gboolean is_tap;
|
||||||
|
|
||||||
|
if (!info_data || !kind)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (nm_streq (kind, "ip6gre"))
|
||||||
|
is_tap = FALSE;
|
||||||
|
else if (nm_streq (kind, "ip6gretap"))
|
||||||
|
is_tap = TRUE;
|
||||||
|
else
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
err = nla_parse_nested (tb, IFLA_GRE_MAX, info_data, policy);
|
||||||
|
if (err < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
obj = nmp_object_new (is_tap ? NMP_OBJECT_TYPE_LNK_IP6GRETAP : NMP_OBJECT_TYPE_LNK_IP6GRE, NULL);
|
||||||
|
props = &obj->lnk_ip6tnl;
|
||||||
|
props->is_gre = TRUE;
|
||||||
|
props->is_tap = is_tap;
|
||||||
|
|
||||||
|
if (tb[IFLA_GRE_LINK])
|
||||||
|
props->parent_ifindex = nla_get_u32 (tb[IFLA_GRE_LINK]);
|
||||||
|
if (tb[IFLA_GRE_IFLAGS])
|
||||||
|
props->input_flags = ntohs (nla_get_u16 (tb[IFLA_GRE_IFLAGS]));
|
||||||
|
if (tb[IFLA_GRE_OFLAGS])
|
||||||
|
props->output_flags = ntohs (nla_get_u16 (tb[IFLA_GRE_OFLAGS]));
|
||||||
|
if (tb[IFLA_GRE_IKEY])
|
||||||
|
props->input_key = ntohl (nla_get_u32 (tb[IFLA_GRE_IKEY]));
|
||||||
|
if (tb[IFLA_GRE_OKEY])
|
||||||
|
props->output_key = ntohl (nla_get_u32 (tb[IFLA_GRE_OKEY]));
|
||||||
|
if (tb[IFLA_GRE_LOCAL])
|
||||||
|
memcpy (&props->local, nla_data (tb[IFLA_GRE_LOCAL]), sizeof (props->local));
|
||||||
|
if (tb[IFLA_GRE_REMOTE])
|
||||||
|
memcpy (&props->remote, nla_data (tb[IFLA_GRE_REMOTE]), sizeof (props->remote));
|
||||||
|
if (tb[IFLA_GRE_TTL])
|
||||||
|
props->ttl = nla_get_u8 (tb[IFLA_GRE_TTL]);
|
||||||
|
if (tb[IFLA_GRE_ENCAP_LIMIT])
|
||||||
|
props->encap_limit = nla_get_u8 (tb[IFLA_GRE_ENCAP_LIMIT]);
|
||||||
|
if (tb[IFLA_GRE_FLOWINFO]) {
|
||||||
|
flowinfo = ntohl (nla_get_u32 (tb[IFLA_GRE_FLOWINFO]));
|
||||||
|
props->flow_label = flowinfo & IP6_FLOWINFO_FLOWLABEL_MASK;
|
||||||
|
props->tclass = (flowinfo & IP6_FLOWINFO_TCLASS_MASK) >> IP6_FLOWINFO_TCLASS_SHIFT;
|
||||||
|
}
|
||||||
|
if (tb[IFLA_GRE_FLAGS])
|
||||||
|
props->flags = nla_get_u32 (tb[IFLA_GRE_FLAGS]);
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
static NMPObject *
|
static NMPObject *
|
||||||
@@ -1870,6 +1945,10 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr
|
|||||||
case NM_LINK_TYPE_IP6TNL:
|
case NM_LINK_TYPE_IP6TNL:
|
||||||
lnk_data = _parse_lnk_ip6tnl (nl_info_kind, nl_info_data);
|
lnk_data = _parse_lnk_ip6tnl (nl_info_kind, nl_info_data);
|
||||||
break;
|
break;
|
||||||
|
case NM_LINK_TYPE_IP6GRE:
|
||||||
|
case NM_LINK_TYPE_IP6GRETAP:
|
||||||
|
lnk_data = _parse_lnk_ip6gre (nl_info_kind, nl_info_data);
|
||||||
|
break;
|
||||||
case NM_LINK_TYPE_IPIP:
|
case NM_LINK_TYPE_IPIP:
|
||||||
lnk_data = _parse_lnk_ipip (nl_info_kind, nl_info_data);
|
lnk_data = _parse_lnk_ipip (nl_info_kind, nl_info_data);
|
||||||
break;
|
break;
|
||||||
@@ -4007,6 +4086,8 @@ cache_on_change (NMPlatform *platform,
|
|||||||
&& NM_IN_SET (obj_new->link.type, NM_LINK_TYPE_GRE,
|
&& NM_IN_SET (obj_new->link.type, NM_LINK_TYPE_GRE,
|
||||||
NM_LINK_TYPE_GRETAP,
|
NM_LINK_TYPE_GRETAP,
|
||||||
NM_LINK_TYPE_IP6TNL,
|
NM_LINK_TYPE_IP6TNL,
|
||||||
|
NM_LINK_TYPE_IP6GRE,
|
||||||
|
NM_LINK_TYPE_IP6GRETAP,
|
||||||
NM_LINK_TYPE_INFINIBAND,
|
NM_LINK_TYPE_INFINIBAND,
|
||||||
NM_LINK_TYPE_MACVLAN,
|
NM_LINK_TYPE_MACVLAN,
|
||||||
NM_LINK_TYPE_MACVLAN,
|
NM_LINK_TYPE_MACVLAN,
|
||||||
@@ -5379,6 +5460,8 @@ link_ip6tnl_add (NMPlatform *platform,
|
|||||||
struct nlattr *data;
|
struct nlattr *data;
|
||||||
guint32 flowinfo;
|
guint32 flowinfo;
|
||||||
|
|
||||||
|
g_return_val_if_fail (!props->is_gre, FALSE);
|
||||||
|
|
||||||
nlmsg = _nl_msg_new_link (RTM_NEWLINK,
|
nlmsg = _nl_msg_new_link (RTM_NEWLINK,
|
||||||
NLM_F_CREATE | NLM_F_EXCL,
|
NLM_F_CREATE | NLM_F_EXCL,
|
||||||
0,
|
0,
|
||||||
@@ -5422,6 +5505,68 @@ nla_put_failure:
|
|||||||
g_return_val_if_reached (FALSE);
|
g_return_val_if_reached (FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
link_ip6gre_add (NMPlatform *platform,
|
||||||
|
const char *name,
|
||||||
|
const NMPlatformLnkIp6Tnl *props,
|
||||||
|
const NMPlatformLink **out_link)
|
||||||
|
{
|
||||||
|
nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
|
||||||
|
struct nlattr *info;
|
||||||
|
struct nlattr *data;
|
||||||
|
guint32 flowinfo;
|
||||||
|
|
||||||
|
g_return_val_if_fail (props->is_gre, FALSE);
|
||||||
|
|
||||||
|
nlmsg = _nl_msg_new_link (RTM_NEWLINK,
|
||||||
|
NLM_F_CREATE | NLM_F_EXCL,
|
||||||
|
0,
|
||||||
|
name,
|
||||||
|
0,
|
||||||
|
0);
|
||||||
|
if (!nlmsg)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (!(info = nla_nest_start (nlmsg, IFLA_LINKINFO)))
|
||||||
|
goto nla_put_failure;
|
||||||
|
|
||||||
|
NLA_PUT_STRING (nlmsg, IFLA_INFO_KIND, props->is_tap ? "ip6gretap" : "ip6gre");
|
||||||
|
|
||||||
|
if (!(data = nla_nest_start (nlmsg, IFLA_INFO_DATA)))
|
||||||
|
goto nla_put_failure;
|
||||||
|
|
||||||
|
if (props->parent_ifindex)
|
||||||
|
NLA_PUT_U32 (nlmsg, IFLA_GRE_LINK, props->parent_ifindex);
|
||||||
|
|
||||||
|
NLA_PUT_U32 (nlmsg, IFLA_GRE_IKEY, htonl (props->input_key));
|
||||||
|
NLA_PUT_U32 (nlmsg, IFLA_GRE_OKEY, htonl (props->output_key));
|
||||||
|
NLA_PUT_U16 (nlmsg, IFLA_GRE_IFLAGS, htons (props->input_flags));
|
||||||
|
NLA_PUT_U16 (nlmsg, IFLA_GRE_OFLAGS, htons (props->output_flags));
|
||||||
|
|
||||||
|
if (memcmp (&props->local, &in6addr_any, sizeof (in6addr_any)))
|
||||||
|
NLA_PUT (nlmsg, IFLA_GRE_LOCAL, sizeof (props->local), &props->local);
|
||||||
|
if (memcmp (&props->remote, &in6addr_any, sizeof (in6addr_any)))
|
||||||
|
NLA_PUT (nlmsg, IFLA_GRE_REMOTE, sizeof (props->remote), &props->remote);
|
||||||
|
|
||||||
|
NLA_PUT_U8 (nlmsg, IFLA_GRE_TTL, props->ttl);
|
||||||
|
NLA_PUT_U8 (nlmsg, IFLA_GRE_ENCAP_LIMIT, props->encap_limit);
|
||||||
|
|
||||||
|
flowinfo = props->flow_label & IP6_FLOWINFO_FLOWLABEL_MASK;
|
||||||
|
flowinfo |= (props->tclass << IP6_FLOWINFO_TCLASS_SHIFT)
|
||||||
|
& IP6_FLOWINFO_TCLASS_MASK;
|
||||||
|
NLA_PUT_U32 (nlmsg, IFLA_GRE_FLOWINFO, htonl (flowinfo));
|
||||||
|
NLA_PUT_U32 (nlmsg, IFLA_GRE_FLAGS, props->flags);
|
||||||
|
|
||||||
|
nla_nest_end (nlmsg, data);
|
||||||
|
nla_nest_end (nlmsg, info);
|
||||||
|
|
||||||
|
return do_add_link_with_lookup (platform,
|
||||||
|
props->is_tap ? NM_LINK_TYPE_IP6GRETAP : NM_LINK_TYPE_IP6GRE,
|
||||||
|
name, nlmsg, out_link);
|
||||||
|
nla_put_failure:
|
||||||
|
g_return_val_if_reached (FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
link_ipip_add (NMPlatform *platform,
|
link_ipip_add (NMPlatform *platform,
|
||||||
const char *name,
|
const char *name,
|
||||||
@@ -7314,6 +7459,7 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass)
|
|||||||
|
|
||||||
platform_class->link_gre_add = link_gre_add;
|
platform_class->link_gre_add = link_gre_add;
|
||||||
platform_class->link_ip6tnl_add = link_ip6tnl_add;
|
platform_class->link_ip6tnl_add = link_ip6tnl_add;
|
||||||
|
platform_class->link_ip6gre_add = link_ip6gre_add;
|
||||||
platform_class->link_macsec_add = link_macsec_add;
|
platform_class->link_macsec_add = link_macsec_add;
|
||||||
platform_class->link_macvlan_add = link_macvlan_add;
|
platform_class->link_macvlan_add = link_macvlan_add;
|
||||||
platform_class->link_ipip_add = link_ipip_add;
|
platform_class->link_ipip_add = link_ipip_add;
|
||||||
|
@@ -1872,6 +1872,18 @@ nm_platform_link_get_lnk_ip6tnl (NMPlatform *self, int ifindex, const NMPlatform
|
|||||||
return _link_get_lnk (self, ifindex, NM_LINK_TYPE_IP6TNL, out_link);
|
return _link_get_lnk (self, ifindex, NM_LINK_TYPE_IP6TNL, out_link);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const NMPlatformLnkIp6Tnl *
|
||||||
|
nm_platform_link_get_lnk_ip6gre (NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
|
||||||
|
{
|
||||||
|
return _link_get_lnk (self, ifindex, NM_LINK_TYPE_IP6GRE, out_link);
|
||||||
|
}
|
||||||
|
|
||||||
|
const NMPlatformLnkIp6Tnl *
|
||||||
|
nm_platform_link_get_lnk_ip6gretap (NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
|
||||||
|
{
|
||||||
|
return _link_get_lnk (self, ifindex, NM_LINK_TYPE_IP6GRETAP, out_link);
|
||||||
|
}
|
||||||
|
|
||||||
const NMPlatformLnkIpIp *
|
const NMPlatformLnkIpIp *
|
||||||
nm_platform_link_get_lnk_ipip (NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
|
nm_platform_link_get_lnk_ipip (NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
|
||||||
{
|
{
|
||||||
@@ -2550,6 +2562,7 @@ nm_platform_link_ip6tnl_add (NMPlatform *self,
|
|||||||
|
|
||||||
g_return_val_if_fail (props, NM_PLATFORM_ERROR_BUG);
|
g_return_val_if_fail (props, NM_PLATFORM_ERROR_BUG);
|
||||||
g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG);
|
g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG);
|
||||||
|
g_return_val_if_fail (!props->is_gre, NM_PLATFORM_ERROR_BUG);
|
||||||
|
|
||||||
plerr = _link_add_check_existing (self, name, NM_LINK_TYPE_IP6TNL, out_link);
|
plerr = _link_add_check_existing (self, name, NM_LINK_TYPE_IP6TNL, out_link);
|
||||||
if (plerr != NM_PLATFORM_ERROR_SUCCESS)
|
if (plerr != NM_PLATFORM_ERROR_SUCCESS)
|
||||||
@@ -2563,6 +2576,46 @@ nm_platform_link_ip6tnl_add (NMPlatform *self,
|
|||||||
return NM_PLATFORM_ERROR_SUCCESS;
|
return NM_PLATFORM_ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nm_platform_ip6gre_add:
|
||||||
|
* @self: platform instance
|
||||||
|
* @name: name of the new interface
|
||||||
|
* @props: interface properties
|
||||||
|
* @out_link: on success, the link object
|
||||||
|
*
|
||||||
|
* Create an IPv6 GRE/GRETAP tunnel.
|
||||||
|
*/
|
||||||
|
NMPlatformError
|
||||||
|
nm_platform_link_ip6gre_add (NMPlatform *self,
|
||||||
|
const char *name,
|
||||||
|
const NMPlatformLnkIp6Tnl *props,
|
||||||
|
const NMPlatformLink **out_link)
|
||||||
|
{
|
||||||
|
NMPlatformError plerr;
|
||||||
|
|
||||||
|
_CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG);
|
||||||
|
|
||||||
|
g_return_val_if_fail (props, NM_PLATFORM_ERROR_BUG);
|
||||||
|
g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG);
|
||||||
|
g_return_val_if_fail (props->is_gre, NM_PLATFORM_ERROR_BUG);
|
||||||
|
|
||||||
|
plerr = _link_add_check_existing (self,
|
||||||
|
name,
|
||||||
|
props->is_tap
|
||||||
|
? NM_LINK_TYPE_IP6GRETAP
|
||||||
|
: NM_LINK_TYPE_IP6GRE,
|
||||||
|
out_link);
|
||||||
|
if (plerr != NM_PLATFORM_ERROR_SUCCESS)
|
||||||
|
return plerr;
|
||||||
|
|
||||||
|
_LOGD ("adding link '%s': %s",
|
||||||
|
name, nm_platform_lnk_ip6tnl_to_string (props, NULL, 0));
|
||||||
|
|
||||||
|
if (!klass->link_ip6gre_add (self, name, props, out_link))
|
||||||
|
return NM_PLATFORM_ERROR_UNSPECIFIED;
|
||||||
|
return NM_PLATFORM_ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* nm_platform_ipip_add:
|
* nm_platform_ipip_add:
|
||||||
* @self: platform instance
|
* @self: platform instance
|
||||||
@@ -5118,12 +5171,18 @@ nm_platform_lnk_ip6tnl_to_string (const NMPlatformLnkIp6Tnl *lnk, char *buf, gsi
|
|||||||
char str_encap[30];
|
char str_encap[30];
|
||||||
char str_proto[30];
|
char str_proto[30];
|
||||||
char str_parent_ifindex[30];
|
char str_parent_ifindex[30];
|
||||||
|
char *str_type;
|
||||||
|
|
||||||
if (!nm_utils_to_string_buffer_init_null (lnk, &buf, &len))
|
if (!nm_utils_to_string_buffer_init_null (lnk, &buf, &len))
|
||||||
return buf;
|
return buf;
|
||||||
|
|
||||||
|
if (lnk->is_gre)
|
||||||
|
str_type = lnk->is_tap ? "ip6gretap" : "ip6gre";
|
||||||
|
else
|
||||||
|
str_type = "ip6tnl";
|
||||||
|
|
||||||
g_snprintf (buf, len,
|
g_snprintf (buf, len,
|
||||||
"ip6tnl"
|
"%s" /* type */
|
||||||
"%s" /* remote */
|
"%s" /* remote */
|
||||||
"%s" /* local */
|
"%s" /* local */
|
||||||
"%s" /* parent_ifindex */
|
"%s" /* parent_ifindex */
|
||||||
@@ -5134,6 +5193,7 @@ nm_platform_lnk_ip6tnl_to_string (const NMPlatformLnkIp6Tnl *lnk, char *buf, gsi
|
|||||||
"%s" /* proto */
|
"%s" /* proto */
|
||||||
" flags 0x%x"
|
" flags 0x%x"
|
||||||
"",
|
"",
|
||||||
|
str_type,
|
||||||
nm_sprintf_buf (str_remote, " remote %s", nm_utils_inet6_ntop (&lnk->remote, str_remote1)),
|
nm_sprintf_buf (str_remote, " remote %s", nm_utils_inet6_ntop (&lnk->remote, str_remote1)),
|
||||||
nm_sprintf_buf (str_local, " local %s", nm_utils_inet6_ntop (&lnk->local, str_local1)),
|
nm_sprintf_buf (str_local, " local %s", nm_utils_inet6_ntop (&lnk->local, str_local1)),
|
||||||
lnk->parent_ifindex ? nm_sprintf_buf (str_parent_ifindex, " dev %d", lnk->parent_ifindex) : "",
|
lnk->parent_ifindex ? nm_sprintf_buf (str_parent_ifindex, " dev %d", lnk->parent_ifindex) : "",
|
||||||
@@ -5998,7 +6058,13 @@ nm_platform_lnk_ip6tnl_hash_update (const NMPlatformLnkIp6Tnl *obj, NMHashState
|
|||||||
obj->encap_limit,
|
obj->encap_limit,
|
||||||
obj->proto,
|
obj->proto,
|
||||||
obj->flow_label,
|
obj->flow_label,
|
||||||
obj->flags);
|
obj->flags,
|
||||||
|
obj->input_flags,
|
||||||
|
obj->output_flags,
|
||||||
|
obj->input_key,
|
||||||
|
obj->output_key,
|
||||||
|
(bool) obj->is_gre,
|
||||||
|
(bool) obj->is_tap);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
@@ -6014,6 +6080,12 @@ nm_platform_lnk_ip6tnl_cmp (const NMPlatformLnkIp6Tnl *a, const NMPlatformLnkIp6
|
|||||||
NM_CMP_FIELD (a, b, flow_label);
|
NM_CMP_FIELD (a, b, flow_label);
|
||||||
NM_CMP_FIELD (a, b, proto);
|
NM_CMP_FIELD (a, b, proto);
|
||||||
NM_CMP_FIELD (a, b, flags);
|
NM_CMP_FIELD (a, b, flags);
|
||||||
|
NM_CMP_FIELD (a, b, input_flags);
|
||||||
|
NM_CMP_FIELD (a, b, output_flags);
|
||||||
|
NM_CMP_FIELD (a, b, input_key);
|
||||||
|
NM_CMP_FIELD (a, b, output_key);
|
||||||
|
NM_CMP_FIELD_BOOL (a, b, is_gre);
|
||||||
|
NM_CMP_FIELD_BOOL (a, b, is_tap);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -636,6 +636,14 @@ typedef struct {
|
|||||||
guint8 proto;
|
guint8 proto;
|
||||||
guint flow_label;
|
guint flow_label;
|
||||||
guint32 flags;
|
guint32 flags;
|
||||||
|
|
||||||
|
/* IP6GRE only */
|
||||||
|
guint32 input_key;
|
||||||
|
guint32 output_key;
|
||||||
|
guint16 input_flags;
|
||||||
|
guint16 output_flags;
|
||||||
|
bool is_tap:1;
|
||||||
|
bool is_gre:1;
|
||||||
} NMPlatformLnkIp6Tnl;
|
} NMPlatformLnkIp6Tnl;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -830,6 +838,10 @@ typedef struct {
|
|||||||
const char *name,
|
const char *name,
|
||||||
const NMPlatformLnkIp6Tnl *props,
|
const NMPlatformLnkIp6Tnl *props,
|
||||||
const NMPlatformLink **out_link);
|
const NMPlatformLink **out_link);
|
||||||
|
gboolean (*link_ip6gre_add) (NMPlatform *,
|
||||||
|
const char *name,
|
||||||
|
const NMPlatformLnkIp6Tnl *props,
|
||||||
|
const NMPlatformLink **out_link);
|
||||||
gboolean (*link_ipip_add) (NMPlatform *,
|
gboolean (*link_ipip_add) (NMPlatform *,
|
||||||
const char *name,
|
const char *name,
|
||||||
const NMPlatformLnkIpIp *props,
|
const NMPlatformLnkIpIp *props,
|
||||||
@@ -1201,6 +1213,8 @@ const NMPObject *nm_platform_link_get_lnk (NMPlatform *self, int ifindex, NMLink
|
|||||||
const NMPlatformLnkGre *nm_platform_link_get_lnk_gre (NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
|
const NMPlatformLnkGre *nm_platform_link_get_lnk_gre (NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
|
||||||
const NMPlatformLnkGre *nm_platform_link_get_lnk_gretap (NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
|
const NMPlatformLnkGre *nm_platform_link_get_lnk_gretap (NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
|
||||||
const NMPlatformLnkIp6Tnl *nm_platform_link_get_lnk_ip6tnl (NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
|
const NMPlatformLnkIp6Tnl *nm_platform_link_get_lnk_ip6tnl (NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
|
||||||
|
const NMPlatformLnkIp6Tnl *nm_platform_link_get_lnk_ip6gre (NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
|
||||||
|
const NMPlatformLnkIp6Tnl *nm_platform_link_get_lnk_ip6gretap (NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
|
||||||
const NMPlatformLnkIpIp *nm_platform_link_get_lnk_ipip (NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
|
const NMPlatformLnkIpIp *nm_platform_link_get_lnk_ipip (NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
|
||||||
const NMPlatformLnkInfiniband *nm_platform_link_get_lnk_infiniband (NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
|
const NMPlatformLnkInfiniband *nm_platform_link_get_lnk_infiniband (NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
|
||||||
const NMPlatformLnkIpIp *nm_platform_link_get_lnk_ipip (NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
|
const NMPlatformLnkIpIp *nm_platform_link_get_lnk_ipip (NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
|
||||||
@@ -1285,6 +1299,10 @@ NMPlatformError nm_platform_link_ip6tnl_add (NMPlatform *self,
|
|||||||
const char *name,
|
const char *name,
|
||||||
const NMPlatformLnkIp6Tnl *props,
|
const NMPlatformLnkIp6Tnl *props,
|
||||||
const NMPlatformLink **out_link);
|
const NMPlatformLink **out_link);
|
||||||
|
NMPlatformError nm_platform_link_ip6gre_add (NMPlatform *self,
|
||||||
|
const char *name,
|
||||||
|
const NMPlatformLnkIp6Tnl *props,
|
||||||
|
const NMPlatformLink **out_link);
|
||||||
NMPlatformError nm_platform_link_ipip_add (NMPlatform *self,
|
NMPlatformError nm_platform_link_ipip_add (NMPlatform *self,
|
||||||
const char *name,
|
const char *name,
|
||||||
const NMPlatformLnkIpIp *props,
|
const NMPlatformLnkIpIp *props,
|
||||||
|
@@ -2745,6 +2745,28 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = {
|
|||||||
.cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_lnk_ip6tnl_hash_update,
|
.cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_lnk_ip6tnl_hash_update,
|
||||||
.cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_ip6tnl_cmp,
|
.cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_ip6tnl_cmp,
|
||||||
},
|
},
|
||||||
|
[NMP_OBJECT_TYPE_LNK_IP6GRE - 1] = {
|
||||||
|
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
|
||||||
|
.obj_type = NMP_OBJECT_TYPE_LNK_IP6GRE,
|
||||||
|
.sizeof_data = sizeof (NMPObjectLnkIp6Tnl),
|
||||||
|
.sizeof_public = sizeof (NMPlatformLnkIp6Tnl),
|
||||||
|
.obj_type_name = "ip6gre",
|
||||||
|
.lnk_link_type = NM_LINK_TYPE_IP6GRE,
|
||||||
|
.cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_ip6tnl_to_string,
|
||||||
|
.cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_lnk_ip6tnl_hash_update,
|
||||||
|
.cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_ip6tnl_cmp,
|
||||||
|
},
|
||||||
|
[NMP_OBJECT_TYPE_LNK_IP6GRETAP - 1] = {
|
||||||
|
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
|
||||||
|
.obj_type = NMP_OBJECT_TYPE_LNK_IP6GRETAP,
|
||||||
|
.sizeof_data = sizeof (NMPObjectLnkIp6Tnl),
|
||||||
|
.sizeof_public = sizeof (NMPlatformLnkIp6Tnl),
|
||||||
|
.obj_type_name = "ip6gretap",
|
||||||
|
.lnk_link_type = NM_LINK_TYPE_IP6GRETAP,
|
||||||
|
.cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_ip6tnl_to_string,
|
||||||
|
.cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_lnk_ip6tnl_hash_update,
|
||||||
|
.cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_ip6tnl_cmp,
|
||||||
|
},
|
||||||
[NMP_OBJECT_TYPE_LNK_IPIP - 1] = {
|
[NMP_OBJECT_TYPE_LNK_IPIP - 1] = {
|
||||||
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
|
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
|
||||||
.obj_type = NMP_OBJECT_TYPE_LNK_IPIP,
|
.obj_type = NMP_OBJECT_TYPE_LNK_IPIP,
|
||||||
|
@@ -1297,6 +1297,7 @@ nmtstp_link_ip6tnl_add (NMPlatform *platform,
|
|||||||
gboolean tclass_inherit;
|
gboolean tclass_inherit;
|
||||||
|
|
||||||
g_assert (nm_utils_is_valid_iface_name (name, NULL));
|
g_assert (nm_utils_is_valid_iface_name (name, NULL));
|
||||||
|
g_assert (!lnk->is_gre);
|
||||||
|
|
||||||
external_command = nmtstp_run_command_check_external (external_command);
|
external_command = nmtstp_run_command_check_external (external_command);
|
||||||
|
|
||||||
@@ -1343,6 +1344,56 @@ nmtstp_link_ip6tnl_add (NMPlatform *platform,
|
|||||||
return pllink;
|
return pllink;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const NMPlatformLink *
|
||||||
|
nmtstp_link_ip6gre_add (NMPlatform *platform,
|
||||||
|
gboolean external_command,
|
||||||
|
const char *name,
|
||||||
|
const NMPlatformLnkIp6Tnl *lnk)
|
||||||
|
{
|
||||||
|
const NMPlatformLink *pllink = NULL;
|
||||||
|
gboolean success;
|
||||||
|
char buffer[INET6_ADDRSTRLEN];
|
||||||
|
char tclass[20];
|
||||||
|
gboolean tclass_inherit;
|
||||||
|
|
||||||
|
g_assert (nm_utils_is_valid_iface_name (name, NULL));
|
||||||
|
g_assert (lnk->is_gre);
|
||||||
|
|
||||||
|
external_command = nmtstp_run_command_check_external (external_command);
|
||||||
|
|
||||||
|
_init_platform (&platform, external_command);
|
||||||
|
|
||||||
|
if (external_command) {
|
||||||
|
gs_free char *dev = NULL;
|
||||||
|
|
||||||
|
if (lnk->parent_ifindex)
|
||||||
|
dev = g_strdup_printf ("dev %s", nm_platform_link_get_name (platform, lnk->parent_ifindex));
|
||||||
|
|
||||||
|
tclass_inherit = NM_FLAGS_HAS (lnk->flags, IP6_TNL_F_USE_ORIG_TCLASS);
|
||||||
|
|
||||||
|
success = !nmtstp_run_command ("ip link add %s type %s %s local %s remote %s ttl %u tclass %s flowlabel %x",
|
||||||
|
name,
|
||||||
|
lnk->is_tap ? "ip6gretap" : "ip6gre",
|
||||||
|
dev,
|
||||||
|
nm_utils_inet6_ntop (&lnk->local, NULL),
|
||||||
|
nm_utils_inet6_ntop (&lnk->remote, buffer),
|
||||||
|
lnk->ttl,
|
||||||
|
tclass_inherit ? "inherit" : nm_sprintf_buf (tclass, "%02x", lnk->tclass),
|
||||||
|
lnk->flow_label);
|
||||||
|
if (success) {
|
||||||
|
pllink = nmtstp_assert_wait_for_link (platform,
|
||||||
|
name,
|
||||||
|
lnk->is_tap ? NM_LINK_TYPE_IP6GRETAP : NM_LINK_TYPE_IP6GRE,
|
||||||
|
100);
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
success = nm_platform_link_ip6gre_add (platform, name, lnk, &pllink) == NM_PLATFORM_ERROR_SUCCESS;
|
||||||
|
|
||||||
|
_assert_pllink (platform, success, pllink, name, lnk->is_tap ? NM_LINK_TYPE_IP6GRETAP : NM_LINK_TYPE_IP6GRE);
|
||||||
|
|
||||||
|
return pllink;
|
||||||
|
}
|
||||||
|
|
||||||
const NMPlatformLink *
|
const NMPlatformLink *
|
||||||
nmtstp_link_ipip_add (NMPlatform *platform,
|
nmtstp_link_ipip_add (NMPlatform *platform,
|
||||||
gboolean external_command,
|
gboolean external_command,
|
||||||
|
@@ -297,6 +297,10 @@ const NMPlatformLink *nmtstp_link_ip6tnl_add (NMPlatform *platform,
|
|||||||
gboolean external_command,
|
gboolean external_command,
|
||||||
const char *name,
|
const char *name,
|
||||||
const NMPlatformLnkIp6Tnl *lnk);
|
const NMPlatformLnkIp6Tnl *lnk);
|
||||||
|
const NMPlatformLink *nmtstp_link_ip6gre_add (NMPlatform *platform,
|
||||||
|
gboolean external_command,
|
||||||
|
const char *name,
|
||||||
|
const NMPlatformLnkIp6Tnl *lnk);
|
||||||
const NMPlatformLink *nmtstp_link_ipip_add (NMPlatform *platform,
|
const NMPlatformLink *nmtstp_link_ipip_add (NMPlatform *platform,
|
||||||
gboolean external_command,
|
gboolean external_command,
|
||||||
const char *name,
|
const char *name,
|
||||||
|
@@ -819,6 +819,58 @@ test_software_detect (gconstpointer user_data)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case NM_LINK_TYPE_IP6GRE: {
|
||||||
|
NMPlatformLnkIp6Tnl lnk_ip6tnl = { };
|
||||||
|
gboolean gracefully_skip = FALSE;
|
||||||
|
|
||||||
|
if (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, "ip6gre0")) {
|
||||||
|
/* Seems that the ip6_tunnel module is not loaded... try to load it. */
|
||||||
|
gracefully_skip = nm_utils_modprobe (NULL, TRUE, "ip6_gre", NULL) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
lnk_ip6tnl.local = *nmtst_inet6_from_string ("fd01::42");
|
||||||
|
lnk_ip6tnl.remote = *nmtst_inet6_from_string ("fd01::aaaa");
|
||||||
|
lnk_ip6tnl.parent_ifindex = ifindex_parent;
|
||||||
|
lnk_ip6tnl.tclass = 21;
|
||||||
|
lnk_ip6tnl.flow_label = 1338;
|
||||||
|
lnk_ip6tnl.is_gre = TRUE;
|
||||||
|
|
||||||
|
if (!nmtstp_link_ip6gre_add (NULL, ext, DEVICE_NAME, &lnk_ip6tnl)) {
|
||||||
|
if (gracefully_skip) {
|
||||||
|
g_test_skip ("Cannot create ip6gre tunnel because of missing ip6_gre module (modprobe ip6_gre)");
|
||||||
|
goto out_delete_parent;
|
||||||
|
}
|
||||||
|
g_error ("Failed adding IP6GRE tunnel");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case NM_LINK_TYPE_IP6GRETAP: {
|
||||||
|
NMPlatformLnkIp6Tnl lnk_ip6tnl = { };
|
||||||
|
gboolean gracefully_skip = FALSE;
|
||||||
|
|
||||||
|
if (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, "ip6gre0")) {
|
||||||
|
/* Seems that the ip6_tunnel module is not loaded... try to load it. */
|
||||||
|
gracefully_skip = nm_utils_modprobe (NULL, TRUE, "ip6_gre", NULL) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
lnk_ip6tnl.local = *nmtst_inet6_from_string ("fe80::abcd");
|
||||||
|
lnk_ip6tnl.remote = *nmtst_inet6_from_string ("fc01::bbbb");
|
||||||
|
lnk_ip6tnl.parent_ifindex = ifindex_parent;
|
||||||
|
lnk_ip6tnl.ttl = 10;
|
||||||
|
lnk_ip6tnl.tclass = 22;
|
||||||
|
lnk_ip6tnl.flow_label = 1339;
|
||||||
|
lnk_ip6tnl.is_gre = TRUE;
|
||||||
|
lnk_ip6tnl.is_tap = TRUE;
|
||||||
|
|
||||||
|
if (!nmtstp_link_ip6gre_add (NULL, ext, DEVICE_NAME, &lnk_ip6tnl)) {
|
||||||
|
if (gracefully_skip) {
|
||||||
|
g_test_skip ("Cannot create ip6gretap tunnel because of missing ip6_gre module (modprobe ip6_gre)");
|
||||||
|
goto out_delete_parent;
|
||||||
|
}
|
||||||
|
g_error ("Failed adding IP6GRETAP tunnel");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case NM_LINK_TYPE_MACVLAN: {
|
case NM_LINK_TYPE_MACVLAN: {
|
||||||
NMPlatformLnkMacvlan lnk_macvlan = { };
|
NMPlatformLnkMacvlan lnk_macvlan = { };
|
||||||
const NMPlatformLink *dummy;
|
const NMPlatformLink *dummy;
|
||||||
@@ -1036,6 +1088,33 @@ test_software_detect (gconstpointer user_data)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case NM_LINK_TYPE_IP6GRE: {
|
||||||
|
const NMPlatformLnkIp6Tnl *plnk = &lnk->lnk_ip6tnl;
|
||||||
|
|
||||||
|
g_assert (plnk == nm_platform_link_get_lnk_ip6gre (NM_PLATFORM_GET, ifindex, NULL));
|
||||||
|
g_assert_cmpint (plnk->parent_ifindex, ==, ifindex_parent);
|
||||||
|
nmtst_assert_ip6_address (&plnk->local, "fd01::42");
|
||||||
|
nmtst_assert_ip6_address (&plnk->remote, "fd01::aaaa");
|
||||||
|
g_assert_cmpint (plnk->tclass, ==, 21);
|
||||||
|
g_assert_cmpint (plnk->flow_label, ==, 1338);
|
||||||
|
g_assert_cmpint (plnk->is_gre, ==, TRUE);
|
||||||
|
g_assert_cmpint (plnk->is_tap, ==, FALSE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case NM_LINK_TYPE_IP6GRETAP: {
|
||||||
|
const NMPlatformLnkIp6Tnl *plnk = &lnk->lnk_ip6tnl;
|
||||||
|
|
||||||
|
g_assert (plnk == nm_platform_link_get_lnk_ip6gretap (NM_PLATFORM_GET, ifindex, NULL));
|
||||||
|
g_assert_cmpint (plnk->parent_ifindex, ==, ifindex_parent);
|
||||||
|
nmtst_assert_ip6_address (&plnk->local, "fe80::abcd");
|
||||||
|
nmtst_assert_ip6_address (&plnk->remote, "fc01::bbbb");
|
||||||
|
g_assert_cmpint (plnk->ttl, ==, 10);
|
||||||
|
g_assert_cmpint (plnk->tclass, ==, 22);
|
||||||
|
g_assert_cmpint (plnk->flow_label, ==, 1339);
|
||||||
|
g_assert_cmpint (plnk->is_gre, ==, TRUE);
|
||||||
|
g_assert_cmpint (plnk->is_tap, ==, TRUE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case NM_LINK_TYPE_IPIP: {
|
case NM_LINK_TYPE_IPIP: {
|
||||||
const NMPlatformLnkIpIp *plnk = &lnk->lnk_ipip;
|
const NMPlatformLnkIpIp *plnk = &lnk->lnk_ipip;
|
||||||
|
|
||||||
@@ -2668,6 +2747,8 @@ _nmtstp_setup_tests (void)
|
|||||||
test_software_detect_add ("/link/software/detect/gretap", NM_LINK_TYPE_GRETAP, 0);
|
test_software_detect_add ("/link/software/detect/gretap", NM_LINK_TYPE_GRETAP, 0);
|
||||||
test_software_detect_add ("/link/software/detect/ip6tnl/0", NM_LINK_TYPE_IP6TNL, 0);
|
test_software_detect_add ("/link/software/detect/ip6tnl/0", NM_LINK_TYPE_IP6TNL, 0);
|
||||||
test_software_detect_add ("/link/software/detect/ip6tnl/1", NM_LINK_TYPE_IP6TNL, 1);
|
test_software_detect_add ("/link/software/detect/ip6tnl/1", NM_LINK_TYPE_IP6TNL, 1);
|
||||||
|
test_software_detect_add ("/link/software/detect/ip6gre", NM_LINK_TYPE_IP6GRE, 0);
|
||||||
|
test_software_detect_add ("/link/software/detect/ip6gretap", NM_LINK_TYPE_IP6GRETAP, 0);
|
||||||
test_software_detect_add ("/link/software/detect/ipip", NM_LINK_TYPE_IPIP, 0);
|
test_software_detect_add ("/link/software/detect/ipip", NM_LINK_TYPE_IPIP, 0);
|
||||||
test_software_detect_add ("/link/software/detect/macvlan", NM_LINK_TYPE_MACVLAN, 0);
|
test_software_detect_add ("/link/software/detect/macvlan", NM_LINK_TYPE_MACVLAN, 0);
|
||||||
test_software_detect_add ("/link/software/detect/macvtap", NM_LINK_TYPE_MACVTAP, 0);
|
test_software_detect_add ("/link/software/detect/macvtap", NM_LINK_TYPE_MACVTAP, 0);
|
||||||
|
Reference in New Issue
Block a user