162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * net/tipc/bcast.c: TIPC broadcast code
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (c) 2004-2006, 2014-2017, Ericsson AB
562306a36Sopenharmony_ci * Copyright (c) 2004, Intel Corporation.
662306a36Sopenharmony_ci * Copyright (c) 2005, 2010-2011, Wind River Systems
762306a36Sopenharmony_ci * All rights reserved.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or without
1062306a36Sopenharmony_ci * modification, are permitted provided that the following conditions are met:
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright
1362306a36Sopenharmony_ci *    notice, this list of conditions and the following disclaimer.
1462306a36Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright
1562306a36Sopenharmony_ci *    notice, this list of conditions and the following disclaimer in the
1662306a36Sopenharmony_ci *    documentation and/or other materials provided with the distribution.
1762306a36Sopenharmony_ci * 3. Neither the names of the copyright holders nor the names of its
1862306a36Sopenharmony_ci *    contributors may be used to endorse or promote products derived from
1962306a36Sopenharmony_ci *    this software without specific prior written permission.
2062306a36Sopenharmony_ci *
2162306a36Sopenharmony_ci * Alternatively, this software may be distributed under the terms of the
2262306a36Sopenharmony_ci * GNU General Public License ("GPL") version 2 as published by the Free
2362306a36Sopenharmony_ci * Software Foundation.
2462306a36Sopenharmony_ci *
2562306a36Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
2662306a36Sopenharmony_ci * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2762306a36Sopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2862306a36Sopenharmony_ci * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
2962306a36Sopenharmony_ci * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
3062306a36Sopenharmony_ci * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
3162306a36Sopenharmony_ci * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
3262306a36Sopenharmony_ci * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
3362306a36Sopenharmony_ci * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3462306a36Sopenharmony_ci * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3562306a36Sopenharmony_ci * POSSIBILITY OF SUCH DAMAGE.
3662306a36Sopenharmony_ci */
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#include <linux/tipc_config.h>
3962306a36Sopenharmony_ci#include "socket.h"
4062306a36Sopenharmony_ci#include "msg.h"
4162306a36Sopenharmony_ci#include "bcast.h"
4262306a36Sopenharmony_ci#include "link.h"
4362306a36Sopenharmony_ci#include "name_table.h"
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#define BCLINK_WIN_DEFAULT  50	/* bcast link window size (default) */
4662306a36Sopenharmony_ci#define BCLINK_WIN_MIN      32	/* bcast minimum link window size */
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ciconst char tipc_bclink_name[] = "broadcast-link";
4962306a36Sopenharmony_ciunsigned long sysctl_tipc_bc_retruni __read_mostly;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci/**
5262306a36Sopenharmony_ci * struct tipc_bc_base - base structure for keeping broadcast send state
5362306a36Sopenharmony_ci * @link: broadcast send link structure
5462306a36Sopenharmony_ci * @inputq: data input queue; will only carry SOCK_WAKEUP messages
5562306a36Sopenharmony_ci * @dests: array keeping number of reachable destinations per bearer
5662306a36Sopenharmony_ci * @primary_bearer: a bearer having links to all broadcast destinations, if any
5762306a36Sopenharmony_ci * @bcast_support: indicates if primary bearer, if any, supports broadcast
5862306a36Sopenharmony_ci * @force_bcast: forces broadcast for multicast traffic
5962306a36Sopenharmony_ci * @rcast_support: indicates if all peer nodes support replicast
6062306a36Sopenharmony_ci * @force_rcast: forces replicast for multicast traffic
6162306a36Sopenharmony_ci * @rc_ratio: dest count as percentage of cluster size where send method changes
6262306a36Sopenharmony_ci * @bc_threshold: calculated from rc_ratio; if dests > threshold use broadcast
6362306a36Sopenharmony_ci */
6462306a36Sopenharmony_cistruct tipc_bc_base {
6562306a36Sopenharmony_ci	struct tipc_link *link;
6662306a36Sopenharmony_ci	struct sk_buff_head inputq;
6762306a36Sopenharmony_ci	int dests[MAX_BEARERS];
6862306a36Sopenharmony_ci	int primary_bearer;
6962306a36Sopenharmony_ci	bool bcast_support;
7062306a36Sopenharmony_ci	bool force_bcast;
7162306a36Sopenharmony_ci	bool rcast_support;
7262306a36Sopenharmony_ci	bool force_rcast;
7362306a36Sopenharmony_ci	int rc_ratio;
7462306a36Sopenharmony_ci	int bc_threshold;
7562306a36Sopenharmony_ci};
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic struct tipc_bc_base *tipc_bc_base(struct net *net)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	return tipc_net(net)->bcbase;
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci/* tipc_bcast_get_mtu(): -get the MTU currently used by broadcast link
8362306a36Sopenharmony_ci * Note: the MTU is decremented to give room for a tunnel header, in
8462306a36Sopenharmony_ci * case the message needs to be sent as replicast
8562306a36Sopenharmony_ci */
8662306a36Sopenharmony_ciint tipc_bcast_get_mtu(struct net *net)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	return tipc_link_mss(tipc_bc_sndlink(net));
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_civoid tipc_bcast_toggle_rcast(struct net *net, bool supp)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	tipc_bc_base(net)->rcast_support = supp;
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic void tipc_bcbase_calc_bc_threshold(struct net *net)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	struct tipc_bc_base *bb = tipc_bc_base(net);
9962306a36Sopenharmony_ci	int cluster_size = tipc_link_bc_peers(tipc_bc_sndlink(net));
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	bb->bc_threshold = 1 + (cluster_size * bb->rc_ratio / 100);
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci/* tipc_bcbase_select_primary(): find a bearer with links to all destinations,
10562306a36Sopenharmony_ci *                               if any, and make it primary bearer
10662306a36Sopenharmony_ci */
10762306a36Sopenharmony_cistatic void tipc_bcbase_select_primary(struct net *net)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	struct tipc_bc_base *bb = tipc_bc_base(net);
11062306a36Sopenharmony_ci	int all_dests =  tipc_link_bc_peers(bb->link);
11162306a36Sopenharmony_ci	int max_win = tipc_link_max_win(bb->link);
11262306a36Sopenharmony_ci	int min_win = tipc_link_min_win(bb->link);
11362306a36Sopenharmony_ci	int i, mtu, prim;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	bb->primary_bearer = INVALID_BEARER_ID;
11662306a36Sopenharmony_ci	bb->bcast_support = true;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	if (!all_dests)
11962306a36Sopenharmony_ci		return;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	for (i = 0; i < MAX_BEARERS; i++) {
12262306a36Sopenharmony_ci		if (!bb->dests[i])
12362306a36Sopenharmony_ci			continue;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci		mtu = tipc_bearer_mtu(net, i);
12662306a36Sopenharmony_ci		if (mtu < tipc_link_mtu(bb->link)) {
12762306a36Sopenharmony_ci			tipc_link_set_mtu(bb->link, mtu);
12862306a36Sopenharmony_ci			tipc_link_set_queue_limits(bb->link,
12962306a36Sopenharmony_ci						   min_win,
13062306a36Sopenharmony_ci						   max_win);
13162306a36Sopenharmony_ci		}
13262306a36Sopenharmony_ci		bb->bcast_support &= tipc_bearer_bcast_support(net, i);
13362306a36Sopenharmony_ci		if (bb->dests[i] < all_dests)
13462306a36Sopenharmony_ci			continue;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci		bb->primary_bearer = i;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci		/* Reduce risk that all nodes select same primary */
13962306a36Sopenharmony_ci		if ((i ^ tipc_own_addr(net)) & 1)
14062306a36Sopenharmony_ci			break;
14162306a36Sopenharmony_ci	}
14262306a36Sopenharmony_ci	prim = bb->primary_bearer;
14362306a36Sopenharmony_ci	if (prim != INVALID_BEARER_ID)
14462306a36Sopenharmony_ci		bb->bcast_support = tipc_bearer_bcast_support(net, prim);
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_civoid tipc_bcast_inc_bearer_dst_cnt(struct net *net, int bearer_id)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	struct tipc_bc_base *bb = tipc_bc_base(net);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	tipc_bcast_lock(net);
15262306a36Sopenharmony_ci	bb->dests[bearer_id]++;
15362306a36Sopenharmony_ci	tipc_bcbase_select_primary(net);
15462306a36Sopenharmony_ci	tipc_bcast_unlock(net);
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_civoid tipc_bcast_dec_bearer_dst_cnt(struct net *net, int bearer_id)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	struct tipc_bc_base *bb = tipc_bc_base(net);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	tipc_bcast_lock(net);
16262306a36Sopenharmony_ci	bb->dests[bearer_id]--;
16362306a36Sopenharmony_ci	tipc_bcbase_select_primary(net);
16462306a36Sopenharmony_ci	tipc_bcast_unlock(net);
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci/* tipc_bcbase_xmit - broadcast a packet queue across one or more bearers
16862306a36Sopenharmony_ci *
16962306a36Sopenharmony_ci * Note that number of reachable destinations, as indicated in the dests[]
17062306a36Sopenharmony_ci * array, may transitionally differ from the number of destinations indicated
17162306a36Sopenharmony_ci * in each sent buffer. We can sustain this. Excess destination nodes will
17262306a36Sopenharmony_ci * drop and never acknowledge the unexpected packets, and missing destinations
17362306a36Sopenharmony_ci * will either require retransmission (if they are just about to be added to
17462306a36Sopenharmony_ci * the bearer), or be removed from the buffer's 'ackers' counter (if they
17562306a36Sopenharmony_ci * just went down)
17662306a36Sopenharmony_ci */
17762306a36Sopenharmony_cistatic void tipc_bcbase_xmit(struct net *net, struct sk_buff_head *xmitq)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	int bearer_id;
18062306a36Sopenharmony_ci	struct tipc_bc_base *bb = tipc_bc_base(net);
18162306a36Sopenharmony_ci	struct sk_buff *skb, *_skb;
18262306a36Sopenharmony_ci	struct sk_buff_head _xmitq;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	if (skb_queue_empty(xmitq))
18562306a36Sopenharmony_ci		return;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	/* The typical case: at least one bearer has links to all nodes */
18862306a36Sopenharmony_ci	bearer_id = bb->primary_bearer;
18962306a36Sopenharmony_ci	if (bearer_id >= 0) {
19062306a36Sopenharmony_ci		tipc_bearer_bc_xmit(net, bearer_id, xmitq);
19162306a36Sopenharmony_ci		return;
19262306a36Sopenharmony_ci	}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	/* We have to transmit across all bearers */
19562306a36Sopenharmony_ci	__skb_queue_head_init(&_xmitq);
19662306a36Sopenharmony_ci	for (bearer_id = 0; bearer_id < MAX_BEARERS; bearer_id++) {
19762306a36Sopenharmony_ci		if (!bb->dests[bearer_id])
19862306a36Sopenharmony_ci			continue;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci		skb_queue_walk(xmitq, skb) {
20162306a36Sopenharmony_ci			_skb = pskb_copy_for_clone(skb, GFP_ATOMIC);
20262306a36Sopenharmony_ci			if (!_skb)
20362306a36Sopenharmony_ci				break;
20462306a36Sopenharmony_ci			__skb_queue_tail(&_xmitq, _skb);
20562306a36Sopenharmony_ci		}
20662306a36Sopenharmony_ci		tipc_bearer_bc_xmit(net, bearer_id, &_xmitq);
20762306a36Sopenharmony_ci	}
20862306a36Sopenharmony_ci	__skb_queue_purge(xmitq);
20962306a36Sopenharmony_ci	__skb_queue_purge(&_xmitq);
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic void tipc_bcast_select_xmit_method(struct net *net, int dests,
21362306a36Sopenharmony_ci					  struct tipc_mc_method *method)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	struct tipc_bc_base *bb = tipc_bc_base(net);
21662306a36Sopenharmony_ci	unsigned long exp = method->expires;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	/* Broadcast supported by used bearer/bearers? */
21962306a36Sopenharmony_ci	if (!bb->bcast_support) {
22062306a36Sopenharmony_ci		method->rcast = true;
22162306a36Sopenharmony_ci		return;
22262306a36Sopenharmony_ci	}
22362306a36Sopenharmony_ci	/* Any destinations which don't support replicast ? */
22462306a36Sopenharmony_ci	if (!bb->rcast_support) {
22562306a36Sopenharmony_ci		method->rcast = false;
22662306a36Sopenharmony_ci		return;
22762306a36Sopenharmony_ci	}
22862306a36Sopenharmony_ci	/* Can current method be changed ? */
22962306a36Sopenharmony_ci	method->expires = jiffies + TIPC_METHOD_EXPIRE;
23062306a36Sopenharmony_ci	if (method->mandatory)
23162306a36Sopenharmony_ci		return;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	if (!(tipc_net(net)->capabilities & TIPC_MCAST_RBCTL) &&
23462306a36Sopenharmony_ci	    time_before(jiffies, exp))
23562306a36Sopenharmony_ci		return;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	/* Configuration as force 'broadcast' method */
23862306a36Sopenharmony_ci	if (bb->force_bcast) {
23962306a36Sopenharmony_ci		method->rcast = false;
24062306a36Sopenharmony_ci		return;
24162306a36Sopenharmony_ci	}
24262306a36Sopenharmony_ci	/* Configuration as force 'replicast' method */
24362306a36Sopenharmony_ci	if (bb->force_rcast) {
24462306a36Sopenharmony_ci		method->rcast = true;
24562306a36Sopenharmony_ci		return;
24662306a36Sopenharmony_ci	}
24762306a36Sopenharmony_ci	/* Configuration as 'autoselect' or default method */
24862306a36Sopenharmony_ci	/* Determine method to use now */
24962306a36Sopenharmony_ci	method->rcast = dests <= bb->bc_threshold;
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci/* tipc_bcast_xmit - broadcast the buffer chain to all external nodes
25362306a36Sopenharmony_ci * @net: the applicable net namespace
25462306a36Sopenharmony_ci * @pkts: chain of buffers containing message
25562306a36Sopenharmony_ci * @cong_link_cnt: set to 1 if broadcast link is congested, otherwise 0
25662306a36Sopenharmony_ci * Consumes the buffer chain.
25762306a36Sopenharmony_ci * Returns 0 if success, otherwise errno: -EHOSTUNREACH,-EMSGSIZE
25862306a36Sopenharmony_ci */
25962306a36Sopenharmony_ciint tipc_bcast_xmit(struct net *net, struct sk_buff_head *pkts,
26062306a36Sopenharmony_ci		    u16 *cong_link_cnt)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	struct tipc_link *l = tipc_bc_sndlink(net);
26362306a36Sopenharmony_ci	struct sk_buff_head xmitq;
26462306a36Sopenharmony_ci	int rc = 0;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	__skb_queue_head_init(&xmitq);
26762306a36Sopenharmony_ci	tipc_bcast_lock(net);
26862306a36Sopenharmony_ci	if (tipc_link_bc_peers(l))
26962306a36Sopenharmony_ci		rc = tipc_link_xmit(l, pkts, &xmitq);
27062306a36Sopenharmony_ci	tipc_bcast_unlock(net);
27162306a36Sopenharmony_ci	tipc_bcbase_xmit(net, &xmitq);
27262306a36Sopenharmony_ci	__skb_queue_purge(pkts);
27362306a36Sopenharmony_ci	if (rc == -ELINKCONG) {
27462306a36Sopenharmony_ci		*cong_link_cnt = 1;
27562306a36Sopenharmony_ci		rc = 0;
27662306a36Sopenharmony_ci	}
27762306a36Sopenharmony_ci	return rc;
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci/* tipc_rcast_xmit - replicate and send a message to given destination nodes
28162306a36Sopenharmony_ci * @net: the applicable net namespace
28262306a36Sopenharmony_ci * @pkts: chain of buffers containing message
28362306a36Sopenharmony_ci * @dests: list of destination nodes
28462306a36Sopenharmony_ci * @cong_link_cnt: returns number of congested links
28562306a36Sopenharmony_ci * @cong_links: returns identities of congested links
28662306a36Sopenharmony_ci * Returns 0 if success, otherwise errno
28762306a36Sopenharmony_ci */
28862306a36Sopenharmony_cistatic int tipc_rcast_xmit(struct net *net, struct sk_buff_head *pkts,
28962306a36Sopenharmony_ci			   struct tipc_nlist *dests, u16 *cong_link_cnt)
29062306a36Sopenharmony_ci{
29162306a36Sopenharmony_ci	struct tipc_dest *dst, *tmp;
29262306a36Sopenharmony_ci	struct sk_buff_head _pkts;
29362306a36Sopenharmony_ci	u32 dnode, selector;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	selector = msg_link_selector(buf_msg(skb_peek(pkts)));
29662306a36Sopenharmony_ci	__skb_queue_head_init(&_pkts);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	list_for_each_entry_safe(dst, tmp, &dests->list, list) {
29962306a36Sopenharmony_ci		dnode = dst->node;
30062306a36Sopenharmony_ci		if (!tipc_msg_pskb_copy(dnode, pkts, &_pkts))
30162306a36Sopenharmony_ci			return -ENOMEM;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci		/* Any other return value than -ELINKCONG is ignored */
30462306a36Sopenharmony_ci		if (tipc_node_xmit(net, &_pkts, dnode, selector) == -ELINKCONG)
30562306a36Sopenharmony_ci			(*cong_link_cnt)++;
30662306a36Sopenharmony_ci	}
30762306a36Sopenharmony_ci	return 0;
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci/* tipc_mcast_send_sync - deliver a dummy message with SYN bit
31162306a36Sopenharmony_ci * @net: the applicable net namespace
31262306a36Sopenharmony_ci * @skb: socket buffer to copy
31362306a36Sopenharmony_ci * @method: send method to be used
31462306a36Sopenharmony_ci * @dests: destination nodes for message.
31562306a36Sopenharmony_ci * Returns 0 if success, otherwise errno
31662306a36Sopenharmony_ci */
31762306a36Sopenharmony_cistatic int tipc_mcast_send_sync(struct net *net, struct sk_buff *skb,
31862306a36Sopenharmony_ci				struct tipc_mc_method *method,
31962306a36Sopenharmony_ci				struct tipc_nlist *dests)
32062306a36Sopenharmony_ci{
32162306a36Sopenharmony_ci	struct tipc_msg *hdr, *_hdr;
32262306a36Sopenharmony_ci	struct sk_buff_head tmpq;
32362306a36Sopenharmony_ci	struct sk_buff *_skb;
32462306a36Sopenharmony_ci	u16 cong_link_cnt;
32562306a36Sopenharmony_ci	int rc = 0;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	/* Is a cluster supporting with new capabilities ? */
32862306a36Sopenharmony_ci	if (!(tipc_net(net)->capabilities & TIPC_MCAST_RBCTL))
32962306a36Sopenharmony_ci		return 0;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	hdr = buf_msg(skb);
33262306a36Sopenharmony_ci	if (msg_user(hdr) == MSG_FRAGMENTER)
33362306a36Sopenharmony_ci		hdr = msg_inner_hdr(hdr);
33462306a36Sopenharmony_ci	if (msg_type(hdr) != TIPC_MCAST_MSG)
33562306a36Sopenharmony_ci		return 0;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	/* Allocate dummy message */
33862306a36Sopenharmony_ci	_skb = tipc_buf_acquire(MCAST_H_SIZE, GFP_KERNEL);
33962306a36Sopenharmony_ci	if (!_skb)
34062306a36Sopenharmony_ci		return -ENOMEM;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	/* Preparing for 'synching' header */
34362306a36Sopenharmony_ci	msg_set_syn(hdr, 1);
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	/* Copy skb's header into a dummy header */
34662306a36Sopenharmony_ci	skb_copy_to_linear_data(_skb, hdr, MCAST_H_SIZE);
34762306a36Sopenharmony_ci	skb_orphan(_skb);
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	/* Reverse method for dummy message */
35062306a36Sopenharmony_ci	_hdr = buf_msg(_skb);
35162306a36Sopenharmony_ci	msg_set_size(_hdr, MCAST_H_SIZE);
35262306a36Sopenharmony_ci	msg_set_is_rcast(_hdr, !msg_is_rcast(hdr));
35362306a36Sopenharmony_ci	msg_set_errcode(_hdr, TIPC_ERR_NO_PORT);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	__skb_queue_head_init(&tmpq);
35662306a36Sopenharmony_ci	__skb_queue_tail(&tmpq, _skb);
35762306a36Sopenharmony_ci	if (method->rcast)
35862306a36Sopenharmony_ci		rc = tipc_bcast_xmit(net, &tmpq, &cong_link_cnt);
35962306a36Sopenharmony_ci	else
36062306a36Sopenharmony_ci		rc = tipc_rcast_xmit(net, &tmpq, dests, &cong_link_cnt);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	/* This queue should normally be empty by now */
36362306a36Sopenharmony_ci	__skb_queue_purge(&tmpq);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	return rc;
36662306a36Sopenharmony_ci}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci/* tipc_mcast_xmit - deliver message to indicated destination nodes
36962306a36Sopenharmony_ci *                   and to identified node local sockets
37062306a36Sopenharmony_ci * @net: the applicable net namespace
37162306a36Sopenharmony_ci * @pkts: chain of buffers containing message
37262306a36Sopenharmony_ci * @method: send method to be used
37362306a36Sopenharmony_ci * @dests: destination nodes for message.
37462306a36Sopenharmony_ci * @cong_link_cnt: returns number of encountered congested destination links
37562306a36Sopenharmony_ci * Consumes buffer chain.
37662306a36Sopenharmony_ci * Returns 0 if success, otherwise errno
37762306a36Sopenharmony_ci */
37862306a36Sopenharmony_ciint tipc_mcast_xmit(struct net *net, struct sk_buff_head *pkts,
37962306a36Sopenharmony_ci		    struct tipc_mc_method *method, struct tipc_nlist *dests,
38062306a36Sopenharmony_ci		    u16 *cong_link_cnt)
38162306a36Sopenharmony_ci{
38262306a36Sopenharmony_ci	struct sk_buff_head inputq, localq;
38362306a36Sopenharmony_ci	bool rcast = method->rcast;
38462306a36Sopenharmony_ci	struct tipc_msg *hdr;
38562306a36Sopenharmony_ci	struct sk_buff *skb;
38662306a36Sopenharmony_ci	int rc = 0;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	skb_queue_head_init(&inputq);
38962306a36Sopenharmony_ci	__skb_queue_head_init(&localq);
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	/* Clone packets before they are consumed by next call */
39262306a36Sopenharmony_ci	if (dests->local && !tipc_msg_reassemble(pkts, &localq)) {
39362306a36Sopenharmony_ci		rc = -ENOMEM;
39462306a36Sopenharmony_ci		goto exit;
39562306a36Sopenharmony_ci	}
39662306a36Sopenharmony_ci	/* Send according to determined transmit method */
39762306a36Sopenharmony_ci	if (dests->remote) {
39862306a36Sopenharmony_ci		tipc_bcast_select_xmit_method(net, dests->remote, method);
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci		skb = skb_peek(pkts);
40162306a36Sopenharmony_ci		hdr = buf_msg(skb);
40262306a36Sopenharmony_ci		if (msg_user(hdr) == MSG_FRAGMENTER)
40362306a36Sopenharmony_ci			hdr = msg_inner_hdr(hdr);
40462306a36Sopenharmony_ci		msg_set_is_rcast(hdr, method->rcast);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci		/* Switch method ? */
40762306a36Sopenharmony_ci		if (rcast != method->rcast) {
40862306a36Sopenharmony_ci			rc = tipc_mcast_send_sync(net, skb, method, dests);
40962306a36Sopenharmony_ci			if (unlikely(rc)) {
41062306a36Sopenharmony_ci				pr_err("Unable to send SYN: method %d, rc %d\n",
41162306a36Sopenharmony_ci				       rcast, rc);
41262306a36Sopenharmony_ci				goto exit;
41362306a36Sopenharmony_ci			}
41462306a36Sopenharmony_ci		}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci		if (method->rcast)
41762306a36Sopenharmony_ci			rc = tipc_rcast_xmit(net, pkts, dests, cong_link_cnt);
41862306a36Sopenharmony_ci		else
41962306a36Sopenharmony_ci			rc = tipc_bcast_xmit(net, pkts, cong_link_cnt);
42062306a36Sopenharmony_ci	}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	if (dests->local) {
42362306a36Sopenharmony_ci		tipc_loopback_trace(net, &localq);
42462306a36Sopenharmony_ci		tipc_sk_mcast_rcv(net, &localq, &inputq);
42562306a36Sopenharmony_ci	}
42662306a36Sopenharmony_ciexit:
42762306a36Sopenharmony_ci	/* This queue should normally be empty by now */
42862306a36Sopenharmony_ci	__skb_queue_purge(pkts);
42962306a36Sopenharmony_ci	return rc;
43062306a36Sopenharmony_ci}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci/* tipc_bcast_rcv - receive a broadcast packet, and deliver to rcv link
43362306a36Sopenharmony_ci *
43462306a36Sopenharmony_ci * RCU is locked, no other locks set
43562306a36Sopenharmony_ci */
43662306a36Sopenharmony_ciint tipc_bcast_rcv(struct net *net, struct tipc_link *l, struct sk_buff *skb)
43762306a36Sopenharmony_ci{
43862306a36Sopenharmony_ci	struct tipc_msg *hdr = buf_msg(skb);
43962306a36Sopenharmony_ci	struct sk_buff_head *inputq = &tipc_bc_base(net)->inputq;
44062306a36Sopenharmony_ci	struct sk_buff_head xmitq;
44162306a36Sopenharmony_ci	int rc;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	__skb_queue_head_init(&xmitq);
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	if (msg_mc_netid(hdr) != tipc_netid(net) || !tipc_link_is_up(l)) {
44662306a36Sopenharmony_ci		kfree_skb(skb);
44762306a36Sopenharmony_ci		return 0;
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	tipc_bcast_lock(net);
45162306a36Sopenharmony_ci	if (msg_user(hdr) == BCAST_PROTOCOL)
45262306a36Sopenharmony_ci		rc = tipc_link_bc_nack_rcv(l, skb, &xmitq);
45362306a36Sopenharmony_ci	else
45462306a36Sopenharmony_ci		rc = tipc_link_rcv(l, skb, NULL);
45562306a36Sopenharmony_ci	tipc_bcast_unlock(net);
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	tipc_bcbase_xmit(net, &xmitq);
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	/* Any socket wakeup messages ? */
46062306a36Sopenharmony_ci	if (!skb_queue_empty(inputq))
46162306a36Sopenharmony_ci		tipc_sk_rcv(net, inputq);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	return rc;
46462306a36Sopenharmony_ci}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci/* tipc_bcast_ack_rcv - receive and handle a broadcast acknowledge
46762306a36Sopenharmony_ci *
46862306a36Sopenharmony_ci * RCU is locked, no other locks set
46962306a36Sopenharmony_ci */
47062306a36Sopenharmony_civoid tipc_bcast_ack_rcv(struct net *net, struct tipc_link *l,
47162306a36Sopenharmony_ci			struct tipc_msg *hdr)
47262306a36Sopenharmony_ci{
47362306a36Sopenharmony_ci	struct sk_buff_head *inputq = &tipc_bc_base(net)->inputq;
47462306a36Sopenharmony_ci	u16 acked = msg_bcast_ack(hdr);
47562306a36Sopenharmony_ci	struct sk_buff_head xmitq;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	/* Ignore bc acks sent by peer before bcast synch point was received */
47862306a36Sopenharmony_ci	if (msg_bc_ack_invalid(hdr))
47962306a36Sopenharmony_ci		return;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	__skb_queue_head_init(&xmitq);
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	tipc_bcast_lock(net);
48462306a36Sopenharmony_ci	tipc_link_bc_ack_rcv(l, acked, 0, NULL, &xmitq, NULL);
48562306a36Sopenharmony_ci	tipc_bcast_unlock(net);
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	tipc_bcbase_xmit(net, &xmitq);
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	/* Any socket wakeup messages ? */
49062306a36Sopenharmony_ci	if (!skb_queue_empty(inputq))
49162306a36Sopenharmony_ci		tipc_sk_rcv(net, inputq);
49262306a36Sopenharmony_ci}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci/* tipc_bcast_synch_rcv -  check and update rcv link with peer's send state
49562306a36Sopenharmony_ci *
49662306a36Sopenharmony_ci * RCU is locked, no other locks set
49762306a36Sopenharmony_ci */
49862306a36Sopenharmony_ciint tipc_bcast_sync_rcv(struct net *net, struct tipc_link *l,
49962306a36Sopenharmony_ci			struct tipc_msg *hdr,
50062306a36Sopenharmony_ci			struct sk_buff_head *retrq)
50162306a36Sopenharmony_ci{
50262306a36Sopenharmony_ci	struct sk_buff_head *inputq = &tipc_bc_base(net)->inputq;
50362306a36Sopenharmony_ci	struct tipc_gap_ack_blks *ga;
50462306a36Sopenharmony_ci	struct sk_buff_head xmitq;
50562306a36Sopenharmony_ci	int rc = 0;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	__skb_queue_head_init(&xmitq);
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	tipc_bcast_lock(net);
51062306a36Sopenharmony_ci	if (msg_type(hdr) != STATE_MSG) {
51162306a36Sopenharmony_ci		tipc_link_bc_init_rcv(l, hdr);
51262306a36Sopenharmony_ci	} else if (!msg_bc_ack_invalid(hdr)) {
51362306a36Sopenharmony_ci		tipc_get_gap_ack_blks(&ga, l, hdr, false);
51462306a36Sopenharmony_ci		if (!sysctl_tipc_bc_retruni)
51562306a36Sopenharmony_ci			retrq = &xmitq;
51662306a36Sopenharmony_ci		rc = tipc_link_bc_ack_rcv(l, msg_bcast_ack(hdr),
51762306a36Sopenharmony_ci					  msg_bc_gap(hdr), ga, &xmitq,
51862306a36Sopenharmony_ci					  retrq);
51962306a36Sopenharmony_ci		rc |= tipc_link_bc_sync_rcv(l, hdr, &xmitq);
52062306a36Sopenharmony_ci	}
52162306a36Sopenharmony_ci	tipc_bcast_unlock(net);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	tipc_bcbase_xmit(net, &xmitq);
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	/* Any socket wakeup messages ? */
52662306a36Sopenharmony_ci	if (!skb_queue_empty(inputq))
52762306a36Sopenharmony_ci		tipc_sk_rcv(net, inputq);
52862306a36Sopenharmony_ci	return rc;
52962306a36Sopenharmony_ci}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci/* tipc_bcast_add_peer - add a peer node to broadcast link and bearer
53262306a36Sopenharmony_ci *
53362306a36Sopenharmony_ci * RCU is locked, node lock is set
53462306a36Sopenharmony_ci */
53562306a36Sopenharmony_civoid tipc_bcast_add_peer(struct net *net, struct tipc_link *uc_l,
53662306a36Sopenharmony_ci			 struct sk_buff_head *xmitq)
53762306a36Sopenharmony_ci{
53862306a36Sopenharmony_ci	struct tipc_link *snd_l = tipc_bc_sndlink(net);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	tipc_bcast_lock(net);
54162306a36Sopenharmony_ci	tipc_link_add_bc_peer(snd_l, uc_l, xmitq);
54262306a36Sopenharmony_ci	tipc_bcbase_select_primary(net);
54362306a36Sopenharmony_ci	tipc_bcbase_calc_bc_threshold(net);
54462306a36Sopenharmony_ci	tipc_bcast_unlock(net);
54562306a36Sopenharmony_ci}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci/* tipc_bcast_remove_peer - remove a peer node from broadcast link and bearer
54862306a36Sopenharmony_ci *
54962306a36Sopenharmony_ci * RCU is locked, node lock is set
55062306a36Sopenharmony_ci */
55162306a36Sopenharmony_civoid tipc_bcast_remove_peer(struct net *net, struct tipc_link *rcv_l)
55262306a36Sopenharmony_ci{
55362306a36Sopenharmony_ci	struct tipc_link *snd_l = tipc_bc_sndlink(net);
55462306a36Sopenharmony_ci	struct sk_buff_head *inputq = &tipc_bc_base(net)->inputq;
55562306a36Sopenharmony_ci	struct sk_buff_head xmitq;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	__skb_queue_head_init(&xmitq);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	tipc_bcast_lock(net);
56062306a36Sopenharmony_ci	tipc_link_remove_bc_peer(snd_l, rcv_l, &xmitq);
56162306a36Sopenharmony_ci	tipc_bcbase_select_primary(net);
56262306a36Sopenharmony_ci	tipc_bcbase_calc_bc_threshold(net);
56362306a36Sopenharmony_ci	tipc_bcast_unlock(net);
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	tipc_bcbase_xmit(net, &xmitq);
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	/* Any socket wakeup messages ? */
56862306a36Sopenharmony_ci	if (!skb_queue_empty(inputq))
56962306a36Sopenharmony_ci		tipc_sk_rcv(net, inputq);
57062306a36Sopenharmony_ci}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ciint tipc_bclink_reset_stats(struct net *net, struct tipc_link *l)
57362306a36Sopenharmony_ci{
57462306a36Sopenharmony_ci	if (!l)
57562306a36Sopenharmony_ci		return -ENOPROTOOPT;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	tipc_bcast_lock(net);
57862306a36Sopenharmony_ci	tipc_link_reset_stats(l);
57962306a36Sopenharmony_ci	tipc_bcast_unlock(net);
58062306a36Sopenharmony_ci	return 0;
58162306a36Sopenharmony_ci}
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_cistatic int tipc_bc_link_set_queue_limits(struct net *net, u32 max_win)
58462306a36Sopenharmony_ci{
58562306a36Sopenharmony_ci	struct tipc_link *l = tipc_bc_sndlink(net);
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	if (!l)
58862306a36Sopenharmony_ci		return -ENOPROTOOPT;
58962306a36Sopenharmony_ci	if (max_win < BCLINK_WIN_MIN)
59062306a36Sopenharmony_ci		max_win = BCLINK_WIN_MIN;
59162306a36Sopenharmony_ci	if (max_win > TIPC_MAX_LINK_WIN)
59262306a36Sopenharmony_ci		return -EINVAL;
59362306a36Sopenharmony_ci	tipc_bcast_lock(net);
59462306a36Sopenharmony_ci	tipc_link_set_queue_limits(l, tipc_link_min_win(l), max_win);
59562306a36Sopenharmony_ci	tipc_bcast_unlock(net);
59662306a36Sopenharmony_ci	return 0;
59762306a36Sopenharmony_ci}
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_cistatic int tipc_bc_link_set_broadcast_mode(struct net *net, u32 bc_mode)
60062306a36Sopenharmony_ci{
60162306a36Sopenharmony_ci	struct tipc_bc_base *bb = tipc_bc_base(net);
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	switch (bc_mode) {
60462306a36Sopenharmony_ci	case BCLINK_MODE_BCAST:
60562306a36Sopenharmony_ci		if (!bb->bcast_support)
60662306a36Sopenharmony_ci			return -ENOPROTOOPT;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci		bb->force_bcast = true;
60962306a36Sopenharmony_ci		bb->force_rcast = false;
61062306a36Sopenharmony_ci		break;
61162306a36Sopenharmony_ci	case BCLINK_MODE_RCAST:
61262306a36Sopenharmony_ci		if (!bb->rcast_support)
61362306a36Sopenharmony_ci			return -ENOPROTOOPT;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci		bb->force_bcast = false;
61662306a36Sopenharmony_ci		bb->force_rcast = true;
61762306a36Sopenharmony_ci		break;
61862306a36Sopenharmony_ci	case BCLINK_MODE_SEL:
61962306a36Sopenharmony_ci		if (!bb->bcast_support || !bb->rcast_support)
62062306a36Sopenharmony_ci			return -ENOPROTOOPT;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci		bb->force_bcast = false;
62362306a36Sopenharmony_ci		bb->force_rcast = false;
62462306a36Sopenharmony_ci		break;
62562306a36Sopenharmony_ci	default:
62662306a36Sopenharmony_ci		return -EINVAL;
62762306a36Sopenharmony_ci	}
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	return 0;
63062306a36Sopenharmony_ci}
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_cistatic int tipc_bc_link_set_broadcast_ratio(struct net *net, u32 bc_ratio)
63362306a36Sopenharmony_ci{
63462306a36Sopenharmony_ci	struct tipc_bc_base *bb = tipc_bc_base(net);
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	if (!bb->bcast_support || !bb->rcast_support)
63762306a36Sopenharmony_ci		return -ENOPROTOOPT;
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	if (bc_ratio > 100 || bc_ratio <= 0)
64062306a36Sopenharmony_ci		return -EINVAL;
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	bb->rc_ratio = bc_ratio;
64362306a36Sopenharmony_ci	tipc_bcast_lock(net);
64462306a36Sopenharmony_ci	tipc_bcbase_calc_bc_threshold(net);
64562306a36Sopenharmony_ci	tipc_bcast_unlock(net);
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	return 0;
64862306a36Sopenharmony_ci}
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ciint tipc_nl_bc_link_set(struct net *net, struct nlattr *attrs[])
65162306a36Sopenharmony_ci{
65262306a36Sopenharmony_ci	int err;
65362306a36Sopenharmony_ci	u32 win;
65462306a36Sopenharmony_ci	u32 bc_mode;
65562306a36Sopenharmony_ci	u32 bc_ratio;
65662306a36Sopenharmony_ci	struct nlattr *props[TIPC_NLA_PROP_MAX + 1];
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	if (!attrs[TIPC_NLA_LINK_PROP])
65962306a36Sopenharmony_ci		return -EINVAL;
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	err = tipc_nl_parse_link_prop(attrs[TIPC_NLA_LINK_PROP], props);
66262306a36Sopenharmony_ci	if (err)
66362306a36Sopenharmony_ci		return err;
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	if (!props[TIPC_NLA_PROP_WIN] &&
66662306a36Sopenharmony_ci	    !props[TIPC_NLA_PROP_BROADCAST] &&
66762306a36Sopenharmony_ci	    !props[TIPC_NLA_PROP_BROADCAST_RATIO]) {
66862306a36Sopenharmony_ci		return -EOPNOTSUPP;
66962306a36Sopenharmony_ci	}
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	if (props[TIPC_NLA_PROP_BROADCAST]) {
67262306a36Sopenharmony_ci		bc_mode = nla_get_u32(props[TIPC_NLA_PROP_BROADCAST]);
67362306a36Sopenharmony_ci		err = tipc_bc_link_set_broadcast_mode(net, bc_mode);
67462306a36Sopenharmony_ci	}
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	if (!err && props[TIPC_NLA_PROP_BROADCAST_RATIO]) {
67762306a36Sopenharmony_ci		bc_ratio = nla_get_u32(props[TIPC_NLA_PROP_BROADCAST_RATIO]);
67862306a36Sopenharmony_ci		err = tipc_bc_link_set_broadcast_ratio(net, bc_ratio);
67962306a36Sopenharmony_ci	}
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	if (!err && props[TIPC_NLA_PROP_WIN]) {
68262306a36Sopenharmony_ci		win = nla_get_u32(props[TIPC_NLA_PROP_WIN]);
68362306a36Sopenharmony_ci		err = tipc_bc_link_set_queue_limits(net, win);
68462306a36Sopenharmony_ci	}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	return err;
68762306a36Sopenharmony_ci}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ciint tipc_bcast_init(struct net *net)
69062306a36Sopenharmony_ci{
69162306a36Sopenharmony_ci	struct tipc_net *tn = tipc_net(net);
69262306a36Sopenharmony_ci	struct tipc_bc_base *bb = NULL;
69362306a36Sopenharmony_ci	struct tipc_link *l = NULL;
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	bb = kzalloc(sizeof(*bb), GFP_KERNEL);
69662306a36Sopenharmony_ci	if (!bb)
69762306a36Sopenharmony_ci		goto enomem;
69862306a36Sopenharmony_ci	tn->bcbase = bb;
69962306a36Sopenharmony_ci	spin_lock_init(&tipc_net(net)->bclock);
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	if (!tipc_link_bc_create(net, 0, 0, NULL,
70262306a36Sopenharmony_ci				 one_page_mtu,
70362306a36Sopenharmony_ci				 BCLINK_WIN_DEFAULT,
70462306a36Sopenharmony_ci				 BCLINK_WIN_DEFAULT,
70562306a36Sopenharmony_ci				 0,
70662306a36Sopenharmony_ci				 &bb->inputq,
70762306a36Sopenharmony_ci				 NULL,
70862306a36Sopenharmony_ci				 NULL,
70962306a36Sopenharmony_ci				 &l))
71062306a36Sopenharmony_ci		goto enomem;
71162306a36Sopenharmony_ci	bb->link = l;
71262306a36Sopenharmony_ci	tn->bcl = l;
71362306a36Sopenharmony_ci	bb->rc_ratio = 10;
71462306a36Sopenharmony_ci	bb->rcast_support = true;
71562306a36Sopenharmony_ci	return 0;
71662306a36Sopenharmony_cienomem:
71762306a36Sopenharmony_ci	kfree(bb);
71862306a36Sopenharmony_ci	kfree(l);
71962306a36Sopenharmony_ci	return -ENOMEM;
72062306a36Sopenharmony_ci}
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_civoid tipc_bcast_stop(struct net *net)
72362306a36Sopenharmony_ci{
72462306a36Sopenharmony_ci	struct tipc_net *tn = net_generic(net, tipc_net_id);
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	synchronize_net();
72762306a36Sopenharmony_ci	kfree(tn->bcbase);
72862306a36Sopenharmony_ci	kfree(tn->bcl);
72962306a36Sopenharmony_ci}
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_civoid tipc_nlist_init(struct tipc_nlist *nl, u32 self)
73262306a36Sopenharmony_ci{
73362306a36Sopenharmony_ci	memset(nl, 0, sizeof(*nl));
73462306a36Sopenharmony_ci	INIT_LIST_HEAD(&nl->list);
73562306a36Sopenharmony_ci	nl->self = self;
73662306a36Sopenharmony_ci}
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_civoid tipc_nlist_add(struct tipc_nlist *nl, u32 node)
73962306a36Sopenharmony_ci{
74062306a36Sopenharmony_ci	if (node == nl->self)
74162306a36Sopenharmony_ci		nl->local = true;
74262306a36Sopenharmony_ci	else if (tipc_dest_push(&nl->list, node, 0))
74362306a36Sopenharmony_ci		nl->remote++;
74462306a36Sopenharmony_ci}
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_civoid tipc_nlist_del(struct tipc_nlist *nl, u32 node)
74762306a36Sopenharmony_ci{
74862306a36Sopenharmony_ci	if (node == nl->self)
74962306a36Sopenharmony_ci		nl->local = false;
75062306a36Sopenharmony_ci	else if (tipc_dest_del(&nl->list, node, 0))
75162306a36Sopenharmony_ci		nl->remote--;
75262306a36Sopenharmony_ci}
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_civoid tipc_nlist_purge(struct tipc_nlist *nl)
75562306a36Sopenharmony_ci{
75662306a36Sopenharmony_ci	tipc_dest_list_purge(&nl->list);
75762306a36Sopenharmony_ci	nl->remote = 0;
75862306a36Sopenharmony_ci	nl->local = false;
75962306a36Sopenharmony_ci}
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ciu32 tipc_bcast_get_mode(struct net *net)
76262306a36Sopenharmony_ci{
76362306a36Sopenharmony_ci	struct tipc_bc_base *bb = tipc_bc_base(net);
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	if (bb->force_bcast)
76662306a36Sopenharmony_ci		return BCLINK_MODE_BCAST;
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	if (bb->force_rcast)
76962306a36Sopenharmony_ci		return BCLINK_MODE_RCAST;
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	if (bb->bcast_support && bb->rcast_support)
77262306a36Sopenharmony_ci		return BCLINK_MODE_SEL;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	return 0;
77562306a36Sopenharmony_ci}
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ciu32 tipc_bcast_get_broadcast_ratio(struct net *net)
77862306a36Sopenharmony_ci{
77962306a36Sopenharmony_ci	struct tipc_bc_base *bb = tipc_bc_base(net);
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	return bb->rc_ratio;
78262306a36Sopenharmony_ci}
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_civoid tipc_mcast_filter_msg(struct net *net, struct sk_buff_head *defq,
78562306a36Sopenharmony_ci			   struct sk_buff_head *inputq)
78662306a36Sopenharmony_ci{
78762306a36Sopenharmony_ci	struct sk_buff *skb, *_skb, *tmp;
78862306a36Sopenharmony_ci	struct tipc_msg *hdr, *_hdr;
78962306a36Sopenharmony_ci	bool match = false;
79062306a36Sopenharmony_ci	u32 node, port;
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	skb = skb_peek(inputq);
79362306a36Sopenharmony_ci	if (!skb)
79462306a36Sopenharmony_ci		return;
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	hdr = buf_msg(skb);
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	if (likely(!msg_is_syn(hdr) && skb_queue_empty(defq)))
79962306a36Sopenharmony_ci		return;
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	node = msg_orignode(hdr);
80262306a36Sopenharmony_ci	if (node == tipc_own_addr(net))
80362306a36Sopenharmony_ci		return;
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	port = msg_origport(hdr);
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	/* Has the twin SYN message already arrived ? */
80862306a36Sopenharmony_ci	skb_queue_walk(defq, _skb) {
80962306a36Sopenharmony_ci		_hdr = buf_msg(_skb);
81062306a36Sopenharmony_ci		if (msg_orignode(_hdr) != node)
81162306a36Sopenharmony_ci			continue;
81262306a36Sopenharmony_ci		if (msg_origport(_hdr) != port)
81362306a36Sopenharmony_ci			continue;
81462306a36Sopenharmony_ci		match = true;
81562306a36Sopenharmony_ci		break;
81662306a36Sopenharmony_ci	}
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	if (!match) {
81962306a36Sopenharmony_ci		if (!msg_is_syn(hdr))
82062306a36Sopenharmony_ci			return;
82162306a36Sopenharmony_ci		__skb_dequeue(inputq);
82262306a36Sopenharmony_ci		__skb_queue_tail(defq, skb);
82362306a36Sopenharmony_ci		return;
82462306a36Sopenharmony_ci	}
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	/* Deliver non-SYN message from other link, otherwise queue it */
82762306a36Sopenharmony_ci	if (!msg_is_syn(hdr)) {
82862306a36Sopenharmony_ci		if (msg_is_rcast(hdr) != msg_is_rcast(_hdr))
82962306a36Sopenharmony_ci			return;
83062306a36Sopenharmony_ci		__skb_dequeue(inputq);
83162306a36Sopenharmony_ci		__skb_queue_tail(defq, skb);
83262306a36Sopenharmony_ci		return;
83362306a36Sopenharmony_ci	}
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	/* Queue non-SYN/SYN message from same link */
83662306a36Sopenharmony_ci	if (msg_is_rcast(hdr) == msg_is_rcast(_hdr)) {
83762306a36Sopenharmony_ci		__skb_dequeue(inputq);
83862306a36Sopenharmony_ci		__skb_queue_tail(defq, skb);
83962306a36Sopenharmony_ci		return;
84062306a36Sopenharmony_ci	}
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	/* Matching SYN messages => return the one with data, if any */
84362306a36Sopenharmony_ci	__skb_unlink(_skb, defq);
84462306a36Sopenharmony_ci	if (msg_data_sz(hdr)) {
84562306a36Sopenharmony_ci		kfree_skb(_skb);
84662306a36Sopenharmony_ci	} else {
84762306a36Sopenharmony_ci		__skb_dequeue(inputq);
84862306a36Sopenharmony_ci		kfree_skb(skb);
84962306a36Sopenharmony_ci		__skb_queue_tail(inputq, _skb);
85062306a36Sopenharmony_ci	}
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	/* Deliver subsequent non-SYN messages from same peer */
85362306a36Sopenharmony_ci	skb_queue_walk_safe(defq, _skb, tmp) {
85462306a36Sopenharmony_ci		_hdr = buf_msg(_skb);
85562306a36Sopenharmony_ci		if (msg_orignode(_hdr) != node)
85662306a36Sopenharmony_ci			continue;
85762306a36Sopenharmony_ci		if (msg_origport(_hdr) != port)
85862306a36Sopenharmony_ci			continue;
85962306a36Sopenharmony_ci		if (msg_is_syn(_hdr))
86062306a36Sopenharmony_ci			break;
86162306a36Sopenharmony_ci		__skb_unlink(_skb, defq);
86262306a36Sopenharmony_ci		__skb_queue_tail(inputq, _skb);
86362306a36Sopenharmony_ci	}
86462306a36Sopenharmony_ci}
865