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