sctp: fix the panic caused by route update
[cascardo/linux.git] / net / sctp / output.c
index 31b7bc3..6cb0df8 100644 (file)
@@ -180,7 +180,6 @@ sctp_xmit_t sctp_packet_transmit_chunk(struct sctp_packet *packet,
                                       int one_packet, gfp_t gfp)
 {
        sctp_xmit_t retval;
-       int error = 0;
 
        pr_debug("%s: packet:%p size:%Zu chunk:%p size:%d\n", __func__,
                 packet, packet->size, chunk, chunk->skb ? chunk->skb->len : -1);
@@ -188,6 +187,8 @@ sctp_xmit_t sctp_packet_transmit_chunk(struct sctp_packet *packet,
        switch ((retval = (sctp_packet_append_chunk(packet, chunk)))) {
        case SCTP_XMIT_PMTU_FULL:
                if (!packet->has_cookie_echo) {
+                       int error = 0;
+
                        error = sctp_packet_transmit(packet, gfp);
                        if (error < 0)
                                chunk->skb->sk->sk_err = -error;
@@ -296,7 +297,7 @@ static sctp_xmit_t __sctp_packet_append_chunk(struct sctp_packet *packet,
                                              struct sctp_chunk *chunk)
 {
        sctp_xmit_t retval = SCTP_XMIT_OK;
-       __u16 chunk_len = WORD_ROUND(ntohs(chunk->chunk_hdr->length));
+       __u16 chunk_len = SCTP_PAD4(ntohs(chunk->chunk_hdr->length));
 
        /* Check to see if this chunk will fit into the packet */
        retval = sctp_packet_will_fit(packet, chunk, chunk_len);
@@ -417,6 +418,7 @@ int sctp_packet_transmit(struct sctp_packet *packet, gfp_t gfp)
        __u8 has_data = 0;
        int gso = 0;
        int pktcount = 0;
+       int auth_len = 0;
        struct dst_entry *dst;
        unsigned char *auth = NULL;     /* pointer to auth in skb data */
 
@@ -441,14 +443,14 @@ int sctp_packet_transmit(struct sctp_packet *packet, gfp_t gfp)
                         * time. Application may notice this error.
                         */
                        pr_err_once("Trying to GSO but underlying device doesn't support it.");
-                       goto nomem;
+                       goto err;
                }
        } else {
                pkt_size = packet->size;
        }
        head = alloc_skb(pkt_size + MAX_HEADER, gfp);
        if (!head)
-               goto nomem;
+               goto err;
        if (gso) {
                NAPI_GRO_CB(head)->last = head;
                skb_shinfo(head)->gso_type = sk->sk_gso_type;
@@ -469,8 +471,12 @@ int sctp_packet_transmit(struct sctp_packet *packet, gfp_t gfp)
                }
        }
        dst = dst_clone(tp->dst);
-       if (!dst)
-               goto no_route;
+       if (!dst) {
+               if (asoc)
+                       IP_INC_STATS(sock_net(asoc->base.sk),
+                                    IPSTATS_MIB_OUTNOROUTES);
+               goto nodst;
+       }
        skb_dst_set(head, dst);
 
        /* Build the SCTP header.  */
@@ -503,9 +509,14 @@ int sctp_packet_transmit(struct sctp_packet *packet, gfp_t gfp)
                if (gso) {
                        pkt_size = packet->overhead;
                        list_for_each_entry(chunk, &packet->chunk_list, list) {
-                               int padded = WORD_ROUND(chunk->skb->len);
-
-                               if (pkt_size + padded > tp->pathmtu)
+                               int padded = SCTP_PAD4(chunk->skb->len);
+
+                               if (chunk == packet->auth)
+                                       auth_len = padded;
+                               else if (auth_len + padded + packet->overhead >
+                                        tp->pathmtu)
+                                       goto nomem;
+                               else if (pkt_size + padded > tp->pathmtu)
                                        break;
                                pkt_size += padded;
                        }
@@ -533,7 +544,7 @@ int sctp_packet_transmit(struct sctp_packet *packet, gfp_t gfp)
                 * included in the chunk length field.  The sender should
                 * never pad with more than 3 bytes.
                 *
-                * [This whole comment explains WORD_ROUND() below.]
+                * [This whole comment explains SCTP_PAD4() below.]
                 */
 
                pkt_size -= packet->overhead;
@@ -555,7 +566,7 @@ int sctp_packet_transmit(struct sctp_packet *packet, gfp_t gfp)
                                has_data = 1;
                        }
 
-                       padding = WORD_ROUND(chunk->skb->len) - chunk->skb->len;
+                       padding = SCTP_PAD4(chunk->skb->len) - chunk->skb->len;
                        if (padding)
                                memset(skb_put(chunk->skb, padding), 0, padding);
 
@@ -582,7 +593,7 @@ int sctp_packet_transmit(struct sctp_packet *packet, gfp_t gfp)
                         * acknowledged or have failed.
                         * Re-queue auth chunks if needed.
                         */
-                       pkt_size -= WORD_ROUND(chunk->skb->len);
+                       pkt_size -= SCTP_PAD4(chunk->skb->len);
 
                        if (!sctp_chunk_is_data(chunk) && chunk != packet->auth)
                                sctp_chunk_free(chunk);
@@ -621,8 +632,10 @@ int sctp_packet_transmit(struct sctp_packet *packet, gfp_t gfp)
                if (!gso)
                        break;
 
-               if (skb_gro_receive(&head, nskb))
+               if (skb_gro_receive(&head, nskb)) {
+                       kfree_skb(nskb);
                        goto nomem;
+               }
                nskb = NULL;
                if (WARN_ON_ONCE(skb_shinfo(head)->gso_segs >=
                                 sk->sk_gso_max_segs))
@@ -716,18 +729,13 @@ int sctp_packet_transmit(struct sctp_packet *packet, gfp_t gfp)
        }
        head->ignore_df = packet->ipfragok;
        tp->af_specific->sctp_xmit(head, tp);
+       goto out;
 
-out:
-       sctp_packet_reset(packet);
-       return err;
-no_route:
-       kfree_skb(head);
-       if (nskb != head)
-               kfree_skb(nskb);
-
-       if (asoc)
-               IP_INC_STATS(sock_net(asoc->base.sk), IPSTATS_MIB_OUTNOROUTES);
+nomem:
+       if (packet->auth && list_empty(&packet->auth->list))
+               sctp_chunk_free(packet->auth);
 
+nodst:
        /* FIXME: Returning the 'err' will effect all the associations
         * associated with a socket, although only one of the paths of the
         * association is unreachable.
@@ -736,22 +744,18 @@ no_route:
         * required.
         */
         /* err = -EHOSTUNREACH; */
-err:
-       /* Control chunks are unreliable so just drop them.  DATA chunks
-        * will get resent or dropped later.
-        */
+       kfree_skb(head);
 
+err:
        list_for_each_entry_safe(chunk, tmp, &packet->chunk_list, list) {
                list_del_init(&chunk->list);
                if (!sctp_chunk_is_data(chunk))
                        sctp_chunk_free(chunk);
        }
-       goto out;
-nomem:
-       if (packet->auth && list_empty(&packet->auth->list))
-               sctp_chunk_free(packet->auth);
-       err = -ENOMEM;
-       goto err;
+
+out:
+       sctp_packet_reset(packet);
+       return err;
 }
 
 /********************************************************************
@@ -913,7 +917,7 @@ static sctp_xmit_t sctp_packet_will_fit(struct sctp_packet *packet,
                 */
                maxsize = pmtu - packet->overhead;
                if (packet->auth)
-                       maxsize -= WORD_ROUND(packet->auth->skb->len);
+                       maxsize -= SCTP_PAD4(packet->auth->skb->len);
                if (chunk_len > maxsize)
                        retval = SCTP_XMIT_PMTU_FULL;