static int __init chaos_tg_init(void) { int ret = -EINVAL; xm_tcp = xt_request_find_match(NFPROTO_IPV4, "tcp", 0); if (xm_tcp == NULL) { printk(KERN_WARNING PFX "Error: Could not find or load " "\"tcp\" match\n"); return -EINVAL; } xt_reject = xt_request_find_target(NFPROTO_IPV4, "REJECT", 0); if (xt_reject == NULL) { printk(KERN_WARNING PFX "Error: Could not find or load " "\"REJECT\" target\n"); goto out2; } xt_tarpit = xt_request_find_target(NFPROTO_IPV4, "TARPIT", 0); have_tarpit = xt_tarpit != NULL; if (!have_tarpit) printk(KERN_WARNING PFX "Warning: Could not find or load " "\"TARPIT\" target\n"); xt_delude = xt_request_find_target(NFPROTO_IPV4, "DELUDE", 0); have_delude = xt_delude != NULL; if (!have_delude) printk(KERN_WARNING PFX "Warning: Could not find or load " "\"DELUDE\" target\n"); if ((ret = xt_register_target(&chaos_tg_reg)) != 0) { printk(KERN_WARNING PFX "xt_register_target returned " "error %d\n", ret); goto out3; } return 0; out3: if (have_delude) module_put(xt_delude->me); if (have_tarpit) module_put(xt_tarpit->me); module_put(xt_reject->me); out2: module_put(xm_tcp->me); return ret; }
static int ipt_init_target(struct ipt_entry_target *t, char *table, unsigned int hook) { struct xt_target *target; int ret = 0; target = xt_request_find_target(AF_INET, t->u.user.name, t->u.user.revision); if (!target) return -ENOENT; t->u.kernel.target = target; ret = xt_check_target(target, AF_INET, t->u.target_size - sizeof(*t), table, hook, 0, 0); if (ret) { module_put(t->u.kernel.target->me); return ret; } if (t->u.kernel.target->checkentry && !t->u.kernel.target->checkentry(table, NULL, t->u.kernel.target, t->data, hook)) { module_put(t->u.kernel.target->me); ret = -EINVAL; } return ret; }
static int ipt_init_target(struct xt_entry_target *t, char *table, unsigned int hook) { struct xt_tgchk_param par; struct xt_target *target; int ret = 0; target = xt_request_find_target(AF_INET, t->u.user.name, t->u.user.revision); if (IS_ERR(target)) return PTR_ERR(target); t->u.kernel.target = target; par.table = table; par.entryinfo = NULL; par.target = target; par.targinfo = t->data; par.hook_mask = hook; par.family = NFPROTO_IPV4; ret = xt_check_target(&par, t->u.target_size - sizeof(*t), 0, false); if (ret < 0) { module_put(t->u.kernel.target->me); return ret; } return 0; }
static const struct nft_expr_ops * nft_target_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[]) { struct nft_expr_ops *ops; struct xt_target *target; char *tg_name; u32 rev, family; int err; if (tb[NFTA_TARGET_NAME] == NULL || tb[NFTA_TARGET_REV] == NULL || tb[NFTA_TARGET_INFO] == NULL) return ERR_PTR(-EINVAL); tg_name = nla_data(tb[NFTA_TARGET_NAME]); rev = ntohl(nla_get_be32(tb[NFTA_TARGET_REV])); family = ctx->family; if (strcmp(tg_name, XT_ERROR_TARGET) == 0 || strcmp(tg_name, XT_STANDARD_TARGET) == 0 || strcmp(tg_name, "standard") == 0) return ERR_PTR(-EINVAL); target = xt_request_find_target(family, tg_name, rev); if (IS_ERR(target)) return ERR_PTR(-ENOENT); if (!target->target) { err = -EINVAL; goto err; } if (target->targetsize > nla_len(tb[NFTA_TARGET_INFO])) { err = -EINVAL; goto err; } ops = kzalloc(sizeof(struct nft_expr_ops), GFP_KERNEL); if (!ops) { err = -ENOMEM; goto err; } ops->type = &nft_target_type; ops->size = NFT_EXPR_SIZE(XT_ALIGN(target->targetsize)); ops->init = nft_target_init; ops->destroy = nft_target_destroy; ops->dump = nft_target_dump; ops->validate = nft_target_validate; ops->data = target; if (family == NFPROTO_BRIDGE) ops->eval = nft_target_eval_bridge; else ops->eval = nft_target_eval_xt; return ops; err: module_put(target->me); return ERR_PTR(err); }
/* CHAOS functions */ static void xt_chaos_total(struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_chaos_tginfo *info = par->targinfo; const struct iphdr *iph = ip_hdr(skb); const int thoff = 4 * iph->ihl; const int fragoff = ntohs(iph->frag_off) & IP_OFFSET; typeof(xt_tarpit) destiny; bool ret; #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 22) int hotdrop = false; #else bool hotdrop = false; #endif #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 27) ret = xm_tcp->match(skb, par->in, par->out, xm_tcp, &tcp_params, fragoff, thoff, &hotdrop); #elif LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 34) { struct xt_match_param local_par = { .in = par->in, .out = par->out, .match = xm_tcp, .matchinfo = &tcp_params, .fragoff = fragoff, .thoff = thoff, .hotdrop = &hotdrop, }; ret = xm_tcp->match(skb, &local_par); } #else { struct xt_action_param local_par; local_par.in = par->in, local_par.out = par->out, local_par.match = xm_tcp; local_par.matchinfo = &tcp_params; local_par.fragoff = fragoff; local_par.thoff = thoff; local_par.hotdrop = false; ret = xm_tcp->match(skb, &local_par); hotdrop = local_par.hotdrop; } #endif if (!ret || hotdrop || (unsigned int)net_random() > delude_percentage) return; destiny = (info->variant == XTCHAOS_TARPIT) ? xt_tarpit : xt_delude; #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) destiny->target(&skb, par->in, par->out, par->hooknum, destiny, NULL, NULL); #elif LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 23) destiny->target(&skb, par->in, par->out, par->hooknum, destiny, NULL); #elif LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 27) destiny->target(skb, par->in, par->out, par->hooknum, destiny, NULL); #elif LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 34) { struct xt_target_param local_par = { .in = par->in, .out = par->out, .hooknum = par->hooknum, .target = destiny, .targinfo = par->targinfo, .family = par->family, }; destiny->target(skb, &local_par); } #else { struct xt_action_param local_par; local_par.in = par->in; local_par.out = par->out; local_par.hooknum = par->hooknum; local_par.target = destiny; local_par.targinfo = par->targinfo; local_par.family = par->family; destiny->target(skb, &local_par); } #endif } static unsigned int chaos_tg(struct sk_buff **pskb, const struct xt_action_param *par) { /* * Equivalent to: * -A chaos -m statistic --mode random --probability \ * $reject_percentage -j REJECT --reject-with host-unreach; * -A chaos -p tcp -m statistic --mode random --probability \ * $delude_percentage -j DELUDE; * -A chaos -j DROP; */ const struct xt_chaos_tginfo *info = par->targinfo; struct sk_buff *skb = *pskb; const struct iphdr *iph = ip_hdr(skb); if ((unsigned int)net_random() <= reject_percentage) { #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) return xt_reject->target(pskb, par->in, par->out, par->hooknum, xt_reject, &reject_params, NULL); #elif LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 23) return xt_reject->target(pskb, par->in, par->out, par->hooknum, xt_reject, &reject_params); #elif LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 27) return xt_reject->target(skb, par->in, par->out, par->hooknum, xt_reject, &reject_params); #elif LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 34) struct xt_target_param local_par = { .in = par->in, .out = par->out, .hooknum = par->hooknum, .target = xt_reject, .targinfo = &reject_params, }; return xt_reject->target(skb, &local_par); #else struct xt_action_param local_par; local_par.in = par->in; local_par.out = par->out; local_par.hooknum = par->hooknum; local_par.target = xt_reject; local_par.targinfo = &reject_params; return xt_reject->target(skb, &local_par); #endif } /* TARPIT/DELUDE may not be called from the OUTPUT chain */ if (iph->protocol == IPPROTO_TCP && info->variant != XTCHAOS_NORMAL && par->hooknum != NF_INET_LOCAL_OUT) xt_chaos_total(skb, par); return NF_DROP; } static int chaos_tg_check(const struct xt_tgchk_param *par) { const struct xt_chaos_tginfo *info = par->targinfo; if (info->variant == XTCHAOS_DELUDE && !have_delude) { printk(KERN_WARNING PFX "Error: Cannot use --delude when " "DELUDE module not available\n"); return -EINVAL; } if (info->variant == XTCHAOS_TARPIT && !have_tarpit) { printk(KERN_WARNING PFX "Error: Cannot use --tarpit when " "TARPIT module not available\n"); return -EINVAL; } return 0; } static struct xt_target chaos_tg_reg = { .name = "CHAOS", .revision = 0, .family = NFPROTO_IPV4, .table = "filter", .hooks = (1 << NF_INET_LOCAL_IN) | (1 << NF_INET_FORWARD) | (1 << NF_INET_LOCAL_OUT), .target = chaos_tg, .checkentry = chaos_tg_check, .targetsize = sizeof(struct xt_chaos_tginfo), .me = THIS_MODULE, }; static int __init chaos_tg_init(void) { int ret = -EINVAL; xm_tcp = xt_request_find_match(NFPROTO_IPV4, "tcp", 0); if (xm_tcp == NULL) { printk(KERN_WARNING PFX "Error: Could not find or load " "\"tcp\" match\n"); return -EINVAL; } xt_reject = xt_request_find_target(NFPROTO_IPV4, "REJECT", 0); if (xt_reject == NULL) { printk(KERN_WARNING PFX "Error: Could not find or load " "\"REJECT\" target\n"); goto out2; } xt_tarpit = xt_request_find_target(NFPROTO_IPV4, "TARPIT", 0); have_tarpit = xt_tarpit != NULL; if (!have_tarpit) printk(KERN_WARNING PFX "Warning: Could not find or load " "\"TARPIT\" target\n"); xt_delude = xt_request_find_target(NFPROTO_IPV4, "DELUDE", 0); have_delude = xt_delude != NULL; if (!have_delude) printk(KERN_WARNING PFX "Warning: Could not find or load " "\"DELUDE\" target\n"); if ((ret = xt_register_target(&chaos_tg_reg)) != 0) { printk(KERN_WARNING PFX "xt_register_target returned " "error %d\n", ret); goto out3; } return 0; out3: if (have_delude) module_put(xt_delude->me); if (have_tarpit) module_put(xt_tarpit->me); module_put(xt_reject->me); out2: module_put(xm_tcp->me); return ret; } static void __exit chaos_tg_exit(void) { xt_unregister_target(&chaos_tg_reg); module_put(xm_tcp->me); module_put(xt_reject->me); if (have_delude) module_put(xt_delude->me); if (have_tarpit) module_put(xt_tarpit->me); } module_init(chaos_tg_init); module_exit(chaos_tg_exit); MODULE_DESCRIPTION("Xtables: Network scan slowdown with non-deterministic results"); MODULE_AUTHOR("Jan Engelhardt <*****@*****.**>"); MODULE_LICENSE("GPL"); MODULE_ALIAS("ipt_CHAOS");