diff options
Diffstat (limited to 'net/tipc/link.c')
| -rw-r--r-- | net/tipc/link.c | 201 | 
1 files changed, 151 insertions, 50 deletions
| diff --git a/net/tipc/link.c b/net/tipc/link.c index 695acb783969..201c3b5bc96b 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c @@ -106,7 +106,8 @@ struct tipc_stats {   * @backlogq: queue for messages waiting to be sent   * @snt_nxt: next sequence number to use for outbound messages   * @last_retransmitted: sequence number of most recently retransmitted message - * @stale_count: # of identical retransmit requests made by peer + * @stale_cnt: counter for number of identical retransmit attempts + * @stale_limit: time when repeated identical retransmits must force link reset   * @ackers: # of peers that needs to ack each packet before it can be released   * @acked: # last packet acked by a certain peer. Used for broadcast.   * @rcv_nxt: next sequence number to expect for inbound messages @@ -127,14 +128,17 @@ struct tipc_link {  	struct net *net;  	/* Management and link supervision data */ -	u32 peer_session; -	u32 session; +	u16 peer_session; +	u16 session; +	u16 snd_nxt_state; +	u16 rcv_nxt_state;  	u32 peer_bearer_id;  	u32 bearer_id;  	u32 tolerance;  	u32 abort_limit;  	u32 state;  	u16 peer_caps; +	bool in_session;  	bool active;  	u32 silent_intv_cnt;  	char if_name[TIPC_MAX_IF_NAME]; @@ -161,7 +165,8 @@ struct tipc_link {  	u16 snd_nxt;  	u16 last_retransm;  	u16 window; -	u32 stale_count; +	u16 stale_cnt; +	unsigned long stale_limit;  	/* Reception */  	u16 rcv_nxt; @@ -212,11 +217,6 @@ enum {   */  #define TIPC_NACK_INTV (TIPC_MIN_LINK_WIN * 2) -/* Wildcard value for link session numbers. When it is known that - * peer endpoint is down, any session number must be accepted. - */ -#define ANY_SESSION 0x10000 -  /* Link FSM states:   */  enum { @@ -297,11 +297,6 @@ static bool link_is_bc_rcvlink(struct tipc_link *l)  	return ((l->bc_rcvlink == l) && !link_is_bc_sndlink(l));  } -int tipc_link_is_active(struct tipc_link *l) -{ -	return l->active; -} -  void tipc_link_set_active(struct tipc_link *l, bool active)  {  	l->active = active; @@ -337,6 +332,11 @@ char tipc_link_plane(struct tipc_link *l)  	return l->net_plane;  } +void tipc_link_update_caps(struct tipc_link *l, u16 capabilities) +{ +	l->peer_caps = capabilities; +} +  void tipc_link_add_bc_peer(struct tipc_link *snd_l,  			   struct tipc_link *uc_l,  			   struct sk_buff_head *xmitq) @@ -373,7 +373,7 @@ int tipc_link_bc_peers(struct tipc_link *l)  	return l->ackers;  } -u16 link_bc_rcv_gap(struct tipc_link *l) +static u16 link_bc_rcv_gap(struct tipc_link *l)  {  	struct sk_buff *skb = skb_peek(&l->deferdq);  	u16 gap = 0; @@ -410,6 +410,11 @@ char *tipc_link_name(struct tipc_link *l)  	return l->name;  } +u32 tipc_link_state(struct tipc_link *l) +{ +	return l->state; +} +  /**   * tipc_link_create - create a new link   * @n: pointer to associated node @@ -469,9 +474,11 @@ bool tipc_link_create(struct net *net, char *if_name, int bearer_id,  	l->addr = peer;  	l->peer_caps = peer_caps;  	l->net = net; -	l->peer_session = ANY_SESSION; +	l->in_session = false;  	l->bearer_id = bearer_id;  	l->tolerance = tolerance; +	if (bc_rcvlink) +		bc_rcvlink->tolerance = tolerance;  	l->net_plane = net_plane;  	l->advertised_mtu = mtu;  	l->mtu = mtu; @@ -820,7 +827,7 @@ static int link_schedule_user(struct tipc_link *l, struct tipc_msg *hdr)   * Wake up a number of waiting users, as permitted by available space   * in the send queue   */ -void link_prepare_wakeup(struct tipc_link *l) +static void link_prepare_wakeup(struct tipc_link *l)  {  	struct sk_buff *skb, *tmp;  	int imp, i = 0; @@ -838,12 +845,24 @@ void link_prepare_wakeup(struct tipc_link *l)  void tipc_link_reset(struct tipc_link *l)  { -	l->peer_session = ANY_SESSION; +	struct sk_buff_head list; + +	__skb_queue_head_init(&list); + +	l->in_session = false;  	l->session++;  	l->mtu = l->advertised_mtu; + +	spin_lock_bh(&l->wakeupq.lock); +	skb_queue_splice_init(&l->wakeupq, &list); +	spin_unlock_bh(&l->wakeupq.lock); + +	spin_lock_bh(&l->inputq->lock); +	skb_queue_splice_init(&list, l->inputq); +	spin_unlock_bh(&l->inputq->lock); +  	__skb_queue_purge(&l->transmq);  	__skb_queue_purge(&l->deferdq); -	skb_queue_splice_init(&l->wakeupq, l->inputq);  	__skb_queue_purge(&l->backlogq);  	l->backlog[TIPC_LOW_IMPORTANCE].len = 0;  	l->backlog[TIPC_MEDIUM_IMPORTANCE].len = 0; @@ -857,10 +876,12 @@ void tipc_link_reset(struct tipc_link *l)  	l->rcv_unacked = 0;  	l->snd_nxt = 1;  	l->rcv_nxt = 1; +	l->snd_nxt_state = 1; +	l->rcv_nxt_state = 1;  	l->acked = 0;  	l->silent_intv_cnt = 0;  	l->rst_cnt = 0; -	l->stale_count = 0; +	l->stale_cnt = 0;  	l->bc_peer_is_up = false;  	memset(&l->mon_state, 0, sizeof(l->mon_state));  	tipc_link_reset_stats(l); @@ -954,7 +975,8 @@ int tipc_link_xmit(struct tipc_link *l, struct sk_buff_head *list,  	return rc;  } -void tipc_link_advance_backlog(struct tipc_link *l, struct sk_buff_head *xmitq) +static void tipc_link_advance_backlog(struct tipc_link *l, +				      struct sk_buff_head *xmitq)  {  	struct sk_buff *skb, *_skb;  	struct tipc_msg *hdr; @@ -997,39 +1019,42 @@ static void link_retransmit_failure(struct tipc_link *l, struct sk_buff *skb)  		msg_seqno(hdr), msg_prevnode(hdr), msg_orignode(hdr));  } -int tipc_link_retrans(struct tipc_link *l, struct tipc_link *nacker, -		      u16 from, u16 to, struct sk_buff_head *xmitq) +/* tipc_link_retrans() - retransmit one or more packets + * @l: the link to transmit on + * @r: the receiving link ordering the retransmit. Same as l if unicast + * @from: retransmit from (inclusive) this sequence number + * @to: retransmit to (inclusive) this sequence number + * xmitq: queue for accumulating the retransmitted packets + */ +static int tipc_link_retrans(struct tipc_link *l, struct tipc_link *r, +			     u16 from, u16 to, struct sk_buff_head *xmitq)  {  	struct sk_buff *_skb, *skb = skb_peek(&l->transmq); -	struct tipc_msg *hdr; -	u16 ack = l->rcv_nxt - 1;  	u16 bc_ack = l->bc_rcvlink->rcv_nxt - 1; +	u16 ack = l->rcv_nxt - 1; +	struct tipc_msg *hdr;  	if (!skb)  		return 0;  	/* Detect repeated retransmit failures on same packet */ -	if (nacker->last_retransm != buf_seqno(skb)) { -		nacker->last_retransm = buf_seqno(skb); -		nacker->stale_count = 1; -	} else if (++nacker->stale_count > 100) { +	if (r->last_retransm != buf_seqno(skb)) { +		r->last_retransm = buf_seqno(skb); +		r->stale_limit = jiffies + msecs_to_jiffies(r->tolerance); +		r->stale_cnt = 0; +	} else if (++r->stale_cnt > 99 && time_after(jiffies, r->stale_limit)) {  		link_retransmit_failure(l, skb); -		nacker->stale_count = 0;  		if (link_is_bc_sndlink(l))  			return TIPC_LINK_DOWN_EVT;  		return tipc_link_fsm_evt(l, LINK_FAILURE_EVT);  	} -	/* Move forward to where retransmission should start */  	skb_queue_walk(&l->transmq, skb) { -		if (!less(buf_seqno(skb), from)) -			break; -	} - -	skb_queue_walk_from(&l->transmq, skb) { -		if (more(buf_seqno(skb), to)) -			break;  		hdr = buf_msg(skb); +		if (less(msg_seqno(hdr), from)) +			continue; +		if (more(msg_seqno(hdr), to)) +			break;  		_skb = __pskb_copy(skb, MIN_H_SIZE, GFP_ATOMIC);  		if (!_skb)  			return 0; @@ -1063,6 +1088,7 @@ static bool tipc_data_input(struct tipc_link *l, struct sk_buff *skb,  			skb_queue_tail(mc_inputq, skb);  			return true;  		} +		/* else: fall through */  	case CONN_MANAGER:  		skb_queue_tail(inputq, skb);  		return true; @@ -1271,6 +1297,7 @@ int tipc_link_rcv(struct tipc_link *l, struct sk_buff *skb,  		/* Forward queues and wake up waiting users */  		if (likely(tipc_link_release_pkts(l, msg_ack(hdr)))) { +			l->stale_cnt = 0;  			tipc_link_advance_backlog(l, xmitq);  			if (unlikely(!skb_queue_empty(&l->wakeupq)))  				link_prepare_wakeup(l); @@ -1347,6 +1374,8 @@ static void tipc_link_build_proto_msg(struct tipc_link *l, int mtyp, bool probe,  	msg_set_seqno(hdr, l->snd_nxt + U16_MAX / 2);  	if (mtyp == STATE_MSG) { +		if (l->peer_caps & TIPC_LINK_PROTO_SEQNO) +			msg_set_seqno(hdr, l->snd_nxt_state++);  		msg_set_seq_gap(hdr, rcvgap);  		msg_set_bc_gap(hdr, link_bc_rcv_gap(bcl));  		msg_set_probe(hdr, probe); @@ -1371,6 +1400,36 @@ static void tipc_link_build_proto_msg(struct tipc_link *l, int mtyp, bool probe,  	__skb_queue_tail(xmitq, skb);  } +void tipc_link_create_dummy_tnl_msg(struct tipc_link *l, +				    struct sk_buff_head *xmitq) +{ +	u32 onode = tipc_own_addr(l->net); +	struct tipc_msg *hdr, *ihdr; +	struct sk_buff_head tnlq; +	struct sk_buff *skb; +	u32 dnode = l->addr; + +	skb_queue_head_init(&tnlq); +	skb = tipc_msg_create(TUNNEL_PROTOCOL, FAILOVER_MSG, +			      INT_H_SIZE, BASIC_H_SIZE, +			      dnode, onode, 0, 0, 0); +	if (!skb) { +		pr_warn("%sunable to create tunnel packet\n", link_co_err); +		return; +	} + +	hdr = buf_msg(skb); +	msg_set_msgcnt(hdr, 1); +	msg_set_bearer_id(hdr, l->peer_bearer_id); + +	ihdr = (struct tipc_msg *)msg_data(hdr); +	tipc_msg_init(onode, ihdr, TIPC_LOW_IMPORTANCE, TIPC_DIRECT_MSG, +		      BASIC_H_SIZE, dnode); +	msg_set_errcode(ihdr, TIPC_ERR_NO_PORT); +	__skb_queue_tail(&tnlq, skb); +	tipc_link_xmit(l, &tnlq, xmitq); +} +  /* tipc_link_tnl_prepare(): prepare and return a list of tunnel packets   * with contents of the link's transmit and backlog queues.   */ @@ -1438,6 +1497,47 @@ tnl:  	}  } +/* tipc_link_validate_msg(): validate message against current link state + * Returns true if message should be accepted, otherwise false + */ +bool tipc_link_validate_msg(struct tipc_link *l, struct tipc_msg *hdr) +{ +	u16 curr_session = l->peer_session; +	u16 session = msg_session(hdr); +	int mtyp = msg_type(hdr); + +	if (msg_user(hdr) != LINK_PROTOCOL) +		return true; + +	switch (mtyp) { +	case RESET_MSG: +		if (!l->in_session) +			return true; +		/* Accept only RESET with new session number */ +		return more(session, curr_session); +	case ACTIVATE_MSG: +		if (!l->in_session) +			return true; +		/* Accept only ACTIVATE with new or current session number */ +		return !less(session, curr_session); +	case STATE_MSG: +		/* Accept only STATE with current session number */ +		if (!l->in_session) +			return false; +		if (session != curr_session) +			return false; +		/* Extra sanity check */ +		if (!link_is_up(l) && msg_ack(hdr)) +			return false; +		if (!(l->peer_caps & TIPC_LINK_PROTO_SEQNO)) +			return true; +		/* Accept only STATE with new sequence number */ +		return !less(msg_seqno(hdr), l->rcv_nxt_state); +	default: +		return false; +	} +} +  /* tipc_link_proto_rcv(): receive link level protocol message :   * Note that network plane id propagates through the network, and may   * change at any time. The node with lowest numerical id determines @@ -1471,17 +1571,12 @@ static int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb,  	hdr = buf_msg(skb);  	data = msg_data(hdr); +	if (!tipc_link_validate_msg(l, hdr)) +		goto exit; +  	switch (mtyp) {  	case RESET_MSG: - -		/* Ignore duplicate RESET with old session number */ -		if ((less_eq(msg_session(hdr), l->peer_session)) && -		    (l->peer_session != ANY_SESSION)) -			break; -		/* fall thru' */ -  	case ACTIVATE_MSG: -  		/* Complete own link name with peer's interface name */  		if_name =  strrchr(l->name, ':') + 1;  		if (sizeof(l->name) - (if_name - l->name) <= TIPC_MAX_IF_NAME) @@ -1491,9 +1586,10 @@ static int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb,  		strncpy(if_name, data, TIPC_MAX_IF_NAME);  		/* Update own tolerance if peer indicates a non-zero value */ -		if (in_range(peers_tol, TIPC_MIN_LINK_TOL, TIPC_MAX_LINK_TOL)) +		if (in_range(peers_tol, TIPC_MIN_LINK_TOL, TIPC_MAX_LINK_TOL)) {  			l->tolerance = peers_tol; - +			l->bc_rcvlink->tolerance = peers_tol; +		}  		/* Update own priority if peer's priority is higher */  		if (in_range(peers_prio, l->priority + 1, TIPC_MAX_LINK_PRI))  			l->priority = peers_prio; @@ -1509,17 +1605,20 @@ static int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb,  			rc = TIPC_LINK_UP_EVT;  		l->peer_session = msg_session(hdr); +		l->in_session = true;  		l->peer_bearer_id = msg_bearer_id(hdr);  		if (l->mtu > msg_max_pkt(hdr))  			l->mtu = msg_max_pkt(hdr);  		break;  	case STATE_MSG: +		l->rcv_nxt_state = msg_seqno(hdr) + 1;  		/* Update own tolerance if peer indicates a non-zero value */ -		if (in_range(peers_tol, TIPC_MIN_LINK_TOL, TIPC_MAX_LINK_TOL)) +		if (in_range(peers_tol, TIPC_MIN_LINK_TOL, TIPC_MAX_LINK_TOL)) {  			l->tolerance = peers_tol; - +			l->bc_rcvlink->tolerance = peers_tol; +		}  		/* Update own prio if peer indicates a different value */  		if ((peers_prio != l->priority) &&  		    in_range(peers_prio, 1, TIPC_MAX_LINK_PRI)) { @@ -2136,6 +2235,8 @@ void tipc_link_set_tolerance(struct tipc_link *l, u32 tol,  			     struct sk_buff_head *xmitq)  {  	l->tolerance = tol; +	if (l->bc_rcvlink) +		l->bc_rcvlink->tolerance = tol;  	if (link_is_up(l))  		tipc_link_build_proto_msg(l, STATE_MSG, 0, 0, 0, tol, 0, xmitq);  } | 
