| #include <net/if.h> |
| #include <errno.h> |
| #include <string.h> |
| #include <stdio.h> |
| |
| #include <netlink/genl/genl.h> |
| #include <netlink/genl/family.h> |
| #include <netlink/genl/ctrl.h> |
| #include <netlink/msg.h> |
| #include <netlink/attr.h> |
| |
| #include <arpa/inet.h> |
| |
| #include "nl80211.h" |
| #include "iw.h" |
| |
| SECTION(coalesce); |
| |
| static int handle_coalesce_enable(struct nl80211_state *state, struct nl_cb *cb, |
| struct nl_msg *msg, int argc, char **argv, |
| enum id_input id) |
| { |
| struct nlattr *nl_rules, *nl_rule = NULL, *nl_pats, *nl_pat; |
| unsigned char *pat, *mask; |
| size_t patlen; |
| int patnum = 0, pkt_offset, err = 1; |
| char *eptr, *value1, *value2, *sptr = NULL, *end, buf[16768]; |
| enum nl80211_coalesce_condition condition; |
| FILE *f = fopen(argv[0], "r"); |
| enum { |
| PS_DELAY, |
| PS_CONDITION, |
| PS_PATTERNS |
| } parse_state = PS_DELAY; |
| int rule_num = 0; |
| |
| if (!f) |
| return 1; |
| |
| nl_rules = nla_nest_start(msg, NL80211_ATTR_COALESCE_RULE); |
| if (!nl_rules) { |
| fclose(f); |
| return -ENOBUFS; |
| } |
| |
| while (!feof(f)) { |
| char *eol; |
| |
| if (!fgets(buf, sizeof(buf), f)) |
| break; |
| |
| eol = strchr(buf + 5, '\r'); |
| if (eol) |
| *eol = 0; |
| eol = strchr(buf + 5, '\n'); |
| if (eol) |
| *eol = 0; |
| |
| switch (parse_state) { |
| case PS_DELAY: |
| if (strncmp(buf, "delay=", 6) == 0) { |
| char *delay = buf + 6; |
| |
| rule_num++; |
| nl_rule = nla_nest_start(msg, rule_num); |
| if (!nl_rule) |
| goto close; |
| |
| NLA_PUT_U32(msg, NL80211_ATTR_COALESCE_RULE_DELAY, |
| strtoul(delay, &end, 10)); |
| if (*end != '\0') |
| goto close; |
| parse_state = PS_CONDITION; |
| } else { |
| goto close; |
| } |
| break; |
| case PS_CONDITION: |
| if (strncmp(buf, "condition=", 10) == 0) { |
| char *cond = buf + 10; |
| |
| condition = strtoul(cond, &end, 10); |
| if (*end != '\0') |
| goto close; |
| NLA_PUT_U32(msg, NL80211_ATTR_COALESCE_RULE_CONDITION, |
| condition); |
| parse_state = PS_PATTERNS; |
| } else { |
| goto close; |
| } |
| break; |
| case PS_PATTERNS: |
| if (strncmp(buf, "patterns=", 9) == 0) { |
| char *cur_pat = buf + 9; |
| char *next_pat = strchr(buf + 9, ','); |
| |
| if (next_pat) { |
| *next_pat = 0; |
| next_pat++; |
| } |
| |
| nl_pats = nla_nest_start(msg, NL80211_ATTR_COALESCE_RULE_PKT_PATTERN); |
| while (1) { |
| value1 = strtok_r(cur_pat, "+", &sptr); |
| value2 = strtok_r(NULL, "+", &sptr); |
| |
| if (!value2) { |
| pkt_offset = 0; |
| if (!value1) |
| goto close; |
| value2 = value1; |
| } else { |
| pkt_offset = strtoul(value1, &eptr, 10); |
| if (eptr != value1 + strlen(value1)) |
| goto close; |
| } |
| |
| if (parse_hex_mask(value2, &pat, &patlen, &mask)) |
| goto close; |
| |
| nl_pat = nla_nest_start(msg, ++patnum); |
| NLA_PUT(msg, NL80211_PKTPAT_MASK, |
| DIV_ROUND_UP(patlen, 8), mask); |
| NLA_PUT(msg, NL80211_PKTPAT_PATTERN, patlen, pat); |
| NLA_PUT_U32(msg, NL80211_PKTPAT_OFFSET, |
| pkt_offset); |
| nla_nest_end(msg, nl_pat); |
| free(mask); |
| free(pat); |
| |
| if (!next_pat) |
| break; |
| cur_pat = next_pat; |
| next_pat = strchr(cur_pat, ','); |
| if (next_pat) { |
| *next_pat = 0; |
| next_pat++; |
| } |
| } |
| nla_nest_end(msg, nl_pats); |
| nla_nest_end(msg, nl_rule); |
| parse_state = PS_DELAY; |
| |
| } else { |
| goto close; |
| } |
| break; |
| default: |
| if (buf[0] == '#') |
| continue; |
| goto close; |
| } |
| } |
| |
| if (parse_state == PS_DELAY) |
| err = 0; |
| else |
| err = 1; |
| goto close; |
| nla_put_failure: |
| err = -ENOBUFS; |
| close: |
| fclose(f); |
| nla_nest_end(msg, nl_rules); |
| return err; |
| } |
| |
| COMMAND(coalesce, enable, "<config-file>", |
| NL80211_CMD_SET_COALESCE, 0, CIB_PHY, handle_coalesce_enable, |
| "Enable coalesce with given configuration.\n" |
| "The configuration file contains coalesce rules:\n" |
| " delay=<delay>\n" |
| " condition=<condition>\n" |
| " patterns=<[offset1+]<pattern1>,<[offset2+]<pattern2>,...>\n" |
| " delay=<delay>\n" |
| " condition=<condition>\n" |
| " patterns=<[offset1+]<pattern1>,<[offset2+]<pattern2>,...>\n" |
| " ...\n" |
| "delay: maximum coalescing delay in msec.\n" |
| "condition: 1/0 i.e. 'not match'/'match' the patterns\n" |
| "patterns: each pattern is given as a bytestring with '-' in\n" |
| "places where any byte may be present, e.g. 00:11:22:-:44 will\n" |
| "match 00:11:22:33:44 and 00:11:22:33:ff:44 etc. Offset and\n" |
| "pattern should be separated by '+', e.g. 18+43:34:00:12 will\n" |
| "match '43:34:00:12' after 18 bytes of offset in Rx packet.\n"); |
| |
| 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) |
| { |
| /* just a set w/o coalesce attribute */ |
| return 0; |
| } |
| COMMAND(coalesce, disable, "", NL80211_CMD_SET_COALESCE, 0, CIB_PHY, |
| handle_coalesce_disable, "Disable coalesce."); |
| |
| static int print_coalesce_handler(struct nl_msg *msg, void *arg) |
| { |
| struct nlattr *attrs[NL80211_ATTR_MAX + 1]; |
| struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); |
| struct nlattr *pattern, *rule; |
| int rem_pattern, rem_rule; |
| enum nl80211_coalesce_condition condition; |
| int delay; |
| |
| nla_parse(attrs, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), |
| genlmsg_attrlen(gnlh, 0), NULL); |
| |
| if (!attrs[NL80211_ATTR_COALESCE_RULE]) { |
| printf("Coalesce is disabled.\n"); |
| return NL_SKIP; |
| } |
| |
| printf("Coalesce is enabled:\n"); |
| |
| nla_for_each_nested(rule, attrs[NL80211_ATTR_COALESCE_RULE], rem_rule) { |
| struct nlattr *ruleattr[NUM_NL80211_ATTR_COALESCE_RULE]; |
| |
| nla_parse(ruleattr, NL80211_ATTR_COALESCE_RULE_MAX, |
| nla_data(rule), nla_len(rule), NULL); |
| |
| delay = nla_get_u32(ruleattr[NL80211_ATTR_COALESCE_RULE_DELAY]); |
| condition = |
| nla_get_u32(ruleattr[NL80211_ATTR_COALESCE_RULE_CONDITION]); |
| |
| printf("Rule - max coalescing delay: %dmsec condition:", delay); |
| if (condition) |
| printf("not match\n"); |
| else |
| printf("match\n"); |
| |
| if (ruleattr[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN]) { |
| nla_for_each_nested(pattern, |
| ruleattr[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN], |
| rem_pattern) { |
| struct nlattr *patattr[NUM_NL80211_PKTPAT]; |
| int i, patlen, masklen, pkt_offset; |
| uint8_t *mask, *pat; |
| |
| nla_parse(patattr, MAX_NL80211_PKTPAT, |
| nla_data(pattern), nla_len(pattern), |
| NULL); |
| if (!patattr[NL80211_PKTPAT_MASK] || |
| !patattr[NL80211_PKTPAT_PATTERN] || |
| !patattr[NL80211_PKTPAT_OFFSET]) { |
| printf(" * (invalid pattern specification)\n"); |
| continue; |
| } |
| masklen = nla_len(patattr[NL80211_PKTPAT_MASK]); |
| patlen = nla_len(patattr[NL80211_PKTPAT_PATTERN]); |
| pkt_offset = nla_get_u32(patattr[NL80211_PKTPAT_OFFSET]); |
| if (DIV_ROUND_UP(patlen, 8) != masklen) { |
| printf(" * (invalid pattern specification)\n"); |
| continue; |
| } |
| printf(" * packet offset: %d", pkt_offset); |
| printf(" pattern: "); |
| pat = nla_data(patattr[NL80211_PKTPAT_PATTERN]); |
| mask = nla_data(patattr[NL80211_PKTPAT_MASK]); |
| for (i = 0; i < patlen; i++) { |
| if (mask[i / 8] & (1 << (i % 8))) |
| printf("%.2x", pat[i]); |
| else |
| printf("--"); |
| if (i != patlen - 1) |
| printf(":"); |
| } |
| printf("\n"); |
| } |
| } |
| } |
| |
| return NL_SKIP; |
| } |
| |
| static int handle_coalesce_show(struct nl80211_state *state, struct nl_cb *cb, |
| struct nl_msg *msg, int argc, char **argv, |
| enum id_input id) |
| { |
| nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, |
| print_coalesce_handler, NULL); |
| |
| return 0; |
| } |
| COMMAND(coalesce, show, "", NL80211_CMD_GET_COALESCE, 0, CIB_PHY, handle_coalesce_show, |
| "Show coalesce status."); |