[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: Patrick McHardy <kaber@trash.net>
This commit is contained in:
Родитель
6185f870e2
Коммит
d63a650736
|
@ -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,
|
||||||
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,
|
||||||
}
|
}
|
||||||
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,
|
||||||
}
|
}
|
||||||
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,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче