1e8439270SKOVACS Krisztian /* 2e8439270SKOVACS Krisztian * Transparent proxy support for Linux/iptables 3e8439270SKOVACS Krisztian * 46ad78893SBalazs Scheidler * Copyright (c) 2006-2010 BalaBit IT Ltd. 5e8439270SKOVACS Krisztian * Author: Balazs Scheidler, Krisztian Kovacs 6e8439270SKOVACS Krisztian * 7e8439270SKOVACS Krisztian * This program is free software; you can redistribute it and/or modify 8e8439270SKOVACS Krisztian * it under the terms of the GNU General Public License version 2 as 9e8439270SKOVACS Krisztian * published by the Free Software Foundation. 10e8439270SKOVACS Krisztian * 11e8439270SKOVACS Krisztian */ 12ff67e4e4SJan Engelhardt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 13e8439270SKOVACS Krisztian #include <linux/module.h> 14e8439270SKOVACS Krisztian #include <linux/skbuff.h> 15e8439270SKOVACS Krisztian #include <linux/ip.h> 16e8439270SKOVACS Krisztian #include <net/checksum.h> 17e8439270SKOVACS Krisztian #include <net/udp.h> 1893742cf8SFlorian Westphal #include <net/tcp.h> 19e8439270SKOVACS Krisztian #include <net/inet_sock.h> 2093742cf8SFlorian Westphal #include <net/inet_hashtables.h> 21cc6eb433SBalazs Scheidler #include <linux/inetdevice.h> 22e8439270SKOVACS Krisztian #include <linux/netfilter/x_tables.h> 23e8439270SKOVACS Krisztian #include <linux/netfilter_ipv4/ip_tables.h> 24e8439270SKOVACS Krisztian 25e8439270SKOVACS Krisztian #include <net/netfilter/ipv4/nf_defrag_ipv4.h> 26f6318e55SKOVACS Krisztian 27c0cd1156SIgor Maravić #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) 28f6318e55SKOVACS Krisztian #define XT_TPROXY_HAVE_IPV6 1 29cc6eb433SBalazs Scheidler #include <net/if_inet6.h> 30cc6eb433SBalazs Scheidler #include <net/addrconf.h> 3193742cf8SFlorian Westphal #include <net/inet6_hashtables.h> 32cc6eb433SBalazs Scheidler #include <linux/netfilter_ipv6/ip6_tables.h> 336ad78893SBalazs Scheidler #include <net/netfilter/ipv6/nf_defrag_ipv6.h> 34cc6eb433SBalazs Scheidler #endif 35cc6eb433SBalazs Scheidler 36cc6eb433SBalazs Scheidler #include <linux/netfilter/xt_TPROXY.h> 37cc6eb433SBalazs Scheidler 3893742cf8SFlorian Westphal enum nf_tproxy_lookup_t { 3993742cf8SFlorian Westphal NFT_LOOKUP_LISTENER, 4093742cf8SFlorian Westphal NFT_LOOKUP_ESTABLISHED, 4193742cf8SFlorian Westphal }; 4293742cf8SFlorian Westphal 43d503b30bSFlorian Westphal static bool tproxy_sk_is_transparent(struct sock *sk) 44d503b30bSFlorian Westphal { 458b580147SEric Dumazet switch (sk->sk_state) { 468b580147SEric Dumazet case TCP_TIME_WAIT: 47d503b30bSFlorian Westphal if (inet_twsk(sk)->tw_transparent) 48d503b30bSFlorian Westphal return true; 498b580147SEric Dumazet break; 508b580147SEric Dumazet case TCP_NEW_SYN_RECV: 518b580147SEric Dumazet if (inet_rsk(inet_reqsk(sk))->no_srccheck) 528b580147SEric Dumazet return true; 538b580147SEric Dumazet break; 548b580147SEric Dumazet default: 558b580147SEric Dumazet if (inet_sk(sk)->transparent) 568b580147SEric Dumazet return true; 57d503b30bSFlorian Westphal } 588b580147SEric Dumazet 598b580147SEric Dumazet sock_gen_put(sk); 60d503b30bSFlorian Westphal return false; 61d503b30bSFlorian Westphal } 62d503b30bSFlorian Westphal 63cc6eb433SBalazs Scheidler static inline __be32 64cc6eb433SBalazs Scheidler tproxy_laddr4(struct sk_buff *skb, __be32 user_laddr, __be32 daddr) 65cc6eb433SBalazs Scheidler { 66cc6eb433SBalazs Scheidler struct in_device *indev; 67cc6eb433SBalazs Scheidler __be32 laddr; 68cc6eb433SBalazs Scheidler 69cc6eb433SBalazs Scheidler if (user_laddr) 70cc6eb433SBalazs Scheidler return user_laddr; 71cc6eb433SBalazs Scheidler 72cc6eb433SBalazs Scheidler laddr = 0; 73cc6eb433SBalazs Scheidler rcu_read_lock(); 74cc6eb433SBalazs Scheidler indev = __in_dev_get_rcu(skb->dev); 75cc6eb433SBalazs Scheidler for_primary_ifa(indev) { 76cc6eb433SBalazs Scheidler laddr = ifa->ifa_local; 77cc6eb433SBalazs Scheidler break; 78cc6eb433SBalazs Scheidler } endfor_ifa(indev); 79cc6eb433SBalazs Scheidler rcu_read_unlock(); 80cc6eb433SBalazs Scheidler 81cc6eb433SBalazs Scheidler return laddr ? laddr : daddr; 82cc6eb433SBalazs Scheidler } 83e8439270SKOVACS Krisztian 8493742cf8SFlorian Westphal /* 8593742cf8SFlorian Westphal * This is used when the user wants to intercept a connection matching 8693742cf8SFlorian Westphal * an explicit iptables rule. In this case the sockets are assumed 8793742cf8SFlorian Westphal * matching in preference order: 8893742cf8SFlorian Westphal * 8993742cf8SFlorian Westphal * - match: if there's a fully established connection matching the 9093742cf8SFlorian Westphal * _packet_ tuple, it is returned, assuming the redirection 9193742cf8SFlorian Westphal * already took place and we process a packet belonging to an 9293742cf8SFlorian Westphal * established connection 9393742cf8SFlorian Westphal * 9493742cf8SFlorian Westphal * - match: if there's a listening socket matching the redirection 9593742cf8SFlorian Westphal * (e.g. on-port & on-ip of the connection), it is returned, 9693742cf8SFlorian Westphal * regardless if it was bound to 0.0.0.0 or an explicit 9793742cf8SFlorian Westphal * address. The reasoning is that if there's an explicit rule, it 9893742cf8SFlorian Westphal * does not really matter if the listener is bound to an interface 9993742cf8SFlorian Westphal * or to 0. The user already stated that he wants redirection 10093742cf8SFlorian Westphal * (since he added the rule). 10193742cf8SFlorian Westphal * 10293742cf8SFlorian Westphal * Please note that there's an overlap between what a TPROXY target 10393742cf8SFlorian Westphal * and a socket match will match. Normally if you have both rules the 10493742cf8SFlorian Westphal * "socket" match will be the first one, effectively all packets 10593742cf8SFlorian Westphal * belonging to established connections going through that one. 10693742cf8SFlorian Westphal */ 10793742cf8SFlorian Westphal static inline struct sock * 108a583636aSCraig Gallek nf_tproxy_get_sock_v4(struct net *net, struct sk_buff *skb, void *hp, 109a583636aSCraig Gallek const u8 protocol, 11093742cf8SFlorian Westphal const __be32 saddr, const __be32 daddr, 11193742cf8SFlorian Westphal const __be16 sport, const __be16 dport, 11293742cf8SFlorian Westphal const struct net_device *in, 11393742cf8SFlorian Westphal const enum nf_tproxy_lookup_t lookup_type) 11493742cf8SFlorian Westphal { 11593742cf8SFlorian Westphal struct sock *sk; 116a583636aSCraig Gallek struct tcphdr *tcph; 11793742cf8SFlorian Westphal 11893742cf8SFlorian Westphal switch (protocol) { 11993742cf8SFlorian Westphal case IPPROTO_TCP: 12093742cf8SFlorian Westphal switch (lookup_type) { 12193742cf8SFlorian Westphal case NFT_LOOKUP_LISTENER: 122a583636aSCraig Gallek tcph = hp; 123a583636aSCraig Gallek sk = inet_lookup_listener(net, &tcp_hashinfo, skb, 124a583636aSCraig Gallek ip_hdrlen(skb) + 125a583636aSCraig Gallek __tcp_hdrlen(tcph), 12693742cf8SFlorian Westphal saddr, sport, 12793742cf8SFlorian Westphal daddr, dport, 12893742cf8SFlorian Westphal in->ifindex); 12993742cf8SFlorian Westphal 130dcbe3590SEric Dumazet if (sk && !atomic_inc_not_zero(&sk->sk_refcnt)) 131dcbe3590SEric Dumazet sk = NULL; 13293742cf8SFlorian Westphal /* NOTE: we return listeners even if bound to 13393742cf8SFlorian Westphal * 0.0.0.0, those are filtered out in 13493742cf8SFlorian Westphal * xt_socket, since xt_TPROXY needs 0 bound 13593742cf8SFlorian Westphal * listeners too 13693742cf8SFlorian Westphal */ 13793742cf8SFlorian Westphal break; 13893742cf8SFlorian Westphal case NFT_LOOKUP_ESTABLISHED: 13993742cf8SFlorian Westphal sk = inet_lookup_established(net, &tcp_hashinfo, 14093742cf8SFlorian Westphal saddr, sport, daddr, dport, 14193742cf8SFlorian Westphal in->ifindex); 14293742cf8SFlorian Westphal break; 14393742cf8SFlorian Westphal default: 14493742cf8SFlorian Westphal BUG(); 14593742cf8SFlorian Westphal } 14693742cf8SFlorian Westphal break; 14793742cf8SFlorian Westphal case IPPROTO_UDP: 14893742cf8SFlorian Westphal sk = udp4_lib_lookup(net, saddr, sport, daddr, dport, 14993742cf8SFlorian Westphal in->ifindex); 15093742cf8SFlorian Westphal if (sk) { 15193742cf8SFlorian Westphal int connected = (sk->sk_state == TCP_ESTABLISHED); 15293742cf8SFlorian Westphal int wildcard = (inet_sk(sk)->inet_rcv_saddr == 0); 15393742cf8SFlorian Westphal 15493742cf8SFlorian Westphal /* NOTE: we return listeners even if bound to 15593742cf8SFlorian Westphal * 0.0.0.0, those are filtered out in 15693742cf8SFlorian Westphal * xt_socket, since xt_TPROXY needs 0 bound 15793742cf8SFlorian Westphal * listeners too 15893742cf8SFlorian Westphal */ 15993742cf8SFlorian Westphal if ((lookup_type == NFT_LOOKUP_ESTABLISHED && (!connected || wildcard)) || 16093742cf8SFlorian Westphal (lookup_type == NFT_LOOKUP_LISTENER && connected)) { 16193742cf8SFlorian Westphal sock_put(sk); 16293742cf8SFlorian Westphal sk = NULL; 16393742cf8SFlorian Westphal } 16493742cf8SFlorian Westphal } 16593742cf8SFlorian Westphal break; 16693742cf8SFlorian Westphal default: 16793742cf8SFlorian Westphal WARN_ON(1); 16893742cf8SFlorian Westphal sk = NULL; 16993742cf8SFlorian Westphal } 17093742cf8SFlorian Westphal 17193742cf8SFlorian Westphal pr_debug("tproxy socket lookup: proto %u %08x:%u -> %08x:%u, lookup type: %d, sock %p\n", 17293742cf8SFlorian Westphal protocol, ntohl(saddr), ntohs(sport), ntohl(daddr), ntohs(dport), lookup_type, sk); 17393742cf8SFlorian Westphal 17493742cf8SFlorian Westphal return sk; 17593742cf8SFlorian Westphal } 17693742cf8SFlorian Westphal 177d8b3bfc2SFlorian Westphal #ifdef XT_TPROXY_HAVE_IPV6 17893742cf8SFlorian Westphal static inline struct sock * 179a583636aSCraig Gallek nf_tproxy_get_sock_v6(struct net *net, struct sk_buff *skb, int thoff, void *hp, 180a583636aSCraig Gallek const u8 protocol, 18193742cf8SFlorian Westphal const struct in6_addr *saddr, const struct in6_addr *daddr, 18293742cf8SFlorian Westphal const __be16 sport, const __be16 dport, 18393742cf8SFlorian Westphal const struct net_device *in, 18493742cf8SFlorian Westphal const enum nf_tproxy_lookup_t lookup_type) 18593742cf8SFlorian Westphal { 18693742cf8SFlorian Westphal struct sock *sk; 187a583636aSCraig Gallek struct tcphdr *tcph; 18893742cf8SFlorian Westphal 18993742cf8SFlorian Westphal switch (protocol) { 19093742cf8SFlorian Westphal case IPPROTO_TCP: 19193742cf8SFlorian Westphal switch (lookup_type) { 19293742cf8SFlorian Westphal case NFT_LOOKUP_LISTENER: 193a583636aSCraig Gallek tcph = hp; 194a583636aSCraig Gallek sk = inet6_lookup_listener(net, &tcp_hashinfo, skb, 195a583636aSCraig Gallek thoff + __tcp_hdrlen(tcph), 19693742cf8SFlorian Westphal saddr, sport, 19793742cf8SFlorian Westphal daddr, ntohs(dport), 19893742cf8SFlorian Westphal in->ifindex); 19993742cf8SFlorian Westphal 200dcbe3590SEric Dumazet if (sk && !atomic_inc_not_zero(&sk->sk_refcnt)) 201dcbe3590SEric Dumazet sk = NULL; 20293742cf8SFlorian Westphal /* NOTE: we return listeners even if bound to 20393742cf8SFlorian Westphal * 0.0.0.0, those are filtered out in 20493742cf8SFlorian Westphal * xt_socket, since xt_TPROXY needs 0 bound 20593742cf8SFlorian Westphal * listeners too 20693742cf8SFlorian Westphal */ 20793742cf8SFlorian Westphal break; 20893742cf8SFlorian Westphal case NFT_LOOKUP_ESTABLISHED: 20993742cf8SFlorian Westphal sk = __inet6_lookup_established(net, &tcp_hashinfo, 21093742cf8SFlorian Westphal saddr, sport, daddr, ntohs(dport), 21193742cf8SFlorian Westphal in->ifindex); 21293742cf8SFlorian Westphal break; 21393742cf8SFlorian Westphal default: 21493742cf8SFlorian Westphal BUG(); 21593742cf8SFlorian Westphal } 21693742cf8SFlorian Westphal break; 21793742cf8SFlorian Westphal case IPPROTO_UDP: 21893742cf8SFlorian Westphal sk = udp6_lib_lookup(net, saddr, sport, daddr, dport, 21993742cf8SFlorian Westphal in->ifindex); 22093742cf8SFlorian Westphal if (sk) { 22193742cf8SFlorian Westphal int connected = (sk->sk_state == TCP_ESTABLISHED); 222efe4208fSEric Dumazet int wildcard = ipv6_addr_any(&sk->sk_v6_rcv_saddr); 22393742cf8SFlorian Westphal 22493742cf8SFlorian Westphal /* NOTE: we return listeners even if bound to 22593742cf8SFlorian Westphal * 0.0.0.0, those are filtered out in 22693742cf8SFlorian Westphal * xt_socket, since xt_TPROXY needs 0 bound 22793742cf8SFlorian Westphal * listeners too 22893742cf8SFlorian Westphal */ 22993742cf8SFlorian Westphal if ((lookup_type == NFT_LOOKUP_ESTABLISHED && (!connected || wildcard)) || 23093742cf8SFlorian Westphal (lookup_type == NFT_LOOKUP_LISTENER && connected)) { 23193742cf8SFlorian Westphal sock_put(sk); 23293742cf8SFlorian Westphal sk = NULL; 23393742cf8SFlorian Westphal } 23493742cf8SFlorian Westphal } 23593742cf8SFlorian Westphal break; 23693742cf8SFlorian Westphal default: 23793742cf8SFlorian Westphal WARN_ON(1); 23893742cf8SFlorian Westphal sk = NULL; 23993742cf8SFlorian Westphal } 24093742cf8SFlorian Westphal 24193742cf8SFlorian Westphal pr_debug("tproxy socket lookup: proto %u %pI6:%u -> %pI6:%u, lookup type: %d, sock %p\n", 24293742cf8SFlorian Westphal protocol, saddr, ntohs(sport), daddr, ntohs(dport), lookup_type, sk); 24393742cf8SFlorian Westphal 24493742cf8SFlorian Westphal return sk; 24593742cf8SFlorian Westphal } 24693742cf8SFlorian Westphal #endif 24793742cf8SFlorian Westphal 248106e4c26SBalazs Scheidler /** 2492c53040fSBen Hutchings * tproxy_handle_time_wait4 - handle IPv4 TCP TIME_WAIT reopen redirections 250106e4c26SBalazs Scheidler * @skb: The skb being processed. 2516ad78893SBalazs Scheidler * @laddr: IPv4 address to redirect to or zero. 2526ad78893SBalazs Scheidler * @lport: TCP port to redirect to or zero. 253106e4c26SBalazs Scheidler * @sk: The TIME_WAIT TCP socket found by the lookup. 254106e4c26SBalazs Scheidler * 255106e4c26SBalazs Scheidler * We have to handle SYN packets arriving to TIME_WAIT sockets 256106e4c26SBalazs Scheidler * differently: instead of reopening the connection we should rather 257106e4c26SBalazs Scheidler * redirect the new connection to the proxy if there's a listener 258106e4c26SBalazs Scheidler * socket present. 259106e4c26SBalazs Scheidler * 2606ad78893SBalazs Scheidler * tproxy_handle_time_wait4() consumes the socket reference passed in. 261106e4c26SBalazs Scheidler * 262106e4c26SBalazs Scheidler * Returns the listener socket if there's one, the TIME_WAIT socket if 263106e4c26SBalazs Scheidler * no such listener is found, or NULL if the TCP header is incomplete. 264106e4c26SBalazs Scheidler */ 265106e4c26SBalazs Scheidler static struct sock * 266686c9b50SEric W. Biederman tproxy_handle_time_wait4(struct net *net, struct sk_buff *skb, 267686c9b50SEric W. Biederman __be32 laddr, __be16 lport, struct sock *sk) 268106e4c26SBalazs Scheidler { 269106e4c26SBalazs Scheidler const struct iphdr *iph = ip_hdr(skb); 270106e4c26SBalazs Scheidler struct tcphdr _hdr, *hp; 271106e4c26SBalazs Scheidler 272106e4c26SBalazs Scheidler hp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_hdr), &_hdr); 273106e4c26SBalazs Scheidler if (hp == NULL) { 274106e4c26SBalazs Scheidler inet_twsk_put(inet_twsk(sk)); 275106e4c26SBalazs Scheidler return NULL; 276106e4c26SBalazs Scheidler } 277106e4c26SBalazs Scheidler 278106e4c26SBalazs Scheidler if (hp->syn && !hp->rst && !hp->ack && !hp->fin) { 279106e4c26SBalazs Scheidler /* SYN to a TIME_WAIT socket, we'd rather redirect it 280106e4c26SBalazs Scheidler * to a listener socket if there's one */ 281106e4c26SBalazs Scheidler struct sock *sk2; 282106e4c26SBalazs Scheidler 283a583636aSCraig Gallek sk2 = nf_tproxy_get_sock_v4(net, skb, hp, iph->protocol, 2846ad78893SBalazs Scheidler iph->saddr, laddr ? laddr : iph->daddr, 2856ad78893SBalazs Scheidler hp->source, lport ? lport : hp->dest, 2866ad78893SBalazs Scheidler skb->dev, NFT_LOOKUP_LISTENER); 287106e4c26SBalazs Scheidler if (sk2) { 288dbe7faa4SEric Dumazet inet_twsk_deschedule_put(inet_twsk(sk)); 2896ad78893SBalazs Scheidler sk = sk2; 2906ad78893SBalazs Scheidler } 2916ad78893SBalazs Scheidler } 2926ad78893SBalazs Scheidler 2936ad78893SBalazs Scheidler return sk; 2946ad78893SBalazs Scheidler } 2956ad78893SBalazs Scheidler 296fd158d79SFlorian Westphal /* assign a socket to the skb -- consumes sk */ 297fd158d79SFlorian Westphal static void 298fd158d79SFlorian Westphal nf_tproxy_assign_sock(struct sk_buff *skb, struct sock *sk) 299fd158d79SFlorian Westphal { 300fd158d79SFlorian Westphal skb_orphan(skb); 301fd158d79SFlorian Westphal skb->sk = sk; 302fd158d79SFlorian Westphal skb->destructor = sock_edemux; 303fd158d79SFlorian Westphal } 304fd158d79SFlorian Westphal 305cc6eb433SBalazs Scheidler static unsigned int 306686c9b50SEric W. Biederman tproxy_tg4(struct net *net, struct sk_buff *skb, __be32 laddr, __be16 lport, 307cc6eb433SBalazs Scheidler u_int32_t mark_mask, u_int32_t mark_value) 308cc6eb433SBalazs Scheidler { 309cc6eb433SBalazs Scheidler const struct iphdr *iph = ip_hdr(skb); 310cc6eb433SBalazs Scheidler struct udphdr _hdr, *hp; 311cc6eb433SBalazs Scheidler struct sock *sk; 312cc6eb433SBalazs Scheidler 313cc6eb433SBalazs Scheidler hp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_hdr), &_hdr); 314cc6eb433SBalazs Scheidler if (hp == NULL) 315cc6eb433SBalazs Scheidler return NF_DROP; 316cc6eb433SBalazs Scheidler 317cc6eb433SBalazs Scheidler /* check if there's an ongoing connection on the packet 318cc6eb433SBalazs Scheidler * addresses, this happens if the redirect already happened 319cc6eb433SBalazs Scheidler * and the current packet belongs to an already established 320cc6eb433SBalazs Scheidler * connection */ 321a583636aSCraig Gallek sk = nf_tproxy_get_sock_v4(net, skb, hp, iph->protocol, 322cc6eb433SBalazs Scheidler iph->saddr, iph->daddr, 323cc6eb433SBalazs Scheidler hp->source, hp->dest, 324cc6eb433SBalazs Scheidler skb->dev, NFT_LOOKUP_ESTABLISHED); 325cc6eb433SBalazs Scheidler 326cc6eb433SBalazs Scheidler laddr = tproxy_laddr4(skb, laddr, iph->daddr); 327cc6eb433SBalazs Scheidler if (!lport) 328cc6eb433SBalazs Scheidler lport = hp->dest; 329cc6eb433SBalazs Scheidler 330cc6eb433SBalazs Scheidler /* UDP has no TCP_TIME_WAIT state, so we never enter here */ 331cc6eb433SBalazs Scheidler if (sk && sk->sk_state == TCP_TIME_WAIT) 332cc6eb433SBalazs Scheidler /* reopening a TIME_WAIT connection needs special handling */ 333686c9b50SEric W. Biederman sk = tproxy_handle_time_wait4(net, skb, laddr, lport, sk); 334cc6eb433SBalazs Scheidler else if (!sk) 335cc6eb433SBalazs Scheidler /* no, there's no established connection, check if 336cc6eb433SBalazs Scheidler * there's a listener on the redirected addr/port */ 337a583636aSCraig Gallek sk = nf_tproxy_get_sock_v4(net, skb, hp, iph->protocol, 338cc6eb433SBalazs Scheidler iph->saddr, laddr, 339cc6eb433SBalazs Scheidler hp->source, lport, 340cc6eb433SBalazs Scheidler skb->dev, NFT_LOOKUP_LISTENER); 341cc6eb433SBalazs Scheidler 342cc6eb433SBalazs Scheidler /* NOTE: assign_sock consumes our sk reference */ 343d503b30bSFlorian Westphal if (sk && tproxy_sk_is_transparent(sk)) { 344cc6eb433SBalazs Scheidler /* This should be in a separate target, but we don't do multiple 345cc6eb433SBalazs Scheidler targets on the same rule yet */ 346cc6eb433SBalazs Scheidler skb->mark = (skb->mark & ~mark_mask) ^ mark_value; 347cc6eb433SBalazs Scheidler 348cc6eb433SBalazs Scheidler pr_debug("redirecting: proto %hhu %pI4:%hu -> %pI4:%hu, mark: %x\n", 349cc6eb433SBalazs Scheidler iph->protocol, &iph->daddr, ntohs(hp->dest), 350cc6eb433SBalazs Scheidler &laddr, ntohs(lport), skb->mark); 351d503b30bSFlorian Westphal 352d503b30bSFlorian Westphal nf_tproxy_assign_sock(skb, sk); 353cc6eb433SBalazs Scheidler return NF_ACCEPT; 354cc6eb433SBalazs Scheidler } 355cc6eb433SBalazs Scheidler 356cc6eb433SBalazs Scheidler pr_debug("no socket, dropping: proto %hhu %pI4:%hu -> %pI4:%hu, mark: %x\n", 357cc6eb433SBalazs Scheidler iph->protocol, &iph->saddr, ntohs(hp->source), 358cc6eb433SBalazs Scheidler &iph->daddr, ntohs(hp->dest), skb->mark); 359cc6eb433SBalazs Scheidler return NF_DROP; 360cc6eb433SBalazs Scheidler } 361cc6eb433SBalazs Scheidler 362cc6eb433SBalazs Scheidler static unsigned int 363cc6eb433SBalazs Scheidler tproxy_tg4_v0(struct sk_buff *skb, const struct xt_action_param *par) 364cc6eb433SBalazs Scheidler { 365cc6eb433SBalazs Scheidler const struct xt_tproxy_target_info *tgi = par->targinfo; 366cc6eb433SBalazs Scheidler 367686c9b50SEric W. Biederman return tproxy_tg4(par->net, skb, tgi->laddr, tgi->lport, tgi->mark_mask, tgi->mark_value); 368cc6eb433SBalazs Scheidler } 369cc6eb433SBalazs Scheidler 370cc6eb433SBalazs Scheidler static unsigned int 371cc6eb433SBalazs Scheidler tproxy_tg4_v1(struct sk_buff *skb, const struct xt_action_param *par) 372cc6eb433SBalazs Scheidler { 373cc6eb433SBalazs Scheidler const struct xt_tproxy_target_info_v1 *tgi = par->targinfo; 374cc6eb433SBalazs Scheidler 375686c9b50SEric W. Biederman return tproxy_tg4(par->net, skb, tgi->laddr.ip, tgi->lport, tgi->mark_mask, tgi->mark_value); 376cc6eb433SBalazs Scheidler } 377cc6eb433SBalazs Scheidler 378f6318e55SKOVACS Krisztian #ifdef XT_TPROXY_HAVE_IPV6 379cc6eb433SBalazs Scheidler 380cc6eb433SBalazs Scheidler static inline const struct in6_addr * 381cc6eb433SBalazs Scheidler tproxy_laddr6(struct sk_buff *skb, const struct in6_addr *user_laddr, 382cc6eb433SBalazs Scheidler const struct in6_addr *daddr) 383cc6eb433SBalazs Scheidler { 384cc6eb433SBalazs Scheidler struct inet6_dev *indev; 385cc6eb433SBalazs Scheidler struct inet6_ifaddr *ifa; 386cc6eb433SBalazs Scheidler struct in6_addr *laddr; 387cc6eb433SBalazs Scheidler 388cc6eb433SBalazs Scheidler if (!ipv6_addr_any(user_laddr)) 389cc6eb433SBalazs Scheidler return user_laddr; 390cc6eb433SBalazs Scheidler laddr = NULL; 391cc6eb433SBalazs Scheidler 392cc6eb433SBalazs Scheidler rcu_read_lock(); 393cc6eb433SBalazs Scheidler indev = __in6_dev_get(skb->dev); 394cc6eb433SBalazs Scheidler if (indev) 395cc6eb433SBalazs Scheidler list_for_each_entry(ifa, &indev->addr_list, if_list) { 396cc6eb433SBalazs Scheidler if (ifa->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED)) 397cc6eb433SBalazs Scheidler continue; 398cc6eb433SBalazs Scheidler 399cc6eb433SBalazs Scheidler laddr = &ifa->addr; 400cc6eb433SBalazs Scheidler break; 401cc6eb433SBalazs Scheidler } 402cc6eb433SBalazs Scheidler rcu_read_unlock(); 403cc6eb433SBalazs Scheidler 404cc6eb433SBalazs Scheidler return laddr ? laddr : daddr; 405cc6eb433SBalazs Scheidler } 406cc6eb433SBalazs Scheidler 4076ad78893SBalazs Scheidler /** 4082c53040fSBen Hutchings * tproxy_handle_time_wait6 - handle IPv6 TCP TIME_WAIT reopen redirections 4096ad78893SBalazs Scheidler * @skb: The skb being processed. 4106ad78893SBalazs Scheidler * @tproto: Transport protocol. 4116ad78893SBalazs Scheidler * @thoff: Transport protocol header offset. 4126ad78893SBalazs Scheidler * @par: Iptables target parameters. 4136ad78893SBalazs Scheidler * @sk: The TIME_WAIT TCP socket found by the lookup. 4146ad78893SBalazs Scheidler * 4156ad78893SBalazs Scheidler * We have to handle SYN packets arriving to TIME_WAIT sockets 4166ad78893SBalazs Scheidler * differently: instead of reopening the connection we should rather 4176ad78893SBalazs Scheidler * redirect the new connection to the proxy if there's a listener 4186ad78893SBalazs Scheidler * socket present. 4196ad78893SBalazs Scheidler * 4206ad78893SBalazs Scheidler * tproxy_handle_time_wait6() consumes the socket reference passed in. 4216ad78893SBalazs Scheidler * 4226ad78893SBalazs Scheidler * Returns the listener socket if there's one, the TIME_WAIT socket if 4236ad78893SBalazs Scheidler * no such listener is found, or NULL if the TCP header is incomplete. 424106e4c26SBalazs Scheidler */ 4256ad78893SBalazs Scheidler static struct sock * 4266ad78893SBalazs Scheidler tproxy_handle_time_wait6(struct sk_buff *skb, int tproto, int thoff, 4276ad78893SBalazs Scheidler const struct xt_action_param *par, 4286ad78893SBalazs Scheidler struct sock *sk) 4296ad78893SBalazs Scheidler { 4306ad78893SBalazs Scheidler const struct ipv6hdr *iph = ipv6_hdr(skb); 4316ad78893SBalazs Scheidler struct tcphdr _hdr, *hp; 4326ad78893SBalazs Scheidler const struct xt_tproxy_target_info_v1 *tgi = par->targinfo; 4336ad78893SBalazs Scheidler 4346ad78893SBalazs Scheidler hp = skb_header_pointer(skb, thoff, sizeof(_hdr), &_hdr); 4356ad78893SBalazs Scheidler if (hp == NULL) { 4366ad78893SBalazs Scheidler inet_twsk_put(inet_twsk(sk)); 4376ad78893SBalazs Scheidler return NULL; 4386ad78893SBalazs Scheidler } 4396ad78893SBalazs Scheidler 4406ad78893SBalazs Scheidler if (hp->syn && !hp->rst && !hp->ack && !hp->fin) { 4416ad78893SBalazs Scheidler /* SYN to a TIME_WAIT socket, we'd rather redirect it 4426ad78893SBalazs Scheidler * to a listener socket if there's one */ 4436ad78893SBalazs Scheidler struct sock *sk2; 4446ad78893SBalazs Scheidler 445a583636aSCraig Gallek sk2 = nf_tproxy_get_sock_v6(par->net, skb, thoff, hp, tproto, 4466ad78893SBalazs Scheidler &iph->saddr, 447cc6eb433SBalazs Scheidler tproxy_laddr6(skb, &tgi->laddr.in6, &iph->daddr), 4486ad78893SBalazs Scheidler hp->source, 4496ad78893SBalazs Scheidler tgi->lport ? tgi->lport : hp->dest, 4506ad78893SBalazs Scheidler skb->dev, NFT_LOOKUP_LISTENER); 4516ad78893SBalazs Scheidler if (sk2) { 452dbe7faa4SEric Dumazet inet_twsk_deschedule_put(inet_twsk(sk)); 453106e4c26SBalazs Scheidler sk = sk2; 454106e4c26SBalazs Scheidler } 455106e4c26SBalazs Scheidler } 456106e4c26SBalazs Scheidler 457106e4c26SBalazs Scheidler return sk; 458106e4c26SBalazs Scheidler } 459106e4c26SBalazs Scheidler 460e8439270SKOVACS Krisztian static unsigned int 4616ad78893SBalazs Scheidler tproxy_tg6_v1(struct sk_buff *skb, const struct xt_action_param *par) 4626ad78893SBalazs Scheidler { 4636ad78893SBalazs Scheidler const struct ipv6hdr *iph = ipv6_hdr(skb); 4646ad78893SBalazs Scheidler const struct xt_tproxy_target_info_v1 *tgi = par->targinfo; 4656ad78893SBalazs Scheidler struct udphdr _hdr, *hp; 4666ad78893SBalazs Scheidler struct sock *sk; 467cc6eb433SBalazs Scheidler const struct in6_addr *laddr; 468cc6eb433SBalazs Scheidler __be16 lport; 46984018f55SHans Schillstrom int thoff = 0; 4706ad78893SBalazs Scheidler int tproto; 4716ad78893SBalazs Scheidler 47284018f55SHans Schillstrom tproto = ipv6_find_hdr(skb, &thoff, -1, NULL, NULL); 4736ad78893SBalazs Scheidler if (tproto < 0) { 4746ad78893SBalazs Scheidler pr_debug("unable to find transport header in IPv6 packet, dropping\n"); 4756ad78893SBalazs Scheidler return NF_DROP; 4766ad78893SBalazs Scheidler } 4776ad78893SBalazs Scheidler 4786ad78893SBalazs Scheidler hp = skb_header_pointer(skb, thoff, sizeof(_hdr), &_hdr); 4796ad78893SBalazs Scheidler if (hp == NULL) { 4806ad78893SBalazs Scheidler pr_debug("unable to grab transport header contents in IPv6 packet, dropping\n"); 4816ad78893SBalazs Scheidler return NF_DROP; 4826ad78893SBalazs Scheidler } 4836ad78893SBalazs Scheidler 4846ad78893SBalazs Scheidler /* check if there's an ongoing connection on the packet 4856ad78893SBalazs Scheidler * addresses, this happens if the redirect already happened 4866ad78893SBalazs Scheidler * and the current packet belongs to an already established 4876ad78893SBalazs Scheidler * connection */ 488a583636aSCraig Gallek sk = nf_tproxy_get_sock_v6(par->net, skb, thoff, hp, tproto, 4896ad78893SBalazs Scheidler &iph->saddr, &iph->daddr, 4906ad78893SBalazs Scheidler hp->source, hp->dest, 491106e4c26SBalazs Scheidler par->in, NFT_LOOKUP_ESTABLISHED); 492106e4c26SBalazs Scheidler 493cc6eb433SBalazs Scheidler laddr = tproxy_laddr6(skb, &tgi->laddr.in6, &iph->daddr); 494cc6eb433SBalazs Scheidler lport = tgi->lport ? tgi->lport : hp->dest; 495cc6eb433SBalazs Scheidler 496106e4c26SBalazs Scheidler /* UDP has no TCP_TIME_WAIT state, so we never enter here */ 497106e4c26SBalazs Scheidler if (sk && sk->sk_state == TCP_TIME_WAIT) 4986ad78893SBalazs Scheidler /* reopening a TIME_WAIT connection needs special handling */ 4996ad78893SBalazs Scheidler sk = tproxy_handle_time_wait6(skb, tproto, thoff, par, sk); 500106e4c26SBalazs Scheidler else if (!sk) 5016ad78893SBalazs Scheidler /* no there's no established connection, check if 5026ad78893SBalazs Scheidler * there's a listener on the redirected addr/port */ 503a583636aSCraig Gallek sk = nf_tproxy_get_sock_v6(par->net, skb, thoff, hp, 504a583636aSCraig Gallek tproto, &iph->saddr, laddr, 505cc6eb433SBalazs Scheidler hp->source, lport, 506106e4c26SBalazs Scheidler par->in, NFT_LOOKUP_LISTENER); 507e8439270SKOVACS Krisztian 508e8439270SKOVACS Krisztian /* NOTE: assign_sock consumes our sk reference */ 509d503b30bSFlorian Westphal if (sk && tproxy_sk_is_transparent(sk)) { 510e8439270SKOVACS Krisztian /* This should be in a separate target, but we don't do multiple 511e8439270SKOVACS Krisztian targets on the same rule yet */ 512e8439270SKOVACS Krisztian skb->mark = (skb->mark & ~tgi->mark_mask) ^ tgi->mark_value; 513e8439270SKOVACS Krisztian 5146ad78893SBalazs Scheidler pr_debug("redirecting: proto %hhu %pI6:%hu -> %pI6:%hu, mark: %x\n", 515cc6eb433SBalazs Scheidler tproto, &iph->saddr, ntohs(hp->source), 516cc6eb433SBalazs Scheidler laddr, ntohs(lport), skb->mark); 517d503b30bSFlorian Westphal 518d503b30bSFlorian Westphal nf_tproxy_assign_sock(skb, sk); 519e8439270SKOVACS Krisztian return NF_ACCEPT; 520e8439270SKOVACS Krisztian } 521e8439270SKOVACS Krisztian 5226ad78893SBalazs Scheidler pr_debug("no socket, dropping: proto %hhu %pI6:%hu -> %pI6:%hu, mark: %x\n", 523cc6eb433SBalazs Scheidler tproto, &iph->saddr, ntohs(hp->source), 524cc6eb433SBalazs Scheidler &iph->daddr, ntohs(hp->dest), skb->mark); 525cc6eb433SBalazs Scheidler 526e8439270SKOVACS Krisztian return NF_DROP; 527e8439270SKOVACS Krisztian } 528e8439270SKOVACS Krisztian 5296ad78893SBalazs Scheidler static int tproxy_tg6_check(const struct xt_tgchk_param *par) 5306ad78893SBalazs Scheidler { 5316ad78893SBalazs Scheidler const struct ip6t_ip6 *i = par->entryinfo; 5326ad78893SBalazs Scheidler 5333d8c6dceSPablo Neira Ayuso if ((i->proto == IPPROTO_TCP || i->proto == IPPROTO_UDP) && 5343d8c6dceSPablo Neira Ayuso !(i->invflags & IP6T_INV_PROTO)) 5356ad78893SBalazs Scheidler return 0; 5366ad78893SBalazs Scheidler 5376ad78893SBalazs Scheidler pr_info("Can be used only in combination with " 5386ad78893SBalazs Scheidler "either -p tcp or -p udp\n"); 5396ad78893SBalazs Scheidler return -EINVAL; 5406ad78893SBalazs Scheidler } 5416ad78893SBalazs Scheidler #endif 5426ad78893SBalazs Scheidler 5436ad78893SBalazs Scheidler static int tproxy_tg4_check(const struct xt_tgchk_param *par) 544e8439270SKOVACS Krisztian { 545af5d6dc2SJan Engelhardt const struct ipt_ip *i = par->entryinfo; 546e8439270SKOVACS Krisztian 547e8439270SKOVACS Krisztian if ((i->proto == IPPROTO_TCP || i->proto == IPPROTO_UDP) 548e8439270SKOVACS Krisztian && !(i->invflags & IPT_INV_PROTO)) 549d6b00a53SJan Engelhardt return 0; 550e8439270SKOVACS Krisztian 551ff67e4e4SJan Engelhardt pr_info("Can be used only in combination with " 552e8439270SKOVACS Krisztian "either -p tcp or -p udp\n"); 553d6b00a53SJan Engelhardt return -EINVAL; 554e8439270SKOVACS Krisztian } 555e8439270SKOVACS Krisztian 5566ad78893SBalazs Scheidler static struct xt_target tproxy_tg_reg[] __read_mostly = { 5576ad78893SBalazs Scheidler { 558e8439270SKOVACS Krisztian .name = "TPROXY", 55970a85166SChangli Gao .family = NFPROTO_IPV4, 560e8439270SKOVACS Krisztian .table = "mangle", 5616ad78893SBalazs Scheidler .target = tproxy_tg4_v0, 5626ad78893SBalazs Scheidler .revision = 0, 563e8439270SKOVACS Krisztian .targetsize = sizeof(struct xt_tproxy_target_info), 5646ad78893SBalazs Scheidler .checkentry = tproxy_tg4_check, 565e8439270SKOVACS Krisztian .hooks = 1 << NF_INET_PRE_ROUTING, 566e8439270SKOVACS Krisztian .me = THIS_MODULE, 5676ad78893SBalazs Scheidler }, 5686ad78893SBalazs Scheidler { 5696ad78893SBalazs Scheidler .name = "TPROXY", 5706ad78893SBalazs Scheidler .family = NFPROTO_IPV4, 5716ad78893SBalazs Scheidler .table = "mangle", 5726ad78893SBalazs Scheidler .target = tproxy_tg4_v1, 5736ad78893SBalazs Scheidler .revision = 1, 5746ad78893SBalazs Scheidler .targetsize = sizeof(struct xt_tproxy_target_info_v1), 5756ad78893SBalazs Scheidler .checkentry = tproxy_tg4_check, 5766ad78893SBalazs Scheidler .hooks = 1 << NF_INET_PRE_ROUTING, 5776ad78893SBalazs Scheidler .me = THIS_MODULE, 5786ad78893SBalazs Scheidler }, 579f6318e55SKOVACS Krisztian #ifdef XT_TPROXY_HAVE_IPV6 5806ad78893SBalazs Scheidler { 5816ad78893SBalazs Scheidler .name = "TPROXY", 5826ad78893SBalazs Scheidler .family = NFPROTO_IPV6, 5836ad78893SBalazs Scheidler .table = "mangle", 5846ad78893SBalazs Scheidler .target = tproxy_tg6_v1, 5856ad78893SBalazs Scheidler .revision = 1, 5866ad78893SBalazs Scheidler .targetsize = sizeof(struct xt_tproxy_target_info_v1), 5876ad78893SBalazs Scheidler .checkentry = tproxy_tg6_check, 5886ad78893SBalazs Scheidler .hooks = 1 << NF_INET_PRE_ROUTING, 5896ad78893SBalazs Scheidler .me = THIS_MODULE, 5906ad78893SBalazs Scheidler }, 5916ad78893SBalazs Scheidler #endif 5926ad78893SBalazs Scheidler 593e8439270SKOVACS Krisztian }; 594e8439270SKOVACS Krisztian 595e8439270SKOVACS Krisztian static int __init tproxy_tg_init(void) 596e8439270SKOVACS Krisztian { 597e8439270SKOVACS Krisztian nf_defrag_ipv4_enable(); 598f6318e55SKOVACS Krisztian #ifdef XT_TPROXY_HAVE_IPV6 5996ad78893SBalazs Scheidler nf_defrag_ipv6_enable(); 6006ad78893SBalazs Scheidler #endif 6016ad78893SBalazs Scheidler 6026ad78893SBalazs Scheidler return xt_register_targets(tproxy_tg_reg, ARRAY_SIZE(tproxy_tg_reg)); 603e8439270SKOVACS Krisztian } 604e8439270SKOVACS Krisztian 605e8439270SKOVACS Krisztian static void __exit tproxy_tg_exit(void) 606e8439270SKOVACS Krisztian { 6076ad78893SBalazs Scheidler xt_unregister_targets(tproxy_tg_reg, ARRAY_SIZE(tproxy_tg_reg)); 608e8439270SKOVACS Krisztian } 609e8439270SKOVACS Krisztian 610e8439270SKOVACS Krisztian module_init(tproxy_tg_init); 611e8439270SKOVACS Krisztian module_exit(tproxy_tg_exit); 612e8439270SKOVACS Krisztian MODULE_LICENSE("GPL"); 6136ad78893SBalazs Scheidler MODULE_AUTHOR("Balazs Scheidler, Krisztian Kovacs"); 614e8439270SKOVACS Krisztian MODULE_DESCRIPTION("Netfilter transparent proxy (TPROXY) target module."); 615e8439270SKOVACS Krisztian MODULE_ALIAS("ipt_TPROXY"); 6166ad78893SBalazs Scheidler MODULE_ALIAS("ip6t_TPROXY"); 617