Branch data Line data Source code
1 : : /*
2 : : * IPv6 library code, needed by static components when full IPv6 support is
3 : : * not configured or static.
4 : : */
5 : : #include <linux/export.h>
6 : : #include <net/ipv6.h>
7 : :
8 : : /*
9 : : * find out if nexthdr is a well-known extension header or a protocol
10 : : */
11 : :
12 : 0 : bool ipv6_ext_hdr(u8 nexthdr)
13 : : {
14 : : /*
15 : : * find out if nexthdr is an extension header or a protocol
16 : : */
17 : 0 : return (nexthdr == NEXTHDR_HOP) ||
18 : 0 : (nexthdr == NEXTHDR_ROUTING) ||
19 [ # # ][ # # ]: 0 : (nexthdr == NEXTHDR_FRAGMENT) ||
[ # # ][ # # ]
20 : 0 : (nexthdr == NEXTHDR_AUTH) ||
21 [ # # ][ # # ]: 0 : (nexthdr == NEXTHDR_NONE) ||
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
22 : : (nexthdr == NEXTHDR_DEST);
23 : : }
24 : : EXPORT_SYMBOL(ipv6_ext_hdr);
25 : :
26 : : /*
27 : : * Skip any extension headers. This is used by the ICMP module.
28 : : *
29 : : * Note that strictly speaking this conflicts with RFC 2460 4.0:
30 : : * ...The contents and semantics of each extension header determine whether
31 : : * or not to proceed to the next header. Therefore, extension headers must
32 : : * be processed strictly in the order they appear in the packet; a
33 : : * receiver must not, for example, scan through a packet looking for a
34 : : * particular kind of extension header and process that header prior to
35 : : * processing all preceding ones.
36 : : *
37 : : * We do exactly this. This is a protocol bug. We can't decide after a
38 : : * seeing an unknown discard-with-error flavour TLV option if it's a
39 : : * ICMP error message or not (errors should never be send in reply to
40 : : * ICMP error messages).
41 : : *
42 : : * But I see no other way to do this. This might need to be reexamined
43 : : * when Linux implements ESP (and maybe AUTH) headers.
44 : : * --AK
45 : : *
46 : : * This function parses (probably truncated) exthdr set "hdr".
47 : : * "nexthdrp" initially points to some place,
48 : : * where type of the first header can be found.
49 : : *
50 : : * It skips all well-known exthdrs, and returns pointer to the start
51 : : * of unparsable area i.e. the first header with unknown type.
52 : : * If it is not NULL *nexthdr is updated by type/protocol of this header.
53 : : *
54 : : * NOTES: - if packet terminated with NEXTHDR_NONE it returns NULL.
55 : : * - it may return pointer pointing beyond end of packet,
56 : : * if the last recognized header is truncated in the middle.
57 : : * - if packet is truncated, so that all parsed headers are skipped,
58 : : * it returns NULL.
59 : : * - First fragment header is skipped, not-first ones
60 : : * are considered as unparsable.
61 : : * - Reports the offset field of the final fragment header so it is
62 : : * possible to tell whether this is a first fragment, later fragment,
63 : : * or not fragmented.
64 : : * - ESP is unparsable for now and considered like
65 : : * normal payload protocol.
66 : : * - Note also special handling of AUTH header. Thanks to IPsec wizards.
67 : : *
68 : : * --ANK (980726)
69 : : */
70 : :
71 : 0 : int ipv6_skip_exthdr(const struct sk_buff *skb, int start, u8 *nexthdrp,
72 : : __be16 *frag_offp)
73 : : {
74 : 0 : u8 nexthdr = *nexthdrp;
75 : :
76 : 0 : *frag_offp = 0;
77 : :
78 [ # # ]: 0 : while (ipv6_ext_hdr(nexthdr)) {
79 : : struct ipv6_opt_hdr _hdr, *hp;
80 : : int hdrlen;
81 : :
82 [ # # ]: 0 : if (nexthdr == NEXTHDR_NONE)
83 : 0 : return -1;
84 : : hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
85 [ # # ]: 0 : if (hp == NULL)
86 : : return -1;
87 [ # # ]: 0 : if (nexthdr == NEXTHDR_FRAGMENT) {
88 : : __be16 _frag_off, *fp;
89 : 0 : fp = skb_header_pointer(skb,
90 : 0 : start+offsetof(struct frag_hdr,
91 : : frag_off),
92 : : sizeof(_frag_off),
93 : : &_frag_off);
94 [ # # ]: 0 : if (fp == NULL)
95 : 0 : return -1;
96 : :
97 : 0 : *frag_offp = *fp;
98 [ # # ][ # # ]: 0 : if (ntohs(*frag_offp) & ~0x7)
99 : : break;
100 : : hdrlen = 8;
101 [ # # ]: 0 : } else if (nexthdr == NEXTHDR_AUTH)
102 : 0 : hdrlen = (hp->hdrlen+2)<<2;
103 : : else
104 : 0 : hdrlen = ipv6_optlen(hp);
105 : :
106 : 0 : nexthdr = hp->nexthdr;
107 : 0 : start += hdrlen;
108 : : }
109 : :
110 : 0 : *nexthdrp = nexthdr;
111 : 0 : return start;
112 : : }
113 : : EXPORT_SYMBOL(ipv6_skip_exthdr);
114 : :
115 : 0 : int ipv6_find_tlv(struct sk_buff *skb, int offset, int type)
116 : : {
117 : : const unsigned char *nh = skb_network_header(skb);
118 : 0 : int packet_len = skb_tail_pointer(skb) - skb_network_header(skb);
119 : : struct ipv6_opt_hdr *hdr;
120 : : int len;
121 : :
122 [ # # ]: 0 : if (offset + 2 > packet_len)
123 : : goto bad;
124 : 0 : hdr = (struct ipv6_opt_hdr *)(nh + offset);
125 : 0 : len = ((hdr->hdrlen + 1) << 3);
126 : :
127 [ # # ]: 0 : if (offset + len > packet_len)
128 : : goto bad;
129 : :
130 : : offset += 2;
131 : 0 : len -= 2;
132 : :
133 [ # # ]: 0 : while (len > 0) {
134 : 0 : int opttype = nh[offset];
135 : : int optlen;
136 : :
137 [ # # ]: 0 : if (opttype == type)
138 : : return offset;
139 : :
140 [ # # ]: 0 : switch (opttype) {
141 : : case IPV6_TLV_PAD1:
142 : : optlen = 1;
143 : : break;
144 : : default:
145 : 0 : optlen = nh[offset + 1] + 2;
146 [ # # ]: 0 : if (optlen > len)
147 : : goto bad;
148 : : break;
149 : : }
150 : 0 : offset += optlen;
151 : 0 : len -= optlen;
152 : : }
153 : : /* not_found */
154 : : bad:
155 : : return -1;
156 : : }
157 : : EXPORT_SYMBOL_GPL(ipv6_find_tlv);
158 : :
159 : : /*
160 : : * find the offset to specified header or the protocol number of last header
161 : : * if target < 0. "last header" is transport protocol header, ESP, or
162 : : * "No next header".
163 : : *
164 : : * Note that *offset is used as input/output parameter. an if it is not zero,
165 : : * then it must be a valid offset to an inner IPv6 header. This can be used
166 : : * to explore inner IPv6 header, eg. ICMPv6 error messages.
167 : : *
168 : : * If target header is found, its offset is set in *offset and return protocol
169 : : * number. Otherwise, return -ENOENT or -EBADMSG.
170 : : *
171 : : * If the first fragment doesn't contain the final protocol header or
172 : : * NEXTHDR_NONE it is considered invalid.
173 : : *
174 : : * Note that non-1st fragment is special case that "the protocol number
175 : : * of last header" is "next header" field in Fragment header. In this case,
176 : : * *offset is meaningless. If fragoff is not NULL, the fragment offset is
177 : : * stored in *fragoff; if it is NULL, return -EINVAL.
178 : : *
179 : : * if flags is not NULL and it's a fragment, then the frag flag
180 : : * IP6_FH_F_FRAG will be set. If it's an AH header, the
181 : : * IP6_FH_F_AUTH flag is set and target < 0, then this function will
182 : : * stop at the AH header. If IP6_FH_F_SKIP_RH flag was passed, then this
183 : : * function will skip all those routing headers, where segements_left was 0.
184 : : */
185 : 0 : int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
186 : : int target, unsigned short *fragoff, int *flags)
187 : : {
188 : 0 : unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr);
189 : 0 : u8 nexthdr = ipv6_hdr(skb)->nexthdr;
190 : : unsigned int len;
191 : : bool found;
192 : :
193 [ # # ]: 0 : if (fragoff)
194 : 0 : *fragoff = 0;
195 : :
196 [ # # ]: 0 : if (*offset) {
197 : : struct ipv6hdr _ip6, *ip6;
198 : :
199 : 0 : ip6 = skb_header_pointer(skb, *offset, sizeof(_ip6), &_ip6);
200 [ # # ][ # # ]: 0 : if (!ip6 || (ip6->version != 6)) {
201 : 0 : printk(KERN_ERR "IPv6 header not found\n");
202 : 0 : return -EBADMSG;
203 : : }
204 : 0 : start = *offset + sizeof(struct ipv6hdr);
205 : 0 : nexthdr = ip6->nexthdr;
206 : : }
207 : : len = skb->len - start;
208 : :
209 : : do {
210 : : struct ipv6_opt_hdr _hdr, *hp;
211 : : unsigned int hdrlen;
212 : 0 : found = (nexthdr == target);
213 : :
214 [ # # ][ # # ]: 0 : if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
215 [ # # ]: 0 : if (target < 0 || found)
216 : : break;
217 : 0 : return -ENOENT;
218 : : }
219 : :
220 : 0 : hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
221 [ # # ]: 0 : if (hp == NULL)
222 : : return -EBADMSG;
223 : :
224 [ # # ]: 0 : if (nexthdr == NEXTHDR_ROUTING) {
225 : : struct ipv6_rt_hdr _rh, *rh;
226 : :
227 : : rh = skb_header_pointer(skb, start, sizeof(_rh),
228 : : &_rh);
229 [ # # ]: 0 : if (rh == NULL)
230 : 0 : return -EBADMSG;
231 : :
232 [ # # ][ # # ]: 0 : if (flags && (*flags & IP6_FH_F_SKIP_RH) &&
[ # # ]
233 : 0 : rh->segments_left == 0)
234 : : found = false;
235 : : }
236 : :
237 [ # # ]: 0 : if (nexthdr == NEXTHDR_FRAGMENT) {
238 : : unsigned short _frag_off;
239 : : __be16 *fp;
240 : :
241 [ # # ]: 0 : if (flags) /* Indicate that this is a fragment */
242 : 0 : *flags |= IP6_FH_F_FRAG;
243 : 0 : fp = skb_header_pointer(skb,
244 : 0 : start+offsetof(struct frag_hdr,
245 : : frag_off),
246 : : sizeof(_frag_off),
247 : : &_frag_off);
248 [ # # ]: 0 : if (fp == NULL)
249 : 0 : return -EBADMSG;
250 : :
251 [ # # ]: 0 : _frag_off = ntohs(*fp) & ~0x7;
252 [ # # ]: 0 : if (_frag_off) {
253 [ # # ][ # # ]: 0 : if (target < 0 &&
254 [ # # ]: 0 : ((!ipv6_ext_hdr(hp->nexthdr)) ||
255 : : hp->nexthdr == NEXTHDR_NONE)) {
256 [ # # ]: 0 : if (fragoff) {
257 : 0 : *fragoff = _frag_off;
258 : 0 : return hp->nexthdr;
259 : : } else {
260 : : return -EINVAL;
261 : : }
262 : : }
263 : : return -ENOENT;
264 : : }
265 : : hdrlen = 8;
266 [ # # ]: 0 : } else if (nexthdr == NEXTHDR_AUTH) {
267 [ # # ][ # # ]: 0 : if (flags && (*flags & IP6_FH_F_AUTH) && (target < 0))
[ # # ]
268 : : break;
269 : 0 : hdrlen = (hp->hdrlen + 2) << 2;
270 : : } else
271 : 0 : hdrlen = ipv6_optlen(hp);
272 : :
273 [ # # ]: 0 : if (!found) {
274 : 0 : nexthdr = hp->nexthdr;
275 : : len -= hdrlen;
276 : 0 : start += hdrlen;
277 : : }
278 [ # # ]: 0 : } while (!found);
279 : :
280 : 0 : *offset = start;
281 : 0 : return nexthdr;
282 : : }
283 : : EXPORT_SYMBOL(ipv6_find_hdr);
284 : :
|