#include <stdio.h> #include <string.h> #include <stdlib.h> #include <errno.h> #include <sys/socket.h> #include <linux/netlink.h> #include <linux/rtnetlink.h> #include <linux/if.h> #include <linux/if_ether.h> #include <linux/wireless.h> #include <linux/sockios.h>
#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] #define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" #define luther_printf(msg...) printf("===luther:=== "msg) struct luther_context { int ioctl_sock; int event_sock; char ifname[IFNAMSIZ + 1]; int ifindex; int ifindex2; int has_capability; int we_version_compiled; } luther_context = { .ifname = "eth0", .ifindex = -1, .ifindex2 = -1, };
static void luther_event_rtm_newlink(struct luther_context *ctx, struct nlmsghdr *h, size_t len); static void luther_event_rtm_dellink(struct luther_context *ctx, struct nlmsghdr *h, size_t len); static void luther_wext_event_wireless(struct luther_context *ctx, char *data, int len); static void luther_wext_event_link(struct luther_context *ctx, char *buf, size_t len, int del); static int luther_wext_get_range(struct luther_context *ctx); static unsigned int if_nametoindex(const char *ifname);
int main(int argc, char *argv[]) { struct sockaddr_nl local;
luther_context.ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); if (luther_context.ioctl_sock < 0) { perror("socket(PF_INET,SOCK_DGRAM)"); return 0; }
luther_context.event_sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (luther_context.event_sock < 0) { perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)"); close(luther_context.ioctl_sock); return 0; } memset(&local, 0, sizeof(local)); local.nl_family = AF_NETLINK; local.nl_groups = RTMGRP_LINK;
if (bind(luther_context.event_sock, (struct sockaddr *) &local, sizeof(local)) < 0) { perror("bind(netlink)"); close(luther_context.ioctl_sock); close(luther_context.event_sock); return 0; }
if (argc == 2) { strcpy(luther_context.ifname, argv[1]); }
if( luther_wext_get_range(&luther_context) < 0) { close(luther_context.ioctl_sock); close(luther_context.event_sock); return 0; } luther_context.ifindex = luther_context.ifname[0] ? if_nametoindex(luther_context.ifname) : -1;
do { char buf[8192]; int left; struct sockaddr_nl from; socklen_t fromlen; struct nlmsghdr *h; int sock = luther_context.event_sock; struct luther_context *ctx = &luther_context; int max_events = 10; try_again: fromlen = sizeof(from); left = recvfrom(sock, buf, sizeof(buf), 0/* MSG_DONTWAIT */, (struct sockaddr *) &from, &fromlen); if (left < 0) { if (errno != EINTR && errno != EAGAIN) perror("recvfrom(netlink)"); return 0; }
h = (struct nlmsghdr *) buf; while (left >= (int) sizeof(*h)) { int len, plen;
len = h->nlmsg_len; plen = len - sizeof(*h); if (len > left || plen < 0) { luther_printf("Malformed netlink message: " rtnetlin "len=%d left=%d plen=%d/n", len, left, plen); break; }
switch (h->nlmsg_type) { case RTM_NEWLINK: luther_event_rtm_newlink(ctx, h, plen); break; case RTM_DELLINK: luther_event_rtm_dellink(ctx, h, plen); break; }
len = NLMSG_ALIGN(len); left -= len; h = (struct nlmsghdr *) ((char *) h + len); }
if (left > 0) { luther_printf("%d extra bytes in the end of netlink " "message/n", left); }
if (--max_events > 0) { /* * Try to receive all events in one eloop call in order to * limit race condition on cases where AssocInfo event, Assoc * event, and EAPOL frames are received more or less at the * same time. We want to process the event messages first * before starting EAPOL processing. */ goto try_again; } goto try_again; } while (0) ; }
static void luther_event_rtm_newlink(struct luther_context *ctx, struct nlmsghdr *h, size_t len) { struct ifinfomsg *ifi; int attrlen, nlmsg_len, rta_len; struct rtattr * attr;
if (len < sizeof(*ifi)) return;
ifi = NLMSG_DATA(h);
if ((ctx->ifindex != ifi->ifi_index && ctx->ifindex2 != ifi->ifi_index) && (ctx->ifindex != -1)) { luther_printf("Ignore event for foreign ifindex %d/n", ifi->ifi_index); return; } /* luther_printf("RTM_NEWLINK: ifi_flags=0x%x " "(%s%s)/n", ifi->ifi_flags, (ifi->ifi_flags & IFF_UP) ? "[UP]" : "", (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : ""); */
nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg));
attrlen = h->nlmsg_len - nlmsg_len; if (attrlen < 0) return;
attr = (struct rtattr *) (((char *) ifi) + nlmsg_len);
rta_len = RTA_ALIGN(sizeof(struct rtattr)); while (RTA_OK(attr, attrlen)) { if (attr->rta_type == IFLA_WIRELESS) { luther_wext_event_wireless( ctx, ((char *) attr) + rta_len, attr->rta_len - rta_len); } else if (attr->rta_type == IFLA_IFNAME) { luther_wext_event_link(ctx, ((char *) attr) + rta_len, attr->rta_len - rta_len, 0); } attr = RTA_NEXT(attr, attrlen); } }
static void luther_event_rtm_dellink(struct luther_context *ctx, struct nlmsghdr *h, size_t len) { struct ifinfomsg *ifi; int attrlen, nlmsg_len, rta_len; struct rtattr * attr;
if (len < sizeof(*ifi)) return;
ifi = NLMSG_DATA(h);
nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg));
attrlen = h->nlmsg_len - nlmsg_len; if (attrlen < 0) return;
attr = (struct rtattr *) (((char *) ifi) + nlmsg_len);
rta_len = RTA_ALIGN(sizeof(struct rtattr)); while (RTA_OK(attr, attrlen)) { if (attr->rta_type == IFLA_IFNAME) { luther_wext_event_link(ctx, ((char *) attr) + rta_len, attr->rta_len - rta_len, 1); } attr = RTA_NEXT(attr, attrlen); } }
static void luther_wext_event_wireless(struct luther_context *ctx, char *data, int len) { struct iw_event iwe_buf, *iwe = &iwe_buf; char *pos, *end, *custom, *buf;
pos = data; end = data + len;
while (pos + IW_EV_LCP_LEN <= end) { /* Event data may be unaligned, so make a local, aligned copy * before processing. */ memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); luther_printf("event: cmd=0x%x len=%d/n", iwe->cmd, iwe->len); if (iwe->len <= IW_EV_LCP_LEN) return;
custom = pos + IW_EV_POINT_LEN; if (ctx->we_version_compiled > 18 && (iwe->cmd == IWEVMICHAELMICFAILURE || iwe->cmd == IWEVCUSTOM || iwe->cmd == IWEVASSOCREQIE || iwe->cmd == IWEVASSOCRESPIE || iwe->cmd == IWEVPMKIDCAND)) { /* WE-19 removed the pointer from struct iw_point */ char *dpos = (char *) &iwe_buf.u.data.length; int dlen = dpos - (char *) &iwe_buf; memcpy(dpos, pos + IW_EV_LCP_LEN, sizeof(struct iw_event) - dlen); } else { memcpy(&iwe_buf, pos, sizeof(struct iw_event)); custom += IW_EV_POINT_OFF; }
switch (iwe->cmd) { case SIOCGIWAP: luther_printf("event: new AP: " MACSTR, MAC2STR((unsigned char *) iwe->u.ap_addr.sa_data)); if (memcmp(iwe->u.ap_addr.sa_data, "/x00/x00/x00/x00/x00/x00", ETH_ALEN) == 0 || memcmp(iwe->u.ap_addr.sa_data, "/x44/x44/x44/x44/x44/x44", ETH_ALEN) == 0) { luther_printf("event : Disconnect/n"); } else { luther_printf("event : Associated to a new BSS/n"); } break; case IWEVMICHAELMICFAILURE: luther_printf("event : Michael MIC failure wireless/n"); break; case IWEVCUSTOM: if (custom + iwe->u.data.length > end) return; buf = malloc(iwe->u.data.length + 1); if (buf == NULL) return; memcpy(buf, custom, iwe->u.data.length); buf[iwe->u.data.length] = '/0'; luther_printf("event : custom -> %s/n", buf); free(buf); break; case SIOCGIWSCAN: luther_printf("event : scan_results/n"); break; case IWEVASSOCREQIE: luther_printf("event : AssocReq IE wireless/n"); break; case IWEVASSOCRESPIE: luther_printf("event : AssocResp IE wireless/n"); break; case IWEVPMKIDCAND: luther_printf("event : PMKID candidate wireless/n"); break; }
pos += iwe->len; } }
static void luther_wext_event_link(struct luther_context *ctx, char *buf, size_t len, int del) { luther_printf("RTM_%sLINK, IFLA_IFNAME: Interface '%s' %s/n", del ? "DEL" : "NEW", buf, rtnetlin del ? "removed" : "added"); }
static int luther_wext_get_range(struct luther_context *ctx) { struct iw_range *range; struct iwreq iwr; int minlen; size_t buflen;
/* * Use larger buffer than struct iw_range in order to allow the * structure to grow in the future. */ buflen = sizeof(struct iw_range) + 500; range = calloc(buflen, 1); if (range == NULL) return -1;
memset(&iwr, 0, sizeof(iwr)); strncpy(iwr.ifr_name, ctx->ifname, IFNAMSIZ); iwr.u.data.pointer = (caddr_t) range; iwr.u.data.length = buflen;
minlen = ((char *) &range->enc_capa) - (char *) range + sizeof(range->enc_capa);
if (ioctl(ctx->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) { perror("ioctl[SIOCGIWRANGE]"); free(range); return -1; } else if (iwr.u.data.length >= minlen && range->we_version_compiled >= 18) { /* luther_printf("SIOCGIWRANGE: WE(compiled)=%d " "WE(source)=%d enc_capa=0x%x/n", range->we_version_compiled, range->we_version_source, range->enc_capa); */ ctx->has_capability = 1; ctx->we_version_compiled = range->we_version_compiled; } else { luther_printf("SIOCGIWRANGE: too old (short) data - " "assuming WPA is not supported/n"); }
free(range);
return 0; }
static unsigned int if_nametoindex(const char *ifname) { struct ifreq ifr; int fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0) return 0;
strncpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name)); ifr.ifr_ifindex = 0; if (ioctl (fd, SIOCGIFINDEX, &ifr) < 0) { return 0; } return ifr.ifr_ifindex; }
|