Merge from vendor branch OPENSSL:
[dragonfly.git] / contrib / hostapd-0.4.9 / ctrl_iface.c
1 /*
2  * Host AP (software wireless LAN access point) user space daemon for
3  * Host AP kernel driver / UNIX domain socket -based control interface
4  * Copyright (c) 2004, Jouni Malinen <jkmaline@cc.hut.fi>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  *
10  * Alternatively, this software may be distributed under the terms of BSD
11  * license.
12  *
13  * See README and COPYING for more details.
14  */
15
16 #include <stdlib.h>
17 #include <stdio.h>
18 #include <string.h>
19 #include <unistd.h>
20 #include <sys/types.h>
21 #include <sys/socket.h>
22 #include <sys/un.h>
23 #include <sys/uio.h>
24 #include <sys/stat.h>
25 #include <errno.h>
26 #include <netinet/in.h>
27
28 #include "hostapd.h"
29 #include "eloop.h"
30 #include "config.h"
31 #include "eapol_sm.h"
32 #include "ieee802_1x.h"
33 #include "wpa.h"
34 #include "radius_client.h"
35 #include "ieee802_11.h"
36 #include "ctrl_iface.h"
37 #include "sta_info.h"
38
39
40 struct wpa_ctrl_dst {
41         struct wpa_ctrl_dst *next;
42         struct sockaddr_un addr;
43         socklen_t addrlen;
44         int debug_level;
45         int errors;
46 };
47
48
49 static int hostapd_ctrl_iface_attach(struct hostapd_data *hapd,
50                                      struct sockaddr_un *from,
51                                      socklen_t fromlen)
52 {
53         struct wpa_ctrl_dst *dst;
54
55         dst = malloc(sizeof(*dst));
56         if (dst == NULL)
57                 return -1;
58         memset(dst, 0, sizeof(*dst));
59         memcpy(&dst->addr, from, sizeof(struct sockaddr_un));
60         dst->addrlen = fromlen;
61         dst->debug_level = MSG_INFO;
62         dst->next = hapd->ctrl_dst;
63         hapd->ctrl_dst = dst;
64         wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor attached",
65                     (u8 *) from->sun_path, fromlen);
66         return 0;
67 }
68
69
70 static int hostapd_ctrl_iface_detach(struct hostapd_data *hapd,
71                                      struct sockaddr_un *from,
72                                      socklen_t fromlen)
73 {
74         struct wpa_ctrl_dst *dst, *prev = NULL;
75
76         dst = hapd->ctrl_dst;
77         while (dst) {
78                 if (fromlen == dst->addrlen &&
79                     memcmp(from->sun_path, dst->addr.sun_path, fromlen) == 0) {
80                         if (prev == NULL)
81                                 hapd->ctrl_dst = dst->next;
82                         else
83                                 prev->next = dst->next;
84                         free(dst);
85                         wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor detached",
86                                     (u8 *) from->sun_path, fromlen);
87                         return 0;
88                 }
89                 prev = dst;
90                 dst = dst->next;
91         }
92         return -1;
93 }
94
95
96 static int hostapd_ctrl_iface_level(struct hostapd_data *hapd,
97                                     struct sockaddr_un *from,
98                                     socklen_t fromlen,
99                                     char *level)
100 {
101         struct wpa_ctrl_dst *dst;
102
103         wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level);
104
105         dst = hapd->ctrl_dst;
106         while (dst) {
107                 if (fromlen == dst->addrlen &&
108                     memcmp(from->sun_path, dst->addr.sun_path, fromlen) == 0) {
109                         wpa_hexdump(MSG_DEBUG, "CTRL_IFACE changed monitor "
110                                     "level", (u8 *) from->sun_path, fromlen);
111                         dst->debug_level = atoi(level);
112                         return 0;
113                 }
114                 dst = dst->next;
115         }
116
117         return -1;
118 }
119
120
121 static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd,
122                                       struct sta_info *sta,
123                                       char *buf, size_t buflen)
124 {
125         int len, res;
126
127         if (sta == NULL) {
128                 return snprintf(buf, buflen, "FAIL\n");
129         }
130
131         len = 0;
132         len += snprintf(buf + len, buflen - len, MACSTR "\n",
133                         MAC2STR(sta->addr));
134
135         res = ieee802_11_get_mib_sta(hapd, sta, buf + len, buflen - len);
136         if (res >= 0)
137                 len += res;
138         res = wpa_get_mib_sta(hapd, sta, buf + len, buflen - len);
139         if (res >= 0)
140                 len += res;
141         res = ieee802_1x_get_mib_sta(hapd, sta, buf + len, buflen - len);
142         if (res >= 0)
143                 len += res;
144
145         return len;
146 }
147
148
149 static int hostapd_ctrl_iface_sta_first(struct hostapd_data *hapd,
150                                         char *buf, size_t buflen)
151 {
152         return hostapd_ctrl_iface_sta_mib(hapd, hapd->sta_list, buf, buflen);
153 }
154
155
156 static int hostapd_ctrl_iface_sta(struct hostapd_data *hapd,
157                                   const char *txtaddr,
158                                   char *buf, size_t buflen)
159 {
160         u8 addr[ETH_ALEN];
161
162         if (hwaddr_aton(txtaddr, addr))
163                 return snprintf(buf, buflen, "FAIL\n");
164         return hostapd_ctrl_iface_sta_mib(hapd, ap_get_sta(hapd, addr),
165                                           buf, buflen);
166 }
167
168
169 static int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd,
170                                        const char *txtaddr,
171                                        char *buf, size_t buflen)
172 {
173         u8 addr[ETH_ALEN];
174         struct sta_info *sta;
175
176         if (hwaddr_aton(txtaddr, addr) ||
177             (sta = ap_get_sta(hapd, addr)) == NULL)
178                 return snprintf(buf, buflen, "FAIL\n");
179         return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen);
180 }
181
182
183 static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
184                                        void *sock_ctx)
185 {
186         struct hostapd_data *hapd = eloop_ctx;
187         char buf[256];
188         int res;
189         struct sockaddr_un from;
190         socklen_t fromlen = sizeof(from);
191         char *reply;
192         const int reply_size = 4096;
193         int reply_len;
194
195         res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
196                        (struct sockaddr *) &from, &fromlen);
197         if (res < 0) {
198                 perror("recvfrom(ctrl_iface)");
199                 return;
200         }
201         buf[res] = '\0';
202         wpa_hexdump_ascii(MSG_DEBUG, "RX ctrl_iface", (u8 *) buf, res);
203
204         reply = malloc(reply_size);
205         if (reply == NULL) {
206                 sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
207                        fromlen);
208                 return;
209         }
210
211         memcpy(reply, "OK\n", 3);
212         reply_len = 3;
213
214         if (strcmp(buf, "PING") == 0) {
215                 memcpy(reply, "PONG\n", 5);
216                 reply_len = 5;
217         } else if (strcmp(buf, "MIB") == 0) {
218                 reply_len = ieee802_11_get_mib(hapd, reply, reply_size);
219                 if (reply_len >= 0) {
220                         res = wpa_get_mib(hapd, reply + reply_len,
221                                           reply_size - reply_len);
222                         if (res < 0)
223                                 reply_len = -1;
224                         else
225                                 reply_len += res;
226                 }
227                 if (reply_len >= 0) {
228                         res = ieee802_1x_get_mib(hapd, reply + reply_len,
229                                                  reply_size - reply_len);
230                         if (res < 0)
231                                 reply_len = -1;
232                         else
233                                 reply_len += res;
234                 }
235                 if (reply_len >= 0) {
236                         res = radius_client_get_mib(hapd->radius,
237                                                     reply + reply_len,
238                                                     reply_size - reply_len);
239                         if (res < 0)
240                                 reply_len = -1;
241                         else
242                                 reply_len += res;
243                 }
244         } else if (strcmp(buf, "STA-FIRST") == 0) {
245                 reply_len = hostapd_ctrl_iface_sta_first(hapd, reply,
246                                                          reply_size);
247         } else if (strncmp(buf, "STA ", 4) == 0) {
248                 reply_len = hostapd_ctrl_iface_sta(hapd, buf + 4, reply,
249                                                    reply_size);
250         } else if (strncmp(buf, "STA-NEXT ", 9) == 0) {
251                 reply_len = hostapd_ctrl_iface_sta_next(hapd, buf + 9, reply,
252                                                         reply_size);
253         } else if (strcmp(buf, "ATTACH") == 0) {
254                 if (hostapd_ctrl_iface_attach(hapd, &from, fromlen))
255                         reply_len = -1;
256         } else if (strcmp(buf, "DETACH") == 0) {
257                 if (hostapd_ctrl_iface_detach(hapd, &from, fromlen))
258                         reply_len = -1;
259         } else if (strncmp(buf, "LEVEL ", 6) == 0) {
260                 if (hostapd_ctrl_iface_level(hapd, &from, fromlen,
261                                                     buf + 6))
262                         reply_len = -1;
263         } else {
264                 memcpy(reply, "UNKNOWN COMMAND\n", 16);
265                 reply_len = 16;
266         }
267
268         if (reply_len < 0) {
269                 memcpy(reply, "FAIL\n", 5);
270                 reply_len = 5;
271         }
272         sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, fromlen);
273         free(reply);
274 }
275
276
277 static char * hostapd_ctrl_iface_path(struct hostapd_data *hapd)
278 {
279         char *buf;
280         size_t len;
281
282         if (hapd->conf->ctrl_interface == NULL)
283                 return NULL;
284
285         len = strlen(hapd->conf->ctrl_interface) + strlen(hapd->conf->iface) +
286                 2;
287         buf = malloc(len);
288         if (buf == NULL)
289                 return NULL;
290
291         snprintf(buf, len, "%s/%s",
292                  hapd->conf->ctrl_interface, hapd->conf->iface);
293         return buf;
294 }
295
296
297 int hostapd_ctrl_iface_init(struct hostapd_data *hapd)
298 {
299         struct sockaddr_un addr;
300         int s = -1;
301         char *fname = NULL;
302
303         hapd->ctrl_sock = -1;
304
305         if (hapd->conf->ctrl_interface == NULL)
306                 return 0;
307
308         if (mkdir(hapd->conf->ctrl_interface, S_IRWXU | S_IRWXG) < 0) {
309                 if (errno == EEXIST) {
310                         wpa_printf(MSG_DEBUG, "Using existing control "
311                                    "interface directory.");
312                 } else {
313                         perror("mkdir[ctrl_interface]");
314                         goto fail;
315                 }
316         }
317
318         if (hapd->conf->ctrl_interface_gid_set &&
319             chown(hapd->conf->ctrl_interface, 0,
320                   hapd->conf->ctrl_interface_gid) < 0) {
321                 perror("chown[ctrl_interface]");
322                 return -1;
323         }
324
325         if (strlen(hapd->conf->ctrl_interface) + 1 + strlen(hapd->conf->iface)
326             >= sizeof(addr.sun_path))
327                 goto fail;
328
329         s = socket(PF_UNIX, SOCK_DGRAM, 0);
330         if (s < 0) {
331                 perror("socket(PF_UNIX)");
332                 goto fail;
333         }
334
335         memset(&addr, 0, sizeof(addr));
336         addr.sun_family = AF_UNIX;
337         fname = hostapd_ctrl_iface_path(hapd);
338         if (fname == NULL)
339                 goto fail;
340         strncpy(addr.sun_path, fname, sizeof(addr.sun_path));
341         if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
342                 perror("bind(PF_UNIX)");
343                 goto fail;
344         }
345
346         if (hapd->conf->ctrl_interface_gid_set &&
347             chown(fname, 0, hapd->conf->ctrl_interface_gid) < 0) {
348                 perror("chown[ctrl_interface/ifname]");
349                 goto fail;
350         }
351
352         if (chmod(fname, S_IRWXU | S_IRWXG) < 0) {
353                 perror("chmod[ctrl_interface/ifname]");
354                 goto fail;
355         }
356         free(fname);
357
358         hapd->ctrl_sock = s;
359         eloop_register_read_sock(s, hostapd_ctrl_iface_receive, hapd,
360                                  NULL);
361
362         return 0;
363
364 fail:
365         if (s >= 0)
366                 close(s);
367         if (fname) {
368                 unlink(fname);
369                 free(fname);
370         }
371         return -1;
372 }
373
374
375 void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd)
376 {
377         struct wpa_ctrl_dst *dst, *prev;
378
379         if (hapd->ctrl_sock > -1) {
380                 char *fname;
381                 eloop_unregister_read_sock(hapd->ctrl_sock);
382                 close(hapd->ctrl_sock);
383                 hapd->ctrl_sock = -1;
384                 fname = hostapd_ctrl_iface_path(hapd);
385                 if (fname)
386                         unlink(fname);
387                 free(fname);
388
389                 if (hapd->conf->ctrl_interface &&
390                     rmdir(hapd->conf->ctrl_interface) < 0) {
391                         if (errno == ENOTEMPTY) {
392                                 wpa_printf(MSG_DEBUG, "Control interface "
393                                            "directory not empty - leaving it "
394                                            "behind");
395                         } else {
396                                 perror("rmdir[ctrl_interface]");
397                         }
398                 }
399         }
400
401         dst = hapd->ctrl_dst;
402         while (dst) {
403                 prev = dst;
404                 dst = dst->next;
405                 free(prev);
406         }
407 }
408
409
410 void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
411                              char *buf, size_t len)
412 {
413         struct wpa_ctrl_dst *dst, *next;
414         struct msghdr msg;
415         int idx;
416         struct iovec io[2];
417         char levelstr[10];
418
419         dst = hapd->ctrl_dst;
420         if (hapd->ctrl_sock < 0 || dst == NULL)
421                 return;
422
423         snprintf(levelstr, sizeof(levelstr), "<%d>", level);
424         io[0].iov_base = levelstr;
425         io[0].iov_len = strlen(levelstr);
426         io[1].iov_base = buf;
427         io[1].iov_len = len;
428         memset(&msg, 0, sizeof(msg));
429         msg.msg_iov = io;
430         msg.msg_iovlen = 2;
431
432         idx = 0;
433         while (dst) {
434                 next = dst->next;
435                 if (level >= dst->debug_level) {
436                         wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor send",
437                                     (u8 *) dst->addr.sun_path, dst->addrlen);
438                         msg.msg_name = &dst->addr;
439                         msg.msg_namelen = dst->addrlen;
440                         if (sendmsg(hapd->ctrl_sock, &msg, 0) < 0) {
441                                 fprintf(stderr, "CTRL_IFACE monitor[%d]: ",
442                                         idx);
443                                 perror("sendmsg");
444                                 dst->errors++;
445                                 if (dst->errors > 10) {
446                                         hostapd_ctrl_iface_detach(
447                                                 hapd, &dst->addr,
448                                                 dst->addrlen);
449                                 }
450                         } else
451                                 dst->errors = 0;
452                 }
453                 idx++;
454                 dst = next;
455         }
456 }