xref: /dragonfly/contrib/wpa_supplicant/src/drivers/netlink.c (revision 3a84a4273475ed07d0ab1c2dfeffdfedef35d9cd)
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 
netlink_receive_link(struct netlink_data * netlink,void (* cb)(void * ctx,struct ifinfomsg * ifi,u8 * buf,size_t len),struct nlmsghdr * h)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 
netlink_receive(int sock,void * eloop_ctx,void * sock_ctx)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 
netlink_init(struct netlink_config * cfg)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 
netlink_deinit(struct netlink_data * netlink)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 
linkmode_str(int mode)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 
operstate_str(int state)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 
netlink_send_oper_ifla(struct netlink_data * netlink,int ifindex,int linkmode,int operstate)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 += RTA_SPACE(sizeof(char));
203           }
204           if (operstate != -1) {
205                     rta = aliasing_hide_typecast(
206                               ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)),
207                               struct rtattr);
208                     rta->rta_type = IFLA_OPERSTATE;
209                     rta->rta_len = RTA_LENGTH(sizeof(char));
210                     *((char *) RTA_DATA(rta)) = operstate;
211                     req.hdr.nlmsg_len += RTA_SPACE(sizeof(char));
212           }
213 
214           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           ret = send(netlink->sock, &req, req.hdr.nlmsg_len, 0);
219           if (ret < 0) {
220                     wpa_printf(MSG_DEBUG, "netlink: Sending operstate IFLA "
221                                  "failed: %s (assume operstate is not supported)",
222                                  strerror(errno));
223           }
224 
225           return ret < 0 ? -1 : 0;
226 }
227