本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。
msn: yfydz_no1@hotmail.com
来源: http://yfydz.cublog.cn

6. 系统初始化

pluto的初始化参数可以通过命令输入,也可以通过配置文件进行配置。

/* programs/pluto/plutomain.c */
int
main(int argc, char **argv)
{
    bool fork_desired = TRUE;
    bool log_to_stderr_desired = FALSE;
    int lockfd;
    char* ocspuri = NULL;
    int nhelpers = -1;
    char *coredir;
#ifdef NAT_TRAVERSAL
    /** Overridden by nat_traversal= in ipsec.conf */
    bool nat_traversal = FALSE;
    bool nat_t_spf = TRUE;  /* support port floating */
    unsigned int keep_alive = 0;
    bool force_keepalive = FALSE;
#endif
#ifdef VIRTUAL_IP
    /** Overridden by virtual_private= in ipsec.conf */
    char *virtual_private = NULL;
#endif
    global_argv = argv;
    global_argc = argc;
    openswan_passert_fail = passert_fail;
    /* see if there is an environment variable */
    coredir = getenv("PLUTO_CORE_DIR");
// 处理命令输入
    /* handle arguments */
    for (;;)
    {
# define DBG_OFFSET 256
 static const struct option long_opts[] = {
     /* name, has_arg, flag, val */
     { "help", no_argument, NULL, 'h' },
     { "version", no_argument, NULL, 'v' },
     { "optionsfrom", required_argument, NULL, '+' },
     { "nofork", no_argument, NULL, 'd' },
     { "stderrlog", no_argument, NULL, 'e' },
     { "noklips", no_argument, NULL, 'n' },
     { "use-nostack",  no_argument, NULL, 'n' },
     { "nocrsend", no_argument, NULL, 'c' },
     { "strictcrlpolicy", no_argument, NULL, 'r' },
     { "crlcheckinterval", required_argument, NULL, 'x'},
     { "ocsprequestcert", required_argument, NULL, 'q'},
     { "ocspuri", required_argument, NULL, 'o'},
     { "uniqueids", no_argument, NULL, 'u' },
     { "useklips",  no_argument, NULL, 'k' },
     { "use-klips",  no_argument, NULL, 'k' },
     { "use-auto",  no_argument, NULL, 'G' },
     { "usenetkey", no_argument, NULL, 'K' },
     { "use-netkey", no_argument, NULL, 'K' },
     { "interface", required_argument, NULL, 'i' },
     { "ikeport", required_argument, NULL, 'p' },
     { "ctlbase", required_argument, NULL, 'b' },
     { "secretsfile", required_argument, NULL, 's' },
     { "foodgroupsdir", required_argument, NULL, 'f' },
     { "perpeerlogbase", required_argument, NULL, 'P' },
     { "perpeerlog", no_argument, NULL, 'l' },
     { "noretransmits", no_argument, NULL, 'R' },
     { "coredir", required_argument, NULL, 'C' },
     { "ipsecdir", required_argument, NULL, 'f' },
     { "ipsec_dir", required_argument, NULL, 'f' },
#ifdef USE_LWRES
     { "lwdnsq", required_argument, NULL, 'a' },
#else /* !USE_LWRES */
     { "adns", required_argument, NULL, 'a' },
#endif /* !USE_LWRES */
#ifdef NAT_TRAVERSAL
     { "nat_traversal", no_argument, NULL, '1' },
     { "keep_alive", required_argument, NULL, '2' },
     { "force_keepalive", no_argument, NULL, '3' },
     { "disable_port_floating", no_argument, NULL, '4' },
     { "debug-nat_t", no_argument, NULL, '5' },
     { "debug-nattraversal", no_argument, NULL, '5' },
     { "debug-nat-t", no_argument, NULL, '5' },
#endif
#ifdef VIRTUAL_IP
     { "virtual_private", required_argument, NULL, '6' },
#endif
     { "nhelpers", required_argument, NULL, 'j' },
#ifdef DEBUG
     { "debug-none", no_argument, NULL, 'N' },
     { "debug-all]", no_argument, NULL, 'A' },
     { "debug-raw", no_argument, NULL, DBG_RAW + DBG_OFFSET },
     { "debug-crypt", no_argument, NULL, DBG_CRYPT + DBG_OFFSET },
     { "debug-parsing", no_argument, NULL, DBG_PARSING + DBG_OFFSET },
     { "debug-emitting", no_argument, NULL, DBG_EMITTING + DBG_OFFSET },
     { "debug-control", no_argument, NULL, DBG_CONTROL + DBG_OFFSET },
     { "debug-lifecycle", no_argument, NULL, DBG_LIFECYCLE + DBG_OFFSET },
     { "debug-klips", no_argument, NULL, DBG_KLIPS + DBG_OFFSET },
     { "debug-dns", no_argument, NULL, DBG_DNS + DBG_OFFSET },
     { "debug-oppo", no_argument, NULL, DBG_OPPO + DBG_OFFSET },
     { "debug-controlmore", no_argument, NULL, DBG_CONTROLMORE + DBG_OFFSET },
     { "debug-dpd", no_argument, NULL, DBG_DPD + DBG_OFFSET },
     { "debug-private", no_argument, NULL, DBG_PRIVATE + DBG_OFFSET },
     { "debug-pfkey", no_argument, NULL, DBG_PFKEY + DBG_OFFSET },
     { "impair-delay-adns-key-answer", no_argument, NULL, IMPAIR_DELAY_ADNS_KEY_ANSWER + DBG_OFFSET },
     { "impair-delay-adns-txt-answer", no_argument, NULL, IMPAIR_DELAY_ADNS_TXT_ANSWER + DBG_OFFSET },
     { "impair-bust-mi2", no_argument, NULL, IMPAIR_BUST_MI2 + DBG_OFFSET },
     { "impair-bust-mr2", no_argument, NULL, IMPAIR_BUST_MR2 + DBG_OFFSET },
     { "impair-jacob-two-two", no_argument, NULL, IMPAIR_JACOB_TWO_TWO + DBG_OFFSET },
#endif
     { 0,0,0,0 }
     };
 /* Note: we don't like the way short options get parsed
  * by getopt_long, so we simply pass an empty string as
  * the list.  It could be "hvdenp:l:s:" "NARXPECK".
  */
 int c = getopt_long(argc, argv, "", long_opts, NULL);
 /** Note: "breaking" from case terminates loop */
 switch (c)
 {
 case EOF: /* end of flags */
     break;
 case 0: /* long option already handled */
     continue;
 case ':': /* diagnostic already printed by getopt_long */
 case '?': /* diagnostic already printed by getopt_long */
     usage("");
     break;   /* not actually reached */
 case 'h': /* --help */
// 帮助信息
     usage(NULL);
     break; /* not actually reached */
 case 'C':
// 核心目录,即运行pluto的目录,也将是系统崩溃是core文件的输出目录,缺省是当前目录
     coredir = clone_str(optarg, "coredir");
     break;
 case 'v': /* --version */
// pluto版本信息
     {
  const char **sp = ipsec_copyright_notice();
  printf("%s%s\n", ipsec_version_string(),
     compile_time_interop_options);
  for (; *sp != NULL; sp++)
      puts(*sp);
     }
     exit(0); /* not exit_pluto because we are not initialized yet */
     break; /* not actually reached */
 case '+': /* --optionsfrom <filename> */
// 从文件中获取选项参数
     optionsfrom(optarg, &argc, &argv, optind, stderr);
     /* does not return on error */
     continue;
 case 'j': /* --nhelpers */
            if (optarg == NULL || !isdigit(optarg[0]))
                usage("missing number of pluto helpers");
            {
                char *endptr;
                long count = strtol(optarg, &endptr, 0);
                if (*endptr != '\0' || endptr == optarg
      || count < -1)
                    usage("<interval-time> must be a positive number, 0 or -1");
                nhelpers = count;
            }
     continue;
 case 'd': /* --nofork*/
// 是否是daemon,缺省pluto是daemon
     fork_desired = FALSE;
     continue;
 case 'e': /* --stderrlog */
// 将日志输出到标准错误
     log_to_stderr_desired = TRUE;
     continue;
 case 'G':       /* --use-auto */
// 自动选择内核接口,根据内核IPSEC实现是KLIPS还是NETKEY(2.6 native ipsec)自动设置接口
     kern_interface = AUTO_PICK;
     continue;
 case 'k':       /* --use-klips */
// 明确指定内核IPSEC是KLIPS
     kern_interface = USE_KLIPS;
     continue;
 case 'K':       /* --use-netkey */
// 明确指定内核IPSEC是NETKEY
     kern_interface = USE_NETKEY;
     continue;
 case 'n': /* --use-nostack */
// 明确指定内核无IPSEC
     kern_interface = NO_KERNEL;
     continue;
 case 'c': /* --nocrsend */
// 是否向对方发送证书请求,缺省为FALSE
     no_cr_send = TRUE;
     continue
     ;
 case 'r': /* --strictcrlpolicy */
// 是否严格处理CRL(证书吊销列表)
     strict_crl_policy = TRUE;
     continue
     ;
 case 'R':
// 不重复发送
     no_retransmits = TRUE;
     continue;
 case 'x': /* --crlcheckinterval <time>*/
// CRL定时检查间隔时间
            if (optarg == NULL || !isdigit(optarg[0]))
                usage("missing interval time");
            {
                char *endptr;
                long interval = strtol(optarg, &endptr, 0);
                if (*endptr != '\0' || endptr == optarg
                || interval <= 0)
                    usage("<interval-time> must be a positive number");
                crl_check_interval = interval;
            }
     continue
     ;
 case 'o': /* --ocspuri */
// OSCP的URI地址
     ocspuri = optarg;
     continue;
 case 'u': /* --uniqueids */
// 要求不同IP地址要有不同的ID标识
     uniqueIDs = TRUE;
     continue;
 case 'i': /* --interface <ifname> */
// 监听UDP的网络接口,缺省是全部网卡都监听
     if (!use_interface(optarg))
  usage("too many --interface specifications");
     continue;
 case 'p': /* --port <portnumber> */
// 设置pluto监听端口,缺省是500,NAT穿越时是4500
     if (optarg == NULL || !isdigit(optarg[0]))
  usage("missing port number");
     {
  char *endptr;
  long port = strtol(optarg, &endptr, 0);
  if (*endptr != '\0' || endptr == optarg
  || port <= 0 || port > 0x10000)
      usage("<port-number> must be a number between 1 and 65535");
  pluto_port = port;
     }
     continue;
 case 'b': /* --ctlbase <path> */
// 控制文件目录,缺省是"/var/run/pluto",用于文件锁,文件pid等
     ctlbase = optarg;
     if (snprintf(ctl_addr.sun_path, sizeof(ctl_addr.sun_path)
    , "%s%s", ctlbase, CTL_SUFFIX) == -1)
  usage("<path>" CTL_SUFFIX " too long for sun_path");
     if (snprintf(info_addr.sun_path, sizeof(info_addr.sun_path)
    , "%s%s", ctlbase, INFO_SUFFIX) == -1)
  usage("<path>" INFO_SUFFIX " too long for sun_path");
     if (snprintf(pluto_lock, sizeof(pluto_lock)
    , "%s%s", ctlbase, LOCK_SUFFIX) == -1)
  usage("<path>" LOCK_SUFFIX " must fit");
     continue;
 case 's': /* --secretsfile <secrets-file> */
// 密钥文件,缺省是/etc/ipsec.secrets
     shared_secrets_file = optarg;
     continue;
 case 'f': /* --ipsecdir <ipsec-dir> */
// ipsec配置目录,缺省是/etc/ipsec.d
     ipsec_dir = optarg;
     continue;
 case 'a': /* --adns <pathname> */
// ADNS选项
     pluto_adns_option = optarg;
     continue;
#ifdef DEBUG
 case 'N': /* --debug-none */
// 不支持DEBUG信息
     base_debugging = DBG_NONE;
     continue;
 case 'A': /* --debug-all */
// 对所有类型和级别的DEBUG
     base_debugging = DBG_ALL;
     continue;
#endif
 case 'P':       /* --perpeerlogbase */
// 为每个连接对方都单独记录日志时的文件目录
     base_perpeer_logdir = optarg;
     continue;
 case 'l':
// 是否单独为每个连接对方分开记录日志
     log_to_perpeer = TRUE;
     continue;
#ifdef NAT_TRAVERSAL
 case '1': /* --nat_traversal */
// 打开NAT穿越支持功能
     nat_traversal = TRUE;
     continue;
 case '2': /* --keep_alive */
// 连接保持激活时间
     keep_alive = atoi(optarg);
     continue;
 case '3': /* --force_keepalive */
// 强迫进行连接保持
     force_keepalive = TRUE;
     continue;
 case '4': /* --disable_port_floating */
// 不允许端口漂移,NAT时也使用500端口
     nat_t_spf = FALSE;
     continue;
 case '5': /* --debug-nat_t */
// NAT穿越DEBUG
     base_debugging |= DBG_NATT;
     continue;
#endif
#ifdef VIRTUAL_IP
 case '6': /* --virtual_private */
// 虚拟IP参数
     virtual_private = optarg;
     continue;
#endif
 default:
#ifdef DEBUG
     if (c >= DBG_OFFSET)
     {
  base_debugging |= c - DBG_OFFSET;
  continue;
     }
#undef DBG_OFFSET
#endif
     bad_case(c);
 }
 break;
    }
    if (optind != argc)
 usage("unexpected argument");
// 复位DEBUG处理, pluto
    reset_debugging();
    /* if a core dir was set, chdir there */
// 如果定义核心目录,转到核心目录作为当前目录
    if(coredir) {
 chdir(coredir);
    }
// 建立文件锁,防止进程重复启动
    lockfd = create_lock();
    /* select between logging methods */
// 选择记录日志的地方:syslog还是标准错误
    if (log_to_stderr_desired)
 log_to_syslog = FALSE;
    else
 log_to_stderr = FALSE;
    /* set the logging function of pfkey debugging */
#ifdef DEBUG
// DEBUG和ERROR记录函数
    pfkey_debug_func = DBG_log;
    pfkey_error_func = DBG_log;
#else
    pfkey_debug_func = NULL;
    pfkey_error_func = NULL;
#endif
    /** create control socket.
     * We must create it before the parent process returns so that
     * there will be no race condition in using it.  The easiest
     * place to do this is before the daemon fork.
     */
    {
// 初始化控制套接口, 也即和whack通信的接口, 是AF_UNIX类型套接口
// 缺省是/var/run/pluto/pluto.ctl
 err_t ugh = init_ctl_socket();
 if (ugh != NULL)
 {
     fprintf(stderr, "pluto: %s", ugh);
     exit_pluto(1);
 }
    }
#ifdef IPSECPOLICY
    /* create info socket. */
    {
// 初始化信息套接口, 也是AF_UNIX类型套接口
// 缺省是/var/run/pluto/pluto.info
 err_t ugh = init_info_socket();
 if (ugh != NULL)
 {
     fprintf(stderr, "pluto: %s", ugh);
     exit_pluto(1);
 }
    }
#endif
    /* If not suppressed, do daemon fork */
    if (fork_desired)
    {
// 进程daemon化,标准步骤,两次fork
 {
     pid_t pid = fork();
     if (pid < 0)
     {
  int e = errno;
  fprintf(stderr, "pluto: fork failed (%d %s)\n",
      errno, strerror(e));
  exit_pluto(1);
     }
     if (pid != 0)
     {
  /* parent: die, after filling PID into lock file.
   * must not use exit_pluto: lock would be removed!
   */
  exit(fill_lock(lockfd, pid)? 0 : 1);
     }
 }
 if (setsid() < 0)
 {
     int e = errno;
     fprintf(stderr, "setsid() failed in main(). Errno %d: %s\n",
  errno, strerror(e));
     exit_pluto(1);
 }
    }
    else
    {
 /* no daemon fork: we have to fill in lock file */
 (void) fill_lock(lockfd, getpid());
 fprintf(stdout, "Pluto initialized\n");
 fflush(stdout);
    }
    /** Close everything but ctl_fd and (if needed) stderr.
     * There is some danger that a library that we don't know
     * about is using some fd that we don't know about.
     * I guess we'll soon find out.
     */
    {
 int i;
// 关闭除了标准错误,控制/信息套接口外的其他各种描述符
 for (i = getdtablesize() - 1; i >= 0; i--)  /* Bad hack */
     if ((!log_to_stderr || i != 2)
#ifdef IPSECPOLICY
     && i != info_fd
#endif
     && i != ctl_fd)
  close(i);
// 打开/dev/null,将标准输出标准错误都定向到那
 /* make sure that stdin, stdout, stderr are reserved */
 if (open("/dev/null", O_RDONLY) != 0)
     abort();
 if (dup2(0, 1) != 1)
     abort();
 if (!log_to_stderr && dup2(0, 2) != 2)
     abort();
    }
// 初始化各种常数
    init_constants();
// 日志处理初始化
    pluto_init_log();
    /* Note: some scripts may look for this exact message -- don't change
     * ipsec barf was one, but it no longer does.
     */
    {
#ifdef PLUTO_SENDS_VENDORID
// pluto的提供者和版本信息
        const char *v = init_pluto_vendorid();
 const char *vc = ipsec_version_code();
        openswan_log("Starting Pluto (Openswan Version %s%s; Vendor ID %s)"
            , vc
            , compile_time_interop_options
            , v);
 if(vc[0]=='c' && vc[1]=='v' && vc[2]=='s') {
     /*
      * when people build RPMs from CVS, make sure they get blamed
      * appropriately, and that we get some way to identify who
      * did it, and when they did it. Use string concat, so that
      * strings the binary can or classic SCCS "what", will find
      * stuff too.
      */
     openswan_log("@(#) built on "__DATE__":"__TIME__":"BUILDER);
 }
#else
        openswan_log("Starting Pluto (Openswan Version %s%s)"
            , ipsec_version_code()
            , compile_time_interop_options);
#endif
    }
    if(coredir) {
 openswan_log("core dump dir: %s", coredir);
    }
/** Initialize all of the various features */
#ifdef NAT_TRAVERSAL
// 初始化NAT穿越配置
    init_nat_traversal(nat_traversal, keep_alive, force_keepalive, nat_t_spf);
#endif
#ifdef VIRTUAL_IP
// 初始化虚拟IP配置
    init_virtual_ip(virtual_private);
#endif
// 初始化随机参数池
    init_rnd_pool();
// 初始化密钥
    init_secret();
// 初始化状态
    init_states();
// 初始化连接
    init_connections();
// 初始化加密算法
    init_crypto();
// 初始化加密辅助
    init_crypto_helpers(nhelpers);
// 初始化解码
    init_demux();
// 初始化内核接口
    init_kernel();
// 初始化ADNS
    init_adns();
// 初始化ID
    init_id();
#ifdef HAVE_THREADS
    init_fetch();
#endif
// 设置缺省OCSP的URI
    ocsp_set_default_uri(ocspuri);
// 加载各种证书
    /* loading X.509 CA certificates */
    load_authcerts("CA cert", CA_CERT_PATH, AUTH_CA);
    /* loading X.509 AA certificates */
    load_authcerts("AA cert", AA_CERT_PATH, AUTH_AA);
    /* loading X.509 OCSP certificates */
    load_authcerts("OCSP cert", OCSP_CERT_PATH, AUTH_OCSP);
    /* loading X.509 CRLs */
// 加载CRL
    load_crls();
    /* loading attribute certificates (experimental) */
    load_acerts();
    daily_log_event();
// 进入主循环
    call_server();
    return -1; /* Shouldn't ever reach this */
}

pluto基本上是单进程的,和whack通信的UNIX域套接口、UDP500、UDP4500、与内核的PF_KEY/netlink套接口等多个文件描述符是通过select函数来选择的,call_server()函数就循环进行这种select操作。

/* programs/pluto/server.c */
/* call_server listens for incoming ISAKMP packets and Whack messages,
 * and handles timer events.
 */
void
call_server(void)
{
    struct iface_port *ifp;
    /* catch SIGHUP and SIGTERM */
// 修改SIGHUP,SIGTERM和SIGCHLD信号的处理函数
    {
 int r;
 struct sigaction act;
// 重读配置
 act.sa_handler = &huphandler;
 sigemptyset(&act.sa_mask);
 act.sa_flags = 0; /* no SA_ONESHOT, no SA_RESTART, no nothing */
 r = sigaction(SIGHUP, &act, NULL);
 passert(r == 0);
// 终止进程
 act.sa_handler = &termhandler;
 r = sigaction(SIGTERM, &act, NULL);
 passert(r == 0);
// 处理子进程结束信号
 act.sa_handler = &childhandler;
 act.sa_flags   = SA_RESTART;
 r = sigaction(SIGCHLD, &act, NULL);
 passert(r == 0);
    }
// pluto系统死循环
    for (;;)
    {
// 读操作描述符集合
 fd_set readfds;
// 写操作描述符集合
 fd_set writefds;
 int ndes;
 /* wait for next interesting thing */
// select死循环
 for (;;)
 {
// 下一个事件的时间,则主要是处理各种超时的事件
     long next_time = next_event();   /* time to any pending timer event */
     int maxfd = ctl_fd;
// 如果设置了进程终止标志,结束pluto
// 根据UNP的说法,用标志来处理信号的方法不是真正安全的
     if (sigtermflag)
  exit_pluto(0);
     if (sighupflag)
     {
// 收到了HUP信号,其实不进行什么操作
  /* Ignorant folks think poking any daemon with SIGHUP
   * is polite.  We catch it and tell them otherwise.
   * There is one use: unsticking a hung recvfrom.
   * This sticking happens sometimes -- kernel bug?
   */
  sighupflag = FALSE;
  openswan_log("Pluto ignores SIGHUP -- perhaps you want \"whack --listen\"");
     }

     if(sigchildflag) {
// 收到CHLD信号, 收割子进程
  reapchildren();
     }
     FD_ZERO(&readfds);
     FD_ZERO(&writefds);
// 控制描述符为读的
     FD_SET(ctl_fd, &readfds);
#ifdef IPSECPOLICY
// 信息描述符为读的
     FD_SET(info_fd, &readfds);
     if (maxfd < info_fd)
  maxfd = info_fd;
#endif
     /* the only write file-descriptor of interest */
     if (adns_qfd != NULL_FD && unsent_ADNS_queries)
     {
  if (maxfd < adns_qfd)
      maxfd = adns_qfd;
// ADNS请求描述符为写的
  FD_SET(adns_qfd, &writefds);
     }
     if (adns_afd != NULL_FD)
     {
  if (maxfd < adns_afd)
      maxfd = adns_afd;
// ADNS回应描述符为读的
  FD_SET(adns_afd, &readfds);
     }
#ifdef KLIPS
     if (kern_interface != NO_KERNEL)
     {
// 内核操作接口描述符
  int fd = *kernel_ops->async_fdp;
  if (kernel_ops->process_queue)
      kernel_ops->process_queue();
  if (maxfd < fd)
      maxfd = fd;
  passert(!FD_ISSET(fd, &readfds));
// 内核接口是读的
  FD_SET(fd, &readfds);
     }
#endif
     if (listening)
     {
  for (ifp = interfaces; ifp != NULL; ifp = ifp->next)
  {
      if (maxfd < ifp->fd)
   maxfd = ifp->fd;
      passert(!FD_ISSET(ifp->fd, &readfds));
// 每个接口监听UDP500/4500的描述符是读的
      FD_SET(ifp->fd, &readfds);
  }
     }
     /* see if helpers need attention */
// 可能的话加入加密辅助描述符到读集合
     pluto_crypto_helper_sockets(&readfds);
     if (no_retransmits || next_time < 0)
     {
  /* select without timer */
// 如果非重发模式或当前没有什么可等的, select不加超时时间
  ndes = select(maxfd + 1, &readfds, &writefds, NULL, NULL);
     }
     else if (next_time == 0)
     {
  /* timer without select: there is a timer event pending,
   * and it should fire now so don't bother to do the select.
   */
  ndes = 0; /* signify timer expiration */
     }
     else
     {
  /* select with timer */
  struct timeval tm;
  tm.tv_sec = next_time;
  tm.tv_usec = 0;
// 设置超时的select,如果这段时间内没数据,则进行相关超时操作
  ndes = select(maxfd + 1, &readfds, &writefds, NULL, &tm);
     }
// select成功中断select循环
     if (ndes != -1)
  break; /* success */
// 否则如果不是EINTR错误的话重新循环进行select
     if (errno != EINTR)
  exit_log_errno((e, "select() failed in call_server()"));
     /* retry if terminated by signal */
 }
 /* figure out what is interesting */
 if (ndes == 0)
 {
// ndes为0表示是等待超时了
     /* timer event */
     if(!no_retransmits)
     {
  DBG(DBG_CONTROL,
      DBG_log(BLANK_FORMAT);
      DBG_log("*time to handle event"));
// 进行相关定时器处理,最常见的超时处理是DPD,另外协商到各阶段都有相关超时处理
  handle_timer_event();
  passert(GLOBALS_ARE_RESET());
     }
 }
 else
 {
     /* at least one file descriptor is ready */
// select成功,至少有一个描述符可读或可写了
// 检查是否ADNS请求描述符可写
     if (adns_qfd != NULL_FD && FD_ISSET(adns_qfd, &writefds))
     {
  passert(ndes > 0);
// 发送ADNS请求
  send_unsent_ADNS_queries();
  passert(GLOBALS_ARE_RESET());
  ndes--;
     }
// 检查是否ADNS回应描述符可读
     if (adns_afd != NULL_FD && FD_ISSET(adns_afd, &readfds))
     {
  passert(ndes > 0);
  DBG(DBG_CONTROL,
      DBG_log(BLANK_FORMAT);
      DBG_log("*received adns message"));
// 处理ADNS回应
  handle_adns_answer();
  passert(GLOBALS_ARE_RESET());
  ndes--;
     }
#ifdef KLIPS
// 检查是否内核接口可读
     if (kern_interface != NO_KERNEL
  && FD_ISSET(*kernel_ops->async_fdp, &readfds))
     {
  passert(ndes > 0);
  DBG(DBG_CONTROL,
      DBG_log(BLANK_FORMAT);
      DBG_log("*received kernel message"));
// 处理内核数据
  kernel_ops->process_msg();
  passert(GLOBALS_ARE_RESET());
  ndes--;
     }
#endif
// 遍历所有可用网卡
     for (ifp = interfaces; ifp != NULL; ifp = ifp->next)
     {
// 检查所监听的UDP套接口是否可读
  if (FD_ISSET(ifp->fd, &readfds))
  {
      /* comm_handle will print DBG_CONTROL intro,
       * with more info than we have here.
       */
      passert(ndes > 0);
// 接收处理数据
      comm_handle(ifp);
      passert(GLOBALS_ARE_RESET());
      ndes--;
  }
     }
// 检查whack控制套接口是否可读
     if (FD_ISSET(ctl_fd, &readfds))
     {
  passert(ndes > 0);
  DBG(DBG_CONTROL,
      DBG_log(BLANK_FORMAT);
      DBG_log("*received whack message"));
// 处理whack命令
  whack_handle(ctl_fd);
  passert(GLOBALS_ARE_RESET());
  ndes--;
     }
#ifdef IPSECPOLICY
// 检查信息套接口是否可读
     if (FD_ISSET(info_fd, &readfds))
     {
  passert(ndes > 0);
  DBG(DBG_CONTROL,
      DBG_log(BLANK_FORMAT);
      DBG_log("*received info message"));
// 处理信息通道命令
  info_handle(info_fd);
  passert(GLOBALS_ARE_RESET());
  ndes--;
     }
#endif
     /* note we process helper things last on purpose */
     ndes -= pluto_crypto_helper_ready(&readfds);
     passert(ndes == 0);
 }
    }
}

...... 待续 ......