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
17struct netlink_data {
18	struct netlink_config *cfg;
19	int sock;
20};
21
22
23static 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
36static 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
46try_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
91struct 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
128void 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
141static 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
155static 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
169int 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 = NLMSG_ALIGN(req.hdr.nlmsg_len) +
203			RTA_LENGTH(sizeof(char));
204	}
205	if (operstate != -1) {
206		rta = aliasing_hide_typecast(
207			((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)),
208			struct rtattr);
209		rta->rta_type = IFLA_OPERSTATE;
210		rta->rta_len = RTA_LENGTH(sizeof(char));
211		*((char *) RTA_DATA(rta)) = operstate;
212		req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) +
213			RTA_LENGTH(sizeof(char));
214	}
215
216	wpa_printf(MSG_DEBUG, "netlink: Operstate: ifindex=%d linkmode=%d (%s), operstate=%d (%s)",
217		   ifindex, linkmode, linkmode_str(linkmode),
218		   operstate, operstate_str(operstate));
219
220	ret = send(netlink->sock, &req, req.hdr.nlmsg_len, 0);
221	if (ret < 0) {
222		wpa_printf(MSG_DEBUG, "netlink: Sending operstate IFLA "
223			   "failed: %s (assume operstate is not supported)",
224			   strerror(errno));
225	}
226
227	return ret < 0 ? -1 : 0;
228}
229