diff options
| -rw-r--r-- | net/netfilter/xt_TPROXY.c | 202 |
1 files changed, 132 insertions, 70 deletions
diff --git a/net/netfilter/xt_TPROXY.c b/net/netfilter/xt_TPROXY.c index d5f97e2302b8..19c482caf30b 100644 --- a/net/netfilter/xt_TPROXY.c +++ b/net/netfilter/xt_TPROXY.c | |||
| @@ -16,15 +16,41 @@ | |||
| 16 | #include <net/checksum.h> | 16 | #include <net/checksum.h> |
| 17 | #include <net/udp.h> | 17 | #include <net/udp.h> |
| 18 | #include <net/inet_sock.h> | 18 | #include <net/inet_sock.h> |
| 19 | 19 | #include <linux/inetdevice.h> | |
| 20 | #include <linux/netfilter/x_tables.h> | 20 | #include <linux/netfilter/x_tables.h> |
| 21 | #include <linux/netfilter_ipv4/ip_tables.h> | 21 | #include <linux/netfilter_ipv4/ip_tables.h> |
| 22 | #include <linux/netfilter_ipv6/ip6_tables.h> | ||
| 23 | #include <linux/netfilter/xt_TPROXY.h> | ||
| 24 | 22 | ||
| 25 | #include <net/netfilter/ipv4/nf_defrag_ipv4.h> | 23 | #include <net/netfilter/ipv4/nf_defrag_ipv4.h> |
| 24 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | ||
| 25 | #include <net/if_inet6.h> | ||
| 26 | #include <net/addrconf.h> | ||
| 27 | #include <linux/netfilter_ipv6/ip6_tables.h> | ||
| 26 | #include <net/netfilter/ipv6/nf_defrag_ipv6.h> | 28 | #include <net/netfilter/ipv6/nf_defrag_ipv6.h> |
| 29 | #endif | ||
| 30 | |||
| 27 | #include <net/netfilter/nf_tproxy_core.h> | 31 | #include <net/netfilter/nf_tproxy_core.h> |
| 32 | #include <linux/netfilter/xt_TPROXY.h> | ||
| 33 | |||
| 34 | static inline __be32 | ||
| 35 | tproxy_laddr4(struct sk_buff *skb, __be32 user_laddr, __be32 daddr) | ||
| 36 | { | ||
| 37 | struct in_device *indev; | ||
| 38 | __be32 laddr; | ||
| 39 | |||
| 40 | if (user_laddr) | ||
| 41 | return user_laddr; | ||
| 42 | |||
| 43 | laddr = 0; | ||
| 44 | rcu_read_lock(); | ||
| 45 | indev = __in_dev_get_rcu(skb->dev); | ||
| 46 | for_primary_ifa(indev) { | ||
| 47 | laddr = ifa->ifa_local; | ||
| 48 | break; | ||
| 49 | } endfor_ifa(indev); | ||
| 50 | rcu_read_unlock(); | ||
| 51 | |||
| 52 | return laddr ? laddr : daddr; | ||
| 53 | } | ||
| 28 | 54 | ||
| 29 | /** | 55 | /** |
| 30 | * tproxy_handle_time_wait4() - handle IPv4 TCP TIME_WAIT reopen redirections | 56 | * tproxy_handle_time_wait4() - handle IPv4 TCP TIME_WAIT reopen redirections |
| @@ -75,60 +101,6 @@ tproxy_handle_time_wait4(struct sk_buff *skb, __be32 laddr, __be16 lport, | |||
| 75 | return sk; | 101 | return sk; |
| 76 | } | 102 | } |
| 77 | 103 | ||
| 78 | /** | ||
| 79 | * tproxy_handle_time_wait6() - handle IPv6 TCP TIME_WAIT reopen redirections | ||
| 80 | * @skb: The skb being processed. | ||
| 81 | * @tproto: Transport protocol. | ||
| 82 | * @thoff: Transport protocol header offset. | ||
| 83 | * @par: Iptables target parameters. | ||
| 84 | * @sk: The TIME_WAIT TCP socket found by the lookup. | ||
| 85 | * | ||
| 86 | * We have to handle SYN packets arriving to TIME_WAIT sockets | ||
| 87 | * differently: instead of reopening the connection we should rather | ||
| 88 | * redirect the new connection to the proxy if there's a listener | ||
| 89 | * socket present. | ||
| 90 | * | ||
| 91 | * tproxy_handle_time_wait6() consumes the socket reference passed in. | ||
| 92 | * | ||
| 93 | * Returns the listener socket if there's one, the TIME_WAIT socket if | ||
| 94 | * no such listener is found, or NULL if the TCP header is incomplete. | ||
| 95 | */ | ||
| 96 | static struct sock * | ||
| 97 | tproxy_handle_time_wait6(struct sk_buff *skb, int tproto, int thoff, | ||
| 98 | const struct xt_action_param *par, | ||
| 99 | struct sock *sk) | ||
| 100 | { | ||
| 101 | const struct ipv6hdr *iph = ipv6_hdr(skb); | ||
| 102 | struct tcphdr _hdr, *hp; | ||
| 103 | const struct xt_tproxy_target_info_v1 *tgi = par->targinfo; | ||
| 104 | |||
| 105 | hp = skb_header_pointer(skb, thoff, sizeof(_hdr), &_hdr); | ||
| 106 | if (hp == NULL) { | ||
| 107 | inet_twsk_put(inet_twsk(sk)); | ||
| 108 | return NULL; | ||
| 109 | } | ||
| 110 | |||
| 111 | if (hp->syn && !hp->rst && !hp->ack && !hp->fin) { | ||
| 112 | /* SYN to a TIME_WAIT socket, we'd rather redirect it | ||
| 113 | * to a listener socket if there's one */ | ||
| 114 | struct sock *sk2; | ||
| 115 | |||
| 116 | sk2 = nf_tproxy_get_sock_v6(dev_net(skb->dev), tproto, | ||
| 117 | &iph->saddr, | ||
| 118 | !ipv6_addr_any(&tgi->laddr.in6) ? &tgi->laddr.in6 : &iph->daddr, | ||
| 119 | hp->source, | ||
| 120 | tgi->lport ? tgi->lport : hp->dest, | ||
| 121 | skb->dev, NFT_LOOKUP_LISTENER); | ||
| 122 | if (sk2) { | ||
| 123 | inet_twsk_deschedule(inet_twsk(sk), &tcp_death_row); | ||
| 124 | inet_twsk_put(inet_twsk(sk)); | ||
| 125 | sk = sk2; | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | return sk; | ||
| 130 | } | ||
| 131 | |||
| 132 | static unsigned int | 104 | static unsigned int |
| 133 | tproxy_tg4(struct sk_buff *skb, __be32 laddr, __be16 lport, | 105 | tproxy_tg4(struct sk_buff *skb, __be32 laddr, __be16 lport, |
| 134 | u_int32_t mark_mask, u_int32_t mark_value) | 106 | u_int32_t mark_mask, u_int32_t mark_value) |
| @@ -150,6 +122,10 @@ tproxy_tg4(struct sk_buff *skb, __be32 laddr, __be16 lport, | |||
| 150 | hp->source, hp->dest, | 122 | hp->source, hp->dest, |
| 151 | skb->dev, NFT_LOOKUP_ESTABLISHED); | 123 | skb->dev, NFT_LOOKUP_ESTABLISHED); |
| 152 | 124 | ||
| 125 | laddr = tproxy_laddr4(skb, laddr, iph->daddr); | ||
| 126 | if (!lport) | ||
| 127 | lport = hp->dest; | ||
| 128 | |||
| 153 | /* UDP has no TCP_TIME_WAIT state, so we never enter here */ | 129 | /* UDP has no TCP_TIME_WAIT state, so we never enter here */ |
| 154 | if (sk && sk->sk_state == TCP_TIME_WAIT) | 130 | if (sk && sk->sk_state == TCP_TIME_WAIT) |
| 155 | /* reopening a TIME_WAIT connection needs special handling */ | 131 | /* reopening a TIME_WAIT connection needs special handling */ |
| @@ -158,8 +134,8 @@ tproxy_tg4(struct sk_buff *skb, __be32 laddr, __be16 lport, | |||
| 158 | /* no, there's no established connection, check if | 134 | /* no, there's no established connection, check if |
| 159 | * there's a listener on the redirected addr/port */ | 135 | * there's a listener on the redirected addr/port */ |
| 160 | sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), iph->protocol, | 136 | sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), iph->protocol, |
| 161 | iph->saddr, laddr ? laddr : iph->daddr, | 137 | iph->saddr, laddr, |
| 162 | hp->source, lport ? lport : hp->dest, | 138 | hp->source, lport, |
| 163 | skb->dev, NFT_LOOKUP_LISTENER); | 139 | skb->dev, NFT_LOOKUP_LISTENER); |
| 164 | 140 | ||
| 165 | /* NOTE: assign_sock consumes our sk reference */ | 141 | /* NOTE: assign_sock consumes our sk reference */ |
| @@ -174,9 +150,9 @@ tproxy_tg4(struct sk_buff *skb, __be32 laddr, __be16 lport, | |||
| 174 | return NF_ACCEPT; | 150 | return NF_ACCEPT; |
| 175 | } | 151 | } |
| 176 | 152 | ||
| 177 | pr_debug("no socket, dropping: proto %hhu %08x:%hu -> %08x:%hu, mark: %x\n", | 153 | pr_debug("no socket, dropping: proto %hhu %pI4:%hu -> %pI4:%hu, mark: %x\n", |
| 178 | iph->protocol, ntohl(iph->daddr), ntohs(hp->dest), | 154 | iph->protocol, &iph->saddr, ntohs(hp->source), |
| 179 | ntohl(laddr), ntohs(lport), skb->mark); | 155 | &iph->daddr, ntohs(hp->dest), skb->mark); |
| 180 | return NF_DROP; | 156 | return NF_DROP; |
| 181 | } | 157 | } |
| 182 | 158 | ||
| @@ -197,6 +173,88 @@ tproxy_tg4_v1(struct sk_buff *skb, const struct xt_action_param *par) | |||
| 197 | } | 173 | } |
| 198 | 174 | ||
| 199 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | 175 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) |
| 176 | |||
| 177 | static inline const struct in6_addr * | ||
| 178 | tproxy_laddr6(struct sk_buff *skb, const struct in6_addr *user_laddr, | ||
| 179 | const struct in6_addr *daddr) | ||
| 180 | { | ||
| 181 | struct inet6_dev *indev; | ||
| 182 | struct inet6_ifaddr *ifa; | ||
| 183 | struct in6_addr *laddr; | ||
| 184 | |||
| 185 | if (!ipv6_addr_any(user_laddr)) | ||
| 186 | return user_laddr; | ||
| 187 | laddr = NULL; | ||
| 188 | |||
| 189 | rcu_read_lock(); | ||
| 190 | indev = __in6_dev_get(skb->dev); | ||
| 191 | if (indev) | ||
| 192 | list_for_each_entry(ifa, &indev->addr_list, if_list) { | ||
| 193 | if (ifa->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED)) | ||
| 194 | continue; | ||
| 195 | |||
| 196 | laddr = &ifa->addr; | ||
| 197 | break; | ||
| 198 | } | ||
| 199 | rcu_read_unlock(); | ||
| 200 | |||
| 201 | return laddr ? laddr : daddr; | ||
| 202 | } | ||
| 203 | |||
| 204 | /** | ||
| 205 | * tproxy_handle_time_wait6() - handle IPv6 TCP TIME_WAIT reopen redirections | ||
| 206 | * @skb: The skb being processed. | ||
| 207 | * @tproto: Transport protocol. | ||
| 208 | * @thoff: Transport protocol header offset. | ||
| 209 | * @par: Iptables target parameters. | ||
| 210 | * @sk: The TIME_WAIT TCP socket found by the lookup. | ||
| 211 | * | ||
| 212 | * We have to handle SYN packets arriving to TIME_WAIT sockets | ||
| 213 | * differently: instead of reopening the connection we should rather | ||
| 214 | * redirect the new connection to the proxy if there's a listener | ||
| 215 | * socket present. | ||
| 216 | * | ||
| 217 | * tproxy_handle_time_wait6() consumes the socket reference passed in. | ||
| 218 | * | ||
| 219 | * Returns the listener socket if there's one, the TIME_WAIT socket if | ||
| 220 | * no such listener is found, or NULL if the TCP header is incomplete. | ||
| 221 | */ | ||
| 222 | static struct sock * | ||
| 223 | tproxy_handle_time_wait6(struct sk_buff *skb, int tproto, int thoff, | ||
| 224 | const struct xt_action_param *par, | ||
| 225 | struct sock *sk) | ||
| 226 | { | ||
| 227 | const struct ipv6hdr *iph = ipv6_hdr(skb); | ||
| 228 | struct tcphdr _hdr, *hp; | ||
| 229 | const struct xt_tproxy_target_info_v1 *tgi = par->targinfo; | ||
| 230 | |||
| 231 | hp = skb_header_pointer(skb, thoff, sizeof(_hdr), &_hdr); | ||
| 232 | if (hp == NULL) { | ||
| 233 | inet_twsk_put(inet_twsk(sk)); | ||
| 234 | return NULL; | ||
| 235 | } | ||
| 236 | |||
| 237 | if (hp->syn && !hp->rst && !hp->ack && !hp->fin) { | ||
| 238 | /* SYN to a TIME_WAIT socket, we'd rather redirect it | ||
| 239 | * to a listener socket if there's one */ | ||
| 240 | struct sock *sk2; | ||
| 241 | |||
| 242 | sk2 = nf_tproxy_get_sock_v6(dev_net(skb->dev), tproto, | ||
| 243 | &iph->saddr, | ||
| 244 | tproxy_laddr6(skb, &tgi->laddr.in6, &iph->daddr), | ||
| 245 | hp->source, | ||
| 246 | tgi->lport ? tgi->lport : hp->dest, | ||
| 247 | skb->dev, NFT_LOOKUP_LISTENER); | ||
| 248 | if (sk2) { | ||
| 249 | inet_twsk_deschedule(inet_twsk(sk), &tcp_death_row); | ||
| 250 | inet_twsk_put(inet_twsk(sk)); | ||
| 251 | sk = sk2; | ||
| 252 | } | ||
| 253 | } | ||
| 254 | |||
| 255 | return sk; | ||
| 256 | } | ||
| 257 | |||
| 200 | static unsigned int | 258 | static unsigned int |
| 201 | tproxy_tg6_v1(struct sk_buff *skb, const struct xt_action_param *par) | 259 | tproxy_tg6_v1(struct sk_buff *skb, const struct xt_action_param *par) |
| 202 | { | 260 | { |
| @@ -204,6 +262,8 @@ tproxy_tg6_v1(struct sk_buff *skb, const struct xt_action_param *par) | |||
| 204 | const struct xt_tproxy_target_info_v1 *tgi = par->targinfo; | 262 | const struct xt_tproxy_target_info_v1 *tgi = par->targinfo; |
| 205 | struct udphdr _hdr, *hp; | 263 | struct udphdr _hdr, *hp; |
| 206 | struct sock *sk; | 264 | struct sock *sk; |
| 265 | const struct in6_addr *laddr; | ||
| 266 | __be16 lport; | ||
| 207 | int thoff; | 267 | int thoff; |
| 208 | int tproto; | 268 | int tproto; |
| 209 | 269 | ||
| @@ -228,6 +288,9 @@ tproxy_tg6_v1(struct sk_buff *skb, const struct xt_action_param *par) | |||
| 228 | hp->source, hp->dest, | 288 | hp->source, hp->dest, |
| 229 | par->in, NFT_LOOKUP_ESTABLISHED); | 289 | par->in, NFT_LOOKUP_ESTABLISHED); |
| 230 | 290 | ||
| 291 | laddr = tproxy_laddr6(skb, &tgi->laddr.in6, &iph->daddr); | ||
| 292 | lport = tgi->lport ? tgi->lport : hp->dest; | ||
| 293 | |||
| 231 | /* UDP has no TCP_TIME_WAIT state, so we never enter here */ | 294 | /* UDP has no TCP_TIME_WAIT state, so we never enter here */ |
| 232 | if (sk && sk->sk_state == TCP_TIME_WAIT) | 295 | if (sk && sk->sk_state == TCP_TIME_WAIT) |
| 233 | /* reopening a TIME_WAIT connection needs special handling */ | 296 | /* reopening a TIME_WAIT connection needs special handling */ |
| @@ -236,10 +299,8 @@ tproxy_tg6_v1(struct sk_buff *skb, const struct xt_action_param *par) | |||
| 236 | /* no there's no established connection, check if | 299 | /* no there's no established connection, check if |
| 237 | * there's a listener on the redirected addr/port */ | 300 | * there's a listener on the redirected addr/port */ |
| 238 | sk = nf_tproxy_get_sock_v6(dev_net(skb->dev), tproto, | 301 | sk = nf_tproxy_get_sock_v6(dev_net(skb->dev), tproto, |
| 239 | &iph->saddr, | 302 | &iph->saddr, laddr, |
| 240 | !ipv6_addr_any(&tgi->laddr.in6) ? &tgi->laddr.in6 : &iph->daddr, | 303 | hp->source, lport, |
| 241 | hp->source, | ||
| 242 | tgi->lport ? tgi->lport : hp->dest, | ||
| 243 | par->in, NFT_LOOKUP_LISTENER); | 304 | par->in, NFT_LOOKUP_LISTENER); |
| 244 | 305 | ||
| 245 | /* NOTE: assign_sock consumes our sk reference */ | 306 | /* NOTE: assign_sock consumes our sk reference */ |
| @@ -249,14 +310,15 @@ tproxy_tg6_v1(struct sk_buff *skb, const struct xt_action_param *par) | |||
| 249 | skb->mark = (skb->mark & ~tgi->mark_mask) ^ tgi->mark_value; | 310 | skb->mark = (skb->mark & ~tgi->mark_mask) ^ tgi->mark_value; |
| 250 | 311 | ||
| 251 | pr_debug("redirecting: proto %hhu %pI6:%hu -> %pI6:%hu, mark: %x\n", | 312 | pr_debug("redirecting: proto %hhu %pI6:%hu -> %pI6:%hu, mark: %x\n", |
| 252 | tproto, &iph->saddr, ntohs(hp->dest), | 313 | tproto, &iph->saddr, ntohs(hp->source), |
| 253 | &tgi->laddr.in6, ntohs(tgi->lport), skb->mark); | 314 | laddr, ntohs(lport), skb->mark); |
| 254 | return NF_ACCEPT; | 315 | return NF_ACCEPT; |
| 255 | } | 316 | } |
| 256 | 317 | ||
| 257 | pr_debug("no socket, dropping: proto %hhu %pI6:%hu -> %pI6:%hu, mark: %x\n", | 318 | pr_debug("no socket, dropping: proto %hhu %pI6:%hu -> %pI6:%hu, mark: %x\n", |
| 258 | tproto, &iph->saddr, ntohs(hp->dest), | 319 | tproto, &iph->saddr, ntohs(hp->source), |
| 259 | &tgi->laddr.in6, ntohs(tgi->lport), skb->mark); | 320 | &iph->daddr, ntohs(hp->dest), skb->mark); |
| 321 | |||
| 260 | return NF_DROP; | 322 | return NF_DROP; |
| 261 | } | 323 | } |
| 262 | 324 | ||
