diff options
author | Patrick McHardy <kaber@trash.net> | 2012-08-26 13:13:59 -0400 |
---|---|---|
committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2012-08-29 21:00:11 -0400 |
commit | 2b60af017880f7dc35d1fac65f48fc94f8a3c1ec (patch) | |
tree | 9d31901b188530c740a8b3243580c3bd4de4563a | |
parent | 4cdd34084d539c758d00c5dc7bf95db2e4f2bc70 (diff) |
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>
-rw-r--r-- | net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 63 |
1 files changed, 6 insertions, 57 deletions
diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index 521ddca876f8..dcf6010f68d9 100644 --- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | |||
@@ -64,82 +64,31 @@ static int ipv6_print_tuple(struct seq_file *s, | |||
64 | tuple->src.u3.ip6, tuple->dst.u3.ip6); | 64 | tuple->src.u3.ip6, tuple->dst.u3.ip6); |
65 | } | 65 | } |
66 | 66 | ||
67 | /* | ||
68 | * Based on ipv6_skip_exthdr() in net/ipv6/exthdr.c | ||
69 | * | ||
70 | * This function parses (probably truncated) exthdr set "hdr" | ||
71 | * of length "len". "nexthdrp" initially points to some place, | ||
72 | * where type of the first header can be found. | ||
73 | * | ||
74 | * It skips all well-known exthdrs, and returns pointer to the start | ||
75 | * of unparsable area i.e. the first header with unknown type. | ||
76 | * if success, *nexthdr is updated by type/protocol of this header. | ||
77 | * | ||
78 | * NOTES: - it may return pointer pointing beyond end of packet, | ||
79 | * if the last recognized header is truncated in the middle. | ||
80 | * - if packet is truncated, so that all parsed headers are skipped, | ||
81 | * it returns -1. | ||
82 | * - if packet is fragmented, return pointer of the fragment header. | ||
83 | * - ESP is unparsable for now and considered like | ||
84 | * normal payload protocol. | ||
85 | * - Note also special handling of AUTH header. Thanks to IPsec wizards. | ||
86 | */ | ||
87 | |||
88 | static int nf_ct_ipv6_skip_exthdr(const struct sk_buff *skb, int start, | ||
89 | u8 *nexthdrp, int len) | ||
90 | { | ||
91 | u8 nexthdr = *nexthdrp; | ||
92 | |||
93 | while (ipv6_ext_hdr(nexthdr)) { | ||
94 | struct ipv6_opt_hdr hdr; | ||
95 | int hdrlen; | ||
96 | |||
97 | if (len < (int)sizeof(struct ipv6_opt_hdr)) | ||
98 | return -1; | ||
99 | if (nexthdr == NEXTHDR_NONE) | ||
100 | break; | ||
101 | if (nexthdr == NEXTHDR_FRAGMENT) | ||
102 | break; | ||
103 | if (skb_copy_bits(skb, start, &hdr, sizeof(hdr))) | ||
104 | BUG(); | ||
105 | if (nexthdr == NEXTHDR_AUTH) | ||
106 | hdrlen = (hdr.hdrlen+2)<<2; | ||
107 | else | ||
108 | hdrlen = ipv6_optlen(&hdr); | ||
109 | |||
110 | nexthdr = hdr.nexthdr; | ||
111 | len -= hdrlen; | ||
112 | start += hdrlen; | ||
113 | } | ||
114 | |||
115 | *nexthdrp = nexthdr; | ||
116 | return start; | ||
117 | } | ||
118 | |||
119 | static int ipv6_get_l4proto(const struct sk_buff *skb, unsigned int nhoff, | 67 | static int ipv6_get_l4proto(const struct sk_buff *skb, unsigned int nhoff, |
120 | unsigned int *dataoff, u_int8_t *protonum) | 68 | unsigned int *dataoff, u_int8_t *protonum) |
121 | { | 69 | { |
122 | unsigned int extoff = nhoff + sizeof(struct ipv6hdr); | 70 | unsigned int extoff = nhoff + sizeof(struct ipv6hdr); |
123 | unsigned char pnum; | 71 | __be16 frag_off; |
124 | int protoff; | 72 | int protoff; |
73 | u8 nexthdr; | ||
125 | 74 | ||
126 | if (skb_copy_bits(skb, nhoff + offsetof(struct ipv6hdr, nexthdr), | 75 | if (skb_copy_bits(skb, nhoff + offsetof(struct ipv6hdr, nexthdr), |
127 | &pnum, sizeof(pnum)) != 0) { | 76 | &nexthdr, sizeof(nexthdr)) != 0) { |
128 | pr_debug("ip6_conntrack_core: can't get nexthdr\n"); | 77 | pr_debug("ip6_conntrack_core: can't get nexthdr\n"); |
129 | return -NF_ACCEPT; | 78 | return -NF_ACCEPT; |
130 | } | 79 | } |
131 | protoff = nf_ct_ipv6_skip_exthdr(skb, extoff, &pnum, skb->len - extoff); | 80 | protoff = ipv6_skip_exthdr(skb, extoff, &nexthdr, &frag_off); |
132 | /* | 81 | /* |
133 | * (protoff == skb->len) mean that the packet doesn't have no data | 82 | * (protoff == skb->len) mean that the packet doesn't have no data |
134 | * except of IPv6 & ext headers. but it's tracked anyway. - YK | 83 | * except of IPv6 & ext headers. but it's tracked anyway. - YK |
135 | */ | 84 | */ |
136 | if ((protoff < 0) || (protoff > skb->len)) { | 85 | if (protoff < 0 || (frag_off & htons(~0x7)) != 0) { |
137 | pr_debug("ip6_conntrack_core: can't find proto in pkt\n"); | 86 | pr_debug("ip6_conntrack_core: can't find proto in pkt\n"); |
138 | return -NF_ACCEPT; | 87 | return -NF_ACCEPT; |
139 | } | 88 | } |
140 | 89 | ||
141 | *dataoff = protoff; | 90 | *dataoff = protoff; |
142 | *protonum = pnum; | 91 | *protonum = nexthdr; |
143 | return NF_ACCEPT; | 92 | return NF_ACCEPT; |
144 | } | 93 | } |
145 | 94 | ||