diff --git a/doc/configuration.txt b/doc/configuration.txt index 26d63e669..15bf24e34 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -28279,6 +28279,27 @@ report this to the maintainers. range can or must be specified. It is considered as an alias of 'stream+ipv4@'. +'mptcp@
[:port1[-port2]]' following is considered as an IPv4 + or IPv6 address depending of the syntax but + socket type and transport method is forced to + "stream", with the MPTCP protocol. Depending + on the statement using this address, a port or + a port range can or must be specified. + +'mptcp4@[:port1[-port2]]' following is always considered as + an IPv4 address but socket type and transport + method is forced to "stream", with the MPTCP + protocol. Depending on the statement using + this address, a port or port range can or + must be specified. + +'mptcp6@[:port1[-port2]]' following is always considered as + an IPv6 address but socket type and transport + method is forced to "stream", with the MPTCP + protocol. Depending on the statement using + this address, a port or port range can or + must be specified. + 'udp@[:port1[-port2]]' following is considered as an IPv4 or IPv6 address depending of the syntax but socket type and transport method is forced to diff --git a/examples/mptcp-backend.py b/examples/mptcp-backend.py new file mode 100644 index 000000000..5237de542 --- /dev/null +++ b/examples/mptcp-backend.py @@ -0,0 +1,22 @@ +# ============================================================================= +# Example of a simple backend server using mptcp in python, used with mptcp.cfg +# ============================================================================= + +import socket + +sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM, socket.IPPROTO_MPTCP) +sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) +# dual stack IPv4/IPv6 +sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0) + +sock.bind(("::", 4331)) +sock.listen() + +while True: + (conn, address) = sock.accept() + req = conn.recv(1024) + print(F"Received request : {req}") + conn.send(b"HTTP/1.0 200 OK\r\n\r\nHello\n") + conn.close() + +sock.close() diff --git a/examples/mptcp.cfg b/examples/mptcp.cfg new file mode 100644 index 000000000..d43483dfe --- /dev/null +++ b/examples/mptcp.cfg @@ -0,0 +1,23 @@ +# You can test this configuration by running the command: +# +# $ mptcpize run curl localhost:5000 + +global + strict-limits # refuse to start if insufficient FDs/memory + # add some process-wide tuning here if required + +defaults + mode http + balance roundrobin + timeout client 60s + timeout server 60s + timeout connect 1s + +frontend main + bind mptcp@[::]:5000 + default_backend mptcp_backend + +# MPTCP is usually used on the frontend, but it is also possible +# to enable it to communicate with the backend +backend mptcp_backend + server mptcp_server mptcp@[::]:4331 diff --git a/include/haproxy/compat.h b/include/haproxy/compat.h index 3829060b7..68474fe8e 100644 --- a/include/haproxy/compat.h +++ b/include/haproxy/compat.h @@ -317,6 +317,16 @@ typedef struct { } empty_t; #define queue _queue #endif +/* Define a flag indicating if MPTCP is available */ +#ifdef __linux__ +#define HA_HAVE_MPTCP 1 +#endif + +/* only Linux defines IPPROTO_MPTCP */ +#ifndef IPPROTO_MPTCP +#define IPPROTO_MPTCP 262 +#endif + #endif /* _HAPROXY_COMPAT_H */ /* diff --git a/include/haproxy/sock_inet.h b/include/haproxy/sock_inet.h index 6f07e637a..1c3b7a303 100644 --- a/include/haproxy/sock_inet.h +++ b/include/haproxy/sock_inet.h @@ -31,6 +31,14 @@ extern int sock_inet6_v6only_default; extern int sock_inet_tcp_maxseg_default; extern int sock_inet6_tcp_maxseg_default; +#ifdef HA_HAVE_MPTCP +extern int sock_inet_mptcp_maxseg_default; +extern int sock_inet6_mptcp_maxseg_default; +#else +#define sock_inet_mptcp_maxseg_default -1 +#define sock_inet6_mptcp_maxseg_default -1 +#endif + extern struct proto_fam proto_fam_inet4; extern struct proto_fam proto_fam_inet6; diff --git a/src/backend.c b/src/backend.c index 6956d9bfe..e4bd465e9 100644 --- a/src/backend.c +++ b/src/backend.c @@ -1690,8 +1690,9 @@ skip_reuse: if (!srv_conn->xprt) { /* set the correct protocol on the output stream connector */ + if (srv) { - if (conn_prepare(srv_conn, protocol_lookup(srv_conn->dst->ss_family, PROTO_TYPE_STREAM, 0), srv->xprt)) { + if (conn_prepare(srv_conn, protocol_lookup(srv_conn->dst->ss_family, PROTO_TYPE_STREAM, srv->alt_proto), srv->xprt)) { conn_free(srv_conn); return SF_ERR_INTERNAL; } diff --git a/src/proto_tcp.c b/src/proto_tcp.c index cf79ffbc5..39de465ef 100644 --- a/src/proto_tcp.c +++ b/src/proto_tcp.c @@ -145,6 +145,98 @@ struct protocol proto_tcpv6 = { INITCALL1(STG_REGISTER, protocol_register, &proto_tcpv6); +#ifdef HA_HAVE_MPTCP +/* Most fields are copied from proto_tcpv4 */ +struct protocol proto_mptcpv4 = { + .name = "mptcpv4", + + /* connection layer */ + .xprt_type = PROTO_TYPE_STREAM, + .listen = tcp_bind_listener, + .enable = tcp_enable_listener, + .disable = tcp_disable_listener, + .add = default_add_listener, + .unbind = default_unbind_listener, + .suspend = default_suspend_listener, + .resume = default_resume_listener, + .accept_conn = sock_accept_conn, + .ctrl_init = sock_conn_ctrl_init, + .ctrl_close = sock_conn_ctrl_close, + .connect = tcp_connect_server, + .drain = sock_drain, + .check_events = sock_check_events, + .ignore_events = sock_ignore_events, + .get_info = tcp_get_info, + + /* binding layer */ + .rx_suspend = tcp_suspend_receiver, + .rx_resume = tcp_resume_receiver, + + /* address family */ + .fam = &proto_fam_inet4, + + /* socket layer */ + .proto_type = PROTO_TYPE_STREAM, + .sock_type = SOCK_STREAM, + .sock_prot = IPPROTO_MPTCP, /* MPTCP specific */ + .rx_enable = sock_enable, + .rx_disable = sock_disable, + .rx_unbind = sock_unbind, + .rx_listening = sock_accepting_conn, + .default_iocb = sock_accept_iocb, +#ifdef SO_REUSEPORT + .flags = PROTO_F_REUSEPORT_SUPPORTED, +#endif +}; + +INITCALL1(STG_REGISTER, protocol_register, &proto_mptcpv4); + +/* Most fields are copied from proto_tcpv6 */ +struct protocol proto_mptcpv6 = { + .name = "mptcpv6", + + /* connection layer */ + .xprt_type = PROTO_TYPE_STREAM, + .listen = tcp_bind_listener, + .enable = tcp_enable_listener, + .disable = tcp_disable_listener, + .add = default_add_listener, + .unbind = default_unbind_listener, + .suspend = default_suspend_listener, + .resume = default_resume_listener, + .accept_conn = sock_accept_conn, + .ctrl_init = sock_conn_ctrl_init, + .ctrl_close = sock_conn_ctrl_close, + .connect = tcp_connect_server, + .drain = sock_drain, + .check_events = sock_check_events, + .ignore_events = sock_ignore_events, + .get_info = tcp_get_info, + + /* binding layer */ + .rx_suspend = tcp_suspend_receiver, + .rx_resume = tcp_resume_receiver, + + /* address family */ + .fam = &proto_fam_inet6, + + /* socket layer */ + .proto_type = PROTO_TYPE_STREAM, + .sock_type = SOCK_STREAM, + .sock_prot = IPPROTO_MPTCP, /* MPTCP specific */ + .rx_enable = sock_enable, + .rx_disable = sock_disable, + .rx_unbind = sock_unbind, + .rx_listening = sock_accepting_conn, + .default_iocb = sock_accept_iocb, +#ifdef SO_REUSEPORT + .flags = PROTO_F_REUSEPORT_SUPPORTED, +#endif +}; + +INITCALL1(STG_REGISTER, protocol_register, &proto_mptcpv6); +#endif + /* Binds ipv4/ipv6 address