最近在调试Hostapd,尝试通过配置使能一个支持MLO的AP,不过不知道hostapd conf里面哪些选项开启后可以使能,所以对Hostapd做一个整体解析.
本篇分析介绍hostapd的netlink信息如何发送到内核、内核如何处理这些信息、处理后如何调用cfg80211,内核如何发送cfg80211信息,驱动如何处理cfg80211信息,最后如何start_ap。
Hostapd的nl命令收发函数:
cpp
int send_and_recv(struct nl80211_global *global,
struct nl_sock *nl_handle, struct nl_msg *msg,
int (*valid_handler)(struct nl_msg *, void *),
void *valid_data,
int (*ack_handler_custom)(struct nl_msg *, void *),
void *ack_data,
struct nl80211_err_info *err_info)
{
struct nl_cb *cb, *s_nl_cb;
struct nl80211_ack_err_args err;
int opt;
if (!msg)
return -ENOMEM;
err.err = -ENOMEM;
s_nl_cb = nl_socket_get_cb(nl_handle);
cb = nl_cb_clone(s_nl_cb);
nl_cb_put(s_nl_cb);
if (!cb)
goto out;
/* try to set NETLINK_EXT_ACK to 1, ignoring errors */
opt = 1;
setsockopt(nl_socket_get_fd(nl_handle), SOL_NETLINK,
NETLINK_EXT_ACK, &opt, sizeof(opt));
/* try to set NETLINK_CAP_ACK to 1, ignoring errors */
opt = 1;
setsockopt(nl_socket_get_fd(nl_handle), SOL_NETLINK,
NETLINK_CAP_ACK, &opt, sizeof(opt));
err.err = nl_send_auto_complete(nl_handle, msg);
if (err.err < 0) {
wpa_printf(MSG_INFO,
"nl80211: nl_send_auto_complete() failed: %s",
nl_geterror(err.err));
/* Need to convert libnl error code to an errno value. For now,
* just hardcode this to EBADF; the real error reason is shown
* in that error print above. */
err.err = -EBADF;
goto out;
}
err.err = 1;
err.orig_msg = msg;
err.err_info = err_info;
nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err);
nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err.err);
if (ack_handler_custom) {
struct nl80211_ack_ext_arg *ext_arg = ack_data;
ext_arg->err = &err.err;
nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM,
ack_handler_custom, ack_data);
} else {
nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err);
}
if (valid_handler)
nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM,
valid_handler, valid_data);
while (err.err > 0) {
int res = nl_recvmsgs(nl_handle, cb);
if (res == -NLE_DUMP_INTR) {
/* Most likely one of the nl80211 dump routines hit a
* case where internal results changed while the dump
* was being sent. The most common known case for this
* is scan results fetching while associated were every
* received Beacon frame from the AP may end up
* incrementing bss_generation. This
* NL80211_CMD_GET_SCAN case tries again in the caller;
* other cases (of which there are no known common ones)
* will stop and return an error. */
wpa_printf(MSG_DEBUG, "nl80211: %s; convert to -EAGAIN",
nl_geterror(res));
err.err = -EAGAIN;
} else if (res < 0) {
wpa_printf(MSG_INFO,
"nl80211: %s->nl_recvmsgs failed: %d (%s)",
__func__, res, nl_geterror(res));
}
}
out:
nl_cb_put(cb);
/* Always clear the message as it can potentially contain keys */
nl80211_nlmsg_clear(msg);
nlmsg_free(msg);
return err.err;
}
其中涉及的nl函数及解析
cpp
nl_socket_get_cb(nl_handle);
nl_cb_clone(s_nl_cb);
nl_cb_put(s_nl_cb);
nl_socket_get_fd(nl_handle), SOL_NETLINK,
nl_send_auto_complete(nl_handle, msg);
nl_geterror(err.err));
nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err);
nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err.err);
nl_recvmsgs(nl_handle, cb);
nl80211_nlmsg_clear(msg);
nlmsg_free(msg);
nla_put_u32
nl_socket_get_cb
用于获取nl_sock对应的回调函数
linbl还可为每个nl_sock设置消息处理回调函数,当该socket上收到消息后,就会回调此函数进行处理。 回调函数及其重写函数都封装在结构体 struct nl_cb中
cpp
struct nl_cb{
nl_recvmsg_msg_cb_t cb_set[NL_CB_TYPE_MAX+1];
void * cb_args[NL_CB_TYPE_MAX+1];
nl_recvmsg_err_cb_t cb_err;
void *cb_err_arg;
/** May be used to replace nl_recvmsgs with your own implementation in all internal calls to nl_recvmsgs. */
int (*cb_recvmsgs_ow)(struct nl_sock *, struct nl_cb *);
/** Overwrite internal calls to nl_recv, must return the number of octets read and allocate a buffer for the received data. */
int (*cb_recv_ow)(struct nl_sock *, struct sockaddr_nl *, unsigned char **, struct ucred **);
/** Overwrites internal calls to nl_send, must send the netlink message. */
int (*cb_send_ow)(struct nl_sock *, struct nl_msg *);
int cb_refcnt;
};
设置回调:void nl_socket_set_cb(struct nl_sock *sk, struct nl_cb *cb);
获取该nl_sock设置的回调函数信息:struct nl_cb *nl_socket_get_cb(const struct nl_sock *sk);
nl_cb_clone
初始化时的函数,
struct nl_cb *nl_cb_alloc(enum nl_cb_kind kind);//分配新的nl_cb,返回新nl_cb地址或者NULL
struct nl_cb *nl_cb_clone(struct nl_cb *orig) ; //根据给定nl_cb复制新的nl_cb
nl_cb_put
nl_socket_get_fd
获取nl_sock对应的文件描述符fd
介于netlink采用BSD socket,因此每个socket对应一个文件描述符
#include <netlink/socket.h>
int nl_socket_get_fd(const struct nl_sock *sk);
int nl_socket_set_nonblocking(const struct nl_sock *sk); //若socket用于接收notification,则最好设为非阻塞并定期查询
nl_send_auto_complete
一个nl message发送函数。
(1) nl_send_auto_complete()将 自动填充netlink message header,并根据nl_sock里面的选项或地址设置完成寻址,而后调用 nl_send()。
ps:所有nl_send_auto()内部调用都会使用nl_send()传输信息。如果nl_send()不能满足要求,可以调用nl_cb_overwrite_send()重写自己的send函数。
(2) nl_send()将netlink message插入到struct iovec结构体中,而后调用 nl_send_iovec();
ps: struct iovec结构体定义一个向量元素, 对于每一个传输的元素,指针成员iov_base指向一个缓冲区,这个缓冲区是存放的是readv所接收的数据或是writev将要发送的数据。成员iov_len在各种情况下分别确定了接收的最大长度以及实际写入的长度。
struct iovec{
void *iov_base; /* Pointer to data. */
size_t iov_len; /* Length of data. */
};
-
int readv(int fd, const struct iovec *vector, int count);
-
int writev(int fd, const struct iovec *vector, int count);
(3)nl_send_iovec()将利用nl_msg填充msghdr结构体,并检查寻址状态,而后调用nl_sendmsg()继续发送数据
(4)nl_sendmsg()调用sendmsg()下发数据
通过netlink socket发送raw data:
int nl_sendto(struct nl_sock *sock, void *buf, size_t size);
nl_geterror
nl_cb_err
给nl_cb的nl_cb_kind(也就是某一个报错MSG) 增加错误回调函数
设置error message callback hook:
typedef int(* nl_recvmsg_err_cb_t)(struct sockaddr_nl *nla, struct nlmsgerr *nlerr, void *arg);
int nl_cb_err(struct nl_cb *cb, enum nl_cb_kind kind, nl_recvmsg_err_cb_t func, void * arg); //设置错误callback
nl_cb_set
给nl_cb的nl_cb_kind(也就是某一个NL MSG) 增加回调函数
设置callback:
typedef int (*nl_recvmsg_msg_cb_t)(struct nl_msg *msg, void *arg);
int nl_cb_set(struct nl_cb *cb, enum nl_cb_type type, enum nl_cb_kind kind, nl_recvmsg_msg_cb_t func, void *arg); //设置给定kind_type的callback
nl_recvmsgs
就是一个接收NLMSG的函数
libnl调用nl_recvmsgs_default()按照socket格式规定调用nl_recvmsgs() 接收数据。
nl_recvmsgs()在netlink message到来前保持阻塞,可以通过nl_cb_overwrite_recvmsgs()重写。nl_recvmsgs()调用recvmsgs()接收数据。
nl80211_nlmsg_clear
nlmsg_free
对应alloc的free
/*释放*/
void nlmsg_free(struct nl_msg *msg);
nla_put_u32
int nla_put_u32(struct nl_msg *msg, int attrtype, uint32_t value);
把value填充到nl msg里面
cpp
struct nlattr *nla_reserve(struct nl_msg *msg, int attrtype, int len);
为netlink消息添加属性的接口是基于常规消息构造接口的。它假设消息头和最终协议头已经添加到消息中。函数nla_reserve()在消息的末尾添加一个属性头,并为len字节的有效负载保留空间。该函数返回一个指向消息内部属性有效负载部分的指针。在属性的末尾添加填充,以确保下一个属性正确对齐。
cpp
int nla_put(struct nl_msg *msg, int attrtype, int attrlen, const void *data);
函数nla_put()以nla_reserve()为基础,但接受一个额外的指针数据,该指针数据指向包含属性有效负载的缓冲区。它将自动将缓冲区复制到消息中。