tproxy: split off ipv6 defragmentation to a separate module
[linux-2.6.git] / net / rose / rose_route.c
index fb9359f..b4fdaac 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/string.h>
 #include <linux/sockios.h>
 #include <linux/net.h>
+#include <linux/slab.h>
 #include <net/ax25.h>
 #include <linux/inet.h>
 #include <linux/netdevice.h>
@@ -77,8 +78,9 @@ static int __must_check rose_add_node(struct rose_route_struct *rose_route,
 
        rose_neigh = rose_neigh_list;
        while (rose_neigh != NULL) {
-               if (ax25cmp(&rose_route->neighbour, &rose_neigh->callsign) == 0
-                   && rose_neigh->dev == dev)
+               if (ax25cmp(&rose_route->neighbour,
+                           &rose_neigh->callsign) == 0 &&
+                   rose_neigh->dev == dev)
                        break;
                rose_neigh = rose_neigh->next;
        }
@@ -107,7 +109,9 @@ static int __must_check rose_add_node(struct rose_route_struct *rose_route,
                init_timer(&rose_neigh->t0timer);
 
                if (rose_route->ndigis != 0) {
-                       if ((rose_neigh->digipeat = kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL) {
+                       rose_neigh->digipeat =
+                               kmalloc(sizeof(ax25_digi), GFP_ATOMIC);
+                       if (rose_neigh->digipeat == NULL) {
                                kfree(rose_neigh);
                                res = -ENOMEM;
                                goto out;
@@ -234,6 +238,8 @@ static void rose_remove_neigh(struct rose_neigh *rose_neigh)
 
        if ((s = rose_neigh_list) == rose_neigh) {
                rose_neigh_list = rose_neigh->next;
+               if (rose_neigh->ax25)
+                       ax25_cb_put(rose_neigh->ax25);
                kfree(rose_neigh->digipeat);
                kfree(rose_neigh);
                return;
@@ -242,6 +248,8 @@ static void rose_remove_neigh(struct rose_neigh *rose_neigh)
        while (s != NULL && s->next != NULL) {
                if (s->next == rose_neigh) {
                        s->next = rose_neigh->next;
+                       if (rose_neigh->ax25)
+                               ax25_cb_put(rose_neigh->ax25);
                        kfree(rose_neigh->digipeat);
                        kfree(rose_neigh);
                        return;
@@ -311,8 +319,9 @@ static int rose_del_node(struct rose_route_struct *rose_route,
 
        rose_neigh = rose_neigh_list;
        while (rose_neigh != NULL) {
-               if (ax25cmp(&rose_route->neighbour, &rose_neigh->callsign) == 0
-                   && rose_neigh->dev == dev)
+               if (ax25cmp(&rose_route->neighbour,
+                           &rose_neigh->callsign) == 0 &&
+                   rose_neigh->dev == dev)
                        break;
                rose_neigh = rose_neigh->next;
        }
@@ -578,18 +587,18 @@ static int rose_clear_routes(void)
 
 /*
  *     Check that the device given is a valid AX.25 interface that is "up".
+ *     called whith RTNL
  */
-static struct net_device *rose_ax25_dev_get(char *devname)
+static struct net_device *rose_ax25_dev_find(char *devname)
 {
        struct net_device *dev;
 
-       if ((dev = dev_get_by_name(&init_net, devname)) == NULL)
+       if ((dev = __dev_get_by_name(&init_net, devname)) == NULL)
                return NULL;
 
        if ((dev->flags & IFF_UP) && dev->type == ARPHRD_AX25)
                return dev;
 
-       dev_put(dev);
        return NULL;
 }
 
@@ -600,13 +609,13 @@ struct net_device *rose_dev_first(void)
 {
        struct net_device *dev, *first = NULL;
 
-       read_lock(&dev_base_lock);
-       for_each_netdev(&init_net, dev) {
+       rcu_read_lock();
+       for_each_netdev_rcu(&init_net, dev) {
                if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ROSE)
                        if (first == NULL || strncmp(dev->name, first->name, 3) < 0)
                                first = dev;
        }
-       read_unlock(&dev_base_lock);
+       rcu_read_unlock();
 
        return first;
 }
@@ -618,8 +627,8 @@ struct net_device *rose_dev_get(rose_address *addr)
 {
        struct net_device *dev;
 
-       read_lock(&dev_base_lock);
-       for_each_netdev(&init_net, dev) {
+       rcu_read_lock();
+       for_each_netdev_rcu(&init_net, dev) {
                if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ROSE && rosecmp(addr, (rose_address *)dev->dev_addr) == 0) {
                        dev_hold(dev);
                        goto out;
@@ -627,7 +636,7 @@ struct net_device *rose_dev_get(rose_address *addr)
        }
        dev = NULL;
 out:
-       read_unlock(&dev_base_lock);
+       rcu_read_unlock();
        return dev;
 }
 
@@ -635,14 +644,14 @@ static int rose_dev_exists(rose_address *addr)
 {
        struct net_device *dev;
 
-       read_lock(&dev_base_lock);
-       for_each_netdev(&init_net, dev) {
+       rcu_read_lock();
+       for_each_netdev_rcu(&init_net, dev) {
                if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ROSE && rosecmp(addr, (rose_address *)dev->dev_addr) == 0)
                        goto out;
        }
        dev = NULL;
 out:
-       read_unlock(&dev_base_lock);
+       rcu_read_unlock();
        return dev != NULL;
 }
 
@@ -662,27 +671,34 @@ struct rose_route *rose_route_free_lci(unsigned int lci, struct rose_neigh *neig
 }
 
 /*
- *     Find a neighbour given a ROSE address.
+ *     Find a neighbour or a route given a ROSE address.
  */
 struct rose_neigh *rose_get_neigh(rose_address *addr, unsigned char *cause,
-       unsigned char *diagnostic)
+       unsigned char *diagnostic, int new)
 {
        struct rose_neigh *res = NULL;
        struct rose_node *node;
        int failed = 0;
        int i;
 
-       spin_lock_bh(&rose_node_list_lock);
+       if (!new) spin_lock_bh(&rose_node_list_lock);
        for (node = rose_node_list; node != NULL; node = node->next) {
                if (rosecmpm(addr, &node->address, node->mask) == 0) {
                        for (i = 0; i < node->count; i++) {
-                               if (!rose_ftimer_running(node->neighbour[i])) {
-                                       res = node->neighbour[i];
-                                       goto out;
-                               } else
-                                       failed = 1;
+                               if (new) {
+                                       if (node->neighbour[i]->restarted) {
+                                               res = node->neighbour[i];
+                                               goto out;
+                                       }
+                               }
+                               else {
+                                       if (!rose_ftimer_running(node->neighbour[i])) {
+                                               res = node->neighbour[i];
+                                               goto out;
+                                       } else
+                                               failed = 1;
+                               }
                        }
-                       break;
                }
        }
 
@@ -695,7 +711,7 @@ struct rose_neigh *rose_get_neigh(rose_address *addr, unsigned char *cause,
        }
 
 out:
-       spin_unlock_bh(&rose_node_list_lock);
+       if (!new) spin_unlock_bh(&rose_node_list_lock);
 
        return res;
 }
@@ -713,27 +729,23 @@ int rose_rt_ioctl(unsigned int cmd, void __user *arg)
        case SIOCADDRT:
                if (copy_from_user(&rose_route, arg, sizeof(struct rose_route_struct)))
                        return -EFAULT;
-               if ((dev = rose_ax25_dev_get(rose_route.device)) == NULL)
+               if ((dev = rose_ax25_dev_find(rose_route.device)) == NULL)
                        return -EINVAL;
-               if (rose_dev_exists(&rose_route.address)) { /* Can't add routes to ourself */
-                       dev_put(dev);
+               if (rose_dev_exists(&rose_route.address)) /* Can't add routes to ourself */
                        return -EINVAL;
-               }
                if (rose_route.mask > 10) /* Mask can't be more than 10 digits */
                        return -EINVAL;
                if (rose_route.ndigis > AX25_MAX_DIGIS)
                        return -EINVAL;
                err = rose_add_node(&rose_route, dev);
-               dev_put(dev);
                return err;
 
        case SIOCDELRT:
                if (copy_from_user(&rose_route, arg, sizeof(struct rose_route_struct)))
                        return -EFAULT;
-               if ((dev = rose_ax25_dev_get(rose_route.device)) == NULL)
+               if ((dev = rose_ax25_dev_find(rose_route.device)) == NULL)
                        return -EINVAL;
                err = rose_del_node(&rose_route, dev);
-               dev_put(dev);
                return err;
 
        case SIOCRSCLRRT:
@@ -807,6 +819,7 @@ void rose_link_failed(ax25_cb *ax25, int reason)
 
        if (rose_neigh != NULL) {
                rose_neigh->ax25 = NULL;
+               ax25_cb_put(ax25);
 
                rose_del_route_by_neigh(rose_neigh);
                rose_kill_by_neigh(rose_neigh);
@@ -857,7 +870,6 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25)
        src_addr  = (rose_address *)(skb->data + 9);
        dest_addr = (rose_address *)(skb->data + 4);
 
-       spin_lock_bh(&rose_node_list_lock);
        spin_lock_bh(&rose_neigh_list_lock);
        spin_lock_bh(&rose_route_list_lock);
 
@@ -1019,7 +1031,7 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25)
                rose_route = rose_route->next;
        }
 
-       if ((new_neigh = rose_get_neigh(dest_addr, &cause, &diagnostic)) == NULL) {
+       if ((new_neigh = rose_get_neigh(dest_addr, &cause, &diagnostic, 1)) == NULL) {
                rose_transmit_clear_request(rose_neigh, lci, cause, diagnostic);
                goto out;
        }
@@ -1060,7 +1072,6 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25)
 out:
        spin_unlock_bh(&rose_route_list_lock);
        spin_unlock_bh(&rose_neigh_list_lock);
-       spin_unlock_bh(&rose_node_list_lock);
 
        return res;
 }
@@ -1068,12 +1079,12 @@ out:
 #ifdef CONFIG_PROC_FS
 
 static void *rose_node_start(struct seq_file *seq, loff_t *pos)
-       __acquires(rose_neigh_list_lock)
+       __acquires(rose_node_list_lock)
 {
        struct rose_node *rose_node;
        int i = 1;
 
-       spin_lock_bh(&rose_neigh_list_lock);
+       spin_lock_bh(&rose_node_list_lock);
        if (*pos == 0)
                return SEQ_START_TOKEN;
 
@@ -1092,13 +1103,14 @@ static void *rose_node_next(struct seq_file *seq, void *v, loff_t *pos)
 }
 
 static void rose_node_stop(struct seq_file *seq, void *v)
-       __releases(rose_neigh_list_lock)
+       __releases(rose_node_list_lock)
 {
-       spin_unlock_bh(&rose_neigh_list_lock);
+       spin_unlock_bh(&rose_node_list_lock);
 }
 
 static int rose_node_show(struct seq_file *seq, void *v)
 {
+       char rsbuf[11];
        int i;
 
        if (v == SEQ_START_TOKEN)
@@ -1107,13 +1119,13 @@ static int rose_node_show(struct seq_file *seq, void *v)
                const struct rose_node *rose_node = v;
                /* if (rose_node->loopback) {
                        seq_printf(seq, "%-10s %04d 1 loopback\n",
-                               rose2asc(&rose_node->address),
-                               rose_node->mask);
+                                  rose2asc(rsbuf, &rose_node->address),
+                                  rose_node->mask);
                } else { */
                        seq_printf(seq, "%-10s %04d %d",
-                               rose2asc(&rose_node->address),
-                               rose_node->mask,
-                               rose_node->count);
+                                  rose2asc(rsbuf, &rose_node->address),
+                                  rose_node->mask,
+                                  rose_node->count);
 
                        for (i = 0; i < rose_node->count; i++)
                                seq_printf(seq, " %05d",
@@ -1262,7 +1274,7 @@ static void rose_route_stop(struct seq_file *seq, void *v)
 
 static int rose_route_show(struct seq_file *seq, void *v)
 {
-       char buf[11];
+       char buf[11], rsbuf[11];
 
        if (v == SEQ_START_TOKEN)
                seq_puts(seq,
@@ -1274,7 +1286,7 @@ static int rose_route_show(struct seq_file *seq, void *v)
                        seq_printf(seq,
                                   "%3.3X  %-10s  %-9s  %05d      ",
                                   rose_route->lci1,
-                                  rose2asc(&rose_route->src_addr),
+                                  rose2asc(rsbuf, &rose_route->src_addr),
                                   ax2asc(buf, &rose_route->src_call),
                                   rose_route->neigh1->number);
                else
@@ -1284,10 +1296,10 @@ static int rose_route_show(struct seq_file *seq, void *v)
                if (rose_route->neigh2)
                        seq_printf(seq,
                                   "%3.3X  %-10s  %-9s  %05d\n",
-                               rose_route->lci2,
-                               rose2asc(&rose_route->dest_addr),
-                               ax2asc(buf, &rose_route->dest_call),
-                               rose_route->neigh2->number);
+                                  rose_route->lci2,
+                                  rose2asc(rsbuf, &rose_route->dest_addr),
+                                  ax2asc(buf, &rose_route->dest_call),
+                                  rose_route->neigh2->number);
                 else
                         seq_puts(seq,
                                  "000  *           *          00000\n");