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