Blame view

net/ipv6/exthdrs_core.c 7.55 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
  /*
   * IPv6 library code, needed by static components when full IPv6 support is
   * not configured or static.
   */
bc3b2d7fb   Paul Gortmaker   net: Add export.h...
5
  #include <linux/export.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
6
  #include <net/ipv6.h>
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
7
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
8
9
   * find out if nexthdr is a well-known extension header or a protocol
   */
a50feda54   Eric Dumazet   ipv6: bool/const ...
10
  bool ipv6_ext_hdr(u8 nexthdr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
11
  {
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
12
  	/*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
13
14
  	 * find out if nexthdr is an extension header or a protocol
  	 */
a02cec215   Eric Dumazet   net: return opera...
15
  	return   (nexthdr == NEXTHDR_HOP)	||
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
16
17
18
19
  		 (nexthdr == NEXTHDR_ROUTING)	||
  		 (nexthdr == NEXTHDR_FRAGMENT)	||
  		 (nexthdr == NEXTHDR_AUTH)	||
  		 (nexthdr == NEXTHDR_NONE)	||
a02cec215   Eric Dumazet   net: return opera...
20
  		 (nexthdr == NEXTHDR_DEST);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
21
  }
81bd60b51   Eldad Zack   net/ipv6/exthdrs_...
22
  EXPORT_SYMBOL(ipv6_ext_hdr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23
24
25
26
27
  
  /*
   * Skip any extension headers. This is used by the ICMP module.
   *
   * Note that strictly speaking this conflicts with RFC 2460 4.0:
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
28
   * ...The contents and semantics of each extension header determine whether
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
29
30
31
32
33
   * 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.
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
34
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
35
   * We do exactly this. This is a protocol bug. We can't decide after a
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
36
   * seeing an unknown discard-with-error flavour TLV option if it's a
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
37
38
   * ICMP error message or not (errors should never be send in reply to
   * ICMP error messages).
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
39
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
40
41
42
43
   * But I see no other way to do this. This might need to be reexamined
   * when Linux implements ESP (and maybe AUTH) headers.
   * --AK
   *
0d3d077cd   Herbert Xu   [SELINUX]: Fix ip...
44
45
   * This function parses (probably truncated) exthdr set "hdr".
   * "nexthdrp" initially points to some place,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
46
47
48
49
50
51
52
53
54
55
56
57
58
   * 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.
75f2811c6   Jesse Gross   ipv6: Add fragmen...
59
60
61
   *	  - Reports the offset field of the final fragment header so it is
   *	    possible to tell whether this is a first fragment, later fragment,
   *	    or not fragmented.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
62
63
64
65
66
67
   *	  - ESP is unparsable for now and considered like
   *	    normal payload protocol.
   *	  - Note also special handling of AUTH header. Thanks to IPsec wizards.
   *
   * --ANK (980726)
   */
75f2811c6   Jesse Gross   ipv6: Add fragmen...
68
69
  int ipv6_skip_exthdr(const struct sk_buff *skb, int start, u8 *nexthdrp,
  		     __be16 *frag_offp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
70
71
  {
  	u8 nexthdr = *nexthdrp;
75f2811c6   Jesse Gross   ipv6: Add fragmen...
72
  	*frag_offp = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
73
74
75
  	while (ipv6_ext_hdr(nexthdr)) {
  		struct ipv6_opt_hdr _hdr, *hp;
  		int hdrlen;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
76
77
78
  		if (nexthdr == NEXTHDR_NONE)
  			return -1;
  		hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
63159f29b   Ian Morris   ipv6: coding styl...
79
  		if (!hp)
0d3d077cd   Herbert Xu   [SELINUX]: Fix ip...
80
  			return -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
81
  		if (nexthdr == NEXTHDR_FRAGMENT) {
e69a4adc6   Al Viro   [IPV6]: Misc endi...
82
  			__be16 _frag_off, *fp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
83
84
85
86
87
  			fp = skb_header_pointer(skb,
  						start+offsetof(struct frag_hdr,
  							       frag_off),
  						sizeof(_frag_off),
  						&_frag_off);
63159f29b   Ian Morris   ipv6: coding styl...
88
  			if (!fp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
89
  				return -1;
75f2811c6   Jesse Gross   ipv6: Add fragmen...
90
91
  			*frag_offp = *fp;
  			if (ntohs(*frag_offp) & ~0x7)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
92
93
94
  				break;
  			hdrlen = 8;
  		} else if (nexthdr == NEXTHDR_AUTH)
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
95
  			hdrlen = (hp->hdrlen+2)<<2;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
96
  		else
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
97
  			hdrlen = ipv6_optlen(hp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
98
99
  
  		nexthdr = hp->nexthdr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
100
101
102
103
104
105
  		start += hdrlen;
  	}
  
  	*nexthdrp = nexthdr;
  	return start;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
106
  EXPORT_SYMBOL(ipv6_skip_exthdr);
3c73a0368   Vlad Yasevich   ipv6: Update ipv6...
107

0868383b8   Huw Davies   ipv6: constify th...
108
  int ipv6_find_tlv(const struct sk_buff *skb, int offset, int type)
3c73a0368   Vlad Yasevich   ipv6: Update ipv6...
109
110
  {
  	const unsigned char *nh = skb_network_header(skb);
29a3cad5c   Simon Horman   ipv6: Correct com...
111
  	int packet_len = skb_tail_pointer(skb) - skb_network_header(skb);
3c73a0368   Vlad Yasevich   ipv6: Update ipv6...
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
  	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);
e7165030d   David S. Miller   Merge branch 'mas...
151

f8f626754   Jesse Gross   ipv6: Move ipv6_f...
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
  /*
   * 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.
   *
9195bb8e3   Ansis Atteka   ipv6: improve ipv...
172
173
174
175
176
   * 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.
f8f626754   Jesse Gross   ipv6: Move ipv6_f...
177
178
179
180
181
182
183
   */
  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;
9195bb8e3   Ansis Atteka   ipv6: improve ipv...
184
  	bool found;
f8f626754   Jesse Gross   ipv6: Move ipv6_f...
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
  
  	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
  ");
  			return -EBADMSG;
  		}
  		start = *offset + sizeof(struct ipv6hdr);
  		nexthdr = ip6->nexthdr;
  	}
  	len = skb->len - start;
9195bb8e3   Ansis Atteka   ipv6: improve ipv...
202
  	do {
f8f626754   Jesse Gross   ipv6: Move ipv6_f...
203
204
  		struct ipv6_opt_hdr _hdr, *hp;
  		unsigned int hdrlen;
9195bb8e3   Ansis Atteka   ipv6: improve ipv...
205
  		found = (nexthdr == target);
f8f626754   Jesse Gross   ipv6: Move ipv6_f...
206
207
  
  		if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
accfe0e35   Hans Schillstrom   ipv6: ipv6_find_h...
208
  			if (target < 0 || found)
f8f626754   Jesse Gross   ipv6: Move ipv6_f...
209
210
211
212
213
  				break;
  			return -ENOENT;
  		}
  
  		hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
63159f29b   Ian Morris   ipv6: coding styl...
214
  		if (!hp)
f8f626754   Jesse Gross   ipv6: Move ipv6_f...
215
  			return -EBADMSG;
9195bb8e3   Ansis Atteka   ipv6: improve ipv...
216
217
218
219
220
221
  
  		if (nexthdr == NEXTHDR_ROUTING) {
  			struct ipv6_rt_hdr _rh, *rh;
  
  			rh = skb_header_pointer(skb, start, sizeof(_rh),
  						&_rh);
63159f29b   Ian Morris   ipv6: coding styl...
222
  			if (!rh)
9195bb8e3   Ansis Atteka   ipv6: improve ipv...
223
224
225
226
227
228
  				return -EBADMSG;
  
  			if (flags && (*flags & IP6_FH_F_SKIP_RH) &&
  			    rh->segments_left == 0)
  				found = false;
  		}
f8f626754   Jesse Gross   ipv6: Move ipv6_f...
229
230
231
232
233
234
235
236
237
238
239
  		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);
63159f29b   Ian Morris   ipv6: coding styl...
240
  			if (!fp)
f8f626754   Jesse Gross   ipv6: Move ipv6_f...
241
242
243
244
245
246
247
248
249
250
251
  				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;
  				}
5d150a985   Florian Westphal   ipv6: re-enable f...
252
253
254
255
256
  				if (!found)
  					return -ENOENT;
  				if (fragoff)
  					*fragoff = _frag_off;
  				break;
f8f626754   Jesse Gross   ipv6: Move ipv6_f...
257
258
259
260
261
262
263
264
  			}
  			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);
9195bb8e3   Ansis Atteka   ipv6: improve ipv...
265
266
267
268
269
270
  		if (!found) {
  			nexthdr = hp->nexthdr;
  			len -= hdrlen;
  			start += hdrlen;
  		}
  	} while (!found);
f8f626754   Jesse Gross   ipv6: Move ipv6_f...
271
272
273
274
275
  
  	*offset = start;
  	return nexthdr;
  }
  EXPORT_SYMBOL(ipv6_find_hdr);
e7165030d   David S. Miller   Merge branch 'mas...
276