#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;
}
|