tipc: make bearer packet filtering generic
[cascardo/linux.git] / net / tipc / bearer.c
index 65b1bbf..6fc4e3c 100644 (file)
@@ -56,6 +56,13 @@ static struct tipc_media * const media_info_array[] = {
        NULL
 };
 
+static struct tipc_bearer *bearer_get(struct net *net, int bearer_id)
+{
+       struct tipc_net *tn = tipc_net(net);
+
+       return rcu_dereference_rtnl(tn->bearer_list[bearer_id]);
+}
+
 static void bearer_disable(struct net *net, struct tipc_bearer *b);
 
 /**
@@ -323,6 +330,7 @@ restart:
        b->domain = disc_domain;
        b->net_plane = bearer_id + 'A';
        b->priority = priority;
+       test_and_set_bit_lock(0, &b->up);
 
        res = tipc_disc_create(net, b, &b->bcast_addr, &skb);
        if (res) {
@@ -360,15 +368,24 @@ static int tipc_reset_bearer(struct net *net, struct tipc_bearer *b)
  */
 void tipc_bearer_reset_all(struct net *net)
 {
-       struct tipc_net *tn = tipc_net(net);
        struct tipc_bearer *b;
        int i;
 
        for (i = 0; i < MAX_BEARERS; i++) {
-               b = rcu_dereference_rtnl(tn->bearer_list[i]);
+               b = bearer_get(net, i);
+               if (b)
+                       clear_bit_unlock(0, &b->up);
+       }
+       for (i = 0; i < MAX_BEARERS; i++) {
+               b = bearer_get(net, i);
                if (b)
                        tipc_reset_bearer(net, b);
        }
+       for (i = 0; i < MAX_BEARERS; i++) {
+               b = bearer_get(net, i);
+               if (b)
+                       test_and_set_bit_lock(0, &b->up);
+       }
 }
 
 /**
@@ -382,8 +399,9 @@ static void bearer_disable(struct net *net, struct tipc_bearer *b)
        int bearer_id = b->identity;
 
        pr_info("Disabling bearer <%s>\n", b->name);
-       b->media->disable_media(b);
+       clear_bit_unlock(0, &b->up);
        tipc_node_delete_links(net, bearer_id);
+       b->media->disable_media(b);
        RCU_INIT_POINTER(b->media_ptr, NULL);
        if (b->link_req)
                tipc_disc_delete(b->link_req);
@@ -440,22 +458,16 @@ int tipc_l2_send_msg(struct net *net, struct sk_buff *skb,
 {
        struct net_device *dev;
        int delta;
-       void *tipc_ptr;
 
        dev = (struct net_device *)rcu_dereference_rtnl(b->media_ptr);
        if (!dev)
                return 0;
 
-       /* Send RESET message even if bearer is detached from device */
-       tipc_ptr = rcu_dereference_rtnl(dev->tipc_ptr);
-       if (unlikely(!tipc_ptr && !msg_is_reset(buf_msg(skb))))
-               goto drop;
-
-       delta = dev->hard_header_len - skb_headroom(skb);
-       if ((delta > 0) &&
-           pskb_expand_head(skb, SKB_DATA_ALIGN(delta), 0, GFP_ATOMIC))
-               goto drop;
-
+       delta = SKB_DATA_ALIGN(dev->hard_header_len - skb_headroom(skb));
+       if ((delta > 0) && pskb_expand_head(skb, delta, 0, GFP_ATOMIC)) {
+               kfree_skb(skb);
+               return 0;
+       }
        skb_reset_network_header(skb);
        skb->dev = dev;
        skb->protocol = htons(ETH_P_TIPC);
@@ -463,9 +475,6 @@ int tipc_l2_send_msg(struct net *net, struct sk_buff *skb,
                        dev->dev_addr, skb->len);
        dev_queue_xmit(skb);
        return 0;
-drop:
-       kfree_skb(skb);
-       return 0;
 }
 
 int tipc_bearer_mtu(struct net *net, u32 bearer_id)
@@ -487,12 +496,12 @@ void tipc_bearer_xmit_skb(struct net *net, u32 bearer_id,
                          struct sk_buff *skb,
                          struct tipc_media_addr *dest)
 {
-       struct tipc_net *tn = tipc_net(net);
+       struct tipc_msg *hdr = buf_msg(skb);
        struct tipc_bearer *b;
 
        rcu_read_lock();
-       b = rcu_dereference_rtnl(tn->bearer_list[bearer_id]);
-       if (likely(b))
+       b = bearer_get(net, bearer_id);
+       if (likely(b && (test_bit(0, &b->up) || msg_is_reset(hdr))))
                b->media->send_msg(net, skb, b, dest);
        else
                kfree_skb(skb);
@@ -505,7 +514,6 @@ void tipc_bearer_xmit(struct net *net, u32 bearer_id,
                      struct sk_buff_head *xmitq,
                      struct tipc_media_addr *dst)
 {
-       struct tipc_net *tn = net_generic(net, tipc_net_id);
        struct tipc_bearer *b;
        struct sk_buff *skb, *tmp;
 
@@ -513,12 +521,15 @@ void tipc_bearer_xmit(struct net *net, u32 bearer_id,
                return;
 
        rcu_read_lock();
-       b = rcu_dereference_rtnl(tn->bearer_list[bearer_id]);
+       b = bearer_get(net, bearer_id);
        if (unlikely(!b))
                __skb_queue_purge(xmitq);
        skb_queue_walk_safe(xmitq, skb, tmp) {
                __skb_dequeue(xmitq);
-               b->media->send_msg(net, skb, b, dst);
+               if (likely(test_bit(0, &b->up) || msg_is_reset(buf_msg(skb))))
+                       b->media->send_msg(net, skb, b, dst);
+               else
+                       kfree(skb);
        }
        rcu_read_unlock();
 }
@@ -535,8 +546,8 @@ void tipc_bearer_bc_xmit(struct net *net, u32 bearer_id,
        struct tipc_msg *hdr;
 
        rcu_read_lock();
-       b = rcu_dereference_rtnl(tn->bearer_list[bearer_id]);
-       if (unlikely(!b))
+       b = bearer_get(net, bearer_id);
+       if (unlikely(!b || !test_bit(0, &b->up)))
                __skb_queue_purge(xmitq);
        skb_queue_walk_safe(xmitq, skb, tmp) {
                hdr = buf_msg(skb);
@@ -566,7 +577,8 @@ static int tipc_l2_rcv_msg(struct sk_buff *skb, struct net_device *dev,
 
        rcu_read_lock();
        b = rcu_dereference_rtnl(dev->tipc_ptr);
-       if (likely(b && (skb->pkt_type <= PACKET_BROADCAST))) {
+       if (likely(b && test_bit(0, &b->up) &&
+                  (skb->pkt_type <= PACKET_BROADCAST))) {
                skb->next = NULL;
                tipc_rcv(dev_net(dev), skb, b);
                rcu_read_unlock();
@@ -591,18 +603,9 @@ static int tipc_l2_device_event(struct notifier_block *nb, unsigned long evt,
 {
        struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct net *net = dev_net(dev);
-       struct tipc_net *tn = tipc_net(net);
        struct tipc_bearer *b;
-       int i;
 
        b = rtnl_dereference(dev->tipc_ptr);
-       if (!b) {
-               for (i = 0; i < MAX_BEARERS; b = NULL, i++) {
-                       b = rtnl_dereference(tn->bearer_list[i]);
-                       if (b && (b->media_ptr == dev))
-                               break;
-               }
-       }
        if (!b)
                return NOTIFY_DONE;
 
@@ -613,11 +616,10 @@ static int tipc_l2_device_event(struct notifier_block *nb, unsigned long evt,
                if (netif_carrier_ok(dev))
                        break;
        case NETDEV_UP:
-               rcu_assign_pointer(dev->tipc_ptr, b);
+               test_and_set_bit_lock(0, &b->up);
                break;
        case NETDEV_GOING_DOWN:
-               RCU_INIT_POINTER(dev->tipc_ptr, NULL);
-               synchronize_net();
+               clear_bit_unlock(0, &b->up);
                tipc_reset_bearer(net, b);
                break;
        case NETDEV_CHANGEMTU: