Skip to content

Commit f73b128

Browse files
Hoang Ledavem330
Hoang Le
authored andcommitted
tipc: improve throughput between nodes in netns
Currently, TIPC transports intra-node user data messages directly socket to socket, hence shortcutting all the lower layers of the communication stack. This gives TIPC very good intra node performance, both regarding throughput and latency. We now introduce a similar mechanism for TIPC data traffic across network namespaces located in the same kernel. On the send path, the call chain is as always accompanied by the sending node's network name space pointer. However, once we have reliably established that the receiving node is represented by a namespace on the same host, we just replace the namespace pointer with the receiving node/namespace's ditto, and follow the regular socket receive patch though the receiving node. This technique gives us a throughput similar to the node internal throughput, several times larger than if we let the traffic go though the full network stacks. As a comparison, max throughput for 64k messages is four times larger than TCP throughput for the same type of traffic. To meet any security concerns, the following should be noted. - All nodes joining a cluster are supposed to have been be certified and authenticated by mechanisms outside TIPC. This is no different for nodes/namespaces on the same host; they have to auto discover each other using the attached interfaces, and establish links which are supervised via the regular link monitoring mechanism. Hence, a kernel local node has no other way to join a cluster than any other node, and have to obey to policies set in the IP or device layers of the stack. - Only when a sender has established with 100% certainty that the peer node is located in a kernel local namespace does it choose to let user data messages, and only those, take the crossover path to the receiving node/namespace. - If the receiving node/namespace is removed, its namespace pointer is invalidated at all peer nodes, and their neighbor link monitoring will eventually note that this node is gone. - To ensure the "100% certainty" criteria, and prevent any possible spoofing, received discovery messages must contain a proof that the sender knows a common secret. We use the hash mix of the sending node/namespace for this purpose, since it can be accessed directly by all other namespaces in the kernel. Upon reception of a discovery message, the receiver checks this proof against all the local namespaces'hash_mix:es. If it finds a match, that, along with a matching node id and cluster id, this is deemed sufficient proof that the peer node in question is in a local namespace, and a wormhole can be opened. - We should also consider that TIPC is intended to be a cluster local IPC mechanism (just like e.g. UNIX sockets) rather than a network protocol, and hence we think it can justified to allow it to shortcut the lower protocol layers. Regarding traceability, we should notice that since commit 6c9081a ("tipc: add loopback device tracking") it is possible to follow the node internal packet flow by just activating tcpdump on the loopback interface. This will be true even for this mechanism; by activating tcpdump on the involved nodes' loopback interfaces their inter-name space messaging can easily be tracked. v2: - update 'net' pointer when node left/rejoined v3: - grab read/write lock when using node ref obj v4: - clone traffics between netns to loopback Suggested-by: Jon Maloy <jon.maloy@ericsson.com> Acked-by: Jon Maloy <jon.maloy@ericsson.com> Signed-off-by: Hoang Le <hoang.h.le@dektech.com.au> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 51210ad commit f73b128

File tree

8 files changed

+197
-11
lines changed

8 files changed

+197
-11
lines changed

net/tipc/core.c

+16
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,15 @@ static void __net_exit tipc_exit_net(struct net *net)
105105
tipc_sk_rht_destroy(net);
106106
}
107107

108+
static void __net_exit tipc_pernet_pre_exit(struct net *net)
109+
{
110+
tipc_node_pre_cleanup_net(net);
111+
}
112+
113+
static struct pernet_operations tipc_pernet_pre_exit_ops = {
114+
.pre_exit = tipc_pernet_pre_exit,
115+
};
116+
108117
static struct pernet_operations tipc_net_ops = {
109118
.init = tipc_init_net,
110119
.exit = tipc_exit_net,
@@ -151,13 +160,19 @@ static int __init tipc_init(void)
151160
if (err)
152161
goto out_pernet_topsrv;
153162

163+
err = register_pernet_subsys(&tipc_pernet_pre_exit_ops);
164+
if (err)
165+
goto out_register_pernet_subsys;
166+
154167
err = tipc_bearer_setup();
155168
if (err)
156169
goto out_bearer;
157170

158171
pr_info("Started in single node mode\n");
159172
return 0;
160173
out_bearer:
174+
unregister_pernet_subsys(&tipc_pernet_pre_exit_ops);
175+
out_register_pernet_subsys:
161176
unregister_pernet_device(&tipc_topsrv_net_ops);
162177
out_pernet_topsrv:
163178
tipc_socket_stop();
@@ -177,6 +192,7 @@ static int __init tipc_init(void)
177192
static void __exit tipc_exit(void)
178193
{
179194
tipc_bearer_cleanup();
195+
unregister_pernet_subsys(&tipc_pernet_pre_exit_ops);
180196
unregister_pernet_device(&tipc_topsrv_net_ops);
181197
tipc_socket_stop();
182198
unregister_pernet_device(&tipc_net_ops);

net/tipc/core.h

+6
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
#include <net/netns/generic.h>
6060
#include <linux/rhashtable.h>
6161
#include <net/genetlink.h>
62+
#include <net/netns/hash.h>
6263

6364
struct tipc_node;
6465
struct tipc_bearer;
@@ -185,6 +186,11 @@ static inline int in_range(u16 val, u16 min, u16 max)
185186
return !less(val, min) && !more(val, max);
186187
}
187188

189+
static inline u32 tipc_net_hash_mixes(struct net *net, int tn_rand)
190+
{
191+
return net_hash_mix(&init_net) ^ net_hash_mix(net) ^ tn_rand;
192+
}
193+
188194
#ifdef CONFIG_SYSCTL
189195
int tipc_register_sysctl(void);
190196
void tipc_unregister_sysctl(void);

net/tipc/discover.c

+3-1
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ static void tipc_disc_init_msg(struct net *net, struct sk_buff *skb,
9494
msg_set_dest_domain(hdr, dest_domain);
9595
msg_set_bc_netid(hdr, tn->net_id);
9696
b->media->addr2msg(msg_media_addr(hdr), &b->addr);
97+
msg_set_peer_net_hash(hdr, tipc_net_hash_mixes(net, tn->random));
9798
msg_set_node_id(hdr, tipc_own_id(net));
9899
}
99100

@@ -242,7 +243,8 @@ void tipc_disc_rcv(struct net *net, struct sk_buff *skb,
242243
if (!tipc_in_scope(legacy, b->domain, src))
243244
return;
244245
tipc_node_check_dest(net, src, peer_id, b, caps, signature,
245-
&maddr, &respond, &dupl_addr);
246+
msg_peer_net_hash(hdr), &maddr, &respond,
247+
&dupl_addr);
246248
if (dupl_addr)
247249
disc_dupl_alert(b, src, &maddr);
248250
if (!respond)

net/tipc/msg.h

+14
Original file line numberDiff line numberDiff line change
@@ -1026,6 +1026,20 @@ static inline bool msg_is_reset(struct tipc_msg *hdr)
10261026
return (msg_user(hdr) == LINK_PROTOCOL) && (msg_type(hdr) == RESET_MSG);
10271027
}
10281028

1029+
/* Word 13
1030+
*/
1031+
static inline void msg_set_peer_net_hash(struct tipc_msg *m, u32 n)
1032+
{
1033+
msg_set_word(m, 13, n);
1034+
}
1035+
1036+
static inline u32 msg_peer_net_hash(struct tipc_msg *m)
1037+
{
1038+
return msg_word(m, 13);
1039+
}
1040+
1041+
/* Word 14
1042+
*/
10291043
static inline u32 msg_sugg_node_addr(struct tipc_msg *m)
10301044
{
10311045
return msg_word(m, 14);

net/tipc/name_distr.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ static void named_distribute(struct net *net, struct sk_buff_head *list,
146146
struct publication *publ;
147147
struct sk_buff *skb = NULL;
148148
struct distr_item *item = NULL;
149-
u32 msg_dsz = ((tipc_node_get_mtu(net, dnode, 0) - INT_H_SIZE) /
149+
u32 msg_dsz = ((tipc_node_get_mtu(net, dnode, 0, false) - INT_H_SIZE) /
150150
ITEM_SIZE) * ITEM_SIZE;
151151
u32 msg_rem = msg_dsz;
152152

net/tipc/node.c

+151-4
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,8 @@ struct tipc_node {
126126
struct timer_list timer;
127127
struct rcu_head rcu;
128128
unsigned long delete_at;
129+
struct net *peer_net;
130+
u32 peer_hash_mix;
129131
};
130132

131133
/* Node FSM states and events:
@@ -184,7 +186,7 @@ static struct tipc_link *node_active_link(struct tipc_node *n, int sel)
184186
return n->links[bearer_id].link;
185187
}
186188

187-
int tipc_node_get_mtu(struct net *net, u32 addr, u32 sel)
189+
int tipc_node_get_mtu(struct net *net, u32 addr, u32 sel, bool connected)
188190
{
189191
struct tipc_node *n;
190192
int bearer_id;
@@ -194,6 +196,14 @@ int tipc_node_get_mtu(struct net *net, u32 addr, u32 sel)
194196
if (unlikely(!n))
195197
return mtu;
196198

199+
/* Allow MAX_MSG_SIZE when building connection oriented message
200+
* if they are in the same core network
201+
*/
202+
if (n->peer_net && connected) {
203+
tipc_node_put(n);
204+
return mtu;
205+
}
206+
197207
bearer_id = n->active_links[sel & 1];
198208
if (likely(bearer_id != INVALID_BEARER_ID))
199209
mtu = n->links[bearer_id].mtu;
@@ -360,8 +370,37 @@ static void tipc_node_write_unlock(struct tipc_node *n)
360370
}
361371
}
362372

373+
static void tipc_node_assign_peer_net(struct tipc_node *n, u32 hash_mixes)
374+
{
375+
int net_id = tipc_netid(n->net);
376+
struct tipc_net *tn_peer;
377+
struct net *tmp;
378+
u32 hash_chk;
379+
380+
if (n->peer_net)
381+
return;
382+
383+
for_each_net_rcu(tmp) {
384+
tn_peer = tipc_net(tmp);
385+
if (!tn_peer)
386+
continue;
387+
/* Integrity checking whether node exists in namespace or not */
388+
if (tn_peer->net_id != net_id)
389+
continue;
390+
if (memcmp(n->peer_id, tn_peer->node_id, NODE_ID_LEN))
391+
continue;
392+
hash_chk = tipc_net_hash_mixes(tmp, tn_peer->random);
393+
if (hash_mixes ^ hash_chk)
394+
continue;
395+
n->peer_net = tmp;
396+
n->peer_hash_mix = hash_mixes;
397+
break;
398+
}
399+
}
400+
363401
static struct tipc_node *tipc_node_create(struct net *net, u32 addr,
364-
u8 *peer_id, u16 capabilities)
402+
u8 *peer_id, u16 capabilities,
403+
u32 signature, u32 hash_mixes)
365404
{
366405
struct tipc_net *tn = net_generic(net, tipc_net_id);
367406
struct tipc_node *n, *temp_node;
@@ -372,6 +411,8 @@ static struct tipc_node *tipc_node_create(struct net *net, u32 addr,
372411
spin_lock_bh(&tn->node_list_lock);
373412
n = tipc_node_find(net, addr);
374413
if (n) {
414+
if (n->peer_hash_mix ^ hash_mixes)
415+
tipc_node_assign_peer_net(n, hash_mixes);
375416
if (n->capabilities == capabilities)
376417
goto exit;
377418
/* Same node may come back with new capabilities */
@@ -389,6 +430,7 @@ static struct tipc_node *tipc_node_create(struct net *net, u32 addr,
389430
list_for_each_entry_rcu(temp_node, &tn->node_list, list) {
390431
tn->capabilities &= temp_node->capabilities;
391432
}
433+
392434
goto exit;
393435
}
394436
n = kzalloc(sizeof(*n), GFP_ATOMIC);
@@ -399,6 +441,10 @@ static struct tipc_node *tipc_node_create(struct net *net, u32 addr,
399441
n->addr = addr;
400442
memcpy(&n->peer_id, peer_id, 16);
401443
n->net = net;
444+
n->peer_net = NULL;
445+
n->peer_hash_mix = 0;
446+
/* Assign kernel local namespace if exists */
447+
tipc_node_assign_peer_net(n, hash_mixes);
402448
n->capabilities = capabilities;
403449
kref_init(&n->kref);
404450
rwlock_init(&n->lock);
@@ -426,6 +472,10 @@ static struct tipc_node *tipc_node_create(struct net *net, u32 addr,
426472
tipc_bc_sndlink(net),
427473
&n->bc_entry.link)) {
428474
pr_warn("Broadcast rcv link creation failed, no memory\n");
475+
if (n->peer_net) {
476+
n->peer_net = NULL;
477+
n->peer_hash_mix = 0;
478+
}
429479
kfree(n);
430480
n = NULL;
431481
goto exit;
@@ -979,7 +1029,7 @@ u32 tipc_node_try_addr(struct net *net, u8 *id, u32 addr)
9791029

9801030
void tipc_node_check_dest(struct net *net, u32 addr,
9811031
u8 *peer_id, struct tipc_bearer *b,
982-
u16 capabilities, u32 signature,
1032+
u16 capabilities, u32 signature, u32 hash_mixes,
9831033
struct tipc_media_addr *maddr,
9841034
bool *respond, bool *dupl_addr)
9851035
{
@@ -998,7 +1048,8 @@ void tipc_node_check_dest(struct net *net, u32 addr,
9981048
*dupl_addr = false;
9991049
*respond = false;
10001050

1001-
n = tipc_node_create(net, addr, peer_id, capabilities);
1051+
n = tipc_node_create(net, addr, peer_id, capabilities, signature,
1052+
hash_mixes);
10021053
if (!n)
10031054
return;
10041055

@@ -1343,6 +1394,10 @@ static void node_lost_contact(struct tipc_node *n,
13431394
/* Notify publications from this node */
13441395
n->action_flags |= TIPC_NOTIFY_NODE_DOWN;
13451396

1397+
if (n->peer_net) {
1398+
n->peer_net = NULL;
1399+
n->peer_hash_mix = 0;
1400+
}
13461401
/* Notify sockets connected to node */
13471402
list_for_each_entry_safe(conn, safe, conns, list) {
13481403
skb = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, TIPC_CONN_MSG,
@@ -1424,6 +1479,56 @@ static int __tipc_nl_add_node(struct tipc_nl_msg *msg, struct tipc_node *node)
14241479
return -EMSGSIZE;
14251480
}
14261481

1482+
static void tipc_lxc_xmit(struct net *peer_net, struct sk_buff_head *list)
1483+
{
1484+
struct tipc_msg *hdr = buf_msg(skb_peek(list));
1485+
struct sk_buff_head inputq;
1486+
1487+
switch (msg_user(hdr)) {
1488+
case TIPC_LOW_IMPORTANCE:
1489+
case TIPC_MEDIUM_IMPORTANCE:
1490+
case TIPC_HIGH_IMPORTANCE:
1491+
case TIPC_CRITICAL_IMPORTANCE:
1492+
if (msg_connected(hdr) || msg_named(hdr)) {
1493+
tipc_loopback_trace(peer_net, list);
1494+
spin_lock_init(&list->lock);
1495+
tipc_sk_rcv(peer_net, list);
1496+
return;
1497+
}
1498+
if (msg_mcast(hdr)) {
1499+
tipc_loopback_trace(peer_net, list);
1500+
skb_queue_head_init(&inputq);
1501+
tipc_sk_mcast_rcv(peer_net, list, &inputq);
1502+
__skb_queue_purge(list);
1503+
skb_queue_purge(&inputq);
1504+
return;
1505+
}
1506+
return;
1507+
case MSG_FRAGMENTER:
1508+
if (tipc_msg_assemble(list)) {
1509+
tipc_loopback_trace(peer_net, list);
1510+
skb_queue_head_init(&inputq);
1511+
tipc_sk_mcast_rcv(peer_net, list, &inputq);
1512+
__skb_queue_purge(list);
1513+
skb_queue_purge(&inputq);
1514+
}
1515+
return;
1516+
case GROUP_PROTOCOL:
1517+
case CONN_MANAGER:
1518+
tipc_loopback_trace(peer_net, list);
1519+
spin_lock_init(&list->lock);
1520+
tipc_sk_rcv(peer_net, list);
1521+
return;
1522+
case LINK_PROTOCOL:
1523+
case NAME_DISTRIBUTOR:
1524+
case TUNNEL_PROTOCOL:
1525+
case BCAST_PROTOCOL:
1526+
return;
1527+
default:
1528+
return;
1529+
};
1530+
}
1531+
14271532
/**
14281533
* tipc_node_xmit() is the general link level function for message sending
14291534
* @net: the applicable net namespace
@@ -1439,6 +1544,7 @@ int tipc_node_xmit(struct net *net, struct sk_buff_head *list,
14391544
struct tipc_link_entry *le = NULL;
14401545
struct tipc_node *n;
14411546
struct sk_buff_head xmitq;
1547+
bool node_up = false;
14421548
int bearer_id;
14431549
int rc;
14441550

@@ -1456,6 +1562,17 @@ int tipc_node_xmit(struct net *net, struct sk_buff_head *list,
14561562
}
14571563

14581564
tipc_node_read_lock(n);
1565+
node_up = node_is_up(n);
1566+
if (node_up && n->peer_net && check_net(n->peer_net)) {
1567+
/* xmit inner linux container */
1568+
tipc_lxc_xmit(n->peer_net, list);
1569+
if (likely(skb_queue_empty(list))) {
1570+
tipc_node_read_unlock(n);
1571+
tipc_node_put(n);
1572+
return 0;
1573+
}
1574+
}
1575+
14591576
bearer_id = n->active_links[selector & 1];
14601577
if (unlikely(bearer_id == INVALID_BEARER_ID)) {
14611578
tipc_node_read_unlock(n);
@@ -2587,3 +2704,33 @@ int tipc_node_dump(struct tipc_node *n, bool more, char *buf)
25872704

25882705
return i;
25892706
}
2707+
2708+
void tipc_node_pre_cleanup_net(struct net *exit_net)
2709+
{
2710+
struct tipc_node *n;
2711+
struct tipc_net *tn;
2712+
struct net *tmp;
2713+
2714+
rcu_read_lock();
2715+
for_each_net_rcu(tmp) {
2716+
if (tmp == exit_net)
2717+
continue;
2718+
tn = tipc_net(tmp);
2719+
if (!tn)
2720+
continue;
2721+
spin_lock_bh(&tn->node_list_lock);
2722+
list_for_each_entry_rcu(n, &tn->node_list, list) {
2723+
if (!n->peer_net)
2724+
continue;
2725+
if (n->peer_net != exit_net)
2726+
continue;
2727+
tipc_node_write_lock(n);
2728+
n->peer_net = NULL;
2729+
n->peer_hash_mix = 0;
2730+
tipc_node_write_unlock_fast(n);
2731+
break;
2732+
}
2733+
spin_unlock_bh(&tn->node_list_lock);
2734+
}
2735+
rcu_read_unlock();
2736+
}

net/tipc/node.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ u32 tipc_node_get_addr(struct tipc_node *node);
7575
u32 tipc_node_try_addr(struct net *net, u8 *id, u32 addr);
7676
void tipc_node_check_dest(struct net *net, u32 onode, u8 *peer_id128,
7777
struct tipc_bearer *bearer,
78-
u16 capabilities, u32 signature,
78+
u16 capabilities, u32 signature, u32 hash_mixes,
7979
struct tipc_media_addr *maddr,
8080
bool *respond, bool *dupl_addr);
8181
void tipc_node_delete_links(struct net *net, int bearer_id);
@@ -92,7 +92,7 @@ void tipc_node_unsubscribe(struct net *net, struct list_head *subscr, u32 addr);
9292
void tipc_node_broadcast(struct net *net, struct sk_buff *skb);
9393
int tipc_node_add_conn(struct net *net, u32 dnode, u32 port, u32 peer_port);
9494
void tipc_node_remove_conn(struct net *net, u32 dnode, u32 port);
95-
int tipc_node_get_mtu(struct net *net, u32 addr, u32 sel);
95+
int tipc_node_get_mtu(struct net *net, u32 addr, u32 sel, bool connected);
9696
bool tipc_node_is_up(struct net *net, u32 addr);
9797
u16 tipc_node_get_capabilities(struct net *net, u32 addr);
9898
int tipc_nl_node_dump(struct sk_buff *skb, struct netlink_callback *cb);
@@ -107,4 +107,5 @@ int tipc_nl_node_get_monitor(struct sk_buff *skb, struct genl_info *info);
107107
int tipc_nl_node_dump_monitor(struct sk_buff *skb, struct netlink_callback *cb);
108108
int tipc_nl_node_dump_monitor_peer(struct sk_buff *skb,
109109
struct netlink_callback *cb);
110+
void tipc_node_pre_cleanup_net(struct net *exit_net);
110111
#endif

0 commit comments

Comments
 (0)