Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/jesse/openvswitch
authorDavid S. Miller <davem@davemloft.net>
Fri, 30 Nov 2012 17:01:30 +0000 (12:01 -0500)
committerDavid S. Miller <davem@davemloft.net>
Fri, 30 Nov 2012 17:01:30 +0000 (12:01 -0500)
Conflicts:
net/ipv6/exthdrs_core.c

Jesse Gross says:

====================
This series of improvements for 3.8/net-next contains four components:
 * Support for modifying IPv6 headers
 * Support for matching and setting skb->mark for better integration with
   things like iptables
 * Ability to recognize the EtherType for RARP packets
 * Two small performance enhancements

The movement of ipv6_find_hdr() into exthdrs_core.c causes two small merge
conflicts.  I left it as is but can do the merge if you want.  The conflicts
are:
 * ipv6_find_hdr() and ipv6_find_tlv() were both moved to the bottom of
   exthdrs_core.c.  Both should stay.
 * A new use of ipv6_find_hdr() was added to net/netfilter/ipvs/ip_vs_core.c
   after this patch.  The IPVS user has two instances of the old constant
   name IP6T_FH_F_FRAG which has been renamed to IP6_FH_F_FRAG.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
1  2 
net/ipv6/exthdrs_core.c
net/ipv6/netfilter/ip6_tables.c
net/netfilter/ipvs/ip_vs_core.c

index e7d756e19d1d60b1ff6e125b7533454391c1ac31,11b4e29c8452bb82a5d0a45e254f19f78f15908c..c5e83fae4df423ccbe02bed8bf31ffd415014ad2
@@@ -112,46 -112,125 +112,170 @@@ int ipv6_skip_exthdr(const struct sk_bu
  }
  EXPORT_SYMBOL(ipv6_skip_exthdr);
  
 +int ipv6_find_tlv(struct sk_buff *skb, int offset, int type)
 +{
 +      const unsigned char *nh = skb_network_header(skb);
 +      int packet_len = skb->tail - skb->network_header;
 +      struct ipv6_opt_hdr *hdr;
 +      int len;
 +
 +      if (offset + 2 > packet_len)
 +              goto bad;
 +      hdr = (struct ipv6_opt_hdr *)(nh + offset);
 +      len = ((hdr->hdrlen + 1) << 3);
 +
 +      if (offset + len > packet_len)
 +              goto bad;
 +
 +      offset += 2;
 +      len -= 2;
 +
 +      while (len > 0) {
 +              int opttype = nh[offset];
 +              int optlen;
 +
 +              if (opttype == type)
 +                      return offset;
 +
 +              switch (opttype) {
 +              case IPV6_TLV_PAD1:
 +                      optlen = 1;
 +                      break;
 +              default:
 +                      optlen = nh[offset + 1] + 2;
 +                      if (optlen > len)
 +                              goto bad;
 +                      break;
 +              }
 +              offset += optlen;
 +              len -= optlen;
 +      }
 +      /* not_found */
 + bad:
 +      return -1;
 +}
 +EXPORT_SYMBOL_GPL(ipv6_find_tlv);
++
+ /*
+  * 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 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)
+                               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);
+       *offset = start;
+       return nexthdr;
+ }
+ EXPORT_SYMBOL(ipv6_find_hdr);
++
Simple merge
index fb45640dc1fb7dff034c9909302b1bf355877c6d,58918e20f9d5b038c2181b893b0e0458dbd3a8f2..47edf5a40a5939d2401dc11baa89142b9bcd64b6
@@@ -939,29 -956,14 +939,29 @@@ static int ip_vs_out_icmp_v6(struct sk_
                *related = 0;
                return NF_ACCEPT;
        }
-       if (ipvsh->flags & IP6T_FH_F_FRAG)
 +      /* Fragment header that is before ICMP header tells us that:
 +       * it's not an error message since they can't be fragmented.
 +       */
++      if (ipvsh->flags & IP6_FH_F_FRAG)
 +              return NF_DROP;
 +
 +      IP_VS_DBG(8, "Outgoing ICMPv6 (%d,%d) %pI6c->%pI6c\n",
 +                ic->icmp6_type, ntohs(icmpv6_id(ic)),
 +                &ipvsh->saddr, &ipvsh->daddr);
  
        /* Now find the contained IP header */
 -      offset += sizeof(_icmph);
 -      cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph);
 -      if (cih == NULL)
 +      ciph.len = ipvsh->len + sizeof(_icmph);
 +      ip6h = skb_header_pointer(skb, ciph.len, sizeof(_ip6h), &_ip6h);
 +      if (ip6h == NULL)
                return NF_ACCEPT; /* The packet looks wrong, ignore */
 -
 -      pp = ip_vs_proto_get(cih->nexthdr);
 +      ciph.saddr.in6 = ip6h->saddr; /* conn_out_get() handles reverse order */
 +      ciph.daddr.in6 = ip6h->daddr;
 +      /* skip possible IPv6 exthdrs of contained IPv6 packet */
 +      ciph.protocol = ipv6_find_hdr(skb, &ciph.len, -1, &ciph.fragoffs, NULL);
 +      if (ciph.protocol < 0)
 +              return NF_ACCEPT; /* Contained IPv6 hdr looks wrong, ignore */
 +
 +      pp = ip_vs_proto_get(ciph.protocol);
        if (!pp)
                return NF_ACCEPT;
  
@@@ -1472,31 -1504,15 +1472,31 @@@ static int ip_vs_in_icmp_v6(struct sk_b
                *related = 0;
                return NF_ACCEPT;
        }
-       if (iph->flags & IP6T_FH_F_FRAG)
 +      /* Fragment header that is before ICMP header tells us that:
 +       * it's not an error message since they can't be fragmented.
 +       */
++      if (iph->flags & IP6_FH_F_FRAG)
 +              return NF_DROP;
 +
 +      IP_VS_DBG(8, "Incoming ICMPv6 (%d,%d) %pI6c->%pI6c\n",
 +                ic->icmp6_type, ntohs(icmpv6_id(ic)),
 +                &iph->saddr, &iph->daddr);
  
        /* Now find the contained IP header */
 -      offset += sizeof(_icmph);
 -      cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph);
 -      if (cih == NULL)
 +      ciph.len = iph->len + sizeof(_icmph);
 +      offs_ciph = ciph.len; /* Save ip header offset */
 +      ip6h = skb_header_pointer(skb, ciph.len, sizeof(_ip6h), &_ip6h);
 +      if (ip6h == NULL)
                return NF_ACCEPT; /* The packet looks wrong, ignore */
 +      ciph.saddr.in6 = ip6h->saddr; /* conn_in_get() handles reverse order */
 +      ciph.daddr.in6 = ip6h->daddr;
 +      /* skip possible IPv6 exthdrs of contained IPv6 packet */
 +      ciph.protocol = ipv6_find_hdr(skb, &ciph.len, -1, &ciph.fragoffs, NULL);
 +      if (ciph.protocol < 0)
 +              return NF_ACCEPT; /* Contained IPv6 hdr looks wrong, ignore */
  
        net = skb_net(skb);
 -      pd = ip_vs_proto_data_get(net, cih->nexthdr);
 +      pd = ip_vs_proto_data_get(net, ciph.protocol);
        if (!pd)
                return NF_ACCEPT;
        pp = pd->pp;