wifidog源码分析 - 初始化阶段

来源:互联网 发布:北京宝库在线网络 编辑:程序博客网 时间:2024/05/20 03:45

    Wifidog是一个linux下开源的认证网关软件,它主要用于配合认证服务器实现无线路由器的认证放行功能。

    wifidog是一个后台的服务程序,可以通过wdctrl命令对wifidog主程序进行控制。

本文解释wifidog在启动阶段所做的初始化主要工作(代码片段1.1

  • 初始化配置(先将配置结构体初始化为默认值,在读取配置文件修改配置结构体)
  • 初始化已连接客户端列表(如果是通过wdctrl重启wifidog,将会读取之前wifidog的已连接客户端列表 代码片段1.2 代码片段1.3
  • 如无特殊情况,分离进程,建立守护进程 (代码片段1.1
  • 添加多个http请求回调函数(包括404错误回调函数) (见之后章节)
  • 摧毁删除现有的iptables路由表规则 (见之后章节)
  • 建立新的iptables路由表规则 (见之后章节)
  • 启动多个功能线程 (见之后章节)
  • 循环等待客户端连接 (见之后章节)

 

代码片段1.1

复制代码
 1 int main(int argc, char **argv) { 2  3     s_config *config = config_get_config();  //就是返回全局变量config结构体的地址 4     config_init();    //初始化全局变量config结构体为默认值 5  6     parse_commandline(argc, argv);    //根据传入参数执行操作(如果参数有-x则会设置restart_orig_pid为已运行的wifidog的pid) 7  8     /* Initialize the config */ 9     config_read(config->configfile);    //根据配置文件设置全局变量config结构体10     config_validate();    //判断GatewayInterface和AuthServer是否为空,空则无效退出程序。11 12     /* Initializes the linked list of connected clients */13     client_list_init();    //将已连接客户端链表置空。14 15     /* Init the signals to catch chld/quit/etc */16     init_signals();    //初始化一些信号17 18     if (restart_orig_pid) {    //用于restart,如果有已运行的wifidog,先会kill它19         /*20          * We were restarted and our parent is waiting for us to talk to it over the socket21          */22         get_clients_from_parent();    //从已运行的wifidog中获取客户端列表,详见 代码片段1.223 24         /*25          * At this point the parent will start destroying itself and the firewall. Let it finish it's job before we continue26          */27 28         while (kill(restart_orig_pid, 0) != -1) {    //kill已运行的wifidog29             debug(LOG_INFO, "Waiting for parent PID %d to die before continuing loading", restart_orig_pid);30             sleep(1);31         }32 33         debug(LOG_INFO, "Parent PID %d seems to be dead. Continuing loading.");34     }35 36     if (config->daemon) {    //创建为守护进程,config->daemon默认值为-137 38         debug(LOG_INFO, "Forking into background");39 40         switch(safe_fork()) {41             case 0: /* child */42                 setsid();    //创建新会话,脱离此终端,实现守护进程43                 append_x_restartargv();44                 main_loop();    //进入主循环(核心代码在此)。45                 break;46 47             default: /* parent */48                 exit(0);49                 break;50         }51     }52     else {53         append_x_restartargv();54         main_loop();55     }56 57     return(0); /* never reached */58 }
复制代码

 

代码片段1.2(获取已启动的wifidog的客户端列表):

此段代表描述了新启动的wifidog如何从已启动的wifidog程序中获取已连接的客户端列表。发送端见 代码片段1.3

复制代码
  1 void get_clients_from_parent(void) {  2     int sock;  3     struct sockaddr_un    sa_un;  4     s_config * config = NULL;  5     char linebuffer[MAX_BUF];  6     int len = 0;  7     char *running1 = NULL;  8     char *running2 = NULL;  9     char *token1 = NULL; 10     char *token2 = NULL; 11     char onechar; 12     char *command = NULL; 13     char *key = NULL; 14     char *value = NULL; 15     t_client * client = NULL; 16     t_client * lastclient = NULL; 17  18     config = config_get_config(); 19      20     debug(LOG_INFO, "Connecting to parent to download clients"); 21  22     /* 连接socket */ 23     sock = socket(AF_UNIX, SOCK_STREAM, 0); 24     memset(&sa_un, 0, sizeof(sa_un)); 25     sa_un.sun_family = AF_UNIX; 26     strncpy(sa_un.sun_path, config->internal_sock, (sizeof(sa_un.sun_path) - 1));    //config->internal_sock的值为"/tmp/wifidog.sock" 27  28     /* 连接已启动的wifidog */ 29     if (connect(sock, (struct sockaddr *)&sa_un, strlen(sa_un.sun_path) + sizeof(sa_un.sun_family))) { 30         debug(LOG_ERR, "Failed to connect to parent (%s) - client list not downloaded", strerror(errno)); 31         return; 32     } 33  34     debug(LOG_INFO, "Connected to parent.  Downloading clients"); 35  36     LOCK_CLIENT_LIST(); 37  38     command = NULL; 39     memset(linebuffer, 0, sizeof(linebuffer)); 40     len = 0; 41     client = NULL; 42     /* 接收数据,逐个字符接收 */ 43     /* 数据包格式为 CLIENT|ip=%s|mac=%s|token=%s|fw_connection_state=%u|fd=%d|counters_incoming=%llu|counters_outgoing=%llu|counters_last_updated=%lu\n */ 44     while (read(sock, &onechar, 1) == 1) { 45         if (onechar == '\n') { 46             /* 如果接收到末尾('\n'),则转为'\0' */ 47             onechar = '\0'; 48         } 49         linebuffer[len++] = onechar; 50          51         if (!onechar) { 52             /* 以下将数据转化为t_client结构体添加到客户端列表 */ 53             debug(LOG_DEBUG, "Received from parent: [%s]", linebuffer); 54             running1 = linebuffer; 55             while ((token1 = strsep(&running1, "|")) != NULL) { 56                 if (!command) { 57                     /* The first token is the command */ 58                     command = token1; 59                 } 60                 else { 61                 /* Token1 has something like "foo=bar" */ 62                     running2 = token1; 63                     key = value = NULL; 64                     while ((token2 = strsep(&running2, "=")) != NULL) { 65                         if (!key) { 66                             key = token2; 67                         } 68                         else if (!value) { 69                             value = token2; 70                         } 71                     } 72                 } 73  74                 if (strcmp(command, "CLIENT") == 0) { 75                     /* This line has info about a client in the client list */ 76                     if (!client) { 77                         /* Create a new client struct */ 78                         client = safe_malloc(sizeof(t_client)); 79                         memset(client, 0, sizeof(t_client)); 80                     } 81                 } 82  83                 if (key && value) { 84                     if (strcmp(command, "CLIENT") == 0) { 85                         /* Assign the key into the appropriate slot in the connection structure */ 86                         if (strcmp(key, "ip") == 0) { 87                             client->ip = safe_strdup(value); 88                         } 89                         else if (strcmp(key, "mac") == 0) { 90                             client->mac = safe_strdup(value); 91                         } 92                         else if (strcmp(key, "token") == 0) { 93                             client->token = safe_strdup(value); 94                         } 95                         else if (strcmp(key, "fw_connection_state") == 0) { 96                             client->fw_connection_state = atoi(value); 97                         } 98                         else if (strcmp(key, "fd") == 0) { 99                             client->fd = atoi(value);100                         }101                         else if (strcmp(key, "counters_incoming") == 0) {102                             client->counters.incoming_history = atoll(value);103                             client->counters.incoming = client->counters.incoming_history;104                         }105                         else if (strcmp(key, "counters_outgoing") == 0) {106                             client->counters.outgoing_history = atoll(value);107                             client->counters.outgoing = client->counters.outgoing_history;108                         }109                         else if (strcmp(key, "counters_last_updated") == 0) {110                             client->counters.last_updated = atol(value);111                         }112                         else {113                             debug(LOG_NOTICE, "I don't know how to inherit key [%s] value [%s] from parent", key, value);114                         }115                     }116                 }117             }118 119             /* End of parsing this command */120             if (client) {121                 /* Add this client to the client list */122                 if (!firstclient) {123                     firstclient = client;124                     lastclient = firstclient;125                 }126                 else {127                     lastclient->next = client;128                     lastclient = client;129                 }130             }131 132             /* Clean up */133             command = NULL;134             memset(linebuffer, 0, sizeof(linebuffer));135             len = 0;136             client = NULL;137         }138     }139 140     UNLOCK_CLIENT_LIST();141     debug(LOG_INFO, "Client list downloaded successfully from parent");142 143     close(sock);144 }
复制代码

 

 

代码片段1.3(已启动的wifidog发送客户端列表到新启动的wifidog):

复制代码
  1 //thread_wdctl_handler(void *arg)函数是wifidog启动后自动创建的控制线程,主要用于与wdctrl进行socket通信,根据wdctrl命令执行不同的操作。这里我们着重讲解的是wdctrl发送restart后wifidog的执行逻辑。  2 static void *  3 thread_wdctl_handler(void *arg)  4 {  5     int    fd,  6         done,  7         i;  8     char    request[MAX_BUF];  9     ssize_t    read_bytes, 10         len; 11  12     debug(LOG_DEBUG, "Entering thread_wdctl_handler...."); 13  14     fd = (int)arg; 15      16     debug(LOG_DEBUG, "Read bytes and stuff from %d", fd); 17  18     /* 初始化变量 */ 19     read_bytes = 0; 20     done = 0; 21     memset(request, 0, sizeof(request)); 22      23     /* 读取命令 */ 24     while (!done && read_bytes < (sizeof(request) - 1)) { 25         len = read(fd, request + read_bytes, 26                 sizeof(request) - read_bytes);    //读取wdctrl发送的命令 27  28         /* 判断命令正确性 */ 29         for (i = read_bytes; i < (read_bytes + len); i++) { 30             if (request[i] == '\r' || request[i] == '\n') { 31                 request[i] = '\0'; 32                 done = 1; 33             } 34         } 35          36         /* Increment position */ 37         read_bytes += len; 38     } 39  40         //判断命令 41     if (strncmp(request, "status", 6) == 0) { 42         wdctl_status(fd); 43     } else if (strncmp(request, "stop", 4) == 0) { 44         wdctl_stop(fd); 45     } else if (strncmp(request, "reset", 5) == 0) { 46         wdctl_reset(fd, (request + 6)); 47     } else if (strncmp(request, "restart", 7) == 0) { 48         wdctl_restart(fd);    //执行wdctl_restart(int afd)函数 49     } 50  51     if (!done) { 52         debug(LOG_ERR, "Invalid wdctl request."); 53                 //关闭套接字 54         shutdown(fd, 2); 55         close(fd); 56         pthread_exit(NULL); 57     } 58  59     debug(LOG_DEBUG, "Request received: [%s]", request); 60      61         //关闭套接字 62     shutdown(fd, 2); 63     close(fd); 64     debug(LOG_DEBUG, "Exiting thread_wdctl_handler...."); 65  66     return NULL; 67 } 68  69  70 //wdctl_restart(int afd)函数详解 71 static void 72 wdctl_restart(int afd) 73 { 74     int    sock, 75         fd; 76     char    *sock_name; 77     struct     sockaddr_un    sa_un; 78     s_config * conf = NULL; 79     t_client * client = NULL; 80     char * tempstring = NULL; 81     pid_t pid; 82     ssize_t written; 83     socklen_t len; 84  85     conf = config_get_config(); 86  87     debug(LOG_NOTICE, "Will restart myself"); 88  89     /* 90      * 准备内部连接socket 91      */ 92     memset(&sa_un, 0, sizeof(sa_un)); 93     sock_name = conf->internal_sock;    //conf->internal_sock值为"/tmp/wifidog.sock" 94     debug(LOG_DEBUG, "Socket name: %s", sock_name); 95  96     if (strlen(sock_name) > (sizeof(sa_un.sun_path) - 1)) { 97         98         debug(LOG_ERR, "INTERNAL socket name too long"); 99         return;100     }101 102     debug(LOG_DEBUG, "Creating socket");103     sock = socket(PF_UNIX, SOCK_STREAM, 0);    //建立内部socket套接字104 105     debug(LOG_DEBUG, "Got internal socket %d", sock);106 107     /* 如果sock_name文件存在,则删除*/108     unlink(sock_name);109 110     debug(LOG_DEBUG, "Filling sockaddr_un");111     strcpy(sa_un.sun_path, sock_name); 112     sa_un.sun_family = AF_UNIX;113     114     debug(LOG_DEBUG, "Binding socket (%s) (%d)", sa_un.sun_path, strlen(sock_name));115     116    117     if (bind(sock, (struct sockaddr *)&sa_un, strlen(sock_name) + sizeof(sa_un.sun_family))) {118         debug(LOG_ERR, "Could not bind internal socket: %s", strerror(errno));119         return;120     }121 122     if (listen(sock, 5)) {123         debug(LOG_ERR, "Could not listen on internal socket: %s", strerror(errno));124         return;125     }126     127     /*128      * socket建立完成,创建子进程129      */130     debug(LOG_DEBUG, "Forking in preparation for exec()...");131     pid = safe_fork();132     if (pid > 0) {133         /* 父进程 */134 135         /* 等待子进程连接此socket :*/136         debug(LOG_DEBUG, "Waiting for child to connect on internal socket");137         len = sizeof(sa_un);138         if ((fd = accept(sock, (struct sockaddr *)&sa_un, &len)) == -1){    //接受连接139             debug(LOG_ERR, "Accept failed on internal socket: %s", strerror(errno));140             close(sock);141             return;142         }143 144         close(sock);145 146         debug(LOG_DEBUG, "Received connection from child.  Sending them all existing clients");147 148         /*子进程已经完成连接,发送客户端列表 */149         LOCK_CLIENT_LIST();150         client = client_get_first_client();    //获取第一个客户端151         while (client) {152             /* Send this client */153             safe_asprintf(&tempstring, "CLIENT|ip=%s|mac=%s|token=%s|fw_connection_state=%u|fd=%d|counters_incoming=%llu|counters_outgoing=%llu|counters_last_updated=%lu\n", client->ip, client->mac, client->token, client->fw_connection_state, client->fd, client->counters.incoming, client->counters.outgoing, client->counters.last_updated);154             debug(LOG_DEBUG, "Sending to child client data: %s", tempstring);155             len = 0;156             while (len != strlen(tempstring)) {157                 written = write(fd, (tempstring + len), strlen(tempstring) - len);    //发送给子进程158                 if (written == -1) {159                     debug(LOG_ERR, "Failed to write client data to child: %s", strerror(errno));160                     free(tempstring);161                     break;162                 }163                 else {164                     len += written;165                 }166             }167             free(tempstring);168             client = client->next;169         }170         UNLOCK_CLIENT_LIST();171 172         close(fd);173 174         debug(LOG_INFO, "Sent all existing clients to child.  Committing suicide!");175 176         shutdown(afd, 2);177         close(afd);178 179         180         wdctl_stop(afd);181     }182     else {183         /* 子进程,先关闭资源 */184         close(wdctl_socket_server);185         close(icmp_fd);186         close(sock);187         shutdown(afd, 2);188         close(afd);189         debug(LOG_NOTICE, "Re-executing myself (%s)", restartargv[0]);190 191         setsid();192         execvp(restartargv[0], restartargv);    //执行外部命令,这里重新启动wifidog193         194         debug(LOG_ERR, "I failed to re-execute myself: %s", strerror(errno));195         debug(LOG_ERR, "Exiting without cleanup");196         exit(1);197     }198 }
复制代码

 

小结

  客户端列表只有在restart命令中才会执行,实际上流程就是

  • 父wifidog准备socket
  • 父wifidog启动子wifidog
  • 子wifidog连接父wifidog
  • 客户端列表传递
  • 子wifidog终止父wifidog

Wifidog系列文章转载自:http://www.cnblogs.com/tolimit/

0 0
原创粉丝点击