Commit c82910e2 authored by Jon Paul Maloy's avatar Jon Paul Maloy Committed by David S. Miller

tipc: clean up neigbor discovery message reception

The function tipc_disc_rcv(), which is handling received neighbor
discovery messages, is perceived as messy, and it is hard to verify
its correctness by code inspection. The fact that the task it is set
to resolve is fairly complex does not make the situation better.

In this commit we try to take a more systematic approach to the
problem. We define a decision machine which takes three state flags
 as input, and produces three action flags as output. We then walk
through all permutations of the state flags, and for each of them we
describe verbally what is going on, plus that we set zero or more of
the action flags. The action flags indicate what should be done once
the decision machine has finished its job, while the last part of the
function deals with performing those actions.
Signed-off-by: default avatarJon Maloy <jon.maloy@ericsson.com>
Reviewed-by: default avatarYing Xue <ying.xue@windriver.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 38504c28
/* /*
* net/tipc/discover.c * 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 * Copyright (c) 2005-2006, 2010-2011, Wind River Systems
* All rights reserved. * All rights reserved.
* *
...@@ -106,147 +106,150 @@ static void disc_dupl_alert(struct tipc_bearer *b_ptr, u32 node_addr, ...@@ -106,147 +106,150 @@ static void disc_dupl_alert(struct tipc_bearer *b_ptr, u32 node_addr,
} }
/** /**
* 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 * @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_link *link;
struct tipc_media_addr media_addr; struct tipc_media_addr maddr;
struct sk_buff *rbuf; struct sk_buff *rbuf;
struct tipc_msg *msg = buf_msg(buf); struct tipc_msg *msg = buf_msg(buf);
u32 dest = msg_dest_domain(msg); u32 ddom = msg_dest_domain(msg);
u32 orig = msg_prevnode(msg); u32 onode = msg_prevnode(msg);
u32 net_id = msg_bc_netid(msg); u32 net_id = msg_bc_netid(msg);
u32 type = msg_type(msg); u32 mtyp = msg_type(msg);
u32 signature = msg_node_sig(msg); u32 signature = msg_node_sig(msg);
int addr_mismatch; bool addr_match = false;
int link_fully_up; bool sign_match = false;
bool link_up = false;
media_addr.broadcast = 1; bool accept_addr = false;
b_ptr->media->msg2addr(b_ptr, &media_addr, msg_media_addr(msg)); bool accept_sign = false;
bool respond = false;
bearer->media->msg2addr(bearer, &maddr, msg_media_addr(msg));
kfree_skb(buf); kfree_skb(buf);
/* Ensure message from node is valid and communication is permitted */ /* Ensure message from node is valid and communication is permitted */
if (net_id != tipc_net_id) if (net_id != tipc_net_id)
return; return;
if (media_addr.broadcast) if (maddr.broadcast)
return; return;
if (!tipc_addr_domain_valid(dest)) if (!tipc_addr_domain_valid(ddom))
return; return;
if (!tipc_addr_node_valid(orig)) if (!tipc_addr_node_valid(onode))
return; return;
if (orig == tipc_own_addr) {
if (memcmp(&media_addr, &b_ptr->addr, sizeof(media_addr))) if (in_own_node(onode)) {
disc_dupl_alert(b_ptr, tipc_own_addr, &media_addr); if (memcmp(&maddr, &bearer->addr, sizeof(maddr)))
disc_dupl_alert(bearer, tipc_own_addr, &maddr);
return; return;
} }
if (!tipc_in_scope(dest, tipc_own_addr)) if (!tipc_in_scope(ddom, tipc_own_addr))
return; return;
if (!tipc_in_scope(b_ptr->domain, orig)) if (!tipc_in_scope(bearer->domain, onode))
return; return;
/* Locate structure corresponding to requesting node */ /* Locate, or if necessary, create, node: */
n_ptr = tipc_node_find(orig); node = tipc_node_find(onode);
if (!n_ptr) { if (!node)
n_ptr = tipc_node_create(orig); node = tipc_node_create(onode);
if (!n_ptr) if (!node)
return; return;
}
tipc_node_lock(n_ptr);
/* Prepare to validate requesting node's signature and media address */ tipc_node_lock(node);
link = n_ptr->links[b_ptr->identity]; link = node->links[bearer->identity];
addr_mismatch = (link != NULL) &&
memcmp(&link->media_addr, &media_addr, sizeof(media_addr));
/* /* Prepare to validate requesting node's signature and media address */
* Ensure discovery message's signature is correct sign_match = (signature == node->signature);
* addr_match = link && !memcmp(&link->media_addr, &maddr, sizeof(maddr));
* If signature is incorrect and there is no working link to the node, link_up = link && tipc_link_is_up(link);
* accept the new signature but invalidate all existing links to the
* node so they won't re-activate without a new discovery message.
* /* These three flags give us eight permutations: */
* If signature is incorrect and the requested link to the node is
* working, accept the new signature. (This is an instance of delayed if (sign_match && addr_match && link_up) {
* rediscovery, where a link endpoint was able to re-establish contact /* All is fine. Do nothing. */
* with its peer endpoint on a node that rebooted before receiving a } else if (sign_match && addr_match && !link_up) {
* discovery message from that node.) /* Respond. The link will come up in due time */
* respond = true;
* If signature is incorrect and there is a working link to the node } else if (sign_match && !addr_match && link_up) {
* that is not the requested link, reject the request (must be from /* Peer has changed i/f address without rebooting.
* a duplicate node). * If so, the link will reset soon, and the next
*/ * discovery will be accepted. So we can ignore it.
if (signature != n_ptr->signature) { * It may also be an cloned or malicious peer having
if (n_ptr->working_links == 0) { * chosen the same node address and signature as an
struct tipc_link *curr_link; * existing one.
int i; * Ignore requests until the link goes down, if ever.
*/
for (i = 0; i < MAX_BEARERS; i++) { disc_dupl_alert(bearer, onode, &maddr);
curr_link = n_ptr->links[i]; } else if (sign_match && !addr_match && !link_up) {
if (curr_link) { /* Peer link has changed i/f address without rebooting.
memset(&curr_link->media_addr, 0, * It may also be a cloned or malicious peer; we can't
sizeof(media_addr)); * distinguish between the two.
tipc_link_reset(curr_link); * The signature is correct, so we must accept.
} */
} accept_addr = true;
addr_mismatch = (link != NULL); respond = true;
} else if (tipc_link_is_up(link) && !addr_mismatch) { } else if (!sign_match && addr_match && link_up) {
/* delayed rediscovery */ /* Peer node rebooted. Two possibilities:
} else { * - Delayed re-discovery; this link endpoint has already
disc_dupl_alert(b_ptr, orig, &media_addr); * reset and re-established contact with the peer, before
tipc_node_unlock(n_ptr); * receiving a discovery message from that node.
return; * (The peer happened to receive one from this node first).
} * - The peer came back so fast that our side has not
n_ptr->signature = signature; * 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;
} }
/* if (accept_sign)
* Ensure requesting node's media address is correct node->signature = signature;
*
* 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);
}
}
/* Create a link endpoint for this bearer, if necessary */ if (accept_addr) {
if (!link) { if (!link)
link = tipc_link_create(n_ptr, b_ptr, &media_addr); link = tipc_link_create(node, bearer, &maddr);
if (!link) { if (link) {
tipc_node_unlock(n_ptr); memcpy(&link->media_addr, &maddr, sizeof(maddr));
return; tipc_link_reset(link);
} else {
respond = false;
} }
} }
/* Accept discovery message & send response, if necessary */ /* Send response, if necessary */
link_fully_up = link_working_working(link); if (respond && (mtyp == DSC_REQ_MSG)) {
if ((type == DSC_REQ_MSG) && !link_fully_up) {
rbuf = tipc_buf_acquire(INT_H_SIZE); rbuf = tipc_buf_acquire(INT_H_SIZE);
if (rbuf) { if (rbuf) {
tipc_disc_init_msg(rbuf, DSC_RESP_MSG, b_ptr); tipc_disc_init_msg(rbuf, DSC_RESP_MSG, bearer);
tipc_bearer_send(b_ptr->identity, rbuf, &media_addr); tipc_bearer_send(bearer->identity, rbuf, &maddr);
kfree_skb(rbuf); kfree_skb(rbuf);
} }
} }
tipc_node_unlock(node);
tipc_node_unlock(n_ptr);
} }
/** /**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment