1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2007-2008 BalaBit IT Ltd. |
4 | * Author: Krisztian Kovacs |
5 | */ |
6 | |
7 | #include <net/netfilter/nf_tproxy.h> |
8 | #include <linux/module.h> |
9 | #include <linux/skbuff.h> |
10 | #include <net/sock.h> |
11 | #include <net/inet_sock.h> |
12 | #include <linux/ip.h> |
13 | #include <net/checksum.h> |
14 | #include <net/udp.h> |
15 | #include <net/tcp.h> |
16 | #include <linux/inetdevice.h> |
17 | |
18 | struct sock * |
19 | nf_tproxy_handle_time_wait4(struct net *net, struct sk_buff *skb, |
20 | __be32 laddr, __be16 lport, struct sock *sk) |
21 | { |
22 | const struct iphdr *iph = ip_hdr(skb); |
23 | struct tcphdr _hdr, *hp; |
24 | |
25 | hp = skb_header_pointer(skb, offset: ip_hdrlen(skb), len: sizeof(_hdr), buffer: &_hdr); |
26 | if (hp == NULL) { |
27 | inet_twsk_put(tw: inet_twsk(sk)); |
28 | return NULL; |
29 | } |
30 | |
31 | if (hp->syn && !hp->rst && !hp->ack && !hp->fin) { |
32 | /* SYN to a TIME_WAIT socket, we'd rather redirect it |
33 | * to a listener socket if there's one */ |
34 | struct sock *sk2; |
35 | |
36 | sk2 = nf_tproxy_get_sock_v4(net, skb, protocol: iph->protocol, |
37 | saddr: iph->saddr, daddr: laddr ? laddr : iph->daddr, |
38 | sport: hp->source, dport: lport ? lport : hp->dest, |
39 | in: skb->dev, lookup_type: NF_TPROXY_LOOKUP_LISTENER); |
40 | if (sk2) { |
41 | nf_tproxy_twsk_deschedule_put(tw: inet_twsk(sk)); |
42 | sk = sk2; |
43 | } |
44 | } |
45 | |
46 | return sk; |
47 | } |
48 | EXPORT_SYMBOL_GPL(nf_tproxy_handle_time_wait4); |
49 | |
50 | __be32 nf_tproxy_laddr4(struct sk_buff *skb, __be32 user_laddr, __be32 daddr) |
51 | { |
52 | const struct in_ifaddr *ifa; |
53 | struct in_device *indev; |
54 | __be32 laddr; |
55 | |
56 | if (user_laddr) |
57 | return user_laddr; |
58 | |
59 | laddr = 0; |
60 | indev = __in_dev_get_rcu(dev: skb->dev); |
61 | |
62 | in_dev_for_each_ifa_rcu(ifa, indev) { |
63 | if (ifa->ifa_flags & IFA_F_SECONDARY) |
64 | continue; |
65 | |
66 | laddr = ifa->ifa_local; |
67 | break; |
68 | } |
69 | |
70 | return laddr ? laddr : daddr; |
71 | } |
72 | EXPORT_SYMBOL_GPL(nf_tproxy_laddr4); |
73 | |
74 | struct sock * |
75 | nf_tproxy_get_sock_v4(struct net *net, struct sk_buff *skb, |
76 | const u8 protocol, |
77 | const __be32 saddr, const __be32 daddr, |
78 | const __be16 sport, const __be16 dport, |
79 | const struct net_device *in, |
80 | const enum nf_tproxy_lookup_t lookup_type) |
81 | { |
82 | struct inet_hashinfo *hinfo = net->ipv4.tcp_death_row.hashinfo; |
83 | struct sock *sk; |
84 | |
85 | switch (protocol) { |
86 | case IPPROTO_TCP: { |
87 | struct tcphdr _hdr, *hp; |
88 | |
89 | hp = skb_header_pointer(skb, offset: ip_hdrlen(skb), |
90 | len: sizeof(struct tcphdr), buffer: &_hdr); |
91 | if (hp == NULL) |
92 | return NULL; |
93 | |
94 | switch (lookup_type) { |
95 | case NF_TPROXY_LOOKUP_LISTENER: |
96 | sk = inet_lookup_listener(net, hashinfo: hinfo, skb, |
97 | doff: ip_hdrlen(skb) + __tcp_hdrlen(th: hp), |
98 | saddr, sport, daddr, dport, |
99 | dif: in->ifindex, sdif: 0); |
100 | |
101 | if (sk && !refcount_inc_not_zero(r: &sk->sk_refcnt)) |
102 | sk = NULL; |
103 | /* NOTE: we return listeners even if bound to |
104 | * 0.0.0.0, those are filtered out in |
105 | * xt_socket, since xt_TPROXY needs 0 bound |
106 | * listeners too |
107 | */ |
108 | break; |
109 | case NF_TPROXY_LOOKUP_ESTABLISHED: |
110 | sk = inet_lookup_established(net, hashinfo: hinfo, saddr, sport, |
111 | daddr, dport, dif: in->ifindex); |
112 | break; |
113 | default: |
114 | BUG(); |
115 | } |
116 | break; |
117 | } |
118 | case IPPROTO_UDP: |
119 | sk = udp4_lib_lookup(net, saddr, sport, daddr, dport, |
120 | dif: in->ifindex); |
121 | if (sk) { |
122 | int connected = (sk->sk_state == TCP_ESTABLISHED); |
123 | int wildcard = (inet_sk(sk)->inet_rcv_saddr == 0); |
124 | |
125 | /* NOTE: we return listeners even if bound to |
126 | * 0.0.0.0, those are filtered out in |
127 | * xt_socket, since xt_TPROXY needs 0 bound |
128 | * listeners too |
129 | */ |
130 | if ((lookup_type == NF_TPROXY_LOOKUP_ESTABLISHED && |
131 | (!connected || wildcard)) || |
132 | (lookup_type == NF_TPROXY_LOOKUP_LISTENER && connected)) { |
133 | sock_put(sk); |
134 | sk = NULL; |
135 | } |
136 | } |
137 | break; |
138 | default: |
139 | WARN_ON(1); |
140 | sk = NULL; |
141 | } |
142 | |
143 | pr_debug("tproxy socket lookup: proto %u %08x:%u -> %08x:%u, lookup type: %d, sock %p\n" , |
144 | protocol, ntohl(saddr), ntohs(sport), ntohl(daddr), ntohs(dport), lookup_type, sk); |
145 | |
146 | return sk; |
147 | } |
148 | EXPORT_SYMBOL_GPL(nf_tproxy_get_sock_v4); |
149 | |
150 | MODULE_LICENSE("GPL" ); |
151 | MODULE_AUTHOR("Balazs Scheidler, Krisztian Kovacs" ); |
152 | MODULE_DESCRIPTION("Netfilter IPv4 transparent proxy support" ); |
153 | |