0001
0002
0003
0004
0005
0006 #include <linux/export.h>
0007 #include <net/ipv6.h>
0008
0009
0010
0011
0012
0013 bool ipv6_ext_hdr(u8 nexthdr)
0014 {
0015
0016
0017
0018 return (nexthdr == NEXTHDR_HOP) ||
0019 (nexthdr == NEXTHDR_ROUTING) ||
0020 (nexthdr == NEXTHDR_FRAGMENT) ||
0021 (nexthdr == NEXTHDR_AUTH) ||
0022 (nexthdr == NEXTHDR_NONE) ||
0023 (nexthdr == NEXTHDR_DEST);
0024 }
0025 EXPORT_SYMBOL(ipv6_ext_hdr);
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067
0068
0069
0070
0071
0072 int ipv6_skip_exthdr(const struct sk_buff *skb, int start, u8 *nexthdrp,
0073 __be16 *frag_offp)
0074 {
0075 u8 nexthdr = *nexthdrp;
0076
0077 *frag_offp = 0;
0078
0079 while (ipv6_ext_hdr(nexthdr)) {
0080 struct ipv6_opt_hdr _hdr, *hp;
0081 int hdrlen;
0082
0083 if (nexthdr == NEXTHDR_NONE)
0084 return -1;
0085 hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
0086 if (!hp)
0087 return -1;
0088 if (nexthdr == NEXTHDR_FRAGMENT) {
0089 __be16 _frag_off, *fp;
0090 fp = skb_header_pointer(skb,
0091 start+offsetof(struct frag_hdr,
0092 frag_off),
0093 sizeof(_frag_off),
0094 &_frag_off);
0095 if (!fp)
0096 return -1;
0097
0098 *frag_offp = *fp;
0099 if (ntohs(*frag_offp) & ~0x7)
0100 break;
0101 hdrlen = 8;
0102 } else if (nexthdr == NEXTHDR_AUTH)
0103 hdrlen = ipv6_authlen(hp);
0104 else
0105 hdrlen = ipv6_optlen(hp);
0106
0107 nexthdr = hp->nexthdr;
0108 start += hdrlen;
0109 }
0110
0111 *nexthdrp = nexthdr;
0112 return start;
0113 }
0114 EXPORT_SYMBOL(ipv6_skip_exthdr);
0115
0116 int ipv6_find_tlv(const struct sk_buff *skb, int offset, int type)
0117 {
0118 const unsigned char *nh = skb_network_header(skb);
0119 int packet_len = skb_tail_pointer(skb) - skb_network_header(skb);
0120 struct ipv6_opt_hdr *hdr;
0121 int len;
0122
0123 if (offset + 2 > packet_len)
0124 goto bad;
0125 hdr = (struct ipv6_opt_hdr *)(nh + offset);
0126 len = ((hdr->hdrlen + 1) << 3);
0127
0128 if (offset + len > packet_len)
0129 goto bad;
0130
0131 offset += 2;
0132 len -= 2;
0133
0134 while (len > 0) {
0135 int opttype = nh[offset];
0136 int optlen;
0137
0138 if (opttype == type)
0139 return offset;
0140
0141 switch (opttype) {
0142 case IPV6_TLV_PAD1:
0143 optlen = 1;
0144 break;
0145 default:
0146 optlen = nh[offset + 1] + 2;
0147 if (optlen > len)
0148 goto bad;
0149 break;
0150 }
0151 offset += optlen;
0152 len -= optlen;
0153 }
0154
0155 bad:
0156 return -1;
0157 }
0158 EXPORT_SYMBOL_GPL(ipv6_find_tlv);
0159
0160
0161
0162
0163
0164
0165
0166
0167
0168
0169
0170
0171
0172
0173
0174
0175
0176
0177
0178
0179
0180
0181
0182
0183
0184
0185
0186 int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
0187 int target, unsigned short *fragoff, int *flags)
0188 {
0189 unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr);
0190 u8 nexthdr = ipv6_hdr(skb)->nexthdr;
0191 bool found;
0192
0193 if (fragoff)
0194 *fragoff = 0;
0195
0196 if (*offset) {
0197 struct ipv6hdr _ip6, *ip6;
0198
0199 ip6 = skb_header_pointer(skb, *offset, sizeof(_ip6), &_ip6);
0200 if (!ip6 || (ip6->version != 6))
0201 return -EBADMSG;
0202 start = *offset + sizeof(struct ipv6hdr);
0203 nexthdr = ip6->nexthdr;
0204 }
0205
0206 do {
0207 struct ipv6_opt_hdr _hdr, *hp;
0208 unsigned int hdrlen;
0209 found = (nexthdr == target);
0210
0211 if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
0212 if (target < 0 || found)
0213 break;
0214 return -ENOENT;
0215 }
0216
0217 hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
0218 if (!hp)
0219 return -EBADMSG;
0220
0221 if (nexthdr == NEXTHDR_ROUTING) {
0222 struct ipv6_rt_hdr _rh, *rh;
0223
0224 rh = skb_header_pointer(skb, start, sizeof(_rh),
0225 &_rh);
0226 if (!rh)
0227 return -EBADMSG;
0228
0229 if (flags && (*flags & IP6_FH_F_SKIP_RH) &&
0230 rh->segments_left == 0)
0231 found = false;
0232 }
0233
0234 if (nexthdr == NEXTHDR_FRAGMENT) {
0235 unsigned short _frag_off;
0236 __be16 *fp;
0237
0238 if (flags)
0239 *flags |= IP6_FH_F_FRAG;
0240 fp = skb_header_pointer(skb,
0241 start+offsetof(struct frag_hdr,
0242 frag_off),
0243 sizeof(_frag_off),
0244 &_frag_off);
0245 if (!fp)
0246 return -EBADMSG;
0247
0248 _frag_off = ntohs(*fp) & ~0x7;
0249 if (_frag_off) {
0250 if (target < 0 &&
0251 ((!ipv6_ext_hdr(hp->nexthdr)) ||
0252 hp->nexthdr == NEXTHDR_NONE)) {
0253 if (fragoff)
0254 *fragoff = _frag_off;
0255 return hp->nexthdr;
0256 }
0257 if (!found)
0258 return -ENOENT;
0259 if (fragoff)
0260 *fragoff = _frag_off;
0261 break;
0262 }
0263 hdrlen = 8;
0264 } else if (nexthdr == NEXTHDR_AUTH) {
0265 if (flags && (*flags & IP6_FH_F_AUTH) && (target < 0))
0266 break;
0267 hdrlen = ipv6_authlen(hp);
0268 } else
0269 hdrlen = ipv6_optlen(hp);
0270
0271 if (!found) {
0272 nexthdr = hp->nexthdr;
0273 start += hdrlen;
0274 }
0275 } while (!found);
0276
0277 *offset = start;
0278 return nexthdr;
0279 }
0280 EXPORT_SYMBOL(ipv6_find_hdr);