Home | History | Annotate | Download | only in iw
      1 #include <stdbool.h>
      2 #include <errno.h>
      3 #include <net/if.h>
      4 #include <strings.h>
      5 #include <sys/param.h>
      6 #include <sys/stat.h>
      7 #include <fcntl.h>
      8 
      9 #include <netlink/genl/genl.h>
     10 #include <netlink/genl/family.h>
     11 #include <netlink/genl/ctrl.h>
     12 #include <netlink/msg.h>
     13 #include <netlink/attr.h>
     14 
     15 #include "nl80211.h"
     16 #include "iw.h"
     17 
     18 static int handle_name(struct nl80211_state *state,
     19 		       struct nl_cb *cb,
     20 		       struct nl_msg *msg,
     21 		       int argc, char **argv,
     22 		       enum id_input id)
     23 {
     24 	if (argc != 1)
     25 		return 1;
     26 
     27 	NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, *argv);
     28 
     29 	return 0;
     30  nla_put_failure:
     31 	return -ENOBUFS;
     32 }
     33 COMMAND(set, name, "<new name>", NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_name,
     34 	"Rename this wireless device.");
     35 
     36 static int handle_freqs(struct nl_msg *msg, int argc, char **argv)
     37 {
     38 	static const struct {
     39 		const char *name;
     40 		unsigned int val;
     41 	} bwmap[] = {
     42 		{ .name = "20", .val = NL80211_CHAN_WIDTH_20, },
     43 		{ .name = "40", .val = NL80211_CHAN_WIDTH_40, },
     44 		{ .name = "80", .val = NL80211_CHAN_WIDTH_80, },
     45 		{ .name = "80+80", .val = NL80211_CHAN_WIDTH_80P80, },
     46 		{ .name = "160", .val = NL80211_CHAN_WIDTH_160, },
     47 	};
     48 	uint32_t freq;
     49 	int i, bwval = NL80211_CHAN_WIDTH_20_NOHT;
     50 	char *end;
     51 
     52 	if (argc < 1)
     53 		return 1;
     54 
     55 	for (i = 0; i < ARRAY_SIZE(bwmap); i++) {
     56 		if (strcasecmp(bwmap[i].name, argv[0]) == 0) {
     57 			bwval = bwmap[i].val;
     58 			break;
     59 		}
     60 	}
     61 
     62 	if (bwval == NL80211_CHAN_WIDTH_20_NOHT)
     63 		return 1;
     64 
     65 	NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, bwval);
     66 
     67 	if (argc == 1)
     68 		return 0;
     69 
     70 	/* center freq 1 */
     71 	if (!*argv[1])
     72 		return 1;
     73 	freq = strtoul(argv[1], &end, 10);
     74 	if (*end)
     75 		return 1;
     76 	NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ1, freq);
     77 
     78 	if (argc == 2)
     79 		return 0;
     80 
     81 	/* center freq 2 */
     82 	if (!*argv[2])
     83 		return 1;
     84 	freq = strtoul(argv[2], &end, 10);
     85 	if (*end)
     86 		return 1;
     87 	NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ2, freq);
     88 
     89 	return 0;
     90  nla_put_failure:
     91 	return -ENOBUFS;
     92 }
     93 
     94 static int handle_freqchan(struct nl_msg *msg, bool chan,
     95 			   int argc, char **argv)
     96 {
     97 	char *end;
     98 	static const struct {
     99 		const char *name;
    100 		unsigned int val;
    101 	} htmap[] = {
    102 		{ .name = "HT20", .val = NL80211_CHAN_HT20, },
    103 		{ .name = "HT40+", .val = NL80211_CHAN_HT40PLUS, },
    104 		{ .name = "HT40-", .val = NL80211_CHAN_HT40MINUS, },
    105 	};
    106 	unsigned int htval = NL80211_CHAN_NO_HT;
    107 	unsigned int freq;
    108 	int i;
    109 
    110 	if (!argc || argc > 4)
    111 		return 1;
    112 
    113 	if (!*argv[0])
    114 		return 1;
    115 	freq = strtoul(argv[0], &end, 10);
    116 	if (*end)
    117 		return 1;
    118 
    119 	if (chan) {
    120 		enum nl80211_band band;
    121 		band = freq <= 14 ? NL80211_BAND_2GHZ : NL80211_BAND_5GHZ;
    122 		freq = ieee80211_channel_to_frequency(freq, band);
    123 	}
    124 
    125 	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
    126 
    127 	if (argc > 2) {
    128 		return handle_freqs(msg, argc - 1, argv + 1);
    129 	} else if (argc == 2) {
    130 		for (i = 0; i < ARRAY_SIZE(htmap); i++) {
    131 			if (strcasecmp(htmap[i].name, argv[1]) == 0) {
    132 				htval = htmap[i].val;
    133 				break;
    134 			}
    135 		}
    136 		if (htval == NL80211_CHAN_NO_HT)
    137 			return handle_freqs(msg, argc - 1, argv + 1);
    138 	}
    139 
    140 	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, htval);
    141 
    142 	return 0;
    143  nla_put_failure:
    144 	return -ENOBUFS;
    145 }
    146 
    147 static int handle_freq(struct nl80211_state *state,
    148 		       struct nl_cb *cb, struct nl_msg *msg,
    149 		       int argc, char **argv,
    150 		       enum id_input id)
    151 {
    152 	return handle_freqchan(msg, false, argc, argv);
    153 }
    154 COMMAND(set, freq, "<freq> [HT20|HT40+|HT40-]",
    155 	NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_freq,
    156 	"Set frequency/channel the hardware is using, including HT\n"
    157 	"configuration.");
    158 COMMAND(set, freq, "<freq> [HT20|HT40+|HT40-]\n"
    159 		   "<control freq> [20|40|80|80+80|160] [<center freq 1>] [<center freq 2>]",
    160 	NL80211_CMD_SET_WIPHY, 0, CIB_NETDEV, handle_freq, NULL);
    161 
    162 static int handle_chan(struct nl80211_state *state,
    163 		       struct nl_cb *cb, struct nl_msg *msg,
    164 		       int argc, char **argv,
    165 		       enum id_input id)
    166 {
    167 	return handle_freqchan(msg, true, argc, argv);
    168 }
    169 COMMAND(set, channel, "<channel> [HT20|HT40+|HT40-]",
    170 	NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_chan, NULL);
    171 COMMAND(set, channel, "<channel> [HT20|HT40+|HT40-]",
    172 	NL80211_CMD_SET_WIPHY, 0, CIB_NETDEV, handle_chan, NULL);
    173 
    174 static int handle_fragmentation(struct nl80211_state *state,
    175 				struct nl_cb *cb, struct nl_msg *msg,
    176 				int argc, char **argv,
    177 				enum id_input id)
    178 {
    179 	unsigned int frag;
    180 
    181 	if (argc != 1)
    182 		return 1;
    183 
    184 	if (strcmp("off", argv[0]) == 0)
    185 		frag = -1;
    186 	else {
    187 		char *end;
    188 
    189 		if (!*argv[0])
    190 			return 1;
    191 		frag = strtoul(argv[0], &end, 10);
    192 		if (*end != '\0')
    193 			return 1;
    194 	}
    195 
    196 	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, frag);
    197 
    198 	return 0;
    199  nla_put_failure:
    200 	return -ENOBUFS;
    201 }
    202 COMMAND(set, frag, "<fragmentation threshold|off>",
    203 	NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_fragmentation,
    204 	"Set fragmentation threshold.");
    205 
    206 static int handle_rts(struct nl80211_state *state,
    207 		      struct nl_cb *cb, struct nl_msg *msg,
    208 		      int argc, char **argv,
    209 		      enum id_input id)
    210 {
    211 	unsigned int rts;
    212 
    213 	if (argc != 1)
    214 		return 1;
    215 
    216 	if (strcmp("off", argv[0]) == 0)
    217 		rts = -1;
    218 	else {
    219 		char *end;
    220 
    221 		if (!*argv[0])
    222 			return 1;
    223 		rts = strtoul(argv[0], &end, 10);
    224 		if (*end != '\0')
    225 			return 1;
    226 	}
    227 
    228 	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, rts);
    229 
    230 	return 0;
    231  nla_put_failure:
    232 	return -ENOBUFS;
    233 }
    234 COMMAND(set, rts, "<rts threshold|off>",
    235 	NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_rts,
    236 	"Set rts threshold.");
    237 
    238 static int handle_retry(struct nl80211_state *state,
    239 			struct nl_cb *cb, struct nl_msg *msg,
    240 			int argc, char **argv, enum id_input id)
    241 {
    242 	unsigned int retry_short = 0, retry_long = 0;
    243 	bool have_retry_s = false, have_retry_l = false;
    244 	int i;
    245 	enum {
    246 		S_NONE,
    247 		S_SHORT,
    248 		S_LONG,
    249 	} parser_state = S_NONE;
    250 
    251 	if (!argc || (argc != 2 && argc != 4))
    252 		return 1;
    253 
    254 	for (i = 0; i < argc; i++) {
    255 		char *end;
    256 		unsigned int tmpul;
    257 
    258 		if (strcmp(argv[i], "short") == 0) {
    259 			if (have_retry_s)
    260 				return 1;
    261 			parser_state = S_SHORT;
    262 			have_retry_s = true;
    263 		} else if (strcmp(argv[i], "long") == 0) {
    264 			if (have_retry_l)
    265 				return 1;
    266 			parser_state = S_LONG;
    267 			have_retry_l = true;
    268 		} else {
    269 			tmpul = strtoul(argv[i], &end, 10);
    270 			if (*end != '\0')
    271 				return 1;
    272 			if (!tmpul || tmpul > 255)
    273 				return -EINVAL;
    274 			switch (parser_state) {
    275 			case S_SHORT:
    276 				retry_short = tmpul;
    277 				break;
    278 			case S_LONG:
    279 				retry_long = tmpul;
    280 				break;
    281 			default:
    282 				return 1;
    283 			}
    284 		}
    285 	}
    286 
    287 	if (!have_retry_s && !have_retry_l)
    288 		return 1;
    289 	if (have_retry_s)
    290 		NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT, retry_short);
    291 	if (have_retry_l)
    292 		NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_LONG, retry_long);
    293 
    294 	return 0;
    295  nla_put_failure:
    296 	return -ENOBUFS;
    297 }
    298 COMMAND(set, retry, "[short <limit>] [long <limit>]",
    299 	NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_retry,
    300 	"Set retry limit.");
    301 
    302 #ifndef NETNS_RUN_DIR
    303 #define NETNS_RUN_DIR "/var/run/netns"
    304 #endif
    305 int netns_get_fd(const char *name)
    306 {
    307 	char pathbuf[MAXPATHLEN];
    308 	const char *path, *ptr;
    309 
    310 	path = name;
    311 	ptr = strchr(name, '/');
    312 	if (!ptr) {
    313 		snprintf(pathbuf, sizeof(pathbuf), "%s/%s",
    314 			NETNS_RUN_DIR, name );
    315 		path = pathbuf;
    316 	}
    317 	return open(path, O_RDONLY);
    318 }
    319 
    320 static int handle_netns(struct nl80211_state *state,
    321 			struct nl_cb *cb,
    322 			struct nl_msg *msg,
    323 			int argc, char **argv,
    324 			enum id_input id)
    325 {
    326 	char *end;
    327 	int fd;
    328 
    329 	if (argc < 1 || !*argv[0])
    330 		return 1;
    331 
    332 	if (argc == 1) {
    333 		NLA_PUT_U32(msg, NL80211_ATTR_PID,
    334 				strtoul(argv[0], &end, 10));
    335 		if (*end != '\0') {
    336 			printf("Invalid parameter: pid(%s)\n", argv[0]);
    337 			return 1;
    338 		}
    339 		return 0;
    340 	}
    341 
    342 	if (argc != 2 || strcmp(argv[0], "name"))
    343 		return 1;
    344 
    345 	if ((fd = netns_get_fd(argv[1])) >= 0) {
    346 		NLA_PUT_U32(msg, NL80211_ATTR_NETNS_FD, fd);
    347 		return 0;
    348 	} else {
    349 		printf("Invalid parameter: nsname(%s)\n", argv[0]);
    350 	}
    351 
    352 	return 1;
    353 
    354  nla_put_failure:
    355 	return -ENOBUFS;
    356 }
    357 COMMAND(set, netns, "{ <pid> | name <nsname> }",
    358 	NL80211_CMD_SET_WIPHY_NETNS, 0, CIB_PHY, handle_netns,
    359 	"Put this wireless device into a different network namespace:\n"
    360 	"    <pid>    - change network namespace by process id\n"
    361 	"    <nsname> - change network namespace by name from "NETNS_RUN_DIR"\n"
    362 	"               or by absolute path (man ip-netns)\n");
    363 
    364 static int handle_coverage(struct nl80211_state *state,
    365 			struct nl_cb *cb,
    366 			struct nl_msg *msg,
    367 			int argc, char **argv,
    368 			enum id_input id)
    369 {
    370 	char *end;
    371 	unsigned int coverage;
    372 
    373 	if (argc != 1)
    374 		return 1;
    375 
    376 	if (!*argv[0])
    377 		return 1;
    378 	coverage = strtoul(argv[0], &end, 10);
    379 	if (coverage > 255)
    380 		return 1;
    381 
    382 	if (*end)
    383 		return 1;
    384 
    385 	NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS, coverage);
    386 
    387 	return 0;
    388  nla_put_failure:
    389 	return -ENOBUFS;
    390 }
    391 COMMAND(set, coverage, "<coverage class>",
    392 	NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_coverage,
    393 	"Set coverage class (1 for every 3 usec of air propagation time).\n"
    394 	"Valid values: 0 - 255.");
    395 
    396 static int handle_distance(struct nl80211_state *state,
    397 			struct nl_cb *cb,
    398 			struct nl_msg *msg,
    399 			int argc, char **argv,
    400 			enum id_input id)
    401 {
    402 	if (argc != 1)
    403 		return 1;
    404 
    405 	if (!*argv[0])
    406 		return 1;
    407 
    408 	if (strcmp("auto", argv[0]) == 0) {
    409 		NLA_PUT_FLAG(msg, NL80211_ATTR_WIPHY_DYN_ACK);
    410 	} else {
    411 		char *end;
    412 		unsigned int distance, coverage;
    413 
    414 		distance = strtoul(argv[0], &end, 10);
    415 
    416 		if (*end)
    417 			return 1;
    418 
    419 		/*
    420 		 * Divide double the distance by the speed of light
    421 		 * in m/usec (300) to get round-trip time in microseconds
    422 		 * and then divide the result by three to get coverage class
    423 		 * as specified in IEEE 802.11-2007 table 7-27.
    424 		 * Values are rounded upwards.
    425 		 */
    426 		coverage = (distance + 449) / 450;
    427 		if (coverage > 255)
    428 			return 1;
    429 
    430 		NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS, coverage);
    431 	}
    432 
    433 	return 0;
    434  nla_put_failure:
    435 	return -ENOBUFS;
    436 }
    437 COMMAND(set, distance, "<auto|distance>",
    438 	NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_distance,
    439 	"Enable ACK timeout estimation algorithm (dynack) or set appropriate\n"
    440 	"coverage class for given link distance in meters.\n"
    441 	"To disable dynack set valid value for coverage class.\n"
    442 	"Valid values: 0 - 114750");
    443 
    444 static int handle_txpower(struct nl80211_state *state,
    445 			  struct nl_cb *cb,
    446 			  struct nl_msg *msg,
    447 			  int argc, char **argv,
    448 			  enum id_input id)
    449 {
    450 	enum nl80211_tx_power_setting type;
    451 	int mbm;
    452 
    453 	/* get the required args */
    454 	if (argc != 1 && argc != 2)
    455 		return 1;
    456 
    457 	if (!strcmp(argv[0], "auto"))
    458 		type = NL80211_TX_POWER_AUTOMATIC;
    459 	else if (!strcmp(argv[0], "fixed"))
    460 		type = NL80211_TX_POWER_FIXED;
    461 	else if (!strcmp(argv[0], "limit"))
    462 		type = NL80211_TX_POWER_LIMITED;
    463 	else {
    464 		printf("Invalid parameter: %s\n", argv[0]);
    465 		return 2;
    466 	}
    467 
    468 	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_TX_POWER_SETTING, type);
    469 
    470 	if (type != NL80211_TX_POWER_AUTOMATIC) {
    471 		char *endptr;
    472 		if (argc != 2) {
    473 			printf("Missing TX power level argument.\n");
    474 			return 2;
    475 		}
    476 
    477 		mbm = strtol(argv[1], &endptr, 10);
    478 		if (*endptr)
    479 			return 2;
    480 		NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_TX_POWER_LEVEL, mbm);
    481 	} else if (argc != 1)
    482 		return 1;
    483 
    484 	return 0;
    485 
    486  nla_put_failure:
    487 	return -ENOBUFS;
    488 }
    489 COMMAND(set, txpower, "<auto|fixed|limit> [<tx power in mBm>]",
    490 	NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_txpower,
    491 	"Specify transmit power level and setting type.");
    492 COMMAND(set, txpower, "<auto|fixed|limit> [<tx power in mBm>]",
    493 	NL80211_CMD_SET_WIPHY, 0, CIB_NETDEV, handle_txpower,
    494 	"Specify transmit power level and setting type.");
    495 
    496 static int handle_antenna(struct nl80211_state *state,
    497 			  struct nl_cb *cb,
    498 			  struct nl_msg *msg,
    499 			  int argc, char **argv,
    500 			  enum id_input id)
    501 {
    502 	char *end;
    503 	uint32_t tx_ant = 0, rx_ant = 0;
    504 
    505 	if (argc == 1 && strcmp(argv[0], "all") == 0) {
    506 		tx_ant = 0xffffffff;
    507 		rx_ant = 0xffffffff;
    508 	} else if (argc == 1) {
    509 		tx_ant = rx_ant = strtoul(argv[0], &end, 0);
    510 		if (*end)
    511 			return 1;
    512 	}
    513 	else if (argc == 2) {
    514 		tx_ant = strtoul(argv[0], &end, 0);
    515 		if (*end)
    516 			return 1;
    517 		rx_ant = strtoul(argv[1], &end, 0);
    518 		if (*end)
    519 			return 1;
    520 	} else
    521 		return 1;
    522 
    523 	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_TX, tx_ant);
    524 	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_RX, rx_ant);
    525 
    526 	return 0;
    527 
    528  nla_put_failure:
    529 	return -ENOBUFS;
    530 }
    531 COMMAND(set, antenna, "<bitmap> | all | <tx bitmap> <rx bitmap>",
    532 	NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_antenna,
    533 	"Set a bitmap of allowed antennas to use for TX and RX.\n"
    534 	"The driver may reject antenna configurations it cannot support.");
    535