]> git.ipfire.org Git - thirdparty/iw.git/commitdiff
Add support for mesh interfaces
authorLuis Carlos Cobo <luisca@cozybit.com>
Tue, 1 Apr 2008 19:03:41 +0000 (12:03 -0700)
committerJohannes Berg <johannes@sipsolutions.net>
Wed, 2 Apr 2008 09:12:52 +0000 (11:12 +0200)
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

Makefile
interface.c
iw.c
iw.h
mpath.c [new file with mode: 0644]
station.c [new file with mode: 0644]
util.c [new file with mode: 0644]

index 5e620a521433c7dfd50b69d81e7413f8f1e2d549..82e1a25acb89af05ed93fe08693294d1066e0b6b 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@ CC ?= "gcc"
 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)
index efc2d93280a21b55c16899f2c874e1db14d21c9e..3ef9b187292b410d267094569472f1f5603ece21 100644 (file)
@@ -45,6 +45,10 @@ static int get_if_type(int *argc, char ***argv, enum nl80211_iftype *type)
        } 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 @@ static int handle_interface_add(struct nl80211_state *state,
                                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;
@@ -75,6 +80,23 @@ static int handle_interface_add(struct nl80211_state *state,
        if (tpset <= 0)
                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 @@ static int handle_interface_add(struct nl80211_state *state,
        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 bff5f4b78fe2f8931284f4249129e502fc2adecb..3811f6d6012c40315a072780c361b1949568cca8 100644 (file)
--- a/iw.c
+++ b/iw.c
@@ -90,13 +90,12 @@ static int get_phy_or_dev(int *argc, char ***argv, char **name)
 
 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 @@ int main(int argc, char **argv)
                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 3fad652bedc0ff08083f2a5c0ce2665573fbc668..0ece3af791ae5f1a136772ee93c015695aa8ad62 100644 (file)
--- 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_interface(struct nl80211_state *state,
 
 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 (file)
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 (file)
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 (file)
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;
+}