1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2013 Patrick McHardy <kaber@trash.net> |
4 | */ |
5 | |
6 | #include <linux/netfilter_ipv4/ip_tables.h> |
7 | #include <linux/netfilter/x_tables.h> |
8 | #include <linux/netfilter/xt_SYNPROXY.h> |
9 | |
10 | #include <net/netfilter/nf_synproxy.h> |
11 | |
12 | static unsigned int |
13 | synproxy_tg4(struct sk_buff *skb, const struct xt_action_param *par) |
14 | { |
15 | const struct xt_synproxy_info *info = par->targinfo; |
16 | struct net *net = xt_net(par); |
17 | struct synproxy_net *snet = synproxy_pernet(net); |
18 | struct synproxy_options opts = {}; |
19 | struct tcphdr *th, _th; |
20 | |
21 | if (nf_ip_checksum(skb, hook: xt_hooknum(par), dataoff: par->thoff, IPPROTO_TCP)) |
22 | return NF_DROP; |
23 | |
24 | th = skb_header_pointer(skb, offset: par->thoff, len: sizeof(_th), buffer: &_th); |
25 | if (th == NULL) |
26 | return NF_DROP; |
27 | |
28 | if (!synproxy_parse_options(skb, doff: par->thoff, th, opts: &opts)) |
29 | return NF_DROP; |
30 | |
31 | if (th->syn && !(th->ack || th->fin || th->rst)) { |
32 | /* Initial SYN from client */ |
33 | this_cpu_inc(snet->stats->syn_received); |
34 | |
35 | if (th->ece && th->cwr) |
36 | opts.options |= XT_SYNPROXY_OPT_ECN; |
37 | |
38 | opts.options &= info->options; |
39 | opts.mss_encode = opts.mss_option; |
40 | opts.mss_option = info->mss; |
41 | if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP) |
42 | synproxy_init_timestamp_cookie(info, opts: &opts); |
43 | else |
44 | opts.options &= ~(XT_SYNPROXY_OPT_WSCALE | |
45 | XT_SYNPROXY_OPT_SACK_PERM | |
46 | XT_SYNPROXY_OPT_ECN); |
47 | |
48 | synproxy_send_client_synack(net, skb, th, opts: &opts); |
49 | consume_skb(skb); |
50 | return NF_STOLEN; |
51 | } else if (th->ack && !(th->fin || th->rst || th->syn)) { |
52 | /* ACK from client */ |
53 | if (synproxy_recv_client_ack(net, skb, th, opts: &opts, ntohl(th->seq))) { |
54 | consume_skb(skb); |
55 | return NF_STOLEN; |
56 | } else { |
57 | return NF_DROP; |
58 | } |
59 | } |
60 | |
61 | return XT_CONTINUE; |
62 | } |
63 | |
64 | static int synproxy_tg4_check(const struct xt_tgchk_param *par) |
65 | { |
66 | struct synproxy_net *snet = synproxy_pernet(net: par->net); |
67 | const struct ipt_entry *e = par->entryinfo; |
68 | int err; |
69 | |
70 | if (e->ip.proto != IPPROTO_TCP || |
71 | e->ip.invflags & XT_INV_PROTO) |
72 | return -EINVAL; |
73 | |
74 | err = nf_ct_netns_get(net: par->net, nfproto: par->family); |
75 | if (err) |
76 | return err; |
77 | |
78 | err = nf_synproxy_ipv4_init(snet, net: par->net); |
79 | if (err) { |
80 | nf_ct_netns_put(net: par->net, nfproto: par->family); |
81 | return err; |
82 | } |
83 | |
84 | return err; |
85 | } |
86 | |
87 | static void synproxy_tg4_destroy(const struct xt_tgdtor_param *par) |
88 | { |
89 | struct synproxy_net *snet = synproxy_pernet(net: par->net); |
90 | |
91 | nf_synproxy_ipv4_fini(snet, net: par->net); |
92 | nf_ct_netns_put(net: par->net, nfproto: par->family); |
93 | } |
94 | |
95 | static struct xt_target synproxy_tg4_reg __read_mostly = { |
96 | .name = "SYNPROXY" , |
97 | .family = NFPROTO_IPV4, |
98 | .hooks = (1 << NF_INET_LOCAL_IN) | (1 << NF_INET_FORWARD), |
99 | .target = synproxy_tg4, |
100 | .targetsize = sizeof(struct xt_synproxy_info), |
101 | .checkentry = synproxy_tg4_check, |
102 | .destroy = synproxy_tg4_destroy, |
103 | .me = THIS_MODULE, |
104 | }; |
105 | |
106 | static int __init synproxy_tg4_init(void) |
107 | { |
108 | return xt_register_target(target: &synproxy_tg4_reg); |
109 | } |
110 | |
111 | static void __exit synproxy_tg4_exit(void) |
112 | { |
113 | xt_unregister_target(target: &synproxy_tg4_reg); |
114 | } |
115 | |
116 | module_init(synproxy_tg4_init); |
117 | module_exit(synproxy_tg4_exit); |
118 | |
119 | MODULE_LICENSE("GPL" ); |
120 | MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>" ); |
121 | MODULE_DESCRIPTION("Intercept TCP connections and establish them using syncookies" ); |
122 | |