├── README.md ├── c ├── linux │ ├── Makefile │ ├── README.md │ └── mptcphello.c └── macOS │ ├── Makefile │ ├── README.md │ └── main.c ├── elixir ├── .formatter.exs ├── .gitignore ├── README.md ├── lib │ └── mptcp.ex └── mix.exs ├── erlang ├── README.md └── mptcp.erl ├── objective-c ├── README.md └── src │ ├── NSURLSession+sharedMPTCPSession.h │ ├── NSURLSession+sharedMPTCPSession.m │ ├── NSURLSessionConfiguration+default.h │ ├── NSURLSessionConfiguration+default.m │ └── main.m ├── perl ├── README.md └── mptcp-hello.pl ├── python ├── README.md └── mptcp-hello.py ├── rust ├── .gitignore ├── README.md ├── hyper_server │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ │ └── main.rs └── socket2 │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ └── main.rs └── swift ├── iOS ├── README.md └── src │ ├── MainApp.swift │ ├── URLSession+sharedMPTCP.swift │ └── URLSessionConfiguration+defaultMPTCP.swift └── macOS ├── README.md └── src ├── MPTCPClient.swift └── MainApp.swift /README.md: -------------------------------------------------------------------------------- 1 | # Hello Multipath TCP! 2 | 3 | Multipath TCP (MPTCP) is a TCP extension specified in [RFC8684](https://www.rfc-editor.org/rfc/rfc8684.html) that allows a TCP connection to use different paths such as Wi-Fi and cellular on smartphones or Wi-Fi and Ethernet on laptops. Multipath TCP is supported by Linux kernel versions >=5.6 and enabled on many popular distributions. It is also possible for applications to use it on iOS >= 11 for the client side only. 4 | 5 | The utilization of Multipath TCP is negotiated during the TCP three-way handshake. This implies that if a Multipath TCP client contacts a regular TCP server, they will use a regular TCP connection. Similarly, if a regular TCP client contacts a Multipath TCP server, they will use a regular TCP connection. Multipath TCP will only be used if both the client and the server support Multipath TCP. 6 | 7 | Enabling Multipath TCP in an applications on an operating system supporting it is easy: MPTCP has to be enabled before creating the connection. On Linux, applications must pass `IPPROTO_MPTCP` as the third parameter of the [socket()](https://www.man7.org/linux/man-pages/man3/socket.3p.html) system call. On iOS, the [`MultiPath Service`](https://developer.apple.com/documentation/foundation/nsurlsessionmultipathservicetype) should be enabled. For more details about that: please check the [mptcp.dev](https://mptcp.dev) website. 8 | 9 | The following examples show how enable Multipath TCP with different programming languages: 10 | 11 | - On Linux: 12 | - [Using Multipath TCP in C](c/linux/README.md) 13 | - [Using Multipath TCP in Elixir](elixir/README.md) 14 | - [Using Multipath TCP in Erlang](erlang/README.md) 15 | - [Using Multipath TCP in python](python/README.md) 16 | - [Using Multipath TCP in perl](perl/README.md) 17 | - [Using Multipath TCP in Rust](rust/README.md) 18 | 19 | - On macOS/iOS: 20 | - [Using Multipath TCP in C](c/macOS/README.md) 21 | - [Using Multipath TCP in Swift](swift/macOS/README.md) 22 | 23 | - On iOS only: 24 | - [Using Multipath TCP in objective-C](objective-c/README.md) 25 | - [Using Multipath TCP in Swift](swift/iOS/README.md) 26 | 27 | 28 | 29 | If you do not have access to the application's source code, you can use [mptcpize](https://manpages.ubuntu.com/manpages/kinetic/en/man8/mptcpize.8.html) on Linux to automatically transform the TCP socket system calls into Multipath TCP sockets. 30 | 31 | The implementation of Multipath TCP in the Linux kernel is regularly improved. You can track the changes [here](https://github.com/multipath-tcp/mptcp_net-next/wiki#changelog). 32 | 33 | -------------------------------------------------------------------------------- /c/linux/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile 2 | 3 | CFLAGS = -Wall -g 4 | CC = gcc 5 | 6 | mptcphello: mptcphello.c 7 | ${CC} ${CFLAGS} mptcphello.c -o mptcphello 8 | 9 | clean: 10 | rm -f mptcphello *.o 11 | -------------------------------------------------------------------------------- /c/linux/README.md: -------------------------------------------------------------------------------- 1 | # Using Multipath TCP in C 2 | 3 | 4 | It is pretty simple to use Multipath TCP with the C language. You simply need to 5 | pass `IPPROTO_MPTCP` as the third argument of the 6 | [`socket()`](https://www.man7.org/linux/man-pages/man3/socket.3p.html) system 7 | call. Make sure that `IPPROTO_MPTCP` is correctly defined and, if needed, define 8 | it as follows : 9 | 10 | ```c 11 | #ifndef IPPROTO_MPTCP 12 | #define IPPROTO_MPTCP 262 13 | #endif 14 | ``` 15 | 16 | A typical socket call for Multipath TCP will look like: 17 | 18 | ```c 19 | s = socket(AF_INET, SOCK_STREAM, IPPROTO_MPTCP); 20 | ``` 21 | 22 | Note: because the build and run-time environments might be different, it is not 23 | recommended to check if the kernel at build time supports MPTCP. The kernel at 24 | run-time might be different, or the kernel might be updated later. 25 | -------------------------------------------------------------------------------- /c/linux/mptcphello.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | #ifndef IPPROTO_MPTCP 11 | #define IPPROTO_MPTCP 262 12 | #endif 13 | 14 | 15 | 16 | 17 | bool use_mptcp = true; 18 | 19 | int main(int argc, char **argv) { 20 | 21 | int s; 22 | 23 | 24 | if (use_mptcp) { 25 | s = socket(AF_INET, SOCK_STREAM, IPPROTO_MPTCP); 26 | if (s == -1) { 27 | use_mptcp = false; 28 | fprintf(stderr, "Could not create MPTCP socket, falling back to TCP \n"); 29 | s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 30 | if (s ==-1) { 31 | fprintf(stderr, "Could not create TCP socket\n"); 32 | exit(-1); 33 | } 34 | } 35 | close(s); 36 | exit(0); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /c/macOS/Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | FLAGS=-Wall 3 | LIBS=-lNetwork 4 | 5 | main: main.c 6 | $(CC) $(FLAGS) -o $@ $^ $(LIBS) 7 | 8 | clean: 9 | rm main 10 | 11 | .PHONY: clean -------------------------------------------------------------------------------- /c/macOS/README.md: -------------------------------------------------------------------------------- 1 | # Using Multipath TCP in C on macOS/iOS 2 | 3 | To use Multipath TCP in C, you simply need to change a bit the configuration given to the connection, by selecting a Multipath TCP mode. The list of available modes is given in [the official documentation](https://developer.apple.com/documentation/network/nw_multipath_service_t). Here is an example: 4 | 5 | ```c 6 | // request to create a new TCP connection with TLS enabled 7 | nw_parameters_t params = nw_parameters_create_secure_tcp( 8 | NW_PARAMETERS_DEFAULT_CONFIGURATION, NW_PARAMETERS_DEFAULT_CONFIGURATION); 9 | // Handover mode: Other types can be selected 10 | nw_parameters_set_multipath_service(params, nw_multipath_service_handover); 11 | 12 | nw_connection_t connection = nw_connection_create(endpoint, params); 13 | ``` 14 | 15 | Please, note that currently, the _Aggregate_ mode doesn't seem to work according to our tests. Also, only the client side is supported. -------------------------------------------------------------------------------- /c/macOS/main.c: -------------------------------------------------------------------------------- 1 | // 2 | // main.c 3 | // mptcp-network-client 4 | // 5 | // Created by Anthony Doeraene on 08/08/2024. 6 | // 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | static dispatch_semaphore_t semaphore; 13 | 14 | void get(char *path, nw_connection_t conn, dispatch_queue_t queue){ 15 | char req[1024]; 16 | int size = sprintf(req, "GET %s HTTP/1.0\r\nUser-Agent: curl/1.0.0\r\n\n", path); 17 | 18 | dispatch_data_t data = dispatch_data_create(req, size, queue, ^{ 19 | }); 20 | printf("Sending request...\n"); 21 | nw_content_context_t context = nw_content_context_create("context"); 22 | nw_connection_send(conn, data, context, true, ^(nw_error_t _Nullable error) { 23 | if (error){ 24 | printf("Error sending %d", nw_error_get_error_code(error)); 25 | dispatch_semaphore_signal(semaphore); 26 | } 27 | }); 28 | 29 | nw_connection_receive(conn, 0, 1024, ^(dispatch_data_t _Nullable content, nw_content_context_t _Nullable context, bool is_complete, nw_error_t _Nullable error) { 30 | if (error){ 31 | printf("Error receiving %d", nw_error_get_error_code(error)); 32 | dispatch_semaphore_signal(semaphore); 33 | return; 34 | } 35 | 36 | dispatch_data_apply(content, ^bool(dispatch_data_t _Nonnull region, size_t offset, const void * _Nonnull buffer, size_t size) { 37 | char res[size+1]; 38 | memcpy(res, buffer, size); 39 | res[size] = '\0'; 40 | printf("Received %ld bytes from server:\n%s\n", size, res); 41 | dispatch_semaphore_signal(semaphore); 42 | return true; 43 | }); 44 | }); 45 | } 46 | 47 | int main(int argc, const char * argv[]) { 48 | dispatch_queue_t queue = dispatch_queue_create("MPTCP client queue", NULL); 49 | 50 | nw_endpoint_t endpoint = nw_endpoint_create_host("check.mptcp.dev", "443"); 51 | nw_parameters_t params = nw_parameters_create_secure_tcp(NW_PARAMETERS_DEFAULT_CONFIGURATION, NW_PARAMETERS_DEFAULT_CONFIGURATION); 52 | nw_parameters_set_multipath_service(params, nw_multipath_service_handover); 53 | 54 | nw_connection_t connection = nw_connection_create(endpoint, params); 55 | nw_connection_set_queue(connection, queue); 56 | nw_release(endpoint); 57 | nw_release(params); 58 | 59 | semaphore = dispatch_semaphore_create(0); 60 | nw_connection_set_state_changed_handler(connection, ^(nw_connection_state_t state, nw_error_t _Nullable event_error) { 61 | switch (state) { 62 | case nw_connection_state_ready: { 63 | printf("Ready\n"); 64 | get("/", connection, queue); 65 | break; 66 | } 67 | case nw_connection_state_failed: 68 | printf("Error: failed to create the MPTCP connection"); 69 | dispatch_semaphore_signal(semaphore); 70 | break; 71 | case nw_connection_state_cancelled: 72 | printf("Error: cancelled to create the MPTCP connection"); 73 | dispatch_semaphore_signal(semaphore); 74 | break; 75 | case nw_connection_state_waiting: 76 | dispatch_semaphore_signal(semaphore); 77 | break; 78 | default: { 79 | // Ignore other states 80 | break; 81 | } 82 | } 83 | }); 84 | 85 | nw_connection_start(connection); 86 | 87 | dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); 88 | dispatch_release(semaphore); 89 | dispatch_release(queue); 90 | } 91 | -------------------------------------------------------------------------------- /elixir/.formatter.exs: -------------------------------------------------------------------------------- 1 | # Used by "mix format" 2 | [ 3 | inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] 4 | ] 5 | -------------------------------------------------------------------------------- /elixir/.gitignore: -------------------------------------------------------------------------------- 1 | # The directory Mix will write compiled artifacts to. 2 | /_build/ 3 | 4 | # If you run "mix test --cover", coverage assets end up here. 5 | /cover/ 6 | 7 | # The directory Mix downloads your dependencies sources to. 8 | /deps/ 9 | 10 | # Where third-party dependencies like ExDoc output generated docs. 11 | /doc/ 12 | 13 | # Ignore .fetch files in case you like to edit your project deps locally. 14 | /.fetch 15 | 16 | # If the VM crashes, it generates a dump, let's ignore it too. 17 | erl_crash.dump 18 | 19 | # Also ignore archive artifacts (built via "mix archive.build"). 20 | *.ez 21 | 22 | # Ignore package tarball (built via "mix hex.build"). 23 | mptcp_elixir-*.tar 24 | 25 | # Temporary files, for example, from tests. 26 | /tmp/ 27 | -------------------------------------------------------------------------------- /elixir/README.md: -------------------------------------------------------------------------------- 1 | # Using MPTCP in elixir 2 | 3 | It is pretty simple to use Multipath TCP with elixir as the API is closely related to C. You simply need to pass the value 262 (=IPPROTO_MPTCP) as the third argument of the [:socket.open](https://www.erlang.org/doc/apps/kernel/socket.html#open/3) function. 4 | 5 | A typical socket call for Multipath TCP will look like: `{:ok, sock} = :socket.open(:inet, :stream, 262)` 6 | 7 | This simple project also shows how to fallback automatically to TCP in case of MPTCP not being available. 8 | 9 | ## Server 10 | 11 | Here is an example on how to run a MPTCP server listening on 192.168.0.100:8080 using this example 12 | 13 | ```elixir 14 | # iex -S mix 15 | 16 | 1> MPTCP.server({192, 168, 0, 100}, 8080) 17 | ``` 18 | 19 | ## Client 20 | 21 | Here is an example on how to run a MPTCP client connecting to 192.168.0.100:8080 using this example 22 | 23 | ```elixir 24 | # iex -S mix 25 | 1> MPTCP.client({192, 168, 0, 100}, 8080) 26 | ``` -------------------------------------------------------------------------------- /elixir/lib/mptcp.ex: -------------------------------------------------------------------------------- 1 | # Author: Anthony Doeraene 2 | 3 | defmodule MPTCP do 4 | @ipproto_mptcp 262 5 | 6 | defp open_socket() do 7 | {status, sock} = :socket.open(:inet, :stream, @ipproto_mptcp) 8 | case status do 9 | :ok -> {:ok, sock} 10 | _ -> :socket.open(:inet, :stream, 0) 11 | end 12 | end 13 | 14 | def client(addr, port) do 15 | {:ok, sock} = open_socket() 16 | :ok = :socket.connect(sock, %{family: :inet, 17 | addr: addr, 18 | port: port}) 19 | msg = <<"hello">> 20 | :ok = :socket.send(sock, msg) 21 | :ok = :socket.shutdown(sock, :write) 22 | {:ok, msg} = :socket.recv(sock) 23 | :ok = :socket.close(sock) 24 | msg 25 | end 26 | 27 | def server(addr, port) do 28 | {:ok, lsock} = open_socket() 29 | :ok = :socket.bind(lsock, %{family: :inet, 30 | port: port, 31 | addr: addr}) 32 | :ok = :socket.listen(lsock) 33 | {:ok, sock} = :socket.accept(lsock) 34 | {:ok, msg} = :socket.recv(sock) 35 | :ok = :socket.send(sock, msg) 36 | :ok = :socket.close(sock) 37 | :ok = :socket.close(lsock) 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /elixir/mix.exs: -------------------------------------------------------------------------------- 1 | defmodule MptcpElixir.MixProject do 2 | use Mix.Project 3 | 4 | def project do 5 | [ 6 | app: :mptcp_elixir, 7 | version: "0.1.0", 8 | elixir: "~> 1.15", 9 | start_permanent: Mix.env() == :prod, 10 | deps: deps() 11 | ] 12 | end 13 | 14 | # Run "mix help compile.app" to learn about applications. 15 | def application do 16 | [ 17 | extra_applications: [:logger] 18 | ] 19 | end 20 | 21 | # Run "mix help deps" to learn about dependencies. 22 | defp deps do 23 | [ 24 | # {:dep_from_hexpm, "~> 0.3.0"}, 25 | # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"} 26 | ] 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /erlang/README.md: -------------------------------------------------------------------------------- 1 | # Using MPTCP in erlang 2 | 3 | It is pretty simple to use Multipath TCP with erlang as the API is closely related to C. You simply need to pass the value 262 (=IPPROTO_MPTCP) as the third argument of the [socket:open](https://www.erlang.org/doc/apps/kernel/socket.html#open/3) function. 4 | 5 | A typical socket call for Multipath TCP will look like: `{:ok, sock} = socket:open(inet, stream, 262)` 6 | 7 | This simple project also shows how to fallback automatically to TCP in case of MPTCP not being available. 8 | 9 | ## Run a server 10 | 11 | Here is an example on how to run a MPTCP server listening on 192.168.0.100:8080 using this example. 12 | 13 | ```erl 14 | # erl 15 | 16 | 1> c(mptcp). 17 | 2> mptcp:server({192, 168, 0, 100}, 8080). 18 | ``` 19 | 20 | ## Run a client 21 | 22 | Here is an example on how to run a MPTCP client connecting to 192.168.0.100:8080 using this example. 23 | 24 | ```erl 25 | # erl 26 | 27 | 1> c(mptcp). 28 | 2> mptcp:client({192, 168, 0, 100}, 8080). 29 | ``` -------------------------------------------------------------------------------- /erlang/mptcp.erl: -------------------------------------------------------------------------------- 1 | %%% @author Anthony Doerane 2 | 3 | -module(mptcp). 4 | -export([client/2, server/2]). 5 | 6 | -define(IPPROTO_MPTCP, 262). 7 | 8 | open_socket() -> 9 | {Status, Sock} = socket:open(inet, stream, ?IPPROTO_MPTCP), 10 | case Status of 11 | ok -> {Status, Sock}; 12 | _ -> socket:open(inet, stream, 0) 13 | end. 14 | 15 | client(SAddr, SPort) -> 16 | {ok, Sock} = open_socket(), 17 | ok = socket:connect(Sock, #{family => inet, 18 | addr => SAddr, 19 | port => SPort}), 20 | Msg = <<"hello">>, 21 | ok = socket:send(Sock, Msg), 22 | ok = socket:shutdown(Sock, write), 23 | {ok, Msg} = socket:recv(Sock), 24 | ok = socket:close(Sock). 25 | 26 | server(Addr, Port) -> 27 | {ok, LSock} = open_socket(), 28 | ok = socket:bind(LSock, #{family => inet, 29 | port => Port, 30 | addr => Addr}), 31 | ok = socket:listen(LSock), 32 | {ok, Sock} = socket:accept(LSock), 33 | {ok, Msg} = socket:recv(Sock), 34 | ok = socket:send(Sock, Msg), 35 | ok = socket:close(Sock), 36 | ok = socket:close(LSock). 37 | -------------------------------------------------------------------------------- /objective-c/README.md: -------------------------------------------------------------------------------- 1 | # Using Multipath TCP in Objective-C on iOS 2 | 3 | To use Multipath TCP in Objective-C, you simply need to change a bit the configuration given to `NSURLSession`, by selecting a Multipath TCP mode. The list of available modes is given in [the official documentation](https://developer.apple.com/documentation/foundation/nsurlsessionmultipathservicetype). 4 | 5 | Currently, enabling MPTCP on `NSURLSession` can only be done on **iOS**, **not OSX**. 6 | 7 | Here is an example: 8 | 9 | ```objc 10 | NSURLSessionConfiguration *conf = [NSURLSessionConfiguration defaultSessionConfiguration]; 11 | 12 | #if TARGET_OS_IOS 13 | // multipath is only supported in iOS 14 | conf.multipathServiceType = NSURLSessionMultipathServiceTypeHandover; 15 | #endif 16 | 17 | NSURLSession *session = [NSURLSession sessionWithConfiguration:conf]; 18 | ``` 19 | 20 | In this repository, you'll also find categories that extend the `NSURLSession` and `NSURLSessionConfiguration`, which allows having a single shared instance, called `sharedMPTCPSession`, for `NSURLSession` with Multipath TCP enabled in handover mode. 21 | Another remark: currently, only the client side is supported. -------------------------------------------------------------------------------- /objective-c/src/NSURLSession+sharedMPTCPSession.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSURLSession+sharedMPTCPSession.h 3 | // mptcp-test 4 | // 5 | // Created by Anthony Doeraene on 05/08/2024. 6 | // 7 | 8 | #ifndef NSURLSession_sharedMPTCPSession_h 9 | #define NSURLSession_sharedMPTCPSession_h 10 | 11 | @interface NSURLSession (sharedMPTCPSession) 12 | + (NSURLSession *) sharedMPTCPSession; 13 | @end 14 | 15 | #endif /* NSURLSession_sharedMPTCPSession_h */ 16 | -------------------------------------------------------------------------------- /objective-c/src/NSURLSession+sharedMPTCPSession.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSURLSession+sharedMPTCPSession.m 3 | // mptcp-test 4 | // 5 | // Created by Anthony Doeraene on 05/08/2024. 6 | // 7 | 8 | #import 9 | #include "NSURLSession+sharedMPTCPSession.h" 10 | #include "NSURLSessionConfiguration+default.h" 11 | 12 | @implementation NSURLSession (sharedMPTCPSession) 13 | + (NSURLSession *) sharedMPTCPSession { 14 | static NSURLSession *sharedMPTCPSession = nil; 15 | static dispatch_once_t onceToken; 16 | dispatch_once(&onceToken, ^{ 17 | NSURLSessionConfiguration *conf = [NSURLSessionConfiguration defaultMPTCPConfiguration]; 18 | // create a session with the MPTCP configuration if MPTCP is enabled, else fall back to TCP 19 | sharedMPTCPSession = [NSURLSession sessionWithConfiguration:conf]; 20 | }); 21 | return sharedMPTCPSession; 22 | } 23 | @end 24 | -------------------------------------------------------------------------------- /objective-c/src/NSURLSessionConfiguration+default.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSURLSessionConfiguration+default.h 3 | // mptcp-test 4 | // 5 | // Created by Anthony Doeraene on 05/08/2024. 6 | // 7 | 8 | #ifndef NSURLSessionConfiguration_default_h 9 | #define NSURLSessionConfiguration_default_h 10 | // add a static var defaultMPTCPConfiguration to NSURLSessionConfiguration 11 | @interface NSURLSessionConfiguration (defaultMPTCPConfiguration) 12 | + (NSURLSessionConfiguration *) defaultMPTCPConfiguration; 13 | @end 14 | #endif /* NSURLSessionConfiguration_default_h */ 15 | -------------------------------------------------------------------------------- /objective-c/src/NSURLSessionConfiguration+default.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSURLSessionConfiguration+default.m 3 | // mptcp-test 4 | // 5 | // Created by Anthony Doeraene on 05/08/2024. 6 | // 7 | 8 | #import 9 | #include "NSURLSessionConfiguration+default.h" 10 | 11 | @implementation NSURLSessionConfiguration (defaultMPTCPConfiguration) 12 | + (NSURLSessionConfiguration *) defaultMPTCPConfiguration { 13 | static NSURLSessionConfiguration *defaultMPTCPConfiguration = nil; 14 | static dispatch_once_t onceToken; 15 | dispatch_once(&onceToken, ^{ 16 | NSURLSessionConfiguration *conf = [NSURLSessionConfiguration defaultSessionConfiguration]; 17 | #if TARGET_OS_IOS 18 | // multipath is only supported in iOS 19 | conf.multipathServiceType = NSURLSessionMultipathServiceTypeHandover; 20 | #endif 21 | // on other platforms, the defaultMPTCPConfiguration is simply [NSURLSessionConfiguration defaultSessionConfiguration], which is a standard TCP configuration 22 | 23 | defaultMPTCPConfiguration = conf; 24 | }); 25 | return defaultMPTCPConfiguration; 26 | } 27 | @end 28 | -------------------------------------------------------------------------------- /objective-c/src/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // mptcp-test 4 | // 5 | // Created by Anthony Doeraene on 05/08/2024. 6 | // 7 | 8 | #import 9 | #include "NSURLSession+sharedMPTCPSession.h" 10 | #include "NSURLSessionConfiguration+default.h" 11 | 12 | int main(int argc, const char * argv[]) { 13 | @autoreleasepool { 14 | dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); 15 | 16 | NSURL *url = [NSURL URLWithString:@"https://check.mptcp.dev"]; 17 | 18 | // do a get request to https://check.mptcp.dev 19 | NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init]; 20 | [request setHTTPMethod:@"GET"]; 21 | [request setURL:url]; 22 | [request setValue:@"curl/1.0.0" forHTTPHeaderField:@"User-Agent"]; 23 | 24 | NSURLSession *session = [NSURLSession sharedMPTCPSession]; 25 | // alternatively, to create a new session you can use: 26 | // NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultMPTCPConfiguration]]; 27 | 28 | NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler: 29 | ^(NSData * _Nullable data, 30 | NSURLResponse * _Nullable response, 31 | NSError * _Nullable error) { 32 | 33 | NSString *res = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 34 | NSLog(@"Data received: %@", res); 35 | dispatch_semaphore_signal(semaphore); 36 | }]; 37 | [dataTask resume]; 38 | dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); 39 | } 40 | 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /perl/README.md: -------------------------------------------------------------------------------- 1 | # Using Multipath TCP in perl 2 | 3 | 4 | To use Multipath TCP in perl, simply pass Multipath TCP's protocol number (262) as the third argument of the socket system call. 5 | 6 | [simple example](mptcp-hello.pl) 7 | -------------------------------------------------------------------------------- /perl/mptcp-hello.pl: -------------------------------------------------------------------------------- 1 | use Socket qw(PF_INET SOCK_STREAM pack_sockaddr_in inet_aton); 2 | 3 | use Net::protoent; 4 | $p = getprotobyname(shift || 'mptcp') || die "no proto"; 5 | printf "proto for %s is %d, aliases are %s\n", 6 | $p->name, $p->proto, "@{$p->aliases}"; 7 | 8 | use constant IPPROTO_MPTCP => 262; 9 | 10 | my $proto; 11 | if(defined getprotobyname( shift || 'mptcp' )) { 12 | $proto = getprotobyname (shift || 'mptcp' ); 13 | } 14 | else 15 | $proto = ('mptcp', "MPTCP", 262); 16 | } 17 | 18 | 19 | socket(my $socket, PF_INET, SOCK_STREAM, $proto) 20 | or die "socket: $!"; 21 | 22 | 23 | 24 | #socket(my $socket, PF_INET, SOCK_STREAM, IPROTO_MPTCP) 25 | # or die "socket: $!"; 26 | 27 | my $port = getservbyname "http", "tcp"; 28 | connect($socket, pack_sockaddr_in($port, inet_aton("test.multipath-tcp.org"))) 29 | or die "connect: $!"; 30 | 31 | 32 | print $socket "GET / HTTP/1.0\r\n\r\n"; 33 | print <$socket>; 34 | -------------------------------------------------------------------------------- /python/README.md: -------------------------------------------------------------------------------- 1 | # Using Multipath TCP in python 2 | 3 | To use Multipath TCP in python, you need to pass ```IPPROTO_MPTCP``` as the third argument of the ```socket.socket``` call as in C. On python 3.10+, this is as 4 | simple as : 5 | 6 | ```python 7 | socket.socket(sockaf, socket.SOCK_STREAM, socket.IPPROTO_MPTCP) 8 | ``` 9 | 10 | However, if you want your code to be portable and to run on hosts using older Linux kernels, kernels where Multipath TCP is disabled or older versions of python, you need to take some care. Here are some hints that you might find useful : 11 | 12 | 1. Do not simply use ```socket.IPPROTO_MPTCP```, use a ```try ... except``` and set a variable to the expected value of ```socket.IPPROTO_MPTCP``` like 13 | 14 | ```python 15 | try: 16 | IPPROTO_MPTCP = socket.IPPROTO_MPTCP 17 | except AttributeError: 18 | IPPROTO_MPTCP = 262 19 | ``` 20 | 21 | Although 262 alone would work, using it directly in your make would make it unreadable. 22 | 23 | 2. Since the Multipath TCP support is a system property, you should only test it once. If your application creates several TCP sockets and you want to add support for Multipath TCP, you should use a function that creates all the sockets that you need. This function tries to create a Multipath TCP socket. If this socket fails with either ```ENOPROTOOPT 92 Protocol not available``` (linked to `sysctl net.mptcp.enabled`), ```EPROTONOSUPPORT 93 Protocol not supported``` (MPTCP is not compiled on >=v5.6) or ```EINVAL 22 Invalid argument``` (MPTCP is not available on <5.6), this means that it runs on a system that does not support Multipath TCP. You should fallback to TCP and return a regular TCP socket and cache this information such you automatically create TCP sockets at the next calls. 24 | 25 | [simple example](mptcp-hello.py) 26 | -------------------------------------------------------------------------------- /python/mptcp-hello.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import errno 3 | 4 | # On Python 3.10 and above, socket.IPPROTO_MPTCP is defined. 5 | # If not, we set it manually 6 | try: 7 | IPPROTO_MPTCP = socket.IPPROTO_MPTCP 8 | except AttributeError: 9 | IPPROTO_MPTCP = 262 10 | 11 | # By default, the application wishes to use Multipath TCP for all sockets 12 | # provided that it is running on a system that supports Multipath TCP 13 | _use_mptcp = True 14 | 15 | def create_socket(sockaf): 16 | global _use_mptcp 17 | global IPPROTO_MPTCP 18 | # If Multipath TCP is enabled on this system, we create a Multipath TCP 19 | # socket 20 | if _use_mptcp : 21 | try: 22 | return socket.socket(sockaf, socket.SOCK_STREAM, IPPROTO_MPTCP) 23 | except OSError as e: 24 | # Multipath TCP is not supported, we fall back to regular TCP 25 | # and remember that Multipath TCP is not enabled 26 | if e.errno == errno.ENOPROTOOPT or \ 27 | e.errno == errno.EPROTONOSUPPORT or \ 28 | e.errno == errno.EINVAL: 29 | _use_mptcp = False 30 | 31 | # Multipath TCP does not work or socket failed, we try TCP 32 | return socket.socket(sockaf, socket.SOCK_STREAM, socket.IPPROTO_TCP) 33 | 34 | # 35 | # Example usage 36 | # 37 | s = create_socket(socket.AF_INET) 38 | s.connect(("test.multipath-tcp.org" , 80)) 39 | 40 | # use the socket as you wish 41 | 42 | s.close() 43 | -------------------------------------------------------------------------------- /rust/.gitignore: -------------------------------------------------------------------------------- 1 | target/ -------------------------------------------------------------------------------- /rust/README.md: -------------------------------------------------------------------------------- 1 | # Using Multipath TCP in Rust 2 | 3 | 4 | Rust and C are pretty much the same for the No framework example. You simply need to open a socket with the `IPPROTO_MPTCP` protocol. For that you can use the `Socket2` library [see here](Socket2). 5 | 6 | If you plan to use a more complex library (HTTP library, Web framework, ...), it may not be as straigthforward as just using `Socket2`. Let's take `Hyper` for example, they support differents IO type, allowing you to create a `Socket2`'s socket and pass it to [Server::from_tcp](https://docs.rs/hyper/0.14.20/hyper/server/struct.Server.html#method.from_tcp). 7 | 8 | - [No framework example (socket2)](socket2) 9 | - [Hyper framework (server)](hyper_server) -------------------------------------------------------------------------------- /rust/hyper_server/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "autocfg" 7 | version = "1.1.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 10 | 11 | [[package]] 12 | name = "bitflags" 13 | version = "1.3.2" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 16 | 17 | [[package]] 18 | name = "bytes" 19 | version = "1.2.1" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" 22 | 23 | [[package]] 24 | name = "cfg-if" 25 | version = "1.0.0" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 28 | 29 | [[package]] 30 | name = "fnv" 31 | version = "1.0.7" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 34 | 35 | [[package]] 36 | name = "futures-channel" 37 | version = "0.3.24" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" 40 | dependencies = [ 41 | "futures-core", 42 | ] 43 | 44 | [[package]] 45 | name = "futures-core" 46 | version = "0.3.24" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" 49 | 50 | [[package]] 51 | name = "futures-sink" 52 | version = "0.3.24" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" 55 | 56 | [[package]] 57 | name = "futures-task" 58 | version = "0.3.24" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" 61 | 62 | [[package]] 63 | name = "futures-util" 64 | version = "0.3.24" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" 67 | dependencies = [ 68 | "futures-core", 69 | "futures-task", 70 | "pin-project-lite", 71 | "pin-utils", 72 | ] 73 | 74 | [[package]] 75 | name = "h2" 76 | version = "0.3.14" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "5ca32592cf21ac7ccab1825cd87f6c9b3d9022c44d086172ed0966bec8af30be" 79 | dependencies = [ 80 | "bytes", 81 | "fnv", 82 | "futures-core", 83 | "futures-sink", 84 | "futures-util", 85 | "http", 86 | "indexmap", 87 | "slab", 88 | "tokio", 89 | "tokio-util", 90 | "tracing", 91 | ] 92 | 93 | [[package]] 94 | name = "hashbrown" 95 | version = "0.12.3" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 98 | 99 | [[package]] 100 | name = "hermit-abi" 101 | version = "0.1.19" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 104 | dependencies = [ 105 | "libc", 106 | ] 107 | 108 | [[package]] 109 | name = "http" 110 | version = "0.2.8" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" 113 | dependencies = [ 114 | "bytes", 115 | "fnv", 116 | "itoa", 117 | ] 118 | 119 | [[package]] 120 | name = "http-body" 121 | version = "0.4.5" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" 124 | dependencies = [ 125 | "bytes", 126 | "http", 127 | "pin-project-lite", 128 | ] 129 | 130 | [[package]] 131 | name = "httparse" 132 | version = "1.8.0" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" 135 | 136 | [[package]] 137 | name = "httpdate" 138 | version = "1.0.2" 139 | source = "registry+https://github.com/rust-lang/crates.io-index" 140 | checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" 141 | 142 | [[package]] 143 | name = "hyper" 144 | version = "0.14.20" 145 | source = "registry+https://github.com/rust-lang/crates.io-index" 146 | checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" 147 | dependencies = [ 148 | "bytes", 149 | "futures-channel", 150 | "futures-core", 151 | "futures-util", 152 | "h2", 153 | "http", 154 | "http-body", 155 | "httparse", 156 | "httpdate", 157 | "itoa", 158 | "pin-project-lite", 159 | "socket2 0.4.7", 160 | "tokio", 161 | "tower-service", 162 | "tracing", 163 | "want", 164 | ] 165 | 166 | [[package]] 167 | name = "indexmap" 168 | version = "1.9.1" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" 171 | dependencies = [ 172 | "autocfg", 173 | "hashbrown", 174 | ] 175 | 176 | [[package]] 177 | name = "itoa" 178 | version = "1.0.3" 179 | source = "registry+https://github.com/rust-lang/crates.io-index" 180 | checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" 181 | 182 | [[package]] 183 | name = "libc" 184 | version = "0.2.132" 185 | source = "registry+https://github.com/rust-lang/crates.io-index" 186 | checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" 187 | 188 | [[package]] 189 | name = "lock_api" 190 | version = "0.4.8" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "9f80bf5aacaf25cbfc8210d1cfb718f2bf3b11c4c54e5afe36c236853a8ec390" 193 | dependencies = [ 194 | "autocfg", 195 | "scopeguard", 196 | ] 197 | 198 | [[package]] 199 | name = "log" 200 | version = "0.4.17" 201 | source = "registry+https://github.com/rust-lang/crates.io-index" 202 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 203 | dependencies = [ 204 | "cfg-if", 205 | ] 206 | 207 | [[package]] 208 | name = "memchr" 209 | version = "2.5.0" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 212 | 213 | [[package]] 214 | name = "mio" 215 | version = "0.8.4" 216 | source = "registry+https://github.com/rust-lang/crates.io-index" 217 | checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" 218 | dependencies = [ 219 | "libc", 220 | "log", 221 | "wasi", 222 | "windows-sys", 223 | ] 224 | 225 | [[package]] 226 | name = "mptcp-hello-hyper" 227 | version = "0.1.0" 228 | dependencies = [ 229 | "hyper", 230 | "socket2 0.5.0", 231 | "tokio", 232 | ] 233 | 234 | [[package]] 235 | name = "num_cpus" 236 | version = "1.13.1" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" 239 | dependencies = [ 240 | "hermit-abi", 241 | "libc", 242 | ] 243 | 244 | [[package]] 245 | name = "once_cell" 246 | version = "1.14.0" 247 | source = "registry+https://github.com/rust-lang/crates.io-index" 248 | checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0" 249 | 250 | [[package]] 251 | name = "parking_lot" 252 | version = "0.12.1" 253 | source = "registry+https://github.com/rust-lang/crates.io-index" 254 | checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" 255 | dependencies = [ 256 | "lock_api", 257 | "parking_lot_core", 258 | ] 259 | 260 | [[package]] 261 | name = "parking_lot_core" 262 | version = "0.9.3" 263 | source = "registry+https://github.com/rust-lang/crates.io-index" 264 | checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" 265 | dependencies = [ 266 | "cfg-if", 267 | "libc", 268 | "redox_syscall", 269 | "smallvec", 270 | "windows-sys", 271 | ] 272 | 273 | [[package]] 274 | name = "pin-project-lite" 275 | version = "0.2.9" 276 | source = "registry+https://github.com/rust-lang/crates.io-index" 277 | checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" 278 | 279 | [[package]] 280 | name = "pin-utils" 281 | version = "0.1.0" 282 | source = "registry+https://github.com/rust-lang/crates.io-index" 283 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 284 | 285 | [[package]] 286 | name = "proc-macro2" 287 | version = "1.0.43" 288 | source = "registry+https://github.com/rust-lang/crates.io-index" 289 | checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" 290 | dependencies = [ 291 | "unicode-ident", 292 | ] 293 | 294 | [[package]] 295 | name = "quote" 296 | version = "1.0.21" 297 | source = "registry+https://github.com/rust-lang/crates.io-index" 298 | checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" 299 | dependencies = [ 300 | "proc-macro2", 301 | ] 302 | 303 | [[package]] 304 | name = "redox_syscall" 305 | version = "0.2.16" 306 | source = "registry+https://github.com/rust-lang/crates.io-index" 307 | checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" 308 | dependencies = [ 309 | "bitflags", 310 | ] 311 | 312 | [[package]] 313 | name = "scopeguard" 314 | version = "1.1.0" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 317 | 318 | [[package]] 319 | name = "signal-hook-registry" 320 | version = "1.4.0" 321 | source = "registry+https://github.com/rust-lang/crates.io-index" 322 | checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" 323 | dependencies = [ 324 | "libc", 325 | ] 326 | 327 | [[package]] 328 | name = "slab" 329 | version = "0.4.7" 330 | source = "registry+https://github.com/rust-lang/crates.io-index" 331 | checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" 332 | dependencies = [ 333 | "autocfg", 334 | ] 335 | 336 | [[package]] 337 | name = "smallvec" 338 | version = "1.9.0" 339 | source = "registry+https://github.com/rust-lang/crates.io-index" 340 | checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" 341 | 342 | [[package]] 343 | name = "socket2" 344 | version = "0.4.7" 345 | source = "registry+https://github.com/rust-lang/crates.io-index" 346 | checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" 347 | dependencies = [ 348 | "libc", 349 | "winapi", 350 | ] 351 | 352 | [[package]] 353 | name = "socket2" 354 | version = "0.5.0" 355 | source = "git+https://github.com/rust-lang/socket2#4c436818cddec880c26c4e79aca7106ba88827ff" 356 | dependencies = [ 357 | "libc", 358 | "windows-sys", 359 | ] 360 | 361 | [[package]] 362 | name = "syn" 363 | version = "1.0.99" 364 | source = "registry+https://github.com/rust-lang/crates.io-index" 365 | checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" 366 | dependencies = [ 367 | "proc-macro2", 368 | "quote", 369 | "unicode-ident", 370 | ] 371 | 372 | [[package]] 373 | name = "tokio" 374 | version = "1.21.0" 375 | source = "registry+https://github.com/rust-lang/crates.io-index" 376 | checksum = "89797afd69d206ccd11fb0ea560a44bbb87731d020670e79416d442919257d42" 377 | dependencies = [ 378 | "autocfg", 379 | "bytes", 380 | "libc", 381 | "memchr", 382 | "mio", 383 | "num_cpus", 384 | "once_cell", 385 | "parking_lot", 386 | "pin-project-lite", 387 | "signal-hook-registry", 388 | "socket2 0.4.7", 389 | "tokio-macros", 390 | "winapi", 391 | ] 392 | 393 | [[package]] 394 | name = "tokio-macros" 395 | version = "1.8.0" 396 | source = "registry+https://github.com/rust-lang/crates.io-index" 397 | checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" 398 | dependencies = [ 399 | "proc-macro2", 400 | "quote", 401 | "syn", 402 | ] 403 | 404 | [[package]] 405 | name = "tokio-util" 406 | version = "0.7.4" 407 | source = "registry+https://github.com/rust-lang/crates.io-index" 408 | checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" 409 | dependencies = [ 410 | "bytes", 411 | "futures-core", 412 | "futures-sink", 413 | "pin-project-lite", 414 | "tokio", 415 | "tracing", 416 | ] 417 | 418 | [[package]] 419 | name = "tower-service" 420 | version = "0.3.2" 421 | source = "registry+https://github.com/rust-lang/crates.io-index" 422 | checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" 423 | 424 | [[package]] 425 | name = "tracing" 426 | version = "0.1.36" 427 | source = "registry+https://github.com/rust-lang/crates.io-index" 428 | checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307" 429 | dependencies = [ 430 | "cfg-if", 431 | "pin-project-lite", 432 | "tracing-core", 433 | ] 434 | 435 | [[package]] 436 | name = "tracing-core" 437 | version = "0.1.29" 438 | source = "registry+https://github.com/rust-lang/crates.io-index" 439 | checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7" 440 | dependencies = [ 441 | "once_cell", 442 | ] 443 | 444 | [[package]] 445 | name = "try-lock" 446 | version = "0.2.3" 447 | source = "registry+https://github.com/rust-lang/crates.io-index" 448 | checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" 449 | 450 | [[package]] 451 | name = "unicode-ident" 452 | version = "1.0.3" 453 | source = "registry+https://github.com/rust-lang/crates.io-index" 454 | checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf" 455 | 456 | [[package]] 457 | name = "want" 458 | version = "0.3.0" 459 | source = "registry+https://github.com/rust-lang/crates.io-index" 460 | checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" 461 | dependencies = [ 462 | "log", 463 | "try-lock", 464 | ] 465 | 466 | [[package]] 467 | name = "wasi" 468 | version = "0.11.0+wasi-snapshot-preview1" 469 | source = "registry+https://github.com/rust-lang/crates.io-index" 470 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 471 | 472 | [[package]] 473 | name = "winapi" 474 | version = "0.3.9" 475 | source = "registry+https://github.com/rust-lang/crates.io-index" 476 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 477 | dependencies = [ 478 | "winapi-i686-pc-windows-gnu", 479 | "winapi-x86_64-pc-windows-gnu", 480 | ] 481 | 482 | [[package]] 483 | name = "winapi-i686-pc-windows-gnu" 484 | version = "0.4.0" 485 | source = "registry+https://github.com/rust-lang/crates.io-index" 486 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 487 | 488 | [[package]] 489 | name = "winapi-x86_64-pc-windows-gnu" 490 | version = "0.4.0" 491 | source = "registry+https://github.com/rust-lang/crates.io-index" 492 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 493 | 494 | [[package]] 495 | name = "windows-sys" 496 | version = "0.36.1" 497 | source = "registry+https://github.com/rust-lang/crates.io-index" 498 | checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" 499 | dependencies = [ 500 | "windows_aarch64_msvc", 501 | "windows_i686_gnu", 502 | "windows_i686_msvc", 503 | "windows_x86_64_gnu", 504 | "windows_x86_64_msvc", 505 | ] 506 | 507 | [[package]] 508 | name = "windows_aarch64_msvc" 509 | version = "0.36.1" 510 | source = "registry+https://github.com/rust-lang/crates.io-index" 511 | checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" 512 | 513 | [[package]] 514 | name = "windows_i686_gnu" 515 | version = "0.36.1" 516 | source = "registry+https://github.com/rust-lang/crates.io-index" 517 | checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" 518 | 519 | [[package]] 520 | name = "windows_i686_msvc" 521 | version = "0.36.1" 522 | source = "registry+https://github.com/rust-lang/crates.io-index" 523 | checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" 524 | 525 | [[package]] 526 | name = "windows_x86_64_gnu" 527 | version = "0.36.1" 528 | source = "registry+https://github.com/rust-lang/crates.io-index" 529 | checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" 530 | 531 | [[package]] 532 | name = "windows_x86_64_msvc" 533 | version = "0.36.1" 534 | source = "registry+https://github.com/rust-lang/crates.io-index" 535 | checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" 536 | -------------------------------------------------------------------------------- /rust/hyper_server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mptcp-hello-hyper" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | hyper = { version = "0.14", features = ["full"] } 10 | socket2 = { git = "https://github.com/rust-lang/socket2" } 11 | tokio = { version = "1.21", features = ["full"] } -------------------------------------------------------------------------------- /rust/hyper_server/src/main.rs: -------------------------------------------------------------------------------- 1 | //! This example is a modified version of: 2 | //! https://github.com/hyperium/hyper/blob/0.14.x/examples/hello.rs 3 | 4 | use hyper::service::{make_service_fn, service_fn}; 5 | use hyper::{Body, Request, Response, Server}; 6 | use socket2::{Domain, Protocol, Socket, Type}; 7 | use std::convert::Infallible; 8 | 9 | async fn hello(_: Request) -> Result, Infallible> { 10 | Ok(Response::new(Body::from("Hello MPTCP!"))) 11 | } 12 | 13 | #[tokio::main] 14 | pub async fn main() -> Result<(), Box> { 15 | let addr = ([127, 0, 0, 1], 3000).into(); 16 | 17 | let make_svc = make_service_fn(|_| async { Ok::<_, Infallible>(service_fn(hello)) }); 18 | 19 | // Create the MPTCP capable socket but allow for a fallback to 20 | // TCP if the host does not support MPTCP. 21 | let socket = match Socket::new( 22 | Domain::for_address(addr), 23 | Type::STREAM, 24 | Some(Protocol::MPTCP), 25 | ) { 26 | Ok(socket) => socket, 27 | Err(err) => { 28 | eprintln!( 29 | "Unable to create an MPTCP socket, fallback to regular TCP socket: {}", 30 | err 31 | ); 32 | Socket::new(Domain::for_address(addr), Type::STREAM, Some(Protocol::TCP))? 33 | } 34 | }; 35 | // Set common options on the socket as we created it by hand. 36 | socket.set_nonblocking(true)?; 37 | socket.set_reuse_address(true)?; 38 | socket.bind(&addr.into())?; 39 | socket.listen(1024)?; 40 | 41 | let server = Server::from_tcp(socket.into())?.serve(make_svc); 42 | 43 | println!("Listening on http://{}", addr); 44 | 45 | server.await?; 46 | Ok(()) 47 | } 48 | -------------------------------------------------------------------------------- /rust/socket2/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "libc" 7 | version = "0.2.132" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" 10 | 11 | [[package]] 12 | name = "mptcp-hello-socket2" 13 | version = "0.1.0" 14 | dependencies = [ 15 | "libc", 16 | "once_cell", 17 | "socket2", 18 | ] 19 | 20 | [[package]] 21 | name = "once_cell" 22 | version = "1.14.0" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0" 25 | 26 | [[package]] 27 | name = "socket2" 28 | version = "0.5.0" 29 | source = "git+https://github.com/rust-lang/socket2#4c436818cddec880c26c4e79aca7106ba88827ff" 30 | dependencies = [ 31 | "libc", 32 | "windows-sys", 33 | ] 34 | 35 | [[package]] 36 | name = "windows-sys" 37 | version = "0.36.1" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" 40 | dependencies = [ 41 | "windows_aarch64_msvc", 42 | "windows_i686_gnu", 43 | "windows_i686_msvc", 44 | "windows_x86_64_gnu", 45 | "windows_x86_64_msvc", 46 | ] 47 | 48 | [[package]] 49 | name = "windows_aarch64_msvc" 50 | version = "0.36.1" 51 | source = "registry+https://github.com/rust-lang/crates.io-index" 52 | checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" 53 | 54 | [[package]] 55 | name = "windows_i686_gnu" 56 | version = "0.36.1" 57 | source = "registry+https://github.com/rust-lang/crates.io-index" 58 | checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" 59 | 60 | [[package]] 61 | name = "windows_i686_msvc" 62 | version = "0.36.1" 63 | source = "registry+https://github.com/rust-lang/crates.io-index" 64 | checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" 65 | 66 | [[package]] 67 | name = "windows_x86_64_gnu" 68 | version = "0.36.1" 69 | source = "registry+https://github.com/rust-lang/crates.io-index" 70 | checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" 71 | 72 | [[package]] 73 | name = "windows_x86_64_msvc" 74 | version = "0.36.1" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" 77 | -------------------------------------------------------------------------------- /rust/socket2/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mptcp-hello-socket2" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | libc = "0.2" 10 | once_cell = "1.14" 11 | socket2 = { git = "https://github.com/rust-lang/socket2" } -------------------------------------------------------------------------------- /rust/socket2/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::net::SocketAddr; 2 | 3 | use once_cell::sync::OnceCell; 4 | use socket2::{Domain, Protocol, Socket, Type}; 5 | 6 | static USE_MPTCP: OnceCell = OnceCell::new(); 7 | 8 | /// Create a Multiple Path TCP socket for the specified domain 9 | /// 10 | /// Note that it will depends on the USE_MPTCP value. At program 11 | /// startup the USE_MPTCP will be empty, thus will be considered 12 | /// as `true`. If the MPTCP socket creation fails, it will fallback 13 | /// to regular TCP. 14 | /// And in the case where the socket creation fails (for MPTCP) 15 | /// because the protocol is not supported by the host, it will 16 | /// set the USE_MPTCP to `false` so that it won't try on other 17 | /// calls. 18 | fn socket_create(domain: Domain) -> std::io::Result { 19 | if *USE_MPTCP.get().unwrap_or(&true) { 20 | match Socket::new(domain, Type::STREAM, Some(Protocol::MPTCP)) { 21 | Ok(sock) => return Ok(sock), 22 | Err(err) => { 23 | eprintln!( 24 | "Unable to create an MPTCP socket, fallback to regular TCP socket: {}", 25 | err 26 | ); 27 | 28 | if let Some(err_code) = err.raw_os_error() { 29 | if err_code == libc::ENOPROTOOPT || err_code == libc::EPROTONOSUPPORT { 30 | _ = USE_MPTCP.set(false); 31 | } 32 | } 33 | } 34 | } 35 | } 36 | 37 | Socket::new(domain, Type::STREAM, Some(Protocol::TCP)) 38 | } 39 | 40 | fn main() -> std::io::Result<()> { 41 | let socket = socket_create(Domain::IPV4)?; 42 | 43 | // Do anything with the Socket. 44 | // By example, connect to test.multipath-tcp.org. 45 | // note: you can dynamically get the IP address using a resolver 46 | // but for the simplicity of this example, I've already done 47 | // the conversion. 48 | let addr: SocketAddr = ([5, 196, 67, 207], 80).into(); 49 | match socket.connect(&addr.into()) { 50 | Ok(_) => {} 51 | Err(err) => { 52 | eprintln!("Failed to connect to the SocketAddr"); 53 | return Err(err); 54 | } 55 | } 56 | 57 | Ok(()) 58 | } 59 | -------------------------------------------------------------------------------- /swift/iOS/README.md: -------------------------------------------------------------------------------- 1 | # Using Multipath TCP in Swift on iOS 2 | 3 | To use Multipath TCP in Swift, you simply need to change a bit the configuration given to `URLSession`, by selecting a Multipath TCP mode. The list of available modes is given in [the official documentation](https://developer.apple.com/documentation/foundation/urlsessionconfiguration/multipathservicetype). 4 | 5 | Currently, enabling MPTCP on `URLSession` can only be done on **iOS**, **not OSX**. 6 | 7 | Here is an example: 8 | 9 | ```swift 10 | var conf = URLSessionConfiguration.default 11 | #if os(iOS) 12 | // multipath is only available on iOS, enable it only in this case 13 | conf.multipathServiceType = .handover 14 | #endif 15 | 16 | let session = URLSession(configuration: conf) 17 | ``` 18 | 19 | In this repository, you'll also find extensions to `URLSession` and `URLSessionConfiguration`, which allows having a single shared instance, called `sharedMPTCP`, for `URLSession` with Multipath TCP enabled in handover mode. 20 | 21 | Please, note however that the aggregate mode doesn't seem to work according to our tests. It may be needed to add the [according entitlements in Xcode](https://developer.apple.com/documentation/foundation/urlsessionconfiguration/improving_network_reliability_using_multipath_tcp) in order to work, but we didn't test it yet. 22 | Another remark: currently, only the client side is supported. -------------------------------------------------------------------------------- /swift/iOS/src/MainApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // swift-example 4 | // 5 | // Created by Anthony Doeraene on 06/08/2024. 6 | // 7 | 8 | import Foundation 9 | 10 | @main 11 | struct MainApp{ 12 | static func main() async throws{ 13 | let url = URL(string: "https://check.mptcp.dev")! 14 | 15 | var request = URLRequest(url: url) 16 | request.httpMethod = "GET" 17 | request.setValue("curl/1.0.0", forHTTPHeaderField: "User-Agent") 18 | 19 | let session = URLSession.sharedMPTCP 20 | // note, you could also create a new session by using: 21 | // let session = URLSession(configuration: URLSessionConfiguration.defaultMPTCP) 22 | 23 | let (data, response) = try await session.data(for: request) 24 | 25 | if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode != 200 { 26 | print("Error doing request, received status code \(httpResponse.statusCode)") 27 | exit(1) 28 | } 29 | 30 | if let res = String(data: data, encoding: String.Encoding.utf8){ 31 | print(res) 32 | }else{ 33 | print("Failed to decode response") 34 | exit(1) 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /swift/iOS/src/URLSession+sharedMPTCP.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URLSession+sharedMPTCP.swift 3 | // swift-example 4 | // 5 | // Created by Anthony Doeraene on 06/08/2024. 6 | // 7 | 8 | import Foundation 9 | 10 | extension URLSession{ 11 | static let sharedMPTCP: URLSession = URLSession(configuration: URLSessionConfiguration.defaultMPTCP) 12 | } 13 | -------------------------------------------------------------------------------- /swift/iOS/src/URLSessionConfiguration+defaultMPTCP.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URLSessionConfiguration+defaultMPTCP.swift 3 | // swift-example 4 | // 5 | // Created by Anthony Doeraene on 06/08/2024. 6 | // 7 | 8 | import Foundation 9 | 10 | extension URLSessionConfiguration{ 11 | static var defaultMPTCP: URLSessionConfiguration{ 12 | var conf = Self.default 13 | #if os(iOS) 14 | // multipath is only available on iOS, enable it only in this case 15 | conf.multipathServiceType = .handover 16 | #endif 17 | // if we aren't building for iOS, defaultMPTCP == default, thus fall back to TCP 18 | return conf 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /swift/macOS/README.md: -------------------------------------------------------------------------------- 1 | # Using Multipath TCP in Swift on macOS/iOS 2 | 3 | To use Multipath TCP in Swift, you simply need to change a bit the configuration given to the connection, by selecting a Multipath TCP mode. The list of available modes is given in [the official documentation](https://developer.apple.com/documentation/network/nwparameters/multipathservicetype). Here is an example: 4 | 5 | ```swift 6 | // request to create a new TCP connection 7 | // note that we can also choose .tls to create a TLS session above (MP)TCP 8 | let params: NWParameters = .tcp 9 | // Handover mode: Other types can be selected 10 | params.multipathServiceType = .handover 11 | 12 | let connection = NWConnection(to: server, using: params) 13 | ``` 14 | 15 | Please, note that currently, the _Aggregate_ mode doesn't seem to work according to our tests. Also, only the client side is supported. -------------------------------------------------------------------------------- /swift/macOS/src/MPTCPClient.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MPTCPClient.swift 3 | // mptcp-network 4 | // 5 | // Created by Anthony Doeraene on 07/08/2024. 6 | // 7 | 8 | import Foundation 9 | import Network 10 | 11 | class MPTCPClient{ 12 | var connection: NWConnection 13 | var queue: DispatchQueue 14 | var ready: Bool = false 15 | 16 | init(to server: NWEndpoint, params: NWParameters = .tls){ 17 | queue = DispatchQueue(label: "MPTCP client Queue") 18 | 19 | params.multipathServiceType = .handover 20 | 21 | connection = NWConnection(to: server, using: params) 22 | 23 | connection.stateUpdateHandler = { [weak self] (newState) in 24 | switch (newState){ 25 | case .waiting( _): 26 | print("Waiting") 27 | case .ready: 28 | self?.ready = true 29 | case .failed( _): 30 | print("Failed") 31 | default: 32 | break 33 | } 34 | } 35 | } 36 | 37 | func start(){ 38 | connection.start(queue: queue) 39 | while !ready {} 40 | } 41 | 42 | func get(path: String) async -> String{ 43 | let message = "GET \(path) HTTP/1.0\r\nUser-Agent: curl/1.0.0\r\n\n".data(using: .utf8) 44 | connection.send(content: message, completion: .contentProcessed({ (error) in 45 | if let error = error{ 46 | print("Send error: \(error)") 47 | } 48 | })) 49 | 50 | return await withCheckedContinuation{continuation in 51 | connection.receive(minimumIncompleteLength: 0, maximumLength: 1024) { (content, context, isComplete, error) in 52 | if let content, let res = String(data: content, encoding: .ascii){ 53 | continuation.resume(returning: res) 54 | } 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /swift/macOS/src/MainApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // mptcp-network 4 | // 5 | // Created by Anthony Doeraene on 07/08/2024. 6 | // 7 | 8 | import Foundation 9 | import Network 10 | 11 | @main 12 | struct MainApp{ 13 | static func main() async throws{ 14 | let endpoint: NWEndpoint = .url(URL(string: "https://check.mptcp.dev")!) 15 | let client = MPTCPClient(to: endpoint) 16 | client.start() 17 | let res = await client.get(path: "/") 18 | print("Received response:\n\n\(res)") 19 | } 20 | } 21 | 22 | --------------------------------------------------------------------------------