From 393b38aed92d308dece0fddf1e9cae335f0342e0 Mon Sep 17 00:00:00 2001 From: Leonardo Maccari Rufino Date: Fri, 2 Jun 2017 14:07:35 -0300 Subject: [PATCH] Subject: Support to bind accepted socket to device on Linux AG: move new member to end of info, allow info member even on nonsupporting platform, document requires root, apply only to listen skt before we drop root, add -k to test server to allow testing --- README.coding.md | 34 ++++++++++++++++++++++++++++++++++ lib/context.c | 4 ++++ lib/libwebsockets.h | 11 +++++++++++ lib/lws-plat-unix.c | 11 +++++++++++ lib/private-libwebsockets.h | 3 +++ test-server/test-server.c | 9 ++++++++- 6 files changed, 71 insertions(+), 1 deletion(-) diff --git a/README.coding.md b/README.coding.md index 33a13f6..9cd9fdd 100644 --- a/README.coding.md +++ b/README.coding.md @@ -862,6 +862,40 @@ This allocation is only deleted / replaced when the connection accesses a URL region with a different protocol (or the default protocols[0] if no CALLBACK area matches it). +@section BINDTODEV SO_BIND_TO_DEVICE + +The .bind_iface flag in the context / vhost creation struct lets you +declare that you want all traffic for listen and transport on that +vhost to be strictly bound to the network interface named in .iface. + +This Linux-only feature requires SO_BIND_TO_DEVICE, which in turn +requires CAP_NET_RAW capability... root has this capability. + +However this feature needs to apply the binding also to accepted +sockets during normal operation, which implies the server must run +the whole time as root. + +You can avoid this by using the Linux capabilities feature to have +the unprivileged user inherit just the CAP_NET_RAW capability. + +You can confirm this with the test server + + +``` + $ sudo /usr/local/bin/libwebsockets-test-server -u agreen -i eno1 -k +``` + +The part that ensures the capability is inherited by the unprivileged +user is + +``` +#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP) + info.caps[0] = CAP_NET_RAW; + info.count_caps = 1; +#endif +``` + + @section dim Dimming webpage when connection lost The lws test plugins' html provides useful feedback on the webpage about if it diff --git a/lib/context.c b/lib/context.c index 248e29b..9bf5465 100644 --- a/lib/context.c +++ b/lib/context.c @@ -355,6 +355,10 @@ lws_create_vhost(struct lws_context *context, vh->name = info->vhost_name; vh->iface = info->iface; +#if !defined(LWS_WITH_ESP8266) && !defined(LWS_WITH_ESP32) && !defined(OPTEE_TA) && !defined(WIN32) + vh->bind_iface = info->bind_iface; +#endif + for (vh->count_protocols = 0; info->protocols[vh->count_protocols].callback; vh->count_protocols++) diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h index b738554..427fdae 100644 --- a/lib/libwebsockets.h +++ b/lib/libwebsockets.h @@ -1991,6 +1991,17 @@ struct lws_context_creation_info { /**< CONTEXT: count of Linux capabilities in .caps[]. 0 means * no capabilities will be inherited from root (the default) */ #endif + int bind_iface; + /**< VHOST: nonzero to strictly bind sockets to the interface name in + * .iface (eg, "eth2"), using SO_BIND_TO_DEVICE. + * + * Requires SO_BINDTODEVICE support from your OS and CAP_NET_RAW + * capability. + * + * Notice that common things like access network interface IP from + * your local machine use your lo / loopback interface and will be + * disallowed by this. + */ /* Add new things just above here ---^ * This is part of the ABI, don't needlessly break compatibility diff --git a/lib/lws-plat-unix.c b/lib/lws-plat-unix.c index ae73dfc..a2284f4 100644 --- a/lib/lws-plat-unix.c +++ b/lib/lws-plat-unix.c @@ -255,6 +255,17 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, int fd) #endif } +#if defined(SO_BINDTODEVICE) + if (vhost->bind_iface) { + lwsl_info("binding listen skt to %s using SO_BINDTODEVICE\n", vhost->iface); + if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, vhost->iface, + strlen(vhost->iface)) < 0) { + lwsl_warn("Failed to bind to device %s\n", vhost->iface); + return 1; + } + } +#endif + /* Disable Nagle */ optval = 1; #if defined (__sun) diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h index 48940bd..993f6e1 100644 --- a/lib/private-libwebsockets.h +++ b/lib/private-libwebsockets.h @@ -854,6 +854,9 @@ struct lws_vhost { struct lws *lserv_wsi; const char *name; const char *iface; +#if !defined(LWS_WITH_ESP8266) && !defined(LWS_WITH_ESP32) && !defined(OPTEE_TA) && !defined(WIN32) + int bind_iface; +#endif const struct lws_protocols *protocols; void **protocol_vh_privs; const struct lws_protocol_vhost_options *pvo; diff --git a/test-server/test-server.c b/test-server/test-server.c index 74c37da..d6ddb9d 100644 --- a/test-server/test-server.c +++ b/test-server/test-server.c @@ -222,7 +222,7 @@ int main(int argc, char **argv) info.port = 7681; while (n >= 0) { - n = getopt_long(argc, argv, "eci:hsap:d:Dr:C:K:A:R:vu:g:P:", options, NULL); + n = getopt_long(argc, argv, "eci:hsap:d:Dr:C:K:A:R:vu:g:P:k", options, NULL); if (n < 0) continue; switch (n) { @@ -260,6 +260,13 @@ int main(int argc, char **argv) interface_name[(sizeof interface_name) - 1] = '\0'; iface = interface_name; break; + case 'k': + info.bind_iface = 1; +#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP) + info.caps[0] = CAP_NET_RAW; + info.count_caps = 1; +#endif + break; case 'c': close_testing = 1; fprintf(stderr, " Close testing mode -- closes on " -- 2.7.4