Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 76e21053 authored by Erich E. Hoover's avatar Erich E. Hoover Committed by David S. Miller
Browse files

ipv4: Implement IP_UNICAST_IF socket option.



The IP_UNICAST_IF feature is needed by the Wine project.  This patch
implements the feature by setting the outgoing interface in a similar
fashion to that of IP_MULTICAST_IF.  A separate option is needed to
handle this feature since the existing options do not provide all of
the characteristics required by IP_UNICAST_IF, a summary is provided
below.

SO_BINDTODEVICE:
* SO_BINDTODEVICE requires administrative privileges, IP_UNICAST_IF
does not.  From reading some old mailing list articles my
understanding is that SO_BINDTODEVICE requires administrative
privileges because it can override the administrator's routing
settings.
* The SO_BINDTODEVICE option restricts both outbound and inbound
traffic, IP_UNICAST_IF only impacts outbound traffic.

IP_PKTINFO:
* Since IP_PKTINFO and IP_UNICAST_IF are independent options,
implementing IP_UNICAST_IF with IP_PKTINFO will likely break some
applications.
* Implementing IP_UNICAST_IF on top of IP_PKTINFO significantly
complicates the Wine codebase and reduces the socket performance
(doing this requires a lot of extra communication between the
"server" and "user" layers).

bind():
* bind() does not work on broadcast packets, IP_UNICAST_IF is
specifically intended to work with broadcast packets.
* Like SO_BINDTODEVICE, bind() restricts both outbound and inbound
traffic.

Signed-off-by: default avatarErich E. Hoover <ehoover@mines.edu>
Signed-off-by: default avatarEric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 43480aec
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -111,6 +111,7 @@ struct in_addr {
#define MCAST_LEAVE_SOURCE_GROUP	47
#define MCAST_LEAVE_SOURCE_GROUP	47
#define MCAST_MSFILTER			48
#define MCAST_MSFILTER			48
#define IP_MULTICAST_ALL		49
#define IP_MULTICAST_ALL		49
#define IP_UNICAST_IF			50


#define MCAST_EXCLUDE	0
#define MCAST_EXCLUDE	0
#define MCAST_INCLUDE	1
#define MCAST_INCLUDE	1
+2 −0
Original line number Original line Diff line number Diff line
@@ -132,6 +132,7 @@ struct rtable;
 * @tos - TOS
 * @tos - TOS
 * @mc_ttl - Multicasting TTL
 * @mc_ttl - Multicasting TTL
 * @is_icsk - is this an inet_connection_sock?
 * @is_icsk - is this an inet_connection_sock?
 * @uc_index - Unicast outgoing device index
 * @mc_index - Multicast device index
 * @mc_index - Multicast device index
 * @mc_list - Group array
 * @mc_list - Group array
 * @cork - info to build ip hdr on each ip frag while socket is corked
 * @cork - info to build ip hdr on each ip frag while socket is corked
@@ -167,6 +168,7 @@ struct inet_sock {
				transparent:1,
				transparent:1,
				mc_all:1,
				mc_all:1,
				nodefrag:1;
				nodefrag:1;
	int			uc_index;
	int			mc_index;
	int			mc_index;
	__be32			mc_addr;
	__be32			mc_addr;
	struct ip_mc_socklist __rcu	*mc_list;
	struct ip_mc_socklist __rcu	*mc_list;
+33 −0
Original line number Original line Diff line number Diff line
@@ -469,6 +469,7 @@ static int do_ip_setsockopt(struct sock *sk, int level,
			     (1<<IP_ROUTER_ALERT) | (1<<IP_FREEBIND) |
			     (1<<IP_ROUTER_ALERT) | (1<<IP_FREEBIND) |
			     (1<<IP_PASSSEC) | (1<<IP_TRANSPARENT) |
			     (1<<IP_PASSSEC) | (1<<IP_TRANSPARENT) |
			     (1<<IP_MINTTL) | (1<<IP_NODEFRAG))) ||
			     (1<<IP_MINTTL) | (1<<IP_NODEFRAG))) ||
	    optname == IP_UNICAST_IF ||
	    optname == IP_MULTICAST_TTL ||
	    optname == IP_MULTICAST_TTL ||
	    optname == IP_MULTICAST_ALL ||
	    optname == IP_MULTICAST_ALL ||
	    optname == IP_MULTICAST_LOOP ||
	    optname == IP_MULTICAST_LOOP ||
@@ -628,6 +629,35 @@ static int do_ip_setsockopt(struct sock *sk, int level,
			goto e_inval;
			goto e_inval;
		inet->mc_loop = !!val;
		inet->mc_loop = !!val;
		break;
		break;
	case IP_UNICAST_IF:
	{
		struct net_device *dev = NULL;
		int ifindex;

		if (optlen != sizeof(int))
			goto e_inval;

		ifindex = (__force int)ntohl((__force __be32)val);
		if (ifindex == 0) {
			inet->uc_index = 0;
			err = 0;
			break;
		}

		dev = dev_get_by_index(sock_net(sk), ifindex);
		err = -EADDRNOTAVAIL;
		if (!dev)
			break;
		dev_put(dev);

		err = -EINVAL;
		if (sk->sk_bound_dev_if)
			break;

		inet->uc_index = ifindex;
		err = 0;
		break;
	}
	case IP_MULTICAST_IF:
	case IP_MULTICAST_IF:
	{
	{
		struct ip_mreqn mreq;
		struct ip_mreqn mreq;
@@ -1178,6 +1208,9 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname,
	case IP_MULTICAST_LOOP:
	case IP_MULTICAST_LOOP:
		val = inet->mc_loop;
		val = inet->mc_loop;
		break;
		break;
	case IP_UNICAST_IF:
		val = (__force int)htonl((__u32) inet->uc_index);
		break;
	case IP_MULTICAST_IF:
	case IP_MULTICAST_IF:
	{
	{
		struct in_addr addr;
		struct in_addr addr;
+2 −1
Original line number Original line Diff line number Diff line
@@ -556,7 +556,8 @@ static int ping_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
			ipc.oif = inet->mc_index;
			ipc.oif = inet->mc_index;
		if (!saddr)
		if (!saddr)
			saddr = inet->mc_addr;
			saddr = inet->mc_addr;
	}
	} else if (!ipc.oif)
		ipc.oif = inet->uc_index;


	flowi4_init_output(&fl4, ipc.oif, sk->sk_mark, tos,
	flowi4_init_output(&fl4, ipc.oif, sk->sk_mark, tos,
			   RT_SCOPE_UNIVERSE, sk->sk_protocol,
			   RT_SCOPE_UNIVERSE, sk->sk_protocol,
+2 −1
Original line number Original line Diff line number Diff line
@@ -563,7 +563,8 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
			ipc.oif = inet->mc_index;
			ipc.oif = inet->mc_index;
		if (!saddr)
		if (!saddr)
			saddr = inet->mc_addr;
			saddr = inet->mc_addr;
	}
	} else if (!ipc.oif)
		ipc.oif = inet->uc_index;


	flowi4_init_output(&fl4, ipc.oif, sk->sk_mark, tos,
	flowi4_init_output(&fl4, ipc.oif, sk->sk_mark, tos,
			   RT_SCOPE_UNIVERSE,
			   RT_SCOPE_UNIVERSE,
Loading