nexmon – Rev 1

Subversion Repositories:
Rev:
#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.");