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