]> git.hungrycats.org Git - linux/commitdiff
[DECNET]: DECnet routing fixes etc.
authorSteven Whitehouse <steve@gw.chygwyn.com>
Fri, 18 Apr 2003 03:43:09 +0000 (20:43 -0700)
committerDavid S. Miller <davem@nuts.ninka.net>
Fri, 18 Apr 2003 03:43:09 +0000 (20:43 -0700)
o As requested, macros in dn_fib.h changed to decnet specific names
o Two bugs fixed (only in 2.5 decnet stack) relating to bind and connection
  states.
o Numerous style changes: using C99 initialisers and inline rather
  than __inline__
o Use struct flowi as routing key (for forthcoming flow cache)
o Add metrics to routing table
o Many routing table bug fixes
o New wait code to improve efficiency
o We use real device MTUs now rather than saying "hmm... looks like ethernet
  must be 1500" as we used to (still one or two places to fix, but its
  mostly correct in this patch)
o Tidy up in af_decnet.c:dn_sendmsg() in preparation for zerocopy
o Updates to rtnetlink code to return more information
o Removed ioctl() for decnet fib. It never did anything and rtnetlink is
  a far better interface anyway.
o Converted /proc/decnet_neigh to seq_file (other /proc files to follow)
o DECnet route cache now uses RCU like the ipv4 route cache
o Misc bug fixes wherever I found them
o SO_BINDTODEVICE works for outgoing connections

20 files changed:
include/net/dn.h
include/net/dn_dev.h
include/net/dn_fib.h
include/net/dn_neigh.h
include/net/dn_nsp.h
include/net/dn_route.h
include/net/flow.h
include/net/sock.h
net/decnet/TODO
net/decnet/af_decnet.c
net/decnet/dn_dev.c
net/decnet/dn_fib.c
net/decnet/dn_neigh.c
net/decnet/dn_nsp_in.c
net/decnet/dn_nsp_out.c
net/decnet/dn_route.c
net/decnet/dn_rules.c
net/decnet/dn_table.c
net/decnet/dn_timer.c
net/decnet/sysctl_net_decnet.c

index 0954190afd4fdbe48497e641928f715a45321e7d..c364cf8cecd7734a7348cc3bb6b33881024a505f 100644 (file)
@@ -171,17 +171,17 @@ struct dn_skb_cb {
        int iif;
 };
 
-static __inline__ dn_address dn_eth2dn(unsigned char *ethaddr)
+static inline dn_address dn_eth2dn(unsigned char *ethaddr)
 {
        return ethaddr[4] | (ethaddr[5] << 8);
 }
 
-static __inline__ dn_address dn_saddr2dn(struct sockaddr_dn *saddr)
+static inline dn_address dn_saddr2dn(struct sockaddr_dn *saddr)
 {
        return *(dn_address *)saddr->sdn_nodeaddr;
 }
 
-static __inline__ void dn_dn2eth(unsigned char *ethaddr, dn_address addr)
+static inline void dn_dn2eth(unsigned char *ethaddr, dn_address addr)
 {
        ethaddr[0] = 0xAA;
        ethaddr[1] = 0x00;
@@ -191,6 +191,19 @@ static __inline__ void dn_dn2eth(unsigned char *ethaddr, dn_address addr)
        ethaddr[5] = (unsigned char)(addr >> 8);
 }
 
+static inline void dn_sk_ports_copy(struct flowi *fl, struct dn_scp *scp)
+{
+       fl->uli_u.dnports.sport = scp->addrloc;
+       fl->uli_u.dnports.dport = scp->addrrem;
+       fl->uli_u.dnports.objnum = scp->addr.sdn_objnum;
+       if (fl->uli_u.dnports.objnum == 0) {
+               fl->uli_u.dnports.objnamel = scp->addr.sdn_objnamel;
+               memcpy(fl->uli_u.dnports.objname, scp->addr.sdn_objname, 16);
+       }
+}
+
+extern unsigned dn_mss_from_pmtu(struct net_device *dev, int mtu);
+
 #define DN_MENUVER_ACC 0x01
 #define DN_MENUVER_USR 0x02
 #define DN_MENUVER_PRX 0x04
index ed539e79588da6c176084322be8b7ef57e176eac..8154b81831b79e45d2bf1cc742f6652d7f0dc1e2 100644 (file)
@@ -8,6 +8,7 @@ struct dn_ifaddr {
        struct dn_ifaddr *ifa_next;
        struct dn_dev    *ifa_dev;
        dn_address       ifa_local;
+       dn_address       ifa_address;
        unsigned char    ifa_flags;
        unsigned char    ifa_scope;
        char             ifa_label[IFNAMSIZ];
@@ -171,7 +172,10 @@ extern int dn_dev_set_default(struct net_device *dev, int force);
 extern struct net_device *dn_dev_get_default(void);
 extern int dn_dev_bind_default(dn_address *addr);
 
-static __inline__ int dn_dev_islocal(struct net_device *dev, dn_address addr)
+extern int register_dnaddr_notifier(struct notifier_block *nb);
+extern int unregister_dnaddr_notifier(struct notifier_block *nb);
+
+static inline int dn_dev_islocal(struct net_device *dev, dn_address addr)
 {
        struct dn_dev *dn_db = dev->dn_ptr;
        struct dn_ifaddr *ifa;
index e3e5d7b9e1187b8e0b04e140fc31799e0d5df774..e4ad06f3e723d9df5cfb2fb41200fc5ba41e4d8e 100644 (file)
@@ -1,12 +1,6 @@
 #ifndef _NET_DN_FIB_H
 #define _NET_DN_FIB_H
 
-#include <linux/config.h>
-
-#ifdef CONFIG_DECNET_ROUTER
-
-#include <linux/rtnetlink.h>
-
 struct dn_kern_rta
 {
         void            *rta_dst;
@@ -23,15 +17,6 @@ struct dn_kern_rta
         struct rta_cacheinfo *rta_ci;
 };
 
-struct dn_fib_key {
-       dn_address src;
-       dn_address dst;
-       int iif;
-       int oif;
-       u32 fwmark;
-       unsigned char scope;
-};
-
 struct dn_fib_res {
        struct dn_fib_rule *r;
        struct dn_fib_info *fi;
@@ -60,16 +45,23 @@ struct dn_fib_info {
        unsigned                fib_flags;
        int                     fib_protocol;
        dn_address              fib_prefsrc;
-       u32                     fib_priority;
+       __u32                   fib_priority;
+       __u32                   fib_metrics[RTAX_MAX];
+#define dn_fib_mtu  fib_metrics[RTAX_MTU-1]
+#define dn_fib_window fib_metrics[RTAX_WINDOW-1]
+#define dn_fib_rtt fib_metrics[RTAX_RTT-1]
+#define dn_fib_advmss fib_metrics[RTAX_ADVMSS-1]
        int                     fib_nhs;
        int                     fib_power;
        struct dn_fib_nh        fib_nh[0];
-#define fib_dev                fib_nh[0].nh_dev
+#define dn_fib_dev             fib_nh[0].nh_dev
 };
 
 
-#define DN_FIB_RES_NH(res)     ((res).fi->fib_nh[(res).nh_sel])
 #define DN_FIB_RES_RESET(res)  ((res).nh_sel = 0)
+#define DN_FIB_RES_NH(res)     ((res).fi->fib_nh[(res).nh_sel])
+
+#define DN_FIB_RES_PREFSRC(res)        ((res).fi->fib_prefsrc ? : __dn_fib_res_prefsrc(&res))
 #define DN_FIB_RES_GW(res)     (DN_FIB_RES_NH(res).nh_gw)
 #define DN_FIB_RES_DEV(res)    (DN_FIB_RES_NH(res).nh_dev)
 #define DN_FIB_RES_OIF(res)    (DN_FIB_RES_NH(res).nh_oif)
@@ -106,7 +98,7 @@ struct dn_fib_table {
        int (*delete)(struct dn_fib_table *t, struct rtmsg *r,
                        struct dn_kern_rta *rta, struct nlmsghdr *n,
                        struct netlink_skb_parms *req);
-       int (*lookup)(struct dn_fib_table *t, const struct dn_fib_key *key, 
+       int (*lookup)(struct dn_fib_table *t, const struct flowi *fl,
                        struct dn_fib_res *res);
        int (*flush)(struct dn_fib_table *t);
 #ifdef CONFIG_PROC_FS
@@ -118,7 +110,7 @@ struct dn_fib_table {
        unsigned char data[0];
 };
 
-
+#ifdef CONFIG_DECNET_ROUTER
 /*
  * dn_fib.c
  */
@@ -132,11 +124,12 @@ extern struct dn_fib_info *dn_fib_create_info(const struct rtmsg *r,
                                struct dn_kern_rta *rta, 
                                const struct nlmsghdr *nlh, int *errp);
 extern int dn_fib_semantic_match(int type, struct dn_fib_info *fi, 
-                       const struct dn_fib_key *key, struct dn_fib_res *res);
+                       const struct flowi *fl,
+                       struct dn_fib_res *res);
 extern void dn_fib_release_info(struct dn_fib_info *fi);
 extern u16 dn_fib_get_attr16(struct rtattr *attr, int attrlen, int type);
 extern void dn_fib_flush(void);
-extern void dn_fib_select_multipath(const struct dn_fib_key *key, 
+extern void dn_fib_select_multipath(const struct flowi *fl,
                                        struct dn_fib_res *res);
 extern int dn_fib_sync_down(dn_address local, struct net_device *dev, 
                                int force);
@@ -156,7 +149,9 @@ extern void dn_fib_table_cleanup(void);
 extern void dn_fib_rules_init(void);
 extern void dn_fib_rules_cleanup(void);
 extern void dn_fib_rule_put(struct dn_fib_rule *);
-extern int dn_fib_lookup(struct dn_fib_key *key, struct dn_fib_res *res);
+extern __u16 dn_fib_rules_policy(__u16 saddr, struct dn_fib_res *res, unsigned *flags);
+extern unsigned dnet_addr_type(__u16 addr);
+extern int dn_fib_lookup(const struct flowi *fl, struct dn_fib_res *res);
 
 /*
  * rtnetlink interface
@@ -169,21 +164,15 @@ extern int dn_fib_rtm_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, void *a
 extern int dn_fib_rtm_newrule(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg);
 extern int dn_fib_dump_rules(struct sk_buff *skb, struct netlink_callback *cb);
 
-#define DN_NUM_TABLES 255
-#define DN_MIN_TABLE 1
-#define DN_DEFAULT_TABLE 1
-#define DN_L1_TABLE 1
-#define DN_L2_TABLE 2
-
 extern void dn_fib_free_info(struct dn_fib_info *fi);
 
-static __inline__ void dn_fib_info_put(struct dn_fib_info *fi)
+static inline void dn_fib_info_put(struct dn_fib_info *fi)
 {
        if (atomic_dec_and_test(&fi->fib_clntref))
                dn_fib_free_info(fi);
 }
 
-static __inline__ void dn_fib_res_put(struct dn_fib_res *res)
+static inline void dn_fib_res_put(struct dn_fib_res *res)
 {
        if (res->fi)
                dn_fib_info_put(res->fi);
@@ -191,13 +180,23 @@ static __inline__ void dn_fib_res_put(struct dn_fib_res *res)
                dn_fib_rule_put(res->r);
 }
 
-static __inline__ u16 dnet_make_mask(int n)
+extern struct dn_fib_table *dn_fib_tables[];
+
+#else /* Endnode */
+
+#define dn_fib_lookup(fl, res) (-ESRCH)
+#define dn_fib_info_put(fi) do { } while(0)
+#define dn_fib_select_multipath(fl, res) do { } while(0)
+#define dn_fib_rules_policy(saddr,res,flags) (0)
+#define dn_fib_res_put(res) do { } while(0)
+
+#endif /* CONFIG_DECNET_ROUTER */
+
+static inline u16 dnet_make_mask(int n)
 {
         if (n)
                 return htons(~((1<<(16-n))-1));
         return 0;
 }
 
-#endif /* CONFIG_DECNET_ROUTER */
-
 #endif /* _NET_DN_FIB_H */
index 4887475e1b032e1c47de9b758c45af31e3fada0c..b0c2802a5216d58c4e078182317c542d948d9ce2 100644 (file)
@@ -18,7 +18,7 @@ struct dn_neigh {
 
 extern void dn_neigh_init(void);
 extern void dn_neigh_cleanup(void);
-extern struct neighbour *dn_neigh_lookup(struct neigh_table *tbl, void *ptr);
+extern struct neighbour *dn_neigh_lookup(struct neigh_table *tbl, const void *ptr);
 extern int dn_neigh_router_hello(struct sk_buff *skb);
 extern int dn_neigh_endnode_hello(struct sk_buff *skb);
 extern void dn_neigh_pointopoint_hello(struct sk_buff *skb);
index ea45833addb85b482795267b4f8f31cd999bac76..3826c0579000f3a2f2fb93c7ce0647aa8e895500 100644 (file)
@@ -204,4 +204,6 @@ static __inline__ int dn_congested(struct sock *sk)
         return atomic_read(&sk->rmem_alloc) > (sk->rcvbuf >> 1);
 }
 
+#define DN_MAX_NSP_DATA_HEADER (11)
+
 #endif /* _NET_DN_NSP_H */
index 3f0cffd43b71de3636e1b82dc291fb05b4ed558a..20be3a87e1b749e7ed2974e19084a60110b1f7ad 100644 (file)
@@ -16,7 +16,7 @@
 *******************************************************************************/
 
 extern struct sk_buff *dn_alloc_skb(struct sock *sk, int size, int pri);
-extern int dn_route_output(struct dst_entry **pprt, dn_address dst, dn_address src, int flags);
+extern int dn_route_output_sock(struct dst_entry **pprt, struct flowi *, struct sock *sk, int flags);
 extern int dn_cache_dump(struct sk_buff *skb, struct netlink_callback *cb);
 extern int dn_cache_getroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg);
 extern void dn_rt_cache_flush(int delay);
@@ -59,10 +59,10 @@ extern void dn_rt_cache_flush(int delay);
 #define DN_RT_INFO_BLKR 0x40 /* Blocking Requested            */
 
 /*
- * The key structure is what we used to look up the route.
+ * The fl structure is what we used to look up the route.
  * The rt_saddr & rt_daddr entries are the same as key.saddr & key.daddr
- * except for local input routes, where the rt_saddr = key.daddr and
- * rt_daddr = key.saddr to allow the route to be used for returning
+ * except for local input routes, where the rt_saddr = fl.fld_dst and
+ * rt_daddr = fl.fld_src to allow the route to be used for returning
  * packets to the originating host.
  */
 struct dn_route {
@@ -70,19 +70,18 @@ struct dn_route {
                struct dst_entry dst;
                struct dn_route *rt_next;
        } u;
-       struct {
-               unsigned short saddr;
-               unsigned short daddr;
-               int iif;
-               int oif;
-               u32 fwmark;
-       } key;
-       unsigned short rt_saddr;
-       unsigned short rt_daddr;
-       unsigned char rt_type;
-       unsigned char rt_scope;
-       unsigned char rt_protocol;
-       unsigned char rt_table;
+
+       __u16 rt_saddr;
+       __u16 rt_daddr;
+       __u16 rt_gateway;
+       __u16 __padding;
+       __u16 rt_src_map;
+       __u16 rt_dst_map;
+
+       unsigned rt_flags;
+       unsigned rt_type;
+
+       struct flowi fl;
 };
 
 extern void dn_route_init(void);
@@ -110,32 +109,4 @@ static inline void dn_rt_finish_output(struct sk_buff *skb, char *dst, char *src
                kfree_skb(skb);
 }
 
-static inline void dn_nsp_send(struct sk_buff *skb)
-{
-       struct sock *sk = skb->sk;
-       struct dn_scp *scp = DN_SK(sk);
-       struct dst_entry *dst;
-
-       skb->h.raw = skb->data;
-       scp->stamp = jiffies;
-
-       if ((dst = sk->dst_cache) && !dst->obsolete) {
-try_again:
-               skb->dst = dst_clone(dst);
-               dst_output(skb);
-               return;
-       }
-
-       dst_release(xchg(&sk->dst_cache, NULL));
-
-       if (dn_route_output(&sk->dst_cache, dn_saddr2dn(&scp->peer), dn_saddr2dn(&scp->addr), 0) == 0) {
-               dst = sk->dst_cache;
-               goto try_again;
-       }
-
-       sk->err = EHOSTUNREACH;
-       if (!test_bit(SOCK_DEAD, &sk->flags))
-               sk->state_change(sk);
-}
-
 #endif /* _NET_DN_ROUTE_H */
index 5f5d8f323646a795c9889d548c1e796423e50b1e..c764d9d159d9b18f68b7985b4b3cc34798856501 100644 (file)
@@ -25,7 +25,18 @@ struct flowi {
                        struct in6_addr *       saddr;
                        __u32                   flowlabel;
                } ip6_u;
+
+               struct {
+                       __u16                   daddr;
+                       __u16                   saddr;
+                       __u32                   fwmark;
+                       __u8                    scope;
+               } dn_u;
        } nl_u;
+#define fld_dst                nl_u.dn_u.daddr
+#define fld_src                nl_u.dn_u.saddr
+#define fld_fwmark     nl_u.dn_u.fwmark
+#define fld_scope      nl_u.dn_u.scope
 #define fl6_dst                nl_u.ip6_u.daddr
 #define fl6_src                nl_u.ip6_u.saddr
 #define fl6_flowlabel  nl_u.ip6_u.flowlabel
@@ -48,6 +59,14 @@ struct flowi {
                        __u8    code;
                } icmpt;
 
+               struct {
+                       __u16   sport;
+                       __u16   dport;
+                       __u8    objnum;
+                       __u8    objnamel; /* Not 16 bits since max val is 16 */
+                       __u8    objname[16]; /* Not zero terminated */
+               } dnports;
+
                __u32           spi;
        } uli_u;
 #define fl_ip_sport    uli_u.ports.sport
index 01367c578bad3afa8b8a799e9639cf0adbc17291..4f5b79f3088032bf7894e39173439f7464c40378 100644 (file)
@@ -859,6 +859,9 @@ sock_recv_timestamp(struct msghdr *msg, struct sock *sk, struct sk_buff *skb)
  *     schedule();
  * SOCK_SLEEP_POST(sk)
  *
+ * N.B. These are now obsolete and were, afaik, only ever used in DECnet
+ * and when the last use of them in DECnet has gone, I'm intending to
+ * remove them.
  */
 
 #define SOCK_SLEEP_PRE(sk)     { struct task_struct *tsk = current; \
index 8c5484ff5be1a141dbf43a2b9ad58b22b4e9685a..243fe7e979591d23a2b46169f3812b7ec8e8292d 100644 (file)
@@ -26,25 +26,16 @@ Steve's quick list of things that need finishing off:
  o Start to hack together user level software and add more DECnet support
    in ifconfig for example. 
 
- o Test adding/deleting of routes
-
- o Test route lookup
-
- o Test /proc/net/decnet_route route listing works correctly (maybe I'll
-   change the format of this file... atm its very similar to the IPv4 route
-   file)
-
  o Find all the commonality between DECnet and IPv4 routing code and extract 
    it into a small library of routines. [probably a project for 2.7.xx]
 
- o Test ip_gre tunneling works... it did the last time I tested it and it
-   will have to if I'm to test routing properly.
-
  o Add the routing message grabbing netfilter module [written, tested,
    awaiting merge]
 
- o Add perfect socket hashing - an idea suggested by Paul Koning [part written,
-   awaiting debugging and merge]
+ o Add perfect socket hashing - an idea suggested by Paul Koning. Currently
+   we have a half-way house scheme which seems to work reasonably well, but
+   the full scheme is still worth implementing, its not not top of my list
+   right now.
 
  o Add session control message flow control
 
@@ -54,3 +45,5 @@ Steve's quick list of things that need finishing off:
 
  o AIO for DECnet
 
+ o Eliminate dn_db->parms.blksize
+
index 1cb4608160261081955f864417ceff7a9fa1414a..204b68601c73fc41806b10a38b9eb73e2f9a165f 100644 (file)
@@ -118,6 +118,7 @@ Version 0.0.6    2.1.110   07-aug-98   Eduardo Marcelo Serrat
 #include <linux/netfilter.h>
 #include <net/sock.h>
 #include <net/tcp.h>
+#include <net/flow.h>
 #include <asm/system.h>
 #include <asm/ioctls.h>
 #include <linux/mm.h>
@@ -146,6 +147,7 @@ static void dn_keepalive(struct sock *sk);
 #define DN_SK_HASH_SIZE (1 << DN_SK_HASH_SHIFT)
 #define DN_SK_HASH_MASK (DN_SK_HASH_SIZE - 1)
 
+
 static kmem_cache_t *dn_sk_cachep;
 static struct proto_ops dn_proto_ops;
 static rwlock_t dn_hash_lock = RW_LOCK_UNLOCKED;
@@ -273,7 +275,7 @@ struct sock **listen_hash(struct sockaddr_dn *addr)
 
        if (hash == 0) {
                hash = addr->sdn_objnamel;
-               for(i = 0; i < addr->sdn_objnamel; i++) {
+               for(i = 0; i < dn_ntohs(addr->sdn_objnamel); i++) {
                        hash ^= addr->sdn_objname[i];
                        hash ^= (hash << 3);
                }
@@ -512,8 +514,7 @@ struct sock *dn_alloc_sock(struct socket *sock, int gfp)
        scp->services_loc = 1 | NSP_FC_NONE;
        scp->info_rem = 0;
        scp->info_loc = 0x03; /* NSP version 4.1 */
-       scp->segsize_rem = 230; /* Default: Updated by remote segsize */
-       scp->segsize_loc = 1450; /* Best guess for ethernet */
+       scp->segsize_rem = 230 - DN_MAX_NSP_DATA_HEADER; /* Default: Updated by remote segsize */
        scp->nonagle = 0;
        scp->multi_ireq = 1;
        scp->accept_mode = ACC_IMMED;
@@ -788,8 +789,8 @@ static int dn_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
                return -EINVAL;
 
 #if 1
-       if ((!capable(CAP_NET_BIND_SERVICE) && saddr->sdn_objnum) ||
-           (saddr->sdn_flags & SDF_WILD))
+       if (!capable(CAP_NET_BIND_SERVICE) && (saddr->sdn_objnum ||
+           (saddr->sdn_flags & SDF_WILD)))
                return -EACCES;
 #else
        /*
@@ -877,18 +878,18 @@ static int dn_auto_bind(struct socket *sock)
 static int dn_confirm_accept(struct sock *sk, long *timeo, int allocation)
 {
        struct dn_scp *scp = DN_SK(sk);
-       DECLARE_WAITQUEUE(wait, current);
+       DEFINE_WAIT(wait);
        int err;
 
        if (scp->state != DN_CR)
                return -EINVAL;
 
        scp->state = DN_CC;
+       scp->segsize_loc = dst_path_metric(__sk_dst_get(sk), RTAX_ADVMSS);
        dn_send_conn_conf(sk, allocation);
 
-       add_wait_queue(sk->sleep, &wait);
+       prepare_to_wait(sk->sleep, &wait, TASK_INTERRUPTIBLE);
        for(;;) {
-               set_current_state(TASK_INTERRUPTIBLE);
                release_sock(sk);
                if (scp->state == DN_CC)
                        *timeo = schedule_timeout(*timeo);
@@ -905,16 +906,21 @@ static int dn_confirm_accept(struct sock *sk, long *timeo, int allocation)
                err = -EAGAIN;
                if (!*timeo)
                        break;
+               prepare_to_wait(sk->sleep, &wait, TASK_INTERRUPTIBLE);
+       }
+       finish_wait(sk->sleep, &wait);
+       if (err == 0) {
+               sk->socket->state = SS_CONNECTED;
+       } else if (scp->state != DN_CC) {
+               sk->socket->state = SS_UNCONNECTED;
        }
-       remove_wait_queue(sk->sleep, &wait);
-       current->state = TASK_RUNNING;
        return err;
 }
 
 static int dn_wait_run(struct sock *sk, long *timeo)
 {
        struct dn_scp *scp = DN_SK(sk);
-       DECLARE_WAITQUEUE(wait, current);
+       DEFINE_WAIT(wait);
        int err = 0;
 
        if (scp->state == DN_RUN)
@@ -923,9 +929,8 @@ static int dn_wait_run(struct sock *sk, long *timeo)
        if (!*timeo)
                return -EALREADY;
 
-       add_wait_queue(sk->sleep, &wait);
+       prepare_to_wait(sk->sleep, &wait, TASK_INTERRUPTIBLE);
        for(;;) {
-               set_current_state(TASK_INTERRUPTIBLE);
                release_sock(sk);
                if (scp->state == DN_CI || scp->state == DN_CC)
                        *timeo = schedule_timeout(*timeo);
@@ -942,12 +947,14 @@ static int dn_wait_run(struct sock *sk, long *timeo)
                err = -ETIMEDOUT;
                if (!*timeo)
                        break;
+               prepare_to_wait(sk->sleep, &wait, TASK_INTERRUPTIBLE);
        }
-       remove_wait_queue(sk->sleep, &wait);
-       current->state = TASK_RUNNING;
+       finish_wait(sk->sleep, &wait);
 out:
        if (err == 0) {
                sk->socket->state = SS_CONNECTED;
+       } else if (scp->state != DN_CI && scp->state != DN_CC) {
+               sk->socket->state = SS_UNCONNECTED;
        }
        return err;
 }
@@ -957,6 +964,7 @@ static int __dn_connect(struct sock *sk, struct sockaddr_dn *addr, int addrlen,
        struct socket *sock = sk->socket;
        struct dn_scp *scp = DN_SK(sk);
        int err = -EISCONN;
+       struct flowi fl;
 
        if (sock->state == SS_CONNECTED)
                goto out;
@@ -995,12 +1003,17 @@ static int __dn_connect(struct sock *sk, struct sockaddr_dn *addr, int addrlen,
        memcpy(&scp->peer, addr, sizeof(struct sockaddr_dn));
 
        err = -EHOSTUNREACH;
-       if (dn_route_output(&sk->dst_cache, dn_saddr2dn(&scp->peer),
-                           dn_saddr2dn(&scp->addr), flags & MSG_TRYHARD) < 0)
+       memset(&fl, 0, sizeof(fl));
+       fl.oif = sk->bound_dev_if;
+       fl.fld_dst = dn_saddr2dn(&scp->peer);
+       fl.fld_src = dn_saddr2dn(&scp->addr);
+       dn_sk_ports_copy(&fl, scp);
+       if (dn_route_output_sock(&sk->dst_cache, &fl, sk, flags) < 0)
                goto out;
-
+       sk->route_caps = sk->dst_cache->dev->features;
        sock->state = SS_CONNECTING;
        scp->state = DN_CI;
+       scp->segsize_loc = dst_path_metric(sk->dst_cache, RTAX_ADVMSS);
 
        dn_nsp_send_conninit(sk, NSP_CI);
        err = -EINPROGRESS;
@@ -1077,13 +1090,12 @@ static void dn_user_copy(struct sk_buff *skb, struct optdata_dn *opt)
 
 static struct sk_buff *dn_wait_for_connect(struct sock *sk, long *timeo)
 {
-       DECLARE_WAITQUEUE(wait, current);
+       DEFINE_WAIT(wait);
        struct sk_buff *skb = NULL;
        int err = 0;
 
-       add_wait_queue_exclusive(sk->sleep, &wait);
+       prepare_to_wait(sk->sleep, &wait, TASK_INTERRUPTIBLE);
        for(;;) {
-               set_current_state(TASK_INTERRUPTIBLE);
                release_sock(sk);
                skb = skb_dequeue(&sk->receive_queue);
                if (skb == NULL) {
@@ -1102,9 +1114,10 @@ static struct sk_buff *dn_wait_for_connect(struct sock *sk, long *timeo)
                err = -EAGAIN;
                if (!*timeo)
                        break;
+               prepare_to_wait(sk->sleep, &wait, TASK_INTERRUPTIBLE);
        }
-       remove_wait_queue(sk->sleep, &wait);
-       current->state = TASK_RUNNING;
+       finish_wait(sk->sleep, &wait);
+
        return skb == NULL ? ERR_PTR(err) : skb;
 }
 
@@ -1276,12 +1289,6 @@ static int dn_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
                release_sock(sk);
                return val;
 
-#ifdef CONFIG_DECNET_ROUTER
-       case SIOCADDRT:
-       case SIOCDELRT:
-               return dn_fib_ioctl(sock, cmd, arg);
-#endif /* CONFIG_DECNET_ROUTER */
-
        case TIOCOUTQ:
                amount = sk->sndbuf - atomic_read(&sk->wmem_alloc);
                if (amount < 0)
@@ -1888,6 +1895,64 @@ static inline int dn_queue_too_long(struct dn_scp *scp, struct sk_buff_head *que
        return 0;
 }
 
+/*
+ * The DECnet spec requires the the "routing layer" accepts packets which
+ * are at least 230 bytes in size. This excludes any headers which the NSP
+ * layer might add, so we always assume that we'll be using the maximal
+ * length header on data packets. The variation in length is due to the
+ * inclusion (or not) of the two 16 bit acknowledgement fields so it doesn't
+ * make much practical difference.
+ */
+unsigned dn_mss_from_pmtu(struct net_device *dev, int mtu)
+{
+       unsigned mss = 230 - DN_MAX_NSP_DATA_HEADER;
+       if (dev) {
+               struct dn_dev *dn_db = dev->dn_ptr;
+               mtu -= LL_RESERVED_SPACE(dev);
+               if (dn_db->use_long)
+                       mtu -= 21;
+               else
+                       mtu -= 6;
+               mtu -= DN_MAX_NSP_DATA_HEADER;
+       } else {
+               /*
+                * 21 = long header, 16 = guess at MAC header length
+                */
+               mtu -= (21 + DN_MAX_NSP_DATA_HEADER + 16);
+       }
+       if (mtu > mss)
+               mss = mtu;
+       return mss;
+}
+
+static inline unsigned int dn_current_mss(struct sock *sk, int flags)
+{
+       struct dst_entry *dst = __sk_dst_get(sk);
+       struct dn_scp *scp = DN_SK(sk);
+       int mss_now = min_t(int, scp->segsize_loc, scp->segsize_rem);
+
+       /* Other data messages are limited to 16 bytes per packet */
+       if (flags & MSG_OOB)
+               return 16;
+
+       /* This works out the maximum size of segment we can send out */
+       if (dst) {
+               u32 mtu = dst_pmtu(dst);
+               mss_now = min_t(int, dn_mss_from_pmtu(dst->dev, mtu), mss_now);
+       }
+
+       return mss_now;
+}
+
+static int dn_error(struct sock *sk, int flags, int err)
+{
+       if (err == -EPIPE)
+               err = sock_error(sk) ? : -EPIPE;
+       if (err == -EPIPE && !(flags & MSG_NOSIGNAL))
+               send_sig(SIGPIPE, current, 0);
+       return err;
+}
+
 static int dn_sendmsg(struct kiocb *iocb, struct socket *sock,
           struct msghdr *msg, int size)
 {
@@ -1902,9 +1967,6 @@ static int dn_sendmsg(struct kiocb *iocb, struct socket *sock,
        struct sockaddr_dn *addr = (struct sockaddr_dn *)msg->msg_name;
        struct sk_buff *skb = NULL;
        struct dn_skb_cb *cb;
-       unsigned char msgflg;
-       unsigned char *ptr;
-       unsigned short ack;
        int len;
        unsigned char fctype;
        long timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT);
@@ -1915,17 +1977,26 @@ static int dn_sendmsg(struct kiocb *iocb, struct socket *sock,
        if (addr_len && (addr_len != sizeof(struct sockaddr_dn)))
                return -EINVAL;
 
+       /*
+        * The only difference between stream sockets and sequenced packet
+        * sockets is that the stream sockets always behave as if MSG_EOR
+        * has been set.
+        */
+       if (sock->type == SOCK_STREAM) {
+               if (flags & MSG_EOR)
+                       return -EINVAL;
+               flags |= MSG_EOR;
+       }
+
        lock_sock(sk);
 
        err = dn_check_state(sk, addr, addr_len, &timeo, flags);
        if (err)
-               goto out;
+               goto out_err;
 
        if (sk->shutdown & SEND_SHUTDOWN) {
-               if (!(flags & MSG_NOSIGNAL))
-                       send_sig(SIGPIPE, current, 0);
                err = -EPIPE;
-               goto out;
+               goto out_err;
        }
 
        if ((flags & MSG_TRYHARD) && sk->dst_cache)
@@ -1934,20 +2005,9 @@ static int dn_sendmsg(struct kiocb *iocb, struct socket *sock,
        mss = scp->segsize_rem;
        fctype = scp->services_rem & NSP_FC_MASK;
 
-       if (sk->dst_cache && sk->dst_cache->neighbour) {
-               struct dn_neigh *dn = (struct dn_neigh *)sk->dst_cache->neighbour;
-               if (dn->blksize < (mss + 11))
-                       mss = dn->blksize - 11;
-       }
-
-       /*
-        * The only difference between SEQPACKET & STREAM sockets under DECnet
-        * is that SEQPACKET sockets set the MSG_EOR flag for the last
-        * session control message segment. 
-        */
+       mss = dn_current_mss(sk, flags);
 
        if (flags & MSG_OOB) {
-               mss = 16;
                queue = &scp->other_xmit_queue;
                if (size > mss) {
                        err = -EMSGSIZE;
@@ -2008,7 +2068,7 @@ static int dn_sendmsg(struct kiocb *iocb, struct socket *sock,
 
                cb = DN_SKB_CB(skb);
 
-               ptr = skb_put(skb, 9);
+               skb_reserve(skb, DN_MAX_NSP_DATA_HEADER);
 
                if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) {
                        err = -EFAULT;
@@ -2016,45 +2076,26 @@ static int dn_sendmsg(struct kiocb *iocb, struct socket *sock,
                }
 
                if (flags & MSG_OOB) {
-                       cb->segnum = scp->numoth;
-                       seq_add(&scp->numoth, 1);
-                       msgflg = 0x30;
-                       ack = (scp->numoth_rcv & 0x0FFF) | 0x8000;
-                       scp->ackxmt_oth = scp->numoth_rcv;
+                       cb->nsp_flags = 0x30;
                        if (fctype != NSP_FC_NONE)
                                scp->flowrem_oth--;
                } else {
-                       cb->segnum = scp->numdat;
-                       seq_add(&scp->numdat, 1);
-                       msgflg = 0x00;
-                       if (sock->type == SOCK_STREAM)
-                               msgflg = 0x60;
+                       cb->nsp_flags = 0x00;
                        if (scp->seg_total == 0)
-                               msgflg |= 0x20;
+                               cb->nsp_flags |= 0x20;
 
                        scp->seg_total += len;
                
                        if (((sent + len) == size) && (flags & MSG_EOR)) {
-                               msgflg |= 0x40;
+                               cb->nsp_flags |= 0x40;
                                scp->seg_total = 0;
                                if (fctype == NSP_FC_SCMC)
                                        scp->flowrem_dat--;
                        }
-                       ack = (scp->numdat_rcv & 0x0FFF) | 0x8000;
-                       scp->ackxmt_dat = scp->numdat_rcv;
                        if (fctype == NSP_FC_SRC)
                                scp->flowrem_dat--;
                }
 
-               *ptr++ = msgflg;
-               *(__u16 *)ptr = scp->addrrem;
-               ptr += 2;
-               *(__u16 *)ptr = scp->addrloc;
-               ptr += 2;
-               *(__u16 *)ptr = dn_htons(ack);
-               ptr += 2;
-               *(__u16 *)ptr = dn_htons(cb->segnum);
-
                sent += len;
                dn_nsp_queue_xmit(sk, skb, sk->allocation, flags & MSG_OOB);
                skb = NULL;
@@ -2070,6 +2111,11 @@ out:
        release_sock(sk);
 
        return sent ? sent : err;
+
+out_err:
+       err = dn_error(sk, flags, err);
+       release_sock(sk);
+       return err;
 }
 
 static int dn_device_event(struct notifier_block *this, unsigned long event,
@@ -2215,10 +2261,8 @@ static struct proto_ops dn_proto_ops = {
        .sendpage =     sock_no_sendpage,
 };
 
-#ifdef CONFIG_SYSCTL
 void dn_register_sysctl(void);
 void dn_unregister_sysctl(void);
-#endif
 
 
 MODULE_DESCRIPTION("The Linux DECnet Network Protocol");
@@ -2226,7 +2270,7 @@ MODULE_AUTHOR("Linux DECnet Project Team");
 MODULE_LICENSE("GPL");
 
 
-static char banner[] __initdata = KERN_INFO "NET4: DECnet for Linux: V.2.5.40s (C) 1995-2002 Linux DECnet Project Team\n";
+static char banner[] __initdata = KERN_INFO "NET4: DECnet for Linux: V.2.5.67s (C) 1995-2003 Linux DECnet Project Team\n";
 
 static int __init decnet_init(void)
 {
@@ -2253,9 +2297,7 @@ static int __init decnet_init(void)
        dn_fib_init();
 #endif /* CONFIG_DECNET_ROUTER */
 
-#ifdef CONFIG_SYSCTL
        dn_register_sysctl();
-#endif /* CONFIG_SYSCTL */
 
        /*
         * Prevent DECnet module unloading until its fixed properly.
@@ -2273,9 +2315,7 @@ static void __exit decnet_exit(void)
        sock_unregister(AF_DECnet);
        dev_remove_pack(&dn_dix_packet_type);
 
-#ifdef CONFIG_SYSCTL
        dn_unregister_sysctl();
-#endif /* CONFIG_SYSCTL */
 
        unregister_netdevice_notifier(&dn_dev_notifier);
 
index 776eb0afd9daff5d79e50617878724c8d8c12d43..77ecb50fa1c03263e92cc702c49832e71c009edc 100644 (file)
 #include <linux/skbuff.h>
 #include <linux/rtnetlink.h>
 #include <linux/sysctl.h>
+#include <linux/notifier.h>
 #include <asm/uaccess.h>
 #include <net/neighbour.h>
 #include <net/dst.h>
+#include <net/flow.h>
 #include <net/dn.h>
 #include <net/dn_dev.h>
 #include <net/dn_route.h>
@@ -61,6 +63,7 @@ dn_address decnet_address = 0;
 
 static rwlock_t dndev_lock = RW_LOCK_UNLOCKED;
 static struct net_device *decnet_default_device;
+static struct notifier_block *dnaddr_chain;
 
 static struct dn_dev *dn_dev_create(struct net_device *dev, int *err);
 static void dn_dev_delete(struct net_device *dev);
@@ -478,7 +481,7 @@ static void dn_dev_del_ifa(struct dn_dev *dn_db, struct dn_ifaddr **ifap, int de
        }
 
        rtmsg_ifa(RTM_DELADDR, ifa1);
-
+       notifier_call_chain(&dnaddr_chain, NETDEV_DOWN, ifa1);
        if (destroy) {
                dn_dev_free_ifa(ifa1);
 
@@ -513,6 +516,7 @@ static int dn_dev_insert_ifa(struct dn_dev *dn_db, struct dn_ifaddr *ifa)
        dn_db->ifa_list = ifa;
 
        rtmsg_ifa(RTM_NEWADDR, ifa);
+       notifier_call_chain(&dnaddr_chain, NETDEV_UP, ifa);
 
        return 0;
 }
@@ -609,7 +613,7 @@ int dn_dev_ioctl(unsigned int cmd, void *arg)
                                dn_dev_del_ifa(dn_db, ifap, 0);
                        }
 
-                       ifa->ifa_local = dn_saddr2dn(sdn);
+                       ifa->ifa_local = ifa->ifa_address = dn_saddr2dn(sdn);
 
                        ret = dn_dev_set_ifa(dev, ifa);
        }
@@ -686,7 +690,10 @@ static int dn_dev_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *a
        if ((ifa = dn_dev_alloc_ifa()) == NULL)
                return -ENOBUFS;
 
+       if (!rta[IFA_ADDRESS - 1])
+               rta[IFA_ADDRESS - 1] = rta[IFA_LOCAL - 1];
        memcpy(&ifa->ifa_local, RTA_DATA(rta[IFA_LOCAL-1]), 2);
+       memcpy(&ifa->ifa_address, RTA_DATA(rta[IFA_ADDRESS-1]), 2);
        ifa->ifa_flags = ifm->ifa_flags;
        ifa->ifa_scope = ifm->ifa_scope;
        ifa->ifa_dev = dn_db;
@@ -716,7 +723,10 @@ static int dn_dev_fill_ifaddr(struct sk_buff *skb, struct dn_ifaddr *ifa,
        ifm->ifa_flags = ifa->ifa_flags | IFA_F_PERMANENT;
        ifm->ifa_scope = ifa->ifa_scope;
        ifm->ifa_index = ifa->ifa_dev->dev->ifindex;
-       RTA_PUT(skb, IFA_LOCAL, 2, &ifa->ifa_local);
+       if (ifa->ifa_address)
+               RTA_PUT(skb, IFA_ADDRESS, 2, &ifa->ifa_address);
+       if (ifa->ifa_local)
+               RTA_PUT(skb, IFA_LOCAL, 2, &ifa->ifa_local);
        if (ifa->ifa_label[0])
                RTA_PUT(skb, IFA_LABEL, IFNAMSIZ, &ifa->ifa_label);
        nlh->nlmsg_len = skb->tail - b;
@@ -758,10 +768,7 @@ static int dn_dev_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
        s_idx = cb->args[0];
        s_dn_idx = dn_idx = cb->args[1];
        read_lock(&dev_base_lock);
-       for(dev = dev_base, idx = 0; dev; dev = dev->next) {
-               if ((dn_db = dev->dn_ptr) == NULL)
-                       continue;
-               idx++;
+       for(dev = dev_base, idx = 0; dev; dev = dev->next, idx++) {
                if (idx < s_idx)
                        continue;
                if (idx > s_idx)
@@ -773,7 +780,10 @@ static int dn_dev_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
                        if (dn_idx < s_dn_idx)
                                continue;
 
-                       if (dn_dev_fill_ifaddr(skb, ifa, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, RTM_NEWADDR) <= 0)
+                       if (dn_dev_fill_ifaddr(skb, ifa,
+                                              NETLINK_CB(cb->skb).pid,
+                                              cb->nlh->nlmsg_seq,
+                                              RTM_NEWADDR) <= 0)
                                goto done;
                }
        }
@@ -872,8 +882,6 @@ static void dn_send_endnode_hello(struct net_device *dev, struct dn_ifaddr *ifa)
 }
 
 
-#ifdef CONFIG_DECNET_ROUTER
-
 #define DRDELAY (5 * HZ)
 
 static int dn_am_i_a_router(struct dn_neigh *dn, struct dn_dev *dn_db, struct dn_ifaddr *ifa)
@@ -981,12 +989,6 @@ static void dn_send_brd_hello(struct net_device *dev, struct dn_ifaddr *ifa)
        else
                dn_send_router_hello(dev, ifa);
 }
-#else
-static void dn_send_brd_hello(struct net_device *dev, struct dn_ifaddr *ifa)
-{
-       dn_send_endnode_hello(dev, ifa);
-}
-#endif
 
 #if 0
 static void dn_send_ptp_hello(struct net_device *dev, struct dn_ifaddr *ifa)
@@ -1175,7 +1177,7 @@ void dn_dev_up(struct net_device *dev)
        if ((ifa = dn_dev_alloc_ifa()) == NULL)
                return;
 
-       ifa->ifa_local = addr;
+       ifa->ifa_local = ifa->ifa_address = addr;
        ifa->ifa_flags = 0;
        ifa->ifa_scope = RT_SCOPE_UNIVERSE;
        strcpy(ifa->ifa_label, dev->name);
@@ -1274,6 +1276,15 @@ void dn_dev_devices_on(void)
        rtnl_unlock();
 }
 
+int register_dnaddr_notifier(struct notifier_block *nb)
+{
+       return notifier_chain_register(&dnaddr_chain, nb);
+}
+
+int unregister_dnaddr_notifier(struct notifier_block *nb)
+{
+       return notifier_chain_unregister(&dnaddr_chain, nb);
+}
 
 #ifdef CONFIG_DECNET_SIOCGIFCONF
 /*
@@ -1390,43 +1401,21 @@ static int decnet_dev_get_info(char *buffer, char **start, off_t offset, int len
 
 static struct rtnetlink_link dnet_rtnetlink_table[RTM_MAX-RTM_BASE+1] = 
 {
-       { NULL,                 NULL,                   },
-       { NULL,                 NULL,                   },
-       { NULL,                 NULL,                   },
-       { NULL,                 NULL,                   },
-
-       { dn_dev_rtm_newaddr,   NULL,                   },
-       { dn_dev_rtm_deladdr,   NULL,                   },
-       { NULL,                 dn_dev_dump_ifaddr,     },
-       { NULL,                 NULL,                   },
+        [4] = { .doit   = dn_dev_rtm_newaddr,  },
+        [5] = { .doit   = dn_dev_rtm_deladdr,  },
+        [6] = { .dumpit = dn_dev_dump_ifaddr,  },
 
 #ifdef CONFIG_DECNET_ROUTER
-       { dn_fib_rtm_newroute,  NULL,                   },
-       { dn_fib_rtm_delroute,  NULL,                   },
-       { dn_cache_getroute,    dn_fib_dump,            },
-       { NULL,                 NULL,                   },
+        [8] = { .doit   = dn_fib_rtm_newroute, },
+        [9] = { .doit   = dn_fib_rtm_delroute, },
+       [10] = { .doit   = dn_cache_getroute, .dumpit = dn_fib_dump, },
+       [16] = { .doit   = dn_fib_rtm_newrule, },
+       [17] = { .doit   = dn_fib_rtm_delrule, },
+       [18] = { .dumpit = dn_fib_dump_rules,  },
 #else
-       { NULL,                 NULL,                   },
-       { NULL,                 NULL,                   },
-       { dn_cache_getroute,    dn_cache_dump,          },
-       { NULL,                 NULL,                   },
+       [10] = { .doit   = dn_cache_getroute, .dumpit = dn_cache_dump, },
 #endif
-       { NULL,                 NULL,                   },
-       { NULL,                 NULL,                   },
-       { NULL,                 NULL,                   },
-       { NULL,                 NULL,                   },
 
-#ifdef CONFIG_DECNET_ROUTER
-       { dn_fib_rtm_newrule,   NULL,                   },
-       { dn_fib_rtm_delrule,   NULL,                   },
-       { NULL,                 dn_fib_dump_rules,      },
-       { NULL,                 NULL,                   }
-#else
-       { NULL,                 NULL,                   },
-       { NULL,                 NULL,                   },
-       { NULL,                 NULL,                   },
-       { NULL,                 NULL,                   }
-#endif
 };
 
 #ifdef MODULE
index 59a1c9ffcba52b7646d12d41815a582712eac780..3d79f1dc3d1b972809d5d3a31de187b9a9393df8 100644 (file)
@@ -12,6 +12,9 @@
  *              Alexey Kuznetsov : SMP locking changes
  *              Steve Whitehouse : Rewrote it... Well to be more correct, I
  *                                 copied most of it from the ipv4 fib code.
+ *              Steve Whitehouse : Updated it in style and fixed a few bugs
+ *                                 which were fixed in the ipv4 code since
+ *                                 this code was copied from it.
  *
  */
 #include <linux/config.h>
 #include <asm/uaccess.h>
 #include <net/neighbour.h>
 #include <net/dst.h>
+#include <net/flow.h>
 #include <net/dn.h>
 #include <net/dn_route.h>
 #include <net/dn_fib.h>
 #include <net/dn_neigh.h>
 #include <net/dn_dev.h>
 
+#define RT_MIN_TABLE 1
 
 #define for_fib_info() { struct dn_fib_info *fi;\
        for(fi = dn_fib_info_list; fi; fi = fi->fib_next)
@@ -52,7 +57,7 @@
 
 extern int dn_cache_dump(struct sk_buff *skb, struct netlink_callback *cb);
 
-
+static spinlock_t dn_fib_multipath_lock = SPIN_LOCK_UNLOCKED;
 static struct dn_fib_info *dn_fib_info_list;
 static rwlock_t dn_fib_info_lock = RW_LOCK_UNLOCKED;
 int dn_fib_info_cnt;
@@ -62,18 +67,18 @@ static struct
        int error;
        u8 scope;
 } dn_fib_props[RTA_MAX+1] = {
-       { 0, RT_SCOPE_NOWHERE },                /* RTN_UNSPEC */
-       { 0, RT_SCOPE_UNIVERSE },               /* RTN_UNICAST */
-       { 0, RT_SCOPE_HOST },                   /* RTN_LOCAL */
-       { -EINVAL, RT_SCOPE_NOWHERE },          /* RTN_BROADCAST */
-       { -EINVAL, RT_SCOPE_NOWHERE },          /* RTN_ANYCAST */
-       { -EINVAL, RT_SCOPE_NOWHERE },          /* RTN_MULTICAST */
-       { -EINVAL, RT_SCOPE_UNIVERSE },         /* RTN_BLACKHOLE */
-       { -EHOSTUNREACH, RT_SCOPE_UNIVERSE },   /* RTN_UNREACHABLE */
-       { -EACCES, RT_SCOPE_UNIVERSE },         /* RTN_PROHIBIT */
-       { -EAGAIN, RT_SCOPE_UNIVERSE },         /* RTN_THROW */
-       { -EINVAL, RT_SCOPE_NOWHERE },          /* RTN_NAT */
-       { -EINVAL, RT_SCOPE_NOWHERE }           /* RTN_XRESOLVE */
+       { .error = 0, .scope = RT_SCOPE_NOWHERE },        /* RTN_UNSPEC */
+       { .error = 0, .scope = RT_SCOPE_UNIVERSE },       /* RTN_UNICAST */
+       { .error = 0, .scope = RT_SCOPE_HOST },           /* RTN_LOCAL */
+       { .error = -EINVAL, .scope = RT_SCOPE_NOWHERE },  /* RTN_BROADCAST */
+       { .error = -EINVAL, .scope = RT_SCOPE_NOWHERE },  /* RTN_ANYCAST */
+       { .error = -EINVAL, .scope = RT_SCOPE_NOWHERE },  /* RTN_MULTICAST */
+       { .error = -EINVAL, .scope = RT_SCOPE_UNIVERSE }, /* RTN_BLACKHOLE */
+       { .error = -EHOSTUNREACH, .scope = RT_SCOPE_UNIVERSE }, /* RTN_UNREACHABLE */
+       { .error = -EACCES, .scope = RT_SCOPE_UNIVERSE }, /* RTN_PROHIBIT */
+       { .error = -EAGAIN, .scope = RT_SCOPE_UNIVERSE }, /* RTN_THROW */
+       { .error = 0, .scope = RT_SCOPE_NOWHERE },  /* RTN_NAT */
+       { .error = -EINVAL, .scope = RT_SCOPE_NOWHERE }   /* RTN_XRESOLVE */
 };
 
 void dn_fib_free_info(struct dn_fib_info *fi)
@@ -108,7 +113,7 @@ void dn_fib_release_info(struct dn_fib_info *fi)
        write_unlock(&dn_fib_info_lock);
 }
 
-static __inline__ int dn_fib_nh_comp(const struct dn_fib_info *fi, const struct dn_fib_info *ofi)
+static inline int dn_fib_nh_comp(const struct dn_fib_info *fi, const struct dn_fib_info *ofi)
 {
        const struct dn_fib_nh *onh = ofi->fib_nh;
 
@@ -124,7 +129,7 @@ static __inline__ int dn_fib_nh_comp(const struct dn_fib_info *fi, const struct
        return 0;
 }
 
-static __inline__ struct dn_fib_info *dn_fib_find_info(const struct dn_fib_info *nfi)
+static inline struct dn_fib_info *dn_fib_find_info(const struct dn_fib_info *nfi)
 {
        for_fib_info() {
                if (fi->fib_nhs != nfi->fib_nhs)
@@ -132,6 +137,7 @@ static __inline__ struct dn_fib_info *dn_fib_find_info(const struct dn_fib_info
                if (nfi->fib_protocol == fi->fib_protocol &&
                        nfi->fib_prefsrc == fi->fib_prefsrc &&
                        nfi->fib_priority == fi->fib_priority &&
+                       memcmp(nfi->fib_metrics, fi->fib_metrics, sizeof(fi->fib_metrics)) == 0 &&
                        ((nfi->fib_flags^fi->fib_flags)&~RTNH_F_DEAD) == 0 &&
                        (nfi->fib_nhs == 0 || dn_fib_nh_comp(fi, nfi) == 0))
                                return fi;
@@ -195,14 +201,18 @@ static int dn_fib_check_nh(const struct rtmsg *r, struct dn_fib_info *fi, struct
        int err;
 
        if (nh->nh_gw) {
-               struct dn_fib_key key;
+               struct flowi fl;
                struct dn_fib_res res;
 
+               memset(&fl, 0, sizeof(fl));
+
                if (nh->nh_flags&RTNH_F_ONLINK) {
                        struct net_device *dev;
 
                        if (r->rtm_scope >= RT_SCOPE_LINK)
                                return -EINVAL;
+                       if (dnet_addr_type(nh->nh_gw) != RTN_UNICAST)
+                               return -EINVAL;
                        if ((dev = __dev_get_by_index(nh->nh_oif)) == NULL)
                                return -ENODEV;
                        if (!(dev->flags&IFF_UP))
@@ -213,23 +223,33 @@ static int dn_fib_check_nh(const struct rtmsg *r, struct dn_fib_info *fi, struct
                        return 0;
                }
 
-               memset(&key, 0, sizeof(key));
-               key.dst = nh->nh_gw;
-               key.oif = nh->nh_oif;
-               key.scope = r->rtm_scope + 1;
+               memset(&fl, 0, sizeof(fl));
+               fl.fld_dst = nh->nh_gw;
+               fl.oif = nh->nh_oif;
+               fl.fld_scope = r->rtm_scope + 1;
 
-               if (key.scope < RT_SCOPE_LINK)
-                       key.scope = RT_SCOPE_LINK;
+               if (fl.fld_scope < RT_SCOPE_LINK)
+                       fl.fld_scope = RT_SCOPE_LINK;
 
-               if ((err = dn_fib_lookup(&key, &res)) != 0)
+               if ((err = dn_fib_lookup(&fl, &res)) != 0)
                        return err;
 
+               err = -EINVAL;
+               if (res.type != RTN_UNICAST && res.type != RTN_LOCAL)
+                       goto out;
                nh->nh_scope = res.scope;
                nh->nh_oif = DN_FIB_RES_OIF(res);
                nh->nh_dev = DN_FIB_RES_DEV(res);
-               if (nh->nh_dev)
-                       atomic_inc(&nh->nh_dev->refcnt);
+               if (nh->nh_dev == NULL)
+                       goto out;
+               atomic_inc(&nh->nh_dev->refcnt);
+               err = -ENETDOWN;
+               if (!(nh->nh_dev->flags & IFF_UP))
+                       goto out;
+               err = 0;
+out:
                dn_fib_res_put(&res);
+               return err;
        } else {
                struct net_device *dev;
 
@@ -277,6 +297,20 @@ struct dn_fib_info *dn_fib_create_info(const struct rtmsg *r, struct dn_kern_rta
        fi->fib_flags = r->rtm_flags;
        if (rta->rta_priority)
                fi->fib_priority = *rta->rta_priority;
+       if (rta->rta_mx) {
+               int attrlen = RTA_PAYLOAD(rta->rta_mx);
+               struct rtattr *attr = RTA_DATA(rta->rta_mx);
+
+               while(RTA_OK(attr, attrlen)) {
+                       unsigned flavour = attr->rta_type;
+                       if (flavour) {
+                               if (flavour > RTAX_MAX)
+                                       goto err_inval;
+                               fi->fib_metrics[flavour-1] = *(unsigned*)RTA_DATA(attr);
+                       }
+                       attr = RTA_NEXT(attr, attrlen);
+               }
+       }
        if (rta->rta_prefsrc)
                memcpy(&fi->fib_prefsrc, rta->rta_prefsrc, 2);
 
@@ -297,6 +331,13 @@ struct dn_fib_info *dn_fib_create_info(const struct rtmsg *r, struct dn_kern_rta
                nh->nh_weight = 1;
        }
 
+       if (r->rtm_type == RTN_NAT) {
+               if (rta->rta_gw == NULL || nhs != 1 || rta->rta_oif)
+                       goto err_inval;
+               memcpy(&fi->fib_nh->nh_gw, rta->rta_gw, 2);
+               goto link_it;
+       }
+
        if (dn_fib_props[r->rtm_type].error) {
                if (rta->rta_gw || rta->rta_oif || rta->rta_mp)
                        goto err_inval;
@@ -324,14 +365,12 @@ struct dn_fib_info *dn_fib_create_info(const struct rtmsg *r, struct dn_kern_rta
                } endfor_nexthops(fi)
        }
 
-#if I_GET_AROUND_TO_FIXING_PREFSRC
        if (fi->fib_prefsrc) {
                if (r->rtm_type != RTN_LOCAL || rta->rta_dst == NULL ||
                    memcmp(&fi->fib_prefsrc, rta->rta_dst, 2))
-                       if (dn_addr_type(fi->fib_prefsrc) != RTN_LOCAL)
+                       if (dnet_addr_type(fi->fib_prefsrc) != RTN_LOCAL)
                                goto err_inval;
        }
-#endif
 
 link_it:
        if ((ofi = dn_fib_find_info(fi)) != NULL) {
@@ -366,12 +405,53 @@ failure:
        return NULL;
 }
 
+int dn_fib_semantic_match(int type, struct dn_fib_info *fi, const struct flowi *fl, struct dn_fib_res *res)
+{
+       int err = dn_fib_props[type].error;
+
+       if (err == 0) {
+               if (fi->fib_flags & RTNH_F_DEAD)
+                       return 1;
+
+               res->fi = fi;
+
+               switch(type) {
+                       case RTN_NAT:
+                               DN_FIB_RES_RESET(*res);
+                               atomic_inc(&fi->fib_clntref);
+                               return 0;
+                       case RTN_UNICAST:
+                       case RTN_LOCAL:
+                               for_nexthops(fi) {
+                                       if (nh->nh_flags & RTNH_F_DEAD)
+                                               continue;
+                                       if (!fl->oif || fl->oif == nh->nh_oif)
+                                               break;
+                               }
+                               if (nhsel < fi->fib_nhs) {
+                                       res->nh_sel = nhsel;
+                                       atomic_inc(&fi->fib_clntref);
+                                       return 0;
+                               }
+                               endfor_nexthops(fi);
+                               res->fi = NULL;
+                               return 1;
+                       default:
+                               if (net_ratelimit())
+                                        printk("DECnet: impossible routing event : dn_fib_semantic_match type=%d\n", type);
+                               res->fi = NULL;
+                               return -EINVAL;
+               }
+       }
+       return err;
+}
 
-void dn_fib_select_multipath(const struct dn_fib_key *key, struct dn_fib_res *res)
+void dn_fib_select_multipath(const struct flowi *fl, struct dn_fib_res *res)
 {
        struct dn_fib_info *fi = res->fi;
        int w;
 
+       spin_lock_bh(&dn_fib_multipath_lock);
        if (fi->fib_power <= 0) {
                int power = 0;
                change_nexthops(fi) {
@@ -381,6 +461,11 @@ void dn_fib_select_multipath(const struct dn_fib_key *key, struct dn_fib_res *re
                        }
                } endfor_nexthops(fi);
                fi->fib_power = power;
+               if (power < 0) {
+                       spin_unlock_bh(&dn_fib_multipath_lock);
+                       res->nh_sel = 0;
+                       return;
+               }
        }
 
        w = jiffies % fi->fib_power;
@@ -391,16 +476,16 @@ void dn_fib_select_multipath(const struct dn_fib_key *key, struct dn_fib_res *re
                                nh->nh_power--;
                                fi->fib_power--;
                                res->nh_sel = nhsel;
+                               spin_unlock_bh(&dn_fib_multipath_lock);
                                return;
                        }
                }
        } endfor_nexthops(fi);
-
-       printk(KERN_DEBUG "DECnet: BUG! dn_fib_select_multipath\n");
+       res->nh_sel = 0;
+       spin_unlock_bh(&dn_fib_multipath_lock);
 }
 
 
-
 /*
  * Punt to user via netlink for example, but for now
  * we just drop it.
@@ -475,9 +560,9 @@ int dn_fib_dump(struct sk_buff *skb, struct netlink_callback *cb)
 
        s_t = cb->args[0];
        if (s_t == 0)
-               s_t = cb->args[0] = DN_MIN_TABLE;
+               s_t = cb->args[0] = RT_MIN_TABLE;
 
-       for(t = s_t; t < DN_NUM_TABLES; t++) {
+       for(t = s_t; t <= RT_TABLE_MAX; t++) {
                if (t < s_t)
                        continue;
                if (t > s_t)
@@ -494,6 +579,125 @@ int dn_fib_dump(struct sk_buff *skb, struct netlink_callback *cb)
        return skb->len;
 }
 
+static void fib_magic(int cmd, int type, __u16 dst, int dst_len, struct dn_ifaddr *ifa)
+{
+       struct dn_fib_table *tb;
+       struct {
+               struct nlmsghdr nlh;
+               struct rtmsg rtm;
+       } req;
+       struct dn_kern_rta rta;
+
+       memset(&req.rtm, 0, sizeof(req.rtm));
+       memset(&rta, 0, sizeof(rta));
+
+       if (type == RTN_UNICAST)
+               tb = dn_fib_get_table(RT_MIN_TABLE, 1);
+       else
+               tb = dn_fib_get_table(RT_TABLE_LOCAL, 1);
+
+       if (tb == NULL)
+               return;
+
+       req.nlh.nlmsg_len = sizeof(req);
+       req.nlh.nlmsg_type = cmd;
+       req.nlh.nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_APPEND;
+       req.nlh.nlmsg_pid = 0;
+       req.nlh.nlmsg_seq = 0;
+
+       req.rtm.rtm_dst_len = dst_len;
+       req.rtm.rtm_table = tb->n;
+       req.rtm.rtm_protocol = RTPROT_KERNEL;
+       req.rtm.rtm_scope = (type != RTN_LOCAL ? RT_SCOPE_LINK : RT_SCOPE_HOST);
+       req.rtm.rtm_type = type;
+
+       rta.rta_dst = &dst;
+       rta.rta_prefsrc = &ifa->ifa_local;
+       rta.rta_oif = &ifa->ifa_dev->dev->ifindex;
+
+       if (cmd == RTM_NEWROUTE)
+               tb->insert(tb, &req.rtm, &rta, &req.nlh, NULL);
+       else
+               tb->delete(tb, &req.rtm, &rta, &req.nlh, NULL);
+}
+
+static void dn_fib_add_ifaddr(struct dn_ifaddr *ifa)
+{
+
+       fib_magic(RTM_NEWROUTE, RTN_LOCAL, ifa->ifa_local, 16, ifa);
+
+#if 0
+       if (!(dev->flags&IFF_UP))
+               return;
+       /* In the future, we will want to add default routes here */
+
+#endif
+}
+
+static void dn_fib_del_ifaddr(struct dn_ifaddr *ifa)
+{
+       int found_it = 0;
+       struct net_device *dev;
+       struct dn_dev *dn_db;
+       struct dn_ifaddr *ifa2;
+
+       ASSERT_RTNL();
+
+       /* Scan device list */
+       read_lock(&dev_base_lock);
+       for(dev = dev_base; dev; dev = dev->next) {
+               dn_db = dev->dn_ptr;
+               if (dn_db == NULL)
+                       continue;
+               for(ifa2 = dn_db->ifa_list; ifa2; ifa2 = ifa2->ifa_next) {
+                       if (ifa2->ifa_local == ifa->ifa_local) {
+                               found_it = 1;
+                               break;
+                       }
+               }
+       }
+       read_unlock(&dev_base_lock);
+
+       if (found_it == 0) {
+               fib_magic(RTM_DELROUTE, RTN_LOCAL, ifa->ifa_local, 16, ifa);
+
+               if (dnet_addr_type(ifa->ifa_local) != RTN_LOCAL) {
+                       if (dn_fib_sync_down(ifa->ifa_local, NULL, 0))
+                               dn_fib_flush();
+               }
+       }
+}
+
+static void dn_fib_disable_addr(struct net_device *dev, int force)
+{
+       if (dn_fib_sync_down(0, dev, force))
+               dn_fib_flush();
+       dn_rt_cache_flush(0);
+       neigh_ifdown(&dn_neigh_table, dev);
+}
+
+static int dn_fib_dnaddr_event(struct notifier_block *this, unsigned long event, void *ptr)
+{
+       struct dn_ifaddr *ifa = (struct dn_ifaddr *)ptr;
+
+       switch(event) {
+               case NETDEV_UP:
+                       dn_fib_add_ifaddr(ifa);
+                       dn_fib_sync_up(ifa->ifa_dev->dev);
+                       dn_rt_cache_flush(-1);
+                       break;
+               case NETDEV_DOWN:
+                       dn_fib_del_ifaddr(ifa);
+                       if (ifa->ifa_dev && ifa->ifa_dev->ifa_list == NULL) {
+                               dn_fib_disable_addr(ifa->ifa_dev->dev, 1);
+                       } else {
+                               dn_rt_cache_flush(-1);
+                       }
+                       break;
+       }
+       return NOTIFY_DONE;
+}
+
 int dn_fib_sync_down(dn_address local, struct net_device *dev, int force)
 {
         int ret = 0;
@@ -520,9 +724,11 @@ int dn_fib_sync_down(dn_address local, struct net_device *dev, int force)
                                         dead++;
                                 else if (nh->nh_dev == dev &&
                                                 nh->nh_scope != scope) {
+                                       spin_lock_bh(&dn_fib_multipath_lock);
                                         nh->nh_flags |= RTNH_F_DEAD;
                                         fi->fib_power -= nh->nh_power;
                                         nh->nh_power = 0;
+                                       spin_unlock_bh(&dn_fib_multipath_lock);
                                         dead++;
                                 }
                         } endfor_nexthops(fi)
@@ -556,11 +762,13 @@ int dn_fib_sync_up(struct net_device *dev)
                         if (nh->nh_dev != dev || dev->dn_ptr == NULL)
                                 continue;
                         alive++;
+                       spin_lock_bh(&dn_fib_multipath_lock);
                         nh->nh_power = 0;
                         nh->nh_flags &= ~RTNH_F_DEAD;
+                       spin_unlock_bh(&dn_fib_multipath_lock);
                 } endfor_nexthops(fi);
 
-                if (alive == fi->fib_nhs) {
+                if (alive > 0) {
                         fi->fib_flags &= ~RTNH_F_DEAD;
                         ret++;
                 }
@@ -574,7 +782,7 @@ void dn_fib_flush(void)
         struct dn_fib_table *tb;
         int id;
 
-        for(id = DN_NUM_TABLES; id > 0; id--) {
+        for(id = RT_TABLE_MAX; id > 0; id--) {
                 if ((tb = dn_fib_get_table(id, 0)) == NULL)
                         continue;
                 flushed += tb->flush(tb);
@@ -584,21 +792,6 @@ void dn_fib_flush(void)
                 dn_rt_cache_flush(-1);
 }
 
-int dn_fib_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
-{
-
-       if (!capable(CAP_NET_ADMIN))
-               return -EPERM;
-
-       switch(cmd) {
-               case SIOCADDRT:
-               case SIOCDELRT:
-                       return 0;
-       }
-
-       return -EINVAL;
-}
-
 #ifdef CONFIG_PROC_FS
 
 static int decnet_rt_get_info(char *buffer, char **start, off_t offset, int length)
@@ -620,7 +813,7 @@ static int decnet_rt_get_info(char *buffer, char **start, off_t offset, int leng
         }
 
 
-        for(i = DN_MIN_TABLE; (i <= DN_NUM_TABLES) && (count > 0); i++) {
+        for(i = RT_MIN_TABLE; (i <= RT_TABLE_MAX) && (count > 0); i++) {
                 if ((tb = dn_fib_get_table(i, 0)) != NULL) {
                         int n = tb->get_info(tb, ptr, first, count);
                         count -= n;
@@ -638,12 +831,18 @@ static int decnet_rt_get_info(char *buffer, char **start, off_t offset, int leng
 }
 #endif /* CONFIG_PROC_FS */
 
+static struct notifier_block dn_fib_dnaddr_notifier = {
+       .notifier_call = dn_fib_dnaddr_event,
+};
+
 void __exit dn_fib_cleanup(void)
 {
        proc_net_remove("decnet_route");
 
        dn_fib_table_cleanup();
        dn_fib_rules_cleanup();
+
+       unregister_dnaddr_notifier(&dn_fib_dnaddr_notifier);
 }
 
 
@@ -656,6 +855,8 @@ void __init dn_fib_init(void)
 
        dn_fib_table_init();
        dn_fib_rules_init();
+
+       register_dnaddr_notifier(&dn_fib_dnaddr_notifier);
 }
 
 
index 161cdf8a5f3fcb8ec2dcc569c827e4cf07cd8ca3..d63d04d4f35f0a657810236a7c3f4dff6e37ff6d 100644 (file)
@@ -20,6 +20,7 @@
  *     Steve Whitehouse     : Fixed neighbour states (for now anyway).
  *     Steve Whitehouse     : Made error_report functions dummies. This
  *                            is not the right place to return skbs.
+ *     Steve Whitehouse     : Convert to seq_file
  *
  */
 
 #include <linux/string.h>
 #include <linux/netfilter_decnet.h>
 #include <linux/spinlock.h>
+#include <linux/seq_file.h>
 #include <asm/atomic.h>
 #include <net/neighbour.h>
 #include <net/dst.h>
+#include <net/flow.h>
 #include <net/dn.h>
 #include <net/dn_dev.h>
 #include <net/dn_neigh.h>
@@ -160,7 +163,20 @@ static int dn_neigh_construct(struct neighbour *neigh)
                return -EINVAL;
        }
 
-       dn->blksize = 230;
+       /*
+        * Make an estimate of the remote block size by assuming that its
+        * two less then the device mtu, which it true for ethernet (and
+        * other things which support long format headers) since there is
+        * an extra length field (of 16 bits) which isn't part of the
+        * ethernet headers and which the DECnet specs won't admit is part
+        * of the DECnet routing headers either.
+        *
+        * If we over estimate here its no big deal, the NSP negotiations
+        * will prevent us from sending packets which are too large for the
+        * remote node to handle. In any case this figure is normally updated
+        * by a hello message in most cases.
+        */
+       dn->blksize = dev->mtu - 2;
 
        return 0;
 }
@@ -330,7 +346,7 @@ static int dn_phase3_output(struct sk_buff *skb)
  * basically does a neigh_lookup(), but without comparing the device
  * field. This is required for the On-Ethernet cache
  */
-struct neighbour *dn_neigh_lookup(struct neigh_table *tbl, void *ptr)
+struct neighbour *dn_neigh_lookup(struct neigh_table *tbl, const void *ptr)
 {
        struct neighbour *neigh;
        u32 hash_val;
@@ -466,8 +482,6 @@ int dn_neigh_endnode_hello(struct sk_buff *skb)
        return 0;
 }
 
-
-#ifdef CONFIG_DECNET_ROUTER
 static char *dn_find_slot(char *base, int max, int priority)
 {
        int i;
@@ -526,78 +540,200 @@ int dn_neigh_elist(struct net_device *dev, unsigned char *ptr, int n)
 
        return t;
 }
-#endif /* CONFIG_DECNET_ROUTER */
-
 
 
 #ifdef CONFIG_PROC_FS
-static int dn_neigh_get_info(char *buffer, char **start, off_t offset, int length)
+
+struct dn_neigh_iter_state {
+       int bucket;
+};
+
+static struct neighbour *neigh_get_first(struct seq_file *seq)
 {
-        int len     = 0;
-        off_t pos   = 0;
-        off_t begin = 0;
-       struct neighbour *n;
-       int i;
-       char buf[DN_ASCBUF_LEN];
+       struct dn_neigh_iter_state *state = seq->private;
+       struct neighbour *n = NULL;
+
+       for(state->bucket = 0;
+           state->bucket <= NEIGH_HASHMASK;
+           ++state->bucket) {
+               n = dn_neigh_table.hash_buckets[state->bucket];
+               if (n)
+                       break;
+       }
 
-       len += sprintf(buffer + len, "Addr    Flags State Use Blksize Dev\n");
-
-       for(i=0;i <= NEIGH_HASHMASK; i++) {
-               read_lock_bh(&dn_neigh_table.lock);
-               n = dn_neigh_table.hash_buckets[i];
-               for(; n != NULL; n = n->next) {
-                       struct dn_neigh *dn = (struct dn_neigh *)n;
-
-                       read_lock(&n->lock);
-                       len += sprintf(buffer+len, "%-7s %s%s%s   %02x    %02d  %07ld %-8s\n",
-                                       dn_addr2asc(dn_ntohs(dn->addr), buf),
-                                       (dn->flags&DN_NDFLAG_R1) ? "1" : "-",
-                                       (dn->flags&DN_NDFLAG_R2) ? "2" : "-",
-                                       (dn->flags&DN_NDFLAG_P3) ? "3" : "-",
-                                       dn->n.nud_state,
-                                       atomic_read(&dn->n.refcnt),
-                                       dn->blksize,
-                                       (dn->n.dev) ? dn->n.dev->name : "?");
-                       read_unlock(&n->lock);
-
-                       pos = begin + len;
-
-                       if (pos < offset) {
-                               len = 0;
-                               begin = pos;
-                       }
-
-                       if (pos > offset + length) {
-                               read_unlock_bh(&dn_neigh_table.lock);
-                                       goto done;
-                       }
-               }
+       return n;
+}
+
+static struct neighbour *neigh_get_next(struct seq_file *seq,
+                                       struct neighbour *n)
+{
+       struct dn_neigh_iter_state *state = seq->private;
+
+       n = n->next;
+try_again:
+       if (n)
+               goto out;
+       if (++state->bucket > NEIGH_HASHMASK)
+               goto out;
+       n = dn_neigh_table.hash_buckets[state->bucket];
+       goto try_again;
+out:
+       return n;
+}
+
+static struct neighbour *neigh_get_idx(struct seq_file *seq, loff_t *pos)
+{
+       struct neighbour *n = neigh_get_first(seq);
+
+       if (n)
+               while(*pos && (n = neigh_get_next(seq, n)))
+                       --*pos;
+       return *pos ? NULL : n;
+}
+
+static void *dn_neigh_get_idx(struct seq_file *seq, loff_t pos)
+{
+       void *rc;
+       read_lock_bh(&dn_neigh_table.lock);
+       rc = neigh_get_idx(seq, &pos);
+       if (!rc) {
+               read_unlock_bh(&dn_neigh_table.lock);
+       }
+       return rc;
+}
+
+static void *dn_neigh_seq_start(struct seq_file *seq, loff_t *pos)
+{
+       return *pos ? dn_neigh_get_idx(seq, *pos - 1) : (void*)1;
+}
+
+static void *dn_neigh_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+       void *rc;
+
+
+       if (v == (void*)1) {
+               rc = dn_neigh_get_idx(seq, 0);
+               goto out;
+       }
+
+       rc = neigh_get_next(seq, v);
+       if (rc)
+               goto out;
+       read_unlock_bh(&dn_neigh_table.lock);
+out:
+       ++*pos;
+       return rc;
+}
+
+static void dn_neigh_seq_stop(struct seq_file *seq, void *v)
+{
+       if (v && v != (void*)1)
                read_unlock_bh(&dn_neigh_table.lock);
+}
+
+static inline void dn_neigh_format_entry(struct seq_file *seq,
+                                        struct neighbour *n)
+{
+       struct dn_neigh *dn = (struct dn_neigh *)n;
+       char buf[DN_ASCBUF_LEN];
+
+       read_lock(&n->lock);
+       seq_printf(seq, "%-7s %s%s%s   %02x    %02d  %07ld %-8s\n",
+                  dn_addr2asc(dn_ntohs(dn->addr), buf),
+                  (dn->flags&DN_NDFLAG_R1) ? "1" : "-",
+                  (dn->flags&DN_NDFLAG_R2) ? "2" : "-",
+                  (dn->flags&DN_NDFLAG_P3) ? "3" : "-",
+                  dn->n.nud_state,
+                  atomic_read(&dn->n.refcnt),
+                  dn->blksize,
+                  (dn->n.dev) ? dn->n.dev->name : "?");
+       read_unlock(&n->lock);
+}
+
+static int dn_neigh_seq_show(struct seq_file *seq, void *v)
+{
+       if (v == (void*)1) {
+               seq_puts(seq, "Addr    Flags State Use Blksize Dev\n");
+       } else {
+               dn_neigh_format_entry(seq, v);
        }
 
-done:
+       return 0;
+}
+
+static struct seq_operations dn_neigh_seq_ops = {
+       .start = dn_neigh_seq_start,
+       .next  = dn_neigh_seq_next,
+       .stop  = dn_neigh_seq_stop,
+       .show  = dn_neigh_seq_show,
+};
 
-        *start = buffer + (offset - begin);
-        len   -= offset - begin;
+static int dn_neigh_seq_open(struct inode *inode, struct file *file)
+{
+       struct seq_file *seq;
+       int rc = -ENOMEM;
+       struct dn_neigh_iter_state *s = kmalloc(sizeof(*s), GFP_KERNEL);
+
+       if (!s)
+               goto out;
+
+       rc = seq_open(file, &dn_neigh_seq_ops);
+       if (rc)
+               goto out_kfree;
+
+       seq          = file->private_data;
+       seq->private = s;
+       memset(s, 0, sizeof(*s));
+out:
+       return rc;
+out_kfree:
+       kfree(s);
+       goto out;
+}
 
-        if (len > length) len = length;
+static int dn_seq_release(struct inode *inode, struct file *file)
+{
+       struct seq_file *seq  = (struct seq_file *)file->private_data;
 
-        return len;
+       kfree(seq->private);
+       seq->private = NULL;
+       return seq_release(inode, file);
 }
 
+static struct file_operations dn_neigh_seq_fops = {
+       .open           = dn_neigh_seq_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = dn_seq_release,
+};
+
+static int __init dn_neigh_proc_init(void)
+{
+       int rc = 0;
+       struct proc_dir_entry *p = create_proc_entry("decnet_neigh", S_IRUGO, proc_net);
+       if (p)
+               p->proc_fops = &dn_neigh_seq_fops;
+       else
+               rc = -ENOMEM;
+       return rc;
+}
+
+#else
+static int __init dn_neigh_proc_init(void)
+{
+       return 0;
+}
 #endif
 
 void __init dn_neigh_init(void)
 {
        neigh_table_init(&dn_neigh_table);
 
-#ifdef CONFIG_PROC_FS
-       proc_net_create("decnet_neigh",0,dn_neigh_get_info);
-#endif /* CONFIG_PROC_FS */
+       dn_neigh_proc_init();
 }
 
 void __exit dn_neigh_cleanup(void)
 {
-       proc_net_remove("decnet_neigh");
        neigh_table_clear(&dn_neigh_table);
 }
index a13327accdc9922e5b39f040a0ace72b3fdf9d8c..91a7b0a00236924576e5433c01bbef0f0de34e20 100644 (file)
@@ -434,7 +434,15 @@ static void dn_nsp_disc_init(struct sock *sk, struct sk_buff *skb)
                sk->state_change(sk);
        }
 
-       dn_nsp_send_disc(sk, NSP_DISCCONF, NSP_REASON_DC, GFP_ATOMIC);
+       /* 
+        * It appears that its possible for remote machines to send disc
+        * init messages with no port identifier if we are in the CI and
+        * possibly also the CD state. Obviously we shouldn't reply with
+        * a message if we don't know what the end point is.
+        */
+       if (scp->addrrem) {
+               dn_nsp_send_disc(sk, NSP_DISCCONF, NSP_REASON_DC, GFP_ATOMIC);
+       }
        scp->persist_fxn = dn_destroy_timer;
        scp->persist = dn_nsp_persist(sk);
 
index ead417ca7001c5a35c26cd154e0592a597ef67c2..a6c9b66c90fd511daef19c80020ab6a6ebd40f5e 100644 (file)
@@ -21,6 +21,7 @@
  *         Paul Koning:  Connect Confirm message fix.
  *      Eduardo Serrat:  Fix to stop dn_nsp_do_disc() sending malformed packets.
  *    Steve Whitehouse:  dn_nsp_output() and friends needed a spring clean
+ *    Steve Whitehouse:  Moved dn_nsp_send() in here from route.h
  */
 
 /******************************************************************************
@@ -63,6 +64,7 @@
 #include <linux/if_packet.h>
 #include <net/neighbour.h>
 #include <net/dst.h>
+#include <net/flow.h>
 #include <net/dn.h>
 #include <net/dn_nsp.h>
 #include <net/dn_dev.h>
 
 static int nsp_backoff[NSP_MAXRXTSHIFT + 1] = { 1, 2, 4, 8, 16, 32, 64, 64, 64, 64, 64, 64, 64 };
 
+static void dn_nsp_send(struct sk_buff *skb)
+{
+       struct sock *sk = skb->sk;
+       struct dn_scp *scp = DN_SK(sk);
+       struct dst_entry *dst;
+       struct flowi fl;
+
+       skb->h.raw = skb->data;
+       scp->stamp = jiffies;
+
+       dst = sk_dst_check(sk, 0);
+       if (dst) {
+try_again:
+               skb->dst = dst;
+               dst_output(skb);
+               return;
+       }
+
+       memset(&fl, 0, sizeof(fl));
+       fl.oif = sk->bound_dev_if;
+       fl.fld_src = dn_saddr2dn(&scp->addr);
+       fl.fld_dst = dn_saddr2dn(&scp->peer);
+       dn_sk_ports_copy(&fl, scp);
+       if (dn_route_output_sock(&sk->dst_cache, &fl, sk, 0) == 0) {
+               dst = sk_dst_get(sk);
+               sk->route_caps = dst->dev->features;
+               goto try_again;
+       }
+
+       sk->err = EHOSTUNREACH;
+       if (!test_bit(SOCK_DEAD, &sk->flags))
+               sk->state_change(sk);
+}
+
+
 /*
  * If sk == NULL, then we assume that we are supposed to be making
  * a routing layer skb. If sk != NULL, then we are supposed to be
@@ -356,12 +393,33 @@ static unsigned short *dn_mk_ack_header(struct sock *sk, struct sk_buff *skb, un
        return ptr;
 }
 
+static unsigned short *dn_nsp_mk_data_header(struct sock *sk, struct sk_buff *skb, int oth)
+{
+       struct dn_scp *scp = DN_SK(sk);
+       struct dn_skb_cb *cb = DN_SKB_CB(skb);
+       unsigned short *ptr = dn_mk_ack_header(sk, skb, cb->nsp_flags, 11, oth);
+
+       if (unlikely(oth)) {
+               cb->segnum = scp->numoth;
+               seq_add(&scp->numoth, 1);
+       } else {
+               cb->segnum = scp->numdat;
+               seq_add(&scp->numdat, 1);
+       }
+       *(ptr++) = dn_htons(cb->segnum);
+
+       return ptr;
+}
+
 void dn_nsp_queue_xmit(struct sock *sk, struct sk_buff *skb, int gfp, int oth)
 {
        struct dn_scp *scp = DN_SK(sk);
        struct dn_skb_cb *cb = DN_SKB_CB(skb);
        unsigned long t = ((scp->nsp_srtt >> 2) + scp->nsp_rttvar) >> 1;
 
+       cb->xmit_count = 0;
+       dn_nsp_mk_data_header(sk, skb, oth);
+
        /*
         * Slow start: If we have been idle for more than
         * one RTT, then reset window to min size.
@@ -369,10 +427,6 @@ void dn_nsp_queue_xmit(struct sock *sk, struct sk_buff *skb, int gfp, int oth)
        if ((jiffies - scp->stamp) > t)
                scp->snd_window = NSP_MIN_WINDOW;
 
-       /* printk(KERN_DEBUG "Window: %lu\n", scp->snd_window); */
-
-       cb->xmit_count = 0;
-
        if (oth)
                skb_queue_tail(&scp->other_xmit_queue, skb);
        else
@@ -630,19 +684,15 @@ void dn_nsp_send_link(struct sock *sk, unsigned char lsflags, char fcval)
 {
        struct dn_scp *scp = DN_SK(sk);
        struct sk_buff *skb;
-       unsigned short *segnum;
        unsigned char *ptr;
        int gfp = GFP_ATOMIC;
 
-       if ((skb = dn_alloc_skb(sk, 13, gfp)) == NULL)
+       if ((skb = dn_alloc_skb(sk, DN_MAX_NSP_DATA_HEADER + 2, gfp)) == NULL)
                return;
 
-       skb_reserve(skb, 13);
-       segnum = dn_mk_ack_header(sk, skb, 0x10, 13, 1);
-       *segnum = dn_htons(scp->numoth);
-        DN_SKB_CB(skb)->segnum = scp->numoth;
-       seq_add(&scp->numoth, 1);
-       ptr = (unsigned char *)(segnum + 1);
+       skb_reserve(skb, DN_MAX_NSP_DATA_HEADER);
+       ptr = skb_put(skb, 2);
+       DN_SKB_CB(skb)->nsp_flags = 0x10;
        *ptr++ = lsflags;
        *ptr = fcval;
 
index 21c6f5934e8663f0baa4ea6d3d615871fcc12731..6d180599cc03cc9dc02f7da0809bba9330a12c54 100644 (file)
@@ -37,7 +37,8 @@
  *                                 backlog congestion level return codes.
  *             Steve Whitehouse : Fixed bug where routes were set up with
  *                                 no ref count on net devices.
- *                                 
+ *              Steve Whitehouse : RCU for the route cache
+ *              Steve Whitehouse : Preparations for the flow cache
  */
 
 /******************************************************************************
 #include <linux/netdevice.h>
 #include <linux/inet.h>
 #include <linux/route.h>
+#include <linux/in_route.h>
 #include <net/sock.h>
-#include <linux/fcntl.h>
 #include <linux/mm.h>
 #include <linux/proc_fs.h>
 #include <linux/init.h>
 #include <linux/rtnetlink.h>
 #include <linux/string.h>
 #include <linux/netfilter_decnet.h>
+#include <linux/rcupdate.h>
 #include <asm/errno.h>
 #include <net/neighbour.h>
 #include <net/dst.h>
+#include <net/flow.h>
 #include <net/dn.h>
 #include <net/dn_dev.h>
 #include <net/dn_nsp.h>
@@ -86,7 +89,7 @@
 struct dn_rt_hash_bucket
 {
        struct dn_route *chain;
-       rwlock_t lock;
+       spinlock_t lock;
 } __attribute__((__aligned__(8)));
 
 extern struct neigh_table dn_neigh_table;
@@ -134,6 +137,18 @@ static __inline__ unsigned dn_hash(unsigned short src, unsigned short dst)
        return dn_rt_hash_mask & (unsigned)tmp;
 }
 
+static inline void dnrt_free(struct dn_route *rt)
+{
+       call_rcu(&rt->u.dst.rcu_head, (void (*)(void *))dst_free, &rt->u.dst);
+}
+
+static inline void dnrt_drop(struct dn_route *rt)
+{
+       if (rt)
+               dst_release(&rt->u.dst);
+       call_rcu(&rt->u.dst.rcu_head, (void (*)(void *))dst_free, &rt->u.dst);
+}
+
 static void SMP_TIMER_NAME(dn_dst_check_expire)(unsigned long dummy)
 {
        int i;
@@ -144,7 +159,7 @@ static void SMP_TIMER_NAME(dn_dst_check_expire)(unsigned long dummy)
        for(i = 0; i <= dn_rt_hash_mask; i++) {
                rtp = &dn_rt_hash_table[i].chain;
 
-               write_lock(&dn_rt_hash_table[i].lock);
+               spin_lock(&dn_rt_hash_table[i].lock);
                while((rt=*rtp) != NULL) {
                        if (atomic_read(&rt->u.dst.__refcnt) ||
                                        (now - rt->u.dst.lastuse) < expire) {
@@ -153,9 +168,9 @@ static void SMP_TIMER_NAME(dn_dst_check_expire)(unsigned long dummy)
                        }
                        *rtp = rt->u.rt_next;
                        rt->u.rt_next = NULL;
-                       dst_free(&rt->u.dst);
+                       dnrt_free(rt);
                }
-               write_unlock(&dn_rt_hash_table[i].lock);
+               spin_unlock(&dn_rt_hash_table[i].lock);
 
                if ((jiffies - now) > 0)
                        break;
@@ -175,7 +190,7 @@ static int dn_dst_gc(void)
 
        for(i = 0; i <= dn_rt_hash_mask; i++) {
 
-               write_lock_bh(&dn_rt_hash_table[i].lock);
+               spin_lock_bh(&dn_rt_hash_table[i].lock);
                rtp = &dn_rt_hash_table[i].chain;
 
                while((rt=*rtp) != NULL) {
@@ -186,10 +201,10 @@ static int dn_dst_gc(void)
                        }
                        *rtp = rt->u.rt_next;
                        rt->u.rt_next = NULL;
-                       dst_free(&rt->u.dst);
+                       dnrt_drop(rt);
                        break;
                }
-               write_unlock_bh(&dn_rt_hash_table[i].lock);
+               spin_unlock_bh(&dn_rt_hash_table[i].lock);
        }
 
        return 0;
@@ -216,19 +231,53 @@ static void dn_dst_link_failure(struct sk_buff *skb)
        return;
 }
 
-static void dn_insert_route(struct dn_route *rt, unsigned hash)
+static inline int compare_keys(struct flowi *fl1, struct flowi *fl2)
 {
+       return memcmp(&fl1->nl_u.dn_u, &fl2->nl_u.dn_u, sizeof(fl1->nl_u.dn_u)) == 0 &&
+               fl1->oif == fl2->oif &&
+               fl1->iif == fl2->iif;
+}
+
+static int dn_insert_route(struct dn_route *rt, unsigned hash, struct dn_route **rp)
+{
+       struct dn_route *rth, **rthp;
        unsigned long now = jiffies;
 
-       write_lock_bh(&dn_rt_hash_table[hash].lock);
+       rthp = &dn_rt_hash_table[hash].chain;
+
+       spin_lock_bh(&dn_rt_hash_table[hash].lock);
+       while((rth = *rthp) != NULL) {
+               if (compare_keys(&rth->fl, &rt->fl)) {
+                       /* Put it first */
+                       *rthp = rth->u.rt_next;
+                       smp_wmb();
+                       rth->u.rt_next = dn_rt_hash_table[hash].chain;
+                       smp_wmb();
+                       dn_rt_hash_table[hash].chain = rth;
+
+                       rth->u.dst.__use++;
+                       dst_hold(&rth->u.dst);
+                       rth->u.dst.lastuse = now;
+                       spin_unlock_bh(&dn_rt_hash_table[hash].lock);
+
+                       dnrt_drop(rt);
+                       *rp = rth;
+                       return 0;
+               }
+               rthp = &rth->u.rt_next;
+       }
+
+       smp_wmb();
        rt->u.rt_next = dn_rt_hash_table[hash].chain;
+       smp_wmb();
        dn_rt_hash_table[hash].chain = rt;
        
        dst_hold(&rt->u.dst);
        rt->u.dst.__use++;
        rt->u.dst.lastuse = now;
-
-       write_unlock_bh(&dn_rt_hash_table[hash].lock);
+       spin_unlock_bh(&dn_rt_hash_table[hash].lock);
+       *rp = rt;
+       return 0;
 }
 
 void SMP_TIMER_NAME(dn_run_flush)(unsigned long dummy)
@@ -237,7 +286,7 @@ void SMP_TIMER_NAME(dn_run_flush)(unsigned long dummy)
        struct dn_route *rt, *next;
 
        for(i = 0; i < dn_rt_hash_mask; i++) {
-               write_lock_bh(&dn_rt_hash_table[i].lock);
+               spin_lock_bh(&dn_rt_hash_table[i].lock);
 
                if ((rt = xchg(&dn_rt_hash_table[i].chain, NULL)) == NULL)
                        goto nothing_to_declare;
@@ -249,7 +298,7 @@ void SMP_TIMER_NAME(dn_run_flush)(unsigned long dummy)
                }
 
 nothing_to_declare:
-               write_unlock_bh(&dn_rt_hash_table[i].lock);
+               spin_unlock_bh(&dn_rt_hash_table[i].lock);
        }
 }
 
@@ -628,51 +677,50 @@ error:
        return err;
 }
 
-#ifdef CONFIG_DECNET_ROUTER
 static int dn_forward(struct sk_buff *skb)
 {
        struct dn_skb_cb *cb = DN_SKB_CB(skb);
        struct dst_entry *dst = skb->dst;
-       struct neighbour *neigh;
+       struct dn_dev *dn_db = dst->dev->dn_ptr;
+       struct dn_route *rt;
+       struct neighbour *neigh = dst->neighbour;
+       int header_len;
 #ifdef CONFIG_NETFILTER
        struct net_device *dev = skb->dev;
 #endif
-       int err = -EINVAL;
 
-       if ((neigh = dst->neighbour) == NULL)
-               goto error;
+       if (skb->pkt_type != PACKET_HOST)
+               goto drop;
+
+       /* Ensure that we have enough space for headers */
+       rt = (struct dn_route *)skb->dst;
+       header_len = dn_db->use_long ? 21 : 6;
+       if (skb_cow(skb, LL_RESERVED_SPACE(rt->u.dst.dev)+header_len))
+               goto drop;
 
        /*
         * Hop count exceeded.
         */
-       err = NET_RX_DROP;
        if (++cb->hops > 30)
                goto drop;
 
-       skb->dev = dst->dev;
+       skb->dev = rt->u.dst.dev;
 
        /*
         * If packet goes out same interface it came in on, then set
         * the Intra-Ethernet bit. This has no effect for short
         * packets, so we don't need to test for them here.
         */
-       if (cb->iif == dst->dev->ifindex)
+       cb->rt_flags &= ~DN_RT_F_IE;
+       if (rt->rt_flags | RTCF_DOREDIRECT)
                cb->rt_flags |= DN_RT_F_IE;
-       else
-               cb->rt_flags &= ~DN_RT_F_IE;
 
        return NF_HOOK(PF_DECnet, NF_DN_FORWARD, skb, dev, skb->dev, neigh->output);
 
-
-error:
-       if (net_ratelimit())
-               printk(KERN_DEBUG "dn_forward: This should not happen\n");
 drop:
        kfree_skb(skb);
-
-       return err;
+       return NET_RX_DROP;
 }
-#endif
 
 /*
  * Drop packet. This is used for endnodes and for
@@ -703,182 +751,450 @@ static int dn_rt_bug(struct sk_buff *skb)
        return NET_RX_BAD;
 }
 
-static int dn_route_output_slow(struct dst_entry **pprt, dn_address dst, dn_address src, int flags)
+static int dn_rt_set_next_hop(struct dn_route *rt, struct dn_fib_res *res)
+{
+       struct dn_fib_info *fi = res->fi;
+       struct net_device *dev = rt->u.dst.dev;
+       struct neighbour *n;
+       unsigned mss;
+
+       if (fi) {
+               if (DN_FIB_RES_GW(*res) &&
+                   DN_FIB_RES_NH(*res).nh_scope == RT_SCOPE_LINK)
+                       rt->rt_gateway = DN_FIB_RES_GW(*res);
+               memcpy(rt->u.dst.metrics, fi->fib_metrics,
+                      sizeof(rt->u.dst.metrics));
+       }
+       rt->rt_type = res->type;
+
+       if (dev != NULL && rt->u.dst.neighbour == NULL) {
+               n = __neigh_lookup_errno(&dn_neigh_table, &rt->rt_gateway, dev);
+               if (IS_ERR(n))
+                       return PTR_ERR(n);
+               rt->u.dst.neighbour = n;
+       }
+
+       if (rt->u.dst.metrics[RTAX_MTU-1] == 0 || 
+            rt->u.dst.metrics[RTAX_MTU-1] > rt->u.dst.dev->mtu)
+               rt->u.dst.metrics[RTAX_MTU-1] = rt->u.dst.dev->mtu;
+       mss = dn_mss_from_pmtu(dev, dst_pmtu(&rt->u.dst));
+       if (rt->u.dst.metrics[RTAX_ADVMSS-1] == 0 ||
+           rt->u.dst.metrics[RTAX_ADVMSS-1] > mss)
+               rt->u.dst.metrics[RTAX_ADVMSS-1] = mss;
+       return 0;
+}
+
+static inline int dn_match_addr(__u16 addr1, __u16 addr2)
 {
+       __u16 tmp = dn_ntohs(addr1) ^ dn_ntohs(addr2);
+       int match = 16;
+       while(tmp)
+               tmp >>= 1, match--;
+       return match;
+}
+
+static __u16 dnet_select_source(const struct net_device *dev, __u16 daddr, int scope)
+{
+       __u16 saddr = 0;
+       struct dn_dev *dn_db = dev->dn_ptr;
+       struct dn_ifaddr *ifa;
+       int best_match = 0;
+       int ret;
+
+       read_lock(&dev_base_lock);
+       for(ifa = dn_db->ifa_list; ifa; ifa = ifa->ifa_next) {
+               if (ifa->ifa_scope > scope)
+                       continue;
+               if (!daddr) {
+                       saddr = ifa->ifa_local;
+                       break;
+               }
+               ret = dn_match_addr(daddr, ifa->ifa_local);
+               if (ret > best_match)
+                       saddr = ifa->ifa_local;
+               if (best_match == 0)
+                       saddr = ifa->ifa_local;
+       }
+       read_unlock(&dev_base_lock);
+
+       return saddr;
+}
+
+static inline __u16 __dn_fib_res_prefsrc(struct dn_fib_res *res)
+{
+       return dnet_select_source(DN_FIB_RES_DEV(*res), DN_FIB_RES_GW(*res), res->scope);
+}
+
+static inline __u16 dn_fib_rules_map_destination(__u16 daddr, struct dn_fib_res *res)
+{
+       __u16 mask = dnet_make_mask(res->prefixlen);
+       return (daddr&~mask)|res->fi->fib_nh->nh_gw;
+}
+
+static int dn_route_output_slow(struct dst_entry **pprt, const struct flowi *oldflp, int try_hard)
+{
+       struct flowi fl = { .nl_u = { .dn_u = 
+                                     { .daddr = oldflp->fld_dst,
+                                       .saddr = oldflp->fld_src,
+                                       .scope = RT_SCOPE_UNIVERSE,
+#ifdef CONFIG_DECNET_ROUTE_FWMARK
+                                       .fwmark = oldflp->fld_fwmark
+#endif
+                                    } },
+                           .iif = loopback_dev.ifindex,
+                           .oif = oldflp->oif };
        struct dn_route *rt = NULL;
-       struct net_device *dev = NULL;
+       struct net_device *dev_out = NULL;
        struct neighbour *neigh = NULL;
-       struct dn_dev *dn_db;
        unsigned hash;
-
-#ifdef CONFIG_DECNET_ROUTER
-       struct dn_fib_key key;
-       struct dn_fib_res res;
+       unsigned flags = 0;
+       struct dn_fib_res res = { .fi = NULL, .type = RTN_UNICAST };
        int err;
+       int free_res = 0;
+       __u16 gateway = 0;
 
-       key.src = src;
-       key.dst = dst;
-       key.iif = 0;
-       key.oif = 0;
-       key.fwmark = 0;
-       key.scope = RT_SCOPE_UNIVERSE;
-
-       if ((err = dn_fib_lookup(&key, &res)) == 0) {
-               switch(res.type) {
-                       case RTN_UNICAST:
-                               /*
-                                * This method of handling multipath
-                                * routes is a hack and will change.
-                                * It works for now though.
-                                */
-                               if (res.fi->fib_nhs)
-                                       dn_fib_select_multipath(&key, &res);
-                               neigh = __neigh_lookup(&dn_neigh_table, &DN_FIB_RES_GW(res), DN_FIB_RES_DEV(res), 1);
-                               err = -ENOBUFS;
-                               if (!neigh)
-                                       break;
-                               err = 0;
-                               break;
-                       case RTN_UNREACHABLE:
-                               err = -EHOSTUNREACH;
-                               break;
-                       default:
-                               err = -EINVAL;
+       if (decnet_debug_level & 16)
+               printk(KERN_DEBUG
+                      "dn_route_output_slow: dst=%04x src=%04x mark=%d"
+                      " iif=%d oif=%d\n", oldflp->fld_dst, oldflp->fld_src,
+                       oldflp->fld_fwmark, loopback_dev.ifindex, oldflp->oif);
+
+       /* If we have an output interface, verify its a DECnet device */
+       if (oldflp->oif) {
+               dev_out = dev_get_by_index(oldflp->oif);
+               err = -ENODEV;
+               if (dev_out && dev_out->dn_ptr == NULL) {
+                       dev_put(dev_out);
+                       dev_out = NULL;
                }
-               dn_fib_res_put(&res);
-               if (err < 0)
-                       return err;
-               goto got_route;
+               if (dev_out == NULL)
+                       goto out;
        }
 
-       if (err != -ESRCH)
-               return err;
-#endif 
+       /* If we have a source address, verify that its a local address */
+       if (oldflp->fld_src) {
+               err = -EADDRNOTAVAIL;
 
-       /* Look in On-Ethernet cache first */
-       if (!(flags & MSG_TRYHARD)) {
-               if ((neigh = dn_neigh_lookup(&dn_neigh_table, &dst)) != NULL)
-                       goto got_route;
+               if (dev_out) {
+                       if (dn_dev_islocal(dev_out, oldflp->fld_src))
+                               goto source_ok;
+                       dev_put(dev_out);
+                       goto out;
+               }
+               read_lock(&dev_base_lock);
+               for(dev_out = dev_base; dev_out; dev_out = dev_out->next) {
+                       if (!dev_out->dn_ptr)
+                               continue;
+                       if (dn_dev_islocal(dev_out, oldflp->fld_src))
+                               break;
+               }
+               read_unlock(&dev_base_lock);
+               if (dev_out == NULL)
+                       goto out;
+               dev_hold(dev_out);
+source_ok:
        }
 
-       dev = dn_dev_get_default();
-       if (dev == NULL)
-               return -EINVAL;
+       /* No destination? Assume its local */
+       if (!fl.fld_dst) {
+               fl.fld_dst = fl.fld_src;
+#if 0
+               if (!fl.fld_dst)
+                       /* grab an address from loopback? */
+#endif
+               err = -EADDRNOTAVAIL;
+               if (dev_out)
+                       dev_put(dev_out);
+               if (!fl.fld_dst)
+                       goto out;
+               dev_out = &loopback_dev;
+               dev_hold(dev_out);
+               fl.oif = loopback_dev.ifindex;
+               res.type = RTN_LOCAL;
+               goto make_route;
+       }
 
-       dn_db = dev->dn_ptr;
+       if (decnet_debug_level & 16)
+               printk(KERN_DEBUG
+                      "dn_route_output_slow: initial checks complete."
+                      " dst=%o4x src=%04x oif=%d try_hard=%d\n", fl.fld_dst,
+                      fl.fld_src, fl.oif, try_hard);
 
-       /* Check to see if its one of our own local addresses */
-       if (dn_dev_islocal(dev, dst)) {
-               struct net_device *lo = &loopback_dev;
-               if (lo->dn_ptr) {
-                       neigh = __neigh_lookup(&dn_neigh_table, &dst, lo, 1);
-                       if (neigh)
-                               goto got_route;
+       /*
+        * N.B. If the kernel is compiled without router support then
+        * dn_fib_lookup() will evaluate to non-zero so this if () block
+        * will always be executed.
+        */
+       err = -ESRCH;
+       if (try_hard || (err = dn_fib_lookup(&fl, &res)) != 0) {
+               struct dn_dev *dn_db;
+               if (err != -ESRCH)
+                       goto out;
+               /*
+                * Here the fallback is basically the standard algorithm for 
+                * routing in endnodes which is described in the DECnet routing
+                * docs
+                *
+                * If we are not trying hard, look in neighbour cache.
+                * The result is tested to ensure that if a specific output
+                * device/source address was requested, then we honour that 
+                * here
+                */
+               if (!try_hard) {
+                       neigh = dn_neigh_lookup(&dn_neigh_table, &fl.fld_dst);
+                       if (neigh) {
+                               if ((oldflp->oif && 
+                                   (neigh->dev->ifindex != oldflp->oif)) ||
+                                   (oldflp->fld_src &&
+                                   (!dn_dev_islocal(neigh->dev,
+                                                     oldflp->fld_src)))) {
+                                       neigh_release(neigh);
+                                       neigh = NULL;
+                               } else {
+                                       if (dev_out)
+                                               dev_put(dev_out);
+                                       if (dn_dev_islocal(neigh->dev, fl.fld_dst)) {
+                                               dev_out = &loopback_dev;
+                                               res.type = RTN_LOCAL;
+                                       } else {
+                                               dev_out = neigh->dev;
+                                       }
+                                       dev_hold(dev_out);
+                                       goto select_source;
+                               }
+                       }
                }
-               if (net_ratelimit())
-                       printk("dn_route_output_slow: Dest is local interface address, but loopback device is not up\n");
-               dev_put(dev);
-               return -EINVAL;
-       }
 
-       /* Try default router */
-       if ((neigh = neigh_clone(dn_db->router)) != NULL)
-               goto got_route;
+               /* Not there? Perhaps its a local address */
+               if (dev_out == NULL)
+                       dev_out = dn_dev_get_default();
+               err = -ENODEV;
+               if (dev_out == NULL)
+                       goto out;
+               dn_db = dev_out->dn_ptr;
+               /* Possible improvement - check all devices for local addr */
+               if (dn_dev_islocal(dev_out, fl.fld_dst)) {
+                       dev_put(dev_out);
+                       dev_out = &loopback_dev;
+                       dev_hold(dev_out);
+                       res.type = RTN_LOCAL;
+                       goto select_source;
+               }
+               /* Not local either.... try sending it to the default router */
+               neigh = neigh_clone(dn_db->router);
+               BUG_ON(neigh && neigh->dev != dev_out);
+
+               /* Ok then, we assume its directly connected and move on */
+select_source:
+               if (neigh)
+                       gateway = ((struct dn_neigh *)neigh)->addr;
+               if (gateway == 0)
+                       gateway = fl.fld_dst;
+               if (fl.fld_src == 0) {
+                       fl.fld_src = dnet_select_source(dev_out, gateway,
+                                                        res.type == RTN_LOCAL ?
+                                                        RT_SCOPE_HOST : 
+                                                        RT_SCOPE_LINK);
+                       if (fl.fld_src == 0 && res.type != RTN_LOCAL)
+                               goto e_addr;
+               }
+               fl.oif = dev_out->ifindex;
+               goto make_route;
+       }
+       free_res = 1;
+
+       if (res.type == RTN_NAT)
+               goto e_inval;
+
+       if (res.type == RTN_LOCAL) {
+               if (!fl.fld_src)
+                       fl.fld_src = fl.fld_dst;
+               if (dev_out)
+                       dev_put(dev_out);
+               dev_out = &loopback_dev;
+               dev_hold(dev_out);
+               fl.oif = dev_out->ifindex;
+               if (res.fi)
+                       dn_fib_info_put(res.fi);
+               res.fi = NULL;
+               goto make_route;
+       }
 
-       /* Send to default device (and hope for the best) if above fail */
-       if ((neigh = __neigh_lookup(&dn_neigh_table, &dst, dev, 1)) != NULL)
-               goto got_route;
+       if (res.fi->fib_nhs > 1 && fl.oif == 0)
+               dn_fib_select_multipath(&fl, &res);
 
-       dev_put(dev);
-       return -EINVAL;
+       /* 
+        * We could add some logic to deal with default routes here and
+        * get rid of some of the special casing above.
+        */
 
-got_route:
-       if (dev)
-               dev_put(dev);
+       if (!fl.fld_src)
+               fl.fld_src = DN_FIB_RES_PREFSRC(res);
+       
+       if (dev_out)
+               dev_put(dev_out);
+       dev_out = DN_FIB_RES_DEV(res);
+       dev_hold(dev_out);
+       fl.oif = dev_out->ifindex;
+       gateway = DN_FIB_RES_GW(res);
+
+make_route:
+       if (dev_out->flags & IFF_LOOPBACK)
+               flags |= RTCF_LOCAL;
+
+       rt = dst_alloc(&dn_dst_ops);
+       if (rt == NULL)
+               goto e_nobufs;
+
+       atomic_set(&rt->u.dst.__refcnt, 1);
+       rt->u.dst.flags   = DST_HOST;
+
+       rt->fl.fld_src    = oldflp->fld_src;
+       rt->fl.fld_dst    = oldflp->fld_dst;
+       rt->fl.oif        = oldflp->oif;
+       rt->fl.iif        = 0;
+#ifdef CONFIG_DECNET_ROUTE_FWMARK
+       rt->fl.fld_fwmark = flp->fld_fwmark;
+#endif
 
-       if ((rt = dst_alloc(&dn_dst_ops)) == NULL) {
-               neigh_release(neigh);
-               return -EINVAL;
-       }
+       rt->rt_saddr      = fl.fld_src;
+       rt->rt_daddr      = fl.fld_dst;
+       rt->rt_gateway    = gateway ? gateway : fl.fld_dst;
 
-       dn_db = (struct dn_dev *)neigh->dev->dn_ptr;
-       
-       rt->key.saddr  = src;
-       rt->rt_saddr   = src;
-       rt->key.daddr  = dst;
-       rt->rt_daddr   = dst;
-       rt->key.oif    = neigh ? neigh->dev->ifindex : -1;
-       rt->key.iif    = 0;
-       rt->key.fwmark = 0;
+       rt->rt_dst_map    = fl.fld_dst;
+       rt->rt_src_map    = fl.fld_src;
 
+       rt->u.dst.dev = dev_out;
+       dev_hold(dev_out);
        rt->u.dst.neighbour = neigh;
-       rt->u.dst.dev = neigh ? neigh->dev : NULL;
-       if (rt->u.dst.dev)
-               dev_hold(rt->u.dst.dev);
+       neigh = NULL;
+
        rt->u.dst.lastuse = jiffies;
-       rt->u.dst.output = dn_output;
-       rt->u.dst.input  = dn_rt_bug;
+       rt->u.dst.output  = dn_output;
+       rt->u.dst.input   = dn_rt_bug;
+       rt->rt_flags      = flags;
+       if (flags & RTCF_LOCAL)
+               rt->u.dst.output = dn_nsp_rx;
 
-       if (neigh->dev->flags & IFF_LOOPBACK)
-               rt->u.dst.input = dn_nsp_rx;
+       if (dn_rt_set_next_hop(rt, &res))
+               goto e_neighbour;
 
-       hash = dn_hash(rt->key.saddr, rt->key.daddr);
-       dn_insert_route(rt, hash);
-       *pprt = &rt->u.dst;
+       hash = dn_hash(rt->fl.fld_src, rt->fl.fld_dst);
+       dn_insert_route(rt, hash, (struct dn_route **)pprt);
+       err = 0;
 
-       return 0;
+done:
+       if (neigh)
+               neigh_release(neigh);
+       if (free_res)
+               dn_fib_res_put(&res);
+       if (dev_out)
+               dev_put(dev_out);
+out:
+       return err;
+
+e_addr:
+        err = -EADDRNOTAVAIL;
+        goto done;
+e_inval:
+       err = -EINVAL;
+       goto done;
+e_nobufs:
+       err = -ENOBUFS;
+       goto done;
+e_neighbour:
+       dst_free(&rt->u.dst);
+       goto e_nobufs;
 }
 
-int dn_route_output(struct dst_entry **pprt, dn_address dst, dn_address src, int flags)
+
+/*
+ * N.B. The flags may be moved into the flowi at some future stage.
+ */
+static int __dn_route_output_key(struct dst_entry **pprt, const struct flowi *flp, int flags)
 {
-       unsigned hash = dn_hash(src, dst);
+       unsigned hash = dn_hash(flp->fld_src, flp->fld_dst);
        struct dn_route *rt = NULL;
 
        if (!(flags & MSG_TRYHARD)) {
-               read_lock_bh(&dn_rt_hash_table[hash].lock);
+               rcu_read_lock();
                for(rt = dn_rt_hash_table[hash].chain; rt; rt = rt->u.rt_next) {
-                       if ((dst == rt->key.daddr) &&
-                                       (src == rt->key.saddr) &&
-                                       (rt->key.iif == 0) &&
-                                       (rt->key.oif != 0)) {
+                       read_barrier_depends();
+                       if ((flp->fld_dst == rt->fl.fld_dst) &&
+                           (flp->fld_src == rt->fl.fld_src) &&
+#ifdef CONFIG_DECNET_ROUTE_FWMARK
+                           (flp->fld_fwmark == rt->fl.fld_fwmark) &&
+#endif
+                           (rt->fl.iif == 0) &&
+                           (rt->fl.oif == flp->oif)) {
                                rt->u.dst.lastuse = jiffies;
                                dst_hold(&rt->u.dst);
                                rt->u.dst.__use++;
-                               read_unlock_bh(&dn_rt_hash_table[hash].lock);
+                               rcu_read_unlock();
                                *pprt = &rt->u.dst;
                                return 0;
                        }
                }
-               read_unlock_bh(&dn_rt_hash_table[hash].lock);
+               rcu_read_unlock();
        }
 
-       return dn_route_output_slow(pprt, dst, src, flags);
+       return dn_route_output_slow(pprt, flp, flags);
+}
+
+static int dn_route_output_key(struct dst_entry **pprt, struct flowi *flp, int flags)
+{
+       int err;
+
+       err = __dn_route_output_key(pprt, flp, flags);
+       if (err == 0 && flp->proto) {
+               err = xfrm_lookup(pprt, flp, NULL, 0);
+       }
+       return err;
+}
+
+int dn_route_output_sock(struct dst_entry **pprt, struct flowi *fl, struct sock *sk, int flags)
+{
+       int err;
+
+       err = __dn_route_output_key(pprt, fl, flags & MSG_TRYHARD);
+       if (err == 0 && fl->proto) {
+               err = xfrm_lookup(pprt, fl, sk, !(flags & MSG_DONTWAIT));
+       }
+       return err;
 }
 
 static int dn_route_input_slow(struct sk_buff *skb)
 {
        struct dn_route *rt = NULL;
        struct dn_skb_cb *cb = DN_SKB_CB(skb);
-       struct net_device *dev = skb->dev;
+       struct net_device *in_dev = skb->dev;
+       struct net_device *out_dev = NULL;
        struct dn_dev *dn_db;
        struct neighbour *neigh = NULL;
-       int (*dnrt_input)(struct sk_buff *skb);
-       int (*dnrt_output)(struct sk_buff *skb);
-       u32 fwmark = 0;
        unsigned hash;
-       dn_address saddr = cb->src;
-       dn_address daddr = cb->dst;
-#ifdef CONFIG_DECNET_ROUTER
-       struct dn_fib_key key;
-       struct dn_fib_res res;
-       int err;
+       int flags = 0;
+       __u16 gateway = 0;
+       struct flowi fl = { .nl_u = { .dn_u = 
+                                    { .daddr = cb->dst,
+                                      .saddr = cb->src,
+                                      .scope = RT_SCOPE_UNIVERSE,
+#ifdef CONFIG_DECNET_ROUTE_FWMARK
+                                      .fwmark = skb->fwmark
 #endif
+                                   } },
+                           .iif = skb->dev->ifindex };
+       struct dn_fib_res res = { .fi = NULL, .type = RTN_UNREACHABLE };
+       int err = -EINVAL;
+       int free_res = 0;
 
-       if (dev == NULL)
-               return -EINVAL;
+       dev_hold(in_dev);
 
-       if ((dn_db = dev->dn_ptr) == NULL)
-               return -EINVAL;
+       if ((dn_db = in_dev->dn_ptr) == NULL)
+               goto out;
+
+       /* Zero source addresses are not allowed */
+       if (fl.fld_src == 0)
+               goto out;
 
        /*
         * In this case we've just received a packet from a source
@@ -887,122 +1203,183 @@ static int dn_route_input_slow(struct sk_buff *skb)
         * other nasties. Loopback packets already have the dst attached
         * so this only affects packets which have originated elsewhere.
         */
-       if (dn_dev_islocal(dev, cb->src))
-               return -ENOTUNIQ;
-
-       /*
-        * Default is to create a drop everything entry
-        */
-       dnrt_input  = dn_blackhole;
-       dnrt_output = dn_rt_bug;
-
-       /*
-        * Is the destination us ?
-        */
-       if (!dn_dev_islocal(dev, cb->dst))
-               goto non_local_input;
+       err  = -ENOTUNIQ;
+       if (dn_dev_islocal(in_dev, cb->src))
+               goto out;
 
-       /*
-        * Local input... find source of skb
-        */
-       dnrt_input  = dn_nsp_rx;
-       dnrt_output = dn_output;
-       saddr = cb->dst;
-       daddr = cb->src;
-
-       if ((neigh = neigh_lookup(&dn_neigh_table, &cb->src, dev)) != NULL)
-               goto add_entry;
-
-       if (dn_db->router && ((neigh = neigh_clone(dn_db->router)) != NULL))
-               goto add_entry;
-
-       neigh = neigh_create(&dn_neigh_table, &cb->src, dev);
-       if (!IS_ERR(neigh)) {
-               if (dev->type == ARPHRD_ETHER)
-                       memcpy(neigh->ha, skb->mac.ethernet->h_source, ETH_ALEN);
-               goto add_entry;
+       err = dn_fib_lookup(&fl, &res);
+       if (err) {
+               if (err != -ESRCH)
+                       goto out;
+               /*
+                * Is the destination us ?
+                */
+               if (!dn_dev_islocal(in_dev, cb->dst))
+                       goto e_inval;
+
+               res.type = RTN_LOCAL;
+               flags |= RTCF_DIRECTSRC;
+       } else {
+               __u16 src_map = fl.fld_src;
+               free_res = 1;
+
+               out_dev = DN_FIB_RES_DEV(res);
+               if (out_dev == NULL) {
+                       if (net_ratelimit())
+                               printk(KERN_CRIT "Bug in dn_route_input_slow() "
+                                                "No output device\n");
+                       goto e_inval;
+               }
+               dev_hold(out_dev);
+
+               if (res.r)
+                       src_map = dn_fib_rules_policy(fl.fld_src, &res, &flags);
+
+               gateway = DN_FIB_RES_GW(res);
+               if (res.type == RTN_NAT) {
+                       fl.fld_dst = dn_fib_rules_map_destination(fl.fld_dst, &res);
+                       dn_fib_res_put(&res);
+                       free_res = 0;
+                       if (dn_fib_lookup(&fl, &res))
+                               goto e_inval;
+                       free_res = 1;
+                       if (res.type != RTN_UNICAST)
+                               goto e_inval;
+                       flags |= RTCF_DNAT;
+                       gateway = fl.fld_dst;
+               }
+               fl.fld_src = src_map;
        }
 
-       return PTR_ERR(neigh);
-
-non_local_input:
-
-#ifdef CONFIG_DECNET_ROUTER
-       /*
-        * Destination is another node... find next hop in
-        * routing table here.
-        */
-
-       key.src = cb->src;
-       key.dst = cb->dst;
-       key.iif = dev->ifindex;
-       key.oif = 0;
-       key.scope = RT_SCOPE_UNIVERSE;
-
-#ifdef CONFIG_DECNET_ROUTE_FWMARK
-       key.fwmark = skb->nfmark;
-#else
-       key.fwmark = 0;
-#endif
+       switch(res.type) {
+       case RTN_UNICAST:
+               /*
+                * Forwarding check here, we only check for forwarding
+                * being turned off, if you want to only forward intra
+                * area, its up to you to set the routing tables up
+                * correctly.
+                */
+               if (dn_db->parms.forwarding == 0)
+                       goto e_inval;
+
+               if (res.fi->fib_nhs > 1 && fl.oif == 0)
+                       dn_fib_select_multipath(&fl, &res);
+
+               /* 
+                * Check for out_dev == in_dev. We use the RTCF_DOREDIRECT
+                * flag as a hint to set the intra-ethernet bit when
+                * forwarding. If we've got NAT in operation, we don't do
+                * this optimisation.
+                */
+               if (out_dev == in_dev && !(flags & RTCF_NAT))
+                       flags |= RTCF_DOREDIRECT;
+
+       case RTN_BLACKHOLE:
+       case RTN_UNREACHABLE:
+               break;
+       case RTN_LOCAL:
+               flags |= RTCF_LOCAL;
+               fl.fld_src = cb->dst;
+               fl.fld_dst = cb->src;
+
+               /* Routing tables gave us a gateway */
+               if (gateway)
+                       goto make_route;
+
+               /* Packet was intra-ethernet, so we know its on-link */
+               if (cb->rt_flags | DN_RT_F_IE) {
+                       gateway = cb->src;
+                       flags |= RTCF_DIRECTSRC;
+                       goto make_route;
+               }
 
-       if ((err = dn_fib_lookup(&key, &res)) == 0) {
-               switch(res.type) {
-                       case RTN_UNICAST:
-                               if (res.fi->fib_nhs)
-                                       dn_fib_select_multipath(&key, &res);
-                               neigh = __neigh_lookup(&dn_neigh_table, &DN_FIB_RES_GW(res), DN_FIB_RES_DEV(res), 1);
-                               err = -ENOBUFS;
-                               if (!neigh)
-                                       break;
-                               err = 0;
-                               dnrt_input = dn_forward;
-                               fwmark = key.fwmark;
-                               break;
-                       case RTN_UNREACHABLE:
-                               dnrt_input = dn_blackhole;
-                               fwmark = key.fwmark;
-                               break;
-                       default:
-                               err = -EINVAL;
+               /* Use the default router if there is one */
+               neigh = neigh_clone(dn_db->router);
+               if (neigh) {
+                       gateway = ((struct dn_neigh *)neigh)->addr;
+                       goto make_route;
                }
-               dn_fib_res_put(&res);
-               if (err < 0)
-                       return err;
-               goto add_entry;
-       }
 
-       return err;
+               /* Close eyes and pray */
+               gateway = cb->src;
+               flags |= RTCF_DIRECTSRC;
+               goto make_route;
+       default:
+               goto e_inval;
+       }
 
-#endif /* CONFIG_DECNET_ROUTER */
+make_route:
+       rt = dst_alloc(&dn_dst_ops);
+       if (rt == NULL)
+               goto e_nobufs;
+
+       rt->rt_saddr      = fl.fld_src;
+       rt->rt_daddr      = fl.fld_dst;
+       rt->rt_gateway    = fl.fld_dst;
+       if (gateway)
+               rt->rt_gateway = gateway;
+       rt->rt_dst_map    = fl.fld_dst;
+       rt->rt_src_map    = fl.fld_src;
+
+       rt->fl.fld_src    = cb->src;
+       rt->fl.fld_dst    = cb->dst;
+       rt->fl.oif        = 0;
+       rt->fl.iif        = in_dev->ifindex;
+       rt->fl.fld_fwmark = fl.fld_fwmark;
+
+       rt->u.dst.flags = DST_HOST;
+       rt->u.dst.neighbour = neigh;
+       rt->u.dst.dev = out_dev;
+       rt->u.dst.lastuse = jiffies;
+       rt->u.dst.output = dn_rt_bug;
+       switch(res.type) {
+               case RTN_UNICAST:
+                       rt->u.dst.input = dn_forward;
+                       break;
+               case RTN_LOCAL:
+                       rt->u.dst.output = dn_output;
+                       rt->u.dst.input = dn_nsp_rx;
+                       rt->u.dst.dev = in_dev;
+                       flags |= RTCF_LOCAL;
+                       break;
+               default:
+               case RTN_UNREACHABLE:
+               case RTN_BLACKHOLE:
+                       rt->u.dst.input = dn_blackhole;
+       }
+       rt->rt_flags = flags;
+       if (rt->u.dst.dev)
+               dev_hold(rt->u.dst.dev);
 
-add_entry:
+       if (dn_rt_set_next_hop(rt, &res))
+               goto e_neighbour;
 
-       if ((rt = dst_alloc(&dn_dst_ops)) == NULL) {
-                neigh_release(neigh);
-                return -EINVAL;
-        }
+       hash = dn_hash(rt->fl.fld_src, rt->fl.fld_dst);
+       dn_insert_route(rt, hash, (struct dn_route **)&skb->dst);
+       err = 0;
 
-       rt->key.saddr  = cb->src;
-       rt->rt_saddr   = saddr;
-       rt->key.daddr  = cb->dst;
-       rt->rt_daddr   = daddr;
-       rt->key.oif    = 0;
-       rt->key.iif    = dev->ifindex;
-       rt->key.fwmark = fwmark;
+done:
+       if (neigh)
+               neigh_release(neigh);
+       if (free_res)
+               dn_fib_res_put(&res);
+       dev_put(in_dev);
+       if (out_dev)
+               dev_put(out_dev);
+out:
+       return err;
 
-       rt->u.dst.neighbour = neigh;
-       rt->u.dst.dev = neigh ? neigh->dev : NULL;
-       if (rt->u.dst.dev)
-               dev_hold(rt->u.dst.dev);
-       rt->u.dst.lastuse = jiffies;
-       rt->u.dst.output = dnrt_output;
-       rt->u.dst.input = dnrt_input;
+e_inval:
+       err = -EINVAL;
+       goto done;
 
-       hash = dn_hash(rt->key.saddr, rt->key.daddr);
-       dn_insert_route(rt, hash);
-       skb->dst = (struct dst_entry *)rt;
+e_nobufs:
+       err = -ENOBUFS;
+       goto done;
 
-       return 0;
+e_neighbour:
+       dst_free(&rt->u.dst);
+       goto done;
 }
 
 int dn_route_input(struct sk_buff *skb)
@@ -1014,24 +1391,25 @@ int dn_route_input(struct sk_buff *skb)
        if (skb->dst)
                return 0;
 
-       read_lock(&dn_rt_hash_table[hash].lock);
+       rcu_read_lock();
        for(rt = dn_rt_hash_table[hash].chain; rt != NULL; rt = rt->u.rt_next) {
-               if ((rt->key.saddr == cb->src) &&
-                               (rt->key.daddr == cb->dst) &&
-                               (rt->key.oif == 0) &&
+               read_barrier_depends();
+               if ((rt->fl.fld_src == cb->src) &&
+                   (rt->fl.fld_dst == cb->dst) &&
+                   (rt->fl.oif == 0) &&
 #ifdef CONFIG_DECNET_ROUTE_FWMARK
-                               (rt->key.fwmark == skb->nfmark) &&
+                   (rt->fl.fld_fwmark == skb->nfmark) &&
 #endif
-                               (rt->key.iif == cb->iif)) {
+                   (rt->fl.iif == cb->iif)) {
                        rt->u.dst.lastuse = jiffies;
                        dst_hold(&rt->u.dst);
                        rt->u.dst.__use++;
-                       read_unlock(&dn_rt_hash_table[hash].lock);
+                       rcu_read_unlock();
                        skb->dst = (struct dst_entry *)rt;
                        return 0;
                }
        }
-       read_unlock(&dn_rt_hash_table[hash].lock);
+       rcu_read_unlock();
 
        return dn_route_input_slow(skb);
 }
@@ -1042,29 +1420,51 @@ static int dn_rt_fill_info(struct sk_buff *skb, u32 pid, u32 seq, int event, int
        struct rtmsg *r;
        struct nlmsghdr *nlh;
        unsigned char *b = skb->tail;
+       struct rta_cacheinfo ci;
 
        nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(*r));
        r = NLMSG_DATA(nlh);
-       nlh->nlmsg_flags = nowait ? NLM_F_MULTI : 0;
+       nlh->nlmsg_flags = (nowait && pid) ? NLM_F_MULTI : 0;
        r->rtm_family = AF_DECnet;
        r->rtm_dst_len = 16;
-       r->rtm_src_len = 16;
+       r->rtm_src_len = 0;
        r->rtm_tos = 0;
-       r->rtm_table = 0;
-       r->rtm_type = 0;
-       r->rtm_flags = 0;
+       r->rtm_table = RT_TABLE_MAIN;
+       r->rtm_type = rt->rt_type;
+       r->rtm_flags = (rt->rt_flags & ~0xFFFF) | RTM_F_CLONED;
        r->rtm_scope = RT_SCOPE_UNIVERSE;
        r->rtm_protocol = RTPROT_UNSPEC;
+       if (rt->rt_flags & RTCF_NOTIFY)
+               r->rtm_flags |= RTM_F_NOTIFY;
        RTA_PUT(skb, RTA_DST, 2, &rt->rt_daddr);
-       RTA_PUT(skb, RTA_SRC, 2, &rt->rt_saddr);
+       if (rt->fl.fld_src) {
+               r->rtm_src_len = 16;
+               RTA_PUT(skb, RTA_SRC, 2, &rt->fl.fld_src);
+       }
        if (rt->u.dst.dev)
                RTA_PUT(skb, RTA_OIF, sizeof(int), &rt->u.dst.dev->ifindex);
-       if (dst_metric(&rt->u.dst, RTAX_WINDOW))
-               RTA_PUT(skb, RTAX_WINDOW, sizeof(unsigned),
-                       &rt->u.dst.metrics[RTAX_WINDOW - 1]);
-       if (dst_metric(&rt->u.dst, RTAX_RTT))
-               RTA_PUT(skb, RTAX_RTT, sizeof(unsigned),
-                       &rt->u.dst.metrics[RTAX_RTT]);
+       /*
+        * Note to self - change this if input routes reverse direction when
+        * they deal only with inputs and not with replies like they do
+        * currently.
+        */
+       RTA_PUT(skb, RTA_PREFSRC, 2, &rt->rt_saddr);
+       if (rt->rt_daddr != rt->rt_gateway)
+               RTA_PUT(skb, RTA_GATEWAY, 2, &rt->rt_gateway);
+       if (rtnetlink_put_metrics(skb, rt->u.dst.metrics) < 0)
+               goto rtattr_failure;
+       ci.rta_lastuse = jiffies - rt->u.dst.lastuse;
+       ci.rta_used     = rt->u.dst.__use;
+       ci.rta_clntref  = atomic_read(&rt->u.dst.__refcnt);
+       if (rt->u.dst.expires)
+               ci.rta_expires = rt->u.dst.expires - jiffies;
+       else
+               ci.rta_expires = 0;
+       ci.rta_error    = rt->u.dst.error;
+       ci.rta_id       = ci.rta_ts = ci.rta_tsage = 0;
+       RTA_PUT(skb, RTA_CACHEINFO, sizeof(ci), &ci);
+       if (rt->fl.iif)
+               RTA_PUT(skb, RTA_IIF, sizeof(int), &rt->fl.iif);
 
        nlh->nlmsg_len = skb->tail - b;
        return skb->len;
@@ -1081,13 +1481,14 @@ rtattr_failure:
 int dn_cache_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, void *arg)
 {
        struct rtattr **rta = arg;
+       struct rtmsg *rtm = NLMSG_DATA(nlh);
        struct dn_route *rt = NULL;
        struct dn_skb_cb *cb;
-       dn_address dst = 0;
-       dn_address src = 0;
-       int iif = 0;
        int err;
        struct sk_buff *skb;
+       struct flowi fl;
+
+       memset(&fl, 0, sizeof(fl));
 
        skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
        if (skb == NULL)
@@ -1096,15 +1497,15 @@ int dn_cache_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, void *arg)
        cb = DN_SKB_CB(skb);
 
        if (rta[RTA_SRC-1])
-               memcpy(&src, RTA_DATA(rta[RTA_SRC-1]), 2);
+               memcpy(&fl.fld_src, RTA_DATA(rta[RTA_SRC-1]), 2);
        if (rta[RTA_DST-1])
-               memcpy(&dst, RTA_DATA(rta[RTA_DST-1]), 2);
+               memcpy(&fl.fld_dst, RTA_DATA(rta[RTA_DST-1]), 2);
        if (rta[RTA_IIF-1])
-               memcpy(&iif, RTA_DATA(rta[RTA_IIF-1]), sizeof(int));
+               memcpy(&fl.iif, RTA_DATA(rta[RTA_IIF-1]), sizeof(int));
 
-       if (iif) {
+       if (fl.iif) {
                struct net_device *dev;
-               if ((dev = dev_get_by_index(iif)) == NULL) {
+               if ((dev = dev_get_by_index(fl.iif)) == NULL) {
                        kfree_skb(skb);
                        return -ENODEV;
                }
@@ -1115,25 +1516,31 @@ int dn_cache_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, void *arg)
                }
                skb->protocol = __constant_htons(ETH_P_DNA_RT);
                skb->dev = dev;
-               cb->src = src;
-               cb->dst = dst;
+               cb->src = fl.fld_src;
+               cb->dst = fl.fld_dst;
                local_bh_disable();
                err = dn_route_input(skb);
                local_bh_enable();
                memset(cb, 0, sizeof(struct dn_skb_cb));
                rt = (struct dn_route *)skb->dst;
+               if (!err && -rt->u.dst.error)
+                       err = rt->u.dst.error;
        } else {
-               err = dn_route_output((struct dst_entry **)&rt, dst, src, 0);
+               int oif = 0;
+               if (rta[RTA_OIF - 1])
+                       memcpy(&oif, RTA_DATA(rta[RTA_OIF - 1]), sizeof(int));
+               fl.oif = oif;
+               err = dn_route_output_key((struct dst_entry **)&rt, &fl, 0);
        }
 
-       if (!err && rt->u.dst.error)
-               err = rt->u.dst.error;
        if (skb->dev)
                dev_put(skb->dev);
        skb->dev = NULL;
        if (err)
                goto out_free;
        skb->dst = &rt->u.dst;
+       if (rtm->rtm_flags & RTM_F_NOTIFY)
+               rt->rt_flags |= RTCF_NOTIFY;
 
        NETLINK_CB(skb).dst_pid = NETLINK_CB(in_skb).pid;
 
@@ -1177,20 +1584,21 @@ int dn_cache_dump(struct sk_buff *skb, struct netlink_callback *cb)
                        continue;
                if (h > s_h)
                        s_idx = 0;
-               read_lock_bh(&dn_rt_hash_table[h].lock);
+               rcu_read_lock();
                for(rt = dn_rt_hash_table[h].chain, idx = 0; rt; rt = rt->u.rt_next, idx++) {
+                       read_barrier_depends();
                        if (idx < s_idx)
                                continue;
                        skb->dst = dst_clone(&rt->u.dst);
                        if (dn_rt_fill_info(skb, NETLINK_CB(cb->skb).pid,
                                        cb->nlh->nlmsg_seq, RTM_NEWROUTE, 1) <= 0) {
                                dst_release(xchg(&skb->dst, NULL));
-                               read_unlock_bh(&dn_rt_hash_table[h].lock);
+                               rcu_read_unlock();
                                goto done;
                        }
                        dst_release(xchg(&skb->dst, NULL));
                }
-               read_unlock_bh(&dn_rt_hash_table[h].lock);
+               rcu_read_unlock();
        }
 
 done:
@@ -1211,9 +1619,10 @@ static int decnet_cache_get_info(char *buffer, char **start, off_t offset, int l
        char buf1[DN_ASCBUF_LEN], buf2[DN_ASCBUF_LEN];
 
        for(i = 0; i <= dn_rt_hash_mask; i++) {
-               read_lock_bh(&dn_rt_hash_table[i].lock);
+               rcu_read_lock();
                rt = dn_rt_hash_table[i].chain;
                for(; rt != NULL; rt = rt->u.rt_next) {
+                       read_barrier_depends();
                        len += sprintf(buffer + len, "%-8s %-7s %-7s %04d %04d %04d\n",
                                        rt->u.dst.dev ? rt->u.dst.dev->name : "*",
                                        dn_addr2asc(dn_ntohs(rt->rt_daddr), buf1),
@@ -1234,7 +1643,7 @@ static int decnet_cache_get_info(char *buffer, char **start, off_t offset, int l
                        if (pos > offset + length)
                                break;
                }
-               read_unlock_bh(&dn_rt_hash_table[i].lock);
+               rcu_read_unlock();
                if (pos > offset + length)
                        break;
        }
@@ -1298,7 +1707,7 @@ void __init dn_route_init(void)
 
        dn_rt_hash_mask--;
         for(i = 0; i <= dn_rt_hash_mask; i++) {
-                dn_rt_hash_table[i].lock = RW_LOCK_UNLOCKED;
+                dn_rt_hash_table[i].lock = SPIN_LOCK_UNLOCKED;
                 dn_rt_hash_table[i].chain = NULL;
         }
 
index d4c8db1d416282d797e9c96f6a4b1a3af90c8a71..656ac0f88974fdf0def6e0c89604b71e933874a7 100644 (file)
 #include <linux/netdevice.h>
 #include <linux/timer.h>
 #include <linux/spinlock.h>
+#include <linux/in_route.h>
 #include <asm/atomic.h>
 #include <asm/uaccess.h>
 #include <net/neighbour.h>
 #include <net/dst.h>
+#include <net/flow.h>
 #include <net/dn.h>
 #include <net/dn_fib.h>
 #include <net/dn_neigh.h>
@@ -48,6 +50,7 @@ struct dn_fib_rule
        dn_address              r_srcmask;
        dn_address              r_dst;
        dn_address              r_dstmask;
+       dn_address              r_srcmap;
        u8                      r_flags;
 #ifdef CONFIG_DECNET_ROUTE_FWMARK
        u32                     r_fwmark;
@@ -60,7 +63,7 @@ struct dn_fib_rule
 static struct dn_fib_rule default_rule = {
        .r_clntref =            ATOMIC_INIT(2),
        .r_preference =         0x7fff,
-       .r_table =              DN_DEFAULT_TABLE,
+       .r_table =              RT_TABLE_MAIN,
        .r_action =             RTN_UNICAST
 };
 
@@ -150,6 +153,8 @@ int dn_fib_rtm_newrule(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
                memcpy(&new_r->r_src, RTA_DATA(rta[RTA_SRC-1]), 2);
        if (rta[RTA_DST-1])
                memcpy(&new_r->r_dst, RTA_DATA(rta[RTA_DST-1]), 2);
+       if (rta[RTA_GATEWAY-1])
+               memcpy(&new_r->r_srcmap, RTA_DATA(rta[RTA_GATEWAY-1]), 2);
        new_r->r_src_len = rtm->rtm_src_len;
        new_r->r_dst_len = rtm->rtm_dst_len;
        new_r->r_srcmask = dnet_make_mask(rtm->rtm_src_len);
@@ -198,12 +203,12 @@ int dn_fib_rtm_newrule(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
 }
 
 
-int dn_fib_lookup(struct dn_fib_key *key, struct dn_fib_res *res)
+int dn_fib_lookup(const struct flowi *flp, struct dn_fib_res *res)
 {
        struct dn_fib_rule *r, *policy;
        struct dn_fib_table *tb;
-       dn_address saddr = key->src;
-       dn_address daddr = key->dst;
+       dn_address saddr = flp->fld_src;
+       dn_address daddr = flp->fld_dst;
        int err;
 
        read_lock(&dn_fib_rules_lock);
@@ -211,13 +216,14 @@ int dn_fib_lookup(struct dn_fib_key *key, struct dn_fib_res *res)
                if (((saddr^r->r_src) & r->r_srcmask) ||
                    ((daddr^r->r_dst) & r->r_dstmask) ||
 #ifdef CONFIG_DECNET_ROUTE_FWMARK
-                   (r->r_fwmark && r->r_fwmark != key->fwmark) ||
+                   (r->r_fwmark && r->r_fwmark != flp->fld_fwmark) ||
 #endif
-                   (r->r_ifindex && r->r_ifindex != key->iif))
+                   (r->r_ifindex && r->r_ifindex != flp->iif))
                        continue;
 
                switch(r->r_action) {
                        case RTN_UNICAST:
+                       case RTN_NAT:
                                policy = r;
                                break;
                        case RTN_UNREACHABLE:
@@ -234,7 +240,7 @@ int dn_fib_lookup(struct dn_fib_key *key, struct dn_fib_res *res)
 
                if ((tb = dn_fib_get_table(r->r_table, 0)) == NULL)
                        continue;
-               err = tb->lookup(tb, key, res);
+               err = tb->lookup(tb, flp, res);
                if (err == 0) {
                        res->r = policy;
                        if (policy)
@@ -252,6 +258,42 @@ int dn_fib_lookup(struct dn_fib_key *key, struct dn_fib_res *res)
        return -ESRCH;
 }
 
+unsigned dnet_addr_type(__u16 addr)
+{
+       struct flowi fl = { .nl_u = { .dn_u = { .daddr = addr } } };
+       struct dn_fib_res res;
+       unsigned ret = RTN_UNICAST;
+       struct dn_fib_table *tb = dn_fib_tables[RT_TABLE_LOCAL];
+
+       res.r = NULL;
+
+       if (tb) {
+               if (!tb->lookup(tb, &fl, &res)) {
+                       ret = res.type;
+                       dn_fib_res_put(&res);
+               }
+       }
+       return ret;
+}
+
+__u16 dn_fib_rules_policy(__u16 saddr, struct dn_fib_res *res, unsigned *flags)
+{
+       struct dn_fib_rule *r = res->r;
+
+       if (r->r_action == RTN_NAT) {
+               int addrtype = dnet_addr_type(r->r_srcmap);
+
+               if (addrtype == RTN_NAT) {
+                       saddr = (saddr&~r->r_srcmask)|r->r_srcmap;
+                       *flags |= RTCF_SNAT;
+               } else if (addrtype == RTN_LOCAL || r->r_srcmap == 0) {
+                       saddr = r->r_srcmap;
+                       *flags |= RTCF_MASQ;
+               }
+       }
+       return saddr;
+}
+
 static void dn_fib_rules_detach(struct net_device *dev)
 {
        struct dn_fib_rule *r;
@@ -330,6 +372,8 @@ static int dn_fib_fill_rule(struct sk_buff *skb, struct dn_fib_rule *r, struct n
                RTA_PUT(skb, RTA_IIF, IFNAMSIZ, &r->r_ifname);
        if (r->r_preference)
                RTA_PUT(skb, RTA_PRIORITY, 4, &r->r_preference);
+       if (r->r_srcmap)
+               RTA_PUT(skb, RTA_GATEWAY, 2, &r->r_srcmap);
        nlh->nlmsg_len = skb->tail - b;
        return skb->len;
 
index ea24291b85b857cddd5710b129cc1b11971d7fc0..77766be08869cd962d810e3f8dfbea2a35518c5d 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/route.h> /* RTF_xxx */
 #include <net/neighbour.h>
 #include <net/dst.h>
+#include <net/flow.h>
 #include <net/dn.h>
 #include <net/dn_route.h>
 #include <net/dn_fib.h>
@@ -45,7 +46,7 @@ struct dn_zone
        u32                     dz_hashmask;
 #define DZ_HASHMASK(dz)        ((dz)->dz_hashmask)
        int                     dz_order;
-       u32                     dz_mask;
+       u16                     dz_mask;
 #define DZ_MASK(dz)    ((dz)->dz_mask)
 };
 
@@ -73,53 +74,53 @@ for( ; ((f) = *(fp)) != NULL; (fp) = &(f)->fn_next)
 #define DN_FIB_SCAN_KEY(f, fp, key) \
 for( ; ((f) = *(fp)) != NULL && dn_key_eq((f)->fn_key, (key)); (fp) = &(f)->fn_next)
 
+#define RT_TABLE_MIN 1
 
 static rwlock_t dn_fib_tables_lock = RW_LOCK_UNLOCKED;
-static struct dn_fib_table *dn_fib_tables[DN_NUM_TABLES + 1];
+struct dn_fib_table *dn_fib_tables[RT_TABLE_MAX + 1];
 
 static kmem_cache_t *dn_hash_kmem;
 static int dn_fib_hash_zombies;
 
-static __inline__ dn_fib_idx_t dn_hash(dn_fib_key_t key, struct dn_zone *dz)
+static inline dn_fib_idx_t dn_hash(dn_fib_key_t key, struct dn_zone *dz)
 {
-       u32 h = ntohs(key.datum)>>(16 - dz->dz_order);
+       u16 h = ntohs(key.datum)>>(16 - dz->dz_order);
        h ^= (h >> 10);
        h ^= (h >> 6);
-       h ^= (h >> 3);
        h &= DZ_HASHMASK(dz);
        return *(dn_fib_idx_t *)&h;
 }
 
-static __inline__ dn_fib_key_t dz_key(u16 dst, struct dn_zone *dz)
+static inline dn_fib_key_t dz_key(u16 dst, struct dn_zone *dz)
 {
        dn_fib_key_t k;
        k.datum = dst & DZ_MASK(dz);
        return k;
 }
 
-static __inline__ struct dn_fib_node **dn_chain_p(dn_fib_key_t key, struct dn_zone *dz)
+static inline struct dn_fib_node **dn_chain_p(dn_fib_key_t key, struct dn_zone *dz)
 {
        return &dz->dz_hash[dn_hash(key, dz).datum];
 }
 
-static __inline__ struct dn_fib_node *dz_chain(dn_fib_key_t key, struct dn_zone *dz)
+static inline struct dn_fib_node *dz_chain(dn_fib_key_t key, struct dn_zone *dz)
 {
        return dz->dz_hash[dn_hash(key, dz).datum];
 }
 
-static __inline__ int dn_key_eq(dn_fib_key_t a, dn_fib_key_t b)
+static inline int dn_key_eq(dn_fib_key_t a, dn_fib_key_t b)
 {
        return a.datum == b.datum;
 }
 
-static __inline__ int dn_key_leq(dn_fib_key_t a, dn_fib_key_t b)
+static inline int dn_key_leq(dn_fib_key_t a, dn_fib_key_t b)
 {
        return a.datum <= b.datum;
 }
 
-static __inline__ void dn_rebuild_zone(struct dn_zone *dz,
-                                       struct dn_fib_node **old_ht,
-                                       int old_divisor)
+static inline void dn_rebuild_zone(struct dn_zone *dz,
+                                  struct dn_fib_node **old_ht,
+                                  int old_divisor)
 {
        int i;
        struct dn_fib_node *f, **fp, *next;
@@ -290,6 +291,8 @@ static int dn_fib_dump_info(struct sk_buff *skb, u32 pid, u32 seq, int event,
         rtm->rtm_protocol = fi->fib_protocol;
         if (fi->fib_priority)
                 RTA_PUT(skb, RTA_PRIORITY, 4, &fi->fib_priority);
+       if (rtnetlink_put_metrics(skb, fi->fib_metrics) < 0)
+               goto rtattr_failure;
         if (fi->fib_nhs == 1) {
                 if (fi->fib_nh->nh_gw)
                         RTA_PUT(skb, RTA_GATEWAY, 2, &fi->fib_nh->nh_gw);
@@ -654,7 +657,7 @@ static int dn_fib_table_delete(struct dn_fib_table *tb, struct rtmsg *r, struct
         return -ESRCH;
 }
 
-static __inline__ int dn_flush_list(struct dn_fib_node **fp, int z, struct dn_hash *table)
+static inline int dn_flush_list(struct dn_fib_node **fp, int z, struct dn_hash *table)
 {
        int found = 0;
        struct dn_fib_node *f;
@@ -696,8 +699,7 @@ static int dn_fib_table_flush(struct dn_fib_table *tb)
        return found;
 }
 
-static int dn_fib_table_lookup(struct dn_fib_table *tb, const struct dn_fib_key *
-key, struct dn_fib_res *res)
+static int dn_fib_table_lookup(struct dn_fib_table *tb, const struct flowi *flp, struct dn_fib_res *res)
 {
         int err;
        struct dn_zone *dz;
@@ -706,25 +708,29 @@ key, struct dn_fib_res *res)
        read_lock(&dn_fib_tables_lock);
        for(dz = t->dh_zone_list; dz; dz = dz->dz_next) {
                struct dn_fib_node *f;
-               dn_fib_key_t k = dz_key(key->dst, dz);
+               dn_fib_key_t k = dz_key(flp->fld_dst, dz);
 
                for(f = dz_chain(k, dz); f; f = f->fn_next) {
-                       if (!dn_key_leq(k, f->fn_key))
-                               break;
-                       else
-                               continue;
+                       if (!dn_key_eq(k, f->fn_key)) {
+                               if (dn_key_leq(k, f->fn_key))
+                                       break;
+                               else
+                                       continue;
+                       }
 
                        f->fn_state |= DN_S_ACCESSED;
 
                        if (f->fn_state&DN_S_ZOMBIE)
                                continue;
-                       if (f->fn_scope < key->scope)
+
+                       if (f->fn_scope < flp->fld_scope)
                                continue;
 
-                       err = dn_fib_semantic_match(f->fn_type, DN_FIB_INFO(f), key, res);
+                       err = dn_fib_semantic_match(f->fn_type, DN_FIB_INFO(f), flp, res);
+
                        if (err == 0) {
                                res->type = f->fn_type;
-                               res->scope = f->fn_scope;
+                               res->scope = f->fn_scope; 
                                res->prefixlen = dz->dz_order;
                                goto out;
                        }
@@ -763,7 +769,7 @@ static void dn_fib_node_get_info(int type, int dead, struct dn_fib_info *fi, u16
 
        if (fi) {
                len = sprintf(buffer, "%s\t%04x\t%04x\t%04x\t%d\t%u\t%d\t%04x\t%d\t%u\t%u",
-                               fi->fib_dev ? fi->fib_dev->name : "*", prefix,
+                               fi->dn_fib_dev ? fi->dn_fib_dev->name : "*", prefix,
                                fi->fib_nh->nh_gw, flags, 0, 0, fi->fib_priority,
                                mask, 0, 0, 0);
        } else {
@@ -823,10 +829,10 @@ struct dn_fib_table *dn_fib_get_table(int n, int create)
 {
         struct dn_fib_table *t;
 
-        if (n < DN_MIN_TABLE)
+        if (n < RT_TABLE_MIN)
                 return NULL;
 
-        if (n > DN_NUM_TABLES)
+        if (n > RT_TABLE_MAX)
                 return NULL;
 
         if (dn_fib_tables[n]) 
@@ -839,7 +845,7 @@ struct dn_fib_table *dn_fib_get_table(int n, int create)
                 printk(KERN_DEBUG "DECnet: BUG! Attempt to create routing table from interrupt\n"); 
                 return NULL;
         }
-        if ((t = kmalloc(sizeof(struct dn_fib_table), GFP_KERNEL)) == NULL)
+        if ((t = kmalloc(sizeof(struct dn_fib_table) + sizeof(struct dn_hash), GFP_KERNEL)) == NULL)
                 return NULL;
 
         memset(t, 0, sizeof(struct dn_fib_table));
@@ -853,6 +859,7 @@ struct dn_fib_table *dn_fib_get_table(int n, int create)
        t->get_info = dn_fib_table_get_info;
 #endif
         t->dump = dn_fib_table_dump;
+       memset(t->data, 0, sizeof(struct dn_hash));
         dn_fib_tables[n] = t;
 
         return t;
@@ -876,7 +883,7 @@ struct dn_fib_table *dn_fib_empty_table(void)
 {
         int id;
 
-        for(id = DN_MIN_TABLE; id <= DN_NUM_TABLES; id++)
+        for(id = RT_TABLE_MIN; id <= RT_TABLE_MAX; id++)
                 if (dn_fib_tables[id] == NULL)
                         return dn_fib_get_table(id, 1);
         return NULL;
@@ -894,7 +901,7 @@ void __exit dn_fib_table_cleanup(void)
 {
        int i;
 
-       for (i = 0; i < DN_NUM_TABLES + 1; ++i)
+       for (i = RT_TABLE_MIN; i <= RT_TABLE_MAX; ++i)
                dn_fib_del_tree(i);
 
        return;
index 7a7a0ad22bd621255626c5607c858f887338a2d2..1fe6226a8b8693b908749bde892b17cbd82c06e5 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/spinlock.h>
 #include <net/sock.h>
 #include <asm/atomic.h>
+#include <net/flow.h>
 #include <net/dn.h>
 
 /*
index 384c9d90b27dada1c786be4cfb9fc943bca17885..1c692b40e732d2134d908b4704d18513348b73d7 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/string.h>
 #include <net/neighbour.h>
 #include <net/dst.h>
+#include <net/flow.h>
 
 #include <asm/uaccess.h>