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