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