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

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