Browse Source

nl80211/wext: Share netlink new/del link event receive code

Jouni Malinen 15 years ago
parent
commit
08063178fb
5 changed files with 209 additions and 243 deletions
  1. 30 126
      src/drivers/driver_nl80211.c
  2. 37 113
      src/drivers/driver_wext.c
  3. 1 1
      src/drivers/driver_wext.h
  4. 129 2
      src/drivers/netlink.c
  5. 12 1
      src/drivers/netlink.h

+ 30 - 126
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);
 }
 

+ 37 - 113
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);
 }
 

+ 1 - 1
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];

+ 129 - 2
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)",

+ 12 - 1
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 */