162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * INET		An implementation of the TCP/IP protocol suite for the LINUX
462306a36Sopenharmony_ci *		operating system.  INET is implemented using the  BSD Socket
562306a36Sopenharmony_ci *		interface as the means of communication with the user level.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *		The options processing module for ip.c
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Authors:	A.N.Kuznetsov
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#define pr_fmt(fmt) "IPv4: " fmt
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <linux/capability.h>
1662306a36Sopenharmony_ci#include <linux/module.h>
1762306a36Sopenharmony_ci#include <linux/slab.h>
1862306a36Sopenharmony_ci#include <linux/types.h>
1962306a36Sopenharmony_ci#include <linux/uaccess.h>
2062306a36Sopenharmony_ci#include <asm/unaligned.h>
2162306a36Sopenharmony_ci#include <linux/skbuff.h>
2262306a36Sopenharmony_ci#include <linux/ip.h>
2362306a36Sopenharmony_ci#include <linux/icmp.h>
2462306a36Sopenharmony_ci#include <linux/netdevice.h>
2562306a36Sopenharmony_ci#include <linux/rtnetlink.h>
2662306a36Sopenharmony_ci#include <net/sock.h>
2762306a36Sopenharmony_ci#include <net/ip.h>
2862306a36Sopenharmony_ci#include <net/icmp.h>
2962306a36Sopenharmony_ci#include <net/route.h>
3062306a36Sopenharmony_ci#include <net/cipso_ipv4.h>
3162306a36Sopenharmony_ci#include <net/ip_fib.h>
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/*
3462306a36Sopenharmony_ci * Write options to IP header, record destination address to
3562306a36Sopenharmony_ci * source route option, address of outgoing interface
3662306a36Sopenharmony_ci * (we should already know it, so that this  function is allowed be
3762306a36Sopenharmony_ci * called only after routing decision) and timestamp,
3862306a36Sopenharmony_ci * if we originate this datagram.
3962306a36Sopenharmony_ci *
4062306a36Sopenharmony_ci * daddr is real destination address, next hop is recorded in IP header.
4162306a36Sopenharmony_ci * saddr is address of outgoing interface.
4262306a36Sopenharmony_ci */
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_civoid ip_options_build(struct sk_buff *skb, struct ip_options *opt,
4562306a36Sopenharmony_ci		      __be32 daddr, struct rtable *rt)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	unsigned char *iph = skb_network_header(skb);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	memcpy(&(IPCB(skb)->opt), opt, sizeof(struct ip_options));
5062306a36Sopenharmony_ci	memcpy(iph + sizeof(struct iphdr), opt->__data, opt->optlen);
5162306a36Sopenharmony_ci	opt = &(IPCB(skb)->opt);
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	if (opt->srr)
5462306a36Sopenharmony_ci		memcpy(iph + opt->srr + iph[opt->srr + 1] - 4, &daddr, 4);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	if (opt->rr_needaddr)
5762306a36Sopenharmony_ci		ip_rt_get_source(iph + opt->rr + iph[opt->rr + 2] - 5, skb, rt);
5862306a36Sopenharmony_ci	if (opt->ts_needaddr)
5962306a36Sopenharmony_ci		ip_rt_get_source(iph + opt->ts + iph[opt->ts + 2] - 9, skb, rt);
6062306a36Sopenharmony_ci	if (opt->ts_needtime) {
6162306a36Sopenharmony_ci		__be32 midtime;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci		midtime = inet_current_timestamp();
6462306a36Sopenharmony_ci		memcpy(iph + opt->ts + iph[opt->ts + 2] - 5, &midtime, 4);
6562306a36Sopenharmony_ci	}
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci/*
6962306a36Sopenharmony_ci * Provided (sopt, skb) points to received options,
7062306a36Sopenharmony_ci * build in dopt compiled option set appropriate for answering.
7162306a36Sopenharmony_ci * i.e. invert SRR option, copy anothers,
7262306a36Sopenharmony_ci * and grab room in RR/TS options.
7362306a36Sopenharmony_ci *
7462306a36Sopenharmony_ci * NOTE: dopt cannot point to skb.
7562306a36Sopenharmony_ci */
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ciint __ip_options_echo(struct net *net, struct ip_options *dopt,
7862306a36Sopenharmony_ci		      struct sk_buff *skb, const struct ip_options *sopt)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	unsigned char *sptr, *dptr;
8162306a36Sopenharmony_ci	int soffset, doffset;
8262306a36Sopenharmony_ci	int	optlen;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	memset(dopt, 0, sizeof(struct ip_options));
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	if (sopt->optlen == 0)
8762306a36Sopenharmony_ci		return 0;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	sptr = skb_network_header(skb);
9062306a36Sopenharmony_ci	dptr = dopt->__data;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	if (sopt->rr) {
9362306a36Sopenharmony_ci		optlen  = sptr[sopt->rr+1];
9462306a36Sopenharmony_ci		soffset = sptr[sopt->rr+2];
9562306a36Sopenharmony_ci		dopt->rr = dopt->optlen + sizeof(struct iphdr);
9662306a36Sopenharmony_ci		memcpy(dptr, sptr+sopt->rr, optlen);
9762306a36Sopenharmony_ci		if (sopt->rr_needaddr && soffset <= optlen) {
9862306a36Sopenharmony_ci			if (soffset + 3 > optlen)
9962306a36Sopenharmony_ci				return -EINVAL;
10062306a36Sopenharmony_ci			dptr[2] = soffset + 4;
10162306a36Sopenharmony_ci			dopt->rr_needaddr = 1;
10262306a36Sopenharmony_ci		}
10362306a36Sopenharmony_ci		dptr += optlen;
10462306a36Sopenharmony_ci		dopt->optlen += optlen;
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci	if (sopt->ts) {
10762306a36Sopenharmony_ci		optlen = sptr[sopt->ts+1];
10862306a36Sopenharmony_ci		soffset = sptr[sopt->ts+2];
10962306a36Sopenharmony_ci		dopt->ts = dopt->optlen + sizeof(struct iphdr);
11062306a36Sopenharmony_ci		memcpy(dptr, sptr+sopt->ts, optlen);
11162306a36Sopenharmony_ci		if (soffset <= optlen) {
11262306a36Sopenharmony_ci			if (sopt->ts_needaddr) {
11362306a36Sopenharmony_ci				if (soffset + 3 > optlen)
11462306a36Sopenharmony_ci					return -EINVAL;
11562306a36Sopenharmony_ci				dopt->ts_needaddr = 1;
11662306a36Sopenharmony_ci				soffset += 4;
11762306a36Sopenharmony_ci			}
11862306a36Sopenharmony_ci			if (sopt->ts_needtime) {
11962306a36Sopenharmony_ci				if (soffset + 3 > optlen)
12062306a36Sopenharmony_ci					return -EINVAL;
12162306a36Sopenharmony_ci				if ((dptr[3]&0xF) != IPOPT_TS_PRESPEC) {
12262306a36Sopenharmony_ci					dopt->ts_needtime = 1;
12362306a36Sopenharmony_ci					soffset += 4;
12462306a36Sopenharmony_ci				} else {
12562306a36Sopenharmony_ci					dopt->ts_needtime = 0;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci					if (soffset + 7 <= optlen) {
12862306a36Sopenharmony_ci						__be32 addr;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci						memcpy(&addr, dptr+soffset-1, 4);
13162306a36Sopenharmony_ci						if (inet_addr_type(net, addr) != RTN_UNICAST) {
13262306a36Sopenharmony_ci							dopt->ts_needtime = 1;
13362306a36Sopenharmony_ci							soffset += 8;
13462306a36Sopenharmony_ci						}
13562306a36Sopenharmony_ci					}
13662306a36Sopenharmony_ci				}
13762306a36Sopenharmony_ci			}
13862306a36Sopenharmony_ci			dptr[2] = soffset;
13962306a36Sopenharmony_ci		}
14062306a36Sopenharmony_ci		dptr += optlen;
14162306a36Sopenharmony_ci		dopt->optlen += optlen;
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ci	if (sopt->srr) {
14462306a36Sopenharmony_ci		unsigned char *start = sptr+sopt->srr;
14562306a36Sopenharmony_ci		__be32 faddr;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci		optlen  = start[1];
14862306a36Sopenharmony_ci		soffset = start[2];
14962306a36Sopenharmony_ci		doffset = 0;
15062306a36Sopenharmony_ci		if (soffset > optlen)
15162306a36Sopenharmony_ci			soffset = optlen + 1;
15262306a36Sopenharmony_ci		soffset -= 4;
15362306a36Sopenharmony_ci		if (soffset > 3) {
15462306a36Sopenharmony_ci			memcpy(&faddr, &start[soffset-1], 4);
15562306a36Sopenharmony_ci			for (soffset -= 4, doffset = 4; soffset > 3; soffset -= 4, doffset += 4)
15662306a36Sopenharmony_ci				memcpy(&dptr[doffset-1], &start[soffset-1], 4);
15762306a36Sopenharmony_ci			/*
15862306a36Sopenharmony_ci			 * RFC1812 requires to fix illegal source routes.
15962306a36Sopenharmony_ci			 */
16062306a36Sopenharmony_ci			if (memcmp(&ip_hdr(skb)->saddr,
16162306a36Sopenharmony_ci				   &start[soffset + 3], 4) == 0)
16262306a36Sopenharmony_ci				doffset -= 4;
16362306a36Sopenharmony_ci		}
16462306a36Sopenharmony_ci		if (doffset > 3) {
16562306a36Sopenharmony_ci			dopt->faddr = faddr;
16662306a36Sopenharmony_ci			dptr[0] = start[0];
16762306a36Sopenharmony_ci			dptr[1] = doffset+3;
16862306a36Sopenharmony_ci			dptr[2] = 4;
16962306a36Sopenharmony_ci			dptr += doffset+3;
17062306a36Sopenharmony_ci			dopt->srr = dopt->optlen + sizeof(struct iphdr);
17162306a36Sopenharmony_ci			dopt->optlen += doffset+3;
17262306a36Sopenharmony_ci			dopt->is_strictroute = sopt->is_strictroute;
17362306a36Sopenharmony_ci		}
17462306a36Sopenharmony_ci	}
17562306a36Sopenharmony_ci	if (sopt->cipso) {
17662306a36Sopenharmony_ci		optlen  = sptr[sopt->cipso+1];
17762306a36Sopenharmony_ci		dopt->cipso = dopt->optlen+sizeof(struct iphdr);
17862306a36Sopenharmony_ci		memcpy(dptr, sptr+sopt->cipso, optlen);
17962306a36Sopenharmony_ci		dptr += optlen;
18062306a36Sopenharmony_ci		dopt->optlen += optlen;
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_ci	while (dopt->optlen & 3) {
18362306a36Sopenharmony_ci		*dptr++ = IPOPT_END;
18462306a36Sopenharmony_ci		dopt->optlen++;
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci	return 0;
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci/*
19062306a36Sopenharmony_ci *	Options "fragmenting", just fill options not
19162306a36Sopenharmony_ci *	allowed in fragments with NOOPs.
19262306a36Sopenharmony_ci *	Simple and stupid 8), but the most efficient way.
19362306a36Sopenharmony_ci */
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_civoid ip_options_fragment(struct sk_buff *skb)
19662306a36Sopenharmony_ci{
19762306a36Sopenharmony_ci	unsigned char *optptr = skb_network_header(skb) + sizeof(struct iphdr);
19862306a36Sopenharmony_ci	struct ip_options *opt = &(IPCB(skb)->opt);
19962306a36Sopenharmony_ci	int  l = opt->optlen;
20062306a36Sopenharmony_ci	int  optlen;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	while (l > 0) {
20362306a36Sopenharmony_ci		switch (*optptr) {
20462306a36Sopenharmony_ci		case IPOPT_END:
20562306a36Sopenharmony_ci			return;
20662306a36Sopenharmony_ci		case IPOPT_NOOP:
20762306a36Sopenharmony_ci			l--;
20862306a36Sopenharmony_ci			optptr++;
20962306a36Sopenharmony_ci			continue;
21062306a36Sopenharmony_ci		}
21162306a36Sopenharmony_ci		optlen = optptr[1];
21262306a36Sopenharmony_ci		if (optlen < 2 || optlen > l)
21362306a36Sopenharmony_ci		  return;
21462306a36Sopenharmony_ci		if (!IPOPT_COPIED(*optptr))
21562306a36Sopenharmony_ci			memset(optptr, IPOPT_NOOP, optlen);
21662306a36Sopenharmony_ci		l -= optlen;
21762306a36Sopenharmony_ci		optptr += optlen;
21862306a36Sopenharmony_ci	}
21962306a36Sopenharmony_ci	opt->ts = 0;
22062306a36Sopenharmony_ci	opt->rr = 0;
22162306a36Sopenharmony_ci	opt->rr_needaddr = 0;
22262306a36Sopenharmony_ci	opt->ts_needaddr = 0;
22362306a36Sopenharmony_ci	opt->ts_needtime = 0;
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci/* helper used by ip_options_compile() to call fib_compute_spec_dst()
22762306a36Sopenharmony_ci * at most one time.
22862306a36Sopenharmony_ci */
22962306a36Sopenharmony_cistatic void spec_dst_fill(__be32 *spec_dst, struct sk_buff *skb)
23062306a36Sopenharmony_ci{
23162306a36Sopenharmony_ci	if (*spec_dst == htonl(INADDR_ANY))
23262306a36Sopenharmony_ci		*spec_dst = fib_compute_spec_dst(skb);
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci/*
23662306a36Sopenharmony_ci * Verify options and fill pointers in struct options.
23762306a36Sopenharmony_ci * Caller should clear *opt, and set opt->data.
23862306a36Sopenharmony_ci * If opt == NULL, then skb->data should point to IP header.
23962306a36Sopenharmony_ci */
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ciint __ip_options_compile(struct net *net,
24262306a36Sopenharmony_ci			 struct ip_options *opt, struct sk_buff *skb,
24362306a36Sopenharmony_ci			 __be32 *info)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	__be32 spec_dst = htonl(INADDR_ANY);
24662306a36Sopenharmony_ci	unsigned char *pp_ptr = NULL;
24762306a36Sopenharmony_ci	struct rtable *rt = NULL;
24862306a36Sopenharmony_ci	unsigned char *optptr;
24962306a36Sopenharmony_ci	unsigned char *iph;
25062306a36Sopenharmony_ci	int optlen, l;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	if (skb) {
25362306a36Sopenharmony_ci		rt = skb_rtable(skb);
25462306a36Sopenharmony_ci		optptr = (unsigned char *)&(ip_hdr(skb)[1]);
25562306a36Sopenharmony_ci	} else
25662306a36Sopenharmony_ci		optptr = opt->__data;
25762306a36Sopenharmony_ci	iph = optptr - sizeof(struct iphdr);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	for (l = opt->optlen; l > 0; ) {
26062306a36Sopenharmony_ci		switch (*optptr) {
26162306a36Sopenharmony_ci		case IPOPT_END:
26262306a36Sopenharmony_ci			for (optptr++, l--; l > 0; optptr++, l--) {
26362306a36Sopenharmony_ci				if (*optptr != IPOPT_END) {
26462306a36Sopenharmony_ci					*optptr = IPOPT_END;
26562306a36Sopenharmony_ci					opt->is_changed = 1;
26662306a36Sopenharmony_ci				}
26762306a36Sopenharmony_ci			}
26862306a36Sopenharmony_ci			goto eol;
26962306a36Sopenharmony_ci		case IPOPT_NOOP:
27062306a36Sopenharmony_ci			l--;
27162306a36Sopenharmony_ci			optptr++;
27262306a36Sopenharmony_ci			continue;
27362306a36Sopenharmony_ci		}
27462306a36Sopenharmony_ci		if (unlikely(l < 2)) {
27562306a36Sopenharmony_ci			pp_ptr = optptr;
27662306a36Sopenharmony_ci			goto error;
27762306a36Sopenharmony_ci		}
27862306a36Sopenharmony_ci		optlen = optptr[1];
27962306a36Sopenharmony_ci		if (optlen < 2 || optlen > l) {
28062306a36Sopenharmony_ci			pp_ptr = optptr;
28162306a36Sopenharmony_ci			goto error;
28262306a36Sopenharmony_ci		}
28362306a36Sopenharmony_ci		switch (*optptr) {
28462306a36Sopenharmony_ci		case IPOPT_SSRR:
28562306a36Sopenharmony_ci		case IPOPT_LSRR:
28662306a36Sopenharmony_ci			if (optlen < 3) {
28762306a36Sopenharmony_ci				pp_ptr = optptr + 1;
28862306a36Sopenharmony_ci				goto error;
28962306a36Sopenharmony_ci			}
29062306a36Sopenharmony_ci			if (optptr[2] < 4) {
29162306a36Sopenharmony_ci				pp_ptr = optptr + 2;
29262306a36Sopenharmony_ci				goto error;
29362306a36Sopenharmony_ci			}
29462306a36Sopenharmony_ci			/* NB: cf RFC-1812 5.2.4.1 */
29562306a36Sopenharmony_ci			if (opt->srr) {
29662306a36Sopenharmony_ci				pp_ptr = optptr;
29762306a36Sopenharmony_ci				goto error;
29862306a36Sopenharmony_ci			}
29962306a36Sopenharmony_ci			if (!skb) {
30062306a36Sopenharmony_ci				if (optptr[2] != 4 || optlen < 7 || ((optlen-3) & 3)) {
30162306a36Sopenharmony_ci					pp_ptr = optptr + 1;
30262306a36Sopenharmony_ci					goto error;
30362306a36Sopenharmony_ci				}
30462306a36Sopenharmony_ci				memcpy(&opt->faddr, &optptr[3], 4);
30562306a36Sopenharmony_ci				if (optlen > 7)
30662306a36Sopenharmony_ci					memmove(&optptr[3], &optptr[7], optlen-7);
30762306a36Sopenharmony_ci			}
30862306a36Sopenharmony_ci			opt->is_strictroute = (optptr[0] == IPOPT_SSRR);
30962306a36Sopenharmony_ci			opt->srr = optptr - iph;
31062306a36Sopenharmony_ci			break;
31162306a36Sopenharmony_ci		case IPOPT_RR:
31262306a36Sopenharmony_ci			if (opt->rr) {
31362306a36Sopenharmony_ci				pp_ptr = optptr;
31462306a36Sopenharmony_ci				goto error;
31562306a36Sopenharmony_ci			}
31662306a36Sopenharmony_ci			if (optlen < 3) {
31762306a36Sopenharmony_ci				pp_ptr = optptr + 1;
31862306a36Sopenharmony_ci				goto error;
31962306a36Sopenharmony_ci			}
32062306a36Sopenharmony_ci			if (optptr[2] < 4) {
32162306a36Sopenharmony_ci				pp_ptr = optptr + 2;
32262306a36Sopenharmony_ci				goto error;
32362306a36Sopenharmony_ci			}
32462306a36Sopenharmony_ci			if (optptr[2] <= optlen) {
32562306a36Sopenharmony_ci				if (optptr[2]+3 > optlen) {
32662306a36Sopenharmony_ci					pp_ptr = optptr + 2;
32762306a36Sopenharmony_ci					goto error;
32862306a36Sopenharmony_ci				}
32962306a36Sopenharmony_ci				if (rt) {
33062306a36Sopenharmony_ci					spec_dst_fill(&spec_dst, skb);
33162306a36Sopenharmony_ci					memcpy(&optptr[optptr[2]-1], &spec_dst, 4);
33262306a36Sopenharmony_ci					opt->is_changed = 1;
33362306a36Sopenharmony_ci				}
33462306a36Sopenharmony_ci				optptr[2] += 4;
33562306a36Sopenharmony_ci				opt->rr_needaddr = 1;
33662306a36Sopenharmony_ci			}
33762306a36Sopenharmony_ci			opt->rr = optptr - iph;
33862306a36Sopenharmony_ci			break;
33962306a36Sopenharmony_ci		case IPOPT_TIMESTAMP:
34062306a36Sopenharmony_ci			if (opt->ts) {
34162306a36Sopenharmony_ci				pp_ptr = optptr;
34262306a36Sopenharmony_ci				goto error;
34362306a36Sopenharmony_ci			}
34462306a36Sopenharmony_ci			if (optlen < 4) {
34562306a36Sopenharmony_ci				pp_ptr = optptr + 1;
34662306a36Sopenharmony_ci				goto error;
34762306a36Sopenharmony_ci			}
34862306a36Sopenharmony_ci			if (optptr[2] < 5) {
34962306a36Sopenharmony_ci				pp_ptr = optptr + 2;
35062306a36Sopenharmony_ci				goto error;
35162306a36Sopenharmony_ci			}
35262306a36Sopenharmony_ci			if (optptr[2] <= optlen) {
35362306a36Sopenharmony_ci				unsigned char *timeptr = NULL;
35462306a36Sopenharmony_ci				if (optptr[2]+3 > optlen) {
35562306a36Sopenharmony_ci					pp_ptr = optptr + 2;
35662306a36Sopenharmony_ci					goto error;
35762306a36Sopenharmony_ci				}
35862306a36Sopenharmony_ci				switch (optptr[3]&0xF) {
35962306a36Sopenharmony_ci				case IPOPT_TS_TSONLY:
36062306a36Sopenharmony_ci					if (skb)
36162306a36Sopenharmony_ci						timeptr = &optptr[optptr[2]-1];
36262306a36Sopenharmony_ci					opt->ts_needtime = 1;
36362306a36Sopenharmony_ci					optptr[2] += 4;
36462306a36Sopenharmony_ci					break;
36562306a36Sopenharmony_ci				case IPOPT_TS_TSANDADDR:
36662306a36Sopenharmony_ci					if (optptr[2]+7 > optlen) {
36762306a36Sopenharmony_ci						pp_ptr = optptr + 2;
36862306a36Sopenharmony_ci						goto error;
36962306a36Sopenharmony_ci					}
37062306a36Sopenharmony_ci					if (rt)  {
37162306a36Sopenharmony_ci						spec_dst_fill(&spec_dst, skb);
37262306a36Sopenharmony_ci						memcpy(&optptr[optptr[2]-1], &spec_dst, 4);
37362306a36Sopenharmony_ci						timeptr = &optptr[optptr[2]+3];
37462306a36Sopenharmony_ci					}
37562306a36Sopenharmony_ci					opt->ts_needaddr = 1;
37662306a36Sopenharmony_ci					opt->ts_needtime = 1;
37762306a36Sopenharmony_ci					optptr[2] += 8;
37862306a36Sopenharmony_ci					break;
37962306a36Sopenharmony_ci				case IPOPT_TS_PRESPEC:
38062306a36Sopenharmony_ci					if (optptr[2]+7 > optlen) {
38162306a36Sopenharmony_ci						pp_ptr = optptr + 2;
38262306a36Sopenharmony_ci						goto error;
38362306a36Sopenharmony_ci					}
38462306a36Sopenharmony_ci					{
38562306a36Sopenharmony_ci						__be32 addr;
38662306a36Sopenharmony_ci						memcpy(&addr, &optptr[optptr[2]-1], 4);
38762306a36Sopenharmony_ci						if (inet_addr_type(net, addr) == RTN_UNICAST)
38862306a36Sopenharmony_ci							break;
38962306a36Sopenharmony_ci						if (skb)
39062306a36Sopenharmony_ci							timeptr = &optptr[optptr[2]+3];
39162306a36Sopenharmony_ci					}
39262306a36Sopenharmony_ci					opt->ts_needtime = 1;
39362306a36Sopenharmony_ci					optptr[2] += 8;
39462306a36Sopenharmony_ci					break;
39562306a36Sopenharmony_ci				default:
39662306a36Sopenharmony_ci					if (!skb && !ns_capable(net->user_ns, CAP_NET_RAW)) {
39762306a36Sopenharmony_ci						pp_ptr = optptr + 3;
39862306a36Sopenharmony_ci						goto error;
39962306a36Sopenharmony_ci					}
40062306a36Sopenharmony_ci					break;
40162306a36Sopenharmony_ci				}
40262306a36Sopenharmony_ci				if (timeptr) {
40362306a36Sopenharmony_ci					__be32 midtime;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci					midtime = inet_current_timestamp();
40662306a36Sopenharmony_ci					memcpy(timeptr, &midtime, 4);
40762306a36Sopenharmony_ci					opt->is_changed = 1;
40862306a36Sopenharmony_ci				}
40962306a36Sopenharmony_ci			} else if ((optptr[3]&0xF) != IPOPT_TS_PRESPEC) {
41062306a36Sopenharmony_ci				unsigned int overflow = optptr[3]>>4;
41162306a36Sopenharmony_ci				if (overflow == 15) {
41262306a36Sopenharmony_ci					pp_ptr = optptr + 3;
41362306a36Sopenharmony_ci					goto error;
41462306a36Sopenharmony_ci				}
41562306a36Sopenharmony_ci				if (skb) {
41662306a36Sopenharmony_ci					optptr[3] = (optptr[3]&0xF)|((overflow+1)<<4);
41762306a36Sopenharmony_ci					opt->is_changed = 1;
41862306a36Sopenharmony_ci				}
41962306a36Sopenharmony_ci			}
42062306a36Sopenharmony_ci			opt->ts = optptr - iph;
42162306a36Sopenharmony_ci			break;
42262306a36Sopenharmony_ci		case IPOPT_RA:
42362306a36Sopenharmony_ci			if (optlen < 4) {
42462306a36Sopenharmony_ci				pp_ptr = optptr + 1;
42562306a36Sopenharmony_ci				goto error;
42662306a36Sopenharmony_ci			}
42762306a36Sopenharmony_ci			if (optptr[2] == 0 && optptr[3] == 0)
42862306a36Sopenharmony_ci				opt->router_alert = optptr - iph;
42962306a36Sopenharmony_ci			break;
43062306a36Sopenharmony_ci		case IPOPT_CIPSO:
43162306a36Sopenharmony_ci			if ((!skb && !ns_capable(net->user_ns, CAP_NET_RAW)) || opt->cipso) {
43262306a36Sopenharmony_ci				pp_ptr = optptr;
43362306a36Sopenharmony_ci				goto error;
43462306a36Sopenharmony_ci			}
43562306a36Sopenharmony_ci			opt->cipso = optptr - iph;
43662306a36Sopenharmony_ci			if (cipso_v4_validate(skb, &optptr)) {
43762306a36Sopenharmony_ci				pp_ptr = optptr;
43862306a36Sopenharmony_ci				goto error;
43962306a36Sopenharmony_ci			}
44062306a36Sopenharmony_ci			break;
44162306a36Sopenharmony_ci		case IPOPT_SEC:
44262306a36Sopenharmony_ci		case IPOPT_SID:
44362306a36Sopenharmony_ci		default:
44462306a36Sopenharmony_ci			if (!skb && !ns_capable(net->user_ns, CAP_NET_RAW)) {
44562306a36Sopenharmony_ci				pp_ptr = optptr;
44662306a36Sopenharmony_ci				goto error;
44762306a36Sopenharmony_ci			}
44862306a36Sopenharmony_ci			break;
44962306a36Sopenharmony_ci		}
45062306a36Sopenharmony_ci		l -= optlen;
45162306a36Sopenharmony_ci		optptr += optlen;
45262306a36Sopenharmony_ci	}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_cieol:
45562306a36Sopenharmony_ci	if (!pp_ptr)
45662306a36Sopenharmony_ci		return 0;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_cierror:
45962306a36Sopenharmony_ci	if (info)
46062306a36Sopenharmony_ci		*info = htonl((pp_ptr-iph)<<24);
46162306a36Sopenharmony_ci	return -EINVAL;
46262306a36Sopenharmony_ci}
46362306a36Sopenharmony_ciEXPORT_SYMBOL(__ip_options_compile);
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ciint ip_options_compile(struct net *net,
46662306a36Sopenharmony_ci		       struct ip_options *opt, struct sk_buff *skb)
46762306a36Sopenharmony_ci{
46862306a36Sopenharmony_ci	int ret;
46962306a36Sopenharmony_ci	__be32 info;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	ret = __ip_options_compile(net, opt, skb, &info);
47262306a36Sopenharmony_ci	if (ret != 0 && skb)
47362306a36Sopenharmony_ci		icmp_send(skb, ICMP_PARAMETERPROB, 0, info);
47462306a36Sopenharmony_ci	return ret;
47562306a36Sopenharmony_ci}
47662306a36Sopenharmony_ciEXPORT_SYMBOL(ip_options_compile);
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci/*
47962306a36Sopenharmony_ci *	Undo all the changes done by ip_options_compile().
48062306a36Sopenharmony_ci */
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_civoid ip_options_undo(struct ip_options *opt)
48362306a36Sopenharmony_ci{
48462306a36Sopenharmony_ci	if (opt->srr) {
48562306a36Sopenharmony_ci		unsigned char *optptr = opt->__data + opt->srr - sizeof(struct iphdr);
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci		memmove(optptr + 7, optptr + 3, optptr[1] - 7);
48862306a36Sopenharmony_ci		memcpy(optptr + 3, &opt->faddr, 4);
48962306a36Sopenharmony_ci	}
49062306a36Sopenharmony_ci	if (opt->rr_needaddr) {
49162306a36Sopenharmony_ci		unsigned char *optptr = opt->__data + opt->rr - sizeof(struct iphdr);
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci		optptr[2] -= 4;
49462306a36Sopenharmony_ci		memset(&optptr[optptr[2] - 1], 0, 4);
49562306a36Sopenharmony_ci	}
49662306a36Sopenharmony_ci	if (opt->ts) {
49762306a36Sopenharmony_ci		unsigned char *optptr = opt->__data + opt->ts - sizeof(struct iphdr);
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci		if (opt->ts_needtime) {
50062306a36Sopenharmony_ci			optptr[2] -= 4;
50162306a36Sopenharmony_ci			memset(&optptr[optptr[2] - 1], 0, 4);
50262306a36Sopenharmony_ci			if ((optptr[3] & 0xF) == IPOPT_TS_PRESPEC)
50362306a36Sopenharmony_ci				optptr[2] -= 4;
50462306a36Sopenharmony_ci		}
50562306a36Sopenharmony_ci		if (opt->ts_needaddr) {
50662306a36Sopenharmony_ci			optptr[2] -= 4;
50762306a36Sopenharmony_ci			memset(&optptr[optptr[2] - 1], 0, 4);
50862306a36Sopenharmony_ci		}
50962306a36Sopenharmony_ci	}
51062306a36Sopenharmony_ci}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ciint ip_options_get(struct net *net, struct ip_options_rcu **optp,
51362306a36Sopenharmony_ci		   sockptr_t data, int optlen)
51462306a36Sopenharmony_ci{
51562306a36Sopenharmony_ci	struct ip_options_rcu *opt;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	opt = kzalloc(sizeof(struct ip_options_rcu) + ((optlen + 3) & ~3),
51862306a36Sopenharmony_ci		       GFP_KERNEL);
51962306a36Sopenharmony_ci	if (!opt)
52062306a36Sopenharmony_ci		return -ENOMEM;
52162306a36Sopenharmony_ci	if (optlen && copy_from_sockptr(opt->opt.__data, data, optlen)) {
52262306a36Sopenharmony_ci		kfree(opt);
52362306a36Sopenharmony_ci		return -EFAULT;
52462306a36Sopenharmony_ci	}
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	while (optlen & 3)
52762306a36Sopenharmony_ci		opt->opt.__data[optlen++] = IPOPT_END;
52862306a36Sopenharmony_ci	opt->opt.optlen = optlen;
52962306a36Sopenharmony_ci	if (optlen && ip_options_compile(net, &opt->opt, NULL)) {
53062306a36Sopenharmony_ci		kfree(opt);
53162306a36Sopenharmony_ci		return -EINVAL;
53262306a36Sopenharmony_ci	}
53362306a36Sopenharmony_ci	kfree(*optp);
53462306a36Sopenharmony_ci	*optp = opt;
53562306a36Sopenharmony_ci	return 0;
53662306a36Sopenharmony_ci}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_civoid ip_forward_options(struct sk_buff *skb)
53962306a36Sopenharmony_ci{
54062306a36Sopenharmony_ci	struct   ip_options *opt	= &(IPCB(skb)->opt);
54162306a36Sopenharmony_ci	unsigned char *optptr;
54262306a36Sopenharmony_ci	struct rtable *rt = skb_rtable(skb);
54362306a36Sopenharmony_ci	unsigned char *raw = skb_network_header(skb);
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	if (opt->rr_needaddr) {
54662306a36Sopenharmony_ci		optptr = (unsigned char *)raw + opt->rr;
54762306a36Sopenharmony_ci		ip_rt_get_source(&optptr[optptr[2]-5], skb, rt);
54862306a36Sopenharmony_ci		opt->is_changed = 1;
54962306a36Sopenharmony_ci	}
55062306a36Sopenharmony_ci	if (opt->srr_is_hit) {
55162306a36Sopenharmony_ci		int srrptr, srrspace;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci		optptr = raw + opt->srr;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci		for ( srrptr = optptr[2], srrspace = optptr[1];
55662306a36Sopenharmony_ci		     srrptr <= srrspace;
55762306a36Sopenharmony_ci		     srrptr += 4
55862306a36Sopenharmony_ci		     ) {
55962306a36Sopenharmony_ci			if (srrptr + 3 > srrspace)
56062306a36Sopenharmony_ci				break;
56162306a36Sopenharmony_ci			if (memcmp(&opt->nexthop, &optptr[srrptr-1], 4) == 0)
56262306a36Sopenharmony_ci				break;
56362306a36Sopenharmony_ci		}
56462306a36Sopenharmony_ci		if (srrptr + 3 <= srrspace) {
56562306a36Sopenharmony_ci			opt->is_changed = 1;
56662306a36Sopenharmony_ci			ip_hdr(skb)->daddr = opt->nexthop;
56762306a36Sopenharmony_ci			ip_rt_get_source(&optptr[srrptr-1], skb, rt);
56862306a36Sopenharmony_ci			optptr[2] = srrptr+4;
56962306a36Sopenharmony_ci		} else {
57062306a36Sopenharmony_ci			net_crit_ratelimited("%s(): Argh! Destination lost!\n",
57162306a36Sopenharmony_ci					     __func__);
57262306a36Sopenharmony_ci		}
57362306a36Sopenharmony_ci		if (opt->ts_needaddr) {
57462306a36Sopenharmony_ci			optptr = raw + opt->ts;
57562306a36Sopenharmony_ci			ip_rt_get_source(&optptr[optptr[2]-9], skb, rt);
57662306a36Sopenharmony_ci			opt->is_changed = 1;
57762306a36Sopenharmony_ci		}
57862306a36Sopenharmony_ci	}
57962306a36Sopenharmony_ci	if (opt->is_changed) {
58062306a36Sopenharmony_ci		opt->is_changed = 0;
58162306a36Sopenharmony_ci		ip_send_check(ip_hdr(skb));
58262306a36Sopenharmony_ci	}
58362306a36Sopenharmony_ci}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ciint ip_options_rcv_srr(struct sk_buff *skb, struct net_device *dev)
58662306a36Sopenharmony_ci{
58762306a36Sopenharmony_ci	struct ip_options *opt = &(IPCB(skb)->opt);
58862306a36Sopenharmony_ci	int srrspace, srrptr;
58962306a36Sopenharmony_ci	__be32 nexthop;
59062306a36Sopenharmony_ci	struct iphdr *iph = ip_hdr(skb);
59162306a36Sopenharmony_ci	unsigned char *optptr = skb_network_header(skb) + opt->srr;
59262306a36Sopenharmony_ci	struct rtable *rt = skb_rtable(skb);
59362306a36Sopenharmony_ci	struct rtable *rt2;
59462306a36Sopenharmony_ci	unsigned long orefdst;
59562306a36Sopenharmony_ci	int err;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	if (!rt)
59862306a36Sopenharmony_ci		return 0;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	if (skb->pkt_type != PACKET_HOST)
60162306a36Sopenharmony_ci		return -EINVAL;
60262306a36Sopenharmony_ci	if (rt->rt_type == RTN_UNICAST) {
60362306a36Sopenharmony_ci		if (!opt->is_strictroute)
60462306a36Sopenharmony_ci			return 0;
60562306a36Sopenharmony_ci		icmp_send(skb, ICMP_PARAMETERPROB, 0, htonl(16<<24));
60662306a36Sopenharmony_ci		return -EINVAL;
60762306a36Sopenharmony_ci	}
60862306a36Sopenharmony_ci	if (rt->rt_type != RTN_LOCAL)
60962306a36Sopenharmony_ci		return -EINVAL;
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	for (srrptr = optptr[2], srrspace = optptr[1]; srrptr <= srrspace; srrptr += 4) {
61262306a36Sopenharmony_ci		if (srrptr + 3 > srrspace) {
61362306a36Sopenharmony_ci			icmp_send(skb, ICMP_PARAMETERPROB, 0, htonl((opt->srr+2)<<24));
61462306a36Sopenharmony_ci			return -EINVAL;
61562306a36Sopenharmony_ci		}
61662306a36Sopenharmony_ci		memcpy(&nexthop, &optptr[srrptr-1], 4);
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci		orefdst = skb->_skb_refdst;
61962306a36Sopenharmony_ci		skb_dst_set(skb, NULL);
62062306a36Sopenharmony_ci		err = ip_route_input(skb, nexthop, iph->saddr, iph->tos, dev);
62162306a36Sopenharmony_ci		rt2 = skb_rtable(skb);
62262306a36Sopenharmony_ci		if (err || (rt2->rt_type != RTN_UNICAST && rt2->rt_type != RTN_LOCAL)) {
62362306a36Sopenharmony_ci			skb_dst_drop(skb);
62462306a36Sopenharmony_ci			skb->_skb_refdst = orefdst;
62562306a36Sopenharmony_ci			return -EINVAL;
62662306a36Sopenharmony_ci		}
62762306a36Sopenharmony_ci		refdst_drop(orefdst);
62862306a36Sopenharmony_ci		if (rt2->rt_type != RTN_LOCAL)
62962306a36Sopenharmony_ci			break;
63062306a36Sopenharmony_ci		/* Superfast 8) loopback forward */
63162306a36Sopenharmony_ci		iph->daddr = nexthop;
63262306a36Sopenharmony_ci		opt->is_changed = 1;
63362306a36Sopenharmony_ci	}
63462306a36Sopenharmony_ci	if (srrptr <= srrspace) {
63562306a36Sopenharmony_ci		opt->srr_is_hit = 1;
63662306a36Sopenharmony_ci		opt->nexthop = nexthop;
63762306a36Sopenharmony_ci		opt->is_changed = 1;
63862306a36Sopenharmony_ci	}
63962306a36Sopenharmony_ci	return 0;
64062306a36Sopenharmony_ci}
64162306a36Sopenharmony_ciEXPORT_SYMBOL(ip_options_rcv_srr);
642