root/net/ipv4/ip_options.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ip_options_build
  2. ip_options_echo
  3. ip_options_fragment
  4. ip_options_compile

   1 /*
   2  * INET         An implementation of the TCP/IP protocol suite for the LINUX
   3  *              operating system.  INET is implemented using the  BSD Socket
   4  *              interface as the means of communication with the user level.
   5  *
   6  *              The options processing module for ip.c
   7  *
   8  * Authors:     A.N.Kuznetsov
   9  *              
  10  */
  11 
  12 #include <linux/config.h>
  13 #include <linux/types.h>
  14 #include <linux/skbuff.h>
  15 #include <linux/ip.h>
  16 #include <linux/icmp.h>
  17 #include <linux/netdevice.h>
  18 #include <net/sock.h>
  19 #include <net/ip.h>
  20 #include <net/icmp.h>
  21 
  22 /* 
  23  * Write options to IP header, record destination address to
  24  * source route option, address of outgoing interface
  25  * (we should already know it, so that this  function is allowed be
  26  * called only after routing decision) and timestamp,
  27  * if we originate this datagram.
  28  */
  29 
  30 void ip_options_build(struct sk_buff * skb, struct options * opt,
     /* [previous][next][first][last][top][bottom][index][help] */
  31                             __u32 daddr, __u32 saddr,
  32                             int is_frag) 
  33 {
  34         unsigned char * iph = (unsigned char*)skb->ip_hdr;
  35 
  36         memcpy(skb->proto_priv, opt, sizeof(struct options));
  37         memcpy(iph+sizeof(struct iphdr), opt->__data, opt->optlen);
  38         opt = (struct options*)skb->proto_priv;
  39         opt->is_data = 0;
  40 
  41         if (opt->srr)
  42                 memcpy(iph+opt->srr+iph[opt->srr+1]-4, &daddr, 4);
  43 
  44         if (!is_frag) 
  45         {
  46                 if (opt->rr_needaddr)
  47                         memcpy(iph+opt->rr+iph[opt->rr+2]-5, &saddr, 4);
  48                 if (opt->ts_needaddr)
  49                         memcpy(iph+opt->ts+iph[opt->ts+2]-9, &saddr, 4);
  50                 if (opt->ts_needtime) 
  51                 {
  52                         struct timeval tv;
  53                         __u32 midtime;
  54                         do_gettimeofday(&tv);
  55                         midtime = htonl((tv.tv_sec % 86400) * 1000 + tv.tv_usec / 1000);
  56                         memcpy(iph+opt->ts+iph[opt->ts+2]-5, &midtime, 4);
  57                 }
  58                 return;
  59         }
  60         if (opt->rr) 
  61         {
  62                 memset(iph+opt->rr, IPOPT_NOP, iph[opt->rr+1]);
  63                 opt->rr = 0;
  64                 opt->rr_needaddr = 0;
  65         }
  66         if (opt->ts) 
  67         {
  68                 memset(iph+opt->ts, IPOPT_NOP, iph[opt->ts+1]);
  69                 opt->ts = 0;
  70                 opt->ts_needaddr = opt->ts_needtime = 0;
  71         }
  72 }
  73 
  74 int ip_options_echo(struct options * dopt, struct options * sopt,
     /* [previous][next][first][last][top][bottom][index][help] */
  75                      __u32 daddr, __u32 saddr,
  76                      struct sk_buff * skb) 
  77 {
  78         unsigned char *sptr, *dptr;
  79         int soffset, doffset;
  80         int     optlen;
  81 
  82         memset(dopt, 0, sizeof(struct options));
  83 
  84         dopt->is_data = 1;
  85 
  86         if (!sopt)
  87                 sopt = (struct options*)skb->proto_priv;
  88 
  89         if (sopt->optlen == 0) 
  90         {
  91                 dopt->optlen = 0;
  92                 return 0;
  93         }
  94 
  95         sptr = (sopt->is_data ? sopt->__data - sizeof(struct iphdr) :
  96                 (unsigned char *)skb->ip_hdr);
  97         dptr = dopt->__data;
  98 
  99         if (sopt->rr) 
 100         {
 101                 optlen  = sptr[sopt->rr+1];
 102                 soffset = sptr[sopt->rr+2];
 103                 dopt->rr = dopt->optlen + sizeof(struct iphdr);
 104                 memcpy(dptr, sptr+sopt->rr, optlen);
 105                 if (sopt->rr_needaddr && soffset <= optlen) {
 106                         if (soffset + 3 > optlen)
 107                           return -EINVAL;
 108                         dptr[2] = soffset + 4;
 109                         dopt->rr_needaddr = 1;
 110                 }
 111                 dptr     += optlen;
 112                 dopt->optlen += optlen;
 113         }
 114         if (sopt->ts) 
 115         {
 116                 optlen = sptr[sopt->ts+1];
 117                 soffset = sptr[sopt->ts+2];
 118                 dopt->ts = dopt->optlen + sizeof(struct iphdr);
 119                 memcpy(dptr, sptr+sopt->ts, optlen);
 120                 if (soffset <= optlen) 
 121                 {
 122                         if (dopt->ts_needaddr) 
 123                         {
 124                                 if (soffset + 3 > optlen)
 125                                         return -EINVAL;
 126                                 dopt->ts_needaddr = 1;
 127                                 soffset += 4;
 128                         }
 129                         if (dopt->ts_needtime) 
 130                         {
 131                                 if (soffset + 3 > optlen)
 132                                         return -EINVAL;
 133                                 dopt->ts_needtime = 1;
 134                                 soffset += 4;
 135                         }
 136                         if (((struct timestamp*)(dptr+1))->flags == IPOPT_TS_PRESPEC) 
 137                         {
 138                                 __u32 addr;
 139                                 memcpy(&addr, sptr+soffset-9, 4);
 140                                 if (ip_chk_addr(addr) == 0) 
 141                                 {
 142                                         dopt->ts_needtime = 0;
 143                                         dopt->ts_needaddr = 0;
 144                                         soffset -= 8;
 145                                 }
 146                         }
 147                         dptr[2] = soffset;
 148                 }
 149                 dptr += optlen;
 150                 dopt->optlen += optlen;
 151         }
 152         if (sopt->srr) 
 153         {
 154                 unsigned char * start = sptr+sopt->srr;
 155                 __u32 faddr;
 156 
 157                 optlen  = start[1];
 158                 soffset = start[2];
 159                 doffset = 0;
 160                 if (soffset > optlen)
 161                         soffset = optlen + 1;
 162                 soffset -= 4;
 163                 if (soffset > 3) 
 164                 {
 165                         memcpy(&faddr, &start[soffset-1], 4);
 166                         for (soffset-=4, doffset=4; soffset > 3; soffset-=4, doffset+=4)
 167                                 memcpy(&dptr[doffset-1], &start[soffset-1], 4);
 168                         /*
 169                          * RFC1812 requires to fix illegal source routes.
 170                          */
 171                         if (memcmp(&saddr, &start[soffset+3], 4) == 0)
 172                                 doffset -= 4;
 173                 }
 174                 if (doffset > 3) 
 175                 {
 176                         memcpy(&start[doffset-1], &daddr, 4);
 177                         dopt->faddr = faddr;
 178                         dptr[0] = start[0];
 179                         dptr[1] = doffset+3;
 180                         dptr[2] = 4;
 181                         dptr += doffset+3;
 182                         dopt->srr = dopt->optlen + sizeof(struct iphdr);
 183                         dopt->optlen += doffset+3;
 184                         dopt->is_strictroute = sopt->is_strictroute;
 185                 }
 186         }
 187         while (dopt->optlen & 3) 
 188         {
 189                 *dptr++ = IPOPT_END;
 190                 dopt->optlen++;
 191         }
 192         return 0;
 193 }
 194 
 195 void ip_options_fragment(struct sk_buff * skb) 
     /* [previous][next][first][last][top][bottom][index][help] */
 196 {
 197         unsigned char * optptr = (unsigned char*)skb->ip_hdr;
 198         struct options * opt = (struct options*)skb->proto_priv;
 199         int  l = opt->optlen;
 200         int  optlen;
 201 
 202         while (l > 0) 
 203         {
 204                 switch (*optptr) 
 205                 {
 206                       case IPOPT_END:
 207                         return;
 208                       case IPOPT_NOOP:
 209                         l--;
 210                         optptr++;
 211                         continue;
 212                 }
 213                 optlen = optptr[1];
 214                 if (l<2 || optlen>l)
 215                   return;
 216                 if (!(*optptr & 0x80))
 217                         memset(optptr, IPOPT_NOOP, optlen);
 218                 l -= optlen;
 219                 optptr += optlen;
 220         }
 221         opt->ts = 0;
 222         opt->rr = 0;
 223         opt->rr_needaddr = 0;
 224         opt->ts_needaddr = 0;
 225         opt->ts_needtime = 0;
 226         return;
 227 }
 228 
 229 /*
 230  * Verify options and fill pointers in struct optinos.
 231  * Caller should clear *opt, and set opt->data.
 232  * If opt == NULL, then skb->data should point to IP header.
 233  */
 234 
 235 int ip_options_compile(struct options * opt, struct sk_buff * skb)
     /* [previous][next][first][last][top][bottom][index][help] */
 236 {
 237         int l;
 238         unsigned char * iph;
 239         unsigned char * optptr;
 240         int optlen;
 241         unsigned char * pp_ptr = NULL;
 242 
 243         if (!opt) 
 244         {
 245                 opt = (struct options*)skb->proto_priv;
 246                 memset(opt, 0, sizeof(struct options));
 247                 iph = (unsigned char*)skb->ip_hdr;
 248                 opt->optlen = ((struct iphdr *)iph)->ihl*4 - sizeof(struct iphdr);
 249                 optptr = iph + sizeof(struct iphdr);
 250                 opt->is_data = 0;
 251         }
 252         else 
 253         {
 254                 optptr = opt->is_data ? opt->__data : (unsigned char*)&skb->ip_hdr[1];
 255                 iph = optptr - sizeof(struct iphdr);
 256         }
 257 
 258         for (l = opt->optlen; l > 0; ) 
 259         {
 260                 switch (*optptr) 
 261                 {
 262                       case IPOPT_END:
 263                         for (optptr++, l--; l>0; l--) 
 264                         {
 265                                 if (*optptr != IPOPT_END) 
 266                                 {
 267                                         *optptr = IPOPT_END;
 268                                         opt->is_changed = 1;
 269                                 }
 270                         }
 271                         goto eol;
 272                       case IPOPT_NOOP:
 273                         l--;
 274                         optptr++;
 275                         continue;
 276                 }
 277                 optlen = optptr[1];
 278                 if (l<2 || optlen>l) 
 279                 {
 280                         pp_ptr = optptr;
 281                         break;
 282                 }
 283                 switch (*optptr) 
 284                 {
 285                       case IPOPT_SSRR:
 286                       case IPOPT_LSRR:
 287                         if (optlen < 3) 
 288                         {
 289                                 pp_ptr = optptr + 1;
 290                                 break;
 291                         }
 292                         if (optptr[2] < 4) 
 293                         {
 294                                 pp_ptr = optptr + 2;
 295                                 break;
 296                         }
 297                         /* NB: cf RFC-1812 5.2.4.1 */
 298                         if (opt->srr) 
 299                         {
 300                                 pp_ptr = optptr;
 301                                 break;
 302                         }
 303                         if (!skb) 
 304                         {
 305                                 if (optptr[2] != 4 || optlen < 7 || ((optlen-3) & 3)) 
 306                                 {
 307                                         pp_ptr = optptr + 1;
 308                                         break;
 309                                 }
 310                                 memcpy(&opt->faddr, &optptr[3], 4);
 311                                 if (optlen > 7)
 312                                         memmove(&optptr[3], &optptr[7], optlen-7);
 313                         }
 314                         opt->is_strictroute = (optptr[0] == IPOPT_SSRR);
 315                         opt->srr = optptr - iph;
 316                         break;
 317                       case IPOPT_RR:
 318                         if (opt->rr) 
 319                         {
 320                                 pp_ptr = optptr;
 321                                 break;
 322                         }
 323                         if (optlen < 3) 
 324                         {
 325                                 pp_ptr = optptr + 1;
 326                                 break;
 327                         }
 328                         if (optptr[2] < 4) 
 329                         {
 330                                 pp_ptr = optptr + 2;
 331                                 break;
 332                         }
 333                         if (optptr[2] <= optlen) 
 334                         {
 335                                 if (optptr[2]+3 > optlen) 
 336                                 {
 337                                         pp_ptr = optptr + 2;
 338                                         break;
 339                                 }
 340                                 if (skb) 
 341                                 {
 342                                         memcpy(&optptr[optptr[2]-1], &skb->dev->pa_addr, 4);
 343                                         opt->is_changed = 1;
 344                                 }
 345                                 optptr[2] += 4;
 346                                 opt->rr_needaddr = 1;
 347                         }
 348                         opt->rr = optptr - iph;
 349                         break;
 350                       case IPOPT_TIMESTAMP:
 351                         if (opt->ts) 
 352                         {
 353                                 pp_ptr = optptr;
 354                                 break;
 355                         }
 356                         if (optlen < 4) 
 357                         {
 358                                 pp_ptr = optptr + 1;
 359                                 break;
 360                         }
 361                         if (optptr[2] < 5) 
 362                         {
 363                                 pp_ptr = optptr + 2;
 364                                 break;
 365                         }
 366                         if (optptr[2] <= optlen) 
 367                         {
 368                                 struct timestamp * ts = (struct timestamp*)(optptr+1);
 369                                 __u32 * timeptr = NULL;
 370                                 if (ts->ptr+3 > ts->len) 
 371                                 {
 372                                         pp_ptr = optptr + 2;
 373                                         break;
 374                                 }
 375                                 switch (ts->flags) 
 376                                 {
 377                                       case IPOPT_TS_TSONLY:
 378                                         opt->ts = optptr - iph;
 379                                         if (skb) 
 380                                         {
 381                                                 timeptr = (__u32*)&optptr[ts->ptr-1];
 382                                                 opt->is_changed = 1;
 383                                         }
 384                                         ts->ptr += 4;
 385                                         break;
 386                                       case IPOPT_TS_TSANDADDR:
 387                                         if (ts->ptr+7 > ts->len) 
 388                                         {
 389                                                 pp_ptr = optptr + 2;
 390                                                 break;
 391                                         }
 392                                         opt->ts = optptr - iph;
 393                                         if (skb) 
 394                                         {
 395                                                 memcpy(&optptr[ts->ptr-1], &skb->dev->pa_addr, 4);
 396                                                 timeptr = (__u32*)&optptr[ts->ptr+3];
 397                                         }
 398                                         opt->ts_needaddr = 1;
 399                                         opt->ts_needtime = 1;
 400                                         ts->ptr += 8;
 401                                         break;
 402                                       case IPOPT_TS_PRESPEC:
 403                                         if (ts->ptr+7 > ts->len) 
 404                                         {
 405                                                 pp_ptr = optptr + 2;
 406                                                 break;
 407                                         }
 408                                         opt->ts = optptr - iph;
 409                                         {
 410                                                 __u32 addr;
 411                                                 memcpy(&addr, &optptr[ts->ptr-1], 4);
 412                                                 if (ip_chk_addr(addr) == 0)
 413                                                         break;
 414                                                 if (skb)
 415                                                         timeptr = (__u32*)&optptr[ts->ptr+3];
 416                                         }
 417                                         opt->ts_needaddr = 1;
 418                                         opt->ts_needtime = 1;
 419                                         ts->ptr += 8;
 420                                         break;
 421                                       default:
 422                                         pp_ptr = optptr + 3;
 423                                         break;
 424                                 }
 425                                 if (timeptr) 
 426                                 {
 427                                         struct timeval tv;
 428                                         __u32  midtime;
 429                                         do_gettimeofday(&tv);
 430                                         midtime = htonl((tv.tv_sec % 86400) * 1000 + tv.tv_usec / 1000);
 431                                         memcpy(timeptr, &midtime, sizeof(__u32));
 432                                         opt->is_changed = 1;
 433                                 }
 434                         } 
 435                         else 
 436                         {
 437                                 struct timestamp * ts = (struct timestamp*)(optptr+1);
 438                                 if (ts->overflow == 15) 
 439                                 {
 440                                         pp_ptr = optptr + 3;
 441                                         break;
 442                                 }
 443                                 opt->ts = optptr - iph;
 444                                 if (skb) 
 445                                 {
 446                                         ts->overflow++;
 447                                         opt->is_changed = 1;
 448                                 }
 449                         }
 450                         break;
 451                       case IPOPT_SEC:
 452                       case IPOPT_SID:
 453                       default:
 454                         if (!skb) 
 455                         {
 456                                 pp_ptr = optptr;
 457                                 break;
 458                         }
 459                         break;
 460                 }
 461                 l -= optlen;
 462                 optptr += optlen;
 463         }
 464 
 465 eol:
 466         if (!pp_ptr)
 467                 return 0;
 468 
 469         if (skb) 
 470         {
 471                 icmp_send(skb, ICMP_PARAMETERPROB, 0, pp_ptr-iph, skb->dev);
 472                 kfree_skb(skb, FREE_READ);
 473         }
 474         return -EINVAL;
 475 }
 476 

/* [previous][next][first][last][top][bottom][index][help] */