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