unsigned char *parse_hex(char *hex, size_t *outlen) { unsigned char *result; if (parse_hex_mask(hex, &result, outlen, NULL)) return NULL; return result; }
static int handle_wowlan_enable(struct nl80211_state *state, struct nl_cb *cb, struct nl_msg *msg, int argc, char **argv, enum id_input id) { struct nlattr *wowlan, *pattern; struct nl_msg *patterns = NULL; enum { PS_REG, PS_PAT, } parse_state = PS_REG; int err = -ENOBUFS; unsigned char *pat, *mask; size_t patlen; int patnum = 0; wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS); if (!wowlan) return -ENOBUFS; while (argc) { switch (parse_state) { case PS_REG: if (strcmp(argv[0], "any") == 0) NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY); else if (strcmp(argv[0], "disconnect") == 0) NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT); else if (strcmp(argv[0], "magic-packet") == 0) NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT); else if (strcmp(argv[0], "gtk-rekey-failure") == 0) NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE); else if (strcmp(argv[0], "eap-identity-request") == 0) NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST); else if (strcmp(argv[0], "4way-handshake") == 0) NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE); else if (strcmp(argv[0], "rfkill-release") == 0) NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE); else if (strcmp(argv[0], "patterns") == 0) { parse_state = PS_PAT; patterns = nlmsg_alloc(); if (!patterns) { err = -ENOMEM; goto nla_put_failure; } } else { err = 1; goto nla_put_failure; } break; case PS_PAT: if (parse_hex_mask(argv[0], &pat, &patlen, &mask)) { err = 1; goto nla_put_failure; } pattern = nla_nest_start(patterns, ++patnum); NLA_PUT(patterns, NL80211_WOWLAN_PKTPAT_MASK, DIV_ROUND_UP(patlen, 8), mask); NLA_PUT(patterns, NL80211_WOWLAN_PKTPAT_PATTERN, patlen, pat); nla_nest_end(patterns, pattern); free(mask); free(pat); break; } argv++; argc--; } if (patterns) nla_put_nested(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN, patterns); nla_nest_end(msg, wowlan); err = 0; nla_put_failure: nlmsg_free(patterns); return err; }
static int handle_wowlan_enable(struct nl80211_state *state, struct nl_msg *msg, int argc, char **argv, enum id_input id) { struct nlattr *wowlan, *pattern; struct nl_msg *patterns = NULL; enum { PS_REG, PS_PAT, } parse_state = PS_REG; int err = -ENOBUFS; unsigned char *pat, *mask; size_t patlen; int patnum = 0, pkt_offset; char *eptr, *value1, *value2, *sptr = NULL; wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS); if (!wowlan) return -ENOBUFS; while (argc) { switch (parse_state) { case PS_REG: if (strcmp(argv[0], "any") == 0) NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY); else if (strcmp(argv[0], "disconnect") == 0) NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT); else if (strcmp(argv[0], "magic-packet") == 0) NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT); else if (strcmp(argv[0], "gtk-rekey-failure") == 0) NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE); else if (strcmp(argv[0], "eap-identity-request") == 0) NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST); else if (strcmp(argv[0], "4way-handshake") == 0) NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE); else if (strcmp(argv[0], "rfkill-release") == 0) NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE); else if (strcmp(argv[0], "tcp") == 0) { argv++; argc--; if (!argc) { err = 1; goto nla_put_failure; } err = wowlan_parse_tcp_file(msg, argv[0]); if (err) goto nla_put_failure; } else if (strcmp(argv[0], "patterns") == 0) { parse_state = PS_PAT; patterns = nlmsg_alloc(); if (!patterns) { err = -ENOMEM; goto nla_put_failure; } } else if (strcmp(argv[0], "net-detect") == 0) { argv++; argc--; if (!argc) { err = 1; goto nla_put_failure; } err = wowlan_parse_net_detect(msg, &argc, &argv); if (err) goto nla_put_failure; continue; } else { err = 1; goto nla_put_failure; } break; case PS_PAT: value1 = strtok_r(argv[0], "+", &sptr); value2 = strtok_r(NULL, "+", &sptr); if (!value2) { pkt_offset = 0; value2 = value1; } else { pkt_offset = strtoul(value1, &eptr, 10); if (eptr != value1 + strlen(value1)) { err = 1; goto nla_put_failure; } } if (parse_hex_mask(value2, &pat, &patlen, &mask)) { err = 1; goto nla_put_failure; } pattern = nla_nest_start(patterns, ++patnum); NLA_PUT(patterns, NL80211_PKTPAT_MASK, DIV_ROUND_UP(patlen, 8), mask); NLA_PUT(patterns, NL80211_PKTPAT_PATTERN, patlen, pat); NLA_PUT_U32(patterns, NL80211_PKTPAT_OFFSET, pkt_offset); nla_nest_end(patterns, pattern); free(mask); free(pat); break; } argv++; argc--; } if (patterns) nla_put_nested(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN, patterns); nla_nest_end(msg, wowlan); err = 0; nla_put_failure: nlmsg_free(patterns); return err; }
static int wowlan_parse_tcp_file(struct nl_msg *msg, const char *fn) { char buf[16768]; int err = 1; FILE *f = fopen(fn, "r"); struct nlattr *tcp; if (!f) return 1; tcp = nla_nest_start(msg, NL80211_WOWLAN_TRIG_TCP_CONNECTION); if (!tcp) goto nla_put_failure; 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; if (strncmp(buf, "source=", 7) == 0) { struct in_addr in_addr; char *addr = buf + 7; char *port = strchr(buf + 7, ':'); if (port) { *port = 0; port++; } if (inet_aton(addr, &in_addr) == 0) goto close; NLA_PUT_U32(msg, NL80211_WOWLAN_TCP_SRC_IPV4, in_addr.s_addr); if (port) NLA_PUT_U16(msg, NL80211_WOWLAN_TCP_SRC_PORT, atoi(port)); } else if (strncmp(buf, "dest=", 5) == 0) { struct in_addr in_addr; char *addr = buf + 5; char *port = strchr(buf + 5, ':'); char *mac; unsigned char macbuf[6]; if (!port) goto close; *port = 0; port++; mac = strchr(port, '@'); if (!mac) goto close; *mac = 0; mac++; if (inet_aton(addr, &in_addr) == 0) goto close; NLA_PUT_U32(msg, NL80211_WOWLAN_TCP_DST_IPV4, in_addr.s_addr); NLA_PUT_U16(msg, NL80211_WOWLAN_TCP_DST_PORT, atoi(port)); if (mac_addr_a2n(macbuf, mac)) goto close; NLA_PUT(msg, NL80211_WOWLAN_TCP_DST_MAC, 6, macbuf); } else if (strncmp(buf, "data=", 5) == 0) { size_t len; unsigned char *pkt = parse_hex(buf + 5, &len); if (!pkt) goto close; NLA_PUT(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD, len, pkt); free(pkt); } else if (strncmp(buf, "data.interval=", 14) == 0) { NLA_PUT_U32(msg, NL80211_WOWLAN_TCP_DATA_INTERVAL, atoi(buf + 14)); } else if (strncmp(buf, "wake=", 5) == 0) { unsigned char *pat, *mask; size_t patlen; if (parse_hex_mask(buf + 5, &pat, &patlen, &mask)) goto close; NLA_PUT(msg, NL80211_WOWLAN_TCP_WAKE_MASK, DIV_ROUND_UP(patlen, 8), mask); NLA_PUT(msg, NL80211_WOWLAN_TCP_WAKE_PAYLOAD, patlen, pat); free(mask); free(pat); } else if (strncmp(buf, "data.seq=", 9) == 0) { struct nl80211_wowlan_tcp_data_seq seq = {}; char *len, *offs, *start; len = buf + 9; offs = strchr(len, ','); if (!offs) goto close; *offs = 0; offs++; start = strchr(offs, ','); if (start) { *start = 0; start++; seq.start = atoi(start); } seq.len = atoi(len); seq.offset = atoi(offs); NLA_PUT(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ, sizeof(seq), &seq); } else if (strncmp(buf, "data.tok=", 9) == 0) { struct nl80211_wowlan_tcp_data_token *tok; size_t stream_len; char *len, *offs, *toks; unsigned char *stream; len = buf + 9; offs = strchr(len, ','); if (!offs) goto close; *offs = 0; offs++; toks = strchr(offs, ','); if (!toks) goto close; *toks = 0; toks++; stream = parse_hex(toks, &stream_len); if (!stream) goto close; tok = malloc(sizeof(*tok) + stream_len); if (!tok) { free(stream); err = -ENOMEM; goto close; } tok->len = atoi(len); tok->offset = atoi(offs); memcpy(tok->token_stream, stream, stream_len); NLA_PUT(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN, sizeof(*tok) + stream_len, tok); free(stream); free(tok); } else { if (buf[0] == '#') continue; goto close; } } err = 0; goto close; nla_put_failure: err = -ENOBUFS; close: fclose(f); nla_nest_end(msg, tcp); return err; }
static int handle_coalesce_enable(struct nl80211_state *state, 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; }