netfilter: nf_conntrack_ipv6: fix tracking of ICMPv6 error messages containing fragments
ICMPv6 error messages are tracked by extracting the conntrack tuple of the inner packet and looking up the corresponding conntrack entry. Tuple extraction uses the ->get_l4proto() callback, which in case of fragments returns NEXTHDR_FRAGMENT instead of the upper protocol, even for the first fragment when the entire next header is present, resulting in a failure to find the correct connection tracking entry. This patch changes ipv6_get_l4proto() to use ipv6_skip_exthdr() instead of nf_ct_ipv6_skip_exthdr() in order to skip fragment headers when the fragment offset is zero. Signed-off-by: Patrick McHardy <kaber@trash.net>
This commit is contained in:
parent
4cdd34084d
commit
2b60af0178
1 changed files with 6 additions and 57 deletions
|
@ -64,82 +64,31 @@ static int ipv6_print_tuple(struct seq_file *s,
|
|||
tuple->src.u3.ip6, tuple->dst.u3.ip6);
|
||||
}
|
||||
|
||||
/*
|
||||
* Based on ipv6_skip_exthdr() in net/ipv6/exthdr.c
|
||||
*
|
||||
* This function parses (probably truncated) exthdr set "hdr"
|
||||
* of length "len". "nexthdrp" initially points to some place,
|
||||
* where type of the first header can be found.
|
||||
*
|
||||
* It skips all well-known exthdrs, and returns pointer to the start
|
||||
* of unparsable area i.e. the first header with unknown type.
|
||||
* if success, *nexthdr is updated by type/protocol of this header.
|
||||
*
|
||||
* NOTES: - it may return pointer pointing beyond end of packet,
|
||||
* if the last recognized header is truncated in the middle.
|
||||
* - if packet is truncated, so that all parsed headers are skipped,
|
||||
* it returns -1.
|
||||
* - if packet is fragmented, return pointer of the fragment header.
|
||||
* - ESP is unparsable for now and considered like
|
||||
* normal payload protocol.
|
||||
* - Note also special handling of AUTH header. Thanks to IPsec wizards.
|
||||
*/
|
||||
|
||||
static int nf_ct_ipv6_skip_exthdr(const struct sk_buff *skb, int start,
|
||||
u8 *nexthdrp, int len)
|
||||
{
|
||||
u8 nexthdr = *nexthdrp;
|
||||
|
||||
while (ipv6_ext_hdr(nexthdr)) {
|
||||
struct ipv6_opt_hdr hdr;
|
||||
int hdrlen;
|
||||
|
||||
if (len < (int)sizeof(struct ipv6_opt_hdr))
|
||||
return -1;
|
||||
if (nexthdr == NEXTHDR_NONE)
|
||||
break;
|
||||
if (nexthdr == NEXTHDR_FRAGMENT)
|
||||
break;
|
||||
if (skb_copy_bits(skb, start, &hdr, sizeof(hdr)))
|
||||
BUG();
|
||||
if (nexthdr == NEXTHDR_AUTH)
|
||||
hdrlen = (hdr.hdrlen+2)<<2;
|
||||
else
|
||||
hdrlen = ipv6_optlen(&hdr);
|
||||
|
||||
nexthdr = hdr.nexthdr;
|
||||
len -= hdrlen;
|
||||
start += hdrlen;
|
||||
}
|
||||
|
||||
*nexthdrp = nexthdr;
|
||||
return start;
|
||||
}
|
||||
|
||||
static int ipv6_get_l4proto(const struct sk_buff *skb, unsigned int nhoff,
|
||||
unsigned int *dataoff, u_int8_t *protonum)
|
||||
{
|
||||
unsigned int extoff = nhoff + sizeof(struct ipv6hdr);
|
||||
unsigned char pnum;
|
||||
__be16 frag_off;
|
||||
int protoff;
|
||||
u8 nexthdr;
|
||||
|
||||
if (skb_copy_bits(skb, nhoff + offsetof(struct ipv6hdr, nexthdr),
|
||||
&pnum, sizeof(pnum)) != 0) {
|
||||
&nexthdr, sizeof(nexthdr)) != 0) {
|
||||
pr_debug("ip6_conntrack_core: can't get nexthdr\n");
|
||||
return -NF_ACCEPT;
|
||||
}
|
||||
protoff = nf_ct_ipv6_skip_exthdr(skb, extoff, &pnum, skb->len - extoff);
|
||||
protoff = ipv6_skip_exthdr(skb, extoff, &nexthdr, &frag_off);
|
||||
/*
|
||||
* (protoff == skb->len) mean that the packet doesn't have no data
|
||||
* except of IPv6 & ext headers. but it's tracked anyway. - YK
|
||||
*/
|
||||
if ((protoff < 0) || (protoff > skb->len)) {
|
||||
if (protoff < 0 || (frag_off & htons(~0x7)) != 0) {
|
||||
pr_debug("ip6_conntrack_core: can't find proto in pkt\n");
|
||||
return -NF_ACCEPT;
|
||||
}
|
||||
|
||||
*dataoff = protoff;
|
||||
*protonum = pnum;
|
||||
*protonum = nexthdr;
|
||||
return NF_ACCEPT;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue