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