diff options
-rw-r--r-- | sys/netinet/sctp_timer.c | 85 |
1 files changed, 49 insertions, 36 deletions
diff --git a/sys/netinet/sctp_timer.c b/sys/netinet/sctp_timer.c index bce1f5cd166d..582abd8e8854 100644 --- a/sys/netinet/sctp_timer.c +++ b/sys/netinet/sctp_timer.c @@ -164,8 +164,8 @@ sctp_threshold_management(struct sctp_inpcb *inp, struct sctp_tcb *stcb, } /* - * sctp_find_alternate_net() returns a non-NULL pointer as long - * the argument net is non-NULL. + * sctp_find_alternate_net() returns a non-NULL pointer as long as there + * exists nets, which are not being deleted. */ struct sctp_nets * sctp_find_alternate_net(struct sctp_tcb *stcb, @@ -174,14 +174,14 @@ sctp_find_alternate_net(struct sctp_tcb *stcb, { /* Find and return an alternate network if possible */ struct sctp_nets *alt, *mnet, *min_errors_net = NULL, *max_cwnd_net = NULL; - int once; + bool looped; /* JRS 5/14/07 - Initialize min_errors to an impossible value. */ int min_errors = -1; uint32_t max_cwnd = 0; if (stcb->asoc.numnets == 1) { - /* No others but net */ + /* No selection can be made. */ return (TAILQ_FIRST(&stcb->asoc.nets)); } /* @@ -328,25 +328,22 @@ sctp_find_alternate_net(struct sctp_tcb *stcb, return (max_cwnd_net); } } - mnet = net; - once = 0; - - if (mnet == NULL) { - mnet = TAILQ_FIRST(&stcb->asoc.nets); - if (mnet == NULL) { - return (NULL); - } + /* Look for an alternate net, which is active. */ + if ((net != NULL) && ((net->dest_state & SCTP_ADDR_BEING_DELETED) == 0)) { + alt = TAILQ_NEXT(net, sctp_next);; + } else { + alt = TAILQ_FIRST(&stcb->asoc.nets); } + looped = false; for (;;) { - alt = TAILQ_NEXT(mnet, sctp_next); if (alt == NULL) { - once++; - if (once > 1) { - break; + if (!looped) { + alt = TAILQ_FIRST(&stcb->asoc.nets); + looped = true; } - alt = TAILQ_FIRST(&stcb->asoc.nets); + /* Definitely out of candidates. */ if (alt == NULL) { - return (NULL); + break; } } if (alt->ro.ro_nh == NULL) { @@ -358,43 +355,59 @@ sctp_find_alternate_net(struct sctp_tcb *stcb, } if (((alt->dest_state & SCTP_ADDR_REACHABLE) == SCTP_ADDR_REACHABLE) && (alt->ro.ro_nh != NULL) && - (!(alt->dest_state & SCTP_ADDR_UNCONFIRMED))) { - /* Found a reachable address */ + (!(alt->dest_state & SCTP_ADDR_UNCONFIRMED)) && + (alt != net)) { + /* Found an alternate net, which is reachable. */ break; } - mnet = alt; + alt = TAILQ_NEXT(alt, sctp_next); } if (alt == NULL) { - /* Case where NO insv network exists (dormant state) */ - /* we rotate destinations */ - once = 0; - mnet = net; + /* + * In case no active alternate net has been found, look for + * an alternate net, which is confirmed. + */ + if ((net != NULL) && ((net->dest_state & SCTP_ADDR_BEING_DELETED) == 0)) { + alt = TAILQ_NEXT(net, sctp_next);; + } else { + alt = TAILQ_FIRST(&stcb->asoc.nets); + } + looped = false; for (;;) { - if (mnet == NULL) { - return (TAILQ_FIRST(&stcb->asoc.nets)); - } - alt = TAILQ_NEXT(mnet, sctp_next); if (alt == NULL) { - once++; - if (once > 1) { - break; + if (!looped) { + alt = TAILQ_FIRST(&stcb->asoc.nets); + looped = true; } - alt = TAILQ_FIRST(&stcb->asoc.nets); + /* Definitely out of candidates. */ if (alt == NULL) { break; } } if ((!(alt->dest_state & SCTP_ADDR_UNCONFIRMED)) && (alt != net)) { - /* Found an alternate address */ + /* + * Found an alternate net, which is + * confirmed. + */ break; } - mnet = alt; + alt = TAILQ_NEXT(alt, sctp_next); } } if (alt == NULL) { - return (net); + /* + * In case no confirmed alternate net has been found, just + * return net, if it is not being deleted. In the other case + * just return the first net. + */ + if ((net != NULL) && ((net->dest_state & SCTP_ADDR_BEING_DELETED) == 0)) { + alt = net; + } + if (alt == NULL) { + alt = TAILQ_FIRST(&stcb->asoc.nets); + } } return (alt); } |