diff options
Diffstat (limited to 'net/netfilter/xt_socket.c')
| -rw-r--r-- | net/netfilter/xt_socket.c | 192 | 
1 files changed, 157 insertions, 35 deletions
diff --git a/net/netfilter/xt_socket.c b/net/netfilter/xt_socket.c index 00d6ae83830..1ba67931eb1 100644 --- a/net/netfilter/xt_socket.c +++ b/net/netfilter/xt_socket.c @@ -19,18 +19,18 @@  #include <net/icmp.h>  #include <net/sock.h>  #include <net/inet_sock.h> -#include <net/netfilter/nf_tproxy_core.h>  #include <net/netfilter/ipv4/nf_defrag_ipv4.h> -#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) +#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)  #define XT_SOCKET_HAVE_IPV6 1  #include <linux/netfilter_ipv6/ip6_tables.h> +#include <net/inet6_hashtables.h>  #include <net/netfilter/ipv6/nf_defrag_ipv6.h>  #endif  #include <linux/netfilter/xt_socket.h> -#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) +#if IS_ENABLED(CONFIG_NF_CONNTRACK)  #define XT_SOCKET_HAVE_CONNTRACK 1  #include <net/netfilter/nf_conntrack.h>  #endif @@ -92,16 +92,53 @@ extract_icmp4_fields(const struct sk_buff *skb,  	return 0;  } +/* "socket" match based redirection (no specific rule) + * =================================================== + * + * There are connections with dynamic endpoints (e.g. FTP data + * connection) that the user is unable to add explicit rules + * for. These are taken care of by a generic "socket" rule. It is + * assumed that the proxy application is trusted to open such + * connections without explicit iptables rule (except of course the + * generic 'socket' rule). In this case the following sockets are + * matched in preference order: + * + *   - match: if there's a fully established connection matching the + *     _packet_ tuple + * + *   - match: if there's a non-zero bound listener (possibly with a + *     non-local address) We don't accept zero-bound listeners, since + *     then local services could intercept traffic going through the + *     box. + */ +static struct sock * +xt_socket_get_sock_v4(struct net *net, const u8 protocol, +		      const __be32 saddr, const __be32 daddr, +		      const __be16 sport, const __be16 dport, +		      const struct net_device *in) +{ +	switch (protocol) { +	case IPPROTO_TCP: +		return __inet_lookup(net, &tcp_hashinfo, +				     saddr, sport, daddr, dport, +				     in->ifindex); +	case IPPROTO_UDP: +		return udp4_lib_lookup(net, saddr, sport, daddr, dport, +				       in->ifindex); +	} +	return NULL; +} +  static bool  socket_match(const struct sk_buff *skb, struct xt_action_param *par,  	     const struct xt_socket_mtinfo1 *info)  {  	const struct iphdr *iph = ip_hdr(skb);  	struct udphdr _hdr, *hp = NULL; -	struct sock *sk; -	__be32 daddr, saddr; -	__be16 dport, sport; -	u8 protocol; +	struct sock *sk = skb->sk; +	__be32 uninitialized_var(daddr), uninitialized_var(saddr); +	__be16 uninitialized_var(dport), uninitialized_var(sport); +	u8 uninitialized_var(protocol);  #ifdef XT_SOCKET_HAVE_CONNTRACK  	struct nf_conn const *ct;  	enum ip_conntrack_info ctinfo; @@ -134,9 +171,9 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par,  	ct = nf_ct_get(skb, &ctinfo);  	if (ct && !nf_ct_is_untracked(ct) &&  	    ((iph->protocol != IPPROTO_ICMP && -	      ctinfo == IP_CT_IS_REPLY + IP_CT_ESTABLISHED) || +	      ctinfo == IP_CT_ESTABLISHED_REPLY) ||  	     (iph->protocol == IPPROTO_ICMP && -	      ctinfo == IP_CT_IS_REPLY + IP_CT_RELATED)) && +	      ctinfo == IP_CT_RELATED_REPLY)) &&  	    (ct->status & IPS_SRC_NAT_DONE)) {  		daddr = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip; @@ -146,25 +183,31 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par,  	}  #endif -	sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), protocol, -				   saddr, daddr, sport, dport, par->in, NFT_LOOKUP_ANY); -	if (sk != NULL) { +	if (!sk) +		sk = xt_socket_get_sock_v4(dev_net(skb->dev), protocol, +					   saddr, daddr, sport, dport, +					   par->in); +	if (sk) {  		bool wildcard;  		bool transparent = true; -		/* Ignore sockets listening on INADDR_ANY */ -		wildcard = (sk->sk_state != TCP_TIME_WAIT && +		/* Ignore sockets listening on INADDR_ANY, +		 * unless XT_SOCKET_NOWILDCARD is set +		 */ +		wildcard = (!(info->flags & XT_SOCKET_NOWILDCARD) && +			    sk->sk_state != TCP_TIME_WAIT &&  			    inet_sk(sk)->inet_rcv_saddr == 0);  		/* Ignore non-transparent sockets,  		   if XT_SOCKET_TRANSPARENT is used */ -		if (info && info->flags & XT_SOCKET_TRANSPARENT) +		if (info->flags & XT_SOCKET_TRANSPARENT)  			transparent = ((sk->sk_state != TCP_TIME_WAIT &&  					inet_sk(sk)->transparent) ||  				       (sk->sk_state == TCP_TIME_WAIT &&  					inet_twsk(sk)->tw_transparent)); -		nf_tproxy_put_sock(sk); +		if (sk != skb->sk) +			sock_gen_put(sk);  		if (wildcard || !transparent)  			sk = NULL; @@ -181,11 +224,15 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par,  static bool  socket_mt4_v0(const struct sk_buff *skb, struct xt_action_param *par)  { -	return socket_match(skb, par, NULL); +	static struct xt_socket_mtinfo1 xt_info_v0 = { +		.flags = 0, +	}; + +	return socket_match(skb, par, &xt_info_v0);  }  static bool -socket_mt4_v1(const struct sk_buff *skb, struct xt_action_param *par) +socket_mt4_v1_v2(const struct sk_buff *skb, struct xt_action_param *par)  {  	return socket_match(skb, par, par->matchinfo);  } @@ -205,6 +252,7 @@ extract_icmp6_fields(const struct sk_buff *skb,  	struct icmp6hdr *icmph, _icmph;  	__be16 *ports, _ports[2];  	u8 inside_nexthdr; +	__be16 inside_fragoff;  	int inside_hdrlen;  	icmph = skb_header_pointer(skb, outside_hdrlen, @@ -220,7 +268,8 @@ extract_icmp6_fields(const struct sk_buff *skb,  		return 1;  	inside_nexthdr = inside_iph->nexthdr; -	inside_hdrlen = ipv6_skip_exthdr(skb, outside_hdrlen + sizeof(_icmph) + sizeof(_inside_iph), &inside_nexthdr); +	inside_hdrlen = ipv6_skip_exthdr(skb, outside_hdrlen + sizeof(_icmph) + sizeof(_inside_iph), +					 &inside_nexthdr, &inside_fragoff);  	if (inside_hdrlen < 0)  		return 1; /* hjm: Packet has no/incomplete transport layer headers. */ @@ -244,18 +293,37 @@ extract_icmp6_fields(const struct sk_buff *skb,  	return 0;  } +static struct sock * +xt_socket_get_sock_v6(struct net *net, const u8 protocol, +		      const struct in6_addr *saddr, const struct in6_addr *daddr, +		      const __be16 sport, const __be16 dport, +		      const struct net_device *in) +{ +	switch (protocol) { +	case IPPROTO_TCP: +		return inet6_lookup(net, &tcp_hashinfo, +				    saddr, sport, daddr, dport, +				    in->ifindex); +	case IPPROTO_UDP: +		return udp6_lib_lookup(net, saddr, sport, daddr, dport, +				       in->ifindex); +	} + +	return NULL; +} +  static bool -socket_mt6_v1(const struct sk_buff *skb, struct xt_action_param *par) +socket_mt6_v1_v2(const struct sk_buff *skb, struct xt_action_param *par)  {  	struct ipv6hdr *iph = ipv6_hdr(skb);  	struct udphdr _hdr, *hp = NULL; -	struct sock *sk; -	struct in6_addr *daddr, *saddr; -	__be16 dport, sport; -	int thoff, tproto; +	struct sock *sk = skb->sk; +	struct in6_addr *daddr = NULL, *saddr = NULL; +	__be16 uninitialized_var(dport), uninitialized_var(sport); +	int thoff = 0, uninitialized_var(tproto);  	const struct xt_socket_mtinfo1 *info = (struct xt_socket_mtinfo1 *) par->matchinfo; -	tproto = ipv6_find_hdr(skb, &thoff, -1, NULL); +	tproto = ipv6_find_hdr(skb, &thoff, -1, NULL, NULL);  	if (tproto < 0) {  		pr_debug("unable to find transport header in IPv6 packet, dropping\n");  		return NF_DROP; @@ -280,25 +348,31 @@ socket_mt6_v1(const struct sk_buff *skb, struct xt_action_param *par)  		return false;  	} -	sk = nf_tproxy_get_sock_v6(dev_net(skb->dev), tproto, -				   saddr, daddr, sport, dport, par->in, NFT_LOOKUP_ANY); -	if (sk != NULL) { +	if (!sk) +		sk = xt_socket_get_sock_v6(dev_net(skb->dev), tproto, +					   saddr, daddr, sport, dport, +					   par->in); +	if (sk) {  		bool wildcard;  		bool transparent = true; -		/* Ignore sockets listening on INADDR_ANY */ -		wildcard = (sk->sk_state != TCP_TIME_WAIT && -			    ipv6_addr_any(&inet6_sk(sk)->rcv_saddr)); +		/* Ignore sockets listening on INADDR_ANY +		 * unless XT_SOCKET_NOWILDCARD is set +		 */ +		wildcard = (!(info->flags & XT_SOCKET_NOWILDCARD) && +			    sk->sk_state != TCP_TIME_WAIT && +			    ipv6_addr_any(&sk->sk_v6_rcv_saddr));  		/* Ignore non-transparent sockets,  		   if XT_SOCKET_TRANSPARENT is used */ -		if (info && info->flags & XT_SOCKET_TRANSPARENT) +		if (info->flags & XT_SOCKET_TRANSPARENT)  			transparent = ((sk->sk_state != TCP_TIME_WAIT &&  					inet_sk(sk)->transparent) ||  				       (sk->sk_state == TCP_TIME_WAIT &&  					inet_twsk(sk)->tw_transparent)); -		nf_tproxy_put_sock(sk); +		if (sk != skb->sk) +			sock_gen_put(sk);  		if (wildcard || !transparent)  			sk = NULL; @@ -314,6 +388,28 @@ socket_mt6_v1(const struct sk_buff *skb, struct xt_action_param *par)  }  #endif +static int socket_mt_v1_check(const struct xt_mtchk_param *par) +{ +	const struct xt_socket_mtinfo1 *info = (struct xt_socket_mtinfo1 *) par->matchinfo; + +	if (info->flags & ~XT_SOCKET_FLAGS_V1) { +		pr_info("unknown flags 0x%x\n", info->flags & ~XT_SOCKET_FLAGS_V1); +		return -EINVAL; +	} +	return 0; +} + +static int socket_mt_v2_check(const struct xt_mtchk_param *par) +{ +	const struct xt_socket_mtinfo2 *info = (struct xt_socket_mtinfo2 *) par->matchinfo; + +	if (info->flags & ~XT_SOCKET_FLAGS_V2) { +		pr_info("unknown flags 0x%x\n", info->flags & ~XT_SOCKET_FLAGS_V2); +		return -EINVAL; +	} +	return 0; +} +  static struct xt_match socket_mt_reg[] __read_mostly = {  	{  		.name		= "socket", @@ -328,7 +424,8 @@ static struct xt_match socket_mt_reg[] __read_mostly = {  		.name		= "socket",  		.revision	= 1,  		.family		= NFPROTO_IPV4, -		.match		= socket_mt4_v1, +		.match		= socket_mt4_v1_v2, +		.checkentry	= socket_mt_v1_check,  		.matchsize	= sizeof(struct xt_socket_mtinfo1),  		.hooks		= (1 << NF_INET_PRE_ROUTING) |  				  (1 << NF_INET_LOCAL_IN), @@ -339,7 +436,32 @@ static struct xt_match socket_mt_reg[] __read_mostly = {  		.name		= "socket",  		.revision	= 1,  		.family		= NFPROTO_IPV6, -		.match		= socket_mt6_v1, +		.match		= socket_mt6_v1_v2, +		.checkentry	= socket_mt_v1_check, +		.matchsize	= sizeof(struct xt_socket_mtinfo1), +		.hooks		= (1 << NF_INET_PRE_ROUTING) | +				  (1 << NF_INET_LOCAL_IN), +		.me		= THIS_MODULE, +	}, +#endif +	{ +		.name		= "socket", +		.revision	= 2, +		.family		= NFPROTO_IPV4, +		.match		= socket_mt4_v1_v2, +		.checkentry	= socket_mt_v2_check, +		.matchsize	= sizeof(struct xt_socket_mtinfo1), +		.hooks		= (1 << NF_INET_PRE_ROUTING) | +				  (1 << NF_INET_LOCAL_IN), +		.me		= THIS_MODULE, +	}, +#ifdef XT_SOCKET_HAVE_IPV6 +	{ +		.name		= "socket", +		.revision	= 2, +		.family		= NFPROTO_IPV6, +		.match		= socket_mt6_v1_v2, +		.checkentry	= socket_mt_v2_check,  		.matchsize	= sizeof(struct xt_socket_mtinfo1),  		.hooks		= (1 << NF_INET_PRE_ROUTING) |  				  (1 << NF_INET_LOCAL_IN),  | 
