int ipv6_skip_exthdr(const struct sk_buff *skb, int start, u8 *nexthdrp) { u8 nexthdr = *nexthdrp; while (ipv6_ext_hdr(nexthdr)) { struct ipv6_opt_hdr _hdr, *hp; int hdrlen; if (nexthdr == NEXTHDR_NONE) return -1; hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr); if (hp == NULL) return -1; if (nexthdr == NEXTHDR_FRAGMENT) { unsigned short _frag_off, *fp; fp = skb_header_pointer(skb, start+offsetof(struct frag_hdr, frag_off), sizeof(_frag_off), &_frag_off); if (fp == NULL) return -1; if (ntohs(*fp) & ~0x7) break; hdrlen = 8; } else if (nexthdr == NEXTHDR_AUTH)
int nf_ct_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) break; if (nexthdr == NEXTHDR_FRAGMENT) break; if (skb_copy_bits(skb, start, &hdr, sizeof(hdr))) BUG(); 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; }
int qta_ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, int target, unsigned short *fragoff) { unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr); u8 nexthdr = ipv6_hdr(skb)->nexthdr; unsigned int len = skb->len - start; if (fragoff) *fragoff = 0; while (nexthdr != target) { struct ipv6_opt_hdr _hdr, *hp; unsigned int hdrlen; if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) { if (target < 0) break; return -ENOENT; } hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr); if (hp == NULL) return -EBADMSG; if (nexthdr == NEXTHDR_FRAGMENT) { unsigned short _frag_off; __be16 *fp; fp = skb_header_pointer(skb, start+offsetof(struct frag_hdr, frag_off), sizeof(_frag_off), &_frag_off); if (fp == NULL) return -EBADMSG; _frag_off = ntohs(*fp) & ~0x7; if (_frag_off) { if (target < 0 && ((!ipv6_ext_hdr(hp->nexthdr)) || hp->nexthdr == NEXTHDR_NONE)) { if (fragoff) *fragoff = _frag_off; return hp->nexthdr; } return -ENOENT; } hdrlen = 8; } else if (nexthdr == NEXTHDR_AUTH)
static int ip6_forward_proxy_check(struct sk_buff *skb) { struct ipv6hdr *hdr = ipv6_hdr(skb); u8 nexthdr = hdr->nexthdr; __be16 frag_off; int offset; if (ipv6_ext_hdr(nexthdr)) { offset = ipv6_skip_exthdr(skb, sizeof(*hdr), &nexthdr, &frag_off); if (offset < 0) return 0; } else offset = sizeof(struct ipv6hdr); if (nexthdr == IPPROTO_ICMPV6) { struct icmp6hdr *icmp6; if (!pskb_may_pull(skb, (skb_network_header(skb) + offset + 1 - skb->data))) return 0; icmp6 = (struct icmp6hdr *)(skb_network_header(skb) + offset); switch (icmp6->icmp6_type) { case NDISC_ROUTER_SOLICITATION: case NDISC_ROUTER_ADVERTISEMENT: case NDISC_NEIGHBOUR_SOLICITATION: case NDISC_NEIGHBOUR_ADVERTISEMENT: case NDISC_REDIRECT: /* For reaction involving unicast neighbor discovery * message destined to the proxied address, pass it to * input function. */ return 1; default: break; } } /* * The proxying router can't forward traffic sent to a link-local * address, so signal the sender and discard the packet. This * behavior is clarified by the MIPv6 specification. */ if (ipv6_addr_type(&hdr->daddr) & IPV6_ADDR_LINKLOCAL) { dst_link_failure(skb); return -1; } return 0; }
static __u16 parse_tlv_tnl_enc_lim(struct sk_buff *skb, __u8 * raw) { struct ipv6hdr *ipv6h = (struct ipv6hdr *) raw; __u8 nexthdr = ipv6h->nexthdr; __u16 off = sizeof (*ipv6h); while (ipv6_ext_hdr(nexthdr) && nexthdr != NEXTHDR_NONE) { __u16 optlen = 0; struct ipv6_opt_hdr *hdr; if (raw + off + sizeof (*hdr) > skb->data && !pskb_may_pull(skb, raw - skb->data + off + sizeof (*hdr))) break; hdr = (struct ipv6_opt_hdr *) (raw + off); if (nexthdr == NEXTHDR_FRAGMENT) { struct frag_hdr *frag_hdr = (struct frag_hdr *) hdr; if (frag_hdr->frag_off) break; optlen = 8; } else if (nexthdr == NEXTHDR_AUTH) { optlen = (hdr->hdrlen + 2) << 2; } else { optlen = ipv6_optlen(hdr); } if (nexthdr == NEXTHDR_DEST) { __u16 i = off + 2; while (1) { struct ipv6_tlv_tnl_enc_lim *tel; /* No more room for encapsulation limit */ if (i + sizeof (*tel) > off + optlen) break; tel = (struct ipv6_tlv_tnl_enc_lim *) &raw[i]; /* return index of option if found and valid */ if (tel->type == IPV6_TLV_TNL_ENCAP_LIMIT && tel->length == 1) return i; /* else jump to next option */ if (tel->type) i += tel->length + 2; else i++; } } nexthdr = hdr->nexthdr; off += optlen; } return 0; }
/** * ixgbe_ipsec_offload_ok - can this packet use the xfrm hw offload * @skb: current data packet * @xs: pointer to transformer state struct **/ static bool ixgbe_ipsec_offload_ok(struct sk_buff *skb, struct xfrm_state *xs) { if (xs->props.family == AF_INET) { /* Offload with IPv4 options is not supported yet */ if (ip_hdr(skb)->ihl != 5) return false; } else { /* Offload with IPv6 extension headers is not support yet */ if (ipv6_ext_hdr(ipv6_hdr(skb)->nexthdr)) return false; } return true; }
/* * find the header just before Fragment Header. * * if success return 0 and set ... * (*prevhdrp): the value of "Next Header Field" in the header * just before Fragment Header. * (*prevhoff): the offset of "Next Header Field" in the header * just before Fragment Header. * (*fhoff) : the offset of Fragment Header. * * Based on ipv6_skip_hdr() in net/ipv6/exthdr.c * */ static int find_prev_fhdr(struct sk_buff *skb, u8 *prevhdrp, int *prevhoff, int *fhoff) { u8 nexthdr = skb->nh.ipv6h->nexthdr; u8 prev_nhoff = (u8 *)&skb->nh.ipv6h->nexthdr - skb->data; int start = (u8 *)(skb->nh.ipv6h+1) - skb->data; int len = skb->len - start; u8 prevhdr = NEXTHDR_IPV6; while (nexthdr != NEXTHDR_FRAGMENT) { struct ipv6_opt_hdr hdr; int hdrlen; if (!ipv6_ext_hdr(nexthdr)) { return -1; } if (len < (int)sizeof(struct ipv6_opt_hdr)) { DEBUGP("too short\n"); return -1; } if (nexthdr == NEXTHDR_NONE) { DEBUGP("next header is none\n"); return -1; } if (skb_copy_bits(skb, start, &hdr, sizeof(hdr))) BUG(); if (nexthdr == NEXTHDR_AUTH) hdrlen = (hdr.hdrlen+2)<<2; else hdrlen = ipv6_optlen(&hdr); prevhdr = nexthdr; prev_nhoff = start; nexthdr = hdr.nexthdr; len -= hdrlen; start += hdrlen; } if (len < 0) return -1; *prevhdrp = prevhdr; *prevhoff = prev_nhoff; *fhoff = start; return 0; }
int ip6_mc_input(struct sk_buff *skb) { struct ipv6hdr *hdr; int deliver; IP6_INC_STATS_BH(dev_net(skb->dst->dev), ip6_dst_idev(skb->dst), IPSTATS_MIB_INMCASTPKTS); hdr = ipv6_hdr(skb); deliver = ipv6_chk_mcast_addr(skb->dev, &hdr->daddr, NULL); /* <DTS2012071401081 w00211169 2012-7-14 begin */ #if 1 if(!deliver && (skb->dev->flags & IFF_MULTICAST) && (IPPROTO_ICMPV6 == hdr->nexthdr) && !strcmp(skb->dev->name, "rmnet0")) { if (NDISC_NEIGHBOUR_SOLICITATION == icmp6_hdr(skb)->icmp6_type) { ip6_input_icmp_cheat(skb); return 0; } } #endif /* DTS2012071401081 w00211169 2012-7-14 end> */ #ifdef CONFIG_IPV6_MROUTE /* * IPv6 multicast router mode is now supported ;) */ if (dev_net(skb->dev)->ipv6.devconf_all->mc_forwarding && !(ipv6_addr_type(&hdr->daddr) & IPV6_ADDR_LINKLOCAL) && likely(!(IP6CB(skb)->flags & IP6SKB_FORWARDED))) { /* * Okay, we try to forward - split and duplicate * packets. */ struct sk_buff *skb2; struct inet6_skb_parm *opt = IP6CB(skb); /* Check for MLD */ if (unlikely(opt->ra)) { /* Check if this is a mld message */ u8 *ptr = skb_network_header(skb) + opt->ra; struct icmp6hdr *icmp6; u8 nexthdr = hdr->nexthdr; int offset; /* Check if the value of Router Alert * is for MLD (0x0000). */ if ((ptr[2] | ptr[3]) == 0) { deliver = 0; if (!ipv6_ext_hdr(nexthdr)) { /* BUG */ goto out; } offset = ipv6_skip_exthdr(skb, sizeof(*hdr), &nexthdr); if (offset < 0) goto out; if (nexthdr != IPPROTO_ICMPV6) goto out; if (!pskb_may_pull(skb, (skb_network_header(skb) + offset + 1 - skb->data))) goto out; icmp6 = (struct icmp6hdr *)(skb_network_header(skb) + offset); switch (icmp6->icmp6_type) { case ICMPV6_MGM_QUERY: case ICMPV6_MGM_REPORT: case ICMPV6_MGM_REDUCTION: case ICMPV6_MLD2_REPORT: deliver = 1; break; } goto out; } /* unknown RA - process it normally */ } if (deliver) skb2 = skb_clone(skb, GFP_ATOMIC); else { skb2 = skb; skb = NULL; } if (skb2) { ip6_mr_input(skb2); } } out: #endif if (likely(deliver)) ip6_input(skb); else { /* discard */ kfree_skb(skb); } return 0; }
int ip6_mc_input(struct sk_buff *skb) { const struct ipv6hdr *hdr; bool deliver; __IP6_UPD_PO_STATS(dev_net(skb_dst(skb)->dev), ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_INMCAST, skb->len); hdr = ipv6_hdr(skb); deliver = ipv6_chk_mcast_addr(skb->dev, &hdr->daddr, NULL); #ifdef CONFIG_IPV6_MROUTE /* * IPv6 multicast router mode is now supported ;) */ if (dev_net(skb->dev)->ipv6.devconf_all->mc_forwarding && !(ipv6_addr_type(&hdr->daddr) & (IPV6_ADDR_LOOPBACK|IPV6_ADDR_LINKLOCAL)) && likely(!(IP6CB(skb)->flags & IP6SKB_FORWARDED))) { /* * Okay, we try to forward - split and duplicate * packets. */ struct sk_buff *skb2; struct inet6_skb_parm *opt = IP6CB(skb); /* Check for MLD */ if (unlikely(opt->flags & IP6SKB_ROUTERALERT)) { /* Check if this is a mld message */ u8 nexthdr = hdr->nexthdr; __be16 frag_off; int offset; /* Check if the value of Router Alert * is for MLD (0x0000). */ if (opt->ra == htons(IPV6_OPT_ROUTERALERT_MLD)) { deliver = false; if (!ipv6_ext_hdr(nexthdr)) { /* BUG */ goto out; } offset = ipv6_skip_exthdr(skb, sizeof(*hdr), &nexthdr, &frag_off); if (offset < 0) goto out; if (ipv6_is_mld(skb, nexthdr, offset)) deliver = true; goto out; } /* unknown RA - process it normally */ } if (deliver) skb2 = skb_clone(skb, GFP_ATOMIC); else { skb2 = skb; skb = NULL; } if (skb2) { ip6_mr_input(skb2); } } out: #endif if (likely(deliver)) ip6_input(skb); else { /* discard */ kfree_skb(skb); } return 0; }
int ip6_mc_input(struct sk_buff *skb) { const struct ipv6hdr *hdr; int deliver; IP6_UPD_PO_STATS_BH(dev_net(skb_dst(skb)->dev), ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_INMCAST, skb->len); hdr = ipv6_hdr(skb); deliver = ipv6_chk_mcast_addr(skb->dev, &hdr->daddr, NULL); #ifdef CONFIG_IPV6_MROUTE /* * IPv6 multicast router mode is now supported ;) */ if (dev_net(skb->dev)->ipv6.devconf_all->mc_forwarding && !(ipv6_addr_type(&hdr->daddr) & (IPV6_ADDR_LOOPBACK|IPV6_ADDR_LINKLOCAL)) && likely(!(IP6CB(skb)->flags & IP6SKB_FORWARDED))) { /* * Okay, we try to forward - split and duplicate * packets. */ struct sk_buff *skb2; struct inet6_skb_parm *opt = IP6CB(skb); /* Check for MLD */ if (unlikely(opt->ra)) { /* Check if this is a mld message */ u8 *ptr = skb_network_header(skb) + opt->ra; struct icmp6hdr *icmp6; u8 nexthdr = hdr->nexthdr; __be16 frag_off; int offset; /* Check if the value of Router Alert * is for MLD (0x0000). */ if ((ptr[2] | ptr[3]) == 0) { deliver = 0; if (!ipv6_ext_hdr(nexthdr)) { /* BUG */ goto out; } offset = ipv6_skip_exthdr(skb, sizeof(*hdr), &nexthdr, &frag_off); if (offset < 0) goto out; if (nexthdr != IPPROTO_ICMPV6) goto out; if (!pskb_may_pull(skb, (skb_network_header(skb) + offset + 1 - skb->data))) goto out; icmp6 = (struct icmp6hdr *)(skb_network_header(skb) + offset); switch (icmp6->icmp6_type) { case ICMPV6_MGM_QUERY: case ICMPV6_MGM_REPORT: case ICMPV6_MGM_REDUCTION: case ICMPV6_MLD2_REPORT: deliver = 1; break; } goto out; } } if (deliver) skb2 = skb_clone(skb, GFP_ATOMIC); else { skb2 = skb; skb = NULL; } if (skb2) { ip6_mr_input(skb2); } } out: #endif if (likely(deliver)) ip6_input(skb); else { kfree_skb(skb); } return 0; }
static int match(const struct sk_buff *skb, const struct net_device *in, const struct net_device *out, const void *matchinfo, int offset, const void *protohdr, u_int16_t datalen, int *hotdrop) { struct frag_hdr *frag = NULL; const struct ip6t_frag *fraginfo = matchinfo; unsigned int temp; int len; u8 nexthdr; unsigned int ptr; unsigned int hdrlen = 0; /* type of the 1st exthdr */ nexthdr = skb->nh.ipv6h->nexthdr; /* pointer to the 1st exthdr */ ptr = sizeof(struct ipv6hdr); /* available length */ len = skb->len - ptr; temp = 0; while (ipv6_ext_hdr(nexthdr)) { struct ipv6_opt_hdr *hdr; DEBUGP("ipv6_frag header iteration \n"); /* Is there enough space for the next ext header? */ if (len < (int)sizeof(struct ipv6_opt_hdr)) return 0; /* No more exthdr -> evaluate */ if (nexthdr == NEXTHDR_NONE) { break; } /* ESP -> evaluate */ if (nexthdr == NEXTHDR_ESP) { break; } hdr=(struct ipv6_opt_hdr *)(skb->data+ptr); /* Calculate the header length */ if (nexthdr == NEXTHDR_FRAGMENT) { hdrlen = 8; } else if (nexthdr == NEXTHDR_AUTH) hdrlen = (hdr->hdrlen+2)<<2; else hdrlen = ipv6_optlen(hdr); /* FRAG -> evaluate */ if (nexthdr == NEXTHDR_FRAGMENT) { temp |= MASK_FRAGMENT; break; } /* set the flag */ switch (nexthdr){ case NEXTHDR_HOP: case NEXTHDR_ROUTING: case NEXTHDR_FRAGMENT: case NEXTHDR_AUTH: case NEXTHDR_DEST: break; default: DEBUGP("ipv6_frag match: unknown nextheader %u\n",nexthdr); return 0; break; } nexthdr = hdr->nexthdr; len -= hdrlen; ptr += hdrlen; if ( ptr > skb->len ) { DEBUGP("ipv6_frag: new pointer too large! \n"); break; } } /* FRAG header not found */ if ( temp != MASK_FRAGMENT ) return 0; if (len < sizeof(struct frag_hdr)){ *hotdrop = 1; return 0; } frag = (struct frag_hdr *) (skb->data + ptr); DEBUGP("INFO %04X ", frag->frag_off); DEBUGP("OFFSET %04X ", ntohs(frag->frag_off) & ~0x7); DEBUGP("RES %02X %04X", frag->reserved, ntohs(frag->frag_off) & 0x6); DEBUGP("MF %04X ", frag->frag_off & htons(IP6_MF)); DEBUGP("ID %u %08X\n", ntohl(frag->identification), ntohl(frag->identification)); DEBUGP("IPv6 FRAG id %02X ", (id_match(fraginfo->ids[0], fraginfo->ids[1], ntohl(frag->identification), !!(fraginfo->invflags & IP6T_FRAG_INV_IDS)))); DEBUGP("res %02X %02X%04X %02X ", (fraginfo->flags & IP6T_FRAG_RES), frag->reserved, ntohs(frag->frag_off) & 0x6, !((fraginfo->flags & IP6T_FRAG_RES) && (frag->reserved || (ntohs(frag->frag_off) & 0x6)))); DEBUGP("first %02X %02X %02X ", (fraginfo->flags & IP6T_FRAG_FST), ntohs(frag->frag_off) & ~0x7, !((fraginfo->flags & IP6T_FRAG_FST) && (ntohs(frag->frag_off) & ~0x7))); DEBUGP("mf %02X %02X %02X ", (fraginfo->flags & IP6T_FRAG_MF), ntohs(frag->frag_off) & IP6_MF, !((fraginfo->flags & IP6T_FRAG_MF) && !((ntohs(frag->frag_off) & IP6_MF)))); DEBUGP("last %02X %02X %02X\n", (fraginfo->flags & IP6T_FRAG_NMF), ntohs(frag->frag_off) & IP6_MF, !((fraginfo->flags & IP6T_FRAG_NMF) && (ntohs(frag->frag_off) & IP6_MF))); return (frag != NULL) && (id_match(fraginfo->ids[0], fraginfo->ids[1], ntohl(frag->identification), !!(fraginfo->invflags & IP6T_FRAG_INV_IDS))) && !((fraginfo->flags & IP6T_FRAG_RES) && (frag->reserved || (ntohs(frag->frag_off) & 0x6))) && !((fraginfo->flags & IP6T_FRAG_FST) && (ntohs(frag->frag_off) & ~0x7)) && !((fraginfo->flags & IP6T_FRAG_MF) && !(ntohs(frag->frag_off) & IP6_MF)) && !((fraginfo->flags & IP6T_FRAG_NMF) && (ntohs(frag->frag_off) & IP6_MF)); }
int ip6_mc_input(struct sk_buff *skb) { const struct ipv6hdr *hdr; int deliver; IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), IPSTATS_MIB_INMCASTPKTS); hdr = ipv6_hdr(skb); deliver = ipv6_chk_mcast_addr(skb->dev, &hdr->daddr, NULL); #ifdef CONFIG_IPV6_MROUTE /* * IPv6 multicast router mode is now supported ;) */ if (ipv6_devconf.mc_forwarding && likely(!(IP6CB(skb)->flags & IP6SKB_FORWARDED))) { /* * Okay, we try to forward - split and duplicate * packets. */ struct sk_buff *skb2; struct inet6_skb_parm *opt = IP6CB(skb); /* Check for MLD */ if (unlikely(opt->flags & IP6SKB_ROUTERALERT)) { /* Check if this is a mld message */ struct icmp6hdr *icmp6; u8 nexthdr = hdr->nexthdr; int offset; /* Check if the value of Router Alert * is for MLD (0x0000). */ if (opt->ra == htons(IPV6_OPT_ROUTERALERT_MLD)) { deliver = 0; if (!ipv6_ext_hdr(nexthdr)) { /* BUG */ goto out; } offset = ipv6_skip_exthdr(skb, sizeof(*hdr), &nexthdr); if (offset < 0) goto out; if (nexthdr != IPPROTO_ICMPV6) goto out; if (!pskb_may_pull(skb, (skb_network_header(skb) + offset + 1 - skb->data))) goto out; icmp6 = (struct icmp6hdr *)(skb_network_header(skb) + offset); switch (icmp6->icmp6_type) { case ICMPV6_MGM_QUERY: case ICMPV6_MGM_REPORT: case ICMPV6_MGM_REDUCTION: case ICMPV6_MLD2_REPORT: deliver = 1; break; } goto out; } /* unknown RA - process it normally */ } if (deliver) skb2 = skb_clone(skb, GFP_ATOMIC); else { skb2 = skb; skb = NULL; } if (skb2) { skb2->dev = skb2->dst->dev; ip6_mr_input(skb2); } } out: #endif if (likely(deliver)) { ip6_input(skb); } else { /* discard */ kfree_skb(skb); } return 0; }
/* * find the offset to specified header or the protocol number of last header * if target < 0. "last header" is transport protocol header, ESP, or * "No next header". * * Note that *offset is used as input/output parameter. an if it is not zero, * then it must be a valid offset to an inner IPv6 header. This can be used * to explore inner IPv6 header, eg. ICMPv6 error messages. * * If target header is found, its offset is set in *offset and return protocol * number. Otherwise, return -1. * * If the first fragment doesn't contain the final protocol header or * NEXTHDR_NONE it is considered invalid. * * Note that non-1st fragment is special case that "the protocol number * of last header" is "next header" field in Fragment header. In this case, * *offset is meaningless and fragment offset is stored in *fragoff if fragoff * isn't NULL. * * if flags is not NULL and it's a fragment, then the frag flag * IP6_FH_F_FRAG will be set. If it's an AH header, the * IP6_FH_F_AUTH flag is set and target < 0, then this function will * stop at the AH header. If IP6_FH_F_SKIP_RH flag was passed, then this * function will skip all those routing headers, where segements_left was 0. */ int rpl_ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, int target, unsigned short *fragoff, int *flags) { unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr); u8 nexthdr = ipv6_hdr(skb)->nexthdr; unsigned int len; bool found; if (fragoff) *fragoff = 0; if (*offset) { struct ipv6hdr _ip6, *ip6; ip6 = skb_header_pointer(skb, *offset, sizeof(_ip6), &_ip6); if (!ip6 || (ip6->version != 6)) { printk(KERN_ERR "IPv6 header not found\n"); return -EBADMSG; } start = *offset + sizeof(struct ipv6hdr); nexthdr = ip6->nexthdr; } len = skb->len - start; do { struct ipv6_opt_hdr _hdr, *hp; unsigned int hdrlen; found = (nexthdr == target); if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) { if (target < 0 || found) break; return -ENOENT; } hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr); if (hp == NULL) return -EBADMSG; if (nexthdr == NEXTHDR_ROUTING) { struct ipv6_rt_hdr _rh, *rh; rh = skb_header_pointer(skb, start, sizeof(_rh), &_rh); if (rh == NULL) return -EBADMSG; if (flags && (*flags & IP6_FH_F_SKIP_RH) && rh->segments_left == 0) found = false; } if (nexthdr == NEXTHDR_FRAGMENT) { unsigned short _frag_off; __be16 *fp; if (flags) /* Indicate that this is a fragment */ *flags |= IP6_FH_F_FRAG; fp = skb_header_pointer(skb, start+offsetof(struct frag_hdr, frag_off), sizeof(_frag_off), &_frag_off); if (fp == NULL) return -EBADMSG; _frag_off = ntohs(*fp) & ~0x7; if (_frag_off) { if (target < 0 && ((!ipv6_ext_hdr(hp->nexthdr)) || hp->nexthdr == NEXTHDR_NONE)) { if (fragoff) *fragoff = _frag_off; return hp->nexthdr; } return -ENOENT; } hdrlen = 8; } else if (nexthdr == NEXTHDR_AUTH) { if (flags && (*flags & IP6_FH_F_AUTH) && (target < 0)) break; hdrlen = (hp->hdrlen + 2) << 2; } else hdrlen = ipv6_optlen(hp); if (!found) { nexthdr = hp->nexthdr; len -= hdrlen; start += hdrlen; } } while (!found);
static int match(const struct sk_buff *skb, const struct net_device *in, const struct net_device *out, const void *matchinfo, int offset, const void *protohdr, u_int16_t datalen, int *hotdrop) { struct ipv6_rt_hdr *route = NULL; const struct ip6t_rt *rtinfo = matchinfo; unsigned int temp; unsigned int len; u8 nexthdr; unsigned int ptr; unsigned int hdrlen = 0; unsigned int ret = 0; /* type of the 1st exthdr */ nexthdr = skb->nh.ipv6h->nexthdr; /* pointer to the 1st exthdr */ ptr = sizeof(struct ipv6hdr); /* available length */ len = skb->len - ptr; temp = 0; while (ipv6_ext_hdr(nexthdr)) { struct ipv6_opt_hdr *hdr; DEBUGP("ipv6_rt header iteration \n"); /* Is there enough space for the next ext header? */ if (len < (int)sizeof(struct ipv6_opt_hdr)) return 0; /* No more exthdr -> evaluate */ if (nexthdr == NEXTHDR_NONE) { break; } /* ESP -> evaluate */ if (nexthdr == NEXTHDR_ESP) { break; } hdr=skb->data+ptr; /* Calculate the header length */ if (nexthdr == NEXTHDR_FRAGMENT) { hdrlen = 8; } else if (nexthdr == NEXTHDR_AUTH) hdrlen = (hdr->hdrlen+2)<<2; else hdrlen = ipv6_optlen(hdr); /* ROUTING -> evaluate */ if (nexthdr == NEXTHDR_ROUTING) { temp |= MASK_ROUTING; break; } /* set the flag */ switch (nexthdr){ case NEXTHDR_HOP: case NEXTHDR_ROUTING: case NEXTHDR_FRAGMENT: case NEXTHDR_AUTH: case NEXTHDR_DEST: break; default: DEBUGP("ipv6_rt match: unknown nextheader %u\n",nexthdr); return 0; break; } nexthdr = hdr->nexthdr; len -= hdrlen; ptr += hdrlen; if ( ptr > skb->len ) { DEBUGP("ipv6_rt: new pointer is too large! \n"); break; } } /* ROUTING header not found */ if ( temp != MASK_ROUTING ) return 0; if (len < (int)sizeof(struct ipv6_rt_hdr)){ *hotdrop = 1; return 0; } if (len < hdrlen){ /* Pcket smaller than its length field */ return 0; } route=skb->data+ptr; DEBUGP("IPv6 RT LEN %u %u ", hdrlen, route->hdrlen); DEBUGP("TYPE %04X ", route->type); DEBUGP("SGS_LEFT %u %08X\n", ntohl(route->segments_left), ntohl(route->segments_left)); DEBUGP("IPv6 RT segsleft %02X ", (segsleft_match(rtinfo->segsleft[0], rtinfo->segsleft[1], ntohl(route->segments_left), !!(rtinfo->invflags & IP6T_RT_INV_SGS)))); DEBUGP("type %02X %02X %02X ", rtinfo->rt_type, route->type, (!(rtinfo->flags & IP6T_RT_TYP) || ((rtinfo->rt_type == route->type) ^ !!(rtinfo->invflags & IP6T_RT_INV_TYP)))); DEBUGP("len %02X %04X %02X ", rtinfo->hdrlen, hdrlen, (!(rtinfo->flags & IP6T_RT_LEN) || ((rtinfo->hdrlen == hdrlen) ^ !!(rtinfo->invflags & IP6T_RT_INV_LEN)))); DEBUGP("res %02X %02X %02X ", (rtinfo->flags & IP6T_RT_RES), ((struct rt0_hdr *)route)->reserved, !((rtinfo->flags & IP6T_RT_RES) && (((struct rt0_hdr *)route)->reserved))); ret = (route != NULL) && (segsleft_match(rtinfo->segsleft[0], rtinfo->segsleft[1], ntohl(route->segments_left), !!(rtinfo->invflags & IP6T_RT_INV_SGS))) && (!(rtinfo->flags & IP6T_RT_LEN) || ((rtinfo->hdrlen == hdrlen) ^ !!(rtinfo->invflags & IP6T_RT_INV_LEN))) && (!(rtinfo->flags & IP6T_RT_TYP) || ((rtinfo->rt_type == route->type) ^ !!(rtinfo->invflags & IP6T_RT_INV_TYP))) && !((rtinfo->flags & IP6T_RT_RES) && (((struct rt0_hdr *)route)->reserved)); DEBUGP("#%d ",rtinfo->addrnr); temp = len = ptr = 0; if ( !(rtinfo->flags & IP6T_RT_FST) ){ return ret; } else if (rtinfo->flags & IP6T_RT_FST_NSTRICT) { DEBUGP("Not strict "); if ( rtinfo->addrnr > (unsigned int)((hdrlen-8)/16) ){ DEBUGP("There isn't enough space\n"); return 0; } else { DEBUGP("#%d ",rtinfo->addrnr); ptr = 0; for(temp=0; temp<(unsigned int)((hdrlen-8)/16); temp++){ len = 0; while ((u8)(((struct rt0_hdr *)route)-> addr[temp].s6_addr[len]) == (u8)(rtinfo->addrs[ptr].s6_addr[len])){ DEBUGP("%02X?%02X ", (u8)(((struct rt0_hdr *)route)->addr[temp].s6_addr[len]), (u8)(rtinfo->addrs[ptr].s6_addr[len])); len++; if ( len == 16 ) break; } if (len==16) { DEBUGP("ptr=%d temp=%d;\n",ptr,temp); ptr++; } else { DEBUGP("%02X?%02X ", (u8)(((struct rt0_hdr *)route)->addr[temp].s6_addr[len]), (u8)(rtinfo->addrs[ptr].s6_addr[len])); DEBUGP("!ptr=%d temp=%d;\n",ptr,temp); } if (ptr==rtinfo->addrnr) break; } DEBUGP("ptr=%d len=%d #%d\n",ptr,len, rtinfo->addrnr); if ( (len == 16) && (ptr == rtinfo->addrnr)) return ret; else return 0; } } else { DEBUGP("Strict "); if ( rtinfo->addrnr > (unsigned int)((hdrlen-8)/16) ){ DEBUGP("There isn't enough space\n"); return 0; } else { DEBUGP("#%d ",rtinfo->addrnr); for(temp=0; temp<rtinfo->addrnr; temp++){ len = 0; while ((u8)(((struct rt0_hdr *)route)-> addr[temp].s6_addr[len]) == (u8)(rtinfo->addrs[temp].s6_addr[len])){ DEBUGP("%02X?%02X ", (u8)(((struct rt0_hdr *)route)->addr[temp].s6_addr[len]), (u8)(rtinfo->addrs[temp].s6_addr[len])); len++; if ( len == 16 ) break; } if (len!=16) { DEBUGP("%02X?%02X ", (u8)(((struct rt0_hdr *)route)->addr[temp].s6_addr[len]), (u8)(rtinfo->addrs[temp].s6_addr[len])); DEBUGP("!len=%d temp=%d;\n",len,temp); break; } } DEBUGP("temp=%d len=%d #%d\n",temp,len,rtinfo->addrnr); if ( (len == 16) && (temp == rtinfo->addrnr) && (temp == (unsigned int)((hdrlen-8)/16))) return ret; else return 0; } } return 0; }
int ip6_mc_input(struct sk_buff *skb) { struct ipv6hdr *hdr; int deliver; IP6_INC_STATS_BH(IPSTATS_MIB_INMCASTPKTS); hdr = skb->nh.ipv6h; deliver = likely(!(skb->dev->flags & (IFF_PROMISC|IFF_ALLMULTI))) || ipv6_chk_mcast_addr(skb->dev, &hdr->daddr, NULL); #ifdef CONFIG_IPV6_MROUTE /* * IPv6 multicast router mode is now supported ;) */ if ((ipv6_addr_type(&hdr->daddr) & IPV6_ADDR_MULTICAST) && (IPV6_ADDR_MC_SCOPE(&hdr->daddr) == 0)) goto out; if (ipv6_devconf.mc_forwarding && !(ipv6_addr_type(&hdr->daddr) & IPV6_ADDR_LINKLOCAL) && likely(!(IP6CB(skb)->flags & IP6SKB_FORWARDED))) { /* * Okay, we try to forward - split and duplicate * packets. */ struct sk_buff *skb2; struct inet6_skb_parm *opt = IP6CB(skb); /* Check for MLD */ if (unlikely(opt->ra)) { /* Check if this is a mld message */ //u8 *ptr = skb_network_header(skb) + opt->ra; u8 *ptr; struct icmp6hdr *icmp6; u8 nexthdr = hdr->nexthdr; int offset; ptr = (u8 *) (skb->nh.raw + opt->ra); //chunru: copy from ip6_output /* Check if the value of Router Alert * is for MLD (0x0000). */ if ((ptr[2] | ptr[3]) == 0) { deliver = 0; if (!ipv6_ext_hdr(nexthdr)) { /* BUG */ goto out; } offset = ipv6_skip_exthdr(skb, sizeof(*hdr), &nexthdr); if (offset < 0) goto out; if (nexthdr != IPPROTO_ICMPV6) goto out; if (!pskb_may_pull(skb, (skb->nh.raw + offset + 1 - skb->data))) goto out; icmp6 = (struct icmp6hdr *)( skb->nh.raw + offset); switch (icmp6->icmp6_type) { case ICMPV6_MGM_QUERY: case ICMPV6_MGM_REPORT: case ICMPV6_MGM_REDUCTION: case ICMPV6_MLD2_REPORT: deliver = 1; break; } goto out; } /* unknown RA - process it normally */ } if (deliver) { skb2 = skb_clone(skb, GFP_ATOMIC); } else { skb2 = skb; skb = NULL; } if (skb2) { ip6_mr_input(skb2); } } out: #endif if (likely(deliver)) { ip6_input(skb); return 0; } #ifdef CONFIG_IPV6_MROUTE else if(skb) { /* discard */ kfree_skb(skb); } #else /* discard */ kfree_skb(skb); #endif return 0; }