]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blobdiff - net/tipc/node.c
tipc: handle collisions of 32-bit node address hash values
[mirror_ubuntu-jammy-kernel.git] / net / tipc / node.c
index 7b0c993474061ddfa2e3bffa2e6459cffca7d493..4a95c8c155c6a5e48ca56c18909c036d37327af7 100644 (file)
@@ -115,6 +115,7 @@ struct tipc_node {
        u16 capabilities;
        u32 signature;
        u32 link_id;
+       u8 peer_id[16];
        struct list_head publ_list;
        struct list_head conn_sks;
        unsigned long keepalive_intv;
@@ -156,6 +157,7 @@ static void tipc_node_delete(struct tipc_node *node);
 static void tipc_node_timeout(struct timer_list *t);
 static void tipc_node_fsm_evt(struct tipc_node *n, int evt);
 static struct tipc_node *tipc_node_find(struct net *net, u32 addr);
+static struct tipc_node *tipc_node_find_by_id(struct net *net, u8 *id);
 static void tipc_node_put(struct tipc_node *node);
 static bool node_is_up(struct tipc_node *n);
 
@@ -245,6 +247,30 @@ static struct tipc_node *tipc_node_find(struct net *net, u32 addr)
        return node;
 }
 
+/* tipc_node_find_by_id - locate specified node object by its 128-bit id
+ * Note: this function is called only when a discovery request failed
+ * to find the node by its 32-bit id, and is not time critical
+ */
+static struct tipc_node *tipc_node_find_by_id(struct net *net, u8 *id)
+{
+       struct tipc_net *tn = tipc_net(net);
+       struct tipc_node *n;
+       bool found = false;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(n, &tn->node_list, list) {
+               read_lock_bh(&n->lock);
+               if (!memcmp(id, n->peer_id, 16) &&
+                   kref_get_unless_zero(&n->kref))
+                       found = true;
+               read_unlock_bh(&n->lock);
+               if (found)
+                       break;
+       }
+       rcu_read_unlock();
+       return found ? n : NULL;
+}
+
 static void tipc_node_read_lock(struct tipc_node *n)
 {
        read_lock_bh(&n->lock);
@@ -307,7 +333,8 @@ static void tipc_node_write_unlock(struct tipc_node *n)
        }
 }
 
-struct tipc_node *tipc_node_create(struct net *net, u32 addr, u16 capabilities)
+struct tipc_node *tipc_node_create(struct net *net, u32 addr,
+                                  u8 *peer_id, u16 capabilities)
 {
        struct tipc_net *tn = net_generic(net, tipc_net_id);
        struct tipc_node *n, *temp_node;
@@ -326,6 +353,7 @@ struct tipc_node *tipc_node_create(struct net *net, u32 addr, u16 capabilities)
                goto exit;
        }
        n->addr = addr;
+       memcpy(&n->peer_id, peer_id, 16);
        n->net = net;
        n->capabilities = capabilities;
        kref_init(&n->kref);
@@ -344,8 +372,8 @@ struct tipc_node *tipc_node_create(struct net *net, u32 addr, u16 capabilities)
        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), n->addr,
-                                U16_MAX,
+       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,
@@ -735,8 +763,51 @@ bool tipc_node_is_up(struct net *net, u32 addr)
        return retval;
 }
 
-void tipc_node_check_dest(struct net *net, u32 onode,
-                         struct tipc_bearer *b,
+static u32 tipc_node_suggest_addr(struct net *net, u32 addr)
+{
+       struct tipc_node *n;
+
+       addr ^= tipc_net(net)->random;
+       while ((n = tipc_node_find(net, addr))) {
+               tipc_node_put(n);
+               addr++;
+       }
+       return addr;
+}
+
+/* tipc_node_try_addr(): Check if addr can be used by peer, suggest other if not
+ */
+u32 tipc_node_try_addr(struct net *net, u8 *id, u32 addr)
+{
+       struct tipc_net *tn = tipc_net(net);
+       struct tipc_node *n;
+
+       /* Suggest new address if some other peer is using this one */
+       n = tipc_node_find(net, addr);
+       if (n) {
+               if (!memcmp(n->peer_id, id, NODE_ID_LEN))
+                       addr = 0;
+               tipc_node_put(n);
+               if (!addr)
+                       return 0;
+               return tipc_node_suggest_addr(net, addr);
+       }
+
+       /* Suggest previously used address if peer is known */
+       n = tipc_node_find_by_id(net, id);
+       if (n) {
+               addr = n->addr;
+               tipc_node_put(n);
+       }
+       /* Even this node may be in trial phase */
+       if (tn->trial_addr == addr)
+               return tipc_node_suggest_addr(net, addr);
+
+       return addr;
+}
+
+void tipc_node_check_dest(struct net *net, u32 addr,
+                         u8 *peer_id, struct tipc_bearer *b,
                          u16 capabilities, u32 signature,
                          struct tipc_media_addr *maddr,
                          bool *respond, bool *dupl_addr)
@@ -755,7 +826,7 @@ void tipc_node_check_dest(struct net *net, u32 onode,
        *dupl_addr = false;
        *respond = false;
 
-       n = tipc_node_create(net, onode, capabilities);
+       n = tipc_node_create(net, addr, peer_id, capabilities);
        if (!n)
                return;
 
@@ -840,7 +911,7 @@ void tipc_node_check_dest(struct net *net, u32 onode,
                if (!tipc_link_create(net, if_name, b->identity, b->tolerance,
                                      b->net_plane, b->mtu, b->priority,
                                      b->window, mod(tipc_net(net)->random),
-                                     tipc_own_addr(net), onode,
+                                     tipc_own_addr(net), addr, peer_id,
                                      n->capabilities,
                                      tipc_bc_sndlink(n->net), n->bc_entry.link,
                                      &le->inputq,