Commit d63a6507 authored by Patrick McHardy's avatar Patrick McHardy

[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
...@@ -234,6 +234,11 @@ struct nf_afinfo { ...@@ -234,6 +234,11 @@ struct nf_afinfo {
unsigned short family; unsigned short family;
__sum16 (*checksum)(struct sk_buff *skb, unsigned int hook, __sum16 (*checksum)(struct sk_buff *skb, unsigned int hook,
unsigned int dataoff, u_int8_t protocol); 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); int (*route)(struct dst_entry **dst, struct flowi *fl);
void (*saveroute)(const struct sk_buff *skb, void (*saveroute)(const struct sk_buff *skb,
struct nf_queue_entry *entry); struct nf_queue_entry *entry);
...@@ -263,6 +268,23 @@ nf_checksum(struct sk_buff *skb, unsigned int hook, unsigned int dataoff, ...@@ -263,6 +268,23 @@ nf_checksum(struct sk_buff *skb, unsigned int hook, unsigned int dataoff,
return csum; 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 int nf_register_afinfo(const struct nf_afinfo *afinfo);
extern void nf_unregister_afinfo(const struct nf_afinfo *afinfo); extern void nf_unregister_afinfo(const struct nf_afinfo *afinfo);
......
...@@ -182,21 +182,44 @@ __sum16 nf_ip_checksum(struct sk_buff *skb, unsigned int hook, ...@@ -182,21 +182,44 @@ __sum16 nf_ip_checksum(struct sk_buff *skb, unsigned int hook,
} }
return csum; return csum;
} }
EXPORT_SYMBOL(nf_ip_checksum); 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) static int nf_ip_route(struct dst_entry **dst, struct flowi *fl)
{ {
return ip_route_output_key(&init_net, (struct rtable **)dst, fl); return ip_route_output_key(&init_net, (struct rtable **)dst, fl);
} }
static const struct nf_afinfo nf_ip_afinfo = { static const struct nf_afinfo nf_ip_afinfo = {
.family = AF_INET, .family = AF_INET,
.checksum = nf_ip_checksum, .checksum = nf_ip_checksum,
.route = nf_ip_route, .checksum_partial = nf_ip_checksum_partial,
.saveroute = nf_ip_saveroute, .route = nf_ip_route,
.reroute = nf_ip_reroute, .saveroute = nf_ip_saveroute,
.route_key_size = sizeof(struct ip_rt_info), .reroute = nf_ip_reroute,
.route_key_size = sizeof(struct ip_rt_info),
}; };
static int ipv4_netfilter_init(void) static int ipv4_netfilter_init(void)
......
...@@ -121,16 +121,44 @@ __sum16 nf_ip6_checksum(struct sk_buff *skb, unsigned int hook, ...@@ -121,16 +121,44 @@ __sum16 nf_ip6_checksum(struct sk_buff *skb, unsigned int hook,
} }
return csum; return csum;
} }
EXPORT_SYMBOL(nf_ip6_checksum); 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 = { static const struct nf_afinfo nf_ip6_afinfo = {
.family = AF_INET6, .family = AF_INET6,
.checksum = nf_ip6_checksum, .checksum = nf_ip6_checksum,
.route = nf_ip6_route, .checksum_partial = nf_ip6_checksum_partial,
.saveroute = nf_ip6_saveroute, .route = nf_ip6_route,
.reroute = nf_ip6_reroute, .saveroute = nf_ip6_saveroute,
.route_key_size = sizeof(struct ip6_rt_info), .reroute = nf_ip6_reroute,
.route_key_size = sizeof(struct ip6_rt_info),
}; };
int __init ipv6_netfilter_init(void) int __init ipv6_netfilter_init(void)
......
...@@ -127,32 +127,13 @@ static int udplite_error(struct sk_buff *skb, unsigned int dataoff, ...@@ -127,32 +127,13 @@ static int udplite_error(struct sk_buff *skb, unsigned int dataoff,
} }
/* Checksum invalid? Ignore. */ /* Checksum invalid? Ignore. */
if (nf_conntrack_checksum && !skb_csum_unnecessary(skb) && if (nf_conntrack_checksum && hooknum == NF_INET_PRE_ROUTING &&
hooknum == NF_INET_PRE_ROUTING) { nf_checksum_partial(skb, hooknum, dataoff, cscov, IPPROTO_UDP,
if (pf == PF_INET) { pf)) {
struct iphdr *iph = ip_hdr(skb); if (LOG_INVALID(IPPROTO_UDPLITE))
nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
skb->csum = csum_tcpudp_nofold(iph->saddr, iph->daddr, "nf_ct_udplite: bad UDPLite checksum ");
udplen, IPPROTO_UDPLITE, 0); return -NF_ACCEPT;
} 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 (LOG_INVALID(IPPROTO_UDPLITE))
nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
"nf_ct_udplite: bad UDPLite "
"checksum ");
return -NF_ACCEPT;
}
skb->ip_summed = CHECKSUM_UNNECESSARY;
} }
return NF_ACCEPT; return NF_ACCEPT;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment