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 
17 SECTION(coalesce);
18 
handle_coalesce_enable(struct nl80211_state * state,struct nl_cb * cb,struct nl_msg * msg,int argc,char ** argv,enum id_input id)19 static 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;
160 nla_put_failure:
161 	err = -ENOBUFS;
162 close:
163 	fclose(f);
164 	nla_nest_end(msg, nl_rules);
165 	return err;
166 }
167 
168 COMMAND(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 
187 static int
handle_coalesce_disable(struct nl80211_state * state,struct nl_cb * cb,struct nl_msg * msg,int argc,char ** argv,enum id_input id)188 handle_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 }
195 COMMAND(coalesce, disable, "", NL80211_CMD_SET_COALESCE, 0, CIB_PHY,
196 	handle_coalesce_disable, "Disable coalesce.");
197 
print_coalesce_handler(struct nl_msg * msg,void * arg)198 static 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 
handle_coalesce_show(struct nl80211_state * state,struct nl_cb * cb,struct nl_msg * msg,int argc,char ** argv,enum id_input id)277 static 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 }
286 COMMAND(coalesce, show, "", NL80211_CMD_GET_COALESCE, 0, CIB_PHY, handle_coalesce_show,
287 	"Show coalesce status.");
288