Chinaunix首页 | 论坛 | 博客
  • 博客访问: 413546
  • 博文数量: 96
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 415
  • 用 户 组: 普通用户
  • 注册时间: 2015-05-22 09:08
个人简介

最近的研究方向:Nginx

文章分类
文章存档

2017年(2)

2016年(59)

2015年(35)

我的朋友

分类: 嵌入式

2016-10-17 14:34:10

    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.     s_config *config = config_get_config(); //就是返回全局变量config结构体的地址
  3.     config_init(); //初始化全局变量config结构体为默认值

  4.     parse_commandline(argc, argv); //根据传入参数执行操作(如果参数有-x则会设置restart_orig_pid为已运行的wifidog的pid)

  5.     /* Initialize the config */
  6.     config_read(config->configfile); //根据配置文件设置全局变量config结构体
  7.     config_validate(); //判断GatewayInterface和AuthServer是否为空,空则无效退出程序。

  8.     /* Initializes the linked list of connected clients */
  9.     client_list_init(); //将已连接客户端链表置空。

  10.     /* Init the signals to catch chld/quit/etc */
  11.     init_signals(); //初始化一些信号

  12.     if (restart_orig_pid) { //用于restart,如果有已运行的wifidog,先会kill它
  13.         /*
  14.          * We were restarted and our parent is waiting for us to talk to it over the socket
  15.          */
  16.         get_clients_from_parent(); //从已运行的wifidog中获取客户端列表,详见 代码片段1.2

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

  20.         while (kill(restart_orig_pid, 0) != -1) { //kill已运行的wifidog
  21.             debug(LOG_INFO, "Waiting for parent PID %d to die before continuing loading", restart_orig_pid);
  22.             sleep(1);
  23.         }

  24.         debug(LOG_INFO, "Parent PID %d seems to be dead. Continuing loading.");
  25.     }

  26.     if (config->daemon) { //创建为守护进程,config->daemon默认值为-1

  27.         debug(LOG_INFO, "Forking into background");

  28.         switch(safe_fork()) {
  29.             case 0: /* child */
  30.                 setsid(); //创建新会话,脱离此终端,实现守护进程
  31.                 append_x_restartargv();
  32.                 main_loop(); //进入主循环(核心代码在此)。
  33.                 break;

  34.             default: /* parent */
  35.                 exit(0);
  36.                 break;
  37.         }
  38.     }
  39.     else {
  40.         append_x_restartargv();
  41.         main_loop();
  42.     }

  43.     return(0); /* never reached */
  44. }

代码片段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.     config = config_get_config();
  18.     
  19.     debug(LOG_INFO, "Connecting to parent to download clients");

  20.     /* 连接socket */
  21.     sock = socket(AF_UNIX, SOCK_STREAM, 0);
  22.     memset(&sa_un, 0, sizeof(sa_un));
  23.     sa_un.sun_family = AF_UNIX;
  24.     strncpy(sa_un.sun_path, config->internal_sock, (sizeof(sa_un.sun_path) - 1)); //config->internal_sock的值为"/tmp/wifidog.sock"

  25.     /* 连接已启动的wifidog */
  26.     if (connect(sock, (struct sockaddr *)&sa_un, strlen(sa_un.sun_path) + sizeof(sa_un.sun_family))) {
  27.         debug(LOG_ERR, "Failed to connect to parent (%s) - client list not downloaded", strerror(errno));
  28.         return;
  29.     }

  30.     debug(LOG_INFO, "Connected to parent. Downloading clients");

  31.     LOCK_CLIENT_LIST();

  32.     command = NULL;
  33.     memset(linebuffer, 0, sizeof(linebuffer));
  34.     len = 0;
  35.     client = NULL;
  36.     /* 接收数据,逐个字符接收 */
  37.     /* 数据包格式为 CLIENT|ip=%s|mac=%s|token=%s|fw_connection_state=%u|fd=%d|counters_incoming=%llu|counters_outgoing=%llu|counters_last_updated=%lu\n */
  38.     while (read(sock, &onechar, 1) == 1) {
  39.         if (onechar == '\n') {
  40.             /* 如果接收到末尾('\n'),则转为'\0' */
  41.             onechar = '\0';
  42.         }
  43.         linebuffer[len++] = onechar;
  44.         
  45.         if (!onechar) {
  46.             /* 以下将数据转化为t_client结构体添加到客户端列表 */
  47.             debug(LOG_DEBUG, "Received from parent: [%s]", linebuffer);
  48.             running1 = linebuffer;
  49.             while ((token1 = strsep(&running1, "|")) != NULL) {
  50.                 if (!command) {
  51.                     /* The first token is the command */
  52.                     command = token1;
  53.                 }
  54.                 else {
  55.                 /* Token1 has something like "foo=bar" */
  56.                     running2 = token1;
  57.                     key = value = NULL;
  58.                     while ((token2 = strsep(&running2, "=")) != NULL) {
  59.                         if (!key) {
  60.                             key = token2;
  61.                         }
  62.                         else if (!value) {
  63.                             value = token2;
  64.                         }
  65.                     }
  66.                 }

  67.                 if (strcmp(command, "CLIENT") == 0) {
  68.                     /* This line has info about a client in the client list */
  69.                     if (!client) {
  70.                         /* Create a new client struct */
  71.                         client = safe_malloc(sizeof(t_client));
  72.                         memset(client, 0, sizeof(t_client));
  73.                     }
  74.                 }

  75.                 if (key && value) {
  76.                     if (strcmp(command, "CLIENT") == 0) {
  77.                         /* Assign the key into the appropriate slot in the connection structure */
  78.                         if (strcmp(key, "ip") == 0) {
  79.                             client->ip = safe_strdup(value);
  80.                         }
  81.                         else if (strcmp(key, "mac") == 0) {
  82.                             client->mac = safe_strdup(value);
  83.                         }
  84.                         else if (strcmp(key, "token") == 0) {
  85.                             client->token = safe_strdup(value);
  86.                         }
  87.                         else if (strcmp(key, "fw_connection_state") == 0) {
  88.                             client->fw_connection_state = atoi(value);
  89.                         }
  90.                         else if (strcmp(key, "fd") == 0) {
  91.                             client->fd = atoi(value);
  92.                         }
  93.                         else if (strcmp(key, "counters_incoming") == 0) {
  94.                             client->counters.incoming_history = atoll(value);
  95.                             client->counters.incoming = client->counters.incoming_history;
  96.                         }
  97.                         else if (strcmp(key, "counters_outgoing") == 0) {
  98.                             client->counters.outgoing_history = atoll(value);
  99.                             client->counters.outgoing = client->counters.outgoing_history;
  100.                         }
  101.                         else if (strcmp(key, "counters_last_updated") == 0) {
  102.                             client->counters.last_updated = atol(value);
  103.                         }
  104.                         else {
  105.                             debug(LOG_NOTICE, "I don't know how to inherit key [%s] value [%s] from parent", key, value);
  106.                         }
  107.                     }
  108.                 }
  109.             }

  110.             /* End of parsing this command */
  111.             if (client) {
  112.                 /* Add this client to the client list */
  113.                 if (!firstclient) {
  114.                     firstclient = client;
  115.                     lastclient = firstclient;
  116.                 }
  117.                 else {
  118.                     lastclient->next = client;
  119.                     lastclient = client;
  120.                 }
  121.             }

  122.             /* Clean up */
  123.             command = NULL;
  124.             memset(linebuffer, 0, sizeof(linebuffer));
  125.             len = 0;
  126.             client = NULL;
  127.         }
  128.     }

  129.     UNLOCK_CLIENT_LIST();
  130.     debug(LOG_INFO, "Client list downloaded successfully from parent");

  131.     close(sock);
  132. }

代码片段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.     debug(LOG_DEBUG, "Entering thread_wdctl_handler....");

  12.     fd = (int)arg;
  13.     
  14.     debug(LOG_DEBUG, "Read bytes and stuff from %d", fd);

  15.     /* 初始化变量 */
  16.     read_bytes = 0;
  17.     done = 0;
  18.     memset(request, 0, sizeof(request));
  19.     
  20.     /* 读取命令 */
  21.     while (!done && read_bytes < (sizeof(request) - 1)) {
  22.         len = read(fd, request + read_bytes,
  23.                 sizeof(request) - read_bytes); //读取wdctrl发送的命令

  24.         /* 判断命令正确性 */
  25.         for (i = read_bytes; i < (read_bytes + len); i++) {
  26.             if (request[i] == '\r' || request[i] == '\n') {
  27.                 request[i] = '\0';
  28.                 done = 1;
  29.             }
  30.         }
  31.         
  32.         /* Increment position */
  33.         read_bytes += len;
  34.     }

  35.         //判断命令
  36.     if (strncmp(request, "status", 6) == 0) {
  37.         wdctl_status(fd);
  38.     } else if (strncmp(request, "stop", 4) == 0) {
  39.         wdctl_stop(fd);
  40.     } else if (strncmp(request, "reset", 5) == 0) {
  41.         wdctl_reset(fd, (request + 6));
  42.     } else if (strncmp(request, "restart", 7) == 0) {
  43.         wdctl_restart(fd); //执行wdctl_restart(int afd)函数
  44.     }

  45.     if (!done) {
  46.         debug(LOG_ERR, "Invalid wdctl request.");
  47.                 //关闭套接字
  48.         shutdown(fd, 2);
  49.         close(fd);
  50.         pthread_exit(NULL);
  51.     }

  52.     debug(LOG_DEBUG, "Request received: [%s]", request);
  53.     
  54.         //关闭套接字
  55.     shutdown(fd, 2);
  56.     close(fd);
  57.     debug(LOG_DEBUG, "Exiting thread_wdctl_handler....");

  58.     return NULL;
  59. }


  60. //wdctl_restart(int afd)函数详解
  61. static void
  62. wdctl_restart(int afd)
  63. {
  64.     int sock,
  65.         fd;
  66.     char *sock_name;
  67.     struct sockaddr_un sa_un;
  68.     s_config * conf = NULL;
  69.     t_client * client = NULL;
  70.     char * tempstring = NULL;
  71.     pid_t pid;
  72.     ssize_t written;
  73.     socklen_t len;

  74.     conf = config_get_config();

  75.     debug(LOG_NOTICE, "Will restart myself");

  76.     /*
  77.      * 准备内部连接socket
  78.      */
  79.     memset(&sa_un, 0, sizeof(sa_un));
  80.     sock_name = conf->internal_sock; //conf->internal_sock值为"/tmp/wifidog.sock"
  81.     debug(LOG_DEBUG, "Socket name: %s", sock_name);

  82.     if (strlen(sock_name) > (sizeof(sa_un.sun_path) - 1)) {
  83.        
  84.         debug(LOG_ERR, "INTERNAL socket name too long");
  85.         return;
  86.     }

  87.     debug(LOG_DEBUG, "Creating socket");
  88.     sock = socket(PF_UNIX, SOCK_STREAM, 0); //建立内部socket套接字

  89.     debug(LOG_DEBUG, "Got internal socket %d", sock);

  90.     /* 如果sock_name文件存在,则删除*/
  91.     unlink(sock_name);

  92.     debug(LOG_DEBUG, "Filling sockaddr_un");
  93.     strcpy(sa_un.sun_path, sock_name);
  94.     sa_un.sun_family = AF_UNIX;
  95.     
  96.     debug(LOG_DEBUG, "Binding socket (%s) (%d)", sa_un.sun_path, strlen(sock_name));
  97.     
  98.    
  99.     if (bind(sock, (struct sockaddr *)&sa_un, strlen(sock_name) + sizeof(sa_un.sun_family))) {
  100.         debug(LOG_ERR, "Could not bind internal socket: %s", strerror(errno));
  101.         return;
  102.     }

  103.     if (listen(sock, 5)) {
  104.         debug(LOG_ERR, "Could not listen on internal socket: %s", strerror(errno));
  105.         return;
  106.     }
  107.     
  108.     /*
  109.      * socket建立完成,创建子进程
  110.      */
  111.     debug(LOG_DEBUG, "Forking in preparation for exec()...");
  112.     pid = safe_fork();
  113.     if (pid > 0) {
  114.         /* 父进程 */

  115.         /* 等待子进程连接此socket :*/
  116.         debug(LOG_DEBUG, "Waiting for child to connect on internal socket");
  117.         len = sizeof(sa_un);
  118.         if ((fd = accept(sock, (struct sockaddr *)&sa_un, &len)) == -1){ //接受连接
  119.             debug(LOG_ERR, "Accept failed on internal socket: %s", strerror(errno));
  120.             close(sock);
  121.             return;
  122.         }

  123.         close(sock);

  124.         debug(LOG_DEBUG, "Received connection from child. Sending them all existing clients");

  125.         /*子进程已经完成连接,发送客户端列表 */
  126.         LOCK_CLIENT_LIST();
  127.         client = client_get_first_client(); //获取第一个客户端
  128.         while (client) {
  129.             /* Send this client */
  130.             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);
  131.             debug(LOG_DEBUG, "Sending to child client data: %s", tempstring);
  132.             len = 0;
  133.             while (len != strlen(tempstring)) {
  134.                 written = write(fd, (tempstring + len), strlen(tempstring) - len); //发送给子进程
  135.                 if (written == -1) {
  136.                     debug(LOG_ERR, "Failed to write client data to child: %s", strerror(errno));
  137.                     free(tempstring);
  138.                     break;
  139.                 }
  140.                 else {
  141.                     len += written;
  142.                 }
  143.             }
  144.             free(tempstring);
  145.             client = client->next;
  146.         }
  147.         UNLOCK_CLIENT_LIST();

  148.         close(fd);

  149.         debug(LOG_INFO, "Sent all existing clients to child. Committing suicide!");

  150.         shutdown(afd, 2);
  151.         close(afd);

  152.         
  153.         wdctl_stop(afd);
  154.     }
  155.     else {
  156.         /* 子进程,先关闭资源 */
  157.         close(wdctl_socket_server);
  158.         close(icmp_fd);
  159.         close(sock);
  160.         shutdown(afd, 2);
  161.         close(afd);
  162.         debug(LOG_NOTICE, "Re-executing myself (%s)", restartargv[0]);

  163.         setsid();
  164.         execvp(restartargv[0], restartargv); //执行外部命令,这里重新启动wifidog
  165.         
  166.         debug(LOG_ERR, "I failed to re-execute myself: %s", strerror(errno));
  167.         debug(LOG_ERR, "Exiting without cleanup");
  168.         exit(1);
  169.     }
  170. }

小结

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

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



阅读(10290) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~