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