diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 2e0c871df..b7506e7d1 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -4235,6 +4236,66 @@ link_vlan_change (NMPlatform *platform, return do_change_link (platform, ifindex, nlmsg) == NM_PLATFORM_ERROR_SUCCESS; } +static int +tun_add (NMPlatform *platform, const char *name, gboolean tap, + gint64 owner, gint64 group, gboolean pi, gboolean vnet_hdr, + gboolean multi_queue, NMPlatformLink *out_link) +{ + const NMPObject *obj; + struct ifreq ifr = { }; + int fd; + + _LOGD ("link: add %s '%s' owner %" G_GINT64_FORMAT " group %" G_GINT64_FORMAT, + tap ? "tap" : "tun", name, owner, group); + + fd = open ("/dev/net/tun", O_RDWR); + if (fd < 0) + return FALSE; + + strncpy (ifr.ifr_name, name, IFNAMSIZ); + ifr.ifr_flags = tap ? IFF_TAP : IFF_TUN; + + if (!pi) + ifr.ifr_flags |= IFF_NO_PI; + if (vnet_hdr) + ifr.ifr_flags |= IFF_VNET_HDR; + if (multi_queue) + ifr.ifr_flags |= NM_IFF_MULTI_QUEUE; + + if (ioctl (fd, TUNSETIFF, &ifr)) { + close (fd); + return FALSE; + } + + if (owner >= 0 && owner < G_MAXINT32) { + if (ioctl (fd, TUNSETOWNER, (uid_t) owner)) { + close (fd); + return FALSE; + } + } + + if (group >= 0 && group < G_MAXINT32) { + if (ioctl (fd, TUNSETGROUP, (gid_t) group)) { + close (fd); + return FALSE; + } + } + + if (ioctl (fd, TUNSETPERSIST, 1)) { + close (fd); + return FALSE; + } + do_request_link (platform, 0, name, TRUE); + obj = nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, + 0, name, FALSE, + tap ? NM_LINK_TYPE_TAP : NM_LINK_TYPE_TUN, + NULL, NULL); + if (out_link && obj) + *out_link = obj->link; + + return !!obj; +} + static gboolean link_enslave (NMPlatform *platform, int master, int slave) { @@ -5444,6 +5505,8 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->vlan_add = vlan_add; platform_class->link_vlan_change = link_vlan_change; + platform_class->tun_add = tun_add; + platform_class->infiniband_partition_add = infiniband_partition_add; platform_class->wifi_get_capabilities = wifi_get_capabilities; diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 5124db614..6988d1a5f 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -1531,6 +1531,44 @@ nm_platform_vlan_add (NMPlatform *self, return NM_PLATFORM_ERROR_SUCCESS; } +/** + * nm_platform_tun_add: + * @self: platform instance + * @name: new interface name + * @tap: whether the interface is a TAP + * @owner: interface owner or -1 + * @group: interface group or -1 + * @pi: whether to clear the IFF_NO_PI flag + * @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 + * + * Create a TUN or TAP interface. + */ +NMPlatformError +nm_platform_tun_add (NMPlatform *self, const char *name, gboolean tap, + gint64 owner, gint64 group, gboolean pi, gboolean vnet_hdr, + gboolean multi_queue, NMPlatformLink *out_link) +{ + NMPlatformError plerr; + + _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); + + g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (klass->tun_add, NM_PLATFORM_ERROR_BUG); + + plerr = _link_add_check_existing (self, name, tap ? NM_LINK_TYPE_TAP : NM_LINK_TYPE_TUN, out_link); + if (plerr != NM_PLATFORM_ERROR_SUCCESS) + return plerr; + + _LOGD ("link: adding %s '%s' owner %" G_GINT64_FORMAT " group %" G_GINT64_FORMAT, + tap ? "tap" : "tun", name, owner, group); + if (!klass->tun_add (self, name, tap, owner, group, pi, vnet_hdr, multi_queue, out_link)) + return NM_PLATFORM_ERROR_UNSPECIFIED; + return NM_PLATFORM_ERROR_SUCCESS; +} + + gboolean nm_platform_master_set_option (NMPlatform *self, int ifindex, const char *option, const char *value) { @@ -1844,13 +1882,10 @@ nm_platform_tun_get_properties_ifname (NMPlatform *self, const char *ifname, NMP flags = _nm_utils_ascii_str_to_int64 (val, 16, 0, G_MAXINT64, 0); if (!errno) { -#ifndef IFF_MULTI_QUEUE - const int IFF_MULTI_QUEUE = 0x0100; -#endif props->mode = ((flags & (IFF_TUN | IFF_TAP)) == IFF_TUN) ? "tun" : "tap"; props->no_pi = !!(flags & IFF_NO_PI); props->vnet_hdr = !!(flags & IFF_VNET_HDR); - props->multi_queue = !!(flags & IFF_MULTI_QUEUE); + props->multi_queue = !!(flags & NM_IFF_MULTI_QUEUE); } else success = FALSE; g_free (val); diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 42c3b7939..e64a9bf9e 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -61,6 +61,8 @@ typedef struct _NMPlatform NMPlatform; #define NM_IN6_ADDR_GEN_MODE_NONE 1 /* IN6_ADDR_GEN_MODE_NONE */ #define NM_IN6_ADDR_GEN_MODE_STABLE_PRIVACY 2 /* IN6_ADDR_GEN_MODE_STABLE_PRIVACY */ +#define NM_IFF_MULTI_QUEUE 0x0100 /* IFF_MULTI_QUEUE */ + typedef enum { /*< skip >*/ /* dummy value, to enforce that the enum type is signed and has a size @@ -525,6 +527,9 @@ typedef struct { gboolean (*infiniband_partition_add) (NMPlatform *, int parent, int p_key, NMPlatformLink *out_link); + gboolean (*tun_add) (NMPlatform *platform, const char *name, gboolean tap, gint64 owner, gint64 group, gboolean pi, + gboolean vnet_hdr, gboolean multi_queue, NMPlatformLink *out_link); + gboolean (*wifi_get_capabilities) (NMPlatform *, int ifindex, NMDeviceWifiCapabilities *caps); gboolean (*wifi_get_bssid) (NMPlatform *, int ifindex, guint8 *bssid); GByteArray *(*wifi_get_ssid) (NMPlatform *, int ifindex); @@ -728,6 +733,9 @@ gboolean nm_platform_link_vlan_change (NMPlatform *self, gsize n_egress_map); +NMPlatformError nm_platform_tun_add (NMPlatform *self, const char *name, gboolean tap, gint64 owner, gint64 group, gboolean pi, + gboolean vnet_hdr, gboolean multi_queue, NMPlatformLink *out_link); + NMPlatformError nm_platform_infiniband_partition_add (NMPlatform *self, int parent, int p_key, NMPlatformLink *out_link); gboolean nm_platform_infiniband_get_properties (NMPlatform *self, int ifindex, int *parent, int *p_key, const char **mode);