diff options
-rw-r--r-- | include/net/sctp/sm.h | 1 | ||||
-rw-r--r-- | include/net/sctp/structs.h | 3 | ||||
-rw-r--r-- | net/sctp/chunk.c | 12 | ||||
-rw-r--r-- | net/sctp/output.c | 131 | ||||
-rw-r--r-- | net/sctp/sm_make_chunk.c | 39 |
5 files changed, 159 insertions, 27 deletions
diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h index e8e3a64eb322..148cdb4b9606 100644 --- a/include/net/sctp/sm.h +++ b/include/net/sctp/sm.h @@ -256,6 +256,7 @@ int sctp_process_asconf_ack(struct sctp_association *asoc, struct sctp_chunk *sctp_make_fwdtsn(const struct sctp_association *asoc, __u32 new_cum_tsn, size_t nstreams, struct sctp_fwdtsn_skip *skiplist); +struct sctp_chunk *sctp_make_auth(const struct sctp_association *asoc); void sctp_chunk_assign_tsn(struct sctp_chunk *); void sctp_chunk_assign_ssn(struct sctp_chunk *); diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 18b06afacea0..31841c3a7fe8 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -798,6 +798,9 @@ struct sctp_packet { /* This packet contains an AUTH chunk */ __u8 has_auth; + /* This packet contains at least 1 DATA chunk */ + __u8 has_data; + /* SCTP cannot fragment this packet. So let ip fragment it. */ __u8 ipfragok; diff --git a/net/sctp/chunk.c b/net/sctp/chunk.c index 77fb7b06a9c4..619d0f2dee51 100644 --- a/net/sctp/chunk.c +++ b/net/sctp/chunk.c @@ -194,6 +194,18 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc, max = asoc->frag_point; + /* If the the peer requested that we authenticate DATA chunks + * we need to accound for bundling of the AUTH chunks along with + * DATA. + */ + if (sctp_auth_send_cid(SCTP_CID_DATA, asoc)) { + struct sctp_hmac *hmac_desc = sctp_auth_asoc_get_hmac(asoc); + + if (hmac_desc) + max -= WORD_ROUND(sizeof(sctp_auth_chunk_t) + + hmac_desc->hmac_len); + } + whole = 0; first_len = max; diff --git a/net/sctp/output.c b/net/sctp/output.c index 49b9f5f031a4..847639d542c0 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -80,6 +80,7 @@ struct sctp_packet *sctp_packet_config(struct sctp_packet *packet, packet->has_cookie_echo = 0; packet->has_sack = 0; packet->has_auth = 0; + packet->has_data = 0; packet->ipfragok = 0; packet->auth = NULL; @@ -124,6 +125,7 @@ struct sctp_packet *sctp_packet_init(struct sctp_packet *packet, packet->has_cookie_echo = 0; packet->has_sack = 0; packet->has_auth = 0; + packet->has_data = 0; packet->ipfragok = 0; packet->malloced = 0; packet->auth = NULL; @@ -185,6 +187,39 @@ sctp_xmit_t sctp_packet_transmit_chunk(struct sctp_packet *packet, return retval; } +/* Try to bundle an auth chunk into the packet. */ +static sctp_xmit_t sctp_packet_bundle_auth(struct sctp_packet *pkt, + struct sctp_chunk *chunk) +{ + struct sctp_association *asoc = pkt->transport->asoc; + struct sctp_chunk *auth; + sctp_xmit_t retval = SCTP_XMIT_OK; + + /* if we don't have an association, we can't do authentication */ + if (!asoc) + return retval; + + /* See if this is an auth chunk we are bundling or if + * auth is already bundled. + */ + if (chunk->chunk_hdr->type == SCTP_CID_AUTH || pkt->auth) + return retval; + + /* if the peer did not request this chunk to be authenticated, + * don't do it + */ + if (!chunk->auth) + return retval; + + auth = sctp_make_auth(asoc); + if (!auth) + return retval; + + retval = sctp_packet_append_chunk(pkt, auth); + + return retval; +} + /* Try to bundle a SACK with the packet. */ static sctp_xmit_t sctp_packet_bundle_sack(struct sctp_packet *pkt, struct sctp_chunk *chunk) @@ -231,12 +266,17 @@ sctp_xmit_t sctp_packet_append_chunk(struct sctp_packet *packet, SCTP_DEBUG_PRINTK("%s: packet:%p chunk:%p\n", __FUNCTION__, packet, chunk); - retval = sctp_packet_bundle_sack(packet, chunk); - psize = packet->size; + /* Try to bundle AUTH chunk */ + retval = sctp_packet_bundle_auth(packet, chunk); + if (retval != SCTP_XMIT_OK) + goto finish; + /* Try to bundle SACK chunk */ + retval = sctp_packet_bundle_sack(packet, chunk); if (retval != SCTP_XMIT_OK) goto finish; + psize = packet->size; pmtu = ((packet->transport->asoc) ? (packet->transport->asoc->pathmtu) : (packet->transport->pathmtu)); @@ -245,10 +285,16 @@ sctp_xmit_t sctp_packet_append_chunk(struct sctp_packet *packet, /* Decide if we need to fragment or resubmit later. */ if (too_big) { - /* Both control chunks and data chunks with TSNs are - * non-fragmentable. + /* It's OK to fragmet at IP level if any one of the following + * is true: + * 1. The packet is empty (meaning this chunk is greater + * the MTU) + * 2. The chunk we are adding is a control chunk + * 3. The packet doesn't have any data in it yet and data + * requires authentication. */ - if (sctp_packet_empty(packet) || !sctp_chunk_is_data(chunk)) { + if (sctp_packet_empty(packet) || !sctp_chunk_is_data(chunk) || + (!packet->has_data && chunk->auth)) { /* We no longer do re-fragmentation. * Just fragment at the IP layer, if we * actually hit this condition @@ -270,16 +316,31 @@ append: /* DATA is a special case since we must examine both rwnd and cwnd * before we send DATA. */ - if (sctp_chunk_is_data(chunk)) { + switch (chunk->chunk_hdr->type) { + case SCTP_CID_DATA: retval = sctp_packet_append_data(packet, chunk); /* Disallow SACK bundling after DATA. */ packet->has_sack = 1; + /* Disallow AUTH bundling after DATA */ + packet->has_auth = 1; + /* Let it be knows that packet has DATA in it */ + packet->has_data = 1; if (SCTP_XMIT_OK != retval) goto finish; - } else if (SCTP_CID_COOKIE_ECHO == chunk->chunk_hdr->type) + break; + case SCTP_CID_COOKIE_ECHO: packet->has_cookie_echo = 1; - else if (SCTP_CID_SACK == chunk->chunk_hdr->type) + break; + + case SCTP_CID_SACK: packet->has_sack = 1; + break; + + case SCTP_CID_AUTH: + packet->has_auth = 1; + packet->auth = chunk; + break; + } /* It is OK to send this chunk. */ list_add_tail(&chunk->list, &packet->chunk_list); @@ -307,6 +368,8 @@ int sctp_packet_transmit(struct sctp_packet *packet) int padding; /* How much padding do we need? */ __u8 has_data = 0; struct dst_entry *dst = tp->dst; + unsigned char *auth = NULL; /* pointer to auth in skb data */ + __u32 cksum_buf_len = sizeof(struct sctphdr); SCTP_DEBUG_PRINTK("%s: packet:%p\n", __FUNCTION__, packet); @@ -360,16 +423,6 @@ int sctp_packet_transmit(struct sctp_packet *packet) sh->vtag = htonl(packet->vtag); sh->checksum = 0; - /* 2) Calculate the Adler-32 checksum of the whole packet, - * including the SCTP common header and all the - * chunks. - * - * Note: Adler-32 is no longer applicable, as has been replaced - * by CRC32-C as described in <draft-ietf-tsvwg-sctpcsum-02.txt>. - */ - if (!(dst->dev->features & NETIF_F_NO_CSUM)) - crc32 = sctp_start_cksum((__u8 *)sh, sizeof(struct sctphdr)); - /** * 6.10 Bundling * @@ -420,14 +473,16 @@ int sctp_packet_transmit(struct sctp_packet *packet) if (padding) memset(skb_put(chunk->skb, padding), 0, padding); - if (dst->dev->features & NETIF_F_NO_CSUM) - memcpy(skb_put(nskb, chunk->skb->len), + /* if this is the auth chunk that we are adding, + * store pointer where it will be added and put + * the auth into the packet. + */ + if (chunk == packet->auth) + auth = skb_tail_pointer(nskb); + + cksum_buf_len += chunk->skb->len; + memcpy(skb_put(nskb, chunk->skb->len), chunk->skb->data, chunk->skb->len); - else - crc32 = sctp_update_copy_cksum(skb_put(nskb, - chunk->skb->len), - chunk->skb->data, - chunk->skb->len, crc32); SCTP_DEBUG_PRINTK("%s %p[%s] %s 0x%x, %s %d, %s %d, %s %d\n", "*** Chunk", chunk, @@ -449,9 +504,31 @@ int sctp_packet_transmit(struct sctp_packet *packet) sctp_chunk_free(chunk); } - /* Perform final transformation on checksum. */ - if (!(dst->dev->features & NETIF_F_NO_CSUM)) + /* SCTP-AUTH, Section 6.2 + * The sender MUST calculate the MAC as described in RFC2104 [2] + * using the hash function H as described by the MAC Identifier and + * the shared association key K based on the endpoint pair shared key + * described by the shared key identifier. The 'data' used for the + * computation of the AUTH-chunk is given by the AUTH chunk with its + * HMAC field set to zero (as shown in Figure 6) followed by all + * chunks that are placed after the AUTH chunk in the SCTP packet. + */ + if (auth) + sctp_auth_calculate_hmac(asoc, nskb, + (struct sctp_auth_chunk *)auth, + GFP_ATOMIC); + + /* 2) Calculate the Adler-32 checksum of the whole packet, + * including the SCTP common header and all the + * chunks. + * + * Note: Adler-32 is no longer applicable, as has been replaced + * by CRC32-C as described in <draft-ietf-tsvwg-sctpcsum-02.txt>. + */ + if (!(dst->dev->features & NETIF_F_NO_CSUM)) { + crc32 = sctp_start_cksum((__u8 *)sh, cksum_buf_len); crc32 = sctp_end_cksum(crc32); + } /* 3) Put the resultant value into the checksum field in the * common header, and leave the rest of the bits unchanged. diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 4c02875786ac..fa2ba543183d 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -1111,6 +1111,41 @@ nodata: return retval; } +struct sctp_chunk *sctp_make_auth(const struct sctp_association *asoc) +{ + struct sctp_chunk *retval; + struct sctp_hmac *hmac_desc; + struct sctp_authhdr auth_hdr; + __u8 *hmac; + + /* Get the first hmac that the peer told us to use */ + hmac_desc = sctp_auth_asoc_get_hmac(asoc); + if (unlikely(!hmac_desc)) + return NULL; + + retval = sctp_make_chunk(asoc, SCTP_CID_AUTH, 0, + hmac_desc->hmac_len + sizeof(sctp_authhdr_t)); + if (!retval) + return NULL; + + auth_hdr.hmac_id = htons(hmac_desc->hmac_id); + auth_hdr.shkey_id = htons(asoc->active_key_id); + + retval->subh.auth_hdr = sctp_addto_chunk(retval, sizeof(sctp_authhdr_t), + &auth_hdr); + + hmac = skb_put(retval->skb, hmac_desc->hmac_len); + memset(hmac, 0, hmac_desc->hmac_len); + + /* Adjust the chunk header to include the empty MAC */ + retval->chunk_hdr->length = + htons(ntohs(retval->chunk_hdr->length) + hmac_desc->hmac_len); + retval->chunk_end = skb_tail_pointer(retval->skb); + + return retval; +} + + /******************************************************************** * 2nd Level Abstractions ********************************************************************/ @@ -1225,6 +1260,10 @@ struct sctp_chunk *sctp_make_chunk(const struct sctp_association *asoc, retval->chunk_hdr = chunk_hdr; retval->chunk_end = ((__u8 *)chunk_hdr) + sizeof(struct sctp_chunkhdr); + /* Determine if the chunk needs to be authenticated */ + if (sctp_auth_send_cid(type, asoc)) + retval->auth = 1; + /* Set the skb to the belonging sock for accounting. */ skb->sk = sk; |