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