Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d63a6507 authored by Patrick McHardy's avatar Patrick McHardy
Browse files

[NETFILTER]: Add partial checksum validation helper



Move the UDP-Lite conntrack checksum validation to a generic helper
similar to nf_checksum() and make it fall back to nf_checksum()
in case the full packet is to be checksummed and hardware checksums
are available. This is to be used by DCCP conntrack, which also
needs to verify partial checksums.

Signed-off-by: default avatarPatrick McHardy <kaber@trash.net>
parent 6185f870
Loading
Loading
Loading
Loading
+22 −0
Original line number Diff line number Diff line
@@ -234,6 +234,11 @@ struct nf_afinfo {
	unsigned short	family;
	__sum16		(*checksum)(struct sk_buff *skb, unsigned int hook,
				    unsigned int dataoff, u_int8_t protocol);
	__sum16		(*checksum_partial)(struct sk_buff *skb,
					    unsigned int hook,
					    unsigned int dataoff,
					    unsigned int len,
					    u_int8_t protocol);
	int		(*route)(struct dst_entry **dst, struct flowi *fl);
	void		(*saveroute)(const struct sk_buff *skb,
				     struct nf_queue_entry *entry);
@@ -263,6 +268,23 @@ nf_checksum(struct sk_buff *skb, unsigned int hook, unsigned int dataoff,
	return csum;
}

static inline __sum16
nf_checksum_partial(struct sk_buff *skb, unsigned int hook,
		    unsigned int dataoff, unsigned int len,
		    u_int8_t protocol, unsigned short family)
{
	const struct nf_afinfo *afinfo;
	__sum16 csum = 0;

	rcu_read_lock();
	afinfo = nf_get_afinfo(family);
	if (afinfo)
		csum = afinfo->checksum_partial(skb, hook, dataoff, len,
						protocol);
	rcu_read_unlock();
	return csum;
}

extern int nf_register_afinfo(const struct nf_afinfo *afinfo);
extern void nf_unregister_afinfo(const struct nf_afinfo *afinfo);

+30 −7
Original line number Diff line number Diff line
@@ -182,9 +182,31 @@ __sum16 nf_ip_checksum(struct sk_buff *skb, unsigned int hook,
	}
	return csum;
}

EXPORT_SYMBOL(nf_ip_checksum);

static __sum16 nf_ip_checksum_partial(struct sk_buff *skb, unsigned int hook,
				      unsigned int dataoff, unsigned int len,
				      u_int8_t protocol)
{
	const struct iphdr *iph = ip_hdr(skb);
	__sum16 csum = 0;

	switch (skb->ip_summed) {
	case CHECKSUM_COMPLETE:
		if (len == skb->len - dataoff)
			return nf_ip_checksum(skb, hook, dataoff, protocol);
		/* fall through */
	case CHECKSUM_NONE:
		skb->csum = csum_tcpudp_nofold(iph->saddr, iph->daddr, protocol,
					       skb->len - dataoff, 0);
		skb->ip_summed = CHECKSUM_NONE;
		csum = __skb_checksum_complete_head(skb, dataoff + len);
		if (!csum)
			skb->ip_summed = CHECKSUM_UNNECESSARY;
	}
	return csum;
}

static int nf_ip_route(struct dst_entry **dst, struct flowi *fl)
{
	return ip_route_output_key(&init_net, (struct rtable **)dst, fl);
@@ -193,6 +215,7 @@ static int nf_ip_route(struct dst_entry **dst, struct flowi *fl)
static const struct nf_afinfo nf_ip_afinfo = {
	.family			= AF_INET,
	.checksum		= nf_ip_checksum,
	.checksum_partial	= nf_ip_checksum_partial,
	.route			= nf_ip_route,
	.saveroute		= nf_ip_saveroute,
	.reroute		= nf_ip_reroute,
+35 −7
Original line number Diff line number Diff line
@@ -121,12 +121,40 @@ __sum16 nf_ip6_checksum(struct sk_buff *skb, unsigned int hook,
	}
	return csum;
}

EXPORT_SYMBOL(nf_ip6_checksum);

static __sum16 nf_ip6_checksum_partial(struct sk_buff *skb, unsigned int hook,
				       unsigned int dataoff, unsigned int len,
				       u_int8_t protocol)
{
	struct ipv6hdr *ip6h = ipv6_hdr(skb);
	__wsum hsum;
	__sum16 csum = 0;

	switch (skb->ip_summed) {
	case CHECKSUM_COMPLETE:
		if (len == skb->len - dataoff)
			return nf_ip6_checksum(skb, hook, dataoff, protocol);
		/* fall through */
	case CHECKSUM_NONE:
		hsum = skb_checksum(skb, 0, dataoff, 0);
		skb->csum = ~csum_unfold(csum_ipv6_magic(&ip6h->saddr,
							 &ip6h->daddr,
							 skb->len - dataoff,
							 protocol,
							 csum_sub(0, hsum)));
		skb->ip_summed = CHECKSUM_NONE;
		csum = __skb_checksum_complete_head(skb, dataoff + len);
		if (!csum)
			skb->ip_summed = CHECKSUM_UNNECESSARY;
	}
	return csum;
};

static const struct nf_afinfo nf_ip6_afinfo = {
	.family			= AF_INET6,
	.checksum		= nf_ip6_checksum,
	.checksum_partial	= nf_ip6_checksum_partial,
	.route			= nf_ip6_route,
	.saveroute		= nf_ip6_saveroute,
	.reroute		= nf_ip6_reroute,
+7 −26
Original line number Diff line number Diff line
@@ -127,33 +127,14 @@ static int udplite_error(struct sk_buff *skb, unsigned int dataoff,
	}

	/* Checksum invalid? Ignore. */
	if (nf_conntrack_checksum && !skb_csum_unnecessary(skb) &&
	    hooknum == NF_INET_PRE_ROUTING) {
		if (pf == PF_INET) {
			struct iphdr *iph = ip_hdr(skb);

			skb->csum = csum_tcpudp_nofold(iph->saddr, iph->daddr,
						       udplen, IPPROTO_UDPLITE, 0);
		} else {
			struct ipv6hdr *ipv6h = ipv6_hdr(skb);
			__wsum hsum = skb_checksum(skb, 0, dataoff, 0);

			skb->csum = ~csum_unfold(
				csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr,
						udplen, IPPROTO_UDPLITE,
						csum_sub(0, hsum)));
		}

		skb->ip_summed = CHECKSUM_NONE;
		if (__skb_checksum_complete_head(skb, dataoff + cscov)) {
	if (nf_conntrack_checksum && hooknum == NF_INET_PRE_ROUTING &&
	    nf_checksum_partial(skb, hooknum, dataoff, cscov, IPPROTO_UDP,
	    			pf)) {
		if (LOG_INVALID(IPPROTO_UDPLITE))
			nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
					      "nf_ct_udplite: bad UDPLite "
					      "checksum ");
				      "nf_ct_udplite: bad UDPLite checksum ");
		return -NF_ACCEPT;
	}
		skb->ip_summed = CHECKSUM_UNNECESSARY;
	}

	return NF_ACCEPT;
}