automake build system
[mech_eap.orig] / src / drivers / netlink.c
1 /*
2  * Netlink helper functions for driver wrappers
3  * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation.
8  *
9  * Alternatively, this software may be distributed under the terms of BSD
10  * license.
11  *
12  * See README and COPYING for more details.
13  */
14
15 #include "includes.h"
16
17 #include "common.h"
18 #include "eloop.h"
19 #include "priv_netlink.h"
20 #include "netlink.h"
21
22
23 struct netlink_data {
24         struct netlink_config *cfg;
25         int sock;
26 };
27
28
29 static void netlink_receive_link(struct netlink_data *netlink,
30                                  void (*cb)(void *ctx, struct ifinfomsg *ifi,
31                                             u8 *buf, size_t len),
32                                  struct nlmsghdr *h)
33 {
34         if (cb == NULL || NLMSG_PAYLOAD(h, 0) < sizeof(struct ifinfomsg))
35                 return;
36         cb(netlink->cfg->ctx, NLMSG_DATA(h),
37            NLMSG_DATA(h) + NLMSG_ALIGN(sizeof(struct ifinfomsg)),
38            NLMSG_PAYLOAD(h, sizeof(struct ifinfomsg)));
39 }
40
41
42 static void netlink_receive(int sock, void *eloop_ctx, void *sock_ctx)
43 {
44         struct netlink_data *netlink = eloop_ctx;
45         char buf[8192];
46         int left;
47         struct sockaddr_nl from;
48         socklen_t fromlen;
49         struct nlmsghdr *h;
50         int max_events = 10;
51
52 try_again:
53         fromlen = sizeof(from);
54         left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT,
55                         (struct sockaddr *) &from, &fromlen);
56         if (left < 0) {
57                 if (errno != EINTR && errno != EAGAIN)
58                         wpa_printf(MSG_INFO, "netlink: recvfrom failed: %s",
59                                    strerror(errno));
60                 return;
61         }
62
63         h = (struct nlmsghdr *) buf;
64         while (NLMSG_OK(h, left)) {
65                 switch (h->nlmsg_type) {
66                 case RTM_NEWLINK:
67                         netlink_receive_link(netlink, netlink->cfg->newlink_cb,
68                                              h);
69                         break;
70                 case RTM_DELLINK:
71                         netlink_receive_link(netlink, netlink->cfg->dellink_cb,
72                                              h);
73                         break;
74                 }
75
76                 h = NLMSG_NEXT(h, left);
77         }
78
79         if (left > 0) {
80                 wpa_printf(MSG_DEBUG, "netlink: %d extra bytes in the end of "
81                            "netlink message", left);
82         }
83
84         if (--max_events > 0) {
85                 /*
86                  * Try to receive all events in one eloop call in order to
87                  * limit race condition on cases where AssocInfo event, Assoc
88                  * event, and EAPOL frames are received more or less at the
89                  * same time. We want to process the event messages first
90                  * before starting EAPOL processing.
91                  */
92                 goto try_again;
93         }
94 }
95
96
97 struct netlink_data * netlink_init(struct netlink_config *cfg)
98 {
99         struct netlink_data *netlink;
100         struct sockaddr_nl local;
101
102         netlink = os_zalloc(sizeof(*netlink));
103         if (netlink == NULL)
104                 return NULL;
105
106         netlink->cfg = cfg;
107
108         netlink->sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
109         if (netlink->sock < 0) {
110                 wpa_printf(MSG_ERROR, "netlink: Failed to open netlink "
111                            "socket: %s", strerror(errno));
112                 netlink_deinit(netlink);
113                 return NULL;
114         }
115
116         os_memset(&local, 0, sizeof(local));
117         local.nl_family = AF_NETLINK;
118         local.nl_groups = RTMGRP_LINK;
119         if (bind(netlink->sock, (struct sockaddr *) &local, sizeof(local)) < 0)
120         {
121                 wpa_printf(MSG_ERROR, "netlink: Failed to bind netlink "
122                            "socket: %s", strerror(errno));
123                 netlink_deinit(netlink);
124                 return NULL;
125         }
126
127         eloop_register_read_sock(netlink->sock, netlink_receive, netlink,
128                                  NULL);
129
130         return netlink;
131 }
132
133
134 void netlink_deinit(struct netlink_data *netlink)
135 {
136         if (netlink == NULL)
137                 return;
138         if (netlink->sock >= 0) {
139                 eloop_unregister_read_sock(netlink->sock);
140                 close(netlink->sock);
141         }
142         os_free(netlink->cfg);
143         os_free(netlink);
144 }
145
146 int netlink_send_oper_ifla(struct netlink_data *netlink, int ifindex,
147                            int linkmode, int operstate)
148 {
149         struct {
150                 struct nlmsghdr hdr;
151                 struct ifinfomsg ifinfo;
152                 char opts[16];
153         } req;
154         struct rtattr *rta;
155         static int nl_seq;
156         ssize_t ret;
157
158         os_memset(&req, 0, sizeof(req));
159
160         req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
161         req.hdr.nlmsg_type = RTM_SETLINK;
162         req.hdr.nlmsg_flags = NLM_F_REQUEST;
163         req.hdr.nlmsg_seq = ++nl_seq;
164         req.hdr.nlmsg_pid = 0;
165
166         req.ifinfo.ifi_family = AF_UNSPEC;
167         req.ifinfo.ifi_type = 0;
168         req.ifinfo.ifi_index = ifindex;
169         req.ifinfo.ifi_flags = 0;
170         req.ifinfo.ifi_change = 0;
171
172         if (linkmode != -1) {
173                 rta = aliasing_hide_typecast(
174                         ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)),
175                         struct rtattr);
176                 rta->rta_type = IFLA_LINKMODE;
177                 rta->rta_len = RTA_LENGTH(sizeof(char));
178                 *((char *) RTA_DATA(rta)) = linkmode;
179                 req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) +
180                         RTA_LENGTH(sizeof(char));
181         }
182         if (operstate != -1) {
183                 rta = aliasing_hide_typecast(
184                         ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)),
185                         struct rtattr);
186                 rta->rta_type = IFLA_OPERSTATE;
187                 rta->rta_len = RTA_LENGTH(sizeof(char));
188                 *((char *) RTA_DATA(rta)) = operstate;
189                 req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) +
190                         RTA_LENGTH(sizeof(char));
191         }
192
193         wpa_printf(MSG_DEBUG, "netlink: Operstate: linkmode=%d, operstate=%d",
194                    linkmode, operstate);
195
196         ret = send(netlink->sock, &req, req.hdr.nlmsg_len, 0);
197         if (ret < 0) {
198                 wpa_printf(MSG_DEBUG, "netlink: Sending operstate IFLA "
199                            "failed: %s (assume operstate is not supported)",
200                            strerror(errno));
201         }
202
203         return ret < 0 ? -1 : 0;
204 }