Branch data Line data Source code
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 : 17430 : 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 [ + - ][ - + ]: 17430 : if (cb == NULL || NLMSG_PAYLOAD(h, 0) < sizeof(struct ifinfomsg))
29 : 17430 : return;
30 : 17430 : cb(netlink->cfg->ctx, NLMSG_DATA(h),
31 : : (u8 *) NLMSG_DATA(h) + NLMSG_ALIGN(sizeof(struct ifinfomsg)),
32 : 17430 : NLMSG_PAYLOAD(h, sizeof(struct ifinfomsg)));
33 : : }
34 : :
35 : :
36 : 15778 : static void netlink_receive(int sock, void *eloop_ctx, void *sock_ctx)
37 : : {
38 : 15778 : 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 : 15778 : int max_events = 10;
45 : :
46 : : try_again:
47 : 33206 : fromlen = sizeof(from);
48 : 33206 : left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT,
49 : : (struct sockaddr *) &from, &fromlen);
50 [ + + ]: 33206 : if (left < 0) {
51 [ + - ][ - + ]: 15776 : if (errno != EINTR && errno != EAGAIN)
52 : 0 : wpa_printf(MSG_INFO, "netlink: recvfrom failed: %s",
53 : 0 : strerror(errno));
54 : 15778 : return;
55 : : }
56 : :
57 : 17430 : h = (struct nlmsghdr *) buf;
58 [ + + ][ + - ]: 34860 : while (NLMSG_OK(h, left)) {
[ + - ]
59 [ + + - ]: 17430 : switch (h->nlmsg_type) {
60 : : case RTM_NEWLINK:
61 : 17224 : netlink_receive_link(netlink, netlink->cfg->newlink_cb,
62 : : h);
63 : 17224 : break;
64 : : case RTM_DELLINK:
65 : 206 : netlink_receive_link(netlink, netlink->cfg->dellink_cb,
66 : : h);
67 : 206 : break;
68 : : }
69 : :
70 : 17430 : h = NLMSG_NEXT(h, left);
71 : : }
72 : :
73 [ - + ]: 17430 : if (left > 0) {
74 : 0 : wpa_printf(MSG_DEBUG, "netlink: %d extra bytes in the end of "
75 : : "netlink message", left);
76 : : }
77 : :
78 [ + + ]: 17430 : 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 : 17428 : goto try_again;
87 : : }
88 : : }
89 : :
90 : :
91 : 5 : struct netlink_data * netlink_init(struct netlink_config *cfg)
92 : : {
93 : : struct netlink_data *netlink;
94 : : struct sockaddr_nl local;
95 : :
96 : 5 : netlink = os_zalloc(sizeof(*netlink));
97 [ - + ]: 5 : if (netlink == NULL)
98 : 0 : return NULL;
99 : :
100 : 5 : netlink->sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
101 [ - + ]: 5 : if (netlink->sock < 0) {
102 : 0 : wpa_printf(MSG_ERROR, "netlink: Failed to open netlink "
103 : 0 : "socket: %s", strerror(errno));
104 : 0 : netlink_deinit(netlink);
105 : 0 : return NULL;
106 : : }
107 : :
108 : 5 : os_memset(&local, 0, sizeof(local));
109 : 5 : local.nl_family = AF_NETLINK;
110 : 5 : local.nl_groups = RTMGRP_LINK;
111 [ - + ]: 5 : if (bind(netlink->sock, (struct sockaddr *) &local, sizeof(local)) < 0)
112 : : {
113 : 0 : wpa_printf(MSG_ERROR, "netlink: Failed to bind netlink "
114 : 0 : "socket: %s", strerror(errno));
115 : 0 : netlink_deinit(netlink);
116 : 0 : return NULL;
117 : : }
118 : :
119 : 5 : eloop_register_read_sock(netlink->sock, netlink_receive, netlink,
120 : : NULL);
121 : :
122 : 5 : netlink->cfg = cfg;
123 : :
124 : 5 : return netlink;
125 : : }
126 : :
127 : :
128 : 5 : void netlink_deinit(struct netlink_data *netlink)
129 : : {
130 [ - + ]: 5 : if (netlink == NULL)
131 : 5 : return;
132 [ + - ]: 5 : if (netlink->sock >= 0) {
133 : 5 : eloop_unregister_read_sock(netlink->sock);
134 : 5 : close(netlink->sock);
135 : : }
136 : 5 : os_free(netlink->cfg);
137 : 5 : os_free(netlink);
138 : : }
139 : :
140 : :
141 : 5061 : static const char * linkmode_str(int mode)
142 : : {
143 [ + + + - ]: 5061 : switch (mode) {
144 : : case -1:
145 : 4651 : return "no change";
146 : : case 0:
147 : 371 : return "kernel-control";
148 : : case 1:
149 : 39 : return "userspace-control";
150 : : }
151 : 5061 : return "?";
152 : : }
153 : :
154 : :
155 : 5061 : static const char * operstate_str(int state)
156 : : {
157 [ - + + - ]: 5061 : switch (state) {
158 : : case -1:
159 : 0 : return "no change";
160 : : case IF_OPER_DORMANT:
161 : 3549 : return "IF_OPER_DORMANT";
162 : : case IF_OPER_UP:
163 : 1512 : return "IF_OPER_UP";
164 : : }
165 : 5061 : return "?";
166 : : }
167 : :
168 : :
169 : 5061 : 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 : 5061 : os_memset(&req, 0, sizeof(req));
182 : :
183 : 5061 : req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
184 : 5061 : req.hdr.nlmsg_type = RTM_SETLINK;
185 : 5061 : req.hdr.nlmsg_flags = NLM_F_REQUEST;
186 : 5061 : req.hdr.nlmsg_seq = ++nl_seq;
187 : 5061 : req.hdr.nlmsg_pid = 0;
188 : :
189 : 5061 : req.ifinfo.ifi_family = AF_UNSPEC;
190 : 5061 : req.ifinfo.ifi_type = 0;
191 : 5061 : req.ifinfo.ifi_index = ifindex;
192 : 5061 : req.ifinfo.ifi_flags = 0;
193 : 5061 : req.ifinfo.ifi_change = 0;
194 : :
195 [ + + ]: 5061 : if (linkmode != -1) {
196 : 410 : rta = aliasing_hide_typecast(
197 : : ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)),
198 : : struct rtattr);
199 : 410 : rta->rta_type = IFLA_LINKMODE;
200 : 410 : rta->rta_len = RTA_LENGTH(sizeof(char));
201 : 410 : *((char *) RTA_DATA(rta)) = linkmode;
202 : 410 : req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) +
203 : : RTA_LENGTH(sizeof(char));
204 : : }
205 [ + - ]: 5061 : if (operstate != -1) {
206 : 5061 : rta = aliasing_hide_typecast(
207 : : ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)),
208 : : struct rtattr);
209 : 5061 : rta->rta_type = IFLA_OPERSTATE;
210 : 5061 : rta->rta_len = RTA_LENGTH(sizeof(char));
211 : 5061 : *((char *) RTA_DATA(rta)) = operstate;
212 : 5061 : req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) +
213 : : RTA_LENGTH(sizeof(char));
214 : : }
215 : :
216 : 5061 : 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 : 5061 : ret = send(netlink->sock, &req, req.hdr.nlmsg_len, 0);
221 [ - + ]: 5061 : if (ret < 0) {
222 : 0 : wpa_printf(MSG_DEBUG, "netlink: Sending operstate IFLA "
223 : : "failed: %s (assume operstate is not supported)",
224 : 0 : strerror(errno));
225 : : }
226 : :
227 [ - + ]: 5061 : return ret < 0 ? -1 : 0;
228 : : }
|