blob: 2fa20b1ebb4d17c5ba79b20af4a19df7202a01c6 [file] [log] [blame]
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001/*
2 * Netlink helper functions for driver wrappers
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08003 * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004 *
Dmitry Shmidtc5ec7f52012-03-06 16:33:24 -08005 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07007 */
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),
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -080031 (u8 *) NLMSG_DATA(h) + NLMSG_ALIGN(sizeof(struct ifinfomsg)),
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070032 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
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700100 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
Dmitry Shmidtd5e49232012-12-03 15:08:10 -0800122 netlink->cfg = cfg;
123
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700124 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
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800140
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
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700169int 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
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800216 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));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700219
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}