Commit a308283f authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files


Pablo Neira Ayuso says:

====================
Netfilter/IPVS updates for net-next

The following patchset contains Netfilter/IPVS updates for net-next:

1) Inspect the reply packets coming from DR/TUN and refresh connection
   state and timeout, from longguang yue and Julian Anastasov.

2) Series to add support for the inet ingress chain type in nf_tables.
====================

Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 547848af 793d5d61
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -1081,6 +1081,12 @@ struct nft_table {
	u8				*udata;
};

static inline bool nft_base_chain_netdev(int family, u32 hooknum)
{
	return family == NFPROTO_NETDEV ||
	       (family == NFPROTO_INET && hooknum == NF_INET_INGRESS);
}

void nft_register_chain_type(const struct nft_chain_type *);
void nft_unregister_chain_type(const struct nft_chain_type *);

+33 −0
Original line number Diff line number Diff line
@@ -53,4 +53,37 @@ static inline void nft_set_pktinfo_ipv4_validate(struct nft_pktinfo *pkt,
		nft_set_pktinfo_unspec(pkt, skb);
}

static inline int nft_set_pktinfo_ipv4_ingress(struct nft_pktinfo *pkt,
					       struct sk_buff *skb)
{
	struct iphdr *iph;
	u32 len, thoff;

	if (!pskb_may_pull(skb, sizeof(*iph)))
		return -1;

	iph = ip_hdr(skb);
	if (iph->ihl < 5 || iph->version != 4)
		goto inhdr_error;

	len = ntohs(iph->tot_len);
	thoff = iph->ihl * 4;
	if (skb->len < len) {
		__IP_INC_STATS(nft_net(pkt), IPSTATS_MIB_INTRUNCATEDPKTS);
		return -1;
	} else if (len < thoff) {
		goto inhdr_error;
	}

	pkt->tprot_set = true;
	pkt->tprot = iph->protocol;
	pkt->xt.thoff = thoff;
	pkt->xt.fragoff = ntohs(iph->frag_off) & IP_OFFSET;

	return 0;

inhdr_error:
	__IP_INC_STATS(nft_net(pkt), IPSTATS_MIB_INHDRERRORS);
	return -1;
}
#endif
+46 −0
Original line number Diff line number Diff line
@@ -70,4 +70,50 @@ static inline void nft_set_pktinfo_ipv6_validate(struct nft_pktinfo *pkt,
		nft_set_pktinfo_unspec(pkt, skb);
}

static inline int nft_set_pktinfo_ipv6_ingress(struct nft_pktinfo *pkt,
					       struct sk_buff *skb)
{
#if IS_ENABLED(CONFIG_IPV6)
	unsigned int flags = IP6_FH_F_AUTH;
	unsigned short frag_off;
	unsigned int thoff = 0;
	struct inet6_dev *idev;
	struct ipv6hdr *ip6h;
	int protohdr;
	u32 pkt_len;

	if (!pskb_may_pull(skb, sizeof(*ip6h)))
		return -1;

	ip6h = ipv6_hdr(skb);
	if (ip6h->version != 6)
		goto inhdr_error;

	pkt_len = ntohs(ip6h->payload_len);
	if (pkt_len + sizeof(*ip6h) > skb->len) {
		idev = __in6_dev_get(nft_in(pkt));
		__IP6_INC_STATS(nft_net(pkt), idev, IPSTATS_MIB_INTRUNCATEDPKTS);
		return -1;
	}

	protohdr = ipv6_find_hdr(pkt->skb, &thoff, -1, &frag_off, &flags);
	if (protohdr < 0)
		goto inhdr_error;

	pkt->tprot_set = true;
	pkt->tprot = protohdr;
	pkt->xt.thoff = thoff;
	pkt->xt.fragoff = frag_off;

	return 0;

inhdr_error:
	idev = __in6_dev_get(nft_in(pkt));
	__IP6_INC_STATS(nft_net(pkt), idev, IPSTATS_MIB_INHDRERRORS);
	return -1;
#else
	return -1;
#endif
}

#endif
+1 −0
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ enum nf_inet_hooks {
	NF_INET_FORWARD,
	NF_INET_LOCAL_OUT,
	NF_INET_POST_ROUTING,
	NF_INET_INGRESS,
	NF_INET_NUMHOOKS
};

+103 −26
Original line number Diff line number Diff line
@@ -281,6 +281,16 @@ nf_hook_entry_head(struct net *net, int pf, unsigned int hooknum,
		if (WARN_ON_ONCE(ARRAY_SIZE(net->nf.hooks_bridge) <= hooknum))
			return NULL;
		return net->nf.hooks_bridge + hooknum;
#endif
#ifdef CONFIG_NETFILTER_INGRESS
	case NFPROTO_INET:
		if (WARN_ON_ONCE(hooknum != NF_INET_INGRESS))
			return NULL;
		if (!dev || dev_net(dev) != net) {
			WARN_ON_ONCE(1);
			return NULL;
		}
		return &dev->nf_hooks_ingress;
#endif
	case NFPROTO_IPV4:
		if (WARN_ON_ONCE(ARRAY_SIZE(net->nf.hooks_ipv4) <= hooknum))
@@ -311,20 +321,80 @@ nf_hook_entry_head(struct net *net, int pf, unsigned int hooknum,
	return NULL;
}

static int __nf_register_net_hook(struct net *net, int pf,
				  const struct nf_hook_ops *reg)
static int nf_ingress_check(struct net *net, const struct nf_hook_ops *reg,
			    int hooknum)
{
	struct nf_hook_entries *p, *new_hooks;
	struct nf_hook_entries __rcu **pp;

	if (pf == NFPROTO_NETDEV) {
#ifndef CONFIG_NETFILTER_INGRESS
		if (reg->hooknum == NF_NETDEV_INGRESS)
	if (reg->hooknum == hooknum)
		return -EOPNOTSUPP;
#endif
		if (reg->hooknum != NF_NETDEV_INGRESS ||
	if (reg->hooknum != hooknum ||
	    !reg->dev || dev_net(reg->dev) != net)
		return -EINVAL;

	return 0;
}

static inline bool nf_ingress_hook(const struct nf_hook_ops *reg, int pf)
{
	if ((pf == NFPROTO_NETDEV && reg->hooknum == NF_NETDEV_INGRESS) ||
	    (pf == NFPROTO_INET && reg->hooknum == NF_INET_INGRESS))
		return true;

	return false;
}

static void nf_static_key_inc(const struct nf_hook_ops *reg, int pf)
{
#ifdef CONFIG_JUMP_LABEL
	int hooknum;

	if (pf == NFPROTO_INET && reg->hooknum == NF_INET_INGRESS) {
		pf = NFPROTO_NETDEV;
		hooknum = NF_NETDEV_INGRESS;
	} else {
		hooknum = reg->hooknum;
	}
	static_key_slow_inc(&nf_hooks_needed[pf][hooknum]);
#endif
}

static void nf_static_key_dec(const struct nf_hook_ops *reg, int pf)
{
#ifdef CONFIG_JUMP_LABEL
	int hooknum;

	if (pf == NFPROTO_INET && reg->hooknum == NF_INET_INGRESS) {
		pf = NFPROTO_NETDEV;
		hooknum = NF_NETDEV_INGRESS;
	} else {
		hooknum = reg->hooknum;
	}
	static_key_slow_dec(&nf_hooks_needed[pf][hooknum]);
#endif
}

static int __nf_register_net_hook(struct net *net, int pf,
				  const struct nf_hook_ops *reg)
{
	struct nf_hook_entries *p, *new_hooks;
	struct nf_hook_entries __rcu **pp;
	int err;

	switch (pf) {
	case NFPROTO_NETDEV:
		err = nf_ingress_check(net, reg, NF_NETDEV_INGRESS);
		if (err < 0)
			return err;
		break;
	case NFPROTO_INET:
		if (reg->hooknum != NF_INET_INGRESS)
			break;

		err = nf_ingress_check(net, reg, NF_INET_INGRESS);
		if (err < 0)
			return err;
		break;
	}

	pp = nf_hook_entry_head(net, pf, reg->hooknum, reg->dev);
@@ -345,12 +415,11 @@ static int __nf_register_net_hook(struct net *net, int pf,

	hooks_validate(new_hooks);
#ifdef CONFIG_NETFILTER_INGRESS
	if (pf == NFPROTO_NETDEV && reg->hooknum == NF_NETDEV_INGRESS)
	if (nf_ingress_hook(reg, pf))
		net_inc_ingress_queue();
#endif
#ifdef CONFIG_JUMP_LABEL
	static_key_slow_inc(&nf_hooks_needed[pf][reg->hooknum]);
#endif
	nf_static_key_inc(reg, pf);

	BUG_ON(p == new_hooks);
	nf_hook_entries_free(p);
	return 0;
@@ -403,12 +472,10 @@ static void __nf_unregister_net_hook(struct net *net, int pf,

	if (nf_remove_net_hook(p, reg)) {
#ifdef CONFIG_NETFILTER_INGRESS
		if (pf == NFPROTO_NETDEV && reg->hooknum == NF_NETDEV_INGRESS)
		if (nf_ingress_hook(reg, pf))
			net_dec_ingress_queue();
#endif
#ifdef CONFIG_JUMP_LABEL
		static_key_slow_dec(&nf_hooks_needed[pf][reg->hooknum]);
#endif
		nf_static_key_dec(reg, pf);
	} else {
		WARN_ONCE(1, "hook not found, pf %d num %d", pf, reg->hooknum);
	}
@@ -425,8 +492,12 @@ static void __nf_unregister_net_hook(struct net *net, int pf,
void nf_unregister_net_hook(struct net *net, const struct nf_hook_ops *reg)
{
	if (reg->pf == NFPROTO_INET) {
		if (reg->hooknum == NF_INET_INGRESS) {
			__nf_unregister_net_hook(net, NFPROTO_INET, reg);
		} else {
			__nf_unregister_net_hook(net, NFPROTO_IPV4, reg);
			__nf_unregister_net_hook(net, NFPROTO_IPV6, reg);
		}
	} else {
		__nf_unregister_net_hook(net, reg->pf, reg);
	}
@@ -451,6 +522,11 @@ int nf_register_net_hook(struct net *net, const struct nf_hook_ops *reg)
	int err;

	if (reg->pf == NFPROTO_INET) {
		if (reg->hooknum == NF_INET_INGRESS) {
			err = __nf_register_net_hook(net, NFPROTO_INET, reg);
			if (err < 0)
				return err;
		} else {
			err = __nf_register_net_hook(net, NFPROTO_IPV4, reg);
			if (err < 0)
				return err;
@@ -460,6 +536,7 @@ int nf_register_net_hook(struct net *net, const struct nf_hook_ops *reg)
				__nf_unregister_net_hook(net, NFPROTO_IPV4, reg);
				return err;
			}
		}
	} else {
		err = __nf_register_net_hook(net, reg->pf, reg);
		if (err < 0)
Loading