/* Build a cookie representing asoc.
* This INCLUDES the param header needed to put the cookie in the INIT ACK.
*/
static sctp_cookie_param_t *sctp_pack_cookie(const struct sctp_endpoint *ep,
const struct sctp_association *asoc,
const struct sctp_chunk *init_chunk,
int *cookie_len,
const __u8 *raw_addrs, int addrs_len)
{
sctp_cookie_param_t *retval;
struct sctp_signed_cookie *cookie;
struct scatterlist sg;
int headersize, bodysize;
unsigned int keylen;
char *key;
headersize = sizeof(sctp_paramhdr_t) + SCTP_SECRET_SIZE;
bodysize = sizeof(struct sctp_cookie)
+ ntohs(init_chunk->chunk_hdr->length) + addrs_len;
/* Pad out the cookie to a multiple to make the signature
* functions simpler to write.
*/
if (bodysize % SCTP_COOKIE_MULTIPLE)
bodysize += SCTP_COOKIE_MULTIPLE
- (bodysize % SCTP_COOKIE_MULTIPLE);
*cookie_len = headersize + bodysize;
retval = (sctp_cookie_param_t *)kmalloc(*cookie_len, GFP_ATOMIC);
if (!retval) {
*cookie_len = 0;
goto nodata;
}
/* Clear this memory since we are sending this data structure
* out on the network.
*/
memset(retval, 0x00, *cookie_len);
cookie = (struct sctp_signed_cookie *) retval->body;
/* Set up the parameter header. */
retval->p.type = SCTP_PARAM_STATE_COOKIE;
retval->p.length = htons(*cookie_len);
/* Copy the cookie part of the association itself. */
cookie->c = asoc->c;
/* Save the raw address list length in the cookie. */
cookie->c.raw_addr_list_len = addrs_len;
/* Remember PR-SCTP capability. */
cookie->c.prsctp_capable = asoc->peer.prsctp_capable;
/* Save adaption indication in the cookie. */
cookie->c.adaption_ind = asoc->peer.adaption_ind;
/* Set an expiration time for the cookie. */
do_gettimeofday(&cookie->c.expiration);
TIMEVAL_ADD(asoc->cookie_life, cookie->c.expiration);
/* Copy the peer's init packet. */
memcpy(&cookie->c.peer_init[0], init_chunk->chunk_hdr,
ntohs(init_chunk->chunk_hdr->length));
/* Copy the raw local address list of the association. */
memcpy((__u8 *)&cookie->c.peer_init[0] +
ntohs(init_chunk->chunk_hdr->length), raw_addrs, addrs_len);
if (sctp_sk(ep->base.sk)->hmac) {
/* Sign the message. */
sg.page = virt_to_page(&cookie->c);
sg.offset = (unsigned long)(&cookie->c) % PAGE_SIZE;
sg.length = bodysize;
keylen = SCTP_SECRET_SIZE;
key = (char *)ep->secret_key[ep->current_key];
sctp_crypto_hmac(sctp_sk(ep->base.sk)->hmac, key, &keylen,
&sg, 1, cookie->signature);
}
nodata:
return retval;
}