diff options
Diffstat (limited to 'src/interface.c')
-rw-r--r-- | src/interface.c | 324 |
1 files changed, 324 insertions, 0 deletions
diff --git a/src/interface.c b/src/interface.c new file mode 100644 index 0000000..e5038f4 --- /dev/null +++ b/src/interface.c @@ -0,0 +1,324 @@ +/* $Id: interface.c 114 2004-12-19 00:08:01Z lennart $ */ + +/* + * This file is part of ifplugd. + * + * ifplugd 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 2 of the License, or + * (at your option) any later version. + * + * ifplugd 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 ifplugd; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <linux/sockios.h> +#include <linux/if_ether.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <linux/if.h> +#include <syslog.h> +#include <string.h> +#include <errno.h> +#include <netinet/in.h> +#include <stdio.h> +#include <ctype.h> +#include <stdlib.h> +#include <assert.h> + +#include "ethtool-local.h" +#include "interface.h" +#include "wireless.h" + +#include <libdaemon/dlog.h> + +void interface_up(int fd, char *iface) { + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name)-1); + + if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) { + if (interface_do_message) + daemon_log(LOG_WARNING, "Warning: Could not get interface flags."); + + return; + } + + if ((ifr.ifr_flags & IFF_UP) == IFF_UP) + return; + + if (ioctl(fd, SIOCGIFADDR, &ifr) < 0) { + if (interface_do_message) + daemon_log(LOG_WARNING, "Warning: Could not get interface address."); + } else if (ifr.ifr_addr.sa_family != AF_INET) { + if (interface_do_message) + daemon_log(LOG_WARNING, "Warning: The interface is not IP-based."); + } else { + ((struct sockaddr_in *)(&ifr.ifr_addr))->sin_addr.s_addr = INADDR_ANY; + if (ioctl(fd, SIOCSIFADDR, &ifr) < 0) { + if (interface_do_message) + daemon_log(LOG_WARNING, "Warning: Could not set interface address."); + } + } + + if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) { + if (interface_do_message) + daemon_log(LOG_WARNING, "Warning: Could not get interface flags."); + + return; + } + + ifr.ifr_flags |= IFF_UP; + + if (ioctl(fd, SIOCSIFFLAGS, &ifr) < 0) + if (interface_do_message) + daemon_log(LOG_WARNING, "Warning: Could not set interface flags."); +} + +interface_status_t interface_detect_beat_mii(int fd, char *iface) { + struct ifreq ifr; + + if (interface_auto_up) + interface_up(fd, iface); + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name)-1); + + if (ioctl(fd, SIOCGMIIPHY, &ifr) == -1) { + if (interface_do_message) + daemon_log(LOG_ERR, "SIOCGMIIPHY failed: %s", strerror(errno)); + + return IFSTATUS_ERR; + } + + ((unsigned short*) &ifr.ifr_data)[1] = 1; + + if (ioctl(fd, SIOCGMIIREG, &ifr) == -1) { + if (interface_do_message) + daemon_log(LOG_ERR, "SIOCGMIIREG failed: %s", strerror(errno)); + + return IFSTATUS_ERR; + } + + return (((unsigned short*) &ifr.ifr_data)[3] & 0x0004) ? IFSTATUS_UP : IFSTATUS_DOWN; +} + +interface_status_t interface_detect_beat_priv(int fd, char *iface) { + struct ifreq ifr; + + if (interface_auto_up) + interface_up(fd, iface); + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name)-1); + + if (ioctl(fd, SIOCDEVPRIVATE, &ifr) == -1) { + if (interface_do_message) + daemon_log(LOG_ERR, "SIOCDEVPRIVATE failed: %s", strerror(errno)); + + return IFSTATUS_ERR; + } + + ((unsigned short*) &ifr.ifr_data)[1] = 1; + + if (ioctl(fd, SIOCDEVPRIVATE+1, &ifr) == -1) { + if (interface_do_message) + daemon_log(LOG_ERR, "SIOCDEVPRIVATE+1 failed: %s", strerror(errno)); + + return IFSTATUS_ERR; + } + + return (((unsigned short*) &ifr.ifr_data)[3] & 0x0004) ? IFSTATUS_UP : IFSTATUS_DOWN; +} + +interface_status_t interface_detect_beat_ethtool(int fd, char *iface) { + + struct ifreq ifr; + struct ethtool_value edata; + + if (interface_auto_up) + interface_up(fd, iface); + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name)-1); + + edata.cmd = ETHTOOL_GLINK; + ifr.ifr_data = (caddr_t) &edata; + + if (ioctl(fd, SIOCETHTOOL, &ifr) == -1) { + if (interface_do_message) + daemon_log(LOG_ERR, "ETHTOOL_GLINK failed: %s", strerror(errno)); + + return IFSTATUS_ERR; + } + + return edata.data ? IFSTATUS_UP : IFSTATUS_DOWN; +} + +interface_status_t interface_detect_beat_iff(int fd, char *iface) { + + struct ifreq ifr; + + if (interface_auto_up) + interface_up(fd, iface); + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name)-1); + + if (ioctl(fd, SIOCGIFFLAGS, &ifr) == -1) { + if (interface_do_message) + daemon_log(LOG_ERR, "SIOCGIFFLAGS failed: %s", strerror(errno)); + + return IFSTATUS_ERR; + } + + return ifr.ifr_flags & IFF_RUNNING ? IFSTATUS_UP : IFSTATUS_DOWN; +} + +static int get_wlan_qual_old(char *iface) { + FILE *f; + char buf[256]; + char *bp; + int l, q = -1; + + l = strlen(iface); + + if (!(f = fopen("/proc/net/wireless", "r"))) { + if (interface_do_message) + daemon_log(LOG_WARNING, "Failed to open /proc/net/wireless: %s",strerror(errno)); + + return -1; + } + + while (fgets(buf, sizeof(buf)-1, f)) { + bp = buf; + + while (*bp && isspace(*bp)) + bp++; + + if(!strncmp(bp, iface, l) && bp[l]==':') { + + /* skip device name */ + if (!(bp = strchr(bp,' '))) + break; + + bp++; + + /* skip status */ + if (!(bp = strchr(bp,' '))) + break; + + q = atoi(bp); + break; + }; + } + + fclose(f); + + if (q < 0) { + if (interface_do_message) + daemon_log(LOG_ERR, "Failed to find interface in /proc/net/wireless"); + } + + return q; +} + +static int get_wlan_qual_new(int fd, char *iface) { + struct iwreq req; + struct iw_statistics q; + static struct iw_range range; + + memset(&req, 0, sizeof(req)); + strncpy(req.ifr_ifrn.ifrn_name, iface, IFNAMSIZ); + + req.u.data.pointer = (caddr_t) &q; + req.u.data.length = sizeof(q); + req.u.data.flags = 1; + + if (ioctl(fd, SIOCGIWSTATS, &req) < 0) { + if (interface_do_message) + daemon_log(LOG_ERR, "Failed to get interface quality: %s\n", strerror(errno)); + return -1; + } + + memset(&req, 0, sizeof(req)); + strncpy(req.ifr_ifrn.ifrn_name, iface, IFNAMSIZ); + + memset(&range, 0, sizeof(struct iw_range)); + req.u.data.pointer = (caddr_t) ⦥ + req.u.data.length = sizeof(struct iw_range); + req.u.data.flags = 0; + + if (ioctl(fd, SIOCGIWRANGE, &req) < 0) { + if (interface_do_message) + daemon_log(LOG_ERR, "SIOCGIWRANGE failed: %s\n", strerror(errno)); + return -1; + } + + /* Test if both qual and level are on their lowest level */ + if (q.qual.qual <= 0 && + (q.qual.level > range.max_qual.level ? q.qual.level <= 156 : q.qual.level <= 0)) + return 0; + + return 1; +} + + +static int is_assoc_ap(uint8_t mac[ETH_ALEN]) { + int b, j; + b = 1; + + for (j = 1; j < ETH_ALEN; j++) + if (mac[j] != mac[0]) { + b = 0; + break; + } + + return !b || (mac[0] != 0xFF && mac[0] != 0x44 && mac[0] != 0x00); +} + +interface_status_t interface_detect_beat_wlan(int fd, char *iface) { + uint8_t mac[6]; + int q; + struct iwreq req; + + if (interface_auto_up) + interface_up(fd, iface); + + memset(&req, 0, sizeof(req)); + strncpy(req.ifr_ifrn.ifrn_name, iface, IFNAMSIZ); + + if (ioctl(fd, SIOCGIWAP, &req) < 0) { + if (interface_do_message) + daemon_log(LOG_WARNING, "Failed to get AP address: %s",strerror(errno)); + return IFSTATUS_ERR; + } + + memcpy(mac, &(req.u.ap_addr.sa_data), ETH_ALEN); + + if (!is_assoc_ap(mac)) + return IFSTATUS_DOWN; + + if ((q = get_wlan_qual_new(fd, iface)) < 0) + if ((q = get_wlan_qual_old(iface)) < 0) { + if (interface_do_message) + daemon_log(LOG_WARNING, "Failed to get wireless link quality."); + + return IFSTATUS_ERR; + } + + return q > 0 ? IFSTATUS_UP : IFSTATUS_DOWN; +} + |