diff options
Diffstat (limited to 'net')
| -rw-r--r-- | net/ipv4/netfilter/ipt_SYNPROXY.c | 394 | ||||
| -rw-r--r-- | net/ipv6/netfilter/ip6t_SYNPROXY.c | 420 | ||||
| -rw-r--r-- | net/netfilter/nf_synproxy_core.c | 896 |
3 files changed, 874 insertions, 836 deletions
diff --git a/net/ipv4/netfilter/ipt_SYNPROXY.c b/net/ipv4/netfilter/ipt_SYNPROXY.c index 690b17ef6a44..7f7979734fb4 100644 --- a/net/ipv4/netfilter/ipt_SYNPROXY.c +++ b/net/ipv4/netfilter/ipt_SYNPROXY.c | |||
| @@ -6,258 +6,11 @@ | |||
| 6 | * published by the Free Software Foundation. | 6 | * published by the Free Software Foundation. |
| 7 | */ | 7 | */ |
| 8 | 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> | 9 | #include <linux/netfilter_ipv4/ip_tables.h> |
| 14 | #include <linux/netfilter/x_tables.h> | 10 | #include <linux/netfilter/x_tables.h> |
| 15 | #include <linux/netfilter/xt_SYNPROXY.h> | 11 | #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 | #include <net/netfilter/nf_conntrack_ecache.h> | ||
| 20 | |||
| 21 | static struct iphdr * | ||
| 22 | synproxy_build_ip(struct net *net, struct sk_buff *skb, __be32 saddr, | ||
| 23 | __be32 daddr) | ||
| 24 | { | ||
| 25 | struct iphdr *iph; | ||
| 26 | |||
| 27 | skb_reset_network_header(skb); | ||
| 28 | iph = skb_put(skb, sizeof(*iph)); | ||
| 29 | iph->version = 4; | ||
| 30 | iph->ihl = sizeof(*iph) / 4; | ||
| 31 | iph->tos = 0; | ||
| 32 | iph->id = 0; | ||
| 33 | iph->frag_off = htons(IP_DF); | ||
| 34 | iph->ttl = net->ipv4.sysctl_ip_default_ttl; | ||
| 35 | iph->protocol = IPPROTO_TCP; | ||
| 36 | iph->check = 0; | ||
| 37 | iph->saddr = saddr; | ||
| 38 | iph->daddr = daddr; | ||
| 39 | |||
| 40 | return iph; | ||
| 41 | } | ||
| 42 | |||
| 43 | static void | ||
| 44 | synproxy_send_tcp(struct net *net, | ||
| 45 | const struct sk_buff *skb, struct sk_buff *nskb, | ||
| 46 | struct nf_conntrack *nfct, enum ip_conntrack_info ctinfo, | ||
| 47 | struct iphdr *niph, struct tcphdr *nth, | ||
| 48 | unsigned int tcp_hdr_size) | ||
| 49 | { | ||
| 50 | nth->check = ~tcp_v4_check(tcp_hdr_size, niph->saddr, niph->daddr, 0); | ||
| 51 | nskb->ip_summed = CHECKSUM_PARTIAL; | ||
| 52 | nskb->csum_start = (unsigned char *)nth - nskb->head; | ||
| 53 | nskb->csum_offset = offsetof(struct tcphdr, check); | ||
| 54 | |||
| 55 | skb_dst_set_noref(nskb, skb_dst(skb)); | ||
| 56 | nskb->protocol = htons(ETH_P_IP); | ||
| 57 | if (ip_route_me_harder(net, nskb, RTN_UNSPEC)) | ||
| 58 | goto free_nskb; | ||
| 59 | |||
| 60 | if (nfct) { | ||
| 61 | nf_ct_set(nskb, (struct nf_conn *)nfct, ctinfo); | ||
| 62 | nf_conntrack_get(nfct); | ||
| 63 | } | ||
| 64 | |||
| 65 | ip_local_out(net, nskb->sk, nskb); | ||
| 66 | return; | ||
| 67 | |||
| 68 | free_nskb: | ||
| 69 | kfree_skb(nskb); | ||
| 70 | } | ||
| 71 | |||
| 72 | static void | ||
| 73 | synproxy_send_client_synack(struct net *net, | ||
| 74 | const struct sk_buff *skb, const struct tcphdr *th, | ||
| 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 | |||
| 92 | niph = synproxy_build_ip(net, nskb, iph->daddr, iph->saddr); | ||
| 93 | |||
| 94 | skb_reset_transport_header(nskb); | ||
| 95 | nth = 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 | |||
| 110 | synproxy_send_tcp(net, skb, nskb, skb_nfct(skb), | ||
| 111 | IP_CT_ESTABLISHED_REPLY, niph, nth, tcp_hdr_size); | ||
| 112 | } | ||
| 113 | |||
| 114 | static void | ||
| 115 | synproxy_send_server_syn(struct net *net, | ||
| 116 | const struct sk_buff *skb, const struct tcphdr *th, | ||
| 117 | const struct synproxy_options *opts, u32 recv_seq) | ||
| 118 | { | ||
| 119 | struct synproxy_net *snet = synproxy_pernet(net); | ||
| 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 | |||
| 134 | niph = synproxy_build_ip(net, nskb, iph->saddr, iph->daddr); | ||
| 135 | |||
| 136 | skb_reset_transport_header(nskb); | ||
| 137 | nth = 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 | |||
| 155 | synproxy_send_tcp(net, skb, nskb, &snet->tmpl->ct_general, IP_CT_NEW, | ||
| 156 | niph, nth, tcp_hdr_size); | ||
| 157 | } | ||
| 158 | |||
| 159 | static void | ||
| 160 | synproxy_send_server_ack(struct net *net, | ||
| 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 | |||
| 179 | niph = synproxy_build_ip(net, nskb, iph->daddr, iph->saddr); | ||
| 180 | 12 | ||
| 181 | skb_reset_transport_header(nskb); | 13 | #include <net/netfilter/nf_synproxy.h> |
| 182 | nth = 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 | |||
| 195 | synproxy_send_tcp(net, skb, nskb, NULL, 0, niph, nth, tcp_hdr_size); | ||
| 196 | } | ||
| 197 | |||
| 198 | static void | ||
| 199 | synproxy_send_client_ack(struct net *net, | ||
| 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 | |||
| 217 | niph = synproxy_build_ip(net, nskb, iph->saddr, iph->daddr); | ||
| 218 | |||
| 219 | skb_reset_transport_header(nskb); | ||
| 220 | nth = 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; | ||
| 227 | nth->window = htons(ntohs(th->window) >> opts->wscale); | ||
| 228 | nth->check = 0; | ||
| 229 | nth->urg_ptr = 0; | ||
| 230 | |||
| 231 | synproxy_build_options(nth, opts); | ||
| 232 | |||
| 233 | synproxy_send_tcp(net, skb, nskb, skb_nfct(skb), | ||
| 234 | IP_CT_ESTABLISHED_REPLY, niph, nth, tcp_hdr_size); | ||
| 235 | } | ||
| 236 | |||
| 237 | static bool | ||
| 238 | synproxy_recv_client_ack(struct net *net, | ||
| 239 | const struct sk_buff *skb, const struct tcphdr *th, | ||
| 240 | struct synproxy_options *opts, u32 recv_seq) | ||
| 241 | { | ||
| 242 | struct synproxy_net *snet = synproxy_pernet(net); | ||
| 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; | ||
| 253 | opts->options |= XT_SYNPROXY_OPT_MSS; | ||
| 254 | |||
| 255 | if (opts->options & XT_SYNPROXY_OPT_TIMESTAMP) | ||
| 256 | synproxy_check_timestamp_cookie(opts); | ||
| 257 | |||
| 258 | synproxy_send_server_syn(net, skb, th, opts, recv_seq); | ||
| 259 | return true; | ||
| 260 | } | ||
| 261 | 14 | ||
| 262 | static unsigned int | 15 | static unsigned int |
| 263 | synproxy_tg4(struct sk_buff *skb, const struct xt_action_param *par) | 16 | synproxy_tg4(struct sk_buff *skb, const struct xt_action_param *par) |
| @@ -309,135 +62,6 @@ synproxy_tg4(struct sk_buff *skb, const struct xt_action_param *par) | |||
| 309 | return XT_CONTINUE; | 62 | return XT_CONTINUE; |
| 310 | } | 63 | } |
| 311 | 64 | ||
| 312 | static unsigned int ipv4_synproxy_hook(void *priv, | ||
| 313 | struct sk_buff *skb, | ||
| 314 | const struct nf_hook_state *nhs) | ||
| 315 | { | ||
| 316 | struct net *net = nhs->net; | ||
| 317 | struct synproxy_net *snet = synproxy_pernet(net); | ||
| 318 | enum ip_conntrack_info ctinfo; | ||
| 319 | struct nf_conn *ct; | ||
| 320 | struct nf_conn_synproxy *synproxy; | ||
| 321 | struct synproxy_options opts = {}; | ||
| 322 | const struct ip_ct_tcp *state; | ||
| 323 | struct tcphdr *th, _th; | ||
| 324 | unsigned int thoff; | ||
| 325 | |||
| 326 | ct = nf_ct_get(skb, &ctinfo); | ||
| 327 | if (ct == NULL) | ||
| 328 | return NF_ACCEPT; | ||
| 329 | |||
| 330 | synproxy = nfct_synproxy(ct); | ||
| 331 | if (synproxy == NULL) | ||
| 332 | return NF_ACCEPT; | ||
| 333 | |||
| 334 | if (nf_is_loopback_packet(skb) || | ||
| 335 | ip_hdr(skb)->protocol != IPPROTO_TCP) | ||
| 336 | return NF_ACCEPT; | ||
| 337 | |||
| 338 | thoff = ip_hdrlen(skb); | ||
| 339 | th = skb_header_pointer(skb, thoff, sizeof(_th), &_th); | ||
| 340 | if (th == NULL) | ||
| 341 | return NF_DROP; | ||
| 342 | |||
| 343 | state = &ct->proto.tcp; | ||
| 344 | switch (state->state) { | ||
| 345 | case TCP_CONNTRACK_CLOSE: | ||
| 346 | if (th->rst && !test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) { | ||
| 347 | nf_ct_seqadj_init(ct, ctinfo, synproxy->isn - | ||
| 348 | ntohl(th->seq) + 1); | ||
| 349 | break; | ||
| 350 | } | ||
| 351 | |||
| 352 | if (!th->syn || th->ack || | ||
| 353 | CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL) | ||
| 354 | break; | ||
| 355 | |||
| 356 | /* Reopened connection - reset the sequence number and timestamp | ||
| 357 | * adjustments, they will get initialized once the connection is | ||
| 358 | * reestablished. | ||
| 359 | */ | ||
| 360 | nf_ct_seqadj_init(ct, ctinfo, 0); | ||
| 361 | synproxy->tsoff = 0; | ||
| 362 | this_cpu_inc(snet->stats->conn_reopened); | ||
| 363 | |||
| 364 | /* fall through */ | ||
| 365 | case TCP_CONNTRACK_SYN_SENT: | ||
| 366 | if (!synproxy_parse_options(skb, thoff, th, &opts)) | ||
| 367 | return NF_DROP; | ||
| 368 | |||
| 369 | if (!th->syn && th->ack && | ||
| 370 | CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) { | ||
| 371 | /* Keep-Alives are sent with SEG.SEQ = SND.NXT-1, | ||
| 372 | * therefore we need to add 1 to make the SYN sequence | ||
| 373 | * number match the one of first SYN. | ||
| 374 | */ | ||
| 375 | if (synproxy_recv_client_ack(net, skb, th, &opts, | ||
| 376 | ntohl(th->seq) + 1)) { | ||
| 377 | this_cpu_inc(snet->stats->cookie_retrans); | ||
| 378 | consume_skb(skb); | ||
| 379 | return NF_STOLEN; | ||
| 380 | } else { | ||
| 381 | return NF_DROP; | ||
| 382 | } | ||
| 383 | } | ||
| 384 | |||
| 385 | synproxy->isn = ntohl(th->ack_seq); | ||
| 386 | if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP) | ||
| 387 | synproxy->its = opts.tsecr; | ||
| 388 | |||
| 389 | nf_conntrack_event_cache(IPCT_SYNPROXY, ct); | ||
| 390 | break; | ||
| 391 | case TCP_CONNTRACK_SYN_RECV: | ||
| 392 | if (!th->syn || !th->ack) | ||
| 393 | break; | ||
| 394 | |||
| 395 | if (!synproxy_parse_options(skb, thoff, th, &opts)) | ||
| 396 | return NF_DROP; | ||
| 397 | |||
| 398 | if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP) { | ||
| 399 | synproxy->tsoff = opts.tsval - synproxy->its; | ||
| 400 | nf_conntrack_event_cache(IPCT_SYNPROXY, ct); | ||
| 401 | } | ||
| 402 | |||
| 403 | opts.options &= ~(XT_SYNPROXY_OPT_MSS | | ||
| 404 | XT_SYNPROXY_OPT_WSCALE | | ||
| 405 | XT_SYNPROXY_OPT_SACK_PERM); | ||
| 406 | |||
| 407 | swap(opts.tsval, opts.tsecr); | ||
| 408 | synproxy_send_server_ack(net, state, skb, th, &opts); | ||
| 409 | |||
| 410 | nf_ct_seqadj_init(ct, ctinfo, synproxy->isn - ntohl(th->seq)); | ||
| 411 | nf_conntrack_event_cache(IPCT_SEQADJ, ct); | ||
| 412 | |||
| 413 | swap(opts.tsval, opts.tsecr); | ||
| 414 | synproxy_send_client_ack(net, skb, th, &opts); | ||
| 415 | |||
| 416 | consume_skb(skb); | ||
| 417 | return NF_STOLEN; | ||
| 418 | default: | ||
| 419 | break; | ||
| 420 | } | ||
| 421 | |||
| 422 | synproxy_tstamp_adjust(skb, thoff, th, ct, ctinfo, synproxy); | ||
| 423 | return NF_ACCEPT; | ||
| 424 | } | ||
| 425 | |||
| 426 | static const struct nf_hook_ops ipv4_synproxy_ops[] = { | ||
| 427 | { | ||
| 428 | .hook = ipv4_synproxy_hook, | ||
| 429 | .pf = NFPROTO_IPV4, | ||
| 430 | .hooknum = NF_INET_LOCAL_IN, | ||
| 431 | .priority = NF_IP_PRI_CONNTRACK_CONFIRM - 1, | ||
| 432 | }, | ||
| 433 | { | ||
| 434 | .hook = ipv4_synproxy_hook, | ||
| 435 | .pf = NFPROTO_IPV4, | ||
| 436 | .hooknum = NF_INET_POST_ROUTING, | ||
| 437 | .priority = NF_IP_PRI_CONNTRACK_CONFIRM - 1, | ||
| 438 | }, | ||
| 439 | }; | ||
| 440 | |||
| 441 | static int synproxy_tg4_check(const struct xt_tgchk_param *par) | 65 | static int synproxy_tg4_check(const struct xt_tgchk_param *par) |
| 442 | { | 66 | { |
| 443 | struct synproxy_net *snet = synproxy_pernet(par->net); | 67 | struct synproxy_net *snet = synproxy_pernet(par->net); |
| @@ -452,13 +76,10 @@ static int synproxy_tg4_check(const struct xt_tgchk_param *par) | |||
| 452 | if (err) | 76 | if (err) |
| 453 | return err; | 77 | return err; |
| 454 | 78 | ||
| 455 | if (snet->hook_ref4 == 0) { | 79 | err = nf_synproxy_ipv4_init(snet, par->net); |
| 456 | err = nf_register_net_hooks(par->net, ipv4_synproxy_ops, | 80 | if (err) { |
| 457 | ARRAY_SIZE(ipv4_synproxy_ops)); | 81 | nf_ct_netns_put(par->net, par->family); |
| 458 | if (err) { | 82 | return err; |
| 459 | nf_ct_netns_put(par->net, par->family); | ||
| 460 | return err; | ||
| 461 | } | ||
| 462 | } | 83 | } |
| 463 | 84 | ||
| 464 | snet->hook_ref4++; | 85 | snet->hook_ref4++; |
| @@ -469,10 +90,7 @@ static void synproxy_tg4_destroy(const struct xt_tgdtor_param *par) | |||
| 469 | { | 90 | { |
| 470 | struct synproxy_net *snet = synproxy_pernet(par->net); | 91 | struct synproxy_net *snet = synproxy_pernet(par->net); |
| 471 | 92 | ||
| 472 | snet->hook_ref4--; | 93 | nf_synproxy_ipv4_fini(snet, par->net); |
| 473 | if (snet->hook_ref4 == 0) | ||
| 474 | nf_unregister_net_hooks(par->net, ipv4_synproxy_ops, | ||
| 475 | ARRAY_SIZE(ipv4_synproxy_ops)); | ||
| 476 | nf_ct_netns_put(par->net, par->family); | 94 | nf_ct_netns_put(par->net, par->family); |
| 477 | } | 95 | } |
| 478 | 96 | ||
diff --git a/net/ipv6/netfilter/ip6t_SYNPROXY.c b/net/ipv6/netfilter/ip6t_SYNPROXY.c index cb6d42b03cb5..55a9b92d0a1f 100644 --- a/net/ipv6/netfilter/ip6t_SYNPROXY.c +++ b/net/ipv6/netfilter/ip6t_SYNPROXY.c | |||
| @@ -6,272 +6,11 @@ | |||
| 6 | * published by the Free Software Foundation. | 6 | * published by the Free Software Foundation. |
| 7 | */ | 7 | */ |
| 8 | 8 | ||
| 9 | #include <linux/module.h> | ||
| 10 | #include <linux/skbuff.h> | ||
| 11 | #include <net/ip6_checksum.h> | ||
| 12 | #include <net/ip6_route.h> | ||
| 13 | #include <net/tcp.h> | ||
| 14 | |||
| 15 | #include <linux/netfilter_ipv6/ip6_tables.h> | 9 | #include <linux/netfilter_ipv6/ip6_tables.h> |
| 16 | #include <linux/netfilter/x_tables.h> | 10 | #include <linux/netfilter/x_tables.h> |
| 17 | #include <linux/netfilter/xt_SYNPROXY.h> | 11 | #include <linux/netfilter/xt_SYNPROXY.h> |
| 18 | #include <net/netfilter/nf_conntrack.h> | ||
| 19 | #include <net/netfilter/nf_conntrack_seqadj.h> | ||
| 20 | #include <net/netfilter/nf_conntrack_synproxy.h> | ||
| 21 | #include <net/netfilter/nf_conntrack_ecache.h> | ||
| 22 | |||
| 23 | static struct ipv6hdr * | ||
| 24 | synproxy_build_ip(struct net *net, struct sk_buff *skb, | ||
| 25 | const struct in6_addr *saddr, | ||
| 26 | const struct in6_addr *daddr) | ||
| 27 | { | ||
| 28 | struct ipv6hdr *iph; | ||
| 29 | |||
| 30 | skb_reset_network_header(skb); | ||
| 31 | iph = skb_put(skb, sizeof(*iph)); | ||
| 32 | ip6_flow_hdr(iph, 0, 0); | ||
| 33 | iph->hop_limit = net->ipv6.devconf_all->hop_limit; | ||
| 34 | iph->nexthdr = IPPROTO_TCP; | ||
| 35 | iph->saddr = *saddr; | ||
| 36 | iph->daddr = *daddr; | ||
| 37 | |||
| 38 | return iph; | ||
| 39 | } | ||
| 40 | |||
| 41 | static void | ||
| 42 | synproxy_send_tcp(struct net *net, | ||
| 43 | const struct sk_buff *skb, struct sk_buff *nskb, | ||
| 44 | struct nf_conntrack *nfct, enum ip_conntrack_info ctinfo, | ||
| 45 | struct ipv6hdr *niph, struct tcphdr *nth, | ||
| 46 | unsigned int tcp_hdr_size) | ||
| 47 | { | ||
| 48 | struct dst_entry *dst; | ||
| 49 | struct flowi6 fl6; | ||
| 50 | |||
| 51 | nth->check = ~tcp_v6_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 | memset(&fl6, 0, sizeof(fl6)); | ||
| 57 | fl6.flowi6_proto = IPPROTO_TCP; | ||
| 58 | fl6.saddr = niph->saddr; | ||
| 59 | fl6.daddr = niph->daddr; | ||
| 60 | fl6.fl6_sport = nth->source; | ||
| 61 | fl6.fl6_dport = nth->dest; | ||
| 62 | security_skb_classify_flow((struct sk_buff *)skb, flowi6_to_flowi(&fl6)); | ||
| 63 | dst = ip6_route_output(net, NULL, &fl6); | ||
| 64 | if (dst->error) { | ||
| 65 | dst_release(dst); | ||
| 66 | goto free_nskb; | ||
| 67 | } | ||
| 68 | dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0); | ||
| 69 | if (IS_ERR(dst)) | ||
| 70 | goto free_nskb; | ||
| 71 | |||
| 72 | skb_dst_set(nskb, dst); | ||
| 73 | |||
| 74 | if (nfct) { | ||
| 75 | nf_ct_set(nskb, (struct nf_conn *)nfct, ctinfo); | ||
| 76 | nf_conntrack_get(nfct); | ||
| 77 | } | ||
| 78 | |||
| 79 | ip6_local_out(net, nskb->sk, nskb); | ||
| 80 | return; | ||
| 81 | |||
| 82 | free_nskb: | ||
| 83 | kfree_skb(nskb); | ||
| 84 | } | ||
| 85 | |||
| 86 | static void | ||
| 87 | synproxy_send_client_synack(struct net *net, | ||
| 88 | const struct sk_buff *skb, const struct tcphdr *th, | ||
| 89 | const struct synproxy_options *opts) | ||
| 90 | { | ||
| 91 | struct sk_buff *nskb; | ||
| 92 | struct ipv6hdr *iph, *niph; | ||
| 93 | struct tcphdr *nth; | ||
| 94 | unsigned int tcp_hdr_size; | ||
| 95 | u16 mss = opts->mss; | ||
| 96 | |||
| 97 | iph = ipv6_hdr(skb); | ||
| 98 | |||
| 99 | tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts); | ||
| 100 | nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER, | ||
| 101 | GFP_ATOMIC); | ||
| 102 | if (nskb == NULL) | ||
| 103 | return; | ||
| 104 | skb_reserve(nskb, MAX_TCP_HEADER); | ||
| 105 | |||
| 106 | niph = synproxy_build_ip(net, nskb, &iph->daddr, &iph->saddr); | ||
| 107 | |||
| 108 | skb_reset_transport_header(nskb); | ||
| 109 | nth = skb_put(nskb, tcp_hdr_size); | ||
| 110 | nth->source = th->dest; | ||
| 111 | nth->dest = th->source; | ||
| 112 | nth->seq = htonl(__cookie_v6_init_sequence(iph, th, &mss)); | ||
| 113 | nth->ack_seq = htonl(ntohl(th->seq) + 1); | ||
| 114 | tcp_flag_word(nth) = TCP_FLAG_SYN | TCP_FLAG_ACK; | ||
| 115 | if (opts->options & XT_SYNPROXY_OPT_ECN) | ||
| 116 | tcp_flag_word(nth) |= TCP_FLAG_ECE; | ||
| 117 | nth->doff = tcp_hdr_size / 4; | ||
| 118 | nth->window = 0; | ||
| 119 | nth->check = 0; | ||
| 120 | nth->urg_ptr = 0; | ||
| 121 | |||
| 122 | synproxy_build_options(nth, opts); | ||
| 123 | |||
| 124 | synproxy_send_tcp(net, skb, nskb, skb_nfct(skb), | ||
| 125 | IP_CT_ESTABLISHED_REPLY, niph, nth, tcp_hdr_size); | ||
| 126 | } | ||
| 127 | 12 | ||
| 128 | static void | 13 | #include <net/netfilter/nf_synproxy.h> |
| 129 | synproxy_send_server_syn(struct net *net, | ||
| 130 | const struct sk_buff *skb, const struct tcphdr *th, | ||
| 131 | const struct synproxy_options *opts, u32 recv_seq) | ||
| 132 | { | ||
| 133 | struct synproxy_net *snet = synproxy_pernet(net); | ||
| 134 | struct sk_buff *nskb; | ||
| 135 | struct ipv6hdr *iph, *niph; | ||
| 136 | struct tcphdr *nth; | ||
| 137 | unsigned int tcp_hdr_size; | ||
| 138 | |||
| 139 | iph = ipv6_hdr(skb); | ||
| 140 | |||
| 141 | tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts); | ||
| 142 | nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER, | ||
| 143 | GFP_ATOMIC); | ||
| 144 | if (nskb == NULL) | ||
| 145 | return; | ||
| 146 | skb_reserve(nskb, MAX_TCP_HEADER); | ||
| 147 | |||
| 148 | niph = synproxy_build_ip(net, nskb, &iph->saddr, &iph->daddr); | ||
| 149 | |||
| 150 | skb_reset_transport_header(nskb); | ||
| 151 | nth = skb_put(nskb, tcp_hdr_size); | ||
| 152 | nth->source = th->source; | ||
| 153 | nth->dest = th->dest; | ||
| 154 | nth->seq = htonl(recv_seq - 1); | ||
| 155 | /* ack_seq is used to relay our ISN to the synproxy hook to initialize | ||
| 156 | * sequence number translation once a connection tracking entry exists. | ||
| 157 | */ | ||
| 158 | nth->ack_seq = htonl(ntohl(th->ack_seq) - 1); | ||
| 159 | tcp_flag_word(nth) = TCP_FLAG_SYN; | ||
| 160 | if (opts->options & XT_SYNPROXY_OPT_ECN) | ||
| 161 | tcp_flag_word(nth) |= TCP_FLAG_ECE | TCP_FLAG_CWR; | ||
| 162 | nth->doff = tcp_hdr_size / 4; | ||
| 163 | nth->window = th->window; | ||
| 164 | nth->check = 0; | ||
| 165 | nth->urg_ptr = 0; | ||
| 166 | |||
| 167 | synproxy_build_options(nth, opts); | ||
| 168 | |||
| 169 | synproxy_send_tcp(net, skb, nskb, &snet->tmpl->ct_general, IP_CT_NEW, | ||
| 170 | niph, nth, tcp_hdr_size); | ||
| 171 | } | ||
| 172 | |||
| 173 | static void | ||
| 174 | synproxy_send_server_ack(struct net *net, | ||
| 175 | const struct ip_ct_tcp *state, | ||
| 176 | const struct sk_buff *skb, const struct tcphdr *th, | ||
| 177 | const struct synproxy_options *opts) | ||
| 178 | { | ||
| 179 | struct sk_buff *nskb; | ||
| 180 | struct ipv6hdr *iph, *niph; | ||
| 181 | struct tcphdr *nth; | ||
| 182 | unsigned int tcp_hdr_size; | ||
| 183 | |||
| 184 | iph = ipv6_hdr(skb); | ||
| 185 | |||
| 186 | tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts); | ||
| 187 | nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER, | ||
| 188 | GFP_ATOMIC); | ||
| 189 | if (nskb == NULL) | ||
| 190 | return; | ||
| 191 | skb_reserve(nskb, MAX_TCP_HEADER); | ||
| 192 | |||
| 193 | niph = synproxy_build_ip(net, nskb, &iph->daddr, &iph->saddr); | ||
| 194 | |||
| 195 | skb_reset_transport_header(nskb); | ||
| 196 | nth = skb_put(nskb, tcp_hdr_size); | ||
| 197 | nth->source = th->dest; | ||
| 198 | nth->dest = th->source; | ||
| 199 | nth->seq = htonl(ntohl(th->ack_seq)); | ||
| 200 | nth->ack_seq = htonl(ntohl(th->seq) + 1); | ||
| 201 | tcp_flag_word(nth) = TCP_FLAG_ACK; | ||
| 202 | nth->doff = tcp_hdr_size / 4; | ||
| 203 | nth->window = htons(state->seen[IP_CT_DIR_ORIGINAL].td_maxwin); | ||
| 204 | nth->check = 0; | ||
| 205 | nth->urg_ptr = 0; | ||
| 206 | |||
| 207 | synproxy_build_options(nth, opts); | ||
| 208 | |||
| 209 | synproxy_send_tcp(net, skb, nskb, NULL, 0, niph, nth, tcp_hdr_size); | ||
| 210 | } | ||
| 211 | |||
| 212 | static void | ||
| 213 | synproxy_send_client_ack(struct net *net, | ||
| 214 | const struct sk_buff *skb, const struct tcphdr *th, | ||
| 215 | const struct synproxy_options *opts) | ||
| 216 | { | ||
| 217 | struct sk_buff *nskb; | ||
| 218 | struct ipv6hdr *iph, *niph; | ||
| 219 | struct tcphdr *nth; | ||
| 220 | unsigned int tcp_hdr_size; | ||
| 221 | |||
| 222 | iph = ipv6_hdr(skb); | ||
| 223 | |||
| 224 | tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts); | ||
| 225 | nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER, | ||
| 226 | GFP_ATOMIC); | ||
| 227 | if (nskb == NULL) | ||
| 228 | return; | ||
| 229 | skb_reserve(nskb, MAX_TCP_HEADER); | ||
| 230 | |||
| 231 | niph = synproxy_build_ip(net, nskb, &iph->saddr, &iph->daddr); | ||
| 232 | |||
| 233 | skb_reset_transport_header(nskb); | ||
| 234 | nth = skb_put(nskb, tcp_hdr_size); | ||
| 235 | nth->source = th->source; | ||
| 236 | nth->dest = th->dest; | ||
| 237 | nth->seq = htonl(ntohl(th->seq) + 1); | ||
| 238 | nth->ack_seq = th->ack_seq; | ||
| 239 | tcp_flag_word(nth) = TCP_FLAG_ACK; | ||
| 240 | nth->doff = tcp_hdr_size / 4; | ||
| 241 | nth->window = htons(ntohs(th->window) >> opts->wscale); | ||
| 242 | nth->check = 0; | ||
| 243 | nth->urg_ptr = 0; | ||
| 244 | |||
| 245 | synproxy_build_options(nth, opts); | ||
| 246 | |||
| 247 | synproxy_send_tcp(net, skb, nskb, skb_nfct(skb), | ||
| 248 | IP_CT_ESTABLISHED_REPLY, niph, nth, tcp_hdr_size); | ||
| 249 | } | ||
| 250 | |||
| 251 | static bool | ||
| 252 | synproxy_recv_client_ack(struct net *net, | ||
| 253 | const struct sk_buff *skb, const struct tcphdr *th, | ||
| 254 | struct synproxy_options *opts, u32 recv_seq) | ||
| 255 | { | ||
| 256 | struct synproxy_net *snet = synproxy_pernet(net); | ||
| 257 | int mss; | ||
| 258 | |||
| 259 | mss = __cookie_v6_check(ipv6_hdr(skb), th, ntohl(th->ack_seq) - 1); | ||
| 260 | if (mss == 0) { | ||
| 261 | this_cpu_inc(snet->stats->cookie_invalid); | ||
| 262 | return false; | ||
| 263 | } | ||
| 264 | |||
| 265 | this_cpu_inc(snet->stats->cookie_valid); | ||
| 266 | opts->mss = mss; | ||
| 267 | opts->options |= XT_SYNPROXY_OPT_MSS; | ||
| 268 | |||
| 269 | if (opts->options & XT_SYNPROXY_OPT_TIMESTAMP) | ||
| 270 | synproxy_check_timestamp_cookie(opts); | ||
| 271 | |||
| 272 | synproxy_send_server_syn(net, skb, th, opts, recv_seq); | ||
| 273 | return true; | ||
| 274 | } | ||
| 275 | 14 | ||
| 276 | static unsigned int | 15 | static unsigned int |
| 277 | synproxy_tg6(struct sk_buff *skb, const struct xt_action_param *par) | 16 | synproxy_tg6(struct sk_buff *skb, const struct xt_action_param *par) |
| @@ -307,13 +46,14 @@ synproxy_tg6(struct sk_buff *skb, const struct xt_action_param *par) | |||
| 307 | XT_SYNPROXY_OPT_SACK_PERM | | 46 | XT_SYNPROXY_OPT_SACK_PERM | |
| 308 | XT_SYNPROXY_OPT_ECN); | 47 | XT_SYNPROXY_OPT_ECN); |
| 309 | 48 | ||
| 310 | synproxy_send_client_synack(net, skb, th, &opts); | 49 | synproxy_send_client_synack_ipv6(net, skb, th, &opts); |
| 311 | consume_skb(skb); | 50 | consume_skb(skb); |
| 312 | return NF_STOLEN; | 51 | return NF_STOLEN; |
| 313 | 52 | ||
| 314 | } else if (th->ack && !(th->fin || th->rst || th->syn)) { | 53 | } else if (th->ack && !(th->fin || th->rst || th->syn)) { |
| 315 | /* ACK from client */ | 54 | /* ACK from client */ |
| 316 | if (synproxy_recv_client_ack(net, skb, th, &opts, ntohl(th->seq))) { | 55 | if (synproxy_recv_client_ack_ipv6(net, skb, th, &opts, |
| 56 | ntohl(th->seq))) { | ||
| 317 | consume_skb(skb); | 57 | consume_skb(skb); |
| 318 | return NF_STOLEN; | 58 | return NF_STOLEN; |
| 319 | } else { | 59 | } else { |
| @@ -324,141 +64,6 @@ synproxy_tg6(struct sk_buff *skb, const struct xt_action_param *par) | |||
| 324 | return XT_CONTINUE; | 64 | return XT_CONTINUE; |
| 325 | } | 65 | } |
| 326 | 66 | ||
| 327 | static unsigned int ipv6_synproxy_hook(void *priv, | ||
| 328 | struct sk_buff *skb, | ||
| 329 | const struct nf_hook_state *nhs) | ||
| 330 | { | ||
| 331 | struct net *net = nhs->net; | ||
| 332 | struct synproxy_net *snet = synproxy_pernet(net); | ||
| 333 | enum ip_conntrack_info ctinfo; | ||
| 334 | struct nf_conn *ct; | ||
| 335 | struct nf_conn_synproxy *synproxy; | ||
| 336 | struct synproxy_options opts = {}; | ||
| 337 | const struct ip_ct_tcp *state; | ||
| 338 | struct tcphdr *th, _th; | ||
| 339 | __be16 frag_off; | ||
| 340 | u8 nexthdr; | ||
| 341 | int thoff; | ||
| 342 | |||
| 343 | ct = nf_ct_get(skb, &ctinfo); | ||
| 344 | if (ct == NULL) | ||
| 345 | return NF_ACCEPT; | ||
| 346 | |||
| 347 | synproxy = nfct_synproxy(ct); | ||
| 348 | if (synproxy == NULL) | ||
| 349 | return NF_ACCEPT; | ||
| 350 | |||
| 351 | if (nf_is_loopback_packet(skb)) | ||
| 352 | return NF_ACCEPT; | ||
| 353 | |||
| 354 | nexthdr = ipv6_hdr(skb)->nexthdr; | ||
| 355 | thoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr, | ||
| 356 | &frag_off); | ||
| 357 | if (thoff < 0 || nexthdr != IPPROTO_TCP) | ||
| 358 | return NF_ACCEPT; | ||
| 359 | |||
| 360 | th = skb_header_pointer(skb, thoff, sizeof(_th), &_th); | ||
| 361 | if (th == NULL) | ||
| 362 | return NF_DROP; | ||
| 363 | |||
| 364 | state = &ct->proto.tcp; | ||
| 365 | switch (state->state) { | ||
| 366 | case TCP_CONNTRACK_CLOSE: | ||
| 367 | if (th->rst && !test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) { | ||
| 368 | nf_ct_seqadj_init(ct, ctinfo, synproxy->isn - | ||
| 369 | ntohl(th->seq) + 1); | ||
| 370 | break; | ||
| 371 | } | ||
| 372 | |||
| 373 | if (!th->syn || th->ack || | ||
| 374 | CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL) | ||
| 375 | break; | ||
| 376 | |||
| 377 | /* Reopened connection - reset the sequence number and timestamp | ||
| 378 | * adjustments, they will get initialized once the connection is | ||
| 379 | * reestablished. | ||
| 380 | */ | ||
| 381 | nf_ct_seqadj_init(ct, ctinfo, 0); | ||
| 382 | synproxy->tsoff = 0; | ||
| 383 | this_cpu_inc(snet->stats->conn_reopened); | ||
| 384 | |||
| 385 | /* fall through */ | ||
| 386 | case TCP_CONNTRACK_SYN_SENT: | ||
| 387 | if (!synproxy_parse_options(skb, thoff, th, &opts)) | ||
| 388 | return NF_DROP; | ||
| 389 | |||
| 390 | if (!th->syn && th->ack && | ||
| 391 | CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) { | ||
| 392 | /* Keep-Alives are sent with SEG.SEQ = SND.NXT-1, | ||
| 393 | * therefore we need to add 1 to make the SYN sequence | ||
| 394 | * number match the one of first SYN. | ||
| 395 | */ | ||
| 396 | if (synproxy_recv_client_ack(net, skb, th, &opts, | ||
| 397 | ntohl(th->seq) + 1)) { | ||
| 398 | this_cpu_inc(snet->stats->cookie_retrans); | ||
| 399 | consume_skb(skb); | ||
| 400 | return NF_STOLEN; | ||
| 401 | } else { | ||
| 402 | return NF_DROP; | ||
| 403 | } | ||
| 404 | } | ||
| 405 | |||
| 406 | synproxy->isn = ntohl(th->ack_seq); | ||
| 407 | if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP) | ||
| 408 | synproxy->its = opts.tsecr; | ||
| 409 | |||
| 410 | nf_conntrack_event_cache(IPCT_SYNPROXY, ct); | ||
| 411 | break; | ||
| 412 | case TCP_CONNTRACK_SYN_RECV: | ||
| 413 | if (!th->syn || !th->ack) | ||
| 414 | break; | ||
| 415 | |||
| 416 | if (!synproxy_parse_options(skb, thoff, th, &opts)) | ||
| 417 | return NF_DROP; | ||
| 418 | |||
| 419 | if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP) { | ||
| 420 | synproxy->tsoff = opts.tsval - synproxy->its; | ||
| 421 | nf_conntrack_event_cache(IPCT_SYNPROXY, ct); | ||
| 422 | } | ||
| 423 | |||
| 424 | opts.options &= ~(XT_SYNPROXY_OPT_MSS | | ||
| 425 | XT_SYNPROXY_OPT_WSCALE | | ||
| 426 | XT_SYNPROXY_OPT_SACK_PERM); | ||
| 427 | |||
| 428 | swap(opts.tsval, opts.tsecr); | ||
| 429 | synproxy_send_server_ack(net, state, skb, th, &opts); | ||
| 430 | |||
| 431 | nf_ct_seqadj_init(ct, ctinfo, synproxy->isn - ntohl(th->seq)); | ||
| 432 | nf_conntrack_event_cache(IPCT_SEQADJ, ct); | ||
| 433 | |||
| 434 | swap(opts.tsval, opts.tsecr); | ||
| 435 | synproxy_send_client_ack(net, skb, th, &opts); | ||
| 436 | |||
| 437 | consume_skb(skb); | ||
| 438 | return NF_STOLEN; | ||
| 439 | default: | ||
| 440 | break; | ||
| 441 | } | ||
| 442 | |||
| 443 | synproxy_tstamp_adjust(skb, thoff, th, ct, ctinfo, synproxy); | ||
| 444 | return NF_ACCEPT; | ||
| 445 | } | ||
| 446 | |||
| 447 | static const struct nf_hook_ops ipv6_synproxy_ops[] = { | ||
| 448 | { | ||
| 449 | .hook = ipv6_synproxy_hook, | ||
| 450 | .pf = NFPROTO_IPV6, | ||
| 451 | .hooknum = NF_INET_LOCAL_IN, | ||
| 452 | .priority = NF_IP_PRI_CONNTRACK_CONFIRM - 1, | ||
| 453 | }, | ||
| 454 | { | ||
| 455 | .hook = ipv6_synproxy_hook, | ||
| 456 | .pf = NFPROTO_IPV6, | ||
| 457 | .hooknum = NF_INET_POST_ROUTING, | ||
| 458 | .priority = NF_IP_PRI_CONNTRACK_CONFIRM - 1, | ||
| 459 | }, | ||
| 460 | }; | ||
| 461 | |||
| 462 | static int synproxy_tg6_check(const struct xt_tgchk_param *par) | 67 | static int synproxy_tg6_check(const struct xt_tgchk_param *par) |
| 463 | { | 68 | { |
| 464 | struct synproxy_net *snet = synproxy_pernet(par->net); | 69 | struct synproxy_net *snet = synproxy_pernet(par->net); |
| @@ -474,16 +79,12 @@ static int synproxy_tg6_check(const struct xt_tgchk_param *par) | |||
| 474 | if (err) | 79 | if (err) |
| 475 | return err; | 80 | return err; |
| 476 | 81 | ||
| 477 | if (snet->hook_ref6 == 0) { | 82 | err = nf_synproxy_ipv6_init(snet, par->net); |
| 478 | err = nf_register_net_hooks(par->net, ipv6_synproxy_ops, | 83 | if (err) { |
| 479 | ARRAY_SIZE(ipv6_synproxy_ops)); | 84 | nf_ct_netns_put(par->net, par->family); |
| 480 | if (err) { | 85 | return err; |
| 481 | nf_ct_netns_put(par->net, par->family); | ||
| 482 | return err; | ||
| 483 | } | ||
| 484 | } | 86 | } |
| 485 | 87 | ||
| 486 | snet->hook_ref6++; | ||
| 487 | return err; | 88 | return err; |
| 488 | } | 89 | } |
| 489 | 90 | ||
| @@ -491,10 +92,7 @@ static void synproxy_tg6_destroy(const struct xt_tgdtor_param *par) | |||
| 491 | { | 92 | { |
| 492 | struct synproxy_net *snet = synproxy_pernet(par->net); | 93 | struct synproxy_net *snet = synproxy_pernet(par->net); |
| 493 | 94 | ||
| 494 | snet->hook_ref6--; | 95 | nf_synproxy_ipv6_fini(snet, par->net); |
| 495 | if (snet->hook_ref6 == 0) | ||
| 496 | nf_unregister_net_hooks(par->net, ipv6_synproxy_ops, | ||
| 497 | ARRAY_SIZE(ipv6_synproxy_ops)); | ||
| 498 | nf_ct_netns_put(par->net, par->family); | 96 | nf_ct_netns_put(par->net, par->family); |
| 499 | } | 97 | } |
| 500 | 98 | ||
diff --git a/net/netfilter/nf_synproxy_core.c b/net/netfilter/nf_synproxy_core.c index 3d58a9e93e5a..50677285f82e 100644 --- a/net/netfilter/nf_synproxy_core.c +++ b/net/netfilter/nf_synproxy_core.c | |||
| @@ -13,16 +13,16 @@ | |||
| 13 | #include <net/netns/generic.h> | 13 | #include <net/netns/generic.h> |
| 14 | #include <linux/proc_fs.h> | 14 | #include <linux/proc_fs.h> |
| 15 | 15 | ||
| 16 | #include <linux/netfilter_ipv4/ip_tables.h> | 16 | #include <linux/netfilter_ipv6.h> |
| 17 | #include <linux/netfilter/x_tables.h> | 17 | #include <linux/netfilter/nf_SYNPROXY.h> |
| 18 | #include <linux/netfilter/xt_tcpudp.h> | ||
| 19 | #include <linux/netfilter/xt_SYNPROXY.h> | ||
| 20 | 18 | ||
| 21 | #include <net/netfilter/nf_conntrack.h> | 19 | #include <net/netfilter/nf_conntrack.h> |
| 20 | #include <net/netfilter/nf_conntrack_ecache.h> | ||
| 22 | #include <net/netfilter/nf_conntrack_extend.h> | 21 | #include <net/netfilter/nf_conntrack_extend.h> |
| 23 | #include <net/netfilter/nf_conntrack_seqadj.h> | 22 | #include <net/netfilter/nf_conntrack_seqadj.h> |
| 24 | #include <net/netfilter/nf_conntrack_synproxy.h> | 23 | #include <net/netfilter/nf_conntrack_synproxy.h> |
| 25 | #include <net/netfilter/nf_conntrack_zones.h> | 24 | #include <net/netfilter/nf_conntrack_zones.h> |
| 25 | #include <net/netfilter/nf_synproxy.h> | ||
| 26 | 26 | ||
| 27 | unsigned int synproxy_net_id; | 27 | unsigned int synproxy_net_id; |
| 28 | EXPORT_SYMBOL_GPL(synproxy_net_id); | 28 | EXPORT_SYMBOL_GPL(synproxy_net_id); |
| @@ -60,7 +60,7 @@ synproxy_parse_options(const struct sk_buff *skb, unsigned int doff, | |||
| 60 | case TCPOPT_MSS: | 60 | case TCPOPT_MSS: |
| 61 | if (opsize == TCPOLEN_MSS) { | 61 | if (opsize == TCPOLEN_MSS) { |
| 62 | opts->mss = get_unaligned_be16(ptr); | 62 | opts->mss = get_unaligned_be16(ptr); |
| 63 | opts->options |= XT_SYNPROXY_OPT_MSS; | 63 | opts->options |= NF_SYNPROXY_OPT_MSS; |
| 64 | } | 64 | } |
| 65 | break; | 65 | break; |
| 66 | case TCPOPT_WINDOW: | 66 | case TCPOPT_WINDOW: |
| @@ -68,19 +68,19 @@ synproxy_parse_options(const struct sk_buff *skb, unsigned int doff, | |||
| 68 | opts->wscale = *ptr; | 68 | opts->wscale = *ptr; |
| 69 | if (opts->wscale > TCP_MAX_WSCALE) | 69 | if (opts->wscale > TCP_MAX_WSCALE) |
| 70 | opts->wscale = TCP_MAX_WSCALE; | 70 | opts->wscale = TCP_MAX_WSCALE; |
| 71 | opts->options |= XT_SYNPROXY_OPT_WSCALE; | 71 | opts->options |= NF_SYNPROXY_OPT_WSCALE; |
| 72 | } | 72 | } |
| 73 | break; | 73 | break; |
| 74 | case TCPOPT_TIMESTAMP: | 74 | case TCPOPT_TIMESTAMP: |
| 75 | if (opsize == TCPOLEN_TIMESTAMP) { | 75 | if (opsize == TCPOLEN_TIMESTAMP) { |
| 76 | opts->tsval = get_unaligned_be32(ptr); | 76 | opts->tsval = get_unaligned_be32(ptr); |
| 77 | opts->tsecr = get_unaligned_be32(ptr + 4); | 77 | opts->tsecr = get_unaligned_be32(ptr + 4); |
| 78 | opts->options |= XT_SYNPROXY_OPT_TIMESTAMP; | 78 | opts->options |= NF_SYNPROXY_OPT_TIMESTAMP; |
| 79 | } | 79 | } |
| 80 | break; | 80 | break; |
| 81 | case TCPOPT_SACK_PERM: | 81 | case TCPOPT_SACK_PERM: |
| 82 | if (opsize == TCPOLEN_SACK_PERM) | 82 | if (opsize == TCPOLEN_SACK_PERM) |
| 83 | opts->options |= XT_SYNPROXY_OPT_SACK_PERM; | 83 | opts->options |= NF_SYNPROXY_OPT_SACK_PERM; |
| 84 | break; | 84 | break; |
| 85 | } | 85 | } |
| 86 | 86 | ||
| @@ -92,36 +92,36 @@ synproxy_parse_options(const struct sk_buff *skb, unsigned int doff, | |||
| 92 | } | 92 | } |
| 93 | EXPORT_SYMBOL_GPL(synproxy_parse_options); | 93 | EXPORT_SYMBOL_GPL(synproxy_parse_options); |
| 94 | 94 | ||
| 95 | unsigned int synproxy_options_size(const struct synproxy_options *opts) | 95 | static unsigned int |
| 96 | synproxy_options_size(const struct synproxy_options *opts) | ||
| 96 | { | 97 | { |
| 97 | unsigned int size = 0; | 98 | unsigned int size = 0; |
| 98 | 99 | ||
| 99 | if (opts->options & XT_SYNPROXY_OPT_MSS) | 100 | if (opts->options & NF_SYNPROXY_OPT_MSS) |
| 100 | size += TCPOLEN_MSS_ALIGNED; | 101 | size += TCPOLEN_MSS_ALIGNED; |
| 101 | if (opts->options & XT_SYNPROXY_OPT_TIMESTAMP) | 102 | if (opts->options & NF_SYNPROXY_OPT_TIMESTAMP) |
| 102 | size += TCPOLEN_TSTAMP_ALIGNED; | 103 | size += TCPOLEN_TSTAMP_ALIGNED; |
| 103 | else if (opts->options & XT_SYNPROXY_OPT_SACK_PERM) | 104 | else if (opts->options & NF_SYNPROXY_OPT_SACK_PERM) |
| 104 | size += TCPOLEN_SACKPERM_ALIGNED; | 105 | size += TCPOLEN_SACKPERM_ALIGNED; |
| 105 | if (opts->options & XT_SYNPROXY_OPT_WSCALE) | 106 | if (opts->options & NF_SYNPROXY_OPT_WSCALE) |
| 106 | size += TCPOLEN_WSCALE_ALIGNED; | 107 | size += TCPOLEN_WSCALE_ALIGNED; |
| 107 | 108 | ||
| 108 | return size; | 109 | return size; |
| 109 | } | 110 | } |
| 110 | EXPORT_SYMBOL_GPL(synproxy_options_size); | ||
| 111 | 111 | ||
| 112 | void | 112 | static void |
| 113 | synproxy_build_options(struct tcphdr *th, const struct synproxy_options *opts) | 113 | synproxy_build_options(struct tcphdr *th, const struct synproxy_options *opts) |
| 114 | { | 114 | { |
| 115 | __be32 *ptr = (__be32 *)(th + 1); | 115 | __be32 *ptr = (__be32 *)(th + 1); |
| 116 | u8 options = opts->options; | 116 | u8 options = opts->options; |
| 117 | 117 | ||
| 118 | if (options & XT_SYNPROXY_OPT_MSS) | 118 | if (options & NF_SYNPROXY_OPT_MSS) |
| 119 | *ptr++ = htonl((TCPOPT_MSS << 24) | | 119 | *ptr++ = htonl((TCPOPT_MSS << 24) | |
| 120 | (TCPOLEN_MSS << 16) | | 120 | (TCPOLEN_MSS << 16) | |
| 121 | opts->mss); | 121 | opts->mss); |
| 122 | 122 | ||
| 123 | if (options & XT_SYNPROXY_OPT_TIMESTAMP) { | 123 | if (options & NF_SYNPROXY_OPT_TIMESTAMP) { |
| 124 | if (options & XT_SYNPROXY_OPT_SACK_PERM) | 124 | if (options & NF_SYNPROXY_OPT_SACK_PERM) |
| 125 | *ptr++ = htonl((TCPOPT_SACK_PERM << 24) | | 125 | *ptr++ = htonl((TCPOPT_SACK_PERM << 24) | |
| 126 | (TCPOLEN_SACK_PERM << 16) | | 126 | (TCPOLEN_SACK_PERM << 16) | |
| 127 | (TCPOPT_TIMESTAMP << 8) | | 127 | (TCPOPT_TIMESTAMP << 8) | |
| @@ -134,58 +134,56 @@ synproxy_build_options(struct tcphdr *th, const struct synproxy_options *opts) | |||
| 134 | 134 | ||
| 135 | *ptr++ = htonl(opts->tsval); | 135 | *ptr++ = htonl(opts->tsval); |
| 136 | *ptr++ = htonl(opts->tsecr); | 136 | *ptr++ = htonl(opts->tsecr); |
| 137 | } else if (options & XT_SYNPROXY_OPT_SACK_PERM) | 137 | } else if (options & NF_SYNPROXY_OPT_SACK_PERM) |
| 138 | *ptr++ = htonl((TCPOPT_NOP << 24) | | 138 | *ptr++ = htonl((TCPOPT_NOP << 24) | |
| 139 | (TCPOPT_NOP << 16) | | 139 | (TCPOPT_NOP << 16) | |
| 140 | (TCPOPT_SACK_PERM << 8) | | 140 | (TCPOPT_SACK_PERM << 8) | |
| 141 | TCPOLEN_SACK_PERM); | 141 | TCPOLEN_SACK_PERM); |
| 142 | 142 | ||
| 143 | if (options & XT_SYNPROXY_OPT_WSCALE) | 143 | if (options & NF_SYNPROXY_OPT_WSCALE) |
| 144 | *ptr++ = htonl((TCPOPT_NOP << 24) | | 144 | *ptr++ = htonl((TCPOPT_NOP << 24) | |
| 145 | (TCPOPT_WINDOW << 16) | | 145 | (TCPOPT_WINDOW << 16) | |
| 146 | (TCPOLEN_WINDOW << 8) | | 146 | (TCPOLEN_WINDOW << 8) | |
| 147 | opts->wscale); | 147 | opts->wscale); |
| 148 | } | 148 | } |
| 149 | EXPORT_SYMBOL_GPL(synproxy_build_options); | ||
| 150 | 149 | ||
| 151 | void synproxy_init_timestamp_cookie(const struct xt_synproxy_info *info, | 150 | void synproxy_init_timestamp_cookie(const struct nf_synproxy_info *info, |
| 152 | struct synproxy_options *opts) | 151 | struct synproxy_options *opts) |
| 153 | { | 152 | { |
| 154 | opts->tsecr = opts->tsval; | 153 | opts->tsecr = opts->tsval; |
| 155 | opts->tsval = tcp_time_stamp_raw() & ~0x3f; | 154 | opts->tsval = tcp_time_stamp_raw() & ~0x3f; |
| 156 | 155 | ||
| 157 | if (opts->options & XT_SYNPROXY_OPT_WSCALE) { | 156 | if (opts->options & NF_SYNPROXY_OPT_WSCALE) { |
| 158 | opts->tsval |= opts->wscale; | 157 | opts->tsval |= opts->wscale; |
| 159 | opts->wscale = info->wscale; | 158 | opts->wscale = info->wscale; |
| 160 | } else | 159 | } else |
| 161 | opts->tsval |= 0xf; | 160 | opts->tsval |= 0xf; |
| 162 | 161 | ||
| 163 | if (opts->options & XT_SYNPROXY_OPT_SACK_PERM) | 162 | if (opts->options & NF_SYNPROXY_OPT_SACK_PERM) |
| 164 | opts->tsval |= 1 << 4; | 163 | opts->tsval |= 1 << 4; |
| 165 | 164 | ||
| 166 | if (opts->options & XT_SYNPROXY_OPT_ECN) | 165 | if (opts->options & NF_SYNPROXY_OPT_ECN) |
| 167 | opts->tsval |= 1 << 5; | 166 | opts->tsval |= 1 << 5; |
| 168 | } | 167 | } |
| 169 | EXPORT_SYMBOL_GPL(synproxy_init_timestamp_cookie); | 168 | EXPORT_SYMBOL_GPL(synproxy_init_timestamp_cookie); |
| 170 | 169 | ||
| 171 | void synproxy_check_timestamp_cookie(struct synproxy_options *opts) | 170 | static void |
| 171 | synproxy_check_timestamp_cookie(struct synproxy_options *opts) | ||
| 172 | { | 172 | { |
| 173 | opts->wscale = opts->tsecr & 0xf; | 173 | opts->wscale = opts->tsecr & 0xf; |
| 174 | if (opts->wscale != 0xf) | 174 | if (opts->wscale != 0xf) |
| 175 | opts->options |= XT_SYNPROXY_OPT_WSCALE; | 175 | opts->options |= NF_SYNPROXY_OPT_WSCALE; |
| 176 | 176 | ||
| 177 | opts->options |= opts->tsecr & (1 << 4) ? XT_SYNPROXY_OPT_SACK_PERM : 0; | 177 | opts->options |= opts->tsecr & (1 << 4) ? NF_SYNPROXY_OPT_SACK_PERM : 0; |
| 178 | 178 | ||
| 179 | opts->options |= opts->tsecr & (1 << 5) ? XT_SYNPROXY_OPT_ECN : 0; | 179 | opts->options |= opts->tsecr & (1 << 5) ? NF_SYNPROXY_OPT_ECN : 0; |
| 180 | } | 180 | } |
| 181 | EXPORT_SYMBOL_GPL(synproxy_check_timestamp_cookie); | ||
| 182 | 181 | ||
| 183 | unsigned int synproxy_tstamp_adjust(struct sk_buff *skb, | 182 | static unsigned int |
| 184 | unsigned int protoff, | 183 | synproxy_tstamp_adjust(struct sk_buff *skb, unsigned int protoff, |
| 185 | struct tcphdr *th, | 184 | struct tcphdr *th, struct nf_conn *ct, |
| 186 | struct nf_conn *ct, | 185 | enum ip_conntrack_info ctinfo, |
| 187 | enum ip_conntrack_info ctinfo, | 186 | const struct nf_conn_synproxy *synproxy) |
| 188 | const struct nf_conn_synproxy *synproxy) | ||
| 189 | { | 187 | { |
| 190 | unsigned int optoff, optend; | 188 | unsigned int optoff, optend; |
| 191 | __be32 *ptr, old; | 189 | __be32 *ptr, old; |
| @@ -235,7 +233,6 @@ unsigned int synproxy_tstamp_adjust(struct sk_buff *skb, | |||
| 235 | } | 233 | } |
| 236 | return 1; | 234 | return 1; |
| 237 | } | 235 | } |
| 238 | EXPORT_SYMBOL_GPL(synproxy_tstamp_adjust); | ||
| 239 | 236 | ||
| 240 | static struct nf_ct_ext_type nf_ct_synproxy_extend __read_mostly = { | 237 | static struct nf_ct_ext_type nf_ct_synproxy_extend __read_mostly = { |
| 241 | .len = sizeof(struct nf_conn_synproxy), | 238 | .len = sizeof(struct nf_conn_synproxy), |
| @@ -416,5 +413,830 @@ static void __exit synproxy_core_exit(void) | |||
| 416 | module_init(synproxy_core_init); | 413 | module_init(synproxy_core_init); |
| 417 | module_exit(synproxy_core_exit); | 414 | module_exit(synproxy_core_exit); |
| 418 | 415 | ||
| 416 | static struct iphdr * | ||
| 417 | synproxy_build_ip(struct net *net, struct sk_buff *skb, __be32 saddr, | ||
| 418 | __be32 daddr) | ||
| 419 | { | ||
| 420 | struct iphdr *iph; | ||
| 421 | |||
| 422 | skb_reset_network_header(skb); | ||
| 423 | iph = skb_put(skb, sizeof(*iph)); | ||
| 424 | iph->version = 4; | ||
| 425 | iph->ihl = sizeof(*iph) / 4; | ||
| 426 | iph->tos = 0; | ||
| 427 | iph->id = 0; | ||
| 428 | iph->frag_off = htons(IP_DF); | ||
| 429 | iph->ttl = net->ipv4.sysctl_ip_default_ttl; | ||
| 430 | iph->protocol = IPPROTO_TCP; | ||
| 431 | iph->check = 0; | ||
| 432 | iph->saddr = saddr; | ||
| 433 | iph->daddr = daddr; | ||
| 434 | |||
| 435 | return iph; | ||
| 436 | } | ||
| 437 | |||
| 438 | static void | ||
| 439 | synproxy_send_tcp(struct net *net, | ||
| 440 | const struct sk_buff *skb, struct sk_buff *nskb, | ||
| 441 | struct nf_conntrack *nfct, enum ip_conntrack_info ctinfo, | ||
| 442 | struct iphdr *niph, struct tcphdr *nth, | ||
| 443 | unsigned int tcp_hdr_size) | ||
| 444 | { | ||
| 445 | nth->check = ~tcp_v4_check(tcp_hdr_size, niph->saddr, niph->daddr, 0); | ||
| 446 | nskb->ip_summed = CHECKSUM_PARTIAL; | ||
| 447 | nskb->csum_start = (unsigned char *)nth - nskb->head; | ||
| 448 | nskb->csum_offset = offsetof(struct tcphdr, check); | ||
| 449 | |||
| 450 | skb_dst_set_noref(nskb, skb_dst(skb)); | ||
| 451 | nskb->protocol = htons(ETH_P_IP); | ||
| 452 | if (ip_route_me_harder(net, nskb, RTN_UNSPEC)) | ||
| 453 | goto free_nskb; | ||
| 454 | |||
| 455 | if (nfct) { | ||
| 456 | nf_ct_set(nskb, (struct nf_conn *)nfct, ctinfo); | ||
| 457 | nf_conntrack_get(nfct); | ||
| 458 | } | ||
| 459 | |||
| 460 | ip_local_out(net, nskb->sk, nskb); | ||
| 461 | return; | ||
| 462 | |||
| 463 | free_nskb: | ||
| 464 | kfree_skb(nskb); | ||
| 465 | } | ||
| 466 | |||
| 467 | void | ||
| 468 | synproxy_send_client_synack(struct net *net, | ||
| 469 | const struct sk_buff *skb, const struct tcphdr *th, | ||
| 470 | const struct synproxy_options *opts) | ||
| 471 | { | ||
| 472 | struct sk_buff *nskb; | ||
| 473 | struct iphdr *iph, *niph; | ||
| 474 | struct tcphdr *nth; | ||
| 475 | unsigned int tcp_hdr_size; | ||
| 476 | u16 mss = opts->mss; | ||
| 477 | |||
| 478 | iph = ip_hdr(skb); | ||
| 479 | |||
| 480 | tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts); | ||
| 481 | nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER, | ||
| 482 | GFP_ATOMIC); | ||
| 483 | if (!nskb) | ||
| 484 | return; | ||
| 485 | skb_reserve(nskb, MAX_TCP_HEADER); | ||
| 486 | |||
| 487 | niph = synproxy_build_ip(net, nskb, iph->daddr, iph->saddr); | ||
| 488 | |||
| 489 | skb_reset_transport_header(nskb); | ||
| 490 | nth = skb_put(nskb, tcp_hdr_size); | ||
| 491 | nth->source = th->dest; | ||
| 492 | nth->dest = th->source; | ||
| 493 | nth->seq = htonl(__cookie_v4_init_sequence(iph, th, &mss)); | ||
| 494 | nth->ack_seq = htonl(ntohl(th->seq) + 1); | ||
| 495 | tcp_flag_word(nth) = TCP_FLAG_SYN | TCP_FLAG_ACK; | ||
| 496 | if (opts->options & NF_SYNPROXY_OPT_ECN) | ||
| 497 | tcp_flag_word(nth) |= TCP_FLAG_ECE; | ||
| 498 | nth->doff = tcp_hdr_size / 4; | ||
| 499 | nth->window = 0; | ||
| 500 | nth->check = 0; | ||
| 501 | nth->urg_ptr = 0; | ||
| 502 | |||
| 503 | synproxy_build_options(nth, opts); | ||
| 504 | |||
| 505 | synproxy_send_tcp(net, skb, nskb, skb_nfct(skb), | ||
| 506 | IP_CT_ESTABLISHED_REPLY, niph, nth, tcp_hdr_size); | ||
| 507 | } | ||
| 508 | EXPORT_SYMBOL_GPL(synproxy_send_client_synack); | ||
| 509 | |||
| 510 | static void | ||
| 511 | synproxy_send_server_syn(struct net *net, | ||
| 512 | const struct sk_buff *skb, const struct tcphdr *th, | ||
| 513 | const struct synproxy_options *opts, u32 recv_seq) | ||
| 514 | { | ||
| 515 | struct synproxy_net *snet = synproxy_pernet(net); | ||
| 516 | struct sk_buff *nskb; | ||
| 517 | struct iphdr *iph, *niph; | ||
| 518 | struct tcphdr *nth; | ||
| 519 | unsigned int tcp_hdr_size; | ||
| 520 | |||
| 521 | iph = ip_hdr(skb); | ||
| 522 | |||
| 523 | tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts); | ||
| 524 | nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER, | ||
| 525 | GFP_ATOMIC); | ||
| 526 | if (!nskb) | ||
| 527 | return; | ||
| 528 | skb_reserve(nskb, MAX_TCP_HEADER); | ||
| 529 | |||
| 530 | niph = synproxy_build_ip(net, nskb, iph->saddr, iph->daddr); | ||
| 531 | |||
| 532 | skb_reset_transport_header(nskb); | ||
| 533 | nth = skb_put(nskb, tcp_hdr_size); | ||
| 534 | nth->source = th->source; | ||
| 535 | nth->dest = th->dest; | ||
| 536 | nth->seq = htonl(recv_seq - 1); | ||
| 537 | /* ack_seq is used to relay our ISN to the synproxy hook to initialize | ||
| 538 | * sequence number translation once a connection tracking entry exists. | ||
| 539 | */ | ||
| 540 | nth->ack_seq = htonl(ntohl(th->ack_seq) - 1); | ||
| 541 | tcp_flag_word(nth) = TCP_FLAG_SYN; | ||
| 542 | if (opts->options & NF_SYNPROXY_OPT_ECN) | ||
| 543 | tcp_flag_word(nth) |= TCP_FLAG_ECE | TCP_FLAG_CWR; | ||
| 544 | nth->doff = tcp_hdr_size / 4; | ||
| 545 | nth->window = th->window; | ||
| 546 | nth->check = 0; | ||
| 547 | nth->urg_ptr = 0; | ||
| 548 | |||
| 549 | synproxy_build_options(nth, opts); | ||
| 550 | |||
| 551 | synproxy_send_tcp(net, skb, nskb, &snet->tmpl->ct_general, IP_CT_NEW, | ||
| 552 | niph, nth, tcp_hdr_size); | ||
| 553 | } | ||
| 554 | |||
| 555 | static void | ||
| 556 | synproxy_send_server_ack(struct net *net, | ||
| 557 | const struct ip_ct_tcp *state, | ||
| 558 | const struct sk_buff *skb, const struct tcphdr *th, | ||
| 559 | const struct synproxy_options *opts) | ||
| 560 | { | ||
| 561 | struct sk_buff *nskb; | ||
| 562 | struct iphdr *iph, *niph; | ||
| 563 | struct tcphdr *nth; | ||
| 564 | unsigned int tcp_hdr_size; | ||
| 565 | |||
| 566 | iph = ip_hdr(skb); | ||
| 567 | |||
| 568 | tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts); | ||
| 569 | nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER, | ||
| 570 | GFP_ATOMIC); | ||
| 571 | if (!nskb) | ||
| 572 | return; | ||
| 573 | skb_reserve(nskb, MAX_TCP_HEADER); | ||
| 574 | |||
| 575 | niph = synproxy_build_ip(net, nskb, iph->daddr, iph->saddr); | ||
| 576 | |||
| 577 | skb_reset_transport_header(nskb); | ||
| 578 | nth = skb_put(nskb, tcp_hdr_size); | ||
| 579 | nth->source = th->dest; | ||
| 580 | nth->dest = th->source; | ||
| 581 | nth->seq = htonl(ntohl(th->ack_seq)); | ||
| 582 | nth->ack_seq = htonl(ntohl(th->seq) + 1); | ||
| 583 | tcp_flag_word(nth) = TCP_FLAG_ACK; | ||
| 584 | nth->doff = tcp_hdr_size / 4; | ||
| 585 | nth->window = htons(state->seen[IP_CT_DIR_ORIGINAL].td_maxwin); | ||
| 586 | nth->check = 0; | ||
| 587 | nth->urg_ptr = 0; | ||
| 588 | |||
| 589 | synproxy_build_options(nth, opts); | ||
| 590 | |||
| 591 | synproxy_send_tcp(net, skb, nskb, NULL, 0, niph, nth, tcp_hdr_size); | ||
| 592 | } | ||
| 593 | |||
| 594 | static void | ||
| 595 | synproxy_send_client_ack(struct net *net, | ||
| 596 | const struct sk_buff *skb, const struct tcphdr *th, | ||
| 597 | const struct synproxy_options *opts) | ||
| 598 | { | ||
| 599 | struct sk_buff *nskb; | ||
| 600 | struct iphdr *iph, *niph; | ||
| 601 | struct tcphdr *nth; | ||
| 602 | unsigned int tcp_hdr_size; | ||
| 603 | |||
| 604 | iph = ip_hdr(skb); | ||
| 605 | |||
| 606 | tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts); | ||
| 607 | nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER, | ||
| 608 | GFP_ATOMIC); | ||
| 609 | if (!nskb) | ||
| 610 | return; | ||
| 611 | skb_reserve(nskb, MAX_TCP_HEADER); | ||
| 612 | |||
| 613 | niph = synproxy_build_ip(net, nskb, iph->saddr, iph->daddr); | ||
| 614 | |||
| 615 | skb_reset_transport_header(nskb); | ||
| 616 | nth = skb_put(nskb, tcp_hdr_size); | ||
| 617 | nth->source = th->source; | ||
| 618 | nth->dest = th->dest; | ||
| 619 | nth->seq = htonl(ntohl(th->seq) + 1); | ||
| 620 | nth->ack_seq = th->ack_seq; | ||
| 621 | tcp_flag_word(nth) = TCP_FLAG_ACK; | ||
| 622 | nth->doff = tcp_hdr_size / 4; | ||
| 623 | nth->window = htons(ntohs(th->window) >> opts->wscale); | ||
| 624 | nth->check = 0; | ||
| 625 | nth->urg_ptr = 0; | ||
| 626 | |||
| 627 | synproxy_build_options(nth, opts); | ||
| 628 | |||
| 629 | synproxy_send_tcp(net, skb, nskb, skb_nfct(skb), | ||
| 630 | IP_CT_ESTABLISHED_REPLY, niph, nth, tcp_hdr_size); | ||
| 631 | } | ||
| 632 | |||
| 633 | bool | ||
| 634 | synproxy_recv_client_ack(struct net *net, | ||
| 635 | const struct sk_buff *skb, const struct tcphdr *th, | ||
| 636 | struct synproxy_options *opts, u32 recv_seq) | ||
| 637 | { | ||
| 638 | struct synproxy_net *snet = synproxy_pernet(net); | ||
| 639 | int mss; | ||
| 640 | |||
| 641 | mss = __cookie_v4_check(ip_hdr(skb), th, ntohl(th->ack_seq) - 1); | ||
| 642 | if (mss == 0) { | ||
| 643 | this_cpu_inc(snet->stats->cookie_invalid); | ||
| 644 | return false; | ||
| 645 | } | ||
| 646 | |||
| 647 | this_cpu_inc(snet->stats->cookie_valid); | ||
| 648 | opts->mss = mss; | ||
| 649 | opts->options |= NF_SYNPROXY_OPT_MSS; | ||
| 650 | |||
| 651 | if (opts->options & NF_SYNPROXY_OPT_TIMESTAMP) | ||
| 652 | synproxy_check_timestamp_cookie(opts); | ||
| 653 | |||
| 654 | synproxy_send_server_syn(net, skb, th, opts, recv_seq); | ||
| 655 | return true; | ||
| 656 | } | ||
| 657 | EXPORT_SYMBOL_GPL(synproxy_recv_client_ack); | ||
| 658 | |||
| 659 | unsigned int | ||
| 660 | ipv4_synproxy_hook(void *priv, struct sk_buff *skb, | ||
| 661 | const struct nf_hook_state *nhs) | ||
| 662 | { | ||
| 663 | struct net *net = nhs->net; | ||
| 664 | struct synproxy_net *snet = synproxy_pernet(net); | ||
| 665 | enum ip_conntrack_info ctinfo; | ||
| 666 | struct nf_conn *ct; | ||
| 667 | struct nf_conn_synproxy *synproxy; | ||
| 668 | struct synproxy_options opts = {}; | ||
| 669 | const struct ip_ct_tcp *state; | ||
| 670 | struct tcphdr *th, _th; | ||
| 671 | unsigned int thoff; | ||
| 672 | |||
| 673 | ct = nf_ct_get(skb, &ctinfo); | ||
| 674 | if (!ct) | ||
| 675 | return NF_ACCEPT; | ||
| 676 | |||
| 677 | synproxy = nfct_synproxy(ct); | ||
| 678 | if (!synproxy) | ||
| 679 | return NF_ACCEPT; | ||
| 680 | |||
| 681 | if (nf_is_loopback_packet(skb) || | ||
| 682 | ip_hdr(skb)->protocol != IPPROTO_TCP) | ||
| 683 | return NF_ACCEPT; | ||
| 684 | |||
| 685 | thoff = ip_hdrlen(skb); | ||
| 686 | th = skb_header_pointer(skb, thoff, sizeof(_th), &_th); | ||
| 687 | if (!th) | ||
| 688 | return NF_DROP; | ||
| 689 | |||
| 690 | state = &ct->proto.tcp; | ||
| 691 | switch (state->state) { | ||
| 692 | case TCP_CONNTRACK_CLOSE: | ||
| 693 | if (th->rst && !test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) { | ||
| 694 | nf_ct_seqadj_init(ct, ctinfo, synproxy->isn - | ||
| 695 | ntohl(th->seq) + 1); | ||
| 696 | break; | ||
| 697 | } | ||
| 698 | |||
| 699 | if (!th->syn || th->ack || | ||
| 700 | CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL) | ||
| 701 | break; | ||
| 702 | |||
| 703 | /* Reopened connection - reset the sequence number and timestamp | ||
| 704 | * adjustments, they will get initialized once the connection is | ||
| 705 | * reestablished. | ||
| 706 | */ | ||
| 707 | nf_ct_seqadj_init(ct, ctinfo, 0); | ||
| 708 | synproxy->tsoff = 0; | ||
| 709 | this_cpu_inc(snet->stats->conn_reopened); | ||
| 710 | |||
| 711 | /* fall through */ | ||
| 712 | case TCP_CONNTRACK_SYN_SENT: | ||
| 713 | if (!synproxy_parse_options(skb, thoff, th, &opts)) | ||
| 714 | return NF_DROP; | ||
| 715 | |||
| 716 | if (!th->syn && th->ack && | ||
| 717 | CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) { | ||
| 718 | /* Keep-Alives are sent with SEG.SEQ = SND.NXT-1, | ||
| 719 | * therefore we need to add 1 to make the SYN sequence | ||
| 720 | * number match the one of first SYN. | ||
| 721 | */ | ||
| 722 | if (synproxy_recv_client_ack(net, skb, th, &opts, | ||
| 723 | ntohl(th->seq) + 1)) { | ||
| 724 | this_cpu_inc(snet->stats->cookie_retrans); | ||
| 725 | consume_skb(skb); | ||
| 726 | return NF_STOLEN; | ||
| 727 | } else { | ||
| 728 | return NF_DROP; | ||
| 729 | } | ||
| 730 | } | ||
| 731 | |||
| 732 | synproxy->isn = ntohl(th->ack_seq); | ||
| 733 | if (opts.options & NF_SYNPROXY_OPT_TIMESTAMP) | ||
| 734 | synproxy->its = opts.tsecr; | ||
| 735 | |||
| 736 | nf_conntrack_event_cache(IPCT_SYNPROXY, ct); | ||
| 737 | break; | ||
| 738 | case TCP_CONNTRACK_SYN_RECV: | ||
| 739 | if (!th->syn || !th->ack) | ||
| 740 | break; | ||
| 741 | |||
| 742 | if (!synproxy_parse_options(skb, thoff, th, &opts)) | ||
| 743 | return NF_DROP; | ||
| 744 | |||
| 745 | if (opts.options & NF_SYNPROXY_OPT_TIMESTAMP) { | ||
| 746 | synproxy->tsoff = opts.tsval - synproxy->its; | ||
| 747 | nf_conntrack_event_cache(IPCT_SYNPROXY, ct); | ||
| 748 | } | ||
| 749 | |||
| 750 | opts.options &= ~(NF_SYNPROXY_OPT_MSS | | ||
| 751 | NF_SYNPROXY_OPT_WSCALE | | ||
| 752 | NF_SYNPROXY_OPT_SACK_PERM); | ||
| 753 | |||
| 754 | swap(opts.tsval, opts.tsecr); | ||
| 755 | synproxy_send_server_ack(net, state, skb, th, &opts); | ||
| 756 | |||
| 757 | nf_ct_seqadj_init(ct, ctinfo, synproxy->isn - ntohl(th->seq)); | ||
| 758 | nf_conntrack_event_cache(IPCT_SEQADJ, ct); | ||
| 759 | |||
| 760 | swap(opts.tsval, opts.tsecr); | ||
| 761 | synproxy_send_client_ack(net, skb, th, &opts); | ||
| 762 | |||
| 763 | consume_skb(skb); | ||
| 764 | return NF_STOLEN; | ||
| 765 | default: | ||
| 766 | break; | ||
| 767 | } | ||
| 768 | |||
| 769 | synproxy_tstamp_adjust(skb, thoff, th, ct, ctinfo, synproxy); | ||
| 770 | return NF_ACCEPT; | ||
| 771 | } | ||
| 772 | EXPORT_SYMBOL_GPL(ipv4_synproxy_hook); | ||
| 773 | |||
| 774 | static const struct nf_hook_ops ipv4_synproxy_ops[] = { | ||
| 775 | { | ||
| 776 | .hook = ipv4_synproxy_hook, | ||
| 777 | .pf = NFPROTO_IPV4, | ||
| 778 | .hooknum = NF_INET_LOCAL_IN, | ||
| 779 | .priority = NF_IP_PRI_CONNTRACK_CONFIRM - 1, | ||
| 780 | }, | ||
| 781 | { | ||
| 782 | .hook = ipv4_synproxy_hook, | ||
| 783 | .pf = NFPROTO_IPV4, | ||
| 784 | .hooknum = NF_INET_POST_ROUTING, | ||
| 785 | .priority = NF_IP_PRI_CONNTRACK_CONFIRM - 1, | ||
| 786 | }, | ||
| 787 | }; | ||
| 788 | |||
| 789 | int nf_synproxy_ipv4_init(struct synproxy_net *snet, struct net *net) | ||
| 790 | { | ||
| 791 | int err; | ||
| 792 | |||
| 793 | if (snet->hook_ref4 == 0) { | ||
| 794 | err = nf_register_net_hooks(net, ipv4_synproxy_ops, | ||
| 795 | ARRAY_SIZE(ipv4_synproxy_ops)); | ||
| 796 | if (err) | ||
| 797 | return err; | ||
| 798 | } | ||
| 799 | |||
| 800 | snet->hook_ref4++; | ||
| 801 | return err; | ||
| 802 | } | ||
| 803 | EXPORT_SYMBOL_GPL(nf_synproxy_ipv4_init); | ||
| 804 | |||
| 805 | void nf_synproxy_ipv4_fini(struct synproxy_net *snet, struct net *net) | ||
| 806 | { | ||
| 807 | snet->hook_ref4--; | ||
| 808 | if (snet->hook_ref4 == 0) | ||
| 809 | nf_unregister_net_hooks(net, ipv4_synproxy_ops, | ||
| 810 | ARRAY_SIZE(ipv4_synproxy_ops)); | ||
| 811 | } | ||
| 812 | EXPORT_SYMBOL_GPL(nf_synproxy_ipv4_fini); | ||
| 813 | |||
| 814 | #if IS_ENABLED(CONFIG_IPV6) | ||
| 815 | static struct ipv6hdr * | ||
| 816 | synproxy_build_ip_ipv6(struct net *net, struct sk_buff *skb, | ||
| 817 | const struct in6_addr *saddr, | ||
| 818 | const struct in6_addr *daddr) | ||
| 819 | { | ||
| 820 | struct ipv6hdr *iph; | ||
| 821 | |||
| 822 | skb_reset_network_header(skb); | ||
| 823 | iph = skb_put(skb, sizeof(*iph)); | ||
| 824 | ip6_flow_hdr(iph, 0, 0); | ||
| 825 | iph->hop_limit = net->ipv6.devconf_all->hop_limit; | ||
| 826 | iph->nexthdr = IPPROTO_TCP; | ||
| 827 | iph->saddr = *saddr; | ||
| 828 | iph->daddr = *daddr; | ||
| 829 | |||
| 830 | return iph; | ||
| 831 | } | ||
| 832 | |||
| 833 | static void | ||
| 834 | synproxy_send_tcp_ipv6(struct net *net, | ||
| 835 | const struct sk_buff *skb, struct sk_buff *nskb, | ||
| 836 | struct nf_conntrack *nfct, enum ip_conntrack_info ctinfo, | ||
| 837 | struct ipv6hdr *niph, struct tcphdr *nth, | ||
| 838 | unsigned int tcp_hdr_size) | ||
| 839 | { | ||
| 840 | struct dst_entry *dst; | ||
| 841 | struct flowi6 fl6; | ||
| 842 | int err; | ||
| 843 | |||
| 844 | nth->check = ~tcp_v6_check(tcp_hdr_size, &niph->saddr, &niph->daddr, 0); | ||
| 845 | nskb->ip_summed = CHECKSUM_PARTIAL; | ||
| 846 | nskb->csum_start = (unsigned char *)nth - nskb->head; | ||
| 847 | nskb->csum_offset = offsetof(struct tcphdr, check); | ||
| 848 | |||
| 849 | memset(&fl6, 0, sizeof(fl6)); | ||
| 850 | fl6.flowi6_proto = IPPROTO_TCP; | ||
| 851 | fl6.saddr = niph->saddr; | ||
| 852 | fl6.daddr = niph->daddr; | ||
| 853 | fl6.fl6_sport = nth->source; | ||
| 854 | fl6.fl6_dport = nth->dest; | ||
| 855 | security_skb_classify_flow((struct sk_buff *)skb, | ||
| 856 | flowi6_to_flowi(&fl6)); | ||
| 857 | err = nf_ip6_route(net, &dst, flowi6_to_flowi(&fl6), false); | ||
| 858 | if (err) { | ||
| 859 | goto free_nskb; | ||
| 860 | } | ||
| 861 | |||
| 862 | dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0); | ||
| 863 | if (IS_ERR(dst)) | ||
| 864 | goto free_nskb; | ||
| 865 | |||
| 866 | skb_dst_set(nskb, dst); | ||
| 867 | |||
| 868 | if (nfct) { | ||
| 869 | nf_ct_set(nskb, (struct nf_conn *)nfct, ctinfo); | ||
| 870 | nf_conntrack_get(nfct); | ||
| 871 | } | ||
| 872 | |||
| 873 | ip6_local_out(net, nskb->sk, nskb); | ||
| 874 | return; | ||
| 875 | |||
| 876 | free_nskb: | ||
| 877 | kfree_skb(nskb); | ||
| 878 | } | ||
| 879 | |||
| 880 | void | ||
| 881 | synproxy_send_client_synack_ipv6(struct net *net, | ||
| 882 | const struct sk_buff *skb, | ||
| 883 | const struct tcphdr *th, | ||
| 884 | const struct synproxy_options *opts) | ||
| 885 | { | ||
| 886 | struct sk_buff *nskb; | ||
| 887 | struct ipv6hdr *iph, *niph; | ||
| 888 | struct tcphdr *nth; | ||
| 889 | unsigned int tcp_hdr_size; | ||
| 890 | u16 mss = opts->mss; | ||
| 891 | |||
| 892 | iph = ipv6_hdr(skb); | ||
| 893 | |||
| 894 | tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts); | ||
| 895 | nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER, | ||
| 896 | GFP_ATOMIC); | ||
| 897 | if (!nskb) | ||
| 898 | return; | ||
| 899 | skb_reserve(nskb, MAX_TCP_HEADER); | ||
| 900 | |||
| 901 | niph = synproxy_build_ip_ipv6(net, nskb, &iph->daddr, &iph->saddr); | ||
| 902 | |||
| 903 | skb_reset_transport_header(nskb); | ||
| 904 | nth = skb_put(nskb, tcp_hdr_size); | ||
| 905 | nth->source = th->dest; | ||
| 906 | nth->dest = th->source; | ||
| 907 | nth->seq = htonl(nf_ipv6_cookie_init_sequence(iph, th, &mss)); | ||
| 908 | nth->ack_seq = htonl(ntohl(th->seq) + 1); | ||
| 909 | tcp_flag_word(nth) = TCP_FLAG_SYN | TCP_FLAG_ACK; | ||
| 910 | if (opts->options & NF_SYNPROXY_OPT_ECN) | ||
| 911 | tcp_flag_word(nth) |= TCP_FLAG_ECE; | ||
| 912 | nth->doff = tcp_hdr_size / 4; | ||
| 913 | nth->window = 0; | ||
| 914 | nth->check = 0; | ||
| 915 | nth->urg_ptr = 0; | ||
| 916 | |||
| 917 | synproxy_build_options(nth, opts); | ||
| 918 | |||
| 919 | synproxy_send_tcp_ipv6(net, skb, nskb, skb_nfct(skb), | ||
| 920 | IP_CT_ESTABLISHED_REPLY, niph, nth, | ||
| 921 | tcp_hdr_size); | ||
| 922 | } | ||
| 923 | EXPORT_SYMBOL_GPL(synproxy_send_client_synack_ipv6); | ||
| 924 | |||
| 925 | static void | ||
| 926 | synproxy_send_server_syn_ipv6(struct net *net, const struct sk_buff *skb, | ||
| 927 | const struct tcphdr *th, | ||
| 928 | const struct synproxy_options *opts, u32 recv_seq) | ||
| 929 | { | ||
| 930 | struct synproxy_net *snet = synproxy_pernet(net); | ||
| 931 | struct sk_buff *nskb; | ||
| 932 | struct ipv6hdr *iph, *niph; | ||
| 933 | struct tcphdr *nth; | ||
| 934 | unsigned int tcp_hdr_size; | ||
| 935 | |||
| 936 | iph = ipv6_hdr(skb); | ||
| 937 | |||
| 938 | tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts); | ||
| 939 | nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER, | ||
| 940 | GFP_ATOMIC); | ||
| 941 | if (!nskb) | ||
| 942 | return; | ||
| 943 | skb_reserve(nskb, MAX_TCP_HEADER); | ||
| 944 | |||
| 945 | niph = synproxy_build_ip_ipv6(net, nskb, &iph->saddr, &iph->daddr); | ||
| 946 | |||
| 947 | skb_reset_transport_header(nskb); | ||
| 948 | nth = skb_put(nskb, tcp_hdr_size); | ||
| 949 | nth->source = th->source; | ||
| 950 | nth->dest = th->dest; | ||
| 951 | nth->seq = htonl(recv_seq - 1); | ||
| 952 | /* ack_seq is used to relay our ISN to the synproxy hook to initialize | ||
| 953 | * sequence number translation once a connection tracking entry exists. | ||
| 954 | */ | ||
| 955 | nth->ack_seq = htonl(ntohl(th->ack_seq) - 1); | ||
| 956 | tcp_flag_word(nth) = TCP_FLAG_SYN; | ||
| 957 | if (opts->options & NF_SYNPROXY_OPT_ECN) | ||
| 958 | tcp_flag_word(nth) |= TCP_FLAG_ECE | TCP_FLAG_CWR; | ||
| 959 | nth->doff = tcp_hdr_size / 4; | ||
| 960 | nth->window = th->window; | ||
| 961 | nth->check = 0; | ||
| 962 | nth->urg_ptr = 0; | ||
| 963 | |||
| 964 | synproxy_build_options(nth, opts); | ||
| 965 | |||
| 966 | synproxy_send_tcp_ipv6(net, skb, nskb, &snet->tmpl->ct_general, | ||
| 967 | IP_CT_NEW, niph, nth, tcp_hdr_size); | ||
| 968 | } | ||
| 969 | |||
| 970 | static void | ||
| 971 | synproxy_send_server_ack_ipv6(struct net *net, const struct ip_ct_tcp *state, | ||
| 972 | const struct sk_buff *skb, | ||
| 973 | const struct tcphdr *th, | ||
| 974 | const struct synproxy_options *opts) | ||
| 975 | { | ||
| 976 | struct sk_buff *nskb; | ||
| 977 | struct ipv6hdr *iph, *niph; | ||
| 978 | struct tcphdr *nth; | ||
| 979 | unsigned int tcp_hdr_size; | ||
| 980 | |||
| 981 | iph = ipv6_hdr(skb); | ||
| 982 | |||
| 983 | tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts); | ||
| 984 | nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER, | ||
| 985 | GFP_ATOMIC); | ||
| 986 | if (!nskb) | ||
| 987 | return; | ||
| 988 | skb_reserve(nskb, MAX_TCP_HEADER); | ||
| 989 | |||
| 990 | niph = synproxy_build_ip_ipv6(net, nskb, &iph->daddr, &iph->saddr); | ||
| 991 | |||
| 992 | skb_reset_transport_header(nskb); | ||
| 993 | nth = skb_put(nskb, tcp_hdr_size); | ||
| 994 | nth->source = th->dest; | ||
| 995 | nth->dest = th->source; | ||
| 996 | nth->seq = htonl(ntohl(th->ack_seq)); | ||
| 997 | nth->ack_seq = htonl(ntohl(th->seq) + 1); | ||
| 998 | tcp_flag_word(nth) = TCP_FLAG_ACK; | ||
| 999 | nth->doff = tcp_hdr_size / 4; | ||
| 1000 | nth->window = htons(state->seen[IP_CT_DIR_ORIGINAL].td_maxwin); | ||
| 1001 | nth->check = 0; | ||
| 1002 | nth->urg_ptr = 0; | ||
| 1003 | |||
| 1004 | synproxy_build_options(nth, opts); | ||
| 1005 | |||
| 1006 | synproxy_send_tcp_ipv6(net, skb, nskb, NULL, 0, niph, nth, | ||
| 1007 | tcp_hdr_size); | ||
| 1008 | } | ||
| 1009 | |||
| 1010 | static void | ||
| 1011 | synproxy_send_client_ack_ipv6(struct net *net, const struct sk_buff *skb, | ||
| 1012 | const struct tcphdr *th, | ||
| 1013 | const struct synproxy_options *opts) | ||
| 1014 | { | ||
| 1015 | struct sk_buff *nskb; | ||
| 1016 | struct ipv6hdr *iph, *niph; | ||
| 1017 | struct tcphdr *nth; | ||
| 1018 | unsigned int tcp_hdr_size; | ||
| 1019 | |||
| 1020 | iph = ipv6_hdr(skb); | ||
| 1021 | |||
| 1022 | tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts); | ||
| 1023 | nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER, | ||
| 1024 | GFP_ATOMIC); | ||
| 1025 | if (!nskb) | ||
| 1026 | return; | ||
| 1027 | skb_reserve(nskb, MAX_TCP_HEADER); | ||
| 1028 | |||
| 1029 | niph = synproxy_build_ip_ipv6(net, nskb, &iph->saddr, &iph->daddr); | ||
| 1030 | |||
| 1031 | skb_reset_transport_header(nskb); | ||
| 1032 | nth = skb_put(nskb, tcp_hdr_size); | ||
| 1033 | nth->source = th->source; | ||
| 1034 | nth->dest = th->dest; | ||
| 1035 | nth->seq = htonl(ntohl(th->seq) + 1); | ||
| 1036 | nth->ack_seq = th->ack_seq; | ||
| 1037 | tcp_flag_word(nth) = TCP_FLAG_ACK; | ||
| 1038 | nth->doff = tcp_hdr_size / 4; | ||
| 1039 | nth->window = htons(ntohs(th->window) >> opts->wscale); | ||
| 1040 | nth->check = 0; | ||
| 1041 | nth->urg_ptr = 0; | ||
| 1042 | |||
| 1043 | synproxy_build_options(nth, opts); | ||
| 1044 | |||
| 1045 | synproxy_send_tcp_ipv6(net, skb, nskb, skb_nfct(skb), | ||
| 1046 | IP_CT_ESTABLISHED_REPLY, niph, nth, | ||
| 1047 | tcp_hdr_size); | ||
| 1048 | } | ||
| 1049 | |||
| 1050 | bool | ||
| 1051 | synproxy_recv_client_ack_ipv6(struct net *net, | ||
| 1052 | const struct sk_buff *skb, | ||
| 1053 | const struct tcphdr *th, | ||
| 1054 | struct synproxy_options *opts, u32 recv_seq) | ||
| 1055 | { | ||
| 1056 | struct synproxy_net *snet = synproxy_pernet(net); | ||
| 1057 | int mss; | ||
| 1058 | |||
| 1059 | mss = __cookie_v6_check(ipv6_hdr(skb), th, ntohl(th->ack_seq) - 1); | ||
| 1060 | if (mss == 0) { | ||
| 1061 | this_cpu_inc(snet->stats->cookie_invalid); | ||
| 1062 | return false; | ||
| 1063 | } | ||
| 1064 | |||
| 1065 | this_cpu_inc(snet->stats->cookie_valid); | ||
| 1066 | opts->mss = mss; | ||
| 1067 | opts->options |= NF_SYNPROXY_OPT_MSS; | ||
| 1068 | |||
| 1069 | if (opts->options & NF_SYNPROXY_OPT_TIMESTAMP) | ||
| 1070 | synproxy_check_timestamp_cookie(opts); | ||
| 1071 | |||
| 1072 | synproxy_send_server_syn_ipv6(net, skb, th, opts, recv_seq); | ||
| 1073 | return true; | ||
| 1074 | } | ||
| 1075 | EXPORT_SYMBOL_GPL(synproxy_recv_client_ack_ipv6); | ||
| 1076 | |||
| 1077 | unsigned int | ||
| 1078 | ipv6_synproxy_hook(void *priv, struct sk_buff *skb, | ||
| 1079 | const struct nf_hook_state *nhs) | ||
| 1080 | { | ||
| 1081 | struct net *net = nhs->net; | ||
| 1082 | struct synproxy_net *snet = synproxy_pernet(net); | ||
| 1083 | enum ip_conntrack_info ctinfo; | ||
| 1084 | struct nf_conn *ct; | ||
| 1085 | struct nf_conn_synproxy *synproxy; | ||
| 1086 | struct synproxy_options opts = {}; | ||
| 1087 | const struct ip_ct_tcp *state; | ||
| 1088 | struct tcphdr *th, _th; | ||
| 1089 | __be16 frag_off; | ||
| 1090 | u8 nexthdr; | ||
| 1091 | int thoff; | ||
| 1092 | |||
| 1093 | ct = nf_ct_get(skb, &ctinfo); | ||
| 1094 | if (!ct) | ||
| 1095 | return NF_ACCEPT; | ||
| 1096 | |||
| 1097 | synproxy = nfct_synproxy(ct); | ||
| 1098 | if (!synproxy) | ||
| 1099 | return NF_ACCEPT; | ||
| 1100 | |||
| 1101 | if (nf_is_loopback_packet(skb)) | ||
| 1102 | return NF_ACCEPT; | ||
| 1103 | |||
| 1104 | nexthdr = ipv6_hdr(skb)->nexthdr; | ||
| 1105 | thoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr, | ||
| 1106 | &frag_off); | ||
| 1107 | if (thoff < 0 || nexthdr != IPPROTO_TCP) | ||
| 1108 | return NF_ACCEPT; | ||
| 1109 | |||
| 1110 | th = skb_header_pointer(skb, thoff, sizeof(_th), &_th); | ||
| 1111 | if (!th) | ||
| 1112 | return NF_DROP; | ||
| 1113 | |||
| 1114 | state = &ct->proto.tcp; | ||
| 1115 | switch (state->state) { | ||
| 1116 | case TCP_CONNTRACK_CLOSE: | ||
| 1117 | if (th->rst && !test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) { | ||
| 1118 | nf_ct_seqadj_init(ct, ctinfo, synproxy->isn - | ||
| 1119 | ntohl(th->seq) + 1); | ||
| 1120 | break; | ||
| 1121 | } | ||
| 1122 | |||
| 1123 | if (!th->syn || th->ack || | ||
| 1124 | CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL) | ||
| 1125 | break; | ||
| 1126 | |||
| 1127 | /* Reopened connection - reset the sequence number and timestamp | ||
| 1128 | * adjustments, they will get initialized once the connection is | ||
| 1129 | * reestablished. | ||
| 1130 | */ | ||
| 1131 | nf_ct_seqadj_init(ct, ctinfo, 0); | ||
| 1132 | synproxy->tsoff = 0; | ||
| 1133 | this_cpu_inc(snet->stats->conn_reopened); | ||
| 1134 | |||
| 1135 | /* fall through */ | ||
| 1136 | case TCP_CONNTRACK_SYN_SENT: | ||
| 1137 | if (!synproxy_parse_options(skb, thoff, th, &opts)) | ||
| 1138 | return NF_DROP; | ||
| 1139 | |||
| 1140 | if (!th->syn && th->ack && | ||
| 1141 | CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) { | ||
| 1142 | /* Keep-Alives are sent with SEG.SEQ = SND.NXT-1, | ||
| 1143 | * therefore we need to add 1 to make the SYN sequence | ||
| 1144 | * number match the one of first SYN. | ||
| 1145 | */ | ||
| 1146 | if (synproxy_recv_client_ack_ipv6(net, skb, th, &opts, | ||
| 1147 | ntohl(th->seq) + 1)) { | ||
| 1148 | this_cpu_inc(snet->stats->cookie_retrans); | ||
| 1149 | consume_skb(skb); | ||
| 1150 | return NF_STOLEN; | ||
| 1151 | } else { | ||
| 1152 | return NF_DROP; | ||
| 1153 | } | ||
| 1154 | } | ||
| 1155 | |||
| 1156 | synproxy->isn = ntohl(th->ack_seq); | ||
| 1157 | if (opts.options & NF_SYNPROXY_OPT_TIMESTAMP) | ||
| 1158 | synproxy->its = opts.tsecr; | ||
| 1159 | |||
| 1160 | nf_conntrack_event_cache(IPCT_SYNPROXY, ct); | ||
| 1161 | break; | ||
| 1162 | case TCP_CONNTRACK_SYN_RECV: | ||
| 1163 | if (!th->syn || !th->ack) | ||
| 1164 | break; | ||
| 1165 | |||
| 1166 | if (!synproxy_parse_options(skb, thoff, th, &opts)) | ||
| 1167 | return NF_DROP; | ||
| 1168 | |||
| 1169 | if (opts.options & NF_SYNPROXY_OPT_TIMESTAMP) { | ||
| 1170 | synproxy->tsoff = opts.tsval - synproxy->its; | ||
| 1171 | nf_conntrack_event_cache(IPCT_SYNPROXY, ct); | ||
| 1172 | } | ||
| 1173 | |||
| 1174 | opts.options &= ~(NF_SYNPROXY_OPT_MSS | | ||
| 1175 | NF_SYNPROXY_OPT_WSCALE | | ||
| 1176 | NF_SYNPROXY_OPT_SACK_PERM); | ||
| 1177 | |||
| 1178 | swap(opts.tsval, opts.tsecr); | ||
| 1179 | synproxy_send_server_ack_ipv6(net, state, skb, th, &opts); | ||
| 1180 | |||
| 1181 | nf_ct_seqadj_init(ct, ctinfo, synproxy->isn - ntohl(th->seq)); | ||
| 1182 | nf_conntrack_event_cache(IPCT_SEQADJ, ct); | ||
| 1183 | |||
| 1184 | swap(opts.tsval, opts.tsecr); | ||
| 1185 | synproxy_send_client_ack_ipv6(net, skb, th, &opts); | ||
| 1186 | |||
| 1187 | consume_skb(skb); | ||
| 1188 | return NF_STOLEN; | ||
| 1189 | default: | ||
| 1190 | break; | ||
| 1191 | } | ||
| 1192 | |||
| 1193 | synproxy_tstamp_adjust(skb, thoff, th, ct, ctinfo, synproxy); | ||
| 1194 | return NF_ACCEPT; | ||
| 1195 | } | ||
| 1196 | EXPORT_SYMBOL_GPL(ipv6_synproxy_hook); | ||
| 1197 | |||
| 1198 | static const struct nf_hook_ops ipv6_synproxy_ops[] = { | ||
| 1199 | { | ||
| 1200 | .hook = ipv6_synproxy_hook, | ||
| 1201 | .pf = NFPROTO_IPV6, | ||
| 1202 | .hooknum = NF_INET_LOCAL_IN, | ||
| 1203 | .priority = NF_IP_PRI_CONNTRACK_CONFIRM - 1, | ||
| 1204 | }, | ||
| 1205 | { | ||
| 1206 | .hook = ipv6_synproxy_hook, | ||
| 1207 | .pf = NFPROTO_IPV6, | ||
| 1208 | .hooknum = NF_INET_POST_ROUTING, | ||
| 1209 | .priority = NF_IP_PRI_CONNTRACK_CONFIRM - 1, | ||
| 1210 | }, | ||
| 1211 | }; | ||
| 1212 | |||
| 1213 | int | ||
| 1214 | nf_synproxy_ipv6_init(struct synproxy_net *snet, struct net *net) | ||
| 1215 | { | ||
| 1216 | int err; | ||
| 1217 | |||
| 1218 | if (snet->hook_ref6 == 0) { | ||
| 1219 | err = nf_register_net_hooks(net, ipv6_synproxy_ops, | ||
| 1220 | ARRAY_SIZE(ipv6_synproxy_ops)); | ||
| 1221 | if (err) | ||
| 1222 | return err; | ||
| 1223 | } | ||
| 1224 | |||
| 1225 | snet->hook_ref6++; | ||
| 1226 | return err; | ||
| 1227 | } | ||
| 1228 | EXPORT_SYMBOL_GPL(nf_synproxy_ipv6_init); | ||
| 1229 | |||
| 1230 | void | ||
| 1231 | nf_synproxy_ipv6_fini(struct synproxy_net *snet, struct net *net) | ||
| 1232 | { | ||
| 1233 | snet->hook_ref6--; | ||
| 1234 | if (snet->hook_ref6 == 0) | ||
| 1235 | nf_unregister_net_hooks(net, ipv6_synproxy_ops, | ||
| 1236 | ARRAY_SIZE(ipv6_synproxy_ops)); | ||
| 1237 | } | ||
| 1238 | EXPORT_SYMBOL_GPL(nf_synproxy_ipv6_fini); | ||
| 1239 | #endif /* CONFIG_IPV6 */ | ||
| 1240 | |||
| 419 | MODULE_LICENSE("GPL"); | 1241 | MODULE_LICENSE("GPL"); |
| 420 | MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); | 1242 | MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); |
