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

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