Add support for mesh interfaces

On interface creation, if interface type is mesh point (mp or mesh), a mesh_id
can be specified.

Mesh paths and stations (including non-mesh stations) can be dumped and manipulated.

You can find some usage examples at:

http://o11s.org/trac/wiki/HOWTO-0.2.1#Testing
http://o11s.org/trac/wiki/HOWTO-0.2.1#AdvancedTinkering
diff --git a/Makefile b/Makefile
index 5e620a5..82e1a25 100644
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@
 CFLAGS += -Wall -I/lib/modules/`uname -r`/build/include -g
 LDFLAGS += -lnl
 
-OBJS = iw.o interface.o info.o
+OBJS = iw.o interface.o info.o station.o util.o mpath.o
 ALL = iw
 
 all: verify_config $(ALL)
diff --git a/interface.c b/interface.c
index efc2d93..3ef9b18 100644
--- a/interface.c
+++ b/interface.c
@@ -45,6 +45,10 @@
 	} else if (strcmp(tpstr, "station") == 0) {
 		*type = NL80211_IFTYPE_STATION;
 		return 1;
+	} else if (strcmp(tpstr, "mp") == 0 ||
+			strcmp(tpstr, "mesh") == 0) {
+		*type = NL80211_IFTYPE_MESH_POINT;
+		return 1;
 	}
 
 
@@ -56,6 +60,7 @@
 				char *phy, char *dev, int argc, char **argv)
 {
 	char *name;
+	char *mesh_id = NULL;
 	enum nl80211_iftype type;
 	int tpset, err;
 	struct nl_msg *msg;
@@ -76,6 +81,23 @@
 		return -1;
 
 	if (argc) {
+		if (strcmp(argv[0], "mesh_id") != 0) {
+			fprintf(stderr, "option %s not supported\n", argv[0]);
+			return -1;
+		}
+		argc--;
+		argv++;
+
+		if (!argc) {
+			fprintf(stderr, "not enough arguments\n");
+			return -1;
+		}
+		mesh_id = argv[0];
+		argc--;
+		argv++;
+	}
+
+	if (argc) {
 		fprintf(stderr, "too many arguments\n");
 		return -1;
 	}
@@ -95,6 +117,8 @@
 	NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, name);
 	if (tpset)
 		NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, type);
+	if (mesh_id)
+		NLA_PUT(msg, NL80211_ATTR_MESH_ID, strlen(mesh_id), mesh_id);
 
 	if ((err = nl_send_auto_complete(state->nl_handle, msg)) < 0 ||
 	    (err = nl_wait_for_ack(state->nl_handle)) < 0) {
diff --git a/iw.c b/iw.c
index bff5f4b..3811f6d 100644
--- a/iw.c
+++ b/iw.c
@@ -90,13 +90,12 @@
 
 void usage(char *argv0)
 {
-	fprintf(stderr, "Usage:	%1$s dev <phydev> interface <COMMAND> [OPTIONS]\n"
-			"	%1$s dev <phydev> info\n"
+	fprintf(stderr, "Usage:	%1$s dev <phydev> <OBJECT> <COMMAND> [OPTIONS]"
+			"\n	%1$s dev <phydev> info\n"
 			"\n"
-			"where COMMAND := { add | del }\n"
-			"\n"
-			"For add, OPTIONS := <name> type <type>\n"
-			"For del, OPTIONS should be blank and phydev is the interface to delete.\n", argv0);
+			"where OBJECT := { interface | station | mpath }\n"
+			"and COMMAND := { add | del | set | get | dump }\n",
+			argv0);
 }
 
 int main(int argc, char **argv)
@@ -142,6 +141,10 @@
 		err = handle_interface(&nlstate, phyname, ifname, argc, argv);
 	else if (strcmp(type, "info") == 0)
 		err = handle_info(&nlstate, phyname, ifname);
+	else if (strcmp(type, "station") == 0)
+		err = handle_station(&nlstate, ifname, argc, argv);
+	else if (strcmp(type, "mpath") == 0)
+		err = handle_mpath(&nlstate, ifname, argc, argv);
 	else {
 		fprintf(stderr, "No such object type %s\n", type);
 		err = 1;
diff --git a/iw.h b/iw.h
index 3fad652..0ece3af 100644
--- a/iw.h
+++ b/iw.h
@@ -5,6 +5,8 @@
 #include <netlink/genl/family.h>
 #include <netlink/genl/ctrl.h>
 
+#define ETH_ALEN 6
+
 struct nl80211_state {
 	struct nl_handle *nl_handle;
 	struct nl_cache *nl_cache;
@@ -17,4 +19,13 @@
 
 int handle_info(struct nl80211_state *state, char *phy, char *dev);
 
+int handle_station(struct nl80211_state *state,
+		   char *dev, int argc, char **argv);
+
+int handle_mpath(struct nl80211_state *state,
+		   char *dev, int argc, char **argv);
+
+int mac_addr_a2n(unsigned char *mac_addr, char *arg);
+int mac_addr_n2a(char *mac_addr, unsigned char *arg);
+
 #endif /* __IW_H */
diff --git a/mpath.c b/mpath.c
new file mode 100644
index 0000000..fab8701
--- /dev/null
+++ b/mpath.c
@@ -0,0 +1,390 @@
+#include <errno.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/family.h>
+#include <netlink/genl/ctrl.h>
+#include <netlink/msg.h>
+#include <netlink/attr.h>
+#include <linux/nl80211.h>
+
+#include <net/if.h>
+
+#include "iw.h"
+
+enum plink_state {
+	LISTEN,
+	OPN_SNT,
+	OPN_RCVD,
+	CNF_RCVD,
+	ESTAB,
+	HOLDING,
+	BLOCKED
+};
+
+enum plink_actions {
+	PLINK_ACTION_UNDEFINED,
+	PLINK_ACTION_OPEN,
+	PLINK_ACTION_BLOCK,
+};
+
+
+static int wait_handler(struct nl_msg *msg, void *arg)
+{
+	int *finished = arg;
+
+	*finished = 1;
+	return NL_STOP;
+}
+
+static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
+			 void *arg)
+{
+	fprintf(stderr, "nl80211 error %d\n", err->error);
+	exit(err->error);
+}
+
+static int print_mpath_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct nlattr *pinfo[NL80211_MPATH_INFO_MAX + 1];
+	char dst[20], next_hop[20], dev[20];
+	static struct nla_policy mpath_policy[NL80211_MPATH_INFO_MAX + 1] = {
+		[NL80211_MPATH_INFO_FRAME_QLEN] = { .type = NLA_U32 },
+		[NL80211_MPATH_INFO_DSN] = { .type = NLA_U32 },
+		[NL80211_MPATH_INFO_METRIC] = { .type = NLA_U32 },
+		[NL80211_MPATH_INFO_EXPTIME] = { .type = NLA_U32 },
+		[NL80211_MPATH_INFO_DISCOVERY_TIMEOUT] = { .type = NLA_U32 },
+		[NL80211_MPATH_INFO_DISCOVERY_RETRIES] = { .type = NLA_U8 },
+		[NL80211_MPATH_INFO_FLAGS] = { .type = NLA_U8 },
+	};
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	/*
+	 * TODO: validate the interface and mac address!
+	 * Otherwise, there's a race condition as soon as
+	 * the kernel starts sending mpath notifications.
+	 */
+
+	if (!tb[NL80211_ATTR_MPATH_INFO]) {
+		fprintf(stderr, "mpath info missing!");
+		return NL_SKIP;
+	}
+	if (nla_parse_nested(pinfo, NL80211_MPATH_INFO_MAX,
+			     tb[NL80211_ATTR_MPATH_INFO],
+			     mpath_policy)) {
+		fprintf(stderr, "failed to parse nested attributes!");
+		return NL_SKIP;
+	}
+
+	mac_addr_n2a(dst, nla_data(tb[NL80211_ATTR_MAC]));
+	mac_addr_n2a(next_hop, nla_data(tb[NL80211_ATTR_MPATH_NEXT_HOP]));
+	if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), dev);
+	printf("%s %s %s", dst, next_hop, dev);
+	if (pinfo[NL80211_MPATH_INFO_DSN])
+		printf("\t%u",
+			nla_get_u32(pinfo[NL80211_MPATH_INFO_DSN]));
+	if (pinfo[NL80211_MPATH_INFO_METRIC])
+		printf("\t%u",
+			nla_get_u32(pinfo[NL80211_MPATH_INFO_METRIC]));
+	if (pinfo[NL80211_MPATH_INFO_FRAME_QLEN])
+		printf("\t%u",
+			nla_get_u32(pinfo[NL80211_MPATH_INFO_FRAME_QLEN]));
+	if (pinfo[NL80211_MPATH_INFO_EXPTIME])
+		printf("\t%u",
+			nla_get_u32(pinfo[NL80211_MPATH_INFO_EXPTIME]));
+	if (pinfo[NL80211_MPATH_INFO_DISCOVERY_TIMEOUT])
+		printf("\t%u",
+		nla_get_u32(pinfo[NL80211_MPATH_INFO_DISCOVERY_TIMEOUT]));
+	if (pinfo[NL80211_MPATH_INFO_DISCOVERY_RETRIES])
+		printf("\t%u",
+		nla_get_u8(pinfo[NL80211_MPATH_INFO_DISCOVERY_RETRIES]));
+	if (pinfo[NL80211_MPATH_INFO_FLAGS])
+		printf("\t0x%x",
+			nla_get_u8(pinfo[NL80211_MPATH_INFO_FLAGS]));
+
+	printf("\n");
+	return NL_SKIP;
+}
+
+static int handle_mpath_get(struct nl80211_state *state,
+				char *dev, int argc, char **argv)
+{
+	struct nl_msg *msg;
+	struct nl_cb *cb = NULL;
+	int ret = -1;
+	int err;
+	int finished = 0;
+	unsigned char dst[ETH_ALEN];
+
+	if (argc < 1) {
+		fprintf(stderr, "not enough arguments\n");
+		return -1;
+	}
+
+	if (mac_addr_a2n(dst, argv[0])) {
+		fprintf(stderr, "invalid mac address\n");
+		return -1;
+	}
+	argc--;
+	argv++;
+
+	if (argc) {
+		fprintf(stderr, "too many arguments\n");
+		return -1;
+	}
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		goto out;
+
+	genlmsg_put(msg, 0, 0, genl_family_get_id(state->nl80211), 0,
+		    0, NL80211_CMD_GET_MPATH, 0);
+
+	NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst);
+	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(dev));
+
+	cb = nl_cb_alloc(NL_CB_CUSTOM);
+	if (!cb)
+		goto out;
+
+	if (nl_send_auto_complete(state->nl_handle, msg) < 0)
+		goto out;
+
+	nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_mpath_handler, NULL);
+	nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, wait_handler, &finished);
+	nl_cb_err(cb, NL_CB_CUSTOM, error_handler, NULL);
+
+	err = nl_recvmsgs(state->nl_handle, cb);
+
+	if (!finished)
+		err = nl_wait_for_ack(state->nl_handle);
+
+	if (err < 0)
+		goto out;
+
+	ret = 0;
+
+ out:
+	nl_cb_put(cb);
+ nla_put_failure:
+	nlmsg_free(msg);
+	return ret;
+}
+
+static int handle_mpath_set(struct nl80211_state *state, int new,
+				char *dev, int argc, char **argv)
+{
+	struct nl_msg *msg;
+	struct nl_cb *cb = NULL;
+	int ret = -1;
+	int err, command;
+	int finished = 0;
+	unsigned char dst[ETH_ALEN];
+	unsigned char next_hop[ETH_ALEN];
+
+	if (argc < 3) {
+		fprintf(stderr, "not enough arguments\n");
+		return -1;
+	}
+
+	if (mac_addr_a2n(dst, argv[0])) {
+		fprintf(stderr, "invalid destination mac address\n");
+		return -1;
+	}
+	argc--;
+	argv++;
+
+	if (strcmp("next_hop", argv[0]) != 0) {
+		fprintf(stderr, "parameter not supported\n");
+		return -1;
+	}
+	argc--;
+	argv++;
+
+	if (mac_addr_a2n(next_hop, argv[0])) {
+		fprintf(stderr, "invalid next hop mac address\n");
+		return -1;
+	}
+	argc--;
+	argv++;
+
+	if (argc) {
+		fprintf(stderr, "too many arguments\n");
+		return -1;
+	}
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		goto out;
+
+	command = new ? NL80211_CMD_NEW_MPATH : NL80211_CMD_SET_MPATH;
+	genlmsg_put(msg, 0, 0, genl_family_get_id(state->nl80211), 0, 0,
+		    command, 0);
+
+	NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst);
+	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(dev));
+	NLA_PUT(msg, NL80211_ATTR_MPATH_NEXT_HOP, ETH_ALEN, next_hop);
+
+	cb = nl_cb_alloc(NL_CB_CUSTOM);
+	if (!cb)
+		goto out;
+
+	if (nl_send_auto_complete(state->nl_handle, msg) < 0)
+		goto out;
+
+	nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_mpath_handler, NULL);
+	nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, wait_handler, &finished);
+	nl_cb_err(cb, NL_CB_CUSTOM, error_handler, NULL);
+
+	err = nl_recvmsgs(state->nl_handle, cb);
+
+	if (!finished)
+		err = nl_wait_for_ack(state->nl_handle);
+
+	if (err < 0)
+		goto out;
+
+	ret = 0;
+
+ out:
+	nl_cb_put(cb);
+ nla_put_failure:
+	nlmsg_free(msg);
+	return ret;
+}
+
+static int handle_mpath_del(struct nl80211_state *state,
+				char *dev, int argc, char **argv)
+{
+	struct nl_msg *msg;
+	struct nl_cb *cb = NULL;
+	int ret = -1;
+	int err;
+	int finished = 0;
+	unsigned char dst[ETH_ALEN];
+
+	if (argc > 1) {
+		fprintf(stderr, "too many arguments\n");
+		return -1;
+	}
+
+	if (argc && mac_addr_a2n(dst, argv[0])) {
+		fprintf(stderr, "invalid mac address\n");
+		return -1;
+	}
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		goto out;
+
+	genlmsg_put(msg, 0, 0, genl_family_get_id(state->nl80211), 0, 0,
+		    NL80211_CMD_DEL_MPATH, 0);
+
+	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(dev));
+	if (argc)
+		NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst);
+
+	cb = nl_cb_alloc(NL_CB_CUSTOM);
+	if (!cb)
+		goto out;
+
+	if (nl_send_auto_complete(state->nl_handle, msg) < 0)
+		goto out;
+
+	nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_mpath_handler, NULL);
+	nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, wait_handler, &finished);
+	nl_cb_err(cb, NL_CB_CUSTOM, error_handler, NULL);
+
+	err = nl_recvmsgs(state->nl_handle, cb);
+
+	if (!finished)
+		err = nl_wait_for_ack(state->nl_handle);
+
+	if (err < 0)
+		goto out;
+
+	ret = 0;
+
+ out:
+	nl_cb_put(cb);
+ nla_put_failure:
+	nlmsg_free(msg);
+	return ret;
+}
+
+static int handle_mpath_dump(struct nl80211_state *state,
+				char *dev, int argc, char **argv)
+{
+	struct nl_msg *msg;
+	struct nl_cb *cb = NULL;
+	int ret = -1;
+	int err;
+	int finished = 0;
+
+	if (argc) {
+		fprintf(stderr, "too many arguments\n");
+		return -1;
+	}
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		goto out;
+
+	genlmsg_put(msg, 0, 0, genl_family_get_id(state->nl80211), 0,
+		    NLM_F_DUMP, NL80211_CMD_GET_MPATH, 0);
+
+	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(dev));
+
+	cb = nl_cb_alloc(NL_CB_CUSTOM);
+	if (!cb)
+		goto out;
+
+	if (nl_send_auto_complete(state->nl_handle, msg) < 0)
+		goto out;
+
+	nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_mpath_handler, NULL);
+	nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, wait_handler, &finished);
+
+	err = nl_recvmsgs(state->nl_handle, cb);
+
+	if (err < 0)
+		goto out;
+
+	ret = 0;
+
+ out:
+	nl_cb_put(cb);
+ nla_put_failure:
+	nlmsg_free(msg);
+	return ret;
+}
+
+int handle_mpath(struct nl80211_state *state,
+		   char *dev, int argc, char **argv)
+{
+	char *cmd = argv[0];
+
+	if (argc < 1) {
+		fprintf(stderr, "you must specify an mpath command\n");
+		return -1;
+	}
+
+	argc--;
+	argv++;
+
+	if (strcmp(cmd, "new") == 0)
+		return handle_mpath_set(state, 1, dev, argc, argv);
+	if (strcmp(cmd, "del") == 0)
+		return handle_mpath_del(state, dev, argc, argv);
+	if (strcmp(cmd, "get") == 0)
+		return handle_mpath_get(state, dev, argc, argv);
+	if (strcmp(cmd, "set") == 0)
+		return handle_mpath_set(state, 0, dev, argc, argv);
+	if (strcmp(cmd, "dump") == 0)
+		return handle_mpath_dump(state, dev, argc, argv);
+
+	printf("invalid interface command %s\n", cmd);
+	return -1;
+}
diff --git a/station.c b/station.c
new file mode 100644
index 0000000..6393a84
--- /dev/null
+++ b/station.c
@@ -0,0 +1,413 @@
+#include <errno.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/family.h>
+#include <netlink/genl/ctrl.h>
+#include <netlink/msg.h>
+#include <netlink/attr.h>
+#include <linux/nl80211.h>
+
+#include <net/if.h>
+
+#include "iw.h"
+
+enum plink_state {
+	LISTEN,
+	OPN_SNT,
+	OPN_RCVD,
+	CNF_RCVD,
+	ESTAB,
+	HOLDING,
+	BLOCKED
+};
+
+enum plink_actions {
+	PLINK_ACTION_UNDEFINED,
+	PLINK_ACTION_OPEN,
+	PLINK_ACTION_BLOCK,
+};
+
+
+static int wait_handler(struct nl_msg *msg, void *arg)
+{
+	int *finished = arg;
+
+	*finished = 1;
+	return NL_STOP;
+}
+
+static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
+			 void *arg)
+{
+	fprintf(stderr, "nl80211 error %d\n", err->error);
+	exit(err->error);
+}
+
+static int print_sta_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1];
+	char mac_addr[20], state_name[10], dev[20];
+	static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = {
+		[NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 },
+		[NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 },
+		[NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 },
+		[NL80211_STA_INFO_LLID] = { .type = NLA_U16 },
+		[NL80211_STA_INFO_PLID] = { .type = NLA_U16 },
+		[NL80211_STA_INFO_PLINK_STATE] = { .type = NLA_U8 },
+	};
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	/*
+	 * TODO: validate the interface and mac address!
+	 * Otherwise, there's a race condition as soon as
+	 * the kernel starts sending station notifications.
+	 */
+
+	if (!tb[NL80211_ATTR_STA_INFO]) {
+		fprintf(stderr, "sta stats missing!");
+		return NL_SKIP;
+	}
+	if (nla_parse_nested(sinfo, NL80211_STA_INFO_MAX,
+			     tb[NL80211_ATTR_STA_INFO],
+			     stats_policy)) {
+		fprintf(stderr, "failed to parse nested attributes!");
+		return NL_SKIP;
+	}
+
+	mac_addr_n2a(mac_addr, nla_data(tb[NL80211_ATTR_MAC]));
+	if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), dev);
+	printf("%s %s", mac_addr, dev);
+
+	if (sinfo[NL80211_STA_INFO_INACTIVE_TIME])
+		printf("\t%d",
+			nla_get_u32(sinfo[NL80211_STA_INFO_INACTIVE_TIME]));
+	if (sinfo[NL80211_STA_INFO_RX_BYTES])
+		printf("\t%d",
+			nla_get_u32(sinfo[NL80211_STA_INFO_RX_BYTES]));
+	if (sinfo[NL80211_STA_INFO_TX_BYTES])
+		printf("\t%d",
+			nla_get_u32(sinfo[NL80211_STA_INFO_TX_BYTES]));
+	if (sinfo[NL80211_STA_INFO_LLID])
+		printf("\t%d",
+			nla_get_u16(sinfo[NL80211_STA_INFO_LLID]));
+	if (sinfo[NL80211_STA_INFO_PLID])
+		printf("\t%d",
+			nla_get_u16(sinfo[NL80211_STA_INFO_PLID]));
+	if (sinfo[NL80211_STA_INFO_PLINK_STATE]) {
+		switch (nla_get_u16(sinfo[NL80211_STA_INFO_PLINK_STATE])) {
+		case LISTEN:
+			strcpy(state_name, "LISTEN");
+			break;
+		case OPN_SNT:
+			strcpy(state_name, "OPN_SNT");
+			break;
+		case OPN_RCVD:
+			strcpy(state_name, "OPN_RCVD");
+			break;
+		case CNF_RCVD:
+			strcpy(state_name, "CNF_RCVD");
+			break;
+		case ESTAB:
+			strcpy(state_name, "ESTAB");
+			break;
+		case HOLDING:
+			strcpy(state_name, "HOLDING");
+			break;
+		case BLOCKED:
+			strcpy(state_name, "BLOCKED");
+			break;
+		default:
+			strcpy(state_name, "UNKNOWN");
+			break;
+		}
+		printf("\t%s", state_name);
+	}
+
+	printf("\n");
+	return NL_SKIP;
+}
+
+static int handle_station_get(struct nl80211_state *state,
+				char *dev, int argc, char **argv)
+{
+	struct nl_msg *msg;
+	struct nl_cb *cb = NULL;
+	int ret = -1;
+	int err;
+	int finished = 0;
+	unsigned char mac_addr[ETH_ALEN];
+
+	if (argc < 1) {
+		fprintf(stderr, "not enough arguments\n");
+		return -1;
+	}
+
+	if (mac_addr_a2n(mac_addr, argv[0])) {
+		fprintf(stderr, "invalid mac address\n");
+		return -1;
+	}
+
+	argc--;
+	argv++;
+
+	if (argc) {
+		fprintf(stderr, "too many arguments\n");
+		return -1;
+	}
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		goto out;
+
+	genlmsg_put(msg, 0, 0, genl_family_get_id(state->nl80211), 0,
+		    0, NL80211_CMD_GET_STATION, 0);
+
+	NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
+	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(dev));
+
+	cb = nl_cb_alloc(NL_CB_CUSTOM);
+	if (!cb)
+		goto out;
+
+	if (nl_send_auto_complete(state->nl_handle, msg) < 0)
+		goto out;
+
+	nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_sta_handler, NULL);
+	nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, wait_handler, &finished);
+	nl_cb_err(cb, NL_CB_CUSTOM, error_handler, NULL);
+
+	err = nl_recvmsgs(state->nl_handle, cb);
+
+	if (!finished)
+		err = nl_wait_for_ack(state->nl_handle);
+
+	if (err < 0)
+		goto out;
+
+	ret = 0;
+
+ out:
+	nl_cb_put(cb);
+ nla_put_failure:
+	nlmsg_free(msg);
+	return ret;
+}
+
+static int handle_station_set(struct nl80211_state *state,
+				char *dev, int argc, char **argv)
+{
+	struct nl_msg *msg;
+	struct nl_cb *cb = NULL;
+	int ret = -1;
+	int err;
+	int finished = 0;
+	unsigned char plink_action;
+	unsigned char mac_addr[ETH_ALEN];
+
+	if (argc < 3) {
+		fprintf(stderr, "not enough arguments\n");
+		return -1;
+	}
+
+	if (mac_addr_a2n(mac_addr, argv[0])) {
+		fprintf(stderr, "invalid mac address\n");
+		return -1;
+	}
+	argc--;
+	argv++;
+
+	if (strcmp("plink_action", argv[0]) != 0) {
+		fprintf(stderr, "parameter not supported\n");
+		return -1;
+	}
+	argc--;
+	argv++;
+
+	if (strcmp("open", argv[0]) == 0)
+		plink_action = PLINK_ACTION_OPEN;
+	else if (strcmp("block", argv[0]) == 0)
+		plink_action = PLINK_ACTION_BLOCK;
+	else {
+		fprintf(stderr, "plink action not supported\n");
+		return -1;
+	}
+	argc--;
+	argv++;
+
+	if (argc) {
+		fprintf(stderr, "too many arguments\n");
+		return -1;
+	}
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		goto out;
+
+	genlmsg_put(msg, 0, 0, genl_family_get_id(state->nl80211), 0,
+		    0, NL80211_CMD_SET_STATION, 0);
+
+	NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
+	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(dev));
+	NLA_PUT_U8(msg, NL80211_ATTR_STA_PLINK_ACTION, plink_action);
+
+	cb = nl_cb_alloc(NL_CB_CUSTOM);
+	if (!cb)
+		goto out;
+
+	if (nl_send_auto_complete(state->nl_handle, msg) < 0)
+		goto out;
+
+	nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_sta_handler, NULL);
+	nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, wait_handler, &finished);
+	nl_cb_err(cb, NL_CB_CUSTOM, error_handler, NULL);
+
+	err = nl_recvmsgs(state->nl_handle, cb);
+
+	if (!finished)
+		err = nl_wait_for_ack(state->nl_handle);
+
+	if (err < 0)
+		goto out;
+
+	ret = 0;
+
+ out:
+	nl_cb_put(cb);
+ nla_put_failure:
+	nlmsg_free(msg);
+	return ret;
+}
+static int handle_station_dump(struct nl80211_state *state,
+				char *dev, int argc, char **argv)
+{
+	struct nl_msg *msg;
+	struct nl_cb *cb = NULL;
+	int ret = -1;
+	int err;
+	int finished = 0;
+
+	if (argc) {
+		fprintf(stderr, "too many arguments\n");
+		return -1;
+	}
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		goto out;
+
+	genlmsg_put(msg, 0, 0, genl_family_get_id(state->nl80211), 0,
+		    NLM_F_DUMP, NL80211_CMD_GET_STATION, 0);
+
+	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(dev));
+
+	cb = nl_cb_alloc(NL_CB_CUSTOM);
+	if (!cb)
+		goto out;
+
+	if (nl_send_auto_complete(state->nl_handle, msg) < 0)
+		goto out;
+
+	nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_sta_handler, NULL);
+	nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, wait_handler, &finished);
+
+	err = nl_recvmsgs(state->nl_handle, cb);
+
+	if (err < 0)
+		goto out;
+
+	ret = 0;
+
+ out:
+	nl_cb_put(cb);
+ nla_put_failure:
+	nlmsg_free(msg);
+	return ret;
+}
+
+static int handle_station_del(struct nl80211_state *state,
+				char *dev, int argc, char **argv)
+{
+	struct nl_msg *msg;
+	struct nl_cb *cb = NULL;
+	int ret = -1;
+	int err;
+	int finished = 0;
+	unsigned char mac[ETH_ALEN];
+
+	if (argc > 1) {
+		fprintf(stderr, "too many arguments\n");
+		return -1;
+	}
+
+	if (argc && mac_addr_a2n(mac, argv[0])) {
+		fprintf(stderr, "invalid mac address\n");
+		return -1;
+	}
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		goto out;
+
+	genlmsg_put(msg, 0, 0, genl_family_get_id(state->nl80211), 0, 0,
+		    NL80211_CMD_DEL_STATION, 0);
+
+	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(dev));
+	if (argc)
+		NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac);
+
+	cb = nl_cb_alloc(NL_CB_CUSTOM);
+	if (!cb)
+		goto out;
+
+	if (nl_send_auto_complete(state->nl_handle, msg) < 0)
+		goto out;
+
+	nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_sta_handler, NULL);
+	nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, wait_handler, &finished);
+	nl_cb_err(cb, NL_CB_CUSTOM, error_handler, NULL);
+
+	err = nl_recvmsgs(state->nl_handle, cb);
+
+	if (!finished)
+		err = nl_wait_for_ack(state->nl_handle);
+
+	if (err < 0)
+		goto out;
+
+	ret = 0;
+
+ out:
+	nl_cb_put(cb);
+ nla_put_failure:
+	nlmsg_free(msg);
+	return ret;
+}
+
+int handle_station(struct nl80211_state *state,
+		   char *dev, int argc, char **argv)
+{
+	char *cmd = argv[0];
+
+	if (argc < 1) {
+		fprintf(stderr, "you must specify an station command\n");
+		return -1;
+	}
+
+	argc--;
+	argv++;
+
+	if (strcmp(cmd, "del") == 0)
+		return handle_station_del(state, dev, argc, argv);
+	if (strcmp(cmd, "get") == 0)
+		return handle_station_get(state, dev, argc, argv);
+	if (strcmp(cmd, "set") == 0)
+		return handle_station_set(state, dev, argc, argv);
+	if (strcmp(cmd, "dump") == 0)
+		return handle_station_dump(state, dev, argc, argv);
+
+	printf("invalid interface command %s\n", cmd);
+	return -1;
+}
diff --git a/util.c b/util.c
new file mode 100644
index 0000000..4a79f61
--- /dev/null
+++ b/util.c
@@ -0,0 +1,45 @@
+#include "iw.h"
+
+int mac_addr_n2a(char *mac_addr, unsigned char *arg)
+{
+	int i, l, blen;
+
+	l = 0;
+	for (i = 0; i < ETH_ALEN ; i++) {
+		if (i == 0) {
+			snprintf(mac_addr+l, blen, "%02x", arg[i]);
+			l += 2;
+		} else {
+			snprintf(mac_addr+l, blen, ":%02x", arg[i]);
+			l += 3;
+		}
+	}
+	return 0;
+}
+
+int mac_addr_a2n(unsigned char *mac_addr, char *arg)
+{
+	int i;
+
+	for (i = 0; i < ETH_ALEN ; i++) {
+		int temp;
+		char *cp = strchr(arg, ':');
+		if (cp) {
+			*cp = 0;
+			cp++;
+		}
+		if (sscanf(arg, "%x", &temp) != 1)
+			return -1;
+		if (temp < 0 || temp > 255)
+			return -1;
+
+		mac_addr[i] = temp;
+		if (!cp)
+			break;
+		arg = cp;
+	}
+	if (i < ETH_ALEN - 1)
+		return -1;
+
+	return 0;
+}