+static bool sctp_outq_select_transport(struct sctp_chunk *chunk,
+ struct sctp_association *asoc,
+ struct sctp_transport **transport,
+ struct list_head *transport_list)
+{
+ struct sctp_transport *new_transport = chunk->transport;
+ struct sctp_transport *curr = *transport;
+ bool changed = false;
+
+ if (!new_transport) {
+ if (!sctp_chunk_is_data(chunk)) {
+ /*
+ * If we have a prior transport pointer, see if
+ * the destination address of the chunk
+ * matches the destination address of the
+ * current transport. If not a match, then
+ * try to look up the transport with a given
+ * destination address. We do this because
+ * after processing ASCONFs, we may have new
+ * transports created.
+ */
+ if (curr && sctp_cmp_addr_exact(&chunk->dest,
+ &curr->ipaddr))
+ new_transport = curr;
+ else
+ new_transport = sctp_assoc_lookup_paddr(asoc,
+ &chunk->dest);
+ }
+
+ /* if we still don't have a new transport, then
+ * use the current active path.
+ */
+ if (!new_transport)
+ new_transport = asoc->peer.active_path;
+ } else {
+ __u8 type;
+
+ switch (new_transport->state) {
+ case SCTP_INACTIVE:
+ case SCTP_UNCONFIRMED:
+ case SCTP_PF:
+ /* If the chunk is Heartbeat or Heartbeat Ack,
+ * send it to chunk->transport, even if it's
+ * inactive.
+ *
+ * 3.3.6 Heartbeat Acknowledgement:
+ * ...
+ * A HEARTBEAT ACK is always sent to the source IP
+ * address of the IP datagram containing the
+ * HEARTBEAT chunk to which this ack is responding.
+ * ...
+ *
+ * ASCONF_ACKs also must be sent to the source.
+ */
+ type = chunk->chunk_hdr->type;
+ if (type != SCTP_CID_HEARTBEAT &&
+ type != SCTP_CID_HEARTBEAT_ACK &&
+ type != SCTP_CID_ASCONF_ACK)
+ new_transport = asoc->peer.active_path;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* Are we switching transports? Take care of transport locks. */
+ if (new_transport != curr) {
+ changed = true;
+ curr = new_transport;
+ *transport = curr;
+ if (list_empty(&curr->send_ready))
+ list_add_tail(&curr->send_ready, transport_list);
+
+ sctp_packet_config(&curr->packet, asoc->peer.i.init_tag,
+ asoc->peer.ecn_capable);
+ /* We've switched transports, so apply the
+ * Burst limit to the new transport.
+ */
+ sctp_transport_burst_limited(curr);
+ }
+
+ return changed;
+}
+