11602Srgrimes// SPDX-License-Identifier: GPL-2.0-only
21602Srgrimes#include <net/netfilter/nf_tproxy.h>
31602Srgrimes#include <linux/module.h>
41602Srgrimes#include <net/inet6_hashtables.h>
51602Srgrimes#include <net/addrconf.h>
61602Srgrimes#include <net/udp.h>
71602Srgrimes#include <net/tcp.h>
81602Srgrimes
91602Srgrimesconst struct in6_addr *
101602Srgrimesnf_tproxy_laddr6(struct sk_buff *skb, const struct in6_addr *user_laddr,
111602Srgrimes	      const struct in6_addr *daddr)
121602Srgrimes{
131602Srgrimes	struct inet6_dev *indev;
141602Srgrimes	struct inet6_ifaddr *ifa;
151602Srgrimes	struct in6_addr *laddr;
161602Srgrimes
171602Srgrimes	if (!ipv6_addr_any(user_laddr))
181602Srgrimes		return user_laddr;
191602Srgrimes	laddr = NULL;
201602Srgrimes
211602Srgrimes	indev = __in6_dev_get(skb->dev);
221602Srgrimes	if (indev) {
231602Srgrimes		read_lock_bh(&indev->lock);
241602Srgrimes		list_for_each_entry(ifa, &indev->addr_list, if_list) {
251602Srgrimes			if (ifa->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED))
261602Srgrimes				continue;
271602Srgrimes
281602Srgrimes			laddr = &ifa->addr;
291602Srgrimes			break;
301602Srgrimes		}
311602Srgrimes		read_unlock_bh(&indev->lock);
321602Srgrimes	}
331602Srgrimes
341602Srgrimes	return laddr ? laddr : daddr;
351602Srgrimes}
361602SrgrimesEXPORT_SYMBOL_GPL(nf_tproxy_laddr6);
371602Srgrimes
3883551Sdillonstruct sock *
3983551Sdillonnf_tproxy_handle_time_wait6(struct sk_buff *skb, int tproto, int thoff,
4083551Sdillon			 struct net *net,
411602Srgrimes			 const struct in6_addr *laddr,
4255127Speter			 const __be16 lport,
431602Srgrimes			 struct sock *sk)
4455127Speter{
451602Srgrimes	const struct ipv6hdr *iph = ipv6_hdr(skb);
461602Srgrimes	struct tcphdr _hdr, *hp;
471602Srgrimes
488870Srgrimes	hp = skb_header_pointer(skb, thoff, sizeof(_hdr), &_hdr);
491602Srgrimes	if (hp == NULL) {
501602Srgrimes		inet_twsk_put(inet_twsk(sk));
511602Srgrimes		return NULL;
521602Srgrimes	}
5376176Smarkm
5476176Smarkm	if (hp->syn && !hp->rst && !hp->ack && !hp->fin) {
551602Srgrimes		/* SYN to a TIME_WAIT socket, we'd rather redirect it
561602Srgrimes		 * to a listener socket if there's one */
571602Srgrimes		struct sock *sk2;
5817141Sjkh
591602Srgrimes		sk2 = nf_tproxy_get_sock_v6(net, skb, thoff, tproto,
601602Srgrimes					    &iph->saddr,
611602Srgrimes					    nf_tproxy_laddr6(skb, laddr, &iph->daddr),
621602Srgrimes					    hp->source,
631602Srgrimes					    lport ? lport : hp->dest,
641602Srgrimes					    skb->dev, NF_TPROXY_LOOKUP_LISTENER);
651602Srgrimes		if (sk2) {
661602Srgrimes			nf_tproxy_twsk_deschedule_put(inet_twsk(sk));
671602Srgrimes			sk = sk2;
681602Srgrimes		}
691602Srgrimes	}
701602Srgrimes
711603Srgrimes	return sk;
721603Srgrimes}
731602SrgrimesEXPORT_SYMBOL_GPL(nf_tproxy_handle_time_wait6);
741602Srgrimes
751602Srgrimesstruct sock *
7618798Speternf_tproxy_get_sock_v6(struct net *net, struct sk_buff *skb, int thoff,
771602Srgrimes		      const u8 protocol,
781602Srgrimes		      const struct in6_addr *saddr, const struct in6_addr *daddr,
791602Srgrimes		      const __be16 sport, const __be16 dport,
8018798Speter		      const struct net_device *in,
8118798Speter		      const enum nf_tproxy_lookup_t lookup_type)
821603Srgrimes{
8318798Speter	struct inet_hashinfo *hinfo = net->ipv4.tcp_death_row.hashinfo;
8418798Speter	struct sock *sk;
8518798Speter
861602Srgrimes	switch (protocol) {
871603Srgrimes	case IPPROTO_TCP: {
881602Srgrimes		struct tcphdr _hdr, *hp;
891602Srgrimes
901602Srgrimes		hp = skb_header_pointer(skb, thoff,
9118798Speter					sizeof(struct tcphdr), &_hdr);
9218798Speter		if (hp == NULL)
931602Srgrimes			return NULL;
941603Srgrimes
9518798Speter		switch (lookup_type) {
9682263Speter		case NF_TPROXY_LOOKUP_LISTENER:
9718798Speter			sk = inet6_lookup_listener(net, hinfo, skb,
981602Srgrimes						   thoff + __tcp_hdrlen(hp),
991602Srgrimes						   saddr, sport,
1001603Srgrimes						   daddr, ntohs(dport),
1011603Srgrimes						   in->ifindex, 0);
1021602Srgrimes
1031603Srgrimes			if (sk && !refcount_inc_not_zero(&sk->sk_refcnt))
1041602Srgrimes				sk = NULL;
10518798Speter			/* NOTE: we return listeners even if bound to
1061602Srgrimes			 * 0.0.0.0, those are filtered out in
10782263Speter			 * xt_socket, since xt_TPROXY needs 0 bound
1081603Srgrimes			 * listeners too
1091602Srgrimes			 */
11082263Speter			break;
11182263Speter		case NF_TPROXY_LOOKUP_ESTABLISHED:
11282263Speter			sk = __inet6_lookup_established(net, hinfo, saddr, sport, daddr,
11382263Speter							ntohs(dport), in->ifindex, 0);
11482263Speter			break;
11582263Speter		default:
11682263Speter			BUG();
11782263Speter		}
1181602Srgrimes		break;
1191602Srgrimes		}
1201602Srgrimes	case IPPROTO_UDP:
1211602Srgrimes		sk = udp6_lib_lookup(net, saddr, sport, daddr, dport,
12282263Speter				     in->ifindex);
12382263Speter		if (sk) {
1241603Srgrimes			int connected = (sk->sk_state == TCP_ESTABLISHED);
1251602Srgrimes			int wildcard = ipv6_addr_any(&sk->sk_v6_rcv_saddr);
1261602Srgrimes
12718798Speter			/* NOTE: we return listeners even if bound to
12818798Speter			 * 0.0.0.0, those are filtered out in
1291603Srgrimes			 * xt_socket, since xt_TPROXY needs 0 bound
1301602Srgrimes			 * listeners too
1311602Srgrimes			 */
13218798Speter			if ((lookup_type == NF_TPROXY_LOOKUP_ESTABLISHED && (!connected || wildcard)) ||
1331602Srgrimes			    (lookup_type == NF_TPROXY_LOOKUP_LISTENER && connected)) {
1341602Srgrimes				sock_put(sk);
1351602Srgrimes				sk = NULL;
1361602Srgrimes			}
13718798Speter		}
13818798Speter		break;
13918798Speter	default:
14018798Speter		WARN_ON(1);
14118798Speter		sk = NULL;
14218798Speter	}
14318798Speter
14418798Speter	pr_debug("tproxy socket lookup: proto %u %pI6:%u -> %pI6:%u, lookup type: %d, sock %p\n",
14518798Speter		 protocol, saddr, ntohs(sport), daddr, ntohs(dport), lookup_type, sk);
14618798Speter
1471602Srgrimes	return sk;
1481602Srgrimes}
1491602SrgrimesEXPORT_SYMBOL_GPL(nf_tproxy_get_sock_v6);
1501602Srgrimes
1511602SrgrimesMODULE_LICENSE("GPL");
15218798SpeterMODULE_AUTHOR("Balazs Scheidler, Krisztian Kovacs");
15318798SpeterMODULE_DESCRIPTION("Netfilter IPv6 transparent proxy support");
15418798Speter