From 003ff32399eb3a674e0ce2ee22e6e8071b30a6a2 Mon Sep 17 00:00:00 2001 From: Colin Pfingstl <19822061+koflin@users.noreply.github.com> Date: Tue, 29 Jul 2025 20:07:11 +0200 Subject: [PATCH 1/2] feat(scion): support QUIC over SCION with csnet --- CMakeLists.txt | 7 +++ csnet-dist/topology/topology.json | 47 ++++++++++++++++ picoquic/picoquic_packet_loop.h | 4 ++ picoquic/picosocks.c | 83 ++++++++++++++++++++------- picoquic/picosocks.h | 39 +++++++++++++ picoquic/sockloop.c | 93 +++++++++++++++++++++++++++++-- sample/sample_server.c | 2 +- 7 files changed, 250 insertions(+), 25 deletions(-) create mode 100644 csnet-dist/topology/topology.json diff --git a/CMakeLists.txt b/CMakeLists.txt index 77de59ab0..f7279bb21 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -272,6 +272,11 @@ else() list(APPEND PICOQUIC_COMPILE_DEFINITIONS PTLS_WITHOUT_OPENSSL) endif() +option(USE_SCION "use SCION" OFF) +if (USE_SCION) + list(APPEND PICOQUIC_COMPILE_DEFINITIONS PICOQUIC_USE_SCION) +endif() + OPTION(WITH_MBEDTLS "enable MBEDTLS" OFF) IF (WITH_MBEDTLS) @@ -341,12 +346,14 @@ target_include_directories(picoquic-core ${OPENSSL_INCLUDE_DIR} PUBLIC ${MBEDTLS_INCLUDE_DIRS} + ${SCION_INCLUDE_DIR} picoquic picoquic_mbedtls) target_link_libraries(picoquic-core PRIVATE ${OPENSSL_LIBRARIES} ${MBEDTLS_LIBRARIES} + ${SCION_LIBRARIES} PUBLIC ${PTLS_LIBRARIES} Threads::Threads) diff --git a/csnet-dist/topology/topology.json b/csnet-dist/topology/topology.json new file mode 100644 index 000000000..deebd40b0 --- /dev/null +++ b/csnet-dist/topology/topology.json @@ -0,0 +1,47 @@ +{ + "attributes": [], + "isd_as": "1-ff00:0:133", + "mtu": 1472, + "dispatched_ports": "1-65535", + "control_service": { + "cs1-ff00_0_133-1": { + "addr": "127.0.0.99:31066" + } + }, + "discovery_service": { + "cs1-ff00_0_133-1": { + "addr": "127.0.0.99:31066" + } + }, + "border_routers": { + "br1-ff00_0_133-1": { + "internal_addr": "127.0.0.97:31068", + "interfaces": { + "1": { + "underlay": { + "local": "127.0.0.29:50000", + "remote": "127.0.0.28:50000" + }, + "isd_as": "1-ff00:0:122", + "link_to": "peer", + "mtu": 1472, + "remote_interface_id": 1 + } + } + }, + "br1-ff00_0_133-2": { + "internal_addr": "127.0.0.98:31070", + "interfaces": { + "2": { + "underlay": { + "local": "127.0.0.35:50000", + "remote": "127.0.0.34:50000" + }, + "isd_as": "1-ff00:0:132", + "link_to": "parent", + "mtu": 1472 + } + } + } + } +} diff --git a/picoquic/picoquic_packet_loop.h b/picoquic/picoquic_packet_loop.h index ed87c6586..27b41d9bd 100644 --- a/picoquic/picoquic_packet_loop.h +++ b/picoquic/picoquic_packet_loop.h @@ -69,6 +69,10 @@ typedef struct st_picoquic_socket_ctx_t { int so_sndbuf; int so_rcvbuf; #endif +#ifdef PICOQUIC_USE_SCION + struct scion_topology *topology; + struct scion_network *network; +#endif } picoquic_socket_ctx_t; /* The packet loop will call the application back after specific events. diff --git a/picoquic/picosocks.c b/picoquic/picosocks.c index 27a65014d..5ebd6691f 100644 --- a/picoquic/picosocks.c +++ b/picoquic/picosocks.c @@ -46,13 +46,22 @@ int picoquic_bind_to_port(SOCKET_TYPE fd, int af, int port) addr_length = sizeof(struct sockaddr_in6); } +#ifdef PICOQUIC_USE_SCION + return scion_bind(fd, (struct sockaddr*)&sa, addr_length); +#else return bind(fd, (struct sockaddr*)&sa, addr_length); +#endif } int picoquic_get_local_address(SOCKET_TYPE sd, struct sockaddr_storage * addr) { socklen_t name_len = sizeof(struct sockaddr_storage); + +#ifdef PICOQUIC_USE_SCION + return scion_getsockname(sd, (struct sockaddr *)addr, &name_len, NULL); +#else return getsockname(sd, (struct sockaddr *)addr, &name_len); +#endif } int picoquic_socket_set_pkt_info(SOCKET_TYPE sd, int af) @@ -61,28 +70,28 @@ int picoquic_socket_set_pkt_info(SOCKET_TYPE sd, int af) #ifdef _WINDOWS int option_value = 1; if (af == AF_INET6) { - ret = setsockopt(sd, IPPROTO_IPV6, IPV6_PKTINFO, (char*)&option_value, sizeof(int)); + ret = SOCKET_SETOPT(sd, IPPROTO_IPV6, IPV6_PKTINFO, (char*)&option_value, sizeof(int)); } else { - ret = setsockopt(sd, IPPROTO_IP, IP_PKTINFO, (char*)&option_value, sizeof(int)); + ret = SOCKET_SETOPT(sd, IPPROTO_IP, IP_PKTINFO, (char*)&option_value, sizeof(int)); } #else if (af == AF_INET6) { int val = 1; - ret = setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY, + ret = SOCKET_SETOPT(sd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)); if (ret == 0) { val = 1; - ret = setsockopt(sd, IPPROTO_IPV6, IPV6_RECVPKTINFO, (char*)&val, sizeof(int)); + ret = SOCKET_SETOPT(sd, IPPROTO_IPV6, IPV6_RECVPKTINFO, (char*)&val, sizeof(int)); } } else { int val = 1; #ifdef IP_PKTINFO - ret = setsockopt(sd, IPPROTO_IP, IP_PKTINFO, (char*)&val, sizeof(int)); + ret = SOCKET_SETOPT(sd, IPPROTO_IP, IP_PKTINFO, (char*)&val, sizeof(int)); #else /* The IP_PKTINFO structure is not defined on BSD */ - ret = setsockopt(sd, IPPROTO_IP, IP_RECVDSTADDR, (char*)&val, sizeof(int)); + ret = SOCKET_SETOPT(sd, IPPROTO_IP, IP_RECVDSTADDR, (char*)&val, sizeof(int)); #endif } #endif @@ -100,7 +109,7 @@ int picoquic_socket_set_ecn_options(SOCKET_TYPE sd, int af, int * recv_set, int { DWORD recvEcn = 1; /* Request receiving ECN reports in recvmsg */ - ret = setsockopt(sd, IPPROTO_IPV6, IPV6_ECN, (char *)&recvEcn, sizeof(recvEcn)); + ret = SOCKET_SETOPT(sd, IPPROTO_IPV6, IPV6_ECN, (char *)&recvEcn, sizeof(recvEcn)); if (ret < 0) { DBG_PRINTF("setsockopt IPV6_ECN (0x%x) fails, errno: %d\n", recvEcn, GetLastError()); ret = -1; @@ -124,7 +133,7 @@ int picoquic_socket_set_ecn_options(SOCKET_TYPE sd, int af, int * recv_set, int DWORD recvEcn =1; /* Request receiving ECN reports in recvmsg */ - ret = setsockopt(sd, IPPROTO_IP, IP_ECN, (CHAR*)&recvEcn, sizeof(recvEcn)); + ret = SOCKET_SETOPT(sd, IPPROTO_IP, IP_ECN, (CHAR*)&recvEcn, sizeof(recvEcn)); if (ret < 0) { DBG_PRINTF("setsockopt IP_ECN (0x%x) fails, errno: %d\n", recvEcn, GetLastError()); ret = -1; @@ -145,7 +154,7 @@ int picoquic_socket_set_ecn_options(SOCKET_TYPE sd, int af, int * recv_set, int #if defined(IPV6_TCLASS) { unsigned int ecn = PICOQUIC_ECN_ECT_1; /* Setting ECN_ECT_1 in outgoing packets */ - if (setsockopt(sd, IPPROTO_IPV6, IPV6_TCLASS, &ecn, sizeof(ecn)) < 0) { + if (SOCKET_SETOPT(sd, IPPROTO_IPV6, IPV6_TCLASS, &ecn, sizeof(ecn)) < 0) { DBG_PRINTF("setsockopt IPV6_TCLASS (0x%x) fails, errno: %d\n", ecn, errno); *send_set = 0; } @@ -162,7 +171,7 @@ int picoquic_socket_set_ecn_options(SOCKET_TYPE sd, int af, int * recv_set, int unsigned int set = 0x01; /* Request receiving TOS reports in recvmsg */ - if (setsockopt(sd, IPPROTO_IPV6, IPV6_RECVTCLASS, &set, sizeof(set)) < 0) { + if (SOCKET_SETOPT(sd, IPPROTO_IPV6, IPV6_RECVTCLASS, &set, sizeof(set)) < 0) { DBG_PRINTF("setsockopt IPv6 IPV6_RECVTCLASS (0x%x) fails, errno: %d\n", set, errno); ret = -1; *recv_set = 0; @@ -183,7 +192,7 @@ int picoquic_socket_set_ecn_options(SOCKET_TYPE sd, int af, int * recv_set, int { unsigned int ecn = PICOQUIC_ECN_ECT_1; /* Request setting ECN_ECT_1 in outgoing packets */ - if (setsockopt(sd, IPPROTO_IP, IP_TOS, &ecn, sizeof(ecn)) < 0) { + if (SOCKET_SETOPT(sd, IPPROTO_IP, IP_TOS, &ecn, sizeof(ecn)) < 0) { DBG_PRINTF("setsockopt IPv4 IP_TOS (0x%x) fails, errno: %d\n", ecn, errno); *send_set = 0; } @@ -201,7 +210,7 @@ int picoquic_socket_set_ecn_options(SOCKET_TYPE sd, int af, int * recv_set, int unsigned int set = 1; /* Request receiving TOS reports in recvmsg */ - if (setsockopt(sd, IPPROTO_IP, IP_RECVTOS, &set, sizeof(set)) < 0) { + if (SOCKET_SETOPT(sd, IPPROTO_IP, IP_RECVTOS, &set, sizeof(set)) < 0) { DBG_PRINTF("setsockopt IPv4 IP_RECVTOS (0x%x) fails, errno: %d\n", set, errno); ret = -1; *recv_set = 0; @@ -227,10 +236,10 @@ int picoquic_socket_set_pmtud_options(SOCKET_TYPE sd, int af) #if defined __linux && defined(IP_MTU_DISCOVER) && defined(IPV6_MTU_DISCOVER) && defined(IP_PMTUDISC_PROBE) int val = IP_PMTUDISC_PROBE; if (af == AF_INET6) { - ret = setsockopt(sd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val, sizeof(int)); + ret = SOCKET_SETOPT(sd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val, sizeof(int)); } else { - ret = setsockopt(sd, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(int)); + ret = SOCKET_SETOPT(sd, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(int)); } #else #ifdef UNREFERENCED_PARAMETER @@ -810,7 +819,7 @@ picoquic_recvmsg_async_ctx_t * picoquic_create_async_socket(int af, int recv_coa } else if (recv_coalesced) { - if ((ret = setsockopt(ctx->fd, IPPROTO_UDP, UDP_RECV_MAX_COALESCED_SIZE, (char*)&coalesced_size, + if ((ret = SOCKET_SETOPT(ctx->fd, IPPROTO_UDP, UDP_RECV_MAX_COALESCED_SIZE, (char*)&coalesced_size, (int)sizeof(coalesced_size))) != 0) { last_error = GetLastError(); DBG_PRINTF("Cannot set UDP_RECV_MAX_COALESCED_SIZE %d, returns %d (%d)", @@ -1024,12 +1033,18 @@ int picoquic_recvmsg(SOCKET_TYPE fd, msg.msg_control = (void*)cmsg_buffer; msg.msg_controllen = sizeof(cmsg_buffer); +#ifdef PICOQUIC_USE_SCION + bytes_recv = scion_recvmsg(fd, &msg, 0, NULL, NULL); +#else bytes_recv = recvmsg(fd, &msg, 0); +#endif if (bytes_recv <= 0) { addr_from->ss_family = 0; } else { +#ifndef PICOQUIC_USE_SCION picoquic_socks_cmsg_parse(&msg, addr_dest, dest_if, received_ecn, NULL); +#endif } return bytes_recv; @@ -1128,14 +1143,28 @@ int picoquic_sendmsg(SOCKET_TYPE fd, msg.msg_control = (void*)cmsg_buffer; msg.msg_controllen = sizeof(cmsg_buffer); +#ifdef PICOQUIC_USE_SCION + msg.msg_control = NULL; + msg.msg_controllen = 0; +#else /* Format the control message */ picoquic_socks_cmsg_format(&msg, length, send_msg_size, addr_from, dest_if); +#endif + // TODO get rid of hardcoded dst_ia +#ifdef PICOQUIC_USE_SCION + bytes_sent = scion_sendmsg(fd, &msg, 0, 0x1ff0000000133, NULL); +#else bytes_sent = sendmsg(fd, &msg, 0); +#endif if (bytes_sent <= 0) { +#ifdef PICOQUIC_USE_SCION + int last_error = bytes_sent; +#else int last_error = errno; +#endif #ifndef DISABLE_DEBUG_PRINTF DBG_PRINTF("Could not send packet on UDP socket[AF=%d]= %d!\n", addr_dest->sa_family, last_error); @@ -1172,10 +1201,17 @@ int picoquic_select_ex(SOCKET_TYPE* sockets, FD_ZERO(&readfds); for (int i = 0; i < nb_sockets; i++) { - if (sockmax < (int)sockets[i]) { - sockmax = (int)sockets[i]; +#ifdef PICOQUIC_USE_SCION + int fd; + scion_getsockfd(sockets[i], &fd); +#else + int fd = (int)sockets[i]; +#endif + + if (sockmax < fd) { + sockmax = fd; } - FD_SET(sockets[i], &readfds); + FD_SET(fd, &readfds); } if (delta_t <= 0) { @@ -1198,7 +1234,14 @@ int picoquic_select_ex(SOCKET_TYPE* sockets, DBG_PRINTF("Error: select returns %d\n", ret_select); } else if (ret_select > 0) { for (int i = 0; i < nb_sockets; i++) { - if (FD_ISSET(sockets[i], &readfds)) { +#ifdef PICOQUIC_USE_SCION + int fd; + scion_getsockfd(sockets[i], &fd); +#else + int fd = (int)sockets[i]; +#endif + + if (FD_ISSET(fd, &readfds)) { *socket_rank = i; bytes_recv = picoquic_recvmsg(sockets[i], addr_from, addr_dest, dest_if, received_ecn, @@ -1390,6 +1433,8 @@ int picoquic_socket_error_implies_unreachable(int sock_err) WSAEACCES, WSAEADDRNOTAVAIL, WSAEAFNOSUPPORT, WSAECONNRESET, WSAEDESTADDRREQ, WSAEHOSTUNREACH, WSAENETDOWN, WSAENETRESET, WSAENETUNREACH, WSAESHUTDOWN, -1 }; +#elif defined(PICOQUIC_USE_SCION) + static int unreachable_errors[] = { SCION_ERR_NO_PATHS, -1 }; #else static int unreachable_errors[] = { EAFNOSUPPORT, ECONNRESET, EHOSTUNREACH, ENETDOWN, ENETUNREACH, -1 }; diff --git a/picoquic/picosocks.h b/picoquic/picosocks.h index d5988696c..70a7d9cff 100644 --- a/picoquic/picosocks.h +++ b/picoquic/picosocks.h @@ -43,6 +43,12 @@ #ifndef SOCKET_CLOSE #define SOCKET_CLOSE(x) closesocket(x) #endif +#ifndef SOCKET_SETOPT +#define SOCKET_SETOPT(socket, level, optname, optval, optlen) setsockopt(socket, level, optname, optval, optlen) +#endif +#ifndef SOCKET_GETOPT +#define SOCKET_GETOPT(socket, level, optname, optval, optlen) getsockopt(socket, level, optname, optval, optlen) +#endif #ifndef WSA_START_DATA #define WSA_START_DATA WSADATA #endif @@ -92,6 +98,30 @@ #include #include +#ifdef PICOQUIC_USE_SCION + +#include +#ifndef SOCKET_TYPE +#define SOCKET_TYPE struct scion_socket * +#endif +#ifndef INVALID_SOCKET +#define INVALID_SOCKET (NULL) +#endif +#ifndef SOCKET_CLOSE +#define SOCKET_CLOSE(x) scion_close(x) +#endif +#ifndef SOCKET_SETOPT +#define SOCKET_SETOPT(socket, level, optname, optval, optlen) scion_setsockopt(socket, level, optname, optval, optlen) +#endif +#ifndef SOCKET_GETOPT +#define SOCKET_GETOPT(socket, level, optname, optval, optlen) scion_getsockopt(socket, level, optname, optval, optlen) +#endif +#ifndef WSA_LAST_ERROR +#define WSA_LAST_ERROR(x) ((long)(x)) +#endif + +#else + #ifndef SOCKET_TYPE #define SOCKET_TYPE int #endif @@ -101,12 +131,21 @@ #ifndef SOCKET_CLOSE #define SOCKET_CLOSE(x) close(x) #endif +#ifndef SOCKET_SETOPT +#define SOCKET_SETOPT(socket, level, optname, optval, optlen) setsockopt(socket, level, optname, optval, optlen) +#endif +#ifndef SOCKET_GETOPT +#define SOCKET_GETOPT(socket, level, optname, optval, optlen) getsockopt(socket, level, optname, optval, optlen) +#endif #ifndef WSA_LAST_ERROR #define WSA_LAST_ERROR(x) ((long)(x)) #endif #ifndef IPV6_RECVPKTINFO #define IPV6_RECVPKTINFO IPV6_PKTINFO /* Cygwin */ #endif + +#endif + #endif #define PICOQUIC_ECN_ECT_0 0x02 diff --git a/picoquic/sockloop.c b/picoquic/sockloop.c index 8286d9eff..4d9180da2 100644 --- a/picoquic/sockloop.c +++ b/picoquic/sockloop.c @@ -50,6 +50,12 @@ #ifndef SOCKET_CLOSE #define SOCKET_CLOSE(x) closesocket(x) #endif +#ifndef SOCKET_SETOPT +#define SOCKET_SETOPT(socket, level, optname, optval, optlen) setsockopt(socket, level, optname, optval, optlen) +#endif +#ifndef SOCKET_GETOPT +#define SOCKET_GETOPT(socket, level, optname, optval, optlen) getsockopt(socket, level, optname, optval, optlen) +#endif #ifndef WSA_LAST_ERROR #define WSA_LAST_ERROR(x) WSAGetLastError() #endif @@ -89,6 +95,30 @@ #include +#ifdef PICOQUIC_USE_SCION + +#include +#ifndef SOCKET_TYPE +#define SOCKET_TYPE struct scion_socket * +#endif +#ifndef INVALID_SOCKET +#define INVALID_SOCKET (NULL) +#endif +#ifndef SOCKET_CLOSE +#define SOCKET_CLOSE(x) scion_close(x) +#endif +#ifndef SOCKET_SETOPT +#define SOCKET_SETOPT(socket, level, optname, optval, optlen) scion_setsockopt(socket, level, optname, optval, optlen) +#endif +#ifndef SOCKET_GETOPT +#define SOCKET_GETOPT(socket, level, optname, optval, optlen) scion_getsockopt(socket, level, optname, optval, optlen) +#endif +#ifndef WSA_LAST_ERROR +#define WSA_LAST_ERROR(x) ((long)(x)) +#endif + +#else + #ifndef SOCKET_TYPE #define SOCKET_TYPE int #endif @@ -98,9 +128,18 @@ #ifndef SOCKET_CLOSE #define SOCKET_CLOSE(x) close(x) #endif +#ifndef SOCKET_SETOPT +#define SOCKET_SETOPT(socket, level, optname, optval, optlen) setsockopt(socket, level, optname, optval, optlen) +#endif +#ifndef SOCKET_GETOPT +#define SOCKET_GETOPT(socket, level, optname, optval, optlen) getsockopt(socket, level, optname, optval, optlen) +#endif #ifndef WSA_LAST_ERROR #define WSA_LAST_ERROR(x) ((long)(x)) #endif + +#endif + #endif #include "picosocks.h" @@ -142,7 +181,7 @@ void picoquic_sockloop_win_coalescing_test(int * recv_coalesced, int * send_coal if (udp_gso_available) { option_length = (int)sizeof(option_value); - if ((ret = getsockopt(fd, IPPROTO_UDP, UDP_SEND_MSG_SIZE, (char*)&option_value, &option_length)) != 0) { + if ((ret = SOCKET_GETOPT(fd, IPPROTO_UDP, UDP_SEND_MSG_SIZE, (char*)&option_value, &option_length)) != 0) { last_error = GetLastError(); DBG_PRINTF("UDP_SEND_MSG_SIZE not supported, returns %d (%d)", ret, last_error); udp_gso_available = 0; @@ -155,7 +194,7 @@ void picoquic_sockloop_win_coalescing_test(int * recv_coalesced, int * send_coal #ifdef UDP_RECV_MAX_COALESCED_SIZE option_value = 1; option_length = (int)sizeof(option_value); - if ((ret = getsockopt(fd, IPPROTO_UDP, UDP_RECV_MAX_COALESCED_SIZE, (char*)&option_value, &option_length)) != 0) { + if ((ret = SOCKET_GETOPT(fd, IPPROTO_UDP, UDP_RECV_MAX_COALESCED_SIZE, (char*)&option_value, &option_length)) != 0) { last_error = GetLastError(); DBG_PRINTF("UDP_RECV_MAX_COALESCED_SIZE not supported, returns %d (%d)", ret, last_error); } @@ -363,8 +402,30 @@ int picoquic_packet_loop_open_socket(int socket_buffer_size, int do_not_use_gso, } s_ctx->overlap.hEvent = WSA_INVALID_EVENT; s_ctx->fd = WSASocket(s_ctx->af, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, WSA_FLAG_OVERLAPPED); +#else +#ifdef PICOQUIC_USE_SCION + // TODO fix cleanup here + // TODO configurable topolog path + ret = scion_topology_from_file(&s_ctx->topology, "csnet-dist/topology/topology.json"); + if (ret != 0) { + DBG_PRINTF("Cannot create topology %d\n", ret); + return ret; + } + + ret = scion_network(&s_ctx->network, s_ctx->topology); + if (ret != 0) { + DBG_PRINTF("Cannot create network %d\n", ret); + return ret; + } + + ret = scion_socket(&s_ctx->fd, s_ctx->af, SCION_SOCK_DGRAM, SCION_PROTO_UDP, s_ctx->network); + if (ret != 0) { + DBG_PRINTF("Cannot create socket %d\n", ret); + return ret; + } #else s_ctx->fd = socket(s_ctx->af, SOCK_DGRAM, IPPROTO_UDP); +#endif #endif if (s_ctx->fd == INVALID_SOCKET || @@ -396,13 +457,13 @@ int picoquic_packet_loop_open_socket(int socket_buffer_size, int do_not_use_gso, opt_len = sizeof(int); so_sndbuf = socket_buffer_size; - opt_ret = setsockopt(s_ctx->fd, SOL_SOCKET, SO_SNDBUF, (const char*)&so_sndbuf, opt_len); + opt_ret = SOCKET_SETOPT(s_ctx->fd, SOL_SOCKET, SO_SNDBUF, (const char*)&so_sndbuf, opt_len); if (opt_ret == 0) { last_op = SO_RCVBUF; last_op_name = "SO_RECVBUF"; opt_len = sizeof(int); so_rcvbuf = socket_buffer_size; - opt_ret = setsockopt(s_ctx->fd, SOL_SOCKET, SO_RCVBUF, (const char*)&so_rcvbuf, opt_len); + opt_ret = SOCKET_SETOPT(s_ctx->fd, SOL_SOCKET, SO_RCVBUF, (const char*)&so_rcvbuf, opt_len); } if (opt_ret != 0) { int so_errbuf = 0; @@ -411,7 +472,7 @@ int picoquic_packet_loop_open_socket(int socket_buffer_size, int do_not_use_gso, #else int sock_error = errno; #endif - opt_ret = getsockopt(s_ctx->fd, SOL_SOCKET, last_op, (char*)&so_errbuf, &opt_len); + opt_ret = SOCKET_GETOPT(s_ctx->fd, SOL_SOCKET, last_op, (char*)&so_errbuf, &opt_len); DBG_PRINTF("Cannot set %s to %d, err=%d, so_sndbuf=%d (%d)", last_op_name, socket_buffer_size, sock_error, so_errbuf, opt_ret); ret = -1; @@ -598,10 +659,20 @@ int picoquic_packet_loop_select(picoquic_socket_ctx_t* s_ctx, FD_ZERO(&readfds); for (int i = 0; i < nb_sockets; i++) { +#ifdef PICOQUIC_USE_SCION + int fd; + scion_getsockfd(s_ctx[i].fd, &fd); + + if (sockmax < fd) { + sockmax = fd; + } + FD_SET(fd, &readfds); +#else if (sockmax < (int)s_ctx[i].fd) { sockmax = (int)s_ctx[i].fd; } FD_SET(s_ctx[i].fd, &readfds); +#endif } *is_wake_up_event = 0; @@ -648,15 +719,27 @@ int picoquic_packet_loop_select(picoquic_socket_ctx_t* s_ctx, else { for (int i = 0; i < nb_sockets; i++) { +#ifdef PICOQUIC_USE_SCION + int fd; + scion_getsockfd(s_ctx[i].fd, &fd); + + if (FD_ISSET(fd, &readfds)) { +#else if (FD_ISSET(s_ctx[i].fd, &readfds)) { +#endif *socket_rank = i; bytes_recv = picoquic_recvmsg(s_ctx[i].fd, addr_from, addr_dest, dest_if, received_ecn, buffer, buffer_max); if (bytes_recv <= 0) { +#ifdef PICOQUIC_USE_SCION + DBG_PRINTF("Could not receive packet on UDP socket[%d]= %d!\n", + i, fd); +#else DBG_PRINTF("Could not receive packet on UDP socket[%d]= %d!\n", i, (int)s_ctx[i].fd); +#endif break; } else { diff --git a/sample/sample_server.c b/sample/sample_server.c index 26057dcf6..bb18b338f 100644 --- a/sample/sample_server.c +++ b/sample/sample_server.c @@ -426,7 +426,7 @@ int picoquic_sample_server(int server_port, const char* server_cert, const char* * still, get the faulty driver fixed. */ if (ret == 0) { - ret = picoquic_packet_loop(quic, server_port, 0, 0, 0, 0, NULL, NULL); + ret = picoquic_packet_loop(quic, server_port, AF_INET, 0, 0, 0, NULL, NULL); } /* And finish. */ From d6086761929eaef81a75b19c11239adaaa4a6edb Mon Sep 17 00:00:00 2001 From: Colin Pfingstl <19822061+koflin@users.noreply.github.com> Date: Mon, 4 Aug 2025 02:08:11 +0200 Subject: [PATCH 2/2] docs: document building, how to run the example and future work --- README.md | 251 ++++-------------- picoquic/picosocks.c | 3 +- picoquic/sockloop.c | 6 +- .../topology/topology.json => topology.json | 0 4 files changed, 54 insertions(+), 206 deletions(-) rename csnet-dist/topology/topology.json => topology.json (100%) diff --git a/README.md b/README.md index 8baa5ce1f..0a6c7943c 100644 --- a/README.md +++ b/README.md @@ -1,202 +1,49 @@ -# picoquic - -Minimalist implementation of the QUIC protocol, as defined by the IETF. -The IETF spec started with the version of QUIC defined by Google and -implemented in Chrome, but the IETF spec is independent of Chrome, and -does not attempt to be backward compatible. - -The first goal of this project was to provide feedback on the development -of a QUIC standard in the IETF QUIC WG. Information on the WG is available at -https://datatracker.ietf.org/wg/quic/charter/. QUIC has been published as -RFC 9000, but there is still ongoing work, for example on multipath. Picoquic -enables developers to test this new work. - -The second goal is to experiment with API for non-HTTP development, such as -DNS over QUIC -- see RFC 9250. Then there are plenty of other features we may dream off, -such as support for peer-to-peer applications or forward error correction. -That's on the horizon, but not there now. - -The current version of Picoquic supports the QUIC specifications per -[RFC 9000](https://datatracker.ietf.org/doc/rfc9000), -[RFC 9001](https://datatracker.ietf.org/doc/rfc9001), -[RFC 9002](https://datatracker.ietf.org/doc/rfc9002), -and [RFC 8999](https://datatracker.ietf.org/doc/rfc8999). -It also implements the following extensions: - -* QUIC datagrams, per [RFC 9221]((https://datatracker.ietf.org/doc/rfc9221) -* Compatible version negotiation for QUIC, per [RFC 9368](https://www.rfc-editor.org/info/rfc9368) -* QUIC Version 2, per [RFC 3969](https://datatracker.ietf.org/doc/rfc9369/) -* Greasing the QUIC bit, per [RFC 9287](https://datatracker.ietf.org/doc/rfc9287/) -* QUIC ACK Frequency, per version 04 of [the ACK Frequency Draft](https://datatracker.ietf.org/doc/draft-ietf-quic-ack-frequency/) -* QUIC Spin Bit, per RFC 9000, -* The evolving [QUIC Multipath draft](https://datatracker.ietf.org/doc/draft-ietf-quic-multipath/), -* The [simple multipath](https://github.com/huitema/quicmpath) alternative to the QUIC Multipath draft -* The experimental [BDP draft](https://datatracker.ietf.org/doc/draft-kuhn-quic-bdpframe-extension/), - tested in various simulations of satellite links of interspatial links. -* An experimental [Timestamp Draft](https://datatracker.ietf.org/doc/draft-huitema-quic-ts/) - -The distribution also includes a minimal implementation of HTTP3 -per [RFC 9114](https://www.rfc-editor.org/rfc/rfc9114.html), including -a static implementation of QPACK, compatible with [RFC9204](https://www.rfc-editor.org/rfc/rfc9204.html) -with support for the following extensions: - -* HTTP Datagrams and Capsules,per [RFC 9297](https://www.rfc-editor.org/rfc/rfc9114.html) -* Web Transport over HTTP3, per [Web Transport Draft](https://datatracker.ietf.org/doc/draft-ietf-webtrans-http3/) - -The code in this repo is stable. Performance work is -ongoing -- recent tests showed picoquic sending data at up to 5Gbps. - -There are many implementations of Quic, listed -at https://github.com/quicwg/base-drafts/wiki/Implementations. Several implementations provide -docker images to the "Quic Interop Runner" project, with results updated daily -at https://interop.seemann.io/. - -Bastian Köcher has developed bindings of the picoquic library to [RUST](https://www.rust-lang.org/en-US/). -His repository can be found [here](https://github.com/bkchr/picoquic-rs). -You may want to check it. - -# Development - -Picoquic is currently developed as a Visual Studio 2017 project, -and simultaneously tested on Windows and on Linux. It has a dependency -on the [Picotls implementation of TLS 1.3](https://github.com/h2o/picotls). -Picotls has two modes, a feature rich version that depends on OpenSSL, and a -leaner version that only depends on the "minicrypto" library. For now, -Picoquic uses the OpenSSL version, and has a dependency on OpenSSL. - -The project consists of a core library (picoquic), of a test library -(picoquictest), and of a test program (picoquicdemo). All these are -written in C. In the Visual Studio project, the -test library is wrapped up in the Visual Studio unittest framework, which -makes for convenient regression testing during development. In the Linux -builds, the tests are run through a command line program. - -# Milestones - -As explained in the Wiki, Picoquic is actively tested against other implementations -during the QUIC Interop days. See https://github.com/private-octopus/picoquic/wiki/QUIC-milestones-and-interop-testing. -The current version is aligned with version 1, [RFC 9000](https://datatracker.ietf.org/doc/rfc9000/). - -An implemention of DNS over QUIC is available -as [Quicdoq](https://github.com/private-octopus/quicdoq). DNS over Quic is interesting -by itself, but it also provides an example for building an application different than -HTTP on top of Picoquic. - -We are spending time bettering the implementation, and the documentation, -including a first pass at [documenting architecture and API](doc/architecture.md). Initially -the focus has been on correctness rather than performance. We will keep correctness, -but we will improve performance, especially in light of practical experience with -applications. To facilitate performance tests, the demo program includes an -implementation of the [quic performance test protocol](doc/quicperf.md). -Suggestions for documentation, API, performance and more are wellcome. Feel free to -open an issue. - -Planned developments include support for the standard version of multipath, improved -support for Real Time Media over QUIC, as well as various other research issues, -in particular related to congestion control. - -# Building Picoquic - -Picoquic is developed in C, and can be built under Windows or Linux. Building the -project requires first managing the dependencies, [Picotls](https://github.com/h2o/picotls) -and OpenSSL. Please note that you will need a recent version of Picotls -- -the Picotls API has evolved recently to support the latest version of QUIC. The -current code is tested against the Picotls version of Tue Oct 31 11:23:32 2023 +0900, -after commit `af66fc4aa8853b0725fcb2c18a702e8f1c656cf1`. (Note that these last -commits changed the Picotls API by removing code for the now obsolete -ESNI draft; prior versions will not work with Picoquic.) -The code can use OpenSSL version 1.1.1 or OpenSSL version 3.0. - -More information can be found in the [docs](doc/building_picoquic.md) - -## Picoquic on Windows - -To build Picoquic on Windows, you need to: - - * Install and build Openssl on your machine - - * Document the location of the Openssl install in the environment variable OPENSSLDIR - (OPENSSL64DIR for the x64 builds) - - * Make sure that a copy of `libcrypto.lib` is available at that location, and that - a copy of `applink.c` is available at the `include` location: $(OPENSSLDIR)\include\ - for win32 builds, $(OPENSSL64DIR)\include\ for the x64 builds. - - * Clone and compile Picotls, using the Picotls for Windows options. The picotls project - should be in the same directory level as the picoquic project, and the folder name - should be kept as picotls. - - * Clone and compile Picoquic, using the Visual Studio 2017 solution picoquic.sln included in - the sources. - - * You can use the unit tests included in the Visual Studio solution to verify the port. - -## Picoquic on Linux - -Thanks to check-ins from Deb Banerjee and Igor Lubashev for the build experience on Linux. - -To build Picoquic on Linux, you need can either build picotls separately -or use an integrated option. In both cases, you need first to install and -build Openssl on your machine - -To build step by step, you should: - - * Clone and compile Picotls, using cmake as explained in the Picotls documentation. - - * Clone and compile Picoquic: -~~~ - cmake . - make -~~~ - -Instead of building picotls separately, you can use an integrated option -(thanks to Paul E. Jones and Suhas Nandakumar for developing that): - - * Clone and compile Picoquic and Picotls in a single command: -~~~ - cmake -DPICOQUIC_FETCH_PTLS=Y . - make -~~~ - -Either way, you can verify that everything worked: - - * Run the test program `picoquic_ct` to verify the port. - -The tests verify that the code compiles and runs correctly under Ubuntu, -using GitHub actions on Intel 64 bit VMs. We rely on user reports to verify -behavior on other architecture, e.g. ARM. Thanks to @defermelowie for testing on ARM 32 bits. - -## Picoquic on MacOSX - -Thanks to Frederik Deweerdt for ensuring that Picoquic runs on MacOSX. The build steps -are the same as for Linux. The tests verify that the code compiles and runs correctly under MacOS, -using GitHub actions on Intel 64 bit VMs. We rely on user reports to verify -behavior on other architecture, e.g. M1. Thanks to @defermelowie for testing on M1. - -## Picoquic on FreeBSD - -Same build steps as Linux. Picoquic probably also works on other BSD variants, but only FreeBSD -has been tested so far. - -## Using Picoquic in CLI mode - -See [Usage](doc/usage.md) for how to use various commands from shell. - -## Developing applications - -Sorry, not all that much documentation yet. This will come as we populate the wiki. Your -best bet is to look at the demonstration program "picoquicdemo" that is included in the -release. The sources are in "picoquicfirst/picoquicdemo.c". The `sample` folder -contains a code sample for a simplistic file transfer protocol, which might -be a good place to start. Look at the README.md file in the sample folder for -more details. - -## Testing previous versions - -The code is constantly updated to track the latest version of the specification. It currently -conforms to Version 1, and will negotiate support for the corresponding version `0x00000001` -- -that is, QUIC Transport version 1. Picoquic will also accept negotiation of previous versions down to draft-27. - -# Creating QLOG Log Files - -See [How To Produce QLOG files with picoquic](doc/QLOG.md) +# csnet-picoquic + +This is a fork of [picoquic](https://github.com/private-octopus/picoquic) that enables QUIC over SCION using [csnet](https://github.com/scionproto-contrib/csnet). + +## Requirements + +1. Install a release build of csnet: [csnet - Building and Installation](https://github.com/scionproto-contrib/csnet?tab=readme-ov-file#building-and-installation) + +## Building + +To build the picoquic fork run: +```bash +cmake -DPICOQUIC_FETCH_PTLS=Y \ + -DUSE_SCION=ON \ + -DSCION_LIBRARIES=/lib/libscion.a;/lib/libprotobuf-c.a;/lib/libnghttp2.a;/lib/libz.a \ + -DSCION_INCLUDE_DIR=/include \ + -DENABLE_ASAN=OFF \ + -DENABLE_UBSAN=OFF \ + . + +cmake --build . +``` + +## Running the Example + +1. **Start the local SCION network** + In the cloned `csnet` repository: + ```bash + sudo ./scripts/run-testnet.sh + ``` +2. **Custom Topology** + If you are not using the default topology, overwrite the `topology.json` file in this repository with your modified version. +3. **Start the sample server** + ```bash + ./picoquic_sample server 4443 certs/cert.pem certs/key.pem doc + ``` +4. **Start the sample client** + ```bash + ./picoquic_sample client 127.0.0.1 4443 /tmp socket_loop.md + ``` + +## Future Work + +- Make the topology path dynamically configurable in `picoquic_packet_loop_open_socket()` + - possibly provide two topology files: one for IPv4 sockets and one for IPv6 sockets +- Properly clean up the topology and network objects when closing the socket +- Make the destination IA a dynamic parameter in `picoquic_sendmsg()` +- Implement the control message `UDP_SEGMENT` in csnet and use it in `picoquic_sendmsg()` and `picoquic_recvmsg()` + - see https://github.com/scionproto-contrib/csnet/issues/73 \ No newline at end of file diff --git a/picoquic/picosocks.c b/picoquic/picosocks.c index 5ebd6691f..2b283a842 100644 --- a/picoquic/picosocks.c +++ b/picoquic/picosocks.c @@ -1144,6 +1144,7 @@ int picoquic_sendmsg(SOCKET_TYPE fd, msg.msg_controllen = sizeof(cmsg_buffer); #ifdef PICOQUIC_USE_SCION + // TODO instead of sending no control messages at all, implement some useful control messages in csnet and use them here (see picoquic_socks_cmsg_format()) msg.msg_control = NULL; msg.msg_controllen = 0; #else @@ -1151,8 +1152,8 @@ int picoquic_sendmsg(SOCKET_TYPE fd, picoquic_socks_cmsg_format(&msg, length, send_msg_size, addr_from, dest_if); #endif - // TODO get rid of hardcoded dst_ia #ifdef PICOQUIC_USE_SCION + // TODO make destination IA dynamic bytes_sent = scion_sendmsg(fd, &msg, 0, 0x1ff0000000133, NULL); #else bytes_sent = sendmsg(fd, &msg, 0); diff --git a/picoquic/sockloop.c b/picoquic/sockloop.c index 4d9180da2..14e052e90 100644 --- a/picoquic/sockloop.c +++ b/picoquic/sockloop.c @@ -404,9 +404,9 @@ int picoquic_packet_loop_open_socket(int socket_buffer_size, int do_not_use_gso, s_ctx->fd = WSASocket(s_ctx->af, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, WSA_FLAG_OVERLAPPED); #else #ifdef PICOQUIC_USE_SCION - // TODO fix cleanup here - // TODO configurable topolog path - ret = scion_topology_from_file(&s_ctx->topology, "csnet-dist/topology/topology.json"); + // TODO cleanup the topology and network objects when a socket is closed + // TODO make topology path configurable (one for each IPv4 and IPv6) + ret = scion_topology_from_file(&s_ctx->topology, "topology.json"); if (ret != 0) { DBG_PRINTF("Cannot create topology %d\n", ret); return ret; diff --git a/csnet-dist/topology/topology.json b/topology.json similarity index 100% rename from csnet-dist/topology/topology.json rename to topology.json