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
18static 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}
33COMMAND(set, name, "<new name>", NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_name,
34	"Rename this wireless device.");
35
36static 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
94static 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
147static 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}
154COMMAND(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.");
158COMMAND(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
162static 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}
169COMMAND(set, channel, "<channel> [HT20|HT40+|HT40-]",
170	NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_chan, NULL);
171COMMAND(set, channel, "<channel> [HT20|HT40+|HT40-]",
172	NL80211_CMD_SET_WIPHY, 0, CIB_NETDEV, handle_chan, NULL);
173
174static 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}
202COMMAND(set, frag, "<fragmentation threshold|off>",
203	NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_fragmentation,
204	"Set fragmentation threshold.");
205
206static 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}
234COMMAND(set, rts, "<rts threshold|off>",
235	NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_rts,
236	"Set rts threshold.");
237
238static 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}
298COMMAND(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
305int 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
320static 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}
357COMMAND(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
364static 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}
391COMMAND(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
396static 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}
437COMMAND(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
444static 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}
489COMMAND(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.");
492COMMAND(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
496static 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}
531COMMAND(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