linux-old/net/ipv4/fib_frontend.c
<<
>>
Prefs
   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 *              IPv4 Forwarding Information Base: FIB frontend.
   7 *
   8 * Version:     $Id: fib_frontend.c,v 1.15 1999/03/21 05:22:31 davem Exp $
   9 *
  10 * Authors:     Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  11 *
  12 *              This program is free software; you can redistribute it and/or
  13 *              modify it under the terms of the GNU General Public License
  14 *              as published by the Free Software Foundation; either version
  15 *              2 of the License, or (at your option) any later version.
  16 */
  17
  18#include <linux/config.h>
  19#include <asm/uaccess.h>
  20#include <asm/system.h>
  21#include <asm/bitops.h>
  22#include <linux/types.h>
  23#include <linux/kernel.h>
  24#include <linux/sched.h>
  25#include <linux/mm.h>
  26#include <linux/string.h>
  27#include <linux/socket.h>
  28#include <linux/sockios.h>
  29#include <linux/errno.h>
  30#include <linux/in.h>
  31#include <linux/inet.h>
  32#include <linux/netdevice.h>
  33#include <linux/if_arp.h>
  34#include <linux/proc_fs.h>
  35#include <linux/skbuff.h>
  36#include <linux/netlink.h>
  37#include <linux/init.h>
  38
  39#include <net/ip.h>
  40#include <net/protocol.h>
  41#include <net/route.h>
  42#include <net/tcp.h>
  43#include <net/sock.h>
  44#include <net/icmp.h>
  45#include <net/arp.h>
  46#include <net/ip_fib.h>
  47
  48#define FFprint(a...) printk(KERN_DEBUG a)
  49
  50#ifndef CONFIG_IP_MULTIPLE_TABLES
  51
  52#define RT_TABLE_MIN RT_TABLE_MAIN
  53
  54struct fib_table *local_table;
  55struct fib_table *main_table;
  56
  57#else
  58
  59#define RT_TABLE_MIN 1
  60
  61struct fib_table *fib_tables[RT_TABLE_MAX+1];
  62
  63struct fib_table *__fib_new_table(int id)
  64{
  65        struct fib_table *tb;
  66
  67        tb = fib_hash_init(id);
  68        if (!tb)
  69                return NULL;
  70        fib_tables[id] = tb;
  71        return tb;
  72}
  73
  74
  75#endif /* CONFIG_IP_MULTIPLE_TABLES */
  76
  77
  78void fib_flush(void)
  79{
  80        int flushed = 0;
  81#ifdef CONFIG_IP_MULTIPLE_TABLES
  82        struct fib_table *tb;
  83        int id;
  84
  85        for (id = RT_TABLE_MAX; id>0; id--) {
  86                if ((tb = fib_get_table(id))==NULL)
  87                        continue;
  88                flushed += tb->tb_flush(tb);
  89        }
  90#else /* CONFIG_IP_MULTIPLE_TABLES */
  91        flushed += main_table->tb_flush(main_table);
  92        flushed += local_table->tb_flush(local_table);
  93#endif /* CONFIG_IP_MULTIPLE_TABLES */
  94
  95        if (flushed)
  96                rt_cache_flush(-1);
  97}
  98
  99
 100#ifdef CONFIG_PROC_FS
 101
 102/* 
 103 *      Called from the PROCfs module. This outputs /proc/net/route.
 104 *
 105 *      It always works in backward compatibility mode.
 106 *      The format of the file is not supposed to be changed.
 107 */
 108 
 109static int
 110fib_get_procinfo(char *buffer, char **start, off_t offset, int length, int dummy)
 111{
 112        int first = offset/128;
 113        char *ptr = buffer;
 114        int count = (length+127)/128;
 115        int len;
 116
 117        *start = buffer + offset%128;
 118        
 119        if (--first < 0) {
 120                sprintf(buffer, "%-127s\n", "Iface\tDestination\tGateway \tFlags\tRefCnt\tUse\tMetric\tMask\t\tMTU\tWindow\tIRTT");
 121                --count;
 122                ptr += 128;
 123                first = 0;
 124        }
 125
 126        /* rtnl_shlock(); -- it is pointless at the moment --ANK */
 127        if (main_table && count > 0) {
 128                int n = main_table->tb_get_info(main_table, ptr, first, count);
 129                count -= n;
 130                ptr += n*128;
 131        }
 132        /* rtnl_shunlock(); */
 133        len = ptr - *start;
 134        if (len >= length)
 135                return length;
 136        if (len >= 0)
 137                return len;
 138        return 0;
 139}
 140
 141#endif /* CONFIG_PROC_FS */
 142
 143/*
 144 *      Find the first device with a given source address.
 145 */
 146
 147struct device * ip_dev_find(u32 addr)
 148{
 149        struct rt_key key;
 150        struct fib_result res;
 151
 152        memset(&key, 0, sizeof(key));
 153        key.dst = addr;
 154
 155        if (!local_table || local_table->tb_lookup(local_table, &key, &res)
 156            || res.type != RTN_LOCAL)
 157                return NULL;
 158
 159        return FIB_RES_DEV(res);
 160}
 161
 162unsigned inet_addr_type(u32 addr)
 163{
 164        struct rt_key           key;
 165        struct fib_result       res;
 166
 167        if (ZERONET(addr) || BADCLASS(addr))
 168                return RTN_BROADCAST;
 169        if (MULTICAST(addr))
 170                return RTN_MULTICAST;
 171
 172        memset(&key, 0, sizeof(key));
 173        key.dst = addr;
 174
 175        if (local_table) {
 176                if (local_table->tb_lookup(local_table, &key, &res) == 0)
 177                        return res.type;
 178                return RTN_UNICAST;
 179        }
 180        return RTN_BROADCAST;
 181}
 182
 183/* Given (packet source, input interface) and optional (dst, oif, tos):
 184   - (main) check, that source is valid i.e. not broadcast or our local
 185     address.
 186   - figure out what "logical" interface this packet arrived
 187     and calculate "specific destination" address.
 188   - check, that packet arrived from expected physical interface.
 189 */
 190
 191int fib_validate_source(u32 src, u32 dst, u8 tos, int oif,
 192                        struct device *dev, u32 *spec_dst, u32 *itag)
 193{
 194        struct in_device *in_dev = dev->ip_ptr;
 195        struct rt_key key;
 196        struct fib_result res;
 197
 198        key.dst = src;
 199        key.src = dst;
 200        key.tos = tos;
 201        key.oif = 0;
 202        key.iif = oif;
 203        key.scope = RT_SCOPE_UNIVERSE;
 204
 205        if (in_dev == NULL)
 206                return -EINVAL;
 207        if (fib_lookup(&key, &res))
 208                goto last_resort;
 209        if (res.type != RTN_UNICAST)
 210                return -EINVAL;
 211        *spec_dst = FIB_RES_PREFSRC(res);
 212        if (itag)
 213                fib_combine_itag(itag, &res);
 214#ifdef CONFIG_IP_ROUTE_MULTIPATH
 215        if (FIB_RES_DEV(res) == dev || res.fi->fib_nhs > 1)
 216#else
 217        if (FIB_RES_DEV(res) == dev)
 218#endif
 219                return FIB_RES_NH(res).nh_scope >= RT_SCOPE_HOST;
 220
 221        if (in_dev->ifa_list == NULL)
 222                goto last_resort;
 223        if (IN_DEV_RPFILTER(in_dev))
 224                return -EINVAL;
 225        key.oif = dev->ifindex;
 226        if (fib_lookup(&key, &res) == 0 && res.type == RTN_UNICAST) {
 227                *spec_dst = FIB_RES_PREFSRC(res);
 228                return FIB_RES_NH(res).nh_scope >= RT_SCOPE_HOST;
 229        }
 230        return 0;
 231
 232last_resort:
 233        if (IN_DEV_RPFILTER(in_dev))
 234                return -EINVAL;
 235        *spec_dst = inet_select_addr(dev, 0, RT_SCOPE_UNIVERSE);
 236        *itag = 0;
 237        return 0;
 238}
 239
 240#ifndef CONFIG_IP_NOSIOCRT
 241
 242/*
 243 *      Handle IP routing ioctl calls. These are used to manipulate the routing tables
 244 */
 245 
 246int ip_rt_ioctl(unsigned int cmd, void *arg)
 247{
 248        int err;
 249        struct kern_rta rta;
 250        struct rtentry  r;
 251        struct {
 252                struct nlmsghdr nlh;
 253                struct rtmsg    rtm;
 254        } req;
 255
 256        switch (cmd) {
 257        case SIOCADDRT:         /* Add a route */
 258        case SIOCDELRT:         /* Delete a route */
 259                if (!capable(CAP_NET_ADMIN))
 260                        return -EPERM;
 261                if (copy_from_user(&r, arg, sizeof(struct rtentry)))
 262                        return -EFAULT;
 263                rtnl_lock();
 264                err = fib_convert_rtentry(cmd, &req.nlh, &req.rtm, &rta, &r);
 265                if (err == 0) {
 266                        if (cmd == SIOCDELRT) {
 267                                struct fib_table *tb = fib_get_table(req.rtm.rtm_table);
 268                                err = -ESRCH;
 269                                if (tb)
 270                                        err = tb->tb_delete(tb, &req.rtm, &rta, &req.nlh, NULL);
 271                        } else {
 272                                struct fib_table *tb = fib_new_table(req.rtm.rtm_table);
 273                                err = -ENOBUFS;
 274                                if (tb)
 275                                        err = tb->tb_insert(tb, &req.rtm, &rta, &req.nlh, NULL);
 276                        }
 277                        if (rta.rta_mx)
 278                                kfree(rta.rta_mx);
 279                }
 280                rtnl_unlock();
 281                return err;
 282        }
 283        return -EINVAL;
 284}
 285
 286#else
 287
 288int ip_rt_ioctl(unsigned int cmd, void *arg)
 289{
 290        return -EINVAL;
 291}
 292
 293#endif
 294
 295#ifdef CONFIG_RTNETLINK
 296
 297static int inet_check_attr(struct rtmsg *r, struct rtattr **rta)
 298{
 299        int i;
 300
 301        for (i=1; i<=RTA_MAX; i++) {
 302                struct rtattr *attr = rta[i-1];
 303                if (attr) {
 304                        if (RTA_PAYLOAD(attr) < 4)
 305                                return -EINVAL;
 306                        if (i != RTA_MULTIPATH && i != RTA_METRICS)
 307                                rta[i-1] = (struct rtattr*)RTA_DATA(attr);
 308                }
 309        }
 310        return 0;
 311}
 312
 313int inet_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
 314{
 315        struct fib_table * tb;
 316        struct rtattr **rta = arg;
 317        struct rtmsg *r = NLMSG_DATA(nlh);
 318
 319        if (inet_check_attr(r, rta))
 320                return -EINVAL;
 321
 322        tb = fib_get_table(r->rtm_table);
 323        if (tb)
 324                return tb->tb_delete(tb, r, (struct kern_rta*)rta, nlh, &NETLINK_CB(skb));
 325        return -ESRCH;
 326}
 327
 328int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
 329{
 330        struct fib_table * tb;
 331        struct rtattr **rta = arg;
 332        struct rtmsg *r = NLMSG_DATA(nlh);
 333
 334        if (inet_check_attr(r, rta))
 335                return -EINVAL;
 336
 337        tb = fib_new_table(r->rtm_table);
 338        if (tb)
 339                return tb->tb_insert(tb, r, (struct kern_rta*)rta, nlh, &NETLINK_CB(skb));
 340        return -ENOBUFS;
 341}
 342
 343int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
 344{
 345        int t;
 346        int s_t;
 347        struct fib_table *tb;
 348
 349        if (NLMSG_PAYLOAD(cb->nlh, 0) >= sizeof(struct rtmsg) &&
 350            ((struct rtmsg*)NLMSG_DATA(cb->nlh))->rtm_flags&RTM_F_CLONED)
 351                return ip_rt_dump(skb, cb);
 352
 353        s_t = cb->args[0];
 354        if (s_t == 0)
 355                s_t = cb->args[0] = RT_TABLE_MIN;
 356
 357        for (t=s_t; t<=RT_TABLE_MAX; t++) {
 358                if (t < s_t) continue;
 359                if (t > s_t)
 360                        memset(&cb->args[1], 0, sizeof(cb->args)-sizeof(cb->args[0]));
 361                if ((tb = fib_get_table(t))==NULL)
 362                        continue;
 363                if (tb->tb_dump(tb, skb, cb) < 0) 
 364                        break;
 365        }
 366
 367        cb->args[0] = t;
 368
 369        return skb->len;
 370}
 371
 372#endif
 373
 374/* Prepare and feed intra-kernel routing request.
 375   Really, it should be netlink message, but :-( netlink
 376   can be not configured, so that we feed it directly
 377   to fib engine. It is legal, because all events occur
 378   only when netlink is already locked.
 379 */
 380
 381static void fib_magic(int cmd, int type, u32 dst, int dst_len, struct in_ifaddr *ifa)
 382{
 383        struct fib_table * tb;
 384        struct {
 385                struct nlmsghdr nlh;
 386                struct rtmsg    rtm;
 387        } req;
 388        struct kern_rta rta;
 389
 390        memset(&req.rtm, 0, sizeof(req.rtm));
 391        memset(&rta, 0, sizeof(rta));
 392
 393        if (type == RTN_UNICAST)
 394                tb = fib_new_table(RT_TABLE_MAIN);
 395        else
 396                tb = fib_new_table(RT_TABLE_LOCAL);
 397
 398        if (tb == NULL)
 399                return;
 400
 401        req.nlh.nlmsg_len = sizeof(req);
 402        req.nlh.nlmsg_type = cmd;
 403        req.nlh.nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_APPEND;
 404        req.nlh.nlmsg_pid = 0;
 405        req.nlh.nlmsg_seq = 0;
 406
 407        req.rtm.rtm_dst_len = dst_len;
 408        req.rtm.rtm_table = tb->tb_id;
 409        req.rtm.rtm_protocol = RTPROT_KERNEL;
 410        req.rtm.rtm_scope = (type != RTN_LOCAL ? RT_SCOPE_LINK : RT_SCOPE_HOST);
 411        req.rtm.rtm_type = type;
 412
 413        rta.rta_dst = &dst;
 414        rta.rta_prefsrc = &ifa->ifa_local;
 415        rta.rta_oif = &ifa->ifa_dev->dev->ifindex;
 416
 417        if (cmd == RTM_NEWROUTE)
 418                tb->tb_insert(tb, &req.rtm, &rta, &req.nlh, NULL);
 419        else
 420                tb->tb_delete(tb, &req.rtm, &rta, &req.nlh, NULL);
 421}
 422
 423static void fib_add_ifaddr(struct in_ifaddr *ifa)
 424{
 425        struct in_device *in_dev = ifa->ifa_dev;
 426        struct device *dev = in_dev->dev;
 427        struct in_ifaddr *prim = ifa;
 428        u32 mask = ifa->ifa_mask;
 429        u32 addr = ifa->ifa_local;
 430        u32 prefix = ifa->ifa_address&mask;
 431
 432        if (ifa->ifa_flags&IFA_F_SECONDARY) {
 433                prim = inet_ifa_byprefix(in_dev, prefix, mask);
 434                if (prim == NULL) {
 435                        printk(KERN_DEBUG "fib_add_ifaddr: bug: prim == NULL\n");
 436                        return;
 437                }
 438        }
 439
 440        fib_magic(RTM_NEWROUTE, RTN_LOCAL, addr, 32, prim);
 441
 442        if (!(dev->flags&IFF_UP))
 443                return;
 444
 445        /* Add broadcast address, if it is explicitly assigned. */
 446        if (ifa->ifa_broadcast && ifa->ifa_broadcast != 0xFFFFFFFF)
 447                fib_magic(RTM_NEWROUTE, RTN_BROADCAST, ifa->ifa_broadcast, 32, prim);
 448
 449        if (!ZERONET(prefix) && !(ifa->ifa_flags&IFA_F_SECONDARY) &&
 450            (prefix != addr || ifa->ifa_prefixlen < 32)) {
 451                fib_magic(RTM_NEWROUTE, dev->flags&IFF_LOOPBACK ? RTN_LOCAL :
 452                          RTN_UNICAST, prefix, ifa->ifa_prefixlen, prim);
 453
 454                /* Add network specific broadcasts, when it takes a sense */
 455                if (ifa->ifa_prefixlen < 31) {
 456                        fib_magic(RTM_NEWROUTE, RTN_BROADCAST, prefix, 32, prim);
 457                        fib_magic(RTM_NEWROUTE, RTN_BROADCAST, prefix|~mask, 32, prim);
 458                }
 459        }
 460}
 461
 462static void fib_del_ifaddr(struct in_ifaddr *ifa)
 463{
 464        struct in_device *in_dev = ifa->ifa_dev;
 465        struct device *dev = in_dev->dev;
 466        struct in_ifaddr *ifa1;
 467        struct in_ifaddr *prim = ifa;
 468        u32 brd = ifa->ifa_address|~ifa->ifa_mask;
 469        u32 any = ifa->ifa_address&ifa->ifa_mask;
 470#define LOCAL_OK        1
 471#define BRD_OK          2
 472#define BRD0_OK         4
 473#define BRD1_OK         8
 474        unsigned ok = 0;
 475
 476        if (!(ifa->ifa_flags&IFA_F_SECONDARY))
 477                fib_magic(RTM_DELROUTE, dev->flags&IFF_LOOPBACK ? RTN_LOCAL :
 478                          RTN_UNICAST, any, ifa->ifa_prefixlen, prim);
 479        else {
 480                prim = inet_ifa_byprefix(in_dev, any, ifa->ifa_mask);
 481                if (prim == NULL) {
 482                        printk(KERN_DEBUG "fib_del_ifaddr: bug: prim == NULL\n");
 483                        return;
 484                }
 485        }
 486
 487        /* Deletion is more complicated than add.
 488           We should take care of not to delete too much :-)
 489
 490           Scan address list to be sure that addresses are really gone.
 491         */
 492
 493        for (ifa1 = in_dev->ifa_list; ifa1; ifa1 = ifa1->ifa_next) {
 494                if (ifa->ifa_local == ifa1->ifa_local)
 495                        ok |= LOCAL_OK;
 496                if (ifa->ifa_broadcast == ifa1->ifa_broadcast)
 497                        ok |= BRD_OK;
 498                if (brd == ifa1->ifa_broadcast)
 499                        ok |= BRD1_OK;
 500                if (any == ifa1->ifa_broadcast)
 501                        ok |= BRD0_OK;
 502        }
 503
 504        if (!(ok&BRD_OK))
 505                fib_magic(RTM_DELROUTE, RTN_BROADCAST, ifa->ifa_broadcast, 32, prim);
 506        if (!(ok&BRD1_OK))
 507                fib_magic(RTM_DELROUTE, RTN_BROADCAST, brd, 32, prim);
 508        if (!(ok&BRD0_OK))
 509                fib_magic(RTM_DELROUTE, RTN_BROADCAST, any, 32, prim);
 510        if (!(ok&LOCAL_OK)) {
 511                fib_magic(RTM_DELROUTE, RTN_LOCAL, ifa->ifa_local, 32, prim);
 512
 513                /* Check, that this local address finally disappeared. */
 514                if (inet_addr_type(ifa->ifa_local) != RTN_LOCAL) {
 515                        /* And the last, but not the least thing.
 516                           We must flush stray FIB entries.
 517
 518                           First of all, we scan fib_info list searching
 519                           for stray nexthop entries, then ignite fib_flush.
 520                        */
 521                        if (fib_sync_down(ifa->ifa_local, NULL, 0))
 522                                fib_flush();
 523                }
 524        }
 525#undef LOCAL_OK
 526#undef BRD_OK
 527#undef BRD0_OK
 528#undef BRD1_OK
 529}
 530
 531static void fib_disable_ip(struct device *dev, int force)
 532{
 533        if (fib_sync_down(0, dev, force))
 534                fib_flush();
 535        rt_cache_flush(0);
 536        arp_ifdown(dev);
 537}
 538
 539static int fib_inetaddr_event(struct notifier_block *this, unsigned long event, void *ptr)
 540{
 541        struct in_ifaddr *ifa = (struct in_ifaddr*)ptr;
 542
 543        switch (event) {
 544        case NETDEV_UP:
 545                fib_add_ifaddr(ifa);
 546                rt_cache_flush(-1);
 547                break;
 548        case NETDEV_DOWN:
 549                if (ifa->ifa_dev && ifa->ifa_dev->ifa_list == NULL) {
 550                        /* Last address was deleted from this interface.
 551                           Disable IP.
 552                         */
 553                        fib_disable_ip(ifa->ifa_dev->dev, 1);
 554                } else {
 555                        fib_del_ifaddr(ifa);
 556                        rt_cache_flush(-1);
 557                }
 558                break;
 559        }
 560        return NOTIFY_DONE;
 561}
 562
 563static int fib_netdev_event(struct notifier_block *this, unsigned long event, void *ptr)
 564{
 565        struct device *dev = ptr;
 566        struct in_device *in_dev = dev->ip_ptr;
 567
 568        if (!in_dev)
 569                return NOTIFY_DONE;
 570
 571        switch (event) {
 572        case NETDEV_UP:
 573                for_ifa(in_dev) {
 574                        fib_add_ifaddr(ifa);
 575                } endfor_ifa(in_dev);
 576#ifdef CONFIG_IP_ROUTE_MULTIPATH
 577                fib_sync_up(dev);
 578#endif
 579                rt_cache_flush(-1);
 580                break;
 581        case NETDEV_DOWN:
 582                fib_disable_ip(dev, 0);
 583                break;
 584        case NETDEV_UNREGISTER:
 585                fib_disable_ip(dev, 1);
 586                break;
 587        case NETDEV_CHANGEMTU:
 588        case NETDEV_CHANGE:
 589                rt_cache_flush(0);
 590                break;
 591        }
 592        return NOTIFY_DONE;
 593}
 594
 595struct notifier_block fib_inetaddr_notifier = {
 596        fib_inetaddr_event,
 597        NULL,
 598        0
 599};
 600
 601struct notifier_block fib_netdev_notifier = {
 602        fib_netdev_event,
 603        NULL,
 604        0
 605};
 606
 607__initfunc(void ip_fib_init(void))
 608{
 609#ifdef CONFIG_PROC_FS
 610        proc_net_register(&(struct proc_dir_entry) {
 611                PROC_NET_ROUTE, 5, "route",
 612                S_IFREG | S_IRUGO, 1, 0, 0,
 613                0, &proc_net_inode_operations,
 614                fib_get_procinfo
 615        });
 616#endif          /* CONFIG_PROC_FS */
 617
 618#ifndef CONFIG_IP_MULTIPLE_TABLES
 619        local_table = fib_hash_init(RT_TABLE_LOCAL);
 620        main_table = fib_hash_init(RT_TABLE_MAIN);
 621#else
 622        fib_rules_init();
 623#endif
 624
 625        register_netdevice_notifier(&fib_netdev_notifier);
 626        register_inetaddr_notifier(&fib_inetaddr_notifier);
 627}
 628
 629
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.