summaryrefslogtreecommitdiff
path: root/src/interface.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/interface.c')
-rw-r--r--src/interface.c324
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) &range;
+ 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;
+}
+