radius_client.c

Go to the documentation of this file.
00001 
00016 #include "includes.h"
00017 
00018 #include "common.h"
00019 #include "radius.h"
00020 #include "radius_client.h"
00021 #include "eloop.h"
00022 
00023 /* Defaults for RADIUS retransmit values (exponential backoff) */
00024 #define RADIUS_CLIENT_FIRST_WAIT 3 /* seconds */
00025 #define RADIUS_CLIENT_MAX_WAIT 120 /* seconds */
00026 #define RADIUS_CLIENT_MAX_RETRIES 10 /* maximum number of retransmit attempts
00027                                       * before entry is removed from retransmit
00028                                       * list */
00029 #define RADIUS_CLIENT_MAX_ENTRIES 30 /* maximum number of entries in retransmit
00030                                       * list (oldest will be removed, if this
00031                                       * limit is exceeded) */
00032 #define RADIUS_CLIENT_NUM_FAILOVER 4 /* try to change RADIUS server after this
00033                                       * many failed retry attempts */
00034 
00035 
00036 struct radius_rx_handler {
00037         RadiusRxResult (*handler)(struct radius_msg *msg,
00038                                   struct radius_msg *req,
00039                                   const u8 *shared_secret,
00040                                   size_t shared_secret_len,
00041                                   void *data);
00042         void *data;
00043 };
00044 
00045 
00046 /* RADIUS message retransmit list */
00047 struct radius_msg_list {
00048         u8 addr[ETH_ALEN]; /* STA/client address; used to find RADIUS messages
00049                             * for the same STA. */
00050         struct radius_msg *msg;
00051         RadiusType msg_type;
00052         os_time_t first_try;
00053         os_time_t next_try;
00054         int attempts;
00055         int next_wait;
00056         struct os_time last_attempt;
00057 
00058         u8 *shared_secret;
00059         size_t shared_secret_len;
00060 
00061         /* TODO: server config with failover to backup server(s) */
00062 
00063         struct radius_msg_list *next;
00064 };
00065 
00066 
00067 struct radius_client_data {
00068         void *ctx;
00069         struct hostapd_radius_servers *conf;
00070 
00071         int auth_serv_sock; /* socket for authentication RADIUS messages */
00072         int acct_serv_sock; /* socket for accounting RADIUS messages */
00073         int auth_serv_sock6;
00074         int acct_serv_sock6;
00075         int auth_sock; /* currently used socket */
00076         int acct_sock; /* currently used socket */
00077 
00078         struct radius_rx_handler *auth_handlers;
00079         size_t num_auth_handlers;
00080         struct radius_rx_handler *acct_handlers;
00081         size_t num_acct_handlers;
00082 
00083         struct radius_msg_list *msgs;
00084         size_t num_msgs;
00085 
00086         u8 next_radius_identifier;
00087 };
00088 
00089 
00090 static int
00091 radius_change_server(struct radius_client_data *radius,
00092                      struct hostapd_radius_server *nserv,
00093                      struct hostapd_radius_server *oserv,
00094                      int sock, int sock6, int auth);
00095 static int radius_client_init_acct(struct radius_client_data *radius);
00096 static int radius_client_init_auth(struct radius_client_data *radius);
00097 
00098 
00099 static void radius_client_msg_free(struct radius_msg_list *req)
00100 {
00101         radius_msg_free(req->msg);
00102         os_free(req->msg);
00103         os_free(req);
00104 }
00105 
00106 
00107 int radius_client_register(struct radius_client_data *radius,
00108                            RadiusType msg_type,
00109                            RadiusRxResult (*handler)(struct radius_msg *msg,
00110                                                      struct radius_msg *req,
00111                                                      const u8 *shared_secret,
00112                                                      size_t shared_secret_len,
00113                                                      void *data),
00114                            void *data)
00115 {
00116         struct radius_rx_handler **handlers, *newh;
00117         size_t *num;
00118 
00119         if (msg_type == RADIUS_ACCT) {
00120                 handlers = &radius->acct_handlers;
00121                 num = &radius->num_acct_handlers;
00122         } else {
00123                 handlers = &radius->auth_handlers;
00124                 num = &radius->num_auth_handlers;
00125         }
00126 
00127         newh = os_realloc(*handlers,
00128                           (*num + 1) * sizeof(struct radius_rx_handler));
00129         if (newh == NULL)
00130                 return -1;
00131 
00132         newh[*num].handler = handler;
00133         newh[*num].data = data;
00134         (*num)++;
00135         *handlers = newh;
00136 
00137         return 0;
00138 }
00139 
00140 
00141 static void radius_client_handle_send_error(struct radius_client_data *radius,
00142                                             int s, RadiusType msg_type)
00143 {
00144 #ifndef CONFIG_NATIVE_WINDOWS
00145         int _errno = errno;
00146         perror("send[RADIUS]");
00147         if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL ||
00148             _errno == EBADF) {
00149                 hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
00150                                HOSTAPD_LEVEL_INFO,
00151                                "Send failed - maybe interface status changed -"
00152                                " try to connect again");
00153                 eloop_unregister_read_sock(s);
00154                 close(s);
00155                 if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM)
00156                         radius_client_init_acct(radius);
00157                 else
00158                         radius_client_init_auth(radius);
00159         }
00160 #endif /* CONFIG_NATIVE_WINDOWS */
00161 }
00162 
00163 
00164 static int radius_client_retransmit(struct radius_client_data *radius,
00165                                     struct radius_msg_list *entry,
00166                                     os_time_t now)
00167 {
00168         struct hostapd_radius_servers *conf = radius->conf;
00169         int s;
00170 
00171         if (entry->msg_type == RADIUS_ACCT ||
00172             entry->msg_type == RADIUS_ACCT_INTERIM) {
00173                 s = radius->acct_sock;
00174                 if (entry->attempts == 0)
00175                         conf->acct_server->requests++;
00176                 else {
00177                         conf->acct_server->timeouts++;
00178                         conf->acct_server->retransmissions++;
00179                 }
00180         } else {
00181                 s = radius->auth_sock;
00182                 if (entry->attempts == 0)
00183                         conf->auth_server->requests++;
00184                 else {
00185                         conf->auth_server->timeouts++;
00186                         conf->auth_server->retransmissions++;
00187                 }
00188         }
00189 
00190         /* retransmit; remove entry if too many attempts */
00191         entry->attempts++;
00192         hostapd_logger(radius->ctx, entry->addr, HOSTAPD_MODULE_RADIUS,
00193                        HOSTAPD_LEVEL_DEBUG, "Resending RADIUS message (id=%d)",
00194                        entry->msg->hdr->identifier);
00195 
00196         os_get_time(&entry->last_attempt);
00197         if (send(s, entry->msg->buf, entry->msg->buf_used, 0) < 0)
00198                 radius_client_handle_send_error(radius, s, entry->msg_type);
00199 
00200         entry->next_try = now + entry->next_wait;
00201         entry->next_wait *= 2;
00202         if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT)
00203                 entry->next_wait = RADIUS_CLIENT_MAX_WAIT;
00204         if (entry->attempts >= RADIUS_CLIENT_MAX_RETRIES) {
00205                 printf("Removing un-ACKed RADIUS message due to too many "
00206                        "failed retransmit attempts\n");
00207                 return 1;
00208         }
00209 
00210         return 0;
00211 }
00212 
00213 
00214 static void radius_client_timer(void *eloop_ctx, void *timeout_ctx)
00215 {
00216         struct radius_client_data *radius = eloop_ctx;
00217         struct hostapd_radius_servers *conf = radius->conf;
00218         struct os_time now;
00219         os_time_t first;
00220         struct radius_msg_list *entry, *prev, *tmp;
00221         int auth_failover = 0, acct_failover = 0;
00222         char abuf[50];
00223 
00224         entry = radius->msgs;
00225         if (!entry)
00226                 return;
00227 
00228         os_get_time(&now);
00229         first = 0;
00230 
00231         prev = NULL;
00232         while (entry) {
00233                 if (now.sec >= entry->next_try &&
00234                     radius_client_retransmit(radius, entry, now.sec)) {
00235                         if (prev)
00236                                 prev->next = entry->next;
00237                         else
00238                                 radius->msgs = entry->next;
00239 
00240                         tmp = entry;
00241                         entry = entry->next;
00242                         radius_client_msg_free(tmp);
00243                         radius->num_msgs--;
00244                         continue;
00245                 }
00246 
00247                 if (entry->attempts > RADIUS_CLIENT_NUM_FAILOVER) {
00248                         if (entry->msg_type == RADIUS_ACCT ||
00249                             entry->msg_type == RADIUS_ACCT_INTERIM)
00250                                 acct_failover++;
00251                         else
00252                                 auth_failover++;
00253                 }
00254 
00255                 if (first == 0 || entry->next_try < first)
00256                         first = entry->next_try;
00257 
00258                 prev = entry;
00259                 entry = entry->next;
00260         }
00261 
00262         if (radius->msgs) {
00263                 if (first < now.sec)
00264                         first = now.sec;
00265                 eloop_register_timeout(first - now.sec, 0,
00266                                        radius_client_timer, radius, NULL);
00267                 hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
00268                                HOSTAPD_LEVEL_DEBUG, "Next RADIUS client "
00269                                "retransmit in %ld seconds",
00270                                (long int) (first - now.sec));
00271         }
00272 
00273         if (auth_failover && conf->num_auth_servers > 1) {
00274                 struct hostapd_radius_server *next, *old;
00275                 old = conf->auth_server;
00276                 hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
00277                                HOSTAPD_LEVEL_NOTICE,
00278                                "No response from Authentication server "
00279                                "%s:%d - failover",
00280                                hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)),
00281                                old->port);
00282 
00283                 for (entry = radius->msgs; entry; entry = entry->next) {
00284                         if (entry->msg_type == RADIUS_AUTH)
00285                                 old->timeouts++;
00286                 }
00287 
00288                 next = old + 1;
00289                 if (next > &(conf->auth_servers[conf->num_auth_servers - 1]))
00290                         next = conf->auth_servers;
00291                 conf->auth_server = next;
00292                 radius_change_server(radius, next, old,
00293                                      radius->auth_serv_sock,
00294                                      radius->auth_serv_sock6, 1);
00295         }
00296 
00297         if (acct_failover && conf->num_acct_servers > 1) {
00298                 struct hostapd_radius_server *next, *old;
00299                 old = conf->acct_server;
00300                 hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
00301                                HOSTAPD_LEVEL_NOTICE,
00302                                "No response from Accounting server "
00303                                "%s:%d - failover",
00304                                hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)),
00305                                old->port);
00306 
00307                 for (entry = radius->msgs; entry; entry = entry->next) {
00308                         if (entry->msg_type == RADIUS_ACCT ||
00309                             entry->msg_type == RADIUS_ACCT_INTERIM)
00310                                 old->timeouts++;
00311                 }
00312 
00313                 next = old + 1;
00314                 if (next > &conf->acct_servers[conf->num_acct_servers - 1])
00315                         next = conf->acct_servers;
00316                 conf->acct_server = next;
00317                 radius_change_server(radius, next, old,
00318                                      radius->acct_serv_sock,
00319                                      radius->acct_serv_sock6, 0);
00320         }
00321 }
00322 
00323 
00324 static void radius_client_update_timeout(struct radius_client_data *radius)
00325 {
00326         struct os_time now;
00327         os_time_t first;
00328         struct radius_msg_list *entry;
00329 
00330         eloop_cancel_timeout(radius_client_timer, radius, NULL);
00331 
00332         if (radius->msgs == NULL) {
00333                 return;
00334         }
00335 
00336         first = 0;
00337         for (entry = radius->msgs; entry; entry = entry->next) {
00338                 if (first == 0 || entry->next_try < first)
00339                         first = entry->next_try;
00340         }
00341 
00342         os_get_time(&now);
00343         if (first < now.sec)
00344                 first = now.sec;
00345         eloop_register_timeout(first - now.sec, 0, radius_client_timer, radius,
00346                                NULL);
00347         hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
00348                        HOSTAPD_LEVEL_DEBUG, "Next RADIUS client retransmit in"
00349                        " %ld seconds\n", (long int) (first - now.sec));
00350 }
00351 
00352 
00353 static void radius_client_list_add(struct radius_client_data *radius,
00354                                    struct radius_msg *msg,
00355                                    RadiusType msg_type, u8 *shared_secret,
00356                                    size_t shared_secret_len, const u8 *addr)
00357 {
00358         struct radius_msg_list *entry, *prev;
00359 
00360         if (eloop_terminated()) {
00361                 /* No point in adding entries to retransmit queue since event
00362                  * loop has already been terminated. */
00363                 radius_msg_free(msg);
00364                 os_free(msg);
00365                 return;
00366         }
00367 
00368         entry = os_zalloc(sizeof(*entry));
00369         if (entry == NULL) {
00370                 printf("Failed to add RADIUS packet into retransmit list\n");
00371                 radius_msg_free(msg);
00372                 os_free(msg);
00373                 return;
00374         }
00375 
00376         if (addr)
00377                 os_memcpy(entry->addr, addr, ETH_ALEN);
00378         entry->msg = msg;
00379         entry->msg_type = msg_type;
00380         entry->shared_secret = shared_secret;
00381         entry->shared_secret_len = shared_secret_len;
00382         os_get_time(&entry->last_attempt);
00383         entry->first_try = entry->last_attempt.sec;
00384         entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT;
00385         entry->attempts = 1;
00386         entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2;
00387         entry->next = radius->msgs;
00388         radius->msgs = entry;
00389         radius_client_update_timeout(radius);
00390 
00391         if (radius->num_msgs >= RADIUS_CLIENT_MAX_ENTRIES) {
00392                 printf("Removing the oldest un-ACKed RADIUS packet due to "
00393                        "retransmit list limits.\n");
00394                 prev = NULL;
00395                 while (entry->next) {
00396                         prev = entry;
00397                         entry = entry->next;
00398                 }
00399                 if (prev) {
00400                         prev->next = NULL;
00401                         radius_client_msg_free(entry);
00402                 }
00403         } else
00404                 radius->num_msgs++;
00405 }
00406 
00407 
00408 static void radius_client_list_del(struct radius_client_data *radius,
00409                                    RadiusType msg_type, const u8 *addr)
00410 {
00411         struct radius_msg_list *entry, *prev, *tmp;
00412 
00413         if (addr == NULL)
00414                 return;
00415 
00416         entry = radius->msgs;
00417         prev = NULL;
00418         while (entry) {
00419                 if (entry->msg_type == msg_type &&
00420                     os_memcmp(entry->addr, addr, ETH_ALEN) == 0) {
00421                         if (prev)
00422                                 prev->next = entry->next;
00423                         else
00424                                 radius->msgs = entry->next;
00425                         tmp = entry;
00426                         entry = entry->next;
00427                         hostapd_logger(radius->ctx, addr,
00428                                        HOSTAPD_MODULE_RADIUS,
00429                                        HOSTAPD_LEVEL_DEBUG,
00430                                        "Removing matching RADIUS message");
00431                         radius_client_msg_free(tmp);
00432                         radius->num_msgs--;
00433                         continue;
00434                 }
00435                 prev = entry;
00436                 entry = entry->next;
00437         }
00438 }
00439 
00440 
00441 int radius_client_send(struct radius_client_data *radius,
00442                        struct radius_msg *msg, RadiusType msg_type,
00443                        const u8 *addr)
00444 {
00445         struct hostapd_radius_servers *conf = radius->conf;
00446         u8 *shared_secret;
00447         size_t shared_secret_len;
00448         char *name;
00449         int s, res;
00450 
00451         if (msg_type == RADIUS_ACCT_INTERIM) {
00452                 /* Remove any pending interim acct update for the same STA. */
00453                 radius_client_list_del(radius, msg_type, addr);
00454         }
00455 
00456         if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) {
00457                 if (conf->acct_server == NULL) {
00458                         hostapd_logger(radius->ctx, NULL,
00459                                        HOSTAPD_MODULE_RADIUS,
00460                                        HOSTAPD_LEVEL_INFO,
00461                                        "No accounting server configured");
00462                         return -1;
00463                 }
00464                 shared_secret = conf->acct_server->shared_secret;
00465                 shared_secret_len = conf->acct_server->shared_secret_len;
00466                 radius_msg_finish_acct(msg, shared_secret, shared_secret_len);
00467                 name = "accounting";
00468                 s = radius->acct_sock;
00469                 conf->acct_server->requests++;
00470         } else {
00471                 if (conf->auth_server == NULL) {
00472                         hostapd_logger(radius->ctx, NULL,
00473                                        HOSTAPD_MODULE_RADIUS,
00474                                        HOSTAPD_LEVEL_INFO,
00475                                        "No authentication server configured");
00476                         return -1;
00477                 }
00478                 shared_secret = conf->auth_server->shared_secret;
00479                 shared_secret_len = conf->auth_server->shared_secret_len;
00480                 radius_msg_finish(msg, shared_secret, shared_secret_len);
00481                 name = "authentication";
00482                 s = radius->auth_sock;
00483                 conf->auth_server->requests++;
00484         }
00485 
00486         hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
00487                        HOSTAPD_LEVEL_DEBUG, "Sending RADIUS message to %s "
00488                        "server", name);
00489         if (conf->msg_dumps)
00490                 radius_msg_dump(msg);
00491 
00492         res = send(s, msg->buf, msg->buf_used, 0);
00493         if (res < 0)
00494                 radius_client_handle_send_error(radius, s, msg_type);
00495 
00496         radius_client_list_add(radius, msg, msg_type, shared_secret,
00497                                shared_secret_len, addr);
00498 
00499         return res;
00500 }
00501 
00502 
00503 static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx)
00504 {
00505         struct radius_client_data *radius = eloop_ctx;
00506         struct hostapd_radius_servers *conf = radius->conf;
00507         RadiusType msg_type = (RadiusType) sock_ctx;
00508         int len, roundtrip;
00509         unsigned char buf[3000];
00510         struct radius_msg *msg;
00511         struct radius_rx_handler *handlers;
00512         size_t num_handlers, i;
00513         struct radius_msg_list *req, *prev_req;
00514         struct os_time now;
00515         struct hostapd_radius_server *rconf;
00516         int invalid_authenticator = 0;
00517 
00518         if (msg_type == RADIUS_ACCT) {
00519                 handlers = radius->acct_handlers;
00520                 num_handlers = radius->num_acct_handlers;
00521                 rconf = conf->acct_server;
00522         } else {
00523                 handlers = radius->auth_handlers;
00524                 num_handlers = radius->num_auth_handlers;
00525                 rconf = conf->auth_server;
00526         }
00527 
00528         len = recv(sock, buf, sizeof(buf), MSG_DONTWAIT);
00529         if (len < 0) {
00530                 perror("recv[RADIUS]");
00531                 return;
00532         }
00533         hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
00534                        HOSTAPD_LEVEL_DEBUG, "Received %d bytes from RADIUS "
00535                        "server", len);
00536         if (len == sizeof(buf)) {
00537                 printf("Possibly too long UDP frame for our buffer - "
00538                        "dropping it\n");
00539                 return;
00540         }
00541 
00542         msg = radius_msg_parse(buf, len);
00543         if (msg == NULL) {
00544                 printf("Parsing incoming RADIUS frame failed\n");
00545                 rconf->malformed_responses++;
00546                 return;
00547         }
00548 
00549         hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
00550                        HOSTAPD_LEVEL_DEBUG, "Received RADIUS message");
00551         if (conf->msg_dumps)
00552                 radius_msg_dump(msg);
00553 
00554         switch (msg->hdr->code) {
00555         case RADIUS_CODE_ACCESS_ACCEPT:
00556                 rconf->access_accepts++;
00557                 break;
00558         case RADIUS_CODE_ACCESS_REJECT:
00559                 rconf->access_rejects++;
00560                 break;
00561         case RADIUS_CODE_ACCESS_CHALLENGE:
00562                 rconf->access_challenges++;
00563                 break;
00564         case RADIUS_CODE_ACCOUNTING_RESPONSE:
00565                 rconf->responses++;
00566                 break;
00567         }
00568 
00569         prev_req = NULL;
00570         req = radius->msgs;
00571         while (req) {
00572                 /* TODO: also match by src addr:port of the packet when using
00573                  * alternative RADIUS servers (?) */
00574                 if ((req->msg_type == msg_type ||
00575                      (req->msg_type == RADIUS_ACCT_INTERIM &&
00576                       msg_type == RADIUS_ACCT)) &&
00577                     req->msg->hdr->identifier == msg->hdr->identifier)
00578                         break;
00579 
00580                 prev_req = req;
00581                 req = req->next;
00582         }
00583 
00584         if (req == NULL) {
00585                 hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
00586                                HOSTAPD_LEVEL_DEBUG,
00587                                "No matching RADIUS request found (type=%d "
00588                                "id=%d) - dropping packet",
00589                                msg_type, msg->hdr->identifier);
00590                 goto fail;
00591         }
00592 
00593         os_get_time(&now);
00594         roundtrip = (now.sec - req->last_attempt.sec) * 100 +
00595                 (now.usec - req->last_attempt.usec) / 10000;
00596         hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS,
00597                        HOSTAPD_LEVEL_DEBUG,
00598                        "Received RADIUS packet matched with a pending "
00599                        "request, round trip time %d.%02d sec",
00600                        roundtrip / 100, roundtrip % 100);
00601         rconf->round_trip_time = roundtrip;
00602 
00603         /* Remove ACKed RADIUS packet from retransmit list */
00604         if (prev_req)
00605                 prev_req->next = req->next;
00606         else
00607                 radius->msgs = req->next;
00608         radius->num_msgs--;
00609 
00610         for (i = 0; i < num_handlers; i++) {
00611                 RadiusRxResult res;
00612                 res = handlers[i].handler(msg, req->msg, req->shared_secret,
00613                                           req->shared_secret_len,
00614                                           handlers[i].data);
00615                 switch (res) {
00616                 case RADIUS_RX_PROCESSED:
00617                         radius_msg_free(msg);
00618                         os_free(msg);
00619                         /* continue */
00620                 case RADIUS_RX_QUEUED:
00621                         radius_client_msg_free(req);
00622                         return;
00623                 case RADIUS_RX_INVALID_AUTHENTICATOR:
00624                         invalid_authenticator++;
00625                         /* continue */
00626                 case RADIUS_RX_UNKNOWN:
00627                         /* continue with next handler */
00628                         break;
00629                 }
00630         }
00631 
00632         if (invalid_authenticator)
00633                 rconf->bad_authenticators++;
00634         else
00635                 rconf->unknown_types++;
00636         hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS,
00637                        HOSTAPD_LEVEL_DEBUG, "No RADIUS RX handler found "
00638                        "(type=%d code=%d id=%d)%s - dropping packet",
00639                        msg_type, msg->hdr->code, msg->hdr->identifier,
00640                        invalid_authenticator ? " [INVALID AUTHENTICATOR]" :
00641                        "");
00642         radius_client_msg_free(req);
00643 
00644  fail:
00645         radius_msg_free(msg);
00646         os_free(msg);
00647 }
00648 
00649 
00650 u8 radius_client_get_id(struct radius_client_data *radius)
00651 {
00652         struct radius_msg_list *entry, *prev, *_remove;
00653         u8 id = radius->next_radius_identifier++;
00654 
00655         /* remove entries with matching id from retransmit list to avoid
00656          * using new reply from the RADIUS server with an old request */
00657         entry = radius->msgs;
00658         prev = NULL;
00659         while (entry) {
00660                 if (entry->msg->hdr->identifier == id) {
00661                         hostapd_logger(radius->ctx, entry->addr,
00662                                        HOSTAPD_MODULE_RADIUS,
00663                                        HOSTAPD_LEVEL_DEBUG,
00664                                        "Removing pending RADIUS message, "
00665                                        "since its id (%d) is reused", id);
00666                         if (prev)
00667                                 prev->next = entry->next;
00668                         else
00669                                 radius->msgs = entry->next;
00670                         _remove = entry;
00671                 } else {
00672                         _remove = NULL;
00673                         prev = entry;
00674                 }
00675                 entry = entry->next;
00676 
00677                 if (_remove)
00678                         radius_client_msg_free(_remove);
00679         }
00680 
00681         return id;
00682 }
00683 
00684 
00685 void radius_client_flush(struct radius_client_data *radius, int only_auth)
00686 {
00687         struct radius_msg_list *entry, *prev, *tmp;
00688 
00689         if (!radius)
00690                 return;
00691 
00692         prev = NULL;
00693         entry = radius->msgs;
00694 
00695         while (entry) {
00696                 if (!only_auth || entry->msg_type == RADIUS_AUTH) {
00697                         if (prev)
00698                                 prev->next = entry->next;
00699                         else
00700                                 radius->msgs = entry->next;
00701 
00702                         tmp = entry;
00703                         entry = entry->next;
00704                         radius_client_msg_free(tmp);
00705                         radius->num_msgs--;
00706                 } else {
00707                         prev = entry;
00708                         entry = entry->next;
00709                 }
00710         }
00711 
00712         if (radius->msgs == NULL)
00713                 eloop_cancel_timeout(radius_client_timer, radius, NULL);
00714 }
00715 
00716 
00717 static void radius_client_update_acct_msgs(struct radius_client_data *radius,
00718                                            u8 *shared_secret,
00719                                            size_t shared_secret_len)
00720 {
00721         struct radius_msg_list *entry;
00722 
00723         if (!radius)
00724                 return;
00725 
00726         for (entry = radius->msgs; entry; entry = entry->next) {
00727                 if (entry->msg_type == RADIUS_ACCT) {
00728                         entry->shared_secret = shared_secret;
00729                         entry->shared_secret_len = shared_secret_len;
00730                         radius_msg_finish_acct(entry->msg, shared_secret,
00731                                                shared_secret_len);
00732                 }
00733         }
00734 }
00735 
00736 
00737 static int
00738 radius_change_server(struct radius_client_data *radius,
00739                      struct hostapd_radius_server *nserv,
00740                      struct hostapd_radius_server *oserv,
00741                      int sock, int sock6, int auth)
00742 {
00743         struct sockaddr_in serv, claddr;
00744 #ifdef CONFIG_IPV6
00745         struct sockaddr_in6 serv6, claddr6;
00746 #endif /* CONFIG_IPV6 */
00747         struct sockaddr *addr, *cl_addr;
00748         socklen_t addrlen, claddrlen;
00749         char abuf[50];
00750         int sel_sock;
00751         struct radius_msg_list *entry;
00752         struct hostapd_radius_servers *conf = radius->conf;
00753 
00754         hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
00755                        HOSTAPD_LEVEL_INFO,
00756                        "%s server %s:%d",
00757                        auth ? "Authentication" : "Accounting",
00758                        hostapd_ip_txt(&nserv->addr, abuf, sizeof(abuf)),
00759                        nserv->port);
00760 
00761         if (!oserv || nserv->shared_secret_len != oserv->shared_secret_len ||
00762             os_memcmp(nserv->shared_secret, oserv->shared_secret,
00763                       nserv->shared_secret_len) != 0) {
00764                 /* Pending RADIUS packets used different shared secret, so
00765                  * they need to be modified. Update accounting message
00766                  * authenticators here. Authentication messages are removed
00767                  * since they would require more changes and the new RADIUS
00768                  * server may not be prepared to receive them anyway due to
00769                  * missing state information. Client will likely retry
00770                  * authentication, so this should not be an issue. */
00771                 if (auth)
00772                         radius_client_flush(radius, 1);
00773                 else {
00774                         radius_client_update_acct_msgs(
00775                                 radius, nserv->shared_secret,
00776                                 nserv->shared_secret_len);
00777                 }
00778         }
00779 
00780         /* Reset retry counters for the new server */
00781         for (entry = radius->msgs; entry; entry = entry->next) {
00782                 if ((auth && entry->msg_type != RADIUS_AUTH) ||
00783                     (!auth && entry->msg_type != RADIUS_ACCT))
00784                         continue;
00785                 entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT;
00786                 entry->attempts = 0;
00787                 entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2;
00788         }
00789 
00790         if (radius->msgs) {
00791                 eloop_cancel_timeout(radius_client_timer, radius, NULL);
00792                 eloop_register_timeout(RADIUS_CLIENT_FIRST_WAIT, 0,
00793                                        radius_client_timer, radius, NULL);
00794         }
00795 
00796         switch (nserv->addr.af) {
00797         case AF_INET:
00798                 os_memset(&serv, 0, sizeof(serv));
00799                 serv.sin_family = AF_INET;
00800                 serv.sin_addr.s_addr = nserv->addr.u.v4.s_addr;
00801                 serv.sin_port = htons(nserv->port);
00802                 addr = (struct sockaddr *) &serv;
00803                 addrlen = sizeof(serv);
00804                 sel_sock = sock;
00805                 break;
00806 #ifdef CONFIG_IPV6
00807         case AF_INET6:
00808                 os_memset(&serv6, 0, sizeof(serv6));
00809                 serv6.sin6_family = AF_INET6;
00810                 os_memcpy(&serv6.sin6_addr, &nserv->addr.u.v6,
00811                           sizeof(struct in6_addr));
00812                 serv6.sin6_port = htons(nserv->port);
00813                 addr = (struct sockaddr *) &serv6;
00814                 addrlen = sizeof(serv6);
00815                 sel_sock = sock6;
00816                 break;
00817 #endif /* CONFIG_IPV6 */
00818         default:
00819                 return -1;
00820         }
00821 
00822         if (conf->force_client_addr) {
00823                 switch (conf->client_addr.af) {
00824                 case AF_INET:
00825                         os_memset(&claddr, 0, sizeof(claddr));
00826                         claddr.sin_family = AF_INET;
00827                         claddr.sin_addr.s_addr = conf->client_addr.u.v4.s_addr;
00828                         claddr.sin_port = htons(0);
00829                         cl_addr = (struct sockaddr *) &claddr;
00830                         claddrlen = sizeof(claddr);
00831                         break;
00832 #ifdef CONFIG_IPV6
00833                 case AF_INET6:
00834                         os_memset(&claddr6, 0, sizeof(claddr6));
00835                         claddr6.sin6_family = AF_INET6;
00836                         os_memcpy(&claddr6.sin6_addr, &conf->client_addr.u.v6,
00837                                   sizeof(struct in6_addr));
00838                         claddr6.sin6_port = htons(0);
00839                         cl_addr = (struct sockaddr *) &claddr6;
00840                         claddrlen = sizeof(claddr6);
00841                         break;
00842 #endif /* CONFIG_IPV6 */
00843                 default:
00844                         return -1;
00845                 }
00846 
00847                 if (bind(sel_sock, cl_addr, claddrlen) < 0) {
00848                         perror("bind[radius]");
00849                         return -1;
00850                 }
00851         }
00852 
00853         if (connect(sel_sock, addr, addrlen) < 0) {
00854                 perror("connect[radius]");
00855                 return -1;
00856         }
00857 
00858 #ifndef CONFIG_NATIVE_WINDOWS
00859         switch (nserv->addr.af) {
00860         case AF_INET:
00861                 claddrlen = sizeof(claddr);
00862                 getsockname(sel_sock, (struct sockaddr *) &claddr, &claddrlen);
00863                 wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u",
00864                            inet_ntoa(claddr.sin_addr), ntohs(claddr.sin_port));
00865                 break;
00866 #ifdef CONFIG_IPV6
00867         case AF_INET6: {
00868                 claddrlen = sizeof(claddr6);
00869                 getsockname(sel_sock, (struct sockaddr *) &claddr6,
00870                             &claddrlen);
00871                 wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u",
00872                            inet_ntop(AF_INET6, &claddr6.sin6_addr,
00873                                      abuf, sizeof(abuf)),
00874                            ntohs(claddr6.sin6_port));
00875                 break;
00876         }
00877 #endif /* CONFIG_IPV6 */
00878         }
00879 #endif /* CONFIG_NATIVE_WINDOWS */
00880 
00881         if (auth)
00882                 radius->auth_sock = sel_sock;
00883         else
00884                 radius->acct_sock = sel_sock;
00885 
00886         return 0;
00887 }
00888 
00889 
00890 static void radius_retry_primary_timer(void *eloop_ctx, void *timeout_ctx)
00891 {
00892         struct radius_client_data *radius = eloop_ctx;
00893         struct hostapd_radius_servers *conf = radius->conf;
00894         struct hostapd_radius_server *oserv;
00895 
00896         if (radius->auth_sock >= 0 && conf->auth_servers &&
00897             conf->auth_server != conf->auth_servers) {
00898                 oserv = conf->auth_server;
00899                 conf->auth_server = conf->auth_servers;
00900                 radius_change_server(radius, conf->auth_server, oserv,
00901                                      radius->auth_serv_sock,
00902                                      radius->auth_serv_sock6, 1);
00903         }
00904 
00905         if (radius->acct_sock >= 0 && conf->acct_servers &&
00906             conf->acct_server != conf->acct_servers) {
00907                 oserv = conf->acct_server;
00908                 conf->acct_server = conf->acct_servers;
00909                 radius_change_server(radius, conf->acct_server, oserv,
00910                                      radius->acct_serv_sock,
00911                                      radius->acct_serv_sock6, 0);
00912         }
00913 
00914         if (conf->retry_primary_interval)
00915                 eloop_register_timeout(conf->retry_primary_interval, 0,
00916                                        radius_retry_primary_timer, radius,
00917                                        NULL);
00918 }
00919 
00920 
00921 static int radius_client_disable_pmtu_discovery(int s)
00922 {
00923         int r = -1;
00924 #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
00925         /* Turn off Path MTU discovery on IPv4/UDP sockets. */
00926         int action = IP_PMTUDISC_DONT;
00927         r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action,
00928                        sizeof(action));
00929         if (r == -1)
00930                 wpa_printf(MSG_ERROR, "Failed to set IP_MTU_DISCOVER: "
00931                            "%s", strerror(errno));
00932 #endif
00933         return r;
00934 }
00935 
00936 
00937 static int radius_client_init_auth(struct radius_client_data *radius)
00938 {
00939         struct hostapd_radius_servers *conf = radius->conf;
00940         int ok = 0;
00941 
00942         radius->auth_serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
00943         if (radius->auth_serv_sock < 0)
00944                 perror("socket[PF_INET,SOCK_DGRAM]");
00945         else {
00946                 radius_client_disable_pmtu_discovery(radius->auth_serv_sock);
00947                 ok++;
00948         }
00949 
00950 #ifdef CONFIG_IPV6
00951         radius->auth_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0);
00952         if (radius->auth_serv_sock6 < 0)
00953                 perror("socket[PF_INET6,SOCK_DGRAM]");
00954         else
00955                 ok++;
00956 #endif /* CONFIG_IPV6 */
00957 
00958         if (ok == 0)
00959                 return -1;
00960 
00961         radius_change_server(radius, conf->auth_server, NULL,
00962                              radius->auth_serv_sock, radius->auth_serv_sock6,
00963                              1);
00964 
00965         if (radius->auth_serv_sock >= 0 &&
00966             eloop_register_read_sock(radius->auth_serv_sock,
00967                                      radius_client_receive, radius,
00968                                      (void *) RADIUS_AUTH)) {
00969                 printf("Could not register read socket for authentication "
00970                        "server\n");
00971                 return -1;
00972         }
00973 
00974 #ifdef CONFIG_IPV6
00975         if (radius->auth_serv_sock6 >= 0 &&
00976             eloop_register_read_sock(radius->auth_serv_sock6,
00977                                      radius_client_receive, radius,
00978                                      (void *) RADIUS_AUTH)) {
00979                 printf("Could not register read socket for authentication "
00980                        "server\n");
00981                 return -1;
00982         }
00983 #endif /* CONFIG_IPV6 */
00984 
00985         return 0;
00986 }
00987 
00988 
00989 static int radius_client_init_acct(struct radius_client_data *radius)
00990 {
00991         struct hostapd_radius_servers *conf = radius->conf;
00992         int ok = 0;
00993 
00994         radius->acct_serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
00995         if (radius->acct_serv_sock < 0)
00996                 perror("socket[PF_INET,SOCK_DGRAM]");
00997         else {
00998                 radius_client_disable_pmtu_discovery(radius->acct_serv_sock);
00999                 ok++;
01000         }
01001 
01002 #ifdef CONFIG_IPV6
01003         radius->acct_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0);
01004         if (radius->acct_serv_sock6 < 0)
01005                 perror("socket[PF_INET6,SOCK_DGRAM]");
01006         else
01007                 ok++;
01008 #endif /* CONFIG_IPV6 */
01009 
01010         if (ok == 0)
01011                 return -1;
01012 
01013         radius_change_server(radius, conf->acct_server, NULL,
01014                              radius->acct_serv_sock, radius->acct_serv_sock6,
01015                              0);
01016 
01017         if (radius->acct_serv_sock >= 0 &&
01018             eloop_register_read_sock(radius->acct_serv_sock,
01019                                      radius_client_receive, radius,
01020                                      (void *) RADIUS_ACCT)) {
01021                 printf("Could not register read socket for accounting "
01022                        "server\n");
01023                 return -1;
01024         }
01025 
01026 #ifdef CONFIG_IPV6
01027         if (radius->acct_serv_sock6 >= 0 &&
01028             eloop_register_read_sock(radius->acct_serv_sock6,
01029                                      radius_client_receive, radius,
01030                                      (void *) RADIUS_ACCT)) {
01031                 printf("Could not register read socket for accounting "
01032                        "server\n");
01033                 return -1;
01034         }
01035 #endif /* CONFIG_IPV6 */
01036 
01037         return 0;
01038 }
01039 
01040 
01041 struct radius_client_data *
01042 radius_client_init(void *ctx, struct hostapd_radius_servers *conf)
01043 {
01044         struct radius_client_data *radius;
01045 
01046         radius = os_zalloc(sizeof(struct radius_client_data));
01047         if (radius == NULL)
01048                 return NULL;
01049 
01050         radius->ctx = ctx;
01051         radius->conf = conf;
01052         radius->auth_serv_sock = radius->acct_serv_sock =
01053                 radius->auth_serv_sock6 = radius->acct_serv_sock6 =
01054                 radius->auth_sock = radius->acct_sock = -1;
01055 
01056         if (conf->auth_server && radius_client_init_auth(radius)) {
01057                 radius_client_deinit(radius);
01058                 return NULL;
01059         }
01060 
01061         if (conf->acct_server && radius_client_init_acct(radius)) {
01062                 radius_client_deinit(radius);
01063                 return NULL;
01064         }
01065 
01066         if (conf->retry_primary_interval)
01067                 eloop_register_timeout(conf->retry_primary_interval, 0,
01068                                        radius_retry_primary_timer, radius,
01069                                        NULL);
01070 
01071         return radius;
01072 }
01073 
01074 
01075 void radius_client_deinit(struct radius_client_data *radius)
01076 {
01077         if (!radius)
01078                 return;
01079 
01080         if (radius->auth_serv_sock >= 0)
01081                 eloop_unregister_read_sock(radius->auth_serv_sock);
01082         if (radius->acct_serv_sock >= 0)
01083                 eloop_unregister_read_sock(radius->acct_serv_sock);
01084 
01085         eloop_cancel_timeout(radius_retry_primary_timer, radius, NULL);
01086 
01087         radius_client_flush(radius, 0);
01088         os_free(radius->auth_handlers);
01089         os_free(radius->acct_handlers);
01090         os_free(radius);
01091 }
01092 
01093 
01094 void radius_client_flush_auth(struct radius_client_data *radius, u8 *addr)
01095 {
01096         struct radius_msg_list *entry, *prev, *tmp;
01097 
01098         prev = NULL;
01099         entry = radius->msgs;
01100         while (entry) {
01101                 if (entry->msg_type == RADIUS_AUTH &&
01102                     os_memcmp(entry->addr, addr, ETH_ALEN) == 0) {
01103                         hostapd_logger(radius->ctx, addr,
01104                                        HOSTAPD_MODULE_RADIUS,
01105                                        HOSTAPD_LEVEL_DEBUG,
01106                                        "Removing pending RADIUS authentication"
01107                                        " message for removed client");
01108 
01109                         if (prev)
01110                                 prev->next = entry->next;
01111                         else
01112                                 radius->msgs = entry->next;
01113 
01114                         tmp = entry;
01115                         entry = entry->next;
01116                         radius_client_msg_free(tmp);
01117                         radius->num_msgs--;
01118                         continue;
01119                 }
01120 
01121                 prev = entry;
01122                 entry = entry->next;
01123         }
01124 }
01125 
01126 
01127 static int radius_client_dump_auth_server(char *buf, size_t buflen,
01128                                           struct hostapd_radius_server *serv,
01129                                           struct radius_client_data *cli)
01130 {
01131         int pending = 0;
01132         struct radius_msg_list *msg;
01133         char abuf[50];
01134 
01135         if (cli) {
01136                 for (msg = cli->msgs; msg; msg = msg->next) {
01137                         if (msg->msg_type == RADIUS_AUTH)
01138                                 pending++;
01139                 }
01140         }
01141 
01142         return os_snprintf(buf, buflen,
01143                            "radiusAuthServerIndex=%d\n"
01144                            "radiusAuthServerAddress=%s\n"
01145                            "radiusAuthClientServerPortNumber=%d\n"
01146                            "radiusAuthClientRoundTripTime=%d\n"
01147                            "radiusAuthClientAccessRequests=%u\n"
01148                            "radiusAuthClientAccessRetransmissions=%u\n"
01149                            "radiusAuthClientAccessAccepts=%u\n"
01150                            "radiusAuthClientAccessRejects=%u\n"
01151                            "radiusAuthClientAccessChallenges=%u\n"
01152                            "radiusAuthClientMalformedAccessResponses=%u\n"
01153                            "radiusAuthClientBadAuthenticators=%u\n"
01154                            "radiusAuthClientPendingRequests=%u\n"
01155                            "radiusAuthClientTimeouts=%u\n"
01156                            "radiusAuthClientUnknownTypes=%u\n"
01157                            "radiusAuthClientPacketsDropped=%u\n",
01158                            serv->index,
01159                            hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)),
01160                            serv->port,
01161                            serv->round_trip_time,
01162                            serv->requests,
01163                            serv->retransmissions,
01164                            serv->access_accepts,
01165                            serv->access_rejects,
01166                            serv->access_challenges,
01167                            serv->malformed_responses,
01168                            serv->bad_authenticators,
01169                            pending,
01170                            serv->timeouts,
01171                            serv->unknown_types,
01172                            serv->packets_dropped);
01173 }
01174 
01175 
01176 static int radius_client_dump_acct_server(char *buf, size_t buflen,
01177                                           struct hostapd_radius_server *serv,
01178                                           struct radius_client_data *cli)
01179 {
01180         int pending = 0;
01181         struct radius_msg_list *msg;
01182         char abuf[50];
01183 
01184         if (cli) {
01185                 for (msg = cli->msgs; msg; msg = msg->next) {
01186                         if (msg->msg_type == RADIUS_ACCT ||
01187                             msg->msg_type == RADIUS_ACCT_INTERIM)
01188                                 pending++;
01189                 }
01190         }
01191 
01192         return os_snprintf(buf, buflen,
01193                            "radiusAccServerIndex=%d\n"
01194                            "radiusAccServerAddress=%s\n"
01195                            "radiusAccClientServerPortNumber=%d\n"
01196                            "radiusAccClientRoundTripTime=%d\n"
01197                            "radiusAccClientRequests=%u\n"
01198                            "radiusAccClientRetransmissions=%u\n"
01199                            "radiusAccClientResponses=%u\n"
01200                            "radiusAccClientMalformedResponses=%u\n"
01201                            "radiusAccClientBadAuthenticators=%u\n"
01202                            "radiusAccClientPendingRequests=%u\n"
01203                            "radiusAccClientTimeouts=%u\n"
01204                            "radiusAccClientUnknownTypes=%u\n"
01205                            "radiusAccClientPacketsDropped=%u\n",
01206                            serv->index,
01207                            hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)),
01208                            serv->port,
01209                            serv->round_trip_time,
01210                            serv->requests,
01211                            serv->retransmissions,
01212                            serv->responses,
01213                            serv->malformed_responses,
01214                            serv->bad_authenticators,
01215                            pending,
01216                            serv->timeouts,
01217                            serv->unknown_types,
01218                            serv->packets_dropped);
01219 }
01220 
01221 
01222 int radius_client_get_mib(struct radius_client_data *radius, char *buf,
01223                           size_t buflen)
01224 {
01225         struct hostapd_radius_servers *conf = radius->conf;
01226         int i;
01227         struct hostapd_radius_server *serv;
01228         int count = 0;
01229 
01230         if (conf->auth_servers) {
01231                 for (i = 0; i < conf->num_auth_servers; i++) {
01232                         serv = &conf->auth_servers[i];
01233                         count += radius_client_dump_auth_server(
01234                                 buf + count, buflen - count, serv,
01235                                 serv == conf->auth_server ?
01236                                 radius : NULL);
01237                 }
01238         }
01239 
01240         if (conf->acct_servers) {
01241                 for (i = 0; i < conf->num_acct_servers; i++) {
01242                         serv = &conf->acct_servers[i];
01243                         count += radius_client_dump_acct_server(
01244                                 buf + count, buflen - count, serv,
01245                                 serv == conf->acct_server ?
01246                                 radius : NULL);
01247                 }
01248         }
01249 
01250         return count;
01251 }
01252 
01253 
01254 static int radius_servers_diff(struct hostapd_radius_server *nserv,
01255                                struct hostapd_radius_server *oserv,
01256                                int num)
01257 {
01258         int i;
01259 
01260         for (i = 0; i < num; i++) {
01261                 if (hostapd_ip_diff(&nserv[i].addr, &oserv[i].addr) ||
01262                     nserv[i].port != oserv[i].port ||
01263                     nserv[i].shared_secret_len != oserv[i].shared_secret_len ||
01264                     os_memcmp(nserv[i].shared_secret, oserv[i].shared_secret,
01265                               nserv[i].shared_secret_len) != 0)
01266                         return 1;
01267         }
01268 
01269         return 0;
01270 }
01271 
01272 
01273 struct radius_client_data *
01274 radius_client_reconfig(struct radius_client_data *old, void *ctx,
01275                        struct hostapd_radius_servers *oldconf,
01276                        struct hostapd_radius_servers *newconf)
01277 {
01278         radius_client_flush(old, 0);
01279 
01280         if (newconf->retry_primary_interval !=
01281             oldconf->retry_primary_interval ||
01282             newconf->num_auth_servers != oldconf->num_auth_servers ||
01283             newconf->num_acct_servers != oldconf->num_acct_servers ||
01284             radius_servers_diff(newconf->auth_servers, oldconf->auth_servers,
01285                                 newconf->num_auth_servers) ||
01286             radius_servers_diff(newconf->acct_servers, oldconf->acct_servers,
01287                                 newconf->num_acct_servers)) {
01288                 hostapd_logger(ctx, NULL, HOSTAPD_MODULE_RADIUS,
01289                                HOSTAPD_LEVEL_DEBUG,
01290                                "Reconfiguring RADIUS client");
01291                 radius_client_deinit(old);
01292                 return radius_client_init(ctx, newconf);
01293         }
01294 
01295         return old;
01296 }
01297 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines

Generated on Sat Nov 21 23:16:52 2009 for hostapd by  doxygen 1.6.1