Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support SCTP port mapping #1825

Merged
merged 1 commit into from
Feb 16, 2018
Merged

Support SCTP port mapping #1825

merged 1 commit into from
Feb 16, 2018

Conversation

ishidawataru
Copy link
Contributor

@ishidawataru ishidawataru commented Jun 29, 2017

Introducing support for SCTP(Stream Control Transmission Protocol) port mapping.

SCTP support is necessary to run S1AP and Diameter applications in docker containers.

Related issue : moby/moby#9689

@AkihiroSuda
Copy link
Member

cc @aboch @mavenugo @sanimej @fcrisciani PTAL

@AkihiroSuda
Copy link
Member

also cc @aaronlehmann (moby/swarmkit#2298)

@ishidawataru
Copy link
Contributor Author

@aboch @mavenugo @sanimej @fcrisciani @aaronlehmann Any comments?
Lack of SCTP port mapping support prevents from running most of the mobile telecom applications on docker. With this feature, we will be able to deploy/test http://git.osmocom.org/ or https://gitlab.eurecom.fr/oai/openair-cn much more easily using docker.

@sanimej
Copy link

sanimej commented Jul 14, 2017

@ishidawataru Thanks. Does SCTP have any dependencies on kernel version ? What distro/kernel version has this been tested with ?

@ishidawataru
Copy link
Contributor Author

@sanimej Thanks for your comment.

Linux has built-in support for the SCTP since the 2.6 kernel, so I believe there is no dependency issue for docker users.
I'm testing with Ubuntu 16.04.2. If there is other distro I should test, please let me know.

@ishidawataru
Copy link
Contributor Author

@sanimej ping

size := m.Size()
data = make([]byte, size)
n, err := m.MarshalTo(data)
dAtA = make([]byte, size)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

was this a deliberate change?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, never mind this is generated code, my eye just fell on it 😅

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@thaJeztah Thanks for your review! Do you have any other comments?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not right now; it's not really my expertise, but I was checking up on the status of this PR (as it's a requirement for your other PR's)

I'll try getting it reviewed, but the networking team is quite busy at the moment, so doing my best 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@thaJeztah Right, this needs to get merged first for the SCTP port mapping feature.
Thanks a lot for your help! I totally understand the networking team is quite busy 😅

@ishidawataru
Copy link
Contributor Author

@aboch @mavenugo @sanimej @fcrisciani @aaronlehmann @thaJeztah Any chance you can take a look and review this PR?

@thaJeztah
Copy link
Member

ping @mavenugo (who I think was going to look into this PR)

@mavenugo
Copy link
Contributor

@ishidawataru @thaJeztah yes. I will take a look at it as soon as other higher priority activities are handled.

@AkihiroSuda
Copy link
Member

@mavenugo ping 😃

@AkihiroSuda
Copy link
Member

Can we move this forward?

ping libnetwork maintainers: @aboch @LK4D4 @icecrime @mavenugo @sanimej @chunchun @fcrisciani

cc those who made comments in moby/moby#9689 @rickhofstede @scottstamp @duglin @thaJeztah @razaborg

Anyone please take a look? 😄

@orbsfoc
Copy link

orbsfoc commented Nov 1, 2017

any chance of this getting merged in the near future?

@rkbug
Copy link

rkbug commented Jan 9, 2018

Any update on this? :)

@Peter-eid
Copy link

@aboch @mavenugo @sanimej @fcrisciani @aaronlehmann What is the current status of this feature ?

@thaJeztah
Copy link
Member

ping @ddebroy PTAL

@AkihiroSuda
Copy link
Member

rebased

@codecov-io
Copy link

codecov-io commented Feb 2, 2018

Codecov Report

❗ No coverage uploaded for pull request base (master@20dd462). Click here to learn what that means.
The diff coverage is 18.91%.

Impacted file tree graph

@@            Coverage Diff            @@
##             master    #1825   +/-   ##
=========================================
  Coverage          ?   40.47%           
=========================================
  Files             ?      139           
  Lines             ?    22314           
  Branches          ?        0           
=========================================
  Hits              ?     9032           
  Misses            ?    11960           
  Partials          ?     1322
Impacted Files Coverage Δ
types/types.go 41.4% <0%> (ø)
cmd/proxy/main.go 0% <0%> (ø)
portmapper/mapper.go 65.24% <0%> (ø)
portmapper/proxy.go 33.33% <0%> (ø)
service_linux.go 3.23% <0%> (ø)
agent.pb.go 0.72% <0.69%> (ø)
portallocator/portallocator.go 81.08% <100%> (ø)
drivers/bridge/port_mapping.go 66.66% <100%> (ø)
cmd/proxy/proxy.go 80% <100%> (ø)
cmd/proxy/sctp_proxy.go 77.55% <77.55%> (ø)
... and 1 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 20dd462...1fced67. Read the comment docs.

@fcrisciani fcrisciani requested review from fcrisciani, ddebroy and a user February 2, 2018 18:18
to.Close()
wg.Done()
}

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't appear that 'dir' is used here. Should it be removed and the invocations below adjusted?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, thought the placement would show more context. I mean the "dir" parameter of the broker function 7 lines up.

Copy link

@fcrisciani fcrisciani left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some comments.
Also did not finish yet the review of the vendored files

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:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you mean that if the port is not specified is ephemeral?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes. the comment is taken from tcp_proxy.go.

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)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please change this to use logrus package.
es: logrus.WithError(err).Errorf("[...]")

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, cmd/proxy package does not use logrus.
Not sure why, probably for reducing dependency.

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)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here use logrus

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above, cmd/proxy looks intentionally avoiding using logrus.

backendC := sctp.NewSCTPSndRcvInfoWrappedConn(backend)

var wg sync.WaitGroup
var broker = func(to, from net.Conn, dir bool) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dir is unused, please remove it

}

if useProxy {
m.userlandProxy, err = newProxy(proto, hostIP, allocatedHostPort, container.(*sctp.SCTPAddr).IP[0], container.(*sctp.SCTPAddr).Port, pm.proxyPath)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

before accessing the first element should you check the len?

addr := &sctp.SCTPAddr{IP: []net.IP{hostIP}, Port: hostPort}
return &dummyProxy{addr: addr}
default:
panic(fmt.Errorf("Unknown addr type: %s", proto))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also here better to use logrus

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also wonder if a panic is correct here?

return buf.Bytes()
}

func htons(h uint16) uint16 {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do you need to redefine this? why not using the standard: binary.BigEndian.PutUint16? Network order is bigendian

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I chose to let binary.Write() to serialize each field of golang structures, we need htons() which returns big endian format of uint16 to put to the structures.
If we serialize each field of the structure by hand, we can use binary.BigEndian.PutUint16() as you suggest.

sndRcvInfoSize = unsafe.Sizeof(info)
}

func toBuf(v interface{}) []byte {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

following the comment below, if you make sure that the network format is respected, this function will have to simply read bigEndian format and the machine itself will take care of storing it with the proper endianess allowing you to delete all the dependency with the endianess discovery and data manipulation

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

toBuf() is not used for endian handling but for serializing golang structure to []byte.
I chose to use binary.Write() instead of using unsafe or serializing each field by hand using PutUint8() etc.

}

func (a *SCTPAddr) ToRawSockAddrBuf() []byte {
buf := []byte{}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's better here to have a defined length, not doing so will create a lot of following allocation for every append.
From the code looks like the worst case the size will be len(a.IP) * 16Bytes (ipv6 len). If you think that the IPv6 case is not so common, maybe you can allocate at least the space for the IPv4 so that the append to them won't require any extra allocation during the append

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We (@ishidawataru and me) believe this overhead is negligible, as a.IP should not contain so many entries.
So we'd like to keep the current code for simplicity.


for n, i := range a.IP {
if a.IP[n].To4() != nil {
b.WriteRune('/')

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from the documentation of net.Addr looks like the format that they are suggesting in the example is like: (for example, "192.0.2.1:25", "[2001:db8::1]:80") this does not seem to match the suggested format

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://golang.org/pkg/net/#Addr

The two methods Network and String conventionally return strings that can be passed as the arguments to Dial, but the exact form and meaning of the strings is up to the implementation.

I think this SCTP-specific form is ok.
@ishidawataru WDYT?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I missed something, but I think that this looks like the same form as suggested by net.Addr. The difference is that this is a list of addresses instead of a single address and so they get separated by '/'s. Until I saw the last line of the function I didn't realize however, that the leading '/' that gets put in the buffer gets omitted from the final string.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

opened ishidawataru/sctp#10 for improving readability

return nil, fmt.Errorf("invalid input: %s", addrs)
}
ipaddrs := make([]net.IP, 0, len(elems))
for _, e := range elems[:len(elems)-1] {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't this start from 1? from above looks like the string starts with /

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above. The starting '/' built in the string gets trimmed.


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 {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if you remove all these if and you merge everything in the init down like:

param := EventSubscribe{
		DataIO:          uint8(flags&SCTP_EVENT_DATA_IO > 0),
		Association:     uint8(flags&SCTP_EVENT_ASSOCIATION > 0),

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Go doesn't allow conversion from bool to int by cast. From what I can see on the mailing lists it was requested around go 1.5, but declined. Pity...

@@ -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}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: should probably update the comment at the start of the function to include SCTP.

if err != nil {
t.Fatal(err)
}
server = &TCPEchoServer{listener: listener, testCtx: t}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Should this structure be renamed now? It's no longer just a "TCP" echo server. Maybe "StreamEchoServer"...


for n, i := range a.IP {
if a.IP[n].To4() != nil {
b.WriteRune('/')
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I missed something, but I think that this looks like the same form as suggested by net.Addr. The difference is that this is a list of addresses instead of a single address and so they get separated by '/'s. Until I saw the last line of the function I didn't realize however, that the leading '/' that gets put in the buffer gets omitted from the final string.

return nil, fmt.Errorf("invalid input: %s", addrs)
}
ipaddrs := make([]net.IP, 0, len(elems))
for _, e := range elems[:len(elems)-1] {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above. The starting '/' built in the string gets trimmed.


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 {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Go doesn't allow conversion from bool to int by cast. From what I can see on the mailing lists it was requested around go 1.5, but declined. Pity...

}

func resolveFromRawAddr(ptr unsafe.Pointer, n int) (*SCTPAddr, error) {
addr := &SCTPAddr{
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not checking the boundary of the underlying buffer makes me a bit nervous on principle, but I suppose we generally trust the kernel.

"net"
)

var ErrUnsupported = errors.New("operation unsupported")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a perfectly reasonable discipline for building cleanly on other platforms in general. But from the docker/libnetwork perspective, I'm concerned about two things:

  • This error message is way too generic -- if libnetwork just returns this and the caller prints the string there's no context to say what isn't supported. At the very least perhaps the string should be changed to "SCTP not supported on this platform" or some such.
  • This file is fairly deep in the call stack to be flagging lack of support as far as libnetwork goes. Ideally either libnetwork or moby itself should flag early that an SCTP operation isn't supported by "this version" on "this platform". It kind of seems wrong to, for example, try to create an SCTP proxy thinking this is perfectly valid up until we attempt to bind a listening socket. Maybe I'm just being overly cautious on this one though.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Signed-off-by: Wataru Ishida <ishida.wataru@lab.ntt.co.jp>
Signed-off-by: Akihiro Suda <suda.akihiro@lab.ntt.co.jp>
@AkihiroSuda
Copy link
Member

@ghost @fcrisciani @thaJeztah

Thank you a lot for reviewing this PR.

Addressed the comments. PTAL.

cc @ishidawataru

@thaJeztah
Copy link
Member

Hm; @ghost was @ctelfer-docker ... @ctelfer did you remove that account?

Copy link
Contributor

@ctelfer ctelfer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They addressed all of my concerns completely: LGTM.

(note: sorry for the confusion, but the "ghost" comments on this PR were mine. Switched accounts mid-review.)

Copy link

@fcrisciani fcrisciani left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@fcrisciani fcrisciani merged commit ed2130d into moby:master Feb 16, 2018
@AkihiroSuda
Copy link
Member

Thanks for merging!

@fcrisciani
Copy link

@AkihiroSuda thanks for contributing and addressing the comments

@AkihiroSuda
Copy link
Member

updated moby-side PR moby/moby#33922

@verizonold
Copy link

@mavenugo I am new to docker support for SCTP apps. Can you please point me to some documentation on how docker supports SCTP apps?

@thaJeztah
Copy link
Member

@verizonold see moby/moby#9689 (comment) for links to all related PR's (including some docs)

@Vishesh-GIT
Copy link

I am very much looking for the SCTP support on docker. I am following this thread from long. can anybody give info on when it will be available with docker installation or if I clone this source code how can it be used in my system so that I can test SCTP support

@Peter-eid
Copy link

Peter-eid commented Mar 23, 2018

@Vishesh-GIT SCTP support is added to the latest Docker release 18.03.0-ce, just update and use it.

@verizonold
Copy link

verizonold commented Mar 23, 2018 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.