diff options
| author | Michael Biebl <biebl@debian.org> | 2019-07-31 10:51:42 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2019-07-31 10:51:42 +0200 |
| commit | 2e5fa45ddfbb5cffa1e78221f1cea706e2f298af (patch) | |
| tree | 86f69d36c56de3074280456eddc854a780b8e04b /shared/n-dhcp4/src/util/socket.c | |
| parent | 85563b7fc7ec2cd21e38debb9b28db342e2e8e7c (diff) | |
New upstream version 1.19.90 upstream/1.19.90
Diffstat (limited to 'shared/n-dhcp4/src/util/socket.c')
| -rw-r--r-- | shared/n-dhcp4/src/util/socket.c | 121 |
1 files changed, 121 insertions, 0 deletions
diff --git a/shared/n-dhcp4/src/util/socket.c b/shared/n-dhcp4/src/util/socket.c new file mode 100644 index 00000000..c25f76fd --- /dev/null +++ b/shared/n-dhcp4/src/util/socket.c @@ -0,0 +1,121 @@ +/* + * Socket Utilities + */ + +#include <assert.h> +#include <c-stdaux.h> +#include <errno.h> +#include <net/if.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include "socket.h" + +/** + * socket_SIOCGIFNAME() - resolve an ifindex to an ifname + * @socket: socket to operate on + * @ifindex: index of network interface to resolve + * @ifname: buffer to store resolved name + * + * This uses the SIOCGIFNAME ioctl to resolve an ifindex to an ifname. The + * buffer provided in @ifnamep must be at least IFNAMSIZ bytes in size. The + * maximum ifname length is IFNAMSIZ-1, and this function always + * zero-terminates the result. + * + * This function is similar to if_indextoname(3) provided by glibc, but it + * allows to specify the target socket explicitly. This allows the caller to + * control the target network-namespace, rather than relying on the network + * namespace of the running process. + * + * Return: 0 on success, negative kernel error code on failure. + */ +int socket_SIOCGIFNAME(int socket, int ifindex, char (*ifnamep)[IFNAMSIZ]) { + struct ifreq req = { .ifr_ifindex = ifindex }; + int r; + + r = ioctl(socket, SIOCGIFNAME, &req); + if (r < 0) + return -errno; + + /* + * The linux kernel guarantees that an interface name is always + * zero-terminated, and it always fully fits into IFNAMSIZ bytes, + * including the zero-terminator. + */ + memcpy(ifnamep, req.ifr_name, IFNAMSIZ); + return 0; +} + +/** + * socket_bind_if() - bind socket to a network interface + * @socket: socket to operate on + * @ifindex: index of network interface to bind to, or 0 + * + * This binds the socket given via @socket to the network interface specified + * via @ifindex. It uses the underlying SO_BINDTODEVICE ioctl of the linux + * kernel. However, if available, if prefers the newer SO_BINDTOIFINDEX ioctl, + * which avoids resolving the interface name temporarily, and thus does not + * suffer from a race-condition. + * + * Return: 0 on success, negative error code on failure. + */ +int socket_bind_if(int socket, int ifindex) { + char ifname[IFNAMSIZ] = {}; + int r; + + c_assert(ifindex >= 0); + + /* + * We first try the newer SO_BINDTOIFINDEX. If it is not available on + * the running kernel, we fall back to SO_BINDTODEVICE. This, however, + * requires us to first resolve the ifindex to an ifname. Note that + * this is racy, since the device name might theoretically change + * asynchronously. + * + * Using 0 as ifindex will remove the device-binding. For + * SO_BINDTOIFINDEX we simply pass-through the 0 to the kernel, which + * recognizes this correctly. For SO_BINDTODEVICE we pass the empty + * string, which the kernel recognizes as a request to remove the + * binding. + * + * The commit introducing SO_BINDTOIFINDEX first appeared in linux-5.1: + * + * commit f5dd3d0c9638a9d9a02b5964c4ad636f06cf7e2c + * Author: David Herrmann <dh.herrmann@gmail.com> + * Date: Tue Jan 15 14:42:14 2019 +0100 + * + * net: introduce SO_BINDTOIFINDEX sockopt + * + * In older kernels, setsockopt(2) is guaranteed to return ENOPROTOOPT + * for this ioctl. + */ + +#ifdef SO_BINDTOIFINDEX + r = setsockopt(socket, + SOL_SOCKET, + SO_BINDTOIFINDEX, + &ifindex, + sizeof(ifindex)); + if (r >= 0) + return 0; + else if (errno != ENOPROTOOPT) + return -errno; +#endif /* SO_BINDTOIFINDEX */ + + if (ifindex > 0) { + r = socket_SIOCGIFNAME(socket, ifindex, &ifname); + if (r) + return r; + } + + r = setsockopt(socket, + SOL_SOCKET, + SO_BINDTODEVICE, + ifname, + strlen(ifname)); + if (r < 0) + return -errno; + + return 0; +} |