/* * \brief Lwip plugin implementation * \author Christian Prochaska * \author Norman Feske * \date 2010-02-12 */ /* * Copyright (C) 2010-2013 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU General Public License version 2. */ #include #include #define LWIP_COMPAT_SOCKETS 0 /* rename lwip functions that have the same name in the libc to lwip_*() */ #define addrinfo lwip_addrinfo #define fd_set lwip_fd_set #define hostent lwip_hostent #define linger lwip_linger #define sockaddr lwip_sockaddr #define timeval lwip_timeval /* the extern "C" declaration is missing in lwip/netdb.h */ extern "C" { #include } #include #include /* lwip and libc have different definitions for the FD_* macros and renaming * of the macros is not possible, so wrapper functions are needed at this * place */ static inline void lwip_FD_ZERO(lwip_fd_set *set) { FD_ZERO(set); } static inline bool lwip_FD_ISSET(int lwip_fd, lwip_fd_set *set) { return FD_ISSET(lwip_fd, set); } static inline void lwip_FD_SET(int lwip_fd, lwip_fd_set *set) { FD_SET(lwip_fd, set); } static const long lwip_FIONBIO = FIONBIO; static const long lwip_FIONREAD = FIONREAD; /* undefine lwip type names that are also defined in libc headers and have * been renamed to lwip_*() */ #undef addrinfo #undef fd_set #undef hostent #undef linger #undef sockaddr #undef timeval /* undefine lwip macros that are also defined in libc headers and cannot be * renamed */ #undef BIG_ENDIAN #undef BYTE_ORDER #undef FD_CLR #undef FD_ISSET #undef FD_SET #undef FD_ZERO #undef FIONBIO #undef FIONREAD #undef HOST_NOT_FOUND #undef IOCPARM_MASK #undef IOC_VOID #undef IOC_OUT #undef IOC_IN #undef _IO #undef _IOR #undef _IOW #undef LITTLE_ENDIAN #undef MSG_DONTWAIT #undef MSG_OOB #undef MSG_PEEK #undef MSG_WAITALL #undef NO_DATA #undef NO_RECOVERY #undef EAI_FAIL #undef EAI_MEMORY #undef EAI_NONAME #undef EAI_SERVICE #undef SIOCATMARK #undef SIOCGHIWAT #undef SIOCGLOWAT #undef SIOCSHIWAT #undef SIOCSLOWAT #undef SOL_SOCKET #undef TRY_AGAIN /********************** ** Plugin interface ** **********************/ #include #include #include #include namespace { /** * For the lwip plugin, we only use the lwip file descriptor as context */ class Plugin_context : public Libc::Plugin_context { private: int _lwip_fd; public: /** * Constructor * * \param lwip_fd file descriptor used for interacting with the * socket API of lwip */ Plugin_context(int lwip_fd) : _lwip_fd(lwip_fd) { } int lwip_fd() const { return _lwip_fd; } }; static inline Plugin_context *context(Libc::File_descriptor *fd) { return static_cast(fd->context); } static inline int get_lwip_fd(Libc::File_descriptor *sockfdo) { return context(sockfdo)->lwip_fd(); } struct Plugin : Libc::Plugin { /** * Constructor */ Plugin(); bool supports_freeaddrinfo(struct ::addrinfo *res); bool supports_getaddrinfo(const char *node, const char *service, const struct ::addrinfo *hints, struct ::addrinfo **res); bool supports_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); bool supports_socket(int domain, int type, int protocol); Libc::File_descriptor *accept(Libc::File_descriptor *sockfdo, struct sockaddr *addr, socklen_t *addrlen); int bind(Libc::File_descriptor *sockfdo, const struct sockaddr *addr, socklen_t addrlen); int close(Libc::File_descriptor *fdo); int connect(Libc::File_descriptor *sockfdo, const struct sockaddr *addr, socklen_t addrlen); int fcntl(Libc::File_descriptor *sockfdo, int cmd, long val); void freeaddrinfo(struct ::addrinfo *res); int getaddrinfo(const char *node, const char *service, const struct ::addrinfo *hints, struct ::addrinfo **res); int getpeername(Libc::File_descriptor *sockfdo, struct sockaddr *addr, socklen_t *addrlen); int getsockname(Libc::File_descriptor *sockfdo, struct sockaddr *addr, socklen_t *addrlen); int getsockopt(Libc::File_descriptor *sockfdo, int level, int optname, void *optval, socklen_t *optlen); int ioctl(Libc::File_descriptor *sockfdo, int request, char *argp); int listen(Libc::File_descriptor *sockfdo, int backlog); ssize_t read(Libc::File_descriptor *fdo, void *buf, ::size_t count); int shutdown(Libc::File_descriptor *fdo, int); int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); ssize_t send(Libc::File_descriptor *, const void *buf, ::size_t len, int flags); ssize_t sendto(Libc::File_descriptor *, const void *buf, ::size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen); ssize_t recv(Libc::File_descriptor *, void *buf, ::size_t len, int flags); ssize_t recvfrom(Libc::File_descriptor *, void *buf, ::size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen); int setsockopt(Libc::File_descriptor *sockfdo, int level, int optname, const void *optval, socklen_t optlen); Libc::File_descriptor *socket(int domain, int type, int protocol); ssize_t write(Libc::File_descriptor *fdo, const void *buf, ::size_t count); }; Plugin::Plugin() { PDBG("using the lwIP libc plugin\n"); lwip_tcpip_init(); } bool Plugin::supports_freeaddrinfo(struct ::addrinfo *) { return true; } bool Plugin::supports_getaddrinfo(const char *, const char *, const struct ::addrinfo *, struct ::addrinfo **) { return true; } bool Plugin::supports_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) { Libc::File_descriptor *fdo; /* return true if any file descriptor which is marked set in one of * the sets belongs to this plugin */ for (int libc_fd = 0; libc_fd < nfds; libc_fd++) { if (FD_ISSET(libc_fd, readfds) || FD_ISSET(libc_fd, writefds) || FD_ISSET(libc_fd, exceptfds)) { fdo = Libc::file_descriptor_allocator()->find_by_libc_fd(libc_fd); if (fdo && (fdo->plugin == this)) { return true; } } } return false; } bool Plugin::supports_socket(int domain, int, int) { if (domain == AF_INET) return true; return false; } Libc::File_descriptor *Plugin::accept(Libc::File_descriptor *sockfdo, struct sockaddr *addr, socklen_t *addrlen) { int lwip_fd = lwip_accept(get_lwip_fd(sockfdo), (struct lwip_sockaddr*)addr, addrlen); if (lwip_fd == -1) { return 0; } Plugin_context *context = new (Genode::env()->heap()) Plugin_context(lwip_fd); Libc::File_descriptor *fd = Libc::file_descriptor_allocator()->alloc(this, context); if (!fd) PERR("could not allocate file descriptor"); return fd; } int Plugin::bind(Libc::File_descriptor *sockfdo, const struct sockaddr *addr, socklen_t addrlen) { return lwip_bind(get_lwip_fd(sockfdo), (struct lwip_sockaddr*)addr, addrlen); } int Plugin::close(Libc::File_descriptor *fdo) { int result = lwip_close(get_lwip_fd(fdo)); if (context(fdo)) Genode::destroy(Genode::env()->heap(), context(fdo)); Libc::file_descriptor_allocator()->free(fdo); return result; } int Plugin::connect(Libc::File_descriptor *sockfdo, const struct sockaddr *addr, socklen_t addrlen) { return lwip_connect(get_lwip_fd(sockfdo), (struct lwip_sockaddr*)addr, addrlen); } int Plugin::fcntl(Libc::File_descriptor *sockfdo, int cmd, long val) { int s = get_lwip_fd(sockfdo); int result = -1; switch (cmd) { case F_GETFL: case F_SETFL: /* * lwip_fcntl() supports only the 'O_NONBLOCK' flag and only if * no other flag is set. */ result = lwip_fcntl(s, cmd, val & O_NONBLOCK); break; default: PERR("unsupported fcntl() request: %d", cmd); break; } return result; } extern "C" void libc_freeaddrinfo(struct ::addrinfo *); void Plugin::freeaddrinfo(struct ::addrinfo *res) { return ::libc_freeaddrinfo(res); } extern "C" int libc_getaddrinfo(const char *, const char *, const struct ::addrinfo *, struct ::addrinfo **); int Plugin::getaddrinfo(const char *node, const char *service, const struct ::addrinfo *hints, struct ::addrinfo **res) { return ::libc_getaddrinfo(node, service, hints, res); } int Plugin::getpeername(Libc::File_descriptor *sockfdo, struct sockaddr *addr, socklen_t *addrlen) { return lwip_getpeername(get_lwip_fd(sockfdo), (struct lwip_sockaddr*)addr, addrlen); } int Plugin::getsockname(Libc::File_descriptor *sockfdo, struct sockaddr *addr, socklen_t *addrlen) { return lwip_getsockname(get_lwip_fd(sockfdo), (struct lwip_sockaddr*)addr, addrlen); } int Plugin::getsockopt(Libc::File_descriptor *sockfdo, int level, int optname, void *optval, socklen_t *optlen) { return lwip_getsockopt(get_lwip_fd(sockfdo), level, optname, optval, optlen); } int Plugin::ioctl(Libc::File_descriptor *sockfdo, int request, char *argp) { switch (request) { case FIONBIO: return lwip_ioctl(get_lwip_fd(sockfdo), lwip_FIONBIO, argp); break; case FIONREAD: return lwip_ioctl(get_lwip_fd(sockfdo), lwip_FIONREAD, argp);; break; default: PERR("unsupported ioctl() request"); errno = ENOSYS; return -1; } } int Plugin::listen(Libc::File_descriptor *sockfdo, int backlog) { return lwip_listen(get_lwip_fd(sockfdo), backlog); } ssize_t Plugin::read(Libc::File_descriptor *fdo, void *buf, ::size_t count) { return lwip_read(get_lwip_fd(fdo), buf, count); } int Plugin::shutdown(Libc::File_descriptor *sockfdo, int how) { return lwip_shutdown(get_lwip_fd(sockfdo), how); } int Plugin::select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) { Libc::File_descriptor *fdo; lwip_fd_set lwip_readfds; lwip_fd_set lwip_writefds; lwip_fd_set lwip_exceptfds; int libc_fd; int lwip_fd; int highest_lwip_fd = -2; int result; lwip_FD_ZERO(&lwip_readfds); lwip_FD_ZERO(&lwip_writefds); lwip_FD_ZERO(&lwip_exceptfds); for (libc_fd = 0; libc_fd < nfds; libc_fd++) { fdo = Libc::file_descriptor_allocator()->find_by_libc_fd(libc_fd); /* handle only libc_fds that belong to this plugin */ if (!fdo || (fdo->plugin != this)) continue; if (FD_ISSET(libc_fd, readfds) || FD_ISSET(libc_fd, writefds) || FD_ISSET(libc_fd, exceptfds)) { lwip_fd = get_lwip_fd(fdo); if (lwip_fd > highest_lwip_fd) { highest_lwip_fd = lwip_fd; } if (FD_ISSET(libc_fd, readfds)) { lwip_FD_SET(lwip_fd, &lwip_readfds); } if (FD_ISSET(libc_fd, writefds)) { lwip_FD_SET(lwip_fd, &lwip_writefds); } if (FD_ISSET(libc_fd, exceptfds)) { lwip_FD_SET(lwip_fd, &lwip_exceptfds); } } } result = lwip_select(highest_lwip_fd + 1, &lwip_readfds, &lwip_writefds, &lwip_exceptfds, (struct lwip_timeval*)timeout); if (result > 0) { /* clear result sets */ FD_ZERO(readfds); FD_ZERO(writefds); FD_ZERO(exceptfds); for (lwip_fd = 0; lwip_fd <= highest_lwip_fd; lwip_fd++) { /* find an lwip_fd which is in the set */ if (lwip_FD_ISSET(lwip_fd, &lwip_readfds) || lwip_FD_ISSET(lwip_fd, &lwip_writefds) || lwip_FD_ISSET(lwip_fd, &lwip_exceptfds)) { /* find matching libc_fd for lwip_fd */ for (libc_fd = 0; libc_fd < nfds; libc_fd++) { fdo = Libc::file_descriptor_allocator()->find_by_libc_fd(libc_fd); if (fdo && (fdo->plugin == this) && (get_lwip_fd(fdo) == lwip_fd)) { if (lwip_FD_ISSET(lwip_fd, &lwip_readfds)) { FD_SET(libc_fd, readfds); } if (lwip_FD_ISSET(lwip_fd, &lwip_writefds)) { FD_SET(libc_fd, writefds); } if (lwip_FD_ISSET(lwip_fd, &lwip_exceptfds)) { FD_SET(libc_fd, exceptfds); } } } } } } return result; } ssize_t Plugin::recv(Libc::File_descriptor *sockfdo, void *buf, ::size_t len, int flags) { return lwip_recv(get_lwip_fd(sockfdo), buf, len, flags); } ssize_t Plugin::recvfrom(Libc::File_descriptor *sockfdo, void *buf, ::size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen) { return lwip_recvfrom(get_lwip_fd(sockfdo), buf, len, flags, (struct lwip_sockaddr *)src_addr, addrlen); } ssize_t Plugin::send(Libc::File_descriptor *sockfdo, const void *buf, ::size_t len, int flags) { return lwip_send(get_lwip_fd(sockfdo), buf, len, flags); } ssize_t Plugin::sendto(Libc::File_descriptor *sockfdo, const void *buf, ::size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen) { return lwip_sendto(get_lwip_fd(sockfdo), buf, len, flags, (struct lwip_sockaddr *)dest_addr, addrlen); } int Plugin::setsockopt(Libc::File_descriptor *sockfdo, int level, int optname, const void *optval, socklen_t optlen) { return lwip_setsockopt(get_lwip_fd(sockfdo), level, optname, optval, optlen); } Libc::File_descriptor *Plugin::socket(int domain, int type, int protocol) { int lwip_fd = lwip_socket(domain, type, protocol); if (lwip_fd == -1) { PERR("lwip_socket() failed"); return 0; } Plugin_context *context = new (Genode::env()->heap()) Plugin_context(lwip_fd); return Libc::file_descriptor_allocator()->alloc(this, context); } ssize_t Plugin::write(Libc::File_descriptor *fdo, const void *buf, ::size_t count) { return lwip_write(get_lwip_fd(fdo), buf, count); } } /* unnamed namespace */ void create_lwip_plugin() { static Plugin lwip_plugin; }