]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/hostapd/ctrl_iface.c
This commit was generated by cvs2svn to compensate for changes in r156251,
[FreeBSD/FreeBSD.git] / contrib / hostapd / 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 (chown(hapd->conf->ctrl_interface, 0,
319                   hapd->conf->ctrl_interface_gid) < 0) {
320                 perror("chown[ctrl_interface]");
321                 return -1;
322         }
323
324         if (strlen(hapd->conf->ctrl_interface) + 1 + strlen(hapd->conf->iface)
325             >= sizeof(addr.sun_path))
326                 goto fail;
327
328         s = socket(PF_UNIX, SOCK_DGRAM, 0);
329         if (s < 0) {
330                 perror("socket(PF_UNIX)");
331                 goto fail;
332         }
333
334         memset(&addr, 0, sizeof(addr));
335         addr.sun_family = AF_UNIX;
336         fname = hostapd_ctrl_iface_path(hapd);
337         if (fname == NULL)
338                 goto fail;
339         strncpy(addr.sun_path, fname, sizeof(addr.sun_path));
340         if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
341                 perror("bind(PF_UNIX)");
342                 goto fail;
343         }
344
345         if (chown(fname, 0, hapd->conf->ctrl_interface_gid) < 0) {
346                 perror("chown[ctrl_interface/ifname]");
347                 goto fail;
348         }
349
350         if (chmod(fname, S_IRWXU | S_IRWXG) < 0) {
351                 perror("chmod[ctrl_interface/ifname]");
352                 goto fail;
353         }
354         free(fname);
355
356         hapd->ctrl_sock = s;
357         eloop_register_read_sock(s, hostapd_ctrl_iface_receive, hapd,
358                                  NULL);
359
360         return 0;
361
362 fail:
363         if (s >= 0)
364                 close(s);
365         if (fname) {
366                 unlink(fname);
367                 free(fname);
368         }
369         return -1;
370 }
371
372
373 void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd)
374 {
375         struct wpa_ctrl_dst *dst, *prev;
376
377         if (hapd->ctrl_sock > -1) {
378                 char *fname;
379                 eloop_unregister_read_sock(hapd->ctrl_sock);
380                 close(hapd->ctrl_sock);
381                 hapd->ctrl_sock = -1;
382                 fname = hostapd_ctrl_iface_path(hapd);
383                 if (fname)
384                         unlink(fname);
385                 free(fname);
386
387                 if (hapd->conf->ctrl_interface &&
388                     rmdir(hapd->conf->ctrl_interface) < 0) {
389                         if (errno == ENOTEMPTY) {
390                                 wpa_printf(MSG_DEBUG, "Control interface "
391                                            "directory not empty - leaving it "
392                                            "behind");
393                         } else {
394                                 perror("rmdir[ctrl_interface]");
395                         }
396                 }
397         }
398
399         dst = hapd->ctrl_dst;
400         while (dst) {
401                 prev = dst;
402                 dst = dst->next;
403                 free(prev);
404         }
405 }
406
407
408 void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
409                              char *buf, size_t len)
410 {
411         struct wpa_ctrl_dst *dst, *next;
412         struct msghdr msg;
413         int idx;
414         struct iovec io[2];
415         char levelstr[10];
416
417         dst = hapd->ctrl_dst;
418         if (hapd->ctrl_sock < 0 || dst == NULL)
419                 return;
420
421         snprintf(levelstr, sizeof(levelstr), "<%d>", level);
422         io[0].iov_base = levelstr;
423         io[0].iov_len = strlen(levelstr);
424         io[1].iov_base = buf;
425         io[1].iov_len = len;
426         memset(&msg, 0, sizeof(msg));
427         msg.msg_iov = io;
428         msg.msg_iovlen = 2;
429
430         idx = 0;
431         while (dst) {
432                 next = dst->next;
433                 if (level >= dst->debug_level) {
434                         wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor send",
435                                     (u8 *) dst->addr.sun_path, dst->addrlen);
436                         msg.msg_name = &dst->addr;
437                         msg.msg_namelen = dst->addrlen;
438                         if (sendmsg(hapd->ctrl_sock, &msg, 0) < 0) {
439                                 fprintf(stderr, "CTRL_IFACE monitor[%d]: ",
440                                         idx);
441                                 perror("sendmsg");
442                                 dst->errors++;
443                                 if (dst->errors > 10) {
444                                         hostapd_ctrl_iface_detach(
445                                                 hapd, &dst->addr,
446                                                 dst->addrlen);
447                                 }
448                         } else
449                                 dst->errors = 0;
450                 }
451                 idx++;
452                 dst = next;
453         }
454 }