Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at master 397 lines 10 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_ipv4.h> 11#include <linux/netfilter/nf_tables.h> 12#include <linux/netfilter/nf_synproxy.h> 13 14struct nft_synproxy { 15 struct nf_synproxy_info info; 16}; 17 18static const struct nla_policy nft_synproxy_policy[NFTA_SYNPROXY_MAX + 1] = { 19 [NFTA_SYNPROXY_MSS] = { .type = NLA_U16 }, 20 [NFTA_SYNPROXY_WSCALE] = NLA_POLICY_MAX(NLA_U8, TCP_MAX_WSCALE), 21 [NFTA_SYNPROXY_FLAGS] = NLA_POLICY_MASK(NLA_BE32, NF_SYNPROXY_OPT_MASK), 22}; 23 24static void nft_synproxy_tcp_options(struct synproxy_options *opts, 25 const struct tcphdr *tcp, 26 struct synproxy_net *snet, 27 struct nf_synproxy_info *info, 28 const struct nft_synproxy *priv) 29{ 30 this_cpu_inc(snet->stats->syn_received); 31 if (tcp->ece && tcp->cwr) 32 opts->options |= NF_SYNPROXY_OPT_ECN; 33 34 opts->options &= priv->info.options; 35 opts->mss_encode = opts->mss_option; 36 opts->mss_option = info->mss; 37 if (opts->options & NF_SYNPROXY_OPT_TIMESTAMP) 38 synproxy_init_timestamp_cookie(info, opts); 39 else 40 opts->options &= ~(NF_SYNPROXY_OPT_WSCALE | 41 NF_SYNPROXY_OPT_SACK_PERM | 42 NF_SYNPROXY_OPT_ECN); 43} 44 45static void nft_synproxy_eval_v4(const struct nft_synproxy *priv, 46 struct nft_regs *regs, 47 const struct nft_pktinfo *pkt, 48 const struct tcphdr *tcp, 49 struct tcphdr *_tcph, 50 struct synproxy_options *opts) 51{ 52 struct nf_synproxy_info info = READ_ONCE(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_synproxy *priv, 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 nf_synproxy_info info = READ_ONCE(priv->info); 84 struct net *net = nft_net(pkt); 85 struct synproxy_net *snet = synproxy_pernet(net); 86 struct sk_buff *skb = pkt->skb; 87 88 if (tcp->syn) { 89 /* Initial SYN from client */ 90 nft_synproxy_tcp_options(opts, tcp, snet, &info, priv); 91 synproxy_send_client_synack_ipv6(net, skb, tcp, opts); 92 consume_skb(skb); 93 regs->verdict.code = NF_STOLEN; 94 } else if (tcp->ack) { 95 /* ACK from client */ 96 if (synproxy_recv_client_ack_ipv6(net, skb, tcp, opts, 97 ntohl(tcp->seq))) { 98 consume_skb(skb); 99 regs->verdict.code = NF_STOLEN; 100 } else { 101 regs->verdict.code = NF_DROP; 102 } 103 } 104} 105#endif /* CONFIG_NF_TABLES_IPV6*/ 106 107static void nft_synproxy_do_eval(const struct nft_synproxy *priv, 108 struct nft_regs *regs, 109 const struct nft_pktinfo *pkt) 110{ 111 struct synproxy_options opts = {}; 112 struct sk_buff *skb = pkt->skb; 113 int thoff = nft_thoff(pkt); 114 const struct tcphdr *tcp; 115 struct tcphdr _tcph; 116 117 if (pkt->tprot != IPPROTO_TCP) { 118 regs->verdict.code = NFT_BREAK; 119 return; 120 } 121 122 if (nf_ip_checksum(skb, nft_hook(pkt), thoff, IPPROTO_TCP)) { 123 regs->verdict.code = NF_DROP; 124 return; 125 } 126 127 tcp = skb_header_pointer(skb, thoff, 128 sizeof(struct tcphdr), 129 &_tcph); 130 if (!tcp) { 131 regs->verdict.code = NF_DROP; 132 return; 133 } 134 135 if (!synproxy_parse_options(skb, thoff, tcp, &opts)) { 136 regs->verdict.code = NF_DROP; 137 return; 138 } 139 140 switch (skb->protocol) { 141 case htons(ETH_P_IP): 142 nft_synproxy_eval_v4(priv, regs, pkt, tcp, &_tcph, &opts); 143 return; 144#if IS_ENABLED(CONFIG_NF_TABLES_IPV6) 145 case htons(ETH_P_IPV6): 146 nft_synproxy_eval_v6(priv, regs, pkt, tcp, &_tcph, &opts); 147 return; 148#endif 149 } 150 regs->verdict.code = NFT_BREAK; 151} 152 153static int nft_synproxy_do_init(const struct nft_ctx *ctx, 154 const struct nlattr * const tb[], 155 struct nft_synproxy *priv) 156{ 157 struct synproxy_net *snet = synproxy_pernet(ctx->net); 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 err = nf_synproxy_ipv4_init(snet, ctx->net); 191 if (err) 192 goto nf_ct_failure; 193 err = nf_synproxy_ipv6_init(snet, ctx->net); 194 if (err) { 195 nf_synproxy_ipv4_fini(snet, ctx->net); 196 goto nf_ct_failure; 197 } 198 break; 199 } 200 201 return 0; 202 203nf_ct_failure: 204 nf_ct_netns_put(ctx->net, ctx->family); 205 return err; 206} 207 208static void nft_synproxy_do_destroy(const struct nft_ctx *ctx) 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 nf_synproxy_ipv4_fini(snet, ctx->net); 223 nf_synproxy_ipv6_fini(snet, ctx->net); 224 break; 225 } 226 nf_ct_netns_put(ctx->net, ctx->family); 227} 228 229static int nft_synproxy_do_dump(struct sk_buff *skb, struct nft_synproxy *priv) 230{ 231 if (nla_put_be16(skb, NFTA_SYNPROXY_MSS, htons(priv->info.mss)) || 232 nla_put_u8(skb, NFTA_SYNPROXY_WSCALE, priv->info.wscale) || 233 nla_put_be32(skb, NFTA_SYNPROXY_FLAGS, htonl(priv->info.options))) 234 goto nla_put_failure; 235 236 return 0; 237 238nla_put_failure: 239 return -1; 240} 241 242static void nft_synproxy_eval(const struct nft_expr *expr, 243 struct nft_regs *regs, 244 const struct nft_pktinfo *pkt) 245{ 246 const struct nft_synproxy *priv = nft_expr_priv(expr); 247 248 nft_synproxy_do_eval(priv, regs, pkt); 249} 250 251static int nft_synproxy_validate(const struct nft_ctx *ctx, 252 const struct nft_expr *expr) 253{ 254 if (ctx->family != NFPROTO_IPV4 && 255 ctx->family != NFPROTO_IPV6 && 256 ctx->family != NFPROTO_INET) 257 return -EOPNOTSUPP; 258 259 return nft_chain_validate_hooks(ctx->chain, (1 << NF_INET_LOCAL_IN) | 260 (1 << NF_INET_FORWARD)); 261} 262 263static int nft_synproxy_init(const struct nft_ctx *ctx, 264 const struct nft_expr *expr, 265 const struct nlattr * const tb[]) 266{ 267 struct nft_synproxy *priv = nft_expr_priv(expr); 268 269 return nft_synproxy_do_init(ctx, tb, priv); 270} 271 272static void nft_synproxy_destroy(const struct nft_ctx *ctx, 273 const struct nft_expr *expr) 274{ 275 nft_synproxy_do_destroy(ctx); 276} 277 278static int nft_synproxy_dump(struct sk_buff *skb, 279 const struct nft_expr *expr, bool reset) 280{ 281 struct nft_synproxy *priv = nft_expr_priv(expr); 282 283 return nft_synproxy_do_dump(skb, priv); 284} 285 286static struct nft_expr_type nft_synproxy_type; 287static const struct nft_expr_ops nft_synproxy_ops = { 288 .eval = nft_synproxy_eval, 289 .size = NFT_EXPR_SIZE(sizeof(struct nft_synproxy)), 290 .init = nft_synproxy_init, 291 .destroy = nft_synproxy_destroy, 292 .dump = nft_synproxy_dump, 293 .type = &nft_synproxy_type, 294 .validate = nft_synproxy_validate, 295}; 296 297static struct nft_expr_type nft_synproxy_type __read_mostly = { 298 .ops = &nft_synproxy_ops, 299 .name = "synproxy", 300 .owner = THIS_MODULE, 301 .policy = nft_synproxy_policy, 302 .maxattr = NFTA_SYNPROXY_MAX, 303}; 304 305static int nft_synproxy_obj_init(const struct nft_ctx *ctx, 306 const struct nlattr * const tb[], 307 struct nft_object *obj) 308{ 309 struct nft_synproxy *priv = nft_obj_data(obj); 310 311 return nft_synproxy_do_init(ctx, tb, priv); 312} 313 314static void nft_synproxy_obj_destroy(const struct nft_ctx *ctx, 315 struct nft_object *obj) 316{ 317 nft_synproxy_do_destroy(ctx); 318} 319 320static int nft_synproxy_obj_dump(struct sk_buff *skb, 321 struct nft_object *obj, bool reset) 322{ 323 struct nft_synproxy *priv = nft_obj_data(obj); 324 325 return nft_synproxy_do_dump(skb, priv); 326} 327 328static void nft_synproxy_obj_eval(struct nft_object *obj, 329 struct nft_regs *regs, 330 const struct nft_pktinfo *pkt) 331{ 332 const struct nft_synproxy *priv = nft_obj_data(obj); 333 334 nft_synproxy_do_eval(priv, regs, pkt); 335} 336 337static void nft_synproxy_obj_update(struct nft_object *obj, 338 struct nft_object *newobj) 339{ 340 struct nft_synproxy *newpriv = nft_obj_data(newobj); 341 struct nft_synproxy *priv = nft_obj_data(obj); 342 343 WRITE_ONCE(priv->info, newpriv->info); 344} 345 346static struct nft_object_type nft_synproxy_obj_type; 347static const struct nft_object_ops nft_synproxy_obj_ops = { 348 .type = &nft_synproxy_obj_type, 349 .size = sizeof(struct nft_synproxy), 350 .init = nft_synproxy_obj_init, 351 .destroy = nft_synproxy_obj_destroy, 352 .dump = nft_synproxy_obj_dump, 353 .eval = nft_synproxy_obj_eval, 354 .update = nft_synproxy_obj_update, 355}; 356 357static struct nft_object_type nft_synproxy_obj_type __read_mostly = { 358 .type = NFT_OBJECT_SYNPROXY, 359 .ops = &nft_synproxy_obj_ops, 360 .maxattr = NFTA_SYNPROXY_MAX, 361 .policy = nft_synproxy_policy, 362 .owner = THIS_MODULE, 363}; 364 365static int __init nft_synproxy_module_init(void) 366{ 367 int err; 368 369 err = nft_register_obj(&nft_synproxy_obj_type); 370 if (err < 0) 371 return err; 372 373 err = nft_register_expr(&nft_synproxy_type); 374 if (err < 0) 375 goto err; 376 377 return 0; 378 379err: 380 nft_unregister_obj(&nft_synproxy_obj_type); 381 return err; 382} 383 384static void __exit nft_synproxy_module_exit(void) 385{ 386 nft_unregister_expr(&nft_synproxy_type); 387 nft_unregister_obj(&nft_synproxy_obj_type); 388} 389 390module_init(nft_synproxy_module_init); 391module_exit(nft_synproxy_module_exit); 392 393MODULE_LICENSE("GPL"); 394MODULE_AUTHOR("Fernando Fernandez <ffmancera@riseup.net>"); 395MODULE_ALIAS_NFT_EXPR("synproxy"); 396MODULE_ALIAS_NFT_OBJ(NFT_OBJECT_SYNPROXY); 397MODULE_DESCRIPTION("nftables SYNPROXY expression support");