platform: support creating non-persistant TUN/TAP devices

For completeness, extend the API to support non-persistant
device. That requires that nm_platform_link_tun_add()
returns the file descriptor.

While NetworkManager doesn't create such devices itself,
it recognizes the IFLA_TUN_PERSIST / IFF_PERSIST flag.
Since ip-tuntap (obviously) cannot create such devices,
we cannot add a test for how non-persistent devices look
in the platform cache. Well, we could instead add them
with ioctl directly, but instead, just extend the platform
API to allow for that.

Also, use the function from test-lldp.c to (optionally) use
nm_platform_link_tun_add() to create the tap device.
This commit is contained in:
Thomas Haller 2018-04-05 18:23:43 +02:00
parent f21ff48a84
commit ef93f6caad
8 changed files with 87 additions and 34 deletions

View file

@ -264,7 +264,8 @@ create_and_realize (NMDevice *device,
plerr = nm_platform_link_tun_add (nm_device_get_platform (device),
iface,
&props,
out_plink);
out_plink,
NULL);
if (plerr != NM_PLATFORM_ERROR_SUCCESS) {
g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
"Failed to create TUN/TAP interface '%s' for '%s': %s",

View file

@ -347,8 +347,7 @@ static void
_test_recv_fixture_setup (TestRecvFixture *fixture, gconstpointer user_data)
{
const NMPlatformLink *link;
struct ifreq ifr = { };
int fd, s;
nm_auto_close int fd = -1;
fd = open ("/dev/net/tun", O_RDWR | O_CLOEXEC);
if (fd == -1) {
@ -357,20 +356,45 @@ _test_recv_fixture_setup (TestRecvFixture *fixture, gconstpointer user_data)
return;
}
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
nm_utils_ifname_cpy (ifr.ifr_name, TEST_IFNAME);
g_assert (ioctl (fd, TUNSETIFF, &ifr) >= 0);
if (nmtst_get_rand_bool ()) {
const NMPlatformLnkTun lnk = {
.type = IFF_TAP,
.pi = FALSE,
.vnet_hdr = FALSE,
.multi_queue = FALSE,
.persist = FALSE,
};
/* Bring the interface up */
s = socket (AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
g_assert (s >= 0);
ifr.ifr_flags |= IFF_UP;
g_assert (ioctl (s, SIOCSIFFLAGS, &ifr) >= 0);
nm_close (s);
nm_close (nm_steal_fd (&fd));
link = nmtstp_link_tun_add (NM_PLATFORM_GET,
FALSE,
TEST_IFNAME,
&lnk,
&fd);
g_assert (link);
nmtstp_link_set_updown (NM_PLATFORM_GET, -1, link->ifindex, TRUE);
link = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, TEST_IFNAME, NM_LINK_TYPE_TUN, 0);
} else {
int s;
struct ifreq ifr = { };
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
nm_utils_ifname_cpy (ifr.ifr_name, TEST_IFNAME);
g_assert (ioctl (fd, TUNSETIFF, &ifr) >= 0);
/* Bring the interface up */
s = socket (AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
g_assert (s >= 0);
ifr.ifr_flags |= IFF_UP;
g_assert (ioctl (s, SIOCSIFFLAGS, &ifr) >= 0);
nm_close (s);
link = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, TEST_IFNAME, NM_LINK_TYPE_TUN, 100);
}
link = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, TEST_IFNAME, NM_LINK_TYPE_TUN, 100);
fixture->ifindex = link->ifindex;
fixture->fd = fd;
fixture->fd = nm_steal_fd (&fd);
memcpy (fixture->mac, link->addr.data, ETH_ALEN);
}

View file

@ -5596,15 +5596,15 @@ static gboolean
link_tun_add (NMPlatform *platform,
const char *name,
const NMPlatformLnkTun *props,
const NMPlatformLink **out_link)
const NMPlatformLink **out_link,
int *out_fd)
{
const NMPObject *obj;
struct ifreq ifr = { };
nm_auto_close int fd = -1;
if ( !NM_IN_SET (props->type, IFF_TAP, IFF_TUN)
|| !props->persist)
g_return_val_if_reached (FALSE);
nm_assert (NM_IN_SET (props->type, IFF_TAP, IFF_TUN));
nm_assert (props->persist || out_fd);
fd = open ("/dev/net/tun", O_RDWR | O_CLOEXEC);
if (fd < 0)
@ -5629,18 +5629,23 @@ link_tun_add (NMPlatform *platform,
return FALSE;
}
if (ioctl (fd, TUNSETPERSIST, (int) !!props->persist))
return FALSE;
if (props->persist) {
if (ioctl (fd, TUNSETPERSIST, 1))
return FALSE;
}
do_request_link (platform, 0, name);
obj = nmp_cache_lookup_link_full (nm_platform_get_cache (platform),
0, name, FALSE,
NM_LINK_TYPE_TUN,
NULL, NULL);
if (out_link)
*out_link = obj ? &obj->link : NULL;
return !!obj;
if (!obj)
return FALSE;
NM_SET_OUT (out_link, &obj->link);
NM_SET_OUT (out_fd, nm_steal_fd (&fd));
return TRUE;
}
static gboolean

View file

@ -2046,6 +2046,11 @@ nm_platform_link_vxlan_add (NMPlatform *self,
* @vnet_hdr: whether to set the IFF_VNET_HDR flag
* @multi_queue: whether to set the IFF_MULTI_QUEUE flag
* @out_link: on success, the link object
* @out_fd: (allow-none): if give, return the file descriptor for the
* created device. Note that when creating a non-persistent device,
* this argument is mandatory, otherwise it makes no sense
* to create such an interface.
* The caller is responsible for closing this file descriptor.
*
* Create a TUN or TAP interface.
*/
@ -2053,7 +2058,8 @@ NMPlatformError
nm_platform_link_tun_add (NMPlatform *self,
const char *name,
const NMPlatformLnkTun *props,
const NMPlatformLink **out_link)
const NMPlatformLink **out_link,
int *out_fd)
{
char b[255];
NMPlatformError plerr;
@ -2062,6 +2068,13 @@ nm_platform_link_tun_add (NMPlatform *self,
g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG);
g_return_val_if_fail (props, NM_PLATFORM_ERROR_BUG);
g_return_val_if_fail (NM_IN_SET (props->type, IFF_TUN, IFF_TAP), NM_PLATFORM_ERROR_BUG);
/* creating a non-persistant device requires that the caller handles
* the file descriptor. */
g_return_val_if_fail (props->persist || out_fd, NM_PLATFORM_ERROR_BUG);
NM_SET_OUT (out_fd, -1);
plerr = _link_add_check_existing (self, name, NM_LINK_TYPE_TUN, out_link);
if (plerr != NM_PLATFORM_ERROR_SUCCESS)
@ -2069,7 +2082,7 @@ nm_platform_link_tun_add (NMPlatform *self,
_LOGD ("link: adding tun '%s' %s",
name, nm_platform_lnk_tun_to_string (props, b, sizeof (b)));
if (!klass->link_tun_add (self, name, props, out_link))
if (!klass->link_tun_add (self, name, props, out_link, out_fd))
return NM_PLATFORM_ERROR_UNSPECIFIED;
return NM_PLATFORM_ERROR_SUCCESS;
}

View file

@ -853,7 +853,8 @@ typedef struct {
gboolean (*link_tun_add) (NMPlatform *platform,
const char *name,
const NMPlatformLnkTun *props,
const NMPlatformLink **out_link);
const NMPlatformLink **out_link,
int *out_fd);
gboolean (*infiniband_partition_add) (NMPlatform *, int parent, int p_key, const NMPlatformLink **out_link);
gboolean (*infiniband_partition_delete) (NMPlatform *, int parent, int p_key);
@ -1286,7 +1287,8 @@ NMPlatformError nm_platform_link_sit_add (NMPlatform *self,
NMPlatformError nm_platform_link_tun_add (NMPlatform *self,
const char *name,
const NMPlatformLnkTun *props,
const NMPlatformLink **out_link);
const NMPlatformLink **out_link,
int *out_fd);
const NMPlatformIP6Address *nm_platform_ip6_address_get (NMPlatform *self, int ifindex, struct in6_addr address);

View file

@ -1472,7 +1472,8 @@ const NMPlatformLink *
nmtstp_link_tun_add (NMPlatform *platform,
gboolean external_command,
const char *name,
const NMPlatformLnkTun *lnk)
const NMPlatformLnkTun *lnk,
int *out_fd)
{
const NMPlatformLink *pllink = NULL;
NMPlatformError plerr;
@ -1481,6 +1482,7 @@ nmtstp_link_tun_add (NMPlatform *platform,
g_assert (nm_utils_is_valid_iface_name (name, NULL));
g_assert (lnk);
g_assert (NM_IN_SET (lnk->type, IFF_TUN, IFF_TAP));
g_assert (!out_fd || *out_fd == -1);
if (!lnk->persist) {
/* ip tuntap does not support non-persistent devices.
@ -1522,7 +1524,8 @@ nmtstp_link_tun_add (NMPlatform *platform,
else
g_error ("failure to add tun/tap device via ip-route");
} else {
plerr = nm_platform_link_tun_add (platform, name, lnk, &pllink);
g_assert (lnk->persist || out_fd);
plerr = nm_platform_link_tun_add (platform, name, lnk, &pllink, out_fd);
g_assert_cmpint (plerr, ==, NM_PLATFORM_ERROR_SUCCESS);
}

View file

@ -313,7 +313,8 @@ const NMPlatformLink *nmtstp_link_sit_add (NMPlatform *platform,
const NMPlatformLink *nmtstp_link_tun_add (NMPlatform *platform,
gboolean external_command,
const char *name,
const NMPlatformLnkTun *lnk);
const NMPlatformLnkTun *lnk,
int *out_fd);
const NMPlatformLink *nmtstp_link_vxlan_add (NMPlatform *platform,
gboolean external_command,
const char *name,

View file

@ -699,6 +699,7 @@ test_software_detect (gconstpointer user_data)
guint i_step;
const gboolean ext = test_data->external_command;
NMPlatformLnkTun lnk_tun;
nm_auto_close int tun_fd = -1;
nmtstp_run_command_check ("ip link add %s type dummy", PARENT_NAME);
ifindex_parent = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, PARENT_NAME, NM_LINK_TYPE_DUMMY, 100)->ifindex;
@ -910,9 +911,9 @@ test_software_detect (gconstpointer user_data)
.vnet_hdr = nmtst_get_rand_bool (),
.multi_queue = nmtst_get_rand_bool (),
/* arguably, adding non-persistant devices from within NetworkManager makes
* no sense. */
.persist = TRUE,
/* if we add the device via iproute2 (external), we can only
* create persistent devices. */
.persist = (ext == 1) ? TRUE : nmtst_get_rand_bool (),
};
break;
default:
@ -920,7 +921,10 @@ test_software_detect (gconstpointer user_data)
break;
}
g_assert (nmtstp_link_tun_add (NULL, ext, DEVICE_NAME, &lnk_tun));
g_assert (nmtstp_link_tun_add (NULL, ext, DEVICE_NAME, &lnk_tun,
(!lnk_tun.persist || nmtst_get_rand_bool ())
? &tun_fd
: NULL));
break;
}
default: