diff --git a/include/libwebsockets/lws-client.h b/include/libwebsockets/lws-client.h index 657108659..48cf21db2 100644 --- a/include/libwebsockets/lws-client.h +++ b/include/libwebsockets/lws-client.h @@ -78,6 +78,18 @@ enum lws_client_connect_ssl_connection_flags { /**< client is a link between SS client and SS proxy */ LCCSCF_SECSTREAM_PROXY_ONWARD = (1 << 23), /**< client the SS proxy's onward connection */ + + LCCSCF_IP_LOW_LATENCY = (1 << 24), + /**< set the "low delay" bit on the IP packets of this connection */ + LCCSCF_IP_HIGH_THROUGHPUT = (1 << 25), + /**< set the "high throughput" bit on the IP packets of this + * connection */ + LCCSCF_IP_HIGH_RELIABILITY = (1 << 26), + /**< set the "high reliability" bit on the IP packets of this + * connection */ + LCCSCF_IP_LOW_COST = (1 << 27), + /**< set the "minimize monetary cost" bit on the IP packets of this + * connection */ }; /** struct lws_client_connect_info - parameters to connect with when using @@ -177,6 +189,13 @@ struct lws_client_connect_info { * to the client connection. */ + uint8_t priority; + /**< 0 means normal priority... otherwise sets the IP priority on + * packets coming from this connection, from 1 - 7. Setting 7 + * (network management priority) requires CAP_NET_ADMIN capability but + * the others can be set by anyone. + */ + #if defined(LWS_ROLE_MQTT) const lws_mqtt_client_connect_param_t *mqtt_cp; #else diff --git a/include/libwebsockets/lws-secure-streams-policy.h b/include/libwebsockets/lws-secure-streams-policy.h index 8255927c1..d22b23e00 100644 --- a/include/libwebsockets/lws-secure-streams-policy.h +++ b/include/libwebsockets/lws-secure-streams-policy.h @@ -128,6 +128,15 @@ enum { /**< follow redirects */ LWSSSPOLF_HTTP_MULTIPART_IN = (1 << 17), /**< handle inbound multipart mime at SS level */ + + LWSSSPOLF_ATTR_LOW_LATENCY = (1 << 18), + /**< stream requires low latency */ + LWSSSPOLF_ATTR_HIGH_THROUGHPUT = (1 << 19), + /**< stream requires high throughput */ + LWSSSPOLF_ATTR_HIGH_RELIABILITY = (1 << 20), + /**< stream requires high reliability */ + LWSSSPOLF_ATTR_LOW_COST = (1 << 21), + /**< stream is not critical and should be handled as cheap as poss */ }; typedef struct lws_ss_trust_store { @@ -334,6 +343,8 @@ typedef struct lws_ss_policy { uint8_t protocol; /**< protocol index */ uint8_t client_cert; /**< which client cert to apply 0 = none, 1+ = cc 0+ */ + uint8_t priority; /* 0 = normal, 6 = max normal, + * 7 = network management */ } lws_ss_policy_t; #if !defined(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY) diff --git a/lib/core-net/adopt.c b/lib/core-net/adopt.c index bbce41fc0..bdb8b94cb 100644 --- a/lib/core-net/adopt.c +++ b/lib/core-net/adopt.c @@ -281,6 +281,20 @@ lws_adopt_ss_server_accept(struct lws *new_wsi) h->policy = new_wsi->a.vhost->ss_handle->policy; + /* apply requested socket options */ + if (lws_plat_set_socket_options_ip(new_wsi->desc.sockfd, + h->policy->priority, + (LCCSCF_IP_LOW_LATENCY * + !!(h->policy->flags & LWSSSPOLF_ATTR_LOW_LATENCY)) | + (LCCSCF_IP_HIGH_THROUGHPUT * + !!(h->policy->flags & LWSSSPOLF_ATTR_HIGH_THROUGHPUT)) | + (LCCSCF_IP_HIGH_RELIABILITY * + !!(h->policy->flags & LWSSSPOLF_ATTR_HIGH_RELIABILITY)) | + (LCCSCF_IP_LOW_COST * + !!(h->policy->flags & LWSSSPOLF_ATTR_LOW_COST)))) + lwsl_warn("%s: %s: unable to set ip options\n", + __func__, new_wsi->lc.gutag); + /* * add us to the list of clients that came in from the server */ diff --git a/lib/core-net/client/connect.c b/lib/core-net/client/connect.c index dd374d3b7..26763d179 100644 --- a/lib/core-net/client/connect.c +++ b/lib/core-net/client/connect.c @@ -154,6 +154,9 @@ lws_client_connect_via_info(const struct lws_client_connect_info *i) wsi->seq = i->seq; wsi->flags = i->ssl_connection; + + wsi->c_pri = i->priority; + if (i->retry_and_idle_policy) wsi->retry_policy = i->retry_and_idle_policy; else diff --git a/lib/core-net/client/connect3.c b/lib/core-net/client/connect3.c index d544bc857..dcd9f72d9 100644 --- a/lib/core-net/client/connect3.c +++ b/lib/core-net/client/connect3.c @@ -324,6 +324,12 @@ ads_known: goto try_next_dns_result_closesock; } + /* apply requested socket options */ + if (lws_plat_set_socket_options_ip(wsi->desc.sockfd, + wsi->c_pri, wsi->flags)) + lwsl_warn("%s: %s: unable to set ip options\n", + __func__, wsi->lc.gutag); + lwsl_debug("%s: %s: WAITING_CONNECT\n", __func__, wsi->lc.gutag); lwsi_set_state(wsi, LRS_WAITING_CONNECT); diff --git a/lib/core-net/private-lib-core-net.h b/lib/core-net/private-lib-core-net.h index eac480352..57028c33f 100644 --- a/lib/core-net/private-lib-core-net.h +++ b/lib/core-net/private-lib-core-net.h @@ -898,6 +898,7 @@ struct lws { char chunk_parser; /* enum lws_chunk_parser */ uint8_t addrinfo_idx; uint8_t sys_tls_client_cert; + uint8_t c_pri; #endif #if defined(LWS_WITH_CGI) || defined(LWS_WITH_CLIENT) char reason_bf; /* internal writeable callback reason bitfield */ @@ -1067,6 +1068,9 @@ int lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd, int unix_skt); +int +lws_plat_set_socket_options_ip(lws_sockfd_type fd, uint8_t pri, int lws_flags); + int lws_plat_check_connection_error(struct lws *wsi); diff --git a/lib/plat/freertos/freertos-sockets.c b/lib/plat/freertos/freertos-sockets.c index f5d975b07..82ac5518a 100644 --- a/lib/plat/freertos/freertos-sockets.c +++ b/lib/plat/freertos/freertos-sockets.c @@ -139,6 +139,63 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, int fd, int unix_skt) return lws_plat_set_nonblocking(fd); } +static const int ip_opt_lws_flags[] = { + LCCSCF_IP_LOW_LATENCY, LCCSCF_IP_HIGH_THROUGHPUT, + LCCSCF_IP_HIGH_RELIABILITY, LCCSCF_IP_LOW_COST +}, ip_opt_val[] = { + IPTOS_LOWDELAY, IPTOS_THROUGHPUT, IPTOS_RELIABILITY, IPTOS_MINCOST +}; +#if !defined(LWS_WITH_NO_LOGS) +static const char *ip_opt_names[] = { + "LOWDELAY", "THROUGHPUT", "RELIABILITY", "MINCOST" +}; +#endif + +int +lws_plat_set_socket_options_ip(lws_sockfd_type fd, uint8_t pri, int lws_flags) +{ + int optval = (int)pri, ret = 0, n; + socklen_t optlen = sizeof(optval); +#if !defined(LWS_WITH_NO_LOGS) + int en; +#endif + +#if defined(SO_PRIORITY) + if (pri) { /* 0 is the default already */ + if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, + (const void *)&optval, optlen) < 0) { +#if !defined(LWS_WITH_NO_LOGS) + en = errno; + lwsl_warn("%s: unable to set socket pri %d: errno %d\n", + __func__, (int)pri, en); +#endif + ret = 1; + } else + lwsl_notice("%s: set pri %u\n", __func__, pri); + } +#endif + + for (n = 0; n < 4; n++) { + if (!(lws_flags & ip_opt_lws_flags[n])) + continue; + + optval = (int)ip_opt_val[n]; + if (setsockopt(fd, IPPROTO_IP, IP_TOS, (const void *)&optval, + optlen) < 0) { +#if !defined(LWS_WITH_NO_LOGS) + en = errno; + lwsl_warn("%s: unable to set %s: errno %d\n", __func__, + ip_opt_names[n], en); +#endif + ret = 1; + } else + lwsl_notice("%s: set ip flag %s\n", __func__, + ip_opt_names[n]); + } + + return ret; +} + /* cast a struct sockaddr_in6 * into addr for ipv6 */ int diff --git a/lib/plat/optee/network.c b/lib/plat/optee/network.c index b0867b77e..918b8827b 100644 --- a/lib/plat/optee/network.c +++ b/lib/plat/optee/network.c @@ -256,6 +256,12 @@ lws_plat_inet_pton(int af, const char *src, void *dst) return 1; } +int +lws_plat_set_socket_options_ip(int fd, uint8_t pri, unsigned int lws_flags) +{ + return 0; +} + #if defined(LWS_WITH_MBEDTLS) int lws_plat_mbedtls_net_send(void *ctx, const uint8_t *buf, size_t len) diff --git a/lib/plat/unix/unix-sockets.c b/lib/plat/unix/unix-sockets.c index fc0963ddf..f6f9ce9b5 100644 --- a/lib/plat/unix/unix-sockets.c +++ b/lib/plat/unix/unix-sockets.c @@ -46,6 +46,8 @@ #endif #endif +#include + int lws_send_pipe_choked(struct lws *wsi) { @@ -188,6 +190,69 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, int fd, int unix_skt) return lws_plat_set_nonblocking(fd); } +static const int ip_opt_lws_flags[] = { + LCCSCF_IP_LOW_LATENCY, LCCSCF_IP_HIGH_THROUGHPUT, + LCCSCF_IP_HIGH_RELIABILITY, LCCSCF_IP_LOW_COST +}, ip_opt_val[] = { + IPTOS_LOWDELAY, IPTOS_THROUGHPUT, IPTOS_RELIABILITY, IPTOS_MINCOST +}; +#if !defined(LWS_WITH_NO_LOGS) +static const char *ip_opt_names[] = { + "LOWDELAY", "THROUGHPUT", "RELIABILITY", "MINCOST" +}; +#endif + +int +lws_plat_set_socket_options_ip(lws_sockfd_type fd, uint8_t pri, int lws_flags) +{ + int optval = (int)pri, ret = 0, n; + socklen_t optlen = sizeof(optval); +#if !defined(LWS_WITH_NO_LOGS) + int en; +#endif + +#if !defined(__APPLE__) && \ + !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__) && \ + !defined(__NetBSD__) && \ + !defined(__OpenBSD__) && \ + !defined(__HAIKU__) + + /* the BSDs don't have SO_PRIORITY */ + + if (pri) { /* 0 is the default already */ + if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, + (const void *)&optval, optlen) < 0) { +#if !defined(LWS_WITH_NO_LOGS) + en = errno; + lwsl_warn("%s: unable to set socket pri %d: errno %d\n", + __func__, (int)pri, en); +#endif + ret = 1; + } else + lwsl_notice("%s: set pri %u\n", __func__, pri); + } +#endif + + for (n = 0; n < 4; n++) { + if (!(lws_flags & ip_opt_lws_flags[n])) + continue; + + optval = (int)ip_opt_val[n]; + if (setsockopt(fd, IPPROTO_IP, IP_TOS, (const void *)&optval, + optlen) < 0) { +#if !defined(LWS_WITH_NO_LOGS) + en = errno; + lwsl_warn("%s: unable to set %s: errno %d\n", __func__, + ip_opt_names[n], en); +#endif + ret = 1; + } else + lwsl_notice("%s: set ip flag %s\n", __func__, + ip_opt_names[n]); + } + + return ret; +} /* cast a struct sockaddr_in6 * into addr for ipv6 */ diff --git a/lib/plat/windows/windows-sockets.c b/lib/plat/windows/windows-sockets.c index 9acbf2c90..d526175d1 100644 --- a/lib/plat/windows/windows-sockets.c +++ b/lib/plat/windows/windows-sockets.c @@ -144,6 +144,19 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd, return lws_plat_set_nonblocking(fd); } +int +lws_plat_set_socket_options_ip(lws_sockfd_type fd, uint8_t pri, int lws_flags) +{ + /* + * Seems to require "differeniated services" but no docs + * + * https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options + * https://docs.microsoft.com/en-us/previous-versions/windows/desktop/qos/differentiated-services + */ + lwsl_warn("%s: not implemented on windows platform\n", __func__); + + return 0; +} int lws_interface_to_sa(int ipv6, diff --git a/lib/secure-streams/README.md b/lib/secure-streams/README.md index f0182601f..bc5c33c0c 100644 --- a/lib/secure-streams/README.md +++ b/lib/secure-streams/README.md @@ -394,6 +394,39 @@ payload buffering (in bytes) the client will hold for this type of stream. If the client sends a lot of data without any flow control, this may need to be correspondingly large. Default is 32KB. +### `attr_priority` + +A number between 0 (normal priority) and 6 (very high priority). 7 is also +possible, but requires CAP_NET_ADMIN on Linux and is reserved for network +administration packets. Normally default priority is fine, but under some +conditions when transporting over IP packets, you may want to control the +IP packet ToS priority for the streamtype by using this. + +### `attr_low_latency` + +This is a flag indicating that the streamtype packets should be transported +in a way that results in lower latency where there is a choice. For IP packets, +this sets the ToS "low delay" flag on packets from this streamtype. + +### `attr_high_throughput` + +This is a flag indicating that this streamtype should be expected to produce +bulk content that requires high throughput. For IP packets, +this sets the ToS "high throughput" flag on packets from this streamtype. + +### `attr_high_reliability` + +This is a flag indicating that extra efforts should be made to deliver packets +from this streamtype where possible. For IP packets, this sets the ToS "high +reliability" flag on packets from this streamtype. + +### `attr_low_cost` + +This is a flag indicating that packets from this streamtype should be routed as +inexpensively as possible by trading off latency and reliability where there is +a choice. For IP packets, this sets the ToS "low cost" flag on packets from +this streamtype. + ### `metadata` This allows declaring basically dynamic symbol names to be used by the streamtype, diff --git a/lib/secure-streams/policy-json.c b/lib/secure-streams/policy-json.c index 45f7c5281..d4e3bb31e 100644 --- a/lib/secure-streams/policy-json.c +++ b/lib/secure-streams/policy-json.c @@ -53,6 +53,11 @@ static const char * const lejp_tokens_policy[] = { "s[].*.allow_redirects", "s[].*.urgent_tx", "s[].*.urgent_rx", + "s[].*.attr_priority", + "s[].*.attr_low_latency", + "s[].*.attr_high_throughput", + "s[].*.attr_high_reliability", + "s[].*.attr_low_cost", "s[].*.long_poll", "s[].*.retry", "s[].*.timeout_ms", @@ -142,6 +147,11 @@ typedef enum { LSSPPT_ALLOW_REDIRECTS, LSSPPT_URGENT_TX, LSSPPT_URGENT_RX, + LSSPPT_ATTR_PRIORITY, + LSSPPT_ATTR_LOW_LATENCY, + LSSPPT_ATTR_HIGH_THROUGHPUT, + LSSPPT_ATTR_HIGH_RELIABILITY, + LSSPPT_ATTR_LOW_COST, LSSPPT_LONG_POLL, LSSPPT_RETRYPTR, LSSPPT_DEFAULT_TIMEOUT_MS, @@ -667,6 +677,10 @@ lws_ss_policy_parser_cb(struct lejp_ctx *ctx, char reason) a->curr[LTY_POLICY].p->timeout_ms = (uint32_t)atoi(ctx->buf); break; + case LSSPPT_ATTR_PRIORITY: + a->curr[LTY_POLICY].p->priority = (uint8_t)atoi(ctx->buf); + break; + case LSSPPT_OPPORTUNISTIC: if (reason == LEJPCB_VAL_TRUE) a->curr[LTY_POLICY].p->flags |= LWSSSPOLF_OPPORTUNISTIC; @@ -708,6 +722,29 @@ lws_ss_policy_parser_cb(struct lejp_ctx *ctx, char reason) LWSSSPOLF_HTTP_MULTIPART_IN; return 0; + case LSSPPT_ATTR_LOW_LATENCY: + if (reason == LEJPCB_VAL_TRUE) + a->curr[LTY_POLICY].p->flags |= + LWSSSPOLF_ATTR_LOW_LATENCY; + return 0; + + case LSSPPT_ATTR_HIGH_THROUGHPUT: + if (reason == LEJPCB_VAL_TRUE) + a->curr[LTY_POLICY].p->flags |= + LWSSSPOLF_ATTR_HIGH_THROUGHPUT; + return 0; + + case LSSPPT_ATTR_HIGH_RELIABILITY: + if (reason == LEJPCB_VAL_TRUE) + a->curr[LTY_POLICY].p->flags |= + LWSSSPOLF_ATTR_HIGH_RELIABILITY; + return 0; + + case LSSPPT_ATTR_LOW_COST: + if (reason == LEJPCB_VAL_TRUE) + a->curr[LTY_POLICY].p->flags |= LWSSSPOLF_ATTR_LOW_COST; + return 0; + case LSSPPT_RETRYPTR: bot = a->heads[LTY_BACKOFF].b; while (bot) { diff --git a/lib/secure-streams/secure-streams.c b/lib/secure-streams/secure-streams.c index ff3412679..35ce7d83b 100644 --- a/lib/secure-streams/secure-streams.c +++ b/lib/secure-streams/secure-streams.c @@ -592,6 +592,21 @@ _lws_ss_client_connect(lws_ss_handle_t *h, int is_retry, void *conn_if_sspc_onw) if (h->policy->flags & LWSSSPOLF_WAKE_SUSPEND__VALIDITY) i.ssl_connection |= LCCSCF_WAKE_SUSPEND__VALIDITY; + /* translate policy attributes to IP ToS flags */ + + if (h->policy->flags & LCCSCF_IP_LOW_LATENCY) + i.ssl_connection |= LWSSSPOLF_ATTR_LOW_LATENCY; + if (h->policy->flags & LCCSCF_IP_HIGH_THROUGHPUT) + i.ssl_connection |= LWSSSPOLF_ATTR_HIGH_THROUGHPUT; + if (h->policy->flags & LCCSCF_IP_HIGH_RELIABILITY) + i.ssl_connection |= LWSSSPOLF_ATTR_HIGH_RELIABILITY; + if (h->policy->flags & LCCSCF_IP_LOW_COST) + i.ssl_connection |= LWSSSPOLF_ATTR_LOW_COST; + + /* mark the connection with the streamtype priority from the policy */ + + i.priority = h->policy->priority; + i.ssl_connection |= LCCSCF_SECSTREAM_CLIENT; if (conn_if_sspc_onw) { diff --git a/minimal-examples/secure-streams/minimal-secure-streams-policy2c/minimal-secure-streams.c b/minimal-examples/secure-streams/minimal-secure-streams-policy2c/minimal-secure-streams.c index c4f6121ca..e06286e81 100644 --- a/minimal-examples/secure-streams/minimal-secure-streams-policy2c/minimal-secure-streams.c +++ b/minimal-examples/secure-streams/minimal-secure-streams-policy2c/minimal-secure-streams.c @@ -620,6 +620,8 @@ int main(int argc, const char **argv) printf("\t.timeout_ms = %u,\n", pol->timeout_ms); if (pol->flags) printf("\t.flags = 0x%x,\n", pol->flags); + if (pol->flags) + printf("\t.priority = 0x%x,\n", (unsigned int)pol->priority); if (pol->port) printf("\t.port = %u,\n", pol->port); if (pol->metadata_count)