diff options
-rw-r--r-- | include/net/ipv6.h | 5 | ||||
-rw-r--r-- | net/ipv6/exthdrs_core.c | 36 |
2 files changed, 31 insertions, 10 deletions
diff --git a/include/net/ipv6.h b/include/net/ipv6.h index b2f0cfb0a381..acbd8e034310 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h | |||
@@ -631,8 +631,9 @@ extern int ipv6_skip_exthdr(const struct sk_buff *, int start, | |||
631 | extern bool ipv6_ext_hdr(u8 nexthdr); | 631 | extern bool ipv6_ext_hdr(u8 nexthdr); |
632 | 632 | ||
633 | enum { | 633 | enum { |
634 | IP6_FH_F_FRAG = (1 << 0), | 634 | IP6_FH_F_FRAG = (1 << 0), |
635 | IP6_FH_F_AUTH = (1 << 1), | 635 | IP6_FH_F_AUTH = (1 << 1), |
636 | IP6_FH_F_SKIP_RH = (1 << 2), | ||
636 | }; | 637 | }; |
637 | 638 | ||
638 | /* find specified header and get offset to it */ | 639 | /* find specified header and get offset to it */ |
diff --git a/net/ipv6/exthdrs_core.c b/net/ipv6/exthdrs_core.c index 8ea253ad35b1..11b4e29c8452 100644 --- a/net/ipv6/exthdrs_core.c +++ b/net/ipv6/exthdrs_core.c | |||
@@ -132,9 +132,11 @@ EXPORT_SYMBOL(ipv6_skip_exthdr); | |||
132 | * *offset is meaningless and fragment offset is stored in *fragoff if fragoff | 132 | * *offset is meaningless and fragment offset is stored in *fragoff if fragoff |
133 | * isn't NULL. | 133 | * isn't NULL. |
134 | * | 134 | * |
135 | * if flags is not NULL and it's a fragment, then the frag flag IP6_FH_F_FRAG | 135 | * if flags is not NULL and it's a fragment, then the frag flag |
136 | * will be set. If it's an AH header, the IP6_FH_F_AUTH flag is set and | 136 | * IP6_FH_F_FRAG will be set. If it's an AH header, the |
137 | * target < 0, then this function will stop at the AH header. | 137 | * IP6_FH_F_AUTH flag is set and target < 0, then this function will |
138 | * stop at the AH header. If IP6_FH_F_SKIP_RH flag was passed, then this | ||
139 | * function will skip all those routing headers, where segements_left was 0. | ||
138 | */ | 140 | */ |
139 | int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, | 141 | int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, |
140 | int target, unsigned short *fragoff, int *flags) | 142 | int target, unsigned short *fragoff, int *flags) |
@@ -142,6 +144,7 @@ int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, | |||
142 | unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr); | 144 | unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr); |
143 | u8 nexthdr = ipv6_hdr(skb)->nexthdr; | 145 | u8 nexthdr = ipv6_hdr(skb)->nexthdr; |
144 | unsigned int len; | 146 | unsigned int len; |
147 | bool found; | ||
145 | 148 | ||
146 | if (fragoff) | 149 | if (fragoff) |
147 | *fragoff = 0; | 150 | *fragoff = 0; |
@@ -159,9 +162,10 @@ int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, | |||
159 | } | 162 | } |
160 | len = skb->len - start; | 163 | len = skb->len - start; |
161 | 164 | ||
162 | while (nexthdr != target) { | 165 | do { |
163 | struct ipv6_opt_hdr _hdr, *hp; | 166 | struct ipv6_opt_hdr _hdr, *hp; |
164 | unsigned int hdrlen; | 167 | unsigned int hdrlen; |
168 | found = (nexthdr == target); | ||
165 | 169 | ||
166 | if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) { | 170 | if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) { |
167 | if (target < 0) | 171 | if (target < 0) |
@@ -172,6 +176,20 @@ int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, | |||
172 | hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr); | 176 | hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr); |
173 | if (hp == NULL) | 177 | if (hp == NULL) |
174 | return -EBADMSG; | 178 | return -EBADMSG; |
179 | |||
180 | if (nexthdr == NEXTHDR_ROUTING) { | ||
181 | struct ipv6_rt_hdr _rh, *rh; | ||
182 | |||
183 | rh = skb_header_pointer(skb, start, sizeof(_rh), | ||
184 | &_rh); | ||
185 | if (rh == NULL) | ||
186 | return -EBADMSG; | ||
187 | |||
188 | if (flags && (*flags & IP6_FH_F_SKIP_RH) && | ||
189 | rh->segments_left == 0) | ||
190 | found = false; | ||
191 | } | ||
192 | |||
175 | if (nexthdr == NEXTHDR_FRAGMENT) { | 193 | if (nexthdr == NEXTHDR_FRAGMENT) { |
176 | unsigned short _frag_off; | 194 | unsigned short _frag_off; |
177 | __be16 *fp; | 195 | __be16 *fp; |
@@ -205,10 +223,12 @@ int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, | |||
205 | } else | 223 | } else |
206 | hdrlen = ipv6_optlen(hp); | 224 | hdrlen = ipv6_optlen(hp); |
207 | 225 | ||
208 | nexthdr = hp->nexthdr; | 226 | if (!found) { |
209 | len -= hdrlen; | 227 | nexthdr = hp->nexthdr; |
210 | start += hdrlen; | 228 | len -= hdrlen; |
211 | } | 229 | start += hdrlen; |
230 | } | ||
231 | } while (!found); | ||
212 | 232 | ||
213 | *offset = start; | 233 | *offset = start; |
214 | return nexthdr; | 234 | return nexthdr; |