Commit 28e144cf authored by Xin Long's avatar Xin Long Committed by Florian Westphal
Browse files

netfilter: move br_nf_check_hbh_len to utils



Rename br_nf_check_hbh_len() to nf_ip6_check_hbh_len() and move it
to netfilter utils, so that it can be used by other modules, like
ovs and tc.

Signed-off-by: default avatarXin Long <lucien.xin@gmail.com>
Reviewed-by: default avatarSimon Horman <simon.horman@corigine.com>
Reviewed-by: default avatarNikolay Aleksandrov <razor@blackwall.org>
Reviewed-by: default avatarAaron Conole <aconole@redhat.com>
Signed-off-by: default avatarFlorian Westphal <fw@strlen.de>
parent 0b24bd71
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -197,6 +197,8 @@ static inline int nf_cookie_v6_check(const struct ipv6hdr *iph,
__sum16 nf_ip6_checksum(struct sk_buff *skb, unsigned int hook,
			unsigned int dataoff, u_int8_t protocol);

int nf_ip6_check_hbh_len(struct sk_buff *skb, u32 *plen);

int ipv6_netfilter_init(void);
void ipv6_netfilter_fini(void);

+1 −54
Original line number Diff line number Diff line
@@ -40,59 +40,6 @@
#include <linux/sysctl.h>
#endif

/* We only check the length. A bridge shouldn't do any hop-by-hop stuff
 * anyway
 */
static int br_nf_check_hbh_len(struct sk_buff *skb, u32 *plen)
{
	int len, off = sizeof(struct ipv6hdr);
	unsigned char *nh;

	if (!pskb_may_pull(skb, off + 8))
		return -1;
	nh = (unsigned char *)(ipv6_hdr(skb) + 1);
	len = (nh[1] + 1) << 3;

	if (!pskb_may_pull(skb, off + len))
		return -1;
	nh = skb_network_header(skb);

	off += 2;
	len -= 2;
	while (len > 0) {
		int optlen;

		if (nh[off] == IPV6_TLV_PAD1) {
			off++;
			len--;
			continue;
		}
		if (len < 2)
			return -1;
		optlen = nh[off + 1] + 2;
		if (optlen > len)
			return -1;

		if (nh[off] == IPV6_TLV_JUMBO) {
			u32 pkt_len;

			if (nh[off + 1] != 4 || (off & 3) != 2)
				return -1;
			pkt_len = ntohl(*(__be32 *)(nh + off + 2));
			if (pkt_len <= IPV6_MAXPLEN ||
			    ipv6_hdr(skb)->payload_len)
				return -1;
			if (pkt_len > skb->len - sizeof(struct ipv6hdr))
				return -1;
			*plen = pkt_len;
		}
		off += optlen;
		len -= optlen;
	}

	return len ? -1 : 0;
}

int br_validate_ipv6(struct net *net, struct sk_buff *skb)
{
	const struct ipv6hdr *hdr;
@@ -112,7 +59,7 @@ int br_validate_ipv6(struct net *net, struct sk_buff *skb)
		goto inhdr_error;

	pkt_len = ntohs(hdr->payload_len);
	if (hdr->nexthdr == NEXTHDR_HOP && br_nf_check_hbh_len(skb, &pkt_len))
	if (hdr->nexthdr == NEXTHDR_HOP && nf_ip6_check_hbh_len(skb, &pkt_len))
		goto drop;

	if (pkt_len + ip6h_len > skb->len) {
+52 −0
Original line number Diff line number Diff line
@@ -215,3 +215,55 @@ int nf_reroute(struct sk_buff *skb, struct nf_queue_entry *entry)
	}
	return ret;
}

/* Only get and check the lengths, not do any hop-by-hop stuff. */
int nf_ip6_check_hbh_len(struct sk_buff *skb, u32 *plen)
{
	int len, off = sizeof(struct ipv6hdr);
	unsigned char *nh;

	if (!pskb_may_pull(skb, off + 8))
		return -ENOMEM;
	nh = (unsigned char *)(ipv6_hdr(skb) + 1);
	len = (nh[1] + 1) << 3;

	if (!pskb_may_pull(skb, off + len))
		return -ENOMEM;
	nh = skb_network_header(skb);

	off += 2;
	len -= 2;
	while (len > 0) {
		int optlen;

		if (nh[off] == IPV6_TLV_PAD1) {
			off++;
			len--;
			continue;
		}
		if (len < 2)
			return -EBADMSG;
		optlen = nh[off + 1] + 2;
		if (optlen > len)
			return -EBADMSG;

		if (nh[off] == IPV6_TLV_JUMBO) {
			u32 pkt_len;

			if (nh[off + 1] != 4 || (off & 3) != 2)
				return -EBADMSG;
			pkt_len = ntohl(*(__be32 *)(nh + off + 2));
			if (pkt_len <= IPV6_MAXPLEN ||
			    ipv6_hdr(skb)->payload_len)
				return -EBADMSG;
			if (pkt_len > skb->len - sizeof(struct ipv6hdr))
				return -EBADMSG;
			*plen = pkt_len;
		}
		off += optlen;
		len -= optlen;
	}

	return len ? -EBADMSG : 0;
}
EXPORT_SYMBOL_GPL(nf_ip6_check_hbh_len);