dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / vscan / vscand / vs_main.c
blob4824a35d8ce5a88e0755109d57fef22131211d66
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * vscand Daemon Program
30 #include <stdio.h>
31 #include <sys/stat.h>
32 #include <sys/filio.h>
33 #include <sys/types.h>
34 #include <sys/socket.h>
35 #include <sys/ioctl.h>
36 #include <sys/param.h>
37 #include <zone.h>
38 #include <string.h>
39 #include <stdlib.h>
40 #include <fcntl.h>
41 #include <wait.h>
42 #include <unistd.h>
43 #include <getopt.h>
44 #include <stdarg.h>
45 #include <libscf.h>
46 #include <signal.h>
47 #include <atomic.h>
48 #include <libintl.h>
49 #include <netinet/in.h>
50 #include <arpa/inet.h>
51 #include <ctype.h>
52 #include <pthread.h>
53 #include <syslog.h>
54 #include <locale.h>
55 #include <pwd.h>
56 #include <grp.h>
57 #include <priv_utils.h>
58 #include <rctl.h>
59 #include "vs_incl.h"
61 #define VS_FILE_DESCRIPTORS 512
63 static int vscand_fg = 0; /* daemon by default */
64 static vs_daemon_state_t vscand_state = VS_STATE_INIT;
65 static volatile uint_t vscand_sigval = 0;
66 static volatile uint_t vscand_n_refresh = 0;
67 static int vscand_kdrv_fd = -1;
68 static pthread_mutex_t vscand_cfg_mutex = PTHREAD_MUTEX_INITIALIZER;
69 static pthread_cond_t vscand_cfg_cv;
70 static pthread_t vscand_cfg_tid = 0;
72 /* virus log path */
73 static char vscand_vlog[MAXPATHLEN];
75 /* user and group ids - default to 0 */
76 static uid_t root_uid = 0, daemon_uid = 0;
77 static gid_t sys_gid = 0;
80 /* local function prototypes */
81 static void vscand_sig_handler(int);
82 static int vscand_parse_args(int, char **);
83 static void vscand_get_uid_gid();
84 static int vscand_init_file(char *, uid_t, gid_t, mode_t);
85 static void vscand_usage(char *);
86 static int vscand_daemonize_init(void);
87 static void vscand_daemonize_fini(int, int);
88 static int vscand_init(void);
89 static void vscand_fini(void);
90 static int vscand_cfg_init(void);
91 static void vscand_cfg_fini(void);
92 static void *vscand_cfg_handler(void *);
93 static int vscand_configure(void);
94 static void vscand_dtrace_cfg(vs_props_all_t *);
95 static int vscand_kernel_bind(void);
96 static void vscand_kernel_unbind(void);
97 static int vscand_kernel_enable(int);
98 static void vscand_kernel_disable(void);
99 static int vscand_kernel_config(vs_config_t *);
100 static int vscand_kernel_max_req(uint32_t *);
101 static void vscand_error(const char *);
102 static int vscand_get_viruslog(void);
103 static int vscand_set_resource_limits(void);
107 * Enable libumem debugging by default on DEBUG builds.
109 #ifdef DEBUG
110 const char *
111 _umem_debug_init(void)
113 return ("default,verbose"); /* $UMEM_DEBUG setting */
116 const char *
117 _umem_logging_init(void)
119 return ("fail,contents"); /* $UMEM_LOGGING setting */
121 #endif
125 * vscand_sig_handler
127 static void
128 vscand_sig_handler(int sig)
130 if (vscand_sigval == 0)
131 (void) atomic_swap_uint(&vscand_sigval, sig);
133 if (sig == SIGHUP)
134 atomic_inc_uint(&vscand_n_refresh);
139 * main
141 * main must return SMF return code (see smf_method (5)) if vscand
142 * is invoked directly by smf (see manifest: vscan.xml)
143 * Exit codes: SMF_EXIT_ERR_CONFIG - error
144 * SMF_EXIT_ERR_FATAL - fatal error
145 * SMF_EXIT_OK - success
148 main(int argc, char **argv)
150 int err_stat = 0, pfd = -1;
151 sigset_t set;
152 struct sigaction act;
153 int sigval;
155 mode_t log_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
156 mode_t door_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
158 (void) setlocale(LC_ALL, "");
159 openlog("vscand", 0, LOG_DAEMON);
161 /* check if running in global zone; other zones not supported */
162 if (getzoneid() != GLOBAL_ZONEID) {
163 vscand_error(gettext("non-global zone not supported"));
164 exit(SMF_EXIT_ERR_FATAL);
167 /* Parse arguments */
168 if (vscand_parse_args(argc, argv) != 0)
169 exit(SMF_EXIT_ERR_CONFIG);
171 vscand_get_uid_gid();
174 * Initializetion of virus log and statistic door file
175 * MUST be done BEFORE vscand_daemonize_init resets uid/gid.
176 * Only root can create the files in /var/log and /var/run.
178 if ((vscand_get_viruslog() != 0) ||
179 (vscand_vlog[0] == '\0') ||
180 (vscand_init_file(vscand_vlog, root_uid, sys_gid, log_mode) != 0)) {
181 *vscand_vlog = 0;
184 (void) vscand_init_file(VS_STATS_DOOR_NAME,
185 daemon_uid, sys_gid, door_mode);
188 * Once we're done setting our global state up, set up signal handlers
189 * for ensuring orderly termination on SIGTERM.
191 (void) sigfillset(&set);
192 (void) sigdelset(&set, SIGABRT); /* always unblocked for ASSERT() */
194 (void) sigfillset(&act.sa_mask);
195 act.sa_handler = vscand_sig_handler;
196 act.sa_flags = 0;
198 (void) sigaction(SIGTERM, &act, NULL);
199 (void) sigaction(SIGHUP, &act, NULL); /* Refresh config */
200 (void) sigaction(SIGINT, &act, NULL);
201 (void) sigaction(SIGPIPE, &act, NULL);
202 (void) sigdelset(&set, SIGTERM);
203 (void) sigdelset(&set, SIGHUP);
204 (void) sigdelset(&set, SIGINT);
205 (void) sigdelset(&set, SIGPIPE);
207 if (vscand_fg) {
208 (void) sigdelset(&set, SIGTSTP);
209 (void) sigdelset(&set, SIGTTIN);
210 (void) sigdelset(&set, SIGTTOU);
212 if (vscand_init() != 0) {
213 vscand_error(gettext("failed to initialize service"));
214 exit(SMF_EXIT_ERR_CONFIG);
216 } else {
218 * "pfd" is a pipe descriptor -- any fatal errors
219 * during subsequent initialization of the child
220 * process should be written to this pipe and the
221 * parent will report this error as the exit status.
223 pfd = vscand_daemonize_init();
225 if (vscand_init() != 0) {
226 vscand_error(gettext("failed to initialize service"));
227 exit(SMF_EXIT_ERR_CONFIG);
230 vscand_daemonize_fini(pfd, err_stat);
233 vscand_state = VS_STATE_RUNNING;
235 /* Wait here until shutdown */
236 while (vscand_state == VS_STATE_RUNNING) {
237 if (vscand_sigval == 0 && vscand_n_refresh == 0)
238 (void) sigsuspend(&set);
240 sigval = atomic_swap_uint(&vscand_sigval, 0);
242 switch (sigval) {
243 case 0:
244 case SIGPIPE:
245 case SIGHUP:
246 break;
247 default:
248 vscand_state = VS_STATE_SHUTDOWN;
249 break;
252 if (atomic_swap_uint(&vscand_n_refresh, 0) != 0)
253 (void) pthread_cond_signal(&vscand_cfg_cv);
256 vscand_fini();
257 return (SMF_EXIT_OK);
262 * vscand_parse_args
263 * Routine to parse the arguments to the daemon program
264 * 'f' argument runs process in the foreground instead of as a daemon
267 vscand_parse_args(int argc, char **argv)
269 int optchar;
271 while ((optchar = getopt(argc, argv, "f?")) != EOF) {
272 switch (optchar) {
273 case 'f':
274 vscand_fg = 1;
275 break;
276 default:
277 vscand_usage(argv[0]);
278 return (-1);
281 return (0);
286 * vscand_usage
288 static void
289 vscand_usage(char *progname)
291 char buf[128];
293 (void) snprintf(buf, sizeof (buf), "%s %s [-f]",
294 gettext("Usage"), progname);
295 vscand_error(buf);
297 (void) snprintf(buf, sizeof (buf), "\t-f %s\n",
298 gettext("run program in foreground"));
299 vscand_error(buf);
304 * vscand_get_uid_gid
306 * failure to access a uid/gid results in the default (0) being used.
308 static void
309 vscand_get_uid_gid()
311 struct passwd *pwd;
312 struct group *grp;
314 if ((pwd = getpwnam("root")) != NULL)
315 root_uid = pwd->pw_uid;
317 if ((pwd = getpwnam("daemon")) != NULL)
318 daemon_uid = pwd->pw_uid;
320 if ((grp = getgrnam("sys")) != NULL)
321 sys_gid = grp->gr_gid;
326 * vscand_daemonize_init
328 * This function will fork off a child process, from which
329 * only the child will return.
331 static int
332 vscand_daemonize_init(void)
334 int status, pfds[2];
335 sigset_t set, oset;
336 pid_t pid;
339 * Reset process owner/group to daemon/sys. Root ownership is only
340 * required to initialize virus log file in /var/log
342 if (__init_daemon_priv(PU_RESETGROUPS | PU_LIMITPRIVS,
343 daemon_uid, sys_gid,
344 PRIV_PROC_AUDIT, PRIV_FILE_DAC_SEARCH, PRIV_FILE_DAC_READ,
345 PRIV_FILE_FLAG_SET, NULL) != 0) {
346 vscand_error(gettext("failed to initialize privileges"));
347 _exit(SMF_EXIT_ERR_FATAL);
351 * Block all signals prior to the fork and leave them blocked in the
352 * parent so we don't get in a situation where the parent gets SIGINT
353 * and returns non-zero exit status and the child is actually running.
354 * In the child, restore the signal mask once we've done our setsid().
356 (void) sigfillset(&set);
357 (void) sigdelset(&set, SIGABRT);
358 (void) sigprocmask(SIG_BLOCK, &set, &oset);
360 if (pipe(pfds) == -1) {
361 vscand_error(gettext("failed to create pipe for daemonize"));
362 _exit(SMF_EXIT_ERR_FATAL);
365 if ((pid = fork()) == -1) {
366 vscand_error(gettext("failed to fork for daemonize"));
367 _exit(SMF_EXIT_ERR_FATAL);
371 * If we're the parent process, wait for either the child to send us
372 * the appropriate exit status over the pipe or for the read to fail
373 * (presumably with 0 for EOF if our child terminated abnormally).
374 * If the read fails, exit with either the child's exit status if it
375 * exited or with SMF_EXIT_ERR_FATAL if it died from a fatal signal.
377 if (pid != 0) {
378 (void) close(pfds[1]);
380 if (read(pfds[0], &status, sizeof (status)) == sizeof (status))
381 _exit(status);
383 if (waitpid(pid, &status, 0) == pid && WIFEXITED(status))
384 _exit(WEXITSTATUS(status));
386 vscand_error(gettext("failed to daemonize"));
387 _exit(SMF_EXIT_ERR_FATAL);
391 (void) setsid();
392 (void) sigprocmask(SIG_SETMASK, &oset, NULL);
393 (void) chdir("/");
394 (void) umask(022);
395 (void) close(pfds[0]);
397 return (pfds[1]);
402 * vscand_daemonize_fini
403 * Now that we're running, if a pipe fd was specified, write an exit
404 * status to it to indicate that our parent process can safely detach.
406 static void
407 vscand_daemonize_fini(int fd, int err_status)
409 if (fd >= 0)
410 (void) write(fd, &err_status, sizeof (err_status));
412 (void) close(fd);
414 /* Restore standard file descriptors */
415 if ((fd = open("/dev/null", O_RDWR)) >= 0) {
416 (void) fcntl(fd, F_DUP2FD, STDIN_FILENO);
417 (void) fcntl(fd, F_DUP2FD, STDOUT_FILENO);
418 (void) fcntl(fd, F_DUP2FD, STDERR_FILENO);
419 (void) close(fd);
422 /* clear basic privileges not required by vscand */
423 __fini_daemon_priv(PRIV_PROC_FORK, PRIV_PROC_EXEC, PRIV_PROC_SESSION,
424 PRIV_FILE_LINK_ANY, PRIV_PROC_INFO, NULL);
429 * vscand_init_file
431 * create specified file and set its uid, gid and mode
433 static int
434 vscand_init_file(char *filepath, uid_t uid, gid_t gid, mode_t access_mode)
436 int fd, rc = 0;
437 struct stat stat_buf;
438 char buf[MAXPATHLEN];
440 if ((fd = open(filepath, O_RDONLY | O_CREAT, access_mode)) == -1) {
441 rc = -1;
442 } else {
443 if (fstat(fd, &stat_buf) != 0) {
444 rc = -1;
445 } else {
446 if ((stat_buf.st_mode & S_IAMB) != access_mode) {
447 if (fchmod(fd, access_mode) != 0)
448 rc = -1;
451 if ((stat_buf.st_uid != uid) ||
452 (stat_buf.st_gid != gid)) {
453 if (fchown(fd, uid, gid) != 0)
454 rc = -1;
458 (void) close(fd);
461 if (rc == -1) {
462 (void) snprintf(buf, MAXPATHLEN, "%s %s",
463 gettext("Failed to initialize"), filepath);
464 vscand_error(buf);
467 return (rc);
472 * vscand_init
474 * There are some requirements on the order in which the daemon
475 * initialization functions are called.
477 * - vscand_kernel_bind - bind to kernel module
478 * - vs_eng_init populates vs_icap data and thus vs_icap_init MUST be
479 * called before vs_eng_init
480 * - vscand_configure - load the configuration
481 * - vs_door_init - start vscan door server
482 * - vscand_kernel_enable - enable scan requests from kernel
484 static int
485 vscand_init(void)
487 int door_fd = -1;
488 uint32_t max_req;
490 if (vscand_kernel_bind() < 0)
491 return (-1);
493 if (vscand_kernel_max_req(&max_req) == -1)
494 return (-1);
496 if (vs_svc_init(max_req) != 0)
497 return (-1);
499 if (vs_stats_init() != 0)
500 vscand_error(
501 gettext("failed to initialize statistics interface"));
503 vs_icap_init();
504 vs_eng_init();
506 /* initialize configuration and handler thread */
507 if (vscand_cfg_init() != 0) {
508 vscand_error(gettext("failed to initialize configuration"));
509 vscand_fini();
510 return (-1);
513 (void) vscand_set_resource_limits();
515 if (((door_fd = vs_door_init()) < 0) ||
516 (vscand_kernel_enable(door_fd) < 0)) {
517 vscand_fini();
518 return (-1);
521 return (0);
526 * vscand_fini
528 * vscand_kernel_disable - should be called first to ensure that no
529 * more scan requests are initiated from the kernel module
530 * vs_svc_terminate - terminate requests and wait for thread completion
531 * vs_xxx_fini - module cleanup routines
532 * vscand_kernel_unbind - should be called last to tell the kernel module
533 * that vscand is shutdown.
535 static void
536 vscand_fini(void)
538 vscand_kernel_disable();
540 /* terminate reconfiguration handler thread */
541 vscand_cfg_fini();
543 /* terminate requests and wait for completion */
544 vs_svc_terminate();
546 /* clean up */
547 vs_svc_fini();
548 vs_eng_fini();
549 vs_icap_fini();
550 vs_door_fini();
551 vs_stats_fini();
553 vscand_kernel_unbind();
558 * vscand_cfg_init
560 * initialize configuration and reconfiguration handler thread
562 static int
563 vscand_cfg_init(void)
565 int rc;
567 (void) pthread_cond_init(&vscand_cfg_cv, NULL);
569 (void) pthread_mutex_lock(&vscand_cfg_mutex);
570 rc = vscand_configure();
571 (void) pthread_mutex_unlock(&vscand_cfg_mutex);
573 if (rc != 0)
574 return (-1);
576 if (pthread_create(&vscand_cfg_tid, NULL, vscand_cfg_handler, 0) != 0) {
577 vscand_cfg_tid = 0;
578 return (-1);
581 return (0);
586 * vscand_cfg_fini
588 * terminate reconfiguration handler thread
590 static void
591 vscand_cfg_fini()
593 if (vscand_cfg_tid != 0) {
594 (void) pthread_cond_signal(&vscand_cfg_cv);
595 (void) pthread_join(vscand_cfg_tid, NULL);
596 vscand_cfg_tid = 0;
598 (void) pthread_cond_destroy(&vscand_cfg_cv);
603 * vscand_cfg_handler
604 * wait for reconfiguration event and reload configuration
605 * exit on VS_STATE_SHUTDOWN
607 /*ARGSUSED*/
608 static void *
609 vscand_cfg_handler(void *arg)
611 (void) pthread_mutex_lock(&vscand_cfg_mutex);
613 while (pthread_cond_wait(&vscand_cfg_cv, &vscand_cfg_mutex) == 0) {
614 if (vscand_state == VS_STATE_SHUTDOWN)
615 break;
617 (void) vscand_configure();
620 (void) pthread_mutex_unlock(&vscand_cfg_mutex);
622 return (NULL);
627 * vscand_configure
629 static int
630 vscand_configure(void)
632 uint32_t len;
633 vs_config_t kconfig;
634 vs_props_all_t config;
636 (void) memset(&config, 0, sizeof (vs_props_all_t));
637 if (vs_props_get_all(&config) != VS_ERR_NONE) {
638 vscand_error(gettext("configuration data error"));
639 return (-1);
642 (void) memset(&kconfig, 0, sizeof (vs_config_t));
643 len = sizeof (kconfig.vsc_types);
644 if (vs_parse_types(config.va_props.vp_types,
645 kconfig.vsc_types, &len) != 0) {
646 vscand_error(gettext("configuration data error - types"));
647 return (-1);
649 kconfig.vsc_types_len = len;
651 /* Convert the maxfsize string from the configuration into bytes */
652 if (vs_strtonum(config.va_props.vp_maxsize,
653 &kconfig.vsc_max_size) != 0) {
654 vscand_error(gettext("configuration data error - max-size"));
655 return (-1);
657 kconfig.vsc_allow = config.va_props.vp_maxsize_action ? 1LL : 0LL;
659 /* Send configuration update to kernel */
660 if (vscand_kernel_config(&kconfig) != 0) {
661 return (-1);
664 /* dtrace the configuration data */
665 vscand_dtrace_cfg(&config);
667 /* propagate configuration changes */
668 vs_eng_config(&config);
669 vs_stats_config(&config);
671 return (0);
676 * vscand_get_state
678 vs_daemon_state_t
679 vscand_get_state(void)
681 return (vscand_state);
686 * vscand_get_viruslog
688 static int
689 vscand_get_viruslog()
691 vs_props_t props;
692 uint64_t propids;
693 int rc;
695 propids = VS_PROPID_VLOG;
696 if ((rc = vs_props_get(&props, propids)) != VS_ERR_NONE) {
697 vscand_error(vs_strerror(rc));
698 return (-1);
701 (void) strlcpy(vscand_vlog, props.vp_vlog, sizeof (vscand_vlog));
702 return (0);
707 * vscand_viruslog
709 char *
710 vscand_viruslog(void)
712 if (vscand_vlog[0] == '\0')
713 return (NULL);
715 return (vscand_vlog);
720 * vscand_kernel_bind
722 static int
723 vscand_kernel_bind(void)
725 char devname[MAXPATHLEN];
726 int inst = 0;
728 (void) snprintf(devname, MAXPATHLEN, "%s%d", VS_DRV_PATH, inst);
730 if ((vscand_kdrv_fd = open(devname, O_RDONLY)) < 0) {
731 vscand_error(gettext("failed to bind to kernel"));
732 return (-1);
735 return (0);
740 * vscand_kernel_unbind
742 static void
743 vscand_kernel_unbind(void)
745 if (vscand_kdrv_fd >= 0)
746 (void) close(vscand_kdrv_fd);
751 * vscand_kernel_enable
753 static int
754 vscand_kernel_enable(int door_fd)
756 if (ioctl(vscand_kdrv_fd, VS_IOCTL_ENABLE, door_fd) < 0) {
757 vscand_error(gettext("failed to bind to kernel"));
758 (void) close(vscand_kdrv_fd);
759 vscand_kdrv_fd = -1;
760 return (-1);
762 return (0);
767 * vscand_kernel_disable
769 static void
770 vscand_kernel_disable()
772 if (vscand_kdrv_fd >= 0)
773 (void) ioctl(vscand_kdrv_fd, VS_IOCTL_DISABLE);
778 * vscand_kernel_config
781 vscand_kernel_config(vs_config_t *conf)
783 if ((vscand_kdrv_fd < 0) ||
784 (ioctl(vscand_kdrv_fd, VS_IOCTL_CONFIG, conf) < 0)) {
785 vscand_error(gettext("failed to send config to kernel"));
786 return (-1);
789 return (0);
794 * vscand_kernel_result
797 vscand_kernel_result(vs_scan_rsp_t *scan_rsp)
799 if ((vscand_kdrv_fd < 0) ||
800 (ioctl(vscand_kdrv_fd, VS_IOCTL_RESULT, scan_rsp) < 0)) {
801 vscand_error(gettext("failed to send result to kernel"));
802 return (-1);
805 return (0);
810 * vscand_kernel_max_req
813 vscand_kernel_max_req(uint32_t *max_req)
815 if ((vscand_kdrv_fd < 0) ||
816 (ioctl(vscand_kdrv_fd, VS_IOCTL_MAX_REQ, max_req) < 0)) {
817 vscand_error(gettext("failed to get config data from kernel"));
818 return (-1);
821 return (0);
826 * vscand_set_resource_limits
828 * If the process's max file descriptor limit is less than
829 * VS_FILE_DESCRIPTORS, increae it to VS_FILE_DESCRIPTORS.
831 static int
832 vscand_set_resource_limits(void)
834 int rc = -1;
835 rctlblk_t *rblk;
836 char *limit = "process.max-file-descriptor";
838 rblk = (rctlblk_t *)malloc(rctlblk_size());
840 if (rblk != NULL) {
841 rc = getrctl(limit, NULL, rblk, 0);
842 if ((rc == 0) &&
843 (rctlblk_get_value(rblk) < VS_FILE_DESCRIPTORS)) {
844 rctlblk_set_value(rblk, VS_FILE_DESCRIPTORS);
845 rc = setrctl(limit, NULL, rblk, 0);
847 (void) free(rblk);
850 return (rc);
855 * vscand_error
857 static void
858 vscand_error(const char *errmsg)
860 (void) fprintf(stderr, "vscand: %s", errmsg);
861 syslog(LOG_ERR, "%s\n", errmsg);
866 * vscand_dtrace_cfg
867 * vscand_dtrace_gen
868 * vscand_dtrace_eng
870 * Support for dtracing vscand configuration when processing
871 * a reconfiguration event (SIGHUP)
873 /*ARGSUSED*/
874 static void
875 vscand_dtrace_eng(char *id, boolean_t enable, char *host, int port, int conn)
878 /*ARGSUSED*/
879 static void
880 vscand_dtrace_gen(char *size, boolean_t action, char *types, char *log)
883 static void
884 vscand_dtrace_cfg(vs_props_all_t *config)
886 int i;
888 vscand_dtrace_gen(config->va_props.vp_maxsize,
889 config->va_props.vp_maxsize_action,
890 config->va_props.vp_types,
891 config->va_props.vp_vlog);
893 for (i = 0; i < VS_SE_MAX; i++) {
894 if (config->va_se[i].vep_engid[0] != 0)
895 vscand_dtrace_eng(config->va_se[i].vep_engid,
896 config->va_se[i].vep_enable,
897 config->va_se[i].vep_host,
898 config->va_se[i].vep_port,
899 config->va_se[i].vep_maxconn);