aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6')
-rw-r--r--net/ipv6/exthdrs.c84
1 files changed, 83 insertions, 1 deletions
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index 1cdd0f0b5d34..6a6466bb5f26 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -196,8 +196,80 @@ bad:
196 Destination options header. 196 Destination options header.
197 *****************************/ 197 *****************************/
198 198
199#ifdef CONFIG_IPV6_MIP6
200static int ipv6_dest_hao(struct sk_buff **skbp, int optoff)
201{
202 struct sk_buff *skb = *skbp;
203 struct ipv6_destopt_hao *hao;
204 struct inet6_skb_parm *opt = IP6CB(skb);
205 struct ipv6hdr *ipv6h = (struct ipv6hdr *)skb->nh.raw;
206 struct in6_addr tmp_addr;
207 int ret;
208
209 if (opt->dsthao) {
210 LIMIT_NETDEBUG(KERN_DEBUG "hao duplicated\n");
211 goto discard;
212 }
213 opt->dsthao = opt->dst1;
214 opt->dst1 = 0;
215
216 hao = (struct ipv6_destopt_hao *)(skb->nh.raw + optoff);
217
218 if (hao->length != 16) {
219 LIMIT_NETDEBUG(
220 KERN_DEBUG "hao invalid option length = %d\n", hao->length);
221 goto discard;
222 }
223
224 if (!(ipv6_addr_type(&hao->addr) & IPV6_ADDR_UNICAST)) {
225 LIMIT_NETDEBUG(
226 KERN_DEBUG "hao is not an unicast addr: " NIP6_FMT "\n", NIP6(hao->addr));
227 goto discard;
228 }
229
230 ret = xfrm6_input_addr(skb, (xfrm_address_t *)&ipv6h->daddr,
231 (xfrm_address_t *)&hao->addr, IPPROTO_DSTOPTS);
232 if (unlikely(ret < 0))
233 goto discard;
234
235 if (skb_cloned(skb)) {
236 struct sk_buff *skb2 = skb_copy(skb, GFP_ATOMIC);
237 if (skb2 == NULL)
238 goto discard;
239
240 kfree_skb(skb);
241
242 /* update all variable using below by copied skbuff */
243 *skbp = skb = skb2;
244 hao = (struct ipv6_destopt_hao *)(skb2->nh.raw + optoff);
245 ipv6h = (struct ipv6hdr *)skb2->nh.raw;
246 }
247
248 if (skb->ip_summed == CHECKSUM_COMPLETE)
249 skb->ip_summed = CHECKSUM_NONE;
250
251 ipv6_addr_copy(&tmp_addr, &ipv6h->saddr);
252 ipv6_addr_copy(&ipv6h->saddr, &hao->addr);
253 ipv6_addr_copy(&hao->addr, &tmp_addr);
254
255 if (skb->tstamp.off_sec == 0)
256 __net_timestamp(skb);
257
258 return 1;
259
260 discard:
261 kfree_skb(skb);
262 return 0;
263}
264#endif
265
199static struct tlvtype_proc tlvprocdestopt_lst[] = { 266static struct tlvtype_proc tlvprocdestopt_lst[] = {
200 /* No destination options are defined now */ 267#ifdef CONFIG_IPV6_MIP6
268 {
269 .type = IPV6_TLV_HAO,
270 .func = ipv6_dest_hao,
271 },
272#endif
201 {-1, NULL} 273 {-1, NULL}
202}; 274};
203 275
@@ -205,6 +277,9 @@ static int ipv6_destopt_rcv(struct sk_buff **skbp)
205{ 277{
206 struct sk_buff *skb = *skbp; 278 struct sk_buff *skb = *skbp;
207 struct inet6_skb_parm *opt = IP6CB(skb); 279 struct inet6_skb_parm *opt = IP6CB(skb);
280#ifdef CONFIG_IPV6_MIP6
281 __u16 dstbuf;
282#endif
208 283
209 if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8) || 284 if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8) ||
210 !pskb_may_pull(skb, (skb->h.raw-skb->data)+((skb->h.raw[1]+1)<<3))) { 285 !pskb_may_pull(skb, (skb->h.raw-skb->data)+((skb->h.raw[1]+1)<<3))) {
@@ -215,11 +290,18 @@ static int ipv6_destopt_rcv(struct sk_buff **skbp)
215 290
216 opt->lastopt = skb->h.raw - skb->nh.raw; 291 opt->lastopt = skb->h.raw - skb->nh.raw;
217 opt->dst1 = skb->h.raw - skb->nh.raw; 292 opt->dst1 = skb->h.raw - skb->nh.raw;
293#ifdef CONFIG_IPV6_MIP6
294 dstbuf = opt->dst1;
295#endif
218 296
219 if (ip6_parse_tlv(tlvprocdestopt_lst, skbp)) { 297 if (ip6_parse_tlv(tlvprocdestopt_lst, skbp)) {
220 skb = *skbp; 298 skb = *skbp;
221 skb->h.raw += ((skb->h.raw[1]+1)<<3); 299 skb->h.raw += ((skb->h.raw[1]+1)<<3);
300#ifdef CONFIG_IPV6_MIP6
301 opt->nhoff = dstbuf;
302#else
222 opt->nhoff = opt->dst1; 303 opt->nhoff = opt->dst1;
304#endif
223 return 1; 305 return 1;
224 } 306 }
225 307