mirror of
https://github.com/pion/webrtc.git
synced 2025-11-03 09:40:59 +08:00
* Make sure errors in SCTP make it all the way back * Move structs into files with proper names in SCTP * Add String() for AssociationState enum * Add callbacks to Association to that we can handle DATA in the DataChannel package and send outbound SCTP
113 lines
3.5 KiB
Go
113 lines
3.5 KiB
Go
package sctp
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"hash/crc32"
|
|
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
/*
|
|
Packet represents an SCTP packet, defined in https://tools.ietf.org/html/rfc4960#section-3
|
|
An SCTP packet is composed of a common header and chunks. A chunk
|
|
contains either control information or user data.
|
|
|
|
|
|
SCTP Packet Format
|
|
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
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
| Common Header |
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
| Chunk #1 |
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
| ... |
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
| Chunk #n |
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
|
|
SCTP Common Header Format
|
|
|
|
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
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
| Source Port Number | Destination Port Number |
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
| Verification Tag |
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
| Checksum |
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
|
|
*/
|
|
type Packet struct {
|
|
SourcePort uint16
|
|
DestinationPort uint16
|
|
VerificationTag uint32
|
|
Chunks []Chunk
|
|
}
|
|
|
|
const (
|
|
packetHeaderSize = 12
|
|
)
|
|
|
|
// Unmarshal populates a Packet from a raw buffer
|
|
func (p *Packet) Unmarshal(raw []byte) error {
|
|
if len(raw) < packetHeaderSize {
|
|
return errors.Errorf("raw only %d bytes, %d is the minimum length for a SCTP packet", len(raw), packetHeaderSize)
|
|
}
|
|
|
|
p.SourcePort = binary.BigEndian.Uint16(raw[0:])
|
|
p.DestinationPort = binary.BigEndian.Uint16(raw[2:])
|
|
p.VerificationTag = binary.BigEndian.Uint32(raw[4:])
|
|
|
|
offset := packetHeaderSize
|
|
for {
|
|
// Exact match, no more chunks
|
|
if offset == len(raw) {
|
|
break
|
|
} else if offset+chunkHeaderSize > len(raw) {
|
|
return errors.Errorf("Unable to parse SCTP chunk, not enough data for complete header: offset %d remaining %d", offset, len(raw))
|
|
}
|
|
|
|
var c Chunk
|
|
switch ChunkType(raw[offset]) {
|
|
case INIT:
|
|
c = &Init{}
|
|
default:
|
|
return errors.Errorf("Failed to unmarshal, contains unknown chunk type %d", raw[offset])
|
|
}
|
|
|
|
if err := c.Unmarshal(raw[offset:]); err != nil {
|
|
return err
|
|
}
|
|
p.Chunks = append(p.Chunks, c)
|
|
chunkValuePadding := c.valueLength() % 4
|
|
offset += chunkHeaderSize + c.valueLength() + chunkValuePadding
|
|
}
|
|
theirChecksum := binary.LittleEndian.Uint32(raw[8:])
|
|
ourChecksum := generatePacketChecksum(raw)
|
|
if theirChecksum != ourChecksum {
|
|
return errors.Errorf("Checksum mismatch theirs: %d ours: %d", theirChecksum, ourChecksum)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Marshal populates a raw buffer from a packet
|
|
func (p *Packet) Marshal() ([]byte, error) {
|
|
return nil, errors.Errorf("Unimplemented")
|
|
}
|
|
|
|
func generatePacketChecksum(raw []byte) uint32 {
|
|
rawCopy := make([]byte, len(raw))
|
|
copy(rawCopy, raw)
|
|
|
|
// Clear existing checksum
|
|
for offset := 8; offset <= 11; offset++ {
|
|
rawCopy[offset] = 0x00
|
|
}
|
|
|
|
return crc32.Checksum(rawCopy, crc32.MakeTable(crc32.Castagnoli))
|
|
}
|