From d7f24d8bca3790a819c2b5974614e2a48d26bd63 Mon Sep 17 00:00:00 2001 From: Emery Hemingway Date: Fri, 5 Jul 2019 16:32:19 +0200 Subject: [PATCH] Libc: implement getifaddrs Implement getifaddrs and freeifaddrs within the libc using socket control files at the VFS. Add an "address" and "netmask" file to the lwIP plugin. Only a single IPv4 address is initially supported, and the broadcast address returned will never be valid. Ref #3439 --- repos/libports/include/lwip/nic_netif.h | 2 + repos/libports/lib/symbols/libc | 2 + repos/libports/src/lib/libc/dummies.cc | 2 - .../libports/src/lib/libc/socket_fs_plugin.cc | 56 ++++++++++- repos/libports/src/lib/vfs/lwip/vfs.cc | 93 ++++++++++++++++++- .../libports/src/test/libc_getaddrinfo/main.c | 40 ++++++++ 6 files changed, 189 insertions(+), 6 deletions(-) diff --git a/repos/libports/include/lwip/nic_netif.h b/repos/libports/include/lwip/nic_netif.h index 687327376..2ee932bfc 100644 --- a/repos/libports/include/lwip/nic_netif.h +++ b/repos/libports/include/lwip/nic_netif.h @@ -256,6 +256,8 @@ class Lwip::Nic_netif virtual ~Nic_netif() { } + Lwip::netif& lwip_netif() { return _netif; } + /** * Status callback to override in subclass */ diff --git a/repos/libports/lib/symbols/libc b/repos/libports/lib/symbols/libc index 4b8fd9ba5..69e67d1c4 100644 --- a/repos/libports/lib/symbols/libc +++ b/repos/libports/lib/symbols/libc @@ -263,6 +263,8 @@ gethostbyaddr_r T gethostbyname W gethostid T gethostname T +getifaddrs T +freeifaddrs T getline T getloadavg T getlogin T diff --git a/repos/libports/src/lib/libc/dummies.cc b/repos/libports/src/lib/libc/dummies.cc index 089c36196..c241dab54 100644 --- a/repos/libports/src/lib/libc/dummies.cc +++ b/repos/libports/src/lib/libc/dummies.cc @@ -106,8 +106,6 @@ DUMMY(uid_t , 0, geteuid, (void)) DUMMY(gid_t , 0, getgid, (void)) DUMMY(int , -1, getgroups, (int, gid_t *)) DUMMY(struct hostent *, 0, gethostbyname, (const char *)) -DUMMY(int, -1, getifaddrs, (struct ifaddrs **)) -DUMMY(void, , freeifaddrs, (struct ifaddrs *ifp)) DUMMY(char *, 0, _getlogin, (void)) DUMMY(int , -1, getnameinfo, (const sockaddr *, socklen_t, char *, size_t, char *, size_t, int)) DUMMY_SILENT(pid_t , -1, getpid, (void)) diff --git a/repos/libports/src/lib/libc/socket_fs_plugin.cc b/repos/libports/src/lib/libc/socket_fs_plugin.cc index 559762bee..a3dd74bcb 100644 --- a/repos/libports/src/lib/libc/socket_fs_plugin.cc +++ b/repos/libports/src/lib/libc/socket_fs_plugin.cc @@ -1,5 +1,3 @@ -#include - /* * \brief Libc pseudo plugin for socket fs * \author Christian Helmuth @@ -34,6 +32,7 @@ #include #include #include +#include /* libc-internal includes */ #include "socket_fs_plugin.h" @@ -973,6 +972,59 @@ extern "C" int socket_fs_socket(int domain, int type, int protocol) } +static int read_ifaddr_file(sockaddr_in &sockaddr, Absolute_path const &path) +{ + Host_string address; + Port_string service; + *service.base() = '0'; + + { + FILE *fp = ::fopen(path.base(), "r"); + if (!fp) return -1; + + ::fscanf(fp, "%s\n", address.base()); + ::fclose(fp); + } + + try { sockaddr = sockaddr_in_struct(address, service); } + catch (...) { return -1; } + + return 0; +} + + +extern "C" int getifaddrs(struct ifaddrs **ifap) +{ + static Genode::Lock lock; + Genode::Lock::Guard guard(lock); + + // TODO: dual-stack / multi-homing + + static sockaddr_in address; + static sockaddr_in netmask { 0 }; + static sockaddr_in broadcast { 0 }; + + static ifaddrs ifaddr { + .ifa_addr = (sockaddr*)&address, + .ifa_netmask = (sockaddr*)&netmask, + .ifa_broadaddr = (sockaddr*)&broadcast, + }; + + *ifap = &ifaddr; + + Absolute_path const root(Libc::config_socket()); + + if (read_ifaddr_file(address, Absolute_path("address", root.base()))) + return -1; + + read_ifaddr_file(netmask, Absolute_path("netmask", root.base())); + return 0; +} + + +extern "C" void freeifaddrs(struct ifaddrs *) { } + + /**************************** ** File-plugin operations ** ****************************/ diff --git a/repos/libports/src/lib/vfs/lwip/vfs.cc b/repos/libports/src/lib/vfs/lwip/vfs.cc index 4dc9a6a42..30815a96e 100644 --- a/repos/libports/src/lib/vfs/lwip/vfs.cc +++ b/repos/libports/src/lib/vfs/lwip/vfs.cc @@ -84,6 +84,8 @@ extern "C" { }; struct Lwip_handle; struct Lwip_nameserver_handle; + struct Lwip_address_handle; + struct Lwip_netmask_handle; struct Lwip_file_handle; struct Lwip_dir_handle; @@ -113,7 +115,8 @@ extern "C" { enum { PORT_STRLEN_MAX = 6, /* :65536 */ - ENDPOINT_STRLEN_MAX = IPADDR_STRLEN_MAX+PORT_STRLEN_MAX + ENDPOINT_STRLEN_MAX = IPADDR_STRLEN_MAX+PORT_STRLEN_MAX, + ADDRESS_FILE_SIZE = IPADDR_STRLEN_MAX+2, }; struct Directory; @@ -195,6 +198,66 @@ struct Lwip::Lwip_nameserver_handle final : Lwip_handle, private Nameserver_regi }; +struct Lwip::Lwip_address_handle final : Lwip_handle +{ + Lwip::netif const &netif; + + Lwip_address_handle(Vfs::File_system &fs, Allocator &alloc, + Lwip::netif &netif) + : Lwip_handle(fs, alloc, Vfs::Directory_service::OPEN_MODE_RDONLY) + , netif(netif) + { } + + Read_result read(char *dst, file_size count, + file_size &out_count) override + { + using namespace Genode; + + char address[IPADDR_STRLEN_MAX] { '\0' }; + + ipaddr_ntoa_r(&netif.ip_addr, address, IPADDR_STRLEN_MAX); + + Genode::String + line((char const *)address, "\n"); + + size_t n = min(line.length(), count); + memcpy(dst, line.string(), n); + out_count = n; + return Read_result::READ_OK; + } +}; + + +struct Lwip::Lwip_netmask_handle final : Lwip_handle +{ + Lwip::netif const &netif; + + Lwip_netmask_handle(Vfs::File_system &fs, Allocator &alloc, + Lwip::netif &netif) + : Lwip_handle(fs, alloc, Vfs::Directory_service::OPEN_MODE_RDONLY) + , netif(netif) + { } + + Read_result read(char *dst, file_size count, + file_size &out_count) override + { + using namespace Genode; + + char netmask[IPADDR_STRLEN_MAX] { '\0' }; + + ipaddr_ntoa_r(&netif.netmask, netmask, IPADDR_STRLEN_MAX); + + Genode::String + line((char const *)netmask, "\n"); + + size_t n = min(line.length(), count); + memcpy(dst, line.string(), n); + out_count = n; + return Read_result::READ_OK; + } +}; + + struct Lwip::Lwip_file_handle final : Lwip_handle, private Lwip_handle_list::Element { friend class Lwip_handle_list; @@ -1674,6 +1737,12 @@ class Lwip::File_system final : public Vfs::File_system, public Lwip::Directory proc(path+3, _netif.udp_dir); } + static bool match_address(char const *name) { + return (!strcmp(name, "address")); } + + static bool match_netmask(char const *name) { + return (!strcmp(name, "netmask")); } + static bool match_nameserver(char const *name) { return (!strcmp(name, "nameserver")); } @@ -1707,7 +1776,9 @@ class Lwip::File_system final : public Vfs::File_system, public Lwip::Directory char const *leaf_path(char const *path) override { if (*path == '/') ++path; - if (match_nameserver(path)) + if (match_address(path) + || match_netmask(path) + || match_nameserver(path)) return path; char const *r = nullptr; @@ -1723,6 +1794,12 @@ class Lwip::File_system final : public Vfs::File_system, public Lwip::Directory st = Stat(); st.device = (Genode::addr_t)this; + if (match_address(path) || match_netmask(path)) { + st.size = ADDRESS_FILE_SIZE; + st.mode = STAT_MODE_FILE; + return STAT_OK; + } + if (match_nameserver(path)) { st.size = IPADDR_STRLEN_MAX; st.mode = STAT_MODE_FILE; @@ -1761,6 +1838,18 @@ class Lwip::File_system final : public Vfs::File_system, public Lwip::Directory */ if (mode & OPEN_MODE_CREATE) return OPEN_ERR_NO_PERM; + if (match_address(path)) { + *out_handle = new (alloc) + Lwip_address_handle(*this, alloc, _netif.lwip_netif()); + return OPEN_OK; + } + + if (match_netmask(path)) { + *out_handle = new (alloc) + Lwip_netmask_handle(*this, alloc, _netif.lwip_netif()); + return OPEN_OK; + } + if (match_nameserver(path)) { *out_handle = new (alloc) Lwip_nameserver_handle(*this, alloc, _netif.nameserver_handles); diff --git a/repos/libports/src/test/libc_getaddrinfo/main.c b/repos/libports/src/test/libc_getaddrinfo/main.c index cc6b82da9..2bb92645e 100644 --- a/repos/libports/src/test/libc_getaddrinfo/main.c +++ b/repos/libports/src/test/libc_getaddrinfo/main.c @@ -15,9 +15,49 @@ #include #include #include +#include int main(int argc, char **argv) { + { + struct ifaddrs *addrs = NULL; + + if (getifaddrs(&addrs)) { + printf("Check getifaddrs failed\n"); + return ~0; + } + + char ip_addr[NI_MAXHOST], + netmask[NI_MAXHOST], + broadcast[NI_MAXHOST], + sbuf[NI_MAXSERV]; + + if (getnameinfo(addrs->ifa_addr, addrs->ifa_addr->sa_len, + ip_addr, sizeof(ip_addr), sbuf, sizeof(sbuf), + NI_NUMERICHOST | NI_NUMERICSERV)) + { + printf("could not get address from getifaddrs\n"); + return ~0; + } + + if (getnameinfo(addrs->ifa_netmask, addrs->ifa_netmask->sa_len, + netmask, sizeof(netmask), sbuf, sizeof(sbuf), + NI_NUMERICHOST | NI_NUMERICSERV)) + { + printf("could not get netmask from getifaddrs\n"); + } + + if (getnameinfo(addrs->ifa_broadaddr, addrs->ifa_broadaddr->sa_len, + broadcast, sizeof(broadcast), sbuf, sizeof(sbuf), + NI_NUMERICHOST | NI_NUMERICSERV)) + { + printf("could not get broadcast from getifaddrs\n"); + } + + freeifaddrs(addrs); + printf("getifaddrs ip_addr=%s, netmask=%s broadcast=%s\n", ip_addr, netmask, broadcast); + } + struct addrinfo hints; char ipstr[INET6_ADDRSTRLEN];