diff --git a/docs/src/misc.rst b/docs/src/misc.rst index 3ecfce486c4..8b81ef8dc6b 100644 --- a/docs/src/misc.rst +++ b/docs/src/misc.rst @@ -153,6 +153,70 @@ Data types } netmask; } uv_interface_address_t; +.. c:type:: uv_network_interface_t + + Data type for network interfaces. + + :: + + typedef struct uv_network_interface_s { + char* name; + uint32_t flags; + char phys_addr[16]; + uint32_t phys_addr_len; + union { + struct sockaddr_in address4; + struct sockaddr_in6 address6; + } address; + struct sockaddr_in broadcast4; + uint32_t prefix; + } uv_network_interface_t; + + +.. c:macro:: UV_NETIF_IS_UP + + Macro that takes an `uv_network_interface_t` and returns + 1 or 0 indicating whether the interface is up and running. + + +.. c:macro:: UV_NETIF_IS_LOOPBACK + + Macro that takes an `uv_network_interface_t` and returns + 1 or 0 indicating whether the interface is a loopback + interface. + + +.. c:macro:: UV_NETIF_IS_PTP + + Macro that takes an `uv_network_interface_t` and returns + 1 or 0 indicating whether the interface is a point-to-point + interface. + + +.. c:macro:: UV_NETIF_IS_PROMISCUOUS + + Macro that takes an `uv_network_interface_t` and returns + 1 or 0 indicating whether the interface is in promiscuous + mode. + + +.. c:macro:: UV_NETIF_HAS_BROADCAST + + Macro that takes an `uv_network_interface_t` and returns + 1 or 0 indicating whether the interface has the broadcast + flag set. Currently, only IPv4 interface records have an + IPv4 broadcast address, but the flag can be set on all + interface records that share the same name. + + +.. c:macro:: UV_NETIF_HAS_MULTICAST + + Macro that takes an `uv_network_interface_t` and returns + 1 or 0 indicating whether the interface has the multicast + flag set. The flag can be set on all interface records + that share the same name. + + .. c:type:: uv_passwd_t Data type for password file information. @@ -295,14 +359,33 @@ API .. c:function:: int uv_interface_addresses(uv_interface_address_t** addresses, int* count) Gets address information about the network interfaces on the system. An - array of `count` elements is allocated and returned in `addresses`. It must - be freed by the user, calling :c:func:`uv_free_interface_addresses`. + array of `count` elements is allocated and returned in `addresses`. Only + includes interfaces with `INET` or `INET6` addresses. It must be freed by + the user, calling :c:func:`uv_free_interface_addresses`. .. c:function:: void uv_free_interface_addresses(uv_interface_address_t* addresses, int count) Free an array of :c:type:`uv_interface_address_t` which was returned by :c:func:`uv_interface_addresses`. +.. c:function:: int uv_network_interfaces(uv_network_interface_t** interfaces, int* count) + + Gets information about internet network interfaces on the system. An array + of `count` elements is allocated and returned in `interfaces`. Includes up, + down, unattached interfaces and interfaces without addresses. Missing + addresses are marked with a family of `AF_UNSPEC` and their contents are + unspecified. At least one link-layer record is returned for each interface. + It must be freed by the user, calling :c:func:`uv_free_network_interfaces`. + + .. note:: + This interface will return `UV_ENOTSUP` until individual platform support + has been implemented for each supported platform. + +.. c:function:: void uv_free_network_interfaces(uv_network_interface_t* interfaces, int count) + + Free an array of :c:type:`uv_network_interface_t` which was returned by + :c:func:`uv_network_interfaces`. + .. c:function:: void uv_loadavg(double avg[3]) Gets the load average. See: ``_ diff --git a/include/uv.h b/include/uv.h index ee94397aab6..c851339cdda 100644 --- a/include/uv.h +++ b/include/uv.h @@ -235,6 +235,7 @@ typedef struct uv_work_s uv_work_t; typedef struct uv_env_item_s uv_env_item_t; typedef struct uv_cpu_info_s uv_cpu_info_t; typedef struct uv_interface_address_s uv_interface_address_t; +typedef struct uv_network_interface_s uv_network_interface_t; typedef struct uv_dirent_s uv_dirent_t; typedef struct uv_passwd_s uv_passwd_t; typedef struct uv_utsname_s uv_utsname_t; @@ -1061,6 +1062,45 @@ struct uv_interface_address_s { } netmask; }; +/* + * These are the flags that are present in the uv_network_interface_t.flags + * field. + */ +enum uv_netif_flags { + /* Indicates the interface is up and running. */ + UV_NETIF_UP = (1 << 0), + /* Indicates the interface is a loopback interface. */ + UV_NETIF_LOOPBACK = (1 << 1), + /* Indicates the interface is a point-to-point interface. */ + UV_NETIF_PTP = (1 << 2), + /* Indicates the interface is running in promiscuous mode. */ + UV_NETIF_PROMISCUOUS = (1 << 3), + /* Indicates the interface broadcast address field is populated. */ + UV_NETIF_BROADCAST = (1 << 4), + /* Indicates the interface is enabled for multicast packets. */ + UV_NETIF_MULTICAST = (1 << 5) +}; + +#define UV_NETIF_IS_UP(netif) (!!(netif.flags & UV_NETIF_UP)) +#define UV_NETIF_IS_LOOPBACK(netif) (!!(netif.flags & UV_NETIF_LOOPBACK)) +#define UV_NETIF_IS_PTP(netif) (!!(netif.flags & UV_NETIF_PTP)) +#define UV_NETIF_IS_PROMISCUOUS(netif) (!!(netif.flags & UV_NETIF_PROMISCUOUS)) +#define UV_NETIF_HAS_BROADCAST(netif) (!!(netif.flags & UV_NETIF_BROADCAST)) +#define UV_NETIF_HAS_MULTICAST(netif) (!!(netif.flags & UV_NETIF_MULTICAST)) + +typedef struct uv_network_interface_s { + char* name; + uint32_t flags; /* uv_netif_flags */ + char phys_addr[16]; + uint32_t phys_addr_len; + union { + struct sockaddr_in address4; + struct sockaddr_in6 address6; + } address; + struct sockaddr_in broadcast4; + uint32_t prefix; /* CIDR prefix for both IPv4 and IPv6 */ +} uv_network_interface_s; + struct uv_passwd_s { char* username; long uid; @@ -1196,6 +1236,10 @@ UV_EXTERN int uv_os_gethostname(char* buffer, size_t* size); UV_EXTERN int uv_os_uname(uv_utsname_t* buffer); +UV_EXTERN int uv_network_interfaces(uv_network_interface_t** interfaces, + int* count); +UV_EXTERN void uv_free_network_interfaces(uv_network_interface_t* interfaces, + int count); typedef enum { UV_FS_UNKNOWN = -1, diff --git a/src/unix/aix.c b/src/unix/aix.c index 1f36926c02e..e07ccb5524d 100644 --- a/src/unix/aix.c +++ b/src/unix/aix.c @@ -1039,6 +1039,22 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { } +int uv_network_interfaces(uv_network_interface_t** interfaces, int* count) { + return UV_ENOTSUP; +} + + +void uv_free_network_interfaces(uv_network_interface_t* interfaces, int count) { + int i; + + for (i = 0; i < count; i++) { + uv__free(interfaces[i].name); + } + + uv__free(interfaces); +} + + void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { struct pollfd* events; uintptr_t i; diff --git a/src/unix/bsd-ifaddrs.c b/src/unix/bsd-ifaddrs.c index 0d7bbe662a5..753e528e870 100644 --- a/src/unix/bsd-ifaddrs.c +++ b/src/unix/bsd-ifaddrs.c @@ -157,3 +157,19 @@ void uv_free_interface_addresses(uv_interface_address_t* addresses, uv__free(addresses); } + + +int uv_network_interfaces(uv_network_interface_t** interfaces, int* count) { + return UV_ENOTSUP; +} + + +void uv_free_network_interfaces(uv_network_interface_t* interfaces, int count) { + int i; + + for (i = 0; i < count; i++) { + uv__free(interfaces[i].name); + } + + uv__free(interfaces); +} diff --git a/src/unix/linux-core.c b/src/unix/linux-core.c index 433e201fe19..e3ca8ad7960 100644 --- a/src/unix/linux-core.c +++ b/src/unix/linux-core.c @@ -926,6 +926,22 @@ void uv_free_interface_addresses(uv_interface_address_t* addresses, } +int uv_network_interfaces(uv_network_interface_t** interfaces, int* count) { + return UV_ENOTSUP; +} + + +void uv_free_network_interfaces(uv_network_interface_t* interfaces, int count) { + int i; + + for (i = 0; i < count; i++) { + uv__free(interfaces[i].name); + } + + uv__free(interfaces); +} + + void uv__set_process_title(const char* title) { #if defined(PR_SET_NAME) prctl(PR_SET_NAME, title); /* Only copies first 16 characters. */ diff --git a/src/unix/sunos.c b/src/unix/sunos.c index 180cc84651d..452b57c15f0 100644 --- a/src/unix/sunos.c +++ b/src/unix/sunos.c @@ -824,6 +824,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { } #endif /* SUNOS_NO_IFADDRS */ + void uv_free_interface_addresses(uv_interface_address_t* addresses, int count) { int i; @@ -834,3 +835,19 @@ void uv_free_interface_addresses(uv_interface_address_t* addresses, uv__free(addresses); } + + +int uv_network_interfaces(uv_network_interface_t** interfaces, int* count) { + return UV_ENOTSUP; +} + + +void uv_free_network_interfaces(uv_network_interface_t* interfaces, int count) { + int i; + + for (i = 0; i < count; i++) { + uv__free(interfaces[i].name); + } + + uv__free(interfaces); +} diff --git a/src/win/util.c b/src/win/util.c index 8849d041bf0..08ba2a7e530 100644 --- a/src/win/util.c +++ b/src/win/util.c @@ -1066,6 +1066,22 @@ void uv_free_interface_addresses(uv_interface_address_t* addresses, } +int uv_network_interfaces(uv_network_interface_t** interfaces, int* count) { + return UV_ENOTSUP; +} + + +void uv_free_network_interfaces(uv_network_interface_t* interfaces, int count) { + int i; + + for (i = 0; i < count; i++) { + uv__free(interfaces[i].name); + } + + uv__free(interfaces); +} + + int uv_getrusage(uv_rusage_t *uv_rusage) { FILETIME createTime, exitTime, kernelTime, userTime; SYSTEMTIME kernelSystemTime, userSystemTime; diff --git a/test/test-platform-output.c b/test/test-platform-output.c index e651e5c5829..1c9329b9685 100644 --- a/test/test-platform-output.c +++ b/test/test-platform-output.c @@ -34,21 +34,25 @@ TEST_IMPL(platform_output) { uv_rusage_t rusage; uv_cpu_info_t* cpus; uv_interface_address_t* interfaces; + uv_network_interface_t* netifs; uv_passwd_t pwd; uv_utsname_t uname; int count; int i; int err; + /* uv_get_process_title */ err = uv_get_process_title(buffer, sizeof(buffer)); ASSERT(err == 0); printf("uv_get_process_title: %s\n", buffer); + /* uv_cwd */ size = sizeof(buffer); err = uv_cwd(buffer, &size); ASSERT(err == 0); printf("uv_cwd: %s\n", buffer); + /* uv_resident_set_memory */ err = uv_resident_set_memory(&rss); #if defined(__MSYS__) ASSERT(err == UV_ENOSYS); @@ -57,11 +61,13 @@ TEST_IMPL(platform_output) { printf("uv_resident_set_memory: %llu\n", (unsigned long long) rss); #endif + /* uv_uptime */ err = uv_uptime(&uptime); ASSERT(err == 0); ASSERT(uptime > 0); printf("uv_uptime: %f\n", uptime); + /* uv_getrusage */ err = uv_getrusage(&rusage); ASSERT(err == 0); ASSERT(rusage.ru_utime.tv_sec >= 0); @@ -79,6 +85,7 @@ TEST_IMPL(platform_output) { printf(" maximum resident set size: %llu\n", (unsigned long long) rusage.ru_maxrss); + /* uv_cpu_info */ err = uv_cpu_info(&cpus, &count); #if defined(__CYGWIN__) || defined(__MSYS__) ASSERT(err == UV_ENOSYS); @@ -101,6 +108,7 @@ TEST_IMPL(platform_output) { #endif uv_free_cpu_info(cpus, count); + /* uv_interface_addresses */ err = uv_interface_addresses(&interfaces, &count); ASSERT(err == 0); @@ -137,6 +145,81 @@ TEST_IMPL(platform_output) { } uv_free_interface_addresses(interfaces, count); + /* uv_network_interfaces */ + err = uv_network_interfaces(&netifs, &count); + ASSERT(err == 0 || err == UV_ENOTSUP); + + /* TODO: Remove this outer `if` after all platforms have implemented the new + * interface and the `ASSERT` above drops the `|| err == UV_ENOTSUP` + * condition. + */ + if (err == 0) { + printf("uv_network_interfaces:\n"); + for (i = 0; i < count; i++) { + + /* Meta & flags */ + printf(" name: %s\n", netifs[i].name); + printf(" is_up: %d\n", UV_NETIF_IS_UP(netifs[i])); + printf(" is_loopback: %d\n", UV_NETIF_IS_LOOPBACK(netifs[i])); + printf(" is_point_to_point: %d\n", UV_NETIF_IS_PTP(netifs[i])); + printf(" is_promiscuous: %d\n", UV_NETIF_IS_PROMISCUOUS(netifs[i])); + printf(" has_broadcast: %d\n", UV_NETIF_HAS_BROADCAST(netifs[i])); + printf(" has_multicast: %d\n", UV_NETIF_HAS_MULTICAST(netifs[i])); + + /* Address */ + if (netifs[i].address.address4.sin_family == AF_INET6) { + /* IPv6 addresses will display the prefix directly after the address, + * while IPv4 addresses will later print a netmask address computed + * from the prefix. + */ + uv_ip6_name(&netifs[i].address.address6, buffer, sizeof(buffer)); + printf(" address: inet6 %s/%u\n", buffer, netifs[i].prefix); + } else if (netifs[i].address.address4.sin_family == AF_INET) { + uv_ip4_name(&netifs[i].address.address4, buffer, sizeof(buffer)); + printf(" address: inet4 %s\n", buffer); + } else { + printf(" address: none\n"); + } + + /* Broadcast */ + if (UV_NETIF_HAS_BROADCAST(netifs[i]) && + netifs[i].broadcast4.sin_family == AF_INET) { + uv_ip4_name(&netifs[i].broadcast4, buffer, sizeof(buffer)); + printf(" broadcast: inet4 %s\n", buffer); + } + + /* Netmask */ + if (netifs[i].address.address4.sin_family == AF_INET) { + /* Only compute and display an IPv4 style netmask from the CIDR prefix + * if the interface address is `AF_INET` + */ + if (netifs[i].prefix) { + uint32_t mask = htonl(~((1 << (32 - netifs[i].prefix)) - 1)); + struct sockaddr_in netmask4; + netmask4.sin_addr.s_addr = mask; + uv_ip4_name(&netmask4, buffer, sizeof(buffer)); + printf(" netmask: inet4 %s\n", buffer); + } else { + printf(" netmask: none\n"); + } + } + + /* Physical layer */ + if (netifs[i].phys_addr_len) { + uint32_t b; + printf(" physical address: "); + for (b = 0; b < netifs[i].phys_addr_len; ++b) { + printf("%s%02x", b == 0 ? "" : ":", + (unsigned char)netifs[i].phys_addr[b]); + } + printf("\n"); + } + } + + uv_free_network_interfaces(netifs, count); + } + + /* uv_os_get_passwd */ err = uv_os_get_passwd(&pwd); ASSERT(err == 0);