Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

tipc: fix retrans failure due to wrong destination

When a user message is sent, TIPC will check if the socket has faced a
congestion at link layer. If that happens, it will make a sleep to wait
for the congestion to disappear. This leaves a gap for other users to
take over the socket (e.g. multi threads) since the socket is released
as well. Also, in case of connectionless (e.g. SOCK_RDM), user is free
to send messages to various destinations (e.g. via 'sendto()'), then
the socket's preformatted header has to be updated correspondingly
prior to the actual payload message building.

Unfortunately, the latter action is done before the first action which
causes a condition issue that the destination of a certain message can
be modified incorrectly in the middle, leading to wrong destination
when that message is built. Consequently, when the message is sent to
the link layer, it gets stuck there forever because the peer node will
simply reject it. After a number of retransmission attempts, the link
is eventually taken down and the retransmission failure is reported.

This commit fixes the problem by rearranging the order of actions to
prevent the race condition from occurring, so the message building is
'atomic' and its header will not be modified by anyone.

Fixes: 365ad353c256 ("tipc: reduce risk of user starvation during link congestion")
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>

authored by

Tuong Lien and committed by
David S. Miller
abc9b4e0 dca4a17d

+18 -14
+18 -14
net/tipc/socket.c
··· 1364 1364 struct tipc_msg *hdr = &tsk->phdr; 1365 1365 struct tipc_name_seq *seq; 1366 1366 struct sk_buff_head pkts; 1367 - u32 dport, dnode = 0; 1368 - u32 type, inst; 1367 + u32 dport = 0, dnode = 0; 1368 + u32 type = 0, inst = 0; 1369 1369 int mtu, rc; 1370 1370 1371 1371 if (unlikely(dlen > TIPC_MAX_USER_MSG_SIZE)) ··· 1418 1418 type = dest->addr.name.name.type; 1419 1419 inst = dest->addr.name.name.instance; 1420 1420 dnode = dest->addr.name.domain; 1421 - msg_set_type(hdr, TIPC_NAMED_MSG); 1422 - msg_set_hdr_sz(hdr, NAMED_H_SIZE); 1423 - msg_set_nametype(hdr, type); 1424 - msg_set_nameinst(hdr, inst); 1425 - msg_set_lookup_scope(hdr, tipc_node2scope(dnode)); 1426 1421 dport = tipc_nametbl_translate(net, type, inst, &dnode); 1427 - msg_set_destnode(hdr, dnode); 1428 - msg_set_destport(hdr, dport); 1429 1422 if (unlikely(!dport && !dnode)) 1430 1423 return -EHOSTUNREACH; 1431 1424 } else if (dest->addrtype == TIPC_ADDR_ID) { 1432 1425 dnode = dest->addr.id.node; 1433 - msg_set_type(hdr, TIPC_DIRECT_MSG); 1434 - msg_set_lookup_scope(hdr, 0); 1435 - msg_set_destnode(hdr, dnode); 1436 - msg_set_destport(hdr, dest->addr.id.ref); 1437 - msg_set_hdr_sz(hdr, BASIC_H_SIZE); 1438 1426 } else { 1439 1427 return -EINVAL; 1440 1428 } ··· 1432 1444 !tipc_dest_find(clinks, dnode, 0)); 1433 1445 if (unlikely(rc)) 1434 1446 return rc; 1447 + 1448 + if (dest->addrtype == TIPC_ADDR_NAME) { 1449 + msg_set_type(hdr, TIPC_NAMED_MSG); 1450 + msg_set_hdr_sz(hdr, NAMED_H_SIZE); 1451 + msg_set_nametype(hdr, type); 1452 + msg_set_nameinst(hdr, inst); 1453 + msg_set_lookup_scope(hdr, tipc_node2scope(dnode)); 1454 + msg_set_destnode(hdr, dnode); 1455 + msg_set_destport(hdr, dport); 1456 + } else { /* TIPC_ADDR_ID */ 1457 + msg_set_type(hdr, TIPC_DIRECT_MSG); 1458 + msg_set_lookup_scope(hdr, 0); 1459 + msg_set_destnode(hdr, dnode); 1460 + msg_set_destport(hdr, dest->addr.id.ref); 1461 + msg_set_hdr_sz(hdr, BASIC_H_SIZE); 1462 + } 1435 1463 1436 1464 __skb_queue_head_init(&pkts); 1437 1465 mtu = tipc_node_get_mtu(net, dnode, tsk->portid, false);