X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=interface.c;h=4f0821d3942f544aaddbb17024a7178441321204;hb=7187aada30f40d6a3afab7e4431634a403c4c9de;hp=b54a66cd29cc90e5d0bc759e0c5fa2398f77e4e4;hpb=1cdd9016a8cd1ca5517586bcefc33da18487fe7c;p=thirdparty%2Fiw.git diff --git a/interface.c b/interface.c index b54a66c..4f0821d 100644 --- a/interface.c +++ b/interface.c @@ -1,166 +1,580 @@ - +#include #include +#include +#include + #include #include #include #include #include -#include - -#include +#include "nl80211.h" #include "iw.h" -/* return 0 if not found, 1 if ok, -1 on error */ -static int get_if_type(int *argc, char ***argv, enum nl80211_iftype *type) +#define VALID_FLAGS "none: no special flags\n"\ + "fcsfail: show frames with FCS errors\n"\ + "control: show control frames\n"\ + "otherbss: show frames from other BSSes\n"\ + "cook: use cooked mode\n"\ + "active: use active mode (ACK incoming unicast packets)" + +SECTION(interface); + +static char *mntr_flags[NL80211_MNTR_FLAG_MAX + 1] = { + "none", + "fcsfail", + "plcpfail", + "control", + "otherbss", + "cook", + "active", +}; + +static int parse_mntr_flags(int *_argc, char ***_argv, + struct nl_msg *msg) +{ + struct nl_msg *flags; + int err = -ENOBUFS; + enum nl80211_mntr_flags flag; + int argc = *_argc; + char **argv = *_argv; + + flags = nlmsg_alloc(); + if (!flags) + return -ENOMEM; + + while (argc) { + int ok = 0; + for (flag = __NL80211_MNTR_FLAG_INVALID; + flag <= NL80211_MNTR_FLAG_MAX; flag++) { + if (strcmp(*argv, mntr_flags[flag]) == 0) { + ok = 1; + /* + * This shouldn't be adding "flag" if that is + * zero, but due to a problem in the kernel's + * nl80211 code (using NLA_NESTED policy) it + * will reject an empty nested attribute but + * not one that contains an invalid attribute + */ + NLA_PUT_FLAG(flags, flag); + break; + } + } + if (!ok) { + err = -EINVAL; + goto out; + } + argc--; + argv++; + } + + nla_put_nested(msg, NL80211_ATTR_MNTR_FLAGS, flags); + err = 0; + nla_put_failure: + out: + nlmsg_free(flags); + + *_argc = argc; + *_argv = argv; + + return err; +} + +/* for help */ +#define IFACE_TYPES "Valid interface types are: managed, ibss, monitor, mesh, wds." + +/* return 0 if ok, internal error otherwise */ +static int get_if_type(int *argc, char ***argv, enum nl80211_iftype *type, + bool need_type) { char *tpstr; - if (*argc < 2) - return 0; + if (*argc < 1 + !!need_type) + return 1; - if (strcmp((*argv)[0], "type")) - return 0; + if (need_type && strcmp((*argv)[0], "type")) + return 1; - tpstr = (*argv)[1]; - *argc -= 2; - *argv += 2; + tpstr = (*argv)[!!need_type]; + *argc -= 1 + !!need_type; + *argv += 1 + !!need_type; if (strcmp(tpstr, "adhoc") == 0 || strcmp(tpstr, "ibss") == 0) { *type = NL80211_IFTYPE_ADHOC; - return 1; + return 0; + } else if (strcmp(tpstr, "ocb") == 0) { + *type = NL80211_IFTYPE_OCB; + return 0; } else if (strcmp(tpstr, "monitor") == 0) { *type = NL80211_IFTYPE_MONITOR; - return 1; - } else if (strcmp(tpstr, "ap") == 0 || strcmp(tpstr, "master") == 0) { + return 0; + } else if (strcmp(tpstr, "master") == 0 || + strcmp(tpstr, "ap") == 0) { + *type = NL80211_IFTYPE_UNSPECIFIED; + fprintf(stderr, "You need to run a management daemon, e.g. hostapd,\n"); + fprintf(stderr, "see http://wireless.kernel.org/en/users/Documentation/hostapd\n"); + fprintf(stderr, "for more information on how to do that.\n"); + return 2; + } else if (strcmp(tpstr, "__ap") == 0) { *type = NL80211_IFTYPE_AP; - return 1; - } else if (strcmp(tpstr, "ap_vlan") == 0) { + return 0; + } else if (strcmp(tpstr, "__ap_vlan") == 0) { *type = NL80211_IFTYPE_AP_VLAN; - return 1; + return 0; } else if (strcmp(tpstr, "wds") == 0) { *type = NL80211_IFTYPE_WDS; - return 1; - } else if (strcmp(tpstr, "station") == 0) { + return 0; + } else if (strcmp(tpstr, "managed") == 0 || + strcmp(tpstr, "mgd") == 0 || + strcmp(tpstr, "station") == 0) { *type = NL80211_IFTYPE_STATION; - return 1; + return 0; + } else if (strcmp(tpstr, "mp") == 0 || + strcmp(tpstr, "mesh") == 0) { + *type = NL80211_IFTYPE_MESH_POINT; + return 0; + } else if (strcmp(tpstr, "__p2pcl") == 0) { + *type = NL80211_IFTYPE_P2P_CLIENT; + return 0; + } else if (strcmp(tpstr, "__p2pdev") == 0) { + *type = NL80211_IFTYPE_P2P_DEVICE; + return 0; + } else if (strcmp(tpstr, "__p2pgo") == 0) { + *type = NL80211_IFTYPE_P2P_GO; + return 0; } - fprintf(stderr, "invalid interface type %s\n", tpstr); - return -1; + return 2; +} + +static int parse_4addr_flag(const char *value, struct nl_msg *msg) +{ + if (strcmp(value, "on") == 0) + NLA_PUT_U8(msg, NL80211_ATTR_4ADDR, 1); + else if (strcmp(value, "off") == 0) + NLA_PUT_U8(msg, NL80211_ATTR_4ADDR, 0); + else + return 1; + return 0; + +nla_put_failure: + return 1; } static int handle_interface_add(struct nl80211_state *state, - char *phy, char *dev, int argc, char **argv) + struct nl_msg *msg, + int argc, char **argv, + enum id_input id) { - char *name = argv[0]; + char *name; + char *mesh_id = NULL; enum nl80211_iftype type; - int tpset, err; - struct nl_msg *msg; + int tpset; + unsigned char mac_addr[ETH_ALEN]; + int found_mac = 0; - if (argc < 1) { - fprintf(stderr, "not enough arguments\n"); - return -1; - } + if (argc < 1) + return 1; + name = argv[0]; argc--; argv++; - if (argc) { - tpset = get_if_type(&argc, &argv, &type); - if (tpset < 0) - return -1; - } + tpset = get_if_type(&argc, &argv, &type, true); + if (tpset) + return tpset; +try_another: if (argc) { - fprintf(stderr, "too many arguments\n"); - return -1; + if (strcmp(argv[0], "mesh_id") == 0) { + argc--; + argv++; + + if (!argc) + return 1; + mesh_id = argv[0]; + argc--; + argv++; + } else if (strcmp(argv[0], "addr") == 0) { + argc--; + argv++; + if (mac_addr_a2n(mac_addr, argv[0])) { + fprintf(stderr, "Invalid MAC address\n"); + return 2; + } + argc--; + argv++; + found_mac = 1; + goto try_another; + } else if (strcmp(argv[0], "4addr") == 0) { + argc--; + argv++; + if (parse_4addr_flag(argv[0], msg)) { + fprintf(stderr, "4addr error\n"); + return 2; + } + argc--; + argv++; + } else if (strcmp(argv[0], "flags") == 0) { + argc--; + argv++; + if (parse_mntr_flags(&argc, &argv, msg)) { + fprintf(stderr, "flags error\n"); + return 2; + } + } else { + return 1; + } } - msg = nlmsg_alloc(); - if (!msg) { - fprintf(stderr, "failed to allocate netlink msg\n"); - return -1; - } + if (argc) + return 1; - genlmsg_put(msg, 0, 0, genl_family_get_id(state->nl80211), 0, - 0, NL80211_CMD_NEW_INTERFACE, 0); - if (dev) - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(dev)); - if (phy) - return -1; /* XXX TODO */ NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, name); - if (tpset) - NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, type); + NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, type); + if (mesh_id) + NLA_PUT(msg, NL80211_ATTR_MESH_ID, strlen(mesh_id), mesh_id); + if (found_mac) + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr); - if ((err = nl_send_auto_complete(state->nl_handle, msg)) < 0 || - (err = nl_wait_for_ack(state->nl_handle)) < 0) { + return 0; nla_put_failure: - fprintf(stderr, "failed to create interface: %d\n", err); - nlmsg_free(msg); - return -1; + return -ENOBUFS; +} +COMMAND(interface, add, " type [mesh_id ] [4addr on|off] [flags *] [addr ]", + NL80211_CMD_NEW_INTERFACE, 0, CIB_PHY, handle_interface_add, + "Add a new virtual interface with the given configuration.\n" + IFACE_TYPES "\n\n" + "The flags are only used for monitor interfaces, valid flags are:\n" + VALID_FLAGS "\n\n" + "The mesh_id is used only for mesh mode."); +COMMAND(interface, add, " type [mesh_id ] [4addr on|off] [flags *] [addr ]", + NL80211_CMD_NEW_INTERFACE, 0, CIB_NETDEV, handle_interface_add, NULL); + +static int handle_interface_del(struct nl80211_state *state, + struct nl_msg *msg, + int argc, char **argv, + enum id_input id) +{ + return 0; +} +TOPLEVEL(del, NULL, NL80211_CMD_DEL_INTERFACE, 0, CIB_NETDEV, handle_interface_del, + "Remove this virtual interface"); +HIDDEN(interface, del, NULL, NL80211_CMD_DEL_INTERFACE, 0, CIB_NETDEV, handle_interface_del); + +static char *channel_type_name(enum nl80211_channel_type channel_type) +{ + switch (channel_type) { + case NL80211_CHAN_NO_HT: + return "NO HT"; + case NL80211_CHAN_HT20: + return "HT20"; + case NL80211_CHAN_HT40MINUS: + return "HT40-"; + case NL80211_CHAN_HT40PLUS: + return "HT40+"; + default: + return "unknown"; + } +} + +char *channel_width_name(enum nl80211_chan_width width) +{ + switch (width) { + case NL80211_CHAN_WIDTH_20_NOHT: + return "20 MHz (no HT)"; + case NL80211_CHAN_WIDTH_20: + return "20 MHz"; + case NL80211_CHAN_WIDTH_40: + return "40 MHz"; + case NL80211_CHAN_WIDTH_80: + return "80 MHz"; + case NL80211_CHAN_WIDTH_80P80: + return "80+80 MHz"; + case NL80211_CHAN_WIDTH_160: + return "160 MHz"; + default: + return "unknown"; + } +} + +static int print_iface_handler(struct nl_msg *msg, void *arg) +{ + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; + unsigned int *wiphy = arg; + const char *indent = ""; + + nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (wiphy && tb_msg[NL80211_ATTR_WIPHY]) { + unsigned int thiswiphy = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY]); + indent = "\t"; + if (*wiphy != thiswiphy) + printf("phy#%d\n", thiswiphy); + *wiphy = thiswiphy; } - nlmsg_free(msg); + if (tb_msg[NL80211_ATTR_IFNAME]) + printf("%sInterface %s\n", indent, nla_get_string(tb_msg[NL80211_ATTR_IFNAME])); + else + printf("%sUnnamed/non-netdev interface\n", indent); + if (tb_msg[NL80211_ATTR_IFINDEX]) + printf("%s\tifindex %d\n", indent, nla_get_u32(tb_msg[NL80211_ATTR_IFINDEX])); + if (tb_msg[NL80211_ATTR_WDEV]) + printf("%s\twdev 0x%llx\n", indent, + (unsigned long long)nla_get_u64(tb_msg[NL80211_ATTR_WDEV])); + if (tb_msg[NL80211_ATTR_MAC]) { + char mac_addr[20]; + mac_addr_n2a(mac_addr, nla_data(tb_msg[NL80211_ATTR_MAC])); + printf("%s\taddr %s\n", indent, mac_addr); + } + if (tb_msg[NL80211_ATTR_SSID]) { + printf("%s\tssid ", indent); + print_ssid_escaped(nla_len(tb_msg[NL80211_ATTR_SSID]), + nla_data(tb_msg[NL80211_ATTR_SSID])); + printf("\n"); + } + if (tb_msg[NL80211_ATTR_IFTYPE]) + printf("%s\ttype %s\n", indent, iftype_name(nla_get_u32(tb_msg[NL80211_ATTR_IFTYPE]))); + if (!wiphy && tb_msg[NL80211_ATTR_WIPHY]) + printf("%s\twiphy %d\n", indent, nla_get_u32(tb_msg[NL80211_ATTR_WIPHY])); + if (tb_msg[NL80211_ATTR_WIPHY_FREQ]) { + uint32_t freq = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_FREQ]); + + printf("%s\tchannel %d (%d MHz)", indent, + ieee80211_frequency_to_channel(freq), freq); + + if (tb_msg[NL80211_ATTR_CHANNEL_WIDTH]) { + printf(", width: %s", + channel_width_name(nla_get_u32(tb_msg[NL80211_ATTR_CHANNEL_WIDTH]))); + if (tb_msg[NL80211_ATTR_CENTER_FREQ1]) + printf(", center1: %d MHz", + nla_get_u32(tb_msg[NL80211_ATTR_CENTER_FREQ1])); + if (tb_msg[NL80211_ATTR_CENTER_FREQ2]) + printf(", center2: %d MHz", + nla_get_u32(tb_msg[NL80211_ATTR_CENTER_FREQ2])); + } else if (tb_msg[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { + enum nl80211_channel_type channel_type; + + channel_type = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); + printf(" %s", channel_type_name(channel_type)); + } + + printf("\n"); + } + if (tb_msg[NL80211_ATTR_WIPHY_TX_POWER_LEVEL]) { + uint32_t txp = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_TX_POWER_LEVEL]); + + printf("%s\ttxpower %d.%.2d dBm\n", + indent, txp / 100, txp % 100); + } + + return NL_SKIP; +} + +static int handle_interface_info(struct nl80211_state *state, + struct nl_msg *msg, + int argc, char **argv, + enum id_input id) +{ + register_handler(print_iface_handler, NULL); return 0; } +TOPLEVEL(info, NULL, NL80211_CMD_GET_INTERFACE, 0, CIB_NETDEV, handle_interface_info, + "Show information for this interface."); -static int handle_interface_del(struct nl80211_state *state, - char *phy, char *dev, int argc, char **argv) +static int handle_interface_set(struct nl80211_state *state, + struct nl_msg *msg, + int argc, char **argv, + enum id_input id) { - int err; - struct nl_msg *msg; + if (!argc) + return 1; - if (argc) { - fprintf(stderr, "too many arguments\n"); - return -1; + NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_MONITOR); + + switch (parse_mntr_flags(&argc, &argv, msg)) { + case 0: + return 0; + case -ENOMEM: + fprintf(stderr, "failed to allocate flags\n"); + return 2; + case -EINVAL: + fprintf(stderr, "unknown flag %s\n", *argv); + return 2; + default: + return 2; } + nla_put_failure: + return -ENOBUFS; +} +COMMAND(set, monitor, "*", + NL80211_CMD_SET_INTERFACE, 0, CIB_NETDEV, handle_interface_set, + "Set monitor flags. Valid flags are:\n" + VALID_FLAGS); + +static int handle_interface_meshid(struct nl80211_state *state, + struct nl_msg *msg, + int argc, char **argv, + enum id_input id) +{ + char *mesh_id = NULL; - msg = nlmsg_alloc(); - if (!msg) - return -1; + if (argc != 1) + return 1; - genlmsg_put(msg, 0, 0, genl_family_get_id(state->nl80211), 0, - 0, NL80211_CMD_DEL_INTERFACE, 0); - if (!dev) { - fprintf(stderr, "need device\n"); - nlmsg_free(msg); - return -1; - } - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(dev)); + mesh_id = argv[0]; + + 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) { + return 0; nla_put_failure: - fprintf(stderr, "failed to remove interface: %d\n", err); - nlmsg_free(msg); - return -1; - } + return -ENOBUFS; +} +COMMAND(set, meshid, "", + NL80211_CMD_SET_INTERFACE, 0, CIB_NETDEV, handle_interface_meshid, NULL); + +static unsigned int dev_dump_wiphy; + +static int handle_dev_dump(struct nl80211_state *state, + struct nl_msg *msg, + int argc, char **argv, + enum id_input id) +{ + dev_dump_wiphy = -1; + register_handler(print_iface_handler, &dev_dump_wiphy); + return 0; +} +TOPLEVEL(dev, NULL, NL80211_CMD_GET_INTERFACE, NLM_F_DUMP, CIB_NONE, handle_dev_dump, + "List all network interfaces for wireless hardware."); + +static int handle_interface_type(struct nl80211_state *state, + struct nl_msg *msg, + int argc, char **argv, + enum id_input id) +{ + enum nl80211_iftype type; + int tpset; + + tpset = get_if_type(&argc, &argv, &type, false); + if (tpset) + return tpset; + + if (argc) + return 1; - nlmsg_free(msg); + NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, type); return 0; + nla_put_failure: + return -ENOBUFS; +} +COMMAND(set, type, "", + NL80211_CMD_SET_INTERFACE, 0, CIB_NETDEV, handle_interface_type, + "Set interface type/mode.\n" + IFACE_TYPES); + +static int handle_interface_4addr(struct nl80211_state *state, + struct nl_msg *msg, + int argc, char **argv, + enum id_input id) +{ + if (argc != 1) + return 1; + return parse_4addr_flag(argv[0], msg); } +COMMAND(set, 4addr, "", + NL80211_CMD_SET_INTERFACE, 0, CIB_NETDEV, handle_interface_4addr, + "Set interface 4addr (WDS) mode."); + +static int handle_interface_noack_map(struct nl80211_state *state, + struct nl_msg *msg, + int argc, char **argv, + enum id_input id) +{ + uint16_t noack_map; + char *end; + + if (argc != 1) + return 1; + + noack_map = strtoul(argv[0], &end, 16); + if (*end) + return 1; + + NLA_PUT_U16(msg, NL80211_ATTR_NOACK_MAP, noack_map); + + return 0; + nla_put_failure: + return -ENOBUFS; + +} +COMMAND(set, noack_map, "", + NL80211_CMD_SET_NOACK_MAP, 0, CIB_NETDEV, handle_interface_noack_map, + "Set the NoAck map for the TIDs. (0x0009 = BE, 0x0006 = BK, 0x0030 = VI, 0x00C0 = VO)"); + -int handle_interface(struct nl80211_state *state, - char *phy, char *dev, int argc, char **argv) +static int handle_interface_wds_peer(struct nl80211_state *state, + struct nl_msg *msg, + int argc, char **argv, + enum id_input id) { - char *cmd = argv[0]; + unsigned char mac_addr[ETH_ALEN]; if (argc < 1) - return -1; + return 1; + + if (mac_addr_a2n(mac_addr, argv[0])) { + fprintf(stderr, "Invalid MAC address\n"); + return 2; + } argc--; argv++; - if (strcmp(cmd, "add") == 0) - return handle_interface_add(state, phy, dev, argc, argv); - else if (strcmp(cmd, "del") == 0) - return handle_interface_del(state, phy, dev, argc, argv); + if (argc) + return 1; + + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr); - printf("invalid interface command %s\n", cmd); - return -1; + return 0; + nla_put_failure: + return -ENOBUFS; } +COMMAND(set, peer, "", + NL80211_CMD_SET_WDS_PEER, 0, CIB_NETDEV, handle_interface_wds_peer, + "Set interface WDS peer."); + +static int set_mcast_rate(struct nl80211_state *state, + struct nl_msg *msg, + int argc, char **argv, + enum id_input id) +{ + float rate; + char *end; + + if (argc != 1) { + printf("Invalid parameters!\n"); + return 2; + } + + rate = strtod(argv[0], &end); + if (*end != '\0') + return 1; + + NLA_PUT_U32(msg, NL80211_ATTR_MCAST_RATE, (int)(rate * 10)); + + return 0; +nla_put_failure: + return -ENOBUFS; +} + +COMMAND(set, mcast_rate, "", + NL80211_CMD_SET_MCAST_RATE, 0, CIB_NETDEV, set_mcast_rate, + "Set the multicast bitrate.");