Merge branch 'vendor/BINUTILS225'
[dragonfly.git] / contrib / hostapd / src / drivers / netlink.c
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 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
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
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
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
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         }
151         return "?";
152 }
153
154
155 static 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
169 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         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
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 }