This commit was manufactured by cvs2svn to create tag
[linux-2.6.git] / net / ipv4 / fib_semantics.c
index a134986..0f4145b 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/errno.h>
 #include <linux/in.h>
 #include <linux/inet.h>
+#include <linux/inetdevice.h>
 #include <linux/netdevice.h>
 #include <linux/if_arp.h>
 #include <linux/proc_fs.h>
 #include <linux/netlink.h>
 #include <linux/init.h>
 
+#include <net/arp.h>
 #include <net/ip.h>
 #include <net/protocol.h>
 #include <net/route.h>
 #include <net/tcp.h>
 #include <net/sock.h>
 #include <net/ip_fib.h>
+#include <net/ip_mp_alg.h>
 
 #include "fib_lookup.h"
 
 #define FSprintk(a...)
 
-static rwlock_t fib_info_lock = RW_LOCK_UNLOCKED;
+static DEFINE_RWLOCK(fib_info_lock);
 static struct hlist_head *fib_info_hash;
 static struct hlist_head *fib_info_laddrhash;
 static unsigned int fib_hash_size;
@@ -59,7 +62,7 @@ static struct hlist_head fib_info_devhash[DEVINDEX_HASHSIZE];
 
 #ifdef CONFIG_IP_ROUTE_MULTIPATH
 
-static spinlock_t fib_multipath_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(fib_multipath_lock);
 
 #define for_nexthops(fi) { int nhsel; const struct fib_nh * nh; \
 for (nhsel=0, nh = (fi)->fib_nh; nhsel < (fi)->fib_nhs; nh++, nhsel++)
@@ -82,7 +85,7 @@ for (nhsel=0; nhsel < 1; nhsel++)
 #define endfor_nexthops(fi) }
 
 
-static struct 
+static const struct 
 {
        int     error;
        u8      scope;
@@ -270,6 +273,74 @@ int ip_fib_check_default(u32 gw, struct net_device *dev)
        return -1;
 }
 
+void rtmsg_fib(int event, u32 key, struct fib_alias *fa,
+              int z, int tb_id,
+              struct nlmsghdr *n, struct netlink_skb_parms *req)
+{
+       struct sk_buff *skb;
+       u32 pid = req ? req->pid : n->nlmsg_pid;
+       int size = NLMSG_SPACE(sizeof(struct rtmsg)+256);
+
+       skb = alloc_skb(size, GFP_KERNEL);
+       if (!skb)
+               return;
+
+       if (fib_dump_info(skb, pid, n->nlmsg_seq, event, tb_id,
+                         fa->fa_type, fa->fa_scope, &key, z,
+                         fa->fa_tos,
+                         fa->fa_info, 0) < 0) {
+               kfree_skb(skb);
+               return;
+       }
+       NETLINK_CB(skb).dst_group = RTNLGRP_IPV4_ROUTE;
+       if (n->nlmsg_flags&NLM_F_ECHO)
+               atomic_inc(&skb->users);
+       netlink_broadcast(rtnl, skb, pid, RTNLGRP_IPV4_ROUTE, GFP_KERNEL);
+       if (n->nlmsg_flags&NLM_F_ECHO)
+               netlink_unicast(rtnl, skb, pid, MSG_DONTWAIT);
+}
+
+/* Return the first fib alias matching TOS with
+ * priority less than or equal to PRIO.
+ */
+struct fib_alias *fib_find_alias(struct list_head *fah, u8 tos, u32 prio)
+{
+       if (fah) {
+               struct fib_alias *fa;
+               list_for_each_entry(fa, fah, fa_list) {
+                       if (fa->fa_tos > tos)
+                               continue;
+                       if (fa->fa_info->fib_priority >= prio ||
+                           fa->fa_tos < tos)
+                               return fa;
+               }
+       }
+       return NULL;
+}
+
+int fib_detect_death(struct fib_info *fi, int order,
+                    struct fib_info **last_resort, int *last_idx, int *dflt)
+{
+       struct neighbour *n;
+       int state = NUD_NONE;
+
+       n = neigh_lookup(&arp_tbl, &fi->fib_nh[0].nh_gw, fi->fib_dev);
+       if (n) {
+               state = n->nud_state;
+               neigh_release(n);
+       }
+       if (state==NUD_REACHABLE)
+               return 0;
+       if ((state&NUD_VALID) && order != *dflt)
+               return 0;
+       if ((state&NUD_VALID) ||
+           (*last_idx<0 && order > *dflt)) {
+               *last_resort = fi;
+               *last_idx = order;
+       }
+       return 1;
+}
+
 #ifdef CONFIG_IP_ROUTE_MULTIPATH
 
 static u32 fib_get_attr32(struct rtattr *attr, int attrlen, int type)
@@ -524,10 +595,13 @@ static void fib_hash_move(struct hlist_head *new_info_hash,
                          struct hlist_head *new_laddrhash,
                          unsigned int new_size)
 {
+       struct hlist_head *old_info_hash, *old_laddrhash;
        unsigned int old_size = fib_hash_size;
-       unsigned int i;
+       unsigned int i, bytes;
 
        write_lock(&fib_info_lock);
+       old_info_hash = fib_info_hash;
+       old_laddrhash = fib_info_laddrhash;
        fib_hash_size = new_size;
 
        for (i = 0; i < old_size; i++) {
@@ -567,6 +641,10 @@ static void fib_hash_move(struct hlist_head *new_info_hash,
        fib_info_laddrhash = new_laddrhash;
 
        write_unlock(&fib_info_lock);
+
+       bytes = old_size * sizeof(struct hlist_head *);
+       fib_hash_free(old_info_hash, bytes);
+       fib_hash_free(old_laddrhash, bytes);
 }
 
 struct fib_info *
@@ -581,6 +659,9 @@ fib_create_info(const struct rtmsg *r, struct kern_rta *rta,
 #else
        const int nhs = 1;
 #endif
+#ifdef CONFIG_IP_ROUTE_MULTIPATH_CACHED
+       u32 mp_alg = IP_MP_ALG_NONE;
+#endif
 
        /* Fast check to catch the most weird cases */
        if (fib_props[r->rtm_type].scope > r->rtm_scope)
@@ -593,6 +674,15 @@ fib_create_info(const struct rtmsg *r, struct kern_rta *rta,
                        goto err_inval;
        }
 #endif
+#ifdef CONFIG_IP_ROUTE_MULTIPATH_CACHED
+       if (rta->rta_mp_alg) {
+               mp_alg = *rta->rta_mp_alg;
+
+               if (mp_alg < IP_MP_ALG_NONE ||
+                   mp_alg > IP_MP_ALG_MAX)
+                       goto err_inval;
+       }
+#endif
 
        err = -ENOBUFS;
        if (fib_info_cnt >= fib_hash_size) {
@@ -684,6 +774,10 @@ fib_create_info(const struct rtmsg *r, struct kern_rta *rta,
 #endif
        }
 
+#ifdef CONFIG_IP_ROUTE_MULTIPATH_CACHED
+       fi->fib_mp_alg = mp_alg;
+#endif
+
        if (fib_props[r->rtm_type].error) {
                if (rta->rta_gw || rta->rta_oif || rta->rta_mp)
                        goto err_inval;
@@ -762,13 +856,15 @@ failure:
        return NULL;
 }
 
+/* Note! fib_semantic_match intentionally uses  RCU list functions. */
 int fib_semantic_match(struct list_head *head, const struct flowi *flp,
-                      struct fib_result *res, int prefixlen)
+                      struct fib_result *res, __u32 zone, __u32 mask, 
+                       int prefixlen)
 {
        struct fib_alias *fa;
        int nh_sel = 0;
 
-       list_for_each_entry(fa, head, fa_list) {
+       list_for_each_entry_rcu(fa, head, fa_list) {
                int err;
 
                if (fa->fa_tos &&
@@ -827,6 +923,11 @@ out_fill_res:
        res->type = fa->fa_type;
        res->scope = fa->fa_scope;
        res->fi = fa->fa_info;
+#ifdef CONFIG_IP_ROUTE_MULTIPATH_CACHED
+       res->netmask = mask;
+       res->network = zone &
+               (0xFFFFFFFF >> (32 - prefixlen));
+#endif
        atomic_inc(&res->fi->fib_clntref);
        return 0;
 }
@@ -841,13 +942,13 @@ u32 __fib_res_prefsrc(struct fib_result *res)
 int
 fib_dump_info(struct sk_buff *skb, u32 pid, u32 seq, int event,
              u8 tb_id, u8 type, u8 scope, void *dst, int dst_len, u8 tos,
-             struct fib_info *fi)
+             struct fib_info *fi, unsigned int flags)
 {
        struct rtmsg *rtm;
        struct nlmsghdr  *nlh;
        unsigned char    *b = skb->tail;
 
-       nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(*rtm));
+       nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*rtm), flags);
        rtm = NLMSG_DATA(nlh);
        rtm->rtm_family = AF_INET;
        rtm->rtm_dst_len = dst_len;
@@ -988,7 +1089,7 @@ fib_convert_rtentry(int cmd, struct nlmsghdr *nl, struct rtmsg *rtm,
                rta->rta_oif = &dev->ifindex;
                if (colon) {
                        struct in_ifaddr *ifa;
-                       struct in_device *in_dev = __in_dev_get(dev);
+                       struct in_device *in_dev = __in_dev_get_rtnl(dev);
                        if (!in_dev)
                                return -ENODEV;
                        *colon = ':';
@@ -1169,7 +1270,7 @@ int fib_sync_up(struct net_device *dev)
                        }
                        if (nh->nh_dev == NULL || !(nh->nh_dev->flags&IFF_UP))
                                continue;
-                       if (nh->nh_dev != dev || __in_dev_get(dev) == NULL)
+                       if (nh->nh_dev != dev || !__in_dev_get_rtnl(dev))
                                continue;
                        alive++;
                        spin_lock_bh(&fib_multipath_lock);