1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 248b1de4cSPatrick McHardy /* 348b1de4cSPatrick McHardy * Copyright (c) 2013 Patrick McHardy <kaber@trash.net> 448b1de4cSPatrick McHardy */ 548b1de4cSPatrick McHardy 648b1de4cSPatrick McHardy #include <linux/module.h> 748b1de4cSPatrick McHardy #include <linux/skbuff.h> 848b1de4cSPatrick McHardy #include <asm/unaligned.h> 948b1de4cSPatrick McHardy #include <net/tcp.h> 1048b1de4cSPatrick McHardy #include <net/netns/generic.h> 1110c04a8eSPablo Neira Ayuso #include <linux/proc_fs.h> 1248b1de4cSPatrick McHardy 13d7f9b2f1SFernando Fernandez Mancera #include <linux/netfilter_ipv6.h> 14f0c1aab2SPablo Neira Ayuso #include <linux/netfilter/nf_synproxy.h> 15308ac914SDaniel Borkmann 1648b1de4cSPatrick McHardy #include <net/netfilter/nf_conntrack.h> 17d7f9b2f1SFernando Fernandez Mancera #include <net/netfilter/nf_conntrack_ecache.h> 1848b1de4cSPatrick McHardy #include <net/netfilter/nf_conntrack_extend.h> 1948b1de4cSPatrick McHardy #include <net/netfilter/nf_conntrack_seqadj.h> 2048b1de4cSPatrick McHardy #include <net/netfilter/nf_conntrack_synproxy.h> 21308ac914SDaniel Borkmann #include <net/netfilter/nf_conntrack_zones.h> 22d7f9b2f1SFernando Fernandez Mancera #include <net/netfilter/nf_synproxy.h> 2348b1de4cSPatrick McHardy 24c7d03a00SAlexey Dobriyan unsigned int synproxy_net_id; 2548b1de4cSPatrick McHardy EXPORT_SYMBOL_GPL(synproxy_net_id); 2648b1de4cSPatrick McHardy 27f4a87e7bSPatrick McHardy bool 2848b1de4cSPatrick McHardy synproxy_parse_options(const struct sk_buff *skb, unsigned int doff, 2948b1de4cSPatrick McHardy const struct tcphdr *th, struct synproxy_options *opts) 3048b1de4cSPatrick McHardy { 3148b1de4cSPatrick McHardy int length = (th->doff * 4) - sizeof(*th); 3248b1de4cSPatrick McHardy u8 buf[40], *ptr; 3348b1de4cSPatrick McHardy 3448b1de4cSPatrick McHardy ptr = skb_header_pointer(skb, doff + sizeof(*th), length, buf); 35f4a87e7bSPatrick McHardy if (ptr == NULL) 36f4a87e7bSPatrick McHardy return false; 3748b1de4cSPatrick McHardy 3848b1de4cSPatrick McHardy opts->options = 0; 3948b1de4cSPatrick McHardy while (length > 0) { 4048b1de4cSPatrick McHardy int opcode = *ptr++; 4148b1de4cSPatrick McHardy int opsize; 4248b1de4cSPatrick McHardy 4348b1de4cSPatrick McHardy switch (opcode) { 4448b1de4cSPatrick McHardy case TCPOPT_EOL: 45f4a87e7bSPatrick McHardy return true; 4648b1de4cSPatrick McHardy case TCPOPT_NOP: 4748b1de4cSPatrick McHardy length--; 4848b1de4cSPatrick McHardy continue; 4948b1de4cSPatrick McHardy default: 5048b1de4cSPatrick McHardy opsize = *ptr++; 5148b1de4cSPatrick McHardy if (opsize < 2) 52f4a87e7bSPatrick McHardy return true; 5348b1de4cSPatrick McHardy if (opsize > length) 54f4a87e7bSPatrick McHardy return true; 5548b1de4cSPatrick McHardy 5648b1de4cSPatrick McHardy switch (opcode) { 5748b1de4cSPatrick McHardy case TCPOPT_MSS: 5848b1de4cSPatrick McHardy if (opsize == TCPOLEN_MSS) { 5948b1de4cSPatrick McHardy opts->mss = get_unaligned_be16(ptr); 60d7f9b2f1SFernando Fernandez Mancera opts->options |= NF_SYNPROXY_OPT_MSS; 6148b1de4cSPatrick McHardy } 6248b1de4cSPatrick McHardy break; 6348b1de4cSPatrick McHardy case TCPOPT_WINDOW: 6448b1de4cSPatrick McHardy if (opsize == TCPOLEN_WINDOW) { 6548b1de4cSPatrick McHardy opts->wscale = *ptr; 66122868b3SGao Feng if (opts->wscale > TCP_MAX_WSCALE) 67122868b3SGao Feng opts->wscale = TCP_MAX_WSCALE; 68d7f9b2f1SFernando Fernandez Mancera opts->options |= NF_SYNPROXY_OPT_WSCALE; 6948b1de4cSPatrick McHardy } 7048b1de4cSPatrick McHardy break; 7148b1de4cSPatrick McHardy case TCPOPT_TIMESTAMP: 7248b1de4cSPatrick McHardy if (opsize == TCPOLEN_TIMESTAMP) { 7348b1de4cSPatrick McHardy opts->tsval = get_unaligned_be32(ptr); 7448b1de4cSPatrick McHardy opts->tsecr = get_unaligned_be32(ptr + 4); 75d7f9b2f1SFernando Fernandez Mancera opts->options |= NF_SYNPROXY_OPT_TIMESTAMP; 7648b1de4cSPatrick McHardy } 7748b1de4cSPatrick McHardy break; 7848b1de4cSPatrick McHardy case TCPOPT_SACK_PERM: 7948b1de4cSPatrick McHardy if (opsize == TCPOLEN_SACK_PERM) 80d7f9b2f1SFernando Fernandez Mancera opts->options |= NF_SYNPROXY_OPT_SACK_PERM; 8148b1de4cSPatrick McHardy break; 8248b1de4cSPatrick McHardy } 8348b1de4cSPatrick McHardy 8448b1de4cSPatrick McHardy ptr += opsize - 2; 8548b1de4cSPatrick McHardy length -= opsize; 8648b1de4cSPatrick McHardy } 8748b1de4cSPatrick McHardy } 88f4a87e7bSPatrick McHardy return true; 8948b1de4cSPatrick McHardy } 9048b1de4cSPatrick McHardy EXPORT_SYMBOL_GPL(synproxy_parse_options); 9148b1de4cSPatrick McHardy 92d7f9b2f1SFernando Fernandez Mancera static unsigned int 93d7f9b2f1SFernando Fernandez Mancera synproxy_options_size(const struct synproxy_options *opts) 9448b1de4cSPatrick McHardy { 9548b1de4cSPatrick McHardy unsigned int size = 0; 9648b1de4cSPatrick McHardy 97d7f9b2f1SFernando Fernandez Mancera if (opts->options & NF_SYNPROXY_OPT_MSS) 9848b1de4cSPatrick McHardy size += TCPOLEN_MSS_ALIGNED; 99d7f9b2f1SFernando Fernandez Mancera if (opts->options & NF_SYNPROXY_OPT_TIMESTAMP) 10048b1de4cSPatrick McHardy size += TCPOLEN_TSTAMP_ALIGNED; 101d7f9b2f1SFernando Fernandez Mancera else if (opts->options & NF_SYNPROXY_OPT_SACK_PERM) 10248b1de4cSPatrick McHardy size += TCPOLEN_SACKPERM_ALIGNED; 103d7f9b2f1SFernando Fernandez Mancera if (opts->options & NF_SYNPROXY_OPT_WSCALE) 10448b1de4cSPatrick McHardy size += TCPOLEN_WSCALE_ALIGNED; 10548b1de4cSPatrick McHardy 10648b1de4cSPatrick McHardy return size; 10748b1de4cSPatrick McHardy } 10848b1de4cSPatrick McHardy 109d7f9b2f1SFernando Fernandez Mancera static void 11048b1de4cSPatrick McHardy synproxy_build_options(struct tcphdr *th, const struct synproxy_options *opts) 11148b1de4cSPatrick McHardy { 11248b1de4cSPatrick McHardy __be32 *ptr = (__be32 *)(th + 1); 11348b1de4cSPatrick McHardy u8 options = opts->options; 11448b1de4cSPatrick McHardy 115d7f9b2f1SFernando Fernandez Mancera if (options & NF_SYNPROXY_OPT_MSS) 11648b1de4cSPatrick McHardy *ptr++ = htonl((TCPOPT_MSS << 24) | 11748b1de4cSPatrick McHardy (TCPOLEN_MSS << 16) | 11848b1de4cSPatrick McHardy opts->mss); 11948b1de4cSPatrick McHardy 120d7f9b2f1SFernando Fernandez Mancera if (options & NF_SYNPROXY_OPT_TIMESTAMP) { 121d7f9b2f1SFernando Fernandez Mancera if (options & NF_SYNPROXY_OPT_SACK_PERM) 12248b1de4cSPatrick McHardy *ptr++ = htonl((TCPOPT_SACK_PERM << 24) | 12348b1de4cSPatrick McHardy (TCPOLEN_SACK_PERM << 16) | 12448b1de4cSPatrick McHardy (TCPOPT_TIMESTAMP << 8) | 12548b1de4cSPatrick McHardy TCPOLEN_TIMESTAMP); 12648b1de4cSPatrick McHardy else 12748b1de4cSPatrick McHardy *ptr++ = htonl((TCPOPT_NOP << 24) | 12848b1de4cSPatrick McHardy (TCPOPT_NOP << 16) | 12948b1de4cSPatrick McHardy (TCPOPT_TIMESTAMP << 8) | 13048b1de4cSPatrick McHardy TCPOLEN_TIMESTAMP); 13148b1de4cSPatrick McHardy 13248b1de4cSPatrick McHardy *ptr++ = htonl(opts->tsval); 13348b1de4cSPatrick McHardy *ptr++ = htonl(opts->tsecr); 134d7f9b2f1SFernando Fernandez Mancera } else if (options & NF_SYNPROXY_OPT_SACK_PERM) 13548b1de4cSPatrick McHardy *ptr++ = htonl((TCPOPT_NOP << 24) | 13648b1de4cSPatrick McHardy (TCPOPT_NOP << 16) | 13748b1de4cSPatrick McHardy (TCPOPT_SACK_PERM << 8) | 13848b1de4cSPatrick McHardy TCPOLEN_SACK_PERM); 13948b1de4cSPatrick McHardy 140d7f9b2f1SFernando Fernandez Mancera if (options & NF_SYNPROXY_OPT_WSCALE) 14148b1de4cSPatrick McHardy *ptr++ = htonl((TCPOPT_NOP << 24) | 14248b1de4cSPatrick McHardy (TCPOPT_WINDOW << 16) | 14348b1de4cSPatrick McHardy (TCPOLEN_WINDOW << 8) | 14448b1de4cSPatrick McHardy opts->wscale); 14548b1de4cSPatrick McHardy } 14648b1de4cSPatrick McHardy 147d7f9b2f1SFernando Fernandez Mancera void synproxy_init_timestamp_cookie(const struct nf_synproxy_info *info, 14848b1de4cSPatrick McHardy struct synproxy_options *opts) 14948b1de4cSPatrick McHardy { 15048b1de4cSPatrick McHardy opts->tsecr = opts->tsval; 1519a568de4SEric Dumazet opts->tsval = tcp_time_stamp_raw() & ~0x3f; 15248b1de4cSPatrick McHardy 153d7f9b2f1SFernando Fernandez Mancera if (opts->options & NF_SYNPROXY_OPT_WSCALE) { 154c1898c4cSMartin Topholm opts->tsval |= opts->wscale; 155c1898c4cSMartin Topholm opts->wscale = info->wscale; 156c1898c4cSMartin Topholm } else 15748b1de4cSPatrick McHardy opts->tsval |= 0xf; 15848b1de4cSPatrick McHardy 159d7f9b2f1SFernando Fernandez Mancera if (opts->options & NF_SYNPROXY_OPT_SACK_PERM) 16048b1de4cSPatrick McHardy opts->tsval |= 1 << 4; 16148b1de4cSPatrick McHardy 162d7f9b2f1SFernando Fernandez Mancera if (opts->options & NF_SYNPROXY_OPT_ECN) 16348b1de4cSPatrick McHardy opts->tsval |= 1 << 5; 16448b1de4cSPatrick McHardy } 16548b1de4cSPatrick McHardy EXPORT_SYMBOL_GPL(synproxy_init_timestamp_cookie); 16648b1de4cSPatrick McHardy 167d7f9b2f1SFernando Fernandez Mancera static void 168d7f9b2f1SFernando Fernandez Mancera synproxy_check_timestamp_cookie(struct synproxy_options *opts) 16948b1de4cSPatrick McHardy { 17048b1de4cSPatrick McHardy opts->wscale = opts->tsecr & 0xf; 17148b1de4cSPatrick McHardy if (opts->wscale != 0xf) 172d7f9b2f1SFernando Fernandez Mancera opts->options |= NF_SYNPROXY_OPT_WSCALE; 17348b1de4cSPatrick McHardy 174d7f9b2f1SFernando Fernandez Mancera opts->options |= opts->tsecr & (1 << 4) ? NF_SYNPROXY_OPT_SACK_PERM : 0; 17548b1de4cSPatrick McHardy 176d7f9b2f1SFernando Fernandez Mancera opts->options |= opts->tsecr & (1 << 5) ? NF_SYNPROXY_OPT_ECN : 0; 17748b1de4cSPatrick McHardy } 17848b1de4cSPatrick McHardy 179d7f9b2f1SFernando Fernandez Mancera static unsigned int 180d7f9b2f1SFernando Fernandez Mancera synproxy_tstamp_adjust(struct sk_buff *skb, unsigned int protoff, 181d7f9b2f1SFernando Fernandez Mancera struct tcphdr *th, struct nf_conn *ct, 18248b1de4cSPatrick McHardy enum ip_conntrack_info ctinfo, 18348b1de4cSPatrick McHardy const struct nf_conn_synproxy *synproxy) 18448b1de4cSPatrick McHardy { 18548b1de4cSPatrick McHardy unsigned int optoff, optend; 186851345c5SFlorian Westphal __be32 *ptr, old; 18748b1de4cSPatrick McHardy 18848b1de4cSPatrick McHardy if (synproxy->tsoff == 0) 18948b1de4cSPatrick McHardy return 1; 19048b1de4cSPatrick McHardy 19148b1de4cSPatrick McHardy optoff = protoff + sizeof(struct tcphdr); 19248b1de4cSPatrick McHardy optend = protoff + th->doff * 4; 19348b1de4cSPatrick McHardy 1942cf6bffcSFlorian Westphal if (skb_ensure_writable(skb, optend)) 19548b1de4cSPatrick McHardy return 0; 19648b1de4cSPatrick McHardy 19748b1de4cSPatrick McHardy while (optoff < optend) { 19848b1de4cSPatrick McHardy unsigned char *op = skb->data + optoff; 19948b1de4cSPatrick McHardy 20048b1de4cSPatrick McHardy switch (op[0]) { 20148b1de4cSPatrick McHardy case TCPOPT_EOL: 20248b1de4cSPatrick McHardy return 1; 20348b1de4cSPatrick McHardy case TCPOPT_NOP: 20448b1de4cSPatrick McHardy optoff++; 20548b1de4cSPatrick McHardy continue; 20648b1de4cSPatrick McHardy default: 20748b1de4cSPatrick McHardy if (optoff + 1 == optend || 20848b1de4cSPatrick McHardy optoff + op[1] > optend || 20948b1de4cSPatrick McHardy op[1] < 2) 21048b1de4cSPatrick McHardy return 0; 21148b1de4cSPatrick McHardy if (op[0] == TCPOPT_TIMESTAMP && 21248b1de4cSPatrick McHardy op[1] == TCPOLEN_TIMESTAMP) { 21348b1de4cSPatrick McHardy if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) { 214851345c5SFlorian Westphal ptr = (__be32 *)&op[2]; 21548b1de4cSPatrick McHardy old = *ptr; 21648b1de4cSPatrick McHardy *ptr = htonl(ntohl(*ptr) - 21748b1de4cSPatrick McHardy synproxy->tsoff); 21848b1de4cSPatrick McHardy } else { 219851345c5SFlorian Westphal ptr = (__be32 *)&op[6]; 22048b1de4cSPatrick McHardy old = *ptr; 22148b1de4cSPatrick McHardy *ptr = htonl(ntohl(*ptr) + 22248b1de4cSPatrick McHardy synproxy->tsoff); 22348b1de4cSPatrick McHardy } 22448b1de4cSPatrick McHardy inet_proto_csum_replace4(&th->check, skb, 2254b048d6dSTom Herbert old, *ptr, false); 22648b1de4cSPatrick McHardy return 1; 22748b1de4cSPatrick McHardy } 22848b1de4cSPatrick McHardy optoff += op[1]; 22948b1de4cSPatrick McHardy } 23048b1de4cSPatrick McHardy } 23148b1de4cSPatrick McHardy return 1; 23248b1de4cSPatrick McHardy } 23348b1de4cSPatrick McHardy 23448b1de4cSPatrick McHardy static struct nf_ct_ext_type nf_ct_synproxy_extend __read_mostly = { 23548b1de4cSPatrick McHardy .len = sizeof(struct nf_conn_synproxy), 23648b1de4cSPatrick McHardy .align = __alignof__(struct nf_conn_synproxy), 23748b1de4cSPatrick McHardy .id = NF_CT_EXT_SYNPROXY, 23848b1de4cSPatrick McHardy }; 23948b1de4cSPatrick McHardy 24048b1de4cSPatrick McHardy #ifdef CONFIG_PROC_FS 24148b1de4cSPatrick McHardy static void *synproxy_cpu_seq_start(struct seq_file *seq, loff_t *pos) 24248b1de4cSPatrick McHardy { 24348b1de4cSPatrick McHardy struct synproxy_net *snet = synproxy_pernet(seq_file_net(seq)); 24448b1de4cSPatrick McHardy int cpu; 24548b1de4cSPatrick McHardy 24648b1de4cSPatrick McHardy if (*pos == 0) 24748b1de4cSPatrick McHardy return SEQ_START_TOKEN; 24848b1de4cSPatrick McHardy 24948b1de4cSPatrick McHardy for (cpu = *pos - 1; cpu < nr_cpu_ids; cpu++) { 25048b1de4cSPatrick McHardy if (!cpu_possible(cpu)) 25148b1de4cSPatrick McHardy continue; 25248b1de4cSPatrick McHardy *pos = cpu + 1; 25348b1de4cSPatrick McHardy return per_cpu_ptr(snet->stats, cpu); 25448b1de4cSPatrick McHardy } 25548b1de4cSPatrick McHardy 25648b1de4cSPatrick McHardy return NULL; 25748b1de4cSPatrick McHardy } 25848b1de4cSPatrick McHardy 25948b1de4cSPatrick McHardy static void *synproxy_cpu_seq_next(struct seq_file *seq, void *v, loff_t *pos) 26048b1de4cSPatrick McHardy { 26148b1de4cSPatrick McHardy struct synproxy_net *snet = synproxy_pernet(seq_file_net(seq)); 26248b1de4cSPatrick McHardy int cpu; 26348b1de4cSPatrick McHardy 26448b1de4cSPatrick McHardy for (cpu = *pos; cpu < nr_cpu_ids; cpu++) { 26548b1de4cSPatrick McHardy if (!cpu_possible(cpu)) 26648b1de4cSPatrick McHardy continue; 26748b1de4cSPatrick McHardy *pos = cpu + 1; 26848b1de4cSPatrick McHardy return per_cpu_ptr(snet->stats, cpu); 26948b1de4cSPatrick McHardy } 27048b1de4cSPatrick McHardy 27148b1de4cSPatrick McHardy return NULL; 27248b1de4cSPatrick McHardy } 27348b1de4cSPatrick McHardy 27448b1de4cSPatrick McHardy static void synproxy_cpu_seq_stop(struct seq_file *seq, void *v) 27548b1de4cSPatrick McHardy { 27648b1de4cSPatrick McHardy return; 27748b1de4cSPatrick McHardy } 27848b1de4cSPatrick McHardy 27948b1de4cSPatrick McHardy static int synproxy_cpu_seq_show(struct seq_file *seq, void *v) 28048b1de4cSPatrick McHardy { 28148b1de4cSPatrick McHardy struct synproxy_stats *stats = v; 28248b1de4cSPatrick McHardy 28348b1de4cSPatrick McHardy if (v == SEQ_START_TOKEN) { 284cdec2685Ssimran singhal seq_puts(seq, "entries\t\tsyn_received\t" 28548b1de4cSPatrick McHardy "cookie_invalid\tcookie_valid\t" 28648b1de4cSPatrick McHardy "cookie_retrans\tconn_reopened\n"); 28748b1de4cSPatrick McHardy return 0; 28848b1de4cSPatrick McHardy } 28948b1de4cSPatrick McHardy 29048b1de4cSPatrick McHardy seq_printf(seq, "%08x\t%08x\t%08x\t%08x\t%08x\t%08x\n", 0, 29148b1de4cSPatrick McHardy stats->syn_received, 29248b1de4cSPatrick McHardy stats->cookie_invalid, 29348b1de4cSPatrick McHardy stats->cookie_valid, 29448b1de4cSPatrick McHardy stats->cookie_retrans, 29548b1de4cSPatrick McHardy stats->conn_reopened); 29648b1de4cSPatrick McHardy 29748b1de4cSPatrick McHardy return 0; 29848b1de4cSPatrick McHardy } 29948b1de4cSPatrick McHardy 30048b1de4cSPatrick McHardy static const struct seq_operations synproxy_cpu_seq_ops = { 30148b1de4cSPatrick McHardy .start = synproxy_cpu_seq_start, 30248b1de4cSPatrick McHardy .next = synproxy_cpu_seq_next, 30348b1de4cSPatrick McHardy .stop = synproxy_cpu_seq_stop, 30448b1de4cSPatrick McHardy .show = synproxy_cpu_seq_show, 30548b1de4cSPatrick McHardy }; 30648b1de4cSPatrick McHardy 30748b1de4cSPatrick McHardy static int __net_init synproxy_proc_init(struct net *net) 30848b1de4cSPatrick McHardy { 309c3506372SChristoph Hellwig if (!proc_create_net("synproxy", 0444, net->proc_net_stat, 310c3506372SChristoph Hellwig &synproxy_cpu_seq_ops, sizeof(struct seq_net_private))) 31148b1de4cSPatrick McHardy return -ENOMEM; 31248b1de4cSPatrick McHardy return 0; 31348b1de4cSPatrick McHardy } 31448b1de4cSPatrick McHardy 31548b1de4cSPatrick McHardy static void __net_exit synproxy_proc_exit(struct net *net) 31648b1de4cSPatrick McHardy { 31748b1de4cSPatrick McHardy remove_proc_entry("synproxy", net->proc_net_stat); 31848b1de4cSPatrick McHardy } 31948b1de4cSPatrick McHardy #else 32048b1de4cSPatrick McHardy static int __net_init synproxy_proc_init(struct net *net) 32148b1de4cSPatrick McHardy { 32248b1de4cSPatrick McHardy return 0; 32348b1de4cSPatrick McHardy } 32448b1de4cSPatrick McHardy 32548b1de4cSPatrick McHardy static void __net_exit synproxy_proc_exit(struct net *net) 32648b1de4cSPatrick McHardy { 32748b1de4cSPatrick McHardy return; 32848b1de4cSPatrick McHardy } 32948b1de4cSPatrick McHardy #endif /* CONFIG_PROC_FS */ 33048b1de4cSPatrick McHardy 33148b1de4cSPatrick McHardy static int __net_init synproxy_net_init(struct net *net) 33248b1de4cSPatrick McHardy { 33348b1de4cSPatrick McHardy struct synproxy_net *snet = synproxy_pernet(net); 33448b1de4cSPatrick McHardy struct nf_conn *ct; 33548b1de4cSPatrick McHardy int err = -ENOMEM; 33648b1de4cSPatrick McHardy 337308ac914SDaniel Borkmann ct = nf_ct_tmpl_alloc(net, &nf_ct_zone_dflt, GFP_KERNEL); 3381a727c63SDan Carpenter if (!ct) 33948b1de4cSPatrick McHardy goto err1; 34048b1de4cSPatrick McHardy 34148b1de4cSPatrick McHardy if (!nfct_seqadj_ext_add(ct)) 34248b1de4cSPatrick McHardy goto err2; 34348b1de4cSPatrick McHardy if (!nfct_synproxy_ext_add(ct)) 34448b1de4cSPatrick McHardy goto err2; 34548b1de4cSPatrick McHardy 3460838aa7fSPablo Neira Ayuso __set_bit(IPS_CONFIRMED_BIT, &ct->status); 3470838aa7fSPablo Neira Ayuso nf_conntrack_get(&ct->ct_general); 34848b1de4cSPatrick McHardy snet->tmpl = ct; 34948b1de4cSPatrick McHardy 35048b1de4cSPatrick McHardy snet->stats = alloc_percpu(struct synproxy_stats); 35148b1de4cSPatrick McHardy if (snet->stats == NULL) 35248b1de4cSPatrick McHardy goto err2; 35348b1de4cSPatrick McHardy 35448b1de4cSPatrick McHardy err = synproxy_proc_init(net); 35548b1de4cSPatrick McHardy if (err < 0) 35648b1de4cSPatrick McHardy goto err3; 35748b1de4cSPatrick McHardy 35848b1de4cSPatrick McHardy return 0; 35948b1de4cSPatrick McHardy 36048b1de4cSPatrick McHardy err3: 36148b1de4cSPatrick McHardy free_percpu(snet->stats); 36248b1de4cSPatrick McHardy err2: 3639cf94eabSDaniel Borkmann nf_ct_tmpl_free(ct); 36448b1de4cSPatrick McHardy err1: 36548b1de4cSPatrick McHardy return err; 36648b1de4cSPatrick McHardy } 36748b1de4cSPatrick McHardy 36848b1de4cSPatrick McHardy static void __net_exit synproxy_net_exit(struct net *net) 36948b1de4cSPatrick McHardy { 37048b1de4cSPatrick McHardy struct synproxy_net *snet = synproxy_pernet(net); 37148b1de4cSPatrick McHardy 372e53376beSPablo Neira Ayuso nf_ct_put(snet->tmpl); 37348b1de4cSPatrick McHardy synproxy_proc_exit(net); 37448b1de4cSPatrick McHardy free_percpu(snet->stats); 37548b1de4cSPatrick McHardy } 37648b1de4cSPatrick McHardy 37748b1de4cSPatrick McHardy static struct pernet_operations synproxy_net_ops = { 37848b1de4cSPatrick McHardy .init = synproxy_net_init, 37948b1de4cSPatrick McHardy .exit = synproxy_net_exit, 38048b1de4cSPatrick McHardy .id = &synproxy_net_id, 38148b1de4cSPatrick McHardy .size = sizeof(struct synproxy_net), 38248b1de4cSPatrick McHardy }; 38348b1de4cSPatrick McHardy 38448b1de4cSPatrick McHardy static int __init synproxy_core_init(void) 38548b1de4cSPatrick McHardy { 38648b1de4cSPatrick McHardy int err; 38748b1de4cSPatrick McHardy 38848b1de4cSPatrick McHardy err = nf_ct_extend_register(&nf_ct_synproxy_extend); 38948b1de4cSPatrick McHardy if (err < 0) 39048b1de4cSPatrick McHardy goto err1; 39148b1de4cSPatrick McHardy 39248b1de4cSPatrick McHardy err = register_pernet_subsys(&synproxy_net_ops); 39348b1de4cSPatrick McHardy if (err < 0) 39448b1de4cSPatrick McHardy goto err2; 39548b1de4cSPatrick McHardy 39648b1de4cSPatrick McHardy return 0; 39748b1de4cSPatrick McHardy 39848b1de4cSPatrick McHardy err2: 39948b1de4cSPatrick McHardy nf_ct_extend_unregister(&nf_ct_synproxy_extend); 40048b1de4cSPatrick McHardy err1: 40148b1de4cSPatrick McHardy return err; 40248b1de4cSPatrick McHardy } 40348b1de4cSPatrick McHardy 40448b1de4cSPatrick McHardy static void __exit synproxy_core_exit(void) 40548b1de4cSPatrick McHardy { 40648b1de4cSPatrick McHardy unregister_pernet_subsys(&synproxy_net_ops); 40748b1de4cSPatrick McHardy nf_ct_extend_unregister(&nf_ct_synproxy_extend); 40848b1de4cSPatrick McHardy } 40948b1de4cSPatrick McHardy 41048b1de4cSPatrick McHardy module_init(synproxy_core_init); 41148b1de4cSPatrick McHardy module_exit(synproxy_core_exit); 41248b1de4cSPatrick McHardy 413d7f9b2f1SFernando Fernandez Mancera static struct iphdr * 414d7f9b2f1SFernando Fernandez Mancera synproxy_build_ip(struct net *net, struct sk_buff *skb, __be32 saddr, 415d7f9b2f1SFernando Fernandez Mancera __be32 daddr) 416d7f9b2f1SFernando Fernandez Mancera { 417d7f9b2f1SFernando Fernandez Mancera struct iphdr *iph; 418d7f9b2f1SFernando Fernandez Mancera 419d7f9b2f1SFernando Fernandez Mancera skb_reset_network_header(skb); 420d7f9b2f1SFernando Fernandez Mancera iph = skb_put(skb, sizeof(*iph)); 421d7f9b2f1SFernando Fernandez Mancera iph->version = 4; 422d7f9b2f1SFernando Fernandez Mancera iph->ihl = sizeof(*iph) / 4; 423d7f9b2f1SFernando Fernandez Mancera iph->tos = 0; 424d7f9b2f1SFernando Fernandez Mancera iph->id = 0; 425d7f9b2f1SFernando Fernandez Mancera iph->frag_off = htons(IP_DF); 426d7f9b2f1SFernando Fernandez Mancera iph->ttl = net->ipv4.sysctl_ip_default_ttl; 427d7f9b2f1SFernando Fernandez Mancera iph->protocol = IPPROTO_TCP; 428d7f9b2f1SFernando Fernandez Mancera iph->check = 0; 429d7f9b2f1SFernando Fernandez Mancera iph->saddr = saddr; 430d7f9b2f1SFernando Fernandez Mancera iph->daddr = daddr; 431d7f9b2f1SFernando Fernandez Mancera 432d7f9b2f1SFernando Fernandez Mancera return iph; 433d7f9b2f1SFernando Fernandez Mancera } 434d7f9b2f1SFernando Fernandez Mancera 435d7f9b2f1SFernando Fernandez Mancera static void 436d7f9b2f1SFernando Fernandez Mancera synproxy_send_tcp(struct net *net, 437d7f9b2f1SFernando Fernandez Mancera const struct sk_buff *skb, struct sk_buff *nskb, 438d7f9b2f1SFernando Fernandez Mancera struct nf_conntrack *nfct, enum ip_conntrack_info ctinfo, 439d7f9b2f1SFernando Fernandez Mancera struct iphdr *niph, struct tcphdr *nth, 440d7f9b2f1SFernando Fernandez Mancera unsigned int tcp_hdr_size) 441d7f9b2f1SFernando Fernandez Mancera { 442d7f9b2f1SFernando Fernandez Mancera nth->check = ~tcp_v4_check(tcp_hdr_size, niph->saddr, niph->daddr, 0); 443d7f9b2f1SFernando Fernandez Mancera nskb->ip_summed = CHECKSUM_PARTIAL; 444d7f9b2f1SFernando Fernandez Mancera nskb->csum_start = (unsigned char *)nth - nskb->head; 445d7f9b2f1SFernando Fernandez Mancera nskb->csum_offset = offsetof(struct tcphdr, check); 446d7f9b2f1SFernando Fernandez Mancera 447d7f9b2f1SFernando Fernandez Mancera skb_dst_set_noref(nskb, skb_dst(skb)); 448d7f9b2f1SFernando Fernandez Mancera nskb->protocol = htons(ETH_P_IP); 449d7f9b2f1SFernando Fernandez Mancera if (ip_route_me_harder(net, nskb, RTN_UNSPEC)) 450d7f9b2f1SFernando Fernandez Mancera goto free_nskb; 451d7f9b2f1SFernando Fernandez Mancera 452d7f9b2f1SFernando Fernandez Mancera if (nfct) { 453d7f9b2f1SFernando Fernandez Mancera nf_ct_set(nskb, (struct nf_conn *)nfct, ctinfo); 454d7f9b2f1SFernando Fernandez Mancera nf_conntrack_get(nfct); 455d7f9b2f1SFernando Fernandez Mancera } 456d7f9b2f1SFernando Fernandez Mancera 457d7f9b2f1SFernando Fernandez Mancera ip_local_out(net, nskb->sk, nskb); 458d7f9b2f1SFernando Fernandez Mancera return; 459d7f9b2f1SFernando Fernandez Mancera 460d7f9b2f1SFernando Fernandez Mancera free_nskb: 461d7f9b2f1SFernando Fernandez Mancera kfree_skb(nskb); 462d7f9b2f1SFernando Fernandez Mancera } 463d7f9b2f1SFernando Fernandez Mancera 464d7f9b2f1SFernando Fernandez Mancera void 465d7f9b2f1SFernando Fernandez Mancera synproxy_send_client_synack(struct net *net, 466d7f9b2f1SFernando Fernandez Mancera const struct sk_buff *skb, const struct tcphdr *th, 467d7f9b2f1SFernando Fernandez Mancera const struct synproxy_options *opts) 468d7f9b2f1SFernando Fernandez Mancera { 469d7f9b2f1SFernando Fernandez Mancera struct sk_buff *nskb; 470d7f9b2f1SFernando Fernandez Mancera struct iphdr *iph, *niph; 471d7f9b2f1SFernando Fernandez Mancera struct tcphdr *nth; 472d7f9b2f1SFernando Fernandez Mancera unsigned int tcp_hdr_size; 473b83329fbSFernando Fernandez Mancera u16 mss = opts->mss_encode; 474d7f9b2f1SFernando Fernandez Mancera 475d7f9b2f1SFernando Fernandez Mancera iph = ip_hdr(skb); 476d7f9b2f1SFernando Fernandez Mancera 477d7f9b2f1SFernando Fernandez Mancera tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts); 478d7f9b2f1SFernando Fernandez Mancera nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER, 479d7f9b2f1SFernando Fernandez Mancera GFP_ATOMIC); 480d7f9b2f1SFernando Fernandez Mancera if (!nskb) 481d7f9b2f1SFernando Fernandez Mancera return; 482d7f9b2f1SFernando Fernandez Mancera skb_reserve(nskb, MAX_TCP_HEADER); 483d7f9b2f1SFernando Fernandez Mancera 484d7f9b2f1SFernando Fernandez Mancera niph = synproxy_build_ip(net, nskb, iph->daddr, iph->saddr); 485d7f9b2f1SFernando Fernandez Mancera 486d7f9b2f1SFernando Fernandez Mancera skb_reset_transport_header(nskb); 487d7f9b2f1SFernando Fernandez Mancera nth = skb_put(nskb, tcp_hdr_size); 488d7f9b2f1SFernando Fernandez Mancera nth->source = th->dest; 489d7f9b2f1SFernando Fernandez Mancera nth->dest = th->source; 490d7f9b2f1SFernando Fernandez Mancera nth->seq = htonl(__cookie_v4_init_sequence(iph, th, &mss)); 491d7f9b2f1SFernando Fernandez Mancera nth->ack_seq = htonl(ntohl(th->seq) + 1); 492d7f9b2f1SFernando Fernandez Mancera tcp_flag_word(nth) = TCP_FLAG_SYN | TCP_FLAG_ACK; 493d7f9b2f1SFernando Fernandez Mancera if (opts->options & NF_SYNPROXY_OPT_ECN) 494d7f9b2f1SFernando Fernandez Mancera tcp_flag_word(nth) |= TCP_FLAG_ECE; 495d7f9b2f1SFernando Fernandez Mancera nth->doff = tcp_hdr_size / 4; 496d7f9b2f1SFernando Fernandez Mancera nth->window = 0; 497d7f9b2f1SFernando Fernandez Mancera nth->check = 0; 498d7f9b2f1SFernando Fernandez Mancera nth->urg_ptr = 0; 499d7f9b2f1SFernando Fernandez Mancera 500d7f9b2f1SFernando Fernandez Mancera synproxy_build_options(nth, opts); 501d7f9b2f1SFernando Fernandez Mancera 502d7f9b2f1SFernando Fernandez Mancera synproxy_send_tcp(net, skb, nskb, skb_nfct(skb), 503d7f9b2f1SFernando Fernandez Mancera IP_CT_ESTABLISHED_REPLY, niph, nth, tcp_hdr_size); 504d7f9b2f1SFernando Fernandez Mancera } 505d7f9b2f1SFernando Fernandez Mancera EXPORT_SYMBOL_GPL(synproxy_send_client_synack); 506d7f9b2f1SFernando Fernandez Mancera 507d7f9b2f1SFernando Fernandez Mancera static void 508d7f9b2f1SFernando Fernandez Mancera synproxy_send_server_syn(struct net *net, 509d7f9b2f1SFernando Fernandez Mancera const struct sk_buff *skb, const struct tcphdr *th, 510d7f9b2f1SFernando Fernandez Mancera const struct synproxy_options *opts, u32 recv_seq) 511d7f9b2f1SFernando Fernandez Mancera { 512d7f9b2f1SFernando Fernandez Mancera struct synproxy_net *snet = synproxy_pernet(net); 513d7f9b2f1SFernando Fernandez Mancera struct sk_buff *nskb; 514d7f9b2f1SFernando Fernandez Mancera struct iphdr *iph, *niph; 515d7f9b2f1SFernando Fernandez Mancera struct tcphdr *nth; 516d7f9b2f1SFernando Fernandez Mancera unsigned int tcp_hdr_size; 517d7f9b2f1SFernando Fernandez Mancera 518d7f9b2f1SFernando Fernandez Mancera iph = ip_hdr(skb); 519d7f9b2f1SFernando Fernandez Mancera 520d7f9b2f1SFernando Fernandez Mancera tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts); 521d7f9b2f1SFernando Fernandez Mancera nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER, 522d7f9b2f1SFernando Fernandez Mancera GFP_ATOMIC); 523d7f9b2f1SFernando Fernandez Mancera if (!nskb) 524d7f9b2f1SFernando Fernandez Mancera return; 525d7f9b2f1SFernando Fernandez Mancera skb_reserve(nskb, MAX_TCP_HEADER); 526d7f9b2f1SFernando Fernandez Mancera 527d7f9b2f1SFernando Fernandez Mancera niph = synproxy_build_ip(net, nskb, iph->saddr, iph->daddr); 528d7f9b2f1SFernando Fernandez Mancera 529d7f9b2f1SFernando Fernandez Mancera skb_reset_transport_header(nskb); 530d7f9b2f1SFernando Fernandez Mancera nth = skb_put(nskb, tcp_hdr_size); 531d7f9b2f1SFernando Fernandez Mancera nth->source = th->source; 532d7f9b2f1SFernando Fernandez Mancera nth->dest = th->dest; 533d7f9b2f1SFernando Fernandez Mancera nth->seq = htonl(recv_seq - 1); 534d7f9b2f1SFernando Fernandez Mancera /* ack_seq is used to relay our ISN to the synproxy hook to initialize 535d7f9b2f1SFernando Fernandez Mancera * sequence number translation once a connection tracking entry exists. 536d7f9b2f1SFernando Fernandez Mancera */ 537d7f9b2f1SFernando Fernandez Mancera nth->ack_seq = htonl(ntohl(th->ack_seq) - 1); 538d7f9b2f1SFernando Fernandez Mancera tcp_flag_word(nth) = TCP_FLAG_SYN; 539d7f9b2f1SFernando Fernandez Mancera if (opts->options & NF_SYNPROXY_OPT_ECN) 540d7f9b2f1SFernando Fernandez Mancera tcp_flag_word(nth) |= TCP_FLAG_ECE | TCP_FLAG_CWR; 541d7f9b2f1SFernando Fernandez Mancera nth->doff = tcp_hdr_size / 4; 542d7f9b2f1SFernando Fernandez Mancera nth->window = th->window; 543d7f9b2f1SFernando Fernandez Mancera nth->check = 0; 544d7f9b2f1SFernando Fernandez Mancera nth->urg_ptr = 0; 545d7f9b2f1SFernando Fernandez Mancera 546d7f9b2f1SFernando Fernandez Mancera synproxy_build_options(nth, opts); 547d7f9b2f1SFernando Fernandez Mancera 548d7f9b2f1SFernando Fernandez Mancera synproxy_send_tcp(net, skb, nskb, &snet->tmpl->ct_general, IP_CT_NEW, 549d7f9b2f1SFernando Fernandez Mancera niph, nth, tcp_hdr_size); 550d7f9b2f1SFernando Fernandez Mancera } 551d7f9b2f1SFernando Fernandez Mancera 552d7f9b2f1SFernando Fernandez Mancera static void 553d7f9b2f1SFernando Fernandez Mancera synproxy_send_server_ack(struct net *net, 554d7f9b2f1SFernando Fernandez Mancera const struct ip_ct_tcp *state, 555d7f9b2f1SFernando Fernandez Mancera const struct sk_buff *skb, const struct tcphdr *th, 556d7f9b2f1SFernando Fernandez Mancera const struct synproxy_options *opts) 557d7f9b2f1SFernando Fernandez Mancera { 558d7f9b2f1SFernando Fernandez Mancera struct sk_buff *nskb; 559d7f9b2f1SFernando Fernandez Mancera struct iphdr *iph, *niph; 560d7f9b2f1SFernando Fernandez Mancera struct tcphdr *nth; 561d7f9b2f1SFernando Fernandez Mancera unsigned int tcp_hdr_size; 562d7f9b2f1SFernando Fernandez Mancera 563d7f9b2f1SFernando Fernandez Mancera iph = ip_hdr(skb); 564d7f9b2f1SFernando Fernandez Mancera 565d7f9b2f1SFernando Fernandez Mancera tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts); 566d7f9b2f1SFernando Fernandez Mancera nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER, 567d7f9b2f1SFernando Fernandez Mancera GFP_ATOMIC); 568d7f9b2f1SFernando Fernandez Mancera if (!nskb) 569d7f9b2f1SFernando Fernandez Mancera return; 570d7f9b2f1SFernando Fernandez Mancera skb_reserve(nskb, MAX_TCP_HEADER); 571d7f9b2f1SFernando Fernandez Mancera 572d7f9b2f1SFernando Fernandez Mancera niph = synproxy_build_ip(net, nskb, iph->daddr, iph->saddr); 573d7f9b2f1SFernando Fernandez Mancera 574d7f9b2f1SFernando Fernandez Mancera skb_reset_transport_header(nskb); 575d7f9b2f1SFernando Fernandez Mancera nth = skb_put(nskb, tcp_hdr_size); 576d7f9b2f1SFernando Fernandez Mancera nth->source = th->dest; 577d7f9b2f1SFernando Fernandez Mancera nth->dest = th->source; 578d7f9b2f1SFernando Fernandez Mancera nth->seq = htonl(ntohl(th->ack_seq)); 579d7f9b2f1SFernando Fernandez Mancera nth->ack_seq = htonl(ntohl(th->seq) + 1); 580d7f9b2f1SFernando Fernandez Mancera tcp_flag_word(nth) = TCP_FLAG_ACK; 581d7f9b2f1SFernando Fernandez Mancera nth->doff = tcp_hdr_size / 4; 582d7f9b2f1SFernando Fernandez Mancera nth->window = htons(state->seen[IP_CT_DIR_ORIGINAL].td_maxwin); 583d7f9b2f1SFernando Fernandez Mancera nth->check = 0; 584d7f9b2f1SFernando Fernandez Mancera nth->urg_ptr = 0; 585d7f9b2f1SFernando Fernandez Mancera 586d7f9b2f1SFernando Fernandez Mancera synproxy_build_options(nth, opts); 587d7f9b2f1SFernando Fernandez Mancera 588d7f9b2f1SFernando Fernandez Mancera synproxy_send_tcp(net, skb, nskb, NULL, 0, niph, nth, tcp_hdr_size); 589d7f9b2f1SFernando Fernandez Mancera } 590d7f9b2f1SFernando Fernandez Mancera 591d7f9b2f1SFernando Fernandez Mancera static void 592d7f9b2f1SFernando Fernandez Mancera synproxy_send_client_ack(struct net *net, 593d7f9b2f1SFernando Fernandez Mancera const struct sk_buff *skb, const struct tcphdr *th, 594d7f9b2f1SFernando Fernandez Mancera const struct synproxy_options *opts) 595d7f9b2f1SFernando Fernandez Mancera { 596d7f9b2f1SFernando Fernandez Mancera struct sk_buff *nskb; 597d7f9b2f1SFernando Fernandez Mancera struct iphdr *iph, *niph; 598d7f9b2f1SFernando Fernandez Mancera struct tcphdr *nth; 599d7f9b2f1SFernando Fernandez Mancera unsigned int tcp_hdr_size; 600d7f9b2f1SFernando Fernandez Mancera 601d7f9b2f1SFernando Fernandez Mancera iph = ip_hdr(skb); 602d7f9b2f1SFernando Fernandez Mancera 603d7f9b2f1SFernando Fernandez Mancera tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts); 604d7f9b2f1SFernando Fernandez Mancera nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER, 605d7f9b2f1SFernando Fernandez Mancera GFP_ATOMIC); 606d7f9b2f1SFernando Fernandez Mancera if (!nskb) 607d7f9b2f1SFernando Fernandez Mancera return; 608d7f9b2f1SFernando Fernandez Mancera skb_reserve(nskb, MAX_TCP_HEADER); 609d7f9b2f1SFernando Fernandez Mancera 610d7f9b2f1SFernando Fernandez Mancera niph = synproxy_build_ip(net, nskb, iph->saddr, iph->daddr); 611d7f9b2f1SFernando Fernandez Mancera 612d7f9b2f1SFernando Fernandez Mancera skb_reset_transport_header(nskb); 613d7f9b2f1SFernando Fernandez Mancera nth = skb_put(nskb, tcp_hdr_size); 614d7f9b2f1SFernando Fernandez Mancera nth->source = th->source; 615d7f9b2f1SFernando Fernandez Mancera nth->dest = th->dest; 616d7f9b2f1SFernando Fernandez Mancera nth->seq = htonl(ntohl(th->seq) + 1); 617d7f9b2f1SFernando Fernandez Mancera nth->ack_seq = th->ack_seq; 618d7f9b2f1SFernando Fernandez Mancera tcp_flag_word(nth) = TCP_FLAG_ACK; 619d7f9b2f1SFernando Fernandez Mancera nth->doff = tcp_hdr_size / 4; 620d7f9b2f1SFernando Fernandez Mancera nth->window = htons(ntohs(th->window) >> opts->wscale); 621d7f9b2f1SFernando Fernandez Mancera nth->check = 0; 622d7f9b2f1SFernando Fernandez Mancera nth->urg_ptr = 0; 623d7f9b2f1SFernando Fernandez Mancera 624d7f9b2f1SFernando Fernandez Mancera synproxy_build_options(nth, opts); 625d7f9b2f1SFernando Fernandez Mancera 626d7f9b2f1SFernando Fernandez Mancera synproxy_send_tcp(net, skb, nskb, skb_nfct(skb), 627d7f9b2f1SFernando Fernandez Mancera IP_CT_ESTABLISHED_REPLY, niph, nth, tcp_hdr_size); 628d7f9b2f1SFernando Fernandez Mancera } 629d7f9b2f1SFernando Fernandez Mancera 630d7f9b2f1SFernando Fernandez Mancera bool 631d7f9b2f1SFernando Fernandez Mancera synproxy_recv_client_ack(struct net *net, 632d7f9b2f1SFernando Fernandez Mancera const struct sk_buff *skb, const struct tcphdr *th, 633d7f9b2f1SFernando Fernandez Mancera struct synproxy_options *opts, u32 recv_seq) 634d7f9b2f1SFernando Fernandez Mancera { 635d7f9b2f1SFernando Fernandez Mancera struct synproxy_net *snet = synproxy_pernet(net); 636d7f9b2f1SFernando Fernandez Mancera int mss; 637d7f9b2f1SFernando Fernandez Mancera 638d7f9b2f1SFernando Fernandez Mancera mss = __cookie_v4_check(ip_hdr(skb), th, ntohl(th->ack_seq) - 1); 639d7f9b2f1SFernando Fernandez Mancera if (mss == 0) { 640d7f9b2f1SFernando Fernandez Mancera this_cpu_inc(snet->stats->cookie_invalid); 641d7f9b2f1SFernando Fernandez Mancera return false; 642d7f9b2f1SFernando Fernandez Mancera } 643d7f9b2f1SFernando Fernandez Mancera 644d7f9b2f1SFernando Fernandez Mancera this_cpu_inc(snet->stats->cookie_valid); 645d7f9b2f1SFernando Fernandez Mancera opts->mss = mss; 646d7f9b2f1SFernando Fernandez Mancera opts->options |= NF_SYNPROXY_OPT_MSS; 647d7f9b2f1SFernando Fernandez Mancera 648d7f9b2f1SFernando Fernandez Mancera if (opts->options & NF_SYNPROXY_OPT_TIMESTAMP) 649d7f9b2f1SFernando Fernandez Mancera synproxy_check_timestamp_cookie(opts); 650d7f9b2f1SFernando Fernandez Mancera 651d7f9b2f1SFernando Fernandez Mancera synproxy_send_server_syn(net, skb, th, opts, recv_seq); 652d7f9b2f1SFernando Fernandez Mancera return true; 653d7f9b2f1SFernando Fernandez Mancera } 654d7f9b2f1SFernando Fernandez Mancera EXPORT_SYMBOL_GPL(synproxy_recv_client_ack); 655d7f9b2f1SFernando Fernandez Mancera 656d7f9b2f1SFernando Fernandez Mancera unsigned int 657d7f9b2f1SFernando Fernandez Mancera ipv4_synproxy_hook(void *priv, struct sk_buff *skb, 658d7f9b2f1SFernando Fernandez Mancera const struct nf_hook_state *nhs) 659d7f9b2f1SFernando Fernandez Mancera { 660d7f9b2f1SFernando Fernandez Mancera struct net *net = nhs->net; 661d7f9b2f1SFernando Fernandez Mancera struct synproxy_net *snet = synproxy_pernet(net); 662d7f9b2f1SFernando Fernandez Mancera enum ip_conntrack_info ctinfo; 663d7f9b2f1SFernando Fernandez Mancera struct nf_conn *ct; 664d7f9b2f1SFernando Fernandez Mancera struct nf_conn_synproxy *synproxy; 665d7f9b2f1SFernando Fernandez Mancera struct synproxy_options opts = {}; 666d7f9b2f1SFernando Fernandez Mancera const struct ip_ct_tcp *state; 667d7f9b2f1SFernando Fernandez Mancera struct tcphdr *th, _th; 668d7f9b2f1SFernando Fernandez Mancera unsigned int thoff; 669d7f9b2f1SFernando Fernandez Mancera 670d7f9b2f1SFernando Fernandez Mancera ct = nf_ct_get(skb, &ctinfo); 671d7f9b2f1SFernando Fernandez Mancera if (!ct) 672d7f9b2f1SFernando Fernandez Mancera return NF_ACCEPT; 673d7f9b2f1SFernando Fernandez Mancera 674d7f9b2f1SFernando Fernandez Mancera synproxy = nfct_synproxy(ct); 675d7f9b2f1SFernando Fernandez Mancera if (!synproxy) 676d7f9b2f1SFernando Fernandez Mancera return NF_ACCEPT; 677d7f9b2f1SFernando Fernandez Mancera 678d7f9b2f1SFernando Fernandez Mancera if (nf_is_loopback_packet(skb) || 679d7f9b2f1SFernando Fernandez Mancera ip_hdr(skb)->protocol != IPPROTO_TCP) 680d7f9b2f1SFernando Fernandez Mancera return NF_ACCEPT; 681d7f9b2f1SFernando Fernandez Mancera 682d7f9b2f1SFernando Fernandez Mancera thoff = ip_hdrlen(skb); 683d7f9b2f1SFernando Fernandez Mancera th = skb_header_pointer(skb, thoff, sizeof(_th), &_th); 684d7f9b2f1SFernando Fernandez Mancera if (!th) 685d7f9b2f1SFernando Fernandez Mancera return NF_DROP; 686d7f9b2f1SFernando Fernandez Mancera 687d7f9b2f1SFernando Fernandez Mancera state = &ct->proto.tcp; 688d7f9b2f1SFernando Fernandez Mancera switch (state->state) { 689d7f9b2f1SFernando Fernandez Mancera case TCP_CONNTRACK_CLOSE: 690e971ceb8SFernando Fernandez Mancera if (th->rst && CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL) { 691d7f9b2f1SFernando Fernandez Mancera nf_ct_seqadj_init(ct, ctinfo, synproxy->isn - 692d7f9b2f1SFernando Fernandez Mancera ntohl(th->seq) + 1); 693d7f9b2f1SFernando Fernandez Mancera break; 694d7f9b2f1SFernando Fernandez Mancera } 695d7f9b2f1SFernando Fernandez Mancera 696d7f9b2f1SFernando Fernandez Mancera if (!th->syn || th->ack || 697d7f9b2f1SFernando Fernandez Mancera CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL) 698d7f9b2f1SFernando Fernandez Mancera break; 699d7f9b2f1SFernando Fernandez Mancera 700d7f9b2f1SFernando Fernandez Mancera /* Reopened connection - reset the sequence number and timestamp 701d7f9b2f1SFernando Fernandez Mancera * adjustments, they will get initialized once the connection is 702d7f9b2f1SFernando Fernandez Mancera * reestablished. 703d7f9b2f1SFernando Fernandez Mancera */ 704d7f9b2f1SFernando Fernandez Mancera nf_ct_seqadj_init(ct, ctinfo, 0); 705d7f9b2f1SFernando Fernandez Mancera synproxy->tsoff = 0; 706d7f9b2f1SFernando Fernandez Mancera this_cpu_inc(snet->stats->conn_reopened); 707d7f9b2f1SFernando Fernandez Mancera 708d7f9b2f1SFernando Fernandez Mancera /* fall through */ 709d7f9b2f1SFernando Fernandez Mancera case TCP_CONNTRACK_SYN_SENT: 710d7f9b2f1SFernando Fernandez Mancera if (!synproxy_parse_options(skb, thoff, th, &opts)) 711d7f9b2f1SFernando Fernandez Mancera return NF_DROP; 712d7f9b2f1SFernando Fernandez Mancera 713d7f9b2f1SFernando Fernandez Mancera if (!th->syn && th->ack && 714d7f9b2f1SFernando Fernandez Mancera CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) { 715d7f9b2f1SFernando Fernandez Mancera /* Keep-Alives are sent with SEG.SEQ = SND.NXT-1, 716d7f9b2f1SFernando Fernandez Mancera * therefore we need to add 1 to make the SYN sequence 717d7f9b2f1SFernando Fernandez Mancera * number match the one of first SYN. 718d7f9b2f1SFernando Fernandez Mancera */ 719d7f9b2f1SFernando Fernandez Mancera if (synproxy_recv_client_ack(net, skb, th, &opts, 720d7f9b2f1SFernando Fernandez Mancera ntohl(th->seq) + 1)) { 721d7f9b2f1SFernando Fernandez Mancera this_cpu_inc(snet->stats->cookie_retrans); 722d7f9b2f1SFernando Fernandez Mancera consume_skb(skb); 723d7f9b2f1SFernando Fernandez Mancera return NF_STOLEN; 724d7f9b2f1SFernando Fernandez Mancera } else { 725d7f9b2f1SFernando Fernandez Mancera return NF_DROP; 726d7f9b2f1SFernando Fernandez Mancera } 727d7f9b2f1SFernando Fernandez Mancera } 728d7f9b2f1SFernando Fernandez Mancera 729d7f9b2f1SFernando Fernandez Mancera synproxy->isn = ntohl(th->ack_seq); 730d7f9b2f1SFernando Fernandez Mancera if (opts.options & NF_SYNPROXY_OPT_TIMESTAMP) 731d7f9b2f1SFernando Fernandez Mancera synproxy->its = opts.tsecr; 732d7f9b2f1SFernando Fernandez Mancera 733d7f9b2f1SFernando Fernandez Mancera nf_conntrack_event_cache(IPCT_SYNPROXY, ct); 734d7f9b2f1SFernando Fernandez Mancera break; 735d7f9b2f1SFernando Fernandez Mancera case TCP_CONNTRACK_SYN_RECV: 736d7f9b2f1SFernando Fernandez Mancera if (!th->syn || !th->ack) 737d7f9b2f1SFernando Fernandez Mancera break; 738d7f9b2f1SFernando Fernandez Mancera 739d7f9b2f1SFernando Fernandez Mancera if (!synproxy_parse_options(skb, thoff, th, &opts)) 740d7f9b2f1SFernando Fernandez Mancera return NF_DROP; 741d7f9b2f1SFernando Fernandez Mancera 742d7f9b2f1SFernando Fernandez Mancera if (opts.options & NF_SYNPROXY_OPT_TIMESTAMP) { 743d7f9b2f1SFernando Fernandez Mancera synproxy->tsoff = opts.tsval - synproxy->its; 744d7f9b2f1SFernando Fernandez Mancera nf_conntrack_event_cache(IPCT_SYNPROXY, ct); 745d7f9b2f1SFernando Fernandez Mancera } 746d7f9b2f1SFernando Fernandez Mancera 747d7f9b2f1SFernando Fernandez Mancera opts.options &= ~(NF_SYNPROXY_OPT_MSS | 748d7f9b2f1SFernando Fernandez Mancera NF_SYNPROXY_OPT_WSCALE | 749d7f9b2f1SFernando Fernandez Mancera NF_SYNPROXY_OPT_SACK_PERM); 750d7f9b2f1SFernando Fernandez Mancera 751d7f9b2f1SFernando Fernandez Mancera swap(opts.tsval, opts.tsecr); 752d7f9b2f1SFernando Fernandez Mancera synproxy_send_server_ack(net, state, skb, th, &opts); 753d7f9b2f1SFernando Fernandez Mancera 754d7f9b2f1SFernando Fernandez Mancera nf_ct_seqadj_init(ct, ctinfo, synproxy->isn - ntohl(th->seq)); 755d7f9b2f1SFernando Fernandez Mancera nf_conntrack_event_cache(IPCT_SEQADJ, ct); 756d7f9b2f1SFernando Fernandez Mancera 757d7f9b2f1SFernando Fernandez Mancera swap(opts.tsval, opts.tsecr); 758d7f9b2f1SFernando Fernandez Mancera synproxy_send_client_ack(net, skb, th, &opts); 759d7f9b2f1SFernando Fernandez Mancera 760d7f9b2f1SFernando Fernandez Mancera consume_skb(skb); 761d7f9b2f1SFernando Fernandez Mancera return NF_STOLEN; 762d7f9b2f1SFernando Fernandez Mancera default: 763d7f9b2f1SFernando Fernandez Mancera break; 764d7f9b2f1SFernando Fernandez Mancera } 765d7f9b2f1SFernando Fernandez Mancera 766d7f9b2f1SFernando Fernandez Mancera synproxy_tstamp_adjust(skb, thoff, th, ct, ctinfo, synproxy); 767d7f9b2f1SFernando Fernandez Mancera return NF_ACCEPT; 768d7f9b2f1SFernando Fernandez Mancera } 769d7f9b2f1SFernando Fernandez Mancera EXPORT_SYMBOL_GPL(ipv4_synproxy_hook); 770d7f9b2f1SFernando Fernandez Mancera 771d7f9b2f1SFernando Fernandez Mancera static const struct nf_hook_ops ipv4_synproxy_ops[] = { 772d7f9b2f1SFernando Fernandez Mancera { 773d7f9b2f1SFernando Fernandez Mancera .hook = ipv4_synproxy_hook, 774d7f9b2f1SFernando Fernandez Mancera .pf = NFPROTO_IPV4, 775d7f9b2f1SFernando Fernandez Mancera .hooknum = NF_INET_LOCAL_IN, 776d7f9b2f1SFernando Fernandez Mancera .priority = NF_IP_PRI_CONNTRACK_CONFIRM - 1, 777d7f9b2f1SFernando Fernandez Mancera }, 778d7f9b2f1SFernando Fernandez Mancera { 779d7f9b2f1SFernando Fernandez Mancera .hook = ipv4_synproxy_hook, 780d7f9b2f1SFernando Fernandez Mancera .pf = NFPROTO_IPV4, 781d7f9b2f1SFernando Fernandez Mancera .hooknum = NF_INET_POST_ROUTING, 782d7f9b2f1SFernando Fernandez Mancera .priority = NF_IP_PRI_CONNTRACK_CONFIRM - 1, 783d7f9b2f1SFernando Fernandez Mancera }, 784d7f9b2f1SFernando Fernandez Mancera }; 785d7f9b2f1SFernando Fernandez Mancera 786d7f9b2f1SFernando Fernandez Mancera int nf_synproxy_ipv4_init(struct synproxy_net *snet, struct net *net) 787d7f9b2f1SFernando Fernandez Mancera { 788d7f9b2f1SFernando Fernandez Mancera int err; 789d7f9b2f1SFernando Fernandez Mancera 790d7f9b2f1SFernando Fernandez Mancera if (snet->hook_ref4 == 0) { 791d7f9b2f1SFernando Fernandez Mancera err = nf_register_net_hooks(net, ipv4_synproxy_ops, 792d7f9b2f1SFernando Fernandez Mancera ARRAY_SIZE(ipv4_synproxy_ops)); 793d7f9b2f1SFernando Fernandez Mancera if (err) 794d7f9b2f1SFernando Fernandez Mancera return err; 795d7f9b2f1SFernando Fernandez Mancera } 796d7f9b2f1SFernando Fernandez Mancera 797d7f9b2f1SFernando Fernandez Mancera snet->hook_ref4++; 79872c5e118SColin Ian King return 0; 799d7f9b2f1SFernando Fernandez Mancera } 800d7f9b2f1SFernando Fernandez Mancera EXPORT_SYMBOL_GPL(nf_synproxy_ipv4_init); 801d7f9b2f1SFernando Fernandez Mancera 802d7f9b2f1SFernando Fernandez Mancera void nf_synproxy_ipv4_fini(struct synproxy_net *snet, struct net *net) 803d7f9b2f1SFernando Fernandez Mancera { 804d7f9b2f1SFernando Fernandez Mancera snet->hook_ref4--; 805d7f9b2f1SFernando Fernandez Mancera if (snet->hook_ref4 == 0) 806d7f9b2f1SFernando Fernandez Mancera nf_unregister_net_hooks(net, ipv4_synproxy_ops, 807d7f9b2f1SFernando Fernandez Mancera ARRAY_SIZE(ipv4_synproxy_ops)); 808d7f9b2f1SFernando Fernandez Mancera } 809d7f9b2f1SFernando Fernandez Mancera EXPORT_SYMBOL_GPL(nf_synproxy_ipv4_fini); 810d7f9b2f1SFernando Fernandez Mancera 811d7f9b2f1SFernando Fernandez Mancera #if IS_ENABLED(CONFIG_IPV6) 812d7f9b2f1SFernando Fernandez Mancera static struct ipv6hdr * 813d7f9b2f1SFernando Fernandez Mancera synproxy_build_ip_ipv6(struct net *net, struct sk_buff *skb, 814d7f9b2f1SFernando Fernandez Mancera const struct in6_addr *saddr, 815d7f9b2f1SFernando Fernandez Mancera const struct in6_addr *daddr) 816d7f9b2f1SFernando Fernandez Mancera { 817d7f9b2f1SFernando Fernandez Mancera struct ipv6hdr *iph; 818d7f9b2f1SFernando Fernandez Mancera 819d7f9b2f1SFernando Fernandez Mancera skb_reset_network_header(skb); 820d7f9b2f1SFernando Fernandez Mancera iph = skb_put(skb, sizeof(*iph)); 821d7f9b2f1SFernando Fernandez Mancera ip6_flow_hdr(iph, 0, 0); 822d7f9b2f1SFernando Fernandez Mancera iph->hop_limit = net->ipv6.devconf_all->hop_limit; 823d7f9b2f1SFernando Fernandez Mancera iph->nexthdr = IPPROTO_TCP; 824d7f9b2f1SFernando Fernandez Mancera iph->saddr = *saddr; 825d7f9b2f1SFernando Fernandez Mancera iph->daddr = *daddr; 826d7f9b2f1SFernando Fernandez Mancera 827d7f9b2f1SFernando Fernandez Mancera return iph; 828d7f9b2f1SFernando Fernandez Mancera } 829d7f9b2f1SFernando Fernandez Mancera 830d7f9b2f1SFernando Fernandez Mancera static void 831d7f9b2f1SFernando Fernandez Mancera synproxy_send_tcp_ipv6(struct net *net, 832d7f9b2f1SFernando Fernandez Mancera const struct sk_buff *skb, struct sk_buff *nskb, 833d7f9b2f1SFernando Fernandez Mancera struct nf_conntrack *nfct, enum ip_conntrack_info ctinfo, 834d7f9b2f1SFernando Fernandez Mancera struct ipv6hdr *niph, struct tcphdr *nth, 835d7f9b2f1SFernando Fernandez Mancera unsigned int tcp_hdr_size) 836d7f9b2f1SFernando Fernandez Mancera { 837d7f9b2f1SFernando Fernandez Mancera struct dst_entry *dst; 838d7f9b2f1SFernando Fernandez Mancera struct flowi6 fl6; 839d7f9b2f1SFernando Fernandez Mancera int err; 840d7f9b2f1SFernando Fernandez Mancera 841d7f9b2f1SFernando Fernandez Mancera nth->check = ~tcp_v6_check(tcp_hdr_size, &niph->saddr, &niph->daddr, 0); 842d7f9b2f1SFernando Fernandez Mancera nskb->ip_summed = CHECKSUM_PARTIAL; 843d7f9b2f1SFernando Fernandez Mancera nskb->csum_start = (unsigned char *)nth - nskb->head; 844d7f9b2f1SFernando Fernandez Mancera nskb->csum_offset = offsetof(struct tcphdr, check); 845d7f9b2f1SFernando Fernandez Mancera 846d7f9b2f1SFernando Fernandez Mancera memset(&fl6, 0, sizeof(fl6)); 847d7f9b2f1SFernando Fernandez Mancera fl6.flowi6_proto = IPPROTO_TCP; 848d7f9b2f1SFernando Fernandez Mancera fl6.saddr = niph->saddr; 849d7f9b2f1SFernando Fernandez Mancera fl6.daddr = niph->daddr; 850d7f9b2f1SFernando Fernandez Mancera fl6.fl6_sport = nth->source; 851d7f9b2f1SFernando Fernandez Mancera fl6.fl6_dport = nth->dest; 852d7f9b2f1SFernando Fernandez Mancera security_skb_classify_flow((struct sk_buff *)skb, 853d7f9b2f1SFernando Fernandez Mancera flowi6_to_flowi(&fl6)); 854d7f9b2f1SFernando Fernandez Mancera err = nf_ip6_route(net, &dst, flowi6_to_flowi(&fl6), false); 855d7f9b2f1SFernando Fernandez Mancera if (err) { 856d7f9b2f1SFernando Fernandez Mancera goto free_nskb; 857d7f9b2f1SFernando Fernandez Mancera } 858d7f9b2f1SFernando Fernandez Mancera 859d7f9b2f1SFernando Fernandez Mancera dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0); 860d7f9b2f1SFernando Fernandez Mancera if (IS_ERR(dst)) 861d7f9b2f1SFernando Fernandez Mancera goto free_nskb; 862d7f9b2f1SFernando Fernandez Mancera 863d7f9b2f1SFernando Fernandez Mancera skb_dst_set(nskb, dst); 864d7f9b2f1SFernando Fernandez Mancera 865d7f9b2f1SFernando Fernandez Mancera if (nfct) { 866d7f9b2f1SFernando Fernandez Mancera nf_ct_set(nskb, (struct nf_conn *)nfct, ctinfo); 867d7f9b2f1SFernando Fernandez Mancera nf_conntrack_get(nfct); 868d7f9b2f1SFernando Fernandez Mancera } 869d7f9b2f1SFernando Fernandez Mancera 870d7f9b2f1SFernando Fernandez Mancera ip6_local_out(net, nskb->sk, nskb); 871d7f9b2f1SFernando Fernandez Mancera return; 872d7f9b2f1SFernando Fernandez Mancera 873d7f9b2f1SFernando Fernandez Mancera free_nskb: 874d7f9b2f1SFernando Fernandez Mancera kfree_skb(nskb); 875d7f9b2f1SFernando Fernandez Mancera } 876d7f9b2f1SFernando Fernandez Mancera 877d7f9b2f1SFernando Fernandez Mancera void 878d7f9b2f1SFernando Fernandez Mancera synproxy_send_client_synack_ipv6(struct net *net, 879d7f9b2f1SFernando Fernandez Mancera const struct sk_buff *skb, 880d7f9b2f1SFernando Fernandez Mancera const struct tcphdr *th, 881d7f9b2f1SFernando Fernandez Mancera const struct synproxy_options *opts) 882d7f9b2f1SFernando Fernandez Mancera { 883d7f9b2f1SFernando Fernandez Mancera struct sk_buff *nskb; 884d7f9b2f1SFernando Fernandez Mancera struct ipv6hdr *iph, *niph; 885d7f9b2f1SFernando Fernandez Mancera struct tcphdr *nth; 886d7f9b2f1SFernando Fernandez Mancera unsigned int tcp_hdr_size; 887b83329fbSFernando Fernandez Mancera u16 mss = opts->mss_encode; 888d7f9b2f1SFernando Fernandez Mancera 889d7f9b2f1SFernando Fernandez Mancera iph = ipv6_hdr(skb); 890d7f9b2f1SFernando Fernandez Mancera 891d7f9b2f1SFernando Fernandez Mancera tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts); 892d7f9b2f1SFernando Fernandez Mancera nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER, 893d7f9b2f1SFernando Fernandez Mancera GFP_ATOMIC); 894d7f9b2f1SFernando Fernandez Mancera if (!nskb) 895d7f9b2f1SFernando Fernandez Mancera return; 896d7f9b2f1SFernando Fernandez Mancera skb_reserve(nskb, MAX_TCP_HEADER); 897d7f9b2f1SFernando Fernandez Mancera 898d7f9b2f1SFernando Fernandez Mancera niph = synproxy_build_ip_ipv6(net, nskb, &iph->daddr, &iph->saddr); 899d7f9b2f1SFernando Fernandez Mancera 900d7f9b2f1SFernando Fernandez Mancera skb_reset_transport_header(nskb); 901d7f9b2f1SFernando Fernandez Mancera nth = skb_put(nskb, tcp_hdr_size); 902d7f9b2f1SFernando Fernandez Mancera nth->source = th->dest; 903d7f9b2f1SFernando Fernandez Mancera nth->dest = th->source; 904d7f9b2f1SFernando Fernandez Mancera nth->seq = htonl(nf_ipv6_cookie_init_sequence(iph, th, &mss)); 905d7f9b2f1SFernando Fernandez Mancera nth->ack_seq = htonl(ntohl(th->seq) + 1); 906d7f9b2f1SFernando Fernandez Mancera tcp_flag_word(nth) = TCP_FLAG_SYN | TCP_FLAG_ACK; 907d7f9b2f1SFernando Fernandez Mancera if (opts->options & NF_SYNPROXY_OPT_ECN) 908d7f9b2f1SFernando Fernandez Mancera tcp_flag_word(nth) |= TCP_FLAG_ECE; 909d7f9b2f1SFernando Fernandez Mancera nth->doff = tcp_hdr_size / 4; 910d7f9b2f1SFernando Fernandez Mancera nth->window = 0; 911d7f9b2f1SFernando Fernandez Mancera nth->check = 0; 912d7f9b2f1SFernando Fernandez Mancera nth->urg_ptr = 0; 913d7f9b2f1SFernando Fernandez Mancera 914d7f9b2f1SFernando Fernandez Mancera synproxy_build_options(nth, opts); 915d7f9b2f1SFernando Fernandez Mancera 916d7f9b2f1SFernando Fernandez Mancera synproxy_send_tcp_ipv6(net, skb, nskb, skb_nfct(skb), 917d7f9b2f1SFernando Fernandez Mancera IP_CT_ESTABLISHED_REPLY, niph, nth, 918d7f9b2f1SFernando Fernandez Mancera tcp_hdr_size); 919d7f9b2f1SFernando Fernandez Mancera } 920d7f9b2f1SFernando Fernandez Mancera EXPORT_SYMBOL_GPL(synproxy_send_client_synack_ipv6); 921d7f9b2f1SFernando Fernandez Mancera 922d7f9b2f1SFernando Fernandez Mancera static void 923d7f9b2f1SFernando Fernandez Mancera synproxy_send_server_syn_ipv6(struct net *net, const struct sk_buff *skb, 924d7f9b2f1SFernando Fernandez Mancera const struct tcphdr *th, 925d7f9b2f1SFernando Fernandez Mancera const struct synproxy_options *opts, u32 recv_seq) 926d7f9b2f1SFernando Fernandez Mancera { 927d7f9b2f1SFernando Fernandez Mancera struct synproxy_net *snet = synproxy_pernet(net); 928d7f9b2f1SFernando Fernandez Mancera struct sk_buff *nskb; 929d7f9b2f1SFernando Fernandez Mancera struct ipv6hdr *iph, *niph; 930d7f9b2f1SFernando Fernandez Mancera struct tcphdr *nth; 931d7f9b2f1SFernando Fernandez Mancera unsigned int tcp_hdr_size; 932d7f9b2f1SFernando Fernandez Mancera 933d7f9b2f1SFernando Fernandez Mancera iph = ipv6_hdr(skb); 934d7f9b2f1SFernando Fernandez Mancera 935d7f9b2f1SFernando Fernandez Mancera tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts); 936d7f9b2f1SFernando Fernandez Mancera nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER, 937d7f9b2f1SFernando Fernandez Mancera GFP_ATOMIC); 938d7f9b2f1SFernando Fernandez Mancera if (!nskb) 939d7f9b2f1SFernando Fernandez Mancera return; 940d7f9b2f1SFernando Fernandez Mancera skb_reserve(nskb, MAX_TCP_HEADER); 941d7f9b2f1SFernando Fernandez Mancera 942d7f9b2f1SFernando Fernandez Mancera niph = synproxy_build_ip_ipv6(net, nskb, &iph->saddr, &iph->daddr); 943d7f9b2f1SFernando Fernandez Mancera 944d7f9b2f1SFernando Fernandez Mancera skb_reset_transport_header(nskb); 945d7f9b2f1SFernando Fernandez Mancera nth = skb_put(nskb, tcp_hdr_size); 946d7f9b2f1SFernando Fernandez Mancera nth->source = th->source; 947d7f9b2f1SFernando Fernandez Mancera nth->dest = th->dest; 948d7f9b2f1SFernando Fernandez Mancera nth->seq = htonl(recv_seq - 1); 949d7f9b2f1SFernando Fernandez Mancera /* ack_seq is used to relay our ISN to the synproxy hook to initialize 950d7f9b2f1SFernando Fernandez Mancera * sequence number translation once a connection tracking entry exists. 951d7f9b2f1SFernando Fernandez Mancera */ 952d7f9b2f1SFernando Fernandez Mancera nth->ack_seq = htonl(ntohl(th->ack_seq) - 1); 953d7f9b2f1SFernando Fernandez Mancera tcp_flag_word(nth) = TCP_FLAG_SYN; 954d7f9b2f1SFernando Fernandez Mancera if (opts->options & NF_SYNPROXY_OPT_ECN) 955d7f9b2f1SFernando Fernandez Mancera tcp_flag_word(nth) |= TCP_FLAG_ECE | TCP_FLAG_CWR; 956d7f9b2f1SFernando Fernandez Mancera nth->doff = tcp_hdr_size / 4; 957d7f9b2f1SFernando Fernandez Mancera nth->window = th->window; 958d7f9b2f1SFernando Fernandez Mancera nth->check = 0; 959d7f9b2f1SFernando Fernandez Mancera nth->urg_ptr = 0; 960d7f9b2f1SFernando Fernandez Mancera 961d7f9b2f1SFernando Fernandez Mancera synproxy_build_options(nth, opts); 962d7f9b2f1SFernando Fernandez Mancera 963d7f9b2f1SFernando Fernandez Mancera synproxy_send_tcp_ipv6(net, skb, nskb, &snet->tmpl->ct_general, 964d7f9b2f1SFernando Fernandez Mancera IP_CT_NEW, niph, nth, tcp_hdr_size); 965d7f9b2f1SFernando Fernandez Mancera } 966d7f9b2f1SFernando Fernandez Mancera 967d7f9b2f1SFernando Fernandez Mancera static void 968d7f9b2f1SFernando Fernandez Mancera synproxy_send_server_ack_ipv6(struct net *net, const struct ip_ct_tcp *state, 969d7f9b2f1SFernando Fernandez Mancera const struct sk_buff *skb, 970d7f9b2f1SFernando Fernandez Mancera const struct tcphdr *th, 971d7f9b2f1SFernando Fernandez Mancera const struct synproxy_options *opts) 972d7f9b2f1SFernando Fernandez Mancera { 973d7f9b2f1SFernando Fernandez Mancera struct sk_buff *nskb; 974d7f9b2f1SFernando Fernandez Mancera struct ipv6hdr *iph, *niph; 975d7f9b2f1SFernando Fernandez Mancera struct tcphdr *nth; 976d7f9b2f1SFernando Fernandez Mancera unsigned int tcp_hdr_size; 977d7f9b2f1SFernando Fernandez Mancera 978d7f9b2f1SFernando Fernandez Mancera iph = ipv6_hdr(skb); 979d7f9b2f1SFernando Fernandez Mancera 980d7f9b2f1SFernando Fernandez Mancera tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts); 981d7f9b2f1SFernando Fernandez Mancera nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER, 982d7f9b2f1SFernando Fernandez Mancera GFP_ATOMIC); 983d7f9b2f1SFernando Fernandez Mancera if (!nskb) 984d7f9b2f1SFernando Fernandez Mancera return; 985d7f9b2f1SFernando Fernandez Mancera skb_reserve(nskb, MAX_TCP_HEADER); 986d7f9b2f1SFernando Fernandez Mancera 987d7f9b2f1SFernando Fernandez Mancera niph = synproxy_build_ip_ipv6(net, nskb, &iph->daddr, &iph->saddr); 988d7f9b2f1SFernando Fernandez Mancera 989d7f9b2f1SFernando Fernandez Mancera skb_reset_transport_header(nskb); 990d7f9b2f1SFernando Fernandez Mancera nth = skb_put(nskb, tcp_hdr_size); 991d7f9b2f1SFernando Fernandez Mancera nth->source = th->dest; 992d7f9b2f1SFernando Fernandez Mancera nth->dest = th->source; 993d7f9b2f1SFernando Fernandez Mancera nth->seq = htonl(ntohl(th->ack_seq)); 994d7f9b2f1SFernando Fernandez Mancera nth->ack_seq = htonl(ntohl(th->seq) + 1); 995d7f9b2f1SFernando Fernandez Mancera tcp_flag_word(nth) = TCP_FLAG_ACK; 996d7f9b2f1SFernando Fernandez Mancera nth->doff = tcp_hdr_size / 4; 997d7f9b2f1SFernando Fernandez Mancera nth->window = htons(state->seen[IP_CT_DIR_ORIGINAL].td_maxwin); 998d7f9b2f1SFernando Fernandez Mancera nth->check = 0; 999d7f9b2f1SFernando Fernandez Mancera nth->urg_ptr = 0; 1000d7f9b2f1SFernando Fernandez Mancera 1001d7f9b2f1SFernando Fernandez Mancera synproxy_build_options(nth, opts); 1002d7f9b2f1SFernando Fernandez Mancera 1003d7f9b2f1SFernando Fernandez Mancera synproxy_send_tcp_ipv6(net, skb, nskb, NULL, 0, niph, nth, 1004d7f9b2f1SFernando Fernandez Mancera tcp_hdr_size); 1005d7f9b2f1SFernando Fernandez Mancera } 1006d7f9b2f1SFernando Fernandez Mancera 1007d7f9b2f1SFernando Fernandez Mancera static void 1008d7f9b2f1SFernando Fernandez Mancera synproxy_send_client_ack_ipv6(struct net *net, const struct sk_buff *skb, 1009d7f9b2f1SFernando Fernandez Mancera const struct tcphdr *th, 1010d7f9b2f1SFernando Fernandez Mancera const struct synproxy_options *opts) 1011d7f9b2f1SFernando Fernandez Mancera { 1012d7f9b2f1SFernando Fernandez Mancera struct sk_buff *nskb; 1013d7f9b2f1SFernando Fernandez Mancera struct ipv6hdr *iph, *niph; 1014d7f9b2f1SFernando Fernandez Mancera struct tcphdr *nth; 1015d7f9b2f1SFernando Fernandez Mancera unsigned int tcp_hdr_size; 1016d7f9b2f1SFernando Fernandez Mancera 1017d7f9b2f1SFernando Fernandez Mancera iph = ipv6_hdr(skb); 1018d7f9b2f1SFernando Fernandez Mancera 1019d7f9b2f1SFernando Fernandez Mancera tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts); 1020d7f9b2f1SFernando Fernandez Mancera nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER, 1021d7f9b2f1SFernando Fernandez Mancera GFP_ATOMIC); 1022d7f9b2f1SFernando Fernandez Mancera if (!nskb) 1023d7f9b2f1SFernando Fernandez Mancera return; 1024d7f9b2f1SFernando Fernandez Mancera skb_reserve(nskb, MAX_TCP_HEADER); 1025d7f9b2f1SFernando Fernandez Mancera 1026d7f9b2f1SFernando Fernandez Mancera niph = synproxy_build_ip_ipv6(net, nskb, &iph->saddr, &iph->daddr); 1027d7f9b2f1SFernando Fernandez Mancera 1028d7f9b2f1SFernando Fernandez Mancera skb_reset_transport_header(nskb); 1029d7f9b2f1SFernando Fernandez Mancera nth = skb_put(nskb, tcp_hdr_size); 1030d7f9b2f1SFernando Fernandez Mancera nth->source = th->source; 1031d7f9b2f1SFernando Fernandez Mancera nth->dest = th->dest; 1032d7f9b2f1SFernando Fernandez Mancera nth->seq = htonl(ntohl(th->seq) + 1); 1033d7f9b2f1SFernando Fernandez Mancera nth->ack_seq = th->ack_seq; 1034d7f9b2f1SFernando Fernandez Mancera tcp_flag_word(nth) = TCP_FLAG_ACK; 1035d7f9b2f1SFernando Fernandez Mancera nth->doff = tcp_hdr_size / 4; 1036d7f9b2f1SFernando Fernandez Mancera nth->window = htons(ntohs(th->window) >> opts->wscale); 1037d7f9b2f1SFernando Fernandez Mancera nth->check = 0; 1038d7f9b2f1SFernando Fernandez Mancera nth->urg_ptr = 0; 1039d7f9b2f1SFernando Fernandez Mancera 1040d7f9b2f1SFernando Fernandez Mancera synproxy_build_options(nth, opts); 1041d7f9b2f1SFernando Fernandez Mancera 1042d7f9b2f1SFernando Fernandez Mancera synproxy_send_tcp_ipv6(net, skb, nskb, skb_nfct(skb), 1043d7f9b2f1SFernando Fernandez Mancera IP_CT_ESTABLISHED_REPLY, niph, nth, 1044d7f9b2f1SFernando Fernandez Mancera tcp_hdr_size); 1045d7f9b2f1SFernando Fernandez Mancera } 1046d7f9b2f1SFernando Fernandez Mancera 1047d7f9b2f1SFernando Fernandez Mancera bool 1048d7f9b2f1SFernando Fernandez Mancera synproxy_recv_client_ack_ipv6(struct net *net, 1049d7f9b2f1SFernando Fernandez Mancera const struct sk_buff *skb, 1050d7f9b2f1SFernando Fernandez Mancera const struct tcphdr *th, 1051d7f9b2f1SFernando Fernandez Mancera struct synproxy_options *opts, u32 recv_seq) 1052d7f9b2f1SFernando Fernandez Mancera { 1053d7f9b2f1SFernando Fernandez Mancera struct synproxy_net *snet = synproxy_pernet(net); 1054d7f9b2f1SFernando Fernandez Mancera int mss; 1055d7f9b2f1SFernando Fernandez Mancera 105622f2efd3SPablo Neira Ayuso mss = nf_cookie_v6_check(ipv6_hdr(skb), th, ntohl(th->ack_seq) - 1); 1057d7f9b2f1SFernando Fernandez Mancera if (mss == 0) { 1058d7f9b2f1SFernando Fernandez Mancera this_cpu_inc(snet->stats->cookie_invalid); 1059d7f9b2f1SFernando Fernandez Mancera return false; 1060d7f9b2f1SFernando Fernandez Mancera } 1061d7f9b2f1SFernando Fernandez Mancera 1062d7f9b2f1SFernando Fernandez Mancera this_cpu_inc(snet->stats->cookie_valid); 1063d7f9b2f1SFernando Fernandez Mancera opts->mss = mss; 1064d7f9b2f1SFernando Fernandez Mancera opts->options |= NF_SYNPROXY_OPT_MSS; 1065d7f9b2f1SFernando Fernandez Mancera 1066d7f9b2f1SFernando Fernandez Mancera if (opts->options & NF_SYNPROXY_OPT_TIMESTAMP) 1067d7f9b2f1SFernando Fernandez Mancera synproxy_check_timestamp_cookie(opts); 1068d7f9b2f1SFernando Fernandez Mancera 1069d7f9b2f1SFernando Fernandez Mancera synproxy_send_server_syn_ipv6(net, skb, th, opts, recv_seq); 1070d7f9b2f1SFernando Fernandez Mancera return true; 1071d7f9b2f1SFernando Fernandez Mancera } 1072d7f9b2f1SFernando Fernandez Mancera EXPORT_SYMBOL_GPL(synproxy_recv_client_ack_ipv6); 1073d7f9b2f1SFernando Fernandez Mancera 1074d7f9b2f1SFernando Fernandez Mancera unsigned int 1075d7f9b2f1SFernando Fernandez Mancera ipv6_synproxy_hook(void *priv, struct sk_buff *skb, 1076d7f9b2f1SFernando Fernandez Mancera const struct nf_hook_state *nhs) 1077d7f9b2f1SFernando Fernandez Mancera { 1078d7f9b2f1SFernando Fernandez Mancera struct net *net = nhs->net; 1079d7f9b2f1SFernando Fernandez Mancera struct synproxy_net *snet = synproxy_pernet(net); 1080d7f9b2f1SFernando Fernandez Mancera enum ip_conntrack_info ctinfo; 1081d7f9b2f1SFernando Fernandez Mancera struct nf_conn *ct; 1082d7f9b2f1SFernando Fernandez Mancera struct nf_conn_synproxy *synproxy; 1083d7f9b2f1SFernando Fernandez Mancera struct synproxy_options opts = {}; 1084d7f9b2f1SFernando Fernandez Mancera const struct ip_ct_tcp *state; 1085d7f9b2f1SFernando Fernandez Mancera struct tcphdr *th, _th; 1086d7f9b2f1SFernando Fernandez Mancera __be16 frag_off; 1087d7f9b2f1SFernando Fernandez Mancera u8 nexthdr; 1088d7f9b2f1SFernando Fernandez Mancera int thoff; 1089d7f9b2f1SFernando Fernandez Mancera 1090d7f9b2f1SFernando Fernandez Mancera ct = nf_ct_get(skb, &ctinfo); 1091d7f9b2f1SFernando Fernandez Mancera if (!ct) 1092d7f9b2f1SFernando Fernandez Mancera return NF_ACCEPT; 1093d7f9b2f1SFernando Fernandez Mancera 1094d7f9b2f1SFernando Fernandez Mancera synproxy = nfct_synproxy(ct); 1095d7f9b2f1SFernando Fernandez Mancera if (!synproxy) 1096d7f9b2f1SFernando Fernandez Mancera return NF_ACCEPT; 1097d7f9b2f1SFernando Fernandez Mancera 1098d7f9b2f1SFernando Fernandez Mancera if (nf_is_loopback_packet(skb)) 1099d7f9b2f1SFernando Fernandez Mancera return NF_ACCEPT; 1100d7f9b2f1SFernando Fernandez Mancera 1101d7f9b2f1SFernando Fernandez Mancera nexthdr = ipv6_hdr(skb)->nexthdr; 1102d7f9b2f1SFernando Fernandez Mancera thoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr, 1103d7f9b2f1SFernando Fernandez Mancera &frag_off); 1104d7f9b2f1SFernando Fernandez Mancera if (thoff < 0 || nexthdr != IPPROTO_TCP) 1105d7f9b2f1SFernando Fernandez Mancera return NF_ACCEPT; 1106d7f9b2f1SFernando Fernandez Mancera 1107d7f9b2f1SFernando Fernandez Mancera th = skb_header_pointer(skb, thoff, sizeof(_th), &_th); 1108d7f9b2f1SFernando Fernandez Mancera if (!th) 1109d7f9b2f1SFernando Fernandez Mancera return NF_DROP; 1110d7f9b2f1SFernando Fernandez Mancera 1111d7f9b2f1SFernando Fernandez Mancera state = &ct->proto.tcp; 1112d7f9b2f1SFernando Fernandez Mancera switch (state->state) { 1113d7f9b2f1SFernando Fernandez Mancera case TCP_CONNTRACK_CLOSE: 1114e971ceb8SFernando Fernandez Mancera if (th->rst && CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL) { 1115d7f9b2f1SFernando Fernandez Mancera nf_ct_seqadj_init(ct, ctinfo, synproxy->isn - 1116d7f9b2f1SFernando Fernandez Mancera ntohl(th->seq) + 1); 1117d7f9b2f1SFernando Fernandez Mancera break; 1118d7f9b2f1SFernando Fernandez Mancera } 1119d7f9b2f1SFernando Fernandez Mancera 1120d7f9b2f1SFernando Fernandez Mancera if (!th->syn || th->ack || 1121d7f9b2f1SFernando Fernandez Mancera CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL) 1122d7f9b2f1SFernando Fernandez Mancera break; 1123d7f9b2f1SFernando Fernandez Mancera 1124d7f9b2f1SFernando Fernandez Mancera /* Reopened connection - reset the sequence number and timestamp 1125d7f9b2f1SFernando Fernandez Mancera * adjustments, they will get initialized once the connection is 1126d7f9b2f1SFernando Fernandez Mancera * reestablished. 1127d7f9b2f1SFernando Fernandez Mancera */ 1128d7f9b2f1SFernando Fernandez Mancera nf_ct_seqadj_init(ct, ctinfo, 0); 1129d7f9b2f1SFernando Fernandez Mancera synproxy->tsoff = 0; 1130d7f9b2f1SFernando Fernandez Mancera this_cpu_inc(snet->stats->conn_reopened); 1131d7f9b2f1SFernando Fernandez Mancera 1132d7f9b2f1SFernando Fernandez Mancera /* fall through */ 1133d7f9b2f1SFernando Fernandez Mancera case TCP_CONNTRACK_SYN_SENT: 1134d7f9b2f1SFernando Fernandez Mancera if (!synproxy_parse_options(skb, thoff, th, &opts)) 1135d7f9b2f1SFernando Fernandez Mancera return NF_DROP; 1136d7f9b2f1SFernando Fernandez Mancera 1137d7f9b2f1SFernando Fernandez Mancera if (!th->syn && th->ack && 1138d7f9b2f1SFernando Fernandez Mancera CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) { 1139d7f9b2f1SFernando Fernandez Mancera /* Keep-Alives are sent with SEG.SEQ = SND.NXT-1, 1140d7f9b2f1SFernando Fernandez Mancera * therefore we need to add 1 to make the SYN sequence 1141d7f9b2f1SFernando Fernandez Mancera * number match the one of first SYN. 1142d7f9b2f1SFernando Fernandez Mancera */ 1143d7f9b2f1SFernando Fernandez Mancera if (synproxy_recv_client_ack_ipv6(net, skb, th, &opts, 1144d7f9b2f1SFernando Fernandez Mancera ntohl(th->seq) + 1)) { 1145d7f9b2f1SFernando Fernandez Mancera this_cpu_inc(snet->stats->cookie_retrans); 1146d7f9b2f1SFernando Fernandez Mancera consume_skb(skb); 1147d7f9b2f1SFernando Fernandez Mancera return NF_STOLEN; 1148d7f9b2f1SFernando Fernandez Mancera } else { 1149d7f9b2f1SFernando Fernandez Mancera return NF_DROP; 1150d7f9b2f1SFernando Fernandez Mancera } 1151d7f9b2f1SFernando Fernandez Mancera } 1152d7f9b2f1SFernando Fernandez Mancera 1153d7f9b2f1SFernando Fernandez Mancera synproxy->isn = ntohl(th->ack_seq); 1154d7f9b2f1SFernando Fernandez Mancera if (opts.options & NF_SYNPROXY_OPT_TIMESTAMP) 1155d7f9b2f1SFernando Fernandez Mancera synproxy->its = opts.tsecr; 1156d7f9b2f1SFernando Fernandez Mancera 1157d7f9b2f1SFernando Fernandez Mancera nf_conntrack_event_cache(IPCT_SYNPROXY, ct); 1158d7f9b2f1SFernando Fernandez Mancera break; 1159d7f9b2f1SFernando Fernandez Mancera case TCP_CONNTRACK_SYN_RECV: 1160d7f9b2f1SFernando Fernandez Mancera if (!th->syn || !th->ack) 1161d7f9b2f1SFernando Fernandez Mancera break; 1162d7f9b2f1SFernando Fernandez Mancera 1163d7f9b2f1SFernando Fernandez Mancera if (!synproxy_parse_options(skb, thoff, th, &opts)) 1164d7f9b2f1SFernando Fernandez Mancera return NF_DROP; 1165d7f9b2f1SFernando Fernandez Mancera 1166d7f9b2f1SFernando Fernandez Mancera if (opts.options & NF_SYNPROXY_OPT_TIMESTAMP) { 1167d7f9b2f1SFernando Fernandez Mancera synproxy->tsoff = opts.tsval - synproxy->its; 1168d7f9b2f1SFernando Fernandez Mancera nf_conntrack_event_cache(IPCT_SYNPROXY, ct); 1169d7f9b2f1SFernando Fernandez Mancera } 1170d7f9b2f1SFernando Fernandez Mancera 1171d7f9b2f1SFernando Fernandez Mancera opts.options &= ~(NF_SYNPROXY_OPT_MSS | 1172d7f9b2f1SFernando Fernandez Mancera NF_SYNPROXY_OPT_WSCALE | 1173d7f9b2f1SFernando Fernandez Mancera NF_SYNPROXY_OPT_SACK_PERM); 1174d7f9b2f1SFernando Fernandez Mancera 1175d7f9b2f1SFernando Fernandez Mancera swap(opts.tsval, opts.tsecr); 1176d7f9b2f1SFernando Fernandez Mancera synproxy_send_server_ack_ipv6(net, state, skb, th, &opts); 1177d7f9b2f1SFernando Fernandez Mancera 1178d7f9b2f1SFernando Fernandez Mancera nf_ct_seqadj_init(ct, ctinfo, synproxy->isn - ntohl(th->seq)); 1179d7f9b2f1SFernando Fernandez Mancera nf_conntrack_event_cache(IPCT_SEQADJ, ct); 1180d7f9b2f1SFernando Fernandez Mancera 1181d7f9b2f1SFernando Fernandez Mancera swap(opts.tsval, opts.tsecr); 1182d7f9b2f1SFernando Fernandez Mancera synproxy_send_client_ack_ipv6(net, skb, th, &opts); 1183d7f9b2f1SFernando Fernandez Mancera 1184d7f9b2f1SFernando Fernandez Mancera consume_skb(skb); 1185d7f9b2f1SFernando Fernandez Mancera return NF_STOLEN; 1186d7f9b2f1SFernando Fernandez Mancera default: 1187d7f9b2f1SFernando Fernandez Mancera break; 1188d7f9b2f1SFernando Fernandez Mancera } 1189d7f9b2f1SFernando Fernandez Mancera 1190d7f9b2f1SFernando Fernandez Mancera synproxy_tstamp_adjust(skb, thoff, th, ct, ctinfo, synproxy); 1191d7f9b2f1SFernando Fernandez Mancera return NF_ACCEPT; 1192d7f9b2f1SFernando Fernandez Mancera } 1193d7f9b2f1SFernando Fernandez Mancera EXPORT_SYMBOL_GPL(ipv6_synproxy_hook); 1194d7f9b2f1SFernando Fernandez Mancera 1195d7f9b2f1SFernando Fernandez Mancera static const struct nf_hook_ops ipv6_synproxy_ops[] = { 1196d7f9b2f1SFernando Fernandez Mancera { 1197d7f9b2f1SFernando Fernandez Mancera .hook = ipv6_synproxy_hook, 1198d7f9b2f1SFernando Fernandez Mancera .pf = NFPROTO_IPV6, 1199d7f9b2f1SFernando Fernandez Mancera .hooknum = NF_INET_LOCAL_IN, 1200d7f9b2f1SFernando Fernandez Mancera .priority = NF_IP_PRI_CONNTRACK_CONFIRM - 1, 1201d7f9b2f1SFernando Fernandez Mancera }, 1202d7f9b2f1SFernando Fernandez Mancera { 1203d7f9b2f1SFernando Fernandez Mancera .hook = ipv6_synproxy_hook, 1204d7f9b2f1SFernando Fernandez Mancera .pf = NFPROTO_IPV6, 1205d7f9b2f1SFernando Fernandez Mancera .hooknum = NF_INET_POST_ROUTING, 1206d7f9b2f1SFernando Fernandez Mancera .priority = NF_IP_PRI_CONNTRACK_CONFIRM - 1, 1207d7f9b2f1SFernando Fernandez Mancera }, 1208d7f9b2f1SFernando Fernandez Mancera }; 1209d7f9b2f1SFernando Fernandez Mancera 1210d7f9b2f1SFernando Fernandez Mancera int 1211d7f9b2f1SFernando Fernandez Mancera nf_synproxy_ipv6_init(struct synproxy_net *snet, struct net *net) 1212d7f9b2f1SFernando Fernandez Mancera { 1213d7f9b2f1SFernando Fernandez Mancera int err; 1214d7f9b2f1SFernando Fernandez Mancera 1215d7f9b2f1SFernando Fernandez Mancera if (snet->hook_ref6 == 0) { 1216d7f9b2f1SFernando Fernandez Mancera err = nf_register_net_hooks(net, ipv6_synproxy_ops, 1217d7f9b2f1SFernando Fernandez Mancera ARRAY_SIZE(ipv6_synproxy_ops)); 1218d7f9b2f1SFernando Fernandez Mancera if (err) 1219d7f9b2f1SFernando Fernandez Mancera return err; 1220d7f9b2f1SFernando Fernandez Mancera } 1221d7f9b2f1SFernando Fernandez Mancera 1222d7f9b2f1SFernando Fernandez Mancera snet->hook_ref6++; 122372c5e118SColin Ian King return 0; 1224d7f9b2f1SFernando Fernandez Mancera } 1225d7f9b2f1SFernando Fernandez Mancera EXPORT_SYMBOL_GPL(nf_synproxy_ipv6_init); 1226d7f9b2f1SFernando Fernandez Mancera 1227d7f9b2f1SFernando Fernandez Mancera void 1228d7f9b2f1SFernando Fernandez Mancera nf_synproxy_ipv6_fini(struct synproxy_net *snet, struct net *net) 1229d7f9b2f1SFernando Fernandez Mancera { 1230d7f9b2f1SFernando Fernandez Mancera snet->hook_ref6--; 1231d7f9b2f1SFernando Fernandez Mancera if (snet->hook_ref6 == 0) 1232d7f9b2f1SFernando Fernandez Mancera nf_unregister_net_hooks(net, ipv6_synproxy_ops, 1233d7f9b2f1SFernando Fernandez Mancera ARRAY_SIZE(ipv6_synproxy_ops)); 1234d7f9b2f1SFernando Fernandez Mancera } 1235d7f9b2f1SFernando Fernandez Mancera EXPORT_SYMBOL_GPL(nf_synproxy_ipv6_fini); 1236d7f9b2f1SFernando Fernandez Mancera #endif /* CONFIG_IPV6 */ 1237d7f9b2f1SFernando Fernandez Mancera 123848b1de4cSPatrick McHardy MODULE_LICENSE("GPL"); 123948b1de4cSPatrick McHardy MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); 1240