1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Support SCTP port mapping

Signed-off-by: Wataru Ishida <ishida.wataru@lab.ntt.co.jp>
Signed-off-by: Akihiro Suda <suda.akihiro@lab.ntt.co.jp>
This commit is contained in:
Wataru Ishida 2017-06-13 05:29:56 +00:00 committed by Akihiro Suda
parent 92888febdb
commit 2120ed2363
20 changed files with 1666 additions and 180 deletions

View file

@ -1,6 +1,5 @@
// Code generated by protoc-gen-gogo.
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: agent.proto
// DO NOT EDIT!
/*
Package libnetwork is a generated protocol buffer package.
@ -20,9 +19,6 @@ import math "math"
import _ "github.com/gogo/protobuf/gogoproto"
import strings "strings"
import github_com_gogo_protobuf_proto "github.com/gogo/protobuf/proto"
import sort "sort"
import strconv "strconv"
import reflect "reflect"
import io "io"
@ -34,22 +30,27 @@ var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
const _ = proto.GoGoProtoPackageIsVersion1
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
type PortConfig_Protocol int32
const (
ProtocolTCP PortConfig_Protocol = 0
ProtocolUDP PortConfig_Protocol = 1
ProtocolTCP PortConfig_Protocol = 0
ProtocolUDP PortConfig_Protocol = 1
ProtocolSCTP PortConfig_Protocol = 2
)
var PortConfig_Protocol_name = map[int32]string{
0: "TCP",
1: "UDP",
2: "SCTP",
}
var PortConfig_Protocol_value = map[string]int32{
"TCP": 0,
"UDP": 1,
"TCP": 0,
"UDP": 1,
"SCTP": 2,
}
func (x PortConfig_Protocol) String() string {
@ -60,7 +61,7 @@ func (PortConfig_Protocol) EnumDescriptor() ([]byte, []int) { return fileDescrip
// EndpointRecord specifies all the endpoint specific information that
// needs to gossiped to nodes participating in the network.
type EndpointRecord struct {
// Name of the endpoint
// Name of the container
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
// Service name of the service to which this endpoint belongs.
ServiceName string `protobuf:"bytes,2,opt,name=service_name,json=serviceName,proto3" json:"service_name,omitempty"`
@ -82,6 +83,41 @@ func (m *EndpointRecord) Reset() { *m = EndpointRecord{} }
func (*EndpointRecord) ProtoMessage() {}
func (*EndpointRecord) Descriptor() ([]byte, []int) { return fileDescriptorAgent, []int{0} }
func (m *EndpointRecord) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *EndpointRecord) GetServiceName() string {
if m != nil {
return m.ServiceName
}
return ""
}
func (m *EndpointRecord) GetServiceID() string {
if m != nil {
return m.ServiceID
}
return ""
}
func (m *EndpointRecord) GetVirtualIP() string {
if m != nil {
return m.VirtualIP
}
return ""
}
func (m *EndpointRecord) GetEndpointIP() string {
if m != nil {
return m.EndpointIP
}
return ""
}
func (m *EndpointRecord) GetIngressPorts() []*PortConfig {
if m != nil {
return m.IngressPorts
@ -89,6 +125,20 @@ func (m *EndpointRecord) GetIngressPorts() []*PortConfig {
return nil
}
func (m *EndpointRecord) GetAliases() []string {
if m != nil {
return m.Aliases
}
return nil
}
func (m *EndpointRecord) GetTaskAliases() []string {
if m != nil {
return m.TaskAliases
}
return nil
}
// PortConfig specifies an exposed port which can be
// addressed using the given name. This can be later queried
// using a service discovery api or a DNS SRV query. The node
@ -115,6 +165,34 @@ func (m *PortConfig) Reset() { *m = PortConfig{} }
func (*PortConfig) ProtoMessage() {}
func (*PortConfig) Descriptor() ([]byte, []int) { return fileDescriptorAgent, []int{1} }
func (m *PortConfig) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *PortConfig) GetProtocol() PortConfig_Protocol {
if m != nil {
return m.Protocol
}
return ProtocolTCP
}
func (m *PortConfig) GetTargetPort() uint32 {
if m != nil {
return m.TargetPort
}
return 0
}
func (m *PortConfig) GetPublishedPort() uint32 {
if m != nil {
return m.PublishedPort
}
return 0
}
func init() {
proto.RegisterType((*EndpointRecord)(nil), "libnetwork.EndpointRecord")
proto.RegisterType((*PortConfig)(nil), "libnetwork.PortConfig")
@ -160,74 +238,57 @@ func valueToGoStringAgent(v interface{}, typ string) string {
pv := reflect.Indirect(rv).Interface()
return fmt.Sprintf("func(v %v) *%v { return &v } ( %#v )", typ, typ, pv)
}
func extensionToGoStringAgent(e map[int32]github_com_gogo_protobuf_proto.Extension) string {
if e == nil {
return "nil"
}
s := "map[int32]proto.Extension{"
keys := make([]int, 0, len(e))
for k := range e {
keys = append(keys, int(k))
}
sort.Ints(keys)
ss := []string{}
for _, k := range keys {
ss = append(ss, strconv.Itoa(k)+": "+e[int32(k)].GoString())
}
s += strings.Join(ss, ",") + "}"
return s
}
func (m *EndpointRecord) Marshal() (data []byte, err error) {
func (m *EndpointRecord) Marshal() (dAtA []byte, err error) {
size := m.Size()
data = make([]byte, size)
n, err := m.MarshalTo(data)
dAtA = make([]byte, size)
n, err := m.MarshalTo(dAtA)
if err != nil {
return nil, err
}
return data[:n], nil
return dAtA[:n], nil
}
func (m *EndpointRecord) MarshalTo(data []byte) (int, error) {
func (m *EndpointRecord) MarshalTo(dAtA []byte) (int, error) {
var i int
_ = i
var l int
_ = l
if len(m.Name) > 0 {
data[i] = 0xa
dAtA[i] = 0xa
i++
i = encodeVarintAgent(data, i, uint64(len(m.Name)))
i += copy(data[i:], m.Name)
i = encodeVarintAgent(dAtA, i, uint64(len(m.Name)))
i += copy(dAtA[i:], m.Name)
}
if len(m.ServiceName) > 0 {
data[i] = 0x12
dAtA[i] = 0x12
i++
i = encodeVarintAgent(data, i, uint64(len(m.ServiceName)))
i += copy(data[i:], m.ServiceName)
i = encodeVarintAgent(dAtA, i, uint64(len(m.ServiceName)))
i += copy(dAtA[i:], m.ServiceName)
}
if len(m.ServiceID) > 0 {
data[i] = 0x1a
dAtA[i] = 0x1a
i++
i = encodeVarintAgent(data, i, uint64(len(m.ServiceID)))
i += copy(data[i:], m.ServiceID)
i = encodeVarintAgent(dAtA, i, uint64(len(m.ServiceID)))
i += copy(dAtA[i:], m.ServiceID)
}
if len(m.VirtualIP) > 0 {
data[i] = 0x22
dAtA[i] = 0x22
i++
i = encodeVarintAgent(data, i, uint64(len(m.VirtualIP)))
i += copy(data[i:], m.VirtualIP)
i = encodeVarintAgent(dAtA, i, uint64(len(m.VirtualIP)))
i += copy(dAtA[i:], m.VirtualIP)
}
if len(m.EndpointIP) > 0 {
data[i] = 0x2a
dAtA[i] = 0x2a
i++
i = encodeVarintAgent(data, i, uint64(len(m.EndpointIP)))
i += copy(data[i:], m.EndpointIP)
i = encodeVarintAgent(dAtA, i, uint64(len(m.EndpointIP)))
i += copy(dAtA[i:], m.EndpointIP)
}
if len(m.IngressPorts) > 0 {
for _, msg := range m.IngressPorts {
data[i] = 0x32
dAtA[i] = 0x32
i++
i = encodeVarintAgent(data, i, uint64(msg.Size()))
n, err := msg.MarshalTo(data[i:])
i = encodeVarintAgent(dAtA, i, uint64(msg.Size()))
n, err := msg.MarshalTo(dAtA[i:])
if err != nil {
return 0, err
}
@ -236,101 +297,101 @@ func (m *EndpointRecord) MarshalTo(data []byte) (int, error) {
}
if len(m.Aliases) > 0 {
for _, s := range m.Aliases {
data[i] = 0x3a
dAtA[i] = 0x3a
i++
l = len(s)
for l >= 1<<7 {
data[i] = uint8(uint64(l)&0x7f | 0x80)
dAtA[i] = uint8(uint64(l)&0x7f | 0x80)
l >>= 7
i++
}
data[i] = uint8(l)
dAtA[i] = uint8(l)
i++
i += copy(data[i:], s)
i += copy(dAtA[i:], s)
}
}
if len(m.TaskAliases) > 0 {
for _, s := range m.TaskAliases {
data[i] = 0x42
dAtA[i] = 0x42
i++
l = len(s)
for l >= 1<<7 {
data[i] = uint8(uint64(l)&0x7f | 0x80)
dAtA[i] = uint8(uint64(l)&0x7f | 0x80)
l >>= 7
i++
}
data[i] = uint8(l)
dAtA[i] = uint8(l)
i++
i += copy(data[i:], s)
i += copy(dAtA[i:], s)
}
}
return i, nil
}
func (m *PortConfig) Marshal() (data []byte, err error) {
func (m *PortConfig) Marshal() (dAtA []byte, err error) {
size := m.Size()
data = make([]byte, size)
n, err := m.MarshalTo(data)
dAtA = make([]byte, size)
n, err := m.MarshalTo(dAtA)
if err != nil {
return nil, err
}
return data[:n], nil
return dAtA[:n], nil
}
func (m *PortConfig) MarshalTo(data []byte) (int, error) {
func (m *PortConfig) MarshalTo(dAtA []byte) (int, error) {
var i int
_ = i
var l int
_ = l
if len(m.Name) > 0 {
data[i] = 0xa
dAtA[i] = 0xa
i++
i = encodeVarintAgent(data, i, uint64(len(m.Name)))
i += copy(data[i:], m.Name)
i = encodeVarintAgent(dAtA, i, uint64(len(m.Name)))
i += copy(dAtA[i:], m.Name)
}
if m.Protocol != 0 {
data[i] = 0x10
dAtA[i] = 0x10
i++
i = encodeVarintAgent(data, i, uint64(m.Protocol))
i = encodeVarintAgent(dAtA, i, uint64(m.Protocol))
}
if m.TargetPort != 0 {
data[i] = 0x18
dAtA[i] = 0x18
i++
i = encodeVarintAgent(data, i, uint64(m.TargetPort))
i = encodeVarintAgent(dAtA, i, uint64(m.TargetPort))
}
if m.PublishedPort != 0 {
data[i] = 0x20
dAtA[i] = 0x20
i++
i = encodeVarintAgent(data, i, uint64(m.PublishedPort))
i = encodeVarintAgent(dAtA, i, uint64(m.PublishedPort))
}
return i, nil
}
func encodeFixed64Agent(data []byte, offset int, v uint64) int {
data[offset] = uint8(v)
data[offset+1] = uint8(v >> 8)
data[offset+2] = uint8(v >> 16)
data[offset+3] = uint8(v >> 24)
data[offset+4] = uint8(v >> 32)
data[offset+5] = uint8(v >> 40)
data[offset+6] = uint8(v >> 48)
data[offset+7] = uint8(v >> 56)
func encodeFixed64Agent(dAtA []byte, offset int, v uint64) int {
dAtA[offset] = uint8(v)
dAtA[offset+1] = uint8(v >> 8)
dAtA[offset+2] = uint8(v >> 16)
dAtA[offset+3] = uint8(v >> 24)
dAtA[offset+4] = uint8(v >> 32)
dAtA[offset+5] = uint8(v >> 40)
dAtA[offset+6] = uint8(v >> 48)
dAtA[offset+7] = uint8(v >> 56)
return offset + 8
}
func encodeFixed32Agent(data []byte, offset int, v uint32) int {
data[offset] = uint8(v)
data[offset+1] = uint8(v >> 8)
data[offset+2] = uint8(v >> 16)
data[offset+3] = uint8(v >> 24)
func encodeFixed32Agent(dAtA []byte, offset int, v uint32) int {
dAtA[offset] = uint8(v)
dAtA[offset+1] = uint8(v >> 8)
dAtA[offset+2] = uint8(v >> 16)
dAtA[offset+3] = uint8(v >> 24)
return offset + 4
}
func encodeVarintAgent(data []byte, offset int, v uint64) int {
func encodeVarintAgent(dAtA []byte, offset int, v uint64) int {
for v >= 1<<7 {
data[offset] = uint8(v&0x7f | 0x80)
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
data[offset] = uint8(v)
dAtA[offset] = uint8(v)
return offset + 1
}
func (m *EndpointRecord) Size() (n int) {
@ -447,8 +508,8 @@ func valueToStringAgent(v interface{}) string {
pv := reflect.Indirect(rv).Interface()
return fmt.Sprintf("*%v", pv)
}
func (m *EndpointRecord) Unmarshal(data []byte) error {
l := len(data)
func (m *EndpointRecord) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
@ -460,7 +521,7 @@ func (m *EndpointRecord) Unmarshal(data []byte) error {
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := data[iNdEx]
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
@ -488,7 +549,7 @@ func (m *EndpointRecord) Unmarshal(data []byte) error {
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := data[iNdEx]
b := dAtA[iNdEx]
iNdEx++
stringLen |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
@ -503,7 +564,7 @@ func (m *EndpointRecord) Unmarshal(data []byte) error {
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Name = string(data[iNdEx:postIndex])
m.Name = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 2:
if wireType != 2 {
@ -517,7 +578,7 @@ func (m *EndpointRecord) Unmarshal(data []byte) error {
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := data[iNdEx]
b := dAtA[iNdEx]
iNdEx++
stringLen |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
@ -532,7 +593,7 @@ func (m *EndpointRecord) Unmarshal(data []byte) error {
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.ServiceName = string(data[iNdEx:postIndex])
m.ServiceName = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 3:
if wireType != 2 {
@ -546,7 +607,7 @@ func (m *EndpointRecord) Unmarshal(data []byte) error {
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := data[iNdEx]
b := dAtA[iNdEx]
iNdEx++
stringLen |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
@ -561,7 +622,7 @@ func (m *EndpointRecord) Unmarshal(data []byte) error {
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.ServiceID = string(data[iNdEx:postIndex])
m.ServiceID = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 4:
if wireType != 2 {
@ -575,7 +636,7 @@ func (m *EndpointRecord) Unmarshal(data []byte) error {
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := data[iNdEx]
b := dAtA[iNdEx]
iNdEx++
stringLen |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
@ -590,7 +651,7 @@ func (m *EndpointRecord) Unmarshal(data []byte) error {
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.VirtualIP = string(data[iNdEx:postIndex])
m.VirtualIP = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 5:
if wireType != 2 {
@ -604,7 +665,7 @@ func (m *EndpointRecord) Unmarshal(data []byte) error {
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := data[iNdEx]
b := dAtA[iNdEx]
iNdEx++
stringLen |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
@ -619,7 +680,7 @@ func (m *EndpointRecord) Unmarshal(data []byte) error {
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.EndpointIP = string(data[iNdEx:postIndex])
m.EndpointIP = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 6:
if wireType != 2 {
@ -633,7 +694,7 @@ func (m *EndpointRecord) Unmarshal(data []byte) error {
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := data[iNdEx]
b := dAtA[iNdEx]
iNdEx++
msglen |= (int(b) & 0x7F) << shift
if b < 0x80 {
@ -648,7 +709,7 @@ func (m *EndpointRecord) Unmarshal(data []byte) error {
return io.ErrUnexpectedEOF
}
m.IngressPorts = append(m.IngressPorts, &PortConfig{})
if err := m.IngressPorts[len(m.IngressPorts)-1].Unmarshal(data[iNdEx:postIndex]); err != nil {
if err := m.IngressPorts[len(m.IngressPorts)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
@ -664,7 +725,7 @@ func (m *EndpointRecord) Unmarshal(data []byte) error {
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := data[iNdEx]
b := dAtA[iNdEx]
iNdEx++
stringLen |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
@ -679,7 +740,7 @@ func (m *EndpointRecord) Unmarshal(data []byte) error {
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Aliases = append(m.Aliases, string(data[iNdEx:postIndex]))
m.Aliases = append(m.Aliases, string(dAtA[iNdEx:postIndex]))
iNdEx = postIndex
case 8:
if wireType != 2 {
@ -693,7 +754,7 @@ func (m *EndpointRecord) Unmarshal(data []byte) error {
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := data[iNdEx]
b := dAtA[iNdEx]
iNdEx++
stringLen |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
@ -708,11 +769,11 @@ func (m *EndpointRecord) Unmarshal(data []byte) error {
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.TaskAliases = append(m.TaskAliases, string(data[iNdEx:postIndex]))
m.TaskAliases = append(m.TaskAliases, string(dAtA[iNdEx:postIndex]))
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipAgent(data[iNdEx:])
skippy, err := skipAgent(dAtA[iNdEx:])
if err != nil {
return err
}
@ -731,8 +792,8 @@ func (m *EndpointRecord) Unmarshal(data []byte) error {
}
return nil
}
func (m *PortConfig) Unmarshal(data []byte) error {
l := len(data)
func (m *PortConfig) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
@ -744,7 +805,7 @@ func (m *PortConfig) Unmarshal(data []byte) error {
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := data[iNdEx]
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
@ -772,7 +833,7 @@ func (m *PortConfig) Unmarshal(data []byte) error {
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := data[iNdEx]
b := dAtA[iNdEx]
iNdEx++
stringLen |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
@ -787,7 +848,7 @@ func (m *PortConfig) Unmarshal(data []byte) error {
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Name = string(data[iNdEx:postIndex])
m.Name = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 2:
if wireType != 0 {
@ -801,7 +862,7 @@ func (m *PortConfig) Unmarshal(data []byte) error {
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := data[iNdEx]
b := dAtA[iNdEx]
iNdEx++
m.Protocol |= (PortConfig_Protocol(b) & 0x7F) << shift
if b < 0x80 {
@ -820,7 +881,7 @@ func (m *PortConfig) Unmarshal(data []byte) error {
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := data[iNdEx]
b := dAtA[iNdEx]
iNdEx++
m.TargetPort |= (uint32(b) & 0x7F) << shift
if b < 0x80 {
@ -839,7 +900,7 @@ func (m *PortConfig) Unmarshal(data []byte) error {
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := data[iNdEx]
b := dAtA[iNdEx]
iNdEx++
m.PublishedPort |= (uint32(b) & 0x7F) << shift
if b < 0x80 {
@ -848,7 +909,7 @@ func (m *PortConfig) Unmarshal(data []byte) error {
}
default:
iNdEx = preIndex
skippy, err := skipAgent(data[iNdEx:])
skippy, err := skipAgent(dAtA[iNdEx:])
if err != nil {
return err
}
@ -867,8 +928,8 @@ func (m *PortConfig) Unmarshal(data []byte) error {
}
return nil
}
func skipAgent(data []byte) (n int, err error) {
l := len(data)
func skipAgent(dAtA []byte) (n int, err error) {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
var wire uint64
@ -879,7 +940,7 @@ func skipAgent(data []byte) (n int, err error) {
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := data[iNdEx]
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
@ -897,7 +958,7 @@ func skipAgent(data []byte) (n int, err error) {
return 0, io.ErrUnexpectedEOF
}
iNdEx++
if data[iNdEx-1] < 0x80 {
if dAtA[iNdEx-1] < 0x80 {
break
}
}
@ -914,7 +975,7 @@ func skipAgent(data []byte) (n int, err error) {
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := data[iNdEx]
b := dAtA[iNdEx]
iNdEx++
length |= (int(b) & 0x7F) << shift
if b < 0x80 {
@ -937,7 +998,7 @@ func skipAgent(data []byte) (n int, err error) {
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := data[iNdEx]
b := dAtA[iNdEx]
iNdEx++
innerWire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
@ -948,7 +1009,7 @@ func skipAgent(data []byte) (n int, err error) {
if innerWireType == 4 {
break
}
next, err := skipAgent(data[start:])
next, err := skipAgent(dAtA[start:])
if err != nil {
return 0, err
}
@ -972,32 +1033,36 @@ var (
ErrIntOverflowAgent = fmt.Errorf("proto: integer overflow")
)
func init() { proto.RegisterFile("agent.proto", fileDescriptorAgent) }
var fileDescriptorAgent = []byte{
// 413 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x6c, 0x90, 0xbf, 0xae, 0xd3, 0x30,
0x14, 0x87, 0x9b, 0xdb, 0x70, 0x6f, 0x73, 0x72, 0x13, 0xae, 0x2c, 0x84, 0xa2, 0x0e, 0x69, 0xa9,
0x84, 0x74, 0x07, 0x94, 0x2b, 0x95, 0xb1, 0x13, 0x6d, 0x19, 0xb2, 0xa0, 0xc8, 0xfc, 0x59, 0xa3,
0xb4, 0x31, 0xc1, 0x6a, 0x88, 0x23, 0xdb, 0x2d, 0x2b, 0x23, 0xe2, 0x1d, 0x98, 0x78, 0x19, 0x26,
0xc4, 0xc8, 0x84, 0x68, 0x57, 0x16, 0x1e, 0x01, 0xdb, 0x49, 0x5a, 0x21, 0x75, 0x38, 0x92, 0xf3,
0xfd, 0xbe, 0xe3, 0x1c, 0x1f, 0x70, 0xb3, 0x82, 0x54, 0x32, 0xaa, 0x39, 0x93, 0x0c, 0x41, 0x49,
0x57, 0x15, 0x91, 0x1f, 0x18, 0xdf, 0x0c, 0x1f, 0x14, 0xac, 0x60, 0x06, 0xdf, 0xe9, 0x53, 0x63,
0x4c, 0xbe, 0x5f, 0x80, 0xff, 0xbc, 0xca, 0x6b, 0x46, 0x2b, 0x89, 0xc9, 0x9a, 0xf1, 0x1c, 0x21,
0xb0, 0xab, 0xec, 0x3d, 0x09, 0xac, 0xb1, 0x75, 0xeb, 0x60, 0x73, 0x46, 0x8f, 0xe0, 0x5a, 0x10,
0xbe, 0xa3, 0x6b, 0x92, 0x9a, 0xec, 0xc2, 0x64, 0x6e, 0xcb, 0x5e, 0x68, 0xe5, 0x09, 0x40, 0xa7,
0xd0, 0x3c, 0xe8, 0x6b, 0x61, 0xee, 0x1d, 0x7e, 0x8d, 0x9c, 0x97, 0x0d, 0x8d, 0x97, 0xd8, 0x69,
0x85, 0x38, 0xd7, 0xf6, 0x8e, 0x72, 0xb9, 0xcd, 0xca, 0x94, 0xd6, 0x81, 0x7d, 0xb2, 0xdf, 0x34,
0x34, 0x4e, 0xb0, 0xd3, 0x0a, 0x71, 0x8d, 0xee, 0xc0, 0x25, 0xed, 0x90, 0x5a, 0xbf, 0x67, 0x74,
0x5f, 0xe9, 0xd0, 0xcd, 0xae, 0x7c, 0xe8, 0x14, 0xd5, 0x30, 0x03, 0x8f, 0x56, 0x05, 0x27, 0x42,
0xa4, 0x35, 0xe3, 0x52, 0x04, 0x97, 0xe3, 0xfe, 0xad, 0x3b, 0x7d, 0x18, 0x9d, 0x16, 0x12, 0x25,
0x2a, 0x58, 0xb0, 0xea, 0x2d, 0x2d, 0xf0, 0x75, 0x2b, 0x6b, 0x24, 0x50, 0x00, 0x57, 0x59, 0x49,
0x33, 0x41, 0x44, 0x70, 0xa5, 0xda, 0x1c, 0xdc, 0x7d, 0xea, 0x35, 0xc8, 0x4c, 0x6c, 0xd2, 0x2e,
0x1e, 0x98, 0xd8, 0xd5, 0xec, 0x59, 0x83, 0x26, 0x7f, 0x2c, 0x80, 0xd3, 0xcd, 0x67, 0x97, 0x39,
0x83, 0x81, 0x59, 0xfe, 0x9a, 0x95, 0x66, 0x91, 0xfe, 0x74, 0x74, 0x7e, 0xae, 0x28, 0x69, 0x35,
0x7c, 0x6c, 0x40, 0x23, 0x50, 0xbf, 0xe3, 0x05, 0x91, 0xe6, 0x61, 0x66, 0xcf, 0x1e, 0x86, 0x06,
0xe9, 0x4e, 0xf4, 0x18, 0xfc, 0x7a, 0xbb, 0x2a, 0xa9, 0x78, 0x47, 0xf2, 0xc6, 0xb1, 0x8d, 0xe3,
0x1d, 0xa9, 0xd6, 0x26, 0x4b, 0x18, 0x74, 0xb7, 0xab, 0x07, 0xf7, 0x5f, 0x2d, 0x92, 0x9b, 0xde,
0xf0, 0xfe, 0xe7, 0x2f, 0x63, 0xb7, 0xc3, 0x0a, 0xe9, 0xe4, 0xf5, 0x32, 0xb9, 0xb1, 0xfe, 0x4f,
0x14, 0x1a, 0xda, 0x9f, 0xbe, 0x86, 0xbd, 0x79, 0xf0, 0x73, 0x1f, 0xf6, 0xfe, 0xee, 0x43, 0xeb,
0xe3, 0x21, 0xb4, 0xbe, 0xa9, 0xfa, 0xa1, 0xea, 0xb7, 0xaa, 0xd5, 0xa5, 0x99, 0xf8, 0xe9, 0xbf,
0x00, 0x00, 0x00, 0xff, 0xff, 0xc9, 0x63, 0x1a, 0x0f, 0x90, 0x02, 0x00, 0x00,
// 437 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x90, 0xc1, 0x6e, 0xd3, 0x30,
0x18, 0xc7, 0x9b, 0x36, 0x6c, 0xcd, 0x97, 0xb6, 0x54, 0x16, 0x42, 0x51, 0x0e, 0x69, 0xa8, 0x84,
0xd4, 0x03, 0xea, 0xa4, 0x71, 0xdc, 0x89, 0xb5, 0x1c, 0x72, 0x41, 0x96, 0xd7, 0x71, 0x0d, 0x69,
0x63, 0x82, 0xb5, 0x10, 0x47, 0xb6, 0x37, 0xae, 0xdc, 0x40, 0x7b, 0x87, 0x9d, 0x78, 0x19, 0x4e,
0x88, 0x23, 0xa7, 0x89, 0xe5, 0x09, 0x78, 0x04, 0x64, 0x27, 0x5e, 0x35, 0x69, 0x37, 0xfb, 0xf7,
0xff, 0xd9, 0xfa, 0xbe, 0x3f, 0xf8, 0x59, 0x41, 0x2b, 0xb5, 0xac, 0x05, 0x57, 0x1c, 0x41, 0xc9,
0xb6, 0x15, 0x55, 0x5f, 0xb8, 0xb8, 0x08, 0x9f, 0x15, 0xbc, 0xe0, 0x06, 0x1f, 0xe9, 0x53, 0x6b,
0xcc, 0x7f, 0xf5, 0x61, 0xf2, 0xb6, 0xca, 0x6b, 0xce, 0x2a, 0x45, 0xe8, 0x8e, 0x8b, 0x1c, 0x21,
0x70, 0xab, 0xec, 0x33, 0x0d, 0x9c, 0xd8, 0x59, 0x78, 0xc4, 0x9c, 0xd1, 0x0b, 0x18, 0x49, 0x2a,
0xae, 0xd8, 0x8e, 0xa6, 0x26, 0xeb, 0x9b, 0xcc, 0xef, 0xd8, 0x3b, 0xad, 0xbc, 0x02, 0xb0, 0x0a,
0xcb, 0x83, 0x81, 0x16, 0x4e, 0xc7, 0xcd, 0xed, 0xcc, 0x3b, 0x6b, 0x69, 0xb2, 0x26, 0x5e, 0x27,
0x24, 0xb9, 0xb6, 0xaf, 0x98, 0x50, 0x97, 0x59, 0x99, 0xb2, 0x3a, 0x70, 0xf7, 0xf6, 0xfb, 0x96,
0x26, 0x98, 0x78, 0x9d, 0x90, 0xd4, 0xe8, 0x08, 0x7c, 0xda, 0x0d, 0xa9, 0xf5, 0x27, 0x46, 0x9f,
0x34, 0xb7, 0x33, 0xb0, 0xb3, 0x27, 0x98, 0x80, 0x55, 0x92, 0x1a, 0x9d, 0xc0, 0x98, 0x55, 0x85,
0xa0, 0x52, 0xa6, 0x35, 0x17, 0x4a, 0x06, 0x07, 0xf1, 0x60, 0xe1, 0x1f, 0x3f, 0x5f, 0xee, 0x0b,
0x59, 0x62, 0x2e, 0xd4, 0x8a, 0x57, 0x1f, 0x59, 0x41, 0x46, 0x9d, 0xac, 0x91, 0x44, 0x01, 0x1c,
0x66, 0x25, 0xcb, 0x24, 0x95, 0xc1, 0x61, 0x3c, 0x58, 0x78, 0xc4, 0x5e, 0x75, 0x0d, 0x2a, 0x93,
0x17, 0xa9, 0x8d, 0x87, 0x26, 0xf6, 0x35, 0x7b, 0xd3, 0xa2, 0xf9, 0xb7, 0x3e, 0xc0, 0xfe, 0xe7,
0x47, 0xcb, 0x3c, 0x81, 0xa1, 0x29, 0x7f, 0xc7, 0x4b, 0x53, 0xe4, 0xe4, 0x78, 0xf6, 0xf8, 0x5c,
0x4b, 0xdc, 0x69, 0xe4, 0xfe, 0x01, 0x9a, 0x81, 0xaf, 0x32, 0x51, 0x50, 0x65, 0x16, 0x33, 0x3d,
0x8f, 0x09, 0xb4, 0x48, 0xbf, 0x44, 0x2f, 0x61, 0x52, 0x5f, 0x6e, 0x4b, 0x26, 0x3f, 0xd1, 0xbc,
0x75, 0x5c, 0xe3, 0x8c, 0xef, 0xa9, 0xd6, 0xe6, 0x1f, 0x60, 0x68, 0x7f, 0x47, 0x01, 0x0c, 0x36,
0x2b, 0x3c, 0xed, 0x85, 0x4f, 0xaf, 0x6f, 0x62, 0xdf, 0xe2, 0xcd, 0x0a, 0xeb, 0xe4, 0x7c, 0x8d,
0xa7, 0xce, 0xc3, 0xe4, 0x7c, 0x8d, 0x51, 0x08, 0xee, 0xd9, 0x6a, 0x83, 0xa7, 0xfd, 0x70, 0x7a,
0x7d, 0x13, 0x8f, 0x6c, 0xa4, 0x59, 0xe8, 0x7e, 0xff, 0x11, 0xf5, 0x4e, 0x83, 0x3f, 0x77, 0x51,
0xef, 0xdf, 0x5d, 0xe4, 0x7c, 0x6d, 0x22, 0xe7, 0x67, 0x13, 0x39, 0xbf, 0x9b, 0xc8, 0xf9, 0xdb,
0x44, 0xce, 0xf6, 0xc0, 0x6c, 0xf3, 0xfa, 0x7f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xce, 0x12, 0x15,
0x67, 0xac, 0x02, 0x00, 0x00,
}

View file

@ -51,6 +51,7 @@ message PortConfig {
TCP = 0 [(gogoproto.enumvalue_customname) = "ProtocolTCP"];
UDP = 1 [(gogoproto.enumvalue_customname) = "ProtocolUDP"];
SCTP = 2 [(gogoproto.enumvalue_customname) = "ProtocolSCTP"];
}
// Name for the port. If provided the port information can

View file

@ -8,6 +8,8 @@ import (
"os"
"os/signal"
"syscall"
"github.com/ishidawataru/sctp"
)
func main() {
@ -28,7 +30,7 @@ func main() {
p.Run()
}
// parseHostContainerAddrs parses the flags passed on reexec to create the TCP or UDP
// parseHostContainerAddrs parses the flags passed on reexec to create the TCP/UDP/SCTP
// net.Addrs to map the host and container ports
func parseHostContainerAddrs() (host net.Addr, container net.Addr) {
var (
@ -48,6 +50,9 @@ func parseHostContainerAddrs() (host net.Addr, container net.Addr) {
case "udp":
host = &net.UDPAddr{IP: net.ParseIP(*hostIP), Port: *hostPort}
container = &net.UDPAddr{IP: net.ParseIP(*containerIP), Port: *containerPort}
case "sctp":
host = &sctp.SCTPAddr{IP: []net.IP{net.ParseIP(*hostIP)}, Port: *hostPort}
container = &sctp.SCTPAddr{IP: []net.IP{net.ParseIP(*containerIP)}, Port: *containerPort}
default:
log.Fatalf("unsupported protocol %s", *proto)
}

View file

@ -10,6 +10,8 @@ import (
"strings"
"testing"
"time"
"github.com/ishidawataru/sctp"
)
var _ = flag.Bool("incontainer", false, "Indicates if the test is running in a container")
@ -27,7 +29,7 @@ type EchoServerOptions struct {
TCPHalfClose bool
}
type TCPEchoServer struct {
type StreamEchoServer struct {
listener net.Listener
testCtx *testing.T
opts EchoServerOptions
@ -40,26 +42,40 @@ type UDPEchoServer struct {
func NewEchoServer(t *testing.T, proto, address string, opts EchoServerOptions) EchoServer {
var server EchoServer
if strings.HasPrefix(proto, "tcp") {
if !strings.HasPrefix(proto, "tcp") && opts.TCPHalfClose {
t.Fatalf("TCPHalfClose is not supported for %s", proto)
}
switch {
case strings.HasPrefix(proto, "tcp"):
listener, err := net.Listen(proto, address)
if err != nil {
t.Fatal(err)
}
server = &TCPEchoServer{listener: listener, testCtx: t, opts: opts}
} else {
if opts.TCPHalfClose {
t.Fatalf("TCPHalfClose is not supported for %s", proto)
}
server = &StreamEchoServer{listener: listener, testCtx: t, opts: opts}
case strings.HasPrefix(proto, "udp"):
socket, err := net.ListenPacket(proto, address)
if err != nil {
t.Fatal(err)
}
server = &UDPEchoServer{conn: socket, testCtx: t}
case strings.HasPrefix(proto, "sctp"):
addr, err := sctp.ResolveSCTPAddr(proto, address)
if err != nil {
t.Fatal(err)
}
listener, err := sctp.ListenSCTP(proto, addr)
if err != nil {
t.Fatal(err)
}
server = &StreamEchoServer{listener: listener, testCtx: t}
default:
t.Fatalf("unknown protocol: %s", proto)
}
return server
}
func (server *TCPEchoServer) Run() {
func (server *StreamEchoServer) Run() {
go func() {
for {
client, err := server.listener.Accept()
@ -87,8 +103,8 @@ func (server *TCPEchoServer) Run() {
}()
}
func (server *TCPEchoServer) LocalAddr() net.Addr { return server.listener.Addr() }
func (server *TCPEchoServer) Close() { server.listener.Close() }
func (server *StreamEchoServer) LocalAddr() net.Addr { return server.listener.Addr() }
func (server *StreamEchoServer) Close() { server.listener.Close() }
func (server *UDPEchoServer) Run() {
go func() {
@ -115,7 +131,19 @@ func (server *UDPEchoServer) Close() { server.conn.Close() }
func testProxyAt(t *testing.T, proto string, proxy Proxy, addr string, halfClose bool) {
defer proxy.Close()
go proxy.Run()
client, err := net.Dial(proto, addr)
var client net.Conn
var err error
if strings.HasPrefix(proto, "sctp") {
var a *sctp.SCTPAddr
a, err = sctp.ResolveSCTPAddr(proto, addr)
if err != nil {
t.Fatal(err)
}
client, err = sctp.DialSCTP(proto, nil, a)
} else {
client, err = net.Dial(proto, addr)
}
if err != nil {
t.Fatalf("Can't connect to the proxy: %v", err)
}
@ -253,3 +281,28 @@ func TestUDPWriteError(t *testing.T) {
t.Fatal(fmt.Errorf("Expected [%v] but got [%v]", testBuf, recvBuf))
}
}
func TestSCTP4Proxy(t *testing.T) {
backend := NewEchoServer(t, "sctp", "127.0.0.1:0", EchoServerOptions{})
defer backend.Close()
backend.Run()
frontendAddr := &sctp.SCTPAddr{IP: []net.IP{net.IPv4(127, 0, 0, 1)}, Port: 0}
proxy, err := NewProxy(frontendAddr, backend.LocalAddr())
if err != nil {
t.Fatal(err)
}
testProxy(t, "sctp", proxy, false)
}
func TestSCTP6Proxy(t *testing.T) {
t.Skip("Need to start CI docker with --ipv6")
backend := NewEchoServer(t, "sctp", "[::1]:0", EchoServerOptions{})
defer backend.Close()
backend.Run()
frontendAddr := &sctp.SCTPAddr{IP: []net.IP{net.IPv6loopback}, Port: 0}
proxy, err := NewProxy(frontendAddr, backend.LocalAddr())
if err != nil {
t.Fatal(err)
}
testProxy(t, "sctp", proxy, false)
}

View file

@ -4,6 +4,8 @@ package main
import (
"net"
"github.com/ishidawataru/sctp"
)
// Proxy defines the behavior of a proxy. It forwards traffic back and forth
@ -30,6 +32,8 @@ func NewProxy(frontendAddr, backendAddr net.Addr) (Proxy, error) {
return NewUDPProxy(frontendAddr.(*net.UDPAddr), backendAddr.(*net.UDPAddr))
case *net.TCPAddr:
return NewTCPProxy(frontendAddr.(*net.TCPAddr), backendAddr.(*net.TCPAddr))
case *sctp.SCTPAddr:
return NewSCTPProxy(frontendAddr.(*sctp.SCTPAddr), backendAddr.(*sctp.SCTPAddr))
default:
panic("Unsupported protocol")
}

View file

@ -0,0 +1,93 @@
package main
import (
"io"
"log"
"net"
"sync"
"github.com/ishidawataru/sctp"
)
// SCTPProxy is a proxy for SCTP connections. It implements the Proxy interface to
// handle SCTP traffic forwarding between the frontend and backend addresses.
type SCTPProxy struct {
listener *sctp.SCTPListener
frontendAddr *sctp.SCTPAddr
backendAddr *sctp.SCTPAddr
}
// NewSCTPProxy creates a new SCTPProxy.
func NewSCTPProxy(frontendAddr, backendAddr *sctp.SCTPAddr) (*SCTPProxy, error) {
listener, err := sctp.ListenSCTP("sctp", frontendAddr)
if err != nil {
return nil, err
}
// If the port in frontendAddr was 0 then ListenSCTP will have a picked
// a port to listen on, hence the call to Addr to get that actual port:
return &SCTPProxy{
listener: listener,
frontendAddr: listener.Addr().(*sctp.SCTPAddr),
backendAddr: backendAddr,
}, nil
}
func (proxy *SCTPProxy) clientLoop(client *sctp.SCTPConn, quit chan bool) {
backend, err := sctp.DialSCTP("sctp", nil, proxy.backendAddr)
if err != nil {
log.Printf("Can't forward traffic to backend sctp/%v: %s\n", proxy.backendAddr, err)
client.Close()
return
}
clientC := sctp.NewSCTPSndRcvInfoWrappedConn(client)
backendC := sctp.NewSCTPSndRcvInfoWrappedConn(backend)
var wg sync.WaitGroup
var broker = func(to, from net.Conn) {
io.Copy(to, from)
from.Close()
to.Close()
wg.Done()
}
wg.Add(2)
go broker(clientC, backendC)
go broker(backendC, clientC)
finish := make(chan struct{})
go func() {
wg.Wait()
close(finish)
}()
select {
case <-quit:
case <-finish:
}
clientC.Close()
backendC.Close()
<-finish
}
// Run starts forwarding the traffic using SCTP.
func (proxy *SCTPProxy) Run() {
quit := make(chan bool)
defer close(quit)
for {
client, err := proxy.listener.Accept()
if err != nil {
log.Printf("Stopping proxy on sctp/%v for sctp/%v (%s)", proxy.frontendAddr, proxy.backendAddr, err)
return
}
go proxy.clientLoop(client.(*sctp.SCTPConn), quit)
}
}
// Close stops forwarding the traffic.
func (proxy *SCTPProxy) Close() { proxy.listener.Close() }
// FrontendAddr returns the SCTP address on which the proxy is listening.
func (proxy *SCTPProxy) FrontendAddr() net.Addr { return proxy.frontendAddr }
// BackendAddr returns the SCTP proxied address.
func (proxy *SCTPProxy) BackendAddr() net.Addr { return proxy.backendAddr }

View file

@ -7,6 +7,7 @@ import (
"net"
"github.com/docker/libnetwork/types"
"github.com/ishidawataru/sctp"
"github.com/sirupsen/logrus"
)
@ -92,6 +93,9 @@ func (n *bridgeNetwork) allocatePort(bnd *types.PortBinding, containerIP, defHos
case *net.UDPAddr:
bnd.HostPort = uint16(host.(*net.UDPAddr).Port)
return nil
case *sctp.SCTPAddr:
bnd.HostPort = uint16(host.(*sctp.SCTPAddr).Port)
return nil
default:
// For completeness
return ErrUnsupportedAddressType(fmt.Sprintf("%T", netAddr))

View file

@ -33,7 +33,8 @@ func TestPortMappingConfig(t *testing.T) {
binding1 := types.PortBinding{Proto: types.UDP, Port: uint16(400), HostPort: uint16(54000)}
binding2 := types.PortBinding{Proto: types.TCP, Port: uint16(500), HostPort: uint16(65000)}
portBindings := []types.PortBinding{binding1, binding2}
binding3 := types.PortBinding{Proto: types.SCTP, Port: uint16(300), HostPort: uint16(65000)}
portBindings := []types.PortBinding{binding1, binding2, binding3}
sbOptions := make(map[string]interface{})
sbOptions[netlabel.PortMap] = portBindings
@ -69,15 +70,17 @@ func TestPortMappingConfig(t *testing.T) {
t.Fatalf("Cannot find network %s inside driver", "dummy")
}
ep, _ := network.endpoints["ep1"]
if len(ep.portMapping) != 2 {
if len(ep.portMapping) != 3 {
t.Fatalf("Failed to store the port bindings into the sandbox info. Found: %v", ep.portMapping)
}
if ep.portMapping[0].Proto != binding1.Proto || ep.portMapping[0].Port != binding1.Port ||
ep.portMapping[1].Proto != binding2.Proto || ep.portMapping[1].Port != binding2.Port {
ep.portMapping[1].Proto != binding2.Proto || ep.portMapping[1].Port != binding2.Port ||
ep.portMapping[2].Proto != binding3.Proto || ep.portMapping[2].Port != binding3.Port {
t.Fatal("bridgeEndpoint has incorrect port mapping values")
}
if ep.portMapping[0].HostIP == nil || ep.portMapping[0].HostPort == 0 ||
ep.portMapping[1].HostIP == nil || ep.portMapping[1].HostPort == 0 {
ep.portMapping[1].HostIP == nil || ep.portMapping[1].HostPort == 0 ||
ep.portMapping[2].HostIP == nil || ep.portMapping[2].HostPort == 0 {
t.Fatal("operational port mapping data not found on bridgeEndpoint")
}

View file

@ -276,7 +276,31 @@ func (c *ChainInfo) Forward(action Action, ip net.IP, port int, proto, destAddr
"--dport", strconv.Itoa(destPort),
"-j", "MASQUERADE",
}
return ProgramRule(Nat, "POSTROUTING", action, args)
if err := ProgramRule(Nat, "POSTROUTING", action, args); err != nil {
return err
}
if proto == "sctp" {
// Linux kernel v4.9 and below enables NETIF_F_SCTP_CRC for veth by
// the following commit.
// This introduces a problem when conbined with a physical NIC without
// NETIF_F_SCTP_CRC. As for a workaround, here we add an iptables entry
// to fill the checksum.
//
// https://github.com/torvalds/linux/commit/c80fafbbb59ef9924962f83aac85531039395b18
args = []string{
"-p", proto,
"--sport", strconv.Itoa(destPort),
"-j", "CHECKSUM",
"--checksum-fill",
}
if err := ProgramRule(Mangle, "POSTROUTING", action, args); err != nil {
return err
}
}
return nil
}
// Link adds reciprocal ACCEPT rule for two supplied IP addresses.

View file

@ -120,7 +120,7 @@ func (p *PortAllocator) RequestPortInRange(ip net.IP, proto string, portStart, p
p.mutex.Lock()
defer p.mutex.Unlock()
if proto != "tcp" && proto != "udp" {
if proto != "tcp" && proto != "udp" && proto != "sctp" {
return 0, ErrUnknownProtocol
}
@ -131,8 +131,9 @@ func (p *PortAllocator) RequestPortInRange(ip net.IP, proto string, portStart, p
protomap, ok := p.ipMap[ipstr]
if !ok {
protomap = protoMap{
"tcp": p.newPortMap(),
"udp": p.newPortMap(),
"tcp": p.newPortMap(),
"udp": p.newPortMap(),
"sctp": p.newPortMap(),
}
p.ipMap[ipstr] = protomap

View file

@ -8,6 +8,7 @@ import (
"github.com/docker/libnetwork/iptables"
"github.com/docker/libnetwork/portallocator"
"github.com/ishidawataru/sctp"
"github.com/sirupsen/logrus"
)
@ -27,6 +28,8 @@ var (
ErrPortMappedForIP = errors.New("port is already mapped to ip")
// ErrPortNotMapped refers to an unmapped port
ErrPortNotMapped = errors.New("port is not mapped")
// ErrSCTPAddrNoIP refers to a SCTP address without IP address.
ErrSCTPAddrNoIP = errors.New("sctp address does not contain any IP address")
)
// PortMapper manages the network address translation
@ -98,7 +101,10 @@ func (pm *PortMapper) MapRange(container net.Addr, hostIP net.IP, hostPortStart,
return nil, err
}
} else {
m.userlandProxy = newDummyProxy(proto, hostIP, allocatedHostPort)
m.userlandProxy, err = newDummyProxy(proto, hostIP, allocatedHostPort)
if err != nil {
return nil, err
}
}
case *net.UDPAddr:
proto = "udp"
@ -118,7 +124,37 @@ func (pm *PortMapper) MapRange(container net.Addr, hostIP net.IP, hostPortStart,
return nil, err
}
} else {
m.userlandProxy = newDummyProxy(proto, hostIP, allocatedHostPort)
m.userlandProxy, err = newDummyProxy(proto, hostIP, allocatedHostPort)
if err != nil {
return nil, err
}
}
case *sctp.SCTPAddr:
proto = "sctp"
if allocatedHostPort, err = pm.Allocator.RequestPortInRange(hostIP, proto, hostPortStart, hostPortEnd); err != nil {
return nil, err
}
m = &mapping{
proto: proto,
host: &sctp.SCTPAddr{IP: []net.IP{hostIP}, Port: allocatedHostPort},
container: container,
}
if useProxy {
sctpAddr := container.(*sctp.SCTPAddr)
if len(sctpAddr.IP) == 0 {
return nil, ErrSCTPAddrNoIP
}
m.userlandProxy, err = newProxy(proto, hostIP, allocatedHostPort, sctpAddr.IP[0], sctpAddr.Port, pm.proxyPath)
if err != nil {
return nil, err
}
} else {
m.userlandProxy, err = newDummyProxy(proto, hostIP, allocatedHostPort)
if err != nil {
return nil, err
}
}
default:
return nil, ErrUnknownBackendAddressType
@ -195,8 +231,13 @@ func (pm *PortMapper) Unmap(host net.Addr) error {
return pm.Allocator.ReleasePort(a.IP, "tcp", a.Port)
case *net.UDPAddr:
return pm.Allocator.ReleasePort(a.IP, "udp", a.Port)
case *sctp.SCTPAddr:
if len(a.IP) == 0 {
return ErrSCTPAddrNoIP
}
return pm.Allocator.ReleasePort(a.IP[0], "sctp", a.Port)
}
return nil
return ErrUnknownBackendAddressType
}
//ReMapAll will re-apply all port mappings
@ -219,6 +260,12 @@ func getKey(a net.Addr) string {
return fmt.Sprintf("%s:%d/%s", t.IP.String(), t.Port, "tcp")
case *net.UDPAddr:
return fmt.Sprintf("%s:%d/%s", t.IP.String(), t.Port, "udp")
case *sctp.SCTPAddr:
if len(t.IP) == 0 {
logrus.Error(ErrSCTPAddrNoIP)
return ""
}
return fmt.Sprintf("%s:%d/%s", t.IP[0].String(), t.Port, "sctp")
}
return ""
}
@ -229,6 +276,12 @@ func getIPAndPort(a net.Addr) (net.IP, int) {
return t.IP, t.Port
case *net.UDPAddr:
return t.IP, t.Port
case *sctp.SCTPAddr:
if len(t.IP) == 0 {
logrus.Error(ErrSCTPAddrNoIP)
return nil, 0
}
return t.IP[0], t.Port
}
return nil, 0
}

View file

@ -8,6 +8,8 @@ import (
"os"
"os/exec"
"time"
"github.com/ishidawataru/sctp"
)
var userlandProxyCommandName = "docker-proxy"
@ -79,16 +81,20 @@ type dummyProxy struct {
addr net.Addr
}
func newDummyProxy(proto string, hostIP net.IP, hostPort int) userlandProxy {
func newDummyProxy(proto string, hostIP net.IP, hostPort int) (userlandProxy, error) {
switch proto {
case "tcp":
addr := &net.TCPAddr{IP: hostIP, Port: hostPort}
return &dummyProxy{addr: addr}
return &dummyProxy{addr: addr}, nil
case "udp":
addr := &net.UDPAddr{IP: hostIP, Port: hostPort}
return &dummyProxy{addr: addr}
return &dummyProxy{addr: addr}, nil
case "sctp":
addr := &sctp.SCTPAddr{IP: []net.IP{hostIP}, Port: hostPort}
return &dummyProxy{addr: addr}, nil
default:
return nil, fmt.Errorf("Unknown addr type: %s", proto)
}
return nil
}
func (p *dummyProxy) Start() error {
@ -105,6 +111,12 @@ func (p *dummyProxy) Start() error {
return err
}
p.listener = l
case *sctp.SCTPAddr:
l, err := sctp.ListenSCTP("sctp", addr)
if err != nil {
return err
}
p.listener = l
default:
return fmt.Errorf("Unknown addr type: %T", p.addr)
}

View file

@ -19,6 +19,7 @@ import (
"github.com/docker/libnetwork/ipvs"
"github.com/docker/libnetwork/ns"
"github.com/gogo/protobuf/proto"
"github.com/ishidawataru/sctp"
"github.com/sirupsen/logrus"
"github.com/vishvananda/netlink/nl"
"github.com/vishvananda/netns"
@ -503,6 +504,10 @@ func plumbProxy(iPort *PortConfig, isDelete bool) error {
l, err = net.ListenTCP("tcp", &net.TCPAddr{Port: int(iPort.PublishedPort)})
case ProtocolUDP:
l, err = net.ListenUDP("udp", &net.UDPAddr{Port: int(iPort.PublishedPort)})
case ProtocolSCTP:
l, err = sctp.ListenSCTP("sctp", &sctp.SCTPAddr{Port: int(iPort.PublishedPort)})
default:
err = fmt.Errorf("unknown protocol %v", iPort.Protocol)
}
if err != nil {
@ -761,6 +766,7 @@ func redirecter() {
// Ensure blocking rules for anything else in/to ingress network
for _, rule := range [][]string{
{"-d", eIP.String(), "-p", "sctp", "-j", "DROP"},
{"-d", eIP.String(), "-p", "udp", "-j", "DROP"},
{"-d", eIP.String(), "-p", "tcp", "-j", "DROP"},
} {

View file

@ -7,6 +7,8 @@ import (
"net"
"strconv"
"strings"
"github.com/ishidawataru/sctp"
)
// constants for the IP address type
@ -96,6 +98,8 @@ func (p PortBinding) HostAddr() (net.Addr, error) {
return &net.UDPAddr{IP: p.HostIP, Port: int(p.HostPort)}, nil
case TCP:
return &net.TCPAddr{IP: p.HostIP, Port: int(p.HostPort)}, nil
case SCTP:
return &sctp.SCTPAddr{IP: []net.IP{p.HostIP}, Port: int(p.HostPort)}, nil
default:
return nil, ErrInvalidProtocolBinding(p.Proto.String())
}
@ -108,6 +112,8 @@ func (p PortBinding) ContainerAddr() (net.Addr, error) {
return &net.UDPAddr{IP: p.IP, Port: int(p.Port)}, nil
case TCP:
return &net.TCPAddr{IP: p.IP, Port: int(p.Port)}, nil
case SCTP:
return &sctp.SCTPAddr{IP: []net.IP{p.IP}, Port: int(p.Port)}, nil
default:
return nil, ErrInvalidProtocolBinding(p.Proto.String())
}
@ -233,6 +239,8 @@ const (
TCP = 6
// UDP is for the UDP ip protocol
UDP = 17
// SCTP is for the SCTP ip protocol
SCTP = 132
)
// Protocol represents an IP protocol number
@ -246,6 +254,8 @@ func (p Protocol) String() string {
return "tcp"
case UDP:
return "udp"
case SCTP:
return "sctp"
default:
return fmt.Sprintf("%d", p)
}
@ -260,6 +270,8 @@ func ParseProtocol(s string) Protocol {
return UDP
case "tcp":
return TCP
case "sctp":
return SCTP
default:
return 0
}

View file

@ -51,3 +51,4 @@ golang.org/x/crypto 558b6879de74bc843225cde5686419267ff707ca
golang.org/x/net 7dcfb8076726a3fdd9353b6b8a1f1b6be6811bd6
golang.org/x/sys 07c182904dbd53199946ba614a412c61d3c548f5
github.com/pkg/errors 839d9e913e063e28dfd0e6c7b7512793e0a48be9
github.com/ishidawataru/sctp 07191f837fedd2f13d1ec7b5f885f0f3ec54b1cb

201
libnetwork/vendor/github.com/ishidawataru/sctp/LICENSE generated vendored Normal file
View file

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View file

@ -0,0 +1,18 @@
Stream Control Transmission Protocol (SCTP)
----
[![Build Status](https://travis-ci.org/ishidawataru/sctp.svg?branch=master)](https://travis-ci.org/ishidawataru/sctp/builds)
Examples
----
See `example/sctp.go`
```go
$ cd example
$ go build
$ # run example SCTP server
$ ./example -server -port 1000 -ip 10.10.0.1,10.20.0.1
$ # run example SCTP client
$ ./example -port 1000 -ip 10.10.0.1,10.20.0.1
```

656
libnetwork/vendor/github.com/ishidawataru/sctp/sctp.go generated vendored Normal file
View file

@ -0,0 +1,656 @@
package sctp
import (
"bytes"
"encoding/binary"
"fmt"
"net"
"strconv"
"strings"
"sync"
"sync/atomic"
"syscall"
"time"
"unsafe"
)
const (
SOL_SCTP = 132
SCTP_BINDX_ADD_ADDR = 0x01
SCTP_BINDX_REM_ADDR = 0x02
MSG_NOTIFICATION = 0x8000
)
const (
SCTP_RTOINFO = iota
SCTP_ASSOCINFO
SCTP_INITMSG
SCTP_NODELAY
SCTP_AUTOCLOSE
SCTP_SET_PEER_PRIMARY_ADDR
SCTP_PRIMARY_ADDR
SCTP_ADAPTATION_LAYER
SCTP_DISABLE_FRAGMENTS
SCTP_PEER_ADDR_PARAMS
SCTP_DEFAULT_SENT_PARAM
SCTP_EVENTS
SCTP_I_WANT_MAPPED_V4_ADDR
SCTP_MAXSEG
SCTP_STATUS
SCTP_GET_PEER_ADDR_INFO
SCTP_DELAYED_ACK_TIME
SCTP_DELAYED_ACK = SCTP_DELAYED_ACK_TIME
SCTP_DELAYED_SACK = SCTP_DELAYED_ACK_TIME
SCTP_SOCKOPT_BINDX_ADD = 100
SCTP_SOCKOPT_BINDX_REM = 101
SCTP_SOCKOPT_PEELOFF = 102
SCTP_GET_PEER_ADDRS = 108
SCTP_GET_LOCAL_ADDRS = 109
SCTP_SOCKOPT_CONNECTX = 110
SCTP_SOCKOPT_CONNECTX3 = 111
)
const (
SCTP_EVENT_DATA_IO = 1 << iota
SCTP_EVENT_ASSOCIATION
SCTP_EVENT_ADDRESS
SCTP_EVENT_SEND_FAILURE
SCTP_EVENT_PEER_ERROR
SCTP_EVENT_SHUTDOWN
SCTP_EVENT_PARTIAL_DELIVERY
SCTP_EVENT_ADAPTATION_LAYER
SCTP_EVENT_AUTHENTICATION
SCTP_EVENT_SENDER_DRY
SCTP_EVENT_ALL = SCTP_EVENT_DATA_IO | SCTP_EVENT_ASSOCIATION | SCTP_EVENT_ADDRESS | SCTP_EVENT_SEND_FAILURE | SCTP_EVENT_PEER_ERROR | SCTP_EVENT_SHUTDOWN | SCTP_EVENT_PARTIAL_DELIVERY | SCTP_EVENT_ADAPTATION_LAYER | SCTP_EVENT_AUTHENTICATION | SCTP_EVENT_SENDER_DRY
)
type SCTPNotificationType int
const (
SCTP_SN_TYPE_BASE = SCTPNotificationType(iota + (1 << 15))
SCTP_ASSOC_CHANGE
SCTP_PEER_ADDR_CHANGE
SCTP_SEND_FAILED
SCTP_REMOTE_ERROR
SCTP_SHUTDOWN_EVENT
SCTP_PARTIAL_DELIVERY_EVENT
SCTP_ADAPTATION_INDICATION
SCTP_AUTHENTICATION_INDICATION
SCTP_SENDER_DRY_EVENT
)
type NotificationHandler func([]byte) error
type EventSubscribe struct {
DataIO uint8
Association uint8
Address uint8
SendFailure uint8
PeerError uint8
Shutdown uint8
PartialDelivery uint8
AdaptationLayer uint8
Authentication uint8
SenderDry uint8
}
const (
SCTP_CMSG_INIT = iota
SCTP_CMSG_SNDRCV
SCTP_CMSG_SNDINFO
SCTP_CMSG_RCVINFO
SCTP_CMSG_NXTINFO
)
const (
SCTP_UNORDERED = 1 << iota
SCTP_ADDR_OVER
SCTP_ABORT
SCTP_SACK_IMMEDIATELY
SCTP_EOF
)
const (
SCTP_MAX_STREAM = 0xffff
)
type InitMsg struct {
NumOstreams uint16
MaxInstreams uint16
MaxAttempts uint16
MaxInitTimeout uint16
}
type SndRcvInfo struct {
Stream uint16
SSN uint16
Flags uint16
_ uint16
PPID uint32
Context uint32
TTL uint32
TSN uint32
CumTSN uint32
AssocID int32
}
type SndInfo struct {
SID uint16
Flags uint16
PPID uint32
Context uint32
AssocID int32
}
type GetAddrsOld struct {
AssocID int32
AddrNum int32
Addrs uintptr
}
type NotificationHeader struct {
Type uint16
Flags uint16
Length uint32
}
type SCTPState uint16
const (
SCTP_COMM_UP = SCTPState(iota)
SCTP_COMM_LOST
SCTP_RESTART
SCTP_SHUTDOWN_COMP
SCTP_CANT_STR_ASSOC
)
var nativeEndian binary.ByteOrder
var sndRcvInfoSize uintptr
func init() {
i := uint16(1)
if *(*byte)(unsafe.Pointer(&i)) == 0 {
nativeEndian = binary.BigEndian
} else {
nativeEndian = binary.LittleEndian
}
info := SndRcvInfo{}
sndRcvInfoSize = unsafe.Sizeof(info)
}
func toBuf(v interface{}) []byte {
var buf bytes.Buffer
binary.Write(&buf, nativeEndian, v)
return buf.Bytes()
}
func htons(h uint16) uint16 {
if nativeEndian == binary.LittleEndian {
return (h << 8 & 0xff00) | (h >> 8 & 0xff)
}
return h
}
var ntohs = htons
func setNumOstreams(fd, num int) error {
param := InitMsg{
NumOstreams: uint16(num),
}
optlen := unsafe.Sizeof(param)
_, _, err := setsockopt(fd, SCTP_INITMSG, uintptr(unsafe.Pointer(&param)), uintptr(optlen))
return err
}
type SCTPAddr struct {
IP []net.IP
Port int
}
func (a *SCTPAddr) ToRawSockAddrBuf() []byte {
buf := []byte{}
p := htons(uint16(a.Port))
for _, ip := range a.IP {
if ip.To4() != nil {
s := syscall.RawSockaddrInet4{
Family: syscall.AF_INET,
Port: p,
}
copy(s.Addr[:], ip.To4())
buf = append(buf, toBuf(s)...)
} else {
s := syscall.RawSockaddrInet6{
Family: syscall.AF_INET6,
Port: p,
}
copy(s.Addr[:], ip)
buf = append(buf, toBuf(s)...)
}
}
return buf
}
func (a *SCTPAddr) String() string {
var b bytes.Buffer
for n, i := range a.IP {
if a.IP[n].To4() != nil {
b.WriteString(i.String())
} else if a.IP[n].To16() != nil {
b.WriteRune('[')
b.WriteString(i.String())
b.WriteRune(']')
}
if n < len(a.IP)-1 {
b.WriteRune('/')
}
}
b.WriteRune(':')
b.WriteString(strconv.Itoa(a.Port))
return b.String()
}
func (a *SCTPAddr) Network() string { return "sctp" }
func ResolveSCTPAddr(network, addrs string) (*SCTPAddr, error) {
tcpnet := ""
switch network {
case "", "sctp":
case "sctp4":
tcpnet = "tcp4"
case "sctp6":
tcpnet = "tcp6"
default:
return nil, fmt.Errorf("invalid net: %s", network)
}
elems := strings.Split(addrs, "/")
if len(elems) == 0 {
return nil, fmt.Errorf("invalid input: %s", addrs)
}
ipaddrs := make([]net.IP, 0, len(elems))
for _, e := range elems[:len(elems)-1] {
tcpa, err := net.ResolveTCPAddr(tcpnet, e+":")
if err != nil {
return nil, err
}
ipaddrs = append(ipaddrs, tcpa.IP)
}
tcpa, err := net.ResolveTCPAddr(tcpnet, elems[len(elems)-1])
if err != nil {
return nil, err
}
if tcpa.IP != nil {
ipaddrs = append(ipaddrs, tcpa.IP)
} else {
ipaddrs = nil
}
return &SCTPAddr{
IP: ipaddrs,
Port: tcpa.Port,
}, nil
}
func SCTPConnect(fd int, addr *SCTPAddr) (int, error) {
buf := addr.ToRawSockAddrBuf()
param := GetAddrsOld{
AddrNum: int32(len(buf)),
Addrs: uintptr(uintptr(unsafe.Pointer(&buf[0]))),
}
optlen := unsafe.Sizeof(param)
_, _, err := getsockopt(fd, SCTP_SOCKOPT_CONNECTX3, uintptr(unsafe.Pointer(&param)), uintptr(unsafe.Pointer(&optlen)))
if err == nil {
return int(param.AssocID), nil
} else if err != syscall.ENOPROTOOPT {
return 0, err
}
r0, _, err := setsockopt(fd, SCTP_SOCKOPT_CONNECTX, uintptr(unsafe.Pointer(&buf[0])), uintptr(len(buf)))
return int(r0), err
}
func SCTPBind(fd int, addr *SCTPAddr, flags int) error {
var option uintptr
switch flags {
case SCTP_BINDX_ADD_ADDR:
option = SCTP_SOCKOPT_BINDX_ADD
case SCTP_BINDX_REM_ADDR:
option = SCTP_SOCKOPT_BINDX_REM
default:
return syscall.EINVAL
}
buf := addr.ToRawSockAddrBuf()
_, _, err := setsockopt(fd, option, uintptr(unsafe.Pointer(&buf[0])), uintptr(len(buf)))
return err
}
type SCTPConn struct {
_fd int32
notificationHandler NotificationHandler
}
func (c *SCTPConn) fd() int {
return int(atomic.LoadInt32(&c._fd))
}
func NewSCTPConn(fd int, handler NotificationHandler) *SCTPConn {
conn := &SCTPConn{
_fd: int32(fd),
notificationHandler: handler,
}
return conn
}
func (c *SCTPConn) Write(b []byte) (int, error) {
return c.SCTPWrite(b, nil)
}
func (c *SCTPConn) Read(b []byte) (int, error) {
n, _, err := c.SCTPRead(b)
if n < 0 {
n = 0
}
return n, err
}
func (c *SCTPConn) SetInitMsg(numOstreams, maxInstreams, maxAttempts, maxInitTimeout int) error {
param := InitMsg{
NumOstreams: uint16(numOstreams),
MaxInstreams: uint16(maxInstreams),
MaxAttempts: uint16(maxAttempts),
MaxInitTimeout: uint16(maxInitTimeout),
}
optlen := unsafe.Sizeof(param)
_, _, err := setsockopt(c.fd(), SCTP_INITMSG, uintptr(unsafe.Pointer(&param)), uintptr(optlen))
return err
}
func (c *SCTPConn) SubscribeEvents(flags int) error {
var d, a, ad, sf, p, sh, pa, ada, au, se uint8
if flags&SCTP_EVENT_DATA_IO > 0 {
d = 1
}
if flags&SCTP_EVENT_ASSOCIATION > 0 {
a = 1
}
if flags&SCTP_EVENT_ADDRESS > 0 {
ad = 1
}
if flags&SCTP_EVENT_SEND_FAILURE > 0 {
sf = 1
}
if flags&SCTP_EVENT_PEER_ERROR > 0 {
p = 1
}
if flags&SCTP_EVENT_SHUTDOWN > 0 {
sh = 1
}
if flags&SCTP_EVENT_PARTIAL_DELIVERY > 0 {
pa = 1
}
if flags&SCTP_EVENT_ADAPTATION_LAYER > 0 {
ada = 1
}
if flags&SCTP_EVENT_AUTHENTICATION > 0 {
au = 1
}
if flags&SCTP_EVENT_SENDER_DRY > 0 {
se = 1
}
param := EventSubscribe{
DataIO: d,
Association: a,
Address: ad,
SendFailure: sf,
PeerError: p,
Shutdown: sh,
PartialDelivery: pa,
AdaptationLayer: ada,
Authentication: au,
SenderDry: se,
}
optlen := unsafe.Sizeof(param)
_, _, err := setsockopt(c.fd(), SCTP_EVENTS, uintptr(unsafe.Pointer(&param)), uintptr(optlen))
return err
}
func (c *SCTPConn) SubscribedEvents() (int, error) {
param := EventSubscribe{}
optlen := unsafe.Sizeof(param)
_, _, err := getsockopt(c.fd(), SCTP_EVENTS, uintptr(unsafe.Pointer(&param)), uintptr(unsafe.Pointer(&optlen)))
if err != nil {
return 0, err
}
var flags int
if param.DataIO > 0 {
flags |= SCTP_EVENT_DATA_IO
}
if param.Association > 0 {
flags |= SCTP_EVENT_ASSOCIATION
}
if param.Address > 0 {
flags |= SCTP_EVENT_ADDRESS
}
if param.SendFailure > 0 {
flags |= SCTP_EVENT_SEND_FAILURE
}
if param.PeerError > 0 {
flags |= SCTP_EVENT_PEER_ERROR
}
if param.Shutdown > 0 {
flags |= SCTP_EVENT_SHUTDOWN
}
if param.PartialDelivery > 0 {
flags |= SCTP_EVENT_PARTIAL_DELIVERY
}
if param.AdaptationLayer > 0 {
flags |= SCTP_EVENT_ADAPTATION_LAYER
}
if param.Authentication > 0 {
flags |= SCTP_EVENT_AUTHENTICATION
}
if param.SenderDry > 0 {
flags |= SCTP_EVENT_SENDER_DRY
}
return flags, nil
}
func (c *SCTPConn) SetDefaultSentParam(info *SndRcvInfo) error {
optlen := unsafe.Sizeof(*info)
_, _, err := setsockopt(c.fd(), SCTP_DEFAULT_SENT_PARAM, uintptr(unsafe.Pointer(info)), uintptr(optlen))
return err
}
func (c *SCTPConn) GetDefaultSentParam() (*SndRcvInfo, error) {
info := &SndRcvInfo{}
optlen := unsafe.Sizeof(*info)
_, _, err := getsockopt(c.fd(), SCTP_DEFAULT_SENT_PARAM, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(&optlen)))
return info, err
}
func resolveFromRawAddr(ptr unsafe.Pointer, n int) (*SCTPAddr, error) {
addr := &SCTPAddr{
IP: make([]net.IP, n),
}
switch family := (*(*syscall.RawSockaddrAny)(ptr)).Addr.Family; family {
case syscall.AF_INET:
addr.Port = int(ntohs(uint16((*(*syscall.RawSockaddrInet4)(ptr)).Port)))
tmp := syscall.RawSockaddrInet4{}
size := unsafe.Sizeof(tmp)
for i := 0; i < n; i++ {
a := *(*syscall.RawSockaddrInet4)(unsafe.Pointer(
uintptr(ptr) + size*uintptr(i)))
addr.IP[i] = a.Addr[:]
}
case syscall.AF_INET6:
addr.Port = int(ntohs(uint16((*(*syscall.RawSockaddrInet4)(ptr)).Port)))
tmp := syscall.RawSockaddrInet6{}
size := unsafe.Sizeof(tmp)
for i := 0; i < n; i++ {
a := *(*syscall.RawSockaddrInet6)(unsafe.Pointer(
uintptr(ptr) + size*uintptr(i)))
addr.IP[i] = a.Addr[:]
}
default:
return nil, fmt.Errorf("unknown address family: %d", family)
}
return addr, nil
}
func sctpGetAddrs(fd, id, optname int) (*SCTPAddr, error) {
type getaddrs struct {
assocId int32
addrNum uint32
addrs [4096]byte
}
param := getaddrs{
assocId: int32(id),
}
optlen := unsafe.Sizeof(param)
_, _, err := getsockopt(fd, uintptr(optname), uintptr(unsafe.Pointer(&param)), uintptr(unsafe.Pointer(&optlen)))
if err != nil {
return nil, err
}
return resolveFromRawAddr(unsafe.Pointer(&param.addrs), int(param.addrNum))
}
func (c *SCTPConn) SCTPGetPrimaryPeerAddr() (*SCTPAddr, error) {
type sctpGetSetPrim struct {
assocId int32
addrs [128]byte
}
param := sctpGetSetPrim{
assocId: int32(0),
}
optlen := unsafe.Sizeof(param)
_, _, err := getsockopt(c.fd(), SCTP_PRIMARY_ADDR, uintptr(unsafe.Pointer(&param)), uintptr(unsafe.Pointer(&optlen)))
if err != nil {
return nil, err
}
return resolveFromRawAddr(unsafe.Pointer(&param.addrs), 1)
}
func (c *SCTPConn) SCTPLocalAddr(id int) (*SCTPAddr, error) {
return sctpGetAddrs(c.fd(), id, SCTP_GET_LOCAL_ADDRS)
}
func (c *SCTPConn) SCTPRemoteAddr(id int) (*SCTPAddr, error) {
return sctpGetAddrs(c.fd(), id, SCTP_GET_PEER_ADDRS)
}
func (c *SCTPConn) LocalAddr() net.Addr {
addr, err := sctpGetAddrs(c.fd(), 0, SCTP_GET_LOCAL_ADDRS)
if err != nil {
return nil
}
return addr
}
func (c *SCTPConn) RemoteAddr() net.Addr {
addr, err := sctpGetAddrs(c.fd(), 0, SCTP_GET_PEER_ADDRS)
if err != nil {
return nil
}
return addr
}
func (c *SCTPConn) PeelOff(id int) (*SCTPConn, error) {
type peeloffArg struct {
assocId int32
sd int
}
param := peeloffArg{
assocId: int32(id),
}
optlen := unsafe.Sizeof(param)
_, _, err := getsockopt(c.fd(), SCTP_SOCKOPT_PEELOFF, uintptr(unsafe.Pointer(&param)), uintptr(unsafe.Pointer(&optlen)))
if err != nil {
return nil, err
}
return &SCTPConn{_fd: int32(param.sd)}, nil
}
func (c *SCTPConn) SetDeadline(t time.Time) error {
return syscall.EOPNOTSUPP
}
func (c *SCTPConn) SetReadDeadline(t time.Time) error {
return syscall.EOPNOTSUPP
}
func (c *SCTPConn) SetWriteDeadline(t time.Time) error {
return syscall.EOPNOTSUPP
}
type SCTPListener struct {
fd int
m sync.Mutex
}
func (ln *SCTPListener) Addr() net.Addr {
laddr, err := sctpGetAddrs(ln.fd, 0, SCTP_GET_LOCAL_ADDRS)
if err != nil {
return nil
}
return laddr
}
type SCTPSndRcvInfoWrappedConn struct {
conn *SCTPConn
}
func NewSCTPSndRcvInfoWrappedConn(conn *SCTPConn) *SCTPSndRcvInfoWrappedConn {
conn.SubscribeEvents(SCTP_EVENT_DATA_IO)
return &SCTPSndRcvInfoWrappedConn{conn}
}
func (c *SCTPSndRcvInfoWrappedConn) Write(b []byte) (int, error) {
if len(b) < int(sndRcvInfoSize) {
return 0, syscall.EINVAL
}
info := (*SndRcvInfo)(unsafe.Pointer(&b[0]))
n, err := c.conn.SCTPWrite(b[sndRcvInfoSize:], info)
return n + int(sndRcvInfoSize), err
}
func (c *SCTPSndRcvInfoWrappedConn) Read(b []byte) (int, error) {
if len(b) < int(sndRcvInfoSize) {
return 0, syscall.EINVAL
}
n, info, err := c.conn.SCTPRead(b[sndRcvInfoSize:])
if err != nil {
return n, err
}
copy(b, toBuf(info))
return n + int(sndRcvInfoSize), err
}
func (c *SCTPSndRcvInfoWrappedConn) Close() error {
return c.conn.Close()
}
func (c *SCTPSndRcvInfoWrappedConn) LocalAddr() net.Addr {
return c.conn.LocalAddr()
}
func (c *SCTPSndRcvInfoWrappedConn) RemoteAddr() net.Addr {
return c.conn.RemoteAddr()
}
func (c *SCTPSndRcvInfoWrappedConn) SetDeadline(t time.Time) error {
return c.conn.SetDeadline(t)
}
func (c *SCTPSndRcvInfoWrappedConn) SetReadDeadline(t time.Time) error {
return c.conn.SetReadDeadline(t)
}
func (c *SCTPSndRcvInfoWrappedConn) SetWriteDeadline(t time.Time) error {
return c.conn.SetWriteDeadline(t)
}

View file

@ -0,0 +1,227 @@
// +build linux,!386
package sctp
import (
"fmt"
"io"
"net"
"sync/atomic"
"syscall"
"unsafe"
)
func setsockopt(fd int, optname, optval, optlen uintptr) (uintptr, uintptr, error) {
// FIXME: syscall.SYS_SETSOCKOPT is undefined on 386
r0, r1, errno := syscall.Syscall6(syscall.SYS_SETSOCKOPT,
uintptr(fd),
SOL_SCTP,
optname,
optval,
optlen,
0)
if errno != 0 {
return r0, r1, errno
}
return r0, r1, nil
}
func getsockopt(fd int, optname, optval, optlen uintptr) (uintptr, uintptr, error) {
// FIXME: syscall.SYS_GETSOCKOPT is undefined on 386
r0, r1, errno := syscall.Syscall6(syscall.SYS_GETSOCKOPT,
uintptr(fd),
SOL_SCTP,
optname,
optval,
optlen,
0)
if errno != 0 {
return r0, r1, errno
}
return r0, r1, nil
}
func (c *SCTPConn) SCTPWrite(b []byte, info *SndRcvInfo) (int, error) {
var cbuf []byte
if info != nil {
cmsgBuf := toBuf(info)
hdr := &syscall.Cmsghdr{
Level: syscall.IPPROTO_SCTP,
Type: SCTP_CMSG_SNDRCV,
}
// bitwidth of hdr.Len is platform-specific,
// so we use hdr.SetLen() rather than directly setting hdr.Len
hdr.SetLen(syscall.CmsgSpace(len(cmsgBuf)))
cbuf = append(toBuf(hdr), cmsgBuf...)
}
return syscall.SendmsgN(c.fd(), b, cbuf, nil, 0)
}
func parseSndRcvInfo(b []byte) (*SndRcvInfo, error) {
msgs, err := syscall.ParseSocketControlMessage(b)
if err != nil {
return nil, err
}
for _, m := range msgs {
if m.Header.Level == syscall.IPPROTO_SCTP {
switch m.Header.Type {
case SCTP_CMSG_SNDRCV:
return (*SndRcvInfo)(unsafe.Pointer(&m.Data[0])), nil
}
}
}
return nil, nil
}
func (c *SCTPConn) SCTPRead(b []byte) (int, *SndRcvInfo, error) {
oob := make([]byte, 254)
for {
n, oobn, recvflags, _, err := syscall.Recvmsg(c.fd(), b, oob, 0)
if err != nil {
return n, nil, err
}
if n == 0 && oobn == 0 {
return 0, nil, io.EOF
}
if recvflags&MSG_NOTIFICATION > 0 && c.notificationHandler != nil {
if err := c.notificationHandler(b[:n]); err != nil {
return 0, nil, err
}
} else {
var info *SndRcvInfo
if oobn > 0 {
info, err = parseSndRcvInfo(oob[:oobn])
}
return n, info, err
}
}
}
func (c *SCTPConn) Close() error {
if c != nil {
fd := atomic.SwapInt32(&c._fd, -1)
if fd > 0 {
info := &SndRcvInfo{
Flags: SCTP_EOF,
}
c.SCTPWrite(nil, info)
syscall.Shutdown(int(fd), syscall.SHUT_RDWR)
return syscall.Close(int(fd))
}
}
return syscall.EBADF
}
func ListenSCTP(net string, laddr *SCTPAddr) (*SCTPListener, error) {
af := syscall.AF_INET
switch net {
case "sctp":
hasv6 := func(addr *SCTPAddr) bool {
if addr == nil {
return false
}
for _, ip := range addr.IP {
if ip.To4() == nil {
return true
}
}
return false
}
if hasv6(laddr) {
af = syscall.AF_INET6
}
case "sctp4":
case "sctp6":
af = syscall.AF_INET6
default:
return nil, fmt.Errorf("invalid net: %s", net)
}
sock, err := syscall.Socket(
af,
syscall.SOCK_STREAM,
syscall.IPPROTO_SCTP,
)
if err != nil {
return nil, err
}
err = setNumOstreams(sock, SCTP_MAX_STREAM)
if err != nil {
return nil, err
}
if laddr != nil && len(laddr.IP) != 0 {
err := SCTPBind(sock, laddr, SCTP_BINDX_ADD_ADDR)
if err != nil {
return nil, err
}
}
err = syscall.Listen(sock, syscall.SOMAXCONN)
if err != nil {
return nil, err
}
return &SCTPListener{
fd: sock,
}, nil
}
func (ln *SCTPListener) Accept() (net.Conn, error) {
fd, _, err := syscall.Accept4(ln.fd, 0)
return NewSCTPConn(fd, nil), err
}
func (ln *SCTPListener) Close() error {
syscall.Shutdown(ln.fd, syscall.SHUT_RDWR)
return syscall.Close(ln.fd)
}
func DialSCTP(net string, laddr, raddr *SCTPAddr) (*SCTPConn, error) {
af := syscall.AF_INET
switch net {
case "sctp":
hasv6 := func(addr *SCTPAddr) bool {
if addr == nil {
return false
}
for _, ip := range addr.IP {
if ip.To4() == nil {
return true
}
}
return false
}
if hasv6(laddr) || hasv6(raddr) {
af = syscall.AF_INET6
}
case "sctp4":
case "sctp6":
af = syscall.AF_INET6
default:
return nil, fmt.Errorf("invalid net: %s", net)
}
sock, err := syscall.Socket(
af,
syscall.SOCK_STREAM,
syscall.IPPROTO_SCTP,
)
if err != nil {
return nil, err
}
err = setNumOstreams(sock, SCTP_MAX_STREAM)
if err != nil {
return nil, err
}
if laddr != nil {
err := SCTPBind(sock, laddr, SCTP_BINDX_ADD_ADDR)
if err != nil {
return nil, err
}
}
_, err = SCTPConnect(sock, raddr)
if err != nil {
return nil, err
}
return NewSCTPConn(sock, nil), nil
}

View file

@ -0,0 +1,47 @@
// +build !linux linux,386
package sctp
import (
"errors"
"net"
"runtime"
)
var ErrUnsupported = errors.New("SCTP is unsupported on " + runtime.GOOS + "/" + runtime.GOARCH)
func setsockopt(fd int, optname, optval, optlen uintptr) (uintptr, uintptr, error) {
return 0, 0, ErrUnsupported
}
func getsockopt(fd int, optname, optval, optlen uintptr) (uintptr, uintptr, error) {
return 0, 0, ErrUnsupported
}
func (c *SCTPConn) SCTPWrite(b []byte, info *SndRcvInfo) (int, error) {
return 0, ErrUnsupported
}
func (c *SCTPConn) SCTPRead(b []byte) (int, *SndRcvInfo, error) {
return 0, nil, ErrUnsupported
}
func (c *SCTPConn) Close() error {
return ErrUnsupported
}
func ListenSCTP(net string, laddr *SCTPAddr) (*SCTPListener, error) {
return nil, ErrUnsupported
}
func (ln *SCTPListener) Accept() (net.Conn, error) {
return nil, ErrUnsupported
}
func (ln *SCTPListener) Close() error {
return ErrUnsupported
}
func DialSCTP(net string, laddr, raddr *SCTPAddr) (*SCTPConn, error) {
return nil, ErrUnsupported
}