Home | History | Annotate | Download | only in iw
      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 		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
    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 
    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 
    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