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

tipc: fix broadcast link synchronization problem

In commit 2d18ac4b ("tipc: extend broadcast link initialization
criteria") we tried to fix a problem with the initial synchronization
of broadcast link acknowledge values. Unfortunately that solution is
not sufficient to solve the issue.

We have seen it happen that LINK_PROTOCOL/STATE packets with a valid
non-zero unicast acknowledge number may bypass BCAST_PROTOCOL
initialization, NAME_DISTRIBUTOR and other STATE packets with invalid
broadcast acknowledge numbers, leading to premature opening of the
broadcast link. When the bypassed packets finally arrive, they are
inadvertently accepted, and the already correctly initialized
acknowledge number in the broadcast receive link is overwritten by
the invalid (zero) value of the said packets. After this the broadcast
link goes stale.

We now fix this by marking the packets where we know the acknowledge
value is or may be invalid, and then ignoring the acks from those.

To this purpose, we claim an unused bit in the header to indicate that
the value is invalid. We set the bit to 1 in the initial BCAST_PROTOCOL
synchronization packet and all initial ("bulk") NAME_DISTRIBUTOR
packets, plus those LINK_PROTOCOL packets sent out before the broadcast
links are fully synchronized.

This minor protocol update is fully backwards compatible.
Reported-by: default avatarJohn Thompson <thompa.atl@gmail.com>
Tested-by: default avatarJohn Thompson <thompa.atl@gmail.com>
Signed-off-by: default avatarJon Maloy <jon.maloy@ericsson.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 8bf371e6
...@@ -247,11 +247,17 @@ int tipc_bcast_rcv(struct net *net, struct tipc_link *l, struct sk_buff *skb) ...@@ -247,11 +247,17 @@ int tipc_bcast_rcv(struct net *net, struct tipc_link *l, struct sk_buff *skb)
* *
* RCU is locked, no other locks set * RCU is locked, no other locks set
*/ */
void tipc_bcast_ack_rcv(struct net *net, struct tipc_link *l, u32 acked) void tipc_bcast_ack_rcv(struct net *net, struct tipc_link *l,
struct tipc_msg *hdr)
{ {
struct sk_buff_head *inputq = &tipc_bc_base(net)->inputq; struct sk_buff_head *inputq = &tipc_bc_base(net)->inputq;
u16 acked = msg_bcast_ack(hdr);
struct sk_buff_head xmitq; struct sk_buff_head xmitq;
/* Ignore bc acks sent by peer before bcast synch point was received */
if (msg_bc_ack_invalid(hdr))
return;
__skb_queue_head_init(&xmitq); __skb_queue_head_init(&xmitq);
tipc_bcast_lock(net); tipc_bcast_lock(net);
...@@ -279,11 +285,11 @@ int tipc_bcast_sync_rcv(struct net *net, struct tipc_link *l, ...@@ -279,11 +285,11 @@ int tipc_bcast_sync_rcv(struct net *net, struct tipc_link *l,
__skb_queue_head_init(&xmitq); __skb_queue_head_init(&xmitq);
tipc_bcast_lock(net); tipc_bcast_lock(net);
if (msg_type(hdr) == STATE_MSG) { if (msg_type(hdr) != STATE_MSG) {
tipc_link_bc_init_rcv(l, hdr);
} else if (!msg_bc_ack_invalid(hdr)) {
tipc_link_bc_ack_rcv(l, msg_bcast_ack(hdr), &xmitq); tipc_link_bc_ack_rcv(l, msg_bcast_ack(hdr), &xmitq);
rc = tipc_link_bc_sync_rcv(l, hdr, &xmitq); rc = tipc_link_bc_sync_rcv(l, hdr, &xmitq);
} else {
tipc_link_bc_init_rcv(l, hdr);
} }
tipc_bcast_unlock(net); tipc_bcast_unlock(net);
......
...@@ -55,7 +55,8 @@ void tipc_bcast_dec_bearer_dst_cnt(struct net *net, int bearer_id); ...@@ -55,7 +55,8 @@ void tipc_bcast_dec_bearer_dst_cnt(struct net *net, int bearer_id);
int tipc_bcast_get_mtu(struct net *net); int tipc_bcast_get_mtu(struct net *net);
int tipc_bcast_xmit(struct net *net, struct sk_buff_head *list); int tipc_bcast_xmit(struct net *net, struct sk_buff_head *list);
int tipc_bcast_rcv(struct net *net, struct tipc_link *l, struct sk_buff *skb); int tipc_bcast_rcv(struct net *net, struct tipc_link *l, struct sk_buff *skb);
void tipc_bcast_ack_rcv(struct net *net, struct tipc_link *l, u32 acked); void tipc_bcast_ack_rcv(struct net *net, struct tipc_link *l,
struct tipc_msg *hdr);
int tipc_bcast_sync_rcv(struct net *net, struct tipc_link *l, int tipc_bcast_sync_rcv(struct net *net, struct tipc_link *l,
struct tipc_msg *hdr); struct tipc_msg *hdr);
int tipc_nl_add_bc_link(struct net *net, struct tipc_nl_msg *msg); int tipc_nl_add_bc_link(struct net *net, struct tipc_nl_msg *msg);
......
...@@ -1312,6 +1312,7 @@ static void tipc_link_build_proto_msg(struct tipc_link *l, int mtyp, bool probe, ...@@ -1312,6 +1312,7 @@ static void tipc_link_build_proto_msg(struct tipc_link *l, int mtyp, bool probe,
msg_set_next_sent(hdr, l->snd_nxt); msg_set_next_sent(hdr, l->snd_nxt);
msg_set_ack(hdr, l->rcv_nxt - 1); msg_set_ack(hdr, l->rcv_nxt - 1);
msg_set_bcast_ack(hdr, bcl->rcv_nxt - 1); msg_set_bcast_ack(hdr, bcl->rcv_nxt - 1);
msg_set_bc_ack_invalid(hdr, !node_up);
msg_set_last_bcast(hdr, l->bc_sndlink->snd_nxt - 1); msg_set_last_bcast(hdr, l->bc_sndlink->snd_nxt - 1);
msg_set_link_tolerance(hdr, tolerance); msg_set_link_tolerance(hdr, tolerance);
msg_set_linkprio(hdr, priority); msg_set_linkprio(hdr, priority);
...@@ -1574,6 +1575,7 @@ static void tipc_link_build_bc_init_msg(struct tipc_link *l, ...@@ -1574,6 +1575,7 @@ static void tipc_link_build_bc_init_msg(struct tipc_link *l,
__skb_queue_head_init(&list); __skb_queue_head_init(&list);
if (!tipc_link_build_bc_proto_msg(l->bc_rcvlink, false, 0, &list)) if (!tipc_link_build_bc_proto_msg(l->bc_rcvlink, false, 0, &list))
return; return;
msg_set_bc_ack_invalid(buf_msg(skb_peek(&list)), true);
tipc_link_xmit(l, &list, xmitq); tipc_link_xmit(l, &list, xmitq);
} }
......
...@@ -714,6 +714,23 @@ static inline void msg_set_peer_stopping(struct tipc_msg *m, u32 s) ...@@ -714,6 +714,23 @@ static inline void msg_set_peer_stopping(struct tipc_msg *m, u32 s)
msg_set_bits(m, 5, 13, 0x1, s); msg_set_bits(m, 5, 13, 0x1, s);
} }
static inline bool msg_bc_ack_invalid(struct tipc_msg *m)
{
switch (msg_user(m)) {
case BCAST_PROTOCOL:
case NAME_DISTRIBUTOR:
case LINK_PROTOCOL:
return msg_bits(m, 5, 14, 0x1);
default:
return false;
}
}
static inline void msg_set_bc_ack_invalid(struct tipc_msg *m, bool invalid)
{
msg_set_bits(m, 5, 14, 0x1, invalid);
}
static inline char *msg_media_addr(struct tipc_msg *m) static inline char *msg_media_addr(struct tipc_msg *m)
{ {
return (char *)&m->hdr[TIPC_MEDIA_INFO_OFFSET]; return (char *)&m->hdr[TIPC_MEDIA_INFO_OFFSET];
......
...@@ -156,6 +156,7 @@ static void named_distribute(struct net *net, struct sk_buff_head *list, ...@@ -156,6 +156,7 @@ static void named_distribute(struct net *net, struct sk_buff_head *list,
pr_warn("Bulk publication failure\n"); pr_warn("Bulk publication failure\n");
return; return;
} }
msg_set_bc_ack_invalid(buf_msg(skb), true);
item = (struct distr_item *)msg_data(buf_msg(skb)); item = (struct distr_item *)msg_data(buf_msg(skb));
} }
......
...@@ -1535,7 +1535,7 @@ void tipc_rcv(struct net *net, struct sk_buff *skb, struct tipc_bearer *b) ...@@ -1535,7 +1535,7 @@ void tipc_rcv(struct net *net, struct sk_buff *skb, struct tipc_bearer *b)
if (unlikely(usr == LINK_PROTOCOL)) if (unlikely(usr == LINK_PROTOCOL))
tipc_node_bc_sync_rcv(n, hdr, bearer_id, &xmitq); tipc_node_bc_sync_rcv(n, hdr, bearer_id, &xmitq);
else if (unlikely(tipc_link_acked(n->bc_entry.link) != bc_ack)) else if (unlikely(tipc_link_acked(n->bc_entry.link) != bc_ack))
tipc_bcast_ack_rcv(net, n->bc_entry.link, bc_ack); tipc_bcast_ack_rcv(net, n->bc_entry.link, hdr);
/* Receive packet directly if conditions permit */ /* Receive packet directly if conditions permit */
tipc_node_read_lock(n); tipc_node_read_lock(n);
......
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