aboutsummaryrefslogtreecommitdiffstats
path: root/src/drivers
diff options
context:
space:
mode:
authorJouni Malinen <j@w1.fi>2009-12-18 15:11:54 (GMT)
committerJouni Malinen <j@w1.fi>2009-12-18 15:11:54 (GMT)
commit08063178fb48e2ee2f46b345326890ac295e2cfa (patch)
treed7a0a567b891c770a42fe90d55d895c4cf331354 /src/drivers
parente2d02c29b50aebacf13a8dcd3f27f46f9d16dff0 (diff)
downloadhostap-08063178fb48e2ee2f46b345326890ac295e2cfa.zip
hostap-08063178fb48e2ee2f46b345326890ac295e2cfa.tar.gz
hostap-08063178fb48e2ee2f46b345326890ac295e2cfa.tar.bz2
nl80211/wext: Share netlink new/del link event receive code
Diffstat (limited to 'src/drivers')
-rw-r--r--src/drivers/driver_nl80211.c156
-rw-r--r--src/drivers/driver_wext.c150
-rw-r--r--src/drivers/driver_wext.h2
-rw-r--r--src/drivers/netlink.c131
-rw-r--r--src/drivers/netlink.h13
5 files changed, 209 insertions, 243 deletions
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 0fff540..feb910b 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -65,7 +65,7 @@ struct i802_bss {
struct wpa_driver_nl80211_data {
void *ctx;
- int link_event_sock;
+ struct netlink_data *netlink;
int ioctl_sock; /* socket for ioctl() use */
char ifname[IFNAMSIZ + 1];
int ifindex;
@@ -325,8 +325,7 @@ static int wpa_driver_nl80211_get_ssid(void *priv, u8 *ssid)
static void wpa_driver_nl80211_event_link(struct wpa_driver_nl80211_data *drv,
- void *ctx, char *buf, size_t len,
- int del)
+ char *buf, size_t len, int del)
{
union wpa_event_data event;
@@ -349,7 +348,7 @@ static void wpa_driver_nl80211_event_link(struct wpa_driver_nl80211_data *drv,
drv->if_removed = 0;
}
- wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event);
+ wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
}
@@ -404,10 +403,10 @@ static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv,
}
-static void wpa_driver_nl80211_event_rtm_newlink(struct wpa_driver_nl80211_data *drv,
- void *ctx, struct nlmsghdr *h,
- size_t len)
+static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, struct nlmsghdr *h,
+ size_t len)
{
+ struct wpa_driver_nl80211_data *drv = ctx;
struct ifinfomsg *ifi;
int attrlen, _nlmsg_len, rta_len;
struct rtattr * attr;
@@ -439,7 +438,7 @@ static void wpa_driver_nl80211_event_rtm_newlink(struct wpa_driver_nl80211_data
if (drv->operstate == 1 &&
(ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP &&
!(ifi->ifi_flags & IFF_RUNNING))
- netlink_send_oper_ifla(drv->link_event_sock, drv->ifindex,
+ netlink_send_oper_ifla(drv->netlink, drv->ifindex,
-1, IF_OPER_UP);
_nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg));
@@ -454,7 +453,7 @@ static void wpa_driver_nl80211_event_rtm_newlink(struct wpa_driver_nl80211_data
while (RTA_OK(attr, attrlen)) {
if (attr->rta_type == IFLA_IFNAME) {
wpa_driver_nl80211_event_link(
- drv, ctx,
+ drv,
((char *) attr) + rta_len,
attr->rta_len - rta_len, 0);
}
@@ -463,10 +462,10 @@ static void wpa_driver_nl80211_event_rtm_newlink(struct wpa_driver_nl80211_data
}
-static void wpa_driver_nl80211_event_rtm_dellink(struct wpa_driver_nl80211_data *drv,
- void *ctx, struct nlmsghdr *h,
- size_t len)
+static void wpa_driver_nl80211_event_rtm_dellink(void *ctx, struct nlmsghdr *h,
+ size_t len)
{
+ struct wpa_driver_nl80211_data *drv = ctx;
struct ifinfomsg *ifi;
int attrlen, _nlmsg_len, rta_len;
struct rtattr * attr;
@@ -488,7 +487,7 @@ static void wpa_driver_nl80211_event_rtm_dellink(struct wpa_driver_nl80211_data
while (RTA_OK(attr, attrlen)) {
if (attr->rta_type == IFLA_IFNAME) {
wpa_driver_nl80211_event_link(
- drv, ctx,
+ drv,
((char *) attr) + rta_len,
attr->rta_len - rta_len, 1);
}
@@ -497,73 +496,6 @@ static void wpa_driver_nl80211_event_rtm_dellink(struct wpa_driver_nl80211_data
}
-static void wpa_driver_nl80211_event_receive_link(int sock, void *eloop_ctx,
- void *sock_ctx)
-{
- char buf[8192];
- int left;
- struct sockaddr_nl from;
- socklen_t fromlen;
- struct nlmsghdr *h;
- int max_events = 10;
-
-try_again:
- fromlen = sizeof(from);
- left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT,
- (struct sockaddr *) &from, &fromlen);
- if (left < 0) {
- if (errno != EINTR && errno != EAGAIN)
- perror("recvfrom(netlink)");
- return;
- }
-
- h = (struct nlmsghdr *) buf;
- while (left >= (int) sizeof(*h)) {
- int len, plen;
-
- len = h->nlmsg_len;
- plen = len - sizeof(*h);
- if (len > left || plen < 0) {
- wpa_printf(MSG_DEBUG, "Malformed netlink message: "
- "len=%d left=%d plen=%d",
- len, left, plen);
- break;
- }
-
- switch (h->nlmsg_type) {
- case RTM_NEWLINK:
- wpa_driver_nl80211_event_rtm_newlink(eloop_ctx, sock_ctx,
- h, plen);
- break;
- case RTM_DELLINK:
- wpa_driver_nl80211_event_rtm_dellink(eloop_ctx, sock_ctx,
- h, plen);
- break;
- }
-
- len = NLMSG_ALIGN(len);
- left -= len;
- h = (struct nlmsghdr *) ((char *) h + len);
- }
-
- if (left > 0) {
- wpa_printf(MSG_DEBUG, "%d extra bytes in the end of netlink "
- "message", left);
- }
-
- if (--max_events > 0) {
- /*
- * Try to receive all events in one eloop call in order to
- * limit race condition on cases where AssocInfo event, Assoc
- * event, and EAPOL frames are received more or less at the
- * same time. We want to process the event messages first
- * before starting EAPOL processing.
- */
- goto try_again;
- }
-}
-
-
static void mlme_event_auth(struct wpa_driver_nl80211_data *drv,
const u8 *frame, size_t len)
{
@@ -1204,35 +1136,6 @@ err1:
}
-static int wpa_driver_nl80211_init_link_events(
- struct wpa_driver_nl80211_data *drv)
-{
- int s;
- struct sockaddr_nl local;
-
- s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
- if (s < 0) {
- perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)");
- return -1;
- }
-
- os_memset(&local, 0, sizeof(local));
- local.nl_family = AF_NETLINK;
- local.nl_groups = RTMGRP_LINK;
- if (bind(s, (struct sockaddr *) &local, sizeof(local)) < 0) {
- perror("bind(netlink)");
- close(s);
- return -1;
- }
-
- eloop_register_read_sock(s, wpa_driver_nl80211_event_receive_link, drv,
- drv->ctx);
- drv->link_event_sock = s;
-
- return 0;
-}
-
-
/**
* wpa_driver_nl80211_init - Initialize nl80211 driver interface
* @ctx: context to be used when calling wpa_supplicant functions,
@@ -1243,6 +1146,7 @@ static int wpa_driver_nl80211_init_link_events(
static void * wpa_driver_nl80211_init(void *ctx, const char *ifname)
{
struct wpa_driver_nl80211_data *drv;
+ struct netlink_config *cfg;
drv = os_zalloc(sizeof(*drv));
if (drv == NULL)
@@ -1251,7 +1155,6 @@ static void * wpa_driver_nl80211_init(void *ctx, const char *ifname)
os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
drv->monitor_ifidx = -1;
drv->monitor_sock = -1;
- drv->link_event_sock = -1;
drv->ioctl_sock = -1;
if (wpa_driver_nl80211_init_nl(drv, ctx)) {
@@ -1265,17 +1168,24 @@ static void * wpa_driver_nl80211_init(void *ctx, const char *ifname)
goto failed;
}
- if (wpa_driver_nl80211_init_link_events(drv) ||
- wpa_driver_nl80211_finish_drv_init(drv))
+ cfg = os_zalloc(sizeof(*cfg));
+ if (cfg == NULL)
+ goto failed;
+ cfg->ctx = drv;
+ cfg->newlink_cb = wpa_driver_nl80211_event_rtm_newlink;
+ cfg->dellink_cb = wpa_driver_nl80211_event_rtm_dellink;
+ drv->netlink = netlink_init(cfg);
+ if (drv->netlink == NULL) {
+ os_free(cfg);
+ goto failed;
+ }
+ if (wpa_driver_nl80211_finish_drv_init(drv))
goto failed;
return drv;
failed:
- if (drv->link_event_sock >= 0) {
- eloop_unregister_read_sock(drv->link_event_sock);
- close(drv->link_event_sock);
- }
+ netlink_deinit(drv->netlink);
if (drv->ioctl_sock >= 0)
close(drv->ioctl_sock);
@@ -1309,7 +1219,7 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv)
if (wpa_driver_nl80211_capa(drv))
return -1;
- netlink_send_oper_ifla(drv->link_event_sock, drv->ifindex,
+ netlink_send_oper_ifla(drv->netlink, drv->ifindex,
1, IF_OPER_DORMANT);
#endif /* HOSTAPD */
@@ -1389,13 +1299,8 @@ static void wpa_driver_nl80211_deinit(void *priv)
wpa_driver_nl80211_free_bss(drv);
#endif /* HOSTAPD */
- netlink_send_oper_ifla(drv->link_event_sock, drv->ifindex,
- 0, IF_OPER_UP);
-
- if (drv->link_event_sock >= 0) {
- eloop_unregister_read_sock(drv->link_event_sock);
- close(drv->link_event_sock);
- }
+ netlink_send_oper_ifla(drv->netlink, drv->ifindex, 0, IF_OPER_UP);
+ netlink_deinit(drv->netlink);
eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
@@ -3709,8 +3614,7 @@ static int wpa_driver_nl80211_set_operstate(void *priv, int state)
wpa_printf(MSG_DEBUG, "%s: operstate %d->%d (%s)",
__func__, drv->operstate, state, state ? "UP" : "DORMANT");
drv->operstate = state;
- return netlink_send_oper_ifla(drv->link_event_sock, drv->ifindex,
- -1,
+ return netlink_send_oper_ifla(drv->netlink, drv->ifindex, -1,
state ? IF_OPER_UP : IF_OPER_DORMANT);
}
diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c
index cdd67c1..9401c77 100644
--- a/src/drivers/driver_wext.c
+++ b/src/drivers/driver_wext.c
@@ -419,7 +419,7 @@ static void wpa_driver_wext_event_assoc_ies(struct wpa_driver_wext_data *drv)
static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv,
- void *ctx, char *data, int len)
+ char *data, int len)
{
struct iw_event iwe_buf, *iwe = &iwe_buf;
char *pos, *end, *custom, *buf;
@@ -467,12 +467,13 @@ static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv,
drv->assoc_req_ies = NULL;
os_free(drv->assoc_resp_ies);
drv->assoc_resp_ies = NULL;
- wpa_supplicant_event(ctx, EVENT_DISASSOC,
+ wpa_supplicant_event(drv->ctx, EVENT_DISASSOC,
NULL);
} else {
wpa_driver_wext_event_assoc_ies(drv);
- wpa_supplicant_event(ctx, EVENT_ASSOC, NULL);
+ wpa_supplicant_event(drv->ctx, EVENT_ASSOC,
+ NULL);
}
break;
case IWEVMICHAELMICFAILURE:
@@ -482,7 +483,7 @@ static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv,
return;
}
wpa_driver_wext_event_wireless_michaelmicfailure(
- ctx, custom, iwe->u.data.length);
+ drv->ctx, custom, iwe->u.data.length);
break;
case IWEVCUSTOM:
if (custom + iwe->u.data.length > end) {
@@ -495,14 +496,15 @@ static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv,
return;
os_memcpy(buf, custom, iwe->u.data.length);
buf[iwe->u.data.length] = '\0';
- wpa_driver_wext_event_wireless_custom(ctx, buf);
+ wpa_driver_wext_event_wireless_custom(drv->ctx, buf);
os_free(buf);
break;
case SIOCGIWSCAN:
drv->scan_complete_events = 1;
eloop_cancel_timeout(wpa_driver_wext_scan_timeout,
- drv, ctx);
- wpa_supplicant_event(ctx, EVENT_SCAN_RESULTS, NULL);
+ drv, drv->ctx);
+ wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS,
+ NULL);
break;
case IWEVASSOCREQIE:
if (custom + iwe->u.data.length > end) {
@@ -539,8 +541,7 @@ static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv,
static void wpa_driver_wext_event_link(struct wpa_driver_wext_data *drv,
- void *ctx, char *buf, size_t len,
- int del)
+ char *buf, size_t len, int del)
{
union wpa_event_data event;
@@ -563,7 +564,7 @@ static void wpa_driver_wext_event_link(struct wpa_driver_wext_data *drv,
drv->if_removed = 0;
}
- wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event);
+ wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
}
@@ -618,13 +619,13 @@ static int wpa_driver_wext_own_ifindex(struct wpa_driver_wext_data *drv,
}
-static void wpa_driver_wext_event_rtm_newlink(struct wpa_driver_wext_data *drv,
- void *ctx, struct nlmsghdr *h,
+static void wpa_driver_wext_event_rtm_newlink(void *ctx, struct nlmsghdr *h,
size_t len)
{
+ struct wpa_driver_wext_data *drv = ctx;
struct ifinfomsg *ifi;
int attrlen, nlmsg_len, rta_len;
- struct rtattr * attr;
+ struct rtattr *attr;
if (len < sizeof(*ifi))
return;
@@ -653,7 +654,7 @@ static void wpa_driver_wext_event_rtm_newlink(struct wpa_driver_wext_data *drv,
if (drv->operstate == 1 &&
(ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP &&
!(ifi->ifi_flags & IFF_RUNNING))
- netlink_send_oper_ifla(drv->event_sock, drv->ifindex,
+ netlink_send_oper_ifla(drv->netlink, drv->ifindex,
-1, IF_OPER_UP);
nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg));
@@ -668,10 +669,10 @@ static void wpa_driver_wext_event_rtm_newlink(struct wpa_driver_wext_data *drv,
while (RTA_OK(attr, attrlen)) {
if (attr->rta_type == IFLA_WIRELESS) {
wpa_driver_wext_event_wireless(
- drv, ctx, ((char *) attr) + rta_len,
+ drv, ((char *) attr) + rta_len,
attr->rta_len - rta_len);
} else if (attr->rta_type == IFLA_IFNAME) {
- wpa_driver_wext_event_link(drv, ctx,
+ wpa_driver_wext_event_link(drv,
((char *) attr) + rta_len,
attr->rta_len - rta_len, 0);
}
@@ -680,13 +681,13 @@ static void wpa_driver_wext_event_rtm_newlink(struct wpa_driver_wext_data *drv,
}
-static void wpa_driver_wext_event_rtm_dellink(struct wpa_driver_wext_data *drv,
- void *ctx, struct nlmsghdr *h,
+static void wpa_driver_wext_event_rtm_dellink(void *ctx, struct nlmsghdr *h,
size_t len)
{
+ struct wpa_driver_wext_data *drv = ctx;
struct ifinfomsg *ifi;
int attrlen, nlmsg_len, rta_len;
- struct rtattr * attr;
+ struct rtattr *attr;
if (len < sizeof(*ifi))
return;
@@ -704,7 +705,7 @@ static void wpa_driver_wext_event_rtm_dellink(struct wpa_driver_wext_data *drv,
rta_len = RTA_ALIGN(sizeof(struct rtattr));
while (RTA_OK(attr, attrlen)) {
if (attr->rta_type == IFLA_IFNAME) {
- wpa_driver_wext_event_link(drv, ctx,
+ wpa_driver_wext_event_link(drv,
((char *) attr) + rta_len,
attr->rta_len - rta_len, 1);
}
@@ -713,73 +714,6 @@ static void wpa_driver_wext_event_rtm_dellink(struct wpa_driver_wext_data *drv,
}
-static void wpa_driver_wext_event_receive(int sock, void *eloop_ctx,
- void *sock_ctx)
-{
- char buf[8192];
- int left;
- struct sockaddr_nl from;
- socklen_t fromlen;
- struct nlmsghdr *h;
- int max_events = 10;
-
-try_again:
- fromlen = sizeof(from);
- left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT,
- (struct sockaddr *) &from, &fromlen);
- if (left < 0) {
- if (errno != EINTR && errno != EAGAIN)
- perror("recvfrom(netlink)");
- return;
- }
-
- h = (struct nlmsghdr *) buf;
- while (left >= (int) sizeof(*h)) {
- int len, plen;
-
- len = h->nlmsg_len;
- plen = len - sizeof(*h);
- if (len > left || plen < 0) {
- wpa_printf(MSG_DEBUG, "Malformed netlink message: "
- "len=%d left=%d plen=%d",
- len, left, plen);
- break;
- }
-
- switch (h->nlmsg_type) {
- case RTM_NEWLINK:
- wpa_driver_wext_event_rtm_newlink(eloop_ctx, sock_ctx,
- h, plen);
- break;
- case RTM_DELLINK:
- wpa_driver_wext_event_rtm_dellink(eloop_ctx, sock_ctx,
- h, plen);
- break;
- }
-
- len = NLMSG_ALIGN(len);
- left -= len;
- h = (struct nlmsghdr *) ((char *) h + len);
- }
-
- if (left > 0) {
- wpa_printf(MSG_DEBUG, "%d extra bytes in the end of netlink "
- "message", left);
- }
-
- if (--max_events > 0) {
- /*
- * Try to receive all events in one eloop call in order to
- * limit race condition on cases where AssocInfo event, Assoc
- * event, and EAPOL frames are received more or less at the
- * same time. We want to process the event messages first
- * before starting EAPOL processing.
- */
- goto try_again;
- }
-}
-
-
static int wpa_driver_wext_get_ifflags_ifname(struct wpa_driver_wext_data *drv,
const char *ifname, int *flags)
{
@@ -845,9 +779,8 @@ int wpa_driver_wext_set_ifflags(struct wpa_driver_wext_data *drv, int flags)
*/
void * wpa_driver_wext_init(void *ctx, const char *ifname)
{
- int s;
- struct sockaddr_nl local;
struct wpa_driver_wext_data *drv;
+ struct netlink_config *cfg;
drv = os_zalloc(sizeof(*drv));
if (drv == NULL)
@@ -861,36 +794,29 @@ void * wpa_driver_wext_init(void *ctx, const char *ifname)
goto err1;
}
- s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
- if (s < 0) {
- perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)");
+ cfg = os_zalloc(sizeof(*cfg));
+ if (cfg == NULL)
+ goto err1;
+ cfg->ctx = drv;
+ cfg->newlink_cb = wpa_driver_wext_event_rtm_newlink;
+ cfg->dellink_cb = wpa_driver_wext_event_rtm_dellink;
+ drv->netlink = netlink_init(cfg);
+ if (drv->netlink == NULL) {
+ os_free(cfg);
goto err2;
}
- drv->event_sock = s;
-
- os_memset(&local, 0, sizeof(local));
- local.nl_family = AF_NETLINK;
- local.nl_groups = RTMGRP_LINK;
- if (bind(s, (struct sockaddr *) &local, sizeof(local)) < 0) {
- perror("bind(netlink)");
- goto err3;
- }
-
- eloop_register_read_sock(s, wpa_driver_wext_event_receive, drv, ctx);
drv->mlme_sock = -1;
if (wpa_driver_wext_finish_drv_init(drv) < 0)
- goto err4;
+ goto err3;
wpa_driver_wext_set_auth_param(drv, IW_AUTH_WPA_ENABLED, 1);
return drv;
-err4:
- eloop_unregister_read_sock(drv->event_sock);
err3:
- close(drv->event_sock);
+ netlink_deinit(drv->netlink);
err2:
close(drv->ioctl_sock);
err1:
@@ -965,7 +891,7 @@ static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv)
wpa_driver_wext_alternative_ifindex(drv, ifname2);
}
- netlink_send_oper_ifla(drv->event_sock, drv->ifindex,
+ netlink_send_oper_ifla(drv->netlink, drv->ifindex,
1, IF_OPER_DORMANT);
return 0;
@@ -994,16 +920,15 @@ void wpa_driver_wext_deinit(void *priv)
*/
wpa_driver_wext_disconnect(drv);
- netlink_send_oper_ifla(drv->event_sock, drv->ifindex, 0, IF_OPER_UP);
+ netlink_send_oper_ifla(drv->netlink, drv->ifindex, 0, IF_OPER_UP);
+ netlink_deinit(drv->netlink);
- eloop_unregister_read_sock(drv->event_sock);
if (drv->mlme_sock >= 0)
eloop_unregister_read_sock(drv->mlme_sock);
if (wpa_driver_wext_get_ifflags(drv, &flags) == 0)
(void) wpa_driver_wext_set_ifflags(drv, flags & ~IFF_UP);
- close(drv->event_sock);
close(drv->ioctl_sock);
if (drv->mlme_sock >= 0)
close(drv->mlme_sock);
@@ -2303,8 +2228,7 @@ int wpa_driver_wext_set_operstate(void *priv, int state)
wpa_printf(MSG_DEBUG, "%s: operstate %d->%d (%s)",
__func__, drv->operstate, state, state ? "UP" : "DORMANT");
drv->operstate = state;
- return netlink_send_oper_ifla(drv->event_sock, drv->ifindex,
- -1,
+ return netlink_send_oper_ifla(drv->netlink, drv->ifindex, -1,
state ? IF_OPER_UP : IF_OPER_DORMANT);
}
diff --git a/src/drivers/driver_wext.h b/src/drivers/driver_wext.h
index 1999e2f..8c64974 100644
--- a/src/drivers/driver_wext.h
+++ b/src/drivers/driver_wext.h
@@ -19,7 +19,7 @@
struct wpa_driver_wext_data {
void *ctx;
- int event_sock;
+ struct netlink_data *netlink;
int ioctl_sock;
int mlme_sock;
char ifname[IFNAMSIZ + 1];
diff --git a/src/drivers/netlink.c b/src/drivers/netlink.c
index 98ec387..5e61183 100644
--- a/src/drivers/netlink.c
+++ b/src/drivers/netlink.c
@@ -15,11 +15,138 @@
#include "includes.h"
#include "common.h"
+#include "eloop.h"
#include "priv_netlink.h"
#include "netlink.h"
-int netlink_send_oper_ifla(int sock, int ifindex, int linkmode, int operstate)
+struct netlink_data {
+ struct netlink_config *cfg;
+ int sock;
+};
+
+
+static void netlink_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct netlink_data *netlink = eloop_ctx;
+ char buf[8192];
+ int left;
+ struct sockaddr_nl from;
+ socklen_t fromlen;
+ struct nlmsghdr *h;
+ int max_events = 10;
+
+try_again:
+ fromlen = sizeof(from);
+ left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT,
+ (struct sockaddr *) &from, &fromlen);
+ if (left < 0) {
+ if (errno != EINTR && errno != EAGAIN)
+ wpa_printf(MSG_INFO, "netlink: recvfrom failed: %s",
+ strerror(errno));
+ return;
+ }
+
+ h = (struct nlmsghdr *) buf;
+ while (left >= (int) sizeof(*h)) {
+ int len, plen;
+
+ len = h->nlmsg_len;
+ plen = len - sizeof(*h);
+ if (len > left || plen < 0) {
+ wpa_printf(MSG_DEBUG, "netlnk: Malformed message: "
+ "len=%d left=%d plen=%d",
+ len, left, plen);
+ break;
+ }
+
+ switch (h->nlmsg_type) {
+ case RTM_NEWLINK:
+ if (netlink->cfg->newlink_cb)
+ netlink->cfg->newlink_cb(netlink->cfg->ctx,
+ h, plen);
+ break;
+ case RTM_DELLINK:
+ if (netlink->cfg->dellink_cb)
+ netlink->cfg->dellink_cb(netlink->cfg->ctx,
+ h, plen);
+ break;
+ }
+
+ len = NLMSG_ALIGN(len);
+ left -= len;
+ h = (struct nlmsghdr *) ((char *) h + len);
+ }
+
+ if (left > 0) {
+ wpa_printf(MSG_DEBUG, "netlink: %d extra bytes in the end of "
+ "netlink message", left);
+ }
+
+ if (--max_events > 0) {
+ /*
+ * Try to receive all events in one eloop call in order to
+ * limit race condition on cases where AssocInfo event, Assoc
+ * event, and EAPOL frames are received more or less at the
+ * same time. We want to process the event messages first
+ * before starting EAPOL processing.
+ */
+ goto try_again;
+ }
+}
+
+
+struct netlink_data * netlink_init(struct netlink_config *cfg)
+{
+ struct netlink_data *netlink;
+ struct sockaddr_nl local;
+
+ netlink = os_zalloc(sizeof(*netlink));
+ if (netlink == NULL)
+ return NULL;
+
+ netlink->cfg = cfg;
+
+ netlink->sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+ if (netlink->sock < 0) {
+ wpa_printf(MSG_ERROR, "netlink: Failed to open netlink "
+ "socket: %s", strerror(errno));
+ netlink_deinit(netlink);
+ return NULL;
+ }
+
+ os_memset(&local, 0, sizeof(local));
+ local.nl_family = AF_NETLINK;
+ local.nl_groups = RTMGRP_LINK;
+ if (bind(netlink->sock, (struct sockaddr *) &local, sizeof(local)) < 0)
+ {
+ wpa_printf(MSG_ERROR, "netlink: Failed to bind netlink "
+ "socket: %s", strerror(errno));
+ netlink_deinit(netlink);
+ return NULL;
+ }
+
+ eloop_register_read_sock(netlink->sock, netlink_receive, netlink,
+ NULL);
+
+ return netlink;
+}
+
+
+void netlink_deinit(struct netlink_data *netlink)
+{
+ if (netlink == NULL)
+ return;
+ if (netlink->sock >= 0) {
+ eloop_unregister_read_sock(netlink->sock);
+ close(netlink->sock);
+ }
+ os_free(netlink->cfg);
+ os_free(netlink);
+}
+
+int netlink_send_oper_ifla(struct netlink_data *netlink, int ifindex,
+ int linkmode, int operstate)
{
struct {
struct nlmsghdr hdr;
@@ -68,7 +195,7 @@ int netlink_send_oper_ifla(int sock, int ifindex, int linkmode, int operstate)
wpa_printf(MSG_DEBUG, "netlink: Operstate: linkmode=%d, operstate=%d",
linkmode, operstate);
- ret = send(sock, &req, req.hdr.nlmsg_len, 0);
+ ret = send(netlink->sock, &req, req.hdr.nlmsg_len, 0);
if (ret < 0) {
wpa_printf(MSG_DEBUG, "netlink: Sending operstate IFLA "
"failed: %s (assume operstate is not supported)",
diff --git a/src/drivers/netlink.h b/src/drivers/netlink.h
index c2cf59d..ee595c1 100644
--- a/src/drivers/netlink.h
+++ b/src/drivers/netlink.h
@@ -15,6 +15,17 @@
#ifndef NETLINK_H
#define NETLINK_H
-int netlink_send_oper_ifla(int sock, int ifindex, int linkmode, int operstate);
+struct netlink_data;
+
+struct netlink_config {
+ void *ctx;
+ void (*newlink_cb)(void *ctx, struct nlmsghdr *buf, size_t len);
+ void (*dellink_cb)(void *ctx, struct nlmsghdr *buf, size_t len);
+};
+
+struct netlink_data * netlink_init(struct netlink_config *cfg);
+void netlink_deinit(struct netlink_data *netlink);
+int netlink_send_oper_ifla(struct netlink_data *netlink, int ifindex,
+ int linkmode, int operstate);
#endif /* NETLINK_H */