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