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  
netlink_receive_link(struct netlink_data * netlink,void (* cb)(void * ctx,struct ifinfomsg * ifi,u8 * buf,size_t len),struct nlmsghdr * h)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  
netlink_receive(int sock,void * eloop_ctx,void * sock_ctx)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  
netlink_init(struct netlink_config * cfg)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  
netlink_deinit(struct netlink_data * netlink)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  
linkmode_str(int mode)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  	default:
151  		return "?";
152  	}
153  }
154  
155  
operstate_str(int state)156  static const char * operstate_str(int state)
157  {
158  	switch (state) {
159  	case -1:
160  		return "no change";
161  	case IF_OPER_DORMANT:
162  		return "IF_OPER_DORMANT";
163  	case IF_OPER_UP:
164  		return "IF_OPER_UP";
165  	default:
166  		return "?";
167  	}
168  }
169  
170  
netlink_send_oper_ifla(struct netlink_data * netlink,int ifindex,int linkmode,int operstate)171  int netlink_send_oper_ifla(struct netlink_data *netlink, int ifindex,
172  			   int linkmode, int operstate)
173  {
174  	struct {
175  		struct nlmsghdr hdr;
176  		struct ifinfomsg ifinfo;
177  		char opts[16];
178  	} req;
179  	struct rtattr *rta;
180  	static int nl_seq;
181  	ssize_t ret;
182  
183  	os_memset(&req, 0, sizeof(req));
184  
185  	req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
186  	req.hdr.nlmsg_type = RTM_SETLINK;
187  	req.hdr.nlmsg_flags = NLM_F_REQUEST;
188  	req.hdr.nlmsg_seq = ++nl_seq;
189  	req.hdr.nlmsg_pid = 0;
190  
191  	req.ifinfo.ifi_family = AF_UNSPEC;
192  	req.ifinfo.ifi_type = 0;
193  	req.ifinfo.ifi_index = ifindex;
194  	req.ifinfo.ifi_flags = 0;
195  	req.ifinfo.ifi_change = 0;
196  
197  	if (linkmode != -1) {
198  		rta = aliasing_hide_typecast(
199  			((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)),
200  			struct rtattr);
201  		rta->rta_type = IFLA_LINKMODE;
202  		rta->rta_len = RTA_LENGTH(sizeof(char));
203  		*((char *) RTA_DATA(rta)) = linkmode;
204  		req.hdr.nlmsg_len += RTA_SPACE(sizeof(char));
205  	}
206  	if (operstate != -1) {
207  		rta = aliasing_hide_typecast(
208  			((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)),
209  			struct rtattr);
210  		rta->rta_type = IFLA_OPERSTATE;
211  		rta->rta_len = RTA_LENGTH(sizeof(char));
212  		*((char *) RTA_DATA(rta)) = operstate;
213  		req.hdr.nlmsg_len += RTA_SPACE(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