📄 exthdrs.c
字号:
len = (skb->h.raw[1]+1)<<2; if (len&7) goto fail; if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+len)) goto fail; opt->auth = skb->h.raw - skb->nh.raw; skb->h.raw += len; return opt->auth;fail: kfree_skb(skb); return -1;}/* This list MUST NOT contain entry for NEXTHDR_HOP. It is parsed immediately after packet received and if it occurs somewhere in another place we must generate error. */struct hdrtype_proc hdrproc_lst[] = { {NEXTHDR_FRAGMENT, ipv6_reassembly}, {NEXTHDR_ROUTING, ipv6_routing_header}, {NEXTHDR_DEST, ipv6_dest_opt}, {NEXTHDR_NONE, ipv6_nodata}, {NEXTHDR_AUTH, ipv6_auth_hdr}, /* {NEXTHDR_ESP, ipv6_esp_hdr}, */ {-1, NULL}};int ipv6_parse_exthdrs(struct sk_buff **skb_in, int nhoff){ struct hdrtype_proc *hdrt; u8 nexthdr = (*skb_in)->nh.raw[nhoff];restart: for (hdrt=hdrproc_lst; hdrt->type >= 0; hdrt++) { if (hdrt->type == nexthdr) { if ((nhoff = hdrt->func(skb_in, nhoff)) >= 0) { nexthdr = (*skb_in)->nh.raw[nhoff]; goto restart; } return -1; } } return nhoff;}/********************************** Hop-by-hop options. **********************************//* Router Alert as of draft-ietf-ipngwg-ipv6router-alert-04 */static int ipv6_hop_ra(struct sk_buff *skb, int optoff){ if (skb->nh.raw[optoff+1] == 2) { ((struct inet6_skb_parm*)skb->cb)->ra = optoff; return 1; } if (net_ratelimit()) printk(KERN_DEBUG "ipv6_hop_ra: wrong RA length %d\n", skb->nh.raw[optoff+1]); kfree_skb(skb); return 0;}/* Jumbo payload */static int ipv6_hop_jumbo(struct sk_buff *skb, int optoff){ u32 pkt_len; if (skb->nh.raw[optoff+1] != 4 || (optoff&3) != 2) { if (net_ratelimit()) printk(KERN_DEBUG "ipv6_hop_jumbo: wrong jumbo opt length/alignment %d\n", skb->nh.raw[optoff+1]); goto drop; } pkt_len = ntohl(*(u32*)(skb->nh.raw+optoff+2)); if (pkt_len < 0x10000) { icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff+2); return 0; } if (skb->nh.ipv6h->payload_len) { icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff); return 0; } if (pkt_len > skb->len - sizeof(struct ipv6hdr)) { IP6_INC_STATS_BH(Ip6InTruncatedPkts); goto drop; } if (pkt_len + sizeof(struct ipv6hdr) < skb->len) { __pskb_trim(skb, pkt_len + sizeof(struct ipv6hdr)); if (skb->ip_summed == CHECKSUM_HW) skb->ip_summed = CHECKSUM_NONE; } return 1;drop: kfree_skb(skb); return 0;}struct tlvtype_proc tlvprochopopt_lst[] = { {IPV6_TLV_ROUTERALERT, ipv6_hop_ra}, {IPV6_TLV_JUMBO, ipv6_hop_jumbo}, {-1, NULL}};int ipv6_parse_hopopts(struct sk_buff *skb, int nhoff){ ((struct inet6_skb_parm*)skb->cb)->hop = sizeof(struct ipv6hdr); if (ip6_parse_tlv(tlvprochopopt_lst, skb)) return sizeof(struct ipv6hdr); return -1;}/* * Creating outbound headers. * * "build" functions work when skb is filled from head to tail (datagram) * "push" functions work when headers are added from tail to head (tcp) * * In both cases we assume, that caller reserved enough room * for headers. */u8 *ipv6_build_rthdr(struct sk_buff *skb, u8 *prev_hdr, struct ipv6_rt_hdr *opt, struct in6_addr *addr){ struct rt0_hdr *phdr, *ihdr; int hops; ihdr = (struct rt0_hdr *) opt; phdr = (struct rt0_hdr *) skb_put(skb, (ihdr->rt_hdr.hdrlen + 1) << 3); memcpy(phdr, ihdr, sizeof(struct rt0_hdr)); hops = ihdr->rt_hdr.hdrlen >> 1; if (hops > 1) memcpy(phdr->addr, ihdr->addr + 1, (hops - 1) * sizeof(struct in6_addr)); ipv6_addr_copy(phdr->addr + (hops - 1), addr); phdr->rt_hdr.nexthdr = *prev_hdr; *prev_hdr = NEXTHDR_ROUTING; return &phdr->rt_hdr.nexthdr;}static u8 *ipv6_build_exthdr(struct sk_buff *skb, u8 *prev_hdr, u8 type, struct ipv6_opt_hdr *opt){ struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_put(skb, ipv6_optlen(opt)); memcpy(h, opt, ipv6_optlen(opt)); h->nexthdr = *prev_hdr; *prev_hdr = type; return &h->nexthdr;}static u8 *ipv6_build_authhdr(struct sk_buff *skb, u8 *prev_hdr, struct ipv6_opt_hdr *opt){ struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_put(skb, (opt->hdrlen+2)<<2); memcpy(h, opt, (opt->hdrlen+2)<<2); h->nexthdr = *prev_hdr; *prev_hdr = NEXTHDR_AUTH; return &h->nexthdr;}u8 *ipv6_build_nfrag_opts(struct sk_buff *skb, u8 *prev_hdr, struct ipv6_txoptions *opt, struct in6_addr *daddr, u32 jumbolen){ struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb->data; if (opt && opt->hopopt) prev_hdr = ipv6_build_exthdr(skb, prev_hdr, NEXTHDR_HOP, opt->hopopt); if (jumbolen) { u8 *jumboopt = (u8 *)skb_put(skb, 8); if (opt && opt->hopopt) { *jumboopt++ = IPV6_TLV_PADN; *jumboopt++ = 0; h->hdrlen++; } else { h = (struct ipv6_opt_hdr *)jumboopt; h->nexthdr = *prev_hdr; h->hdrlen = 0; jumboopt += 2; *prev_hdr = NEXTHDR_HOP; prev_hdr = &h->nexthdr; } jumboopt[0] = IPV6_TLV_JUMBO; jumboopt[1] = 4; *(u32*)(jumboopt+2) = htonl(jumbolen); } if (opt) { if (opt->dst0opt) prev_hdr = ipv6_build_exthdr(skb, prev_hdr, NEXTHDR_DEST, opt->dst0opt); if (opt->srcrt) prev_hdr = ipv6_build_rthdr(skb, prev_hdr, opt->srcrt, daddr); } return prev_hdr;}u8 *ipv6_build_frag_opts(struct sk_buff *skb, u8 *prev_hdr, struct ipv6_txoptions *opt){ if (opt->auth) prev_hdr = ipv6_build_authhdr(skb, prev_hdr, opt->auth); if (opt->dst1opt) prev_hdr = ipv6_build_exthdr(skb, prev_hdr, NEXTHDR_DEST, opt->dst1opt); return prev_hdr;}static void ipv6_push_rthdr(struct sk_buff *skb, u8 *proto, struct ipv6_rt_hdr *opt, struct in6_addr **addr_p){ struct rt0_hdr *phdr, *ihdr; int hops; ihdr = (struct rt0_hdr *) opt; phdr = (struct rt0_hdr *) skb_push(skb, (ihdr->rt_hdr.hdrlen + 1) << 3); memcpy(phdr, ihdr, sizeof(struct rt0_hdr)); hops = ihdr->rt_hdr.hdrlen >> 1; if (hops > 1) memcpy(phdr->addr, ihdr->addr + 1, (hops - 1) * sizeof(struct in6_addr)); ipv6_addr_copy(phdr->addr + (hops - 1), *addr_p); *addr_p = ihdr->addr; phdr->rt_hdr.nexthdr = *proto; *proto = NEXTHDR_ROUTING;}static void ipv6_push_exthdr(struct sk_buff *skb, u8 *proto, u8 type, struct ipv6_opt_hdr *opt){ struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_push(skb, ipv6_optlen(opt)); memcpy(h, opt, ipv6_optlen(opt)); h->nexthdr = *proto; *proto = type;}static void ipv6_push_authhdr(struct sk_buff *skb, u8 *proto, struct ipv6_opt_hdr *opt){ struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_push(skb, (opt->hdrlen+2)<<2); memcpy(h, opt, (opt->hdrlen+2)<<2); h->nexthdr = *proto; *proto = NEXTHDR_AUTH;}void ipv6_push_nfrag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, u8 *proto, struct in6_addr **daddr){ if (opt->srcrt) ipv6_push_rthdr(skb, proto, opt->srcrt, daddr); if (opt->dst0opt) ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst0opt); if (opt->hopopt) ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt);}void ipv6_push_frag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, u8 *proto){ if (opt->dst1opt) ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst1opt); if (opt->auth) ipv6_push_authhdr(skb, proto, opt->auth);}struct ipv6_txoptions *ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt){ struct ipv6_txoptions *opt2; opt2 = sock_kmalloc(sk, opt->tot_len, GFP_ATOMIC); if (opt2) { long dif = (char*)opt2 - (char*)opt; memcpy(opt2, opt, opt->tot_len); if (opt2->hopopt) *((char**)&opt2->hopopt) += dif; if (opt2->dst0opt) *((char**)&opt2->dst0opt) += dif; if (opt2->dst1opt) *((char**)&opt2->dst1opt) += dif; if (opt2->auth) *((char**)&opt2->auth) += dif; if (opt2->srcrt) *((char**)&opt2->srcrt) += dif; } return opt2;}/* * find out if nexthdr is a well-known extension header or a protocol */int ipv6_ext_hdr(u8 nexthdr){ /* * find out if nexthdr is an extension header or a protocol */ return ( (nexthdr == NEXTHDR_HOP) || (nexthdr == NEXTHDR_ROUTING) || (nexthdr == NEXTHDR_FRAGMENT) || (nexthdr == NEXTHDR_AUTH) || (nexthdr == NEXTHDR_NONE) || (nexthdr == NEXTHDR_DEST) );}/* * Skip any extension headers. This is used by the ICMP module. * * Note that strictly speaking this conflicts with RFC1883 4.0: * ...The contents and semantics of each extension header determine whether * or not to proceed to the next header. Therefore, extension headers must * be processed strictly in the order they appear in the packet; a * receiver must not, for example, scan through a packet looking for a * particular kind of extension header and process that header prior to * processing all preceding ones. * * We do exactly this. This is a protocol bug. We can't decide after a * seeing an unknown discard-with-error flavour TLV option if it's a * ICMP error message or not (errors should never be send in reply to * ICMP error messages). * * But I see no other way to do this. This might need to be reexamined * when Linux implements ESP (and maybe AUTH) headers. * --AK * * This function parses (probably truncated) exthdr set "hdr" * of length "len". "nexthdrp" initially points to some place, * where type of the first header can be found. * * It skips all well-known exthdrs, and returns pointer to the start * of unparsable area i.e. the first header with unknown type. * If it is not NULL *nexthdr is updated by type/protocol of this header. * * NOTES: - if packet terminated with NEXTHDR_NONE it returns NULL. * - it may return pointer pointing beyond end of packet, * if the last recognized header is truncated in the middle. * - if packet is truncated, so that all parsed headers are skipped, * it returns NULL. * - First fragment header is skipped, not-first ones * are considered as unparsable. * - ESP is unparsable for now and considered like * normal payload protocol. * - Note also special handling of AUTH header. Thanks to IPsec wizards. * * --ANK (980726) */int ipv6_skip_exthdr(struct sk_buff *skb, int start, u8 *nexthdrp, int len){ u8 nexthdr = *nexthdrp; while (ipv6_ext_hdr(nexthdr)) { struct ipv6_opt_hdr hdr; int hdrlen; if (len < (int)sizeof(struct ipv6_opt_hdr)) return -1; if (nexthdr == NEXTHDR_NONE) return -1; if (skb_copy_bits(skb, start, &hdr, sizeof(hdr))) BUG(); if (nexthdr == NEXTHDR_FRAGMENT) { struct frag_hdr *fhdr = (struct frag_hdr *) &hdr; if (ntohs(fhdr->frag_off) & ~0x7) break; hdrlen = 8; } else if (nexthdr == NEXTHDR_AUTH) hdrlen = (hdr.hdrlen+2)<<2; else hdrlen = ipv6_optlen(&hdr); nexthdr = hdr.nexthdr; len -= hdrlen; start += hdrlen; } *nexthdrp = nexthdr; return start;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -