📄 sm_make_chunk.c
字号:
struct sctp_chunk *sctp_make_abort_no_data( const struct sctp_association *asoc, const struct sctp_chunk *chunk, __u32 tsn){ struct sctp_chunk *retval; __be32 payload; retval = sctp_make_abort(asoc, chunk, sizeof(sctp_errhdr_t) + sizeof(tsn)); if (!retval) goto no_mem; /* Put the tsn back into network byte order. */ payload = htonl(tsn); sctp_init_cause(retval, SCTP_ERROR_NO_DATA, sizeof(payload)); sctp_addto_chunk(retval, sizeof(payload), (const void *)&payload); /* RFC 2960 6.4 Multi-homed SCTP Endpoints * * An endpoint SHOULD transmit reply chunks (e.g., SACK, * HEARTBEAT ACK, * etc.) to the same destination transport * address from which it * received the DATA or control chunk * to which it is replying. * * [ABORT back to where the offender came from.] */ if (chunk) retval->transport = chunk->transport;no_mem: return retval;}/* Helper to create ABORT with a SCTP_ERROR_USER_ABORT error. */struct sctp_chunk *sctp_make_abort_user(const struct sctp_association *asoc, const struct msghdr *msg, size_t paylen){ struct sctp_chunk *retval; void *payload = NULL; int err; retval = sctp_make_abort(asoc, NULL, sizeof(sctp_errhdr_t) + paylen); if (!retval) goto err_chunk; if (paylen) { /* Put the msg_iov together into payload. */ payload = kmalloc(paylen, GFP_KERNEL); if (!payload) goto err_payload; err = memcpy_fromiovec(payload, msg->msg_iov, paylen); if (err < 0) goto err_copy; } sctp_init_cause(retval, SCTP_ERROR_USER_ABORT, paylen); sctp_addto_chunk(retval, paylen, payload); if (paylen) kfree(payload); return retval;err_copy: kfree(payload);err_payload: sctp_chunk_free(retval); retval = NULL;err_chunk: return retval;}/* Append bytes to the end of a parameter. Will panic if chunk is not big * enough. */static void *sctp_addto_param(struct sctp_chunk *chunk, int len, const void *data){ void *target; int chunklen = ntohs(chunk->chunk_hdr->length); target = skb_put(chunk->skb, len); memcpy(target, data, len); /* Adjust the chunk length field. */ chunk->chunk_hdr->length = htons(chunklen + len); chunk->chunk_end = skb_tail_pointer(chunk->skb); return target;}/* Make an ABORT chunk with a PROTOCOL VIOLATION cause code. */struct sctp_chunk *sctp_make_abort_violation( const struct sctp_association *asoc, const struct sctp_chunk *chunk, const __u8 *payload, const size_t paylen){ struct sctp_chunk *retval; struct sctp_paramhdr phdr; retval = sctp_make_abort(asoc, chunk, sizeof(sctp_errhdr_t) + paylen + sizeof(sctp_paramhdr_t)); if (!retval) goto end; sctp_init_cause(retval, SCTP_ERROR_PROTO_VIOLATION, paylen + sizeof(sctp_paramhdr_t)); phdr.type = htons(chunk->chunk_hdr->type); phdr.length = chunk->chunk_hdr->length; sctp_addto_chunk(retval, paylen, payload); sctp_addto_param(retval, sizeof(sctp_paramhdr_t), &phdr);end: return retval;}/* Make a HEARTBEAT chunk. */struct sctp_chunk *sctp_make_heartbeat(const struct sctp_association *asoc, const struct sctp_transport *transport, const void *payload, const size_t paylen){ struct sctp_chunk *retval = sctp_make_chunk(asoc, SCTP_CID_HEARTBEAT, 0, paylen); if (!retval) goto nodata; /* Cast away the 'const', as this is just telling the chunk * what transport it belongs to. */ retval->transport = (struct sctp_transport *) transport; retval->subh.hbs_hdr = sctp_addto_chunk(retval, paylen, payload);nodata: return retval;}struct sctp_chunk *sctp_make_heartbeat_ack(const struct sctp_association *asoc, const struct sctp_chunk *chunk, const void *payload, const size_t paylen){ struct sctp_chunk *retval; retval = sctp_make_chunk(asoc, SCTP_CID_HEARTBEAT_ACK, 0, paylen); if (!retval) goto nodata; retval->subh.hbs_hdr = sctp_addto_chunk(retval, paylen, payload); /* RFC 2960 6.4 Multi-homed SCTP Endpoints * * An endpoint SHOULD transmit reply chunks (e.g., SACK, * HEARTBEAT ACK, * etc.) to the same destination transport * address from which it * received the DATA or control chunk * to which it is replying. * * [HBACK back to where the HEARTBEAT came from.] */ if (chunk) retval->transport = chunk->transport;nodata: return retval;}/* Create an Operation Error chunk with the specified space reserved. * This routine can be used for containing multiple causes in the chunk. */static struct sctp_chunk *sctp_make_op_error_space( const struct sctp_association *asoc, const struct sctp_chunk *chunk, size_t size){ struct sctp_chunk *retval; retval = sctp_make_chunk(asoc, SCTP_CID_ERROR, 0, sizeof(sctp_errhdr_t) + size); if (!retval) goto nodata; /* RFC 2960 6.4 Multi-homed SCTP Endpoints * * An endpoint SHOULD transmit reply chunks (e.g., SACK, * HEARTBEAT ACK, etc.) to the same destination transport * address from which it received the DATA or control chunk * to which it is replying. * */ if (chunk) retval->transport = chunk->transport;nodata: return retval;}/* Create an Operation Error chunk. */struct sctp_chunk *sctp_make_op_error(const struct sctp_association *asoc, const struct sctp_chunk *chunk, __be16 cause_code, const void *payload, size_t paylen){ struct sctp_chunk *retval; retval = sctp_make_op_error_space(asoc, chunk, paylen); if (!retval) goto nodata; sctp_init_cause(retval, cause_code, paylen); sctp_addto_chunk(retval, paylen, payload);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 ********************************************************************//* Turn an skb into a chunk. * FIXME: Eventually move the structure directly inside the skb->cb[]. */struct sctp_chunk *sctp_chunkify(struct sk_buff *skb, const struct sctp_association *asoc, struct sock *sk){ struct sctp_chunk *retval; retval = kmem_cache_zalloc(sctp_chunk_cachep, GFP_ATOMIC); if (!retval) goto nodata; if (!sk) { SCTP_DEBUG_PRINTK("chunkifying skb %p w/o an sk\n", skb); } INIT_LIST_HEAD(&retval->list); retval->skb = skb; retval->asoc = (struct sctp_association *)asoc; retval->resent = 0; retval->has_tsn = 0; retval->has_ssn = 0; retval->rtt_in_progress = 0; retval->sent_at = 0; retval->singleton = 1; retval->end_of_packet = 0; retval->ecn_ce_done = 0; retval->pdiscard = 0; /* sctpimpguide-05.txt Section 2.8.2 * M1) Each time a new DATA chunk is transmitted * set the 'TSN.Missing.Report' count for that TSN to 0. The * 'TSN.Missing.Report' count will be used to determine missing chunks * and when to fast retransmit. */ retval->tsn_missing_report = 0; retval->tsn_gap_acked = 0; retval->fast_retransmit = 0; /* If this is a fragmented message, track all fragments * of the message (for SEND_FAILED). */ retval->msg = NULL; /* Polish the bead hole. */ INIT_LIST_HEAD(&retval->transmitted_list); INIT_LIST_HEAD(&retval->frag_list); SCTP_DBG_OBJCNT_INC(chunk); atomic_set(&retval->refcnt, 1);nodata: return retval;}/* Set chunk->source and dest based on the IP header in chunk->skb. */void sctp_init_addrs(struct sctp_chunk *chunk, union sctp_addr *src, union sctp_addr *dest){ memcpy(&chunk->source, src, sizeof(union sctp_addr)); memcpy(&chunk->dest, dest, sizeof(union sctp_addr));}/* Extract the source address from a chunk. */const union sctp_addr *sctp_source(const struct sctp_chunk *chunk){ /* If we have a known transport, use that. */ if (chunk->transport) { return &chunk->transport->ipaddr; } else { /* Otherwise, extract it from the IP header. */ return &chunk->source; }}/* Create a new chunk, setting the type and flags headers from the * arguments, reserving enough space for a 'paylen' byte payload. */SCTP_STATICstruct sctp_chunk *sctp_make_chunk(const struct sctp_association *asoc, __u8 type, __u8 flags, int paylen){ struct sctp_chunk *retval; sctp_chunkhdr_t *chunk_hdr; struct sk_buff *skb; struct sock *sk; /* No need to allocate LL here, as this is only a chunk. */ skb = alloc_skb(WORD_ROUND(sizeof(sctp_chunkhdr_t) + paylen), GFP_ATOMIC); if (!skb) goto nodata; /* Make room for the chunk header. */ chunk_hdr = (sctp_chunkhdr_t *)skb_put(skb, sizeof(sctp_chunkhdr_t)); chunk_hdr->type = type; chunk_hdr->flags = flags; chunk_hdr->length = htons(sizeof(sctp_chunkhdr_t)); sk = asoc ? asoc->base.sk : NULL; retval = sctp_chunkify(skb, asoc, sk); if (!retval) { kfree_skb(skb); goto nodata; } 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; return retval;nodata: return NULL;}/* Release the memory occupied by a chunk. */static void sctp_chunk_destroy(struct sctp_chunk *chunk){ BUG_ON(!list_empty(&chunk->list)); list_del_init(&chunk->transmitted_list); /* Free the chunk skb data and the SCTP_chunk stub itself. */ dev_kfree_skb(chunk->skb); SCTP_DBG_OBJCNT_DEC(chunk); kmem_cache_free(sctp_chunk_cachep, chunk);}/* Possibly, free the chunk. */void sctp_chunk_free(struct sctp_chunk *chunk){ /* Release our reference on the message tracker. */ if (chunk->msg) sctp_datamsg_put(chunk->msg); sctp_chunk_put(chunk);}/* Grab a reference to the chunk. */void sctp_chunk_hold(struct sctp_chunk *ch){ atomic_inc(&ch->refcnt);}/* Release a reference to the chunk. */void sctp_chunk_put(struct sctp_chunk *ch){ if (atomic_dec_and_test(&ch->refcnt)) sctp_chunk_destroy(ch);}/* Append bytes to the end of a chunk. Will panic if chunk is not big * enough. */void *sctp_addto_chunk(struct sctp_chunk *chunk, int len, const void *data){ void *target; void *padding; int chunklen = ntohs(chunk->chunk_hdr->length); int padlen = WORD_ROUND(chunklen) - chunklen; padding = skb_put(chunk->skb, padlen); target = skb_put(chunk->skb, len); memset(padding, 0, padlen); memcpy(target, data, len); /* Adjust the chunk length field. */ chunk->chunk_hdr->length = htons(chunklen + padlen + len); chunk->chunk_end = skb_tail_pointer(chunk->skb); return target;}/* Append bytes from user space to the end of a chunk. Will panic if * chunk is not big enough. * Returns a kernel err value. */int sctp_user_addto_chunk(struct sctp_chunk *chunk, int off, int len,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -