📄 sm_make_chunk.c
字号:
* RFC 2481 details a specific bit for a sender to send in the header of * its next outbound TCP segment to indicate to its peer that it has * reduced its congestion window. This is termed the CWR bit. For * SCTP the same indication is made by including the CWR chunk. * This chunk contains one data element, i.e. the TSN number that * was sent in the ECNE chunk. This element represents the lowest * TSN number in the datagram that was originally marked with the * CE bit. * * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Chunk Type=13 | Flags=00000000| Chunk Length = 8 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Lowest TSN Number | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * Note: The CWR is considered a Control chunk. */struct sctp_chunk *sctp_make_cwr(const struct sctp_association *asoc, const __u32 lowest_tsn, const struct sctp_chunk *chunk){ struct sctp_chunk *retval; sctp_cwrhdr_t cwr; cwr.lowest_tsn = htonl(lowest_tsn); retval = sctp_make_chunk(asoc, SCTP_CID_ECN_CWR, 0, sizeof(sctp_cwrhdr_t)); if (!retval) goto nodata; retval->subh.ecn_cwr_hdr = sctp_addto_chunk(retval, sizeof(cwr), &cwr); /* 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. * * [Report a reduced congestion window back to where the ECNE * came from.] */ if (chunk) retval->transport = chunk->transport;nodata: return retval;}/* Make an ECNE chunk. This is a congestion experienced report. */struct sctp_chunk *sctp_make_ecne(const struct sctp_association *asoc, const __u32 lowest_tsn){ struct sctp_chunk *retval; sctp_ecnehdr_t ecne; ecne.lowest_tsn = htonl(lowest_tsn); retval = sctp_make_chunk(asoc, SCTP_CID_ECN_ECNE, 0, sizeof(sctp_ecnehdr_t)); if (!retval) goto nodata; retval->subh.ecne_hdr = sctp_addto_chunk(retval, sizeof(ecne), &ecne);nodata: return retval;}/* Make a DATA chunk for the given association from the provided * parameters. However, do not populate the data payload. */struct sctp_chunk *sctp_make_datafrag_empty(struct sctp_association *asoc, const struct sctp_sndrcvinfo *sinfo, int data_len, __u8 flags, __u16 ssn){ struct sctp_chunk *retval; struct sctp_datahdr dp; int chunk_len; /* We assign the TSN as LATE as possible, not here when * creating the chunk. */ dp.tsn = 0; dp.stream = htons(sinfo->sinfo_stream); dp.ppid = sinfo->sinfo_ppid; /* Set the flags for an unordered send. */ if (sinfo->sinfo_flags & MSG_UNORDERED) { flags |= SCTP_DATA_UNORDERED; dp.ssn = 0; } else dp.ssn = htons(ssn); chunk_len = sizeof(dp) + data_len; retval = sctp_make_chunk(asoc, SCTP_CID_DATA, flags, chunk_len); if (!retval) goto nodata; retval->subh.data_hdr = sctp_addto_chunk(retval, sizeof(dp), &dp); memcpy(&retval->sinfo, sinfo, sizeof(struct sctp_sndrcvinfo));nodata: return retval;}/* Make a DATA chunk for the given association. Populate the data * payload. */struct sctp_chunk *sctp_make_datafrag(struct sctp_association *asoc, const struct sctp_sndrcvinfo *sinfo, int data_len, const __u8 *data, __u8 flags, __u16 ssn){ struct sctp_chunk *retval; retval = sctp_make_datafrag_empty(asoc, sinfo, data_len, flags, ssn); if (retval) sctp_addto_chunk(retval, data_len, data); return retval;}/* Make a DATA chunk for the given association to ride on stream id * 'stream', with a payload id of 'payload', and a body of 'data'. */struct sctp_chunk *sctp_make_data(struct sctp_association *asoc, const struct sctp_sndrcvinfo *sinfo, int data_len, const __u8 *data){ struct sctp_chunk *retval = NULL; retval = sctp_make_data_empty(asoc, sinfo, data_len); if (retval) sctp_addto_chunk(retval, data_len, data); return retval;}/* Make a DATA chunk for the given association to ride on stream id * 'stream', with a payload id of 'payload', and a body big enough to * hold 'data_len' octets of data. We use this version when we need * to build the message AFTER allocating memory. */struct sctp_chunk *sctp_make_data_empty(struct sctp_association *asoc, const struct sctp_sndrcvinfo *sinfo, int data_len){ __u8 flags = SCTP_DATA_NOT_FRAG; return sctp_make_datafrag_empty(asoc, sinfo, data_len, flags, 0);}/* Create a selective ackowledgement (SACK) for the given * association. This reports on which TSN's we've seen to date, * including duplicates and gaps. */struct sctp_chunk *sctp_make_sack(const struct sctp_association *asoc){ struct sctp_chunk *retval; struct sctp_sackhdr sack; int len; __u32 ctsn; __u16 num_gabs, num_dup_tsns; struct sctp_tsnmap *map = (struct sctp_tsnmap *)&asoc->peer.tsn_map; ctsn = sctp_tsnmap_get_ctsn(map); SCTP_DEBUG_PRINTK("sackCTSNAck sent: 0x%x.\n", ctsn); /* How much room is needed in the chunk? */ num_gabs = sctp_tsnmap_num_gabs(map); num_dup_tsns = sctp_tsnmap_num_dups(map); /* Initialize the SACK header. */ sack.cum_tsn_ack = htonl(ctsn); sack.a_rwnd = htonl(asoc->a_rwnd); sack.num_gap_ack_blocks = htons(num_gabs); sack.num_dup_tsns = htons(num_dup_tsns); len = sizeof(sack) + sizeof(struct sctp_gap_ack_block) * num_gabs + sizeof(__u32) * num_dup_tsns; /* Create the chunk. */ retval = sctp_make_chunk(asoc, SCTP_CID_SACK, 0, len); 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. This rule should also be followed if * the endpoint is bundling DATA chunks together with the * reply chunk. * * However, when acknowledging multiple DATA chunks received * in packets from different source addresses in a single * SACK, the SACK chunk may be transmitted to one of the * destination transport addresses from which the DATA or * control chunks being acknowledged were received. * * [BUG: We do not implement the following paragraph. * Perhaps we should remember the last transport we used for a * SACK and avoid that (if possible) if we have seen any * duplicates. --piggy] * * When a receiver of a duplicate DATA chunk sends a SACK to a * multi- homed endpoint it MAY be beneficial to vary the * destination address and not use the source address of the * DATA chunk. The reason being that receiving a duplicate * from a multi-homed endpoint might indicate that the return * path (as specified in the source address of the DATA chunk) * for the SACK is broken. * * [Send to the address from which we last received a DATA chunk.] */ retval->transport = asoc->peer.last_data_from; retval->subh.sack_hdr = sctp_addto_chunk(retval, sizeof(sack), &sack); /* Add the gap ack block information. */ if (num_gabs) sctp_addto_chunk(retval, sizeof(__u32) * num_gabs, sctp_tsnmap_get_gabs(map)); /* Add the duplicate TSN information. */ if (num_dup_tsns) sctp_addto_chunk(retval, sizeof(__u32) * num_dup_tsns, sctp_tsnmap_get_dups(map));nodata: return retval;}/* Make a SHUTDOWN chunk. */struct sctp_chunk *sctp_make_shutdown(const struct sctp_association *asoc, const struct sctp_chunk *chunk){ struct sctp_chunk *retval; sctp_shutdownhdr_t shut; __u32 ctsn; ctsn = sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map); shut.cum_tsn_ack = htonl(ctsn); retval = sctp_make_chunk(asoc, SCTP_CID_SHUTDOWN, 0, sizeof(sctp_shutdownhdr_t)); if (!retval) goto nodata; retval->subh.shutdown_hdr = sctp_addto_chunk(retval, sizeof(shut), &shut); if (chunk) retval->transport = chunk->transport;nodata: return retval;}struct sctp_chunk *sctp_make_shutdown_ack(const struct sctp_association *asoc, const struct sctp_chunk *chunk){ struct sctp_chunk *retval; retval = sctp_make_chunk(asoc, SCTP_CID_SHUTDOWN_ACK, 0, 0); /* 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. * * [ACK back to where the SHUTDOWN came from.] */ if (retval && chunk) retval->transport = chunk->transport; return retval;}struct sctp_chunk *sctp_make_shutdown_complete( const struct sctp_association *asoc, const struct sctp_chunk *chunk){ struct sctp_chunk *retval; __u8 flags = 0; /* Maybe set the T-bit if we have no association. */ flags |= asoc ? 0 : SCTP_CHUNK_FLAG_T; retval = sctp_make_chunk(asoc, SCTP_CID_SHUTDOWN_COMPLETE, flags, 0); /* 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. * * [Report SHUTDOWN COMPLETE back to where the SHUTDOWN ACK * came from.] */ if (retval && chunk) retval->transport = chunk->transport; return retval;}/* Create an ABORT. Note that we set the T bit if we have no * association. */struct sctp_chunk *sctp_make_abort(const struct sctp_association *asoc, const struct sctp_chunk *chunk, const size_t hint){ struct sctp_chunk *retval; __u8 flags = 0; /* Maybe set the T-bit if we have no association. */ flags |= asoc ? 0 : SCTP_CHUNK_FLAG_T; retval = sctp_make_chunk(asoc, SCTP_CID_ABORT, flags, hint); /* 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 (retval && chunk) retval->transport = chunk->transport; return retval;}/* Helper to create ABORT with a NO_USER_DATA error. */struct sctp_chunk *sctp_make_abort_no_data( const struct sctp_association *asoc, const struct sctp_chunk *chunk, __u32 tsn){ struct sctp_chunk *retval; __u32 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, (const void *)&payload, sizeof(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 sctp_chunk *chunk, const struct msghdr *msg){ struct sctp_chunk *retval; void *payload = NULL, *payoff; size_t paylen = 0; struct iovec *iov = NULL; int iovlen = 0; if (msg) { iov = msg->msg_iov; iovlen = msg->msg_iovlen; paylen = get_user_iov_size(iov, iovlen); } retval = sctp_make_abort(asoc, chunk, sizeof(sctp_errhdr_t) + paylen); if (!retval) goto err_chunk; if (paylen) { /* Put the msg_iov together into payload. */ payload = kmalloc(paylen, GFP_ATOMIC); if (!payload) goto err_payload; payoff = payload; for (; iovlen > 0; --iovlen) { if (copy_from_user(payoff, iov->iov_base,iov->iov_len)) goto err_copy; payoff += iov->iov_len; iov++; } } sctp_init_cause(retval, SCTP_ERROR_USER_ABORT, payload, paylen); if (paylen) kfree(payload); return retval;err_copy: kfree(payload);err_payload: sctp_chunk_free(retval); retval = NULL;err_chunk: 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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -