/*
  * net/tipc/discover.c
  *
- * Copyright (c) 2003-2006, Ericsson AB
+ * Copyright (c) 2003-2006, 2014, Ericsson AB
  * Copyright (c) 2005-2006, 2010-2011, Wind River Systems
  * All rights reserved.
  *
 }
 
 /**
- * tipc_disc_rcv - handle incoming link setup message (request or response)
+ * tipc_disc_rcv - handle incoming discovery message (request or response)
  * @buf: buffer containing message
- * @b_ptr: bearer that message arrived on
+ * @bearer: bearer that message arrived on
  */
-void tipc_disc_rcv(struct sk_buff *buf, struct tipc_bearer *b_ptr)
+void tipc_disc_rcv(struct sk_buff *buf, struct tipc_bearer *bearer)
 {
-       struct tipc_node *n_ptr;
+       struct tipc_node *node;
        struct tipc_link *link;
-       struct tipc_media_addr media_addr;
+       struct tipc_media_addr maddr;
        struct sk_buff *rbuf;
        struct tipc_msg *msg = buf_msg(buf);
-       u32 dest = msg_dest_domain(msg);
-       u32 orig = msg_prevnode(msg);
+       u32 ddom = msg_dest_domain(msg);
+       u32 onode = msg_prevnode(msg);
        u32 net_id = msg_bc_netid(msg);
-       u32 type = msg_type(msg);
+       u32 mtyp = msg_type(msg);
        u32 signature = msg_node_sig(msg);
-       int addr_mismatch;
-       int link_fully_up;
-
-       media_addr.broadcast = 1;
-       b_ptr->media->msg2addr(b_ptr, &media_addr, msg_media_addr(msg));
+       bool addr_match = false;
+       bool sign_match = false;
+       bool link_up = false;
+       bool accept_addr = false;
+       bool accept_sign = false;
+       bool respond = false;
+
+       bearer->media->msg2addr(bearer, &maddr, msg_media_addr(msg));
        kfree_skb(buf);
 
        /* Ensure message from node is valid and communication is permitted */
        if (net_id != tipc_net_id)
                return;
-       if (media_addr.broadcast)
+       if (maddr.broadcast)
                return;
-       if (!tipc_addr_domain_valid(dest))
+       if (!tipc_addr_domain_valid(ddom))
                return;
-       if (!tipc_addr_node_valid(orig))
+       if (!tipc_addr_node_valid(onode))
                return;
-       if (orig == tipc_own_addr) {
-               if (memcmp(&media_addr, &b_ptr->addr, sizeof(media_addr)))
-                       disc_dupl_alert(b_ptr, tipc_own_addr, &media_addr);
+
+       if (in_own_node(onode)) {
+               if (memcmp(&maddr, &bearer->addr, sizeof(maddr)))
+                       disc_dupl_alert(bearer, tipc_own_addr, &maddr);
                return;
        }
-       if (!tipc_in_scope(dest, tipc_own_addr))
+       if (!tipc_in_scope(ddom, tipc_own_addr))
                return;
-       if (!tipc_in_scope(b_ptr->domain, orig))
+       if (!tipc_in_scope(bearer->domain, onode))
                return;
 
-       /* Locate structure corresponding to requesting node */
-       n_ptr = tipc_node_find(orig);
-       if (!n_ptr) {
-               n_ptr = tipc_node_create(orig);
-               if (!n_ptr)
-                       return;
-       }
-       tipc_node_lock(n_ptr);
+       /* Locate, or if necessary, create, node: */
+       node = tipc_node_find(onode);
+       if (!node)
+               node = tipc_node_create(onode);
+       if (!node)
+               return;
 
-       /* Prepare to validate requesting node's signature and media address */
-       link = n_ptr->links[b_ptr->identity];
-       addr_mismatch = (link != NULL) &&
-               memcmp(&link->media_addr, &media_addr, sizeof(media_addr));
+       tipc_node_lock(node);
+       link = node->links[bearer->identity];
 
-       /*
-        * Ensure discovery message's signature is correct
-        *
-        * If signature is incorrect and there is no working link to the node,
-        * accept the new signature but invalidate all existing links to the
-        * node so they won't re-activate without a new discovery message.
-        *
-        * If signature is incorrect and the requested link to the node is
-        * working, accept the new signature. (This is an instance of delayed
-        * rediscovery, where a link endpoint was able to re-establish contact
-        * with its peer endpoint on a node that rebooted before receiving a
-        * discovery message from that node.)
-        *
-        * If signature is incorrect and there is a working link to the node
-        * that is not the requested link, reject the request (must be from
-        * a duplicate node).
-        */
-       if (signature != n_ptr->signature) {
-               if (n_ptr->working_links == 0) {
-                       struct tipc_link *curr_link;
-                       int i;
-
-                       for (i = 0; i < MAX_BEARERS; i++) {
-                               curr_link = n_ptr->links[i];
-                               if (curr_link) {
-                                       memset(&curr_link->media_addr, 0,
-                                              sizeof(media_addr));
-                                       tipc_link_reset(curr_link);
-                               }
-                       }
-                       addr_mismatch = (link != NULL);
-               } else if (tipc_link_is_up(link) && !addr_mismatch) {
-                       /* delayed rediscovery */
-               } else {
-                       disc_dupl_alert(b_ptr, orig, &media_addr);
-                       tipc_node_unlock(n_ptr);
-                       return;
-               }
-               n_ptr->signature = signature;
+       /* Prepare to validate requesting node's signature and media address */
+       sign_match = (signature == node->signature);
+       addr_match = link && !memcmp(&link->media_addr, &maddr, sizeof(maddr));
+       link_up = link && tipc_link_is_up(link);
+
+
+       /* These three flags give us eight permutations: */
+
+       if (sign_match && addr_match && link_up) {
+               /* All is fine. Do nothing. */
+       } else if (sign_match && addr_match && !link_up) {
+               /* Respond. The link will come up in due time */
+               respond = true;
+       } else if (sign_match && !addr_match && link_up) {
+               /* Peer has changed i/f address without rebooting.
+                * If so, the link will reset soon, and the next
+                * discovery will be accepted. So we can ignore it.
+                * It may also be an cloned or malicious peer having
+                * chosen the same node address and signature as an
+                * existing one.
+                * Ignore requests until the link goes down, if ever.
+                */
+               disc_dupl_alert(bearer, onode, &maddr);
+       } else if (sign_match && !addr_match && !link_up) {
+               /* Peer link has changed i/f address without rebooting.
+                * It may also be a cloned or malicious peer; we can't
+                * distinguish between the two.
+                * The signature is correct, so we must accept.
+                */
+               accept_addr = true;
+               respond = true;
+       } else if (!sign_match && addr_match && link_up) {
+               /* Peer node rebooted. Two possibilities:
+                *  - Delayed re-discovery; this link endpoint has already
+                *    reset and re-established contact with the peer, before
+                *    receiving a discovery message from that node.
+                *    (The peer happened to receive one from this node first).
+                *  - The peer came back so fast that our side has not
+                *    discovered it yet. Probing from this side will soon
+                *    reset the link, since there can be no working link
+                *    endpoint at the peer end, and the link will re-establish.
+                *  Accept the signature, since it comes from a known peer.
+                */
+               accept_sign = true;
+       } else if (!sign_match && addr_match && !link_up) {
+               /*  The peer node has rebooted.
+                *  Accept signature, since it is a known peer.
+                */
+               accept_sign = true;
+               respond = true;
+       } else if (!sign_match && !addr_match && link_up) {
+               /* Peer rebooted with new address, or a new/duplicate peer.
+                * Ignore until the link goes down, if ever.
+                */
+               disc_dupl_alert(bearer, onode, &maddr);
+       } else if (!sign_match && !addr_match && !link_up) {
+               /* Peer rebooted with new address, or it is a new peer.
+                * Accept signature and address.
+               */
+               accept_sign = true;
+               accept_addr = true;
+               respond = true;
        }
 
-       /*
-        * Ensure requesting node's media address is correct
-        *
-        * If media address doesn't match and the link is working, reject the
-        * request (must be from a duplicate node).
-        *
-        * If media address doesn't match and the link is not working, accept
-        * the new media address and reset the link to ensure it starts up
-        * cleanly.
-        */
-       if (addr_mismatch) {
-               if (tipc_link_is_up(link)) {
-                       disc_dupl_alert(b_ptr, orig, &media_addr);
-                       tipc_node_unlock(n_ptr);
-                       return;
-               } else {
-                       memcpy(&link->media_addr, &media_addr,
-                              sizeof(media_addr));
-                       tipc_link_reset(link);
-               }
-       }
+       if (accept_sign)
+               node->signature = signature;
 
-       /* Create a link endpoint for this bearer, if necessary */
-       if (!link) {
-               link = tipc_link_create(n_ptr, b_ptr, &media_addr);
-               if (!link) {
-                       tipc_node_unlock(n_ptr);
-                       return;
+       if (accept_addr) {
+               if (!link)
+                       link = tipc_link_create(node, bearer, &maddr);
+               if (link) {
+                       memcpy(&link->media_addr, &maddr, sizeof(maddr));
+                       tipc_link_reset(link);
+               } else {
+                       respond = false;
                }
        }
 
-       /* Accept discovery message & send response, if necessary */
-       link_fully_up = link_working_working(link);
-
-       if ((type == DSC_REQ_MSG) && !link_fully_up) {
+       /* Send response, if necessary */
+       if (respond && (mtyp == DSC_REQ_MSG)) {
                rbuf = tipc_buf_acquire(INT_H_SIZE);
                if (rbuf) {
-                       tipc_disc_init_msg(rbuf, DSC_RESP_MSG, b_ptr);
-                       tipc_bearer_send(b_ptr->identity, rbuf, &media_addr);
+                       tipc_disc_init_msg(rbuf, DSC_RESP_MSG, bearer);
+                       tipc_bearer_send(bearer->identity, rbuf, &maddr);
                        kfree_skb(rbuf);
                }
        }
-
-       tipc_node_unlock(n_ptr);
+       tipc_node_unlock(node);
 }
 
 /**