diff options
Diffstat (limited to 'mac80211.c')
-rw-r--r-- | mac80211.c | 223 |
1 files changed, 223 insertions, 0 deletions
diff --git a/mac80211.c b/mac80211.c new file mode 100644 index 0000000..14bcc93 --- /dev/null +++ b/mac80211.c @@ -0,0 +1,223 @@ +/* + * netsniff-ng - the packet sniffing beast + * Copyright 2012 Daniel Borkmann. + * Subject to the GPL, version 2. + * Parts derived from iw, subject to ISC license. + * Copyright 2007, 2008 Johannes Berg + * Copyright 2007 Andy Lutomirski + * Copyright 2007 Mike Kershaw + * Copyright 2008-2009 Luis R. Rodriguez + */ + +#define _GNU_SOURCE +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <limits.h> +#include <linux/nl80211.h> +#include <libnl3/netlink/genl/genl.h> +#include <libnl3/netlink/genl/family.h> +#include <libnl3/netlink/genl/ctrl.h> +#include <libnl3/netlink/msg.h> +#include <libnl3/netlink/attr.h> + +#include "die.h" +#include "xutils.h" +#include "mac80211.h" +#include "xmalloc.h" +#include "built_in.h" + +struct nl80211_state { + struct nl_sock *nl_sock; + struct nl_cache *nl_cache; + struct genl_family *nl80211; +}; + +static void get_mac80211_phydev(const char *device, char *phydev_path, + size_t phydev_len) +{ + int ret; + char *pathstr; + ssize_t num; + + ret = asprintf(&pathstr, "/sys/class/net/%s/phy80211", device); + if (ret < 0) + panic("Can't generate path name string for /sys/class/net device"); + + num = readlink(pathstr, phydev_path, phydev_len); + if (num < 0) { + if (errno == ENOENT || errno == EINVAL) + panic("It's probably not a mac80211 device!\n"); + panic("Can't readlink %s: %s!\n", pathstr, strerror(errno)); + } + + xfree(pathstr); + phydev_path[min(num, phydev_len - 1)] = 0; +} + +static inline struct nl_msg *nl80211_nlmsg_xalloc(void) +{ + struct nl_msg *ret = nlmsg_alloc(); + if (!ret) + panic("Cannot allocate nlmsg memory!\n"); + return ret; +} + +static inline struct nl_sock *nl80211_nl_socket_xalloc(void) +{ + struct nl_sock *ret = nl_socket_alloc(); + if (!ret) + panic("Cannot allocate nl socket memory!\n"); + return ret; +} + +static void nl80211_init(struct nl80211_state *state, const char *device) +{ + int ret; + + state->nl_sock = nl80211_nl_socket_xalloc(); + + ret = genl_connect(state->nl_sock); + if (ret) + panic("Cannot connect generic netlink!\n"); + + ret = genl_ctrl_alloc_cache(state->nl_sock, &state->nl_cache); + if (ret < 0) + panic("Failed to allocate generic netlink cache: %s!", + nl_geterror(-ret)); + + state->nl80211 = genl_ctrl_search_by_name(state->nl_cache, "nl80211"); + if (!state->nl80211) + panic("nl80211 not found in netlink cache!\n"); +} + +static void nl80211_cleanup(struct nl80211_state *state) +{ + genl_family_put(state->nl80211); + + nl_cache_free(state->nl_cache); + nl_socket_free(state->nl_sock); +} + +static int nl80211_add_mon_if(struct nl80211_state *state, const char *device, + const char *mondevice) +{ + int ifindex, ret; + struct nl_msg *msg; + + ifindex = device_ifindex(device); + + msg = nl80211_nlmsg_xalloc(); + + genlmsg_put(msg, 0, 0, genl_family_get_id(state->nl80211), 0, + 0, NL80211_CMD_NEW_INTERFACE, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); + NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, mondevice); + NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_MONITOR); + + ret = nl_send_auto_complete(state->nl_sock, msg); + if (ret < 0) { + if (ret == -ENFILE) { + nlmsg_free(msg); + return -EBUSY; + } + + panic("Cannot send_auto_complete!\n"); + } + + ret = nl_wait_for_ack(state->nl_sock); + if (ret < 0) { + if (ret == -ENFILE) { + nlmsg_free(msg); + return -EBUSY; + } + + panic("Waiting for netlink ack failed!\n"); + } + + nlmsg_free(msg); + return 0; + +nla_put_failure: + panic("nla put failure!\n"); + return -EIO; /* dummy */ +} + +static int nl80211_del_mon_if(struct nl80211_state *state, const char *device, + const char *mondevice) +{ + int ifindex, ret; + struct nl_msg *msg; + + ifindex = device_ifindex(mondevice); + + msg = nl80211_nlmsg_xalloc(); + + genlmsg_put(msg, 0, 0, genl_family_get_id(state->nl80211), 0, + 0, NL80211_CMD_DEL_INTERFACE, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); + + ret = nl_send_auto_complete(state->nl_sock, msg); + if (ret < 0) + panic("Cannot send_auto_complete!\n"); + + ret = nl_wait_for_ack(state->nl_sock); + if (ret < 0) + panic("Waiting for netlink ack failed!\n"); + + nlmsg_free(msg); + return 0; + +nla_put_failure: + panic("nla put failure!\n"); + return -EIO; /* dummy */ +} + +void enter_rfmon_mac80211(const char *device, char **mondev) +{ + int ret; + short flags; + uint32_t n; + char phydev_path[256]; + struct nl80211_state nlstate; + + /* XXX: is this already a monN device? */ + get_mac80211_phydev(device, phydev_path, sizeof(phydev_path)); + nl80211_init(&nlstate, device); + + for (n = 0; n < UINT_MAX; n++) { + char mondevice[32]; + + slprintf(mondevice, sizeof(mondevice), "mon%u", n); + ret = nl80211_add_mon_if(&nlstate, device, mondevice); + if (ret == 0) { + *mondev = xstrdup(mondevice); + + flags = device_get_flags(*mondev); + flags |= IFF_UP | IFF_RUNNING; + device_set_flags(*mondev, flags); + + nl80211_cleanup(&nlstate); + return; + } + } + + panic("No free monN interfaces!\n"); +} + +void leave_rfmon_mac80211(const char *device, const char *mondev) +{ + short flags; + struct nl80211_state nlstate; + + flags = device_get_flags(mondev); + flags &= ~(IFF_UP | IFF_RUNNING); + device_set_flags(mondev, flags); + + nl80211_init(&nlstate, device); + nl80211_del_mon_if(&nlstate, device, mondev); + nl80211_cleanup(&nlstate); +} |