1#include <net/if.h>
2#include <errno.h>
3#include <string.h>
4#include <stdio.h>
5
6#include <netlink/genl/genl.h>
7#include <netlink/genl/family.h>
8#include <netlink/genl/ctrl.h>
9#include <netlink/msg.h>
10#include <netlink/attr.h>
11
12#include <arpa/inet.h>
13
14#include "nl80211.h"
15#include "iw.h"
16
17SECTION(coalesce);
18
19static int handle_coalesce_enable(struct nl80211_state *state, struct nl_cb *cb,
20				  struct nl_msg *msg, int argc, char **argv,
21				  enum id_input id)
22{
23	struct nlattr *nl_rules, *nl_rule = NULL, *nl_pats, *nl_pat;
24	unsigned char *pat, *mask;
25	size_t patlen;
26	int patnum = 0, pkt_offset, err = 1;
27	char *eptr, *value1, *value2, *sptr = NULL, *end, buf[16768];
28	enum nl80211_coalesce_condition condition;
29	FILE *f = fopen(argv[0], "r");
30	enum {
31		PS_DELAY,
32		PS_CONDITION,
33		PS_PATTERNS
34	} parse_state = PS_DELAY;
35	int rule_num = 0;
36
37	if (!f)
38		return 1;
39
40	nl_rules = nla_nest_start(msg, NL80211_ATTR_COALESCE_RULE);
41	if (!nl_rules) {
42		fclose(f);
43		return -ENOBUFS;
44	}
45
46	while (!feof(f)) {
47		char *eol;
48
49		if (!fgets(buf, sizeof(buf), f))
50			break;
51
52		eol = strchr(buf + 5, '\r');
53		if (eol)
54			*eol = 0;
55		eol = strchr(buf + 5, '\n');
56		if (eol)
57			*eol = 0;
58
59		switch (parse_state) {
60		case PS_DELAY:
61			if (strncmp(buf, "delay=", 6) == 0) {
62				char *delay = buf + 6;
63
64				rule_num++;
65				nl_rule = nla_nest_start(msg, rule_num);
66				if (!nl_rule)
67					goto close;
68
69				NLA_PUT_U32(msg, NL80211_ATTR_COALESCE_RULE_DELAY,
70					    strtoul(delay, &end, 10));
71				if (*end != '\0')
72					goto close;
73				parse_state = PS_CONDITION;
74			} else {
75				goto close;
76			}
77			break;
78		case PS_CONDITION:
79			if (strncmp(buf, "condition=", 10) == 0) {
80				char *cond = buf + 10;
81
82				condition = strtoul(cond, &end, 10);
83				if (*end != '\0')
84					goto close;
85				NLA_PUT_U32(msg, NL80211_ATTR_COALESCE_RULE_CONDITION,
86					    condition);
87				parse_state = PS_PATTERNS;
88			} else {
89				goto close;
90			}
91			break;
92		case PS_PATTERNS:
93			if (strncmp(buf, "patterns=", 9) == 0) {
94				char *cur_pat = buf + 9;
95				char *next_pat = strchr(buf + 9, ',');
96
97				if (next_pat) {
98					*next_pat = 0;
99					next_pat++;
100				}
101
102				nl_pats = nla_nest_start(msg, NL80211_ATTR_COALESCE_RULE_PKT_PATTERN);
103				while (1) {
104					value1 = strtok_r(cur_pat, "+", &sptr);
105					value2 = strtok_r(NULL, "+", &sptr);
106
107					if (!value2) {
108						pkt_offset = 0;
109						if (!value1)
110							goto close;
111						value2 = value1;
112					} else {
113						pkt_offset = strtoul(value1, &eptr, 10);
114						if (eptr != value1 + strlen(value1))
115							goto close;
116					}
117
118					if (parse_hex_mask(value2, &pat, &patlen, &mask))
119						goto close;
120
121					nl_pat = nla_nest_start(msg, ++patnum);
122					NLA_PUT(msg, NL80211_PKTPAT_MASK,
123						DIV_ROUND_UP(patlen, 8), mask);
124					NLA_PUT(msg, NL80211_PKTPAT_PATTERN, patlen, pat);
125					NLA_PUT_U32(msg, NL80211_PKTPAT_OFFSET,
126						    pkt_offset);
127					nla_nest_end(msg, nl_pat);
128					free(mask);
129					free(pat);
130
131					if (!next_pat)
132						break;
133					cur_pat = next_pat;
134					next_pat = strchr(cur_pat, ',');
135					if (next_pat) {
136						*next_pat = 0;
137						next_pat++;
138					}
139				}
140				nla_nest_end(msg, nl_pats);
141				nla_nest_end(msg, nl_rule);
142				parse_state = PS_DELAY;
143
144			} else {
145				goto close;
146			}
147			break;
148		default:
149			if (buf[0] == '#')
150				continue;
151			goto close;
152		}
153	}
154
155	if (parse_state == PS_DELAY)
156		err = 0;
157	else
158		err = 1;
159	goto close;
160nla_put_failure:
161	err = -ENOBUFS;
162close:
163	fclose(f);
164	nla_nest_end(msg, nl_rules);
165	return err;
166}
167
168COMMAND(coalesce, enable, "<config-file>",
169	NL80211_CMD_SET_COALESCE, 0, CIB_PHY, handle_coalesce_enable,
170	"Enable coalesce with given configuration.\n"
171	"The configuration file contains coalesce rules:\n"
172	"  delay=<delay>\n"
173	"  condition=<condition>\n"
174	"  patterns=<[offset1+]<pattern1>,<[offset2+]<pattern2>,...>\n"
175	"  delay=<delay>\n"
176	"  condition=<condition>\n"
177	"  patterns=<[offset1+]<pattern1>,<[offset2+]<pattern2>,...>\n"
178	"  ...\n"
179	"delay: maximum coalescing delay in msec.\n"
180	"condition: 1/0 i.e. 'not match'/'match' the patterns\n"
181	"patterns: each pattern is given as a bytestring with '-' in\n"
182	"places where any byte may be present, e.g. 00:11:22:-:44 will\n"
183	"match 00:11:22:33:44 and 00:11:22:33:ff:44 etc. Offset and\n"
184	"pattern should be separated by '+', e.g. 18+43:34:00:12 will\n"
185	"match '43:34:00:12' after 18 bytes of offset in Rx packet.\n");
186
187static int
188handle_coalesce_disable(struct nl80211_state *state, struct nl_cb *cb,
189			struct nl_msg *msg, int argc, char **argv,
190			enum id_input id)
191{
192	/* just a set w/o coalesce attribute */
193	return 0;
194}
195COMMAND(coalesce, disable, "", NL80211_CMD_SET_COALESCE, 0, CIB_PHY,
196	handle_coalesce_disable, "Disable coalesce.");
197
198static int print_coalesce_handler(struct nl_msg *msg, void *arg)
199{
200	struct nlattr *attrs[NL80211_ATTR_MAX + 1];
201	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
202	struct nlattr *pattern, *rule;
203	int rem_pattern, rem_rule;
204	enum nl80211_coalesce_condition condition;
205	int delay;
206
207	nla_parse(attrs, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
208		  genlmsg_attrlen(gnlh, 0), NULL);
209
210	if (!attrs[NL80211_ATTR_COALESCE_RULE]) {
211		printf("Coalesce is disabled.\n");
212		return NL_SKIP;
213	}
214
215	printf("Coalesce is enabled:\n");
216
217	nla_for_each_nested(rule, attrs[NL80211_ATTR_COALESCE_RULE], rem_rule) {
218		struct nlattr *ruleattr[NUM_NL80211_ATTR_COALESCE_RULE];
219
220		nla_parse(ruleattr, NL80211_ATTR_COALESCE_RULE_MAX,
221			  nla_data(rule), nla_len(rule), NULL);
222
223		delay = nla_get_u32(ruleattr[NL80211_ATTR_COALESCE_RULE_DELAY]);
224		condition =
225		     nla_get_u32(ruleattr[NL80211_ATTR_COALESCE_RULE_CONDITION]);
226
227		printf("Rule - max coalescing delay: %dmsec condition:", delay);
228		if (condition)
229			printf("not match\n");
230		else
231			printf("match\n");
232
233		if (ruleattr[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN]) {
234			nla_for_each_nested(pattern,
235					    ruleattr[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN],
236					    rem_pattern) {
237				struct nlattr *patattr[NUM_NL80211_PKTPAT];
238				int i, patlen, masklen, pkt_offset;
239				uint8_t *mask, *pat;
240
241				nla_parse(patattr, MAX_NL80211_PKTPAT,
242					  nla_data(pattern), nla_len(pattern),
243					  NULL);
244				if (!patattr[NL80211_PKTPAT_MASK] ||
245				    !patattr[NL80211_PKTPAT_PATTERN] ||
246				    !patattr[NL80211_PKTPAT_OFFSET]) {
247					printf(" * (invalid pattern specification)\n");
248					continue;
249				}
250				masklen = nla_len(patattr[NL80211_PKTPAT_MASK]);
251				patlen = nla_len(patattr[NL80211_PKTPAT_PATTERN]);
252				pkt_offset = nla_get_u32(patattr[NL80211_PKTPAT_OFFSET]);
253				if (DIV_ROUND_UP(patlen, 8) != masklen) {
254					printf(" * (invalid pattern specification)\n");
255					continue;
256				}
257				printf(" * packet offset: %d", pkt_offset);
258				printf(" pattern: ");
259				pat = nla_data(patattr[NL80211_PKTPAT_PATTERN]);
260				mask = nla_data(patattr[NL80211_PKTPAT_MASK]);
261				for (i = 0; i < patlen; i++) {
262					if (mask[i / 8] & (1 << (i % 8)))
263						printf("%.2x", pat[i]);
264					else
265						printf("--");
266					if (i != patlen - 1)
267						printf(":");
268				}
269				printf("\n");
270			}
271		}
272	}
273
274	return NL_SKIP;
275}
276
277static int handle_coalesce_show(struct nl80211_state *state, struct nl_cb *cb,
278			      struct nl_msg *msg, int argc, char **argv,
279			      enum id_input id)
280{
281	nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM,
282		  print_coalesce_handler, NULL);
283
284	return 0;
285}
286COMMAND(coalesce, show, "", NL80211_CMD_GET_COALESCE, 0, CIB_PHY, handle_coalesce_show,
287	"Show coalesce status.");
288