Skip to content

Commit

Permalink
Merge branch 'master' of git://1984.lsi.us.es/nf
Browse files Browse the repository at this point in the history
Pablo Neira Ayuso says:

====================
The following patchset contains Netfilter/IPVS fixes for 3.8-rc7, they are:

* Fix oops in IPVS state-sync due to releasing a random memory area due
  to unitialized pointer, from Dan Carpenter.

* Fix SCTP flow establishment due to bad checksumming mangling in IPVS,
  from Daniel Borkmann.

* Three fixes for the recently added IPv6 NPT, all from YOSHIFUJI Hideaki,
  with an amendment collapsed into those patches from Ulrich Weber. They
  fiix adjustment calculation, fix prefix mangling and ensure LSB of
  prefixes are zeroes (as required by RFC).

Specifically, it took me a while to validate the 1's complement arithmetics/
checksumming approach in the IPv6 NPT code.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Feb 11, 2013
2 parents 044453b + edb2722 commit cfa82e0
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 22 deletions.
18 changes: 14 additions & 4 deletions net/ipv6/netfilter/ip6t_NPT.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/ipv6.h>
#include <net/ipv6.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv6.h>
#include <linux/netfilter_ipv6/ip6t_NPT.h>
Expand All @@ -18,19 +19,28 @@ static int ip6t_npt_checkentry(const struct xt_tgchk_param *par)
{
struct ip6t_npt_tginfo *npt = par->targinfo;
__wsum src_sum = 0, dst_sum = 0;
struct in6_addr pfx;
unsigned int i;

if (npt->src_pfx_len > 64 || npt->dst_pfx_len > 64)
return -EINVAL;

/* Ensure that LSB of prefix is zero */
ipv6_addr_prefix(&pfx, &npt->src_pfx.in6, npt->src_pfx_len);
if (!ipv6_addr_equal(&pfx, &npt->src_pfx.in6))
return -EINVAL;
ipv6_addr_prefix(&pfx, &npt->dst_pfx.in6, npt->dst_pfx_len);
if (!ipv6_addr_equal(&pfx, &npt->dst_pfx.in6))
return -EINVAL;

for (i = 0; i < ARRAY_SIZE(npt->src_pfx.in6.s6_addr16); i++) {
src_sum = csum_add(src_sum,
(__force __wsum)npt->src_pfx.in6.s6_addr16[i]);
dst_sum = csum_add(dst_sum,
(__force __wsum)npt->dst_pfx.in6.s6_addr16[i]);
}

npt->adjustment = (__force __sum16) csum_sub(src_sum, dst_sum);
npt->adjustment = ~csum_fold(csum_sub(src_sum, dst_sum));
return 0;
}

Expand All @@ -51,7 +61,7 @@ static bool ip6t_npt_map_pfx(const struct ip6t_npt_tginfo *npt,

idx = i / 32;
addr->s6_addr32[idx] &= mask;
addr->s6_addr32[idx] |= npt->dst_pfx.in6.s6_addr32[idx];
addr->s6_addr32[idx] |= ~mask & npt->dst_pfx.in6.s6_addr32[idx];
}

if (pfx_len <= 48)
Expand All @@ -66,8 +76,8 @@ static bool ip6t_npt_map_pfx(const struct ip6t_npt_tginfo *npt,
return false;
}

sum = (__force __sum16) csum_add((__force __wsum)addr->s6_addr16[idx],
npt->adjustment);
sum = ~csum_fold(csum_add(csum_unfold((__force __sum16)addr->s6_addr16[idx]),
csum_unfold(npt->adjustment)));
if (sum == CSUM_MANGLED_0)
sum = 0;
*(__force __sum16 *)&addr->s6_addr16[idx] = sum;
Expand Down
35 changes: 17 additions & 18 deletions net/netfilter/ipvs/ip_vs_proto_sctp.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,27 @@ sctp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
return 1;
}

static void sctp_nat_csum(struct sk_buff *skb, sctp_sctphdr_t *sctph,
unsigned int sctphoff)
{
__u32 crc32;
struct sk_buff *iter;

crc32 = sctp_start_cksum((__u8 *)sctph, skb_headlen(skb) - sctphoff);
skb_walk_frags(skb, iter)
crc32 = sctp_update_cksum((u8 *) iter->data,
skb_headlen(iter), crc32);
sctph->checksum = sctp_end_cksum(crc32);

skb->ip_summed = CHECKSUM_UNNECESSARY;
}

static int
sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
struct ip_vs_conn *cp, struct ip_vs_iphdr *iph)
{
sctp_sctphdr_t *sctph;
unsigned int sctphoff = iph->len;
struct sk_buff *iter;
__be32 crc32;

#ifdef CONFIG_IP_VS_IPV6
if (cp->af == AF_INET6 && iph->fragoffs)
Expand All @@ -92,13 +105,7 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
sctph = (void *) skb_network_header(skb) + sctphoff;
sctph->source = cp->vport;

/* Calculate the checksum */
crc32 = sctp_start_cksum((u8 *) sctph, skb_headlen(skb) - sctphoff);
skb_walk_frags(skb, iter)
crc32 = sctp_update_cksum((u8 *) iter->data, skb_headlen(iter),
crc32);
crc32 = sctp_end_cksum(crc32);
sctph->checksum = crc32;
sctp_nat_csum(skb, sctph, sctphoff);

return 1;
}
Expand All @@ -109,8 +116,6 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
{
sctp_sctphdr_t *sctph;
unsigned int sctphoff = iph->len;
struct sk_buff *iter;
__be32 crc32;

#ifdef CONFIG_IP_VS_IPV6
if (cp->af == AF_INET6 && iph->fragoffs)
Expand All @@ -134,13 +139,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
sctph = (void *) skb_network_header(skb) + sctphoff;
sctph->dest = cp->dport;

/* Calculate the checksum */
crc32 = sctp_start_cksum((u8 *) sctph, skb_headlen(skb) - sctphoff);
skb_walk_frags(skb, iter)
crc32 = sctp_update_cksum((u8 *) iter->data, skb_headlen(iter),
crc32);
crc32 = sctp_end_cksum(crc32);
sctph->checksum = crc32;
sctp_nat_csum(skb, sctph, sctphoff);

return 1;
}
Expand Down
2 changes: 2 additions & 0 deletions net/netfilter/ipvs/ip_vs_sync.c
Original file line number Diff line number Diff line change
Expand Up @@ -1795,6 +1795,8 @@ int start_sync_thread(struct net *net, int state, char *mcast_ifn, __u8 syncid)
GFP_KERNEL);
if (!tinfo->buf)
goto outtinfo;
} else {
tinfo->buf = NULL;
}
tinfo->id = id;

Expand Down

0 comments on commit cfa82e0

Please sign in to comment.