tipc: eliminate risk of double link_up events
[cascardo/linux.git] / net / tipc / node.c
index ace178f..e01e2c7 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * net/tipc/node.c: TIPC node management routines
  *
- * Copyright (c) 2000-2006, 2012-2015, Ericsson AB
+ * Copyright (c) 2000-2006, 2012-2016, Ericsson AB
  * Copyright (c) 2005-2006, 2010-2014, Wind River Systems
  * All rights reserved.
  *
@@ -191,6 +191,20 @@ int tipc_node_get_mtu(struct net *net, u32 addr, u32 sel)
        tipc_node_put(n);
        return mtu;
 }
+
+u16 tipc_node_get_capabilities(struct net *net, u32 addr)
+{
+       struct tipc_node *n;
+       u16 caps;
+
+       n = tipc_node_find(net, addr);
+       if (unlikely(!n))
+               return TIPC_NODE_CAPABILITIES;
+       caps = n->capabilities;
+       tipc_node_put(n);
+       return caps;
+}
+
 /*
  * A trivial power-of-two bitmask technique is used for speed, since this
  * operation is done for every incoming TIPC packet. The number of hash table
@@ -304,8 +318,11 @@ struct tipc_node *tipc_node_create(struct net *net, u32 addr, u16 capabilities)
 
        spin_lock_bh(&tn->node_list_lock);
        n = tipc_node_find(net, addr);
-       if (n)
+       if (n) {
+               /* Same node may come back with new capabilities */
+               n->capabilities = capabilities;
                goto exit;
+       }
        n = kzalloc(sizeof(*n), GFP_ATOMIC);
        if (!n) {
                pr_warn("Node creation failed, no memory\n");
@@ -525,7 +542,7 @@ static void __tipc_node_link_up(struct tipc_node *n, int bearer_id,
        struct tipc_link *ol = node_active_link(n, 0);
        struct tipc_link *nl = n->links[bearer_id].link;
 
-       if (!nl)
+       if (!nl || tipc_link_is_up(nl))
                return;
 
        tipc_link_fsm_evt(nl, LINK_ESTABLISH_EVT);
@@ -545,12 +562,16 @@ static void __tipc_node_link_up(struct tipc_node *n, int bearer_id,
        pr_debug("Established link <%s> on network plane %c\n",
                 tipc_link_name(nl), tipc_link_plane(nl));
 
+       /* Ensure that a STATE message goes first */
+       tipc_link_build_state_msg(nl, xmitq);
+
        /* First link? => give it both slots */
        if (!ol) {
                *slot0 = bearer_id;
                *slot1 = bearer_id;
                tipc_node_fsm_evt(n, SELF_ESTABL_CONTACT_EVT);
                n->action_flags |= TIPC_NOTIFY_NODE_UP;
+               tipc_link_set_active(nl, true);
                tipc_bcast_add_peer(n->net, nl, xmitq);
                return;
        }
@@ -581,8 +602,12 @@ static void __tipc_node_link_up(struct tipc_node *n, int bearer_id,
 static void tipc_node_link_up(struct tipc_node *n, int bearer_id,
                              struct sk_buff_head *xmitq)
 {
+       struct tipc_media_addr *maddr;
+
        tipc_node_write_lock(n);
        __tipc_node_link_up(n, bearer_id, xmitq);
+       maddr = &n->links[bearer_id].maddr;
+       tipc_bearer_xmit(n->net, bearer_id, xmitq, maddr);
        tipc_node_write_unlock(n);
 }
 
@@ -1279,7 +1304,7 @@ static void tipc_node_bc_rcv(struct net *net, struct sk_buff *skb, int bearer_id
        /* Broadcast ACKs are sent on a unicast link */
        if (rc & TIPC_LINK_SND_BC_ACK) {
                tipc_node_read_lock(n);
-               tipc_link_build_ack_msg(le->link, &xmitq);
+               tipc_link_build_state_msg(le->link, &xmitq);
                tipc_node_read_unlock(n);
        }
 
@@ -1444,6 +1469,7 @@ void tipc_rcv(struct net *net, struct sk_buff *skb, struct tipc_bearer *b)
        int bearer_id = b->identity;
        struct tipc_link_entry *le;
        u16 bc_ack = msg_bcast_ack(hdr);
+       u32 self = tipc_own_addr(net);
        int rc = 0;
 
        __skb_queue_head_init(&xmitq);
@@ -1460,6 +1486,10 @@ void tipc_rcv(struct net *net, struct sk_buff *skb, struct tipc_bearer *b)
                        return tipc_node_bc_rcv(net, skb, bearer_id);
        }
 
+       /* Discard unicast link messages destined for another node */
+       if (unlikely(!msg_short(hdr) && (msg_destnode(hdr) != self)))
+               goto discard;
+
        /* Locate neighboring node that sent packet */
        n = tipc_node_find(net, msg_prevnode(hdr));
        if (unlikely(!n))