├── Makefile ├── README ├── TODO ├── ipt_SYNPROXY.c ├── ipt_SYNPROXY.h ├── libipt_SYNPROXY.c ├── synproxy-full-2.6.36.patch ├── synproxy-xmit-3.2.39.patch ├── synproxy.diff └── tst.sh /Makefile: -------------------------------------------------------------------------------- 1 | ifneq ($(KERNELRELEASE),) 2 | obj-m += ipt_SYNPROXY.o 3 | else 4 | KERNELDIR ?= /lib/modules/$(shell uname -r)/build 5 | PWD := $(shell pwd) 6 | 7 | #all: ipt_SYNPROXY.ko libipt_SYNPROXY.so 8 | all: libipt_SYNPROXY.so 9 | 10 | #ipt_SYNPROXY.ko: ipt_SYNPROXY.c 11 | # $(MAKE) -C $(KERNELDIR) M=$(PWD) modules 12 | 13 | libipt_SYNPROXY.so: libipt_SYNPROXY.c 14 | $(CC) -Wall -fPIC -shared -o $@ $^ 15 | 16 | clean: 17 | $(MAKE) -C $(KERNELDIR) M=$(PWD) clean 18 | $(RM) -f *.so *.o 19 | endif 20 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | synproxy 2 | ======== 3 | 4 | Based on https://github.com/xiaosuo/xiaosuo/tree/master/synproxy 5 | 6 | synproxy: 7 | It is an implementation of SYNPROXY based on netfilter of Linux. An iptables raw 8 | target SYNPROXY is implemented. In order to use it, you must get the newest 9 | Linux kernel source code, and follow the following steps: 10 | cd linux 11 | patch -p1 < path-to-synproxy.diff 12 | /* you need select raw table, ip_conntrack and syncookies */ 13 | make && make install modules_install && reboot 14 | cd path-to-synproxy 15 | make 16 | cp libipt_SYNPROXY.so path-to-iptables-shared-module 17 | insmod ipt_SYNPROXY.ko 18 | If there isn't any error in the above steps, congratulations, and you can play 19 | with it. For example, you want to protect the local HTTP server from the 20 | SYN-flood attacks: 21 | iptables -t nat -A OUTPUT -p tcp --dport 80 -d XXX.XXX.XXX.XXX -j DNAT --to-destination XXX.XXX.XXX.XXX:80 22 | iptables -t raw -A PREROUTING -p tcp -d XXX.XXX.XXX.XXX --dport 80 --tcp-flags SYN,ACK,RST,FIN SYN -j SYNPROXY 23 | 24 | 25 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | 0. test if synproxy works with bridge, fireware(SNAT, DNAT). 2 | 1. support timeout parameter: 3 | 1.1 initial window size should be mss. 4 | 1.2 relay connection when timeouts. 5 | 1.3 ACK server with data, NF_STOLEN packet for later acking, reuse. 6 | 1.4 timeout is only supported when MSS exists. 7 | 2. synproxy matcher. 8 | 2.1 match data, such as http-url. 9 | 2.2 then it can work with NAT. defer SNAT or DNAT. 10 | 3. stateful synproxy. 11 | 4. zero copy, skb_recycle_check(), NF_STOLEN. 12 | 5. don't always use ip_local_out(). 13 | -------------------------------------------------------------------------------- /ipt_SYNPROXY.c: -------------------------------------------------------------------------------- 1 | /* (C) 2010- Changli Gao 2 | * 3 | * This program is free software; you can redistribute it and/or modify 4 | * it under the terms of the GNU General Public License version 2 as 5 | * published by the Free Software Foundation. 6 | * 7 | * It bases on ipt_REJECT.c 8 | */ 9 | #define DEBUG 10 | #define pr_fmt(fmt) "SYNPROXY: " fmt 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | MODULE_LICENSE("GPL"); 29 | MODULE_AUTHOR("Changli Gao "); 30 | MODULE_DESCRIPTION("Xtables: \"SYNPROXY\" target for IPv4"); 31 | 32 | /* depends on nf_conntrack_proto_tcp and syncookies */ 33 | 34 | enum { 35 | TCP_SEND_FLAG_NOTRACE = 0x1, 36 | TCP_SEND_FLAG_SYNCOOKIE = 0x2, 37 | TCP_SEND_FLAG_ACK2SYN = 0x4, 38 | }; 39 | 40 | struct syn_proxy_state { 41 | u16 seq_inited; 42 | __be16 window; 43 | u32 seq_diff; 44 | }; 45 | 46 | static int get_mtu(const struct dst_entry *dst) 47 | { 48 | int mtu; 49 | 50 | mtu = dst_mtu(dst); 51 | if (mtu) 52 | return mtu; 53 | 54 | return dst->dev ? dst->dev->mtu : 0; 55 | } 56 | 57 | static int get_advmss(const struct dst_entry *dst) 58 | { 59 | int advmss; 60 | 61 | advmss = dst_metric(dst, RTAX_ADVMSS); 62 | if (advmss) 63 | return advmss; 64 | advmss = get_mtu(dst); 65 | if (advmss) 66 | return advmss - (sizeof(struct iphdr) + sizeof(struct tcphdr)); 67 | 68 | return TCP_MSS_DEFAULT; 69 | } 70 | 71 | static int syn_proxy_route(struct sk_buff *skb, struct net *net, u16 *pmss) 72 | { 73 | const struct iphdr *iph = ip_hdr(skb); 74 | struct rtable *rt; 75 | struct flowi fl = {}; 76 | unsigned int type; 77 | int flags = 0; 78 | int err; 79 | u16 mss; 80 | 81 | type = inet_addr_type(net, iph->saddr); 82 | if (type != RTN_LOCAL) { 83 | type = inet_addr_type(net, iph->daddr); 84 | if (type == RTN_LOCAL) 85 | flags |= FLOWI_FLAG_ANYSRC; 86 | } 87 | 88 | if (type == RTN_LOCAL) { 89 | fl.nl_u.ip4_u.daddr = iph->daddr; 90 | fl.nl_u.ip4_u.saddr = iph->saddr; 91 | fl.nl_u.ip4_u.tos = RT_TOS(iph->tos); 92 | fl.flags = flags; 93 | if ((err = ip_route_output_key(net, &rt, &fl)) != 0) 94 | goto out; 95 | 96 | skb_dst_set(skb, &rt->u.dst); 97 | } else { 98 | /* non-local src, find valid iif to satisfy 99 | * rp-filter when calling ip_route_input. */ 100 | fl.nl_u.ip4_u.daddr = iph->saddr; 101 | if ((err = ip_route_output_key(net, &rt, &fl)) != 0) 102 | goto out; 103 | 104 | if ((err = ip_route_input(skb, iph->daddr, iph->saddr, 105 | RT_TOS(iph->tos), 106 | rt->u.dst.dev)) != 0) { 107 | dst_release(&rt->u.dst); 108 | goto out; 109 | } 110 | if (pmss) { 111 | mss = get_advmss(&rt->u.dst); 112 | if (*pmss > mss) 113 | *pmss = mss; 114 | } 115 | dst_release(&rt->u.dst); 116 | } 117 | 118 | err = skb_dst(skb)->error; 119 | if (!err && pmss) { 120 | mss = get_advmss(skb_dst(skb)); 121 | if (*pmss > mss) 122 | *pmss = mss; 123 | } 124 | 125 | out: 126 | return err; 127 | } 128 | 129 | static int tcp_send(__be32 src, __be32 dst, __be16 sport, __be16 dport, 130 | u32 seq, u32 ack_seq, __be16 window, u16 mss, 131 | __be32 tcp_flags, u8 tos, struct net_device *dev, int flags, 132 | struct sk_buff *oskb) 133 | { 134 | struct sk_buff *skb; 135 | struct iphdr *iph; 136 | struct tcphdr *th; 137 | int err, len; 138 | 139 | len = sizeof(*th); 140 | if (mss) 141 | len += TCPOLEN_MSS; 142 | 143 | skb = NULL; 144 | /* caller must give me a large enough oskb */ 145 | if (oskb) { 146 | unsigned char *odata = oskb->data; 147 | 148 | if (skb_recycle_check(oskb, 0)) { 149 | oskb->data = odata; 150 | skb_reset_tail_pointer(oskb); 151 | skb = oskb; 152 | pr_debug("recycle skb\n"); 153 | } 154 | } 155 | if (!skb) { 156 | skb = alloc_skb(LL_MAX_HEADER + sizeof(*iph) + len, GFP_ATOMIC); 157 | if (!skb) { 158 | err = -ENOMEM; 159 | goto out; 160 | } 161 | skb_reserve(skb, LL_MAX_HEADER); 162 | } 163 | 164 | skb_reset_network_header(skb); 165 | if (!(flags & TCP_SEND_FLAG_ACK2SYN) || skb != oskb) { 166 | iph = (struct iphdr *)skb_put(skb, sizeof(*iph)); 167 | iph->version = 4; 168 | iph->ihl = sizeof(*iph) / 4; 169 | iph->tos = tos; 170 | /* tot_len is set in ip_local_out() */ 171 | iph->id = 0; 172 | iph->frag_off = htons(IP_DF); 173 | iph->protocol = IPPROTO_TCP; 174 | iph->saddr = src; 175 | iph->daddr = dst; 176 | th = (struct tcphdr *)skb_put(skb, len); 177 | th->source = sport; 178 | th->dest = dport; 179 | } else { 180 | iph = (struct iphdr*)skb->data; 181 | iph->id = 0; 182 | iph->frag_off = htons(IP_DF); 183 | skb_put(skb, iph->ihl * 4 + len); 184 | th = (struct tcphdr*)(skb->data + iph->ihl * 4); 185 | } 186 | 187 | th->seq = htonl(seq); 188 | th->ack_seq = htonl(ack_seq); 189 | tcp_flag_word(th) = tcp_flags; 190 | th->doff = len / 4; 191 | th->window = window; 192 | th->urg_ptr = 0; 193 | 194 | skb->protocol = htons(ETH_P_IP); 195 | if ((flags & TCP_SEND_FLAG_SYNCOOKIE) && mss) 196 | err = syn_proxy_route(skb, dev_net(dev), &mss); 197 | else 198 | err = syn_proxy_route(skb, dev_net(dev), NULL); 199 | if (err) 200 | goto err_out; 201 | 202 | if ((flags & TCP_SEND_FLAG_SYNCOOKIE)) { 203 | if (mss) { 204 | th->seq = htonl(__cookie_v4_init_sequence(dst, src, 205 | dport, sport, 206 | ack_seq - 1, 207 | &mss)); 208 | } else { 209 | mss = TCP_MSS_DEFAULT; 210 | th->seq = htonl(__cookie_v4_init_sequence(dst, src, 211 | dport, sport, 212 | ack_seq - 1, 213 | &mss)); 214 | mss = 0; 215 | } 216 | } 217 | 218 | if (mss) 219 | * (__force __be32 *)(th + 1) = htonl((TCPOPT_MSS << 24) | 220 | (TCPOLEN_MSS << 16) | 221 | mss); 222 | skb->ip_summed = CHECKSUM_PARTIAL; 223 | th->check = ~tcp_v4_check(len, src, dst, 0); 224 | skb->csum_start = (unsigned char *)th - skb->head; 225 | skb->csum_offset = offsetof(struct tcphdr, check); 226 | 227 | if (!(flags & TCP_SEND_FLAG_ACK2SYN) || skb != oskb) 228 | iph->ttl = dst_metric(skb_dst(skb), RTAX_HOPLIMIT); 229 | 230 | if (skb->len > get_mtu(skb_dst(skb))) { 231 | if (printk_ratelimit()) 232 | pr_warning("%s has smaller mtu: %d\n", 233 | skb_dst(skb)->dev->name, 234 | get_mtu(skb_dst(skb))); 235 | err = -EINVAL; 236 | goto err_out; 237 | } 238 | 239 | if ((flags & TCP_SEND_FLAG_NOTRACE)) { 240 | skb->nfct = &nf_conntrack_untracked.ct_general; 241 | skb->nfctinfo = IP_CT_NEW; 242 | nf_conntrack_get(skb->nfct); 243 | } 244 | 245 | pr_debug("ip_local_out: %pI4n:%hu -> %pI4n:%hu (seq=%u, " 246 | "ack_seq=%u mss=%hu flags=%x)\n", &src, ntohs(th->source), 247 | &dst, ntohs(th->dest), ntohl(th->seq), ack_seq, mss, 248 | ntohl(tcp_flags)); 249 | 250 | err = ip_local_out(skb); 251 | if (err > 0) 252 | err = net_xmit_errno(err); 253 | 254 | pr_debug("ip_local_out: return with %d\n", err); 255 | out: 256 | if (oskb && oskb != skb) 257 | kfree_skb(oskb); 258 | 259 | return err; 260 | 261 | err_out: 262 | kfree_skb(skb); 263 | goto out; 264 | } 265 | 266 | static int get_mss(u8 *data, int len) 267 | { 268 | u8 olen; 269 | 270 | while (len >= TCPOLEN_MSS) { 271 | switch (data[0]) { 272 | case TCPOPT_EOL: 273 | return 0; 274 | case TCPOPT_NOP: 275 | data++; 276 | len--; 277 | break; 278 | case TCPOPT_MSS: 279 | if (data[1] != TCPOLEN_MSS) 280 | return -EINVAL; 281 | return get_unaligned_be16(data + 2); 282 | default: 283 | olen = data[1]; 284 | if (olen < 2 || olen > len) 285 | return -EINVAL; 286 | data += olen; 287 | len -= olen; 288 | break; 289 | } 290 | } 291 | 292 | return 0; 293 | } 294 | 295 | static DEFINE_PER_CPU(struct syn_proxy_state, syn_proxy_state); 296 | 297 | /* syn_proxy_pre isn't under the protection of nf_conntrack_proto_tcp.c */ 298 | static int syn_proxy_pre(struct sk_buff *skb, struct nf_conn *ct, 299 | struct tcphdr *th) 300 | { 301 | struct syn_proxy_state *state; 302 | struct iphdr *iph; 303 | 304 | /* only support IPv4 now */ 305 | iph = ip_hdr(skb); 306 | if (iph->version != 4) 307 | return NF_ACCEPT; 308 | 309 | if (!ct || !nf_ct_is_confirmed(ct)) { 310 | int ret; 311 | 312 | if (!th->syn && th->ack) { 313 | u16 mss; 314 | struct sk_buff *rec_skb; 315 | 316 | mss = cookie_v4_check_sequence(iph, th, 317 | ntohl(th->ack_seq) - 1); 318 | if (!mss) 319 | return NF_ACCEPT; 320 | 321 | pr_debug("%pI4n:%hu -> %pI4n:%hu(mss=%hu)\n", 322 | &iph->saddr, ntohs(th->source), 323 | &iph->daddr, ntohs(th->dest), mss); 324 | 325 | if (skb_tailroom(skb) < TCPOLEN_MSS && 326 | skb->len < iph->ihl * 4 + sizeof(*th) + TCPOLEN_MSS) 327 | rec_skb = NULL; 328 | else 329 | rec_skb = skb; 330 | 331 | local_bh_disable(); 332 | state = &__get_cpu_var(syn_proxy_state); 333 | state->seq_inited = 1; 334 | state->window = th->window; 335 | state->seq_diff = ntohl(th->ack_seq) - 1; 336 | if (rec_skb) 337 | tcp_send(iph->saddr, iph->daddr, 0, 0, 338 | ntohl(th->seq) - 1, 0, th->window, 339 | mss, TCP_FLAG_SYN, 0, skb->dev, 340 | TCP_SEND_FLAG_ACK2SYN, rec_skb); 341 | else 342 | tcp_send(iph->saddr, iph->daddr, th->source, 343 | th->dest, ntohl(th->seq) - 1, 0, 344 | th->window, mss, TCP_FLAG_SYN, 345 | iph->tos, skb->dev, 0, NULL); 346 | state->seq_inited = 0; 347 | local_bh_enable(); 348 | 349 | if (!rec_skb) 350 | kfree_skb(skb); 351 | 352 | return NF_STOLEN; 353 | } 354 | 355 | if (!ct || !th->syn || th->ack) 356 | return NF_ACCEPT; 357 | 358 | ret = NF_ACCEPT; 359 | local_bh_disable(); 360 | state = &__get_cpu_var(syn_proxy_state); 361 | if (state->seq_inited) { 362 | struct syn_proxy_state *nstate; 363 | 364 | nstate = nf_ct_ext_add(ct, NF_CT_EXT_SYNPROXY, 365 | GFP_ATOMIC); 366 | if (nstate != NULL) { 367 | nstate->seq_inited = 0; 368 | nstate->window = state->window; 369 | nstate->seq_diff = state->seq_diff; 370 | pr_debug("seq_diff: %u\n", nstate->seq_diff); 371 | } else { 372 | ret = NF_DROP; 373 | } 374 | } 375 | local_bh_enable(); 376 | 377 | return ret; 378 | } 379 | 380 | state = nf_ct_ext_find(ct, NF_CT_EXT_SYNPROXY); 381 | if (!state) 382 | return NF_ACCEPT; 383 | 384 | if (CTINFO2DIR(skb->nfctinfo) == IP_CT_DIR_ORIGINAL) { 385 | __be32 newack; 386 | 387 | /* don't need to mangle duplicate SYN packets */ 388 | if (th->syn && !th->ack) 389 | return NF_ACCEPT; 390 | if (!skb_make_writable(skb, ip_hdrlen(skb) + sizeof(*th))) 391 | return NF_DROP; 392 | th = (struct tcphdr *)(skb->data + ip_hdrlen(skb)); 393 | newack = htonl(ntohl(th->ack_seq) - state->seq_diff); 394 | inet_proto_csum_replace4(&th->check, skb, th->ack_seq, newack, 395 | 0); 396 | pr_debug("alter ack seq: %u -> %u\n", 397 | ntohl(th->ack_seq), ntohl(newack)); 398 | th->ack_seq = newack; 399 | } else { 400 | /* Simultaneous open ? Oh, no. The connection between 401 | * client and us is established. */ 402 | if (th->syn && !th->ack) 403 | return NF_DROP; 404 | } 405 | 406 | return NF_ACCEPT; 407 | } 408 | 409 | static int syn_proxy_mangle_pkt(struct sk_buff *skb, struct iphdr *iph, 410 | struct tcphdr *th, u32 seq_diff) 411 | { 412 | __be32 new; 413 | int olen; 414 | 415 | if (skb->len < (iph->ihl + th->doff) * 4) 416 | return NF_DROP; 417 | if (!skb_make_writable(skb, (iph->ihl + th->doff) * 4)) 418 | return NF_DROP; 419 | iph = (struct iphdr *)(skb->data); 420 | th = (struct tcphdr *)(skb->data + iph->ihl * 4); 421 | 422 | new = tcp_flag_word(th) & (~TCP_FLAG_SYN); 423 | inet_proto_csum_replace4(&th->check, skb, tcp_flag_word(th), new, 0); 424 | tcp_flag_word(th) = new; 425 | 426 | new = htonl(ntohl(th->seq) + seq_diff); 427 | inet_proto_csum_replace4(&th->check, skb, th->seq, new, 0); 428 | pr_debug("alter seq: %u -> %u\n", ntohl(th->seq), ntohl(new)); 429 | th->seq = new; 430 | 431 | olen = th->doff - sizeof(*th) / 4; 432 | if (olen) { 433 | __be32 *opt; 434 | 435 | opt = (__force __be32 *)(th + 1); 436 | #define TCPOPT_EOL_WORD ((TCPOPT_EOL << 24) + (TCPOPT_EOL << 16) + \ 437 | (TCPOPT_EOL << 8) + TCPOPT_EOL) 438 | inet_proto_csum_replace4(&th->check, skb, *opt, TCPOPT_EOL_WORD, 439 | 0); 440 | *opt = TCPOPT_EOL_WORD; 441 | } 442 | 443 | return NF_ACCEPT; 444 | } 445 | 446 | static int syn_proxy_post(struct sk_buff *skb, struct nf_conn *ct, 447 | enum ip_conntrack_info ctinfo) 448 | { 449 | struct syn_proxy_state *state; 450 | struct iphdr *iph; 451 | struct tcphdr *th; 452 | 453 | /* untraced packets don't have NF_CT_EXT_SYNPROXY ext, as they don't 454 | * enter syn_proxy_pre() */ 455 | state = nf_ct_ext_find(ct, NF_CT_EXT_SYNPROXY); 456 | if (state == NULL) 457 | return NF_ACCEPT; 458 | 459 | iph = ip_hdr(skb); 460 | if (!skb_make_writable(skb, iph->ihl * 4 + sizeof(*th))) 461 | return NF_DROP; 462 | th = (struct tcphdr *)(skb->data + iph->ihl * 4); 463 | if (!state->seq_inited) { 464 | if (th->syn) { 465 | /* It must be from original direction, as the ones 466 | * from the other side are dropped in function 467 | * syn_proxy_pre() */ 468 | if (!th->ack) 469 | return NF_ACCEPT; 470 | 471 | pr_debug("SYN-ACK %pI4n:%hu -> %pI4n:%hu " 472 | "(seq=%u ack_seq=%u)\n", 473 | &iph->saddr, ntohs(th->source), &iph->daddr, 474 | ntohs(th->dest), ntohl(th->seq), 475 | ntohl(th->ack_seq)); 476 | 477 | /* SYN-ACK from reply direction with the protection 478 | * of conntrack */ 479 | spin_lock_bh(&ct->lock); 480 | if (!state->seq_inited) { 481 | state->seq_inited = 1; 482 | pr_debug("update seq_diff %u -> %u\n", 483 | state->seq_diff, 484 | state->seq_diff - ntohl(th->seq)); 485 | state->seq_diff -= ntohl(th->seq); 486 | } 487 | spin_unlock_bh(&ct->lock); 488 | tcp_send(iph->daddr, iph->saddr, th->dest, th->source, 489 | ntohl(th->ack_seq), 490 | ntohl(th->seq) + 1 + state->seq_diff, 491 | state->window, 0, TCP_FLAG_ACK, iph->tos, 492 | skb->dev, 0, NULL); 493 | 494 | return syn_proxy_mangle_pkt(skb, iph, th, 495 | state->seq_diff + 1); 496 | } else { 497 | __be32 newseq; 498 | 499 | if (!th->rst) 500 | return NF_ACCEPT; 501 | newseq = htonl(state->seq_diff + 1); 502 | inet_proto_csum_replace4(&th->check, skb, th->seq, 503 | newseq, 0); 504 | pr_debug("alter RST seq: %u -> %u\n", 505 | ntohl(th->seq), ntohl(newseq)); 506 | th->seq = newseq; 507 | 508 | return NF_ACCEPT; 509 | } 510 | } 511 | 512 | /* ct should be in ESTABLISHED state, but if the ack packets from 513 | * us are lost. */ 514 | if (th->syn) { 515 | if (!th->ack) 516 | return NF_ACCEPT; 517 | 518 | tcp_send(iph->daddr, iph->saddr, th->dest, th->source, 519 | ntohl(th->ack_seq), 520 | ntohl(th->seq) + 1 + state->seq_diff, 521 | state->window, 0, TCP_FLAG_ACK, iph->tos, 522 | skb->dev, 0, NULL); 523 | 524 | return syn_proxy_mangle_pkt(skb, iph, th, state->seq_diff + 1); 525 | } 526 | 527 | if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) { 528 | __be32 newseq; 529 | 530 | newseq = htonl(ntohl(th->seq) + state->seq_diff); 531 | inet_proto_csum_replace4(&th->check, skb, th->seq, newseq, 0); 532 | pr_debug("alter seq: %u -> %u\n", ntohl(th->seq), 533 | ntohl(newseq)); 534 | th->seq = newseq; 535 | } 536 | 537 | return NF_ACCEPT; 538 | } 539 | 540 | static int tcp_process(struct sk_buff *skb) 541 | { 542 | const struct iphdr *iph; 543 | const struct tcphdr *th; 544 | int err; 545 | u16 mss; 546 | 547 | iph = ip_hdr(skb); 548 | if (iph->frag_off & htons(IP_OFFSET)) 549 | goto out; 550 | if (!pskb_may_pull(skb, iph->ihl * 4 + sizeof(*th))) 551 | goto out; 552 | th = (const struct tcphdr *)(skb->data + iph->ihl * 4); 553 | if (th->fin || th->rst || th->ack || !th->syn) 554 | goto out; 555 | 556 | if (nf_ip_checksum(skb, NF_INET_PRE_ROUTING, iph->ihl * 4, IPPROTO_TCP)) 557 | goto out; 558 | mss = 0; 559 | if (th->doff > sizeof(*th) / 4) { 560 | if (!pskb_may_pull(skb, (iph->ihl + th->doff) * 4)) 561 | goto out; 562 | err = get_mss((u8 *)(th + 1), th->doff * 4 - sizeof(*th)); 563 | if (err < 0) 564 | goto out; 565 | if (err != 0) 566 | mss = err; 567 | } else if (th->doff != sizeof(*th) / 4) 568 | goto out; 569 | 570 | tcp_send(iph->daddr, iph->saddr, th->dest, th->source, 0, 571 | ntohl(th->seq) + 1, 0, mss, TCP_FLAG_SYN | TCP_FLAG_ACK, 572 | iph->tos, skb->dev, 573 | TCP_SEND_FLAG_NOTRACE | TCP_SEND_FLAG_SYNCOOKIE, skb); 574 | 575 | return NF_STOLEN; 576 | 577 | out: 578 | return NF_DROP; 579 | } 580 | 581 | static unsigned int synproxy_tg(struct sk_buff *skb, 582 | const struct xt_action_param *par) 583 | { 584 | struct nf_conn *ct; 585 | enum ip_conntrack_info ctinfo; 586 | int ret; 587 | 588 | /* received from lo */ 589 | ct = nf_ct_get(skb, &ctinfo); 590 | if (ct) 591 | return IPT_CONTINUE; 592 | 593 | local_bh_disable(); 594 | if (!__get_cpu_var(syn_proxy_state).seq_inited) 595 | ret = tcp_process(skb); 596 | else 597 | ret = IPT_CONTINUE; 598 | local_bh_enable(); 599 | 600 | return ret; 601 | } 602 | 603 | static struct xt_target synproxy_tg_reg __read_mostly = { 604 | .name = "SYNPROXY", 605 | .family = NFPROTO_IPV4, 606 | .target = synproxy_tg, 607 | .table = "raw", 608 | .hooks = (1 << NF_INET_PRE_ROUTING), 609 | .proto = IPPROTO_TCP, 610 | .me = THIS_MODULE, 611 | }; 612 | 613 | static struct nf_ct_ext_type syn_proxy_state_ext __read_mostly = { 614 | .len = sizeof(struct syn_proxy_state), 615 | .align = __alignof__(struct syn_proxy_state), 616 | .id = NF_CT_EXT_SYNPROXY, 617 | }; 618 | 619 | static int __init synproxy_tg_init(void) 620 | { 621 | int err, cpu; 622 | 623 | for_each_possible_cpu(cpu) 624 | per_cpu(syn_proxy_state, cpu).seq_inited = 0; 625 | rcu_assign_pointer(syn_proxy_pre_hook, syn_proxy_pre); 626 | rcu_assign_pointer(syn_proxy_post_hook, syn_proxy_post); 627 | err = nf_ct_extend_register(&syn_proxy_state_ext); 628 | if (err) 629 | goto err_out; 630 | err = xt_register_target(&synproxy_tg_reg); 631 | if (err) 632 | goto err_out2; 633 | 634 | return err; 635 | 636 | err_out2: 637 | nf_ct_extend_unregister(&syn_proxy_state_ext); 638 | err_out: 639 | rcu_assign_pointer(syn_proxy_post_hook, NULL); 640 | rcu_assign_pointer(syn_proxy_pre_hook, NULL); 641 | rcu_barrier(); 642 | 643 | return err; 644 | } 645 | 646 | static void __exit synproxy_tg_exit(void) 647 | { 648 | xt_unregister_target(&synproxy_tg_reg); 649 | nf_ct_extend_unregister(&syn_proxy_state_ext); 650 | rcu_assign_pointer(syn_proxy_post_hook, NULL); 651 | rcu_assign_pointer(syn_proxy_pre_hook, NULL); 652 | rcu_barrier(); 653 | } 654 | 655 | module_init(synproxy_tg_init); 656 | module_exit(synproxy_tg_exit); 657 | -------------------------------------------------------------------------------- /ipt_SYNPROXY.h: -------------------------------------------------------------------------------- 1 | #ifndef __IPT_SYNPROXY_H 2 | #define __IPT_SYNPROXY_H 3 | 4 | struct ipt_synproxy_info { 5 | u_int32_t timeout; 6 | }; 7 | 8 | #endif /* __IPT_SYNPROXY_H */ 9 | -------------------------------------------------------------------------------- /libipt_SYNPROXY.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static void SYNPROXY_help(void) 5 | { 6 | printf( 7 | "SYNPROXY target options:\n" 8 | "no options\n"); 9 | } 10 | 11 | static struct xtables_target reject_tg_reg = { 12 | .name = "SYNPROXY", 13 | .version = XTABLES_VERSION, 14 | .family = PF_INET, 15 | .help = SYNPROXY_help, 16 | }; 17 | 18 | void _init(void) 19 | { 20 | xtables_register_target(&reject_tg_reg); 21 | } 22 | -------------------------------------------------------------------------------- /synproxy-full-2.6.36.patch: -------------------------------------------------------------------------------- 1 | Subject: [PATCH v3] netfilter: xtables target SYNPROXY 2 | Cc: "David S. Miller" 3 | Cc: Alexey Kuznetsov 4 | Cc: Jan Engelhardt 5 | Cc: Jozsef Kadlecsik 6 | Cc: "Pekka Savola (ipv6)" 7 | Cc: James Morris 8 | Cc: Hideaki YOSHIFUJI 9 | Cc: netfilter-devel@vger.kernel.org 10 | Cc: netdev@vger.kernel.org 11 | 12 | v3: 13 | fix the bug it can't work with bridge. 14 | 15 | netfilter: xtables target SYNPROXY. 16 | 17 | This patch implements an xtables target SYNPROXY. As the connection to the 18 | TCP server won't be established until the ACK from the client is received, it 19 | can protect the TCP server from the SYN-flood attacks. 20 | 21 | It works in the raw table of the PREROUTING chain, before conntracking system. 22 | Syncookies is used, so no new state is introduced into the conntracking system. 23 | In fact, until the first connection is established, conntracking system doesn't 24 | see any packets. So when there is a SYN-flood attack, conntracking system won't 25 | be busy on finding and deleting the un-assured ct. 26 | 27 | As the SYN-packet of the second connection request is sent locally, the DNAT 28 | rules which are in the PREROUTING chain should be moved to the OUTPUT chain. 29 | 30 | Signed-off-by: Changli Gao 31 | ---- 32 | include/net/netfilter/nf_conntrack.h | 10 33 | include/net/netfilter/nf_conntrack_core.h | 21 34 | include/net/netfilter/nf_conntrack_extend.h | 2 35 | include/net/tcp.h | 7 36 | net/ipv4/syncookies.c | 22 37 | net/ipv4/tcp_ipv4.c | 9 38 | net/netfilter/Kconfig | 17 39 | net/netfilter/Makefile | 1 40 | net/netfilter/nf_conntrack_core.c | 45 + 41 | net/netfilter/xt_SYNPROXY.c | 679 ++++++++++++++++++++++++++++ 42 | 10 files changed, 794 insertions(+), 19 deletions(-) 43 | diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h 44 | index e624dae..5e6d8e4 100644 45 | --- a/include/net/netfilter/nf_conntrack.h 46 | +++ b/include/net/netfilter/nf_conntrack.h 47 | @@ -311,5 +311,15 @@ do { \ 48 | #define MODULE_ALIAS_NFCT_HELPER(helper) \ 49 | MODULE_ALIAS("nfct-helper-" helper) 50 | 51 | +#if defined(CONFIG_NETFILTER_XT_TARGET_SYNPROXY) || \ 52 | + defined(CONFIG_NETFILTER_XT_TARGET_SYNPROXY_MODULE) 53 | +extern unsigned int (*syn_proxy_pre_hook)(struct sk_buff *skb, 54 | + struct nf_conn *ct, 55 | + enum ip_conntrack_info ctinfo); 56 | + 57 | +extern unsigned int (*syn_proxy_post_hook)(struct sk_buff *skb, 58 | + struct nf_conn *ct, 59 | + enum ip_conntrack_info ctinfo); 60 | +#endif 61 | #endif /* __KERNEL__ */ 62 | #endif /* _NF_CONNTRACK_H */ 63 | diff --git a/include/net/netfilter/nf_conntrack_core.h b/include/net/netfilter/nf_conntrack_core.h 64 | index aced085..637b404 100644 65 | --- a/include/net/netfilter/nf_conntrack_core.h 66 | +++ b/include/net/netfilter/nf_conntrack_core.h 67 | @@ -54,6 +54,23 @@ nf_conntrack_find_get(struct net *net, u16 zone, 68 | 69 | extern int __nf_conntrack_confirm(struct sk_buff *skb); 70 | 71 | +static inline unsigned int syn_proxy_post_call(struct sk_buff *skb, 72 | + struct nf_conn *ct, 73 | + enum ip_conntrack_info ctinfo) 74 | +{ 75 | + unsigned int ret = NF_ACCEPT; 76 | +#if defined(CONFIG_NETFILTER_XT_TARGET_SYNPROXY) || \ 77 | + defined(CONFIG_NETFILTER_XT_TARGET_SYNPROXY_MODULE) 78 | + unsigned int (*syn_proxy)(struct sk_buff *, struct nf_conn *, 79 | + enum ip_conntrack_info); 80 | + syn_proxy = rcu_dereference(syn_proxy_post_hook); 81 | + if (syn_proxy) 82 | + ret = syn_proxy(skb, ct, ctinfo); 83 | +#endif 84 | + 85 | + return ret; 86 | +} 87 | + 88 | /* Confirm a connection: returns NF_DROP if packet must be dropped. */ 89 | static inline int nf_conntrack_confirm(struct sk_buff *skb) 90 | { 91 | @@ -63,8 +80,10 @@ static inline int nf_conntrack_confirm(struct sk_buff *skb) 92 | if (ct && !nf_ct_is_untracked(ct)) { 93 | if (!nf_ct_is_confirmed(ct)) 94 | ret = __nf_conntrack_confirm(skb); 95 | - if (likely(ret == NF_ACCEPT)) 96 | + if (likely(ret == NF_ACCEPT)) { 97 | nf_ct_deliver_cached_events(ct); 98 | + ret = syn_proxy_post_call(skb, ct, skb->nfctinfo); 99 | + } 100 | } 101 | return ret; 102 | } 103 | diff --git a/include/net/netfilter/nf_conntrack_extend.h b/include/net/netfilter/nf_conntrack_extend.h 104 | index 32d15bd..b2ae7e9 100644 105 | --- a/include/net/netfilter/nf_conntrack_extend.h 106 | +++ b/include/net/netfilter/nf_conntrack_extend.h 107 | @@ -11,6 +11,7 @@ enum nf_ct_ext_id { 108 | NF_CT_EXT_ACCT, 109 | NF_CT_EXT_ECACHE, 110 | NF_CT_EXT_ZONE, 111 | + NF_CT_EXT_SYNPROXY, 112 | NF_CT_EXT_NUM, 113 | }; 114 | 115 | @@ -19,6 +20,7 @@ enum nf_ct_ext_id { 116 | #define NF_CT_EXT_ACCT_TYPE struct nf_conn_counter 117 | #define NF_CT_EXT_ECACHE_TYPE struct nf_conntrack_ecache 118 | #define NF_CT_EXT_ZONE_TYPE struct nf_conntrack_zone 119 | +#define NF_CT_EXT_SYNPROXY_TYPE struct syn_proxy_state 120 | 121 | /* Extensions: optional stuff which isn't permanently in struct. */ 122 | struct nf_ct_ext { 123 | diff --git a/include/net/tcp.h b/include/net/tcp.h 124 | index c2f96c2..06f28d3 100644 125 | --- a/include/net/tcp.h 126 | +++ b/include/net/tcp.h 127 | @@ -460,8 +460,11 @@ extern int tcp_disconnect(struct sock *sk, int flags); 128 | extern __u32 syncookie_secret[2][16-4+SHA_DIGEST_WORDS]; 129 | extern struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, 130 | struct ip_options *opt); 131 | -extern __u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb, 132 | - __u16 *mss); 133 | +extern __u32 __cookie_v4_init_sequence(__be32 saddr, __be32 daddr, 134 | + __be16 sport, __be16 dport, __u32 seq, 135 | + __u16 *mssp); 136 | +extern int cookie_v4_check_sequence(const struct iphdr *iph, 137 | + const struct tcphdr *th, __u32 cookie); 138 | 139 | extern __u32 cookie_init_timestamp(struct request_sock *req); 140 | extern bool cookie_check_timestamp(struct tcp_options_received *opt, bool *); 141 | diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c 142 | index 650cace..3adcba3 100644 143 | --- a/net/ipv4/syncookies.c 144 | +++ b/net/ipv4/syncookies.c 145 | @@ -159,26 +159,21 @@ static __u16 const msstab[] = { 146 | * Generate a syncookie. mssp points to the mss, which is returned 147 | * rounded down to the value encoded in the cookie. 148 | */ 149 | -__u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb, __u16 *mssp) 150 | +__u32 __cookie_v4_init_sequence(__be32 saddr, __be32 daddr, __be16 sport, 151 | + __be16 dport, __u32 seq, __u16 *mssp) 152 | { 153 | - const struct iphdr *iph = ip_hdr(skb); 154 | - const struct tcphdr *th = tcp_hdr(skb); 155 | int mssind; 156 | const __u16 mss = *mssp; 157 | 158 | - tcp_synq_overflow(sk); 159 | - 160 | for (mssind = ARRAY_SIZE(msstab) - 1; mssind ; mssind--) 161 | if (mss >= msstab[mssind]) 162 | break; 163 | *mssp = msstab[mssind]; 164 | 165 | - NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_SYNCOOKIESSENT); 166 | - 167 | - return secure_tcp_syn_cookie(iph->saddr, iph->daddr, 168 | - th->source, th->dest, ntohl(th->seq), 169 | + return secure_tcp_syn_cookie(saddr, daddr, sport, dport, seq, 170 | jiffies / (HZ * 60), mssind); 171 | } 172 | +EXPORT_SYMBOL(__cookie_v4_init_sequence); 173 | 174 | /* 175 | * This (misnamed) value is the age of syncookie which is permitted. 176 | @@ -191,10 +186,9 @@ __u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb, __u16 *mssp) 177 | * Check if a ack sequence number is a valid syncookie. 178 | * Return the decoded mss if it is, or 0 if not. 179 | */ 180 | -static inline int cookie_check(struct sk_buff *skb, __u32 cookie) 181 | +int cookie_v4_check_sequence(const struct iphdr *iph, const struct tcphdr *th, 182 | + __u32 cookie) 183 | { 184 | - const struct iphdr *iph = ip_hdr(skb); 185 | - const struct tcphdr *th = tcp_hdr(skb); 186 | __u32 seq = ntohl(th->seq) - 1; 187 | __u32 mssind = check_tcp_syn_cookie(cookie, iph->saddr, iph->daddr, 188 | th->source, th->dest, seq, 189 | @@ -203,6 +197,7 @@ static inline int cookie_check(struct sk_buff *skb, __u32 cookie) 190 | 191 | return mssind < ARRAY_SIZE(msstab) ? msstab[mssind] : 0; 192 | } 193 | +EXPORT_SYMBOL(cookie_v4_check_sequence); 194 | 195 | static inline struct sock *get_cookie_sock(struct sock *sk, struct sk_buff *skb, 196 | struct request_sock *req, 197 | @@ -282,7 +277,8 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, 198 | goto out; 199 | 200 | if (tcp_synq_no_recent_overflow(sk) || 201 | - (mss = cookie_check(skb, cookie)) == 0) { 202 | + (mss = cookie_v4_check_sequence(ip_hdr(skb), tcp_hdr(skb), 203 | + cookie)) == 0) { 204 | NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_SYNCOOKIESFAILED); 205 | goto out; 206 | } 207 | diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c 208 | index 8fa32f5..3b094c7 100644 209 | --- a/net/ipv4/tcp_ipv4.c 210 | +++ b/net/ipv4/tcp_ipv4.c 211 | @@ -1332,7 +1332,14 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) 212 | TCP_ECN_create_request(req, tcp_hdr(skb)); 213 | 214 | if (want_cookie) { 215 | - isn = cookie_v4_init_sequence(sk, skb, &req->mss); 216 | + struct tcphdr *th; 217 | + 218 | + tcp_synq_overflow(sk); 219 | + NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_SYNCOOKIESSENT); 220 | + th = tcp_hdr(skb); 221 | + isn = __cookie_v4_init_sequence(saddr, daddr, th->source, 222 | + th->dest, ntohl(th->seq), 223 | + &req->mss); 224 | req->cookie_ts = tmp_opt.tstamp_ok; 225 | } else if (!isn) { 226 | struct inet_peer *peer = NULL; 227 | diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig 228 | index 413ed24..fd8ad8c 100644 229 | --- a/net/netfilter/Kconfig 230 | +++ b/net/netfilter/Kconfig 231 | @@ -560,6 +560,23 @@ config NETFILTER_XT_TARGET_SECMARK 232 | 233 | To compile it as a module, choose M here. If unsure, say N. 234 | 235 | +config NETFILTER_XT_TARGET_SYNPROXY 236 | + tristate '"SYNPROXY" target support (EXPERIMENTAL)' 237 | + depends on EXPERIMENTAL 238 | + depends on SYN_COOKIES 239 | + depends on IP_NF_RAW 240 | + depends on NF_CONNTRACK 241 | + depends on NETFILTER_ADVANCED 242 | + help 243 | + The SYNPROXY target allows a raw rule to specify that some TCP 244 | + connections are relayed to protect the TCP servers from the SYN-flood 245 | + DoS attacks. Syn cookies is used to save the initial state, so no 246 | + conntrack is needed until the client side connection is established. 247 | + It frees the connection tracking system from creating/deleting 248 | + conntracks when SYN-flood DoS attack acts. 249 | + 250 | + To compile it as a module, choose M here. If unsure, say N. 251 | + 252 | config NETFILTER_XT_TARGET_TCPMSS 253 | tristate '"TCPMSS" target support' 254 | depends on (IPV6 || IPV6=n) 255 | diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile 256 | index e28420a..4e32834 100644 257 | --- a/net/netfilter/Makefile 258 | +++ b/net/netfilter/Makefile 259 | @@ -62,6 +62,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP) += xt_TCPOPTSTRIP.o 260 | obj-$(CONFIG_NETFILTER_XT_TARGET_TEE) += xt_TEE.o 261 | obj-$(CONFIG_NETFILTER_XT_TARGET_TRACE) += xt_TRACE.o 262 | obj-$(CONFIG_NETFILTER_XT_TARGET_IDLETIMER) += xt_IDLETIMER.o 263 | +obj-$(CONFIG_NETFILTER_XT_TARGET_SYNPROXY) += xt_SYNPROXY.o 264 | 265 | # matches 266 | obj-$(CONFIG_NETFILTER_XT_MATCH_CLUSTER) += xt_cluster.o 267 | diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c 268 | index 16b41b4..dd85d6f 100644 269 | --- a/net/netfilter/nf_conntrack_core.c 270 | +++ b/net/netfilter/nf_conntrack_core.c 271 | @@ -800,6 +800,26 @@ resolve_normal_ct(struct net *net, struct nf_conn *tmpl, 272 | return ct; 273 | } 274 | 275 | +static inline unsigned int syn_proxy_pre_call(int protonum, struct sk_buff *skb, 276 | + struct nf_conn *ct, 277 | + enum ip_conntrack_info ctinfo) 278 | +{ 279 | + unsigned int ret = NF_ACCEPT; 280 | +#if defined(CONFIG_NETFILTER_XT_TARGET_SYNPROXY) || \ 281 | + defined(CONFIG_NETFILTER_XT_TARGET_SYNPROXY_MODULE) 282 | + unsigned int (*syn_proxy)(struct sk_buff *, struct nf_conn *, 283 | + enum ip_conntrack_info); 284 | + 285 | + if (protonum == IPPROTO_TCP) { 286 | + syn_proxy = rcu_dereference(syn_proxy_pre_hook); 287 | + if (syn_proxy) 288 | + ret = syn_proxy(skb, ct, ctinfo); 289 | + } 290 | +#endif 291 | + 292 | + return ret; 293 | +} 294 | + 295 | unsigned int 296 | nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum, 297 | struct sk_buff *skb) 298 | @@ -855,8 +875,9 @@ nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum, 299 | l3proto, l4proto, &set_reply, &ctinfo); 300 | if (!ct) { 301 | /* Not valid part of a connection */ 302 | - NF_CT_STAT_INC_ATOMIC(net, invalid); 303 | - ret = NF_ACCEPT; 304 | + ret = syn_proxy_pre_call(protonum, skb, NULL, ctinfo); 305 | + if (ret == NF_ACCEPT) 306 | + NF_CT_STAT_INC_ATOMIC(net, invalid); 307 | goto out; 308 | } 309 | 310 | @@ -869,6 +890,9 @@ nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum, 311 | 312 | NF_CT_ASSERT(skb->nfct); 313 | 314 | + ret = syn_proxy_pre_call(protonum, skb, ct, ctinfo); 315 | + if (ret != NF_ACCEPT) 316 | + goto out; 317 | ret = l4proto->packet(ct, skb, dataoff, ctinfo, pf, hooknum); 318 | if (ret <= 0) { 319 | /* Invalid: inverse of the return code tells 320 | @@ -1476,6 +1500,17 @@ s16 (*nf_ct_nat_offset)(const struct nf_conn *ct, 321 | u32 seq); 322 | EXPORT_SYMBOL_GPL(nf_ct_nat_offset); 323 | 324 | +#if defined(CONFIG_NETFILTER_XT_TARGET_SYNPROXY) || \ 325 | + defined(CONFIG_NETFILTER_XT_TARGET_SYNPROXY_MODULE) 326 | +unsigned int (*syn_proxy_pre_hook)(struct sk_buff *skb, struct nf_conn *ct, 327 | + enum ip_conntrack_info ctinfo); 328 | +EXPORT_SYMBOL(syn_proxy_pre_hook); 329 | + 330 | +unsigned int (*syn_proxy_post_hook)(struct sk_buff *skb, struct nf_conn *ct, 331 | + enum ip_conntrack_info ctinfo); 332 | +EXPORT_SYMBOL(syn_proxy_post_hook); 333 | +#endif 334 | + 335 | int nf_conntrack_init(struct net *net) 336 | { 337 | int ret; 338 | @@ -1496,6 +1531,12 @@ int nf_conntrack_init(struct net *net) 339 | 340 | /* Howto get NAT offsets */ 341 | rcu_assign_pointer(nf_ct_nat_offset, NULL); 342 | + 343 | +#if defined(CONFIG_NETFILTER_XT_TARGET_SYNPROXY) || \ 344 | + defined(CONFIG_NETFILTER_XT_TARGET_SYNPROXY_MODULE) 345 | + rcu_assign_pointer(syn_proxy_pre_hook, NULL); 346 | + rcu_assign_pointer(syn_proxy_post_hook, NULL); 347 | +#endif 348 | } 349 | return 0; 350 | 351 | diff --git a/net/netfilter/xt_SYNPROXY.c b/net/netfilter/xt_SYNPROXY.c 352 | new file mode 100644 353 | index 0000000..1a55f33 354 | --- /dev/null 355 | +++ b/net/netfilter/xt_SYNPROXY.c 356 | @@ -0,0 +1,679 @@ 357 | +/* (C) 2010- Changli Gao 358 | + * 359 | + * This program is free software; you can redistribute it and/or modify 360 | + * it under the terms of the GNU General Public License version 2 as 361 | + * published by the Free Software Foundation. 362 | + * 363 | + * It bases on ipt_REJECT.c 364 | + */ 365 | +#define pr_fmt(fmt) "SYNPROXY: " fmt 366 | +#include 367 | +#include 368 | +#include 369 | +#include 370 | +#include 371 | +#include 372 | +#include 373 | +#include 374 | +#include 375 | +#include 376 | +#include 377 | +#include 378 | +#include 379 | +#include 380 | +#include 381 | +#include 382 | + 383 | +MODULE_LICENSE("GPL"); 384 | +MODULE_AUTHOR("Changli Gao "); 385 | +MODULE_DESCRIPTION("Xtables: \"SYNPROXY\" target for IPv4"); 386 | +MODULE_ALIAS("ipt_SYNPROXY"); 387 | + 388 | +enum { 389 | + TCP_SEND_FLAG_NOTRACE = 0x1, 390 | + TCP_SEND_FLAG_SYNCOOKIE = 0x2, 391 | + TCP_SEND_FLAG_ACK2SYN = 0x4, 392 | +}; 393 | + 394 | +struct syn_proxy_state { 395 | + u16 seq_inited; 396 | + __be16 window; 397 | + u32 seq_diff; 398 | +}; 399 | + 400 | +static int get_mtu(const struct dst_entry *dst) 401 | +{ 402 | + int mtu; 403 | + 404 | + mtu = dst_mtu(dst); 405 | + if (mtu) 406 | + return mtu; 407 | + 408 | + return dst->dev ? dst->dev->mtu : 0; 409 | +} 410 | + 411 | +static int get_advmss(const struct dst_entry *dst) 412 | +{ 413 | + int advmss; 414 | + 415 | + advmss = dst_metric(dst, RTAX_ADVMSS); 416 | + if (advmss) 417 | + return advmss; 418 | + advmss = get_mtu(dst); 419 | + if (advmss) 420 | + return advmss - (sizeof(struct iphdr) + sizeof(struct tcphdr)); 421 | + 422 | + return TCP_MSS_DEFAULT; 423 | +} 424 | + 425 | +static int syn_proxy_route(struct sk_buff *skb, struct net *net, u16 *pmss) 426 | +{ 427 | + const struct iphdr *iph = ip_hdr(skb); 428 | + struct rtable *rt; 429 | + struct flowi fl = {}; 430 | + unsigned int type; 431 | + int flags = 0; 432 | + int err; 433 | + u16 mss; 434 | + 435 | + type = inet_addr_type(net, iph->saddr); 436 | + if (type != RTN_LOCAL) { 437 | + type = inet_addr_type(net, iph->daddr); 438 | + if (type == RTN_LOCAL) 439 | + flags |= FLOWI_FLAG_ANYSRC; 440 | + } 441 | + 442 | + if (type == RTN_LOCAL) { 443 | + fl.nl_u.ip4_u.daddr = iph->daddr; 444 | + fl.nl_u.ip4_u.saddr = iph->saddr; 445 | + fl.nl_u.ip4_u.tos = RT_TOS(iph->tos); 446 | + fl.flags = flags; 447 | + err = ip_route_output_key(net, &rt, &fl); 448 | + if (err) 449 | + goto out; 450 | + 451 | + skb_dst_set(skb, &rt->dst); 452 | + } else { 453 | + /* non-local src, find valid iif to satisfy 454 | + * rp-filter when calling ip_route_input. */ 455 | + fl.nl_u.ip4_u.daddr = iph->saddr; 456 | + err = ip_route_output_key(net, &rt, &fl); 457 | + if (err) 458 | + goto out; 459 | + 460 | + err = ip_route_input(skb, iph->daddr, iph->saddr, 461 | + RT_TOS(iph->tos), rt->dst.dev); 462 | + if (err) { 463 | + dst_release(&rt->dst); 464 | + goto out; 465 | + } 466 | + if (pmss) { 467 | + mss = get_advmss(&rt->dst); 468 | + if (*pmss > mss) 469 | + *pmss = mss; 470 | + } 471 | + dst_release(&rt->dst); 472 | + } 473 | + 474 | + err = skb_dst(skb)->error; 475 | + if (!err && pmss) { 476 | + mss = get_advmss(skb_dst(skb)); 477 | + if (*pmss > mss) 478 | + *pmss = mss; 479 | + } 480 | + 481 | +out: 482 | + return err; 483 | +} 484 | + 485 | +static int tcp_send(__be32 src, __be32 dst, __be16 sport, __be16 dport, 486 | + u32 seq, u32 ack_seq, __be16 window, u16 mss, u8 tcp_flags, 487 | + u8 tos, struct net_device *dev, int flags, 488 | + struct sk_buff *oskb) 489 | +{ 490 | + struct sk_buff *skb; 491 | + struct iphdr *iph; 492 | + struct tcphdr *th; 493 | + int err, len; 494 | + 495 | + len = sizeof(*th); 496 | + if (mss) 497 | + len += TCPOLEN_MSS; 498 | + 499 | + skb = NULL; 500 | + /* caller must give me a large enough oskb */ 501 | + if (oskb) { 502 | + unsigned char *odata = oskb->data; 503 | + 504 | + if (skb_recycle_check(oskb, 0)) { 505 | + oskb->data = odata; 506 | + skb_reset_tail_pointer(oskb); 507 | + skb = oskb; 508 | + pr_debug("recycle skb\n"); 509 | + } 510 | + } 511 | + if (!skb) { 512 | + skb = alloc_skb(LL_MAX_HEADER + sizeof(*iph) + len, GFP_ATOMIC); 513 | + if (!skb) { 514 | + err = -ENOMEM; 515 | + goto out; 516 | + } 517 | + skb_reserve(skb, LL_MAX_HEADER); 518 | + } 519 | + 520 | + skb_reset_network_header(skb); 521 | + if (!(flags & TCP_SEND_FLAG_ACK2SYN) || skb != oskb) { 522 | + iph = (struct iphdr *)skb_put(skb, sizeof(*iph)); 523 | + iph->version = 4; 524 | + iph->ihl = sizeof(*iph) / 4; 525 | + iph->tos = tos; 526 | + /* tot_len is set in ip_local_out() */ 527 | + iph->id = 0; 528 | + iph->frag_off = htons(IP_DF); 529 | + iph->protocol = IPPROTO_TCP; 530 | + iph->saddr = src; 531 | + iph->daddr = dst; 532 | + th = (struct tcphdr *)skb_put(skb, len); 533 | + th->source = sport; 534 | + th->dest = dport; 535 | + } else { 536 | + iph = (struct iphdr *)skb->data; 537 | + iph->id = 0; 538 | + iph->frag_off = htons(IP_DF); 539 | + skb_put(skb, iph->ihl * 4 + len); 540 | + th = (struct tcphdr *)(skb->data + iph->ihl * 4); 541 | + } 542 | + 543 | + th->seq = htonl(seq); 544 | + th->ack_seq = htonl(ack_seq); 545 | + tcp_flag_byte(th) = tcp_flags; 546 | + th->doff = len / 4; 547 | + th->window = window; 548 | + th->urg_ptr = 0; 549 | + 550 | + skb->protocol = htons(ETH_P_IP); 551 | + if ((flags & TCP_SEND_FLAG_SYNCOOKIE) && mss) 552 | + err = syn_proxy_route(skb, dev_net(dev), &mss); 553 | + else 554 | + err = syn_proxy_route(skb, dev_net(dev), NULL); 555 | + if (err) 556 | + goto err_out; 557 | + 558 | + if ((flags & TCP_SEND_FLAG_SYNCOOKIE)) { 559 | + if (mss) { 560 | + th->seq = htonl(__cookie_v4_init_sequence(dst, src, 561 | + dport, sport, 562 | + ack_seq - 1, 563 | + &mss)); 564 | + } else { 565 | + mss = TCP_MSS_DEFAULT; 566 | + th->seq = htonl(__cookie_v4_init_sequence(dst, src, 567 | + dport, sport, 568 | + ack_seq - 1, 569 | + &mss)); 570 | + mss = 0; 571 | + } 572 | + } 573 | + 574 | + if (mss) 575 | + * (__force __be32 *)(th + 1) = htonl((TCPOPT_MSS << 24) | 576 | + (TCPOLEN_MSS << 16) | 577 | + mss); 578 | + skb->ip_summed = CHECKSUM_PARTIAL; 579 | + th->check = ~tcp_v4_check(len, src, dst, 0); 580 | + skb->csum_start = (unsigned char *)th - skb->head; 581 | + skb->csum_offset = offsetof(struct tcphdr, check); 582 | + 583 | + if (!(flags & TCP_SEND_FLAG_ACK2SYN) || skb != oskb) 584 | + iph->ttl = dst_metric(skb_dst(skb), RTAX_HOPLIMIT); 585 | + 586 | + if (skb->len > get_mtu(skb_dst(skb))) { 587 | + if (printk_ratelimit()) 588 | + pr_warning("%s has smaller mtu: %d\n", 589 | + skb_dst(skb)->dev->name, 590 | + get_mtu(skb_dst(skb))); 591 | + err = -EINVAL; 592 | + goto err_out; 593 | + } 594 | + 595 | + if ((flags & TCP_SEND_FLAG_NOTRACE)) { 596 | + skb->nfct = &nf_ct_untracked_get()->ct_general; 597 | + skb->nfctinfo = IP_CT_NEW; 598 | + nf_conntrack_get(skb->nfct); 599 | + } 600 | + 601 | + pr_debug("ip_local_out: %pI4n:%hu -> %pI4n:%hu (seq=%u, " 602 | + "ack_seq=%u mss=%hu flags=%hhx)\n", &src, ntohs(th->source), 603 | + &dst, ntohs(th->dest), ntohl(th->seq), ack_seq, mss, 604 | + tcp_flags); 605 | + 606 | + err = ip_local_out(skb); 607 | + if (err > 0) 608 | + err = net_xmit_errno(err); 609 | + 610 | + pr_debug("ip_local_out: return with %d\n", err); 611 | +out: 612 | + if (oskb && oskb != skb) 613 | + kfree_skb(oskb); 614 | + 615 | + return err; 616 | + 617 | +err_out: 618 | + kfree_skb(skb); 619 | + goto out; 620 | +} 621 | + 622 | +static int get_mss(u8 *data, int len) 623 | +{ 624 | + u8 olen; 625 | + 626 | + while (len >= TCPOLEN_MSS) { 627 | + switch (data[0]) { 628 | + case TCPOPT_EOL: 629 | + return 0; 630 | + case TCPOPT_NOP: 631 | + data++; 632 | + len--; 633 | + break; 634 | + case TCPOPT_MSS: 635 | + if (data[1] != TCPOLEN_MSS) 636 | + return -EINVAL; 637 | + return get_unaligned_be16(data + 2); 638 | + default: 639 | + olen = data[1]; 640 | + if (olen < 2 || olen > len) 641 | + return -EINVAL; 642 | + data += olen; 643 | + len -= olen; 644 | + break; 645 | + } 646 | + } 647 | + 648 | + return 0; 649 | +} 650 | + 651 | +static DEFINE_PER_CPU(struct syn_proxy_state, syn_proxy_state); 652 | + 653 | +/* syn_proxy_pre isn't under the protection of nf_conntrack_proto_tcp.c */ 654 | +static unsigned int syn_proxy_pre(struct sk_buff *skb, struct nf_conn *ct, 655 | + enum ip_conntrack_info ctinfo) 656 | +{ 657 | + struct syn_proxy_state *state; 658 | + struct iphdr *iph; 659 | + struct tcphdr *th, _th; 660 | + 661 | + /* only support IPv4 now */ 662 | + iph = ip_hdr(skb); 663 | + if (iph->version != 4) 664 | + return NF_ACCEPT; 665 | + 666 | + th = skb_header_pointer(skb, iph->ihl * 4, sizeof(_th), &_th); 667 | + if (th == NULL) 668 | + return NF_DROP; 669 | + 670 | + if (!ct || !nf_ct_is_confirmed(ct)) { 671 | + int ret; 672 | + 673 | + if (!th->syn && th->ack) { 674 | + u16 mss; 675 | + struct sk_buff *rec_skb; 676 | + 677 | + mss = cookie_v4_check_sequence(iph, th, 678 | + ntohl(th->ack_seq) - 1); 679 | + if (!mss) 680 | + return NF_ACCEPT; 681 | + 682 | + pr_debug("%pI4n:%hu -> %pI4n:%hu(mss=%hu)\n", 683 | + &iph->saddr, ntohs(th->source), 684 | + &iph->daddr, ntohs(th->dest), mss); 685 | + 686 | + if (skb_tailroom(skb) < TCPOLEN_MSS && 687 | + skb->len < iph->ihl * 4 + sizeof(*th) + TCPOLEN_MSS) 688 | + rec_skb = NULL; 689 | + else 690 | + rec_skb = skb; 691 | + 692 | + local_bh_disable(); 693 | + state = &__get_cpu_var(syn_proxy_state); 694 | + state->seq_inited = 1; 695 | + state->window = th->window; 696 | + state->seq_diff = ntohl(th->ack_seq) - 1; 697 | + if (rec_skb) 698 | + tcp_send(iph->saddr, iph->daddr, 0, 0, 699 | + ntohl(th->seq) - 1, 0, th->window, 700 | + mss, TCPHDR_SYN, 0, skb->dev, 701 | + TCP_SEND_FLAG_ACK2SYN, rec_skb); 702 | + else 703 | + tcp_send(iph->saddr, iph->daddr, th->source, 704 | + th->dest, ntohl(th->seq) - 1, 0, 705 | + th->window, mss, TCPHDR_SYN, 706 | + iph->tos, skb->dev, 0, NULL); 707 | + state->seq_inited = 0; 708 | + local_bh_enable(); 709 | + 710 | + if (!rec_skb) 711 | + kfree_skb(skb); 712 | + 713 | + return NF_STOLEN; 714 | + } 715 | + 716 | + if (!ct || !th->syn || th->ack) 717 | + return NF_ACCEPT; 718 | + 719 | + ret = NF_ACCEPT; 720 | + local_bh_disable(); 721 | + state = &__get_cpu_var(syn_proxy_state); 722 | + if (state->seq_inited) { 723 | + struct syn_proxy_state *nstate; 724 | + 725 | + nstate = nf_ct_ext_add(ct, NF_CT_EXT_SYNPROXY, 726 | + GFP_ATOMIC); 727 | + if (nstate != NULL) { 728 | + nstate->seq_inited = 0; 729 | + nstate->window = state->window; 730 | + nstate->seq_diff = state->seq_diff; 731 | + pr_debug("seq_diff: %u\n", nstate->seq_diff); 732 | + } else { 733 | + ret = NF_DROP; 734 | + } 735 | + } 736 | + local_bh_enable(); 737 | + 738 | + return ret; 739 | + } 740 | + 741 | + state = nf_ct_ext_find(ct, NF_CT_EXT_SYNPROXY); 742 | + if (!state) 743 | + return NF_ACCEPT; 744 | + 745 | + if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) { 746 | + __be32 newack; 747 | + 748 | + /* don't need to mangle duplicate SYN packets */ 749 | + if (th->syn && !th->ack) 750 | + return NF_ACCEPT; 751 | + if (!skb_make_writable(skb, ip_hdrlen(skb) + sizeof(*th))) 752 | + return NF_DROP; 753 | + th = (struct tcphdr *)(skb->data + ip_hdrlen(skb)); 754 | + newack = htonl(ntohl(th->ack_seq) - state->seq_diff); 755 | + inet_proto_csum_replace4(&th->check, skb, th->ack_seq, newack, 756 | + 0); 757 | + pr_debug("alter ack seq: %u -> %u\n", 758 | + ntohl(th->ack_seq), ntohl(newack)); 759 | + th->ack_seq = newack; 760 | + } else { 761 | + /* Simultaneous open ? Oh, no. The connection between 762 | + * client and us is established. */ 763 | + if (th->syn && !th->ack) 764 | + return NF_DROP; 765 | + } 766 | + 767 | + return NF_ACCEPT; 768 | +} 769 | + 770 | +static unsigned int syn_proxy_mangle_pkt(struct sk_buff *skb, struct iphdr *iph, 771 | + struct tcphdr *th, u32 seq_diff) 772 | +{ 773 | + __be32 new; 774 | + int olen; 775 | + 776 | + if (skb->len < (iph->ihl + th->doff) * 4) 777 | + return NF_DROP; 778 | + if (!skb_make_writable(skb, (iph->ihl + th->doff) * 4)) 779 | + return NF_DROP; 780 | + iph = (struct iphdr *)(skb->data); 781 | + th = (struct tcphdr *)(skb->data + iph->ihl * 4); 782 | + 783 | + new = tcp_flag_word(th) & (~TCP_FLAG_SYN); 784 | + inet_proto_csum_replace4(&th->check, skb, tcp_flag_word(th), new, 0); 785 | + tcp_flag_word(th) = new; 786 | + 787 | + new = htonl(ntohl(th->seq) + seq_diff); 788 | + inet_proto_csum_replace4(&th->check, skb, th->seq, new, 0); 789 | + pr_debug("alter seq: %u -> %u\n", ntohl(th->seq), ntohl(new)); 790 | + th->seq = new; 791 | + 792 | + olen = th->doff - sizeof(*th) / 4; 793 | + if (olen) { 794 | + __be32 *opt; 795 | + 796 | + opt = (__force __be32 *)(th + 1); 797 | +#define TCPOPT_EOL_WORD ((TCPOPT_EOL << 24) + (TCPOPT_EOL << 16) + \ 798 | + (TCPOPT_EOL << 8) + TCPOPT_EOL) 799 | + inet_proto_csum_replace4(&th->check, skb, *opt, TCPOPT_EOL_WORD, 800 | + 0); 801 | + *opt = TCPOPT_EOL_WORD; 802 | + } 803 | + 804 | + return NF_ACCEPT; 805 | +} 806 | + 807 | +static unsigned int syn_proxy_post(struct sk_buff *skb, struct nf_conn *ct, 808 | + enum ip_conntrack_info ctinfo) 809 | +{ 810 | + struct syn_proxy_state *state; 811 | + struct iphdr *iph; 812 | + struct tcphdr *th; 813 | + 814 | + /* untraced packets don't have NF_CT_EXT_SYNPROXY ext, as they don't 815 | + * enter syn_proxy_pre() */ 816 | + state = nf_ct_ext_find(ct, NF_CT_EXT_SYNPROXY); 817 | + if (state == NULL) 818 | + return NF_ACCEPT; 819 | + 820 | + iph = ip_hdr(skb); 821 | + if (!skb_make_writable(skb, iph->ihl * 4 + sizeof(*th))) 822 | + return NF_DROP; 823 | + th = (struct tcphdr *)(skb->data + iph->ihl * 4); 824 | + if (!state->seq_inited) { 825 | + if (th->syn) { 826 | + /* It must be from original direction, as the ones 827 | + * from the other side are dropped in function 828 | + * syn_proxy_pre() */ 829 | + if (!th->ack) 830 | + return NF_ACCEPT; 831 | + 832 | + pr_debug("SYN-ACK %pI4n:%hu -> %pI4n:%hu " 833 | + "(seq=%u ack_seq=%u)\n", 834 | + &iph->saddr, ntohs(th->source), &iph->daddr, 835 | + ntohs(th->dest), ntohl(th->seq), 836 | + ntohl(th->ack_seq)); 837 | + 838 | + /* SYN-ACK from reply direction with the protection 839 | + * of conntrack */ 840 | + spin_lock_bh(&ct->lock); 841 | + if (!state->seq_inited) { 842 | + state->seq_inited = 1; 843 | + pr_debug("update seq_diff %u -> %u\n", 844 | + state->seq_diff, 845 | + state->seq_diff - ntohl(th->seq)); 846 | + state->seq_diff -= ntohl(th->seq); 847 | + } 848 | + spin_unlock_bh(&ct->lock); 849 | + tcp_send(iph->daddr, iph->saddr, th->dest, th->source, 850 | + ntohl(th->ack_seq), 851 | + ntohl(th->seq) + 1 + state->seq_diff, 852 | + state->window, 0, TCPHDR_ACK, iph->tos, 853 | + skb->dev, 0, NULL); 854 | + 855 | + return syn_proxy_mangle_pkt(skb, iph, th, 856 | + state->seq_diff + 1); 857 | + } else { 858 | + __be32 newseq; 859 | + 860 | + if (!th->rst) 861 | + return NF_ACCEPT; 862 | + newseq = htonl(state->seq_diff + 1); 863 | + inet_proto_csum_replace4(&th->check, skb, th->seq, 864 | + newseq, 0); 865 | + pr_debug("alter RST seq: %u -> %u\n", 866 | + ntohl(th->seq), ntohl(newseq)); 867 | + th->seq = newseq; 868 | + 869 | + return NF_ACCEPT; 870 | + } 871 | + } 872 | + 873 | + /* ct should be in ESTABLISHED state, but if the ack packets from 874 | + * us are lost. */ 875 | + if (th->syn) { 876 | + if (!th->ack) 877 | + return NF_ACCEPT; 878 | + 879 | + tcp_send(iph->daddr, iph->saddr, th->dest, th->source, 880 | + ntohl(th->ack_seq), 881 | + ntohl(th->seq) + 1 + state->seq_diff, 882 | + state->window, 0, TCPHDR_ACK, iph->tos, 883 | + skb->dev, 0, NULL); 884 | + 885 | + return syn_proxy_mangle_pkt(skb, iph, th, state->seq_diff + 1); 886 | + } 887 | + 888 | + if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) { 889 | + __be32 newseq; 890 | + 891 | + newseq = htonl(ntohl(th->seq) + state->seq_diff); 892 | + inet_proto_csum_replace4(&th->check, skb, th->seq, newseq, 0); 893 | + pr_debug("alter seq: %u -> %u\n", ntohl(th->seq), 894 | + ntohl(newseq)); 895 | + th->seq = newseq; 896 | + } 897 | + 898 | + return NF_ACCEPT; 899 | +} 900 | + 901 | +static unsigned int tcp_process(struct sk_buff *skb) 902 | +{ 903 | + const struct iphdr *iph; 904 | + const struct tcphdr *th; 905 | + int err; 906 | + u16 mss; 907 | + 908 | + iph = ip_hdr(skb); 909 | + if (iph->frag_off & htons(IP_OFFSET)) 910 | + goto out; 911 | + if (!pskb_may_pull(skb, iph->ihl * 4 + sizeof(*th))) 912 | + goto out; 913 | + th = (const struct tcphdr *)(skb->data + iph->ihl * 4); 914 | + if ((tcp_flag_byte(th) & 915 | + (TCPHDR_FIN | TCPHDR_RST | TCPHDR_ACK | TCPHDR_SYN)) != TCPHDR_SYN) 916 | + goto out; 917 | + 918 | + if (nf_ip_checksum(skb, NF_INET_PRE_ROUTING, iph->ihl * 4, IPPROTO_TCP)) 919 | + goto out; 920 | + mss = 0; 921 | + if (th->doff > sizeof(*th) / 4) { 922 | + if (!pskb_may_pull(skb, (iph->ihl + th->doff) * 4)) 923 | + goto out; 924 | + err = get_mss((u8 *)(th + 1), th->doff * 4 - sizeof(*th)); 925 | + if (err < 0) 926 | + goto out; 927 | + if (err != 0) 928 | + mss = err; 929 | + } else if (th->doff != sizeof(*th) / 4) 930 | + goto out; 931 | + 932 | + tcp_send(iph->daddr, iph->saddr, th->dest, th->source, 0, 933 | + ntohl(th->seq) + 1, 0, mss, TCPHDR_SYN | TCPHDR_ACK, 934 | + iph->tos, skb->dev, 935 | + TCP_SEND_FLAG_NOTRACE | TCP_SEND_FLAG_SYNCOOKIE, skb); 936 | + 937 | + return NF_STOLEN; 938 | + 939 | +out: 940 | + return NF_DROP; 941 | +} 942 | + 943 | +static unsigned int synproxy_tg(struct sk_buff *skb, 944 | + const struct xt_action_param *par) 945 | +{ 946 | + struct nf_conn *ct; 947 | + enum ip_conntrack_info ctinfo; 948 | + int ret; 949 | + 950 | + /* received from lo */ 951 | + ct = nf_ct_get(skb, &ctinfo); 952 | + if (ct) 953 | + return IPT_CONTINUE; 954 | + 955 | + local_bh_disable(); 956 | + if (!__get_cpu_var(syn_proxy_state).seq_inited) 957 | + ret = tcp_process(skb); 958 | + else 959 | + ret = IPT_CONTINUE; 960 | + local_bh_enable(); 961 | + 962 | + return ret; 963 | +} 964 | + 965 | +static int synproxy_tg_check(const struct xt_tgchk_param *par) 966 | +{ 967 | + int ret; 968 | + 969 | + ret = nf_ct_l3proto_try_module_get(par->family); 970 | + if (ret < 0) 971 | + pr_info("cannot load conntrack support for proto=%u\n", 972 | + par->family); 973 | + 974 | + return ret; 975 | +} 976 | + 977 | +static void synproxy_tg_destroy(const struct xt_tgdtor_param *par) 978 | +{ 979 | + nf_ct_l3proto_module_put(par->family); 980 | +} 981 | + 982 | +static struct xt_target synproxy_tg_reg __read_mostly = { 983 | + .name = "SYNPROXY", 984 | + .family = NFPROTO_IPV4, 985 | + .target = synproxy_tg, 986 | + .table = "raw", 987 | + .hooks = 1 << NF_INET_PRE_ROUTING, 988 | + .proto = IPPROTO_TCP, 989 | + .checkentry = synproxy_tg_check, 990 | + .destroy = synproxy_tg_destroy, 991 | + .me = THIS_MODULE, 992 | +}; 993 | + 994 | +static struct nf_ct_ext_type syn_proxy_state_ext __read_mostly = { 995 | + .len = sizeof(struct syn_proxy_state), 996 | + .align = __alignof__(struct syn_proxy_state), 997 | + .id = NF_CT_EXT_SYNPROXY, 998 | +}; 999 | + 1000 | +static int __init synproxy_tg_init(void) 1001 | +{ 1002 | + int err; 1003 | + 1004 | + rcu_assign_pointer(syn_proxy_pre_hook, syn_proxy_pre); 1005 | + rcu_assign_pointer(syn_proxy_post_hook, syn_proxy_post); 1006 | + err = nf_ct_extend_register(&syn_proxy_state_ext); 1007 | + if (err) 1008 | + goto err_out; 1009 | + err = xt_register_target(&synproxy_tg_reg); 1010 | + if (err) 1011 | + goto err_out2; 1012 | + 1013 | + return err; 1014 | + 1015 | +err_out2: 1016 | + nf_ct_extend_unregister(&syn_proxy_state_ext); 1017 | +err_out: 1018 | + rcu_assign_pointer(syn_proxy_post_hook, NULL); 1019 | + rcu_assign_pointer(syn_proxy_pre_hook, NULL); 1020 | + rcu_barrier(); 1021 | + 1022 | + return err; 1023 | +} 1024 | + 1025 | +static void __exit synproxy_tg_exit(void) 1026 | +{ 1027 | + xt_unregister_target(&synproxy_tg_reg); 1028 | + nf_ct_extend_unregister(&syn_proxy_state_ext); 1029 | + rcu_assign_pointer(syn_proxy_post_hook, NULL); 1030 | + rcu_assign_pointer(syn_proxy_pre_hook, NULL); 1031 | + rcu_barrier(); 1032 | +} 1033 | + 1034 | +module_init(synproxy_tg_init); 1035 | +module_exit(synproxy_tg_exit); 1036 | -------------------------------------------------------------------------------- /synproxy-xmit-3.2.39.patch: -------------------------------------------------------------------------------- 1 | diff -r -u a/include/net/netfilter/nf_conntrack_core.h b/include/net/netfilter/nf_conntrack_core.h 2 | --- a/include/net/netfilter/nf_conntrack_core.h 2013-02-20 07:15:40.000000000 +0400 3 | +++ b/include/net/netfilter/nf_conntrack_core.h 2013-02-22 08:11:47.562858766 +0400 4 | @@ -54,6 +54,23 @@ 5 | 6 | extern int __nf_conntrack_confirm(struct sk_buff *skb); 7 | 8 | +static inline unsigned int syn_proxy_post_call(struct sk_buff *skb, 9 | + struct nf_conn *ct, 10 | + enum ip_conntrack_info ctinfo) 11 | +{ 12 | + unsigned int ret = NF_ACCEPT; 13 | +#if defined(CONFIG_NETFILTER_XT_TARGET_SYNPROXY) || \ 14 | + defined(CONFIG_NETFILTER_XT_TARGET_SYNPROXY_MODULE) 15 | + unsigned int (*syn_proxy)(struct sk_buff *, struct nf_conn *, 16 | + enum ip_conntrack_info); 17 | + syn_proxy = rcu_dereference(syn_proxy_post_hook); 18 | + if (syn_proxy) 19 | + ret = syn_proxy(skb, ct, ctinfo); 20 | +#endif 21 | + 22 | + return ret; 23 | +} 24 | + 25 | /* Confirm a connection: returns NF_DROP if packet must be dropped. */ 26 | static inline int nf_conntrack_confirm(struct sk_buff *skb) 27 | { 28 | @@ -63,8 +80,10 @@ 29 | if (ct && !nf_ct_is_untracked(ct)) { 30 | if (!nf_ct_is_confirmed(ct)) 31 | ret = __nf_conntrack_confirm(skb); 32 | - if (likely(ret == NF_ACCEPT)) 33 | + if (likely(ret == NF_ACCEPT)) { 34 | nf_ct_deliver_cached_events(ct); 35 | + ret = syn_proxy_post_call(skb, ct, skb->nfctinfo); 36 | + } 37 | } 38 | return ret; 39 | } 40 | diff -r -u a/include/net/netfilter/nf_conntrack_extend.h b/include/net/netfilter/nf_conntrack_extend.h 41 | --- a/include/net/netfilter/nf_conntrack_extend.h 2013-02-20 07:15:40.000000000 +0400 42 | +++ b/include/net/netfilter/nf_conntrack_extend.h 2013-02-22 08:15:55.890942331 +0400 43 | @@ -20,6 +20,7 @@ 44 | #ifdef CONFIG_NF_CONNTRACK_TIMESTAMP 45 | NF_CT_EXT_TSTAMP, 46 | #endif 47 | + NF_CT_EXT_SYNPROXY, 48 | NF_CT_EXT_NUM, 49 | }; 50 | 51 | @@ -28,6 +29,7 @@ 52 | #define NF_CT_EXT_ACCT_TYPE struct nf_conn_counter 53 | #define NF_CT_EXT_ECACHE_TYPE struct nf_conntrack_ecache 54 | #define NF_CT_EXT_ZONE_TYPE struct nf_conntrack_zone 55 | +#define NF_CT_EXT_SYNPROXY_TYPE struct syn_proxy_state 56 | #define NF_CT_EXT_TSTAMP_TYPE struct nf_conn_tstamp 57 | 58 | /* Extensions: optional stuff which isn't permanently in struct. */ 59 | diff -r -u a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h 60 | --- a/include/net/netfilter/nf_conntrack.h 2013-02-20 07:15:40.000000000 +0400 61 | +++ b/include/net/netfilter/nf_conntrack.h 2013-02-22 08:14:33.878884030 +0400 62 | @@ -333,4 +333,16 @@ 63 | #define MODULE_ALIAS_NFCT_HELPER(helper) \ 64 | MODULE_ALIAS("nfct-helper-" helper) 65 | 66 | +#if defined(CONFIG_NETFILTER_XT_TARGET_SYNPROXY) || \ 67 | + defined(CONFIG_NETFILTER_XT_TARGET_SYNPROXY_MODULE) 68 | +extern unsigned int (*syn_proxy_pre_hook)(struct sk_buff *skb, 69 | + struct nf_conn *ct, 70 | + enum ip_conntrack_info ctinfo); 71 | + 72 | +extern unsigned int (*syn_proxy_post_hook)(struct sk_buff *skb, 73 | + struct nf_conn *ct, 74 | + enum ip_conntrack_info ctinfo); 75 | +#endif 76 | + 77 | + 78 | #endif /* _NF_CONNTRACK_H */ 79 | diff -r -u a/include/net/tcp.h b/include/net/tcp.h 80 | --- a/include/net/tcp.h 2013-02-20 07:15:40.000000000 +0400 81 | +++ b/include/net/tcp.h 2013-02-22 08:19:11.974939754 +0400 82 | @@ -433,8 +433,14 @@ 83 | extern struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, 84 | struct ip_options *opt); 85 | #ifdef CONFIG_SYN_COOKIES 86 | -extern __u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb, 87 | - __u16 *mss); 88 | +//extern __u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb, 89 | +// __u16 *mss); 90 | +extern __u32 __cookie_v4_init_sequence(__be32 saddr, __be32 daddr, 91 | + __be16 sport, __be16 dport, __u32 seq, 92 | + __u16 *mssp); 93 | +extern int cookie_v4_check_sequence(const struct iphdr *iph, 94 | + const struct tcphdr *th, __u32 cookie); 95 | + 96 | #else 97 | static inline __u32 cookie_v4_init_sequence(struct sock *sk, 98 | struct sk_buff *skb, 99 | diff -r -u a/net/bridge/netfilter/ebt_snat.c b/net/bridge/netfilter/ebt_snat.c 100 | --- a/net/bridge/netfilter/ebt_snat.c 2013-02-20 07:15:40.000000000 +0400 101 | +++ b/net/bridge/netfilter/ebt_snat.c 2013-02-24 06:46:54.632333310 +0400 102 | @@ -24,7 +24,8 @@ 103 | if (!skb_make_writable(skb, 0)) 104 | return EBT_DROP; 105 | 106 | - memcpy(eth_hdr(skb)->h_source, info->mac, ETH_ALEN); 107 | + //memcpy(eth_hdr(skb)->h_source, info->mac, ETH_ALEN); 108 | + memcpy(eth_hdr(skb)->h_dest, info->mac, ETH_ALEN); 109 | if (!(info->target & NAT_ARP_BIT) && 110 | eth_hdr(skb)->h_proto == htons(ETH_P_ARP)) { 111 | const struct arphdr *ap; 112 | diff -r -u a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c 113 | --- a/net/ipv4/syncookies.c 2013-02-20 07:15:40.000000000 +0400 114 | +++ b/net/ipv4/syncookies.c 2013-02-26 00:44:19.862760563 +0400 115 | @@ -44,6 +44,8 @@ 116 | static u32 cookie_hash(__be32 saddr, __be32 daddr, __be16 sport, __be16 dport, 117 | u32 count, int c) 118 | { 119 | + //return saddr+daddr+sport+dport+count+c; 120 | + 121 | __u32 *tmp = __get_cpu_var(ipv4_cookie_scratch); 122 | 123 | memcpy(tmp + 4, syncookie_secret[c], sizeof(syncookie_secret[c])); 124 | @@ -160,26 +162,21 @@ 125 | * Generate a syncookie. mssp points to the mss, which is returned 126 | * rounded down to the value encoded in the cookie. 127 | */ 128 | -__u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb, __u16 *mssp) 129 | +__u32 __cookie_v4_init_sequence(__be32 saddr, __be32 daddr, __be16 sport, 130 | + __be16 dport, __u32 seq, __u16 *mssp) 131 | { 132 | - const struct iphdr *iph = ip_hdr(skb); 133 | - const struct tcphdr *th = tcp_hdr(skb); 134 | int mssind; 135 | const __u16 mss = *mssp; 136 | 137 | - tcp_synq_overflow(sk); 138 | - 139 | for (mssind = ARRAY_SIZE(msstab) - 1; mssind ; mssind--) 140 | if (mss >= msstab[mssind]) 141 | break; 142 | *mssp = msstab[mssind]; 143 | 144 | - NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_SYNCOOKIESSENT); 145 | - 146 | - return secure_tcp_syn_cookie(iph->saddr, iph->daddr, 147 | - th->source, th->dest, ntohl(th->seq), 148 | + return secure_tcp_syn_cookie(saddr, daddr, sport, dport, seq, 149 | jiffies / (HZ * 60), mssind); 150 | } 151 | +EXPORT_SYMBOL(__cookie_v4_init_sequence); 152 | 153 | /* 154 | * This (misnamed) value is the age of syncookie which is permitted. 155 | @@ -192,10 +189,9 @@ 156 | * Check if a ack sequence number is a valid syncookie. 157 | * Return the decoded mss if it is, or 0 if not. 158 | */ 159 | -static inline int cookie_check(struct sk_buff *skb, __u32 cookie) 160 | +int cookie_v4_check_sequence(const struct iphdr *iph, const struct tcphdr *th, 161 | + __u32 cookie) 162 | { 163 | - const struct iphdr *iph = ip_hdr(skb); 164 | - const struct tcphdr *th = tcp_hdr(skb); 165 | __u32 seq = ntohl(th->seq) - 1; 166 | __u32 mssind = check_tcp_syn_cookie(cookie, iph->saddr, iph->daddr, 167 | th->source, th->dest, seq, 168 | @@ -204,6 +200,7 @@ 169 | 170 | return mssind < ARRAY_SIZE(msstab) ? msstab[mssind] : 0; 171 | } 172 | +EXPORT_SYMBOL(cookie_v4_check_sequence); 173 | 174 | static inline struct sock *get_cookie_sock(struct sock *sk, struct sk_buff *skb, 175 | struct request_sock *req, 176 | @@ -284,7 +281,8 @@ 177 | goto out; 178 | 179 | if (tcp_synq_no_recent_overflow(sk) || 180 | - (mss = cookie_check(skb, cookie)) == 0) { 181 | + (mss = cookie_v4_check_sequence(ip_hdr(skb), tcp_hdr(skb), 182 | + cookie)) == 0) { 183 | NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_SYNCOOKIESFAILED); 184 | goto out; 185 | } 186 | diff -r -u a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c 187 | --- a/net/ipv4/tcp_ipv4.c 2013-02-20 07:15:40.000000000 +0400 188 | +++ b/net/ipv4/tcp_ipv4.c 2013-02-22 08:11:47.562858766 +0400 189 | @@ -1355,7 +1355,14 @@ 190 | TCP_ECN_create_request(req, tcp_hdr(skb)); 191 | 192 | if (want_cookie) { 193 | - isn = cookie_v4_init_sequence(sk, skb, &req->mss); 194 | + struct tcphdr *th; 195 | + 196 | + tcp_synq_overflow(sk); 197 | + NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_SYNCOOKIESSENT); 198 | + th = tcp_hdr(skb); 199 | + isn = __cookie_v4_init_sequence(saddr, daddr, th->source, 200 | + th->dest, ntohl(th->seq), 201 | + &req->mss); 202 | req->cookie_ts = tmp_opt.tstamp_ok; 203 | } else if (!isn) { 204 | struct inet_peer *peer = NULL; 205 | diff -r -u a/net/netfilter/Kconfig b/net/netfilter/Kconfig 206 | --- a/net/netfilter/Kconfig 2013-02-20 07:15:40.000000000 +0400 207 | +++ b/net/netfilter/Kconfig 2013-02-22 08:11:47.562858766 +0400 208 | @@ -607,6 +607,23 @@ 209 | 210 | To compile it as a module, choose M here. If unsure, say N. 211 | 212 | +config NETFILTER_XT_TARGET_SYNPROXY 213 | + tristate '"SYNPROXY" target support (EXPERIMENTAL)' 214 | + depends on EXPERIMENTAL 215 | + depends on SYN_COOKIES 216 | + depends on IP_NF_RAW 217 | + depends on NF_CONNTRACK 218 | + depends on NETFILTER_ADVANCED 219 | + help 220 | + The SYNPROXY target allows a raw rule to specify that some TCP 221 | + connections are relayed to protect the TCP servers from the SYN-flood 222 | + DoS attacks. Syn cookies is used to save the initial state, so no 223 | + conntrack is needed until the client side connection is established. 224 | + It frees the connection tracking system from creating/deleting 225 | + conntracks when SYN-flood DoS attack acts. 226 | + 227 | + To compile it as a module, choose M here. If unsure, say N. 228 | + 229 | config NETFILTER_XT_TARGET_TCPMSS 230 | tristate '"TCPMSS" target support' 231 | depends on (IPV6 || IPV6=n) 232 | diff -r -u a/net/netfilter/Makefile b/net/netfilter/Makefile 233 | --- a/net/netfilter/Makefile 2013-02-20 07:15:40.000000000 +0400 234 | +++ b/net/netfilter/Makefile 2013-02-22 08:11:47.566862424 +0400 235 | @@ -68,6 +68,7 @@ 236 | obj-$(CONFIG_NETFILTER_XT_TARGET_TEE) += xt_TEE.o 237 | obj-$(CONFIG_NETFILTER_XT_TARGET_TRACE) += xt_TRACE.o 238 | obj-$(CONFIG_NETFILTER_XT_TARGET_IDLETIMER) += xt_IDLETIMER.o 239 | +obj-$(CONFIG_NETFILTER_XT_TARGET_SYNPROXY) += xt_SYNPROXY.o 240 | 241 | # matches 242 | obj-$(CONFIG_NETFILTER_XT_MATCH_ADDRTYPE) += xt_addrtype.o 243 | diff -r -u a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c 244 | --- a/net/netfilter/nf_conntrack_core.c 2013-02-20 07:15:40.000000000 +0400 245 | +++ b/net/netfilter/nf_conntrack_core.c 2013-02-22 08:21:34.262939614 +0400 246 | @@ -876,6 +876,26 @@ 247 | return ct; 248 | } 249 | 250 | +static inline unsigned int syn_proxy_pre_call(int protonum, struct sk_buff *skb, 251 | + struct nf_conn *ct, 252 | + enum ip_conntrack_info ctinfo) 253 | +{ 254 | + unsigned int ret = NF_ACCEPT; 255 | +#if defined(CONFIG_NETFILTER_XT_TARGET_SYNPROXY) || \ 256 | + defined(CONFIG_NETFILTER_XT_TARGET_SYNPROXY_MODULE) 257 | + unsigned int (*syn_proxy)(struct sk_buff *, struct nf_conn *, 258 | + enum ip_conntrack_info); 259 | + 260 | + if (protonum == IPPROTO_TCP) { 261 | + syn_proxy = rcu_dereference(syn_proxy_pre_hook); 262 | + if (syn_proxy) 263 | + ret = syn_proxy(skb, ct, ctinfo); 264 | + } 265 | +#endif 266 | + 267 | + return ret; 268 | +} 269 | + 270 | unsigned int 271 | nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum, 272 | struct sk_buff *skb) 273 | @@ -934,8 +954,9 @@ 274 | l3proto, l4proto, &set_reply, &ctinfo); 275 | if (!ct) { 276 | /* Not valid part of a connection */ 277 | - NF_CT_STAT_INC_ATOMIC(net, invalid); 278 | - ret = NF_ACCEPT; 279 | + ret = syn_proxy_pre_call(protonum, skb, NULL, ctinfo); 280 | + if (ret == NF_ACCEPT) 281 | + NF_CT_STAT_INC_ATOMIC(net, invalid); 282 | goto out; 283 | } 284 | 285 | @@ -948,6 +969,9 @@ 286 | 287 | NF_CT_ASSERT(skb->nfct); 288 | 289 | + ret = syn_proxy_pre_call(protonum, skb, ct, ctinfo); 290 | + if (ret != NF_ACCEPT) 291 | + goto out; 292 | ret = l4proto->packet(ct, skb, dataoff, ctinfo, pf, hooknum); 293 | if (ret <= 0) { 294 | /* Invalid: inverse of the return code tells 295 | @@ -1564,6 +1588,17 @@ 296 | u32 seq); 297 | EXPORT_SYMBOL_GPL(nf_ct_nat_offset); 298 | 299 | +#if defined(CONFIG_NETFILTER_XT_TARGET_SYNPROXY) || \ 300 | + defined(CONFIG_NETFILTER_XT_TARGET_SYNPROXY_MODULE) 301 | +unsigned int (*syn_proxy_pre_hook)(struct sk_buff *skb, struct nf_conn *ct, 302 | + enum ip_conntrack_info ctinfo); 303 | +EXPORT_SYMBOL(syn_proxy_pre_hook); 304 | + 305 | +unsigned int (*syn_proxy_post_hook)(struct sk_buff *skb, struct nf_conn *ct, 306 | + enum ip_conntrack_info ctinfo); 307 | +EXPORT_SYMBOL(syn_proxy_post_hook); 308 | +#endif 309 | + 310 | int nf_conntrack_init(struct net *net) 311 | { 312 | int ret; 313 | @@ -1584,6 +1619,12 @@ 314 | 315 | /* Howto get NAT offsets */ 316 | RCU_INIT_POINTER(nf_ct_nat_offset, NULL); 317 | +#if defined(CONFIG_NETFILTER_XT_TARGET_SYNPROXY) || \ 318 | + defined(CONFIG_NETFILTER_XT_TARGET_SYNPROXY_MODULE) 319 | + rcu_assign_pointer(syn_proxy_pre_hook, NULL); 320 | + rcu_assign_pointer(syn_proxy_post_hook, NULL); 321 | +#endif 322 | + 323 | } 324 | return 0; 325 | 326 | --- a/net/netfilter/xt_SYNPROXY.c 2013-02-28 20:46:14.522497476 +0400 327 | +++ b/net/netfilter/xt_SYNPROXY.c 2013-02-26 01:44:40.406757441 +0400 328 | @@ -0,0 +1,1021 @@ 329 | +/* (C) 2010- Changli Gao 330 | + * 331 | + * This program is free software; you can redistribute it and/or modify 332 | + * it under the terms of the GNU General Public License version 2 as 333 | + * published by the Free Software Foundation. 334 | + * 335 | + * It bases on ipt_REJECT.c 336 | + */ 337 | +#define pr_fmt(fmt) "SYNPROXY: " fmt 338 | +#include 339 | +#include 340 | +#include 341 | +#include 342 | +#include 343 | +#include 344 | +#include 345 | +#include 346 | +#include 347 | +#include 348 | +#include 349 | +#include 350 | +#include 351 | +#include 352 | +#include 353 | +#include 354 | +#include 355 | +#include 356 | + 357 | + 358 | +#define NIPQUAD(addr) \ 359 | + ((unsigned char *)&addr)[0], \ 360 | + ((unsigned char *)&addr)[1], \ 361 | + ((unsigned char *)&addr)[2], \ 362 | + ((unsigned char *)&addr)[3] 363 | + 364 | +MODULE_LICENSE("GPL"); 365 | +MODULE_AUTHOR("Changli Gao "); 366 | +MODULE_DESCRIPTION("Xtables: \"SYNPROXY\" target for IPv4"); 367 | +MODULE_ALIAS("ipt_SYNPROXY"); 368 | + 369 | +enum { 370 | + TCP_SEND_FLAG_NOTRACE = 0x1, 371 | + TCP_SEND_FLAG_SYNCOOKIE = 0x2, 372 | + TCP_SEND_FLAG_ACK2SYN = 0x4, 373 | +}; 374 | + 375 | +struct syn_proxy_state { 376 | + u16 seq_inited; 377 | + __be16 window; 378 | + u32 seq_diff; 379 | +}; 380 | + 381 | +u_int16_t 382 | +in_cksum_shouldbe(u_int16_t sum, u_int16_t computed_sum) 383 | +{ 384 | + u_int32_t shouldbe; 385 | + 386 | + /* 387 | + * The value that should have gone into the checksum field 388 | + * is the negative of the value gotten by summing up everything 389 | + * *but* the checksum field. 390 | + * 391 | + * We can compute that by subtracting the value of the checksum 392 | + * field from the sum of all the data in the packet, and then 393 | + * computing the negative of that value. 394 | + * 395 | + * "sum" is the value of the checksum field, and "computed_sum" 396 | + * is the negative of the sum of all the data in the packets, 397 | + * so that's -(-computed_sum - sum), or (sum + computed_sum). 398 | + * 399 | + * All the arithmetic in question is one's complement, so the 400 | + * addition must include an end-around carry; we do this by 401 | + * doing the arithmetic in 32 bits (with no sign-extension), 402 | + * and then adding the upper 16 bits of the sum, which contain 403 | + * the carry, to the lower 16 bits of the sum, and then do it 404 | + * again in case *that* sum produced a carry. 405 | + * 406 | + * As RFC 1071 notes, the checksum can be computed without 407 | + * byte-swapping the 16-bit words; summing 16-bit words 408 | + * on a big-endian machine gives a big-endian checksum, which 409 | + * can be directly stuffed into the big-endian checksum fields 410 | + * in protocol headers, and summing words on a little-endian 411 | + * machine gives a little-endian checksum, which must be 412 | + * byte-swapped before being stuffed into a big-endian checksum 413 | + * field. 414 | + * 415 | + * "computed_sum" is a network-byte-order value, so we must put 416 | + * it in host byte order before subtracting it from the 417 | + * host-byte-order value from the header; the adjusted checksum 418 | + * will be in host byte order, which is what we'll return. 419 | + */ 420 | + shouldbe = sum; 421 | + shouldbe += ntohs(computed_sum); 422 | + shouldbe = (shouldbe & 0xFFFF) + (shouldbe >> 16); 423 | + shouldbe = (shouldbe & 0xFFFF) + (shouldbe >> 16); 424 | + return shouldbe; 425 | +} 426 | + 427 | + 428 | +u_short 429 | +in_cksum(const u_short *addr, register u_int len, int csum) 430 | +{ 431 | + int nleft = len; 432 | + const u_short *w = addr; 433 | + u_short answer; 434 | + int sum = csum; 435 | + 436 | + /* 437 | + * Our algorithm is simple, using a 32 bit accumulator (sum), 438 | + * we add sequential 16 bit words to it, and at the end, fold 439 | + * back all the carry bits from the top 16 bits into the lower 440 | + * 16 bits. 441 | + */ 442 | + while (nleft > 1) { 443 | + sum += *w++; 444 | + nleft -= 2; 445 | + } 446 | + if (nleft == 1) 447 | + sum += ntohs(*(u_char *)w<<8); 448 | + 449 | + /* 450 | + * add back carry outs from top 16 bits to low 16 bits 451 | + */ 452 | + sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ 453 | + sum += (sum >> 16); /* add carry */ 454 | + answer = ~sum; /* truncate to 16 bits */ 455 | + return (answer); 456 | +} 457 | + 458 | +u_short tcp_cksum(register const struct iphdr * iph, 459 | + register const struct tcphdr *th, 460 | + register u_int len) 461 | +{ 462 | + union phu { 463 | + struct phdr { 464 | + u_int32_t src; 465 | + u_int32_t dst; 466 | + u_char mbz; 467 | + u_char proto; 468 | + u_int16_t len; 469 | + } ph; 470 | + u_int16_t pa[6]; 471 | + } phu; 472 | + const u_int16_t *sp; 473 | + 474 | + /* pseudo-header.. */ 475 | + phu.ph.len = htons((u_int16_t)len); 476 | + phu.ph.mbz = 0; 477 | + phu.ph.proto = IPPROTO_TCP; 478 | + memcpy(&phu.ph.src, &iph->saddr, sizeof(u_int32_t)); 479 | + memcpy(&phu.ph.dst, &iph->daddr, sizeof(u_int32_t)); 480 | + //printk("sizeof phu %d src %d dst %d proto %d\n",sizeof(struct phdr),phu.ph.src,phu.ph.dst,phu.ph.proto); 481 | + //if (iph->ihl == 5) 482 | + // memcpy(&phu.ph.dst, &iph->saddr, sizeof(u_int32_t)); 483 | + //else 484 | + // phu.ph.dst = iph->daddr; 485 | + 486 | + sp = &phu.pa[0]; 487 | + //printk("pseudo header sum %d\n",sp[0]+sp[1]+sp[3]+sp[3]+sp[4]+sp[5]); 488 | + return in_cksum((u_short *)th, len, 489 | + sp[0]+sp[1]+sp[2]+sp[3]+sp[4]+sp[5]); 490 | +} 491 | + 492 | +static int get_mtu(const struct dst_entry *dst) 493 | +{ 494 | + int mtu; 495 | + 496 | + mtu = dst_mtu(dst); 497 | + if (mtu) 498 | + return mtu; 499 | + 500 | + return dst->dev ? dst->dev->mtu : 0; 501 | +} 502 | + 503 | +static int get_advmss(const struct dst_entry *dst) 504 | +{ 505 | + int advmss; 506 | + 507 | + advmss = dst_metric(dst, RTAX_ADVMSS); 508 | + if (advmss) 509 | + return advmss; 510 | + advmss = get_mtu(dst); 511 | + if (advmss) 512 | + return advmss - (sizeof(struct iphdr) + sizeof(struct tcphdr)); 513 | + 514 | + return TCP_MSS_DEFAULT; 515 | +} 516 | + 517 | +static int syn_proxy_route(struct sk_buff *skb, struct net *net, u16 *pmss) 518 | +{ 519 | + const struct iphdr *iph = ip_hdr(skb); 520 | + struct rtable *rt; 521 | + struct flowi4 fl = {}; 522 | + unsigned int type; 523 | + int flags = 0; 524 | + int err; 525 | + u16 mss; 526 | + 527 | + type = inet_addr_type(net, iph->saddr); 528 | + if (type != RTN_LOCAL) { 529 | + type = inet_addr_type(net, iph->daddr); 530 | + if (type == RTN_LOCAL) 531 | + flags |= FLOWI_FLAG_ANYSRC; 532 | + } 533 | + 534 | + if (type == RTN_LOCAL) { 535 | + fl.daddr = iph->daddr; 536 | + fl.saddr = iph->saddr; 537 | + fl.flowi4_tos = RT_TOS(iph->tos); 538 | + //fl.flags = flags; 539 | + fl.flowi4_flags = flags; 540 | + rt = ip_route_output_key(net, &fl); 541 | + if (IS_ERR(rt)) 542 | + goto out; 543 | + 544 | + skb_dst_set(skb, &rt->dst); 545 | + } else { 546 | + /* non-local src, find valid iif to satisfy 547 | + * rp-filter when calling ip_route_input. */ 548 | + fl.daddr = iph->saddr; 549 | + rt = ip_route_output_key(net, &fl); 550 | + if (IS_ERR(rt)) 551 | + goto out; 552 | + 553 | + err = ip_route_input(skb, iph->daddr, iph->saddr, 554 | + RT_TOS(iph->tos), rt->dst.dev); 555 | + if (err) { 556 | + dst_release(&rt->dst); 557 | + goto out; 558 | + } 559 | + if (pmss) { 560 | + mss = get_advmss(&rt->dst); 561 | + if (*pmss > mss) 562 | + *pmss = mss; 563 | + } 564 | + dst_release(&rt->dst); 565 | + } 566 | + 567 | + err = skb_dst(skb)->error; 568 | + if (!err && pmss) { 569 | + mss = get_advmss(skb_dst(skb)); 570 | + if (*pmss > mss) 571 | + *pmss = mss; 572 | + } 573 | + 574 | +out: 575 | + return err; 576 | +} 577 | + 578 | +static int send_cookie (struct sk_buff *skb, u16 mss) 579 | +{ 580 | + int err, len, ret; 581 | + struct tcphdr *th; 582 | + 583 | + //printk(KERN_DEBUG "send_cookie begin\n"); 584 | +/// printk(KERN_DEBUG "skb->dev %s\n",skb->dev->name); 585 | + //if (!skb_make_writable(skb, ip_hdrlen(skb) + sizeof(*th))) { 586 | + //if (!skb_make_writable(skb, skb->len + ETH_HLEN )) { 587 | + //if (!skb_make_writable(skb, 0)) { 588 | + // printk(KERN_DEBUG "skb_make_writable fail\n"); 589 | + //} 590 | + 591 | + //struct net_device *dev_eth0 = dev_get_by_name(&init_net,"eth0"); 592 | + //skb->dev = dev_eth0; 593 | +/// printk(KERN_DEBUG "skb->dev %s\n",skb->dev->name); 594 | +/// printk(KERN_DEBUG "skb->len %d,%d\n",skb->len,ip_hdrlen(skb) + sizeof(*th) + ETH_HLEN); 595 | +/// printk(KERN_DEBUG "before pkt_type %d/%d\n",skb->pkt_type,PACKET_OUTGOING); 596 | + skb->pkt_type = PACKET_OUTGOING; 597 | +/// printk(KERN_DEBUG "after pkt_type %d/%d\n",skb->pkt_type,PACKET_OUTGOING); 598 | + 599 | + // save old data offset 600 | + struct iphdr *iph = (struct iphdr *)skb->data; 601 | + th = (struct tcphdr *)(skb->data + iph->ihl * 4); 602 | + 603 | + //rcu_read_lock(); 604 | + 605 | +// skb->data = (unsigned char *)skb->mac_header; 606 | + //skb->len += ETH_HLEN; 607 | + 608 | + /*changing Mac address */ 609 | + unsigned char srcaddr[6]; 610 | + unsigned char dstaddr[6]; 611 | + struct ethhdr *eth = eth_hdr(skb); 612 | + struct ethhdr *mh = eth; 613 | +/// printk(KERN_DEBUG "eth type: %d, %d\n", ntohs(eth->h_proto)); 614 | +/// printk(KERN_DEBUG "eth before src/dst: %d, %d\n", eth->h_source, eth->h_dest); 615 | +/// printk(KERN_DEBUG "Source MAC=%x:%x:%x:%x:%x:%x\n",mh->h_source[0],mh->h_source[1],mh->h_source[2],mh->h_source[3],mh->h_source[4],mh->h_source[5]); 616 | +/// printk(KERN_DEBUG "DST MAC=%x:%x:%x:%x:%x:%x\n",mh->h_dest[0],mh->h_dest[1],mh->h_dest[2],mh->h_dest[3],mh->h_dest[4],mh->h_dest[5]); 617 | + memcpy(srcaddr, eth->h_source, ETH_ALEN); 618 | + memcpy(dstaddr, eth->h_dest, ETH_ALEN); 619 | + 620 | + if (!eth_header(skb, skb->dev, ntohs(eth->h_proto), srcaddr, 0, ntohs(eth->h_proto))) { 621 | +/// printk(KERN_DEBUG "eth_header error\n"); 622 | + } 623 | +/// printk(KERN_DEBUG "skb->len %d,%d\n",skb->len,ip_hdrlen(skb) + sizeof(*th) + ETH_HLEN); 624 | + // get new eth hdr 625 | + eth = skb->data; 626 | + 627 | + 628 | +// memcpy(eth->h_source, eth->h_dest, ETH_ALEN); 629 | +// memcpy(eth->h_dest, srcaddr, ETH_ALEN); 630 | +// memset(eth->h_dest, 0, ETH_ALEN); 631 | + 632 | +/// printk(KERN_DEBUG "eth after src/dst: %d, %d\n", eth->h_source, eth->h_dest); 633 | + 634 | + //rcu_read_unlock(); 635 | + 636 | + /*changing IP header */ 637 | + //struct iphdr *iph = (struct iphdr *)skb->data; 638 | + // restore old iph pointer 639 | + //skb->data = iph; 640 | + //printk(KERN_DEBUG "iph before src/dst: %d.%d.%d.%d/%d.%d.%d.%d\n",NIPQUAD(iph->saddr), NIPQUAD(iph->daddr)); 641 | + //iph->version = 4; 642 | +/// iph->ihl = sizeof(*iph) / 4; 643 | +/// iph->id = 0; 644 | + /* changing ip address */ 645 | + __be32 ipaddr = iph->saddr; 646 | + iph->saddr = iph->daddr; 647 | + iph->daddr = ipaddr; 648 | +/// iph->tot_len = htons(40); 649 | + //printk(KERN_DEBUG "iph->tot_len %d\n",ntohs(iph->tot_len)); 650 | +/// iph->check = 0; 651 | +/// ip_send_check(iph); 652 | + 653 | + //printk(KERN_DEBUG "iph after src/dst: %d.%d.%d.%d/%d.%d.%d.%d\n",NIPQUAD(iph->saddr), NIPQUAD(iph->daddr)); 654 | + //printk(KERN_DEBUG "iph after src/dst: %d/%d\n",iph->saddr, iph->daddr); 655 | + 656 | + /* changing TCP header */ 657 | + //len = sizeof(*th); 658 | +// if (mss) 659 | +// len += TCPOLEN_MSS; 660 | + //th = (struct tcphdr *)(skb->data + iph->ihl * 4); 661 | +/// printk(KERN_DEBUG "offsets eth/iph/tcp: %d/%d/%d\n", eth, iph, th); 662 | +/// printk(KERN_DEBUG "th before src/dst: %d, %d\n", ntohs(th->source),ntohs(th->dest)); 663 | + th->ack_seq = htonl(ntohl(th->seq) + 1); 664 | + tcp_flag_byte(th) = TCPHDR_SYN | TCPHDR_ACK; 665 | + __be16 tcp_port = th->source; 666 | + th->source = th->dest; 667 | + th->dest = tcp_port; 668 | + //th->doff = len / 4; 669 | +/// th->doff = sizeof(*th)/4; 670 | + //printk(KERN_DEBUG "th after src/dst: %d, %d, doff %d\n", ntohs(th->source),ntohs(th->dest),th->doff*4); 671 | + 672 | + //printk("mss default: %hu %d\n",mss,TCP_MSS_DEFAULT); 673 | + 674 | + if (mss) { 675 | + th->seq = htonl(__cookie_v4_init_sequence(iph->daddr, iph->saddr, 676 | + th->dest,th->source, 677 | + ntohl(th->ack_seq) - 1, 678 | + &mss)); 679 | + } else { 680 | + 681 | + mss = TCP_MSS_DEFAULT; 682 | + th->seq = htonl(__cookie_v4_init_sequence(iph->daddr, iph->saddr, 683 | + th->dest,th->source, 684 | + ntohl(th->ack_seq) - 1, 685 | + &mss)); 686 | + mss = 0; 687 | + } 688 | + 689 | + 690 | + if (mss) 691 | + * (__force __be32 *)(th + 1) = htonl((TCPOPT_MSS << 24) | 692 | + (TCPOLEN_MSS << 16) | 693 | + mss); 694 | + 695 | + 696 | + //tcp_v4_gso_send_check(skb); 697 | + //skb->ip_summed = CHECKSUM_PARTIAL; 698 | + //skb->ip_summed = CHECKSUM_COMPLETE; 699 | + //skb->data = iph; 700 | + //th->check = 0; 701 | + //th->seq = htonl(1); 702 | + //inet_proto_csum_replace4(&th->check, skb, th->seq, htonl(1), 0); 703 | + //skb->data = eth; 704 | + 705 | + //th->check = ~tcp_v4_check(ntohs(iph->tot_len), iph->saddr, iph->daddr, 0); 706 | + //skb->csum_start = (unsigned char *)th - skb->head; 707 | + //skb->csum_offset = offsetof(struct tcphdr, check); 708 | + 709 | + //skb->len = 14+40; 710 | + th->check = 0; 711 | + th->check = tcp_cksum(iph, th, ntohs(iph->tot_len)-iph->ihl*4); 712 | + 713 | + //th->check = in_cksum((u_short *)th, ntohs(iph->tot_len),0); 714 | + //th->check = in_cksum_shouldbe(,th->check); 715 | + //printk("checksumm: len %d, th->doff %d, tot_len %d, ch1 0x%04x\n",len,th->doff*4,ntohs(iph->tot_len),ntohs(th->check)); 716 | + 717 | + //skb->ip_summed = CHECKSUM_UNNECESSARY; 718 | + //printk(KERN_DEBUG "th size %d\n", sizeof(*th)); 719 | + //printk(KERN_DEBUG "iph size %d\n", sizeof(*iph)); 720 | + 721 | + 722 | + //len = skb->len - ip_hdrlen(skb); 723 | + //th->check = ~tcp_v4_check(40, iph->saddr, iph->daddr, 724 | + // csum_partial((char *)th, 32, 0)); 725 | + 726 | + //skb->len = ntohs(iph->tot_len) + ETH_HLEN; 727 | + //th->check = 0; 728 | + //skb->csum = csum_partial(th, ntohs(iph->tot_len) - iph->ihl*4, 0); 729 | + //th->check = ~tcp_v4_check(ntohs(iph->tot_len) - iph->ihl, iph->saddr, iph->daddr, skb->csum); 730 | + //th->check = ~tcp_v4_check(20, iph->saddr, iph->daddr, 0); 731 | + //printk(KERN_DEBUG "skb len %d, csum %d, len - ihl %d, ihl %d, check %04x\n",skb->len,skb->csum,ntohs(iph->tot_len) - iph->ihl*4,iph->ihl,ntohs(th->check)); 732 | + 733 | + //skb->ip_summed = CHECKSUM_NONE; 734 | + 735 | + 736 | +/* 737 | + 738 | + if (skb->len > get_mtu(skb_dst(skb))) { 739 | + if (printk_ratelimit()) 740 | + pr_warning("%s has smaller mtu: %d\n", 741 | + skb_dst(skb)->dev->name, 742 | + get_mtu(skb_dst(skb))); 743 | + err = -EINVAL; 744 | + goto err_out; 745 | + } 746 | +*/ 747 | + skb->nfct = &nf_ct_untracked_get()->ct_general; 748 | + skb->nfctinfo = IP_CT_NEW; 749 | + nf_conntrack_get(skb->nfct); 750 | + 751 | + /* send the packet */ 752 | + //local_bh_enable(); 753 | + ret = dev_queue_xmit(skb); 754 | + if (ret != NET_XMIT_SUCCESS) { 755 | + printk(KERN_DEBUG "dev_queue_xmit returned error: %d\n", ret); 756 | + } 757 | +/* 758 | +else { 759 | + printk(KERN_DEBUG "dev_queue_xmit returned OK: %d\n", ret); 760 | + } 761 | +*/ 762 | + return ret; 763 | +} 764 | + 765 | +///////////////////////////////////////////////////////////// 766 | + 767 | + 768 | +static int tcp_send(__be32 src, __be32 dst, __be16 sport, __be16 dport, 769 | + u32 seq, u32 ack_seq, __be16 window, u16 mss, u8 tcp_flags, 770 | + u8 tos, struct net_device *dev, int flags, 771 | + struct sk_buff *oskb) 772 | +{ 773 | + struct sk_buff *skb; 774 | + struct iphdr *iph; 775 | + struct tcphdr *th; 776 | + int err, len; 777 | + 778 | + len = sizeof(*th); 779 | + if (mss) 780 | + len += TCPOLEN_MSS; 781 | + 782 | + skb = NULL; 783 | + /* caller must give me a large enough oskb */ 784 | + if (oskb) { 785 | + unsigned char *odata = oskb->data; 786 | + 787 | + if (skb_recycle_check(oskb, 0)) { 788 | + oskb->data = odata; 789 | + skb_reset_tail_pointer(oskb); 790 | + skb = oskb; 791 | + pr_debug("recycle skb\n"); 792 | + } 793 | + } 794 | + if (!skb) { 795 | + skb = alloc_skb(LL_MAX_HEADER + sizeof(*iph) + len, GFP_ATOMIC); 796 | + if (!skb) { 797 | + err = -ENOMEM; 798 | + goto out; 799 | + } 800 | + skb_reserve(skb, LL_MAX_HEADER); 801 | + } 802 | + 803 | + skb_reset_network_header(skb); 804 | + if (!(flags & TCP_SEND_FLAG_ACK2SYN) || skb != oskb) { 805 | + iph = (struct iphdr *)skb_put(skb, sizeof(*iph)); 806 | + iph->version = 4; 807 | + iph->ihl = sizeof(*iph) / 4; 808 | + iph->tos = tos; 809 | + /* tot_len is set in ip_local_out() */ 810 | + iph->id = 0; 811 | + iph->frag_off = htons(IP_DF); 812 | + iph->protocol = IPPROTO_TCP; 813 | + iph->saddr = src; 814 | + iph->daddr = dst; 815 | + th = (struct tcphdr *)skb_put(skb, len); 816 | + th->source = sport; 817 | + th->dest = dport; 818 | + } else { 819 | + iph = (struct iphdr *)skb->data; 820 | + iph->id = 0; 821 | + iph->frag_off = htons(IP_DF); 822 | + skb_put(skb, iph->ihl * 4 + len); 823 | + th = (struct tcphdr *)(skb->data + iph->ihl * 4); 824 | + } 825 | + 826 | + th->seq = htonl(seq); 827 | + th->ack_seq = htonl(ack_seq); 828 | + tcp_flag_byte(th) = tcp_flags; 829 | + th->doff = len / 4; 830 | + th->window = window; 831 | + th->urg_ptr = 0; 832 | + 833 | + skb->protocol = htons(ETH_P_IP); 834 | + 835 | + if ((flags & TCP_SEND_FLAG_SYNCOOKIE) && mss) 836 | + err = syn_proxy_route(skb, dev_net(dev), &mss); 837 | + else 838 | + err = syn_proxy_route(skb, dev_net(dev), NULL); 839 | + if (err) 840 | + goto err_out; 841 | + 842 | + 843 | + if ((flags & TCP_SEND_FLAG_SYNCOOKIE)) { 844 | + if (mss) { 845 | + th->seq = htonl(__cookie_v4_init_sequence(dst, src, 846 | + dport, sport, 847 | + ack_seq - 1, 848 | + &mss)); 849 | + } else { 850 | + mss = TCP_MSS_DEFAULT; 851 | + th->seq = htonl(__cookie_v4_init_sequence(dst, src, 852 | + dport, sport, 853 | + ack_seq - 1, 854 | + &mss)); 855 | + mss = 0; 856 | + } 857 | + } 858 | + 859 | + if (mss) 860 | + * (__force __be32 *)(th + 1) = htonl((TCPOPT_MSS << 24) | 861 | + (TCPOLEN_MSS << 16) | 862 | + mss); 863 | + skb->ip_summed = CHECKSUM_PARTIAL; 864 | + th->check = ~tcp_v4_check(len, src, dst, 0); 865 | + skb->csum_start = (unsigned char *)th - skb->head; 866 | + skb->csum_offset = offsetof(struct tcphdr, check); 867 | + 868 | + //if (!(flags & TCP_SEND_FLAG_ACK2SYN) || skb != oskb) 869 | + // iph->ttl = dst_metric(skb_dst(skb), RTAX_HOPLIMIT); 870 | + 871 | + if (skb->len > get_mtu(skb_dst(skb))) { 872 | + if (printk_ratelimit()) 873 | + pr_warning("%s has smaller mtu: %d\n", 874 | + skb_dst(skb)->dev->name, 875 | + get_mtu(skb_dst(skb))); 876 | + err = -EINVAL; 877 | + goto err_out; 878 | + } 879 | + 880 | + if ((flags & TCP_SEND_FLAG_NOTRACE)) { 881 | + skb->nfct = &nf_ct_untracked_get()->ct_general; 882 | + skb->nfctinfo = IP_CT_NEW; 883 | + nf_conntrack_get(skb->nfct); 884 | + } 885 | + 886 | + pr_debug("ip_local_out: %pI4n:%hu -> %pI4n:%hu (seq=%u, " 887 | + "ack_seq=%u mss=%hu flags=%hhx)\n", &src, ntohs(th->source), 888 | + &dst, ntohs(th->dest), ntohl(th->seq), ack_seq, mss, 889 | + tcp_flags); 890 | + 891 | + err = ip_local_out(skb); 892 | + if (err > 0) 893 | + err = net_xmit_errno(err); 894 | + 895 | + pr_debug("ip_local_out: return with %d\n", err); 896 | +out: 897 | + if (oskb && oskb != skb) 898 | + kfree_skb(oskb); 899 | + 900 | + return err; 901 | + 902 | +err_out: 903 | + kfree_skb(skb); 904 | + goto out; 905 | +} 906 | + 907 | +static int get_mss(u8 *data, int len) 908 | +{ 909 | + u8 olen; 910 | + 911 | + while (len >= TCPOLEN_MSS) { 912 | + switch (data[0]) { 913 | + case TCPOPT_EOL: 914 | + return 0; 915 | + case TCPOPT_NOP: 916 | + data++; 917 | + len--; 918 | + break; 919 | + case TCPOPT_MSS: 920 | + if (data[1] != TCPOLEN_MSS) 921 | + return -EINVAL; 922 | + return get_unaligned_be16(data + 2); 923 | + default: 924 | + olen = data[1]; 925 | + if (olen < 2 || olen > len) 926 | + return -EINVAL; 927 | + data += olen; 928 | + len -= olen; 929 | + break; 930 | + } 931 | + } 932 | + 933 | + return 0; 934 | +} 935 | + 936 | +static DEFINE_PER_CPU(struct syn_proxy_state, syn_proxy_state); 937 | + 938 | +/* syn_proxy_pre isn't under the protection of nf_conntrack_proto_tcp.c */ 939 | +static unsigned int syn_proxy_pre(struct sk_buff *skb, struct nf_conn *ct, 940 | + enum ip_conntrack_info ctinfo) 941 | +{ 942 | + struct syn_proxy_state *state; 943 | + struct iphdr *iph; 944 | + struct tcphdr *th, _th; 945 | + 946 | + /* only support IPv4 now */ 947 | + iph = ip_hdr(skb); 948 | + if (iph->version != 4) 949 | + return NF_ACCEPT; 950 | + 951 | + th = skb_header_pointer(skb, iph->ihl * 4, sizeof(_th), &_th); 952 | + if (th == NULL) 953 | + return NF_DROP; 954 | + 955 | + if (!ct || !nf_ct_is_confirmed(ct)) { 956 | + int ret; 957 | + 958 | + if (!th->syn && th->ack) { 959 | + u16 mss; 960 | + struct sk_buff *rec_skb; 961 | + 962 | + mss = cookie_v4_check_sequence(iph, th, 963 | + ntohl(th->ack_seq) - 1); 964 | + //if (!mss) { 965 | + // printk("cookie_v4_check_sequence fail: mss=%hu\n",mss); 966 | + //} 967 | + if (!mss) 968 | + return NF_ACCEPT; 969 | + 970 | + pr_debug("%pI4n:%hu -> %pI4n:%hu(mss=%hu)\n", 971 | + &iph->saddr, ntohs(th->source), 972 | + &iph->daddr, ntohs(th->dest), mss); 973 | + 974 | + if (skb_tailroom(skb) < TCPOLEN_MSS && 975 | + skb->len < iph->ihl * 4 + sizeof(*th) + TCPOLEN_MSS) 976 | + rec_skb = NULL; 977 | + else 978 | + rec_skb = skb; 979 | + 980 | + local_bh_disable(); 981 | + state = &__get_cpu_var(syn_proxy_state); 982 | + state->seq_inited = 1; 983 | + state->window = th->window; 984 | + state->seq_diff = ntohl(th->ack_seq) - 1; 985 | + if (rec_skb) 986 | + tcp_send(iph->saddr, iph->daddr, 0, 0, 987 | + ntohl(th->seq) - 1, 0, th->window, 988 | + mss, TCPHDR_SYN, 0, skb->dev, 989 | + TCP_SEND_FLAG_ACK2SYN, rec_skb); 990 | + else 991 | + tcp_send(iph->saddr, iph->daddr, th->source, 992 | + th->dest, ntohl(th->seq) - 1, 0, 993 | + th->window, mss, TCPHDR_SYN, 994 | + iph->tos, skb->dev, 0, NULL); 995 | + state->seq_inited = 0; 996 | + local_bh_enable(); 997 | + 998 | + if (!rec_skb) 999 | + kfree_skb(skb); 1000 | + 1001 | + return NF_STOLEN; 1002 | + } 1003 | + 1004 | + if (!ct || !th->syn || th->ack) 1005 | + return NF_ACCEPT; 1006 | + 1007 | + ret = NF_ACCEPT; 1008 | + local_bh_disable(); 1009 | + state = &__get_cpu_var(syn_proxy_state); 1010 | + if (state->seq_inited) { 1011 | + struct syn_proxy_state *nstate; 1012 | + 1013 | + nstate = nf_ct_ext_add(ct, NF_CT_EXT_SYNPROXY, 1014 | + GFP_ATOMIC); 1015 | + if (nstate != NULL) { 1016 | + nstate->seq_inited = 0; 1017 | + nstate->window = state->window; 1018 | + nstate->seq_diff = state->seq_diff; 1019 | + pr_debug("seq_diff: %u\n", nstate->seq_diff); 1020 | + } else { 1021 | + ret = NF_DROP; 1022 | + } 1023 | + } 1024 | + local_bh_enable(); 1025 | + 1026 | + return ret; 1027 | + } 1028 | + 1029 | + state = nf_ct_ext_find(ct, NF_CT_EXT_SYNPROXY); 1030 | + if (!state) 1031 | + return NF_ACCEPT; 1032 | + 1033 | + if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) { 1034 | + __be32 newack; 1035 | + 1036 | + /* don't need to mangle duplicate SYN packets */ 1037 | + if (th->syn && !th->ack) 1038 | + return NF_ACCEPT; 1039 | + if (!skb_make_writable(skb, ip_hdrlen(skb) + sizeof(*th))) 1040 | + return NF_DROP; 1041 | + th = (struct tcphdr *)(skb->data + ip_hdrlen(skb)); 1042 | + newack = htonl(ntohl(th->ack_seq) - state->seq_diff); 1043 | + inet_proto_csum_replace4(&th->check, skb, th->ack_seq, newack, 1044 | + 0); 1045 | + pr_debug("alter ack seq: %u -> %u\n", 1046 | + ntohl(th->ack_seq), ntohl(newack)); 1047 | + th->ack_seq = newack; 1048 | + } else { 1049 | + /* Simultaneous open ? Oh, no. The connection between 1050 | + * client and us is established. */ 1051 | + if (th->syn && !th->ack) 1052 | + return NF_DROP; 1053 | + } 1054 | + 1055 | + return NF_ACCEPT; 1056 | +} 1057 | + 1058 | +static unsigned int syn_proxy_mangle_pkt(struct sk_buff *skb, struct iphdr *iph, 1059 | + struct tcphdr *th, u32 seq_diff) 1060 | +{ 1061 | + __be32 new; 1062 | + int olen; 1063 | + 1064 | + if (skb->len < (iph->ihl + th->doff) * 4) 1065 | + return NF_DROP; 1066 | + if (!skb_make_writable(skb, (iph->ihl + th->doff) * 4)) 1067 | + return NF_DROP; 1068 | + iph = (struct iphdr *)(skb->data); 1069 | + th = (struct tcphdr *)(skb->data + iph->ihl * 4); 1070 | + 1071 | + new = tcp_flag_word(th) & (~TCP_FLAG_SYN); 1072 | + inet_proto_csum_replace4(&th->check, skb, tcp_flag_word(th), new, 0); 1073 | + tcp_flag_word(th) = new; 1074 | + 1075 | + new = htonl(ntohl(th->seq) + seq_diff); 1076 | + inet_proto_csum_replace4(&th->check, skb, th->seq, new, 0); 1077 | + pr_debug("alter seq: %u -> %u\n", ntohl(th->seq), ntohl(new)); 1078 | + th->seq = new; 1079 | + 1080 | + olen = th->doff - sizeof(*th) / 4; 1081 | + if (olen) { 1082 | + __be32 *opt; 1083 | + 1084 | + opt = (__force __be32 *)(th + 1); 1085 | +#define TCPOPT_EOL_WORD ((TCPOPT_EOL << 24) + (TCPOPT_EOL << 16) + \ 1086 | + (TCPOPT_EOL << 8) + TCPOPT_EOL) 1087 | + inet_proto_csum_replace4(&th->check, skb, *opt, TCPOPT_EOL_WORD, 1088 | + 0); 1089 | + *opt = TCPOPT_EOL_WORD; 1090 | + } 1091 | + 1092 | + return NF_ACCEPT; 1093 | +} 1094 | + 1095 | +static unsigned int syn_proxy_post(struct sk_buff *skb, struct nf_conn *ct, 1096 | + enum ip_conntrack_info ctinfo) 1097 | +{ 1098 | + struct syn_proxy_state *state; 1099 | + struct iphdr *iph; 1100 | + struct tcphdr *th; 1101 | + 1102 | + /* untraced packets don't have NF_CT_EXT_SYNPROXY ext, as they don't 1103 | + * enter syn_proxy_pre() */ 1104 | + state = nf_ct_ext_find(ct, NF_CT_EXT_SYNPROXY); 1105 | + if (state == NULL) 1106 | + return NF_ACCEPT; 1107 | + 1108 | + iph = ip_hdr(skb); 1109 | + if (!skb_make_writable(skb, iph->ihl * 4 + sizeof(*th))) 1110 | + return NF_DROP; 1111 | + th = (struct tcphdr *)(skb->data + iph->ihl * 4); 1112 | + if (!state->seq_inited) { 1113 | + if (th->syn) { 1114 | + /* It must be from original direction, as the ones 1115 | + * from the other side are dropped in function 1116 | + * syn_proxy_pre() */ 1117 | + if (!th->ack) 1118 | + return NF_ACCEPT; 1119 | + 1120 | + pr_debug("SYN-ACK %pI4n:%hu -> %pI4n:%hu " 1121 | + "(seq=%u ack_seq=%u)\n", 1122 | + &iph->saddr, ntohs(th->source), &iph->daddr, 1123 | + ntohs(th->dest), ntohl(th->seq), 1124 | + ntohl(th->ack_seq)); 1125 | + 1126 | + /* SYN-ACK from reply direction with the protection 1127 | + * of conntrack */ 1128 | + spin_lock_bh(&ct->lock); 1129 | + if (!state->seq_inited) { 1130 | + state->seq_inited = 1; 1131 | + pr_debug("update seq_diff %u -> %u\n", 1132 | + state->seq_diff, 1133 | + state->seq_diff - ntohl(th->seq)); 1134 | + state->seq_diff -= ntohl(th->seq); 1135 | + } 1136 | + spin_unlock_bh(&ct->lock); 1137 | + tcp_send(iph->daddr, iph->saddr, th->dest, th->source, 1138 | + ntohl(th->ack_seq), 1139 | + ntohl(th->seq) + 1 + state->seq_diff, 1140 | + state->window, 0, TCPHDR_ACK, iph->tos, 1141 | + skb->dev, 0, NULL); 1142 | + 1143 | + return syn_proxy_mangle_pkt(skb, iph, th, 1144 | + state->seq_diff + 1); 1145 | + } else { 1146 | + __be32 newseq; 1147 | + 1148 | + if (!th->rst) 1149 | + return NF_ACCEPT; 1150 | + newseq = htonl(state->seq_diff + 1); 1151 | + inet_proto_csum_replace4(&th->check, skb, th->seq, 1152 | + newseq, 0); 1153 | + pr_debug("alter RST seq: %u -> %u\n", 1154 | + ntohl(th->seq), ntohl(newseq)); 1155 | + th->seq = newseq; 1156 | + 1157 | + return NF_ACCEPT; 1158 | + } 1159 | + } 1160 | + 1161 | + /* ct should be in ESTABLISHED state, but if the ack packets from 1162 | + * us are lost. */ 1163 | + if (th->syn) { 1164 | + if (!th->ack) 1165 | + return NF_ACCEPT; 1166 | + 1167 | + tcp_send(iph->daddr, iph->saddr, th->dest, th->source, 1168 | + ntohl(th->ack_seq), 1169 | + ntohl(th->seq) + 1 + state->seq_diff, 1170 | + state->window, 0, TCPHDR_ACK, iph->tos, 1171 | + skb->dev, 0, NULL); 1172 | + 1173 | + return syn_proxy_mangle_pkt(skb, iph, th, state->seq_diff + 1); 1174 | + } 1175 | + 1176 | + if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) { 1177 | + __be32 newseq; 1178 | + 1179 | + newseq = htonl(ntohl(th->seq) + state->seq_diff); 1180 | + inet_proto_csum_replace4(&th->check, skb, th->seq, newseq, 0); 1181 | + pr_debug("alter seq: %u -> %u\n", ntohl(th->seq), 1182 | + ntohl(newseq)); 1183 | + th->seq = newseq; 1184 | + } 1185 | + 1186 | + return NF_ACCEPT; 1187 | +} 1188 | + 1189 | +static unsigned int tcp_process(struct sk_buff *skb) 1190 | +{ 1191 | + const struct iphdr *iph; 1192 | + const struct tcphdr *th; 1193 | + int err; 1194 | + u16 mss; 1195 | + 1196 | + iph = ip_hdr(skb); 1197 | + if (iph->frag_off & htons(IP_OFFSET)) 1198 | + goto out; 1199 | + if (!pskb_may_pull(skb, iph->ihl * 4 + sizeof(*th))) 1200 | + goto out; 1201 | + th = (const struct tcphdr *)(skb->data + iph->ihl * 4); 1202 | + if ((tcp_flag_byte(th) & 1203 | + (TCPHDR_FIN | TCPHDR_RST | TCPHDR_ACK | TCPHDR_SYN)) != TCPHDR_SYN) 1204 | + goto out; 1205 | + 1206 | + if (nf_ip_checksum(skb, NF_INET_PRE_ROUTING, iph->ihl * 4, IPPROTO_TCP)) 1207 | + goto out; 1208 | + mss = 0; 1209 | + if (th->doff > sizeof(*th) / 4) { 1210 | + if (!pskb_may_pull(skb, (iph->ihl + th->doff) * 4)) 1211 | + goto out; 1212 | + err = get_mss((u8 *)(th + 1), th->doff * 4 - sizeof(*th)); 1213 | + if (err < 0) 1214 | + goto out; 1215 | + if (err != 0) 1216 | + mss = err; 1217 | + } else if (th->doff != sizeof(*th) / 4) 1218 | + goto out; 1219 | + 1220 | + 1221 | +/* 1222 | + tcp_send(iph->daddr, iph->saddr, th->dest, th->source, 0, 1223 | + ntohl(th->seq) + 1, 0, mss, TCPHDR_SYN | TCPHDR_ACK, 1224 | + iph->tos, skb->dev, 1225 | + TCP_SEND_FLAG_NOTRACE | TCP_SEND_FLAG_SYNCOOKIE, skb); 1226 | +*/ 1227 | + 1228 | + send_cookie(skb,mss); 1229 | + 1230 | + return NF_STOLEN; 1231 | + 1232 | +out: 1233 | + return NF_DROP; 1234 | +} 1235 | + 1236 | +static unsigned int synproxy_tg(struct sk_buff *skb, 1237 | + const struct xt_action_param *par) 1238 | +{ 1239 | + struct nf_conn *ct; 1240 | + enum ip_conntrack_info ctinfo; 1241 | + int ret; 1242 | + 1243 | + /* received from lo */ 1244 | + ct = nf_ct_get(skb, &ctinfo); 1245 | + if (ct) 1246 | + return XT_CONTINUE; 1247 | + 1248 | +// local_bh_disable(); 1249 | + if (!__get_cpu_var(syn_proxy_state).seq_inited) 1250 | + ret = tcp_process(skb); 1251 | + else 1252 | + ret = XT_CONTINUE; 1253 | +// local_bh_enable(); 1254 | + 1255 | + return ret; 1256 | +} 1257 | + 1258 | +static int synproxy_tg_check(const struct xt_tgchk_param *par) 1259 | +{ 1260 | + int ret; 1261 | + 1262 | + ret = nf_ct_l3proto_try_module_get(par->family); 1263 | + if (ret < 0) 1264 | + pr_info("cannot load conntrack support for proto=%u\n", 1265 | + par->family); 1266 | + 1267 | + return ret; 1268 | +} 1269 | + 1270 | +static void synproxy_tg_destroy(const struct xt_tgdtor_param *par) 1271 | +{ 1272 | + nf_ct_l3proto_module_put(par->family); 1273 | +} 1274 | + 1275 | +static struct xt_target synproxy_tg_reg __read_mostly = { 1276 | + .name = "SYNPROXY", 1277 | + .family = NFPROTO_IPV4, 1278 | + .target = synproxy_tg, 1279 | + .table = "raw", 1280 | + .hooks = 1 << NF_INET_PRE_ROUTING, 1281 | + .proto = IPPROTO_TCP, 1282 | + .checkentry = synproxy_tg_check, 1283 | + .destroy = synproxy_tg_destroy, 1284 | + .me = THIS_MODULE, 1285 | +}; 1286 | + 1287 | +static struct nf_ct_ext_type syn_proxy_state_ext __read_mostly = { 1288 | + .len = sizeof(struct syn_proxy_state), 1289 | + .align = __alignof__(struct syn_proxy_state), 1290 | + .id = NF_CT_EXT_SYNPROXY, 1291 | +}; 1292 | + 1293 | +static int __init synproxy_tg_init(void) 1294 | +{ 1295 | + int err; 1296 | + 1297 | + rcu_assign_pointer(syn_proxy_pre_hook, syn_proxy_pre); 1298 | + rcu_assign_pointer(syn_proxy_post_hook, syn_proxy_post); 1299 | + err = nf_ct_extend_register(&syn_proxy_state_ext); 1300 | + if (err) 1301 | + goto err_out; 1302 | + err = xt_register_target(&synproxy_tg_reg); 1303 | + if (err) 1304 | + goto err_out2; 1305 | + 1306 | + return err; 1307 | + 1308 | +err_out2: 1309 | + nf_ct_extend_unregister(&syn_proxy_state_ext); 1310 | +err_out: 1311 | + rcu_assign_pointer(syn_proxy_post_hook, NULL); 1312 | + rcu_assign_pointer(syn_proxy_pre_hook, NULL); 1313 | + rcu_barrier(); 1314 | + 1315 | + return err; 1316 | +} 1317 | + 1318 | +static void __exit synproxy_tg_exit(void) 1319 | +{ 1320 | + xt_unregister_target(&synproxy_tg_reg); 1321 | + nf_ct_extend_unregister(&syn_proxy_state_ext); 1322 | + rcu_assign_pointer(syn_proxy_post_hook, NULL); 1323 | + rcu_assign_pointer(syn_proxy_pre_hook, NULL); 1324 | + rcu_barrier(); 1325 | +} 1326 | + 1327 | +/* 1328 | +static unsigned short compute_checksum(unsigned short *addr, unsigned int count) { 1329 | + register unsigned long sum = 0; 1330 | + while (count > 1) { 1331 | + sum += * addr++; 1332 | + count -= 2; 1333 | + } 1334 | + //if any bytes left, pad the bytes and add 1335 | + if(count > 0) { 1336 | + sum += ((*addr)&htons(0xFF00)); 1337 | + } 1338 | + //Fold sum to 16 bits: add carrier to result 1339 | + while (sum>>16) { 1340 | + sum = (sum & 0xffff) + (sum >> 16); 1341 | + } 1342 | + //one's complement 1343 | + sum = ~sum; 1344 | + return ((unsigned short)sum); 1345 | +} 1346 | +*/ 1347 | + 1348 | +module_init(synproxy_tg_init); 1349 | +module_exit(synproxy_tg_exit); 1350 | -------------------------------------------------------------------------------- /synproxy.diff: -------------------------------------------------------------------------------- 1 | diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h 2 | index bde095f..1046633 100644 3 | --- a/include/net/netfilter/nf_conntrack.h 4 | +++ b/include/net/netfilter/nf_conntrack.h 5 | @@ -17,6 +17,7 @@ 6 | #ifdef __KERNEL__ 7 | #include 8 | #include 9 | +#include 10 | #include 11 | 12 | #include 13 | @@ -310,5 +311,10 @@ do { \ 14 | #define MODULE_ALIAS_NFCT_HELPER(helper) \ 15 | MODULE_ALIAS("nfct-helper-" helper) 16 | 17 | +extern int (*syn_proxy_pre_hook)(struct sk_buff *skb, struct nf_conn *ct, 18 | + struct tcphdr *th); 19 | + 20 | +extern int (*syn_proxy_post_hook)(struct sk_buff *skb, struct nf_conn *ct, 21 | + enum ip_conntrack_info ctinfo); 22 | #endif /* __KERNEL__ */ 23 | #endif /* _NF_CONNTRACK_H */ 24 | diff --git a/include/net/netfilter/nf_conntrack_core.h b/include/net/netfilter/nf_conntrack_core.h 25 | index dffde8e..e9b739e 100644 26 | --- a/include/net/netfilter/nf_conntrack_core.h 27 | +++ b/include/net/netfilter/nf_conntrack_core.h 28 | @@ -63,8 +63,15 @@ static inline int nf_conntrack_confirm(struct sk_buff *skb) 29 | if (ct && ct != &nf_conntrack_untracked) { 30 | if (!nf_ct_is_confirmed(ct) && !nf_ct_is_dying(ct)) 31 | ret = __nf_conntrack_confirm(skb); 32 | - if (likely(ret == NF_ACCEPT)) 33 | + if (likely(ret == NF_ACCEPT)) { 34 | + int (*syn_proxy)(struct sk_buff *, struct nf_conn *, 35 | + enum ip_conntrack_info); 36 | + 37 | nf_ct_deliver_cached_events(ct); 38 | + syn_proxy = rcu_dereference(syn_proxy_post_hook); 39 | + if (syn_proxy) 40 | + ret = syn_proxy(skb, ct, skb->nfctinfo); 41 | + } 42 | } 43 | return ret; 44 | } 45 | diff --git a/include/net/netfilter/nf_conntrack_extend.h b/include/net/netfilter/nf_conntrack_extend.h 46 | index 32d15bd..b2ae7e9 100644 47 | --- a/include/net/netfilter/nf_conntrack_extend.h 48 | +++ b/include/net/netfilter/nf_conntrack_extend.h 49 | @@ -11,6 +11,7 @@ enum nf_ct_ext_id { 50 | NF_CT_EXT_ACCT, 51 | NF_CT_EXT_ECACHE, 52 | NF_CT_EXT_ZONE, 53 | + NF_CT_EXT_SYNPROXY, 54 | NF_CT_EXT_NUM, 55 | }; 56 | 57 | @@ -19,6 +20,7 @@ enum nf_ct_ext_id { 58 | #define NF_CT_EXT_ACCT_TYPE struct nf_conn_counter 59 | #define NF_CT_EXT_ECACHE_TYPE struct nf_conntrack_ecache 60 | #define NF_CT_EXT_ZONE_TYPE struct nf_conntrack_zone 61 | +#define NF_CT_EXT_SYNPROXY_TYPE struct syn_proxy_state 62 | 63 | /* Extensions: optional stuff which isn't permanently in struct. */ 64 | struct nf_ct_ext { 65 | diff --git a/include/net/tcp.h b/include/net/tcp.h 66 | index a144914..b1d59c2 100644 67 | --- a/include/net/tcp.h 68 | +++ b/include/net/tcp.h 69 | @@ -460,8 +460,18 @@ extern int tcp_disconnect(struct sock *sk, int flags); 70 | extern __u32 syncookie_secret[2][16-4+SHA_DIGEST_WORDS]; 71 | extern struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, 72 | struct ip_options *opt); 73 | -extern __u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb, 74 | - __u16 *mss); 75 | +extern __u32 __cookie_v4_init_sequence(__be32 saddr, __be32 daddr, 76 | + __be16 sport, __be16 dport, __u32 seq, 77 | + __u16 *mssp); 78 | +static inline __u32 cookie_v4_init_sequence(const struct iphdr *iph, 79 | + const struct tcphdr *th, 80 | + __u16 *mssp) 81 | +{ 82 | + return __cookie_v4_init_sequence(iph->saddr, iph->daddr, th->source, 83 | + th->dest, ntohl(th->seq), mssp); 84 | +} 85 | +extern int cookie_v4_check_sequence(const struct iphdr *iph, 86 | + const struct tcphdr *th, __u32 cookie); 87 | 88 | extern __u32 cookie_init_timestamp(struct request_sock *req); 89 | extern void cookie_check_timestamp(struct tcp_options_received *tcp_opt); 90 | diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c 91 | index 5c24db4..d61d374 100644 92 | --- a/net/ipv4/syncookies.c 93 | +++ b/net/ipv4/syncookies.c 94 | @@ -160,26 +160,22 @@ static __u16 const msstab[] = { 95 | * Generate a syncookie. mssp points to the mss, which is returned 96 | * rounded down to the value encoded in the cookie. 97 | */ 98 | -__u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb, __u16 *mssp) 99 | +__u32 __cookie_v4_init_sequence(__be32 saddr, __be32 daddr, __be16 sport, 100 | + __be16 dport, __u32 seq, __u16 *mssp) 101 | { 102 | - const struct iphdr *iph = ip_hdr(skb); 103 | - const struct tcphdr *th = tcp_hdr(skb); 104 | int mssind; 105 | const __u16 mss = *mssp; 106 | 107 | - tcp_synq_overflow(sk); 108 | - 109 | /* XXX sort msstab[] by probability? Binary search? */ 110 | for (mssind = 0; mss > msstab[mssind + 1]; mssind++) 111 | ; 112 | *mssp = msstab[mssind] + 1; 113 | 114 | - NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_SYNCOOKIESSENT); 115 | 116 | - return secure_tcp_syn_cookie(iph->saddr, iph->daddr, 117 | - th->source, th->dest, ntohl(th->seq), 118 | + return secure_tcp_syn_cookie(saddr, daddr, sport, dport, seq, 119 | jiffies / (HZ * 60), mssind); 120 | } 121 | +EXPORT_SYMBOL(__cookie_v4_init_sequence); 122 | 123 | /* 124 | * This (misnamed) value is the age of syncookie which is permitted. 125 | @@ -192,10 +188,9 @@ __u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb, __u16 *mssp) 126 | * Check if a ack sequence number is a valid syncookie. 127 | * Return the decoded mss if it is, or 0 if not. 128 | */ 129 | -static inline int cookie_check(struct sk_buff *skb, __u32 cookie) 130 | +int cookie_v4_check_sequence(const struct iphdr *iph, const struct tcphdr *th, 131 | + __u32 cookie) 132 | { 133 | - const struct iphdr *iph = ip_hdr(skb); 134 | - const struct tcphdr *th = tcp_hdr(skb); 135 | __u32 seq = ntohl(th->seq) - 1; 136 | __u32 mssind = check_tcp_syn_cookie(cookie, iph->saddr, iph->daddr, 137 | th->source, th->dest, seq, 138 | @@ -204,6 +199,7 @@ static inline int cookie_check(struct sk_buff *skb, __u32 cookie) 139 | 140 | return mssind < NUM_MSS ? msstab[mssind] + 1 : 0; 141 | } 142 | +EXPORT_SYMBOL(cookie_v4_check_sequence); 143 | 144 | static inline struct sock *get_cookie_sock(struct sock *sk, struct sk_buff *skb, 145 | struct request_sock *req, 146 | @@ -270,7 +266,8 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, 147 | goto out; 148 | 149 | if (tcp_synq_no_recent_overflow(sk) || 150 | - (mss = cookie_check(skb, cookie)) == 0) { 151 | + (mss = cookie_v4_check_sequence(ip_hdr(skb), tcp_hdr(skb), 152 | + cookie)) == 0) { 153 | NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_SYNCOOKIESFAILED); 154 | goto out; 155 | } 156 | diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c 157 | index 202cf09..9879c3b 100644 158 | --- a/net/ipv4/tcp_ipv4.c 159 | +++ b/net/ipv4/tcp_ipv4.c 160 | @@ -1330,8 +1330,11 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) 161 | #ifdef CONFIG_SYN_COOKIES 162 | syn_flood_warning(skb); 163 | req->cookie_ts = tmp_opt.tstamp_ok; 164 | + tcp_synq_overflow(sk); 165 | + NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_SYNCOOKIESSENT); 166 | + isn = cookie_v4_init_sequence(ip_hdr(skb), tcp_hdr(skb), 167 | + &req->mss); 168 | #endif 169 | - isn = cookie_v4_init_sequence(sk, skb, &req->mss); 170 | } else if (!isn) { 171 | struct inet_peer *peer = NULL; 172 | 173 | diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c 174 | index b83c530..7a4b0e0 100644 175 | --- a/net/netfilter/nf_conntrack_core.c 176 | +++ b/net/netfilter/nf_conntrack_core.c 177 | @@ -847,8 +847,19 @@ nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum, 178 | l3proto, l4proto, &set_reply, &ctinfo); 179 | if (!ct) { 180 | /* Not valid part of a connection */ 181 | - NF_CT_STAT_INC_ATOMIC(net, invalid); 182 | + int (*syn_proxy)(struct sk_buff *, struct nf_conn *, 183 | + struct tcphdr *); 184 | + 185 | + if (protonum == IPPROTO_TCP && 186 | + (syn_proxy = rcu_dereference(syn_proxy_pre_hook))) { 187 | + struct tcphdr *th, _th; 188 | + 189 | + th = skb_header_pointer(skb, dataoff, sizeof(_th), &th); 190 | + BUG_ON(th == NULL); 191 | + ret = syn_proxy(skb, NULL, th); 192 | + } else 193 | ret = NF_ACCEPT; 194 | + NF_CT_STAT_INC_ATOMIC(net, invalid); 195 | goto out; 196 | } 197 | 198 | @@ -1448,6 +1459,14 @@ s16 (*nf_ct_nat_offset)(const struct nf_conn *ct, 199 | u32 seq); 200 | EXPORT_SYMBOL_GPL(nf_ct_nat_offset); 201 | 202 | +int (*syn_proxy_pre_hook)(struct sk_buff *skb, struct nf_conn *ct, 203 | + struct tcphdr *th) = NULL; 204 | +EXPORT_SYMBOL(syn_proxy_pre_hook); 205 | + 206 | +int (*syn_proxy_post_hook)(struct sk_buff *skb, struct nf_conn *ct, 207 | + enum ip_conntrack_info ctinfo) = NULL; 208 | +EXPORT_SYMBOL(syn_proxy_post_hook); 209 | + 210 | int nf_conntrack_init(struct net *net) 211 | { 212 | int ret; 213 | diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c 214 | index 9dd8cd4..358c06b 100644 215 | --- a/net/netfilter/nf_conntrack_proto_tcp.c 216 | +++ b/net/netfilter/nf_conntrack_proto_tcp.c 217 | @@ -830,10 +830,21 @@ static int tcp_packet(struct nf_conn *ct, 218 | struct tcphdr _tcph; 219 | unsigned long timeout; 220 | unsigned int index; 221 | + int (*syn_proxy)(struct sk_buff *, struct nf_conn *, 222 | + struct tcphdr *); 223 | 224 | th = skb_header_pointer(skb, dataoff, sizeof(_tcph), &_tcph); 225 | BUG_ON(th == NULL); 226 | 227 | + syn_proxy = rcu_dereference(syn_proxy_pre_hook); 228 | + if (syn_proxy) { 229 | + int ret = syn_proxy((struct sk_buff*)skb, ct, 230 | + (struct tcphdr *)th); 231 | + 232 | + if (ret != NF_ACCEPT) 233 | + return ret; 234 | + } 235 | + 236 | spin_lock_bh(&ct->lock); 237 | old_state = ct->proto.tcp.state; 238 | dir = CTINFO2DIR(ctinfo); 239 | -------------------------------------------------------------------------------- /tst.sh: -------------------------------------------------------------------------------- 1 | iptables -t raw -F 2 | rmmod ipt_SYNPROXY 3 | insmod ipt_SYNPROXY.ko 4 | iptables -t raw -A PREROUTING -p tcp --dport 80 --tcp-flags SYN,ACK,RST,FIN SYN\ 5 | -m conntrack --ctstate INVALID -j SYNPROXY 6 | --------------------------------------------------------------------------------