tipc: enable creating a "preliminary" node
authorTuong Lien <tuong.t.lien@dektech.com.au>
Fri, 8 Nov 2019 05:05:09 +0000 (12:05 +0700)
committerDavid S. Miller <davem@davemloft.net>
Fri, 8 Nov 2019 22:01:59 +0000 (14:01 -0800)
When user sets RX key for a peer not existing on the own node, a new
node entry is needed to which the RX key will be attached. However,
since the peer node address (& capabilities) is unknown at that moment,
only the node-ID is provided, this commit allows the creation of a node
with only the data that we call as “preliminary”.

A preliminary node is not the object of the “tipc_node_find()” but the
“tipc_node_find_by_id()”. Once the first message i.e. LINK_CONFIG comes
from that peer, and is successfully decrypted by the own node, the
actual peer node data will be properly updated and the node will
function as usual.

In addition, the node timer always starts when a node object is created
so if a preliminary node is not used, it will be cleaned up.

The later encryption functions will also use the node timer and be able
to create a preliminary node automatically when needed.

Acked-by: Ying Xue <ying.xue@windreiver.com>
Acked-by: Jon Maloy <jon.maloy@ericsson.com>
Signed-off-by: Tuong Lien <tuong.t.lien@dektech.com.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/tipc/node.c
net/tipc/node.h

index b66d2f67b1dd274495ef2dc88a5471097726472c..43d12a630f344236019139107047f7428bcfe31a 100644 (file)
@@ -89,6 +89,7 @@ struct tipc_bclink_entry {
  * @links: array containing references to all links to node
  * @action_flags: bit mask of different types of node actions
  * @state: connectivity state vs peer node
+ * @preliminary: a preliminary node or not
  * @sync_point: sequence number where synch/failover is finished
  * @list: links to adjacent nodes in sorted list of cluster's nodes
  * @working_links: number of working links to node (both active and standby)
@@ -112,6 +113,7 @@ struct tipc_node {
        int action_flags;
        struct list_head list;
        int state;
+       bool preliminary;
        bool failover_sent;
        u16 sync_point;
        int link_cnt;
@@ -120,6 +122,7 @@ struct tipc_node {
        u32 signature;
        u32 link_id;
        u8 peer_id[16];
+       char peer_id_string[NODE_ID_STR_LEN];
        struct list_head publ_list;
        struct list_head conn_sks;
        unsigned long keepalive_intv;
@@ -245,6 +248,16 @@ u16 tipc_node_get_capabilities(struct net *net, u32 addr)
        return caps;
 }
 
+u32 tipc_node_get_addr(struct tipc_node *node)
+{
+       return (node) ? node->addr : 0;
+}
+
+char *tipc_node_get_id_str(struct tipc_node *node)
+{
+       return node->peer_id_string;
+}
+
 static void tipc_node_kref_release(struct kref *kref)
 {
        struct tipc_node *n = container_of(kref, struct tipc_node, kref);
@@ -274,7 +287,7 @@ static struct tipc_node *tipc_node_find(struct net *net, u32 addr)
 
        rcu_read_lock();
        hlist_for_each_entry_rcu(node, &tn->node_htable[thash], hash) {
-               if (node->addr != addr)
+               if (node->addr != addr || node->preliminary)
                        continue;
                if (!kref_get_unless_zero(&node->kref))
                        node = NULL;
@@ -400,17 +413,39 @@ static void tipc_node_assign_peer_net(struct tipc_node *n, u32 hash_mixes)
 
 static struct tipc_node *tipc_node_create(struct net *net, u32 addr,
                                          u8 *peer_id, u16 capabilities,
-                                         u32 signature, u32 hash_mixes)
+                                         u32 hash_mixes, bool preliminary)
 {
        struct tipc_net *tn = net_generic(net, tipc_net_id);
        struct tipc_node *n, *temp_node;
        struct tipc_link *l;
+       unsigned long intv;
        int bearer_id;
        int i;
 
        spin_lock_bh(&tn->node_list_lock);
-       n = tipc_node_find(net, addr);
+       n = tipc_node_find(net, addr) ?:
+               tipc_node_find_by_id(net, peer_id);
        if (n) {
+               if (!n->preliminary)
+                       goto update;
+               if (preliminary)
+                       goto exit;
+               /* A preliminary node becomes "real" now, refresh its data */
+               tipc_node_write_lock(n);
+               n->preliminary = false;
+               n->addr = addr;
+               hlist_del_rcu(&n->hash);
+               hlist_add_head_rcu(&n->hash,
+                                  &tn->node_htable[tipc_hashfn(addr)]);
+               list_del_rcu(&n->list);
+               list_for_each_entry_rcu(temp_node, &tn->node_list, list) {
+                       if (n->addr < temp_node->addr)
+                               break;
+               }
+               list_add_tail_rcu(&n->list, &temp_node->list);
+               tipc_node_write_unlock_fast(n);
+
+update:
                if (n->peer_hash_mix ^ hash_mixes)
                        tipc_node_assign_peer_net(n, hash_mixes);
                if (n->capabilities == capabilities)
@@ -438,7 +473,9 @@ static struct tipc_node *tipc_node_create(struct net *net, u32 addr,
                pr_warn("Node creation failed, no memory\n");
                goto exit;
        }
+       tipc_nodeid2string(n->peer_id_string, peer_id);
        n->addr = addr;
+       n->preliminary = preliminary;
        memcpy(&n->peer_id, peer_id, 16);
        n->net = net;
        n->peer_net = NULL;
@@ -463,22 +500,14 @@ static struct tipc_node *tipc_node_create(struct net *net, u32 addr,
        n->signature = INVALID_NODE_SIG;
        n->active_links[0] = INVALID_BEARER_ID;
        n->active_links[1] = INVALID_BEARER_ID;
-       if (!tipc_link_bc_create(net, tipc_own_addr(net),
-                                addr, U16_MAX,
-                                tipc_link_window(tipc_bc_sndlink(net)),
-                                n->capabilities,
-                                &n->bc_entry.inputq1,
-                                &n->bc_entry.namedq,
-                                tipc_bc_sndlink(net),
-                                &n->bc_entry.link)) {
-               pr_warn("Broadcast rcv link creation failed, no memory\n");
-               kfree(n);
-               n = NULL;
-               goto exit;
-       }
+       n->bc_entry.link = NULL;
        tipc_node_get(n);
        timer_setup(&n->timer, tipc_node_timeout, 0);
-       n->keepalive_intv = U32_MAX;
+       /* Start a slow timer anyway, crypto needs it */
+       n->keepalive_intv = 10000;
+       intv = jiffies + msecs_to_jiffies(n->keepalive_intv);
+       if (!mod_timer(&n->timer, intv))
+               tipc_node_get(n);
        hlist_add_head_rcu(&n->hash, &tn->node_htable[tipc_hashfn(addr)]);
        list_for_each_entry_rcu(temp_node, &tn->node_list, list) {
                if (n->addr < temp_node->addr)
@@ -1001,6 +1030,8 @@ u32 tipc_node_try_addr(struct net *net, u8 *id, u32 addr)
 {
        struct tipc_net *tn = tipc_net(net);
        struct tipc_node *n;
+       bool preliminary;
+       u32 sugg_addr;
 
        /* Suggest new address if some other peer is using this one */
        n = tipc_node_find(net, addr);
@@ -1016,9 +1047,11 @@ u32 tipc_node_try_addr(struct net *net, u8 *id, u32 addr)
        /* Suggest previously used address if peer is known */
        n = tipc_node_find_by_id(net, id);
        if (n) {
-               addr = n->addr;
+               sugg_addr = n->addr;
+               preliminary = n->preliminary;
                tipc_node_put(n);
-               return addr;
+               if (!preliminary)
+                       return sugg_addr;
        }
 
        /* Even this node may be in conflict */
@@ -1035,7 +1068,7 @@ void tipc_node_check_dest(struct net *net, u32 addr,
                          bool *respond, bool *dupl_addr)
 {
        struct tipc_node *n;
-       struct tipc_link *l;
+       struct tipc_link *l, *snd_l;
        struct tipc_link_entry *le;
        bool addr_match = false;
        bool sign_match = false;
@@ -1049,12 +1082,27 @@ void tipc_node_check_dest(struct net *net, u32 addr,
        *dupl_addr = false;
        *respond = false;
 
-       n = tipc_node_create(net, addr, peer_id, capabilities, signature,
-                            hash_mixes);
+       n = tipc_node_create(net, addr, peer_id, capabilities, hash_mixes,
+                            false);
        if (!n)
                return;
 
        tipc_node_write_lock(n);
+       if (unlikely(!n->bc_entry.link)) {
+               snd_l = tipc_bc_sndlink(net);
+               if (!tipc_link_bc_create(net, tipc_own_addr(net),
+                                        addr, U16_MAX,
+                                        tipc_link_window(snd_l),
+                                        n->capabilities,
+                                        &n->bc_entry.inputq1,
+                                        &n->bc_entry.namedq, snd_l,
+                                        &n->bc_entry.link)) {
+                       pr_warn("Broadcast rcv link creation failed, no mem\n");
+                       tipc_node_write_unlock_fast(n);
+                       tipc_node_put(n);
+                       return;
+               }
+       }
 
        le = &n->links[b->identity];
 
@@ -2134,6 +2182,8 @@ int tipc_nl_node_dump(struct sk_buff *skb, struct netlink_callback *cb)
        }
 
        list_for_each_entry_rcu(node, &tn->node_list, list) {
+               if (node->preliminary)
+                       continue;
                if (last_addr) {
                        if (node->addr == last_addr)
                                last_addr = 0;
@@ -2649,11 +2699,6 @@ int tipc_nl_node_dump_monitor_peer(struct sk_buff *skb,
        return skb->len;
 }
 
-u32 tipc_node_get_addr(struct tipc_node *node)
-{
-       return (node) ? node->addr : 0;
-}
-
 /**
  * tipc_node_dump - dump TIPC node data
  * @n: tipc node to be dumped
index c39cd861c07d05dc046df5efc91316c77c024cdd..50f8838b32c2fdcaaabf723e704d6ed471cad693 100644 (file)
@@ -75,6 +75,7 @@ enum {
 void tipc_node_stop(struct net *net);
 bool tipc_node_get_id(struct net *net, u32 addr, u8 *id);
 u32 tipc_node_get_addr(struct tipc_node *node);
+char *tipc_node_get_id_str(struct tipc_node *node);
 u32 tipc_node_try_addr(struct net *net, u8 *id, u32 addr);
 void tipc_node_check_dest(struct net *net, u32 onode, u8 *peer_id128,
                          struct tipc_bearer *bearer,