~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/net/ipv6/exthdrs_core.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php