chunk_init_common.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. package sctp
  2. import (
  3. "encoding/binary"
  4. "errors"
  5. "fmt"
  6. )
  7. /*
  8. chunkInitCommon represents an SCTP Chunk body of type INIT and INIT ACK
  9. 0 1 2 3
  10. 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
  11. +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  12. | Type = 1 | Chunk Flags | Chunk Length |
  13. +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  14. | Initiate Tag |
  15. +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  16. | Advertised Receiver Window Credit (a_rwnd) |
  17. +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  18. | Number of Outbound Streams | Number of Inbound Streams |
  19. +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  20. | Initial TSN |
  21. +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  22. | |
  23. | Optional/Variable-Length Parameters |
  24. | |
  25. +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  26. The INIT chunk contains the following parameters. Unless otherwise
  27. noted, each parameter MUST only be included once in the INIT chunk.
  28. Fixed Parameters Status
  29. ----------------------------------------------
  30. Initiate Tag Mandatory
  31. Advertised Receiver Window Credit Mandatory
  32. Number of Outbound Streams Mandatory
  33. Number of Inbound Streams Mandatory
  34. Initial TSN Mandatory
  35. */
  36. type chunkInitCommon struct {
  37. initiateTag uint32
  38. advertisedReceiverWindowCredit uint32
  39. numOutboundStreams uint16
  40. numInboundStreams uint16
  41. initialTSN uint32
  42. params []param
  43. }
  44. const (
  45. initChunkMinLength = 16
  46. initOptionalVarHeaderLength = 4
  47. )
  48. var (
  49. errInitChunkParseParamTypeFailed = errors.New("failed to parse param type")
  50. errInitChunkUnmarshalParam = errors.New("failed unmarshalling param in Init Chunk")
  51. errInitAckMarshalParam = errors.New("unable to marshal parameter for INIT/INITACK")
  52. )
  53. func (i *chunkInitCommon) unmarshal(raw []byte) error {
  54. i.initiateTag = binary.BigEndian.Uint32(raw[0:])
  55. i.advertisedReceiverWindowCredit = binary.BigEndian.Uint32(raw[4:])
  56. i.numOutboundStreams = binary.BigEndian.Uint16(raw[8:])
  57. i.numInboundStreams = binary.BigEndian.Uint16(raw[10:])
  58. i.initialTSN = binary.BigEndian.Uint32(raw[12:])
  59. // https://tools.ietf.org/html/rfc4960#section-3.2.1
  60. //
  61. // Chunk values of SCTP control chunks consist of a chunk-type-specific
  62. // header of required fields, followed by zero or more parameters. The
  63. // optional and variable-length parameters contained in a chunk are
  64. // defined in a Type-Length-Value format as shown below.
  65. //
  66. // 0 1 2 3
  67. // 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
  68. // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  69. // | Parameter Type | Parameter Length |
  70. // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  71. // | |
  72. // | Parameter Value |
  73. // | |
  74. // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  75. offset := initChunkMinLength
  76. remaining := len(raw) - offset
  77. for remaining > 0 {
  78. if remaining > initOptionalVarHeaderLength {
  79. pType, err := parseParamType(raw[offset:])
  80. if err != nil {
  81. return fmt.Errorf("%w: %v", errInitChunkParseParamTypeFailed, err)
  82. }
  83. p, err := buildParam(pType, raw[offset:])
  84. if err != nil {
  85. return fmt.Errorf("%w: %v", errInitChunkUnmarshalParam, err)
  86. }
  87. i.params = append(i.params, p)
  88. padding := getPadding(p.length())
  89. offset += p.length() + padding
  90. remaining -= p.length() + padding
  91. } else {
  92. break
  93. }
  94. }
  95. return nil
  96. }
  97. func (i *chunkInitCommon) marshal() ([]byte, error) {
  98. out := make([]byte, initChunkMinLength)
  99. binary.BigEndian.PutUint32(out[0:], i.initiateTag)
  100. binary.BigEndian.PutUint32(out[4:], i.advertisedReceiverWindowCredit)
  101. binary.BigEndian.PutUint16(out[8:], i.numOutboundStreams)
  102. binary.BigEndian.PutUint16(out[10:], i.numInboundStreams)
  103. binary.BigEndian.PutUint32(out[12:], i.initialTSN)
  104. for idx, p := range i.params {
  105. pp, err := p.marshal()
  106. if err != nil {
  107. return nil, fmt.Errorf("%w: %v", errInitAckMarshalParam, err)
  108. }
  109. out = append(out, pp...)
  110. // Chunks (including Type, Length, and Value fields) are padded out
  111. // by the sender with all zero bytes to be a multiple of 4 bytes
  112. // long. This padding MUST NOT be more than 3 bytes in total. The
  113. // Chunk Length value does not include terminating padding of the
  114. // chunk. *However, it does include padding of any variable-length
  115. // parameter except the last parameter in the chunk.* The receiver
  116. // MUST ignore the padding.
  117. if idx != len(i.params)-1 {
  118. out = padByte(out, getPadding(len(pp)))
  119. }
  120. }
  121. return out, nil
  122. }
  123. // String makes chunkInitCommon printable
  124. func (i chunkInitCommon) String() string {
  125. format := `initiateTag: %d
  126. advertisedReceiverWindowCredit: %d
  127. numOutboundStreams: %d
  128. numInboundStreams: %d
  129. initialTSN: %d`
  130. res := fmt.Sprintf(format,
  131. i.initiateTag,
  132. i.advertisedReceiverWindowCredit,
  133. i.numOutboundStreams,
  134. i.numInboundStreams,
  135. i.initialTSN,
  136. )
  137. for i, param := range i.params {
  138. res += fmt.Sprintf("Param %d:\n %s", i, param)
  139. }
  140. return res
  141. }