wifidog源码分析 - 初始化阶段

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 socket

21          */

22         get_clients_from_parent();    //从已运行的wifidog中获取客户端列表,详见 代码片段1.2

23 

24         /*

25          * At this point the parent will start destroying itself and the firewall. Let it finish it's job before we continue

26          */

27 

28         while (kill(restart_orig_pid, 0) != -1) {    //kill已运行的wifidog

29             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默认值为-1

37 

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);    //执行外部命令,这里重新启动wifidog

193         

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

你可能感兴趣的:(源码分析)