mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	ipv4/raw: support binding to nonlocal addresses
Add support to inet v4 raw sockets for binding to nonlocal addresses through the IP_FREEBIND and IP_TRANSPARENT socket options, as well as the ipv4.ip_nonlocal_bind kernel parameter. Add helper function to inet_sock.h to check for bind address validity on the base of the address type and whether nonlocal address are enabled for the socket via any of the sockopts/sysctl, deduplicating checks in ipv4/ping.c, ipv4/af_inet.c, ipv6/af_inet6.c (for mapped v4->v6 addresses), and ipv4/raw.c. Add test cases with IP[V6]_FREEBIND verifying that both v4 and v6 raw sockets support binding to nonlocal addresses after the change. Add necessary support for the test cases to nettest. Signed-off-by: Riccardo Paolo Bestetti <pbl@bestov.io> Reviewed-by: David Ahern <dsahern@kernel.org> Link: https://lore.kernel.org/r/20211117090010.125393-1-pbl@bestov.io Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
		
							parent
							
								
									75082e7f46
								
							
						
					
					
						commit
						8ff978b8b2
					
				
					 7 changed files with 99 additions and 25 deletions
				
			
		| 
						 | 
				
			
			@ -372,4 +372,16 @@ static inline bool inet_can_nonlocal_bind(struct net *net,
 | 
			
		|||
		inet->freebind || inet->transparent;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline bool inet_addr_valid_or_nonlocal(struct net *net,
 | 
			
		||||
					       struct inet_sock *inet,
 | 
			
		||||
					       __be32 addr,
 | 
			
		||||
					       int addr_type)
 | 
			
		||||
{
 | 
			
		||||
	return inet_can_nonlocal_bind(net, inet) ||
 | 
			
		||||
		addr == htonl(INADDR_ANY) ||
 | 
			
		||||
		addr_type == RTN_LOCAL ||
 | 
			
		||||
		addr_type == RTN_MULTICAST ||
 | 
			
		||||
		addr_type == RTN_BROADCAST;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif	/* _INET_SOCK_H */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -489,11 +489,8 @@ int __inet_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len,
 | 
			
		|||
	 *  is temporarily down)
 | 
			
		||||
	 */
 | 
			
		||||
	err = -EADDRNOTAVAIL;
 | 
			
		||||
	if (!inet_can_nonlocal_bind(net, inet) &&
 | 
			
		||||
	    addr->sin_addr.s_addr != htonl(INADDR_ANY) &&
 | 
			
		||||
	    chk_addr_ret != RTN_LOCAL &&
 | 
			
		||||
	    chk_addr_ret != RTN_MULTICAST &&
 | 
			
		||||
	    chk_addr_ret != RTN_BROADCAST)
 | 
			
		||||
	if (!inet_addr_valid_or_nonlocal(net, inet, addr->sin_addr.s_addr,
 | 
			
		||||
	                                 chk_addr_ret))
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	snum = ntohs(addr->sin_port);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -311,15 +311,11 @@ static int ping_check_bind_addr(struct sock *sk, struct inet_sock *isk,
 | 
			
		|||
		pr_debug("ping_check_bind_addr(sk=%p,addr=%pI4,port=%d)\n",
 | 
			
		||||
			 sk, &addr->sin_addr.s_addr, ntohs(addr->sin_port));
 | 
			
		||||
 | 
			
		||||
		if (addr->sin_addr.s_addr == htonl(INADDR_ANY))
 | 
			
		||||
			chk_addr_ret = RTN_LOCAL;
 | 
			
		||||
		else
 | 
			
		||||
		chk_addr_ret = inet_addr_type(net, addr->sin_addr.s_addr);
 | 
			
		||||
 | 
			
		||||
		if ((!inet_can_nonlocal_bind(net, isk) &&
 | 
			
		||||
		     chk_addr_ret != RTN_LOCAL) ||
 | 
			
		||||
		    chk_addr_ret == RTN_MULTICAST ||
 | 
			
		||||
		    chk_addr_ret == RTN_BROADCAST)
 | 
			
		||||
		if (!inet_addr_valid_or_nonlocal(net, inet_sk(sk),
 | 
			
		||||
					         addr->sin_addr.s_addr,
 | 
			
		||||
	                                         chk_addr_ret))
 | 
			
		||||
			return -EADDRNOTAVAIL;
 | 
			
		||||
 | 
			
		||||
#if IS_ENABLED(CONFIG_IPV6)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -717,6 +717,7 @@ static int raw_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
 | 
			
		|||
{
 | 
			
		||||
	struct inet_sock *inet = inet_sk(sk);
 | 
			
		||||
	struct sockaddr_in *addr = (struct sockaddr_in *) uaddr;
 | 
			
		||||
	struct net *net = sock_net(sk);
 | 
			
		||||
	u32 tb_id = RT_TABLE_LOCAL;
 | 
			
		||||
	int ret = -EINVAL;
 | 
			
		||||
	int chk_addr_ret;
 | 
			
		||||
| 
						 | 
				
			
			@ -725,16 +726,16 @@ static int raw_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
 | 
			
		|||
		goto out;
 | 
			
		||||
 | 
			
		||||
	if (sk->sk_bound_dev_if)
 | 
			
		||||
		tb_id = l3mdev_fib_table_by_index(sock_net(sk),
 | 
			
		||||
		tb_id = l3mdev_fib_table_by_index(net,
 | 
			
		||||
						  sk->sk_bound_dev_if) ? : tb_id;
 | 
			
		||||
 | 
			
		||||
	chk_addr_ret = inet_addr_type_table(sock_net(sk), addr->sin_addr.s_addr,
 | 
			
		||||
					    tb_id);
 | 
			
		||||
	chk_addr_ret = inet_addr_type_table(net, addr->sin_addr.s_addr, tb_id);
 | 
			
		||||
 | 
			
		||||
	ret = -EADDRNOTAVAIL;
 | 
			
		||||
	if (addr->sin_addr.s_addr && chk_addr_ret != RTN_LOCAL &&
 | 
			
		||||
	    chk_addr_ret != RTN_MULTICAST && chk_addr_ret != RTN_BROADCAST)
 | 
			
		||||
	if (!inet_addr_valid_or_nonlocal(net, inet, addr->sin_addr.s_addr,
 | 
			
		||||
					 chk_addr_ret))
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	inet->inet_rcv_saddr = inet->inet_saddr = addr->sin_addr.s_addr;
 | 
			
		||||
	if (chk_addr_ret == RTN_MULTICAST || chk_addr_ret == RTN_BROADCAST)
 | 
			
		||||
		inet->inet_saddr = 0;  /* Use device */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -337,11 +337,8 @@ static int __inet6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len,
 | 
			
		|||
		chk_addr_ret = inet_addr_type_dev_table(net, dev, v4addr);
 | 
			
		||||
		rcu_read_unlock();
 | 
			
		||||
 | 
			
		||||
		if (!inet_can_nonlocal_bind(net, inet) &&
 | 
			
		||||
		    v4addr != htonl(INADDR_ANY) &&
 | 
			
		||||
		    chk_addr_ret != RTN_LOCAL &&
 | 
			
		||||
		    chk_addr_ret != RTN_MULTICAST &&
 | 
			
		||||
		    chk_addr_ret != RTN_BROADCAST) {
 | 
			
		||||
		if (!inet_addr_valid_or_nonlocal(net, inet, v4addr,
 | 
			
		||||
						 chk_addr_ret)) {
 | 
			
		||||
			err = -EADDRNOTAVAIL;
 | 
			
		||||
			goto out;
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -66,6 +66,10 @@ NSB_LO_IP=172.16.2.2
 | 
			
		|||
NSA_LO_IP6=2001:db8:2::1
 | 
			
		||||
NSB_LO_IP6=2001:db8:2::2
 | 
			
		||||
 | 
			
		||||
# non-local addresses for freebind tests
 | 
			
		||||
NL_IP=172.17.1.1
 | 
			
		||||
NL_IP6=2001:db8:4::1
 | 
			
		||||
 | 
			
		||||
MD5_PW=abc123
 | 
			
		||||
MD5_WRONG_PW=abc1234
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -316,6 +320,9 @@ addr2str()
 | 
			
		|||
	${NSB_LO_IP6})	echo "ns-B loopback IPv6";;
 | 
			
		||||
	${NSB_LINKIP6}|${NSB_LINKIP6}%*) echo "ns-B IPv6 LLA";;
 | 
			
		||||
 | 
			
		||||
	${NL_IP})       echo "nonlocal IP";;
 | 
			
		||||
	${NL_IP6})      echo "nonlocal IPv6";;
 | 
			
		||||
 | 
			
		||||
	${VRF_IP})	echo "VRF IP";;
 | 
			
		||||
	${VRF_IP6})	echo "VRF IPv6";;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1767,6 +1774,14 @@ ipv4_addr_bind_novrf()
 | 
			
		|||
		log_test_addr ${a} $? 0 "Raw socket bind to local address after device bind"
 | 
			
		||||
	done
 | 
			
		||||
 | 
			
		||||
	#
 | 
			
		||||
	# raw socket with nonlocal bind
 | 
			
		||||
	#
 | 
			
		||||
	a=${NL_IP}
 | 
			
		||||
	log_start
 | 
			
		||||
	run_cmd nettest -s -R -P icmp -f -l ${a} -I ${NSA_DEV} -b
 | 
			
		||||
	log_test_addr ${a} $? 0 "Raw socket bind to nonlocal address after device bind"
 | 
			
		||||
 | 
			
		||||
	#
 | 
			
		||||
	# tcp sockets
 | 
			
		||||
	#
 | 
			
		||||
| 
						 | 
				
			
			@ -1815,6 +1830,14 @@ ipv4_addr_bind_vrf()
 | 
			
		|||
	run_cmd nettest -s -R -P icmp -l ${a} -I ${VRF} -b
 | 
			
		||||
	log_test_addr ${a} $? 1 "Raw socket bind to out of scope address after VRF bind"
 | 
			
		||||
 | 
			
		||||
	#
 | 
			
		||||
	# raw socket with nonlocal bind
 | 
			
		||||
	#
 | 
			
		||||
	a=${NL_IP}
 | 
			
		||||
	log_start
 | 
			
		||||
	run_cmd nettest -s -R -P icmp -f -l ${a} -I ${VRF} -b
 | 
			
		||||
	log_test_addr ${a} $? 0 "Raw socket bind to nonlocal address after VRF bind"
 | 
			
		||||
 | 
			
		||||
	#
 | 
			
		||||
	# tcp sockets
 | 
			
		||||
	#
 | 
			
		||||
| 
						 | 
				
			
			@ -1965,6 +1988,7 @@ ipv4_rt()
 | 
			
		|||
 | 
			
		||||
	a=${NSA_IP}
 | 
			
		||||
	log_start
 | 
			
		||||
 | 
			
		||||
	run_cmd nettest ${varg} -s &
 | 
			
		||||
	sleep 1
 | 
			
		||||
	run_cmd nettest ${varg} -d ${NSA_DEV} -r ${a} &
 | 
			
		||||
| 
						 | 
				
			
			@ -3402,6 +3426,14 @@ ipv6_addr_bind_novrf()
 | 
			
		|||
		log_test_addr ${a} $? 0 "Raw socket bind to local address after device bind"
 | 
			
		||||
	done
 | 
			
		||||
 | 
			
		||||
	#
 | 
			
		||||
	# raw socket with nonlocal bind
 | 
			
		||||
	#
 | 
			
		||||
	a=${NL_IP6}
 | 
			
		||||
	log_start
 | 
			
		||||
	run_cmd nettest -6 -s -R -P icmp -f -l ${a} -I ${NSA_DEV} -b
 | 
			
		||||
	log_test_addr ${a} $? 0 "Raw socket bind to nonlocal address"
 | 
			
		||||
 | 
			
		||||
	#
 | 
			
		||||
	# tcp sockets
 | 
			
		||||
	#
 | 
			
		||||
| 
						 | 
				
			
			@ -3443,6 +3475,14 @@ ipv6_addr_bind_vrf()
 | 
			
		|||
	run_cmd nettest -6 -s -R -P ipv6-icmp -l ${a} -I ${VRF} -b
 | 
			
		||||
	log_test_addr ${a} $? 1 "Raw socket bind to invalid local address after vrf bind"
 | 
			
		||||
 | 
			
		||||
	#
 | 
			
		||||
	# raw socket with nonlocal bind
 | 
			
		||||
	#
 | 
			
		||||
	a=${NL_IP6}
 | 
			
		||||
	log_start
 | 
			
		||||
	run_cmd nettest -6 -s -R -P icmp -f -l ${a} -I ${VRF} -b
 | 
			
		||||
	log_test_addr ${a} $? 0 "Raw socket bind to nonlocal address after VRF bind"
 | 
			
		||||
 | 
			
		||||
	#
 | 
			
		||||
	# tcp sockets
 | 
			
		||||
	#
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -85,6 +85,7 @@ struct sock_args {
 | 
			
		|||
	int version;   /* AF_INET/AF_INET6 */
 | 
			
		||||
 | 
			
		||||
	int use_setsockopt;
 | 
			
		||||
	int use_freebind;
 | 
			
		||||
	int use_cmsg;
 | 
			
		||||
	const char *dev;
 | 
			
		||||
	const char *server_dev;
 | 
			
		||||
| 
						 | 
				
			
			@ -514,6 +515,29 @@ static int set_membership(int sd, uint32_t grp, uint32_t addr, int ifindex)
 | 
			
		|||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int set_freebind(int sd, int version)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int one = 1;
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
 | 
			
		||||
	switch (version) {
 | 
			
		||||
	case AF_INET:
 | 
			
		||||
		if (setsockopt(sd, SOL_IP, IP_FREEBIND, &one, sizeof(one))) {
 | 
			
		||||
			log_err_errno("setsockopt(IP_FREEBIND)");
 | 
			
		||||
			rc = -1;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	case AF_INET6:
 | 
			
		||||
		if (setsockopt(sd, SOL_IPV6, IPV6_FREEBIND, &one, sizeof(one))) {
 | 
			
		||||
			log_err_errno("setsockopt(IPV6_FREEBIND");
 | 
			
		||||
			rc = -1;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int set_broadcast(int sd)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int one = 1;
 | 
			
		||||
| 
						 | 
				
			
			@ -1419,6 +1443,9 @@ static int lsock_init(struct sock_args *args)
 | 
			
		|||
		 set_unicast_if(sd, args->ifindex, args->version))
 | 
			
		||||
		goto err;
 | 
			
		||||
 | 
			
		||||
	if (args->use_freebind && set_freebind(sd, args->version))
 | 
			
		||||
		goto err;
 | 
			
		||||
 | 
			
		||||
	if (bind_socket(sd, args))
 | 
			
		||||
		goto err;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1827,7 +1854,7 @@ static int ipc_parent(int cpid, int fd, struct sock_args *args)
 | 
			
		|||
	return client_status;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define GETOPT_STR  "sr:l:c:p:t:g:P:DRn:M:X:m:d:I:BN:O:SCi6xL:0:1:2:3:Fbq"
 | 
			
		||||
#define GETOPT_STR  "sr:l:c:p:t:g:P:DRn:M:X:m:d:I:BN:O:SCi6xL:0:1:2:3:Fbqf"
 | 
			
		||||
#define OPT_FORCE_BIND_KEY_IFINDEX 1001
 | 
			
		||||
#define OPT_NO_BIND_KEY_IFINDEX 1002
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1864,6 +1891,7 @@ static void print_usage(char *prog)
 | 
			
		|||
	"    -I dev        bind socket to given device name - server mode\n"
 | 
			
		||||
	"    -S            use setsockopt (IP_UNICAST_IF or IP_MULTICAST_IF)\n"
 | 
			
		||||
	"                  to set device binding\n"
 | 
			
		||||
	"    -f            bind socket with the IP[V6]_FREEBIND option\n"
 | 
			
		||||
	"    -C            use cmsg and IP_PKTINFO to specify device binding\n"
 | 
			
		||||
	"\n"
 | 
			
		||||
	"    -L len        send random message of given length\n"
 | 
			
		||||
| 
						 | 
				
			
			@ -1999,6 +2027,9 @@ int main(int argc, char *argv[])
 | 
			
		|||
		case 'S':
 | 
			
		||||
			args.use_setsockopt = 1;
 | 
			
		||||
			break;
 | 
			
		||||
		case 'f':
 | 
			
		||||
			args.use_freebind = 1;
 | 
			
		||||
			break;
 | 
			
		||||
		case 'C':
 | 
			
		||||
			args.use_cmsg = 1;
 | 
			
		||||
			break;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue