netlink.c revision 8d520ff1dc2da35cdca849e982051b86468016d8
18d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt/*
28d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Netlink helper functions for driver wrappers
38d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
48d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt *
58d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * This program is free software; you can redistribute it and/or modify
68d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * it under the terms of the GNU General Public License version 2 as
78d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * published by the Free Software Foundation.
88d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt *
98d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Alternatively, this software may be distributed under the terms of BSD
108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * license.
118d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt *
128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * See README and COPYING for more details.
138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt */
148d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "includes.h"
168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
178d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "common.h"
188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "eloop.h"
198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "priv_netlink.h"
208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "netlink.h"
218d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
228d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
238d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstruct netlink_data {
248d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	struct netlink_config *cfg;
258d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	int sock;
268d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt};
278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
288d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
298d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstatic void netlink_receive_link(struct netlink_data *netlink,
308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				 void (*cb)(void *ctx, struct ifinfomsg *ifi,
318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					    u8 *buf, size_t len),
328d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				 struct nlmsghdr *h)
338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
348d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (cb == NULL || NLMSG_PAYLOAD(h, 0) < sizeof(struct ifinfomsg))
358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		return;
368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	cb(netlink->cfg->ctx, NLMSG_DATA(h),
378d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	   NLMSG_DATA(h) + NLMSG_ALIGN(sizeof(struct ifinfomsg)),
388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	   NLMSG_PAYLOAD(h, sizeof(struct ifinfomsg)));
398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstatic void netlink_receive(int sock, void *eloop_ctx, void *sock_ctx)
438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	struct netlink_data *netlink = eloop_ctx;
458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	char buf[8192];
468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	int left;
478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	struct sockaddr_nl from;
488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	socklen_t fromlen;
498d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	struct nlmsghdr *h;
508d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	int max_events = 10;
518d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
528d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidttry_again:
538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	fromlen = sizeof(from);
548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT,
558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			(struct sockaddr *) &from, &fromlen);
568d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (left < 0) {
578d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (errno != EINTR && errno != EAGAIN)
588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			wpa_printf(MSG_INFO, "netlink: recvfrom failed: %s",
598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				   strerror(errno));
608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		return;
618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
638d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	h = (struct nlmsghdr *) buf;
648d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	while (NLMSG_OK(h, left)) {
658d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		switch (h->nlmsg_type) {
668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		case RTM_NEWLINK:
678d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			netlink_receive_link(netlink, netlink->cfg->newlink_cb,
688d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					     h);
698d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			break;
708d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		case RTM_DELLINK:
718d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			netlink_receive_link(netlink, netlink->cfg->dellink_cb,
728d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					     h);
738d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			break;
748d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		}
758d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
768d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		h = NLMSG_NEXT(h, left);
778d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
788d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
798d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (left > 0) {
808d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		wpa_printf(MSG_DEBUG, "netlink: %d extra bytes in the end of "
818d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			   "netlink message", left);
828d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
838d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
848d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (--max_events > 0) {
858d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		/*
868d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 * Try to receive all events in one eloop call in order to
878d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 * limit race condition on cases where AssocInfo event, Assoc
888d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 * event, and EAPOL frames are received more or less at the
898d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 * same time. We want to process the event messages first
908d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 * before starting EAPOL processing.
918d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 */
928d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		goto try_again;
938d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
948d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
958d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
968d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
978d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstruct netlink_data * netlink_init(struct netlink_config *cfg)
988d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
998d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	struct netlink_data *netlink;
1008d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	struct sockaddr_nl local;
1018d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1028d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	netlink = os_zalloc(sizeof(*netlink));
1038d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (netlink == NULL)
1048d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		return NULL;
1058d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1068d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	netlink->cfg = cfg;
1078d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1088d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	netlink->sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
1098d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (netlink->sock < 0) {
1108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		wpa_printf(MSG_ERROR, "netlink: Failed to open netlink "
1118d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			   "socket: %s", strerror(errno));
1128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		netlink_deinit(netlink);
1138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		return NULL;
1148d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
1158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	os_memset(&local, 0, sizeof(local));
1178d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	local.nl_family = AF_NETLINK;
1188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	local.nl_groups = RTMGRP_LINK;
1198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (bind(netlink->sock, (struct sockaddr *) &local, sizeof(local)) < 0)
1208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	{
1218d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		wpa_printf(MSG_ERROR, "netlink: Failed to bind netlink "
1228d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			   "socket: %s", strerror(errno));
1238d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		netlink_deinit(netlink);
1248d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		return NULL;
1258d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
1268d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	eloop_register_read_sock(netlink->sock, netlink_receive, netlink,
1288d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				 NULL);
1298d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	return netlink;
1318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
1328d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1348d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtvoid netlink_deinit(struct netlink_data *netlink)
1358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
1368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (netlink == NULL)
1378d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		return;
1388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (netlink->sock >= 0) {
1398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		eloop_unregister_read_sock(netlink->sock);
1408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		close(netlink->sock);
1418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
1428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	os_free(netlink->cfg);
1438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	os_free(netlink);
1448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
1458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtint netlink_send_oper_ifla(struct netlink_data *netlink, int ifindex,
1478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			   int linkmode, int operstate)
1488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
1498d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	struct {
1508d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		struct nlmsghdr hdr;
1518d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		struct ifinfomsg ifinfo;
1528d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		char opts[16];
1538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	} req;
1548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	struct rtattr *rta;
1558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	static int nl_seq;
1568d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	ssize_t ret;
1578d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	os_memset(&req, 0, sizeof(req));
1598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
1618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	req.hdr.nlmsg_type = RTM_SETLINK;
1628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	req.hdr.nlmsg_flags = NLM_F_REQUEST;
1638d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	req.hdr.nlmsg_seq = ++nl_seq;
1648d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	req.hdr.nlmsg_pid = 0;
1658d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	req.ifinfo.ifi_family = AF_UNSPEC;
1678d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	req.ifinfo.ifi_type = 0;
1688d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	req.ifinfo.ifi_index = ifindex;
1698d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	req.ifinfo.ifi_flags = 0;
1708d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	req.ifinfo.ifi_change = 0;
1718d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1728d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (linkmode != -1) {
1738d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		rta = aliasing_hide_typecast(
1748d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)),
1758d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			struct rtattr);
1768d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		rta->rta_type = IFLA_LINKMODE;
1778d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		rta->rta_len = RTA_LENGTH(sizeof(char));
1788d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		*((char *) RTA_DATA(rta)) = linkmode;
1798d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) +
1808d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			RTA_LENGTH(sizeof(char));
1818d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
1828d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (operstate != -1) {
1838d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		rta = aliasing_hide_typecast(
1848d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)),
1858d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			struct rtattr);
1868d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		rta->rta_type = IFLA_OPERSTATE;
1878d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		rta->rta_len = RTA_LENGTH(sizeof(char));
1888d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		*((char *) RTA_DATA(rta)) = operstate;
1898d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) +
1908d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			RTA_LENGTH(sizeof(char));
1918d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
1928d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1938d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	wpa_printf(MSG_DEBUG, "netlink: Operstate: linkmode=%d, operstate=%d",
1948d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		   linkmode, operstate);
1958d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1968d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	ret = send(netlink->sock, &req, req.hdr.nlmsg_len, 0);
1978d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (ret < 0) {
1988d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		wpa_printf(MSG_DEBUG, "netlink: Sending operstate IFLA "
1998d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			   "failed: %s (assume operstate is not supported)",
2008d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			   strerror(errno));
2018d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
2028d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
2038d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	return ret < 0 ? -1 : 0;
2048d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
205