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