static void nft_ct_l3proto_module_put(uint8_t family) { if (family == NFPROTO_INET) { nf_ct_l3proto_module_put(NFPROTO_IPV4); nf_ct_l3proto_module_put(NFPROTO_IPV6); } else nf_ct_l3proto_module_put(family); }
static int nf_ct_netns_do_get(struct net *net, u8 nfproto) { const struct nf_conntrack_l3proto *l3proto; int ret; might_sleep(); ret = nf_ct_l3proto_try_module_get(nfproto); if (ret < 0) return ret; /* we already have a reference, can't fail */ rcu_read_lock(); l3proto = __nf_ct_l3proto_find(nfproto); rcu_read_unlock(); if (!l3proto->net_ns_get) return 0; ret = l3proto->net_ns_get(net); if (ret < 0) nf_ct_l3proto_module_put(nfproto); return ret; }
static void xt_ct_tg_destroy_v1(const struct xt_tgdtor_param *par) { struct xt_ct_target_info_v1 *info = par->targinfo; struct nf_conn *ct = info->ct; struct nf_conn_help *help; #ifdef CONFIG_NF_CONNTRACK_TIMEOUT struct nf_conn_timeout *timeout_ext; typeof(nf_ct_timeout_put_hook) timeout_put; #endif if (!nf_ct_is_untracked(ct)) { help = nfct_help(ct); if (help) module_put(help->helper->me); nf_ct_l3proto_module_put(par->family); #ifdef CONFIG_NF_CONNTRACK_TIMEOUT rcu_read_lock(); timeout_put = rcu_dereference(nf_ct_timeout_put_hook); if (timeout_put) { timeout_ext = nf_ct_timeout_find(ct); if (timeout_ext) timeout_put(timeout_ext->timeout); } rcu_read_unlock(); #endif } nf_ct_put(info->ct); }
static bool conntrack_mt_check_v1(const struct xt_mtchk_param *par) { struct xt_conntrack_mtinfo1 *info = par->matchinfo; struct xt_conntrack_mtinfo2 *up; int ret = conntrack_mt_check(par); if (ret < 0) return ret; up = kmalloc(sizeof(*up), GFP_KERNEL); if (up == NULL) { nf_ct_l3proto_module_put(par->family); return -ENOMEM; } /* * The strategy here is to minimize the overhead of v1 matching, * by prebuilding a v2 struct and putting the pointer into the * v1 dataspace. */ memcpy(up, info, offsetof(typeof(*info), state_mask)); up->state_mask = info->state_mask; up->status_mask = info->status_mask; *(void **)info = up; return true; }
static int xt_ct_tg_check(const struct xt_tgchk_param *par, struct xt_ct_target_info_v1 *info) { struct nf_conntrack_tuple t; struct nf_conn *ct; int ret = -EOPNOTSUPP; if (info->flags & XT_CT_NOTRACK) { ct = NULL; goto out; } #ifndef CONFIG_NF_CONNTRACK_ZONES if (info->zone) goto err1; #endif ret = nf_ct_l3proto_try_module_get(par->family); if (ret < 0) goto err1; memset(&t, 0, sizeof(t)); ct = nf_conntrack_alloc(par->net, info->zone, &t, &t, GFP_KERNEL); ret = PTR_ERR(ct); if (IS_ERR(ct)) goto err2; ret = 0; if ((info->ct_events || info->exp_events) && !nf_ct_ecache_ext_add(ct, info->ct_events, info->exp_events, GFP_KERNEL)) { ret = -EINVAL; goto err3; } if (info->helper[0]) { ret = xt_ct_set_helper(ct, info->helper, par); if (ret < 0) goto err3; } if (info->timeout[0]) { ret = xt_ct_set_timeout(ct, par, info->timeout); if (ret < 0) goto err3; } nf_conntrack_tmpl_insert(par->net, ct); out: info->ct = ct; return 0; err3: nf_conntrack_free(ct); err2: nf_ct_l3proto_module_put(par->family); err1: return ret; }
void nf_nat_l3proto_unregister(const struct nf_nat_l3proto *l3proto) { mutex_lock(&nf_nat_proto_mutex); RCU_INIT_POINTER(nf_nat_l3protos[l3proto->l3proto], NULL); mutex_unlock(&nf_nat_proto_mutex); synchronize_rcu(); nf_nat_l3proto_clean(l3proto->l3proto); nf_ct_l3proto_module_put(l3proto->l3proto); }
static void xt_ct_tg_destroy(const struct xt_tgdtor_param *par) { struct xt_ct_target_info *info = par->targinfo; struct nf_conn *ct = info->ct; struct nf_conn_help *help; if (!nf_ct_is_untracked(ct)) { help = nfct_help(ct); if (help) module_put(help->helper->me); nf_ct_l3proto_module_put(par->family); } nf_ct_put(info->ct); }
static void nf_ct_netns_do_put(struct net *net, u8 nfproto) { const struct nf_conntrack_l3proto *l3proto; might_sleep(); /* same as nf_conntrack_netns_get(), reference assumed */ rcu_read_lock(); l3proto = __nf_ct_l3proto_find(nfproto); rcu_read_unlock(); if (WARN_ON(!l3proto)) return; if (l3proto->net_ns_put) l3proto->net_ns_put(net); nf_ct_l3proto_module_put(nfproto); }
static int nft_ct_l3proto_try_module_get(uint8_t family) { int err; if (family == NFPROTO_INET) { err = nf_ct_l3proto_try_module_get(NFPROTO_IPV4); if (err < 0) goto err1; err = nf_ct_l3proto_try_module_get(NFPROTO_IPV6); if (err < 0) goto err2; } else { err = nf_ct_l3proto_try_module_get(family); if (err < 0) goto err1; } return 0; err2: nf_ct_l3proto_module_put(NFPROTO_IPV4); err1: return err; }
static int xt_ct_tg_check(const struct xt_tgchk_param *par) { struct xt_ct_target_info *info = par->targinfo; struct nf_conntrack_tuple t; struct nf_conn_help *help; struct nf_conn *ct; int ret = 0; u8 proto; if (info->flags & ~XT_CT_NOTRACK) return -EINVAL; if (info->flags & XT_CT_NOTRACK) { ct = nf_ct_untracked_get(); atomic_inc(&ct->ct_general.use); goto out; } #ifndef CONFIG_NF_CONNTRACK_ZONES if (info->zone) goto err1; #endif ret = nf_ct_l3proto_try_module_get(par->family); if (ret < 0) goto err1; memset(&t, 0, sizeof(t)); ct = nf_conntrack_alloc(par->net, info->zone, &t, &t, GFP_KERNEL); ret = PTR_ERR(ct); if (IS_ERR(ct)) goto err2; ret = 0; if ((info->ct_events || info->exp_events) && !nf_ct_ecache_ext_add(ct, info->ct_events, info->exp_events, GFP_KERNEL)) goto err3; if (info->helper[0]) { ret = -ENOENT; proto = xt_ct_find_proto(par); if (!proto) goto err3; ret = -ENOMEM; help = nf_ct_helper_ext_add(ct, GFP_KERNEL); if (help == NULL) goto err3; ret = -ENOENT; help->helper = nf_conntrack_helper_try_module_get(info->helper, par->family, proto); if (help->helper == NULL) goto err3; } __set_bit(IPS_TEMPLATE_BIT, &ct->status); __set_bit(IPS_CONFIRMED_BIT, &ct->status); out: info->ct = ct; return 0; err3: nf_conntrack_free(ct); err2: nf_ct_l3proto_module_put(par->family); err1: return ret; }
static void nft_ct_destroy(const struct nft_expr *expr) { struct nft_ct *priv = nft_expr_priv(expr); nf_ct_l3proto_module_put(priv->family); }
static void connmark_tg_destroy(const struct xt_tgdtor_param *par) { nf_ct_l3proto_module_put(par->family); }
static void destroy(const struct xt_match *match, void *matchinfo) { #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) nf_ct_l3proto_module_put(match->family); #endif }
static int xt_ct_tg_check_v1(const struct xt_tgchk_param *par) { struct xt_ct_target_info_v1 *info = par->targinfo; struct nf_conntrack_tuple t; struct nf_conn_help *help; struct nf_conn *ct; int ret = 0; u8 proto; #ifdef CONFIG_NF_CONNTRACK_TIMEOUT struct ctnl_timeout *timeout; #endif if (info->flags & ~XT_CT_NOTRACK) return -EINVAL; if (info->flags & XT_CT_NOTRACK) { ct = nf_ct_untracked_get(); atomic_inc(&ct->ct_general.use); goto out; } #ifndef CONFIG_NF_CONNTRACK_ZONES if (info->zone) goto err1; #endif ret = nf_ct_l3proto_try_module_get(par->family); if (ret < 0) goto err1; memset(&t, 0, sizeof(t)); ct = nf_conntrack_alloc(par->net, info->zone, &t, &t, GFP_KERNEL); ret = PTR_ERR(ct); if (IS_ERR(ct)) goto err2; ret = 0; if ((info->ct_events || info->exp_events) && !nf_ct_ecache_ext_add(ct, info->ct_events, info->exp_events, GFP_KERNEL)) goto err3; if (info->helper[0]) { ret = -ENOENT; proto = xt_ct_find_proto(par); if (!proto) { pr_info("You must specify a L4 protocol, " "and not use inversions on it.\n"); goto err3; } ret = -ENOMEM; help = nf_ct_helper_ext_add(ct, GFP_KERNEL); if (help == NULL) goto err3; ret = -ENOENT; help->helper = nf_conntrack_helper_try_module_get(info->helper, par->family, proto); if (help->helper == NULL) { pr_info("No such helper \"%s\"\n", info->helper); goto err3; } } #ifdef CONFIG_NF_CONNTRACK_TIMEOUT if (info->timeout[0]) { typeof(nf_ct_timeout_find_get_hook) timeout_find_get; struct nf_conn_timeout *timeout_ext; rcu_read_lock(); timeout_find_get = rcu_dereference(nf_ct_timeout_find_get_hook); if (timeout_find_get) { const struct ipt_entry *e = par->entryinfo; struct nf_conntrack_l4proto *l4proto; if (e->ip.invflags & IPT_INV_PROTO) { ret = -EINVAL; pr_info("You cannot use inversion on " "L4 protocol\n"); goto err4; } timeout = timeout_find_get(info->timeout); if (timeout == NULL) { ret = -ENOENT; pr_info("No such timeout policy \"%s\"\n", info->timeout); goto err4; } if (timeout->l3num != par->family) { ret = -EINVAL; pr_info("Timeout policy `%s' can only be " "used by L3 protocol number %d\n", info->timeout, timeout->l3num); goto err5; } /* Make sure the timeout policy matches any existing * protocol tracker, otherwise default to generic. */ l4proto = __nf_ct_l4proto_find(par->family, e->ip.proto); if (timeout->l4proto->l4proto != l4proto->l4proto) { ret = -EINVAL; pr_info("Timeout policy `%s' can only be " "used by L4 protocol number %d\n", info->timeout, timeout->l4proto->l4proto); goto err5; } timeout_ext = nf_ct_timeout_ext_add(ct, timeout, GFP_ATOMIC); if (timeout_ext == NULL) { ret = -ENOMEM; goto err5; } } else { ret = -ENOENT; pr_info("Timeout policy base is empty\n"); goto err4; } rcu_read_unlock(); } #endif __set_bit(IPS_TEMPLATE_BIT, &ct->status); __set_bit(IPS_CONFIRMED_BIT, &ct->status); out: info->ct = ct; return 0; #ifdef CONFIG_NF_CONNTRACK_TIMEOUT err5: __xt_ct_tg_timeout_put(timeout); err4: rcu_read_unlock(); #endif err3: nf_conntrack_free(ct); err2: nf_ct_l3proto_module_put(par->family); err1: return ret; }
static void synproxy_tg6_destroy(const struct xt_tgdtor_param *par) { nf_ct_l3proto_module_put(par->family); }
static int xt_ct_tg_check_v0(const struct xt_tgchk_param *par) { struct xt_ct_target_info *info = par->targinfo; struct xt_ct_target_info_v1 info_v1 = { .flags = info->flags, .zone = info->zone, .ct_events = info->ct_events, .exp_events = info->exp_events, }; int ret; if (info->flags & ~XT_CT_NOTRACK) return -EINVAL; memcpy(info_v1.helper, info->helper, sizeof(info->helper)); ret = xt_ct_tg_check(par, &info_v1); if (ret < 0) return ret; info->ct = info_v1.ct; return ret; } static int xt_ct_tg_check_v1(const struct xt_tgchk_param *par) { struct xt_ct_target_info_v1 *info = par->targinfo; if (info->flags & ~XT_CT_NOTRACK) return -EINVAL; return xt_ct_tg_check(par, par->targinfo); } static int xt_ct_tg_check_v2(const struct xt_tgchk_param *par) { struct xt_ct_target_info_v1 *info = par->targinfo; if (info->flags & ~XT_CT_MASK) return -EINVAL; return xt_ct_tg_check(par, par->targinfo); } static void xt_ct_destroy_timeout(struct nf_conn *ct) { #ifdef CONFIG_NF_CONNTRACK_TIMEOUT struct nf_conn_timeout *timeout_ext; typeof(nf_ct_timeout_put_hook) timeout_put; rcu_read_lock(); timeout_put = rcu_dereference(nf_ct_timeout_put_hook); if (timeout_put) { timeout_ext = nf_ct_timeout_find(ct); if (timeout_ext) timeout_put(timeout_ext->timeout); } rcu_read_unlock(); #endif } static void xt_ct_tg_destroy(const struct xt_tgdtor_param *par, struct xt_ct_target_info_v1 *info) { struct nf_conn *ct = info->ct; struct nf_conn_help *help; if (ct && !nf_ct_is_untracked(ct)) { help = nfct_help(ct); if (help) module_put(help->helper->me); nf_ct_l3proto_module_put(par->family); xt_ct_destroy_timeout(ct); nf_ct_put(info->ct); } } static void xt_ct_tg_destroy_v0(const struct xt_tgdtor_param *par) { struct xt_ct_target_info *info = par->targinfo; struct xt_ct_target_info_v1 info_v1 = { .flags = info->flags, .zone = info->zone, .ct_events = info->ct_events, .exp_events = info->exp_events, .ct = info->ct, }; memcpy(info_v1.helper, info->helper, sizeof(info->helper)); xt_ct_tg_destroy(par, &info_v1); } static void xt_ct_tg_destroy_v1(const struct xt_tgdtor_param *par) { xt_ct_tg_destroy(par, par->targinfo); } static struct xt_target xt_ct_tg_reg[] __read_mostly = { { .name = "CT", .family = NFPROTO_UNSPEC, .targetsize = sizeof(struct xt_ct_target_info), .checkentry = xt_ct_tg_check_v0, .destroy = xt_ct_tg_destroy_v0, .target = xt_ct_target_v0, .table = "raw", .me = THIS_MODULE, }, { .name = "CT", .family = NFPROTO_UNSPEC, .revision = 1, .targetsize = sizeof(struct xt_ct_target_info_v1), .checkentry = xt_ct_tg_check_v1, .destroy = xt_ct_tg_destroy_v1, .target = xt_ct_target_v1, .table = "raw", .me = THIS_MODULE, }, { .name = "CT",
static void conntrack_mt_destroy(const struct xt_match *match, void *matchinfo) { nf_ct_l3proto_module_put(match->family); }
static void destroy(const struct xt_target *target, void *targinfo) { nf_ct_l3proto_module_put(target->family); }
static void state_mt_destroy(const struct xt_mtdtor_param *par) { nf_ct_l3proto_module_put(par->family); }
static int nft_ct_init(const struct nft_ctx *ctx, const struct nft_expr *expr, const struct nlattr * const tb[]) { struct nft_ct *priv = nft_expr_priv(expr); int err; if (tb[NFTA_CT_DREG] == NULL || tb[NFTA_CT_KEY] == NULL) return -EINVAL; priv->key = ntohl(nla_get_be32(tb[NFTA_CT_KEY])); if (tb[NFTA_CT_DIRECTION] != NULL) { priv->dir = nla_get_u8(tb[NFTA_CT_DIRECTION]); switch (priv->dir) { case IP_CT_DIR_ORIGINAL: case IP_CT_DIR_REPLY: break; default: return -EINVAL; } } switch (priv->key) { case NFT_CT_STATE: case NFT_CT_DIRECTION: case NFT_CT_STATUS: #ifdef CONFIG_NF_CONNTRACK_MARK case NFT_CT_MARK: #endif #ifdef CONFIG_NF_CONNTRACK_SECMARK case NFT_CT_SECMARK: #endif case NFT_CT_EXPIRATION: case NFT_CT_HELPER: if (tb[NFTA_CT_DIRECTION] != NULL) return -EINVAL; break; case NFT_CT_PROTOCOL: case NFT_CT_SRC: case NFT_CT_DST: case NFT_CT_PROTO_SRC: case NFT_CT_PROTO_DST: if (tb[NFTA_CT_DIRECTION] == NULL) return -EINVAL; break; default: return -EOPNOTSUPP; } err = nf_ct_l3proto_try_module_get(ctx->afi->family); if (err < 0) return err; priv->family = ctx->afi->family; priv->dreg = ntohl(nla_get_be32(tb[NFTA_CT_DREG])); err = nft_validate_output_register(priv->dreg); if (err < 0) goto err1; err = nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE); if (err < 0) goto err1; return 0; err1: nf_ct_l3proto_module_put(ctx->afi->family); return err; }