blob: 7f8ce26a70b0290c265e0278151b5d854fda46ea [file] [log] [blame] [edit]
#include <stdbool.h>
#include <errno.h>
#include <net/if.h>
#include <strings.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 "nl80211.h"
#include "iw.h"
static int handle_name(struct nl80211_state *state,
struct nl_cb *cb,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
if (argc != 1)
return 1;
NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, *argv);
return 0;
nla_put_failure:
return -ENOBUFS;
}
COMMAND(set, name, "<new name>", NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_name,
"Rename this wireless device.");
static int handle_freqs(struct nl_msg *msg, int argc, char **argv)
{
static const struct {
const char *name;
unsigned int val;
} bwmap[] = {
{ .name = "20", .val = NL80211_CHAN_WIDTH_20, },
{ .name = "40", .val = NL80211_CHAN_WIDTH_40, },
{ .name = "80", .val = NL80211_CHAN_WIDTH_80, },
{ .name = "80+80", .val = NL80211_CHAN_WIDTH_80P80, },
{ .name = "160", .val = NL80211_CHAN_WIDTH_160, },
};
uint32_t freq;
int i, bwval = NL80211_CHAN_WIDTH_20_NOHT;
char *end;
if (argc < 1)
return 1;
for (i = 0; i < ARRAY_SIZE(bwmap); i++) {
if (strcasecmp(bwmap[i].name, argv[0]) == 0) {
bwval = bwmap[i].val;
break;
}
}
if (bwval == NL80211_CHAN_WIDTH_20_NOHT)
return 1;
NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, bwval);
if (argc == 1)
return 0;
/* center freq 1 */
if (!*argv[1])
return 1;
freq = strtoul(argv[1], &end, 10);
if (*end)
return 1;
NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ1, freq);
if (argc == 2)
return 0;
/* center freq 2 */
if (!*argv[2])
return 1;
freq = strtoul(argv[2], &end, 10);
if (*end)
return 1;
NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ2, freq);
return 0;
nla_put_failure:
return -ENOBUFS;
}
static int handle_freqchan(struct nl_msg *msg, bool chan,
int argc, char **argv)
{
char *end;
static const struct {
const char *name;
unsigned int val;
} htmap[] = {
{ .name = "HT20", .val = NL80211_CHAN_HT20, },
{ .name = "HT40+", .val = NL80211_CHAN_HT40PLUS, },
{ .name = "HT40-", .val = NL80211_CHAN_HT40MINUS, },
};
unsigned int htval = NL80211_CHAN_NO_HT;
unsigned int freq;
int i;
if (!argc || argc > 4)
return 1;
if (!*argv[0])
return 1;
freq = strtoul(argv[0], &end, 10);
if (*end)
return 1;
if (chan)
freq = ieee80211_channel_to_frequency(freq);
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
if (argc > 2) {
return handle_freqs(msg, argc - 1, argv + 1);
} else if (argc == 2) {
for (i = 0; i < ARRAY_SIZE(htmap); i++) {
if (strcasecmp(htmap[i].name, argv[1]) == 0) {
htval = htmap[i].val;
break;
}
}
if (htval == NL80211_CHAN_NO_HT)
return handle_freqs(msg, argc - 1, argv + 1);
}
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, htval);
return 0;
nla_put_failure:
return -ENOBUFS;
}
static int handle_freq(struct nl80211_state *state,
struct nl_cb *cb, struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
return handle_freqchan(msg, false, argc, argv);
}
COMMAND(set, freq, "<freq> [HT20|HT40+|HT40-]",
NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_freq,
"Set frequency/channel the hardware is using, including HT\n"
"configuration.");
COMMAND(set, freq, "<freq> [HT20|HT40+|HT40-]\n"
"<control freq> [20|40|80|80+80|160] [<center freq 1>] [<center freq 2>]",
NL80211_CMD_SET_WIPHY, 0, CIB_NETDEV, handle_freq, NULL);
static int handle_chan(struct nl80211_state *state,
struct nl_cb *cb, struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
return handle_freqchan(msg, true, argc, argv);
}
COMMAND(set, channel, "<channel> [HT20|HT40+|HT40-]",
NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_chan, NULL);
COMMAND(set, channel, "<channel> [HT20|HT40+|HT40-]",
NL80211_CMD_SET_WIPHY, 0, CIB_NETDEV, handle_chan, NULL);
static int handle_fragmentation(struct nl80211_state *state,
struct nl_cb *cb, struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
unsigned int frag;
if (argc != 1)
return 1;
if (strcmp("off", argv[0]) == 0)
frag = -1;
else {
char *end;
if (!*argv[0])
return 1;
frag = strtoul(argv[0], &end, 10);
if (*end != '\0')
return 1;
}
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, frag);
return 0;
nla_put_failure:
return -ENOBUFS;
}
COMMAND(set, frag, "<fragmentation threshold|off>",
NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_fragmentation,
"Set fragmentation threshold.");
static int handle_rts(struct nl80211_state *state,
struct nl_cb *cb, struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
unsigned int rts;
if (argc != 1)
return 1;
if (strcmp("off", argv[0]) == 0)
rts = -1;
else {
char *end;
if (!*argv[0])
return 1;
rts = strtoul(argv[0], &end, 10);
if (*end != '\0')
return 1;
}
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, rts);
return 0;
nla_put_failure:
return -ENOBUFS;
}
COMMAND(set, rts, "<rts threshold|off>",
NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_rts,
"Set rts threshold.");
static int handle_netns(struct nl80211_state *state,
struct nl_cb *cb,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
char *end;
if (argc != 1)
return 1;
if (!*argv[0])
return 1;
NLA_PUT_U32(msg, NL80211_ATTR_PID,
strtoul(argv[0], &end, 10));
if (*end != '\0')
return 1;
return 0;
nla_put_failure:
return -ENOBUFS;
}
COMMAND(set, netns, "<pid>",
NL80211_CMD_SET_WIPHY_NETNS, 0, CIB_PHY, handle_netns,
"Put this wireless device into a different network namespace");
static int handle_coverage(struct nl80211_state *state,
struct nl_cb *cb,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
char *end;
unsigned int coverage;
if (argc != 1)
return 1;
if (!*argv[0])
return 1;
coverage = strtoul(argv[0], &end, 10);
if (coverage > 255)
return 1;
if (*end)
return 1;
NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS, coverage);
return 0;
nla_put_failure:
return -ENOBUFS;
}
COMMAND(set, coverage, "<coverage class>",
NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_coverage,
"Set coverage class (1 for every 3 usec of air propagation time).\n"
"Valid values: 0 - 255.");
static int handle_distance(struct nl80211_state *state,
struct nl_cb *cb,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
char *end;
unsigned int distance, coverage;
if (argc != 1)
return 1;
if (!*argv[0])
return 1;
distance = strtoul(argv[0], &end, 10);
if (*end)
return 1;
/*
* Divide double the distance by the speed of light in m/usec (300) to
* get round-trip time in microseconds and then divide the result by
* three to get coverage class as specified in IEEE 802.11-2007 table
* 7-27. Values are rounded upwards.
*/
coverage = (distance + 449) / 450;
if (coverage > 255)
return 1;
NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS, coverage);
return 0;
nla_put_failure:
return -ENOBUFS;
}
COMMAND(set, distance, "<distance>",
NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_distance,
"Set appropriate coverage class for given link distance in meters.\n"
"Valid values: 0 - 114750");
static int handle_txpower(struct nl80211_state *state,
struct nl_cb *cb,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
enum nl80211_tx_power_setting type;
int mbm;
/* get the required args */
if (argc != 1 && argc != 2)
return 1;
if (!strcmp(argv[0], "auto"))
type = NL80211_TX_POWER_AUTOMATIC;
else if (!strcmp(argv[0], "fixed"))
type = NL80211_TX_POWER_FIXED;
else if (!strcmp(argv[0], "limit"))
type = NL80211_TX_POWER_LIMITED;
else {
printf("Invalid parameter: %s\n", argv[0]);
return 2;
}
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_TX_POWER_SETTING, type);
if (type != NL80211_TX_POWER_AUTOMATIC) {
char *endptr;
if (argc != 2) {
printf("Missing TX power level argument.\n");
return 2;
}
mbm = strtol(argv[1], &endptr, 10);
if (*endptr)
return 2;
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_TX_POWER_LEVEL, mbm);
} else if (argc != 1)
return 1;
return 0;
nla_put_failure:
return -ENOBUFS;
}
COMMAND(set, txpower, "<auto|fixed|limit> [<tx power in mBm>]",
NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_txpower,
"Specify transmit power level and setting type.");
COMMAND(set, txpower, "<auto|fixed|limit> [<tx power in mBm>]",
NL80211_CMD_SET_WIPHY, 0, CIB_NETDEV, handle_txpower,
"Specify transmit power level and setting type.");
static int handle_antenna(struct nl80211_state *state,
struct nl_cb *cb,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
char *end;
uint32_t tx_ant = 0, rx_ant = 0;
if (argc == 1 && strcmp(argv[0], "all") == 0) {
tx_ant = 0xffffffff;
rx_ant = 0xffffffff;
} else if (argc == 1) {
tx_ant = rx_ant = strtoul(argv[0], &end, 0);
if (*end)
return 1;
}
else if (argc == 2) {
tx_ant = strtoul(argv[0], &end, 0);
if (*end)
return 1;
rx_ant = strtoul(argv[1], &end, 0);
if (*end)
return 1;
} else
return 1;
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_TX, tx_ant);
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_RX, rx_ant);
return 0;
nla_put_failure:
return -ENOBUFS;
}
COMMAND(set, antenna, "<bitmap> | all | <tx bitmap> <rx bitmap>",
NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_antenna,
"Set a bitmap of allowed antennas to use for TX and RX.\n"
"The driver may reject antenna configurations it cannot support.");