const union sctp_addr *peer,
struct sctp_transport **pt);
+static void sctp_add_backlog(struct sock *sk, struct sk_buff *skb);
+
/* Calculate the SCTP checksum of an SCTP packet. */
static inline int sctp_rcv_checksum(struct sk_buff *skb)
union sctp_addr dest;
int family;
struct sctp_af *af;
- int ret = 0;
if (skb->pkt_type!=PACKET_HOST)
goto discard_it;
*/
if (sk->sk_bound_dev_if && (sk->sk_bound_dev_if != af->skb_iif(skb)))
{
- sock_put(sk);
if (asoc) {
sctp_association_put(asoc);
asoc = NULL;
sk = sctp_get_ctl_sock();
ep = sctp_sk(sk)->ep;
sctp_endpoint_hold(ep);
- sock_hold(sk);
rcvr = &ep->base;
}
if (!xfrm_policy_check(sk, XFRM_POLICY_IN, skb, family))
goto discard_release;
+ nf_reset(skb);
- ret = sk_filter(sk, skb, 1);
- if (ret)
+ if (sk_filter(sk, skb, 1))
goto discard_release;
/* Create an SCTP packet structure. */
chunk = sctp_chunkify(skb, asoc, sk);
- if (!chunk) {
- ret = -ENOMEM;
+ if (!chunk)
goto discard_release;
- }
SCTP_INPUT_CB(skb)->chunk = chunk;
/* Remember what endpoint is to handle this packet. */
sctp_bh_lock_sock(sk);
if (sock_owned_by_user(sk))
- sk_add_backlog(sk, skb);
+ sctp_add_backlog(sk, skb);
else
- sctp_backlog_rcv(sk, skb);
+ sctp_inq_push(&chunk->rcvr->inqueue, chunk);
- /* Release the sock and any reference counts we took in the
- * lookup calls.
- */
sctp_bh_unlock_sock(sk);
+
+ /* Release the asoc/ep ref we took in the lookup calls. */
if (asoc)
sctp_association_put(asoc);
else
sctp_endpoint_put(ep);
- sock_put(sk);
- return ret;
+
+ return 0;
discard_it:
kfree_skb(skb);
- return ret;
+ return 0;
discard_release:
- /* Release any structures we may be holding. */
- sock_put(sk);
+ /* Release the asoc/ep ref we took in the lookup calls. */
if (asoc)
sctp_association_put(asoc);
else
goto discard_it;
}
-/* Handle second half of inbound skb processing. If the sock was busy,
- * we may have need to delay processing until later when the sock is
- * released (on the backlog). If not busy, we call this routine
- * directly from the bottom half.
+/* Process the backlog queue of the socket. Every skb on
+ * the backlog holds a ref on an association or endpoint.
+ * We hold this ref throughout the state machine to make
+ * sure that the structure we need is still around.
*/
int sctp_backlog_rcv(struct sock *sk, struct sk_buff *skb)
{
struct sctp_chunk *chunk = SCTP_INPUT_CB(skb)->chunk;
- struct sctp_inq *inqueue = &chunk->rcvr->inqueue;
+ struct sctp_inq *inqueue = &chunk->rcvr->inqueue;
+ struct sctp_ep_common *rcvr = NULL;
+ int backloged = 0;
+
+ rcvr = chunk->rcvr;
+
+ /* If the rcvr is dead then the association or endpoint
+ * has been deleted and we can safely drop the chunk
+ * and refs that we are holding.
+ */
+ if (rcvr->dead) {
+ sctp_chunk_free(chunk);
+ goto done;
+ }
+
+ if (unlikely(rcvr->sk != sk)) {
+ /* In this case, the association moved from one socket to
+ * another. We are currently sitting on the backlog of the
+ * old socket, so we need to move.
+ * However, since we are here in the process context we
+ * need to take make sure that the user doesn't own
+ * the new socket when we process the packet.
+ * If the new socket is user-owned, queue the chunk to the
+ * backlog of the new socket without dropping any refs.
+ * Otherwise, we can safely push the chunk on the inqueue.
+ */
+
+ sk = rcvr->sk;
+ sctp_bh_lock_sock(sk);
+
+ if (sock_owned_by_user(sk)) {
+ sk_add_backlog(sk, skb);
+ backloged = 1;
+ } else
+ sctp_inq_push(inqueue, chunk);
+
+ sctp_bh_unlock_sock(sk);
+
+ /* If the chunk was backloged again, don't drop refs */
+ if (backloged)
+ return 0;
+ } else {
+ sctp_inq_push(inqueue, chunk);
+ }
+
+done:
+ /* Release the refs we took in sctp_add_backlog */
+ if (SCTP_EP_TYPE_ASSOCIATION == rcvr->type)
+ sctp_association_put(sctp_assoc(rcvr));
+ else if (SCTP_EP_TYPE_SOCKET == rcvr->type)
+ sctp_endpoint_put(sctp_ep(rcvr));
+ else
+ BUG();
- sctp_inq_push(inqueue, chunk);
return 0;
}
+static void sctp_add_backlog(struct sock *sk, struct sk_buff *skb)
+{
+ struct sctp_chunk *chunk = SCTP_INPUT_CB(skb)->chunk;
+ struct sctp_ep_common *rcvr = chunk->rcvr;
+
+ /* Hold the assoc/ep while hanging on the backlog queue.
+ * This way, we know structures we need will not disappear from us
+ */
+ if (SCTP_EP_TYPE_ASSOCIATION == rcvr->type)
+ sctp_association_hold(sctp_assoc(rcvr));
+ else if (SCTP_EP_TYPE_SOCKET == rcvr->type)
+ sctp_endpoint_hold(sctp_ep(rcvr));
+ else
+ BUG();
+
+ sk_add_backlog(sk, skb);
+}
+
/* Handle icmp frag needed error. */
void sctp_icmp_frag_needed(struct sock *sk, struct sctp_association *asoc,
struct sctp_transport *t, __u32 pmtu)
union sctp_addr daddr;
struct sctp_af *af;
struct sock *sk = NULL;
- struct sctp_association *asoc = NULL;
+ struct sctp_association *asoc;
struct sctp_transport *transport = NULL;
*app = NULL; *tpp = NULL;
return sk;
out:
- sock_put(sk);
if (asoc)
sctp_association_put(asoc);
return NULL;
void sctp_err_finish(struct sock *sk, struct sctp_association *asoc)
{
sctp_bh_unlock_sock(sk);
- sock_put(sk);
if (asoc)
sctp_association_put(asoc);
}
int type = skb->h.icmph->type;
int code = skb->h.icmph->code;
struct sock *sk;
- struct sctp_association *asoc;
+ struct sctp_association *asoc = NULL;
struct sctp_transport *transport;
struct inet_sock *inet;
char *saveip, *savesctp;
sctp_errhdr_t *err;
ch = (sctp_chunkhdr_t *) skb->data;
- ch_end = ((__u8 *) ch) + WORD_ROUND(ntohs(ch->length));
/* Scan through all the chunks in the packet. */
- while (ch_end > (__u8 *)ch && ch_end < skb->tail) {
+ do {
+ /* Break out if chunk length is less then minimal. */
+ if (ntohs(ch->length) < sizeof(sctp_chunkhdr_t))
+ break;
+
+ ch_end = ((__u8 *)ch) + WORD_ROUND(ntohs(ch->length));
+ if (ch_end > skb->tail)
+ break;
/* RFC 8.4, 2) If the OOTB packet contains an ABORT chunk, the
* receiver MUST silently discard the OOTB packet and take no
}
ch = (sctp_chunkhdr_t *) ch_end;
- ch_end = ((__u8 *) ch) + WORD_ROUND(ntohs(ch->length));
- }
+ } while (ch_end < skb->tail);
return 0;
hit:
sctp_endpoint_hold(ep);
- sock_hold(epb->sk);
read_unlock(&head->lock);
return ep;
}
hit:
*pt = transport;
sctp_association_hold(asoc);
- sock_hold(epb->sk);
read_unlock(&head->lock);
return asoc;
}
struct sctp_transport *transport;
if ((asoc = sctp_lookup_association(laddr, paddr, &transport))) {
- sock_put(asoc->base.sk);
sctp_association_put(asoc);
return 1;
}