diff options
Diffstat (limited to 'common/elapi/elapi_ioctl.c')
-rw-r--r-- | common/elapi/elapi_ioctl.c | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/common/elapi/elapi_ioctl.c b/common/elapi/elapi_ioctl.c new file mode 100644 index 00000000..c2a42a17 --- /dev/null +++ b/common/elapi/elapi_ioctl.c @@ -0,0 +1,142 @@ +/* + ELAPI + + Special platform specific functions related to networking + + Copyright (C) Dmitri Pal <dpal@redhat.com> 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define _GNU_SOURCE + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <stdlib.h> +#include <errno.h> + +#include "elapi_ioctl.h" +#include "trace.h" +#include "config.h" + +#ifndef HAVE_GETIFADDRS + +/* These functions are taken form Stevens's + * UNIX Network Programming Volume 1 + */ + +int elapi_get_addrlist(struct ifconf *ifc) +{ + int sockfd; + int length, lastlen; + int error; + char *buffer; + + TRACE_FLOW_STRING("elapi_get_addrlist", "Entry"); + + /* Open socket */ + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + + lastlen = 0; + length = INTERFACE_NUM_GUESS * sizeof(struct ifreq); + + /* Allocate right amount of memory */ + /* This is a trick from Stevens book */ + /* Search web for "get_ifi_info" to get original code */ + for ( ; ; ) { + + buffer = malloc(length); + ifc->ifc_len = length; + ifc->ifc_buf = buffer; + + /* Read list */ + if (ioctl(sockfd, SIOCGIFCONF, ifc) < 0) { + error = errno; + TRACE_INFO_NUMBER("Ioctl call failed", error); + if (error != EINVAL || lastlen != 0) { + free(buffer); + TRACE_ERROR_NUMBER("ioctl failed", error); + return error; + } + } else { + TRACE_INFO_NUMBER("Length returned", ifc->ifc_len); + TRACE_INFO_NUMBER("Previous length", lastlen); + /* Break if length is same */ + if (ifc->ifc_len == lastlen) break; + lastlen = ifc->ifc_len; + } + + /* Grow length */ + length += INTERFACE_NUM_INC * sizeof(struct ifreq); + free(buffer); + } + + TRACE_FLOW_STRING("elapi_get_addrlist", "Exit"); + return EOK; +} + +/* Get the variable part of the size of the address */ +static int elapi_get_addrlen(struct ifreq *ifr) +{ + int len; + + TRACE_FLOW_STRING("elapi_get_addrlen", "Entry"); + +#ifdef HAVE_SOCKADDR_SA_LEN + len = max(sizeof(struct sockaddr), ifr->ifr_addr.sa_len); +#else + switch (ifr->ifr_addr.sa_family) { +#ifdef IPV6 + case AF_INET6: + len = sizeof(struct sockaddr_in6); + break; +#endif + case AF_INET: + default: + len = sizeof(struct sockaddr); + break; + } +#endif /* HAVE_SOCKADDR_SA_LEN */ + + TRACE_FLOW_NUMBER("elapi_get_addrlen Returning", len); + return len; +} + +/* Get next address */ +struct ifreq *elapi_get_next_addr(struct ifconf *ifc, struct ifreq *current) +{ + char *ifr; + + TRACE_FLOW_STRING("elapi_get_next_addr", "Entry"); + + TRACE_INFO_NUMBER("Current ifi", current); + TRACE_INFO_NUMBER("Address", ¤t->ifr_addr); + + /* Move to the next item */ + ifr = (char *)current + sizeof(current->ifr_name) + elapi_get_addrlen(current); + + TRACE_INFO_NUMBER("New ifi", ifr); + + /* Check if we are beyond the end of the allocated area */ + /* Have to cast otherwise get warnings */ + if (ifr >= ((char *)ifc->ifc_buf + ifc->ifc_len)) ifr = NULL; + + TRACE_INFO_NUMBER("New ifi adjusted", ifr); + + TRACE_FLOW_STRING("elapi_get_next_addr", "Exit"); + + return (struct ifreq *)ifr; +} + + +#endif /* HAVE_GETIFADDRS */ |