root/net/inet/route.c

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

DEFINITIONS

This source file includes following definitions.
  1. rt_print
  2. rt_del
  3. rt_flush
  4. default_mask
  5. guess_mask
  6. get_gw_dev
  7. rt_add
  8. bad_mask
  9. rt_new
  10. rt_kill
  11. rt_get_info
  12. rt_route
  13. rt_ioctl

   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  *              ROUTE - implementation of the IP router.
   7  *
   8  * Version:     @(#)route.c     1.0.14  05/31/93
   9  *
  10  * Authors:     Ross Biro, <bir7@leland.Stanford.Edu>
  11  *              Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
  12  *
  13  * Fixes:
  14  *              Alan Cox        :       Verify area fixes.
  15  *              Alan Cox        :       cli() protects routing changes
  16  *              Rui Oliveira    :       ICMP routing table updates
  17  *              (rco@di.uminho.pt)      Routing table insertion and update
  18  *              Linus Torvalds  :       Rewrote bits to be sensible
  19  *
  20  *              This program is free software; you can redistribute it and/or
  21  *              modify it under the terms of the GNU General Public License
  22  *              as published by the Free Software Foundation; either version
  23  *              2 of the License, or (at your option) any later version.
  24  */
  25 #include <asm/segment.h>
  26 #include <asm/system.h>
  27 #include <linux/types.h>
  28 #include <linux/kernel.h>
  29 #include <linux/sched.h>
  30 #include <linux/string.h>
  31 #include <linux/socket.h>
  32 #include <linux/sockios.h>
  33 #include <linux/errno.h>
  34 #include <linux/in.h>
  35 #include "inet.h"
  36 #include "dev.h"
  37 #include "ip.h"
  38 #include "protocol.h"
  39 #include "route.h"
  40 #include "tcp.h"
  41 #include "skbuff.h"
  42 #include "sock.h"
  43 #include "arp.h"
  44 #include "icmp.h"
  45 
  46 
  47 static struct rtable *rt_base = NULL;
  48 static struct rtable *rt_loopback = NULL;
  49 
  50 /* Dump the contents of a routing table entry. */
  51 static void
  52 rt_print(struct rtable *rt)
     /* [previous][next][first][last][top][bottom][index][help] */
  53 {
  54   if (rt == NULL || inet_debug != DBG_RT) return;
  55 
  56   printk("RT: %06lx NXT=%06lx FLAGS=0x%02x\n",
  57                 (long) rt, (long) rt->rt_next, rt->rt_flags);
  58   printk("    TARGET=%s ", in_ntoa(rt->rt_dst));
  59   printk("GW=%s ", in_ntoa(rt->rt_gateway));
  60   printk("    DEV=%s USE=%ld REF=%d\n",
  61         (rt->rt_dev == NULL) ? "NONE" : rt->rt_dev->name,
  62         rt->rt_use, rt->rt_refcnt);
  63 }
  64 
  65 
  66 /*
  67  * Remove a routing table entry.
  68  */
  69 static void rt_del(unsigned long dst)
     /* [previous][next][first][last][top][bottom][index][help] */
  70 {
  71         struct rtable *r, **rp;
  72         unsigned long flags;
  73 
  74         DPRINTF((DBG_RT, "RT: flushing for dst %s\n", in_ntoa(dst)));
  75         rp = &rt_base;
  76         save_flags(flags);
  77         cli();
  78         while((r = *rp) != NULL) {
  79                 if (r->rt_dst != dst) {
  80                         rp = &r->rt_next;
  81                         continue;
  82                 }
  83                 *rp = r->rt_next;
  84                 if (rt_loopback == r)
  85                         rt_loopback = NULL;
  86                 kfree_s(r, sizeof(struct rtable));
  87         } 
  88         restore_flags(flags);
  89 }
  90 
  91 
  92 /*
  93  * Remove all routing table entries for a device.
  94  */
  95 void rt_flush(struct device *dev)
     /* [previous][next][first][last][top][bottom][index][help] */
  96 {
  97         struct rtable *r;
  98         struct rtable **rp;
  99         unsigned long flags;
 100 
 101         DPRINTF((DBG_RT, "RT: flushing for dev 0x%08lx (%s)\n", (long)dev, dev->name));
 102         rp = &rt_base;
 103         cli();
 104         save_flags(flags);
 105         while ((r = *rp) != NULL) {
 106                 if (r->rt_dev != dev) {
 107                         rp = &r->rt_next;
 108                         continue;
 109                 }
 110                 *rp = r->rt_next;
 111                 if (rt_loopback == r)
 112                         rt_loopback = NULL;
 113                 kfree_s(r, sizeof(struct rtable));
 114         } 
 115         restore_flags(flags);
 116 }
 117 
 118 /*
 119  * Used by 'rt_add()' when we can't get the netmask any other way..
 120  *
 121  * If the lower byte or two are zero, we guess the mask based on the
 122  * number of zero 8-bit net numbers, otherwise we use the "default"
 123  * masks judging by the destination address and our device netmask.
 124  */
 125 static inline unsigned long default_mask(unsigned long dst)
     /* [previous][next][first][last][top][bottom][index][help] */
 126 {
 127         dst = ntohl(dst);
 128         if (IN_CLASSA(dst))
 129                 return htonl(IN_CLASSA_NET);
 130         if (IN_CLASSB(dst))
 131                 return htonl(IN_CLASSB_NET);
 132         return htonl(IN_CLASSC_NET);
 133 }
 134 
 135 static unsigned long guess_mask(unsigned long dst, struct device * dev)
     /* [previous][next][first][last][top][bottom][index][help] */
 136 {
 137         unsigned long mask;
 138 
 139         if (!dst)
 140                 return 0;
 141         mask = default_mask(dst);
 142         if ((dst ^ dev->pa_addr) & mask)
 143                 return mask;
 144         return dev->pa_mask;
 145 }
 146 
 147 static inline struct device * get_gw_dev(unsigned long gw)
     /* [previous][next][first][last][top][bottom][index][help] */
 148 {
 149         struct rtable * rt;
 150 
 151         for (rt = rt_base ; ; rt = rt->rt_next) {
 152                 if (!rt)
 153                         return NULL;
 154                 if ((gw ^ rt->rt_dst) & rt->rt_mask)
 155                         continue;
 156                 /* gateways behind gateways are a no-no */
 157                 if (rt->rt_flags & RTF_GATEWAY)
 158                         return NULL;
 159                 return rt->rt_dev;
 160         }
 161 }
 162 
 163 /*
 164  * rewrote rt_add(), as the old one was weird. Linus
 165  */
 166 void rt_add(short flags, unsigned long dst, unsigned long mask,
     /* [previous][next][first][last][top][bottom][index][help] */
 167         unsigned long gw, struct device *dev)
 168 {
 169         struct rtable *r, *rt;
 170         struct rtable **rp;
 171         unsigned long cpuflags;
 172 
 173         if (flags & RTF_HOST) {
 174                 mask = 0xffffffff;
 175         } else if (!mask) {
 176                 if (!((dst ^ dev->pa_addr) & dev->pa_mask)) {
 177                         mask = dev->pa_mask;
 178                         flags &= ~RTF_GATEWAY;
 179                         if (flags & RTF_DYNAMIC) {
 180                                 /*printk("Dynamic route to my own net rejected\n");*/
 181                                 return;
 182                         }
 183                 } else
 184                         mask = guess_mask(dst, dev);
 185                 dst &= mask;
 186         }
 187         if (gw == dev->pa_addr)
 188                 flags &= ~RTF_GATEWAY;
 189         if (flags & RTF_GATEWAY) {
 190                 /* don't try to add a gateway we can't reach.. */
 191                 if (dev != get_gw_dev(gw))
 192                         return;
 193                 flags |= RTF_GATEWAY;
 194         } else
 195                 gw = 0;
 196         /* Allocate an entry. */
 197         rt = (struct rtable *) kmalloc(sizeof(struct rtable), GFP_ATOMIC);
 198         if (rt == NULL) {
 199                 DPRINTF((DBG_RT, "RT: no memory for new route!\n"));
 200                 return;
 201         }
 202         memset(rt, 0, sizeof(struct rtable));
 203         rt->rt_flags = flags | RTF_UP;
 204         rt->rt_dst = dst;
 205         rt->rt_dev = dev;
 206         rt->rt_gateway = gw;
 207         rt->rt_mask = mask;
 208         rt->rt_mtu = dev->mtu;
 209         rt_print(rt);
 210         /*
 211          * What we have to do is loop though this until we have
 212          * found the first address which has a higher generality than
 213          * the one in rt.  Then we can put rt in right before it.
 214          */
 215         save_flags(cpuflags);
 216         cli();
 217         /* remove old route if we are getting a duplicate. */
 218         rp = &rt_base;
 219         while ((r = *rp) != NULL) {
 220                 if (r->rt_dst != dst) {
 221                         rp = &r->rt_next;
 222                         continue;
 223                 }
 224                 *rp = r->rt_next;
 225                 if (rt_loopback == r)
 226                         rt_loopback = NULL;
 227                 kfree_s(r, sizeof(struct rtable));
 228         }
 229         /* add the new route */
 230         rp = &rt_base;
 231         while ((r = *rp) != NULL) {
 232                 if ((r->rt_mask & mask) != mask)
 233                         break;
 234                 rp = &r->rt_next;
 235         }
 236         rt->rt_next = r;
 237         *rp = rt;
 238         if (rt->rt_dev->flags & IFF_LOOPBACK)
 239                 rt_loopback = rt;
 240         restore_flags(cpuflags);
 241         return;
 242 }
 243 
 244 static inline int bad_mask(unsigned long mask, unsigned long addr)
     /* [previous][next][first][last][top][bottom][index][help] */
 245 {
 246         if (addr & (mask = ~mask))
 247                 return 1;
 248         mask = ntohl(mask);
 249         if (mask & (mask+1))
 250                 return 1;
 251         return 0;
 252 }
 253 
 254 static int rt_new(struct rtentry *r)
     /* [previous][next][first][last][top][bottom][index][help] */
 255 {
 256         struct device *dev;
 257         unsigned long flags, daddr, mask, gw;
 258 
 259         if (r->rt_dst.sa_family != AF_INET)
 260                 return -EAFNOSUPPORT;
 261 
 262         flags = r->rt_flags;
 263         daddr = ((struct sockaddr_in *) &r->rt_dst)->sin_addr.s_addr;
 264         mask = r->rt_genmask;
 265         gw = ((struct sockaddr_in *) &r->rt_gateway)->sin_addr.s_addr;
 266         dev = (struct device *) r->rt_dev;
 267 
 268         if (flags & RTF_GATEWAY) {
 269                 if (r->rt_gateway.sa_family != AF_INET)
 270                         return -EAFNOSUPPORT;
 271                 if (!dev)
 272                         dev = get_gw_dev(gw);
 273         } else if (!dev)
 274                 dev = dev_check(daddr);
 275 
 276         if (dev == NULL)
 277                 return -ENETUNREACH;
 278 
 279         if (bad_mask(mask, daddr))
 280                 mask = 0;
 281 
 282         rt_add(flags, daddr, mask, gw, dev);
 283         return 0;
 284 }
 285 
 286 
 287 static int rt_kill(struct rtentry *r)
     /* [previous][next][first][last][top][bottom][index][help] */
 288 {
 289         struct sockaddr_in *trg;
 290 
 291         trg = (struct sockaddr_in *) &r->rt_dst;
 292         rt_del(trg->sin_addr.s_addr);
 293         return 0;
 294 }
 295 
 296 
 297 /* Called from the PROCfs module. */
 298 int
 299 rt_get_info(char *buffer)
     /* [previous][next][first][last][top][bottom][index][help] */
 300 {
 301   struct rtable *r;
 302   char *pos;
 303 
 304   pos = buffer;
 305 
 306   pos += sprintf(pos,
 307                  "Iface\tDestination\tGateway \tFlags\tRefCnt\tUse\tMetric\tMask\n");
 308   
 309   /* This isn't quite right -- r->rt_dst is a struct! */
 310   for (r = rt_base; r != NULL; r = r->rt_next) {
 311         pos += sprintf(pos, "%s\t%08lX\t%08lX\t%02X\t%d\t%lu\t%d\t%08lX\n",
 312                 r->rt_dev->name, r->rt_dst, r->rt_gateway,
 313                 r->rt_flags, r->rt_refcnt, r->rt_use, r->rt_metric,
 314                 r->rt_mask);
 315   }
 316   return(pos - buffer);
 317 }
 318 
 319 /*
 320  * This is hackish, but results in better code. Use "-S" to see why.
 321  */
 322 #define early_out ({ goto no_route; 1; })
 323 
 324 struct rtable * rt_route(unsigned long daddr, struct options *opt)
     /* [previous][next][first][last][top][bottom][index][help] */
 325 {
 326         struct rtable *rt;
 327 
 328         for (rt = rt_base; rt != NULL || early_out ; rt = rt->rt_next) {
 329                 if (!((rt->rt_dst ^ daddr) & rt->rt_mask))
 330                         break;
 331                 /* broadcast addresses can be special cases.. */
 332                 if ((rt->rt_dev->flags & IFF_BROADCAST) &&
 333                      rt->rt_dev->pa_brdaddr == daddr)
 334                         break;
 335         }
 336         if (daddr == rt->rt_dev->pa_addr) {
 337                 if ((rt = rt_loopback) == NULL)
 338                         goto no_route;
 339         }
 340         rt->rt_use++;
 341         return rt;
 342 no_route:
 343         return NULL;
 344 }
 345 
 346 
 347 int rt_ioctl(unsigned int cmd, void *arg)
     /* [previous][next][first][last][top][bottom][index][help] */
 348 {
 349         struct device *dev;
 350         struct rtentry rt;
 351         char *devname;
 352         int ret;
 353         int err;
 354 
 355         switch(cmd) {
 356         case DDIOCSDBG:
 357                 ret = dbg_ioctl(arg, DBG_RT);
 358                 break;
 359         case SIOCADDRT:
 360         case SIOCDELRT:
 361                 if (!suser())
 362                         return -EPERM;
 363                 err=verify_area(VERIFY_READ, arg, sizeof(struct rtentry));
 364                 if (err)
 365                         return err;
 366                 memcpy_fromfs(&rt, arg, sizeof(struct rtentry));
 367                 if ((devname = (char *) rt.rt_dev) != NULL) {
 368                         err = getname(devname, &devname);
 369                         if (err)
 370                                 return err;
 371                         dev = dev_get(devname);
 372                         putname(devname);
 373                         if (!dev)
 374                                 return -EINVAL;
 375                         rt.rt_dev = dev;
 376                 }
 377                 ret = (cmd == SIOCDELRT) ? rt_kill(&rt) : rt_new(&rt);
 378                 break;
 379         default:
 380                 ret = -EINVAL;
 381         }
 382 
 383         return ret;
 384 }

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