Blame view
net/ipv6/exthdrs_core.c
7.55 KB
1da177e4c 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 net: Add export.h... |
5 |
#include <linux/export.h> |
1da177e4c Linux-2.6.12-rc2 |
6 |
#include <net/ipv6.h> |
1ab1457c4 [NET] IPV6: Fix w... |
7 |
/* |
1da177e4c Linux-2.6.12-rc2 |
8 9 |
* find out if nexthdr is a well-known extension header or a protocol */ |
a50feda54 ipv6: bool/const ... |
10 |
bool ipv6_ext_hdr(u8 nexthdr) |
1da177e4c Linux-2.6.12-rc2 |
11 |
{ |
1ab1457c4 [NET] IPV6: Fix w... |
12 |
/* |
1da177e4c Linux-2.6.12-rc2 |
13 14 |
* find out if nexthdr is an extension header or a protocol */ |
a02cec215 net: return opera... |
15 |
return (nexthdr == NEXTHDR_HOP) || |
1da177e4c Linux-2.6.12-rc2 |
16 17 18 19 |
(nexthdr == NEXTHDR_ROUTING) || (nexthdr == NEXTHDR_FRAGMENT) || (nexthdr == NEXTHDR_AUTH) || (nexthdr == NEXTHDR_NONE) || |
a02cec215 net: return opera... |
20 |
(nexthdr == NEXTHDR_DEST); |
1da177e4c Linux-2.6.12-rc2 |
21 |
} |
81bd60b51 net/ipv6/exthdrs_... |
22 |
EXPORT_SYMBOL(ipv6_ext_hdr); |
1da177e4c 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 [NET] IPV6: Fix w... |
28 |
* ...The contents and semantics of each extension header determine whether |
1da177e4c 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 [NET] IPV6: Fix w... |
34 |
* |
1da177e4c Linux-2.6.12-rc2 |
35 |
* We do exactly this. This is a protocol bug. We can't decide after a |
1ab1457c4 [NET] IPV6: Fix w... |
36 |
* seeing an unknown discard-with-error flavour TLV option if it's a |
1da177e4c Linux-2.6.12-rc2 |
37 38 |
* ICMP error message or not (errors should never be send in reply to * ICMP error messages). |
1ab1457c4 [NET] IPV6: Fix w... |
39 |
* |
1da177e4c 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 [SELINUX]: Fix ip... |
44 45 |
* This function parses (probably truncated) exthdr set "hdr". * "nexthdrp" initially points to some place, |
1da177e4c 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 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 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 ipv6: Add fragmen... |
68 69 |
int ipv6_skip_exthdr(const struct sk_buff *skb, int start, u8 *nexthdrp, __be16 *frag_offp) |
1da177e4c Linux-2.6.12-rc2 |
70 71 |
{ u8 nexthdr = *nexthdrp; |
75f2811c6 ipv6: Add fragmen... |
72 |
*frag_offp = 0; |
1da177e4c Linux-2.6.12-rc2 |
73 74 75 |
while (ipv6_ext_hdr(nexthdr)) { struct ipv6_opt_hdr _hdr, *hp; int hdrlen; |
1da177e4c Linux-2.6.12-rc2 |
76 77 78 |
if (nexthdr == NEXTHDR_NONE) return -1; hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr); |
63159f29b ipv6: coding styl... |
79 |
if (!hp) |
0d3d077cd [SELINUX]: Fix ip... |
80 |
return -1; |
1da177e4c Linux-2.6.12-rc2 |
81 |
if (nexthdr == NEXTHDR_FRAGMENT) { |
e69a4adc6 [IPV6]: Misc endi... |
82 |
__be16 _frag_off, *fp; |
1da177e4c 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 ipv6: coding styl... |
88 |
if (!fp) |
1da177e4c Linux-2.6.12-rc2 |
89 |
return -1; |
75f2811c6 ipv6: Add fragmen... |
90 91 |
*frag_offp = *fp; if (ntohs(*frag_offp) & ~0x7) |
1da177e4c Linux-2.6.12-rc2 |
92 93 94 |
break; hdrlen = 8; } else if (nexthdr == NEXTHDR_AUTH) |
1ab1457c4 [NET] IPV6: Fix w... |
95 |
hdrlen = (hp->hdrlen+2)<<2; |
1da177e4c Linux-2.6.12-rc2 |
96 |
else |
1ab1457c4 [NET] IPV6: Fix w... |
97 |
hdrlen = ipv6_optlen(hp); |
1da177e4c Linux-2.6.12-rc2 |
98 99 |
nexthdr = hp->nexthdr; |
1da177e4c Linux-2.6.12-rc2 |
100 101 102 103 104 105 |
start += hdrlen; } *nexthdrp = nexthdr; return start; } |
1da177e4c Linux-2.6.12-rc2 |
106 |
EXPORT_SYMBOL(ipv6_skip_exthdr); |
3c73a0368 ipv6: Update ipv6... |
107 |
|
0868383b8 ipv6: constify th... |
108 |
int ipv6_find_tlv(const struct sk_buff *skb, int offset, int type) |
3c73a0368 ipv6: Update ipv6... |
109 110 |
{ const unsigned char *nh = skb_network_header(skb); |
29a3cad5c ipv6: Correct com... |
111 |
int packet_len = skb_tail_pointer(skb) - skb_network_header(skb); |
3c73a0368 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 Merge branch 'mas... |
151 |
|
f8f626754 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 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 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 ipv6: improve ipv... |
184 |
bool found; |
f8f626754 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 ipv6: improve ipv... |
202 |
do { |
f8f626754 ipv6: Move ipv6_f... |
203 204 |
struct ipv6_opt_hdr _hdr, *hp; unsigned int hdrlen; |
9195bb8e3 ipv6: improve ipv... |
205 |
found = (nexthdr == target); |
f8f626754 ipv6: Move ipv6_f... |
206 207 |
if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) { |
accfe0e35 ipv6: ipv6_find_h... |
208 |
if (target < 0 || found) |
f8f626754 ipv6: Move ipv6_f... |
209 210 211 212 213 |
break; return -ENOENT; } hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr); |
63159f29b ipv6: coding styl... |
214 |
if (!hp) |
f8f626754 ipv6: Move ipv6_f... |
215 |
return -EBADMSG; |
9195bb8e3 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 ipv6: coding styl... |
222 |
if (!rh) |
9195bb8e3 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 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 ipv6: coding styl... |
240 |
if (!fp) |
f8f626754 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 ipv6: re-enable f... |
252 253 254 255 256 |
if (!found) return -ENOENT; if (fragoff) *fragoff = _frag_off; break; |
f8f626754 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 ipv6: improve ipv... |
265 266 267 268 269 270 |
if (!found) { nexthdr = hp->nexthdr; len -= hdrlen; start += hdrlen; } } while (!found); |
f8f626754 ipv6: Move ipv6_f... |
271 272 273 274 275 |
*offset = start; return nexthdr; } EXPORT_SYMBOL(ipv6_find_hdr); |
e7165030d Merge branch 'mas... |
276 |