1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-09 00:00:04 +01:00

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
This commit is contained in:
Leonardo Maccari Rufino 2017-06-02 14:07:35 -03:00 committed by Andy Green
parent 156363f3de
commit 393b38aed9
6 changed files with 71 additions and 1 deletions

View file

@ -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

View file

@ -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++)

View file

@ -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

View file

@ -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)

View file

@ -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;

View file

@ -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 "