00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018 #include <linux/errno.h>
00019 #include <linux/types.h>
00020 #include <linux/socket.h>
00021 #include <linux/sockios.h>
00022 #include <linux/sched.h>
00023 #include <linux/net.h>
00024 #include <linux/netdevice.h>
00025 #include <linux/in6.h>
00026 #include <linux/icmpv6.h>
00027
00028 #include <net/sock.h>
00029 #include <net/snmp.h>
00030
00031 #include <net/ipv6.h>
00032 #include <net/protocol.h>
00033 #include <net/transp_v6.h>
00034 #include <net/rawv6.h>
00035 #include <net/ndisc.h>
00036 #include <net/ip6_route.h>
00037 #include <net/addrconf.h>
00038
00039 #include <asm/uaccess.h>
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049 struct hdrtype_proc
00050 {
00051 int type;
00052 u8* (*func) (struct sk_buff **, u8 *ptr);
00053 };
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063 struct tlvtype_proc
00064 {
00065 int type;
00066 int (*func) (struct sk_buff *, __u8 *ptr);
00067 };
00068
00069
00070
00071
00072
00073
00074
00075 int ip6_tlvopt_unknown(struct sk_buff *skb, u8 *opt)
00076 {
00077 switch ((opt[0] & 0xC0) >> 6) {
00078 case 0:
00079 return 1;
00080
00081 case 1:
00082 break;
00083
00084 case 3:
00085
00086
00087
00088 if (ipv6_addr_is_multicast(&skb->nh.ipv6h->daddr))
00089 break;
00090 case 2:
00091 icmpv6_param_prob(skb, ICMPV6_UNK_OPTION, opt);
00092 return 0;
00093 };
00094
00095 kfree_skb(skb);
00096 return 0;
00097 }
00098
00099
00100
00101 static int ip6_parse_tlv(struct tlvtype_proc *procs, struct sk_buff *skb,
00102 __u8 *nhptr)
00103 {
00104 struct tlvtype_proc *curr;
00105 u8 *ptr = skb->h.raw;
00106 int len = ((ptr[1]+1)<<3) - 2;
00107
00108 ptr += 2;
00109
00110 if (skb->tail - (ptr + len) < 0) {
00111 kfree_skb(skb);
00112 return 0;
00113 }
00114
00115 while (len > 0) {
00116 int optlen = ptr[1]+2;
00117
00118 switch (ptr[0]) {
00119 case IPV6_TLV_PAD0:
00120 optlen = 1;
00121 break;
00122
00123 case IPV6_TLV_PADN:
00124 break;
00125
00126 default:
00127 for (curr=procs; curr->type >= 0; curr++) {
00128 if (curr->type == ptr[0]) {
00129 if (curr->func(skb, ptr) == 0)
00130 return 0;
00131 break;
00132 }
00133 }
00134 if (curr->type < 0) {
00135 if (ip6_tlvopt_unknown(skb, ptr) == 0)
00136 return 0;
00137 }
00138 break;
00139 }
00140 ptr += optlen;
00141 len -= optlen;
00142 }
00143 if (len == 0)
00144 return 1;
00145 kfree_skb(skb);
00146 return 0;
00147 }
00148
00149
00150
00151
00152
00153 struct tlvtype_proc tlvprocdestopt_lst[] = {
00154
00155 {-1, NULL}
00156 };
00157
00158 static u8 *ipv6_dest_opt(struct sk_buff **skb_ptr, u8 *nhptr)
00159 {
00160 struct sk_buff *skb=*skb_ptr;
00161 struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb;
00162 struct ipv6_destopt_hdr *hdr = (struct ipv6_destopt_hdr *) skb->h.raw;
00163
00164 opt->dst1 = (u8*)hdr - skb->nh.raw;
00165
00166 if (ip6_parse_tlv(tlvprocdestopt_lst, skb, nhptr)) {
00167 skb->h.raw += ((hdr->hdrlen+1)<<3);
00168 return &hdr->nexthdr;
00169 }
00170
00171 return NULL;
00172 }
00173
00174
00175
00176
00177
00178 static u8 *ipv6_nodata(struct sk_buff **skb_ptr, u8 *nhptr)
00179 {
00180 kfree_skb(*skb_ptr);
00181 return NULL;
00182 }
00183
00184
00185
00186
00187
00188 static u8* ipv6_routing_header(struct sk_buff **skb_ptr, u8 *nhptr)
00189 {
00190 struct sk_buff *skb = *skb_ptr;
00191 struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb;
00192 struct in6_addr *addr;
00193 struct in6_addr daddr;
00194 int addr_type;
00195 int n, i;
00196
00197 struct ipv6_rt_hdr *hdr = (struct ipv6_rt_hdr *) skb->h.raw;
00198 struct rt0_hdr *rthdr;
00199
00200 if (((hdr->hdrlen+1)<<3) > skb->tail - skb->h.raw) {
00201 ipv6_statistics.Ip6InHdrErrors++;
00202 kfree_skb(skb);
00203 return NULL;
00204 }
00205
00206 looped_back:
00207 if (hdr->segments_left == 0) {
00208 opt->srcrt = (u8*)hdr - skb->nh.raw;
00209 skb->h.raw += (hdr->hdrlen + 1) << 3;
00210 opt->dst0 = opt->dst1;
00211 opt->dst1 = 0;
00212 return &hdr->nexthdr;
00213 }
00214
00215 if (hdr->type != IPV6_SRCRT_TYPE_0 || hdr->hdrlen & 0x01) {
00216 u8 *pos = (u8*) hdr;
00217
00218 if (hdr->type != IPV6_SRCRT_TYPE_0)
00219 pos += 2;
00220 else
00221 pos += 1;
00222
00223 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, pos);
00224 return NULL;
00225 }
00226
00227
00228
00229
00230
00231
00232 n = hdr->hdrlen >> 1;
00233
00234 if (hdr->segments_left > n) {
00235 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, &hdr->segments_left);
00236 return NULL;
00237 }
00238
00239
00240
00241
00242 if (skb_cloned(skb)) {
00243 struct sk_buff *skb2 = skb_copy(skb, GFP_ATOMIC);
00244 kfree_skb(skb);
00245 if (skb2 == NULL)
00246 return NULL;
00247 *skb_ptr = skb = skb2;
00248 opt = (struct inet6_skb_parm *)skb2->cb;
00249 hdr = (struct ipv6_rt_hdr *) skb2->h.raw;
00250 }
00251
00252 i = n - --hdr->segments_left;
00253
00254 rthdr = (struct rt0_hdr *) hdr;
00255 addr = rthdr->addr;
00256 addr += i - 1;
00257
00258 addr_type = ipv6_addr_type(addr);
00259
00260 if (addr_type == IPV6_ADDR_MULTICAST) {
00261 kfree_skb(skb);
00262 return NULL;
00263 }
00264
00265 ipv6_addr_copy(&daddr, addr);
00266 ipv6_addr_copy(addr, &skb->nh.ipv6h->daddr);
00267 ipv6_addr_copy(&skb->nh.ipv6h->daddr, &daddr);
00268
00269 dst_release(xchg(&skb->dst, NULL));
00270 ip6_route_input(skb);
00271 if (skb->dst->error) {
00272 skb->dst->input(skb);
00273 return NULL;
00274 }
00275 if (skb->dst->dev->flags&IFF_LOOPBACK) {
00276 if (skb->nh.ipv6h->hop_limit <= 1) {
00277 icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT,
00278 0, skb->dev);
00279 kfree_skb(skb);
00280 return NULL;
00281 }
00282 skb->nh.ipv6h->hop_limit--;
00283 goto looped_back;
00284 }
00285
00286 skb->dst->input(skb);
00287 return NULL;
00288 }
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311 struct ipv6_txoptions *
00312 ipv6_invert_rthdr(struct sock *sk, struct ipv6_rt_hdr *hdr)
00313 {
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323
00324
00325 int n, i;
00326 struct rt0_hdr *rthdr = (struct rt0_hdr*)hdr;
00327 struct rt0_hdr *irthdr;
00328 struct ipv6_txoptions *opt;
00329 int hdrlen = ipv6_optlen(hdr);
00330
00331 if (hdr->segments_left ||
00332 hdr->type != IPV6_SRCRT_TYPE_0 ||
00333 hdr->hdrlen & 0x01)
00334 return NULL;
00335
00336 n = hdr->hdrlen >> 1;
00337 opt = sock_kmalloc(sk, sizeof(*opt) + hdrlen, GFP_ATOMIC);
00338 if (opt == NULL)
00339 return NULL;
00340 memset(opt, 0, sizeof(*opt));
00341 opt->tot_len = sizeof(*opt) + hdrlen;
00342 opt->srcrt = (void*)(opt+1);
00343 opt->opt_nflen = hdrlen;
00344
00345 memcpy(opt->srcrt, hdr, sizeof(*hdr));
00346 irthdr = (struct rt0_hdr*)opt->srcrt;
00347
00348 irthdr->bitmap = 0;
00349 opt->srcrt->segments_left = n;
00350 for (i=0; i<n; i++)
00351 memcpy(irthdr->addr+i, rthdr->addr+(n-1-i), 16);
00352 return opt;
00353 }
00354
00355
00356
00357
00358
00359
00360
00361
00362
00363
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377 static u8 *ipv6_auth_hdr(struct sk_buff **skb_ptr, u8 *nhptr)
00378 {
00379 struct sk_buff *skb=*skb_ptr;
00380 struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb;
00381 struct ipv6_opt_hdr *hdr = (struct ipv6_opt_hdr *)skb->h.raw;
00382 int len = (hdr->hdrlen+2)<<2;
00383
00384 if (len&7)
00385 return NULL;
00386 opt->auth = (u8*)hdr - skb->nh.raw;
00387 if (skb->h.raw + len > skb->tail)
00388 return NULL;
00389 skb->h.raw += len;
00390 return &hdr->nexthdr;
00391 }
00392
00393
00394
00395
00396
00397
00398
00399 struct hdrtype_proc hdrproc_lst[] = {
00400 {NEXTHDR_FRAGMENT, ipv6_reassembly},
00401 {NEXTHDR_ROUTING, ipv6_routing_header},
00402 {NEXTHDR_DEST, ipv6_dest_opt},
00403 {NEXTHDR_NONE, ipv6_nodata},
00404 {NEXTHDR_AUTH, ipv6_auth_hdr},
00405
00406
00407
00408 {-1, NULL}
00409 };
00410
00411 u8 *ipv6_parse_exthdrs(struct sk_buff **skb_in, u8 *nhptr)
00412 {
00413 struct hdrtype_proc *hdrt;
00414 u8 nexthdr = *nhptr;
00415
00416 restart:
00417 for (hdrt=hdrproc_lst; hdrt->type >= 0; hdrt++) {
00418 if (hdrt->type == nexthdr) {
00419 if ((nhptr = hdrt->func(skb_in, nhptr)) != NULL) {
00420 nexthdr = *nhptr;
00421 goto restart;
00422 }
00423 return NULL;
00424 }
00425 }
00426 return nhptr;
00427 }
00428
00429
00430
00431
00432
00433
00434
00435
00436 static int ipv6_hop_ra(struct sk_buff *skb, u8 *ptr)
00437 {
00438 if (ptr[1] == 2) {
00439 ((struct inet6_skb_parm*)skb->cb)->ra = ptr - skb->nh.raw;
00440 return 1;
00441 }
00442 if (net_ratelimit())
00443 printk(KERN_DEBUG "ipv6_hop_ra: wrong RA length %d\n", ptr[1]);
00444 kfree_skb(skb);
00445 return 0;
00446 }
00447
00448
00449
00450 static int ipv6_hop_jumbo(struct sk_buff *skb, u8 *ptr)
00451 {
00452 u32 pkt_len;
00453
00454 if (ptr[1] != 4 || ((ptr-skb->nh.raw)&3) != 2) {
00455 if (net_ratelimit())
00456 printk(KERN_DEBUG "ipv6_hop_jumbo: wrong jumbo opt length/alignment %d\n", ptr[1]);
00457 goto drop;
00458 }
00459
00460 pkt_len = ntohl(*(u32*)(ptr+2));
00461 if (pkt_len < 0x10000) {
00462 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, ptr+2);
00463 return 0;
00464 }
00465 if (skb->nh.ipv6h->payload_len) {
00466 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, ptr);
00467 return 0;
00468 }
00469
00470 if (pkt_len > skb->len - sizeof(struct ipv6hdr)) {
00471 ipv6_statistics.Ip6InTruncatedPkts++;
00472 goto drop;
00473 }
00474 skb_trim(skb, pkt_len + sizeof(struct ipv6hdr));
00475 return 1;
00476
00477 drop:
00478 kfree_skb(skb);
00479 return 0;
00480 }
00481
00482 struct tlvtype_proc tlvprochopopt_lst[] = {
00483 {IPV6_TLV_ROUTERALERT, ipv6_hop_ra},
00484 {IPV6_TLV_JUMBO, ipv6_hop_jumbo},
00485 {-1, NULL}
00486 };
00487
00488 u8 * ipv6_parse_hopopts(struct sk_buff *skb, u8 *nhptr)
00489 {
00490 ((struct inet6_skb_parm*)skb->cb)->hop = sizeof(struct ipv6hdr);
00491 if (ip6_parse_tlv(tlvprochopopt_lst, skb, nhptr))
00492 return nhptr+((nhptr[1]+1)<<3);
00493 return NULL;
00494 }
00495
00496
00497
00498
00499
00500
00501
00502
00503
00504
00505
00506 u8 *ipv6_build_rthdr(struct sk_buff *skb, u8 *prev_hdr,
00507 struct ipv6_rt_hdr *opt, struct in6_addr *addr)
00508 {
00509 struct rt0_hdr *phdr, *ihdr;
00510 int hops;
00511
00512 ihdr = (struct rt0_hdr *) opt;
00513
00514 phdr = (struct rt0_hdr *) skb_put(skb, (ihdr->rt_hdr.hdrlen + 1) << 3);
00515 memcpy(phdr, ihdr, sizeof(struct rt0_hdr));
00516
00517 hops = ihdr->rt_hdr.hdrlen >> 1;
00518
00519 if (hops > 1)
00520 memcpy(phdr->addr, ihdr->addr + 1,
00521 (hops - 1) * sizeof(struct in6_addr));
00522
00523 ipv6_addr_copy(phdr->addr + (hops - 1), addr);
00524
00525 phdr->rt_hdr.nexthdr = *prev_hdr;
00526 *prev_hdr = NEXTHDR_ROUTING;
00527 return &phdr->rt_hdr.nexthdr;
00528 }
00529
00530 static u8 *ipv6_build_exthdr(struct sk_buff *skb, u8 *prev_hdr, u8 type, struct ipv6_opt_hdr *opt)
00531 {
00532 struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_put(skb, ipv6_optlen(opt));
00533
00534 memcpy(h, opt, ipv6_optlen(opt));
00535 h->nexthdr = *prev_hdr;
00536 *prev_hdr = type;
00537 return &h->nexthdr;
00538 }
00539
00540 static u8 *ipv6_build_authhdr(struct sk_buff *skb, u8 *prev_hdr, struct ipv6_opt_hdr *opt)
00541 {
00542 struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_put(skb, (opt->hdrlen+2)<<2);
00543
00544 memcpy(h, opt, (opt->hdrlen+2)<<2);
00545 h->nexthdr = *prev_hdr;
00546 *prev_hdr = NEXTHDR_AUTH;
00547 return &h->nexthdr;
00548 }
00549
00550
00551 u8 *ipv6_build_nfrag_opts(struct sk_buff *skb, u8 *prev_hdr, struct ipv6_txoptions *opt,
00552 struct in6_addr *daddr, u32 jumbolen)
00553 {
00554 struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb->data;
00555
00556 if (opt && opt->hopopt)
00557 prev_hdr = ipv6_build_exthdr(skb, prev_hdr, NEXTHDR_HOP, opt->hopopt);
00558
00559 if (jumbolen) {
00560 u8 *jumboopt = (u8 *)skb_put(skb, 8);
00561
00562 if (opt && opt->hopopt) {
00563 *jumboopt++ = IPV6_TLV_PADN;
00564 *jumboopt++ = 0;
00565 h->hdrlen++;
00566 } else {
00567 h = (struct ipv6_opt_hdr *)jumboopt;
00568 h->nexthdr = *prev_hdr;
00569 h->hdrlen = 0;
00570 jumboopt += 2;
00571 *prev_hdr = NEXTHDR_HOP;
00572 prev_hdr = &h->nexthdr;
00573 }
00574 jumboopt[0] = IPV6_TLV_JUMBO;
00575 jumboopt[1] = 4;
00576 *(u32*)(jumboopt+2) = htonl(jumbolen);
00577 }
00578 if (opt) {
00579 if (opt->dst0opt)
00580 prev_hdr = ipv6_build_exthdr(skb, prev_hdr, NEXTHDR_DEST, opt->dst0opt);
00581 if (opt->srcrt)
00582 prev_hdr = ipv6_build_rthdr(skb, prev_hdr, opt->srcrt, daddr);
00583 }
00584 return prev_hdr;
00585 }
00586
00587 u8 *ipv6_build_frag_opts(struct sk_buff *skb, u8 *prev_hdr, struct ipv6_txoptions *opt)
00588 {
00589 if (opt->auth)
00590 prev_hdr = ipv6_build_authhdr(skb, prev_hdr, opt->auth);
00591 if (opt->dst1opt)
00592 prev_hdr = ipv6_build_exthdr(skb, prev_hdr, NEXTHDR_DEST, opt->dst1opt);
00593 return prev_hdr;
00594 }
00595
00596 static void ipv6_push_rthdr(struct sk_buff *skb, u8 *proto,
00597 struct ipv6_rt_hdr *opt,
00598 struct in6_addr **addr_p)
00599 {
00600 struct rt0_hdr *phdr, *ihdr;
00601 int hops;
00602
00603 ihdr = (struct rt0_hdr *) opt;
00604
00605 phdr = (struct rt0_hdr *) skb_push(skb, (ihdr->rt_hdr.hdrlen + 1) << 3);
00606 memcpy(phdr, ihdr, sizeof(struct rt0_hdr));
00607
00608 hops = ihdr->rt_hdr.hdrlen >> 1;
00609
00610 if (hops > 1)
00611 memcpy(phdr->addr, ihdr->addr + 1,
00612 (hops - 1) * sizeof(struct in6_addr));
00613
00614 ipv6_addr_copy(phdr->addr + (hops - 1), *addr_p);
00615 *addr_p = ihdr->addr;
00616
00617 phdr->rt_hdr.nexthdr = *proto;
00618 *proto = NEXTHDR_ROUTING;
00619 }
00620
00621 static void ipv6_push_exthdr(struct sk_buff *skb, u8 *proto, u8 type, struct ipv6_opt_hdr *opt)
00622 {
00623 struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_push(skb, ipv6_optlen(opt));
00624
00625 memcpy(h, opt, ipv6_optlen(opt));
00626 h->nexthdr = *proto;
00627 *proto = type;
00628 }
00629
00630 static void ipv6_push_authhdr(struct sk_buff *skb, u8 *proto, struct ipv6_opt_hdr *opt)
00631 {
00632 struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_push(skb, (opt->hdrlen+2)<<2);
00633
00634 memcpy(h, opt, (opt->hdrlen+2)<<2);
00635 h->nexthdr = *proto;
00636 *proto = NEXTHDR_AUTH;
00637 }
00638
00639 void ipv6_push_nfrag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt,
00640 u8 *proto,
00641 struct in6_addr **daddr)
00642 {
00643 if (opt->srcrt)
00644 ipv6_push_rthdr(skb, proto, opt->srcrt, daddr);
00645 if (opt->dst0opt)
00646 ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst0opt);
00647 if (opt->hopopt)
00648 ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt);
00649 }
00650
00651 void ipv6_push_frag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, u8 *proto)
00652 {
00653 if (opt->dst1opt)
00654 ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst1opt);
00655 if (opt->auth)
00656 ipv6_push_authhdr(skb, proto, opt->auth);
00657 }
00658
00659 struct ipv6_txoptions *
00660 ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt)
00661 {
00662 struct ipv6_txoptions *opt2;
00663
00664 opt2 = sock_kmalloc(sk, opt->tot_len, GFP_ATOMIC);
00665 if (opt2) {
00666 long dif = (char*)opt2 - (char*)opt;
00667 memcpy(opt2, opt, opt->tot_len);
00668 if (opt2->hopopt)
00669 *((char**)&opt2->hopopt) += dif;
00670 if (opt2->dst0opt)
00671 *((char**)&opt2->dst0opt) += dif;
00672 if (opt2->dst1opt)
00673 *((char**)&opt2->dst1opt) += dif;
00674 if (opt2->auth)
00675 *((char**)&opt2->auth) += dif;
00676 if (opt2->srcrt)
00677 *((char**)&opt2->srcrt) += dif;
00678 }
00679 return opt2;
00680 }
00681
00682
00683
00684
00685
00686
00687 static __inline__ int ipv6_ext_hdr(u8 nexthdr)
00688 {
00689
00690
00691
00692 return ( (nexthdr == NEXTHDR_HOP) ||
00693 (nexthdr == NEXTHDR_ROUTING) ||
00694 (nexthdr == NEXTHDR_FRAGMENT) ||
00695 (nexthdr == NEXTHDR_AUTH) ||
00696 (nexthdr == NEXTHDR_NONE) ||
00697 (nexthdr == NEXTHDR_DEST) );
00698 }
00699
00700
00701
00702
00703
00704
00705
00706
00707
00708
00709
00710
00711
00712
00713
00714
00715
00716
00717
00718
00719
00720
00721
00722
00723
00724
00725
00726
00727
00728
00729
00730
00731
00732
00733
00734
00735
00736
00737
00738
00739
00740
00741
00742 u8 *ipv6_skip_exthdr(struct ipv6_opt_hdr *hdr, u8 *nexthdrp, int len)
00743 {
00744 u8 nexthdr = *nexthdrp;
00745
00746 while (ipv6_ext_hdr(nexthdr)) {
00747 int hdrlen;
00748
00749 if (len < sizeof(struct ipv6_opt_hdr))
00750 return NULL;
00751 if (nexthdr == NEXTHDR_NONE)
00752 return NULL;
00753 if (nexthdr == NEXTHDR_FRAGMENT) {
00754 struct frag_hdr *fhdr = (struct frag_hdr *) hdr;
00755 if (ntohs(fhdr->frag_off) & ~0x7)
00756 break;
00757 hdrlen = 8;
00758 } else if (nexthdr == NEXTHDR_AUTH)
00759 hdrlen = (hdr->hdrlen+2)<<2;
00760 else
00761 hdrlen = ipv6_optlen(hdr);
00762
00763 nexthdr = hdr->nexthdr;
00764 hdr = (struct ipv6_opt_hdr *) ((u8*)hdr + hdrlen);
00765 len -= hdrlen;
00766 }
00767
00768 *nexthdrp = nexthdr;
00769 return (u8*)hdr;
00770 }
00771