netlink.c revision 8d520ff1dc2da35cdca849e982051b86468016d8
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
23struct netlink_data {
24	struct netlink_config *cfg;
25	int sock;
26};
27
28
29static 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
42static 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
52try_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
97struct 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
134void 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
146int 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