at v5.3-rc4 289 lines 7.6 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2#include <linux/types.h> 3#include <net/ip.h> 4#include <net/tcp.h> 5#include <net/netlink.h> 6#include <net/netfilter/nf_tables.h> 7#include <net/netfilter/nf_conntrack.h> 8#include <net/netfilter/nf_conntrack_synproxy.h> 9#include <net/netfilter/nf_synproxy.h> 10#include <linux/netfilter/nf_tables.h> 11#include <linux/netfilter/nf_synproxy.h> 12 13struct nft_synproxy { 14 struct nf_synproxy_info info; 15}; 16 17static const struct nla_policy nft_synproxy_policy[NFTA_SYNPROXY_MAX + 1] = { 18 [NFTA_SYNPROXY_MSS] = { .type = NLA_U16 }, 19 [NFTA_SYNPROXY_WSCALE] = { .type = NLA_U8 }, 20 [NFTA_SYNPROXY_FLAGS] = { .type = NLA_U32 }, 21}; 22 23static void nft_synproxy_tcp_options(struct synproxy_options *opts, 24 const struct tcphdr *tcp, 25 struct synproxy_net *snet, 26 struct nf_synproxy_info *info, 27 struct nft_synproxy *priv) 28{ 29 this_cpu_inc(snet->stats->syn_received); 30 if (tcp->ece && tcp->cwr) 31 opts->options |= NF_SYNPROXY_OPT_ECN; 32 33 opts->options &= priv->info.options; 34 opts->mss_encode = opts->mss; 35 opts->mss = info->mss; 36 if (opts->options & NF_SYNPROXY_OPT_TIMESTAMP) 37 synproxy_init_timestamp_cookie(info, opts); 38 else 39 opts->options &= ~(NF_SYNPROXY_OPT_WSCALE | 40 NF_SYNPROXY_OPT_SACK_PERM | 41 NF_SYNPROXY_OPT_ECN); 42} 43 44static void nft_synproxy_eval_v4(const struct nft_expr *expr, 45 struct nft_regs *regs, 46 const struct nft_pktinfo *pkt, 47 const struct tcphdr *tcp, 48 struct tcphdr *_tcph, 49 struct synproxy_options *opts) 50{ 51 struct nft_synproxy *priv = nft_expr_priv(expr); 52 struct nf_synproxy_info info = priv->info; 53 struct net *net = nft_net(pkt); 54 struct synproxy_net *snet = synproxy_pernet(net); 55 struct sk_buff *skb = pkt->skb; 56 57 if (tcp->syn) { 58 /* Initial SYN from client */ 59 nft_synproxy_tcp_options(opts, tcp, snet, &info, priv); 60 synproxy_send_client_synack(net, skb, tcp, opts); 61 consume_skb(skb); 62 regs->verdict.code = NF_STOLEN; 63 } else if (tcp->ack) { 64 /* ACK from client */ 65 if (synproxy_recv_client_ack(net, skb, tcp, opts, 66 ntohl(tcp->seq))) { 67 consume_skb(skb); 68 regs->verdict.code = NF_STOLEN; 69 } else { 70 regs->verdict.code = NF_DROP; 71 } 72 } 73} 74 75#if IS_ENABLED(CONFIG_NF_TABLES_IPV6) 76static void nft_synproxy_eval_v6(const struct nft_expr *expr, 77 struct nft_regs *regs, 78 const struct nft_pktinfo *pkt, 79 const struct tcphdr *tcp, 80 struct tcphdr *_tcph, 81 struct synproxy_options *opts) 82{ 83 struct nft_synproxy *priv = nft_expr_priv(expr); 84 struct nf_synproxy_info info = priv->info; 85 struct net *net = nft_net(pkt); 86 struct synproxy_net *snet = synproxy_pernet(net); 87 struct sk_buff *skb = pkt->skb; 88 89 if (tcp->syn) { 90 /* Initial SYN from client */ 91 nft_synproxy_tcp_options(opts, tcp, snet, &info, priv); 92 synproxy_send_client_synack_ipv6(net, skb, tcp, opts); 93 consume_skb(skb); 94 regs->verdict.code = NF_STOLEN; 95 } else if (tcp->ack) { 96 /* ACK from client */ 97 if (synproxy_recv_client_ack_ipv6(net, skb, tcp, opts, 98 ntohl(tcp->seq))) { 99 consume_skb(skb); 100 regs->verdict.code = NF_STOLEN; 101 } else { 102 regs->verdict.code = NF_DROP; 103 } 104 } 105} 106#endif /* CONFIG_NF_TABLES_IPV6*/ 107 108static void nft_synproxy_eval(const struct nft_expr *expr, 109 struct nft_regs *regs, 110 const struct nft_pktinfo *pkt) 111{ 112 struct synproxy_options opts = {}; 113 struct sk_buff *skb = pkt->skb; 114 int thoff = pkt->xt.thoff; 115 const struct tcphdr *tcp; 116 struct tcphdr _tcph; 117 118 if (pkt->tprot != IPPROTO_TCP) { 119 regs->verdict.code = NFT_BREAK; 120 return; 121 } 122 123 if (nf_ip_checksum(skb, nft_hook(pkt), thoff, IPPROTO_TCP)) { 124 regs->verdict.code = NF_DROP; 125 return; 126 } 127 128 tcp = skb_header_pointer(skb, pkt->xt.thoff, 129 sizeof(struct tcphdr), 130 &_tcph); 131 if (!tcp) { 132 regs->verdict.code = NF_DROP; 133 return; 134 } 135 136 if (!synproxy_parse_options(skb, thoff, tcp, &opts)) { 137 regs->verdict.code = NF_DROP; 138 return; 139 } 140 141 switch (skb->protocol) { 142 case htons(ETH_P_IP): 143 nft_synproxy_eval_v4(expr, regs, pkt, tcp, &_tcph, &opts); 144 return; 145#if IS_ENABLED(CONFIG_NF_TABLES_IPV6) 146 case htons(ETH_P_IPV6): 147 nft_synproxy_eval_v6(expr, regs, pkt, tcp, &_tcph, &opts); 148 return; 149#endif 150 } 151 regs->verdict.code = NFT_BREAK; 152} 153 154static int nft_synproxy_init(const struct nft_ctx *ctx, 155 const struct nft_expr *expr, 156 const struct nlattr * const tb[]) 157{ 158 struct synproxy_net *snet = synproxy_pernet(ctx->net); 159 struct nft_synproxy *priv = nft_expr_priv(expr); 160 u32 flags; 161 int err; 162 163 if (tb[NFTA_SYNPROXY_MSS]) 164 priv->info.mss = ntohs(nla_get_be16(tb[NFTA_SYNPROXY_MSS])); 165 if (tb[NFTA_SYNPROXY_WSCALE]) 166 priv->info.wscale = nla_get_u8(tb[NFTA_SYNPROXY_WSCALE]); 167 if (tb[NFTA_SYNPROXY_FLAGS]) { 168 flags = ntohl(nla_get_be32(tb[NFTA_SYNPROXY_FLAGS])); 169 if (flags & ~NF_SYNPROXY_OPT_MASK) 170 return -EOPNOTSUPP; 171 priv->info.options = flags; 172 } 173 174 err = nf_ct_netns_get(ctx->net, ctx->family); 175 if (err) 176 return err; 177 178 switch (ctx->family) { 179 case NFPROTO_IPV4: 180 err = nf_synproxy_ipv4_init(snet, ctx->net); 181 if (err) 182 goto nf_ct_failure; 183 break; 184#if IS_ENABLED(CONFIG_NF_TABLES_IPV6) 185 case NFPROTO_IPV6: 186 err = nf_synproxy_ipv6_init(snet, ctx->net); 187 if (err) 188 goto nf_ct_failure; 189 break; 190#endif 191 case NFPROTO_INET: 192 case NFPROTO_BRIDGE: 193 err = nf_synproxy_ipv4_init(snet, ctx->net); 194 if (err) 195 goto nf_ct_failure; 196 err = nf_synproxy_ipv6_init(snet, ctx->net); 197 if (err) 198 goto nf_ct_failure; 199 break; 200 } 201 202 return 0; 203 204nf_ct_failure: 205 nf_ct_netns_put(ctx->net, ctx->family); 206 return err; 207} 208 209static void nft_synproxy_destroy(const struct nft_ctx *ctx, 210 const struct nft_expr *expr) 211{ 212 struct synproxy_net *snet = synproxy_pernet(ctx->net); 213 214 switch (ctx->family) { 215 case NFPROTO_IPV4: 216 nf_synproxy_ipv4_fini(snet, ctx->net); 217 break; 218#if IS_ENABLED(CONFIG_NF_TABLES_IPV6) 219 case NFPROTO_IPV6: 220 nf_synproxy_ipv6_fini(snet, ctx->net); 221 break; 222#endif 223 case NFPROTO_INET: 224 case NFPROTO_BRIDGE: 225 nf_synproxy_ipv4_fini(snet, ctx->net); 226 nf_synproxy_ipv6_fini(snet, ctx->net); 227 break; 228 } 229 nf_ct_netns_put(ctx->net, ctx->family); 230} 231 232static int nft_synproxy_dump(struct sk_buff *skb, const struct nft_expr *expr) 233{ 234 const struct nft_synproxy *priv = nft_expr_priv(expr); 235 236 if (nla_put_be16(skb, NFTA_SYNPROXY_MSS, htons(priv->info.mss)) || 237 nla_put_u8(skb, NFTA_SYNPROXY_WSCALE, priv->info.wscale) || 238 nla_put_be32(skb, NFTA_SYNPROXY_FLAGS, htonl(priv->info.options))) 239 goto nla_put_failure; 240 241 return 0; 242 243nla_put_failure: 244 return -1; 245} 246 247static int nft_synproxy_validate(const struct nft_ctx *ctx, 248 const struct nft_expr *expr, 249 const struct nft_data **data) 250{ 251 return nft_chain_validate_hooks(ctx->chain, (1 << NF_INET_LOCAL_IN) | 252 (1 << NF_INET_FORWARD)); 253} 254 255static struct nft_expr_type nft_synproxy_type; 256static const struct nft_expr_ops nft_synproxy_ops = { 257 .eval = nft_synproxy_eval, 258 .size = NFT_EXPR_SIZE(sizeof(struct nft_synproxy)), 259 .init = nft_synproxy_init, 260 .destroy = nft_synproxy_destroy, 261 .dump = nft_synproxy_dump, 262 .type = &nft_synproxy_type, 263 .validate = nft_synproxy_validate, 264}; 265 266static struct nft_expr_type nft_synproxy_type __read_mostly = { 267 .ops = &nft_synproxy_ops, 268 .name = "synproxy", 269 .owner = THIS_MODULE, 270 .policy = nft_synproxy_policy, 271 .maxattr = NFTA_SYNPROXY_MAX, 272}; 273 274static int __init nft_synproxy_module_init(void) 275{ 276 return nft_register_expr(&nft_synproxy_type); 277} 278 279static void __exit nft_synproxy_module_exit(void) 280{ 281 return nft_unregister_expr(&nft_synproxy_type); 282} 283 284module_init(nft_synproxy_module_init); 285module_exit(nft_synproxy_module_exit); 286 287MODULE_LICENSE("GPL"); 288MODULE_AUTHOR("Fernando Fernandez <ffmancera@riseup.net>"); 289MODULE_ALIAS_NFT_EXPR("synproxy");