109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
245ca4e0cSMáté Eckl #include <net/netfilter/nf_tproxy.h>
345ca4e0cSMáté Eckl #include <linux/module.h>
445ca4e0cSMáté Eckl #include <net/inet6_hashtables.h>
545ca4e0cSMáté Eckl #include <net/addrconf.h>
645ca4e0cSMáté Eckl #include <net/udp.h>
745ca4e0cSMáté Eckl #include <net/tcp.h>
845ca4e0cSMáté Eckl 
945ca4e0cSMáté Eckl const struct in6_addr *
nf_tproxy_laddr6(struct sk_buff * skb,const struct in6_addr * user_laddr,const struct in6_addr * daddr)1045ca4e0cSMáté Eckl nf_tproxy_laddr6(struct sk_buff *skb, const struct in6_addr *user_laddr,
1145ca4e0cSMáté Eckl 	      const struct in6_addr *daddr)
1245ca4e0cSMáté Eckl {
1345ca4e0cSMáté Eckl 	struct inet6_dev *indev;
1445ca4e0cSMáté Eckl 	struct inet6_ifaddr *ifa;
1545ca4e0cSMáté Eckl 	struct in6_addr *laddr;
1645ca4e0cSMáté Eckl 
1745ca4e0cSMáté Eckl 	if (!ipv6_addr_any(user_laddr))
1845ca4e0cSMáté Eckl 		return user_laddr;
1945ca4e0cSMáté Eckl 	laddr = NULL;
2045ca4e0cSMáté Eckl 
2145ca4e0cSMáté Eckl 	indev = __in6_dev_get(skb->dev);
2245ca4e0cSMáté Eckl 	if (indev) {
2345ca4e0cSMáté Eckl 		read_lock_bh(&indev->lock);
2445ca4e0cSMáté Eckl 		list_for_each_entry(ifa, &indev->addr_list, if_list) {
2545ca4e0cSMáté Eckl 			if (ifa->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED))
2645ca4e0cSMáté Eckl 				continue;
2745ca4e0cSMáté Eckl 
2845ca4e0cSMáté Eckl 			laddr = &ifa->addr;
2945ca4e0cSMáté Eckl 			break;
3045ca4e0cSMáté Eckl 		}
3145ca4e0cSMáté Eckl 		read_unlock_bh(&indev->lock);
3245ca4e0cSMáté Eckl 	}
3345ca4e0cSMáté Eckl 
3445ca4e0cSMáté Eckl 	return laddr ? laddr : daddr;
3545ca4e0cSMáté Eckl }
3645ca4e0cSMáté Eckl EXPORT_SYMBOL_GPL(nf_tproxy_laddr6);
3745ca4e0cSMáté Eckl 
3845ca4e0cSMáté Eckl struct sock *
nf_tproxy_handle_time_wait6(struct sk_buff * skb,int tproto,int thoff,struct net * net,const struct in6_addr * laddr,const __be16 lport,struct sock * sk)3945ca4e0cSMáté Eckl nf_tproxy_handle_time_wait6(struct sk_buff *skb, int tproto, int thoff,
4045ca4e0cSMáté Eckl 			 struct net *net,
4145ca4e0cSMáté Eckl 			 const struct in6_addr *laddr,
4245ca4e0cSMáté Eckl 			 const __be16 lport,
4345ca4e0cSMáté Eckl 			 struct sock *sk)
4445ca4e0cSMáté Eckl {
4545ca4e0cSMáté Eckl 	const struct ipv6hdr *iph = ipv6_hdr(skb);
4645ca4e0cSMáté Eckl 	struct tcphdr _hdr, *hp;
4745ca4e0cSMáté Eckl 
4845ca4e0cSMáté Eckl 	hp = skb_header_pointer(skb, thoff, sizeof(_hdr), &_hdr);
4945ca4e0cSMáté Eckl 	if (hp == NULL) {
5045ca4e0cSMáté Eckl 		inet_twsk_put(inet_twsk(sk));
5145ca4e0cSMáté Eckl 		return NULL;
5245ca4e0cSMáté Eckl 	}
5345ca4e0cSMáté Eckl 
5445ca4e0cSMáté Eckl 	if (hp->syn && !hp->rst && !hp->ack && !hp->fin) {
5545ca4e0cSMáté Eckl 		/* SYN to a TIME_WAIT socket, we'd rather redirect it
5645ca4e0cSMáté Eckl 		 * to a listener socket if there's one */
5745ca4e0cSMáté Eckl 		struct sock *sk2;
5845ca4e0cSMáté Eckl 
595711b4e8SMáté Eckl 		sk2 = nf_tproxy_get_sock_v6(net, skb, thoff, tproto,
6045ca4e0cSMáté Eckl 					    &iph->saddr,
6145ca4e0cSMáté Eckl 					    nf_tproxy_laddr6(skb, laddr, &iph->daddr),
6245ca4e0cSMáté Eckl 					    hp->source,
6345ca4e0cSMáté Eckl 					    lport ? lport : hp->dest,
6445ca4e0cSMáté Eckl 					    skb->dev, NF_TPROXY_LOOKUP_LISTENER);
6545ca4e0cSMáté Eckl 		if (sk2) {
66*4a024267SFlorian Westphal 			nf_tproxy_twsk_deschedule_put(inet_twsk(sk));
6745ca4e0cSMáté Eckl 			sk = sk2;
6845ca4e0cSMáté Eckl 		}
6945ca4e0cSMáté Eckl 	}
7045ca4e0cSMáté Eckl 
7145ca4e0cSMáté Eckl 	return sk;
7245ca4e0cSMáté Eckl }
7345ca4e0cSMáté Eckl EXPORT_SYMBOL_GPL(nf_tproxy_handle_time_wait6);
7445ca4e0cSMáté Eckl 
7545ca4e0cSMáté Eckl struct sock *
nf_tproxy_get_sock_v6(struct net * net,struct sk_buff * skb,int thoff,const u8 protocol,const struct in6_addr * saddr,const struct in6_addr * daddr,const __be16 sport,const __be16 dport,const struct net_device * in,const enum nf_tproxy_lookup_t lookup_type)765711b4e8SMáté Eckl nf_tproxy_get_sock_v6(struct net *net, struct sk_buff *skb, int thoff,
7745ca4e0cSMáté Eckl 		      const u8 protocol,
7845ca4e0cSMáté Eckl 		      const struct in6_addr *saddr, const struct in6_addr *daddr,
7945ca4e0cSMáté Eckl 		      const __be16 sport, const __be16 dport,
8045ca4e0cSMáté Eckl 		      const struct net_device *in,
8145ca4e0cSMáté Eckl 		      const enum nf_tproxy_lookup_t lookup_type)
8245ca4e0cSMáté Eckl {
834461568aSKuniyuki Iwashima 	struct inet_hashinfo *hinfo = net->ipv4.tcp_death_row.hashinfo;
8445ca4e0cSMáté Eckl 	struct sock *sk;
8545ca4e0cSMáté Eckl 
8645ca4e0cSMáté Eckl 	switch (protocol) {
875711b4e8SMáté Eckl 	case IPPROTO_TCP: {
885711b4e8SMáté Eckl 		struct tcphdr _hdr, *hp;
895711b4e8SMáté Eckl 
905711b4e8SMáté Eckl 		hp = skb_header_pointer(skb, thoff,
915711b4e8SMáté Eckl 					sizeof(struct tcphdr), &_hdr);
925711b4e8SMáté Eckl 		if (hp == NULL)
935711b4e8SMáté Eckl 			return NULL;
945711b4e8SMáté Eckl 
9545ca4e0cSMáté Eckl 		switch (lookup_type) {
9645ca4e0cSMáté Eckl 		case NF_TPROXY_LOOKUP_LISTENER:
974461568aSKuniyuki Iwashima 			sk = inet6_lookup_listener(net, hinfo, skb,
985711b4e8SMáté Eckl 						   thoff + __tcp_hdrlen(hp),
9945ca4e0cSMáté Eckl 						   saddr, sport,
10045ca4e0cSMáté Eckl 						   daddr, ntohs(dport),
10145ca4e0cSMáté Eckl 						   in->ifindex, 0);
10245ca4e0cSMáté Eckl 
10345ca4e0cSMáté Eckl 			if (sk && !refcount_inc_not_zero(&sk->sk_refcnt))
10445ca4e0cSMáté Eckl 				sk = NULL;
10545ca4e0cSMáté Eckl 			/* NOTE: we return listeners even if bound to
10645ca4e0cSMáté Eckl 			 * 0.0.0.0, those are filtered out in
10745ca4e0cSMáté Eckl 			 * xt_socket, since xt_TPROXY needs 0 bound
10845ca4e0cSMáté Eckl 			 * listeners too
10945ca4e0cSMáté Eckl 			 */
11045ca4e0cSMáté Eckl 			break;
11145ca4e0cSMáté Eckl 		case NF_TPROXY_LOOKUP_ESTABLISHED:
1124461568aSKuniyuki Iwashima 			sk = __inet6_lookup_established(net, hinfo, saddr, sport, daddr,
1134461568aSKuniyuki Iwashima 							ntohs(dport), in->ifindex, 0);
11445ca4e0cSMáté Eckl 			break;
11545ca4e0cSMáté Eckl 		default:
11645ca4e0cSMáté Eckl 			BUG();
11745ca4e0cSMáté Eckl 		}
11845ca4e0cSMáté Eckl 		break;
1195711b4e8SMáté Eckl 		}
12045ca4e0cSMáté Eckl 	case IPPROTO_UDP:
12145ca4e0cSMáté Eckl 		sk = udp6_lib_lookup(net, saddr, sport, daddr, dport,
12245ca4e0cSMáté Eckl 				     in->ifindex);
12345ca4e0cSMáté Eckl 		if (sk) {
12445ca4e0cSMáté Eckl 			int connected = (sk->sk_state == TCP_ESTABLISHED);
12545ca4e0cSMáté Eckl 			int wildcard = ipv6_addr_any(&sk->sk_v6_rcv_saddr);
12645ca4e0cSMáté Eckl 
12745ca4e0cSMáté Eckl 			/* NOTE: we return listeners even if bound to
12845ca4e0cSMáté Eckl 			 * 0.0.0.0, those are filtered out in
12945ca4e0cSMáté Eckl 			 * xt_socket, since xt_TPROXY needs 0 bound
13045ca4e0cSMáté Eckl 			 * listeners too
13145ca4e0cSMáté Eckl 			 */
13245ca4e0cSMáté Eckl 			if ((lookup_type == NF_TPROXY_LOOKUP_ESTABLISHED && (!connected || wildcard)) ||
13345ca4e0cSMáté Eckl 			    (lookup_type == NF_TPROXY_LOOKUP_LISTENER && connected)) {
13445ca4e0cSMáté Eckl 				sock_put(sk);
13545ca4e0cSMáté Eckl 				sk = NULL;
13645ca4e0cSMáté Eckl 			}
13745ca4e0cSMáté Eckl 		}
13845ca4e0cSMáté Eckl 		break;
13945ca4e0cSMáté Eckl 	default:
14045ca4e0cSMáté Eckl 		WARN_ON(1);
14145ca4e0cSMáté Eckl 		sk = NULL;
14245ca4e0cSMáté Eckl 	}
14345ca4e0cSMáté Eckl 
14445ca4e0cSMáté Eckl 	pr_debug("tproxy socket lookup: proto %u %pI6:%u -> %pI6:%u, lookup type: %d, sock %p\n",
14545ca4e0cSMáté Eckl 		 protocol, saddr, ntohs(sport), daddr, ntohs(dport), lookup_type, sk);
14645ca4e0cSMáté Eckl 
14745ca4e0cSMáté Eckl 	return sk;
14845ca4e0cSMáté Eckl }
14945ca4e0cSMáté Eckl EXPORT_SYMBOL_GPL(nf_tproxy_get_sock_v6);
15045ca4e0cSMáté Eckl 
15145ca4e0cSMáté Eckl MODULE_LICENSE("GPL");
15245ca4e0cSMáté Eckl MODULE_AUTHOR("Balazs Scheidler, Krisztian Kovacs");
1530a9b3385SNorman Rasmussen MODULE_DESCRIPTION("Netfilter IPv6 transparent proxy support");
154