diff --git a/libnetwork/agent.pb.go b/libnetwork/agent.pb.go index 5b969232a7..54a46111a9 100644 --- a/libnetwork/agent.pb.go +++ b/libnetwork/agent.pb.go @@ -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, } diff --git a/libnetwork/agent.proto b/libnetwork/agent.proto index 54c71c0e2a..a069e98fdd 100644 --- a/libnetwork/agent.proto +++ b/libnetwork/agent.proto @@ -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 diff --git a/libnetwork/cmd/proxy/main.go b/libnetwork/cmd/proxy/main.go index fb68796148..3d51de6fb7 100644 --- a/libnetwork/cmd/proxy/main.go +++ b/libnetwork/cmd/proxy/main.go @@ -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) } diff --git a/libnetwork/cmd/proxy/network_proxy_test.go b/libnetwork/cmd/proxy/network_proxy_test.go index be7f8754ce..38b5dce69a 100644 --- a/libnetwork/cmd/proxy/network_proxy_test.go +++ b/libnetwork/cmd/proxy/network_proxy_test.go @@ -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) +} diff --git a/libnetwork/cmd/proxy/proxy.go b/libnetwork/cmd/proxy/proxy.go index 67574afa5e..b04bd88da0 100644 --- a/libnetwork/cmd/proxy/proxy.go +++ b/libnetwork/cmd/proxy/proxy.go @@ -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") } diff --git a/libnetwork/cmd/proxy/sctp_proxy.go b/libnetwork/cmd/proxy/sctp_proxy.go new file mode 100644 index 0000000000..ce00fdb05d --- /dev/null +++ b/libnetwork/cmd/proxy/sctp_proxy.go @@ -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 } diff --git a/libnetwork/drivers/bridge/port_mapping.go b/libnetwork/drivers/bridge/port_mapping.go index 48010e9c59..853129fc27 100644 --- a/libnetwork/drivers/bridge/port_mapping.go +++ b/libnetwork/drivers/bridge/port_mapping.go @@ -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)) diff --git a/libnetwork/drivers/bridge/port_mapping_test.go b/libnetwork/drivers/bridge/port_mapping_test.go index 057ecfecb1..23df710388 100644 --- a/libnetwork/drivers/bridge/port_mapping_test.go +++ b/libnetwork/drivers/bridge/port_mapping_test.go @@ -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") } diff --git a/libnetwork/iptables/iptables.go b/libnetwork/iptables/iptables.go index 5518fcb278..b8126248b4 100644 --- a/libnetwork/iptables/iptables.go +++ b/libnetwork/iptables/iptables.go @@ -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. diff --git a/libnetwork/portallocator/portallocator.go b/libnetwork/portallocator/portallocator.go index b7f790bed1..9798d23eb1 100644 --- a/libnetwork/portallocator/portallocator.go +++ b/libnetwork/portallocator/portallocator.go @@ -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 diff --git a/libnetwork/portmapper/mapper.go b/libnetwork/portmapper/mapper.go index f480edae03..7fa37b1fb6 100644 --- a/libnetwork/portmapper/mapper.go +++ b/libnetwork/portmapper/mapper.go @@ -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 } diff --git a/libnetwork/portmapper/proxy.go b/libnetwork/portmapper/proxy.go index 45df200e96..1183c33a7e 100644 --- a/libnetwork/portmapper/proxy.go +++ b/libnetwork/portmapper/proxy.go @@ -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) } diff --git a/libnetwork/service_linux.go b/libnetwork/service_linux.go index d8a95b2f50..f8de3a1159 100644 --- a/libnetwork/service_linux.go +++ b/libnetwork/service_linux.go @@ -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"}, } { diff --git a/libnetwork/types/types.go b/libnetwork/types/types.go index 164b18096c..cb18f054c5 100644 --- a/libnetwork/types/types.go +++ b/libnetwork/types/types.go @@ -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 } diff --git a/libnetwork/vendor.conf b/libnetwork/vendor.conf index 62ebec5099..31dc0b906c 100644 --- a/libnetwork/vendor.conf +++ b/libnetwork/vendor.conf @@ -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 diff --git a/libnetwork/vendor/github.com/ishidawataru/sctp/LICENSE b/libnetwork/vendor/github.com/ishidawataru/sctp/LICENSE new file mode 100644 index 0000000000..8dada3edaf --- /dev/null +++ b/libnetwork/vendor/github.com/ishidawataru/sctp/LICENSE @@ -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. diff --git a/libnetwork/vendor/github.com/ishidawataru/sctp/README.md b/libnetwork/vendor/github.com/ishidawataru/sctp/README.md new file mode 100644 index 0000000000..574ececa86 --- /dev/null +++ b/libnetwork/vendor/github.com/ishidawataru/sctp/README.md @@ -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 +``` diff --git a/libnetwork/vendor/github.com/ishidawataru/sctp/sctp.go b/libnetwork/vendor/github.com/ishidawataru/sctp/sctp.go new file mode 100644 index 0000000000..cac1a889ca --- /dev/null +++ b/libnetwork/vendor/github.com/ishidawataru/sctp/sctp.go @@ -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(¶m)), 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(¶m)), 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(¶m)), 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(¶m)), 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(¶m)), 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(¶m)), uintptr(unsafe.Pointer(&optlen))) + if err != nil { + return nil, err + } + return resolveFromRawAddr(unsafe.Pointer(¶m.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(¶m)), uintptr(unsafe.Pointer(&optlen))) + if err != nil { + return nil, err + } + return resolveFromRawAddr(unsafe.Pointer(¶m.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(¶m)), 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) +} diff --git a/libnetwork/vendor/github.com/ishidawataru/sctp/sctp_linux.go b/libnetwork/vendor/github.com/ishidawataru/sctp/sctp_linux.go new file mode 100644 index 0000000000..f93ab8622a --- /dev/null +++ b/libnetwork/vendor/github.com/ishidawataru/sctp/sctp_linux.go @@ -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 +} diff --git a/libnetwork/vendor/github.com/ishidawataru/sctp/sctp_unsupported.go b/libnetwork/vendor/github.com/ishidawataru/sctp/sctp_unsupported.go new file mode 100644 index 0000000000..adcbf78b46 --- /dev/null +++ b/libnetwork/vendor/github.com/ishidawataru/sctp/sctp_unsupported.go @@ -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 +}