]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/net/pppoe.c
[NET]: Make device event notification network namespace safe
[linux-2.6-omap-h63xx.git] / drivers / net / pppoe.c
index 35a7385ccb2a35a5e2bf5c73d0de4e2a9fa53685..c5c70e4b1d3616a2c7902d7bca22fe7e62d03183 100644 (file)
@@ -78,6 +78,7 @@
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
 
+#include <net/net_namespace.h>
 #include <net/sock.h>
 
 #include <asm/uaccess.h>
@@ -108,19 +109,24 @@ static inline int cmp_addr(struct pppoe_addr *a, unsigned long sid, char *addr)
                (memcmp(a->remote,addr,ETH_ALEN) == 0));
 }
 
-static int hash_item(unsigned long sid, unsigned char *addr)
+#if 8%PPPOE_HASH_BITS
+#error 8 must be a multiple of PPPOE_HASH_BITS
+#endif
+
+static int hash_item(unsigned int sid, unsigned char *addr)
 {
-       char hash = 0;
-       int i, j;
+       unsigned char hash = 0;
+       unsigned int i;
 
-       for (i = 0; i < ETH_ALEN ; ++i) {
-               for (j = 0; j < 8/PPPOE_HASH_BITS ; ++j) {
-                       hash ^= addr[i] >> ( j * PPPOE_HASH_BITS );
-               }
+       for (i = 0 ; i < ETH_ALEN ; i++) {
+               hash ^= addr[i];
+       }
+       for (i = 0 ; i < sizeof(sid_t)*8 ; i += 8 ){
+               hash ^= sid>>i;
+       }
+       for (i = 8 ; (i>>=1) >= PPPOE_HASH_BITS ; ) {
+               hash ^= hash>>i;
        }
-
-       for (i = 0; i < (sizeof(unsigned long)*8) / PPPOE_HASH_BITS ; ++i)
-               hash ^= sid >> (i*PPPOE_HASH_BITS);
 
        return hash & ( PPPOE_HASH_SIZE - 1 );
 }
@@ -295,6 +301,9 @@ static int pppoe_device_event(struct notifier_block *this,
 {
        struct net_device *dev = (struct net_device *) ptr;
 
+       if (dev->nd_net != &init_net)
+               return NOTIFY_DONE;
+
        /* Only look at sockets that are using this specific device. */
        switch (event) {
        case NETDEV_CHANGEMTU:
@@ -380,12 +389,15 @@ static int pppoe_rcv(struct sk_buff *skb,
        struct pppoe_hdr *ph;
        struct pppox_sock *po;
 
-       if (!pskb_may_pull(skb, sizeof(struct pppoe_hdr)))
-               goto drop;
-
        if (!(skb = skb_share_check(skb, GFP_ATOMIC)))
                goto out;
 
+       if (dev->nd_net != &init_net)
+               goto drop;
+
+       if (!pskb_may_pull(skb, sizeof(struct pppoe_hdr)))
+               goto drop;
+
        ph = pppoe_hdr(skb);
 
        po = get_item((unsigned long) ph->sid, eth_hdr(skb)->h_source, dev->ifindex);
@@ -412,6 +424,9 @@ static int pppoe_disc_rcv(struct sk_buff *skb,
        struct pppoe_hdr *ph;
        struct pppox_sock *po;
 
+       if (dev->nd_net != &init_net)
+               goto abort;
+
        if (!pskb_may_pull(skb, sizeof(struct pppoe_hdr)))
                goto abort;
 
@@ -471,12 +486,12 @@ static struct proto pppoe_sk_proto = {
  * Initialize a new struct sock.
  *
  **********************************************************************/
-static int pppoe_create(struct socket *sock)
+static int pppoe_create(struct net *net, struct socket *sock)
 {
        int error = -ENOMEM;
        struct sock *sk;
 
-       sk = sk_alloc(PF_PPPOX, GFP_KERNEL, &pppoe_sk_proto, 1);
+       sk = sk_alloc(net, PF_PPPOX, GFP_KERNEL, &pppoe_sk_proto, 1);
        if (!sk)
                goto out;
 
@@ -843,71 +858,44 @@ static int __pppoe_xmit(struct sock *sk, struct sk_buff *skb)
 {
        struct pppox_sock *po = pppox_sk(sk);
        struct net_device *dev = po->pppoe_dev;
-       struct pppoe_hdr hdr;
        struct pppoe_hdr *ph;
-       int headroom = skb_headroom(skb);
        int data_len = skb->len;
-       struct sk_buff *skb2;
 
        if (sock_flag(sk, SOCK_DEAD) || !(sk->sk_state & PPPOX_CONNECTED))
                goto abort;
 
-       hdr.ver = 1;
-       hdr.type = 1;
-       hdr.code = 0;
-       hdr.sid = po->num;
-       hdr.length = htons(skb->len);
-
        if (!dev)
                goto abort;
 
-       /* Copy the skb if there is no space for the header. */
-       if (headroom < (sizeof(struct pppoe_hdr) + dev->hard_header_len)) {
-               skb2 = dev_alloc_skb(32+skb->len +
-                                    sizeof(struct pppoe_hdr) +
-                                    dev->hard_header_len);
-
-               if (skb2 == NULL)
-                       goto abort;
-
-               skb_reserve(skb2, dev->hard_header_len + sizeof(struct pppoe_hdr));
-               skb_copy_from_linear_data(skb, skb_put(skb2, skb->len),
-                                         skb->len);
-       } else {
-               /* Make a clone so as to not disturb the original skb,
-                * give dev_queue_xmit something it can free.
-                */
-               skb2 = skb_clone(skb, GFP_ATOMIC);
-
-               if (skb2 == NULL)
-                       goto abort;
-       }
+       /* Copy the data if there is no space for the header or if it's
+        * read-only.
+        */
+       if (skb_cow_head(skb, sizeof(*ph) + dev->hard_header_len))
+               goto abort;
 
-       ph = (struct pppoe_hdr *) skb_push(skb2, sizeof(struct pppoe_hdr));
-       memcpy(ph, &hdr, sizeof(struct pppoe_hdr));
-       skb2->protocol = __constant_htons(ETH_P_PPP_SES);
+       __skb_push(skb, sizeof(*ph));
+       skb_reset_network_header(skb);
 
-       skb_reset_network_header(skb2);
+       ph = pppoe_hdr(skb);
+       ph->ver = 1;
+       ph->type = 1;
+       ph->code = 0;
+       ph->sid = po->num;
+       ph->length = htons(data_len);
 
-       skb2->dev = dev;
+       skb->protocol = __constant_htons(ETH_P_PPP_SES);
+       skb->dev = dev;
 
-       dev->hard_header(skb2, dev, ETH_P_PPP_SES,
+       dev->hard_header(skb, dev, ETH_P_PPP_SES,
                         po->pppoe_pa.remote, NULL, data_len);
 
-       /* We're transmitting skb2, and assuming that dev_queue_xmit
-        * will free it.  The generic ppp layer however, is expecting
-        * that we give back 'skb' (not 'skb2') in case of failure,
-        * but free it in case of success.
-        */
-
-       if (dev_queue_xmit(skb2) < 0)
-               goto abort;
+       dev_queue_xmit(skb);
 
-       kfree_skb(skb);
        return 1;
 
 abort:
-       return 0;
+       kfree_skb(skb);
+       return 1;
 }
 
 
@@ -1064,7 +1052,7 @@ static int __init pppoe_proc_init(void)
 {
        struct proc_dir_entry *p;
 
-       p = create_proc_entry("net/pppoe", S_IRUGO, NULL);
+       p = create_proc_entry("pppoe", S_IRUGO, init_net.proc_net);
        if (!p)
                return -ENOMEM;
 
@@ -1135,7 +1123,7 @@ static void __exit pppoe_exit(void)
        dev_remove_pack(&pppoes_ptype);
        dev_remove_pack(&pppoed_ptype);
        unregister_netdevice_notifier(&pppoe_notifier);
-       remove_proc_entry("net/pppoe", NULL);
+       remove_proc_entry("pppoe", init_net.proc_net);
        proto_unregister(&pppoe_sk_proto);
 }