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