LCOV - code coverage report
Current view: top level - net/ipv4 - ip_options.c (source / functions) Hit Total Coverage
Test: landlock.info Lines: 0 405 0.0 %
Date: 2021-04-22 12:43:58 Functions: 0 9 0.0 %

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

Generated by: LCOV version 1.14