Here's a simple iw patch that shows the error message string: --- a/iw.c +++ b/iw.c @@ -62,6 +62,10 @@ static int nl80211_init(struct nl80211_state *state) nl_socket_set_buffer_size(state->nl_sock, 8192, 8192); + /* try to set NETLINK_EXT_ACK to 1 (ignore errors) */ + err = 1; + setsockopt(nl_socket_get_fd(state->nl_sock), SOL_NETLINK, 11, &err, sizeof(err)); + state->nl80211_id = genl_ctrl_resolve(state->nl_sock, "nl80211"); if (state->nl80211_id < 0) { fprintf(stderr, "nl80211 not found.\n"); @@ -270,11 +274,45 @@ static int phy_lookup(char *name) return atoi(buf); } +#ifndef NETLINK_EXT_ACK +enum nlmsgerr_attrs { + NLMSGERR_ATTR_UNUSED, + NLMSGERR_ATTR_MSG, + NLMSGERR_ATTR_OFFS, + NLMSGERR_ATTR_ATTR, + NLMSGERR_ATTR_COOKIE, + + NUM_NLMSGERR_ATTRS, + NLMSGERR_ATTR_MAX = NUM_NLMSGERR_ATTRS - 1 +}; +#endif + static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) { + struct nlmsghdr *nlh = (struct nlmsghdr *)err - 1; + int len = nlh->nlmsg_len; + struct nlattr *attrs; + struct nlattr *tb[NUM_NLMSGERR_ATTRS]; int *ret = arg; + int ack_len = sizeof(*nlh) + sizeof(int) + err->msg.nlmsg_len; + *ret = err->error; + + if (len <= ack_len) + return NL_STOP; + + attrs = (void *)((unsigned char *)nlh + ack_len); + len -= ack_len; + + nla_parse(tb, NLMSGERR_ATTR_MAX, attrs, len, NULL); + if (tb[NLMSGERR_ATTR_MSG]) { + len = strnlen((char *)nla_data(tb[NLMSGERR_ATTR_MSG]), + nla_len(tb[NLMSGERR_ATTR_MSG])); + fprintf(stderr, "kernel reports: %*s\n", len, + (char *)nla_data(tb[NLMSGERR_ATTR_MSG])); + } + return NL_STOP; } johannes