cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

exthdrs_core.c (7648B)


      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
     13bool 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}
     25EXPORT_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
     72int 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}
    114EXPORT_SYMBOL(ipv6_skip_exthdr);
    115
    116int 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			optlen = nh[offset + 1] + 2;
    147			if (optlen > len)
    148				goto bad;
    149			break;
    150		}
    151		offset += optlen;
    152		len -= optlen;
    153	}
    154	/* not_found */
    155 bad:
    156	return -1;
    157}
    158EXPORT_SYMBOL_GPL(ipv6_find_tlv);
    159
    160/*
    161 * find the offset to specified header or the protocol number of last header
    162 * if target < 0. "last header" is transport protocol header, ESP, or
    163 * "No next header".
    164 *
    165 * Note that *offset is used as input/output parameter, and if it is not zero,
    166 * then it must be a valid offset to an inner IPv6 header. This can be used
    167 * to explore inner IPv6 header, eg. ICMPv6 error messages.
    168 *
    169 * If target header is found, its offset is set in *offset and return protocol
    170 * number. Otherwise, return -1.
    171 *
    172 * If the first fragment doesn't contain the final protocol header or
    173 * NEXTHDR_NONE it is considered invalid.
    174 *
    175 * Note that non-1st fragment is special case that "the protocol number
    176 * of last header" is "next header" field in Fragment header. In this case,
    177 * *offset is meaningless and fragment offset is stored in *fragoff if fragoff
    178 * isn't NULL.
    179 *
    180 * if flags is not NULL and it's a fragment, then the frag flag
    181 * IP6_FH_F_FRAG will be set. If it's an AH header, the
    182 * IP6_FH_F_AUTH flag is set and target < 0, then this function will
    183 * stop at the AH header. If IP6_FH_F_SKIP_RH flag was passed, then this
    184 * function will skip all those routing headers, where segements_left was 0.
    185 */
    186int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
    187		  int target, unsigned short *fragoff, int *flags)
    188{
    189	unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr);
    190	u8 nexthdr = ipv6_hdr(skb)->nexthdr;
    191	bool found;
    192
    193	if (fragoff)
    194		*fragoff = 0;
    195
    196	if (*offset) {
    197		struct ipv6hdr _ip6, *ip6;
    198
    199		ip6 = skb_header_pointer(skb, *offset, sizeof(_ip6), &_ip6);
    200		if (!ip6 || (ip6->version != 6))
    201			return -EBADMSG;
    202		start = *offset + sizeof(struct ipv6hdr);
    203		nexthdr = ip6->nexthdr;
    204	}
    205
    206	do {
    207		struct ipv6_opt_hdr _hdr, *hp;
    208		unsigned int hdrlen;
    209		found = (nexthdr == target);
    210
    211		if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
    212			if (target < 0 || found)
    213				break;
    214			return -ENOENT;
    215		}
    216
    217		hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
    218		if (!hp)
    219			return -EBADMSG;
    220
    221		if (nexthdr == NEXTHDR_ROUTING) {
    222			struct ipv6_rt_hdr _rh, *rh;
    223
    224			rh = skb_header_pointer(skb, start, sizeof(_rh),
    225						&_rh);
    226			if (!rh)
    227				return -EBADMSG;
    228
    229			if (flags && (*flags & IP6_FH_F_SKIP_RH) &&
    230			    rh->segments_left == 0)
    231				found = false;
    232		}
    233
    234		if (nexthdr == NEXTHDR_FRAGMENT) {
    235			unsigned short _frag_off;
    236			__be16 *fp;
    237
    238			if (flags)	/* Indicate that this is a fragment */
    239				*flags |= IP6_FH_F_FRAG;
    240			fp = skb_header_pointer(skb,
    241						start+offsetof(struct frag_hdr,
    242							       frag_off),
    243						sizeof(_frag_off),
    244						&_frag_off);
    245			if (!fp)
    246				return -EBADMSG;
    247
    248			_frag_off = ntohs(*fp) & ~0x7;
    249			if (_frag_off) {
    250				if (target < 0 &&
    251				    ((!ipv6_ext_hdr(hp->nexthdr)) ||
    252				     hp->nexthdr == NEXTHDR_NONE)) {
    253					if (fragoff)
    254						*fragoff = _frag_off;
    255					return hp->nexthdr;
    256				}
    257				if (!found)
    258					return -ENOENT;
    259				if (fragoff)
    260					*fragoff = _frag_off;
    261				break;
    262			}
    263			hdrlen = 8;
    264		} else if (nexthdr == NEXTHDR_AUTH) {
    265			if (flags && (*flags & IP6_FH_F_AUTH) && (target < 0))
    266				break;
    267			hdrlen = ipv6_authlen(hp);
    268		} else
    269			hdrlen = ipv6_optlen(hp);
    270
    271		if (!found) {
    272			nexthdr = hp->nexthdr;
    273			start += hdrlen;
    274		}
    275	} while (!found);
    276
    277	*offset = start;
    278	return nexthdr;
    279}
    280EXPORT_SYMBOL(ipv6_find_hdr);