Commit b777e0ce7437a0e788e2aeb42aca9af2cce1f2e1

Authored by Patrick McHardy
Committed by David S. Miller
1 parent 1bd9bef6f9

[NETFILTER]: make ipv6_find_hdr() find transport protocol header

The original ipv6_find_hdr() finds the specified header in IPv6 packets.
This makes it possible to get transport header so that we can kill similar
loop in ip6_match_packet().

Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>

Showing 8 changed files with 49 additions and 75 deletions Side-by-side Diff

include/linux/netfilter_ipv6/ip6_tables.h
... ... @@ -474,7 +474,7 @@
474 474 extern int ip6t_ext_hdr(u8 nexthdr);
475 475 /* find specified header and get offset to it */
476 476 extern int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
477   - u8 target);
  477 + int target, unsigned short *fragoff);
478 478  
479 479 #define IP6T_ALIGN(s) (((s) + (__alignof__(struct ip6t_entry)-1)) & ~(__alignof__(struct ip6t_entry)-1))
480 480  
net/ipv6/netfilter/ip6_tables.c
... ... @@ -205,69 +205,21 @@
205 205  
206 206 /* look for the desired protocol header */
207 207 if((ip6info->flags & IP6T_F_PROTO)) {
208   - u_int8_t currenthdr = ipv6->nexthdr;
209   - struct ipv6_opt_hdr _hdr, *hp;
210   - u_int16_t ptr; /* Header offset in skb */
211   - u_int16_t hdrlen; /* Header */
212   - u_int16_t _fragoff = 0, *fp = NULL;
  208 + int protohdr;
  209 + unsigned short _frag_off;
213 210  
214   - ptr = IPV6_HDR_LEN;
  211 + protohdr = ipv6_find_hdr(skb, protoff, -1, &_frag_off);
  212 + if (protohdr < 0)
  213 + return 0;
215 214  
216   - while (ip6t_ext_hdr(currenthdr)) {
217   - /* Is there enough space for the next ext header? */
218   - if (skb->len - ptr < IPV6_OPTHDR_LEN)
219   - return 0;
  215 + *fragoff = _frag_off;
220 216  
221   - /* NONE or ESP: there isn't protocol part */
222   - /* If we want to count these packets in '-p all',
223   - * we will change the return 0 to 1*/
224   - if ((currenthdr == IPPROTO_NONE) ||
225   - (currenthdr == IPPROTO_ESP))
226   - break;
227   -
228   - hp = skb_header_pointer(skb, ptr, sizeof(_hdr), &_hdr);
229   - BUG_ON(hp == NULL);
230   -
231   - /* Size calculation */
232   - if (currenthdr == IPPROTO_FRAGMENT) {
233   - fp = skb_header_pointer(skb,
234   - ptr+offsetof(struct frag_hdr,
235   - frag_off),
236   - sizeof(_fragoff),
237   - &_fragoff);
238   - if (fp == NULL)
239   - return 0;
240   -
241   - _fragoff = ntohs(*fp) & ~0x7;
242   - hdrlen = 8;
243   - } else if (currenthdr == IPPROTO_AH)
244   - hdrlen = (hp->hdrlen+2)<<2;
245   - else
246   - hdrlen = ipv6_optlen(hp);
247   -
248   - currenthdr = hp->nexthdr;
249   - ptr += hdrlen;
250   - /* ptr is too large */
251   - if ( ptr > skb->len )
252   - return 0;
253   - if (_fragoff) {
254   - if (ip6t_ext_hdr(currenthdr))
255   - return 0;
256   - break;
257   - }
258   - }
259   -
260   - *protoff = ptr;
261   - *fragoff = _fragoff;
262   -
263   - /* currenthdr contains the protocol header */
264   -
265 217 dprintf("Packet protocol %hi ?= %s%hi.\n",
266   - currenthdr,
  218 + protohdr,
267 219 ip6info->invflags & IP6T_INV_PROTO ? "!":"",
268 220 ip6info->proto);
269 221  
270   - if (ip6info->proto == currenthdr) {
  222 + if (ip6info->proto == protohdr) {
271 223 if(ip6info->invflags & IP6T_INV_PROTO) {
272 224 return 0;
273 225 }
274 226  
275 227  
276 228  
277 229  
278 230  
... ... @@ -2098,26 +2050,39 @@
2098 2050 }
2099 2051  
2100 2052 /*
2101   - * find specified header up to transport protocol header.
2102   - * If found target header, the offset to the header is set to *offset
2103   - * and return 0. otherwise, return -1.
  2053 + * find the offset to specified header or the protocol number of last header
  2054 + * if target < 0. "last header" is transport protocol header, ESP, or
  2055 + * "No next header".
2104 2056 *
2105   - * Notes: - non-1st Fragment Header isn't skipped.
2106   - * - ESP header isn't skipped.
2107   - * - The target header may be trancated.
  2057 + * If target header is found, its offset is set in *offset and return protocol
  2058 + * number. Otherwise, return -1.
  2059 + *
  2060 + * Note that non-1st fragment is special case that "the protocol number
  2061 + * of last header" is "next header" field in Fragment header. In this case,
  2062 + * *offset is meaningless and fragment offset is stored in *fragoff if fragoff
  2063 + * isn't NULL.
  2064 + *
2108 2065 */
2109   -int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, u8 target)
  2066 +int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
  2067 + int target, unsigned short *fragoff)
2110 2068 {
2111 2069 unsigned int start = (u8*)(skb->nh.ipv6h + 1) - skb->data;
2112 2070 u8 nexthdr = skb->nh.ipv6h->nexthdr;
2113 2071 unsigned int len = skb->len - start;
2114 2072  
  2073 + if (fragoff)
  2074 + *fragoff = 0;
  2075 +
2115 2076 while (nexthdr != target) {
2116 2077 struct ipv6_opt_hdr _hdr, *hp;
2117 2078 unsigned int hdrlen;
2118 2079  
2119   - if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE)
  2080 + if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
  2081 + if (target < 0)
  2082 + break;
2120 2083 return -1;
  2084 + }
  2085 +
2121 2086 hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
2122 2087 if (hp == NULL)
2123 2088 return -1;
2124 2089  
... ... @@ -2131,8 +2096,17 @@
2131 2096 if (fp == NULL)
2132 2097 return -1;
2133 2098  
2134   - if (ntohs(*fp) & ~0x7)
  2099 + _frag_off = ntohs(*fp) & ~0x7;
  2100 + if (_frag_off) {
  2101 + if (target < 0 &&
  2102 + ((!ipv6_ext_hdr(hp->nexthdr)) ||
  2103 + nexthdr == NEXTHDR_NONE)) {
  2104 + if (fragoff)
  2105 + *fragoff = _frag_off;
  2106 + return hp->nexthdr;
  2107 + }
2135 2108 return -1;
  2109 + }
2136 2110 hdrlen = 8;
2137 2111 } else if (nexthdr == NEXTHDR_AUTH)
2138 2112 hdrlen = (hp->hdrlen + 2) << 2;
... ... @@ -2145,7 +2119,7 @@
2145 2119 }
2146 2120  
2147 2121 *offset = start;
2148   - return 0;
  2122 + return nexthdr;
2149 2123 }
2150 2124  
2151 2125 EXPORT_SYMBOL(ip6t_register_table);
net/ipv6/netfilter/ip6t_ah.c
... ... @@ -54,7 +54,7 @@
54 54 unsigned int ptr;
55 55 unsigned int hdrlen = 0;
56 56  
57   - if (ipv6_find_hdr(skb, &ptr, NEXTHDR_AUTH) < 0)
  57 + if (ipv6_find_hdr(skb, &ptr, NEXTHDR_AUTH, NULL) < 0)
58 58 return 0;
59 59  
60 60 ah = skb_header_pointer(skb, ptr, sizeof(_ah), &_ah);
net/ipv6/netfilter/ip6t_dst.c
... ... @@ -71,9 +71,9 @@
71 71 unsigned int optlen;
72 72  
73 73 #if HOPBYHOP
74   - if (ipv6_find_hdr(skb, &ptr, NEXTHDR_HOP) < 0)
  74 + if (ipv6_find_hdr(skb, &ptr, NEXTHDR_HOP, NULL) < 0)
75 75 #else
76   - if (ipv6_find_hdr(skb, &ptr, NEXTHDR_DEST) < 0)
  76 + if (ipv6_find_hdr(skb, &ptr, NEXTHDR_DEST, NULL) < 0)
77 77 #endif
78 78 return 0;
79 79  
net/ipv6/netfilter/ip6t_esp.c
... ... @@ -56,7 +56,7 @@
56 56 /* Make sure this isn't an evil packet */
57 57 /*DEBUGP("ipv6_esp entered \n");*/
58 58  
59   - if (ipv6_find_hdr(skb, &ptr, NEXTHDR_ESP) < 0)
  59 + if (ipv6_find_hdr(skb, &ptr, NEXTHDR_ESP, NULL) < 0)
60 60 return 0;
61 61  
62 62 eh = skb_header_pointer(skb, ptr, sizeof(_esp), &_esp);
net/ipv6/netfilter/ip6t_frag.c
... ... @@ -52,7 +52,7 @@
52 52 const struct ip6t_frag *fraginfo = matchinfo;
53 53 unsigned int ptr;
54 54  
55   - if (ipv6_find_hdr(skb, &ptr, NEXTHDR_FRAGMENT) < 0)
  55 + if (ipv6_find_hdr(skb, &ptr, NEXTHDR_FRAGMENT, NULL) < 0)
56 56 return 0;
57 57  
58 58 fh = skb_header_pointer(skb, ptr, sizeof(_frag), &_frag);
net/ipv6/netfilter/ip6t_hbh.c
... ... @@ -71,9 +71,9 @@
71 71 unsigned int optlen;
72 72  
73 73 #if HOPBYHOP
74   - if (ipv6_find_hdr(skb, &ptr, NEXTHDR_HOP) < 0)
  74 + if (ipv6_find_hdr(skb, &ptr, NEXTHDR_HOP, NULL) < 0)
75 75 #else
76   - if (ipv6_find_hdr(skb, &ptr, NEXTHDR_DEST) < 0)
  76 + if (ipv6_find_hdr(skb, &ptr, NEXTHDR_DEST, NULL) < 0)
77 77 #endif
78 78 return 0;
79 79  
net/ipv6/netfilter/ip6t_rt.c
... ... @@ -58,7 +58,7 @@
58 58 unsigned int ret = 0;
59 59 struct in6_addr *ap, _addr;
60 60  
61   - if (ipv6_find_hdr(skb, &ptr, NEXTHDR_ROUTING) < 0)
  61 + if (ipv6_find_hdr(skb, &ptr, NEXTHDR_ROUTING, NULL) < 0)
62 62 return 0;
63 63  
64 64 rh = skb_header_pointer(skb, ptr, sizeof(_route), &_route);