2 * IPv6 library code, needed by static components when full IPv6 support is
3 * not configured or static.
5 #include <linux/export.h>
9 * find out if nexthdr is a well-known extension header or a protocol
12 bool ipv6_ext_hdr(u8 nexthdr
)
15 * find out if nexthdr is an extension header or a protocol
17 return (nexthdr
== NEXTHDR_HOP
) ||
18 (nexthdr
== NEXTHDR_ROUTING
) ||
19 (nexthdr
== NEXTHDR_FRAGMENT
) ||
20 (nexthdr
== NEXTHDR_AUTH
) ||
21 (nexthdr
== NEXTHDR_NONE
) ||
22 (nexthdr
== NEXTHDR_DEST
);
24 EXPORT_SYMBOL(ipv6_ext_hdr
);
27 * Skip any extension headers. This is used by the ICMP module.
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.
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).
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.
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.
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.
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,
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,
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.
71 int ipv6_skip_exthdr(const struct sk_buff
*skb
, int start
, u8
*nexthdrp
,
74 u8 nexthdr
= *nexthdrp
;
78 while (ipv6_ext_hdr(nexthdr
)) {
79 struct ipv6_opt_hdr _hdr
, *hp
;
82 if (nexthdr
== NEXTHDR_NONE
)
84 hp
= skb_header_pointer(skb
, start
, sizeof(_hdr
), &_hdr
);
87 if (nexthdr
== NEXTHDR_FRAGMENT
) {
88 __be16 _frag_off
, *fp
;
89 fp
= skb_header_pointer(skb
,
90 start
+offsetof(struct frag_hdr
,
98 if (ntohs(*frag_offp
) & ~0x7)
101 } else if (nexthdr
== NEXTHDR_AUTH
)
102 hdrlen
= (hp
->hdrlen
+2)<<2;
104 hdrlen
= ipv6_optlen(hp
);
106 nexthdr
= hp
->nexthdr
;
113 EXPORT_SYMBOL(ipv6_skip_exthdr
);
115 int ipv6_find_tlv(struct sk_buff
*skb
, int offset
, int type
)
117 const unsigned char *nh
= skb_network_header(skb
);
118 int packet_len
= skb
->tail
- skb
->network_header
;
119 struct ipv6_opt_hdr
*hdr
;
122 if (offset
+ 2 > packet_len
)
124 hdr
= (struct ipv6_opt_hdr
*)(nh
+ offset
);
125 len
= ((hdr
->hdrlen
+ 1) << 3);
127 if (offset
+ len
> packet_len
)
134 int opttype
= nh
[offset
];
145 optlen
= nh
[offset
+ 1] + 2;
157 EXPORT_SYMBOL_GPL(ipv6_find_tlv
);
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
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.
168 * If target header is found, its offset is set in *offset and return protocol
169 * number. Otherwise, return -1.
171 * If the first fragment doesn't contain the final protocol header or
172 * NEXTHDR_NONE it is considered invalid.
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 and fragment offset is stored in *fragoff if fragoff
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.
185 int ipv6_find_hdr(const struct sk_buff
*skb
, unsigned int *offset
,
186 int target
, unsigned short *fragoff
, int *flags
)
188 unsigned int start
= skb_network_offset(skb
) + sizeof(struct ipv6hdr
);
189 u8 nexthdr
= ipv6_hdr(skb
)->nexthdr
;
197 struct ipv6hdr _ip6
, *ip6
;
199 ip6
= skb_header_pointer(skb
, *offset
, sizeof(_ip6
), &_ip6
);
200 if (!ip6
|| (ip6
->version
!= 6)) {
201 printk(KERN_ERR
"IPv6 header not found\n");
204 start
= *offset
+ sizeof(struct ipv6hdr
);
205 nexthdr
= ip6
->nexthdr
;
207 len
= skb
->len
- start
;
210 struct ipv6_opt_hdr _hdr
, *hp
;
212 found
= (nexthdr
== target
);
214 if ((!ipv6_ext_hdr(nexthdr
)) || nexthdr
== NEXTHDR_NONE
) {
215 if (target
< 0 || found
)
220 hp
= skb_header_pointer(skb
, start
, sizeof(_hdr
), &_hdr
);
224 if (nexthdr
== NEXTHDR_ROUTING
) {
225 struct ipv6_rt_hdr _rh
, *rh
;
227 rh
= skb_header_pointer(skb
, start
, sizeof(_rh
),
232 if (flags
&& (*flags
& IP6_FH_F_SKIP_RH
) &&
233 rh
->segments_left
== 0)
237 if (nexthdr
== NEXTHDR_FRAGMENT
) {
238 unsigned short _frag_off
;
241 if (flags
) /* Indicate that this is a fragment */
242 *flags
|= IP6_FH_F_FRAG
;
243 fp
= skb_header_pointer(skb
,
244 start
+offsetof(struct frag_hdr
,
251 _frag_off
= ntohs(*fp
) & ~0x7;
254 ((!ipv6_ext_hdr(hp
->nexthdr
)) ||
255 hp
->nexthdr
== NEXTHDR_NONE
)) {
257 *fragoff
= _frag_off
;
263 } else if (nexthdr
== NEXTHDR_AUTH
) {
264 if (flags
&& (*flags
& IP6_FH_F_AUTH
) && (target
< 0))
266 hdrlen
= (hp
->hdrlen
+ 2) << 2;
268 hdrlen
= ipv6_optlen(hp
);
271 nexthdr
= hp
->nexthdr
;
280 EXPORT_SYMBOL(ipv6_find_hdr
);