Tag Archives: sctp

SCTP on the internet: More good news

So I’m back after a hiatus, but the good news is that I was finally able to do these network tests more systematically.

If you haven’t read Part 1, please do, it’s essential reading, but a TL;DR refresher is that I tried out SCTP over IPv6 and it worked for more than one of the internet connections where I have IPv6 available.

As a preamble, I must emphasise that somehow, a full 100% of the internet connections that I was able to lay my hands on do indeed support IPv6. This is without any far-sighted selection criterion for catching up to the new now 14-year-old version of the internet protocol, mind you. (And not super-ugly either, with separate IA_NA for the router and IA_PD for the LAN, etc. Albeit delegating only a single /64, but which is arguably not too bad.)

A neater setup

On this note, the story resumed from where I earlier tested connections only between machines I was physically present with. It, however, quickly became a little annoying:

Also, any odd behaviour I encountered always retained an uncertainty about whether it arose from the iperf client ISP or the server ISP. Isolating that required more than one test using different clients/servers, making each test double the work and half the insight.

So I bit the bullet and bought a really cheap IPv6-only VPS (thanks hostbrr!). For less than the price of some of even third-world country coffees, I got a quick, robust, and sophisticated setup experience, and most importantly with zero firewalls of any kind. The end-to-end internet is shocking and almost unnerving to experience first-hand. Just find the globally unique address of any other machine, anything at all in the wide world, and just send packets. There was absolutely nothing to futz around with. So obviously this became the reference machine and my device was acting as the client and then as the server for an iPerf test, allowing me to check internet connections one-by-one and one direction at a time.

Securing the borders a.k.a How to assume you’re valuable on the internet

But first, a few niggling issues had to be tackled, starting with security. While the number of SCTP-aware attackers on the internet is likely to be preeetty low, I was still uncomfortable leaving a default allow on my router firewall for it at all times (All my earlier tests were done by doing this temporarily. But enabling and disabling rules each time had quickly become a pain). Who knows what documented vulnerabilities and zero-days exist across my internal network… Unfortunately, my earlier router’s firewall (an EdgeRouter-X) did not support filtering SCTP traffic by ports, at least with stock firmware. This was because the required kernel module (nf_conntrack.ko) was not compiled for stateful filtering of SCTP out of the box, and needed to be recompiled with different config settings. Along with other issues relating to second-class support for IPv6, this prompted me to jump to OpenWRT which was happily fully supported on the same hardware.

It turned out that while stateful filtering still needed a recompile on OpenWRT as well, stateless filtering worked out of the box due to the move to nftables from iptables! I proceeded with it as an acceptable compromise for now, but ideally the stateful firewall is needed for a production setup. More details here and here on this matter, drop me a comment if you have further questions.

Blackholing a.k.a ghosting

The next big issue is something I referred to in the previous part when my local machine was the server: having to clamp MSS else I would lose packets entirely. This turned out to be pretty simply because of my machine connecting to the internet via PPPoE which added another 8 bytes of packet overhead compared to just plain ethernet. Thus the maximum IPv6 packet size (called the MTU), and in turn the SCTP packet size (the MSS), should have been that much smaller. This should not be a problem on sane networks, but this particular ISP was not informing my VPS, the sender, with the appropriate ICMP message. Instead the packets were simply dropped by the ISP routers on the inbound journey (aptly termed getting “blackholed”). Of course this is not SCTP-specific.

Too fast too soon

The last problem I had was ironically one of SCTP’s headline features, multihoming. You see, in a fit of optimism, I had multiple IPv6 addresses (from different GUA subnets!) available on my client due to both fiber connections being active and delegating their own prefixes to my home network (and both stable and privacy addresses enabled). And the SCTP association setup process by iperf on my local machine included all of them while handshaking (a 4-step process in contrast with TCP’s 3) with the VPS client. This was, for whatever reason, causing the expected next packet of the handshake to never be sent from the client. I haven’t yet investigated what the reason is, but since the packet was never even sent, the network wasn’t at fault. I worked around this issue by specifically asking iperf to bind to a single address, which worked around the problem successfully.

Opening the borders a.k.a how to get out of a cage that’s technically yours

During my tests with the mobile service providers, I used USB tethering (or Wi-Fi hotspot, no difference for our purposes). The Jio “SIM card” happened to be in an iPhone, and it turns out that the iPhone behaves significantly different from Android. The way it works is that the mobile has one IPv6 address but the tethered devices are on a different prefix entirely. And Android has been super-cooperative with me in that it leaves the tethered subnet entirely wide-open to the internet, no filtering. That, however is not the case with iPhone, sadly, which seems to put in some sort of firewall in place. I haven’t dug deep enough into this, for e.g. to deactivate/configure the firewall, I just popped the card to an Android phone temporarily.

The sweet fruit of success

After dealing with all these local device level software issues, I ran tests across 3 of the major mobile providers and 2 FTTH/FTTN wired internet connections available with me. This set of results essentially supersede my results in part 1. When we look at the broader internet i.e. the one we don’t have direct control over, the result is:

TypeLocal connectionAs clientAs server
Fixed-lineACT (FTTN)SuccessSuccess
Fixed-lineBSNL (FTTH)SuccessSuccess
MobileAirtel (LTE)SuccessSuccess
MobileJio (LTE)SuccessSuccess
MobileVi (LTE)SuccessISP-side stateful firewall drops packets

ALL of them allowed iPerf SCTP outbound, and all but 1 allowed it inbound. AND, Vi (LTE) was blocking ALL unsolicited inbound traffic, so not an ossification problem!

This is a massively positive finding: no ossification and no middleboxes. (I’m quite happy to bet quite a bit that SCTP was not explicitly allowlisted in their firewalls 🙂). The Indian IPv6 internet is free and ripe for transport protocol (Layer 4) innovation!

Epilogue

This doesn’t mean I didn’t face challenges, far from it as just described above. Overcoming them required some tenacity, some ugly hacks/workarounds, as well as a measure of luck. But all of that was encountered at the end-points, in my own devices. This is exactly the opposite of what a lot of the IETF literature, internet enthusiasts and blogosphere claim.

What are the next steps? One important thing is to check if multihoming works, which gives us an immediate and attractive reason to keep interest in this. The speed is also significantly lower than what I was able to achieve via TCP, something necessary to fix if it is to be mainstreamed someday. Another thing is to find applications that would benefit from using SCTP directly. This is not an easy problem to tackle, as its double-sided — both a server and client have to support it to work. We will tackle these in part 3. Till then, Sayonara.

SCTP on the internet: A (small) happy surprise

You might have heard about HTTP/3, which is used in on top of QUIC – a spanking-new transport layer protocol that replaces TCP. (If you’re thinking, “waitaminute, didn’t HTTP/2 come out just recently?!”, you’re not alone; its like suddenly there’s an urgency to develop on what was previously iterated slooowwwly and carefully).

The venerable TCP has been around for around 50 years now, but has had revisions throughout the years, some as recently as 2001. With changing patterns of usage (driven primarily by dominance of web traffic), further evolution was found necessary. The consensus view is that TCP today has been “ossified” in its current configurations by network middle-boxes. Hence QUIC, proposed and developed in large part by Google in the last decade, is intended to replace TCP, at least for web uses. So clearly, there was no other candidate protocol… right?

There was a choice developed in 90s, and standardised for around 20 years now. Stream Control Transport Protocol (SCTP) was originally intended for PSTN (telephone) signalling, but is a general-purpose modern transport protocol. It supports streams, one of the headline features of QUIC (and HTTP/2 as well), and multi-homing1, a feature that is not yet supported by QUIC. So why did SCTP not replace TCP?

For the same reason QUIC has to be tunnelled over UDP (it is defined as such in the QUIC specification, it’s not a choice). SCTP is one of the poster-children of protocol-ossification — network equipment that you don’t control sitting between you and your desired content, interfering with protocols in ways you can’t usually change such that you’re forced to use the lowest common-denominator protocol. The biggest of these ways is to only pass-through TCP (and, at best UDP), branding everything else as unusual and simply dropping it in the name of security.

IPv6: the game-changer

But what about IPv6? IPv6 does not need or want NA(P)T. The question arises — would SCTP face the same problem on IPv6? Sometimes, the right answer is to just try it out and see what happens and how bad it is…

To test whether SCTP works or not, I’ll be using iperf3. First step is to use SCTP in a local, private network – good things begin at home.

Using Fedora 38 on both ends, I had to do the following to get iperf to connect and test successfully:

  • Enable kernel SCTP module with modprobe sctp
  • Allow TCP and SCTP port 5201 in the firewall — the TCP port is to allow iperf to do some apparently necessary coordination b/w client and server before the actual bulk transfer test

Here’s how that looks

[milind@desktop-linux ~]$ iperf3 -c 192.168.1.212 --sctp
Connecting to host 192.168.1.212, port 5201
[  5] local 192.168.1.107 port 58950 connected to 192.168.1.212 port 5201
[ ID] Interval           Transfer     Bitrate
[  5]   0.00-1.00   sec  66.2 MBytes   556 Mbits/sec                  
[  5]   1.00-2.00   sec  67.0 MBytes   562 Mbits/sec                  
[  5]   2.00-3.00   sec  72.5 MBytes   608 Mbits/sec                  
[  5]   3.00-4.00   sec  70.1 MBytes   588 Mbits/sec                  
[  5]   4.00-5.00   sec  66.7 MBytes   560 Mbits/sec                  
[  5]   5.00-6.00   sec  65.2 MBytes   547 Mbits/sec                  
[  5]   6.00-7.00   sec  57.9 MBytes   486 Mbits/sec                  
[  5]   7.00-8.00   sec  63.2 MBytes   530 Mbits/sec                  
[  5]   8.00-9.00   sec  68.4 MBytes   574 Mbits/sec                  
[  5]   9.00-10.00  sec  63.2 MBytes   531 Mbits/sec                  
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate
[  5]   0.00-10.00  sec   661 MBytes   554 Mbits/sec                  sender
[  5]   0.00-10.00  sec   660 MBytes   554 Mbits/sec                  receiver

iperf Done.

Well it works, phew, but why such low transfer throughput? Here’s how it looks for TCP

[milind@desktop-linux ~]$ iperf3 -c 192.168.1.212
Connecting to host 192.168.1.212, port 5201
[  5] local 192.168.1.107 port 33508 connected to 192.168.1.212 port 5201
[ ID] Interval           Transfer     Bitrate         Retr  Cwnd
[  5]   0.00-1.00   sec   116 MBytes   970 Mbits/sec    0    662 KBytes       
[  5]   1.00-2.00   sec   112 MBytes   944 Mbits/sec    0    662 KBytes       
[  5]   2.00-3.00   sec   111 MBytes   933 Mbits/sec    0    696 KBytes       
[  5]   3.00-4.00   sec   112 MBytes   944 Mbits/sec    0    696 KBytes       
[  5]   4.00-5.00   sec   112 MBytes   944 Mbits/sec    0    757 KBytes       
[  5]   5.00-6.00   sec   112 MBytes   944 Mbits/sec    0    757 KBytes       
[  5]   6.00-7.00   sec   111 MBytes   933 Mbits/sec    0    757 KBytes       
[  5]   7.00-8.00   sec   112 MBytes   944 Mbits/sec    0    757 KBytes       
[  5]   8.00-9.00   sec   112 MBytes   944 Mbits/sec    0    757 KBytes       
[  5]   9.00-10.00  sec   112 MBytes   944 Mbits/sec    0    757 KBytes       
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.00  sec  1.10 GBytes   944 Mbits/sec    0             sender
[  5]   0.00-10.00  sec  1.10 GBytes   941 Mbits/sec                  receiver

iperf Done.

What gives? UDP?

[milind@desktop-linux ~]$ iperf3 -c 192.168.1.212 --udp
Connecting to host 192.168.1.212, port 5201
[  5] local 192.168.1.107 port 51952 connected to 192.168.1.212 port 5201
[ ID] Interval           Transfer     Bitrate         Total Datagrams
[  5]   0.00-1.00   sec   129 KBytes  1.05 Mbits/sec  91  
[  5]   1.00-2.00   sec   127 KBytes  1.04 Mbits/sec  90  
[  5]   2.00-3.00   sec   129 KBytes  1.05 Mbits/sec  91  
[  5]   3.00-4.00   sec   127 KBytes  1.04 Mbits/sec  90  
[  5]   4.00-5.00   sec   129 KBytes  1.05 Mbits/sec  91  
[  5]   5.00-6.00   sec   129 KBytes  1.05 Mbits/sec  91  
[  5]   6.00-7.00   sec   127 KBytes  1.04 Mbits/sec  90  
[  5]   7.00-8.00   sec   129 KBytes  1.05 Mbits/sec  91  
[  5]   8.00-9.00   sec   127 KBytes  1.04 Mbits/sec  90  
[  5]   9.00-10.00  sec   129 KBytes  1.05 Mbits/sec  91  
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Jitter    Lost/Total Datagrams
[  5]   0.00-10.00  sec  1.25 MBytes  1.05 Mbits/sec  0.000 ms  0/906 (0%)  sender
[  5]   0.00-10.00  sec  1.25 MBytes  1.05 Mbits/sec  0.027 ms  0/906 (0%)  receiver

iperf Done.

Uhm, so much for a gigabit link through a 2.5g switch. Something is clearly up…

Okay, so it works on my local network at home. Big deal. That isn’t the “internet” by any stretch of imagination. Alright then, onto the next step. I guess I’m lucky that my ISP gives me a single full /64 IPv6 subnet of addresses (although it’s “not official” and I had to figure out how to enable it myself). With that I venture forth with no interest in legacy technologies like IPv4 <flips non-existent long hair backward arrogantly>.

So I got a friend with a Raspberry Pi to hook it to an LTE (with an Airtel SIM) USB modem (usually called a “dongle”) and configured it to not bother with any intermediate shenanigans routing2. I SSHed into it (Yep, that was also over IPv6!) to control and observe both sides. I then tried the same experiment over IPv6 using SCTP. With bated breath

[milind@desktop-linux ~]$ iperf3 -c sctptest.sampleddns.net -6 --sctp -M 1000
Connecting to host sctptest.sampleddns.net, port 5201
[  5] local 2406:7400:9f:xxxx:xxxx:xxxx:xxxx:xxxx port 39980 connected to 2401:4900:yyyy:yyyy:yyyy:yyyy:yyyy:yyyy port 5201
[ ID] Interval           Transfer     Bitrate
[  5]   0.00-1.00   sec   832 KBytes  6.81 Mbits/sec                  
[  5]   1.00-2.00   sec  1.38 MBytes  11.5 Mbits/sec                  
[  5]   2.00-3.00   sec  1.38 MBytes  11.5 Mbits/sec                  
[  5]   3.00-4.00   sec  1.31 MBytes  11.0 Mbits/sec                  
[  5]   4.00-5.00   sec  1.31 MBytes  11.0 Mbits/sec                  
[  5]   5.00-6.00   sec  1.19 MBytes  9.96 Mbits/sec                  
[  5]   6.00-7.00   sec  1.06 MBytes  8.91 Mbits/sec                  
[  5]   7.00-8.00   sec  1.38 MBytes  11.5 Mbits/sec                  
[  5]   8.00-9.00   sec  1.31 MBytes  11.0 Mbits/sec                  
[  5]   9.00-10.00  sec  1.38 MBytes  11.5 Mbits/sec                  
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate
[  5]   0.00-10.00  sec  12.5 MBytes  10.5 Mbits/sec                  sender
[  5]   0.00-10.02  sec  12.4 MBytes  10.4 Mbits/sec                  receiver

So SCTP over the open internet works!! (Note: hostname shown is faked)

On one of the tries, I had to change the Maximum Segment Size (MSS) to 1000 bytes instead of 1424 that it assumed to begin with. Some experimentation showed the max MSS to be 1283 — significance is as yet unknown. However, on another try on a different day it worked without any such parameter. Further digging required, clearly.

Another trial, this time using a hotspot from my phone’s LTE (by Vodafone-Idea, or as they glibly call themselves, “Vi”) connection to this Raspberry Pi.

[milind@desktop-linux ~]$ iperf3 -c sctptest.sampleddns.net --sctp
Connecting to host sctptest.sampleddns.net, port 5201
[  5] local 2402:8100:25xx:xxxx:xxxx:xxxx:xxxx:xxxx port 42110 connected to 2401:4900:yyyy:yyyy:yyyy:yyyy:yyyy:yyyy port 5201
[ ID] Interval           Transfer     Bitrate
[  5]   0.00-1.00   sec   128 KBytes  1.05 Mbits/sec                  
[  5]   1.00-2.00   sec  64.0 KBytes   525 Kbits/sec                  
[  5]   2.00-3.00   sec   128 KBytes  1.05 Mbits/sec                  
[  5]   3.00-4.00   sec   256 KBytes  2.10 Mbits/sec                  
[  5]   4.00-5.00   sec   192 KBytes  1.57 Mbits/sec                  
[  5]   5.00-6.00   sec   192 KBytes  1.57 Mbits/sec                  
[  5]   6.00-7.00   sec   192 KBytes  1.57 Mbits/sec                  
[  5]   7.00-8.00   sec   192 KBytes  1.57 Mbits/sec                  
[  5]   8.00-9.00   sec   128 KBytes  1.05 Mbits/sec                  
[  5]   9.00-10.00  sec   192 KBytes  1.57 Mbits/sec                  
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate
[  5]   0.00-10.00  sec  1.62 MBytes  1.36 Mbits/sec                  sender
[  5]   0.00-10.26  sec  1.56 MBytes  1.28 Mbits/sec                  receiver

iperf Done.

Trying it the other way round (from RPi to desktop) didn’t work at first, either because a firewall somewhere is “protecting” me, or because there truly are middle-boxes at play… ?

To complete the triangle, I also ran the test on its third leg, between my desktop on FTTN and laptop on the LTE-fed hotspot. I could not get it to work with desktop as server and laptop as client, until I tried opening the port on my trusty Ubiquiti EdgeRouterX firewall (I’m not going to show it here, both because it occupies space and because I’m embarrassed at the low speed of Wi-Fi on the laptop.)

Switching back to the RPi on Airtel as a client and my desktop as a server, it was successful, showing it was indeed my firewall blocking connections.

ISP, Connection methodClient ↓ \ Server →DesktopLaptopRaspberry Pi
ACT Fibernet, PPPoE DesktopNoYes
Vi, LTE hotspot hosted by phoneLaptopYesYes
Airtel, LTE directRaspberry PiYesNo
Summary of my SCTP experiments over the internet

Overall the score is 2/3 internet connections work okay, and one may or may not be affected by Android’s firewall.

So there’s something to this after all, eh? Other than the MSS, I encountered issues only with firewalls on my devices — hardly “middle-boxes”.

The next step is to of course verify that Vi LTE also really does support SCTP on IPv6. If that is the case, then a good test (which I hope to do “soon”) would be to verify among all the major mobile operators and the major wired ISPs which do support IPv6. UPDATE: Check out further findings in Part 2.

To be sure, this is a very small sample set, and IPv6 is not exactly dominant on the internet yet. But it does show that sometimes the Right Thing ™ happens even without explicit intent, and we should be attentive to such opportunities to grab them. The real lesson is to not give up before even trying.


  1. I must take a sentence out to forcefully recommend Avery Pennarun’s article on networking. Nominally it is about IPv6, but each digression grants an interested reader with deep insight; it is the very best on the topic. ↩︎
  2. Which meant that on IPv4 I got a single “public” address directly from the ISP, which was of course lovingly CGNATed. ↩︎