aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Tuexen <tuexen@FreeBSD.org>2021-02-21 16:13:06 +0000
committerMichael Tuexen <tuexen@FreeBSD.org>2021-02-21 16:13:06 +0000
commitb963ce4588b33f733aef3f7a7f3fbe5018a89728 (patch)
tree83ce0ed31142d9ee754bd343775f95990f380231
parent5ac839029d01c0f48e1b1ff1a599cb47cf5e98ee (diff)
downloadsrc-b963ce4588b33f733aef3f7a7f3fbe5018a89728.tar.gz
src-b963ce4588b33f733aef3f7a7f3fbe5018a89728.zip
sctp: improve computation of an alternate net
Espeially handle the case where the net passed in is about to be deleted and therefore not in the list of nets anymore. MFC after: 3 days Reported by: syzbot+9756917a7c8381adf5e8@syzkaller.appspotmail.com
-rw-r--r--sys/netinet/sctp_timer.c85
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);
}