X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=interface.c;h=712bbdcad283fa23192838f477d434edc4d21ef2;hb=d4f1ea11061c83ab3586e1a874ac5cdd66a13fd7;hp=7d9fc8b6cb5cf3c411c0b19e83c385ca77eb75d5;hpb=e08b548840f167ae3aa33bfa436787f926cdb2ec;p=thirdparty%2Fiw.git diff --git a/interface.c b/interface.c index 7d9fc8b..712bbdc 100644 --- a/interface.c +++ b/interface.c @@ -1,4 +1,3 @@ -#include #include #include #include @@ -16,7 +15,10 @@ "fcsfail: show frames with FCS errors\n"\ "control: show control frames\n"\ "otherbss: show frames from other BSSes\n"\ - "cook: use cooked mode" + "cook: use cooked mode\n"\ + "active: use active mode (ACK incoming unicast packets)\n"\ + "mumimo-groupid : use MUMIMO according to a group id\n"\ + "mumimo-follow-mac : use MUMIMO according to a MAC address" SECTION(interface); @@ -27,8 +29,58 @@ static char *mntr_flags[NL80211_MNTR_FLAG_MAX + 1] = { "control", "otherbss", "cook", + "active", }; +static int parse_mumimo_options(int *_argc, char ***_argv, struct nl_msg *msg) +{ + uint8_t mumimo_group[VHT_MUMIMO_GROUP_LEN]; + unsigned char mac_addr[ETH_ALEN]; + char **argv = *_argv; + int argc = *_argc; + int i; + unsigned int val; + + if (strcmp(*argv, "mumimo-groupid") == 0) { + argc--; + argv++; + if (!argc || strlen(*argv) != VHT_MUMIMO_GROUP_LEN*2) { + fprintf(stderr, "Invalid groupID: %s\n", *argv); + return 1; + } + + for (i = 0; i < VHT_MUMIMO_GROUP_LEN; i++) { + if (sscanf((*argv) + i*2, "%2x", &val) != 1) { + fprintf(stderr, "Failed reading groupID\n"); + return 1; + } + mumimo_group[i] = val; + } + + NLA_PUT(msg, + NL80211_ATTR_MU_MIMO_GROUP_DATA, + VHT_MUMIMO_GROUP_LEN, + mumimo_group); + argc--; + argv++; + } else if (strcmp(*argv, "mumimo-follow-mac") == 0) { + argc--; + argv++; + if (!argc || mac_addr_a2n(mac_addr, *argv)) { + fprintf(stderr, "Invalid MAC address\n"); + return 1; + } + NLA_PUT(msg, NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR, + ETH_ALEN, mac_addr); + argc--; + argv++; + } + nla_put_failure: + *_argc = argc; + *_argv = argv; + return 0; +} + static int parse_mntr_flags(int *_argc, char ***_argv, struct nl_msg *msg) { @@ -44,6 +96,15 @@ static int parse_mntr_flags(int *_argc, char ***_argv, while (argc) { int ok = 0; + + /* parse MU-MIMO options */ + err = parse_mumimo_options(&argc, &argv, msg); + if (err) + goto out; + else if (!argc) + break; + + /* parse monitor flags */ for (flag = __NL80211_MNTR_FLAG_INVALID; flag <= NL80211_MNTR_FLAG_MAX; flag++) { if (strcmp(*argv, mntr_flags[flag]) == 0) { @@ -102,6 +163,9 @@ static int get_if_type(int *argc, char ***argv, enum nl80211_iftype *type, strcmp(tpstr, "ibss") == 0) { *type = NL80211_IFTYPE_ADHOC; 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 0; @@ -130,6 +194,18 @@ static int get_if_type(int *argc, char ***argv, enum nl80211_iftype *type, 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; + } else if (strcmp(tpstr, "__nan") == 0) { + *type = NL80211_IFTYPE_NAN; + return 0; } fprintf(stderr, "invalid interface type %s\n", tpstr); @@ -151,14 +227,16 @@ nla_put_failure: } static int handle_interface_add(struct nl80211_state *state, - struct nl_cb *cb, struct nl_msg *msg, - int argc, char **argv) + int argc, char **argv, + enum id_input id) { char *name; char *mesh_id = NULL; enum nl80211_iftype type; int tpset; + unsigned char mac_addr[ETH_ALEN]; + int found_mac = 0; if (argc < 1) return 1; @@ -171,6 +249,7 @@ static int handle_interface_add(struct nl80211_state *state, if (tpset) return tpset; +try_another: if (argc) { if (strcmp(argv[0], "mesh_id") == 0) { argc--; @@ -181,6 +260,17 @@ static int handle_interface_add(struct nl80211_state *state, 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++; @@ -209,25 +299,27 @@ static int handle_interface_add(struct nl80211_state *state, 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); return 0; nla_put_failure: return -ENOBUFS; } -COMMAND(interface, add, " type [mesh_id ] [4addr on|off] [flags *]", +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 *]", +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_cb *cb, struct nl_msg *msg, - int argc, char **argv) + int argc, char **argv, + enum id_input id) { return 0; } @@ -235,6 +327,46 @@ TOPLEVEL(del, NULL, NL80211_CMD_DEL_INTERFACE, 0, CIB_NETDEV, handle_interface_d "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"; + case NL80211_CHAN_WIDTH_5: + return "5 MHz"; + case NL80211_CHAN_WIDTH_10: + return "10 MHz"; + default: + return "unknown"; + } +} + static int print_iface_handler(struct nl_msg *msg, void *arg) { struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); @@ -255,29 +387,84 @@ static int print_iface_handler(struct nl_msg *msg, void *arg) 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); + } + + if (tb_msg[NL80211_ATTR_TXQ_STATS]) { + char buf[150]; + parse_txq_stats(buf, sizeof(buf), tb_msg[NL80211_ATTR_TXQ_STATS], 1, -1, indent); + printf("%s\tmulticast TXQ:%s\n", indent, buf); + } return NL_SKIP; } static int handle_interface_info(struct nl80211_state *state, - struct nl_cb *cb, struct nl_msg *msg, - int argc, char **argv) + int argc, char **argv, + enum id_input id) { - nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_iface_handler, NULL); + 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_set(struct nl80211_state *state, - struct nl_cb *cb, struct nl_msg *msg, - int argc, char **argv) + int argc, char **argv, + enum id_input id) { if (!argc) return 1; @@ -287,6 +474,8 @@ static int handle_interface_set(struct nl80211_state *state, switch (parse_mntr_flags(&argc, &argv, msg)) { case 0: return 0; + case 1: + return 1; case -ENOMEM: fprintf(stderr, "failed to allocate flags\n"); return 2; @@ -305,9 +494,9 @@ COMMAND(set, monitor, "*", VALID_FLAGS); static int handle_interface_meshid(struct nl80211_state *state, - struct nl_cb *cb, struct nl_msg *msg, - int argc, char **argv) + int argc, char **argv, + enum id_input id) { char *mesh_id = NULL; @@ -328,21 +517,21 @@ COMMAND(set, meshid, "", static unsigned int dev_dump_wiphy; static int handle_dev_dump(struct nl80211_state *state, - struct nl_cb *cb, struct nl_msg *msg, - int argc, char **argv) + int argc, char **argv, + enum id_input id) { dev_dump_wiphy = -1; - nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_iface_handler, &dev_dump_wiphy); + 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_cb *cb, struct nl_msg *msg, - int argc, char **argv) + int argc, char **argv, + enum id_input id) { enum nl80211_iftype type; int tpset; @@ -364,3 +553,171 @@ 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)"); + + +static int handle_interface_wds_peer(struct nl80211_state *state, + struct nl_msg *msg, + int argc, char **argv, + enum id_input id) +{ + unsigned char mac_addr[ETH_ALEN]; + + if (argc < 1) + return 1; + + if (mac_addr_a2n(mac_addr, argv[0])) { + fprintf(stderr, "Invalid MAC address\n"); + return 2; + } + + argc--; + argv++; + + if (argc) + return 1; + + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr); + + 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) + return 1; + + 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."); + + +static int handle_chanfreq(struct nl80211_state *state, struct nl_msg *msg, + bool chan, int argc, char **argv, + enum id_input id) +{ + struct chandef chandef; + int res; + int parsed; + char *end; + + res = parse_freqchan(&chandef, chan, argc, argv, &parsed); + if (res) + return res; + + argc -= parsed; + argv += parsed; + + while (argc) { + unsigned int beacons = 10; + + if (strcmp(argv[0], "beacons") == 0) { + if (argc < 2) + return 1; + + beacons = strtol(argv[1], &end, 10); + if (*end) + return 1; + + argc -= 2; + argv += 2; + + NLA_PUT_U32(msg, NL80211_ATTR_CH_SWITCH_COUNT, beacons); + } else if (strcmp(argv[0], "block-tx") == 0) { + argc -= 1; + argv += 1; + + NLA_PUT_FLAG(msg, NL80211_ATTR_CH_SWITCH_BLOCK_TX); + } else { + return 1; + } + } + + return put_chandef(msg, &chandef); + + nla_put_failure: + return -ENOBUFS; +} + +static int handle_freq(struct nl80211_state *state, struct nl_msg *msg, + int argc, char **argv, + enum id_input id) +{ + return handle_chanfreq(state, msg, false, argc, argv, id); +} + +static int handle_chan(struct nl80211_state *state, struct nl_msg *msg, + int argc, char **argv, + enum id_input id) +{ + return handle_chanfreq(state, msg, true, argc, argv, id); +} + +SECTION(switch); +COMMAND(switch, freq, + " [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz] [beacons ] [block-tx]\n" + " [5|10|20|40|80|80+80|160] [ []] [beacons ] [block-tx]", + NL80211_CMD_CHANNEL_SWITCH, 0, CIB_NETDEV, handle_freq, + "Switch the operating channel by sending a channel switch announcement (CSA)."); +COMMAND(switch, channel, " [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz] [beacons ] [block-tx]", + NL80211_CMD_CHANNEL_SWITCH, 0, CIB_NETDEV, handle_chan, NULL);