aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/net/ipv6.h5
-rw-r--r--net/ipv6/exthdrs_core.c36
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,
631extern bool ipv6_ext_hdr(u8 nexthdr); 631extern bool ipv6_ext_hdr(u8 nexthdr);
632 632
633enum { 633enum {
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 */
139int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, 141int 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;