]>
Commit | Line | Data |
---|---|---|
aa0f5dbe AK |
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 | ||
34b23014 | 19 | static int handle_coalesce_enable(struct nl80211_state *state, |
aa0f5dbe AK |
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); | |
bfb64ed0 YY |
41 | if (!nl_rules) { |
42 | fclose(f); | |
aa0f5dbe | 43 | return -ENOBUFS; |
bfb64ed0 | 44 | } |
aa0f5dbe AK |
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" | |
4f7bb083 | 180 | "condition: 1/0 i.e. 'not match'/'match' the patterns\n" |
aa0f5dbe AK |
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 | |
34b23014 | 188 | handle_coalesce_disable(struct nl80211_state *state, |
aa0f5dbe AK |
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 | ||
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) | |
aa0f5dbe | 229 | printf("not match\n"); |
4f7bb083 AK |
230 | else |
231 | printf("match\n"); | |
aa0f5dbe AK |
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 | ||
34b23014 | 277 | static int handle_coalesce_show(struct nl80211_state *state, |
aa0f5dbe AK |
278 | struct nl_msg *msg, int argc, char **argv, |
279 | enum id_input id) | |
280 | { | |
34b23014 | 281 | register_handler(print_coalesce_handler, NULL); |
aa0f5dbe AK |
282 | |
283 | return 0; | |
284 | } | |
285 | COMMAND(coalesce, show, "", NL80211_CMD_GET_COALESCE, 0, CIB_PHY, handle_coalesce_show, | |
286 | "Show coalesce status."); |