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