]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - contrib/wpa/hostapd/ctrl_iface.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / contrib / wpa / hostapd / ctrl_iface.c
1 /*
2  * hostapd / UNIX domain socket -based control interface
3  * Copyright (c) 2004-2008, 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 #ifndef CONFIG_NATIVE_WINDOWS
18
19 #include <sys/un.h>
20 #include <sys/stat.h>
21
22 #include "hostapd.h"
23 #include "eloop.h"
24 #include "config.h"
25 #include "ieee802_1x.h"
26 #include "wpa.h"
27 #include "radius/radius_client.h"
28 #include "ieee802_11.h"
29 #include "ctrl_iface.h"
30 #include "sta_info.h"
31 #include "accounting.h"
32 #include "wps_hostapd.h"
33
34
35 struct wpa_ctrl_dst {
36         struct wpa_ctrl_dst *next;
37         struct sockaddr_un addr;
38         socklen_t addrlen;
39         int debug_level;
40         int errors;
41 };
42
43
44 static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
45                                     const char *buf, size_t len);
46
47
48 static int hostapd_ctrl_iface_attach(struct hostapd_data *hapd,
49                                      struct sockaddr_un *from,
50                                      socklen_t fromlen)
51 {
52         struct wpa_ctrl_dst *dst;
53
54         dst = os_zalloc(sizeof(*dst));
55         if (dst == NULL)
56                 return -1;
57         os_memcpy(&dst->addr, from, sizeof(struct sockaddr_un));
58         dst->addrlen = fromlen;
59         dst->debug_level = MSG_INFO;
60         dst->next = hapd->ctrl_dst;
61         hapd->ctrl_dst = dst;
62         wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor attached",
63                     (u8 *) from->sun_path, fromlen);
64         return 0;
65 }
66
67
68 static int hostapd_ctrl_iface_detach(struct hostapd_data *hapd,
69                                      struct sockaddr_un *from,
70                                      socklen_t fromlen)
71 {
72         struct wpa_ctrl_dst *dst, *prev = NULL;
73
74         dst = hapd->ctrl_dst;
75         while (dst) {
76                 if (fromlen == dst->addrlen &&
77                     os_memcmp(from->sun_path, dst->addr.sun_path, fromlen) ==
78                     0) {
79                         if (prev == NULL)
80                                 hapd->ctrl_dst = dst->next;
81                         else
82                                 prev->next = dst->next;
83                         os_free(dst);
84                         wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor detached",
85                                     (u8 *) from->sun_path, fromlen);
86                         return 0;
87                 }
88                 prev = dst;
89                 dst = dst->next;
90         }
91         return -1;
92 }
93
94
95 static int hostapd_ctrl_iface_level(struct hostapd_data *hapd,
96                                     struct sockaddr_un *from,
97                                     socklen_t fromlen,
98                                     char *level)
99 {
100         struct wpa_ctrl_dst *dst;
101
102         wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level);
103
104         dst = hapd->ctrl_dst;
105         while (dst) {
106                 if (fromlen == dst->addrlen &&
107                     os_memcmp(from->sun_path, dst->addr.sun_path, fromlen) ==
108                     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, ret;
126
127         if (sta == NULL) {
128                 ret = os_snprintf(buf, buflen, "FAIL\n");
129                 if (ret < 0 || (size_t) ret >= buflen)
130                         return 0;
131                 return ret;
132         }
133
134         len = 0;
135         ret = os_snprintf(buf + len, buflen - len, MACSTR "\n",
136                           MAC2STR(sta->addr));
137         if (ret < 0 || (size_t) ret >= buflen - len)
138                 return len;
139         len += ret;
140
141         res = ieee802_11_get_mib_sta(hapd, sta, buf + len, buflen - len);
142         if (res >= 0)
143                 len += res;
144         res = wpa_get_mib_sta(sta->wpa_sm, buf + len, buflen - len);
145         if (res >= 0)
146                 len += res;
147         res = ieee802_1x_get_mib_sta(hapd, sta, buf + len, buflen - len);
148         if (res >= 0)
149                 len += res;
150
151         return len;
152 }
153
154
155 static int hostapd_ctrl_iface_sta_first(struct hostapd_data *hapd,
156                                         char *buf, size_t buflen)
157 {
158         return hostapd_ctrl_iface_sta_mib(hapd, hapd->sta_list, buf, buflen);
159 }
160
161
162 static int hostapd_ctrl_iface_sta(struct hostapd_data *hapd,
163                                   const char *txtaddr,
164                                   char *buf, size_t buflen)
165 {
166         u8 addr[ETH_ALEN];
167         int ret;
168
169         if (hwaddr_aton(txtaddr, addr)) {
170                 ret = os_snprintf(buf, buflen, "FAIL\n");
171                 if (ret < 0 || (size_t) ret >= buflen)
172                         return 0;
173                 return ret;
174         }
175         return hostapd_ctrl_iface_sta_mib(hapd, ap_get_sta(hapd, addr),
176                                           buf, buflen);
177 }
178
179
180 static int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd,
181                                        const char *txtaddr,
182                                        char *buf, size_t buflen)
183 {
184         u8 addr[ETH_ALEN];
185         struct sta_info *sta;
186         int ret;
187
188         if (hwaddr_aton(txtaddr, addr) ||
189             (sta = ap_get_sta(hapd, addr)) == NULL) {
190                 ret = os_snprintf(buf, buflen, "FAIL\n");
191                 if (ret < 0 || (size_t) ret >= buflen)
192                         return 0;
193                 return ret;
194         }               
195         return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen);
196 }
197
198
199 static int hostapd_ctrl_iface_new_sta(struct hostapd_data *hapd,
200                                       const char *txtaddr)
201 {
202         u8 addr[ETH_ALEN];
203         struct sta_info *sta;
204
205         wpa_printf(MSG_DEBUG, "CTRL_IFACE NEW_STA %s", txtaddr);
206
207         if (hwaddr_aton(txtaddr, addr))
208                 return -1;
209
210         sta = ap_get_sta(hapd, addr);
211         if (sta)
212                 return 0;
213
214         wpa_printf(MSG_DEBUG, "Add new STA " MACSTR " based on ctrl_iface "
215                    "notification", MAC2STR(addr));
216         sta = ap_sta_add(hapd, addr);
217         if (sta == NULL)
218                 return -1;
219
220         hostapd_new_assoc_sta(hapd, sta, 0);
221         return 0;
222 }
223
224
225 #ifdef CONFIG_IEEE80211W
226 static int hostapd_ctrl_iface_sa_query(struct hostapd_data *hapd,
227                                        const char *txtaddr)
228 {
229         u8 addr[ETH_ALEN];
230         u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN];
231
232         wpa_printf(MSG_DEBUG, "CTRL_IFACE SA_QUERY %s", txtaddr);
233
234         if (hwaddr_aton(txtaddr, addr))
235                 return -1;
236
237         os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN);
238         ieee802_11_send_sa_query_req(hapd, addr, trans_id);
239
240         return 0;
241 }
242 #endif /* CONFIG_IEEE80211W */
243
244
245 #ifdef CONFIG_WPS
246 static int hostapd_ctrl_iface_wps_pin(struct hostapd_data *hapd, char *txt)
247 {
248         char *pin = os_strchr(txt, ' ');
249         if (pin == NULL)
250                 return -1;
251         *pin++ = '\0';
252         return hostapd_wps_add_pin(hapd, txt, pin);
253 }
254 #endif /* CONFIG_WPS */
255
256
257 static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
258                                        void *sock_ctx)
259 {
260         struct hostapd_data *hapd = eloop_ctx;
261         char buf[256];
262         int res;
263         struct sockaddr_un from;
264         socklen_t fromlen = sizeof(from);
265         char *reply;
266         const int reply_size = 4096;
267         int reply_len;
268
269         res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
270                        (struct sockaddr *) &from, &fromlen);
271         if (res < 0) {
272                 perror("recvfrom(ctrl_iface)");
273                 return;
274         }
275         buf[res] = '\0';
276         wpa_hexdump_ascii(MSG_DEBUG, "RX ctrl_iface", (u8 *) buf, res);
277
278         reply = os_malloc(reply_size);
279         if (reply == NULL) {
280                 sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
281                        fromlen);
282                 return;
283         }
284
285         os_memcpy(reply, "OK\n", 3);
286         reply_len = 3;
287
288         if (os_strcmp(buf, "PING") == 0) {
289                 os_memcpy(reply, "PONG\n", 5);
290                 reply_len = 5;
291         } else if (os_strcmp(buf, "MIB") == 0) {
292                 reply_len = ieee802_11_get_mib(hapd, reply, reply_size);
293                 if (reply_len >= 0) {
294                         res = wpa_get_mib(hapd->wpa_auth, reply + reply_len,
295                                           reply_size - reply_len);
296                         if (res < 0)
297                                 reply_len = -1;
298                         else
299                                 reply_len += res;
300                 }
301                 if (reply_len >= 0) {
302                         res = ieee802_1x_get_mib(hapd, reply + reply_len,
303                                                  reply_size - reply_len);
304                         if (res < 0)
305                                 reply_len = -1;
306                         else
307                                 reply_len += res;
308                 }
309                 if (reply_len >= 0) {
310                         res = radius_client_get_mib(hapd->radius,
311                                                     reply + reply_len,
312                                                     reply_size - reply_len);
313                         if (res < 0)
314                                 reply_len = -1;
315                         else
316                                 reply_len += res;
317                 }
318         } else if (os_strcmp(buf, "STA-FIRST") == 0) {
319                 reply_len = hostapd_ctrl_iface_sta_first(hapd, reply,
320                                                          reply_size);
321         } else if (os_strncmp(buf, "STA ", 4) == 0) {
322                 reply_len = hostapd_ctrl_iface_sta(hapd, buf + 4, reply,
323                                                    reply_size);
324         } else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) {
325                 reply_len = hostapd_ctrl_iface_sta_next(hapd, buf + 9, reply,
326                                                         reply_size);
327         } else if (os_strcmp(buf, "ATTACH") == 0) {
328                 if (hostapd_ctrl_iface_attach(hapd, &from, fromlen))
329                         reply_len = -1;
330         } else if (os_strcmp(buf, "DETACH") == 0) {
331                 if (hostapd_ctrl_iface_detach(hapd, &from, fromlen))
332                         reply_len = -1;
333         } else if (os_strncmp(buf, "LEVEL ", 6) == 0) {
334                 if (hostapd_ctrl_iface_level(hapd, &from, fromlen,
335                                                     buf + 6))
336                         reply_len = -1;
337         } else if (os_strncmp(buf, "NEW_STA ", 8) == 0) {
338                 if (hostapd_ctrl_iface_new_sta(hapd, buf + 8))
339                         reply_len = -1;
340 #ifdef CONFIG_IEEE80211W
341         } else if (os_strncmp(buf, "SA_QUERY ", 9) == 0) {
342                 if (hostapd_ctrl_iface_sa_query(hapd, buf + 9))
343                         reply_len = -1;
344 #endif /* CONFIG_IEEE80211W */
345 #ifdef CONFIG_WPS
346         } else if (os_strncmp(buf, "WPS_PIN ", 8) == 0) {
347                 if (hostapd_ctrl_iface_wps_pin(hapd, buf + 8))
348                         reply_len = -1;
349         } else if (os_strcmp(buf, "WPS_PBC") == 0) {
350                 if (hostapd_wps_button_pushed(hapd))
351                         reply_len = -1;
352 #endif /* CONFIG_WPS */
353         } else {
354                 os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
355                 reply_len = 16;
356         }
357
358         if (reply_len < 0) {
359                 os_memcpy(reply, "FAIL\n", 5);
360                 reply_len = 5;
361         }
362         sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, fromlen);
363         os_free(reply);
364 }
365
366
367 static char * hostapd_ctrl_iface_path(struct hostapd_data *hapd)
368 {
369         char *buf;
370         size_t len;
371
372         if (hapd->conf->ctrl_interface == NULL)
373                 return NULL;
374
375         len = os_strlen(hapd->conf->ctrl_interface) +
376                 os_strlen(hapd->conf->iface) + 2;
377         buf = os_malloc(len);
378         if (buf == NULL)
379                 return NULL;
380
381         os_snprintf(buf, len, "%s/%s",
382                     hapd->conf->ctrl_interface, hapd->conf->iface);
383         buf[len - 1] = '\0';
384         return buf;
385 }
386
387
388 static void hostapd_ctrl_iface_msg_cb(void *ctx, int level,
389                                       const char *txt, size_t len)
390 {
391         struct hostapd_data *hapd = ctx;
392         if (hapd == NULL)
393                 return;
394         hostapd_ctrl_iface_send(hapd, level, txt, len);
395 }
396
397
398 int hostapd_ctrl_iface_init(struct hostapd_data *hapd)
399 {
400         struct sockaddr_un addr;
401         int s = -1;
402         char *fname = NULL;
403
404         hapd->ctrl_sock = -1;
405
406         if (hapd->conf->ctrl_interface == NULL)
407                 return 0;
408
409         if (mkdir(hapd->conf->ctrl_interface, S_IRWXU | S_IRWXG) < 0) {
410                 if (errno == EEXIST) {
411                         wpa_printf(MSG_DEBUG, "Using existing control "
412                                    "interface directory.");
413                 } else {
414                         perror("mkdir[ctrl_interface]");
415                         goto fail;
416                 }
417         }
418
419         if (hapd->conf->ctrl_interface_gid_set &&
420             chown(hapd->conf->ctrl_interface, 0,
421                   hapd->conf->ctrl_interface_gid) < 0) {
422                 perror("chown[ctrl_interface]");
423                 return -1;
424         }
425
426         if (os_strlen(hapd->conf->ctrl_interface) + 1 +
427             os_strlen(hapd->conf->iface) >= sizeof(addr.sun_path))
428                 goto fail;
429
430         s = socket(PF_UNIX, SOCK_DGRAM, 0);
431         if (s < 0) {
432                 perror("socket(PF_UNIX)");
433                 goto fail;
434         }
435
436         os_memset(&addr, 0, sizeof(addr));
437         addr.sun_family = AF_UNIX;
438         fname = hostapd_ctrl_iface_path(hapd);
439         if (fname == NULL)
440                 goto fail;
441         os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path));
442         if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
443                 perror("bind(PF_UNIX)");
444                 goto fail;
445         }
446
447         if (hapd->conf->ctrl_interface_gid_set &&
448             chown(fname, 0, hapd->conf->ctrl_interface_gid) < 0) {
449                 perror("chown[ctrl_interface/ifname]");
450                 goto fail;
451         }
452
453         if (chmod(fname, S_IRWXU | S_IRWXG) < 0) {
454                 perror("chmod[ctrl_interface/ifname]");
455                 goto fail;
456         }
457         os_free(fname);
458
459         hapd->ctrl_sock = s;
460         eloop_register_read_sock(s, hostapd_ctrl_iface_receive, hapd,
461                                  NULL);
462         wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb);
463
464         return 0;
465
466 fail:
467         if (s >= 0)
468                 close(s);
469         if (fname) {
470                 unlink(fname);
471                 os_free(fname);
472         }
473         return -1;
474 }
475
476
477 void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd)
478 {
479         struct wpa_ctrl_dst *dst, *prev;
480
481         if (hapd->ctrl_sock > -1) {
482                 char *fname;
483                 eloop_unregister_read_sock(hapd->ctrl_sock);
484                 close(hapd->ctrl_sock);
485                 hapd->ctrl_sock = -1;
486                 fname = hostapd_ctrl_iface_path(hapd);
487                 if (fname)
488                         unlink(fname);
489                 os_free(fname);
490
491                 if (hapd->conf->ctrl_interface &&
492                     rmdir(hapd->conf->ctrl_interface) < 0) {
493                         if (errno == ENOTEMPTY) {
494                                 wpa_printf(MSG_DEBUG, "Control interface "
495                                            "directory not empty - leaving it "
496                                            "behind");
497                         } else {
498                                 perror("rmdir[ctrl_interface]");
499                         }
500                 }
501         }
502
503         dst = hapd->ctrl_dst;
504         while (dst) {
505                 prev = dst;
506                 dst = dst->next;
507                 os_free(prev);
508         }
509 }
510
511
512 static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
513                                     const char *buf, size_t len)
514 {
515         struct wpa_ctrl_dst *dst, *next;
516         struct msghdr msg;
517         int idx;
518         struct iovec io[2];
519         char levelstr[10];
520
521         dst = hapd->ctrl_dst;
522         if (hapd->ctrl_sock < 0 || dst == NULL)
523                 return;
524
525         os_snprintf(levelstr, sizeof(levelstr), "<%d>", level);
526         io[0].iov_base = levelstr;
527         io[0].iov_len = os_strlen(levelstr);
528         io[1].iov_base = (char *) buf;
529         io[1].iov_len = len;
530         os_memset(&msg, 0, sizeof(msg));
531         msg.msg_iov = io;
532         msg.msg_iovlen = 2;
533
534         idx = 0;
535         while (dst) {
536                 next = dst->next;
537                 if (level >= dst->debug_level) {
538                         wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor send",
539                                     (u8 *) dst->addr.sun_path, dst->addrlen);
540                         msg.msg_name = &dst->addr;
541                         msg.msg_namelen = dst->addrlen;
542                         if (sendmsg(hapd->ctrl_sock, &msg, 0) < 0) {
543                                 fprintf(stderr, "CTRL_IFACE monitor[%d]: ",
544                                         idx);
545                                 perror("sendmsg");
546                                 dst->errors++;
547                                 if (dst->errors > 10) {
548                                         hostapd_ctrl_iface_detach(
549                                                 hapd, &dst->addr,
550                                                 dst->addrlen);
551                                 }
552                         } else
553                                 dst->errors = 0;
554                 }
555                 idx++;
556                 dst = next;
557         }
558 }
559
560 #endif /* CONFIG_NATIVE_WINDOWS */