Home | History | Annotate | Download | only in drivers
      1 /*
      2  * Netlink helper functions for driver wrappers
      3  * Copyright (c) 2002-2014, Jouni Malinen <j (at) w1.fi>
      4  *
      5  * This software may be distributed under the terms of the BSD license.
      6  * See README for more details.
      7  */
      8 
      9 #include "includes.h"
     10 
     11 #include "common.h"
     12 #include "eloop.h"
     13 #include "priv_netlink.h"
     14 #include "netlink.h"
     15 
     16 
     17 struct netlink_data {
     18 	struct netlink_config *cfg;
     19 	int sock;
     20 };
     21 
     22 
     23 static void netlink_receive_link(struct netlink_data *netlink,
     24 				 void (*cb)(void *ctx, struct ifinfomsg *ifi,
     25 					    u8 *buf, size_t len),
     26 				 struct nlmsghdr *h)
     27 {
     28 	if (cb == NULL || NLMSG_PAYLOAD(h, 0) < sizeof(struct ifinfomsg))
     29 		return;
     30 	cb(netlink->cfg->ctx, NLMSG_DATA(h),
     31 	   (u8 *) NLMSG_DATA(h) + NLMSG_ALIGN(sizeof(struct ifinfomsg)),
     32 	   NLMSG_PAYLOAD(h, sizeof(struct ifinfomsg)));
     33 }
     34 
     35 
     36 static void netlink_receive(int sock, void *eloop_ctx, void *sock_ctx)
     37 {
     38 	struct netlink_data *netlink = eloop_ctx;
     39 	char buf[8192];
     40 	int left;
     41 	struct sockaddr_nl from;
     42 	socklen_t fromlen;
     43 	struct nlmsghdr *h;
     44 	int max_events = 10;
     45 
     46 try_again:
     47 	fromlen = sizeof(from);
     48 	left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT,
     49 			(struct sockaddr *) &from, &fromlen);
     50 	if (left < 0) {
     51 		if (errno != EINTR && errno != EAGAIN)
     52 			wpa_printf(MSG_INFO, "netlink: recvfrom failed: %s",
     53 				   strerror(errno));
     54 		return;
     55 	}
     56 
     57 	h = (struct nlmsghdr *) buf;
     58 	while (NLMSG_OK(h, left)) {
     59 		switch (h->nlmsg_type) {
     60 		case RTM_NEWLINK:
     61 			netlink_receive_link(netlink, netlink->cfg->newlink_cb,
     62 					     h);
     63 			break;
     64 		case RTM_DELLINK:
     65 			netlink_receive_link(netlink, netlink->cfg->dellink_cb,
     66 					     h);
     67 			break;
     68 		}
     69 
     70 		h = NLMSG_NEXT(h, left);
     71 	}
     72 
     73 	if (left > 0) {
     74 		wpa_printf(MSG_DEBUG, "netlink: %d extra bytes in the end of "
     75 			   "netlink message", left);
     76 	}
     77 
     78 	if (--max_events > 0) {
     79 		/*
     80 		 * Try to receive all events in one eloop call in order to
     81 		 * limit race condition on cases where AssocInfo event, Assoc
     82 		 * event, and EAPOL frames are received more or less at the
     83 		 * same time. We want to process the event messages first
     84 		 * before starting EAPOL processing.
     85 		 */
     86 		goto try_again;
     87 	}
     88 }
     89 
     90 
     91 struct netlink_data * netlink_init(struct netlink_config *cfg)
     92 {
     93 	struct netlink_data *netlink;
     94 	struct sockaddr_nl local;
     95 
     96 	netlink = os_zalloc(sizeof(*netlink));
     97 	if (netlink == NULL)
     98 		return NULL;
     99 
    100 	netlink->sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
    101 	if (netlink->sock < 0) {
    102 		wpa_printf(MSG_ERROR, "netlink: Failed to open netlink "
    103 			   "socket: %s", strerror(errno));
    104 		netlink_deinit(netlink);
    105 		return NULL;
    106 	}
    107 
    108 	os_memset(&local, 0, sizeof(local));
    109 	local.nl_family = AF_NETLINK;
    110 	local.nl_groups = RTMGRP_LINK;
    111 	if (bind(netlink->sock, (struct sockaddr *) &local, sizeof(local)) < 0)
    112 	{
    113 		wpa_printf(MSG_ERROR, "netlink: Failed to bind netlink "
    114 			   "socket: %s", strerror(errno));
    115 		netlink_deinit(netlink);
    116 		return NULL;
    117 	}
    118 
    119 	eloop_register_read_sock(netlink->sock, netlink_receive, netlink,
    120 				 NULL);
    121 
    122 	netlink->cfg = cfg;
    123 
    124 	return netlink;
    125 }
    126 
    127 
    128 void netlink_deinit(struct netlink_data *netlink)
    129 {
    130 	if (netlink == NULL)
    131 		return;
    132 	if (netlink->sock >= 0) {
    133 		eloop_unregister_read_sock(netlink->sock);
    134 		close(netlink->sock);
    135 	}
    136 	os_free(netlink->cfg);
    137 	os_free(netlink);
    138 }
    139 
    140 
    141 static const char * linkmode_str(int mode)
    142 {
    143 	switch (mode) {
    144 	case -1:
    145 		return "no change";
    146 	case 0:
    147 		return "kernel-control";
    148 	case 1:
    149 		return "userspace-control";
    150 	}
    151 	return "?";
    152 }
    153 
    154 
    155 static const char * operstate_str(int state)
    156 {
    157 	switch (state) {
    158 	case -1:
    159 		return "no change";
    160 	case IF_OPER_DORMANT:
    161 		return "IF_OPER_DORMANT";
    162 	case IF_OPER_UP:
    163 		return "IF_OPER_UP";
    164 	}
    165 	return "?";
    166 }
    167 
    168 
    169 int netlink_send_oper_ifla(struct netlink_data *netlink, int ifindex,
    170 			   int linkmode, int operstate)
    171 {
    172 	struct {
    173 		struct nlmsghdr hdr;
    174 		struct ifinfomsg ifinfo;
    175 		char opts[16];
    176 	} req;
    177 	struct rtattr *rta;
    178 	static int nl_seq;
    179 	ssize_t ret;
    180 
    181 	os_memset(&req, 0, sizeof(req));
    182 
    183 	req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
    184 	req.hdr.nlmsg_type = RTM_SETLINK;
    185 	req.hdr.nlmsg_flags = NLM_F_REQUEST;
    186 	req.hdr.nlmsg_seq = ++nl_seq;
    187 	req.hdr.nlmsg_pid = 0;
    188 
    189 	req.ifinfo.ifi_family = AF_UNSPEC;
    190 	req.ifinfo.ifi_type = 0;
    191 	req.ifinfo.ifi_index = ifindex;
    192 	req.ifinfo.ifi_flags = 0;
    193 	req.ifinfo.ifi_change = 0;
    194 
    195 	if (linkmode != -1) {
    196 		rta = aliasing_hide_typecast(
    197 			((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)),
    198 			struct rtattr);
    199 		rta->rta_type = IFLA_LINKMODE;
    200 		rta->rta_len = RTA_LENGTH(sizeof(char));
    201 		*((char *) RTA_DATA(rta)) = linkmode;
    202 		req.hdr.nlmsg_len += RTA_SPACE(sizeof(char));
    203 	}
    204 	if (operstate != -1) {
    205 		rta = aliasing_hide_typecast(
    206 			((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)),
    207 			struct rtattr);
    208 		rta->rta_type = IFLA_OPERSTATE;
    209 		rta->rta_len = RTA_LENGTH(sizeof(char));
    210 		*((char *) RTA_DATA(rta)) = operstate;
    211 		req.hdr.nlmsg_len += RTA_SPACE(sizeof(char));
    212 	}
    213 
    214 	wpa_printf(MSG_DEBUG, "netlink: Operstate: ifindex=%d linkmode=%d (%s), operstate=%d (%s)",
    215 		   ifindex, linkmode, linkmode_str(linkmode),
    216 		   operstate, operstate_str(operstate));
    217 
    218 	ret = send(netlink->sock, &req, req.hdr.nlmsg_len, 0);
    219 	if (ret < 0) {
    220 		wpa_printf(MSG_DEBUG, "netlink: Sending operstate IFLA "
    221 			   "failed: %s (assume operstate is not supported)",
    222 			   strerror(errno));
    223 	}
    224 
    225 	return ret < 0 ? -1 : 0;
    226 }
    227