Example #1
0
/* this is guaranteed to always return a valid protocol helper, since
 * it falls back to generic_protocol */
struct nf_conntrack_l4proto *
nf_ct_l4proto_find_get(u_int16_t l3proto, u_int8_t l4proto)
{
    struct nf_conntrack_l4proto *p;

    rcu_read_lock();
    p = __nf_ct_l4proto_find(l3proto, l4proto);
    if (!try_module_get(p->me))
        p = &nf_conntrack_l4proto_generic;
    rcu_read_unlock();

    return p;
}
Example #2
0
/* this is guaranteed to always return a valid protocol helper, since
 * it falls back to generic_protocol */
struct nf_conntrack_l4proto *
nf_ct_l4proto_find_get(u_int16_t l3proto, u_int8_t l4proto)
{
	struct nf_conntrack_l4proto *p;

	preempt_disable();
	p = __nf_ct_l4proto_find(l3proto, l4proto);
	if (!try_module_get(p->me))
		p = &nf_conntrack_l4proto_generic;
	preempt_enable();

	return p;
}
Example #3
0
static int
icmpv6_error_message(struct net *net, struct nf_conn *tmpl,
                     struct sk_buff *skb,
                     unsigned int icmp6off,
                     enum ip_conntrack_info *ctinfo,
                     unsigned int hooknum)
{
    struct nf_conntrack_tuple intuple, origtuple;
    const struct nf_conntrack_tuple_hash *h;
    const struct nf_conntrack_l4proto *inproto;
    u16 zone = tmpl ? nf_ct_zone(tmpl) : NF_CT_DEFAULT_ZONE;

    NF_CT_ASSERT(skb->nfct == NULL);

    /* Are they talking about one of our connections? */
    if (!nf_ct_get_tuplepr(skb,
                           skb_network_offset(skb)
                           + sizeof(struct ipv6hdr)
                           + sizeof(struct icmp6hdr),
                           PF_INET6, &origtuple)) {
        pr_debug("icmpv6_error: Can't get tuple\n");
        return -NF_ACCEPT;
    }

    /* rcu_read_lock()ed by nf_hook_slow */
    inproto = __nf_ct_l4proto_find(PF_INET6, origtuple.dst.protonum);

    /* Ordinarily, we'd expect the inverted tupleproto, but it's
       been preserved inside the ICMP. */
    if (!nf_ct_invert_tuple(&intuple, &origtuple,
                            &nf_conntrack_l3proto_ipv6, inproto)) {
        pr_debug("icmpv6_error: Can't invert tuple\n");
        return -NF_ACCEPT;
    }

    *ctinfo = IP_CT_RELATED;

    h = nf_conntrack_find_get(net, zone, &intuple);
    if (!h) {
        pr_debug("icmpv6_error: no match\n");
        return -NF_ACCEPT;
    } else {
        if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY)
            *ctinfo += IP_CT_IS_REPLY;
    }

    /* Update skb to refer to this connection */
    skb->nfct = &nf_ct_tuplehash_to_ctrack(h)->ct_general;
    skb->nfctinfo = *ctinfo;
    return -NF_ACCEPT;
}
static int
icmpv6_error_message(struct net *net, struct nf_conn *tmpl,
		     struct sk_buff *skb,
		     unsigned int icmp6off)
{
	struct nf_conntrack_tuple intuple, origtuple;
	const struct nf_conntrack_tuple_hash *h;
	const struct nf_conntrack_l4proto *inproto;
	enum ip_conntrack_info ctinfo;
	struct nf_conntrack_zone tmp;

	WARN_ON(skb_nfct(skb));

	/* Are they talking about one of our connections? */
	if (!nf_ct_get_tuplepr(skb,
			       skb_network_offset(skb)
				+ sizeof(struct ipv6hdr)
				+ sizeof(struct icmp6hdr),
			       PF_INET6, net, &origtuple)) {
		pr_debug("icmpv6_error: Can't get tuple\n");
		return -NF_ACCEPT;
	}

	/* rcu_read_lock()ed by nf_hook_thresh */
	inproto = __nf_ct_l4proto_find(PF_INET6, origtuple.dst.protonum);

	/* Ordinarily, we'd expect the inverted tupleproto, but it's
	   been preserved inside the ICMP. */
	if (!nf_ct_invert_tuple(&intuple, &origtuple,
				&nf_conntrack_l3proto_ipv6, inproto)) {
		pr_debug("icmpv6_error: Can't invert tuple\n");
		return -NF_ACCEPT;
	}

	ctinfo = IP_CT_RELATED;

	h = nf_conntrack_find_get(net, nf_ct_zone_tmpl(tmpl, skb, &tmp),
				  &intuple);
	if (!h) {
		pr_debug("icmpv6_error: no match\n");
		return -NF_ACCEPT;
	} else {
		if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY)
			ctinfo += IP_CT_IS_REPLY;
	}

	/* Update skb to refer to this connection */
	nf_ct_set(skb, nf_ct_tuplehash_to_ctrack(h), ctinfo);
	return NF_ACCEPT;
}
static int
icmpv6_error_message(struct net *net, struct nf_conn *tmpl,
		     struct sk_buff *skb,
		     unsigned int icmp6off,
		     enum ip_conntrack_info *ctinfo,
		     unsigned int hooknum)
{
	struct nf_conntrack_tuple intuple, origtuple;
	const struct nf_conntrack_tuple_hash *h;
	const struct nf_conntrack_l4proto *inproto;
	u16 zone = tmpl ? nf_ct_zone(tmpl) : NF_CT_DEFAULT_ZONE;

	NF_CT_ASSERT(skb->nfct == NULL);

	
	if (!nf_ct_get_tuplepr(skb,
			       skb_network_offset(skb)
				+ sizeof(struct ipv6hdr)
				+ sizeof(struct icmp6hdr),
			       PF_INET6, &origtuple)) {
		pr_debug("icmpv6_error: Can't get tuple\n");
		return -NF_ACCEPT;
	}

	
	inproto = __nf_ct_l4proto_find(PF_INET6, origtuple.dst.protonum);

	if (!nf_ct_invert_tuple(&intuple, &origtuple,
				&nf_conntrack_l3proto_ipv6, inproto)) {
		pr_debug("icmpv6_error: Can't invert tuple\n");
		return -NF_ACCEPT;
	}

	*ctinfo = IP_CT_RELATED;

	h = nf_conntrack_find_get(net, zone, &intuple);
	if (!h) {
		pr_debug("icmpv6_error: no match\n");
		return -NF_ACCEPT;
	} else {
		if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY)
			*ctinfo += IP_CT_IS_REPLY;
	}

	
	skb->nfct = &nf_ct_tuplehash_to_ctrack(h)->ct_general;
	skb->nfctinfo = *ctinfo;
	return NF_ACCEPT;
}
/* Returns conntrack if it dealt with ICMP, and filled in skb fields */
static int
icmp_error_message(struct sk_buff *skb,
		 enum ip_conntrack_info *ctinfo,
		 unsigned int hooknum)
{
	struct nf_conntrack_tuple innertuple, origtuple;
	struct nf_conntrack_l4proto *innerproto;
	struct nf_conntrack_tuple_hash *h;

	NF_CT_ASSERT(skb->nfct == NULL);

	/* Are they talking about one of our connections? */
	if (!nf_ct_get_tuplepr(skb,
			       skb_network_offset(skb) + ip_hdrlen(skb)
						       + sizeof(struct icmphdr),
			       PF_INET, &origtuple)) {
		pr_debug("icmp_error_message: failed to get tuple\n");
		return -NF_ACCEPT;
	}

	/* rcu_read_lock()ed by nf_hook_slow */
	innerproto = __nf_ct_l4proto_find(PF_INET, origtuple.dst.protonum);

	/* Ordinarily, we'd expect the inverted tupleproto, but it's
	   been preserved inside the ICMP. */
	if (!nf_ct_invert_tuple(&innertuple, &origtuple,
				&nf_conntrack_l3proto_ipv4, innerproto)) {
		pr_debug("icmp_error_message: no match\n");
		return -NF_ACCEPT;
	}

	*ctinfo = IP_CT_RELATED;

	h = nf_conntrack_find_get(&innertuple);
	if (!h) {
		pr_debug("icmp_error_message: no match\n");
		return -NF_ACCEPT;
	}

	if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY)
		*ctinfo += IP_CT_IS_REPLY;

	/* Update skb to refer to this connection */
	skb->nfct = &nf_ct_tuplehash_to_ctrack(h)->ct_general;
	skb->nfctinfo = *ctinfo;
	return -NF_ACCEPT;
}
/*
 * Function to get the tuple out of a given struct sk_buff.
 */
bool nat64_get_tuple(u_int8_t l3protocol, u_int8_t l4protocol,
		struct sk_buff *skb, struct nf_conntrack_tuple * inner)
{
	const struct nf_conntrack_l4proto *l4proto;
	struct nf_conntrack_l3proto *l3proto;
	int l3_hdrlen, ret;
	unsigned int protoff = 0;
	u_int8_t protonum = 0;

	pr_debug("NAT64: Getting the protocol and header length");

	/*
	 * Get L3 header length
	 */
	l3_hdrlen = nat64_get_l3hdrlen(skb, l3protocol);

	if (l3_hdrlen == -1) {
		pr_debug("NAT64: Something went wrong getting the"
				" l3 header length");
		return false;
	}

	/*
	 * Get L3 struct to access it's functions.
	 */
	if (!(nat64_get_l3struct(l3protocol, &l3proto)))
		return false;

	if (l3proto == NULL) {
		pr_info("NAT64: nat64_get_tuple - the l3proto pointer is null");
		return false;
	}

	rcu_read_lock();

	pr_debug("NAT64: l3_hdrlen = %d", l3_hdrlen);

	/*
	 * Gets the structure with the respective L4 protocol functions.
	 */
	ret = l3proto->get_l4proto(skb, skb_network_offset(skb),
			&protoff, &protonum);

	if (ret != NF_ACCEPT) {
		pr_info("NAT64: nat64_get_tuple - error getting the L4 offset");
		pr_debug("NAT64: ret = %d", ret);
		pr_debug("NAT64: protoff = %u", protoff);
		rcu_read_unlock();
		return false;
	} else if (protonum != l4protocol) {
		pr_info("NAT64: nat64_get_tuple - protocols don't match");
		pr_debug("NAT64: protonum = %u", protonum);
		pr_debug("NAT64: l4protocol = %u", l4protocol);
		rcu_read_unlock();
		return false;
	}

	l4proto = __nf_ct_l4proto_find(l3protocol, l4protocol);
	pr_debug("l4proto name = %s %d %d", l4proto->name,
			(u_int32_t)l4proto->l3proto, (u_int32_t)l4proto->l4proto);

	/*
	 * Get the tuple out of the sk_buff.
	 */
	if (!nf_ct_get_tuple(skb, skb_network_offset(skb),
				l3_hdrlen,
				(u_int16_t)l3protocol, l4protocol,
				inner, l3proto, l4proto)) {
		pr_debug("NAT64: couldn't get the tuple");
		rcu_read_unlock();
		return false;
	}

	pr_debug("\nPRINTED TUPLE");
	nat64_print_tuple(inner);
	pr_debug("\n");
	rcu_read_unlock();

	return true;
}
/* Returns conntrack if it dealt with ICMP, and filled in skb fields */
static int
icmp_error_message(struct net *net, struct sk_buff *skb,
		 enum ip_conntrack_info *ctinfo,
		 unsigned int hooknum)
{
	struct nf_conntrack_tuple innertuple, origtuple;
	const struct nf_conntrack_l4proto *innerproto;
	const struct nf_conntrack_tuple_hash *h;
#ifdef CFG_LINUX_NET_PACKED
	struct {
		struct icmphdr icmp;
		struct iphdr ip;
	} _in, *inside;
#endif


	NF_CT_ASSERT(skb->nfct == NULL);

#ifdef CFG_LINUX_NET_PACKED
	/* Not enough header? */
	inside = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_in), &_in);
	if (inside == NULL)
		return -NF_ACCEPT;
#endif

	/* Are they talking about one of our connections? */
	if (!nf_ct_get_tuplepr(skb,
			       skb_network_offset(skb) + ip_hdrlen(skb)
						       + sizeof(struct icmphdr),
			       PF_INET, &origtuple)) {
		pr_debug("icmp_error_message: failed to get tuple\n");
		return -NF_ACCEPT;
	}

	/* rcu_read_lock()ed by nf_hook_slow */
#ifdef CFG_LINUX_NET_PACKED
	innerproto = __nf_ct_l4proto_find(PF_INET, inside->ip.protocol);
	origtuple.src.u3.ip = inside->ip.saddr;
	origtuple.dst.u3.ip = inside->ip.daddr;
#else
	innerproto = __nf_ct_l4proto_find(PF_INET, origtuple.dst.protonum);
#endif

	/* Ordinarily, we'd expect the inverted tupleproto, but it's
	   been preserved inside the ICMP. */
	if (!nf_ct_invert_tuple(&innertuple, &origtuple,
				&nf_conntrack_l3proto_ipv4, innerproto)) {
		pr_debug("icmp_error_message: no match\n");
		return -NF_ACCEPT;
	}

	*ctinfo = IP_CT_RELATED;

	h = nf_conntrack_find_get(net, &innertuple);
	if (!h) {
		pr_debug("icmp_error_message: no match\n");
		return -NF_ACCEPT;
	}

	if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY)
		*ctinfo += IP_CT_IS_REPLY;

	/* Update skb to refer to this connection */
	skb->nfct = &nf_ct_tuplehash_to_ctrack(h)->ct_general;
	skb->nfctinfo = *ctinfo;
	return -NF_ACCEPT;
}
/* Returns conntrack if it dealt with ICMP, and filled in skb fields */
static int
icmp_error_message(struct sk_buff *skb,
                 enum ip_conntrack_info *ctinfo,
                 unsigned int hooknum)
{
	struct nf_conntrack_tuple innertuple, origtuple;
	struct {
		struct icmphdr icmp;
		struct iphdr ip;
	} _in, *inside;
	struct nf_conntrack_l4proto *innerproto;
	struct nf_conntrack_tuple_hash *h;
	int dataoff;

	NF_CT_ASSERT(skb->nfct == NULL);

	/* Not enough header? */
	inside = skb_header_pointer(skb, skb->nh.iph->ihl*4, sizeof(_in), &_in);
	if (inside == NULL)
		return -NF_ACCEPT;

	/* Ignore ICMP's containing fragments (shouldn't happen) */
	if (inside->ip.frag_off & htons(IP_OFFSET)) {
		DEBUGP("icmp_error_message: fragment of proto %u\n",
		       inside->ip.protocol);
		return -NF_ACCEPT;
	}

	innerproto = __nf_ct_l4proto_find(PF_INET, inside->ip.protocol);
	dataoff = skb->nh.iph->ihl*4 + sizeof(inside->icmp);
	/* Are they talking about one of our connections? */
	if (!nf_ct_get_tuple(skb, dataoff, dataoff + inside->ip.ihl*4, PF_INET,
			     inside->ip.protocol, &origtuple,
			     &nf_conntrack_l3proto_ipv4, innerproto)) {
		DEBUGP("icmp_error_message: ! get_tuple p=%u",
		       inside->ip.protocol);
		return -NF_ACCEPT;
	}

        /* Ordinarily, we'd expect the inverted tupleproto, but it's
           been preserved inside the ICMP. */
        if (!nf_ct_invert_tuple(&innertuple, &origtuple,
				&nf_conntrack_l3proto_ipv4, innerproto)) {
		DEBUGP("icmp_error_message: no match\n");
		return -NF_ACCEPT;
	}

	*ctinfo = IP_CT_RELATED;

	h = nf_conntrack_find_get(&innertuple, NULL);
	if (!h) {
		/* Locally generated ICMPs will match inverted if they
		   haven't been SNAT'ed yet */
		/* FIXME: NAT code has to handle half-done double NAT --RR */
		if (hooknum == NF_IP_LOCAL_OUT)
			h = nf_conntrack_find_get(&origtuple, NULL);

		if (!h) {
			DEBUGP("icmp_error_message: no match\n");
			return -NF_ACCEPT;
		}

		/* Reverse direction from that found */
		if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY)
			*ctinfo += IP_CT_IS_REPLY;
	} else {
		if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY)
			*ctinfo += IP_CT_IS_REPLY;
	}

        /* Update skb to refer to this connection */
        skb->nfct = &nf_ct_tuplehash_to_ctrack(h)->ct_general;
        skb->nfctinfo = *ctinfo;
        return -NF_ACCEPT;
}
Example #10
0
static int
xt_ct_set_timeout(struct nf_conn *ct, const struct xt_tgchk_param *par,
		  const char *timeout_name)
{
#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
	typeof(nf_ct_timeout_find_get_hook) timeout_find_get;
	struct ctnl_timeout *timeout;
	struct nf_conn_timeout *timeout_ext;
	struct nf_conntrack_l4proto *l4proto;
	int ret = 0;
	u8 proto;

	rcu_read_lock();
	timeout_find_get = rcu_dereference(nf_ct_timeout_find_get_hook);
	if (timeout_find_get == NULL) {
		ret = -ENOENT;
		pr_info("Timeout policy base is empty\n");
		goto out;
	}

	proto = xt_ct_find_proto(par);
	if (!proto) {
		ret = -EINVAL;
		pr_info("You must specify a L4 protocol, and not use "
			"inversions on it.\n");
		goto out;
	}

	timeout = timeout_find_get(timeout_name);
	if (timeout == NULL) {
		ret = -ENOENT;
		pr_info("No such timeout policy \"%s\"\n", timeout_name);
		goto out;
	}

	if (timeout->l3num != par->family) {
		ret = -EINVAL;
		pr_info("Timeout policy `%s' can only be used by L3 protocol "
			"number %d\n", timeout_name, timeout->l3num);
		goto err_put_timeout;
	}
	/* Make sure the timeout policy matches any existing protocol tracker,
	 * otherwise default to generic.
	 */
	l4proto = __nf_ct_l4proto_find(par->family, proto);
	if (timeout->l4proto->l4proto != l4proto->l4proto) {
		ret = -EINVAL;
		pr_info("Timeout policy `%s' can only be used by L4 protocol "
			"number %d\n",
			timeout_name, timeout->l4proto->l4proto);
		goto err_put_timeout;
	}
	timeout_ext = nf_ct_timeout_ext_add(ct, timeout, GFP_ATOMIC);
	if (timeout_ext == NULL)
		ret = -ENOMEM;

err_put_timeout:
	__xt_ct_tg_timeout_put(timeout);
out:
	rcu_read_unlock();
	return ret;
#else
	return -EOPNOTSUPP;
#endif
}
Example #11
0
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;
}