ODPM 266: Go-libmemif + 2 examples.
[govpp.git] / vendor / github.com / google / gopacket / layers / sctp.go
diff --git a/vendor/github.com/google/gopacket/layers/sctp.go b/vendor/github.com/google/gopacket/layers/sctp.go
new file mode 100644 (file)
index 0000000..511176e
--- /dev/null
@@ -0,0 +1,746 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+package layers
+
+import (
+       "encoding/binary"
+       "errors"
+       "fmt"
+       "hash/crc32"
+
+       "github.com/google/gopacket"
+)
+
+// SCTP contains information on the top level of an SCTP packet.
+type SCTP struct {
+       BaseLayer
+       SrcPort, DstPort SCTPPort
+       VerificationTag  uint32
+       Checksum         uint32
+       sPort, dPort     []byte
+}
+
+// LayerType returns gopacket.LayerTypeSCTP
+func (s *SCTP) LayerType() gopacket.LayerType { return LayerTypeSCTP }
+
+func decodeSCTP(data []byte, p gopacket.PacketBuilder) error {
+       sctp := &SCTP{}
+       err := sctp.DecodeFromBytes(data, p)
+       p.AddLayer(sctp)
+       p.SetTransportLayer(sctp)
+       if err != nil {
+               return err
+       }
+       return p.NextDecoder(sctpChunkTypePrefixDecoder)
+}
+
+var sctpChunkTypePrefixDecoder = gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix)
+
+// TransportFlow returns a flow based on the source and destination SCTP port.
+func (s *SCTP) TransportFlow() gopacket.Flow {
+       return gopacket.NewFlow(EndpointSCTPPort, s.sPort, s.dPort)
+}
+
+func decodeWithSCTPChunkTypePrefix(data []byte, p gopacket.PacketBuilder) error {
+       chunkType := SCTPChunkType(data[0])
+       return chunkType.Decode(data, p)
+}
+
+// SerializeTo is for gopacket.SerializableLayer.
+func (s SCTP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+       bytes, err := b.PrependBytes(12)
+       if err != nil {
+               return err
+       }
+       binary.BigEndian.PutUint16(bytes[0:2], uint16(s.SrcPort))
+       binary.BigEndian.PutUint16(bytes[2:4], uint16(s.DstPort))
+       binary.BigEndian.PutUint32(bytes[4:8], s.VerificationTag)
+       if opts.ComputeChecksums {
+               // Note:  MakeTable(Castagnoli) actually only creates the table once, then
+               // passes back a singleton on every other call, so this shouldn't cause
+               // excessive memory allocation.
+               binary.LittleEndian.PutUint32(bytes[8:12], crc32.Checksum(b.Bytes(), crc32.MakeTable(crc32.Castagnoli)))
+       }
+       return nil
+}
+
+func (sctp *SCTP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+       if len(data) < 12 {
+               return errors.New("Invalid SCTP common header length")
+       }
+       sctp.SrcPort = SCTPPort(binary.BigEndian.Uint16(data[:2]))
+       sctp.sPort = data[:2]
+       sctp.DstPort = SCTPPort(binary.BigEndian.Uint16(data[2:4]))
+       sctp.dPort = data[2:4]
+       sctp.VerificationTag = binary.BigEndian.Uint32(data[4:8])
+       sctp.Checksum = binary.BigEndian.Uint32(data[8:12])
+       sctp.BaseLayer = BaseLayer{data[:12], data[12:]}
+
+       return nil
+}
+
+func (t *SCTP) CanDecode() gopacket.LayerClass {
+       return LayerTypeSCTP
+}
+
+func (t *SCTP) NextLayerType() gopacket.LayerType {
+       return gopacket.LayerTypePayload
+}
+
+// SCTPChunk contains the common fields in all SCTP chunks.
+type SCTPChunk struct {
+       BaseLayer
+       Type   SCTPChunkType
+       Flags  uint8
+       Length uint16
+       // ActualLength is the total length of an SCTP chunk, including padding.
+       // SCTP chunks start and end on 4-byte boundaries.  So if a chunk has a length
+       // of 18, it means that it has data up to and including byte 18, then padding
+       // up to the next 4-byte boundary, 20.  In this case, Length would be 18, and
+       // ActualLength would be 20.
+       ActualLength int
+}
+
+func roundUpToNearest4(i int) int {
+       if i%4 == 0 {
+               return i
+       }
+       return i + 4 - (i % 4)
+}
+
+func decodeSCTPChunk(data []byte) (SCTPChunk, error) {
+       length := binary.BigEndian.Uint16(data[2:4])
+       if length < 4 {
+               return SCTPChunk{}, errors.New("invalid SCTP chunk length")
+       }
+       actual := roundUpToNearest4(int(length))
+       ct := SCTPChunkType(data[0])
+
+       // For SCTP Data, use a separate layer for the payload
+       delta := 0
+       if ct == SCTPChunkTypeData {
+               delta = int(actual) - int(length)
+               actual = 16
+       }
+
+       return SCTPChunk{
+               Type:         ct,
+               Flags:        data[1],
+               Length:       length,
+               ActualLength: actual,
+               BaseLayer:    BaseLayer{data[:actual], data[actual : len(data)-delta]},
+       }, nil
+}
+
+// SCTPParameter is a TLV parameter inside a SCTPChunk.
+type SCTPParameter struct {
+       Type         uint16
+       Length       uint16
+       ActualLength int
+       Value        []byte
+}
+
+func decodeSCTPParameter(data []byte) SCTPParameter {
+       length := binary.BigEndian.Uint16(data[2:4])
+       return SCTPParameter{
+               Type:         binary.BigEndian.Uint16(data[0:2]),
+               Length:       length,
+               Value:        data[4:length],
+               ActualLength: roundUpToNearest4(int(length)),
+       }
+}
+
+func (p SCTPParameter) Bytes() []byte {
+       length := 4 + len(p.Value)
+       data := make([]byte, roundUpToNearest4(length))
+       binary.BigEndian.PutUint16(data[0:2], p.Type)
+       binary.BigEndian.PutUint16(data[2:4], uint16(length))
+       copy(data[4:], p.Value)
+       return data
+}
+
+// SCTPUnknownChunkType is the layer type returned when we don't recognize the
+// chunk type.  Since there's a length in a known location, we can skip over
+// it even if we don't know what it is, and continue parsing the rest of the
+// chunks.  This chunk is stored as an ErrorLayer in the packet.
+type SCTPUnknownChunkType struct {
+       SCTPChunk
+       bytes []byte
+}
+
+func decodeSCTPChunkTypeUnknown(data []byte, p gopacket.PacketBuilder) error {
+       chunk, err := decodeSCTPChunk(data)
+       if err != nil {
+               return err
+       }
+       sc := &SCTPUnknownChunkType{SCTPChunk: chunk}
+       sc.bytes = data[:sc.ActualLength]
+       p.AddLayer(sc)
+       p.SetErrorLayer(sc)
+       return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix))
+}
+
+// SerializeTo is for gopacket.SerializableLayer.
+func (s SCTPUnknownChunkType) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+       bytes, err := b.PrependBytes(s.ActualLength)
+       if err != nil {
+               return err
+       }
+       copy(bytes, s.bytes)
+       return nil
+}
+
+// LayerType returns gopacket.LayerTypeSCTPUnknownChunkType.
+func (s *SCTPUnknownChunkType) LayerType() gopacket.LayerType { return LayerTypeSCTPUnknownChunkType }
+
+// Payload returns all bytes in this header, including the decoded Type, Length,
+// and Flags.
+func (s *SCTPUnknownChunkType) Payload() []byte { return s.bytes }
+
+// Error implements ErrorLayer.
+func (s *SCTPUnknownChunkType) Error() error {
+       return fmt.Errorf("No decode method available for SCTP chunk type %s", s.Type)
+}
+
+// SCTPData is the SCTP Data chunk layer.
+type SCTPData struct {
+       SCTPChunk
+       Unordered, BeginFragment, EndFragment bool
+       TSN                                   uint32
+       StreamId                              uint16
+       StreamSequence                        uint16
+       PayloadProtocol                       SCTPPayloadProtocol
+}
+
+// LayerType returns gopacket.LayerTypeSCTPData.
+func (s *SCTPData) LayerType() gopacket.LayerType { return LayerTypeSCTPData }
+
+// SCTPPayloadProtocol represents a payload protocol
+type SCTPPayloadProtocol uint32
+
+// SCTPPayloadProtocol constonts from http://www.iana.org/assignments/sctp-parameters/sctp-parameters.xhtml
+const (
+       SCTPProtocolReserved  SCTPPayloadProtocol = 0
+       SCTPPayloadUIA                            = 1
+       SCTPPayloadM2UA                           = 2
+       SCTPPayloadM3UA                           = 3
+       SCTPPayloadSUA                            = 4
+       SCTPPayloadM2PA                           = 5
+       SCTPPayloadV5UA                           = 6
+       SCTPPayloadH248                           = 7
+       SCTPPayloadBICC                           = 8
+       SCTPPayloadTALI                           = 9
+       SCTPPayloadDUA                            = 10
+       SCTPPayloadASAP                           = 11
+       SCTPPayloadENRP                           = 12
+       SCTPPayloadH323                           = 13
+       SCTPPayloadQIPC                           = 14
+       SCTPPayloadSIMCO                          = 15
+       SCTPPayloadDDPSegment                     = 16
+       SCTPPayloadDDPStream                      = 17
+       SCTPPayloadS1AP                           = 18
+)
+
+func (p SCTPPayloadProtocol) String() string {
+       switch p {
+       case SCTPProtocolReserved:
+               return "Reserved"
+       case SCTPPayloadUIA:
+               return "UIA"
+       case SCTPPayloadM2UA:
+               return "M2UA"
+       case SCTPPayloadM3UA:
+               return "M3UA"
+       case SCTPPayloadSUA:
+               return "SUA"
+       case SCTPPayloadM2PA:
+               return "M2PA"
+       case SCTPPayloadV5UA:
+               return "V5UA"
+       case SCTPPayloadH248:
+               return "H.248"
+       case SCTPPayloadBICC:
+               return "BICC"
+       case SCTPPayloadTALI:
+               return "TALI"
+       case SCTPPayloadDUA:
+               return "DUA"
+       case SCTPPayloadASAP:
+               return "ASAP"
+       case SCTPPayloadENRP:
+               return "ENRP"
+       case SCTPPayloadH323:
+               return "H.323"
+       case SCTPPayloadQIPC:
+               return "QIPC"
+       case SCTPPayloadSIMCO:
+               return "SIMCO"
+       case SCTPPayloadDDPSegment:
+               return "DDPSegment"
+       case SCTPPayloadDDPStream:
+               return "DDPStream"
+       case SCTPPayloadS1AP:
+               return "S1AP"
+       }
+       return fmt.Sprintf("Unknown(%d)", p)
+}
+
+func decodeSCTPData(data []byte, p gopacket.PacketBuilder) error {
+       chunk, err := decodeSCTPChunk(data)
+       if err != nil {
+               return err
+       }
+       sc := &SCTPData{
+               SCTPChunk:       chunk,
+               Unordered:       data[1]&0x4 != 0,
+               BeginFragment:   data[1]&0x2 != 0,
+               EndFragment:     data[1]&0x1 != 0,
+               TSN:             binary.BigEndian.Uint32(data[4:8]),
+               StreamId:        binary.BigEndian.Uint16(data[8:10]),
+               StreamSequence:  binary.BigEndian.Uint16(data[10:12]),
+               PayloadProtocol: SCTPPayloadProtocol(binary.BigEndian.Uint32(data[12:16])),
+       }
+       // Length is the length in bytes of the data, INCLUDING the 16-byte header.
+       p.AddLayer(sc)
+       return p.NextDecoder(gopacket.LayerTypePayload)
+}
+
+// SerializeTo is for gopacket.SerializableLayer.
+func (sc SCTPData) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+       payload := b.Bytes()
+       // Pad the payload to a 32 bit boundary
+       if rem := len(payload) % 4; rem != 0 {
+               b.AppendBytes(4 - rem)
+       }
+       length := 16
+       bytes, err := b.PrependBytes(length)
+       if err != nil {
+               return err
+       }
+       bytes[0] = uint8(sc.Type)
+       flags := uint8(0)
+       if sc.Unordered {
+               flags |= 0x4
+       }
+       if sc.BeginFragment {
+               flags |= 0x2
+       }
+       if sc.EndFragment {
+               flags |= 0x1
+       }
+       bytes[1] = flags
+       binary.BigEndian.PutUint16(bytes[2:4], uint16(length+len(payload)))
+       binary.BigEndian.PutUint32(bytes[4:8], sc.TSN)
+       binary.BigEndian.PutUint16(bytes[8:10], sc.StreamId)
+       binary.BigEndian.PutUint16(bytes[10:12], sc.StreamSequence)
+       binary.BigEndian.PutUint32(bytes[12:16], uint32(sc.PayloadProtocol))
+       return nil
+}
+
+// SCTPInitParameter is a parameter for an SCTP Init or InitAck packet.
+type SCTPInitParameter SCTPParameter
+
+// SCTPInit is used as the return value for both SCTPInit and SCTPInitAck
+// messages.
+type SCTPInit struct {
+       SCTPChunk
+       InitiateTag                     uint32
+       AdvertisedReceiverWindowCredit  uint32
+       OutboundStreams, InboundStreams uint16
+       InitialTSN                      uint32
+       Parameters                      []SCTPInitParameter
+}
+
+// LayerType returns either gopacket.LayerTypeSCTPInit or gopacket.LayerTypeSCTPInitAck.
+func (sc *SCTPInit) LayerType() gopacket.LayerType {
+       if sc.Type == SCTPChunkTypeInitAck {
+               return LayerTypeSCTPInitAck
+       }
+       // sc.Type == SCTPChunkTypeInit
+       return LayerTypeSCTPInit
+}
+
+func decodeSCTPInit(data []byte, p gopacket.PacketBuilder) error {
+       chunk, err := decodeSCTPChunk(data)
+       if err != nil {
+               return err
+       }
+       sc := &SCTPInit{
+               SCTPChunk:                      chunk,
+               InitiateTag:                    binary.BigEndian.Uint32(data[4:8]),
+               AdvertisedReceiverWindowCredit: binary.BigEndian.Uint32(data[8:12]),
+               OutboundStreams:                binary.BigEndian.Uint16(data[12:14]),
+               InboundStreams:                 binary.BigEndian.Uint16(data[14:16]),
+               InitialTSN:                     binary.BigEndian.Uint32(data[16:20]),
+       }
+       paramData := data[20:sc.ActualLength]
+       for len(paramData) > 0 {
+               p := SCTPInitParameter(decodeSCTPParameter(paramData))
+               paramData = paramData[p.ActualLength:]
+               sc.Parameters = append(sc.Parameters, p)
+       }
+       p.AddLayer(sc)
+       return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix))
+}
+
+// SerializeTo is for gopacket.SerializableLayer.
+func (sc SCTPInit) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+       var payload []byte
+       for _, param := range sc.Parameters {
+               payload = append(payload, SCTPParameter(param).Bytes()...)
+       }
+       length := 20 + len(payload)
+       bytes, err := b.PrependBytes(roundUpToNearest4(length))
+       if err != nil {
+               return err
+       }
+       bytes[0] = uint8(sc.Type)
+       bytes[1] = sc.Flags
+       binary.BigEndian.PutUint16(bytes[2:4], uint16(length))
+       binary.BigEndian.PutUint32(bytes[4:8], sc.InitiateTag)
+       binary.BigEndian.PutUint32(bytes[8:12], sc.AdvertisedReceiverWindowCredit)
+       binary.BigEndian.PutUint16(bytes[12:14], sc.OutboundStreams)
+       binary.BigEndian.PutUint16(bytes[14:16], sc.InboundStreams)
+       binary.BigEndian.PutUint32(bytes[16:20], sc.InitialTSN)
+       copy(bytes[20:], payload)
+       return nil
+}
+
+// SCTPSack is the SCTP Selective ACK chunk layer.
+type SCTPSack struct {
+       SCTPChunk
+       CumulativeTSNAck               uint32
+       AdvertisedReceiverWindowCredit uint32
+       NumGapACKs, NumDuplicateTSNs   uint16
+       GapACKs                        []uint16
+       DuplicateTSNs                  []uint32
+}
+
+// LayerType return LayerTypeSCTPSack
+func (sc *SCTPSack) LayerType() gopacket.LayerType {
+       return LayerTypeSCTPSack
+}
+
+func decodeSCTPSack(data []byte, p gopacket.PacketBuilder) error {
+       chunk, err := decodeSCTPChunk(data)
+       if err != nil {
+               return err
+       }
+       sc := &SCTPSack{
+               SCTPChunk:                      chunk,
+               CumulativeTSNAck:               binary.BigEndian.Uint32(data[4:8]),
+               AdvertisedReceiverWindowCredit: binary.BigEndian.Uint32(data[8:12]),
+               NumGapACKs:                     binary.BigEndian.Uint16(data[12:14]),
+               NumDuplicateTSNs:               binary.BigEndian.Uint16(data[14:16]),
+       }
+       // We maximize gapAcks and dupTSNs here so we're not allocating tons
+       // of memory based on a user-controlable field.  Our maximums are not exact,
+       // but should give us sane defaults... we'll still hit slice boundaries and
+       // fail if the user-supplied values are too high (in the for loops below), but
+       // the amount of memory we'll have allocated because of that should be small
+       // (< sc.ActualLength)
+       gapAcks := sc.SCTPChunk.ActualLength / 2
+       dupTSNs := (sc.SCTPChunk.ActualLength - gapAcks*2) / 4
+       if gapAcks > int(sc.NumGapACKs) {
+               gapAcks = int(sc.NumGapACKs)
+       }
+       if dupTSNs > int(sc.NumDuplicateTSNs) {
+               dupTSNs = int(sc.NumDuplicateTSNs)
+       }
+       sc.GapACKs = make([]uint16, 0, gapAcks)
+       sc.DuplicateTSNs = make([]uint32, 0, dupTSNs)
+       bytesRemaining := data[16:]
+       for i := 0; i < int(sc.NumGapACKs); i++ {
+               sc.GapACKs = append(sc.GapACKs, binary.BigEndian.Uint16(bytesRemaining[:2]))
+               bytesRemaining = bytesRemaining[2:]
+       }
+       for i := 0; i < int(sc.NumDuplicateTSNs); i++ {
+               sc.DuplicateTSNs = append(sc.DuplicateTSNs, binary.BigEndian.Uint32(bytesRemaining[:4]))
+               bytesRemaining = bytesRemaining[4:]
+       }
+       p.AddLayer(sc)
+       return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix))
+}
+
+// SerializeTo is for gopacket.SerializableLayer.
+func (sc SCTPSack) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+       length := 16 + 2*len(sc.GapACKs) + 4*len(sc.DuplicateTSNs)
+       bytes, err := b.PrependBytes(roundUpToNearest4(length))
+       if err != nil {
+               return err
+       }
+       bytes[0] = uint8(sc.Type)
+       bytes[1] = sc.Flags
+       binary.BigEndian.PutUint16(bytes[2:4], uint16(length))
+       binary.BigEndian.PutUint32(bytes[4:8], sc.CumulativeTSNAck)
+       binary.BigEndian.PutUint32(bytes[8:12], sc.AdvertisedReceiverWindowCredit)
+       binary.BigEndian.PutUint16(bytes[12:14], uint16(len(sc.GapACKs)))
+       binary.BigEndian.PutUint16(bytes[14:16], uint16(len(sc.DuplicateTSNs)))
+       for i, v := range sc.GapACKs {
+               binary.BigEndian.PutUint16(bytes[16+i*2:], v)
+       }
+       offset := 16 + 2*len(sc.GapACKs)
+       for i, v := range sc.DuplicateTSNs {
+               binary.BigEndian.PutUint32(bytes[offset+i*4:], v)
+       }
+       return nil
+}
+
+// SCTPHeartbeatParameter is the parameter type used by SCTP heartbeat and
+// heartbeat ack layers.
+type SCTPHeartbeatParameter SCTPParameter
+
+// SCTPHeartbeat is the SCTP heartbeat layer, also used for heatbeat ack.
+type SCTPHeartbeat struct {
+       SCTPChunk
+       Parameters []SCTPHeartbeatParameter
+}
+
+// LayerType returns gopacket.LayerTypeSCTPHeartbeat.
+func (sc *SCTPHeartbeat) LayerType() gopacket.LayerType {
+       if sc.Type == SCTPChunkTypeHeartbeatAck {
+               return LayerTypeSCTPHeartbeatAck
+       }
+       // sc.Type == SCTPChunkTypeHeartbeat
+       return LayerTypeSCTPHeartbeat
+}
+
+func decodeSCTPHeartbeat(data []byte, p gopacket.PacketBuilder) error {
+       chunk, err := decodeSCTPChunk(data)
+       if err != nil {
+               return err
+       }
+       sc := &SCTPHeartbeat{
+               SCTPChunk: chunk,
+       }
+       paramData := data[4:sc.Length]
+       for len(paramData) > 0 {
+               p := SCTPHeartbeatParameter(decodeSCTPParameter(paramData))
+               paramData = paramData[p.ActualLength:]
+               sc.Parameters = append(sc.Parameters, p)
+       }
+       p.AddLayer(sc)
+       return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix))
+}
+
+// SerializeTo is for gopacket.SerializableLayer.
+func (sc SCTPHeartbeat) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+       var payload []byte
+       for _, param := range sc.Parameters {
+               payload = append(payload, SCTPParameter(param).Bytes()...)
+       }
+       length := 4 + len(payload)
+
+       bytes, err := b.PrependBytes(roundUpToNearest4(length))
+       if err != nil {
+               return err
+       }
+       bytes[0] = uint8(sc.Type)
+       bytes[1] = sc.Flags
+       binary.BigEndian.PutUint16(bytes[2:4], uint16(length))
+       copy(bytes[4:], payload)
+       return nil
+}
+
+// SCTPErrorParameter is the parameter type used by SCTP Abort and Error layers.
+type SCTPErrorParameter SCTPParameter
+
+// SCTPError is the SCTP error layer, also used for SCTP aborts.
+type SCTPError struct {
+       SCTPChunk
+       Parameters []SCTPErrorParameter
+}
+
+// LayerType returns LayerTypeSCTPAbort or LayerTypeSCTPError.
+func (sc *SCTPError) LayerType() gopacket.LayerType {
+       if sc.Type == SCTPChunkTypeAbort {
+               return LayerTypeSCTPAbort
+       }
+       // sc.Type == SCTPChunkTypeError
+       return LayerTypeSCTPError
+}
+
+func decodeSCTPError(data []byte, p gopacket.PacketBuilder) error {
+       // remarkably similar to decodeSCTPHeartbeat ;)
+       chunk, err := decodeSCTPChunk(data)
+       if err != nil {
+               return err
+       }
+       sc := &SCTPError{
+               SCTPChunk: chunk,
+       }
+       paramData := data[4:sc.Length]
+       for len(paramData) > 0 {
+               p := SCTPErrorParameter(decodeSCTPParameter(paramData))
+               paramData = paramData[p.ActualLength:]
+               sc.Parameters = append(sc.Parameters, p)
+       }
+       p.AddLayer(sc)
+       return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix))
+}
+
+// SerializeTo is for gopacket.SerializableLayer.
+func (sc SCTPError) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+       var payload []byte
+       for _, param := range sc.Parameters {
+               payload = append(payload, SCTPParameter(param).Bytes()...)
+       }
+       length := 4 + len(payload)
+
+       bytes, err := b.PrependBytes(roundUpToNearest4(length))
+       if err != nil {
+               return err
+       }
+       bytes[0] = uint8(sc.Type)
+       bytes[1] = sc.Flags
+       binary.BigEndian.PutUint16(bytes[2:4], uint16(length))
+       copy(bytes[4:], payload)
+       return nil
+}
+
+// SCTPShutdown is the SCTP shutdown layer.
+type SCTPShutdown struct {
+       SCTPChunk
+       CumulativeTSNAck uint32
+}
+
+// LayerType returns gopacket.LayerTypeSCTPShutdown.
+func (sc *SCTPShutdown) LayerType() gopacket.LayerType { return LayerTypeSCTPShutdown }
+
+func decodeSCTPShutdown(data []byte, p gopacket.PacketBuilder) error {
+       chunk, err := decodeSCTPChunk(data)
+       if err != nil {
+               return err
+       }
+       sc := &SCTPShutdown{
+               SCTPChunk:        chunk,
+               CumulativeTSNAck: binary.BigEndian.Uint32(data[4:8]),
+       }
+       p.AddLayer(sc)
+       return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix))
+}
+
+// SerializeTo is for gopacket.SerializableLayer.
+func (sc SCTPShutdown) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+       bytes, err := b.PrependBytes(8)
+       if err != nil {
+               return err
+       }
+       bytes[0] = uint8(sc.Type)
+       bytes[1] = sc.Flags
+       binary.BigEndian.PutUint16(bytes[2:4], 8)
+       binary.BigEndian.PutUint32(bytes[4:8], sc.CumulativeTSNAck)
+       return nil
+}
+
+// SCTPShutdownAck is the SCTP shutdown layer.
+type SCTPShutdownAck struct {
+       SCTPChunk
+}
+
+// LayerType returns gopacket.LayerTypeSCTPShutdownAck.
+func (sc *SCTPShutdownAck) LayerType() gopacket.LayerType { return LayerTypeSCTPShutdownAck }
+
+func decodeSCTPShutdownAck(data []byte, p gopacket.PacketBuilder) error {
+       chunk, err := decodeSCTPChunk(data)
+       if err != nil {
+               return err
+       }
+       sc := &SCTPShutdownAck{
+               SCTPChunk: chunk,
+       }
+       p.AddLayer(sc)
+       return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix))
+}
+
+// SerializeTo is for gopacket.SerializableLayer.
+func (sc SCTPShutdownAck) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+       bytes, err := b.PrependBytes(4)
+       if err != nil {
+               return err
+       }
+       bytes[0] = uint8(sc.Type)
+       bytes[1] = sc.Flags
+       binary.BigEndian.PutUint16(bytes[2:4], 4)
+       return nil
+}
+
+// SCTPCookieEcho is the SCTP Cookie Echo layer.
+type SCTPCookieEcho struct {
+       SCTPChunk
+       Cookie []byte
+}
+
+// LayerType returns gopacket.LayerTypeSCTPCookieEcho.
+func (sc *SCTPCookieEcho) LayerType() gopacket.LayerType { return LayerTypeSCTPCookieEcho }
+
+func decodeSCTPCookieEcho(data []byte, p gopacket.PacketBuilder) error {
+       chunk, err := decodeSCTPChunk(data)
+       if err != nil {
+               return err
+       }
+       sc := &SCTPCookieEcho{
+               SCTPChunk: chunk,
+       }
+       sc.Cookie = data[4:sc.Length]
+       p.AddLayer(sc)
+       return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix))
+}
+
+// SerializeTo is for gopacket.SerializableLayer.
+func (sc SCTPCookieEcho) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+       length := 4 + len(sc.Cookie)
+       bytes, err := b.PrependBytes(roundUpToNearest4(length))
+       if err != nil {
+               return err
+       }
+       bytes[0] = uint8(sc.Type)
+       bytes[1] = sc.Flags
+       binary.BigEndian.PutUint16(bytes[2:4], uint16(length))
+       copy(bytes[4:], sc.Cookie)
+       return nil
+}
+
+// This struct is used by all empty SCTP chunks (currently CookieAck and
+// ShutdownComplete).
+type SCTPEmptyLayer struct {
+       SCTPChunk
+}
+
+// LayerType returns either gopacket.LayerTypeSCTPShutdownComplete or
+// LayerTypeSCTPCookieAck.
+func (sc *SCTPEmptyLayer) LayerType() gopacket.LayerType {
+       if sc.Type == SCTPChunkTypeShutdownComplete {
+               return LayerTypeSCTPShutdownComplete
+       }
+       // sc.Type == SCTPChunkTypeCookieAck
+       return LayerTypeSCTPCookieAck
+}
+
+func decodeSCTPEmptyLayer(data []byte, p gopacket.PacketBuilder) error {
+       chunk, err := decodeSCTPChunk(data)
+       if err != nil {
+               return err
+       }
+       sc := &SCTPEmptyLayer{
+               SCTPChunk: chunk,
+       }
+       p.AddLayer(sc)
+       return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix))
+}
+
+// SerializeTo is for gopacket.SerializableLayer.
+func (sc SCTPEmptyLayer) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+       bytes, err := b.PrependBytes(4)
+       if err != nil {
+               return err
+       }
+       bytes[0] = uint8(sc.Type)
+       bytes[1] = sc.Flags
+       binary.BigEndian.PutUint16(bytes[2:4], 4)
+       return nil
+}