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

ip_options.c (14840B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * INET		An implementation of the TCP/IP protocol suite for the LINUX
      4 *		operating system.  INET is implemented using the  BSD Socket
      5 *		interface as the means of communication with the user level.
      6 *
      7 *		The options processing module for ip.c
      8 *
      9 * Authors:	A.N.Kuznetsov
     10 *
     11 */
     12
     13#define pr_fmt(fmt) "IPv4: " fmt
     14
     15#include <linux/capability.h>
     16#include <linux/module.h>
     17#include <linux/slab.h>
     18#include <linux/types.h>
     19#include <linux/uaccess.h>
     20#include <asm/unaligned.h>
     21#include <linux/skbuff.h>
     22#include <linux/ip.h>
     23#include <linux/icmp.h>
     24#include <linux/netdevice.h>
     25#include <linux/rtnetlink.h>
     26#include <net/sock.h>
     27#include <net/ip.h>
     28#include <net/icmp.h>
     29#include <net/route.h>
     30#include <net/cipso_ipv4.h>
     31#include <net/ip_fib.h>
     32
     33/*
     34 * Write options to IP header, record destination address to
     35 * source route option, address of outgoing interface
     36 * (we should already know it, so that this  function is allowed be
     37 * called only after routing decision) and timestamp,
     38 * if we originate this datagram.
     39 *
     40 * daddr is real destination address, next hop is recorded in IP header.
     41 * saddr is address of outgoing interface.
     42 */
     43
     44void ip_options_build(struct sk_buff *skb, struct ip_options *opt,
     45		      __be32 daddr, struct rtable *rt)
     46{
     47	unsigned char *iph = skb_network_header(skb);
     48
     49	memcpy(&(IPCB(skb)->opt), opt, sizeof(struct ip_options));
     50	memcpy(iph + sizeof(struct iphdr), opt->__data, opt->optlen);
     51	opt = &(IPCB(skb)->opt);
     52
     53	if (opt->srr)
     54		memcpy(iph + opt->srr + iph[opt->srr + 1] - 4, &daddr, 4);
     55
     56	if (opt->rr_needaddr)
     57		ip_rt_get_source(iph + opt->rr + iph[opt->rr + 2] - 5, skb, rt);
     58	if (opt->ts_needaddr)
     59		ip_rt_get_source(iph + opt->ts + iph[opt->ts + 2] - 9, skb, rt);
     60	if (opt->ts_needtime) {
     61		__be32 midtime;
     62
     63		midtime = inet_current_timestamp();
     64		memcpy(iph + opt->ts + iph[opt->ts + 2] - 5, &midtime, 4);
     65	}
     66}
     67
     68/*
     69 * Provided (sopt, skb) points to received options,
     70 * build in dopt compiled option set appropriate for answering.
     71 * i.e. invert SRR option, copy anothers,
     72 * and grab room in RR/TS options.
     73 *
     74 * NOTE: dopt cannot point to skb.
     75 */
     76
     77int __ip_options_echo(struct net *net, struct ip_options *dopt,
     78		      struct sk_buff *skb, const struct ip_options *sopt)
     79{
     80	unsigned char *sptr, *dptr;
     81	int soffset, doffset;
     82	int	optlen;
     83
     84	memset(dopt, 0, sizeof(struct ip_options));
     85
     86	if (sopt->optlen == 0)
     87		return 0;
     88
     89	sptr = skb_network_header(skb);
     90	dptr = dopt->__data;
     91
     92	if (sopt->rr) {
     93		optlen  = sptr[sopt->rr+1];
     94		soffset = sptr[sopt->rr+2];
     95		dopt->rr = dopt->optlen + sizeof(struct iphdr);
     96		memcpy(dptr, sptr+sopt->rr, optlen);
     97		if (sopt->rr_needaddr && soffset <= optlen) {
     98			if (soffset + 3 > optlen)
     99				return -EINVAL;
    100			dptr[2] = soffset + 4;
    101			dopt->rr_needaddr = 1;
    102		}
    103		dptr += optlen;
    104		dopt->optlen += optlen;
    105	}
    106	if (sopt->ts) {
    107		optlen = sptr[sopt->ts+1];
    108		soffset = sptr[sopt->ts+2];
    109		dopt->ts = dopt->optlen + sizeof(struct iphdr);
    110		memcpy(dptr, sptr+sopt->ts, optlen);
    111		if (soffset <= optlen) {
    112			if (sopt->ts_needaddr) {
    113				if (soffset + 3 > optlen)
    114					return -EINVAL;
    115				dopt->ts_needaddr = 1;
    116				soffset += 4;
    117			}
    118			if (sopt->ts_needtime) {
    119				if (soffset + 3 > optlen)
    120					return -EINVAL;
    121				if ((dptr[3]&0xF) != IPOPT_TS_PRESPEC) {
    122					dopt->ts_needtime = 1;
    123					soffset += 4;
    124				} else {
    125					dopt->ts_needtime = 0;
    126
    127					if (soffset + 7 <= optlen) {
    128						__be32 addr;
    129
    130						memcpy(&addr, dptr+soffset-1, 4);
    131						if (inet_addr_type(net, addr) != RTN_UNICAST) {
    132							dopt->ts_needtime = 1;
    133							soffset += 8;
    134						}
    135					}
    136				}
    137			}
    138			dptr[2] = soffset;
    139		}
    140		dptr += optlen;
    141		dopt->optlen += optlen;
    142	}
    143	if (sopt->srr) {
    144		unsigned char *start = sptr+sopt->srr;
    145		__be32 faddr;
    146
    147		optlen  = start[1];
    148		soffset = start[2];
    149		doffset = 0;
    150		if (soffset > optlen)
    151			soffset = optlen + 1;
    152		soffset -= 4;
    153		if (soffset > 3) {
    154			memcpy(&faddr, &start[soffset-1], 4);
    155			for (soffset -= 4, doffset = 4; soffset > 3; soffset -= 4, doffset += 4)
    156				memcpy(&dptr[doffset-1], &start[soffset-1], 4);
    157			/*
    158			 * RFC1812 requires to fix illegal source routes.
    159			 */
    160			if (memcmp(&ip_hdr(skb)->saddr,
    161				   &start[soffset + 3], 4) == 0)
    162				doffset -= 4;
    163		}
    164		if (doffset > 3) {
    165			dopt->faddr = faddr;
    166			dptr[0] = start[0];
    167			dptr[1] = doffset+3;
    168			dptr[2] = 4;
    169			dptr += doffset+3;
    170			dopt->srr = dopt->optlen + sizeof(struct iphdr);
    171			dopt->optlen += doffset+3;
    172			dopt->is_strictroute = sopt->is_strictroute;
    173		}
    174	}
    175	if (sopt->cipso) {
    176		optlen  = sptr[sopt->cipso+1];
    177		dopt->cipso = dopt->optlen+sizeof(struct iphdr);
    178		memcpy(dptr, sptr+sopt->cipso, optlen);
    179		dptr += optlen;
    180		dopt->optlen += optlen;
    181	}
    182	while (dopt->optlen & 3) {
    183		*dptr++ = IPOPT_END;
    184		dopt->optlen++;
    185	}
    186	return 0;
    187}
    188
    189/*
    190 *	Options "fragmenting", just fill options not
    191 *	allowed in fragments with NOOPs.
    192 *	Simple and stupid 8), but the most efficient way.
    193 */
    194
    195void ip_options_fragment(struct sk_buff *skb)
    196{
    197	unsigned char *optptr = skb_network_header(skb) + sizeof(struct iphdr);
    198	struct ip_options *opt = &(IPCB(skb)->opt);
    199	int  l = opt->optlen;
    200	int  optlen;
    201
    202	while (l > 0) {
    203		switch (*optptr) {
    204		case IPOPT_END:
    205			return;
    206		case IPOPT_NOOP:
    207			l--;
    208			optptr++;
    209			continue;
    210		}
    211		optlen = optptr[1];
    212		if (optlen < 2 || optlen > l)
    213		  return;
    214		if (!IPOPT_COPIED(*optptr))
    215			memset(optptr, IPOPT_NOOP, optlen);
    216		l -= optlen;
    217		optptr += optlen;
    218	}
    219	opt->ts = 0;
    220	opt->rr = 0;
    221	opt->rr_needaddr = 0;
    222	opt->ts_needaddr = 0;
    223	opt->ts_needtime = 0;
    224}
    225
    226/* helper used by ip_options_compile() to call fib_compute_spec_dst()
    227 * at most one time.
    228 */
    229static void spec_dst_fill(__be32 *spec_dst, struct sk_buff *skb)
    230{
    231	if (*spec_dst == htonl(INADDR_ANY))
    232		*spec_dst = fib_compute_spec_dst(skb);
    233}
    234
    235/*
    236 * Verify options and fill pointers in struct options.
    237 * Caller should clear *opt, and set opt->data.
    238 * If opt == NULL, then skb->data should point to IP header.
    239 */
    240
    241int __ip_options_compile(struct net *net,
    242			 struct ip_options *opt, struct sk_buff *skb,
    243			 __be32 *info)
    244{
    245	__be32 spec_dst = htonl(INADDR_ANY);
    246	unsigned char *pp_ptr = NULL;
    247	struct rtable *rt = NULL;
    248	unsigned char *optptr;
    249	unsigned char *iph;
    250	int optlen, l;
    251
    252	if (skb) {
    253		rt = skb_rtable(skb);
    254		optptr = (unsigned char *)&(ip_hdr(skb)[1]);
    255	} else
    256		optptr = opt->__data;
    257	iph = optptr - sizeof(struct iphdr);
    258
    259	for (l = opt->optlen; l > 0; ) {
    260		switch (*optptr) {
    261		case IPOPT_END:
    262			for (optptr++, l--; l > 0; optptr++, l--) {
    263				if (*optptr != IPOPT_END) {
    264					*optptr = IPOPT_END;
    265					opt->is_changed = 1;
    266				}
    267			}
    268			goto eol;
    269		case IPOPT_NOOP:
    270			l--;
    271			optptr++;
    272			continue;
    273		}
    274		if (unlikely(l < 2)) {
    275			pp_ptr = optptr;
    276			goto error;
    277		}
    278		optlen = optptr[1];
    279		if (optlen < 2 || optlen > l) {
    280			pp_ptr = optptr;
    281			goto error;
    282		}
    283		switch (*optptr) {
    284		case IPOPT_SSRR:
    285		case IPOPT_LSRR:
    286			if (optlen < 3) {
    287				pp_ptr = optptr + 1;
    288				goto error;
    289			}
    290			if (optptr[2] < 4) {
    291				pp_ptr = optptr + 2;
    292				goto error;
    293			}
    294			/* NB: cf RFC-1812 5.2.4.1 */
    295			if (opt->srr) {
    296				pp_ptr = optptr;
    297				goto error;
    298			}
    299			if (!skb) {
    300				if (optptr[2] != 4 || optlen < 7 || ((optlen-3) & 3)) {
    301					pp_ptr = optptr + 1;
    302					goto error;
    303				}
    304				memcpy(&opt->faddr, &optptr[3], 4);
    305				if (optlen > 7)
    306					memmove(&optptr[3], &optptr[7], optlen-7);
    307			}
    308			opt->is_strictroute = (optptr[0] == IPOPT_SSRR);
    309			opt->srr = optptr - iph;
    310			break;
    311		case IPOPT_RR:
    312			if (opt->rr) {
    313				pp_ptr = optptr;
    314				goto error;
    315			}
    316			if (optlen < 3) {
    317				pp_ptr = optptr + 1;
    318				goto error;
    319			}
    320			if (optptr[2] < 4) {
    321				pp_ptr = optptr + 2;
    322				goto error;
    323			}
    324			if (optptr[2] <= optlen) {
    325				if (optptr[2]+3 > optlen) {
    326					pp_ptr = optptr + 2;
    327					goto error;
    328				}
    329				if (rt) {
    330					spec_dst_fill(&spec_dst, skb);
    331					memcpy(&optptr[optptr[2]-1], &spec_dst, 4);
    332					opt->is_changed = 1;
    333				}
    334				optptr[2] += 4;
    335				opt->rr_needaddr = 1;
    336			}
    337			opt->rr = optptr - iph;
    338			break;
    339		case IPOPT_TIMESTAMP:
    340			if (opt->ts) {
    341				pp_ptr = optptr;
    342				goto error;
    343			}
    344			if (optlen < 4) {
    345				pp_ptr = optptr + 1;
    346				goto error;
    347			}
    348			if (optptr[2] < 5) {
    349				pp_ptr = optptr + 2;
    350				goto error;
    351			}
    352			if (optptr[2] <= optlen) {
    353				unsigned char *timeptr = NULL;
    354				if (optptr[2]+3 > optlen) {
    355					pp_ptr = optptr + 2;
    356					goto error;
    357				}
    358				switch (optptr[3]&0xF) {
    359				case IPOPT_TS_TSONLY:
    360					if (skb)
    361						timeptr = &optptr[optptr[2]-1];
    362					opt->ts_needtime = 1;
    363					optptr[2] += 4;
    364					break;
    365				case IPOPT_TS_TSANDADDR:
    366					if (optptr[2]+7 > optlen) {
    367						pp_ptr = optptr + 2;
    368						goto error;
    369					}
    370					if (rt)  {
    371						spec_dst_fill(&spec_dst, skb);
    372						memcpy(&optptr[optptr[2]-1], &spec_dst, 4);
    373						timeptr = &optptr[optptr[2]+3];
    374					}
    375					opt->ts_needaddr = 1;
    376					opt->ts_needtime = 1;
    377					optptr[2] += 8;
    378					break;
    379				case IPOPT_TS_PRESPEC:
    380					if (optptr[2]+7 > optlen) {
    381						pp_ptr = optptr + 2;
    382						goto error;
    383					}
    384					{
    385						__be32 addr;
    386						memcpy(&addr, &optptr[optptr[2]-1], 4);
    387						if (inet_addr_type(net, addr) == RTN_UNICAST)
    388							break;
    389						if (skb)
    390							timeptr = &optptr[optptr[2]+3];
    391					}
    392					opt->ts_needtime = 1;
    393					optptr[2] += 8;
    394					break;
    395				default:
    396					if (!skb && !ns_capable(net->user_ns, CAP_NET_RAW)) {
    397						pp_ptr = optptr + 3;
    398						goto error;
    399					}
    400					break;
    401				}
    402				if (timeptr) {
    403					__be32 midtime;
    404
    405					midtime = inet_current_timestamp();
    406					memcpy(timeptr, &midtime, 4);
    407					opt->is_changed = 1;
    408				}
    409			} else if ((optptr[3]&0xF) != IPOPT_TS_PRESPEC) {
    410				unsigned int overflow = optptr[3]>>4;
    411				if (overflow == 15) {
    412					pp_ptr = optptr + 3;
    413					goto error;
    414				}
    415				if (skb) {
    416					optptr[3] = (optptr[3]&0xF)|((overflow+1)<<4);
    417					opt->is_changed = 1;
    418				}
    419			}
    420			opt->ts = optptr - iph;
    421			break;
    422		case IPOPT_RA:
    423			if (optlen < 4) {
    424				pp_ptr = optptr + 1;
    425				goto error;
    426			}
    427			if (optptr[2] == 0 && optptr[3] == 0)
    428				opt->router_alert = optptr - iph;
    429			break;
    430		case IPOPT_CIPSO:
    431			if ((!skb && !ns_capable(net->user_ns, CAP_NET_RAW)) || opt->cipso) {
    432				pp_ptr = optptr;
    433				goto error;
    434			}
    435			opt->cipso = optptr - iph;
    436			if (cipso_v4_validate(skb, &optptr)) {
    437				pp_ptr = optptr;
    438				goto error;
    439			}
    440			break;
    441		case IPOPT_SEC:
    442		case IPOPT_SID:
    443		default:
    444			if (!skb && !ns_capable(net->user_ns, CAP_NET_RAW)) {
    445				pp_ptr = optptr;
    446				goto error;
    447			}
    448			break;
    449		}
    450		l -= optlen;
    451		optptr += optlen;
    452	}
    453
    454eol:
    455	if (!pp_ptr)
    456		return 0;
    457
    458error:
    459	if (info)
    460		*info = htonl((pp_ptr-iph)<<24);
    461	return -EINVAL;
    462}
    463EXPORT_SYMBOL(__ip_options_compile);
    464
    465int ip_options_compile(struct net *net,
    466		       struct ip_options *opt, struct sk_buff *skb)
    467{
    468	int ret;
    469	__be32 info;
    470
    471	ret = __ip_options_compile(net, opt, skb, &info);
    472	if (ret != 0 && skb)
    473		icmp_send(skb, ICMP_PARAMETERPROB, 0, info);
    474	return ret;
    475}
    476EXPORT_SYMBOL(ip_options_compile);
    477
    478/*
    479 *	Undo all the changes done by ip_options_compile().
    480 */
    481
    482void ip_options_undo(struct ip_options *opt)
    483{
    484	if (opt->srr) {
    485		unsigned char *optptr = opt->__data + opt->srr - sizeof(struct iphdr);
    486
    487		memmove(optptr + 7, optptr + 3, optptr[1] - 7);
    488		memcpy(optptr + 3, &opt->faddr, 4);
    489	}
    490	if (opt->rr_needaddr) {
    491		unsigned char *optptr = opt->__data + opt->rr - sizeof(struct iphdr);
    492
    493		optptr[2] -= 4;
    494		memset(&optptr[optptr[2] - 1], 0, 4);
    495	}
    496	if (opt->ts) {
    497		unsigned char *optptr = opt->__data + opt->ts - sizeof(struct iphdr);
    498
    499		if (opt->ts_needtime) {
    500			optptr[2] -= 4;
    501			memset(&optptr[optptr[2] - 1], 0, 4);
    502			if ((optptr[3] & 0xF) == IPOPT_TS_PRESPEC)
    503				optptr[2] -= 4;
    504		}
    505		if (opt->ts_needaddr) {
    506			optptr[2] -= 4;
    507			memset(&optptr[optptr[2] - 1], 0, 4);
    508		}
    509	}
    510}
    511
    512int ip_options_get(struct net *net, struct ip_options_rcu **optp,
    513		   sockptr_t data, int optlen)
    514{
    515	struct ip_options_rcu *opt;
    516
    517	opt = kzalloc(sizeof(struct ip_options_rcu) + ((optlen + 3) & ~3),
    518		       GFP_KERNEL);
    519	if (!opt)
    520		return -ENOMEM;
    521	if (optlen && copy_from_sockptr(opt->opt.__data, data, optlen)) {
    522		kfree(opt);
    523		return -EFAULT;
    524	}
    525
    526	while (optlen & 3)
    527		opt->opt.__data[optlen++] = IPOPT_END;
    528	opt->opt.optlen = optlen;
    529	if (optlen && ip_options_compile(net, &opt->opt, NULL)) {
    530		kfree(opt);
    531		return -EINVAL;
    532	}
    533	kfree(*optp);
    534	*optp = opt;
    535	return 0;
    536}
    537
    538void ip_forward_options(struct sk_buff *skb)
    539{
    540	struct   ip_options *opt	= &(IPCB(skb)->opt);
    541	unsigned char *optptr;
    542	struct rtable *rt = skb_rtable(skb);
    543	unsigned char *raw = skb_network_header(skb);
    544
    545	if (opt->rr_needaddr) {
    546		optptr = (unsigned char *)raw + opt->rr;
    547		ip_rt_get_source(&optptr[optptr[2]-5], skb, rt);
    548		opt->is_changed = 1;
    549	}
    550	if (opt->srr_is_hit) {
    551		int srrptr, srrspace;
    552
    553		optptr = raw + opt->srr;
    554
    555		for ( srrptr = optptr[2], srrspace = optptr[1];
    556		     srrptr <= srrspace;
    557		     srrptr += 4
    558		     ) {
    559			if (srrptr + 3 > srrspace)
    560				break;
    561			if (memcmp(&opt->nexthop, &optptr[srrptr-1], 4) == 0)
    562				break;
    563		}
    564		if (srrptr + 3 <= srrspace) {
    565			opt->is_changed = 1;
    566			ip_hdr(skb)->daddr = opt->nexthop;
    567			ip_rt_get_source(&optptr[srrptr-1], skb, rt);
    568			optptr[2] = srrptr+4;
    569		} else {
    570			net_crit_ratelimited("%s(): Argh! Destination lost!\n",
    571					     __func__);
    572		}
    573		if (opt->ts_needaddr) {
    574			optptr = raw + opt->ts;
    575			ip_rt_get_source(&optptr[optptr[2]-9], skb, rt);
    576			opt->is_changed = 1;
    577		}
    578	}
    579	if (opt->is_changed) {
    580		opt->is_changed = 0;
    581		ip_send_check(ip_hdr(skb));
    582	}
    583}
    584
    585int ip_options_rcv_srr(struct sk_buff *skb, struct net_device *dev)
    586{
    587	struct ip_options *opt = &(IPCB(skb)->opt);
    588	int srrspace, srrptr;
    589	__be32 nexthop;
    590	struct iphdr *iph = ip_hdr(skb);
    591	unsigned char *optptr = skb_network_header(skb) + opt->srr;
    592	struct rtable *rt = skb_rtable(skb);
    593	struct rtable *rt2;
    594	unsigned long orefdst;
    595	int err;
    596
    597	if (!rt)
    598		return 0;
    599
    600	if (skb->pkt_type != PACKET_HOST)
    601		return -EINVAL;
    602	if (rt->rt_type == RTN_UNICAST) {
    603		if (!opt->is_strictroute)
    604			return 0;
    605		icmp_send(skb, ICMP_PARAMETERPROB, 0, htonl(16<<24));
    606		return -EINVAL;
    607	}
    608	if (rt->rt_type != RTN_LOCAL)
    609		return -EINVAL;
    610
    611	for (srrptr = optptr[2], srrspace = optptr[1]; srrptr <= srrspace; srrptr += 4) {
    612		if (srrptr + 3 > srrspace) {
    613			icmp_send(skb, ICMP_PARAMETERPROB, 0, htonl((opt->srr+2)<<24));
    614			return -EINVAL;
    615		}
    616		memcpy(&nexthop, &optptr[srrptr-1], 4);
    617
    618		orefdst = skb->_skb_refdst;
    619		skb_dst_set(skb, NULL);
    620		err = ip_route_input(skb, nexthop, iph->saddr, iph->tos, dev);
    621		rt2 = skb_rtable(skb);
    622		if (err || (rt2->rt_type != RTN_UNICAST && rt2->rt_type != RTN_LOCAL)) {
    623			skb_dst_drop(skb);
    624			skb->_skb_refdst = orefdst;
    625			return -EINVAL;
    626		}
    627		refdst_drop(orefdst);
    628		if (rt2->rt_type != RTN_LOCAL)
    629			break;
    630		/* Superfast 8) loopback forward */
    631		iph->daddr = nexthop;
    632		opt->is_changed = 1;
    633	}
    634	if (srrptr <= srrspace) {
    635		opt->srr_is_hit = 1;
    636		opt->nexthop = nexthop;
    637		opt->is_changed = 1;
    638	}
    639	return 0;
    640}
    641EXPORT_SYMBOL(ip_options_rcv_srr);