summaryrefslogtreecommitdiff
path: root/vendor/github.com/pion/sctp/chunk_init_common.go
blob: f64be7865be1c9809634e65ba8ea12332932388b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
package sctp

import (
	"encoding/binary"
	"fmt"

	"github.com/pkg/errors"
)

/*
chunkInitCommon represents an SCTP Chunk body of type INIT and INIT ACK

 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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|   Type = 1    |  Chunk Flags  |      Chunk Length             |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                         Initiate Tag                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           Advertised Receiver Window Credit (a_rwnd)          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  Number of Outbound Streams   |  Number of Inbound Streams    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                          Initial TSN                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
|              Optional/Variable-Length Parameters              |
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

The INIT chunk contains the following parameters.  Unless otherwise
noted, each parameter MUST only be included once in the INIT chunk.

Fixed Parameters                     Status
----------------------------------------------
Initiate Tag                        Mandatory
Advertised Receiver Window Credit   Mandatory
Number of Outbound Streams          Mandatory
Number of Inbound Streams           Mandatory
Initial TSN                         Mandatory
*/

type chunkInitCommon struct {
	initiateTag                    uint32
	advertisedReceiverWindowCredit uint32
	numOutboundStreams             uint16
	numInboundStreams              uint16
	initialTSN                     uint32
	params                         []param
}

const (
	initChunkMinLength          = 16
	initOptionalVarHeaderLength = 4
)

func (i *chunkInitCommon) unmarshal(raw []byte) error {
	i.initiateTag = binary.BigEndian.Uint32(raw[0:])
	i.advertisedReceiverWindowCredit = binary.BigEndian.Uint32(raw[4:])
	i.numOutboundStreams = binary.BigEndian.Uint16(raw[8:])
	i.numInboundStreams = binary.BigEndian.Uint16(raw[10:])
	i.initialTSN = binary.BigEndian.Uint32(raw[12:])

	// https://tools.ietf.org/html/rfc4960#section-3.2.1
	//
	// Chunk values of SCTP control chunks consist of a chunk-type-specific
	// header of required fields, followed by zero or more parameters.  The
	// optional and variable-length parameters contained in a chunk are
	// defined in a Type-Length-Value format as shown below.
	//
	// 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
	// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	// |          Parameter Type       |       Parameter Length        |
	// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	// |                                                               |
	// |                       Parameter Value                         |
	// |                                                               |
	// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

	offset := initChunkMinLength
	remaining := len(raw) - offset
	for remaining > 0 {
		if remaining > initOptionalVarHeaderLength {
			pType, err := parseParamType(raw[offset:])
			if err != nil {
				return errors.Wrap(err, "failed to parse param type")
			}
			p, err := buildParam(pType, raw[offset:])
			if err != nil {
				return errors.Wrap(err, "Failed unmarshalling param in Init Chunk")
			}
			i.params = append(i.params, p)
			padding := getPadding(p.length())
			offset += p.length() + padding
			remaining -= p.length() + padding
		} else {
			break
		}
	}

	return nil
}

func (i *chunkInitCommon) marshal() ([]byte, error) {
	out := make([]byte, initChunkMinLength)
	binary.BigEndian.PutUint32(out[0:], i.initiateTag)
	binary.BigEndian.PutUint32(out[4:], i.advertisedReceiverWindowCredit)
	binary.BigEndian.PutUint16(out[8:], i.numOutboundStreams)
	binary.BigEndian.PutUint16(out[10:], i.numInboundStreams)
	binary.BigEndian.PutUint32(out[12:], i.initialTSN)
	for idx, p := range i.params {
		pp, err := p.marshal()
		if err != nil {
			return nil, errors.Wrap(err, "Unable to marshal parameter for INIT/INITACK")
		}

		out = append(out, pp...)

		// Chunks (including Type, Length, and Value fields) are padded out
		// by the sender with all zero bytes to be a multiple of 4 bytes
		// long.  This padding MUST NOT be more than 3 bytes in total.  The
		// Chunk Length value does not include terminating padding of the
		// chunk.  *However, it does include padding of any variable-length
		// parameter except the last parameter in the chunk.*  The receiver
		// MUST ignore the padding.
		if idx != len(i.params)-1 {
			out = padByte(out, getPadding(len(pp)))
		}
	}

	return out, nil
}

// String makes chunkInitCommon printable
func (i chunkInitCommon) String() string {
	format := `initiateTag: %d
	advertisedReceiverWindowCredit: %d
	numOutboundStreams: %d
	numInboundStreams: %d
	initialTSN: %d`

	res := fmt.Sprintf(format,
		i.initiateTag,
		i.advertisedReceiverWindowCredit,
		i.numOutboundStreams,
		i.numInboundStreams,
		i.initialTSN,
	)

	for i, param := range i.params {
		res += fmt.Sprintf("Param %d:\n %s", i, param)
	}
	return res
}