/* $Id$ */ /* * This file is part of waproamd. * * waproamd 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. * * waproamd 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 waproamd; if not, write to the Free Software Foundation, * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #include #include #include #include #include #include "iwapi.h" int iw_set_essid(struct interface *i, const char* essid) { struct iwreq req; char e[IW_ESSID_MAX_SIZE + 1]; assert(i); memset(&req, 0, sizeof(req)); strncpy(req.ifr_ifrn.ifrn_name, i->name, IFNAMSIZ); if (essid && *essid) { memset(&e, 0, sizeof(e)); strncpy(e, essid, IW_ESSID_MAX_SIZE); req.u.essid.pointer = e; req.u.essid.length = strlen(essid) + 1; req.u.essid.flags = 1; } if (ioctl(i->fd, SIOCSIWESSID, &req) < 0) { daemon_log(LOG_ERR, "ioctl(SIOCSIWESSID): %s", strerror(errno)); return -1; } return 0; } int iw_set_mode(struct interface *i, int m) { struct iwreq req; assert(i); memset(&req, 0, sizeof(req)); strncpy(req.ifr_ifrn.ifrn_name, i->name, IFNAMSIZ); req.u.mode = m; if (ioctl(i->fd, SIOCSIWMODE, &req) < 0) { daemon_log(LOG_ERR, "ioctl(SIOCSIWMODE): %s", strerror(errno)); return -1; } return 0; } /* int iw_set_freq(struct interface *i, const struct iw_freq *f) { */ /* struct iwreq req; */ /* assert(i && f); */ /* memset(&req, 0, sizeof(req)); */ /* strncpy(req.ifr_ifrn.ifrn_name, i->name, IFNAMSIZ); */ /* req.u.freq = *f; */ /* if (ioctl(i->fd, SIOCSIWFREQ, &req) < 0) { */ /* daemon_log(LOG_ERR, "ioctl(SIOCSIWFREQ): %s", strerror(errno)); */ /* return -1; */ /* } */ /* return 0; */ /* } */ int iw_set_ap(struct interface *i, const struct hw_addr *ap) { struct iwreq req; assert(i && ap); memset(&req, 0, sizeof(req)); strncpy(req.ifr_ifrn.ifrn_name, i->name, IFNAMSIZ); req.u.ap_addr.sa_family = ARPHRD_ETHER; memcpy(req.u.ap_addr.sa_data, ap->addr, ETH_ALEN); if (ioctl(i->fd, SIOCSIWAP, &req) < 0) { daemon_log(LOG_ERR, "ioctl(SIOCSIWAP): %s", strerror(errno)); return -1; } return 0; } int iw_scan(struct interface *i) { struct iwreq req; assert(i); daemon_log(LOG_INFO, "Scanning..."); iw_set_mode(i, IW_MODE_INFRA); iw_set_essid(i, NULL); /* Due to driver issues, the return codes of these function calls are no longer evaluated */ memset(&req, 0, sizeof(req)); strncpy(req.ifr_ifrn.ifrn_name, i->name, IFNAMSIZ); req.u.param.flags = IW_SCAN_DEFAULT; req.u.param.value = 0; if (ioctl(i->fd, SIOCSIWSCAN, &req) < 0) { daemon_log(LOG_ERR, "ioctl(SIOCSIWSCAN): %s", strerror(errno)); return -1; } return 0; } int iw_scan_result(struct interface *i, int (*callback)(struct ap_info* ap)) { struct ap_info ap; int f, l, hs; struct iwreq req; struct iw_event *e; uint8_t buffer[IW_SCAN_MAX_DATA]; assert(i && callback); memset(&req, 0, sizeof(req)); strncpy(req.ifr_ifrn.ifrn_name, i->name, IFNAMSIZ); req.u.data.pointer = buffer; req.u.data.flags = 0; req.u.data.length = sizeof(buffer); if (ioctl(i->fd, SIOCGIWSCAN, &req) < 0) { if (errno == EAGAIN) return 1; daemon_log(LOG_ERR, "ioctl(SIOCGIWSCAN): %s", strerror(errno)); return -1; } e = (struct iw_event*) req.u.data.pointer; l = req.u.data.length; f = 0; hs = sizeof(struct iw_event)-sizeof(union iwreq_data); while (l >= sizeof(struct iw_event)) { if (e->len < hs) { daemon_log(LOG_ERR, "Recieved bogus wireless event"); return -1; } if (!f) memset(&ap, 0, sizeof(ap)); switch (e->cmd) { case SIOCGIWAP: f = 1; if (e->len < hs + sizeof(struct sockaddr)) { daemon_log(LOG_ERR, "Corrupt scan result (1)"); return -1; } memcpy(&ap.ap, e->u.ap_addr.sa_data, ETH_ALEN); break; case SIOCGIWESSID: if (e->len < hs + sizeof(struct iw_point)) { daemon_log(LOG_ERR, "Corrupt scan result (2)"); return -1; } memset(&ap.essid, 0, sizeof(ap.essid)); memcpy(&ap.essid, ((uint8_t*) e)+hs+sizeof(struct iw_point), MIN(sizeof(ap.essid)-1, e->len-hs-sizeof(struct iw_point))); f |= 2; break; case SIOCGIWMODE: if (e->len < hs + sizeof(__u32)) { daemon_log(LOG_ERR, "Corrupt scan result (3)"); return -1; } if ((e->u.mode != IW_MODE_INFRA) && (e->u.mode != IW_MODE_MASTER)) f = 0; // Ignore non-APs else f |= 4; break; case SIOCGIWFREQ: if (e->len < hs + sizeof(struct iw_freq)) { daemon_log(LOG_ERR, "Corrupt scan result (4)"); return -1; } memcpy(&ap.freq, &e->u.freq, sizeof(struct iw_freq)); f |= 8; break; } if (f == 15) { if (callback(&ap) < 0) return -1; f = 0; } l -= e->len; e = (struct iw_event*) (((uint8_t*) e) + e->len); } return 0; } int iw_tune(struct interface *i, struct ap_info *ap) { char c[67]; assert(i && ap); snprint_hw_addr(c, sizeof(c), &ap->ap); /* We are not interested in the return values of these functions due to driver issues */ iw_set_mode(i, IW_MODE_INFRA); iw_set_essid(i, ap->essid); //iw_set_freq(i, &ap->freq); iw_set_ap(i, &ap->ap); return 0; } int iw_get_ap(struct interface *i, struct hw_addr *ap) { struct iwreq req; assert(i && ap); memset(&req, 0, sizeof(req)); strncpy(req.ifr_ifrn.ifrn_name, i->name, IFNAMSIZ); if (ioctl(i->fd, SIOCGIWAP, &req) < 0) { daemon_log(LOG_ERR, "ioctl(SIOCGIWAP): %s", strerror(errno)); return -1; } memcpy(ap->addr, &(req.u.ap_addr.sa_data), ETH_ALEN); return 0; } int iw_get_essid(struct interface *i, char *essid) { struct iwreq req; assert(i && essid); memset(&req, 0, sizeof(req)); strncpy(req.ifr_ifrn.ifrn_name, i->name, IFNAMSIZ); memset(essid, 0, IW_ESSID_MAX_SIZE); req.u.essid.pointer = essid; req.u.essid.length = IW_ESSID_MAX_SIZE+1; req.u.essid.flags = 1; if (ioctl(i->fd, SIOCGIWESSID, &req) < 0) { daemon_log(LOG_ERR, "ioctl(SIOCGSIWESSID): %s", strerror(errno)); return -1; } essid[IW_ESSID_MAX_SIZE] = 0; return 0; } int iw_get_mode(struct interface *i, int *m) { struct iwreq req; assert(i && m); memset(&req, 0, sizeof(req)); strncpy(req.ifr_ifrn.ifrn_name, i->name, IFNAMSIZ); req.u.mode = 0; if (ioctl(i->fd, SIOCGIWMODE, &req) < 0) { daemon_log(LOG_ERR, "ioctl(SIOCGIWMODE): %s", strerror(errno)); return -1; } *m = req.u.mode; return 0; } /* int iw_get_freq(struct interface *i, struct iw_freq *f) { */ /* struct iwreq req; */ /* assert(i && f); */ /* memset(&req, 0, sizeof(req)); */ /* strncpy(req.ifr_ifrn.ifrn_name, i->name, IFNAMSIZ); */ /* if (ioctl(i->fd, SIOCGIWFREQ, &req) < 0) { */ /* memset(f, 0, sizeof(struct iw_freq)); /\* hostap 0.1.2 fails to report the freq sometimes *\/ */ /* return 0; */ /* } */ /* *f = req.u.freq; */ /* return 0; */ /* } */ int iw_assoc(struct interface *i, struct ap_info *ap) { struct hw_addr hw; struct iwreq req; struct iw_statistics q; static struct iw_range range; int m; assert(i); if (iw_get_mode(i, &m) < 0) return -1; if (m != IW_MODE_INFRA) return 0; if (iw_get_ap(i, &hw) < 0) return -1; if (!is_assoc_ap(&hw)) return 0; memset(&req, 0, sizeof(req)); strncpy(req.ifr_ifrn.ifrn_name, i->name, IFNAMSIZ); req.u.data.pointer = (caddr_t) &q; req.u.data.length = sizeof(q); req.u.data.flags = 1; if (ioctl(i->fd, SIOCGIWSTATS, &req) < 0) { daemon_log(LOG_ERR, "Failed to get interface quality"); return -1; } memset(&req, 0, sizeof(req)); strncpy(req.ifr_ifrn.ifrn_name, i->name, 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(i->fd, SIOCGIWRANGE, &req) < 0) { fprintf(stderr, "SIOCGIWRANGE failed: %s", 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; if (ap) { memset(ap, 0, sizeof(struct ap_info)); memcpy(ap->ap.addr, hw.addr, ETH_ALEN); if (iw_get_essid(i, ap->essid) < 0) return -1; /* if (iw_get_freq(i, &ap->freq) < 0) */ /* return -1; */ } return 1; } int iw_ap_info_equal(const struct ap_info *a, const struct ap_info *b) { assert(a && b); return !strcmp(a->essid, b->essid) && !memcmp(&a->ap, &b->ap, sizeof(struct hw_addr)); }