diff --git a/include/libwebsockets/lws-adopt.h b/include/libwebsockets/lws-adopt.h index cbbf29f91..1ffccad0f 100644 --- a/include/libwebsockets/lws-adopt.h +++ b/include/libwebsockets/lws-adopt.h @@ -166,7 +166,9 @@ lws_adopt_socket_vhost_readbuf(struct lws_vhost *vhost, lws_sockfd_type accept_fd, const char *readbuf, size_t len); -#define LWS_CAUDP_BIND 1 +#define LWS_CAUDP_BIND (1 << 0) +#define LWS_CAUDP_BROADCAST (1 << 1) +#define LWS_CAUDP_PF_PACKET (1 << 2) #if defined(LWS_WITH_UDP) /** @@ -177,6 +179,7 @@ lws_adopt_socket_vhost_readbuf(struct lws_vhost *vhost, * \param port: UDP port to bind to, -1 means unbound * \param flags: 0 or LWS_CAUDP_NO_BIND * \param protocol_name: Name of protocol on vhost to bind wsi to + * \param ifname: NULL, for network interface name to bind socket to * \param parent_wsi: NULL or parent wsi new wsi will be a child of * \param retry_policy: NULL for vhost default policy else wsi specific policy * @@ -185,7 +188,7 @@ lws_adopt_socket_vhost_readbuf(struct lws_vhost *vhost, * */ LWS_VISIBLE LWS_EXTERN struct lws * lws_create_adopt_udp(struct lws_vhost *vhost, const char *ads, int port, - int flags, const char *protocol_name, + int flags, const char *protocol_name, const char *ifname, struct lws *parent_wsi, const lws_retry_bo_t *retry_policy); #endif ///@} diff --git a/lib/core-net/adopt.c b/lib/core-net/adopt.c index c1ed82d6f..538a58b9f 100644 --- a/lib/core-net/adopt.c +++ b/lib/core-net/adopt.c @@ -430,18 +430,19 @@ lws_create_adopt_udp2(struct lws *wsi, const char *ads, const struct addrinfo *r, int n, void *opaque) { lws_sock_file_fd_type sock; + int bc = 1; assert(wsi); if (!wsi->dns_results) wsi->dns_results_next = wsi->dns_results = r; - if (n < 0 || !r) { + if (ads && (n < 0 || !r)) { /* * DNS lookup failed: there are no usable results. Fail the * overall connection request. */ - lwsl_debug("%s: bad: n %d, r %p\n", __func__, n, r); + lwsl_notice("%s: bad: n %d, r %p\n", __func__, n, r); /* * We didn't get a callback on a cache item and bump the @@ -464,27 +465,51 @@ lws_create_adopt_udp2(struct lws *wsi, const char *ads, * function is for. */ +#if !defined(__linux__) + /* PF_PACKET is linux-only */ sock.sockfd = socket(wsi->dns_results_next->ai_family, SOCK_DGRAM, IPPROTO_UDP); - +#else + sock.sockfd = socket(wsi->pf_packet ? PF_PACKET : + wsi->dns_results_next->ai_family, + SOCK_DGRAM, wsi->pf_packet ? + htons(0x800) : IPPROTO_UDP); +#endif if (sock.sockfd == LWS_SOCK_INVALID) goto resume; + ((struct sockaddr_in *)wsi->dns_results_next->ai_addr)->sin_port = + htons(wsi->c_port); + + if (setsockopt(sock.sockfd, SOL_SOCKET, SO_REUSEADDR, (const char *)&bc, + sizeof(bc)) < 0) + lwsl_err("%s: failed to set reuse\n", __func__); + + if (wsi->do_broadcast && + setsockopt(sock.sockfd, SOL_SOCKET, SO_BROADCAST, (const char *)&bc, + sizeof(bc)) < 0) + lwsl_err("%s: failed to set broadcast\n", + __func__); + + /* Bind the udp socket to a particular network interface */ + + if (opaque && + lws_plat_BINDTODEVICE(sock.sockfd, (const char *)opaque)) + goto resume; + if (wsi->do_bind && bind(sock.sockfd, wsi->dns_results_next->ai_addr, #if defined(_WIN32) (int)wsi->dns_results_next->ai_addrlen #else - wsi->dns_results_next->ai_addrlen + sizeof(struct sockaddr)//wsi->dns_results_next->ai_addrlen #endif ) == -1) { - lwsl_notice("%s: bind failed\n", __func__); + lwsl_err("%s: bind failed\n", __func__); goto resume; } - if (!wsi->do_bind) { - ((struct sockaddr_in *)wsi->dns_results_next->ai_addr)-> - sin_port = htons(wsi->c_port); + if (!wsi->do_bind && !wsi->pf_packet) { if (connect(sock.sockfd, wsi->dns_results_next->ai_addr, wsi->dns_results_next->ai_addrlen) == -1) { @@ -514,7 +539,7 @@ resume: wsi->dns_results_next = wsi->dns_results_next->ai_next; } - lwsl_err("%s: unable to create INET socket\n", __func__); + lwsl_err("%s: unable to create INET socket %d\n", __func__, LWS_ERRNO); lws_addrinfo_clean(wsi); bail: @@ -525,7 +550,7 @@ bail: struct lws * lws_create_adopt_udp(struct lws_vhost *vhost, const char *ads, int port, - int flags, const char *protocol_name, + int flags, const char *protocol_name, const char *ifname, struct lws *parent_wsi, const lws_retry_bo_t *retry_policy) { #if !defined(LWS_PLAT_OPTEE) @@ -543,6 +568,8 @@ lws_create_adopt_udp(struct lws_vhost *vhost, const char *ads, int port, goto bail; } wsi->do_bind = !!(flags & LWS_CAUDP_BIND); + wsi->do_broadcast = !!(flags & LWS_CAUDP_BROADCAST); + wsi->pf_packet = !!(flags & LWS_CAUDP_PF_PACKET); wsi->c_port = port; if (retry_policy) wsi->retry_policy = retry_policy; @@ -599,7 +626,7 @@ lws_create_adopt_udp(struct lws_vhost *vhost, const char *ads, int port, */ n = lws_async_dns_query(vhost->context, 0, ads, LWS_ADNS_RECORD_A, - lws_create_adopt_udp2, wsi, NULL); + lws_create_adopt_udp2, wsi, (void *)ifname); lwsl_debug("%s: dns query returned %d\n", __func__, n); if (n == LADNS_RET_FAILED) { lwsl_err("%s: async dns failed\n", __func__); @@ -611,8 +638,8 @@ lws_create_adopt_udp(struct lws_vhost *vhost, const char *ads, int port, goto bail; } } else { - lwsl_debug("%s: fail on no ads\n", __func__); - wsi = lws_create_adopt_udp2(wsi, ads, NULL, 0, NULL); + lwsl_debug("%s: udp adopt has no ads\n", __func__); + wsi = lws_create_adopt_udp2(wsi, ads, NULL, 0, (void *)ifname); } /* dns lookup is happening asynchronously */ diff --git a/lib/core-net/private-lib-core-net.h b/lib/core-net/private-lib-core-net.h index 7e335adc5..79e85a250 100644 --- a/lib/core-net/private-lib-core-net.h +++ b/lib/core-net/private-lib-core-net.h @@ -695,6 +695,8 @@ struct lws { unsigned int h2_acked_settings:1; unsigned int seen_nonpseudoheader:1; unsigned int listener:1; + unsigned int pf_packet:1; + unsigned int do_broadcast:1; unsigned int user_space_externally_allocated:1; unsigned int socket_is_permanently_unusable:1; unsigned int rxflow_change_to:2; diff --git a/lib/plat/freertos/freertos-sockets.c b/lib/plat/freertos/freertos-sockets.c index d162266f9..c979a59de 100644 --- a/lib/plat/freertos/freertos-sockets.c +++ b/lib/plat/freertos/freertos-sockets.c @@ -224,6 +224,44 @@ lws_plat_inet_pton(int af, const char *src, void *dst) return 1; // inet_pton(af, src, dst); } +int +lws_plat_ifname_to_hwaddr(int fd, const char *ifname, uint8_t *hwaddr, int len) +{ + lwsl_err("%s: UNIMPLEMENTED on this platform\n", __func__); + return -1; +} +int +lws_plat_rawudp_broadcast(uint8_t *p, const uint8_t *canned, int canned_len, + int n, int fd, const char *iface) +{ + lwsl_err("%s: UNIMPLEMENTED on this platform\n", __func__); + return -1; +} + +int +lws_plat_if_up(const char *ifname, int fd, int up) +{ + lwsl_err("%s: UNIMPLEMENTED on this platform\n", __func__); + + return -1; +} + +int +lws_plat_BINDTODEVICE(int fd, const char *ifname) +{ + lwsl_err("%s: UNIMPLEMENTED on this platform\n", __func__); + + return -1; +} + +int +lws_plat_ifconfig_ip(const char *ifname, int fd, uint8_t *ip, uint8_t *mask_ip, + uint8_t *gateway_ip) +{ + lwsl_err("%s: UNIMPLEMENTED on this platform\n", __func__); + + return -1; +} diff --git a/lib/plat/unix/private-lib-plat-unix.h b/lib/plat/unix/private-lib-plat-unix.h index 855535421..c7c05a3e0 100644 --- a/lib/plat/unix/private-lib-plat-unix.h +++ b/lib/plat/unix/private-lib-plat-unix.h @@ -55,6 +55,8 @@ #endif #if defined(__linux__) #include +#include +#include #endif #if defined(__QNX__) #include @@ -147,6 +149,10 @@ wsi_from_fd(const struct lws_context *context, int fd); int insert_wsi(const struct lws_context *context, struct lws *wsi); +int +lws_plat_ifconfig_ip(const char *ifname, int fd, uint8_t *ip, uint8_t *mask_ip, + uint8_t *gateway_ip); + void delete_from_fd(const struct lws_context *context, int fd); @@ -174,3 +180,13 @@ delete_from_fd(const struct lws_context *context, int fd); #if defined(__sun) && !defined(MSG_NOSIGNAL) #define MSG_NOSIGNAL 0 #endif + +int +lws_plat_BINDTODEVICE(int fd, const char *ifname); + +int +lws_plat_rawudp_broadcast(uint8_t *p, const uint8_t *canned, int canned_len, + int n, int fd, const char *iface); + +int +lws_plat_if_up(const char *ifname, int fd, int up); diff --git a/lib/plat/unix/unix-sockets.c b/lib/plat/unix/unix-sockets.c index 9925f58bb..91dbd5d24 100644 --- a/lib/plat/unix/unix-sockets.c +++ b/lib/plat/unix/unix-sockets.c @@ -25,6 +25,10 @@ #define _GNU_SOURCE #include "private-lib-core.h" +#include +#include +#include + #include #include @@ -266,3 +270,184 @@ lws_plat_inet_pton(int af, const char *src, void *dst) { return inet_pton(af, src, dst); } + +int +lws_plat_ifname_to_hwaddr(int fd, const char *ifname, uint8_t *hwaddr, int len) +{ +#if defined(__linux__) + struct ifreq i; + + memset(&i, 0, sizeof(i)); + lws_strncpy(i.ifr_name, ifname, sizeof(i.ifr_name)); + + if (ioctl(fd, SIOCGIFHWADDR, &i) < 0) + return -1; + + memcpy(hwaddr, &i.ifr_hwaddr.sa_data, 6); + + return 6; +#else + lwsl_err("%s: UNIMPLEMENTED on this platform\n", __func__); + + return -1; +#endif +} + +int +lws_plat_rawudp_broadcast(uint8_t *p, const uint8_t *canned, int canned_len, + int n, int fd, const char *iface) +{ +#if defined(__linux__) + struct sockaddr_ll sll; + uint16_t *p16 = (uint16_t *)p; + uint32_t ucs = 0; + + memcpy(p, canned, canned_len); + + p[2] = n >> 8; + p[3] = n; + + while (p16 < (uint16_t *)(p + 20)) + ucs += ntohs(*p16++); + + ucs += ucs >> 16; + ucs ^= 0xffff; + + p[10] = ucs >> 8; + p[11] = ucs; + p[24] = (n - 20) >> 8; + p[25] = (n - 20); + + memset(&sll, 0, sizeof(sll)); + sll.sll_family = AF_PACKET; + sll.sll_protocol = htons(0x800); + sll.sll_halen = 6; + sll.sll_ifindex = if_nametoindex(iface); + memset(sll.sll_addr, 0xff, 6); + + return sendto(fd, p, n, 0, (struct sockaddr *)&sll, sizeof(sll)); +#else + lwsl_err("%s: UNIMPLEMENTED on this platform\n", __func__); + + return -1; +#endif +} + +int +lws_plat_if_up(const char *ifname, int fd, int up) +{ +#if defined(__linux__) + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + lws_strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + + if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) { + lwsl_err("%s: SIOCGIFFLAGS fail\n", __func__); + return 1; + } + + if (up) + ifr.ifr_flags |= IFF_UP; + else + ifr.ifr_flags &= ~IFF_UP; + + if (ioctl(fd, SIOCSIFFLAGS, &ifr) < 0) { + lwsl_err("%s: SIOCSIFFLAGS fail\n", __func__); + return 1; + } + + return 0; +#else + lwsl_err("%s: UNIMPLEMENTED on this platform\n", __func__); + + return -1; +#endif +} + +int +lws_plat_BINDTODEVICE(int fd, const char *ifname) +{ +#if defined(__linux__) + struct ifreq i; + + memset(&i, 0, sizeof(i)); + i.ifr_addr.sa_family = AF_INET; + lws_strncpy(i.ifr_ifrn.ifrn_name, ifname, + sizeof(i.ifr_ifrn.ifrn_name)); + if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &i, sizeof(i)) < 0) { + lwsl_notice("%s: failed %d\n", __func__, LWS_ERRNO); + return 1; + } + + return 0; +#else + lwsl_err("%s: UNIMPLEMENTED on this platform\n", __func__); + + return -1; +#endif +} + +int +lws_plat_ifconfig_ip(const char *ifname, int fd, uint8_t *ip, uint8_t *mask_ip, + uint8_t *gateway_ip) +{ +#if defined(__linux__) + struct sockaddr_in *addr; + struct sockaddr_in sin; + struct rtentry route; + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + memset(&route, 0, sizeof(route)); + memset(&sin, 0, sizeof(sin)); + + lws_strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + + lws_plat_if_up(ifname, fd, 0); + + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(*(uint32_t *)ip); + + memcpy(&ifr.ifr_addr, &sin, sizeof(struct sockaddr)); + if (ioctl(fd, SIOCSIFADDR, &ifr) < 0) { + lwsl_err("%s: SIOCSIFADDR fail\n", __func__); + return 1; + } + + sin.sin_addr.s_addr = htonl(*(uint32_t *)mask_ip); + memcpy(&ifr.ifr_addr, &sin, sizeof(struct sockaddr)); + if (ioctl(fd, SIOCSIFNETMASK, &ifr) < 0) { + lwsl_err("%s: SIOCSIFNETMASK fail\n", __func__); + return 1; + } + + lws_plat_if_up(ifname, fd, 1); + + addr = (struct sockaddr_in *)&route.rt_gateway; + addr->sin_family = AF_INET; + addr->sin_addr.s_addr = htonl(*(uint32_t *)gateway_ip); + + addr = (struct sockaddr_in *)&route.rt_dst; + addr->sin_family = AF_INET; + addr->sin_addr.s_addr = 0; + + addr = (struct sockaddr_in *)&route.rt_genmask; + addr->sin_family = AF_INET; + addr->sin_addr.s_addr = 0; + + route.rt_flags = RTF_UP | RTF_GATEWAY; + route.rt_metric = 100; + + if (ioctl(fd, SIOCADDRT, &route) < 0) { + lwsl_err("%s: SIOCADDRT fail: %d\n", __func__, LWS_ERRNO); + return 1; + } + + return 0; +#else + lwsl_err("%s: UNIMPLEMENTED on this platform\n", __func__); + + return -1; +#endif +} diff --git a/lib/plat/windows/windows-sockets.c b/lib/plat/windows/windows-sockets.c index 5b26bf91d..50dbb26cb 100644 --- a/lib/plat/windows/windows-sockets.c +++ b/lib/plat/windows/windows-sockets.c @@ -315,3 +315,46 @@ lws_plat_inet_pton(int af, const char *src, void *dst) lws_free(buffer); return ok ? 1 : -1; } + +int +lws_plat_ifname_to_hwaddr(int fd, const char *ifname, uint8_t *hwaddr, int len) +{ + lwsl_err("%s: UNIMPLEMENTED on this platform\n", __func__); + + return -1; +} + +int +lws_plat_rawudp_broadcast(uint8_t *p, const uint8_t *canned, int canned_len, + int n, int fd, const char *iface) +{ + lwsl_err("%s: UNIMPLEMENTED on this platform\n", __func__); + + return -1; +} + +int +lws_plat_if_up(const char *ifname, int fd, int up) +{ + lwsl_err("%s: UNIMPLEMENTED on this platform\n", __func__); + + return -1; +} + +int +lws_plat_BINDTODEVICE(int fd, const char *ifname) +{ + lwsl_err("%s: UNIMPLEMENTED on this platform\n", __func__); + + return -1; +} + +int +lws_plat_ifconfig_ip(const char *ifname, int fd, uint8_t *ip, uint8_t *mask_ip, + uint8_t *gateway_ip) +{ + lwsl_err("%s: UNIMPLEMENTED on this platform\n", __func__); + + return -1; +} + diff --git a/lib/system/async-dns/async-dns.c b/lib/system/async-dns/async-dns.c index a72f3c98e..0fe559ec4 100644 --- a/lib/system/async-dns/async-dns.c +++ b/lib/system/async-dns/async-dns.c @@ -312,7 +312,7 @@ lws_async_dns_init(struct lws_context *context) context->async_dns.wsi = lws_create_adopt_udp(context->vhost_list, ads, 53, 0, lws_async_dns_protocol.name, NULL, - &retry_policy); + NULL, &retry_policy); if (!dns->wsi) { lwsl_err("%s: foreign socket adoption failed\n", __func__); return 1; diff --git a/minimal-examples/raw/minimal-raw-adopt-udp/minimal-raw-adopt-udp.c b/minimal-examples/raw/minimal-raw-adopt-udp/minimal-raw-adopt-udp.c index 9c2c21231..36419e89a 100644 --- a/minimal-examples/raw/minimal-raw-adopt-udp/minimal-raw-adopt-udp.c +++ b/minimal-examples/raw/minimal-raw-adopt-udp/minimal-raw-adopt-udp.c @@ -170,7 +170,7 @@ int main(int argc, const char **argv) * Create our own "foreign" UDP socket bound to 7681/udp */ if (!lws_create_adopt_udp(vhost, NULL, 7681, LWS_CAUDP_BIND, - protocols[0].name, NULL, NULL)) { + protocols[0].name, NULL, NULL, NULL)) { lwsl_err("%s: foreign socket adoption failed\n", __func__); goto bail; }