#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.");