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