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