l = kzalloc(sizeof(*l), GFP_ATOMIC); if (!l) returnfalse;
*link = l;
l->session = session;
/* Set link name for unicast links only */ if (peer_id) {
tipc_nodeid2string(self_str, tipc_own_id(net)); if (strlen(self_str) > 16)
sprintf(self_str, "%x", self);
tipc_nodeid2string(peer_str, peer_id); if (strlen(peer_str) > 16)
sprintf(peer_str, "%x", peer);
} /* Peer i/f name will be completed by reset/activate message */
snprintf(l->name, sizeof(l->name), "%s:%s-%s:unknown",
self_str, if_name, peer_str);
/* Broadcast send link is always up */ if (link_is_bc_sndlink(l))
l->state = LINK_ESTABLISHED;
/* Disable replicast if even a single peer doesn't support it */ if (link_is_bc_rcvlink(l) && !(peer_caps & TIPC_BCAST_RCAST))
tipc_bcast_toggle_rcast(net, false);
returntrue;
}
/** *tipc_link_fsm_evt-linkfinitestatemachine *@l:pointertolink *@evt:statemachineeventtobeprocessed
*/ int tipc_link_fsm_evt(struct tipc_link *l, int evt)
{ int rc = 0; int old_state = l->state;
switch (l->state) { case LINK_RESETTING: switch (evt) { case LINK_PEER_RESET_EVT:
l->state = LINK_PEER_RESET; break; case LINK_RESET_EVT:
l->state = LINK_RESET; break; case LINK_FAILURE_EVT: case LINK_FAILOVER_BEGIN_EVT: case LINK_ESTABLISH_EVT: case LINK_FAILOVER_END_EVT: case LINK_SYNCH_BEGIN_EVT: case LINK_SYNCH_END_EVT: default: goto illegal_evt;
} break; case LINK_RESET: switch (evt) { case LINK_PEER_RESET_EVT:
l->state = LINK_ESTABLISHING; break; case LINK_FAILOVER_BEGIN_EVT:
l->state = LINK_FAILINGOVER; break; case LINK_FAILURE_EVT: case LINK_RESET_EVT: case LINK_ESTABLISH_EVT: case LINK_FAILOVER_END_EVT: break; case LINK_SYNCH_BEGIN_EVT: case LINK_SYNCH_END_EVT: default: goto illegal_evt;
} break; case LINK_PEER_RESET: switch (evt) { case LINK_RESET_EVT:
l->state = LINK_ESTABLISHING; break; case LINK_PEER_RESET_EVT: case LINK_ESTABLISH_EVT: case LINK_FAILURE_EVT: break; case LINK_SYNCH_BEGIN_EVT: case LINK_SYNCH_END_EVT: case LINK_FAILOVER_BEGIN_EVT: case LINK_FAILOVER_END_EVT: default: goto illegal_evt;
} break; case LINK_FAILINGOVER: switch (evt) { case LINK_FAILOVER_END_EVT:
l->state = LINK_RESET; break; case LINK_PEER_RESET_EVT: case LINK_RESET_EVT: case LINK_ESTABLISH_EVT: case LINK_FAILURE_EVT: break; case LINK_FAILOVER_BEGIN_EVT: case LINK_SYNCH_BEGIN_EVT: case LINK_SYNCH_END_EVT: default: goto illegal_evt;
} break; case LINK_ESTABLISHING: switch (evt) { case LINK_ESTABLISH_EVT:
l->state = LINK_ESTABLISHED; break; case LINK_FAILOVER_BEGIN_EVT:
l->state = LINK_FAILINGOVER; break; case LINK_RESET_EVT:
l->state = LINK_RESET; break; case LINK_FAILURE_EVT: case LINK_PEER_RESET_EVT: case LINK_SYNCH_BEGIN_EVT: case LINK_FAILOVER_END_EVT: break; case LINK_SYNCH_END_EVT: default: goto illegal_evt;
} break; case LINK_ESTABLISHED: switch (evt) { case LINK_PEER_RESET_EVT:
l->state = LINK_PEER_RESET;
rc |= TIPC_LINK_DOWN_EVT; break; case LINK_FAILURE_EVT:
l->state = LINK_RESETTING;
rc |= TIPC_LINK_DOWN_EVT; break; case LINK_RESET_EVT:
l->state = LINK_RESET; break; case LINK_ESTABLISH_EVT: case LINK_SYNCH_END_EVT: break; case LINK_SYNCH_BEGIN_EVT:
l->state = LINK_SYNCHING; break; case LINK_FAILOVER_BEGIN_EVT: case LINK_FAILOVER_END_EVT: default: goto illegal_evt;
} break; case LINK_SYNCHING: switch (evt) { case LINK_PEER_RESET_EVT:
l->state = LINK_PEER_RESET;
rc |= TIPC_LINK_DOWN_EVT; break; case LINK_FAILURE_EVT:
l->state = LINK_RESETTING;
rc |= TIPC_LINK_DOWN_EVT; break; case LINK_RESET_EVT:
l->state = LINK_RESET; break; case LINK_ESTABLISH_EVT: case LINK_SYNCH_BEGIN_EVT: break; case LINK_SYNCH_END_EVT:
l->state = LINK_ESTABLISHED; break; case LINK_FAILOVER_BEGIN_EVT: case LINK_FAILOVER_END_EVT: default: goto illegal_evt;
} break; default:
pr_err("Unknown FSM state %x in %s\n", l->state, l->name);
}
trace_tipc_link_fsm(l->name, old_state, l->state, evt); return rc;
illegal_evt:
pr_err("Illegal FSM event %x in state %x on link %s\n",
evt, l->state, l->name);
trace_tipc_link_fsm(l->name, old_state, l->state, evt); return rc;
}
hdr = buf_msg(skb_peek(list)); if (unlikely(msg_size(hdr) > mtu)) {
pr_warn("Too large msg, purging xmit list %d %d %d %d %d!\n",
skb_queue_len(list), msg_user(hdr),
msg_type(hdr), msg_size(hdr), mtu);
__skb_queue_purge(list); return -EMSGSIZE;
}
imp = msg_importance(hdr); /* Allow oversubscription of one data msg per source at congestion */ if (unlikely(l->backlog[imp].len >= l->backlog[imp].limit)) { if (imp == TIPC_SYSTEM_IMPORTANCE) {
pr_warn("%s<%s>, link overflow", link_rst_msg, l->name);
__skb_queue_purge(list); return -ENOBUFS;
}
rc = link_schedule_user(l, hdr);
}
if (pkt_cnt > 1) {
l->stats.sent_fragmented++;
l->stats.sent_fragments += pkt_cnt;
}
/* Prepare each packet for sending, and add to relevant queue: */ while ((skb = __skb_dequeue(list))) { if (likely(skb_queue_len(transmq) < cwin)) {
hdr = buf_msg(skb);
msg_set_seqno(hdr, seqno);
msg_set_ack(hdr, ack);
msg_set_bcast_ack(hdr, bc_ack);
_skb = skb_clone(skb, GFP_ATOMIC); if (!_skb) {
kfree_skb(skb);
__skb_queue_purge(list); return -ENOBUFS;
}
__skb_queue_tail(transmq, skb);
tipc_link_set_skb_retransmit_time(skb, l);
__skb_queue_tail(xmitq, _skb);
TIPC_SKB_CB(skb)->ackers = l->ackers;
l->rcv_unacked = 0;
l->stats.sent_pkts++;
seqno++; continue;
} if (tipc_msg_try_bundle(l->backlog[imp].target_bskb, &skb,
mss, l->addr, &new_bundle)) { if (skb) { /* Keep a ref. to the skb for next try */
l->backlog[imp].target_bskb = skb;
l->backlog[imp].len++;
__skb_queue_tail(backlogq, skb);
} else { if (new_bundle) {
l->stats.sent_bundles++;
l->stats.sent_bundled++;
}
l->stats.sent_bundled++;
} continue;
}
l->backlog[imp].target_bskb = NULL;
l->backlog[imp].len += (1 + skb_queue_len(list));
__skb_queue_tail(backlogq, skb);
skb_queue_splice_tail_init(list, backlogq);
}
l->snd_nxt = seqno; return rc;
}
staticvoid tipc_link_update_cwin(struct tipc_link *l, int released, bool retransmitted)
{ int bklog_len = skb_queue_len(&l->backlogq); struct sk_buff_head *txq = &l->transmq; int txq_len = skb_queue_len(txq);
u16 cwin = l->window;
/* Enter fast recovery */ if (unlikely(retransmitted)) {
l->ssthresh = max_t(u16, l->window / 2, 300);
l->window = min_t(u16, l->ssthresh, l->window); return;
} /* Enter slow start */ if (unlikely(!released)) {
l->ssthresh = max_t(u16, l->window / 2, 300);
l->window = l->min_win; return;
} /* Don't increase window if no pressure on the transmit queue */ if (txq_len + bklog_len < cwin) return;
/* Don't increase window if there are holes the transmit queue */ if (txq_len && l->snd_nxt - buf_seqno(skb_peek(txq)) != txq_len) return;
/* tipc_data_input - deliver data and name distr msgs to upper layer * *Consumesbufferifmessageisofrighttype *Nodelockmustbeheld
*/ staticbool tipc_data_input(struct tipc_link *l, struct sk_buff *skb, struct sk_buff_head *inputq)
{ struct sk_buff_head *mc_inputq = l->bc_rcvlink->inputq; struct tipc_msg *hdr = buf_msg(skb);
switch (msg_user(hdr)) { case TIPC_LOW_IMPORTANCE: case TIPC_MEDIUM_IMPORTANCE: case TIPC_HIGH_IMPORTANCE: case TIPC_CRITICAL_IMPORTANCE: if (unlikely(msg_in_group(hdr) || msg_mcast(hdr))) {
skb_queue_tail(mc_inputq, skb); returntrue;
}
fallthrough; case CONN_MANAGER:
skb_queue_tail(inputq, skb); returntrue; case GROUP_PROTOCOL:
skb_queue_tail(mc_inputq, skb); returntrue; case NAME_DISTRIBUTOR:
l->bc_rcvlink->state = LINK_ESTABLISHED;
skb_queue_tail(l->namedq, skb); returntrue; case MSG_BUNDLER: case TUNNEL_PROTOCOL: case MSG_FRAGMENTER: case BCAST_PROTOCOL: returnfalse; #ifdef CONFIG_TIPC_CRYPTO case MSG_CRYPTO: if (sysctl_tipc_key_exchange_enabled &&
TIPC_SKB_CB(skb)->decrypted) {
tipc_crypto_msg_rcv(l->net, skb); returntrue;
}
fallthrough; #endif default:
pr_warn("Dropping received illegal msg type\n");
kfree_skb(skb); returntrue;
}
}
/* tipc_link_input - process packet that has passed link protocol check * *Consumesbuffer
*/ staticint tipc_link_input(struct tipc_link *l, struct sk_buff *skb, struct sk_buff_head *inputq, struct sk_buff **reasm_skb)
{ struct tipc_msg *hdr = buf_msg(skb); struct sk_buff *iskb; struct sk_buff_head tmpq; int usr = msg_user(hdr); int pos = 0;
/* tipc_link_tnl_rcv() - receive TUNNEL_PROTOCOL message, drop or process the *innermessagealongwiththeonesintheoldlink's *deferdq *@l:tunnellink *@skb:TUNNEL_PROTOCOLmessage *@inputq:queuetoputmessagesreadyfordelivery
*/ staticint 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 **reasm_tnlmsg = &l->reasm_tnlmsg; 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;
if (msg_type(hdr) == SYNCH_MSG) {
kfree_skb(skb); return0;
}
/* Not a fragment? */ if (likely(!msg_nof_fragms(hdr))) { if (unlikely(!tipc_msg_extract(skb, &iskb, &ipos))) {
pr_warn_ratelimited("Unable to extract msg, defq: %d\n",
skb_queue_len(fdefq)); return0;
}
kfree_skb(skb);
} else { /* Set fragment type for buf_append */ if (msg_fragm_no(hdr) == 1)
msg_set_type(hdr, FIRST_FRAGMENT); elseif (msg_fragm_no(hdr) < msg_nof_fragms(hdr))
msg_set_type(hdr, FRAGMENT); else
msg_set_type(hdr, LAST_FRAGMENT);
if (!tipc_buf_append(reasm_tnlmsg, &skb)) { /* Successful but non-complete reassembly? */ if (*reasm_tnlmsg || link_is_bc_rcvlink(l)) return0;
pr_warn_ratelimited("Unable to reassemble tunnel msg\n"); return tipc_link_fsm_evt(l, LINK_FAILURE_EVT);
}
iskb = skb;
}
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)));
/* Determine Gap ACK blocks if any for the particular link */ if (ga && is_uc) { /* Get the Gap ACKs, uc part */
gack_cnt = ga->ugack_cnt;
gacks = &ga->gacks[ga->bgack_cnt];
} elseif (ga) { /* Copy the Gap ACKs, bc part, for later renewal if needed */
this_ga = kmemdup(ga, struct_size(ga, gacks, ga->bgack_cnt),
GFP_ATOMIC); if (likely(this_ga)) {
this_ga->start_index = 0; /* Start with the bc Gap ACKs */
gack_cnt = this_ga->bgack_cnt;
gacks = &this_ga->gacks[0];
} else { /* Hmm, we can get in trouble..., simply ignore it */
pr_warn_ratelimited("Ignoring bc Gap ACKs, no memory\n");
}
}
/* Advance the link transmq */
skb_queue_walk_safe(&l->transmq, skb, tmp) {
seqno = buf_seqno(skb);
next_gap_ack: if (less_eq(seqno, nacked)) { if (is_uc) goto release; /* Skip packets peer has already acked */ if (!more(seqno, r->acked)) continue; /* Get the next of last Gap ACK blocks */ while (more(seqno, end)) { if (!last_ga || si >= last_ga->bgack_cnt) break;
start = end + offset + 1;
end = ntohs(last_ga->gacks[si].ack);
offset = ntohs(last_ga->gacks[si].gap);
si++;
WARN_ONCE(more(start, end) ||
(!offset &&
si < last_ga->bgack_cnt) ||
si > MAX_GAP_ACK_BLKS, "Corrupted Gap ACK: %d %d %d %d %d\n",
start, end, offset, si,
last_ga->bgack_cnt);
} /* Check against the last Gap ACK block */ if (tipc_in_range(seqno, start, end)) continue; /* Update/release the packet peer is acking */
bc_has_acked = true; if (--TIPC_SKB_CB(skb)->ackers) continue;
release: /* release skb */
__skb_unlink(skb, &l->transmq);
kfree_skb(skb);
} elseif (less_eq(seqno, nacked + ngap)) { /* First gap: check if repeated retrans failures? */ if (unlikely(seqno == acked + 1 &&
link_retransmit_failure(l, r, rc))) { /* Ignore this bc Gap ACKs if any */
kfree(this_ga);
this_ga = NULL; break;
} /* retransmit skb if unrestricted*/ if (time_before(jiffies, TIPC_SKB_CB(skb)->nxt_retr)) continue;
tipc_link_set_skb_retransmit_time(skb, l);
_skb = pskb_copy(skb, 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++; if (!is_uc)
r->stats.retransmitted++;
*retransmitted = true; /* Increase actual retrans counter & mark first time */ if (!TIPC_SKB_CB(skb)->retr_cnt++)
TIPC_SKB_CB(skb)->retr_stamp = jiffies;
} else { /* retry with Gap ACK blocks if any */ if (n >= gack_cnt) break;
nacked = ntohs(gacks[n].ack);
ngap = ntohs(gacks[n].gap);
n++; goto next_gap_ack;
}
}
/* Renew last Gap ACK blocks for bc if needed */ if (bc_has_acked) { if (this_ga) {
kfree(last_ga);
r->last_ga = this_ga;
r->last_gap = gap;
} elseif (last_ga) { if (less(acked, start)) {
si--;
offset = start - acked - 1;
} elseif (less(acked, end)) {
acked = end;
} if (si < last_ga->bgack_cnt) {
last_ga->start_index = si;
r->last_gap = offset;
} else {
kfree(last_ga);
r->last_ga = NULL;
r->last_gap = 0;
}
} else {
r->last_gap = 0;
}
r->acked = acked;
} else {
kfree(this_ga);
}
return qlen - skb_queue_len(&l->transmq);
}
/* tipc_link_build_state_msg: prepare link state message for transmission * *Notethatsendingofbroadcastackiscoordinatedamongnodes,toreduce *riskofackstormstowardsthesender
*/ int tipc_link_build_state_msg(struct tipc_link *l, struct sk_buff_head *xmitq)
{ if (!l) return0;
/* Broadcast ACK must be sent via a unicast link => defer to caller */ if (link_is_bc_rcvlink(l)) { if (((l->rcv_nxt ^ tipc_own_addr(l->net)) & 0xf) != 0xf) return0;
l->rcv_unacked = 0;
/* Use snd_nxt to store peer's snd_nxt in broadcast rcv link */
l->snd_nxt = l->rcv_nxt; return TIPC_LINK_SND_STATE;
} /* Unicast ACK */
l->rcv_unacked = 0;
l->stats.sent_acks++;
tipc_link_build_proto_msg(l, STATE_MSG, 0, 0, 0, 0, 0, xmitq); return0;
}
/* tipc_link_build_reset_msg: prepare link RESET or ACTIVATE message
*/ void tipc_link_build_reset_msg(struct tipc_link *l, struct sk_buff_head *xmitq)
{ int mtyp = RESET_MSG; struct sk_buff *skb;
if (l->state == LINK_ESTABLISHING)
mtyp = ACTIVATE_MSG;
/* Inform peer that this endpoint is going down if applicable */
skb = skb_peek_tail(xmitq); if (skb && (l->state == LINK_RESET))
msg_set_peer_stopping(buf_msg(skb), 1);
}
if (unlikely(msg_user(hdr) == TUNNEL_PROTOCOL))
rc |= tipc_link_tnl_rcv(l, skb, l->inputq); elseif (!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 = __tipc_skb_dequeue(defq, l->rcv_nxt)));
/* Forward queues and wake up waiting users */ if (released) {
tipc_link_update_cwin(l, released, 0);
tipc_link_advance_backlog(l, xmitq); if (unlikely(!skb_queue_empty(&l->wakeupq)))
link_prepare_wakeup(l);
} return rc;
}
/* This failover link endpoint was never established before, *soithasnotreceivedanythingfrompeer. *Otherwise,itmustbeanormalfailoversituationorthe *nodehasenteredSELF_DOWN_PEER_LEAVINGandbothpeernodes *wouldhavetostartoverfromscratchinstead.
*/
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);
}
}
/* tipc_link_validate_msg(): validate message against current link state *Returnstrueifmessageshouldbeaccepted,otherwisefalse
*/ 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) returntrue;
switch (mtyp) { case RESET_MSG: if (!l->in_session) returntrue; /* Accept only RESET with new session number */ return more(session, curr_session); case ACTIVATE_MSG: if (!l->in_session) returntrue; /* 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) returnfalse; if (session != curr_session) returnfalse; /* Extra sanity check */ if (!tipc_link_is_up(l) && msg_ack(hdr)) returnfalse; if (!(l->peer_caps & TIPC_LINK_PROTO_SEQNO)) returntrue; /* Accept only STATE with new sequence number */ return !less(msg_seqno(hdr), l->rcv_nxt_state); default: returnfalse;
}
}
switch (mtyp) { case RESET_MSG: case ACTIVATE_MSG:
msg_max = msg_max_pkt(hdr); if (msg_max < tipc_bearer_min_mtu(l->net, l->bearer_id)) break; /* 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) break; if (msg_data_sz(hdr) < TIPC_MAX_IF_NAME) break;
strscpy(if_name, data, TIPC_MAX_IF_NAME);
/* Update own tolerance if peer indicates a non-zero value */ if (tipc_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 (tipc_in_range(peers_prio, l->priority + 1, TIPC_MAX_LINK_PRI))
l->priority = peers_prio;
/* If peer is going down we want full re-establish cycle */ if (msg_peer_stopping(hdr)) {
rc = tipc_link_fsm_evt(l, LINK_FAILURE_EVT); break;
}
/* If this endpoint was re-created while peer was ESTABLISHING *itdoesn'tknowcurrentsessionnumber.Forcere-synch.
*/ if (mtyp == ACTIVATE_MSG && msg_dest_session_valid(hdr) &&
l->session != msg_dest_session(hdr)) { if (less(l->session, msg_dest_session(hdr)))
l->session = msg_dest_session(hdr) + 1; break;
}
/* ACTIVATE_MSG serves as PEER_RESET if link is already down */ if (mtyp == RESET_MSG || !tipc_link_is_up(l))
rc = tipc_link_fsm_evt(l, LINK_PEER_RESET_EVT);
/* ACTIVATE_MSG takes up link if it was already locally reset */ if (mtyp == ACTIVATE_MSG && l->state == LINK_ESTABLISHING)
rc = TIPC_LINK_UP_EVT;
case STATE_MSG: /* Validate Gap ACK blocks, drop if invalid */
glen = tipc_get_gap_ack_blks(&ga, l, hdr, true); if (glen > dlen) break;
l->rcv_nxt_state = msg_seqno(hdr) + 1;
/* Update own tolerance if peer indicates a non-zero value */ if (tipc_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) &&
tipc_in_range(peers_prio, 1, TIPC_MAX_LINK_PRI)) {
l->priority = peers_prio;
rc = tipc_link_fsm_evt(l, LINK_FAILURE_EVT);
}
l->silent_intv_cnt = 0;
l->stats.recv_states++; if (msg_probe(hdr))
l->stats.recv_probes++;
if (!tipc_link_is_up(l)) { if (l->state == LINK_ESTABLISHING)
rc = TIPC_LINK_UP_EVT; break;
}
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 */ if ((reply || msg_is_keepalive(hdr)) &&
more(peers_snd_nxt, rcv_nxt) &&
!tipc_link_is_synching(l) &&
skb_queue_empty(&l->deferdq))
rcvgap = peers_snd_nxt - l->rcv_nxt; if (rcvgap || reply)
tipc_link_build_proto_msg(l, STATE_MSG, 0, reply,
rcvgap, 0, 0, xmitq);
released = tipc_link_advance_transmq(l, l, ack, gap, ga, xmitq,
&retransmitted, &rc); if (gap)
l->stats.recv_nacks++; if (released || retransmitted)
tipc_link_update_cwin(l, released, retransmitted); if (released)
tipc_link_advance_backlog(l, xmitq); if (unlikely(!skb_queue_empty(&l->wakeupq)))
link_prepare_wakeup(l);
} exit:
kfree_skb(skb); return rc;
}
/* Compatibility: accept older, less safe initial synch data */ if ((mtyp == RESET_MSG) || (mtyp == ACTIVATE_MSG))
l->rcv_nxt = peers_snd_nxt;
}
/* tipc_link_bc_sync_rcv - update rcv link according to peer's send state
*/ int tipc_link_bc_sync_rcv(struct tipc_link *l, struct tipc_msg *hdr, struct sk_buff_head *xmitq)
{
u16 peers_snd_nxt = msg_bc_snd_nxt(hdr); int rc = 0;
if (!tipc_link_is_up(l)) return rc;
if (!msg_peer_node_is_up(hdr)) return rc;
/* Open when peer acknowledges our bcast init msg (pkt #1) */ if (msg_ack(hdr))
l->bc_peer_is_up = true;
if (!l->bc_peer_is_up) return rc;
/* Ignore if peers_snd_nxt goes beyond receive window */ if (more(peers_snd_nxt, l->rcv_nxt + l->window)) return rc;
l->snd_nxt = peers_snd_nxt; if (link_bc_rcv_gap(l))
rc |= TIPC_LINK_SND_STATE;
/* Return now if sender supports nack via STATE messages */ if (l->peer_caps & TIPC_BCAST_STATE_NACK) return rc;
/* Otherwise, be backwards compatible */
if (!more(peers_snd_nxt, l->rcv_nxt)) {
l->nack_state = BC_NACK_SND_CONDITIONAL; return0;
}
/* Don't NACK if one was recently sent or peeked */ if (l->nack_state == BC_NACK_SND_SUPPRESS) {
l->nack_state = BC_NACK_SND_UNCONDITIONAL; return0;
}
/* Conditionally delay NACK sending until next synch rcv */ if (l->nack_state == BC_NACK_SND_CONDITIONAL) {
l->nack_state = BC_NACK_SND_UNCONDITIONAL; if ((peers_snd_nxt - l->rcv_nxt) < TIPC_MIN_LINK_WIN) return0;
}
/* Send NACK now but suppress next one */
tipc_link_build_bc_proto_msg(l, true, peers_snd_nxt, xmitq);
l->nack_state = BC_NACK_SND_SUPPRESS; return0;
}
tipc_link_advance_backlog(l, xmitq); if (unlikely(!skb_queue_empty(&l->wakeupq)))
link_prepare_wakeup(l);
return rc;
}
/* tipc_link_bc_nack_rcv(): receive broadcast nack message *Thisfunctionishereforbackwardscompatibility,since *noBCAST_PROTOCOL/STATEmessagesoccurfromTIPCv2.5.
*/ int tipc_link_bc_nack_rcv(struct tipc_link *l, struct sk_buff *skb, struct sk_buff_head *xmitq)
{ struct tipc_msg *hdr = buf_msg(skb);
u32 dnode = msg_destnode(hdr); int mtyp = msg_type(hdr);
u16 acked = msg_bcast_ack(hdr);
u16 from = acked + 1;
u16 to = msg_bcgap_to(hdr);
u16 peers_snd_nxt = to + 1; int rc = 0;
kfree_skb(skb);
if (!tipc_link_is_up(l) || !l->bc_peer_is_up) return0;
if (mtyp != STATE_MSG) return0;
if (dnode == tipc_own_addr(l->net)) {
rc = tipc_link_bc_ack_rcv(l, acked, to - acked, NULL, xmitq,
xmitq);
l->stats.recv_nacks++; return rc;
}
/* Msg for other node => suppress own NACK at next sync if applicable */ if (more(peers_snd_nxt, l->rcv_nxt) && !less(l->rcv_nxt, from))
l->nack_state = BC_NACK_SND_SUPPRESS;
/* Parse and validate nested (link) properties valid for media, bearer and link
*/ int tipc_nl_parse_link_prop(struct nlattr *prop, struct nlattr *props[])
{ int err;
stats = nla_nest_start_noflag(skb, TIPC_NLA_LINK_STATS); if (!stats) return -EMSGSIZE;
for (i = 0; i < ARRAY_SIZE(map); i++) if (nla_put_u32(skb, map[i].key, map[i].val)) goto msg_full;
nla_nest_end(skb, stats);
return0;
msg_full:
nla_nest_cancel(skb, stats);
return -EMSGSIZE;
}
/* Caller should hold appropriate locks to protect the link */ int __tipc_nl_add_link(struct net *net, struct tipc_nl_msg *msg, struct tipc_link *link, int nlflags)
{
u32 self = tipc_own_addr(net); struct nlattr *attrs; struct nlattr *prop; void *hdr; int err;
attrs = nla_nest_start_noflag(msg->skb, TIPC_NLA_LINK); if (!attrs) goto msg_full;
if (nla_put_string(msg->skb, TIPC_NLA_LINK_NAME, link->name)) goto attr_msg_full; if (nla_put_u32(msg->skb, TIPC_NLA_LINK_DEST, tipc_cluster_mask(self))) goto attr_msg_full; if (nla_put_u32(msg->skb, TIPC_NLA_LINK_MTU, link->mtu)) goto attr_msg_full; if (nla_put_u32(msg->skb, TIPC_NLA_LINK_RX, link->stats.recv_pkts)) goto attr_msg_full; if (nla_put_u32(msg->skb, TIPC_NLA_LINK_TX, link->stats.sent_pkts)) goto attr_msg_full;
if (tipc_link_is_up(link)) if (nla_put_flag(msg->skb, TIPC_NLA_LINK_UP)) goto attr_msg_full; if (link->active) if (nla_put_flag(msg->skb, TIPC_NLA_LINK_ACTIVE)) goto attr_msg_full;
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)) goto prop_msg_full; if (nla_put_u32(msg->skb, TIPC_NLA_PROP_TOL, link->tolerance)) goto prop_msg_full; if (nla_put_u32(msg->skb, TIPC_NLA_PROP_WIN,
link->window)) goto prop_msg_full; if (nla_put_u32(msg->skb, TIPC_NLA_PROP_PRIO, link->priority)) goto prop_msg_full;
nla_nest_end(msg->skb, prop);
err = __tipc_nl_add_stats(msg->skb, &link->stats); if (err) goto attr_msg_full;
if (!l) {
i += scnprintf(buf, sz, "link data: (null)\n"); return i;
}
i += scnprintf(buf, sz, "link data: %x", l->addr);
i += scnprintf(buf + i, sz - i, " %x", l->state);
i += scnprintf(buf + i, sz - i, " %u", l->in_session);
i += scnprintf(buf + i, sz - i, " %u", l->session);
i += scnprintf(buf + i, sz - i, " %u", l->peer_session);
i += scnprintf(buf + i, sz - i, " %u", l->snd_nxt);
i += scnprintf(buf + i, sz - i, " %u", l->rcv_nxt);
i += scnprintf(buf + i, sz - i, " %u", l->snd_nxt_state);
i += scnprintf(buf + i, sz - i, " %u", l->rcv_nxt_state);
i += scnprintf(buf + i, sz - i, " %x", l->peer_caps);
i += scnprintf(buf + i, sz - i, " %u", l->silent_intv_cnt);
i += scnprintf(buf + i, sz - i, " %u", l->rst_cnt);
i += scnprintf(buf + i, sz - i, " %u", 0);
i += scnprintf(buf + i, sz - i, " %u", 0);
i += scnprintf(buf + i, sz - i, " %u", l->acked);
list = &l->transmq;
len = skb_queue_len(list);
hskb = skb_peek(list);
tskb = skb_peek_tail(list);
i += scnprintf(buf + i, sz - i, " | %u %u %u", len,
(hskb) ? msg_seqno(buf_msg(hskb)) : 0,
(tskb) ? msg_seqno(buf_msg(tskb)) : 0);
list = &l->deferdq;
len = skb_queue_len(list);
hskb = skb_peek(list);
tskb = skb_peek_tail(list);
i += scnprintf(buf + i, sz - i, " | %u %u %u", len,
(hskb) ? msg_seqno(buf_msg(hskb)) : 0,
(tskb) ? msg_seqno(buf_msg(tskb)) : 0);
list = &l->backlogq;
len = skb_queue_len(list);
hskb = skb_peek(list);
tskb = skb_peek_tail(list);
i += scnprintf(buf + i, sz - i, " | %u %u %u", len,
(hskb) ? msg_seqno(buf_msg(hskb)) : 0,
(tskb) ? msg_seqno(buf_msg(tskb)) : 0);
list = l->inputq;
len = skb_queue_len(list);
hskb = skb_peek(list);
tskb = skb_peek_tail(list);
i += scnprintf(buf + i, sz - i, " | %u %u %u\n", len,
(hskb) ? msg_seqno(buf_msg(hskb)) : 0,
(tskb) ? msg_seqno(buf_msg(tskb)) : 0);
if (dqueues & TIPC_DUMP_TRANSMQ) {
i += scnprintf(buf + i, sz - i, "transmq: ");
i += tipc_list_dump(&l->transmq, false, buf + i);
} if (dqueues & TIPC_DUMP_BACKLOGQ) {
i += scnprintf(buf + i, sz - i, "backlogq: <%u %u %u %u %u>, ",
l->backlog[TIPC_LOW_IMPORTANCE].len,
l->backlog[TIPC_MEDIUM_IMPORTANCE].len,
l->backlog[TIPC_HIGH_IMPORTANCE].len,
l->backlog[TIPC_CRITICAL_IMPORTANCE].len,
l->backlog[TIPC_SYSTEM_IMPORTANCE].len);
i += tipc_list_dump(&l->backlogq, false, buf + i);
} if (dqueues & TIPC_DUMP_DEFERDQ) {
i += scnprintf(buf + i, sz - i, "deferdq: ");
i += tipc_list_dump(&l->deferdq, false, buf + i);
} if (dqueues & TIPC_DUMP_INPUTQ) {
i += scnprintf(buf + i, sz - i, "inputq: ");
i += tipc_list_dump(l->inputq, false, buf + i);
} if (dqueues & TIPC_DUMP_WAKEUP) {
i += scnprintf(buf + i, sz - i, "wakeup: ");
i += tipc_list_dump(&l->wakeupq, false, buf + i);
}
return i;
}
Messung V0.5 in Prozent
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.94Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-09-27)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.