aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/sctp/associola.c129
1 files changed, 79 insertions, 50 deletions
diff --git a/net/sctp/associola.c b/net/sctp/associola.c
index f558433537b8..ee13d28d39d1 100644
--- a/net/sctp/associola.c
+++ b/net/sctp/associola.c
@@ -1239,78 +1239,107 @@ void sctp_assoc_update(struct sctp_association *asoc,
1239} 1239}
1240 1240
1241/* Update the retran path for sending a retransmitted packet. 1241/* Update the retran path for sending a retransmitted packet.
1242 * Round-robin through the active transports, else round-robin 1242 * See also RFC4960, 6.4. Multi-Homed SCTP Endpoints:
1243 * through the inactive transports as this is the next best thing 1243 *
1244 * we can try. 1244 * When there is outbound data to send and the primary path
1245 * becomes inactive (e.g., due to failures), or where the
1246 * SCTP user explicitly requests to send data to an
1247 * inactive destination transport address, before reporting
1248 * an error to its ULP, the SCTP endpoint should try to send
1249 * the data to an alternate active destination transport
1250 * address if one exists.
1251 *
1252 * When retransmitting data that timed out, if the endpoint
1253 * is multihomed, it should consider each source-destination
1254 * address pair in its retransmission selection policy.
1255 * When retransmitting timed-out data, the endpoint should
1256 * attempt to pick the most divergent source-destination
1257 * pair from the original source-destination pair to which
1258 * the packet was transmitted.
1259 *
1260 * Note: Rules for picking the most divergent source-destination
1261 * pair are an implementation decision and are not specified
1262 * within this document.
1263 *
1264 * Our basic strategy is to round-robin transports in priorities
1265 * according to sctp_state_prio_map[] e.g., if no such
1266 * transport with state SCTP_ACTIVE exists, round-robin through
1267 * SCTP_UNKNOWN, etc. You get the picture.
1245 */ 1268 */
1246void sctp_assoc_update_retran_path(struct sctp_association *asoc) 1269static const u8 sctp_trans_state_to_prio_map[] = {
1270 [SCTP_ACTIVE] = 3, /* best case */
1271 [SCTP_UNKNOWN] = 2,
1272 [SCTP_PF] = 1,
1273 [SCTP_INACTIVE] = 0, /* worst case */
1274};
1275
1276static u8 sctp_trans_score(const struct sctp_transport *trans)
1247{ 1277{
1248 struct sctp_transport *t, *next; 1278 return sctp_trans_state_to_prio_map[trans->state];
1249 struct list_head *head = &asoc->peer.transport_addr_list; 1279}
1250 struct list_head *pos;
1251 1280
1252 if (asoc->peer.transport_count == 1) 1281static struct sctp_transport *sctp_trans_elect_best(struct sctp_transport *curr,
1253 return; 1282 struct sctp_transport *best)
1283{
1284 if (best == NULL)
1285 return curr;
1254 1286
1255 /* Find the next transport in a round-robin fashion. */ 1287 return sctp_trans_score(curr) > sctp_trans_score(best) ? curr : best;
1256 t = asoc->peer.retran_path; 1288}
1257 pos = &t->transports;
1258 next = NULL;
1259 1289
1260 while (1) { 1290void sctp_assoc_update_retran_path(struct sctp_association *asoc)
1261 /* Skip the head. */ 1291{
1262 if (pos->next == head) 1292 struct sctp_transport *trans = asoc->peer.retran_path;
1263 pos = head->next; 1293 struct sctp_transport *trans_next = NULL;
1264 else
1265 pos = pos->next;
1266 1294
1267 t = list_entry(pos, struct sctp_transport, transports); 1295 /* We're done as we only have the one and only path. */
1296 if (asoc->peer.transport_count == 1)
1297 return;
1298 /* If active_path and retran_path are the same and active,
1299 * then this is the only active path. Use it.
1300 */
1301 if (asoc->peer.active_path == asoc->peer.retran_path &&
1302 asoc->peer.active_path->state == SCTP_ACTIVE)
1303 return;
1268 1304
1269 /* We have exhausted the list, but didn't find any 1305 /* Iterate from retran_path's successor back to retran_path. */
1270 * other active transports. If so, use the next 1306 for (trans = list_next_entry(trans, transports); 1;
1271 * transport. 1307 trans = list_next_entry(trans, transports)) {
1272 */ 1308 /* Manually skip the head element. */
1273 if (t == asoc->peer.retran_path) { 1309 if (&trans->transports == &asoc->peer.transport_addr_list)
1274 t = next; 1310 continue;
1311 if (trans->state == SCTP_UNCONFIRMED)
1312 continue;
1313 trans_next = sctp_trans_elect_best(trans, trans_next);
1314 /* Active is good enough for immediate return. */
1315 if (trans_next->state == SCTP_ACTIVE)
1275 break; 1316 break;
1276 } 1317 /* We've reached the end, time to update path. */
1277 1318 if (trans == asoc->peer.retran_path)
1278 /* Try to find an active transport. */
1279
1280 if ((t->state == SCTP_ACTIVE) ||
1281 (t->state == SCTP_UNKNOWN)) {
1282 break; 1319 break;
1283 } else {
1284 /* Keep track of the next transport in case
1285 * we don't find any active transport.
1286 */
1287 if (t->state != SCTP_UNCONFIRMED && !next)
1288 next = t;
1289 }
1290 } 1320 }
1291 1321
1292 if (t) 1322 if (trans_next != NULL)
1293 asoc->peer.retran_path = t; 1323 asoc->peer.retran_path = trans_next;
1294 else
1295 t = asoc->peer.retran_path;
1296 1324
1297 pr_debug("%s: association:%p addr:%pISpc\n", __func__, asoc, 1325 pr_debug("%s: association:%p updated new path to addr:%pISpc\n",
1298 &t->ipaddr.sa); 1326 __func__, asoc, &asoc->peer.retran_path->ipaddr.sa);
1299} 1327}
1300 1328
1301/* Choose the transport for sending retransmit packet. */ 1329struct sctp_transport *
1302struct sctp_transport *sctp_assoc_choose_alter_transport( 1330sctp_assoc_choose_alter_transport(struct sctp_association *asoc,
1303 struct sctp_association *asoc, struct sctp_transport *last_sent_to) 1331 struct sctp_transport *last_sent_to)
1304{ 1332{
1305 /* If this is the first time packet is sent, use the active path, 1333 /* If this is the first time packet is sent, use the active path,
1306 * else use the retran path. If the last packet was sent over the 1334 * else use the retran path. If the last packet was sent over the
1307 * retran path, update the retran path and use it. 1335 * retran path, update the retran path and use it.
1308 */ 1336 */
1309 if (!last_sent_to) 1337 if (last_sent_to == NULL) {
1310 return asoc->peer.active_path; 1338 return asoc->peer.active_path;
1311 else { 1339 } else {
1312 if (last_sent_to == asoc->peer.retran_path) 1340 if (last_sent_to == asoc->peer.retran_path)
1313 sctp_assoc_update_retran_path(asoc); 1341 sctp_assoc_update_retran_path(asoc);
1342
1314 return asoc->peer.retran_path; 1343 return asoc->peer.retran_path;
1315 } 1344 }
1316} 1345}