Patrick McHardy | 48b1de4 | 2013-08-27 08:50:14 +0200 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2013 Patrick McHardy <kaber@trash.net> |
| 3 | * |
| 4 | * This program is free software; you can redistribute it and/or modify |
| 5 | * it under the terms of the GNU General Public License version 2 as |
| 6 | * published by the Free Software Foundation. |
| 7 | */ |
| 8 | |
| 9 | #include <linux/module.h> |
| 10 | #include <linux/skbuff.h> |
| 11 | #include <net/tcp.h> |
| 12 | |
| 13 | #include <linux/netfilter_ipv4/ip_tables.h> |
| 14 | #include <linux/netfilter/x_tables.h> |
| 15 | #include <linux/netfilter/xt_SYNPROXY.h> |
| 16 | #include <net/netfilter/nf_conntrack.h> |
| 17 | #include <net/netfilter/nf_conntrack_seqadj.h> |
| 18 | #include <net/netfilter/nf_conntrack_synproxy.h> |
| 19 | |
| 20 | static struct iphdr * |
Liping Zhang | 2942119 | 2016-03-26 16:32:57 +0800 | [diff] [blame] | 21 | synproxy_build_ip(struct net *net, struct sk_buff *skb, __be32 saddr, |
| 22 | __be32 daddr) |
Patrick McHardy | 48b1de4 | 2013-08-27 08:50:14 +0200 | [diff] [blame] | 23 | { |
| 24 | struct iphdr *iph; |
| 25 | |
| 26 | skb_reset_network_header(skb); |
| 27 | iph = (struct iphdr *)skb_put(skb, sizeof(*iph)); |
| 28 | iph->version = 4; |
| 29 | iph->ihl = sizeof(*iph) / 4; |
| 30 | iph->tos = 0; |
| 31 | iph->id = 0; |
| 32 | iph->frag_off = htons(IP_DF); |
Nikolay Borisov | fa50d97 | 2016-02-15 12:11:27 +0200 | [diff] [blame] | 33 | iph->ttl = net->ipv4.sysctl_ip_default_ttl; |
Patrick McHardy | 48b1de4 | 2013-08-27 08:50:14 +0200 | [diff] [blame] | 34 | iph->protocol = IPPROTO_TCP; |
| 35 | iph->check = 0; |
| 36 | iph->saddr = saddr; |
| 37 | iph->daddr = daddr; |
| 38 | |
| 39 | return iph; |
| 40 | } |
| 41 | |
| 42 | static void |
Liping Zhang | 2942119 | 2016-03-26 16:32:57 +0800 | [diff] [blame] | 43 | synproxy_send_tcp(struct net *net, |
Eric W. Biederman | 6a1d689 | 2015-09-25 15:07:29 -0500 | [diff] [blame] | 44 | const struct sk_buff *skb, struct sk_buff *nskb, |
Patrick McHardy | 48b1de4 | 2013-08-27 08:50:14 +0200 | [diff] [blame] | 45 | struct nf_conntrack *nfct, enum ip_conntrack_info ctinfo, |
| 46 | struct iphdr *niph, struct tcphdr *nth, |
| 47 | unsigned int tcp_hdr_size) |
| 48 | { |
| 49 | nth->check = ~tcp_v4_check(tcp_hdr_size, niph->saddr, niph->daddr, 0); |
| 50 | nskb->ip_summed = CHECKSUM_PARTIAL; |
| 51 | nskb->csum_start = (unsigned char *)nth - nskb->head; |
| 52 | nskb->csum_offset = offsetof(struct tcphdr, check); |
| 53 | |
| 54 | skb_dst_set_noref(nskb, skb_dst(skb)); |
| 55 | nskb->protocol = htons(ETH_P_IP); |
Eric W. Biederman | e45f506 | 2015-09-25 15:07:30 -0500 | [diff] [blame] | 56 | if (ip_route_me_harder(net, nskb, RTN_UNSPEC)) |
Patrick McHardy | 48b1de4 | 2013-08-27 08:50:14 +0200 | [diff] [blame] | 57 | goto free_nskb; |
| 58 | |
| 59 | if (nfct) { |
| 60 | nskb->nfct = nfct; |
| 61 | nskb->nfctinfo = ctinfo; |
| 62 | nf_conntrack_get(nfct); |
| 63 | } |
| 64 | |
Eric W. Biederman | 33224b1 | 2015-10-07 16:48:46 -0500 | [diff] [blame] | 65 | ip_local_out(net, nskb->sk, nskb); |
Patrick McHardy | 48b1de4 | 2013-08-27 08:50:14 +0200 | [diff] [blame] | 66 | return; |
| 67 | |
| 68 | free_nskb: |
| 69 | kfree_skb(nskb); |
| 70 | } |
| 71 | |
| 72 | static void |
Liping Zhang | 2942119 | 2016-03-26 16:32:57 +0800 | [diff] [blame] | 73 | synproxy_send_client_synack(struct net *net, |
Eric W. Biederman | 6a1d689 | 2015-09-25 15:07:29 -0500 | [diff] [blame] | 74 | const struct sk_buff *skb, const struct tcphdr *th, |
Patrick McHardy | 48b1de4 | 2013-08-27 08:50:14 +0200 | [diff] [blame] | 75 | const struct synproxy_options *opts) |
| 76 | { |
| 77 | struct sk_buff *nskb; |
| 78 | struct iphdr *iph, *niph; |
| 79 | struct tcphdr *nth; |
| 80 | unsigned int tcp_hdr_size; |
| 81 | u16 mss = opts->mss; |
| 82 | |
| 83 | iph = ip_hdr(skb); |
| 84 | |
| 85 | tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts); |
| 86 | nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER, |
| 87 | GFP_ATOMIC); |
| 88 | if (nskb == NULL) |
| 89 | return; |
| 90 | skb_reserve(nskb, MAX_TCP_HEADER); |
| 91 | |
Liping Zhang | 2942119 | 2016-03-26 16:32:57 +0800 | [diff] [blame] | 92 | niph = synproxy_build_ip(net, nskb, iph->daddr, iph->saddr); |
Patrick McHardy | 48b1de4 | 2013-08-27 08:50:14 +0200 | [diff] [blame] | 93 | |
| 94 | skb_reset_transport_header(nskb); |
| 95 | nth = (struct tcphdr *)skb_put(nskb, tcp_hdr_size); |
| 96 | nth->source = th->dest; |
| 97 | nth->dest = th->source; |
| 98 | nth->seq = htonl(__cookie_v4_init_sequence(iph, th, &mss)); |
| 99 | nth->ack_seq = htonl(ntohl(th->seq) + 1); |
| 100 | tcp_flag_word(nth) = TCP_FLAG_SYN | TCP_FLAG_ACK; |
| 101 | if (opts->options & XT_SYNPROXY_OPT_ECN) |
| 102 | tcp_flag_word(nth) |= TCP_FLAG_ECE; |
| 103 | nth->doff = tcp_hdr_size / 4; |
| 104 | nth->window = 0; |
| 105 | nth->check = 0; |
| 106 | nth->urg_ptr = 0; |
| 107 | |
| 108 | synproxy_build_options(nth, opts); |
| 109 | |
Liping Zhang | 2942119 | 2016-03-26 16:32:57 +0800 | [diff] [blame] | 110 | synproxy_send_tcp(net, skb, nskb, skb->nfct, IP_CT_ESTABLISHED_REPLY, |
Patrick McHardy | 48b1de4 | 2013-08-27 08:50:14 +0200 | [diff] [blame] | 111 | niph, nth, tcp_hdr_size); |
| 112 | } |
| 113 | |
| 114 | static void |
Liping Zhang | 2942119 | 2016-03-26 16:32:57 +0800 | [diff] [blame] | 115 | synproxy_send_server_syn(struct net *net, |
Patrick McHardy | 48b1de4 | 2013-08-27 08:50:14 +0200 | [diff] [blame] | 116 | const struct sk_buff *skb, const struct tcphdr *th, |
| 117 | const struct synproxy_options *opts, u32 recv_seq) |
| 118 | { |
Liping Zhang | 2942119 | 2016-03-26 16:32:57 +0800 | [diff] [blame] | 119 | struct synproxy_net *snet = synproxy_pernet(net); |
Patrick McHardy | 48b1de4 | 2013-08-27 08:50:14 +0200 | [diff] [blame] | 120 | struct sk_buff *nskb; |
| 121 | struct iphdr *iph, *niph; |
| 122 | struct tcphdr *nth; |
| 123 | unsigned int tcp_hdr_size; |
| 124 | |
| 125 | iph = ip_hdr(skb); |
| 126 | |
| 127 | tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts); |
| 128 | nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER, |
| 129 | GFP_ATOMIC); |
| 130 | if (nskb == NULL) |
| 131 | return; |
| 132 | skb_reserve(nskb, MAX_TCP_HEADER); |
| 133 | |
Liping Zhang | 2942119 | 2016-03-26 16:32:57 +0800 | [diff] [blame] | 134 | niph = synproxy_build_ip(net, nskb, iph->saddr, iph->daddr); |
Patrick McHardy | 48b1de4 | 2013-08-27 08:50:14 +0200 | [diff] [blame] | 135 | |
| 136 | skb_reset_transport_header(nskb); |
| 137 | nth = (struct tcphdr *)skb_put(nskb, tcp_hdr_size); |
| 138 | nth->source = th->source; |
| 139 | nth->dest = th->dest; |
| 140 | nth->seq = htonl(recv_seq - 1); |
| 141 | /* ack_seq is used to relay our ISN to the synproxy hook to initialize |
| 142 | * sequence number translation once a connection tracking entry exists. |
| 143 | */ |
| 144 | nth->ack_seq = htonl(ntohl(th->ack_seq) - 1); |
| 145 | tcp_flag_word(nth) = TCP_FLAG_SYN; |
| 146 | if (opts->options & XT_SYNPROXY_OPT_ECN) |
| 147 | tcp_flag_word(nth) |= TCP_FLAG_ECE | TCP_FLAG_CWR; |
| 148 | nth->doff = tcp_hdr_size / 4; |
| 149 | nth->window = th->window; |
| 150 | nth->check = 0; |
| 151 | nth->urg_ptr = 0; |
| 152 | |
| 153 | synproxy_build_options(nth, opts); |
| 154 | |
Liping Zhang | 2942119 | 2016-03-26 16:32:57 +0800 | [diff] [blame] | 155 | synproxy_send_tcp(net, skb, nskb, &snet->tmpl->ct_general, IP_CT_NEW, |
Patrick McHardy | 48b1de4 | 2013-08-27 08:50:14 +0200 | [diff] [blame] | 156 | niph, nth, tcp_hdr_size); |
| 157 | } |
| 158 | |
| 159 | static void |
Liping Zhang | 2942119 | 2016-03-26 16:32:57 +0800 | [diff] [blame] | 160 | synproxy_send_server_ack(struct net *net, |
Patrick McHardy | 48b1de4 | 2013-08-27 08:50:14 +0200 | [diff] [blame] | 161 | const struct ip_ct_tcp *state, |
| 162 | const struct sk_buff *skb, const struct tcphdr *th, |
| 163 | const struct synproxy_options *opts) |
| 164 | { |
| 165 | struct sk_buff *nskb; |
| 166 | struct iphdr *iph, *niph; |
| 167 | struct tcphdr *nth; |
| 168 | unsigned int tcp_hdr_size; |
| 169 | |
| 170 | iph = ip_hdr(skb); |
| 171 | |
| 172 | tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts); |
| 173 | nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER, |
| 174 | GFP_ATOMIC); |
| 175 | if (nskb == NULL) |
| 176 | return; |
| 177 | skb_reserve(nskb, MAX_TCP_HEADER); |
| 178 | |
Liping Zhang | 2942119 | 2016-03-26 16:32:57 +0800 | [diff] [blame] | 179 | niph = synproxy_build_ip(net, nskb, iph->daddr, iph->saddr); |
Patrick McHardy | 48b1de4 | 2013-08-27 08:50:14 +0200 | [diff] [blame] | 180 | |
| 181 | skb_reset_transport_header(nskb); |
| 182 | nth = (struct tcphdr *)skb_put(nskb, tcp_hdr_size); |
| 183 | nth->source = th->dest; |
| 184 | nth->dest = th->source; |
| 185 | nth->seq = htonl(ntohl(th->ack_seq)); |
| 186 | nth->ack_seq = htonl(ntohl(th->seq) + 1); |
| 187 | tcp_flag_word(nth) = TCP_FLAG_ACK; |
| 188 | nth->doff = tcp_hdr_size / 4; |
| 189 | nth->window = htons(state->seen[IP_CT_DIR_ORIGINAL].td_maxwin); |
| 190 | nth->check = 0; |
| 191 | nth->urg_ptr = 0; |
| 192 | |
| 193 | synproxy_build_options(nth, opts); |
| 194 | |
Liping Zhang | 2942119 | 2016-03-26 16:32:57 +0800 | [diff] [blame] | 195 | synproxy_send_tcp(net, skb, nskb, NULL, 0, niph, nth, tcp_hdr_size); |
Patrick McHardy | 48b1de4 | 2013-08-27 08:50:14 +0200 | [diff] [blame] | 196 | } |
| 197 | |
| 198 | static void |
Liping Zhang | 2942119 | 2016-03-26 16:32:57 +0800 | [diff] [blame] | 199 | synproxy_send_client_ack(struct net *net, |
Patrick McHardy | 48b1de4 | 2013-08-27 08:50:14 +0200 | [diff] [blame] | 200 | const struct sk_buff *skb, const struct tcphdr *th, |
| 201 | const struct synproxy_options *opts) |
| 202 | { |
| 203 | struct sk_buff *nskb; |
| 204 | struct iphdr *iph, *niph; |
| 205 | struct tcphdr *nth; |
| 206 | unsigned int tcp_hdr_size; |
| 207 | |
| 208 | iph = ip_hdr(skb); |
| 209 | |
| 210 | tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts); |
| 211 | nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER, |
| 212 | GFP_ATOMIC); |
| 213 | if (nskb == NULL) |
| 214 | return; |
| 215 | skb_reserve(nskb, MAX_TCP_HEADER); |
| 216 | |
Liping Zhang | 2942119 | 2016-03-26 16:32:57 +0800 | [diff] [blame] | 217 | niph = synproxy_build_ip(net, nskb, iph->saddr, iph->daddr); |
Patrick McHardy | 48b1de4 | 2013-08-27 08:50:14 +0200 | [diff] [blame] | 218 | |
| 219 | skb_reset_transport_header(nskb); |
| 220 | nth = (struct tcphdr *)skb_put(nskb, tcp_hdr_size); |
| 221 | nth->source = th->source; |
| 222 | nth->dest = th->dest; |
| 223 | nth->seq = htonl(ntohl(th->seq) + 1); |
| 224 | nth->ack_seq = th->ack_seq; |
| 225 | tcp_flag_word(nth) = TCP_FLAG_ACK; |
| 226 | nth->doff = tcp_hdr_size / 4; |
Eric Dumazet | ba6d056 | 2015-05-15 09:07:31 -0700 | [diff] [blame] | 227 | nth->window = htons(ntohs(th->window) >> opts->wscale); |
Patrick McHardy | 48b1de4 | 2013-08-27 08:50:14 +0200 | [diff] [blame] | 228 | nth->check = 0; |
| 229 | nth->urg_ptr = 0; |
| 230 | |
| 231 | synproxy_build_options(nth, opts); |
| 232 | |
Liping Zhang | 2942119 | 2016-03-26 16:32:57 +0800 | [diff] [blame] | 233 | synproxy_send_tcp(net, skb, nskb, skb->nfct, IP_CT_ESTABLISHED_REPLY, |
Ian Morris | 24cebe3 | 2015-10-14 23:17:07 +0100 | [diff] [blame] | 234 | niph, nth, tcp_hdr_size); |
Patrick McHardy | 48b1de4 | 2013-08-27 08:50:14 +0200 | [diff] [blame] | 235 | } |
| 236 | |
| 237 | static bool |
Liping Zhang | 2942119 | 2016-03-26 16:32:57 +0800 | [diff] [blame] | 238 | synproxy_recv_client_ack(struct net *net, |
Patrick McHardy | 48b1de4 | 2013-08-27 08:50:14 +0200 | [diff] [blame] | 239 | const struct sk_buff *skb, const struct tcphdr *th, |
| 240 | struct synproxy_options *opts, u32 recv_seq) |
| 241 | { |
Liping Zhang | 2942119 | 2016-03-26 16:32:57 +0800 | [diff] [blame] | 242 | struct synproxy_net *snet = synproxy_pernet(net); |
Patrick McHardy | 48b1de4 | 2013-08-27 08:50:14 +0200 | [diff] [blame] | 243 | int mss; |
| 244 | |
| 245 | mss = __cookie_v4_check(ip_hdr(skb), th, ntohl(th->ack_seq) - 1); |
| 246 | if (mss == 0) { |
| 247 | this_cpu_inc(snet->stats->cookie_invalid); |
| 248 | return false; |
| 249 | } |
| 250 | |
| 251 | this_cpu_inc(snet->stats->cookie_valid); |
| 252 | opts->mss = mss; |
Martin Topholm | a6441b7 | 2013-11-14 15:35:30 +0100 | [diff] [blame] | 253 | opts->options |= XT_SYNPROXY_OPT_MSS; |
Patrick McHardy | 48b1de4 | 2013-08-27 08:50:14 +0200 | [diff] [blame] | 254 | |
| 255 | if (opts->options & XT_SYNPROXY_OPT_TIMESTAMP) |
| 256 | synproxy_check_timestamp_cookie(opts); |
| 257 | |
Liping Zhang | 2942119 | 2016-03-26 16:32:57 +0800 | [diff] [blame] | 258 | synproxy_send_server_syn(net, skb, th, opts, recv_seq); |
Patrick McHardy | 48b1de4 | 2013-08-27 08:50:14 +0200 | [diff] [blame] | 259 | return true; |
| 260 | } |
| 261 | |
| 262 | static unsigned int |
| 263 | synproxy_tg4(struct sk_buff *skb, const struct xt_action_param *par) |
| 264 | { |
| 265 | const struct xt_synproxy_info *info = par->targinfo; |
Liping Zhang | 2942119 | 2016-03-26 16:32:57 +0800 | [diff] [blame] | 266 | struct net *net = par->net; |
| 267 | struct synproxy_net *snet = synproxy_pernet(net); |
Patrick McHardy | 48b1de4 | 2013-08-27 08:50:14 +0200 | [diff] [blame] | 268 | struct synproxy_options opts = {}; |
| 269 | struct tcphdr *th, _th; |
| 270 | |
| 271 | if (nf_ip_checksum(skb, par->hooknum, par->thoff, IPPROTO_TCP)) |
| 272 | return NF_DROP; |
| 273 | |
| 274 | th = skb_header_pointer(skb, par->thoff, sizeof(_th), &_th); |
| 275 | if (th == NULL) |
| 276 | return NF_DROP; |
| 277 | |
Patrick McHardy | f4a87e7 | 2013-09-30 08:51:46 +0100 | [diff] [blame] | 278 | if (!synproxy_parse_options(skb, par->thoff, th, &opts)) |
| 279 | return NF_DROP; |
Patrick McHardy | 48b1de4 | 2013-08-27 08:50:14 +0200 | [diff] [blame] | 280 | |
Jesper Dangaard Brouer | 775ada6 | 2013-08-28 15:14:38 +0200 | [diff] [blame] | 281 | if (th->syn && !(th->ack || th->fin || th->rst)) { |
Patrick McHardy | 48b1de4 | 2013-08-27 08:50:14 +0200 | [diff] [blame] | 282 | /* Initial SYN from client */ |
| 283 | this_cpu_inc(snet->stats->syn_received); |
| 284 | |
| 285 | if (th->ece && th->cwr) |
| 286 | opts.options |= XT_SYNPROXY_OPT_ECN; |
| 287 | |
| 288 | opts.options &= info->options; |
| 289 | if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP) |
| 290 | synproxy_init_timestamp_cookie(info, &opts); |
| 291 | else |
| 292 | opts.options &= ~(XT_SYNPROXY_OPT_WSCALE | |
| 293 | XT_SYNPROXY_OPT_SACK_PERM | |
| 294 | XT_SYNPROXY_OPT_ECN); |
| 295 | |
Liping Zhang | 2942119 | 2016-03-26 16:32:57 +0800 | [diff] [blame] | 296 | synproxy_send_client_synack(net, skb, th, &opts); |
Jesper Dangaard Brouer | 7cc9eb6 | 2013-08-29 12:18:46 +0200 | [diff] [blame] | 297 | return NF_DROP; |
| 298 | |
| 299 | } else if (th->ack && !(th->fin || th->rst || th->syn)) { |
Patrick McHardy | 48b1de4 | 2013-08-27 08:50:14 +0200 | [diff] [blame] | 300 | /* ACK from client */ |
Liping Zhang | 2942119 | 2016-03-26 16:32:57 +0800 | [diff] [blame] | 301 | synproxy_recv_client_ack(net, skb, th, &opts, ntohl(th->seq)); |
Jesper Dangaard Brouer | 7cc9eb6 | 2013-08-29 12:18:46 +0200 | [diff] [blame] | 302 | return NF_DROP; |
| 303 | } |
Patrick McHardy | 48b1de4 | 2013-08-27 08:50:14 +0200 | [diff] [blame] | 304 | |
Jesper Dangaard Brouer | 7cc9eb6 | 2013-08-29 12:18:46 +0200 | [diff] [blame] | 305 | return XT_CONTINUE; |
Patrick McHardy | 48b1de4 | 2013-08-27 08:50:14 +0200 | [diff] [blame] | 306 | } |
| 307 | |
Eric W. Biederman | 06198b3 | 2015-09-18 14:33:06 -0500 | [diff] [blame] | 308 | static unsigned int ipv4_synproxy_hook(void *priv, |
Patrick McHardy | 48b1de4 | 2013-08-27 08:50:14 +0200 | [diff] [blame] | 309 | struct sk_buff *skb, |
David S. Miller | 238e54c | 2015-04-03 20:32:56 -0400 | [diff] [blame] | 310 | const struct nf_hook_state *nhs) |
Patrick McHardy | 48b1de4 | 2013-08-27 08:50:14 +0200 | [diff] [blame] | 311 | { |
Liping Zhang | 2942119 | 2016-03-26 16:32:57 +0800 | [diff] [blame] | 312 | struct net *net = nhs->net; |
| 313 | struct synproxy_net *snet = synproxy_pernet(net); |
Patrick McHardy | 48b1de4 | 2013-08-27 08:50:14 +0200 | [diff] [blame] | 314 | enum ip_conntrack_info ctinfo; |
| 315 | struct nf_conn *ct; |
| 316 | struct nf_conn_synproxy *synproxy; |
| 317 | struct synproxy_options opts = {}; |
| 318 | const struct ip_ct_tcp *state; |
| 319 | struct tcphdr *th, _th; |
| 320 | unsigned int thoff; |
| 321 | |
| 322 | ct = nf_ct_get(skb, &ctinfo); |
| 323 | if (ct == NULL) |
| 324 | return NF_ACCEPT; |
| 325 | |
| 326 | synproxy = nfct_synproxy(ct); |
| 327 | if (synproxy == NULL) |
| 328 | return NF_ACCEPT; |
| 329 | |
| 330 | if (nf_is_loopback_packet(skb)) |
| 331 | return NF_ACCEPT; |
| 332 | |
| 333 | thoff = ip_hdrlen(skb); |
| 334 | th = skb_header_pointer(skb, thoff, sizeof(_th), &_th); |
| 335 | if (th == NULL) |
| 336 | return NF_DROP; |
| 337 | |
| 338 | state = &ct->proto.tcp; |
| 339 | switch (state->state) { |
| 340 | case TCP_CONNTRACK_CLOSE: |
| 341 | if (th->rst && !test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) { |
| 342 | nf_ct_seqadj_init(ct, ctinfo, synproxy->isn - |
| 343 | ntohl(th->seq) + 1); |
| 344 | break; |
| 345 | } |
| 346 | |
| 347 | if (!th->syn || th->ack || |
| 348 | CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL) |
| 349 | break; |
| 350 | |
| 351 | /* Reopened connection - reset the sequence number and timestamp |
| 352 | * adjustments, they will get initialized once the connection is |
| 353 | * reestablished. |
| 354 | */ |
| 355 | nf_ct_seqadj_init(ct, ctinfo, 0); |
| 356 | synproxy->tsoff = 0; |
| 357 | this_cpu_inc(snet->stats->conn_reopened); |
| 358 | |
| 359 | /* fall through */ |
| 360 | case TCP_CONNTRACK_SYN_SENT: |
Patrick McHardy | f4a87e7 | 2013-09-30 08:51:46 +0100 | [diff] [blame] | 361 | if (!synproxy_parse_options(skb, thoff, th, &opts)) |
| 362 | return NF_DROP; |
Patrick McHardy | 48b1de4 | 2013-08-27 08:50:14 +0200 | [diff] [blame] | 363 | |
| 364 | if (!th->syn && th->ack && |
| 365 | CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) { |
| 366 | /* Keep-Alives are sent with SEG.SEQ = SND.NXT-1, |
| 367 | * therefore we need to add 1 to make the SYN sequence |
| 368 | * number match the one of first SYN. |
| 369 | */ |
Liping Zhang | 2942119 | 2016-03-26 16:32:57 +0800 | [diff] [blame] | 370 | if (synproxy_recv_client_ack(net, skb, th, &opts, |
Patrick McHardy | 48b1de4 | 2013-08-27 08:50:14 +0200 | [diff] [blame] | 371 | ntohl(th->seq) + 1)) |
| 372 | this_cpu_inc(snet->stats->cookie_retrans); |
| 373 | |
| 374 | return NF_DROP; |
| 375 | } |
| 376 | |
| 377 | synproxy->isn = ntohl(th->ack_seq); |
| 378 | if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP) |
| 379 | synproxy->its = opts.tsecr; |
| 380 | break; |
| 381 | case TCP_CONNTRACK_SYN_RECV: |
| 382 | if (!th->syn || !th->ack) |
| 383 | break; |
| 384 | |
Patrick McHardy | f4a87e7 | 2013-09-30 08:51:46 +0100 | [diff] [blame] | 385 | if (!synproxy_parse_options(skb, thoff, th, &opts)) |
| 386 | return NF_DROP; |
| 387 | |
Patrick McHardy | 48b1de4 | 2013-08-27 08:50:14 +0200 | [diff] [blame] | 388 | if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP) |
| 389 | synproxy->tsoff = opts.tsval - synproxy->its; |
| 390 | |
| 391 | opts.options &= ~(XT_SYNPROXY_OPT_MSS | |
| 392 | XT_SYNPROXY_OPT_WSCALE | |
| 393 | XT_SYNPROXY_OPT_SACK_PERM); |
| 394 | |
| 395 | swap(opts.tsval, opts.tsecr); |
Liping Zhang | 2942119 | 2016-03-26 16:32:57 +0800 | [diff] [blame] | 396 | synproxy_send_server_ack(net, state, skb, th, &opts); |
Patrick McHardy | 48b1de4 | 2013-08-27 08:50:14 +0200 | [diff] [blame] | 397 | |
| 398 | nf_ct_seqadj_init(ct, ctinfo, synproxy->isn - ntohl(th->seq)); |
| 399 | |
| 400 | swap(opts.tsval, opts.tsecr); |
Liping Zhang | 2942119 | 2016-03-26 16:32:57 +0800 | [diff] [blame] | 401 | synproxy_send_client_ack(net, skb, th, &opts); |
Patrick McHardy | 48b1de4 | 2013-08-27 08:50:14 +0200 | [diff] [blame] | 402 | |
| 403 | consume_skb(skb); |
| 404 | return NF_STOLEN; |
| 405 | default: |
| 406 | break; |
| 407 | } |
| 408 | |
| 409 | synproxy_tstamp_adjust(skb, thoff, th, ct, ctinfo, synproxy); |
| 410 | return NF_ACCEPT; |
| 411 | } |
| 412 | |
| 413 | static int synproxy_tg4_check(const struct xt_tgchk_param *par) |
| 414 | { |
| 415 | const struct ipt_entry *e = par->entryinfo; |
| 416 | |
| 417 | if (e->ip.proto != IPPROTO_TCP || |
| 418 | e->ip.invflags & XT_INV_PROTO) |
| 419 | return -EINVAL; |
| 420 | |
| 421 | return nf_ct_l3proto_try_module_get(par->family); |
| 422 | } |
| 423 | |
| 424 | static void synproxy_tg4_destroy(const struct xt_tgdtor_param *par) |
| 425 | { |
| 426 | nf_ct_l3proto_module_put(par->family); |
| 427 | } |
| 428 | |
| 429 | static struct xt_target synproxy_tg4_reg __read_mostly = { |
| 430 | .name = "SYNPROXY", |
| 431 | .family = NFPROTO_IPV4, |
Patrick McHardy | f01b392 | 2013-12-08 16:52:31 +0000 | [diff] [blame] | 432 | .hooks = (1 << NF_INET_LOCAL_IN) | (1 << NF_INET_FORWARD), |
Patrick McHardy | 48b1de4 | 2013-08-27 08:50:14 +0200 | [diff] [blame] | 433 | .target = synproxy_tg4, |
| 434 | .targetsize = sizeof(struct xt_synproxy_info), |
| 435 | .checkentry = synproxy_tg4_check, |
| 436 | .destroy = synproxy_tg4_destroy, |
| 437 | .me = THIS_MODULE, |
| 438 | }; |
| 439 | |
| 440 | static struct nf_hook_ops ipv4_synproxy_ops[] __read_mostly = { |
| 441 | { |
| 442 | .hook = ipv4_synproxy_hook, |
Patrick McHardy | 48b1de4 | 2013-08-27 08:50:14 +0200 | [diff] [blame] | 443 | .pf = NFPROTO_IPV4, |
| 444 | .hooknum = NF_INET_LOCAL_IN, |
| 445 | .priority = NF_IP_PRI_CONNTRACK_CONFIRM - 1, |
| 446 | }, |
| 447 | { |
| 448 | .hook = ipv4_synproxy_hook, |
Patrick McHardy | 48b1de4 | 2013-08-27 08:50:14 +0200 | [diff] [blame] | 449 | .pf = NFPROTO_IPV4, |
| 450 | .hooknum = NF_INET_POST_ROUTING, |
| 451 | .priority = NF_IP_PRI_CONNTRACK_CONFIRM - 1, |
| 452 | }, |
| 453 | }; |
| 454 | |
| 455 | static int __init synproxy_tg4_init(void) |
| 456 | { |
| 457 | int err; |
| 458 | |
| 459 | err = nf_register_hooks(ipv4_synproxy_ops, |
| 460 | ARRAY_SIZE(ipv4_synproxy_ops)); |
| 461 | if (err < 0) |
| 462 | goto err1; |
| 463 | |
| 464 | err = xt_register_target(&synproxy_tg4_reg); |
| 465 | if (err < 0) |
| 466 | goto err2; |
| 467 | |
| 468 | return 0; |
| 469 | |
| 470 | err2: |
| 471 | nf_unregister_hooks(ipv4_synproxy_ops, ARRAY_SIZE(ipv4_synproxy_ops)); |
| 472 | err1: |
| 473 | return err; |
| 474 | } |
| 475 | |
| 476 | static void __exit synproxy_tg4_exit(void) |
| 477 | { |
| 478 | xt_unregister_target(&synproxy_tg4_reg); |
| 479 | nf_unregister_hooks(ipv4_synproxy_ops, ARRAY_SIZE(ipv4_synproxy_ops)); |
| 480 | } |
| 481 | |
| 482 | module_init(synproxy_tg4_init); |
| 483 | module_exit(synproxy_tg4_exit); |
| 484 | |
| 485 | MODULE_LICENSE("GPL"); |
| 486 | MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); |