| /* $Id: bsd-if.c,v 1.3 2003/10/16 02:48:23 fredette Exp $ */ |
| |
| /* host/bsd/bsd-if.c - BSD interface support: */ |
| |
| /* |
| * Copyright (c) 2001, 2003 Matt Fredette |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. All advertising materials mentioning features or use of this software |
| * must display the following acknowledgement: |
| * This product includes software developed by Matt Fredette. |
| * 4. The name of the author may not be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
| * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
| * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
| * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include <tme/common.h> |
| _TME_RCSID("$Id: bsd-if.c,v 1.3 2003/10/16 02:48:23 fredette Exp $"); |
| |
| /* includes: */ |
| #include "bsd-impl.h" |
| #include <string.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <netdb.h> |
| #include <sys/param.h> |
| #include <sys/socket.h> |
| #include <sys/stat.h> |
| #include <net/if.h> |
| #include <netinet/in_systm.h> |
| #include <netinet/in.h> |
| #if defined(HAVE_SYS_SOCKIO_H) |
| #include <sys/sockio.h> |
| #elif defined(HAVE_SYS_SOCKETIO_H) |
| #include <sys/socketio.h> |
| #endif /* HAVE_SYS_SOCKETIO_H */ |
| #include <sys/ioctl.h> |
| #ifdef HAVE_IOCTLS_H |
| #include <ioctls.h> |
| #endif /* HAVE_IOCTLS_H */ |
| #ifdef HAVE_NET_IF_ETHER_H |
| #include <net/if_ether.h> |
| #endif /* HAVE_NET_IF_ETHER_H */ |
| #ifdef HAVE_NET_ETHERNET_H |
| #include <net/ethernet.h> |
| #endif /* HAVE_NET_ETHERNET_H */ |
| #include <netinet/ip.h> |
| #ifdef HAVE_NET_IF_DL_H |
| #include <net/if_dl.h> |
| #endif /* HAVE_NET_IF_DL_H */ |
| #include <arpa/inet.h> |
| |
| /* this macro helps us size a struct ifreq: */ |
| #ifdef HAVE_SOCKADDR_SA_LEN |
| #define SIZEOF_IFREQ(ifr) (sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len) |
| #else /* !HAVE_SOCKADDR_SA_LEN */ |
| #define SIZEOF_IFREQ(ifr) (sizeof(ifr->ifr_name) + sizeof(struct sockaddr)) |
| #endif /* !HAVE_SOCKADDR_SA_LEN */ |
| |
| /* this finds a network interface: */ |
| int |
| tme_bsd_if_find(const char *ifr_name_user, struct ifreq **_ifreq, tme_uint8_t **_if_addr, unsigned int *_if_addr_size) |
| { |
| int saved_errno; |
| int dummy_fd; |
| char ifreq_buffer[16384]; /* FIXME - magic constant. */ |
| struct ifconf ifc; |
| struct ifreq *ifr; |
| struct ifreq *ifr_user; |
| size_t ifr_offset; |
| struct sockaddr_in saved_ip_address; |
| short saved_flags; |
| #ifdef HAVE_AF_LINK |
| struct ifreq *link_ifreqs[20]; /* FIXME - magic constant. */ |
| size_t link_ifreqs_count; |
| size_t link_ifreqs_i; |
| struct sockaddr_dl *sadl; |
| #endif /* HAVE_AF_LINK */ |
| |
| /* make a dummy socket so we can read the interface list: */ |
| if ((dummy_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { |
| return (-1); |
| } |
| |
| /* read the interface list: */ |
| ifc.ifc_len = sizeof(ifreq_buffer); |
| ifc.ifc_buf = ifreq_buffer; |
| if (ioctl(dummy_fd, SIOCGIFCONF, &ifc) < 0) { |
| saved_errno = errno; |
| close(dummy_fd); |
| errno = saved_errno; |
| return (-1); |
| } |
| |
| #ifdef HAVE_AF_LINK |
| /* start our list of link address ifreqs: */ |
| link_ifreqs_count = 0; |
| #endif /* HAVE_AF_LINK */ |
| |
| /* walk the interface list: */ |
| ifr_user = NULL; |
| for (ifr_offset = 0;; ifr_offset += SIZEOF_IFREQ(ifr)) { |
| |
| /* stop walking if we have run out of space in the buffer. note |
| that before we can use SIZEOF_IFREQ, we have to make sure that |
| there is a minimum number of bytes in the buffer to use it |
| (namely, that there's a whole struct sockaddr available): */ |
| ifr = (struct ifreq *) (ifreq_buffer + ifr_offset); |
| if (((ifr_offset |
| + sizeof(ifr->ifr_name) |
| + sizeof(struct sockaddr)) |
| > (size_t) ifc.ifc_len) |
| || ((ifr_offset |
| + SIZEOF_IFREQ(ifr)) |
| > (size_t) ifc.ifc_len)) { |
| errno = ENOENT; |
| break; |
| } |
| |
| #ifdef HAVE_AF_LINK |
| /* if this is a hardware address, save it: */ |
| if (ifr->ifr_addr.sa_family == AF_LINK) { |
| if (link_ifreqs_count < TME_ARRAY_ELS(link_ifreqs)) { |
| link_ifreqs[link_ifreqs_count++] = ifr; |
| } |
| continue; |
| } |
| #endif /* HAVE_AF_LINK */ |
| |
| /* ignore this interface if it doesn't do IP: */ |
| /* XXX is this actually important? */ |
| if (ifr->ifr_addr.sa_family != AF_INET) { |
| continue; |
| } |
| |
| /* get the interface flags, preserving the IP address in the |
| struct ifreq across the call: */ |
| saved_ip_address = *((struct sockaddr_in *) & ifr->ifr_addr); |
| if (ioctl(dummy_fd, SIOCGIFFLAGS, ifr) < 0) { |
| ifr = NULL; |
| break; |
| } |
| saved_flags = ifr->ifr_flags; |
| *((struct sockaddr_in *) & ifr->ifr_addr) = saved_ip_address; |
| |
| /* ignore this interface if it isn't up and running: */ |
| if ((saved_flags & (IFF_UP | IFF_RUNNING)) |
| != (IFF_UP | IFF_RUNNING)) { |
| continue; |
| } |
| |
| /* if we don't have an interface yet, take this one depending on |
| whether the user asked for an interface by name or not. if he |
| did, and this is it, take this one. if he didn't, and this |
| isn't a loopback interface, take this one: */ |
| if (ifr_user == NULL |
| && (ifr_name_user != NULL |
| ? !strncmp(ifr->ifr_name, ifr_name_user, sizeof(ifr->ifr_name)) |
| : !(ifr->ifr_flags & IFF_LOOPBACK))) { |
| ifr_user = ifr; |
| } |
| } |
| |
| /* close the dummy socket: */ |
| saved_errno = errno; |
| close(dummy_fd); |
| errno = saved_errno; |
| |
| /* if we don't have an interface to return: */ |
| if (ifr_user == NULL) { |
| return (errno); |
| } |
| |
| /* return this interface: */ |
| *_ifreq = (struct ifreq *) tme_memdup(ifr_user, SIZEOF_IFREQ(ifr_user)); |
| |
| /* assume that we can't find this interface's hardware address: */ |
| if (_if_addr != NULL) { |
| *_if_addr = NULL; |
| } |
| if (_if_addr_size != NULL) { |
| *_if_addr_size = 0; |
| } |
| |
| #ifdef HAVE_AF_LINK |
| |
| /* try to find an AF_LINK ifreq that gives us the interface's |
| hardware address: */ |
| ifr = NULL; |
| for (link_ifreqs_i = 0; |
| link_ifreqs_i < link_ifreqs_count; |
| link_ifreqs_i++) { |
| if (!strncmp(link_ifreqs[link_ifreqs_i]->ifr_name, |
| ifr_user->ifr_name, |
| sizeof(ifr_user->ifr_name))) { |
| ifr = link_ifreqs[link_ifreqs_i]; |
| break; |
| } |
| } |
| |
| /* if we found one, return the hardware address: */ |
| if (ifr != NULL) { |
| sadl = (struct sockaddr_dl *) &ifr->ifr_addr; |
| if (_if_addr_size != NULL) { |
| *_if_addr_size = sadl->sdl_alen; |
| } |
| if (_if_addr != NULL) { |
| *_if_addr = tme_new(tme_uint8_t, sadl->sdl_alen); |
| memcpy(*_if_addr, LLADDR(sadl), sadl->sdl_alen); |
| } |
| } |
| |
| #endif /* HAVE_AF_LINK */ |
| |
| /* done: */ |
| return (TME_OK); |
| } |