diff options
Diffstat (limited to 'net/tipc')
| -rw-r--r-- | net/tipc/bcast.c | 275 | ||||
| -rw-r--r-- | net/tipc/bcast.h | 12 | ||||
| -rw-r--r-- | net/tipc/bearer.c | 50 | ||||
| -rw-r--r-- | net/tipc/core.c | 2 | ||||
| -rw-r--r-- | net/tipc/core.h | 3 | ||||
| -rw-r--r-- | net/tipc/group.c | 2 | ||||
| -rw-r--r-- | net/tipc/link.c | 325 | ||||
| -rw-r--r-- | net/tipc/link.h | 2 | ||||
| -rw-r--r-- | net/tipc/monitor.c | 4 | ||||
| -rw-r--r-- | net/tipc/msg.h | 62 | ||||
| -rw-r--r-- | net/tipc/name_table.c | 4 | ||||
| -rw-r--r-- | net/tipc/net.c | 8 | ||||
| -rw-r--r-- | net/tipc/netlink.c | 51 | ||||
| -rw-r--r-- | net/tipc/netlink_compat.c | 88 | ||||
| -rw-r--r-- | net/tipc/node.c | 119 | ||||
| -rw-r--r-- | net/tipc/node.h | 8 | ||||
| -rw-r--r-- | net/tipc/socket.c | 25 | ||||
| -rw-r--r-- | net/tipc/udp_media.c | 27 | 
18 files changed, 853 insertions, 214 deletions
| diff --git a/net/tipc/bcast.c b/net/tipc/bcast.c index d8026543bf4c..6c997d4a6218 100644 --- a/net/tipc/bcast.c +++ b/net/tipc/bcast.c @@ -54,7 +54,9 @@ const char tipc_bclink_name[] = "broadcast-link";   * @dests: array keeping number of reachable destinations per bearer   * @primary_bearer: a bearer having links to all broadcast destinations, if any   * @bcast_support: indicates if primary bearer, if any, supports broadcast + * @force_bcast: forces broadcast for multicast traffic   * @rcast_support: indicates if all peer nodes support replicast + * @force_rcast: forces replicast for multicast traffic   * @rc_ratio: dest count as percentage of cluster size where send method changes   * @bc_threshold: calculated from rc_ratio; if dests > threshold use broadcast   */ @@ -64,7 +66,9 @@ struct tipc_bc_base {  	int dests[MAX_BEARERS];  	int primary_bearer;  	bool bcast_support; +	bool force_bcast;  	bool rcast_support; +	bool force_rcast;  	int rc_ratio;  	int bc_threshold;  }; @@ -216,9 +220,24 @@ static void tipc_bcast_select_xmit_method(struct net *net, int dests,  	}  	/* Can current method be changed ? */  	method->expires = jiffies + TIPC_METHOD_EXPIRE; -	if (method->mandatory || time_before(jiffies, exp)) +	if (method->mandatory)  		return; +	if (!(tipc_net(net)->capabilities & TIPC_MCAST_RBCTL) && +	    time_before(jiffies, exp)) +		return; + +	/* Configuration as force 'broadcast' method */ +	if (bb->force_bcast) { +		method->rcast = false; +		return; +	} +	/* Configuration as force 'replicast' method */ +	if (bb->force_rcast) { +		method->rcast = true; +		return; +	} +	/* Configuration as 'autoselect' or default method */  	/* Determine method to use now */  	method->rcast = dests <= bb->bc_threshold;  } @@ -281,6 +300,63 @@ static int tipc_rcast_xmit(struct net *net, struct sk_buff_head *pkts,  	return 0;  } +/* tipc_mcast_send_sync - deliver a dummy message with SYN bit + * @net: the applicable net namespace + * @skb: socket buffer to copy + * @method: send method to be used + * @dests: destination nodes for message. + * @cong_link_cnt: returns number of encountered congested destination links + * Returns 0 if success, otherwise errno + */ +static int tipc_mcast_send_sync(struct net *net, struct sk_buff *skb, +				struct tipc_mc_method *method, +				struct tipc_nlist *dests, +				u16 *cong_link_cnt) +{ +	struct tipc_msg *hdr, *_hdr; +	struct sk_buff_head tmpq; +	struct sk_buff *_skb; + +	/* Is a cluster supporting with new capabilities ? */ +	if (!(tipc_net(net)->capabilities & TIPC_MCAST_RBCTL)) +		return 0; + +	hdr = buf_msg(skb); +	if (msg_user(hdr) == MSG_FRAGMENTER) +		hdr = msg_get_wrapped(hdr); +	if (msg_type(hdr) != TIPC_MCAST_MSG) +		return 0; + +	/* Allocate dummy message */ +	_skb = tipc_buf_acquire(MCAST_H_SIZE, GFP_KERNEL); +	if (!_skb) +		return -ENOMEM; + +	/* Preparing for 'synching' header */ +	msg_set_syn(hdr, 1); + +	/* Copy skb's header into a dummy header */ +	skb_copy_to_linear_data(_skb, hdr, MCAST_H_SIZE); +	skb_orphan(_skb); + +	/* Reverse method for dummy message */ +	_hdr = buf_msg(_skb); +	msg_set_size(_hdr, MCAST_H_SIZE); +	msg_set_is_rcast(_hdr, !msg_is_rcast(hdr)); + +	skb_queue_head_init(&tmpq); +	__skb_queue_tail(&tmpq, _skb); +	if (method->rcast) +		tipc_bcast_xmit(net, &tmpq, cong_link_cnt); +	else +		tipc_rcast_xmit(net, &tmpq, dests, cong_link_cnt); + +	/* This queue should normally be empty by now */ +	__skb_queue_purge(&tmpq); + +	return 0; +} +  /* tipc_mcast_xmit - deliver message to indicated destination nodes   *                   and to identified node local sockets   * @net: the applicable net namespace @@ -296,6 +372,9 @@ int tipc_mcast_xmit(struct net *net, struct sk_buff_head *pkts,  		    u16 *cong_link_cnt)  {  	struct sk_buff_head inputq, localq; +	bool rcast = method->rcast; +	struct tipc_msg *hdr; +	struct sk_buff *skb;  	int rc = 0;  	skb_queue_head_init(&inputq); @@ -309,6 +388,18 @@ int tipc_mcast_xmit(struct net *net, struct sk_buff_head *pkts,  	/* Send according to determined transmit method */  	if (dests->remote) {  		tipc_bcast_select_xmit_method(net, dests->remote, method); + +		skb = skb_peek(pkts); +		hdr = buf_msg(skb); +		if (msg_user(hdr) == MSG_FRAGMENTER) +			hdr = msg_get_wrapped(hdr); +		msg_set_is_rcast(hdr, method->rcast); + +		/* Switch method ? */ +		if (rcast != method->rcast) +			tipc_mcast_send_sync(net, skb, method, +					     dests, cong_link_cnt); +  		if (method->rcast)  			rc = tipc_rcast_xmit(net, pkts, dests, cong_link_cnt);  		else @@ -485,10 +576,63 @@ static int tipc_bc_link_set_queue_limits(struct net *net, u32 limit)  	return 0;  } +static int tipc_bc_link_set_broadcast_mode(struct net *net, u32 bc_mode) +{ +	struct tipc_bc_base *bb = tipc_bc_base(net); + +	switch (bc_mode) { +	case BCLINK_MODE_BCAST: +		if (!bb->bcast_support) +			return -ENOPROTOOPT; + +		bb->force_bcast = true; +		bb->force_rcast = false; +		break; +	case BCLINK_MODE_RCAST: +		if (!bb->rcast_support) +			return -ENOPROTOOPT; + +		bb->force_bcast = false; +		bb->force_rcast = true; +		break; +	case BCLINK_MODE_SEL: +		if (!bb->bcast_support || !bb->rcast_support) +			return -ENOPROTOOPT; + +		bb->force_bcast = false; +		bb->force_rcast = false; +		break; +	default: +		return -EINVAL; +	} + +	return 0; +} + +static int tipc_bc_link_set_broadcast_ratio(struct net *net, u32 bc_ratio) +{ +	struct tipc_bc_base *bb = tipc_bc_base(net); + +	if (!bb->bcast_support || !bb->rcast_support) +		return -ENOPROTOOPT; + +	if (bc_ratio > 100 || bc_ratio <= 0) +		return -EINVAL; + +	bb->rc_ratio = bc_ratio; +	tipc_bcast_lock(net); +	tipc_bcbase_calc_bc_threshold(net); +	tipc_bcast_unlock(net); + +	return 0; +} +  int tipc_nl_bc_link_set(struct net *net, struct nlattr *attrs[])  {  	int err;  	u32 win; +	u32 bc_mode; +	u32 bc_ratio;  	struct nlattr *props[TIPC_NLA_PROP_MAX + 1];  	if (!attrs[TIPC_NLA_LINK_PROP]) @@ -498,12 +642,28 @@ int tipc_nl_bc_link_set(struct net *net, struct nlattr *attrs[])  	if (err)  		return err; -	if (!props[TIPC_NLA_PROP_WIN]) +	if (!props[TIPC_NLA_PROP_WIN] && +	    !props[TIPC_NLA_PROP_BROADCAST] && +	    !props[TIPC_NLA_PROP_BROADCAST_RATIO]) {  		return -EOPNOTSUPP; +	} + +	if (props[TIPC_NLA_PROP_BROADCAST]) { +		bc_mode = nla_get_u32(props[TIPC_NLA_PROP_BROADCAST]); +		err = tipc_bc_link_set_broadcast_mode(net, bc_mode); +	} + +	if (!err && props[TIPC_NLA_PROP_BROADCAST_RATIO]) { +		bc_ratio = nla_get_u32(props[TIPC_NLA_PROP_BROADCAST_RATIO]); +		err = tipc_bc_link_set_broadcast_ratio(net, bc_ratio); +	} -	win = nla_get_u32(props[TIPC_NLA_PROP_WIN]); +	if (!err && props[TIPC_NLA_PROP_WIN]) { +		win = nla_get_u32(props[TIPC_NLA_PROP_WIN]); +		err = tipc_bc_link_set_queue_limits(net, win); +	} -	return tipc_bc_link_set_queue_limits(net, win); +	return err;  }  int tipc_bcast_init(struct net *net) @@ -529,7 +689,7 @@ int tipc_bcast_init(struct net *net)  		goto enomem;  	bb->link = l;  	tn->bcl = l; -	bb->rc_ratio = 25; +	bb->rc_ratio = 10;  	bb->rcast_support = true;  	return 0;  enomem: @@ -576,3 +736,108 @@ void tipc_nlist_purge(struct tipc_nlist *nl)  	nl->remote = 0;  	nl->local = false;  } + +u32 tipc_bcast_get_broadcast_mode(struct net *net) +{ +	struct tipc_bc_base *bb = tipc_bc_base(net); + +	if (bb->force_bcast) +		return BCLINK_MODE_BCAST; + +	if (bb->force_rcast) +		return BCLINK_MODE_RCAST; + +	if (bb->bcast_support && bb->rcast_support) +		return BCLINK_MODE_SEL; + +	return 0; +} + +u32 tipc_bcast_get_broadcast_ratio(struct net *net) +{ +	struct tipc_bc_base *bb = tipc_bc_base(net); + +	return bb->rc_ratio; +} + +void tipc_mcast_filter_msg(struct net *net, struct sk_buff_head *defq, +			   struct sk_buff_head *inputq) +{ +	struct sk_buff *skb, *_skb, *tmp; +	struct tipc_msg *hdr, *_hdr; +	bool match = false; +	u32 node, port; + +	skb = skb_peek(inputq); +	if (!skb) +		return; + +	hdr = buf_msg(skb); + +	if (likely(!msg_is_syn(hdr) && skb_queue_empty(defq))) +		return; + +	node = msg_orignode(hdr); +	if (node == tipc_own_addr(net)) +		return; + +	port = msg_origport(hdr); + +	/* Has the twin SYN message already arrived ? */ +	skb_queue_walk(defq, _skb) { +		_hdr = buf_msg(_skb); +		if (msg_orignode(_hdr) != node) +			continue; +		if (msg_origport(_hdr) != port) +			continue; +		match = true; +		break; +	} + +	if (!match) { +		if (!msg_is_syn(hdr)) +			return; +		__skb_dequeue(inputq); +		__skb_queue_tail(defq, skb); +		return; +	} + +	/* Deliver non-SYN message from other link, otherwise queue it */ +	if (!msg_is_syn(hdr)) { +		if (msg_is_rcast(hdr) != msg_is_rcast(_hdr)) +			return; +		__skb_dequeue(inputq); +		__skb_queue_tail(defq, skb); +		return; +	} + +	/* Queue non-SYN/SYN message from same link */ +	if (msg_is_rcast(hdr) == msg_is_rcast(_hdr)) { +		__skb_dequeue(inputq); +		__skb_queue_tail(defq, skb); +		return; +	} + +	/* Matching SYN messages => return the one with data, if any */ +	__skb_unlink(_skb, defq); +	if (msg_data_sz(hdr)) { +		kfree_skb(_skb); +	} else { +		__skb_dequeue(inputq); +		kfree_skb(skb); +		__skb_queue_tail(inputq, _skb); +	} + +	/* Deliver subsequent non-SYN messages from same peer */ +	skb_queue_walk_safe(defq, _skb, tmp) { +		_hdr = buf_msg(_skb); +		if (msg_orignode(_hdr) != node) +			continue; +		if (msg_origport(_hdr) != port) +			continue; +		if (msg_is_syn(_hdr)) +			break; +		__skb_unlink(_skb, defq); +		__skb_queue_tail(inputq, _skb); +	} +} diff --git a/net/tipc/bcast.h b/net/tipc/bcast.h index 751530ab0c49..dadad953e2be 100644 --- a/net/tipc/bcast.h +++ b/net/tipc/bcast.h @@ -48,6 +48,10 @@ extern const char tipc_bclink_name[];  #define TIPC_METHOD_EXPIRE msecs_to_jiffies(5000) +#define BCLINK_MODE_BCAST  0x1 +#define BCLINK_MODE_RCAST  0x2 +#define BCLINK_MODE_SEL    0x4 +  struct tipc_nlist {  	struct list_head list;  	u32 self; @@ -63,11 +67,13 @@ void tipc_nlist_del(struct tipc_nlist *nl, u32 node);  /* Cookie to be used between socket and broadcast layer   * @rcast: replicast (instead of broadcast) was used at previous xmit   * @mandatory: broadcast/replicast indication was set by user + * @deferredq: defer queue to make message in order   * @expires: re-evaluate non-mandatory transmit method if we are past this   */  struct tipc_mc_method {  	bool rcast;  	bool mandatory; +	struct sk_buff_head deferredq;  	unsigned long expires;  }; @@ -92,6 +98,12 @@ int tipc_nl_add_bc_link(struct net *net, struct tipc_nl_msg *msg);  int tipc_nl_bc_link_set(struct net *net, struct nlattr *attrs[]);  int tipc_bclink_reset_stats(struct net *net); +u32 tipc_bcast_get_broadcast_mode(struct net *net); +u32 tipc_bcast_get_broadcast_ratio(struct net *net); + +void tipc_mcast_filter_msg(struct net *net, struct sk_buff_head *defq, +			   struct sk_buff_head *inputq); +  static inline void tipc_bcast_lock(struct net *net)  {  	spin_lock_bh(&tipc_net(net)->bclock); diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c index d27f30a9a01d..2bed6589f41e 100644 --- a/net/tipc/bearer.c +++ b/net/tipc/bearer.c @@ -687,14 +687,14 @@ static int __tipc_nl_add_bearer(struct tipc_nl_msg *msg,  	if (!hdr)  		return -EMSGSIZE; -	attrs = nla_nest_start(msg->skb, TIPC_NLA_BEARER); +	attrs = nla_nest_start_noflag(msg->skb, TIPC_NLA_BEARER);  	if (!attrs)  		goto msg_full;  	if (nla_put_string(msg->skb, TIPC_NLA_BEARER_NAME, bearer->name))  		goto attr_msg_full; -	prop = nla_nest_start(msg->skb, TIPC_NLA_BEARER_PROP); +	prop = nla_nest_start_noflag(msg->skb, TIPC_NLA_BEARER_PROP);  	if (!prop)  		goto prop_msg_full;  	if (nla_put_u32(msg->skb, TIPC_NLA_PROP_PRIO, bearer->priority)) @@ -776,9 +776,9 @@ int tipc_nl_bearer_get(struct sk_buff *skb, struct genl_info *info)  	if (!info->attrs[TIPC_NLA_BEARER])  		return -EINVAL; -	err = nla_parse_nested(attrs, TIPC_NLA_BEARER_MAX, -			       info->attrs[TIPC_NLA_BEARER], -			       tipc_nl_bearer_policy, info->extack); +	err = nla_parse_nested_deprecated(attrs, TIPC_NLA_BEARER_MAX, +					  info->attrs[TIPC_NLA_BEARER], +					  tipc_nl_bearer_policy, info->extack);  	if (err)  		return err; @@ -825,9 +825,9 @@ int __tipc_nl_bearer_disable(struct sk_buff *skb, struct genl_info *info)  	if (!info->attrs[TIPC_NLA_BEARER])  		return -EINVAL; -	err = nla_parse_nested(attrs, TIPC_NLA_BEARER_MAX, -			       info->attrs[TIPC_NLA_BEARER], -			       tipc_nl_bearer_policy, info->extack); +	err = nla_parse_nested_deprecated(attrs, TIPC_NLA_BEARER_MAX, +					  info->attrs[TIPC_NLA_BEARER], +					  tipc_nl_bearer_policy, info->extack);  	if (err)  		return err; @@ -870,9 +870,9 @@ int __tipc_nl_bearer_enable(struct sk_buff *skb, struct genl_info *info)  	if (!info->attrs[TIPC_NLA_BEARER])  		return -EINVAL; -	err = nla_parse_nested(attrs, TIPC_NLA_BEARER_MAX, -			       info->attrs[TIPC_NLA_BEARER], -			       tipc_nl_bearer_policy, info->extack); +	err = nla_parse_nested_deprecated(attrs, TIPC_NLA_BEARER_MAX, +					  info->attrs[TIPC_NLA_BEARER], +					  tipc_nl_bearer_policy, info->extack);  	if (err)  		return err; @@ -921,9 +921,9 @@ int tipc_nl_bearer_add(struct sk_buff *skb, struct genl_info *info)  	if (!info->attrs[TIPC_NLA_BEARER])  		return -EINVAL; -	err = nla_parse_nested(attrs, TIPC_NLA_BEARER_MAX, -			       info->attrs[TIPC_NLA_BEARER], -			       tipc_nl_bearer_policy, info->extack); +	err = nla_parse_nested_deprecated(attrs, TIPC_NLA_BEARER_MAX, +					  info->attrs[TIPC_NLA_BEARER], +					  tipc_nl_bearer_policy, info->extack);  	if (err)  		return err; @@ -964,9 +964,9 @@ int __tipc_nl_bearer_set(struct sk_buff *skb, struct genl_info *info)  	if (!info->attrs[TIPC_NLA_BEARER])  		return -EINVAL; -	err = nla_parse_nested(attrs, TIPC_NLA_BEARER_MAX, -			       info->attrs[TIPC_NLA_BEARER], -			       tipc_nl_bearer_policy, info->extack); +	err = nla_parse_nested_deprecated(attrs, TIPC_NLA_BEARER_MAX, +					  info->attrs[TIPC_NLA_BEARER], +					  tipc_nl_bearer_policy, info->extack);  	if (err)  		return err; @@ -1033,14 +1033,14 @@ static int __tipc_nl_add_media(struct tipc_nl_msg *msg,  	if (!hdr)  		return -EMSGSIZE; -	attrs = nla_nest_start(msg->skb, TIPC_NLA_MEDIA); +	attrs = nla_nest_start_noflag(msg->skb, TIPC_NLA_MEDIA);  	if (!attrs)  		goto msg_full;  	if (nla_put_string(msg->skb, TIPC_NLA_MEDIA_NAME, media->name))  		goto attr_msg_full; -	prop = nla_nest_start(msg->skb, TIPC_NLA_MEDIA_PROP); +	prop = nla_nest_start_noflag(msg->skb, TIPC_NLA_MEDIA_PROP);  	if (!prop)  		goto prop_msg_full;  	if (nla_put_u32(msg->skb, TIPC_NLA_PROP_PRIO, media->priority)) @@ -1107,9 +1107,9 @@ int tipc_nl_media_get(struct sk_buff *skb, struct genl_info *info)  	if (!info->attrs[TIPC_NLA_MEDIA])  		return -EINVAL; -	err = nla_parse_nested(attrs, TIPC_NLA_MEDIA_MAX, -			       info->attrs[TIPC_NLA_MEDIA], -			       tipc_nl_media_policy, info->extack); +	err = nla_parse_nested_deprecated(attrs, TIPC_NLA_MEDIA_MAX, +					  info->attrs[TIPC_NLA_MEDIA], +					  tipc_nl_media_policy, info->extack);  	if (err)  		return err; @@ -1155,9 +1155,9 @@ int __tipc_nl_media_set(struct sk_buff *skb, struct genl_info *info)  	if (!info->attrs[TIPC_NLA_MEDIA])  		return -EINVAL; -	err = nla_parse_nested(attrs, TIPC_NLA_MEDIA_MAX, -			       info->attrs[TIPC_NLA_MEDIA], -			       tipc_nl_media_policy, info->extack); +	err = nla_parse_nested_deprecated(attrs, TIPC_NLA_MEDIA_MAX, +					  info->attrs[TIPC_NLA_MEDIA], +					  tipc_nl_media_policy, info->extack);  	if (!attrs[TIPC_NLA_MEDIA_NAME])  		return -EINVAL; diff --git a/net/tipc/core.c b/net/tipc/core.c index 5b38f5164281..27cccd101ef6 100644 --- a/net/tipc/core.c +++ b/net/tipc/core.c @@ -43,6 +43,7 @@  #include "net.h"  #include "socket.h"  #include "bcast.h" +#include "node.h"  #include <linux/module.h> @@ -59,6 +60,7 @@ static int __net_init tipc_init_net(struct net *net)  	tn->node_addr = 0;  	tn->trial_addr = 0;  	tn->addr_trial_end = 0; +	tn->capabilities = TIPC_NODE_CAPABILITIES;  	memset(tn->node_id, 0, sizeof(tn->node_id));  	memset(tn->node_id_string, 0, sizeof(tn->node_id_string));  	tn->mon_threshold = TIPC_DEF_MON_THRESHOLD; diff --git a/net/tipc/core.h b/net/tipc/core.h index 8020a6c360ff..7a68e1b6a066 100644 --- a/net/tipc/core.h +++ b/net/tipc/core.h @@ -122,6 +122,9 @@ struct tipc_net {  	/* Topology subscription server */  	struct tipc_topsrv *topsrv;  	atomic_t subscription_count; + +	/* Cluster capabilities */ +	u16 capabilities;  };  static inline struct tipc_net *tipc_net(struct net *net) diff --git a/net/tipc/group.c b/net/tipc/group.c index 63f39201e41e..992be6113676 100644 --- a/net/tipc/group.c +++ b/net/tipc/group.c @@ -917,7 +917,7 @@ void tipc_group_member_evt(struct tipc_group *grp,  int tipc_group_fill_sock_diag(struct tipc_group *grp, struct sk_buff *skb)  { -	struct nlattr *group = nla_nest_start(skb, TIPC_NLA_SOCK_GROUP); +	struct nlattr *group = nla_nest_start_noflag(skb, TIPC_NLA_SOCK_GROUP);  	if (!group)  		return -EMSGSIZE; diff --git a/net/tipc/link.c b/net/tipc/link.c index 131aa2f0fd27..f5cd986e1e50 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c @@ -151,6 +151,7 @@ struct tipc_link {  	/* Failover/synch */  	u16 drop_point;  	struct sk_buff *failover_reasm_skb; +	struct sk_buff_head failover_deferdq;  	/* Max packet negotiation */  	u16 mtu; @@ -209,6 +210,7 @@ enum {  };  #define TIPC_BC_RETR_LIM msecs_to_jiffies(10)   /* [ms] */ +#define TIPC_UC_RETR_TIME (jiffies + msecs_to_jiffies(1))  /*   * Interval between NACKs when packets arrive out of order @@ -246,6 +248,10 @@ static int tipc_link_build_nack_msg(struct tipc_link *l,  static void tipc_link_build_bc_init_msg(struct tipc_link *l,  					struct sk_buff_head *xmitq);  static bool tipc_link_release_pkts(struct tipc_link *l, u16 to); +static u16 tipc_build_gap_ack_blks(struct tipc_link *l, void *data); +static void tipc_link_advance_transmq(struct tipc_link *l, u16 acked, u16 gap, +				      struct tipc_gap_ack_blks *ga, +				      struct sk_buff_head *xmitq);  /*   *  Simple non-static link routines (i.e. referenced outside this file) @@ -493,6 +499,7 @@ bool tipc_link_create(struct net *net, char *if_name, int bearer_id,  	__skb_queue_head_init(&l->transmq);  	__skb_queue_head_init(&l->backlogq);  	__skb_queue_head_init(&l->deferdq); +	__skb_queue_head_init(&l->failover_deferdq);  	skb_queue_head_init(&l->wakeupq);  	skb_queue_head_init(l->inputq);  	return true; @@ -885,6 +892,7 @@ void tipc_link_reset(struct tipc_link *l)  	__skb_queue_purge(&l->transmq);  	__skb_queue_purge(&l->deferdq);  	__skb_queue_purge(&l->backlogq); +	__skb_queue_purge(&l->failover_deferdq);  	l->backlog[TIPC_LOW_IMPORTANCE].len = 0;  	l->backlog[TIPC_MEDIUM_IMPORTANCE].len = 0;  	l->backlog[TIPC_HIGH_IMPORTANCE].len = 0; @@ -1156,34 +1164,14 @@ static bool tipc_data_input(struct tipc_link *l, struct sk_buff *skb,   * Consumes buffer   */  static int tipc_link_input(struct tipc_link *l, struct sk_buff *skb, -			   struct sk_buff_head *inputq) +			   struct sk_buff_head *inputq, +			   struct sk_buff **reasm_skb)  {  	struct tipc_msg *hdr = buf_msg(skb); -	struct sk_buff **reasm_skb = &l->reasm_buf;  	struct sk_buff *iskb;  	struct sk_buff_head tmpq;  	int usr = msg_user(hdr); -	int rc = 0;  	int pos = 0; -	int ipos = 0; - -	if (unlikely(usr == TUNNEL_PROTOCOL)) { -		if (msg_type(hdr) == SYNCH_MSG) { -			__skb_queue_purge(&l->deferdq); -			goto drop; -		} -		if (!tipc_msg_extract(skb, &iskb, &ipos)) -			return rc; -		kfree_skb(skb); -		skb = iskb; -		hdr = buf_msg(skb); -		if (less(msg_seqno(hdr), l->drop_point)) -			goto drop; -		if (tipc_data_input(l, skb, inputq)) -			return rc; -		usr = msg_user(hdr); -		reasm_skb = &l->failover_reasm_skb; -	}  	if (usr == MSG_BUNDLER) {  		skb_queue_head_init(&tmpq); @@ -1208,11 +1196,66 @@ static int tipc_link_input(struct tipc_link *l, struct sk_buff *skb,  		tipc_link_bc_init_rcv(l->bc_rcvlink, hdr);  		tipc_bcast_unlock(l->net);  	} -drop: +  	kfree_skb(skb);  	return 0;  } +/* tipc_link_tnl_rcv() - receive TUNNEL_PROTOCOL message, drop or process the + *			 inner message along with the ones in the old link's + *			 deferdq + * @l: tunnel link + * @skb: TUNNEL_PROTOCOL message + * @inputq: queue to put messages ready for delivery + */ +static int tipc_link_tnl_rcv(struct tipc_link *l, struct sk_buff *skb, +			     struct sk_buff_head *inputq) +{ +	struct sk_buff **reasm_skb = &l->failover_reasm_skb; +	struct sk_buff_head *fdefq = &l->failover_deferdq; +	struct tipc_msg *hdr = buf_msg(skb); +	struct sk_buff *iskb; +	int ipos = 0; +	int rc = 0; +	u16 seqno; + +	/* SYNCH_MSG */ +	if (msg_type(hdr) == SYNCH_MSG) +		goto drop; + +	/* FAILOVER_MSG */ +	if (!tipc_msg_extract(skb, &iskb, &ipos)) { +		pr_warn_ratelimited("Cannot extract FAILOVER_MSG, defq: %d\n", +				    skb_queue_len(fdefq)); +		return rc; +	} + +	do { +		seqno = buf_seqno(iskb); + +		if (unlikely(less(seqno, l->drop_point))) { +			kfree_skb(iskb); +			continue; +		} + +		if (unlikely(seqno != l->drop_point)) { +			__tipc_skb_queue_sorted(fdefq, seqno, iskb); +			continue; +		} + +		l->drop_point++; + +		if (!tipc_data_input(l, iskb, inputq)) +			rc |= tipc_link_input(l, iskb, inputq, reasm_skb); +		if (unlikely(rc)) +			break; +	} while ((iskb = __tipc_skb_dequeue(fdefq, l->drop_point))); + +drop: +	kfree_skb(skb); +	return rc; +} +  static bool tipc_link_release_pkts(struct tipc_link *l, u16 acked)  {  	bool released = false; @@ -1228,6 +1271,106 @@ static bool tipc_link_release_pkts(struct tipc_link *l, u16 acked)  	return released;  } +/* tipc_build_gap_ack_blks - build Gap ACK blocks + * @l: tipc link that data have come with gaps in sequence if any + * @data: data buffer to store the Gap ACK blocks after built + * + * returns the actual allocated memory size + */ +static u16 tipc_build_gap_ack_blks(struct tipc_link *l, void *data) +{ +	struct sk_buff *skb = skb_peek(&l->deferdq); +	struct tipc_gap_ack_blks *ga = data; +	u16 len, expect, seqno = 0; +	u8 n = 0; + +	if (!skb) +		goto exit; + +	expect = buf_seqno(skb); +	skb_queue_walk(&l->deferdq, skb) { +		seqno = buf_seqno(skb); +		if (unlikely(more(seqno, expect))) { +			ga->gacks[n].ack = htons(expect - 1); +			ga->gacks[n].gap = htons(seqno - expect); +			if (++n >= MAX_GAP_ACK_BLKS) { +				pr_info_ratelimited("Too few Gap ACK blocks!\n"); +				goto exit; +			} +		} else if (unlikely(less(seqno, expect))) { +			pr_warn("Unexpected skb in deferdq!\n"); +			continue; +		} +		expect = seqno + 1; +	} + +	/* last block */ +	ga->gacks[n].ack = htons(seqno); +	ga->gacks[n].gap = 0; +	n++; + +exit: +	len = tipc_gap_ack_blks_sz(n); +	ga->len = htons(len); +	ga->gack_cnt = n; +	return len; +} + +/* tipc_link_advance_transmq - advance TIPC link transmq queue by releasing + *			       acked packets, also doing retransmissions if + *			       gaps found + * @l: tipc link with transmq queue to be advanced + * @acked: seqno of last packet acked by peer without any gaps before + * @gap: # of gap packets + * @ga: buffer pointer to Gap ACK blocks from peer + * @xmitq: queue for accumulating the retransmitted packets if any + */ +static void tipc_link_advance_transmq(struct tipc_link *l, u16 acked, u16 gap, +				      struct tipc_gap_ack_blks *ga, +				      struct sk_buff_head *xmitq) +{ +	struct sk_buff *skb, *_skb, *tmp; +	struct tipc_msg *hdr; +	u16 bc_ack = l->bc_rcvlink->rcv_nxt - 1; +	u16 ack = l->rcv_nxt - 1; +	u16 seqno; +	u16 n = 0; + +	skb_queue_walk_safe(&l->transmq, skb, tmp) { +		seqno = buf_seqno(skb); + +next_gap_ack: +		if (less_eq(seqno, acked)) { +			/* release skb */ +			__skb_unlink(skb, &l->transmq); +			kfree_skb(skb); +		} else if (less_eq(seqno, acked + gap)) { +			/* retransmit skb */ +			if (time_before(jiffies, TIPC_SKB_CB(skb)->nxt_retr)) +				continue; +			TIPC_SKB_CB(skb)->nxt_retr = TIPC_UC_RETR_TIME; + +			_skb = __pskb_copy(skb, MIN_H_SIZE, GFP_ATOMIC); +			if (!_skb) +				continue; +			hdr = buf_msg(_skb); +			msg_set_ack(hdr, ack); +			msg_set_bcast_ack(hdr, bc_ack); +			_skb->priority = TC_PRIO_CONTROL; +			__skb_queue_tail(xmitq, _skb); +			l->stats.retransmitted++; +		} else { +			/* retry with Gap ACK blocks if any */ +			if (!ga || n >= ga->gack_cnt) +				break; +			acked = ntohs(ga->gacks[n].ack); +			gap = ntohs(ga->gacks[n].gap); +			n++; +			goto next_gap_ack; +		} +	} +} +  /* tipc_link_build_state_msg: prepare link state message for transmission   *   * Note that sending of broadcast ack is coordinated among nodes, to reduce @@ -1282,6 +1425,7 @@ static int tipc_link_build_nack_msg(struct tipc_link *l,  				    struct sk_buff_head *xmitq)  {  	u32 def_cnt = ++l->stats.deferred_recv; +	u32 defq_len = skb_queue_len(&l->deferdq);  	int match1, match2;  	if (link_is_bc_rcvlink(l)) { @@ -1292,7 +1436,7 @@ static int tipc_link_build_nack_msg(struct tipc_link *l,  		return 0;  	} -	if ((skb_queue_len(&l->deferdq) == 1) || !(def_cnt % TIPC_NACK_INTV)) +	if (defq_len >= 3 && !((defq_len - 3) % 16))  		tipc_link_build_proto_msg(l, STATE_MSG, 0, 0, 0, 0, 0, xmitq);  	return 0;  } @@ -1306,29 +1450,29 @@ int tipc_link_rcv(struct tipc_link *l, struct sk_buff *skb,  		  struct sk_buff_head *xmitq)  {  	struct sk_buff_head *defq = &l->deferdq; -	struct tipc_msg *hdr; +	struct tipc_msg *hdr = buf_msg(skb);  	u16 seqno, rcv_nxt, win_lim;  	int rc = 0; +	/* Verify and update link state */ +	if (unlikely(msg_user(hdr) == LINK_PROTOCOL)) +		return tipc_link_proto_rcv(l, skb, xmitq); + +	/* Don't send probe at next timeout expiration */ +	l->silent_intv_cnt = 0; +  	do {  		hdr = buf_msg(skb);  		seqno = msg_seqno(hdr);  		rcv_nxt = l->rcv_nxt;  		win_lim = rcv_nxt + TIPC_MAX_LINK_WIN; -		/* Verify and update link state */ -		if (unlikely(msg_user(hdr) == LINK_PROTOCOL)) -			return tipc_link_proto_rcv(l, skb, xmitq); -  		if (unlikely(!link_is_up(l))) {  			if (l->state == LINK_ESTABLISHING)  				rc = TIPC_LINK_UP_EVT;  			goto drop;  		} -		/* Don't send probe at next timeout expiration */ -		l->silent_intv_cnt = 0; -  		/* Drop if outside receive window */  		if (unlikely(less(seqno, rcv_nxt) || more(seqno, win_lim))) {  			l->stats.duplicates++; @@ -1353,13 +1497,16 @@ int tipc_link_rcv(struct tipc_link *l, struct sk_buff *skb,  		/* Deliver packet */  		l->rcv_nxt++;  		l->stats.recv_pkts++; -		if (!tipc_data_input(l, skb, l->inputq)) -			rc |= tipc_link_input(l, skb, l->inputq); + +		if (unlikely(msg_user(hdr) == TUNNEL_PROTOCOL)) +			rc |= tipc_link_tnl_rcv(l, skb, l->inputq); +		else if (!tipc_data_input(l, skb, l->inputq)) +			rc |= tipc_link_input(l, skb, l->inputq, &l->reasm_buf);  		if (unlikely(++l->rcv_unacked >= TIPC_MIN_LINK_WIN))  			rc |= tipc_link_build_state_msg(l, xmitq);  		if (unlikely(rc & ~TIPC_LINK_SND_STATE))  			break; -	} while ((skb = __skb_dequeue(defq))); +	} while ((skb = __tipc_skb_dequeue(defq, l->rcv_nxt)));  	return rc;  drop: @@ -1380,6 +1527,7 @@ static void tipc_link_build_proto_msg(struct tipc_link *l, int mtyp, bool probe,  	struct tipc_mon_state *mstate = &l->mon_state;  	int dlen = 0;  	void *data; +	u16 glen = 0;  	/* Don't send protocol message during reset or link failover */  	if (tipc_link_is_blocked(l)) @@ -1392,8 +1540,8 @@ static void tipc_link_build_proto_msg(struct tipc_link *l, int mtyp, bool probe,  		rcvgap = buf_seqno(skb_peek(dfq)) - l->rcv_nxt;  	skb = tipc_msg_create(LINK_PROTOCOL, mtyp, INT_H_SIZE, -			      tipc_max_domain_size, l->addr, -			      tipc_own_addr(l->net), 0, 0, 0); +			      tipc_max_domain_size + MAX_GAP_ACK_BLKS_SZ, +			      l->addr, tipc_own_addr(l->net), 0, 0, 0);  	if (!skb)  		return; @@ -1420,9 +1568,11 @@ static void tipc_link_build_proto_msg(struct tipc_link *l, int mtyp, bool probe,  		msg_set_bc_gap(hdr, link_bc_rcv_gap(bcl));  		msg_set_probe(hdr, probe);  		msg_set_is_keepalive(hdr, probe || probe_reply); -		tipc_mon_prep(l->net, data, &dlen, mstate, l->bearer_id); -		msg_set_size(hdr, INT_H_SIZE + dlen); -		skb_trim(skb, INT_H_SIZE + dlen); +		if (l->peer_caps & TIPC_GAP_ACK_BLOCK) +			glen = tipc_build_gap_ack_blks(l, data); +		tipc_mon_prep(l->net, data + glen, &dlen, mstate, l->bearer_id); +		msg_set_size(hdr, INT_H_SIZE + glen + dlen); +		skb_trim(skb, INT_H_SIZE + glen + dlen);  		l->stats.sent_states++;  		l->rcv_unacked = 0;  	} else { @@ -1481,6 +1631,7 @@ void tipc_link_create_dummy_tnl_msg(struct tipc_link *l,  void tipc_link_tnl_prepare(struct tipc_link *l, struct tipc_link *tnl,  			   int mtyp, struct sk_buff_head *xmitq)  { +	struct sk_buff_head *fdefq = &tnl->failover_deferdq;  	struct sk_buff *skb, *tnlskb;  	struct tipc_msg *hdr, tnlhdr;  	struct sk_buff_head *queue = &l->transmq; @@ -1508,7 +1659,11 @@ void tipc_link_tnl_prepare(struct tipc_link *l, struct tipc_link *tnl,  	/* Initialize reusable tunnel packet header */  	tipc_msg_init(tipc_own_addr(l->net), &tnlhdr, TUNNEL_PROTOCOL,  		      mtyp, INT_H_SIZE, l->addr); -	pktcnt = skb_queue_len(&l->transmq) + skb_queue_len(&l->backlogq); +	if (mtyp == SYNCH_MSG) +		pktcnt = l->snd_nxt - buf_seqno(skb_peek(&l->transmq)); +	else +		pktcnt = skb_queue_len(&l->transmq); +	pktcnt += skb_queue_len(&l->backlogq);  	msg_set_msgcnt(&tnlhdr, pktcnt);  	msg_set_bearer_id(&tnlhdr, l->peer_bearer_id);  tnl: @@ -1539,6 +1694,49 @@ tnl:  		tnl->drop_point = l->rcv_nxt;  		tnl->failover_reasm_skb = l->reasm_buf;  		l->reasm_buf = NULL; + +		/* Failover the link's deferdq */ +		if (unlikely(!skb_queue_empty(fdefq))) { +			pr_warn("Link failover deferdq not empty: %d!\n", +				skb_queue_len(fdefq)); +			__skb_queue_purge(fdefq); +		} +		skb_queue_splice_init(&l->deferdq, fdefq); +	} +} + +/** + * tipc_link_failover_prepare() - prepare tnl for link failover + * + * This is a special version of the precursor - tipc_link_tnl_prepare(), + * see the tipc_node_link_failover() for details + * + * @l: failover link + * @tnl: tunnel link + * @xmitq: queue for messages to be xmited + */ +void tipc_link_failover_prepare(struct tipc_link *l, struct tipc_link *tnl, +				struct sk_buff_head *xmitq) +{ +	struct sk_buff_head *fdefq = &tnl->failover_deferdq; + +	tipc_link_create_dummy_tnl_msg(tnl, xmitq); + +	/* This failover link enpoint was never established before, +	 * so it has not received anything from peer. +	 * Otherwise, it must be a normal failover situation or the +	 * node has entered SELF_DOWN_PEER_LEAVING and both peer nodes +	 * would have to start over from scratch instead. +	 */ +	WARN_ON(l && tipc_link_is_up(l)); +	tnl->drop_point = 1; +	tnl->failover_reasm_skb = NULL; + +	/* Initiate the link's failover deferdq */ +	if (unlikely(!skb_queue_empty(fdefq))) { +		pr_warn("Link failover deferdq not empty: %d!\n", +			skb_queue_len(fdefq)); +		__skb_queue_purge(fdefq);  	}  } @@ -1592,6 +1790,7 @@ static int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb,  			       struct sk_buff_head *xmitq)  {  	struct tipc_msg *hdr = buf_msg(skb); +	struct tipc_gap_ack_blks *ga = NULL;  	u16 rcvgap = 0;  	u16 ack = msg_ack(hdr);  	u16 gap = msg_seq_gap(hdr); @@ -1602,6 +1801,7 @@ static int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb,  	u16 dlen = msg_data_sz(hdr);  	int mtyp = msg_type(hdr);  	bool reply = msg_probe(hdr); +	u16 glen = 0;  	void *data;  	char *if_name;  	int rc = 0; @@ -1699,7 +1899,17 @@ static int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb,  				rc = TIPC_LINK_UP_EVT;  			break;  		} -		tipc_mon_rcv(l->net, data, dlen, l->addr, + +		/* Receive Gap ACK blocks from peer if any */ +		if (l->peer_caps & TIPC_GAP_ACK_BLOCK) { +			ga = (struct tipc_gap_ack_blks *)data; +			glen = ntohs(ga->len); +			/* sanity check: if failed, ignore Gap ACK blocks */ +			if (glen != tipc_gap_ack_blks_sz(ga->gack_cnt)) +				ga = NULL; +		} + +		tipc_mon_rcv(l->net, data + glen, dlen - glen, l->addr,  			     &l->mon_state, l->bearer_id);  		/* Send NACK if peer has sent pkts we haven't received yet */ @@ -1708,13 +1918,12 @@ static int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb,  		if (rcvgap || reply)  			tipc_link_build_proto_msg(l, STATE_MSG, 0, reply,  						  rcvgap, 0, 0, xmitq); -		tipc_link_release_pkts(l, ack); + +		tipc_link_advance_transmq(l, ack, gap, ga, xmitq);  		/* If NACK, retransmit will now start at right position */ -		if (gap) { -			rc = tipc_link_retrans(l, l, ack + 1, ack + gap, xmitq); +		if (gap)  			l->stats.recv_nacks++; -		}  		tipc_link_advance_backlog(l, xmitq);  		if (unlikely(!skb_queue_empty(&l->wakeupq))) @@ -1974,8 +2183,8 @@ int tipc_nl_parse_link_prop(struct nlattr *prop, struct nlattr *props[])  {  	int err; -	err = nla_parse_nested(props, TIPC_NLA_PROP_MAX, prop, -			       tipc_nl_prop_policy, NULL); +	err = nla_parse_nested_deprecated(props, TIPC_NLA_PROP_MAX, prop, +					  tipc_nl_prop_policy, NULL);  	if (err)  		return err; @@ -2054,7 +2263,7 @@ static int __tipc_nl_add_stats(struct sk_buff *skb, struct tipc_stats *s)  			(s->accu_queue_sz / s->queue_sz_counts) : 0}  	}; -	stats = nla_nest_start(skb, TIPC_NLA_LINK_STATS); +	stats = nla_nest_start_noflag(skb, TIPC_NLA_LINK_STATS);  	if (!stats)  		return -EMSGSIZE; @@ -2086,7 +2295,7 @@ int __tipc_nl_add_link(struct net *net, struct tipc_nl_msg *msg,  	if (!hdr)  		return -EMSGSIZE; -	attrs = nla_nest_start(msg->skb, TIPC_NLA_LINK); +	attrs = nla_nest_start_noflag(msg->skb, TIPC_NLA_LINK);  	if (!attrs)  		goto msg_full; @@ -2108,7 +2317,7 @@ int __tipc_nl_add_link(struct net *net, struct tipc_nl_msg *msg,  		if (nla_put_flag(msg->skb, TIPC_NLA_LINK_ACTIVE))  			goto attr_msg_full; -	prop = nla_nest_start(msg->skb, TIPC_NLA_LINK_PROP); +	prop = nla_nest_start_noflag(msg->skb, TIPC_NLA_LINK_PROP);  	if (!prop)  		goto attr_msg_full;  	if (nla_put_u32(msg->skb, TIPC_NLA_PROP_PRIO, link->priority)) @@ -2175,7 +2384,7 @@ static int __tipc_nl_add_bc_link_stat(struct sk_buff *skb,  			(stats->accu_queue_sz / stats->queue_sz_counts) : 0}  	}; -	nest = nla_nest_start(skb, TIPC_NLA_LINK_STATS); +	nest = nla_nest_start_noflag(skb, TIPC_NLA_LINK_STATS);  	if (!nest)  		return -EMSGSIZE; @@ -2199,6 +2408,8 @@ int tipc_nl_add_bc_link(struct net *net, struct tipc_nl_msg *msg)  	struct nlattr *attrs;  	struct nlattr *prop;  	struct tipc_net *tn = net_generic(net, tipc_net_id); +	u32 bc_mode = tipc_bcast_get_broadcast_mode(net); +	u32 bc_ratio = tipc_bcast_get_broadcast_ratio(net);  	struct tipc_link *bcl = tn->bcl;  	if (!bcl) @@ -2213,7 +2424,7 @@ int tipc_nl_add_bc_link(struct net *net, struct tipc_nl_msg *msg)  		return -EMSGSIZE;  	} -	attrs = nla_nest_start(msg->skb, TIPC_NLA_LINK); +	attrs = nla_nest_start_noflag(msg->skb, TIPC_NLA_LINK);  	if (!attrs)  		goto msg_full; @@ -2230,11 +2441,17 @@ int tipc_nl_add_bc_link(struct net *net, struct tipc_nl_msg *msg)  	if (nla_put_u32(msg->skb, TIPC_NLA_LINK_TX, 0))  		goto attr_msg_full; -	prop = nla_nest_start(msg->skb, TIPC_NLA_LINK_PROP); +	prop = nla_nest_start_noflag(msg->skb, TIPC_NLA_LINK_PROP);  	if (!prop)  		goto attr_msg_full;  	if (nla_put_u32(msg->skb, TIPC_NLA_PROP_WIN, bcl->window))  		goto prop_msg_full; +	if (nla_put_u32(msg->skb, TIPC_NLA_PROP_BROADCAST, bc_mode)) +		goto prop_msg_full; +	if (bc_mode & BCLINK_MODE_SEL) +		if (nla_put_u32(msg->skb, TIPC_NLA_PROP_BROADCAST_RATIO, +				bc_ratio)) +			goto prop_msg_full;  	nla_nest_end(msg->skb, prop);  	err = __tipc_nl_add_bc_link_stat(msg->skb, &bcl->stats); diff --git a/net/tipc/link.h b/net/tipc/link.h index 8439e0ee53a8..adcad65e761c 100644 --- a/net/tipc/link.h +++ b/net/tipc/link.h @@ -90,6 +90,8 @@ void tipc_link_tnl_prepare(struct tipc_link *l, struct tipc_link *tnl,  			   int mtyp, struct sk_buff_head *xmitq);  void tipc_link_create_dummy_tnl_msg(struct tipc_link *tnl,  				    struct sk_buff_head *xmitq); +void tipc_link_failover_prepare(struct tipc_link *l, struct tipc_link *tnl, +				struct sk_buff_head *xmitq);  void tipc_link_build_reset_msg(struct tipc_link *l, struct sk_buff_head *xmitq);  int tipc_link_fsm_evt(struct tipc_link *l, int evt);  bool tipc_link_is_up(struct tipc_link *l); diff --git a/net/tipc/monitor.c b/net/tipc/monitor.c index 67f69389ec17..6a6eae88442f 100644 --- a/net/tipc/monitor.c +++ b/net/tipc/monitor.c @@ -696,7 +696,7 @@ static int __tipc_nl_add_monitor_peer(struct tipc_peer *peer,  	if (!hdr)  		return -EMSGSIZE; -	attrs = nla_nest_start(msg->skb, TIPC_NLA_MON_PEER); +	attrs = nla_nest_start_noflag(msg->skb, TIPC_NLA_MON_PEER);  	if (!attrs)  		goto msg_full; @@ -785,7 +785,7 @@ int __tipc_nl_add_monitor(struct net *net, struct tipc_nl_msg *msg,  	if (!hdr)  		return -EMSGSIZE; -	attrs = nla_nest_start(msg->skb, TIPC_NLA_MON); +	attrs = nla_nest_start_noflag(msg->skb, TIPC_NLA_MON);  	if (!attrs)  		goto msg_full; diff --git a/net/tipc/msg.h b/net/tipc/msg.h index d7e4b8b93f9d..8de02ad6e352 100644 --- a/net/tipc/msg.h +++ b/net/tipc/msg.h @@ -117,6 +117,37 @@ struct tipc_msg {  	__be32 hdr[15];  }; +/* struct tipc_gap_ack - TIPC Gap ACK block + * @ack: seqno of the last consecutive packet in link deferdq + * @gap: number of gap packets since the last ack + * + * E.g: + *       link deferdq: 1 2 3 4      10 11      13 14 15       20 + * --> Gap ACK blocks:      <4, 5>,   <11, 1>,      <15, 4>, <20, 0> + */ +struct tipc_gap_ack { +	__be16 ack; +	__be16 gap; +}; + +/* struct tipc_gap_ack_blks + * @len: actual length of the record + * @gack_cnt: number of Gap ACK blocks in the record + * @gacks: array of Gap ACK blocks + */ +struct tipc_gap_ack_blks { +	__be16 len; +	u8 gack_cnt; +	u8 reserved; +	struct tipc_gap_ack gacks[]; +}; + +#define tipc_gap_ack_blks_sz(n) (sizeof(struct tipc_gap_ack_blks) + \ +				 sizeof(struct tipc_gap_ack) * (n)) + +#define MAX_GAP_ACK_BLKS	32 +#define MAX_GAP_ACK_BLKS_SZ	tipc_gap_ack_blks_sz(MAX_GAP_ACK_BLKS) +  static inline struct tipc_msg *buf_msg(struct sk_buff *skb)  {  	return (struct tipc_msg *)skb->data; @@ -257,6 +288,16 @@ static inline void msg_set_src_droppable(struct tipc_msg *m, u32 d)  	msg_set_bits(m, 0, 18, 1, d);  } +static inline bool msg_is_rcast(struct tipc_msg *m) +{ +	return msg_bits(m, 0, 18, 0x1); +} + +static inline void msg_set_is_rcast(struct tipc_msg *m, bool d) +{ +	msg_set_bits(m, 0, 18, 0x1, d); +} +  static inline void msg_set_size(struct tipc_msg *m, u32 sz)  {  	m->hdr[0] = htonl((msg_word(m, 0) & ~0x1ffff) | sz); @@ -1110,4 +1151,25 @@ static inline void tipc_skb_queue_splice_tail_init(struct sk_buff_head *list,  	tipc_skb_queue_splice_tail(&tmp, head);  } +/* __tipc_skb_dequeue() - dequeue the head skb according to expected seqno + * @list: list to be dequeued from + * @seqno: seqno of the expected msg + * + * returns skb dequeued from the list if its seqno is less than or equal to + * the expected one, otherwise the skb is still hold + * + * Note: must be used with appropriate locks held only + */ +static inline struct sk_buff *__tipc_skb_dequeue(struct sk_buff_head *list, +						 u16 seqno) +{ +	struct sk_buff *skb = skb_peek(list); + +	if (skb && less_eq(buf_seqno(skb), seqno)) { +		__skb_unlink(skb, list); +		return skb; +	} +	return NULL; +} +  #endif diff --git a/net/tipc/name_table.c b/net/tipc/name_table.c index 89993afe0fbd..66a65c2cdb23 100644 --- a/net/tipc/name_table.c +++ b/net/tipc/name_table.c @@ -829,11 +829,11 @@ static int __tipc_nl_add_nametable_publ(struct tipc_nl_msg *msg,  		if (!hdr)  			return -EMSGSIZE; -		attrs = nla_nest_start(msg->skb, TIPC_NLA_NAME_TABLE); +		attrs = nla_nest_start_noflag(msg->skb, TIPC_NLA_NAME_TABLE);  		if (!attrs)  			goto msg_full; -		b = nla_nest_start(msg->skb, TIPC_NLA_NAME_TABLE_PUBL); +		b = nla_nest_start_noflag(msg->skb, TIPC_NLA_NAME_TABLE_PUBL);  		if (!b)  			goto attr_msg_full; diff --git a/net/tipc/net.c b/net/tipc/net.c index 7ce1e86b024f..85707c185360 100644 --- a/net/tipc/net.c +++ b/net/tipc/net.c @@ -187,7 +187,7 @@ static int __tipc_nl_add_net(struct net *net, struct tipc_nl_msg *msg)  	if (!hdr)  		return -EMSGSIZE; -	attrs = nla_nest_start(msg->skb, TIPC_NLA_NET); +	attrs = nla_nest_start_noflag(msg->skb, TIPC_NLA_NET);  	if (!attrs)  		goto msg_full; @@ -245,9 +245,9 @@ int __tipc_nl_net_set(struct sk_buff *skb, struct genl_info *info)  	if (!info->attrs[TIPC_NLA_NET])  		return -EINVAL; -	err = nla_parse_nested(attrs, TIPC_NLA_NET_MAX, -			       info->attrs[TIPC_NLA_NET], tipc_nl_net_policy, -			       info->extack); +	err = nla_parse_nested_deprecated(attrs, TIPC_NLA_NET_MAX, +					  info->attrs[TIPC_NLA_NET], +					  tipc_nl_net_policy, info->extack);  	if (err)  		return err; diff --git a/net/tipc/netlink.c b/net/tipc/netlink.c index 99ee419210ba..99bd166bccec 100644 --- a/net/tipc/netlink.c +++ b/net/tipc/netlink.c @@ -110,7 +110,9 @@ const struct nla_policy tipc_nl_prop_policy[TIPC_NLA_PROP_MAX + 1] = {  	[TIPC_NLA_PROP_UNSPEC]		= { .type = NLA_UNSPEC },  	[TIPC_NLA_PROP_PRIO]		= { .type = NLA_U32 },  	[TIPC_NLA_PROP_TOL]		= { .type = NLA_U32 }, -	[TIPC_NLA_PROP_WIN]		= { .type = NLA_U32 } +	[TIPC_NLA_PROP_WIN]		= { .type = NLA_U32 }, +	[TIPC_NLA_PROP_BROADCAST]	= { .type = NLA_U32 }, +	[TIPC_NLA_PROP_BROADCAST_RATIO]	= { .type = NLA_U32 }  };  const struct nla_policy tipc_nl_bearer_policy[TIPC_NLA_BEARER_MAX + 1]	= { @@ -141,115 +143,115 @@ const struct nla_policy tipc_nl_udp_policy[TIPC_NLA_UDP_MAX + 1] = {  static const struct genl_ops tipc_genl_v2_ops[] = {  	{  		.cmd	= TIPC_NL_BEARER_DISABLE, +		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,  		.doit	= tipc_nl_bearer_disable, -		.policy = tipc_nl_policy,  	},  	{  		.cmd	= TIPC_NL_BEARER_ENABLE, +		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,  		.doit	= tipc_nl_bearer_enable, -		.policy = tipc_nl_policy,  	},  	{  		.cmd	= TIPC_NL_BEARER_GET, +		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,  		.doit	= tipc_nl_bearer_get,  		.dumpit	= tipc_nl_bearer_dump, -		.policy = tipc_nl_policy,  	},  	{  		.cmd	= TIPC_NL_BEARER_ADD, +		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,  		.doit	= tipc_nl_bearer_add, -		.policy = tipc_nl_policy,  	},  	{  		.cmd	= TIPC_NL_BEARER_SET, +		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,  		.doit	= tipc_nl_bearer_set, -		.policy = tipc_nl_policy,  	},  	{  		.cmd	= TIPC_NL_SOCK_GET, +		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,  		.start = tipc_dump_start,  		.dumpit	= tipc_nl_sk_dump,  		.done	= tipc_dump_done, -		.policy = tipc_nl_policy,  	},  	{  		.cmd	= TIPC_NL_PUBL_GET, +		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,  		.dumpit	= tipc_nl_publ_dump, -		.policy = tipc_nl_policy,  	},  	{  		.cmd	= TIPC_NL_LINK_GET, +		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,  		.doit   = tipc_nl_node_get_link,  		.dumpit	= tipc_nl_node_dump_link, -		.policy = tipc_nl_policy,  	},  	{  		.cmd	= TIPC_NL_LINK_SET, +		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,  		.doit	= tipc_nl_node_set_link, -		.policy = tipc_nl_policy,  	},  	{  		.cmd	= TIPC_NL_LINK_RESET_STATS, +		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,  		.doit   = tipc_nl_node_reset_link_stats, -		.policy = tipc_nl_policy,  	},  	{  		.cmd	= TIPC_NL_MEDIA_GET, +		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,  		.doit	= tipc_nl_media_get,  		.dumpit	= tipc_nl_media_dump, -		.policy = tipc_nl_policy,  	},  	{  		.cmd	= TIPC_NL_MEDIA_SET, +		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,  		.doit	= tipc_nl_media_set, -		.policy = tipc_nl_policy,  	},  	{  		.cmd	= TIPC_NL_NODE_GET, +		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,  		.dumpit	= tipc_nl_node_dump, -		.policy = tipc_nl_policy,  	},  	{  		.cmd	= TIPC_NL_NET_GET, +		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,  		.dumpit	= tipc_nl_net_dump, -		.policy = tipc_nl_policy,  	},  	{  		.cmd	= TIPC_NL_NET_SET, +		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,  		.doit	= tipc_nl_net_set, -		.policy = tipc_nl_policy,  	},  	{  		.cmd	= TIPC_NL_NAME_TABLE_GET, +		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,  		.dumpit	= tipc_nl_name_table_dump, -		.policy = tipc_nl_policy,  	},  	{  		.cmd	= TIPC_NL_MON_SET, +		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,  		.doit	= tipc_nl_node_set_monitor, -		.policy = tipc_nl_policy,  	},  	{  		.cmd	= TIPC_NL_MON_GET, +		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,  		.doit	= tipc_nl_node_get_monitor,  		.dumpit	= tipc_nl_node_dump_monitor, -		.policy = tipc_nl_policy,  	},  	{  		.cmd	= TIPC_NL_MON_PEER_GET, +		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,  		.dumpit	= tipc_nl_node_dump_monitor_peer, -		.policy = tipc_nl_policy,  	},  	{  		.cmd	= TIPC_NL_PEER_REMOVE, +		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,  		.doit	= tipc_nl_peer_rm, -		.policy = tipc_nl_policy,  	},  #ifdef CONFIG_TIPC_MEDIA_UDP  	{  		.cmd	= TIPC_NL_UDP_GET_REMOTEIP, +		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,  		.dumpit	= tipc_udp_nl_dump_remoteip, -		.policy = tipc_nl_policy,  	},  #endif  }; @@ -259,6 +261,7 @@ struct genl_family tipc_genl_family __ro_after_init = {  	.version	= TIPC_GENL_V2_VERSION,  	.hdrsize	= 0,  	.maxattr	= TIPC_NLA_MAX, +	.policy = tipc_nl_policy,  	.netnsok	= true,  	.module		= THIS_MODULE,  	.ops		= tipc_genl_v2_ops, @@ -273,8 +276,8 @@ int tipc_nlmsg_parse(const struct nlmsghdr *nlh, struct nlattr ***attr)  	if (!*attr)  		return -EOPNOTSUPP; -	return nlmsg_parse(nlh, GENL_HDRLEN, *attr, maxattr, tipc_nl_policy, -			   NULL); +	return nlmsg_parse_deprecated(nlh, GENL_HDRLEN, *attr, maxattr, +				      tipc_nl_policy, NULL);  }  int __init tipc_netlink_start(void) diff --git a/net/tipc/netlink_compat.c b/net/tipc/netlink_compat.c index 340a6e7c43a7..c6a04c09d075 100644 --- a/net/tipc/netlink_compat.c +++ b/net/tipc/netlink_compat.c @@ -328,9 +328,9 @@ static int __tipc_nl_compat_doit(struct tipc_nl_compat_cmd_doit *cmd,  	if (err)  		goto doit_out; -	err = nla_parse(attrbuf, tipc_genl_family.maxattr, -			(const struct nlattr *)trans_buf->data, -			trans_buf->len, NULL, NULL); +	err = nla_parse_deprecated(attrbuf, tipc_genl_family.maxattr, +				   (const struct nlattr *)trans_buf->data, +				   trans_buf->len, NULL, NULL);  	if (err)  		goto doit_out; @@ -378,8 +378,8 @@ static int tipc_nl_compat_bearer_dump(struct tipc_nl_compat_msg *msg,  	if (!attrs[TIPC_NLA_BEARER])  		return -EINVAL; -	err = nla_parse_nested(bearer, TIPC_NLA_BEARER_MAX, -			       attrs[TIPC_NLA_BEARER], NULL, NULL); +	err = nla_parse_nested_deprecated(bearer, TIPC_NLA_BEARER_MAX, +					  attrs[TIPC_NLA_BEARER], NULL, NULL);  	if (err)  		return err; @@ -399,7 +399,7 @@ static int tipc_nl_compat_bearer_enable(struct tipc_nl_compat_cmd_doit *cmd,  	b = (struct tipc_bearer_config *)TLV_DATA(msg->req); -	bearer = nla_nest_start(skb, TIPC_NLA_BEARER); +	bearer = nla_nest_start_noflag(skb, TIPC_NLA_BEARER);  	if (!bearer)  		return -EMSGSIZE; @@ -419,7 +419,7 @@ static int tipc_nl_compat_bearer_enable(struct tipc_nl_compat_cmd_doit *cmd,  		return -EMSGSIZE;  	if (ntohl(b->priority) <= TIPC_MAX_LINK_PRI) { -		prop = nla_nest_start(skb, TIPC_NLA_BEARER_PROP); +		prop = nla_nest_start_noflag(skb, TIPC_NLA_BEARER_PROP);  		if (!prop)  			return -EMSGSIZE;  		if (nla_put_u32(skb, TIPC_NLA_PROP_PRIO, ntohl(b->priority))) @@ -441,7 +441,7 @@ static int tipc_nl_compat_bearer_disable(struct tipc_nl_compat_cmd_doit *cmd,  	name = (char *)TLV_DATA(msg->req); -	bearer = nla_nest_start(skb, TIPC_NLA_BEARER); +	bearer = nla_nest_start_noflag(skb, TIPC_NLA_BEARER);  	if (!bearer)  		return -EMSGSIZE; @@ -514,24 +514,26 @@ static int tipc_nl_compat_link_stat_dump(struct tipc_nl_compat_msg *msg,  	if (!attrs[TIPC_NLA_LINK])  		return -EINVAL; -	err = nla_parse_nested(link, TIPC_NLA_LINK_MAX, attrs[TIPC_NLA_LINK], -			       NULL, NULL); +	err = nla_parse_nested_deprecated(link, TIPC_NLA_LINK_MAX, +					  attrs[TIPC_NLA_LINK], NULL, NULL);  	if (err)  		return err;  	if (!link[TIPC_NLA_LINK_PROP])  		return -EINVAL; -	err = nla_parse_nested(prop, TIPC_NLA_PROP_MAX, -			       link[TIPC_NLA_LINK_PROP], NULL, NULL); +	err = nla_parse_nested_deprecated(prop, TIPC_NLA_PROP_MAX, +					  link[TIPC_NLA_LINK_PROP], NULL, +					  NULL);  	if (err)  		return err;  	if (!link[TIPC_NLA_LINK_STATS])  		return -EINVAL; -	err = nla_parse_nested(stats, TIPC_NLA_STATS_MAX, -			       link[TIPC_NLA_LINK_STATS], NULL, NULL); +	err = nla_parse_nested_deprecated(stats, TIPC_NLA_STATS_MAX, +					  link[TIPC_NLA_LINK_STATS], NULL, +					  NULL);  	if (err)  		return err; @@ -645,8 +647,8 @@ static int tipc_nl_compat_link_dump(struct tipc_nl_compat_msg *msg,  	if (!attrs[TIPC_NLA_LINK])  		return -EINVAL; -	err = nla_parse_nested(link, TIPC_NLA_LINK_MAX, attrs[TIPC_NLA_LINK], -			       NULL, NULL); +	err = nla_parse_nested_deprecated(link, TIPC_NLA_LINK_MAX, +					  attrs[TIPC_NLA_LINK], NULL, NULL);  	if (err)  		return err; @@ -685,7 +687,7 @@ static int tipc_nl_compat_media_set(struct sk_buff *skb,  	lc = (struct tipc_link_config *)TLV_DATA(msg->req); -	media = nla_nest_start(skb, TIPC_NLA_MEDIA); +	media = nla_nest_start_noflag(skb, TIPC_NLA_MEDIA);  	if (!media)  		return -EMSGSIZE; @@ -696,7 +698,7 @@ static int tipc_nl_compat_media_set(struct sk_buff *skb,  	if (nla_put_string(skb, TIPC_NLA_MEDIA_NAME, lc->name))  		return -EMSGSIZE; -	prop = nla_nest_start(skb, TIPC_NLA_MEDIA_PROP); +	prop = nla_nest_start_noflag(skb, TIPC_NLA_MEDIA_PROP);  	if (!prop)  		return -EMSGSIZE; @@ -717,7 +719,7 @@ static int tipc_nl_compat_bearer_set(struct sk_buff *skb,  	lc = (struct tipc_link_config *)TLV_DATA(msg->req); -	bearer = nla_nest_start(skb, TIPC_NLA_BEARER); +	bearer = nla_nest_start_noflag(skb, TIPC_NLA_BEARER);  	if (!bearer)  		return -EMSGSIZE; @@ -728,7 +730,7 @@ static int tipc_nl_compat_bearer_set(struct sk_buff *skb,  	if (nla_put_string(skb, TIPC_NLA_BEARER_NAME, lc->name))  		return -EMSGSIZE; -	prop = nla_nest_start(skb, TIPC_NLA_BEARER_PROP); +	prop = nla_nest_start_noflag(skb, TIPC_NLA_BEARER_PROP);  	if (!prop)  		return -EMSGSIZE; @@ -748,14 +750,14 @@ static int __tipc_nl_compat_link_set(struct sk_buff *skb,  	lc = (struct tipc_link_config *)TLV_DATA(msg->req); -	link = nla_nest_start(skb, TIPC_NLA_LINK); +	link = nla_nest_start_noflag(skb, TIPC_NLA_LINK);  	if (!link)  		return -EMSGSIZE;  	if (nla_put_string(skb, TIPC_NLA_LINK_NAME, lc->name))  		return -EMSGSIZE; -	prop = nla_nest_start(skb, TIPC_NLA_LINK_PROP); +	prop = nla_nest_start_noflag(skb, TIPC_NLA_LINK_PROP);  	if (!prop)  		return -EMSGSIZE; @@ -811,7 +813,7 @@ static int tipc_nl_compat_link_reset_stats(struct tipc_nl_compat_cmd_doit *cmd,  	name = (char *)TLV_DATA(msg->req); -	link = nla_nest_start(skb, TIPC_NLA_LINK); +	link = nla_nest_start_noflag(skb, TIPC_NLA_LINK);  	if (!link)  		return -EMSGSIZE; @@ -869,16 +871,18 @@ static int tipc_nl_compat_name_table_dump(struct tipc_nl_compat_msg *msg,  	if (!attrs[TIPC_NLA_NAME_TABLE])  		return -EINVAL; -	err = nla_parse_nested(nt, TIPC_NLA_NAME_TABLE_MAX, -			       attrs[TIPC_NLA_NAME_TABLE], NULL, NULL); +	err = nla_parse_nested_deprecated(nt, TIPC_NLA_NAME_TABLE_MAX, +					  attrs[TIPC_NLA_NAME_TABLE], NULL, +					  NULL);  	if (err)  		return err;  	if (!nt[TIPC_NLA_NAME_TABLE_PUBL])  		return -EINVAL; -	err = nla_parse_nested(publ, TIPC_NLA_PUBL_MAX, -			       nt[TIPC_NLA_NAME_TABLE_PUBL], NULL, NULL); +	err = nla_parse_nested_deprecated(publ, TIPC_NLA_PUBL_MAX, +					  nt[TIPC_NLA_NAME_TABLE_PUBL], NULL, +					  NULL);  	if (err)  		return err; @@ -937,8 +941,8 @@ static int __tipc_nl_compat_publ_dump(struct tipc_nl_compat_msg *msg,  	if (!attrs[TIPC_NLA_PUBL])  		return -EINVAL; -	err = nla_parse_nested(publ, TIPC_NLA_PUBL_MAX, attrs[TIPC_NLA_PUBL], -			       NULL, NULL); +	err = nla_parse_nested_deprecated(publ, TIPC_NLA_PUBL_MAX, +					  attrs[TIPC_NLA_PUBL], NULL, NULL);  	if (err)  		return err; @@ -973,7 +977,7 @@ static int tipc_nl_compat_publ_dump(struct tipc_nl_compat_msg *msg, u32 sock)  		return -EMSGSIZE;  	} -	nest = nla_nest_start(args, TIPC_NLA_SOCK); +	nest = nla_nest_start_noflag(args, TIPC_NLA_SOCK);  	if (!nest) {  		kfree_skb(args);  		return -EMSGSIZE; @@ -1007,8 +1011,8 @@ static int tipc_nl_compat_sk_dump(struct tipc_nl_compat_msg *msg,  	if (!attrs[TIPC_NLA_SOCK])  		return -EINVAL; -	err = nla_parse_nested(sock, TIPC_NLA_SOCK_MAX, attrs[TIPC_NLA_SOCK], -			       NULL, NULL); +	err = nla_parse_nested_deprecated(sock, TIPC_NLA_SOCK_MAX, +					  attrs[TIPC_NLA_SOCK], NULL, NULL);  	if (err)  		return err; @@ -1019,8 +1023,9 @@ static int tipc_nl_compat_sk_dump(struct tipc_nl_compat_msg *msg,  		u32 node;  		struct nlattr *con[TIPC_NLA_CON_MAX + 1]; -		err = nla_parse_nested(con, TIPC_NLA_CON_MAX, -				       sock[TIPC_NLA_SOCK_CON], NULL, NULL); +		err = nla_parse_nested_deprecated(con, TIPC_NLA_CON_MAX, +						  sock[TIPC_NLA_SOCK_CON], +						  NULL, NULL);  		if (err)  			return err; @@ -1059,8 +1064,8 @@ static int tipc_nl_compat_media_dump(struct tipc_nl_compat_msg *msg,  	if (!attrs[TIPC_NLA_MEDIA])  		return -EINVAL; -	err = nla_parse_nested(media, TIPC_NLA_MEDIA_MAX, -			       attrs[TIPC_NLA_MEDIA], NULL, NULL); +	err = nla_parse_nested_deprecated(media, TIPC_NLA_MEDIA_MAX, +					  attrs[TIPC_NLA_MEDIA], NULL, NULL);  	if (err)  		return err; @@ -1079,8 +1084,8 @@ static int tipc_nl_compat_node_dump(struct tipc_nl_compat_msg *msg,  	if (!attrs[TIPC_NLA_NODE])  		return -EINVAL; -	err = nla_parse_nested(node, TIPC_NLA_NODE_MAX, attrs[TIPC_NLA_NODE], -			       NULL, NULL); +	err = nla_parse_nested_deprecated(node, TIPC_NLA_NODE_MAX, +					  attrs[TIPC_NLA_NODE], NULL, NULL);  	if (err)  		return err; @@ -1100,7 +1105,7 @@ static int tipc_nl_compat_net_set(struct tipc_nl_compat_cmd_doit *cmd,  	val = ntohl(*(__be32 *)TLV_DATA(msg->req)); -	net = nla_nest_start(skb, TIPC_NLA_NET); +	net = nla_nest_start_noflag(skb, TIPC_NLA_NET);  	if (!net)  		return -EMSGSIZE; @@ -1126,8 +1131,8 @@ static int tipc_nl_compat_net_dump(struct tipc_nl_compat_msg *msg,  	if (!attrs[TIPC_NLA_NET])  		return -EINVAL; -	err = nla_parse_nested(net, TIPC_NLA_NET_MAX, attrs[TIPC_NLA_NET], -			       NULL, NULL); +	err = nla_parse_nested_deprecated(net, TIPC_NLA_NET_MAX, +					  attrs[TIPC_NLA_NET], NULL, NULL);  	if (err)  		return err; @@ -1300,6 +1305,7 @@ send:  static const struct genl_ops tipc_genl_compat_ops[] = {  	{  		.cmd		= TIPC_GENL_CMD, +		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,  		.doit		= tipc_nl_compat_recv,  	},  }; diff --git a/net/tipc/node.c b/net/tipc/node.c index dd3b6dc17662..9e106d3ed187 100644 --- a/net/tipc/node.c +++ b/net/tipc/node.c @@ -375,14 +375,20 @@ static struct tipc_node *tipc_node_create(struct net *net, u32 addr,  		if (n->capabilities == capabilities)  			goto exit;  		/* Same node may come back with new capabilities */ -		write_lock_bh(&n->lock); +		tipc_node_write_lock(n);  		n->capabilities = capabilities;  		for (bearer_id = 0; bearer_id < MAX_BEARERS; bearer_id++) {  			l = n->links[bearer_id].link;  			if (l)  				tipc_link_update_caps(l, capabilities);  		} -		write_unlock_bh(&n->lock); +		tipc_node_write_unlock_fast(n); + +		/* Calculate cluster capabilities */ +		tn->capabilities = TIPC_NODE_CAPABILITIES; +		list_for_each_entry_rcu(temp_node, &tn->node_list, list) { +			tn->capabilities &= temp_node->capabilities; +		}  		goto exit;  	}  	n = kzalloc(sizeof(*n), GFP_ATOMIC); @@ -433,6 +439,11 @@ static struct tipc_node *tipc_node_create(struct net *net, u32 addr,  			break;  	}  	list_add_tail_rcu(&n->list, &temp_node->list); +	/* Calculate cluster capabilities */ +	tn->capabilities = TIPC_NODE_CAPABILITIES; +	list_for_each_entry_rcu(temp_node, &tn->node_list, list) { +		tn->capabilities &= temp_node->capabilities; +	}  	trace_tipc_node_create(n, true, " ");  exit:  	spin_unlock_bh(&tn->node_list_lock); @@ -589,6 +600,7 @@ static void  tipc_node_clear_links(struct tipc_node *node)   */  static bool tipc_node_cleanup(struct tipc_node *peer)  { +	struct tipc_node *temp_node;  	struct tipc_net *tn = tipc_net(peer->net);  	bool deleted = false; @@ -604,6 +616,13 @@ static bool tipc_node_cleanup(struct tipc_node *peer)  		deleted = true;  	}  	tipc_node_write_unlock(peer); + +	/* Calculate cluster capabilities */ +	tn->capabilities = TIPC_NODE_CAPABILITIES; +	list_for_each_entry_rcu(temp_node, &tn->node_list, list) { +		tn->capabilities &= temp_node->capabilities; +	} +  	spin_unlock_bh(&tn->node_list_lock);  	return deleted;  } @@ -695,7 +714,6 @@ static void __tipc_node_link_up(struct tipc_node *n, int bearer_id,  		*slot0 = bearer_id;  		*slot1 = bearer_id;  		tipc_node_fsm_evt(n, SELF_ESTABL_CONTACT_EVT); -		n->failover_sent = false;  		n->action_flags |= TIPC_NOTIFY_NODE_UP;  		tipc_link_set_active(nl, true);  		tipc_bcast_add_peer(n->net, nl, xmitq); @@ -738,6 +756,45 @@ static void tipc_node_link_up(struct tipc_node *n, int bearer_id,  }  /** + * tipc_node_link_failover() - start failover in case "half-failover" + * + * This function is only called in a very special situation where link + * failover can be already started on peer node but not on this node. + * This can happen when e.g. + *	1. Both links <1A-2A>, <1B-2B> down + *	2. Link endpoint 2A up, but 1A still down (e.g. due to network + *	   disturbance, wrong session, etc.) + *	3. Link <1B-2B> up + *	4. Link endpoint 2A down (e.g. due to link tolerance timeout) + *	5. Node B starts failover onto link <1B-2B> + * + *	==> Node A does never start link/node failover! + * + * @n: tipc node structure + * @l: link peer endpoint failingover (- can be NULL) + * @tnl: tunnel link + * @xmitq: queue for messages to be xmited on tnl link later + */ +static void tipc_node_link_failover(struct tipc_node *n, struct tipc_link *l, +				    struct tipc_link *tnl, +				    struct sk_buff_head *xmitq) +{ +	/* Avoid to be "self-failover" that can never end */ +	if (!tipc_link_is_up(tnl)) +		return; + +	tipc_link_fsm_evt(tnl, LINK_SYNCH_END_EVT); +	tipc_node_fsm_evt(n, NODE_SYNCH_END_EVT); + +	n->sync_point = tipc_link_rcv_nxt(tnl) + (U16_MAX / 2 - 1); +	tipc_link_failover_prepare(l, tnl, xmitq); + +	if (l) +		tipc_link_fsm_evt(l, LINK_FAILOVER_BEGIN_EVT); +	tipc_node_fsm_evt(n, NODE_FAILOVER_BEGIN_EVT); +} + +/**   * __tipc_node_link_down - handle loss of link   */  static void __tipc_node_link_down(struct tipc_node *n, int *bearer_id, @@ -1340,7 +1397,7 @@ static int __tipc_nl_add_node(struct tipc_nl_msg *msg, struct tipc_node *node)  	if (!hdr)  		return -EMSGSIZE; -	attrs = nla_nest_start(msg->skb, TIPC_NLA_NODE); +	attrs = nla_nest_start_noflag(msg->skb, TIPC_NLA_NODE);  	if (!attrs)  		goto msg_full; @@ -1656,14 +1713,16 @@ static bool tipc_node_check_state(struct tipc_node *n, struct sk_buff *skb,  			tipc_skb_queue_splice_tail_init(tipc_link_inputq(pl),  							tipc_link_inputq(l));  		} +  		/* If parallel link was already down, and this happened before -		 * the tunnel link came up, FAILOVER was never sent. Ensure that -		 * FAILOVER is sent to get peer out of NODE_FAILINGOVER state. +		 * the tunnel link came up, node failover was never started. +		 * Ensure that a FAILOVER_MSG is sent to get peer out of +		 * NODE_FAILINGOVER state, also this node must accept +		 * TUNNEL_MSGs from peer.  		 */ -		if (n->state != NODE_FAILINGOVER && !n->failover_sent) { -			tipc_link_create_dummy_tnl_msg(l, xmitq); -			n->failover_sent = true; -		} +		if (n->state != NODE_FAILINGOVER) +			tipc_node_link_failover(n, pl, l, xmitq); +  		/* If pkts arrive out of order, use lowest calculated syncpt */  		if (less(syncpt, n->sync_point))  			n->sync_point = syncpt; @@ -1866,9 +1925,9 @@ int tipc_nl_peer_rm(struct sk_buff *skb, struct genl_info *info)  	if (!info->attrs[TIPC_NLA_NET])  		return -EINVAL; -	err = nla_parse_nested(attrs, TIPC_NLA_NET_MAX, -			       info->attrs[TIPC_NLA_NET], tipc_nl_net_policy, -			       info->extack); +	err = nla_parse_nested_deprecated(attrs, TIPC_NLA_NET_MAX, +					  info->attrs[TIPC_NLA_NET], +					  tipc_nl_net_policy, info->extack);  	if (err)  		return err; @@ -2024,9 +2083,9 @@ int tipc_nl_node_set_link(struct sk_buff *skb, struct genl_info *info)  	if (!info->attrs[TIPC_NLA_LINK])  		return -EINVAL; -	err = nla_parse_nested(attrs, TIPC_NLA_LINK_MAX, -			       info->attrs[TIPC_NLA_LINK], -			       tipc_nl_link_policy, info->extack); +	err = nla_parse_nested_deprecated(attrs, TIPC_NLA_LINK_MAX, +					  info->attrs[TIPC_NLA_LINK], +					  tipc_nl_link_policy, info->extack);  	if (err)  		return err; @@ -2100,9 +2159,9 @@ int tipc_nl_node_get_link(struct sk_buff *skb, struct genl_info *info)  	if (!info->attrs[TIPC_NLA_LINK])  		return -EINVAL; -	err = nla_parse_nested(attrs, TIPC_NLA_LINK_MAX, -			       info->attrs[TIPC_NLA_LINK], -			       tipc_nl_link_policy, info->extack); +	err = nla_parse_nested_deprecated(attrs, TIPC_NLA_LINK_MAX, +					  info->attrs[TIPC_NLA_LINK], +					  tipc_nl_link_policy, info->extack);  	if (err)  		return err; @@ -2165,9 +2224,9 @@ int tipc_nl_node_reset_link_stats(struct sk_buff *skb, struct genl_info *info)  	if (!info->attrs[TIPC_NLA_LINK])  		return -EINVAL; -	err = nla_parse_nested(attrs, TIPC_NLA_LINK_MAX, -			       info->attrs[TIPC_NLA_LINK], -			       tipc_nl_link_policy, info->extack); +	err = nla_parse_nested_deprecated(attrs, TIPC_NLA_LINK_MAX, +					  info->attrs[TIPC_NLA_LINK], +					  tipc_nl_link_policy, info->extack);  	if (err)  		return err; @@ -2305,9 +2364,10 @@ int tipc_nl_node_set_monitor(struct sk_buff *skb, struct genl_info *info)  	if (!info->attrs[TIPC_NLA_MON])  		return -EINVAL; -	err = nla_parse_nested(attrs, TIPC_NLA_MON_MAX, -			       info->attrs[TIPC_NLA_MON], -			       tipc_nl_monitor_policy, info->extack); +	err = nla_parse_nested_deprecated(attrs, TIPC_NLA_MON_MAX, +					  info->attrs[TIPC_NLA_MON], +					  tipc_nl_monitor_policy, +					  info->extack);  	if (err)  		return err; @@ -2334,7 +2394,7 @@ static int __tipc_nl_add_monitor_prop(struct net *net, struct tipc_nl_msg *msg)  	if (!hdr)  		return -EMSGSIZE; -	attrs = nla_nest_start(msg->skb, TIPC_NLA_MON); +	attrs = nla_nest_start_noflag(msg->skb, TIPC_NLA_MON);  	if (!attrs)  		goto msg_full; @@ -2425,9 +2485,10 @@ int tipc_nl_node_dump_monitor_peer(struct sk_buff *skb,  		if (!attrs[TIPC_NLA_MON])  			return -EINVAL; -		err = nla_parse_nested(mon, TIPC_NLA_MON_MAX, -				       attrs[TIPC_NLA_MON], -				       tipc_nl_monitor_policy, NULL); +		err = nla_parse_nested_deprecated(mon, TIPC_NLA_MON_MAX, +						  attrs[TIPC_NLA_MON], +						  tipc_nl_monitor_policy, +						  NULL);  		if (err)  			return err; diff --git a/net/tipc/node.h b/net/tipc/node.h index 4f59a30e989a..c0bf49ea3de4 100644 --- a/net/tipc/node.h +++ b/net/tipc/node.h @@ -51,7 +51,9 @@ enum {  	TIPC_BLOCK_FLOWCTL    = (1 << 3),  	TIPC_BCAST_RCAST      = (1 << 4),  	TIPC_NODE_ID128       = (1 << 5), -	TIPC_LINK_PROTO_SEQNO = (1 << 6) +	TIPC_LINK_PROTO_SEQNO = (1 << 6), +	TIPC_MCAST_RBCTL      = (1 << 7), +	TIPC_GAP_ACK_BLOCK    = (1 << 8)  };  #define TIPC_NODE_CAPABILITIES (TIPC_SYN_BIT           |  \ @@ -60,7 +62,9 @@ enum {  				TIPC_BCAST_RCAST       |   \  				TIPC_BLOCK_FLOWCTL     |   \  				TIPC_NODE_ID128        |   \ -				TIPC_LINK_PROTO_SEQNO) +				TIPC_LINK_PROTO_SEQNO  |   \ +				TIPC_MCAST_RBCTL       |   \ +				TIPC_GAP_ACK_BLOCK)  #define INVALID_BEARER_ID -1  void tipc_node_stop(struct net *net); diff --git a/net/tipc/socket.c b/net/tipc/socket.c index b542f14ed444..145e4decb0c9 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -485,6 +485,7 @@ static int tipc_sk_create(struct net *net, struct socket *sock,  		tsk_set_unreturnable(tsk, true);  		if (sock->type == SOCK_DGRAM)  			tsk_set_unreliable(tsk, true); +		__skb_queue_head_init(&tsk->mc_method.deferredq);  	}  	trace_tipc_sk_create(sk, NULL, TIPC_DUMP_NONE, " "); @@ -582,6 +583,7 @@ static int tipc_release(struct socket *sock)  	sk->sk_shutdown = SHUTDOWN_MASK;  	tipc_sk_leave(tsk);  	tipc_sk_withdraw(tsk, 0, NULL); +	__skb_queue_purge(&tsk->mc_method.deferredq);  	sk_stop_timer(sk, &sk->sk_timer);  	tipc_sk_remove(tsk); @@ -2149,6 +2151,7 @@ static void tipc_sk_filter_rcv(struct sock *sk, struct sk_buff *skb,  	struct tipc_msg *hdr = buf_msg(skb);  	struct net *net = sock_net(sk);  	struct sk_buff_head inputq; +	int mtyp = msg_type(hdr);  	int limit, err = TIPC_OK;  	trace_tipc_sk_filter_rcv(sk, skb, TIPC_DUMP_ALL, " "); @@ -2162,6 +2165,9 @@ static void tipc_sk_filter_rcv(struct sock *sk, struct sk_buff *skb,  	if (unlikely(grp))  		tipc_group_filter_msg(grp, &inputq, xmitq); +	if (unlikely(!grp) && mtyp == TIPC_MCAST_MSG) +		tipc_mcast_filter_msg(net, &tsk->mc_method.deferredq, &inputq); +  	/* Validate and add to receive buffer if there is space */  	while ((skb = __skb_dequeue(&inputq))) {  		hdr = buf_msg(skb); @@ -3064,6 +3070,9 @@ static int tipc_getsockopt(struct socket *sock, int lvl, int opt,  	case TIPC_SOCK_RECVQ_DEPTH:  		value = skb_queue_len(&sk->sk_receive_queue);  		break; +	case TIPC_SOCK_RECVQ_USED: +		value = sk_rmem_alloc_get(sk); +		break;  	case TIPC_GROUP_JOIN:  		seq.type = 0;  		if (tsk->group) @@ -3264,7 +3273,7 @@ static int __tipc_nl_add_sk_con(struct sk_buff *skb, struct tipc_sock *tsk)  	peer_node = tsk_peer_node(tsk);  	peer_port = tsk_peer_port(tsk); -	nest = nla_nest_start(skb, TIPC_NLA_SOCK_CON); +	nest = nla_nest_start_noflag(skb, TIPC_NLA_SOCK_CON);  	if (!nest)  		return -EMSGSIZE; @@ -3323,7 +3332,7 @@ static int __tipc_nl_add_sk(struct sk_buff *skb, struct netlink_callback *cb,  	if (!hdr)  		goto msg_cancel; -	attrs = nla_nest_start(skb, TIPC_NLA_SOCK); +	attrs = nla_nest_start_noflag(skb, TIPC_NLA_SOCK);  	if (!attrs)  		goto genlmsg_cancel; @@ -3428,7 +3437,7 @@ int tipc_sk_fill_sock_diag(struct sk_buff *skb, struct netlink_callback *cb,  	if (!(sk_filter_state & (1 << sk->sk_state)))  		return 0; -	attrs = nla_nest_start(skb, TIPC_NLA_SOCK); +	attrs = nla_nest_start_noflag(skb, TIPC_NLA_SOCK);  	if (!attrs)  		goto msg_cancel; @@ -3446,7 +3455,7 @@ int tipc_sk_fill_sock_diag(struct sk_buff *skb, struct netlink_callback *cb,  			      TIPC_NLA_SOCK_PAD))  		goto attr_msg_cancel; -	stat = nla_nest_start(skb, TIPC_NLA_SOCK_STAT); +	stat = nla_nest_start_noflag(skb, TIPC_NLA_SOCK_STAT);  	if (!stat)  		goto attr_msg_cancel; @@ -3503,7 +3512,7 @@ static int __tipc_nl_add_sk_publ(struct sk_buff *skb,  	if (!hdr)  		goto msg_cancel; -	attrs = nla_nest_start(skb, TIPC_NLA_PUBL); +	attrs = nla_nest_start_noflag(skb, TIPC_NLA_PUBL);  	if (!attrs)  		goto genlmsg_cancel; @@ -3590,9 +3599,9 @@ int tipc_nl_publ_dump(struct sk_buff *skb, struct netlink_callback *cb)  		if (!attrs[TIPC_NLA_SOCK])  			return -EINVAL; -		err = nla_parse_nested(sock, TIPC_NLA_SOCK_MAX, -				       attrs[TIPC_NLA_SOCK], -				       tipc_nl_sock_policy, NULL); +		err = nla_parse_nested_deprecated(sock, TIPC_NLA_SOCK_MAX, +						  attrs[TIPC_NLA_SOCK], +						  tipc_nl_sock_policy, NULL);  		if (err)  			return err; diff --git a/net/tipc/udp_media.c b/net/tipc/udp_media.c index 4d85d71f16e2..7fc02d84c4f1 100644 --- a/net/tipc/udp_media.c +++ b/net/tipc/udp_media.c @@ -44,7 +44,7 @@  #include <net/sock.h>  #include <net/ip.h>  #include <net/udp_tunnel.h> -#include <net/addrconf.h> +#include <net/ipv6_stubs.h>  #include <linux/tipc_netlink.h>  #include "core.h"  #include "addr.h" @@ -354,25 +354,21 @@ static int tipc_udp_recv(struct sock *sk, struct sk_buff *skb)  	skb_pull(skb, sizeof(struct udphdr));  	hdr = buf_msg(skb); -	rcu_read_lock(); -	b = rcu_dereference_rtnl(ub->bearer); +	b = rcu_dereference(ub->bearer);  	if (!b) -		goto rcu_out; +		goto out;  	if (b && test_bit(0, &b->up)) {  		tipc_rcv(sock_net(sk), skb, b); -		rcu_read_unlock();  		return 0;  	}  	if (unlikely(msg_user(hdr) == LINK_CONFIG)) {  		err = tipc_udp_rcast_disc(b, skb);  		if (err) -			goto rcu_out; +			goto out;  	} -rcu_out: -	rcu_read_unlock();  out:  	kfree_skb(skb);  	return 0; @@ -451,9 +447,9 @@ int tipc_udp_nl_dump_remoteip(struct sk_buff *skb, struct netlink_callback *cb)  		if (!attrs[TIPC_NLA_BEARER])  			return -EINVAL; -		err = nla_parse_nested(battrs, TIPC_NLA_BEARER_MAX, -				       attrs[TIPC_NLA_BEARER], -				       tipc_nl_bearer_policy, NULL); +		err = nla_parse_nested_deprecated(battrs, TIPC_NLA_BEARER_MAX, +						  attrs[TIPC_NLA_BEARER], +						  tipc_nl_bearer_policy, NULL);  		if (err)  			return err; @@ -527,7 +523,7 @@ int tipc_udp_nl_add_bearer_data(struct tipc_nl_msg *msg, struct tipc_bearer *b)  	if (!ub)  		return -ENODEV; -	nest = nla_nest_start(msg->skb, TIPC_NLA_BEARER_UDP_OPTS); +	nest = nla_nest_start_noflag(msg->skb, TIPC_NLA_BEARER_UDP_OPTS);  	if (!nest)  		goto msg_full; @@ -605,8 +601,7 @@ int tipc_udp_nl_bearer_add(struct tipc_bearer *b, struct nlattr *attr)  	struct nlattr *opts[TIPC_NLA_UDP_MAX + 1];  	struct udp_media_addr *dst; -	if (nla_parse_nested(opts, TIPC_NLA_UDP_MAX, attr, -			     tipc_nl_udp_policy, NULL)) +	if (nla_parse_nested_deprecated(opts, TIPC_NLA_UDP_MAX, attr, tipc_nl_udp_policy, NULL))  		return -EINVAL;  	if (!opts[TIPC_NLA_UDP_REMOTE]) @@ -659,9 +654,7 @@ static int tipc_udp_enable(struct net *net, struct tipc_bearer *b,  	if (!attrs[TIPC_NLA_BEARER_UDP_OPTS])  		goto err; -	if (nla_parse_nested(opts, TIPC_NLA_UDP_MAX, -			     attrs[TIPC_NLA_BEARER_UDP_OPTS], -			     tipc_nl_udp_policy, NULL)) +	if (nla_parse_nested_deprecated(opts, TIPC_NLA_UDP_MAX, attrs[TIPC_NLA_BEARER_UDP_OPTS], tipc_nl_udp_policy, NULL))  		goto err;  	if (!opts[TIPC_NLA_UDP_LOCAL] || !opts[TIPC_NLA_UDP_REMOTE]) { |