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