17c478bdstevel@tonic-gate/*
27c478bdstevel@tonic-gate * CDDL HEADER START
37c478bdstevel@tonic-gate *
47c478bdstevel@tonic-gate * The contents of this file are subject to the terms of the
5f53eecfJames Carlson * Common Development and Distribution License (the "License").
6f53eecfJames Carlson * You may not use this file except in compliance with the License.
77c478bdstevel@tonic-gate *
87c478bdstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bdstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
107c478bdstevel@tonic-gate * See the License for the specific language governing permissions
117c478bdstevel@tonic-gate * and limitations under the License.
127c478bdstevel@tonic-gate *
137c478bdstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
147c478bdstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bdstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
167c478bdstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
177c478bdstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bdstevel@tonic-gate *
197c478bdstevel@tonic-gate * CDDL HEADER END
207c478bdstevel@tonic-gate */
217c478bdstevel@tonic-gate/*
227c478bdstevel@tonic-gate * PPPoE Server-mode daemon for use with Solaris PPP 4.0.
237c478bdstevel@tonic-gate *
24f53eecfJames Carlson * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
25f53eecfJames Carlson * Use is subject to license terms.
267c478bdstevel@tonic-gate */
277c478bdstevel@tonic-gate
287c478bdstevel@tonic-gate#include <stdio.h>
297c478bdstevel@tonic-gate#include <sys/types.h>
307c478bdstevel@tonic-gate#include <sys/stat.h>
317c478bdstevel@tonic-gate#include <unistd.h>
327c478bdstevel@tonic-gate#include <stdlib.h>
337c478bdstevel@tonic-gate#include <string.h>
347c478bdstevel@tonic-gate#include <fcntl.h>
357c478bdstevel@tonic-gate#include <errno.h>
367c478bdstevel@tonic-gate#include <signal.h>
377c478bdstevel@tonic-gate#include <stropts.h>
387c478bdstevel@tonic-gate#include <wait.h>
397c478bdstevel@tonic-gate#include <sys/resource.h>
407c478bdstevel@tonic-gate#include <netinet/in.h>
417c478bdstevel@tonic-gate#include <net/sppptun.h>
427c478bdstevel@tonic-gate#include <net/pppoe.h>
437c478bdstevel@tonic-gate
447c478bdstevel@tonic-gate#include "common.h"
457c478bdstevel@tonic-gate#include "pppoed.h"
467c478bdstevel@tonic-gate#include "logging.h"
477c478bdstevel@tonic-gate
487c478bdstevel@tonic-gatestatic int tunfd;		/* Global connection to tunnel device */
497c478bdstevel@tonic-gate
507c478bdstevel@tonic-gatechar *myname;			/* Copied from argv[0] for logging */
517c478bdstevel@tonic-gatestatic int main_argc;		/* Saved for reparse on SIGHUP */
527c478bdstevel@tonic-gatestatic char **main_argv;	/* Saved for reparse on SIGHUP */
537c478bdstevel@tonic-gate
547c478bdstevel@tonic-gatestatic time_t time_started;	/* Time daemon was started; for debug */
557c478bdstevel@tonic-gatestatic time_t last_reread;	/* Last time configuration was read. */
567c478bdstevel@tonic-gate
577c478bdstevel@tonic-gate/* Various operational statistics. */
587c478bdstevel@tonic-gatestatic unsigned long input_packets, padi_packets, padr_packets;
597c478bdstevel@tonic-gatestatic unsigned long output_packets;
607c478bdstevel@tonic-gatestatic unsigned long sessions_started;
617c478bdstevel@tonic-gate
627c478bdstevel@tonic-gatestatic sigset_t sigmask;	/* Global signal mask */
637c478bdstevel@tonic-gate
647c478bdstevel@tonic-gate/*
657c478bdstevel@tonic-gate * Used for handling errors that occur before we daemonize.
667c478bdstevel@tonic-gate */
677c478bdstevel@tonic-gatestatic void
687c478bdstevel@tonic-gateearly_error(const char *str)
697c478bdstevel@tonic-gate{
707c478bdstevel@tonic-gate	const char *cp;
717c478bdstevel@tonic-gate
727c478bdstevel@tonic-gate	cp = mystrerror(errno);
737c478bdstevel@tonic-gate	if (isatty(2)) {
747c478bdstevel@tonic-gate		(void) fprintf(stderr, "%s: %s: %s\n", myname, str, cp);
757c478bdstevel@tonic-gate	} else {
767c478bdstevel@tonic-gate		reopen_log();
777c478bdstevel@tonic-gate		logerr("%s: %s", str, cp);
787c478bdstevel@tonic-gate	}
797c478bdstevel@tonic-gate	exit(1);
807c478bdstevel@tonic-gate}
817c478bdstevel@tonic-gate
827c478bdstevel@tonic-gate/*
837c478bdstevel@tonic-gate * Open the sppptun driver.
847c478bdstevel@tonic-gate */
857c478bdstevel@tonic-gatestatic void
867c478bdstevel@tonic-gateopen_tunnel_dev(void)
877c478bdstevel@tonic-gate{
887c478bdstevel@tonic-gate	struct ppptun_peer ptp;
897c478bdstevel@tonic-gate
907c478bdstevel@tonic-gate	tunfd = open(tunnam, O_RDWR);
917c478bdstevel@tonic-gate	if (tunfd == -1) {
927c478bdstevel@tonic-gate		early_error(tunnam);
937c478bdstevel@tonic-gate	}
947c478bdstevel@tonic-gate
957c478bdstevel@tonic-gate	/*
967c478bdstevel@tonic-gate	 * Tell the device driver that I'm a daemon handling inbound
977c478bdstevel@tonic-gate	 * connections, not a PPP session.
987c478bdstevel@tonic-gate	 */
997c478bdstevel@tonic-gate	(void) memset(&ptp, '\0', sizeof (ptp));
1007c478bdstevel@tonic-gate	ptp.ptp_style = PTS_PPPOE;
1017c478bdstevel@tonic-gate	ptp.ptp_flags = PTPF_DAEMON;
1027c478bdstevel@tonic-gate	(void) memcpy(ptp.ptp_address.pta_pppoe.ptma_mac, ether_bcast,
1037c478bdstevel@tonic-gate	    sizeof (ptp.ptp_address.pta_pppoe.ptma_mac));
1047c478bdstevel@tonic-gate	if (strioctl(tunfd, PPPTUN_SPEER, &ptp, sizeof (ptp), sizeof (ptp)) <
1057c478bdstevel@tonic-gate	    0) {
1067c478bdstevel@tonic-gate		myperror("PPPTUN_SPEER");
1077c478bdstevel@tonic-gate		exit(1);
1087c478bdstevel@tonic-gate	}
1097c478bdstevel@tonic-gate}
1107c478bdstevel@tonic-gate
1117c478bdstevel@tonic-gate/*
1127c478bdstevel@tonic-gate * Callback function for fdwalk.  Closes everything but the tunnel
1137c478bdstevel@tonic-gate * file descriptor when becoming daemon.  (Log file must be reopened
1147c478bdstevel@tonic-gate * manually, since syslog file descriptor, if any, is unknown.)
1157c478bdstevel@tonic-gate */
1167c478bdstevel@tonic-gate/*ARGSUSED*/
1177c478bdstevel@tonic-gatestatic int
1187c478bdstevel@tonic-gatefdcloser(void *arg, int fd)
1197c478bdstevel@tonic-gate{
1207c478bdstevel@tonic-gate	if (fd != tunfd)
1217c478bdstevel@tonic-gate		(void) close(fd);
1227c478bdstevel@tonic-gate	return (0);
1237c478bdstevel@tonic-gate}
1247c478bdstevel@tonic-gate
1257c478bdstevel@tonic-gate/*
1267c478bdstevel@tonic-gate * Become a daemon.
1277c478bdstevel@tonic-gate */
1287c478bdstevel@tonic-gatestatic void
1297c478bdstevel@tonic-gatedaemonize(void)
1307c478bdstevel@tonic-gate{
1317c478bdstevel@tonic-gate	pid_t cpid;
1327c478bdstevel@tonic-gate
1337c478bdstevel@tonic-gate	/*
1347c478bdstevel@tonic-gate	 * A little bit of magic here.  By the first fork+setsid, we
1357c478bdstevel@tonic-gate	 * disconnect from our current controlling terminal and become
1367c478bdstevel@tonic-gate	 * a session group leader.  By forking again without setsid,
1377c478bdstevel@tonic-gate	 * we make certain that we're not the session group leader and
1387c478bdstevel@tonic-gate	 * can never reacquire a controlling terminal.
1397c478bdstevel@tonic-gate	 */
1407c478bdstevel@tonic-gate	if ((cpid = fork()) == (pid_t)-1) {
1417c478bdstevel@tonic-gate		early_error("fork 1");
1427c478bdstevel@tonic-gate	}
1437c478bdstevel@tonic-gate	if (cpid != 0) {
1447c478bdstevel@tonic-gate		(void) wait(NULL);
1457c478bdstevel@tonic-gate		_exit(0);
1467c478bdstevel@tonic-gate	}
1477c478bdstevel@tonic-gate	if (setsid() == (pid_t)-1) {
1487c478bdstevel@tonic-gate		early_error("setsid");
1497c478bdstevel@tonic-gate	}
1507c478bdstevel@tonic-gate	if ((cpid = fork()) == (pid_t)-1) {
1517c478bdstevel@tonic-gate		early_error("fork 2");
1527c478bdstevel@tonic-gate	}
1537c478bdstevel@tonic-gate	if (cpid != 0) {
1547c478bdstevel@tonic-gate		/* Parent just exits */
1557c478bdstevel@tonic-gate		(void) printf("%d\n", (int)cpid);
1567c478bdstevel@tonic-gate		(void) fflush(stdout);
1577c478bdstevel@tonic-gate		_exit(0);
1587c478bdstevel@tonic-gate	}
1597c478bdstevel@tonic-gate	(void) chdir("/");
1607c478bdstevel@tonic-gate	(void) umask(0);
1617c478bdstevel@tonic-gate	(void) fdwalk(fdcloser, NULL);
1627c478bdstevel@tonic-gate	reopen_log();
1637c478bdstevel@tonic-gate}
1647c478bdstevel@tonic-gate
1657c478bdstevel@tonic-gate/*
1667c478bdstevel@tonic-gate * Handle SIGHUP -- close and reopen non-syslog log files and reparse
1677c478bdstevel@tonic-gate * options.
1687c478bdstevel@tonic-gate */
1697c478bdstevel@tonic-gate/*ARGSUSED*/
1707c478bdstevel@tonic-gatestatic void
1717c478bdstevel@tonic-gatehandle_hup(int sig)
1727c478bdstevel@tonic-gate{
1737c478bdstevel@tonic-gate	close_log_files();
1747c478bdstevel@tonic-gate	global_logging();
1757c478bdstevel@tonic-gate	last_reread = time(NULL);
1767c478bdstevel@tonic-gate	parse_options(tunfd, main_argc, main_argv);
1777c478bdstevel@tonic-gate}
1787c478bdstevel@tonic-gate
1797c478bdstevel@tonic-gate/*
1807c478bdstevel@tonic-gate * Handle SIGINT -- write current daemon status to /tmp.
1817c478bdstevel@tonic-gate */
1827c478bdstevel@tonic-gate/*ARGSUSED*/
1837c478bdstevel@tonic-gatestatic void
1847c478bdstevel@tonic-gatehandle_int(int sig)
1857c478bdstevel@tonic-gate{
1867c478bdstevel@tonic-gate	FILE *fp;
1877c478bdstevel@tonic-gate	char dumpname[MAXPATHLEN];
1887c478bdstevel@tonic-gate	time_t now;
1897c478bdstevel@tonic-gate	struct rusage rusage;
1907c478bdstevel@tonic-gate
1917c478bdstevel@tonic-gate	(void) snprintf(dumpname, sizeof (dumpname), "/tmp/pppoed.%ld",
1927c478bdstevel@tonic-gate	    getpid());
1937c478bdstevel@tonic-gate	if ((fp = fopen(dumpname, "w+")) == NULL) {
1947c478bdstevel@tonic-gate		logerr("%s: %s", dumpname, mystrerror(errno));
1957c478bdstevel@tonic-gate		return;
1967c478bdstevel@tonic-gate	}
1977c478bdstevel@tonic-gate	now = time(NULL);
1987c478bdstevel@tonic-gate	(void) fprintf(fp, "pppoed running %s", ctime(&now));
1997c478bdstevel@tonic-gate	(void) fprintf(fp, "Started on     %s", ctime(&time_started));
2007c478bdstevel@tonic-gate	if (last_reread != 0)
2017c478bdstevel@tonic-gate		(void) fprintf(fp, "Last reconfig  %s", ctime(&last_reread));
2027c478bdstevel@tonic-gate	(void) putc('\n', fp);
2037c478bdstevel@tonic-gate	if (getrusage(RUSAGE_SELF, &rusage) == 0) {
2047c478bdstevel@tonic-gate		(void) fprintf(fp,
2057c478bdstevel@tonic-gate		    "CPU usage:  user %ld.%06ld, system %ld.%06ld\n",
2067c478bdstevel@tonic-gate		    rusage.ru_utime.tv_sec, rusage.ru_utime.tv_usec,
2077c478bdstevel@tonic-gate		    rusage.ru_stime.tv_sec, rusage.ru_stime.tv_usec);
2087c478bdstevel@tonic-gate	}
2097c478bdstevel@tonic-gate	(void) fprintf(fp, "Packets:  %lu received (%lu PADI, %lu PADR), ",
2107c478bdstevel@tonic-gate	    input_packets, padi_packets, padr_packets);
2117c478bdstevel@tonic-gate	(void) fprintf(fp, "%lu transmitted\n", output_packets);
2127c478bdstevel@tonic-gate	(void) fprintf(fp, "Sessions started:  %lu\n\n", sessions_started);
2137c478bdstevel@tonic-gate	dump_configuration(fp);
2147c478bdstevel@tonic-gate	(void) fclose(fp);
2157c478bdstevel@tonic-gate}
2167c478bdstevel@tonic-gate
2177c478bdstevel@tonic-gatestatic void
2187c478bdstevel@tonic-gateadd_signal_handlers(void)
2197c478bdstevel@tonic-gate{
2207c478bdstevel@tonic-gate	struct sigaction sa;
2217c478bdstevel@tonic-gate
2227c478bdstevel@tonic-gate	(void) sigemptyset(&sigmask);
2237c478bdstevel@tonic-gate	(void) sigaddset(&sigmask, SIGHUP);
2247c478bdstevel@tonic-gate	(void) sigaddset(&sigmask, SIGCHLD);
2257c478bdstevel@tonic-gate	(void) sigaddset(&sigmask, SIGINT);
2267c478bdstevel@tonic-gate	(void) sigprocmask(SIG_BLOCK, &sigmask, NULL);
2277c478bdstevel@tonic-gate
2287c478bdstevel@tonic-gate	sa.sa_mask = sigmask;
2297c478bdstevel@tonic-gate	sa.sa_flags = 0;
2307c478bdstevel@tonic-gate
2317c478bdstevel@tonic-gate	/* Signals to handle */
2327c478bdstevel@tonic-gate	sa.sa_handler = handle_hup;
2337c478bdstevel@tonic-gate	if (sigaction(SIGHUP, &sa, NULL) < 0)
2347c478bdstevel@tonic-gate		early_error("sigaction HUP");
2357c478bdstevel@tonic-gate	sa.sa_handler = handle_int;
2367c478bdstevel@tonic-gate	if (sigaction(SIGINT, &sa, NULL) < 0)
2377c478bdstevel@tonic-gate		early_error("sigaction INT");
2387c478bdstevel@tonic-gate
2397c478bdstevel@tonic-gate	/*
2407c478bdstevel@tonic-gate	 * Signals to ignore.  Ignoring SIGCHLD in this way makes the
2417c478bdstevel@tonic-gate	 * children exit without ever creating zombies.  (No wait(2)
2427c478bdstevel@tonic-gate	 * call required.)
2437c478bdstevel@tonic-gate	 */
2447c478bdstevel@tonic-gate	sa.sa_handler = SIG_IGN;
2457c478bdstevel@tonic-gate	if (sigaction(SIGPIPE, &sa, NULL) < 0)
2467c478bdstevel@tonic-gate		early_error("sigaction PIPE");
2477c478bdstevel@tonic-gate	sa.sa_flags = SA_NOCLDWAIT;
2487c478bdstevel@tonic-gate	if (sigaction(SIGCHLD, &sa, NULL) < 0)
2497c478bdstevel@tonic-gate		early_error("sigaction CHLD");
2507c478bdstevel@tonic-gate}
2517c478bdstevel@tonic-gate
2527c478bdstevel@tonic-gate/*
2537c478bdstevel@tonic-gate * Dispatch a message from the tunnel driver.  It could be an actual
2547c478bdstevel@tonic-gate * PPPoE message or just an event notification.
2557c478bdstevel@tonic-gate */
2567c478bdstevel@tonic-gatestatic void
2577c478bdstevel@tonic-gatehandle_input(uint32_t *ctrlbuf, int ctrllen, uint32_t *databuf, int datalen)
2587c478bdstevel@tonic-gate{
2597c478bdstevel@tonic-gate	poep_t *poep = (poep_t *)databuf;
2607c478bdstevel@tonic-gate	union ppptun_name ptn;
2617c478bdstevel@tonic-gate	int retv;
2627c478bdstevel@tonic-gate	struct strbuf ctrl;
2637c478bdstevel@tonic-gate	struct strbuf data;
2647c478bdstevel@tonic-gate	void *srvp;
2657c478bdstevel@tonic-gate	boolean_t launch;
2667c478bdstevel@tonic-gate	struct ppptun_control *ptc;
2677c478bdstevel@tonic-gate
2687c478bdstevel@tonic-gate	if (ctrllen != sizeof (*ptc)) {
2697c478bdstevel@tonic-gate		logdbg("bogus %d byte control message from driver",
2707c478bdstevel@tonic-gate		    ctrllen);
2717c478bdstevel@tonic-gate		return;
2727c478bdstevel@tonic-gate	}
2737c478bdstevel@tonic-gate	ptc = (struct ppptun_control *)ctrlbuf;
2747c478bdstevel@tonic-gate
2757c478bdstevel@tonic-gate	/* Switch out on event notifications. */
2767c478bdstevel@tonic-gate	switch (ptc->ptc_action) {
2777c478bdstevel@tonic-gate	case PTCA_TEST:
2787c478bdstevel@tonic-gate		logdbg("test reply for discriminator %X", ptc->ptc_discrim);
2797c478bdstevel@tonic-gate		return;
2807c478bdstevel@tonic-gate
2817c478bdstevel@tonic-gate	case PTCA_CONTROL:
2827c478bdstevel@tonic-gate		break;
2837c478bdstevel@tonic-gate
2847c478bdstevel@tonic-gate	case PTCA_DISCONNECT:
2857c478bdstevel@tonic-gate		logdbg("session %d disconnected on %s; send PADT",
2867c478bdstevel@tonic-gate		    ptc->ptc_rsessid, ptc->ptc_name);
2877c478bdstevel@tonic-gate		poep = poe_mkheader(pkt_output, POECODE_PADT,
2887c478bdstevel@tonic-gate		    ptc->ptc_rsessid);
2897c478bdstevel@tonic-gate		ptc->ptc_action = PTCA_CONTROL;
2907c478bdstevel@tonic-gate		ctrl.len = sizeof (*ptc);
2917c478bdstevel@tonic-gate		ctrl.buf = (caddr_t)ptc;
2927c478bdstevel@tonic-gate		data.len = poe_length(poep) + sizeof (*poep);
2937c478bdstevel@tonic-gate		data.buf = (caddr_t)poep;
2947c478bdstevel@tonic-gate		if (putmsg(tunfd, &ctrl, &data, 0) < 0) {
2957c478bdstevel@tonic-gate			logerr("putmsg PADT: %s", mystrerror(errno));
2967c478bdstevel@tonic-gate		} else {
2977c478bdstevel@tonic-gate			output_packets++;
2987c478bdstevel@tonic-gate		}
2997c478bdstevel@tonic-gate		return;
3007c478bdstevel@tonic-gate
3017c478bdstevel@tonic-gate	case PTCA_UNPLUMB:
3027c478bdstevel@tonic-gate		logdbg("%s unplumbed", ptc->ptc_name);
3037c478bdstevel@tonic-gate		return;
3047c478bdstevel@tonic-gate
305f53eecfJames Carlson	case PTCA_BADCTRL:
306f53eecfJames Carlson		logwarn("bad control data on %s for session %u", ptc->ptc_name,
307f53eecfJames Carlson		    ptc->ptc_rsessid);
308f53eecfJames Carlson		return;
309f53eecfJames Carlson
3107c478bdstevel@tonic-gate	default:
3117c478bdstevel@tonic-gate		logdbg("unexpected code %d from driver", ptc->ptc_action);
3127c478bdstevel@tonic-gate		return;
3137c478bdstevel@tonic-gate	}
3147c478bdstevel@tonic-gate
3157c478bdstevel@tonic-gate	/* Only PPPoE control messages get here. */
3167c478bdstevel@tonic-gate
3177c478bdstevel@tonic-gate	input_packets++;
3187c478bdstevel@tonic-gate	if (datalen < sizeof (*poep)) {
3197c478bdstevel@tonic-gate		logdbg("incomplete PPPoE message from %s/%s",
3207c478bdstevel@tonic-gate		    ehost(&ptc->ptc_address), ptc->ptc_name);
3217c478bdstevel@tonic-gate		return;
3227c478bdstevel@tonic-gate	}
3237c478bdstevel@tonic-gate
3247c478bdstevel@tonic-gate	/* Server handles only PADI and PADR; all others are ignored. */
3257c478bdstevel@tonic-gate	if (poep->poep_code == POECODE_PADI) {
3267c478bdstevel@tonic-gate		padi_packets++;
3277c478bdstevel@tonic-gate	} else if (poep->poep_code == POECODE_PADR) {
3287c478bdstevel@tonic-gate		padr_packets++;
3297c478bdstevel@tonic-gate	} else {
3307c478bdstevel@tonic-gate		loginfo("unexpected %s from %s",
3317c478bdstevel@tonic-gate		    poe_codename(poep->poep_code), ehost(&ptc->ptc_address));
3327c478bdstevel@tonic-gate		return;
3337c478bdstevel@tonic-gate	}
3347c478bdstevel@tonic-gate	logdbg("Recv from %s/%s: %s", ehost(&ptc->ptc_address), ptc->ptc_name,
3357c478bdstevel@tonic-gate	    poe_codename(poep->poep_code));
3367c478bdstevel@tonic-gate
3377c478bdstevel@tonic-gate	/* Parse out service and formulate template reply. */
3387c478bdstevel@tonic-gate	retv = locate_service(poep, datalen, ptc->ptc_name, &ptc->ptc_address,
3397c478bdstevel@tonic-gate	    pkt_output, &srvp);
3407c478bdstevel@tonic-gate
3417c478bdstevel@tonic-gate	/* Continue formulating reply */
3427c478bdstevel@tonic-gate	launch = B_FALSE;
3437c478bdstevel@tonic-gate	if (retv != 1) {
3447c478bdstevel@tonic-gate		/* Ignore initiation if we don't offer a service. */
3457c478bdstevel@tonic-gate		if (retv <= 0 && poep->poep_code == POECODE_PADI) {
3467c478bdstevel@tonic-gate			logdbg("no services; no reply");
3477c478bdstevel@tonic-gate			return;
3487c478bdstevel@tonic-gate		}
3497c478bdstevel@tonic-gate		if (retv == 0)
3507c478bdstevel@tonic-gate			(void) poe_add_str((poep_t *)pkt_output, POETT_NAMERR,
3517c478bdstevel@tonic-gate			    "No such service.");
3527c478bdstevel@tonic-gate	} else {
3537c478bdstevel@tonic-gate		/* Exactly one service chosen; if it's PADR, then we start. */
3547c478bdstevel@tonic-gate		if (poep->poep_code == POECODE_PADR) {
3557c478bdstevel@tonic-gate			launch = B_TRUE;
3567c478bdstevel@tonic-gate		}
3577c478bdstevel@tonic-gate	}
3587c478bdstevel@tonic-gate	poep = (poep_t *)pkt_output;
3597c478bdstevel@tonic-gate
3607c478bdstevel@tonic-gate	/* Select control interface for output. */
3617c478bdstevel@tonic-gate	(void) strncpy(ptn.ptn_name, ptc->ptc_name, sizeof (ptn.ptn_name));
3627c478bdstevel@tonic-gate	if (strioctl(tunfd, PPPTUN_SCTL, &ptn, sizeof (ptn), 0) < 0) {
3637c478bdstevel@tonic-gate		logerr("PPPTUN_SCTL %s: %s", ptn.ptn_name, mystrerror(errno));
3647c478bdstevel@tonic-gate		return;
3657c478bdstevel@tonic-gate	}
3667c478bdstevel@tonic-gate
3677c478bdstevel@tonic-gate	/* Launch the PPP service */
3687c478bdstevel@tonic-gate	if (launch && launch_service(tunfd, poep, srvp, ptc))
3697c478bdstevel@tonic-gate		sessions_started++;
3707c478bdstevel@tonic-gate
3717c478bdstevel@tonic-gate	/* Send the reply. */
3727c478bdstevel@tonic-gate	ctrl.len = sizeof (*ptc);
3737c478bdstevel@tonic-gate	ctrl.buf = (caddr_t)ptc;
3747c478bdstevel@tonic-gate	data.len = poe_length(poep) + sizeof (*poep);
3757c478bdstevel@tonic-gate	data.buf = (caddr_t)poep;
3767c478bdstevel@tonic-gate	if (putmsg(tunfd, &ctrl, &data, 0) < 0) {
3777c478bdstevel@tonic-gate		logerr("putmsg %s: %s", ptc->ptc_name, mystrerror(errno));
3787c478bdstevel@tonic-gate	} else {
3797c478bdstevel@tonic-gate		output_packets++;
3807c478bdstevel@tonic-gate		logdbg("Send to   %s/%s: %s", ehost(&ptc->ptc_address),
3817c478bdstevel@tonic-gate		    ptc->ptc_name, poe_codename(poep->poep_code));
3827c478bdstevel@tonic-gate	}
3837c478bdstevel@tonic-gate}
3847c478bdstevel@tonic-gate
3857c478bdstevel@tonic-gatestatic void
3867c478bdstevel@tonic-gatemain_loop(void)
3877c478bdstevel@tonic-gate{
3887c478bdstevel@tonic-gate	struct strbuf ctrl;
3897c478bdstevel@tonic-gate	struct strbuf data;
3907c478bdstevel@tonic-gate	int flags;
3917c478bdstevel@tonic-gate	int rc;
3927c478bdstevel@tonic-gate	int err;
3937c478bdstevel@tonic-gate
3947c478bdstevel@tonic-gate	for (;;) {
3957c478bdstevel@tonic-gate		ctrl.maxlen = PKT_OCTL_LEN;
3967c478bdstevel@tonic-gate		ctrl.buf = (caddr_t)pkt_octl;
3977c478bdstevel@tonic-gate		data.maxlen = PKT_INPUT_LEN;
3987c478bdstevel@tonic-gate		data.buf = (caddr_t)pkt_input;
3997c478bdstevel@tonic-gate		/* Allow signals only while idle */
4007c478bdstevel@tonic-gate		(void) sigprocmask(SIG_UNBLOCK, &sigmask, NULL);
4017c478bdstevel@tonic-gate		errno = 0;
4027c478bdstevel@tonic-gate		flags = 0;
4037c478bdstevel@tonic-gate		rc = mygetmsg(tunfd, &ctrl, &data, &flags);
4047c478bdstevel@tonic-gate		err = errno;
4057c478bdstevel@tonic-gate		/*
4067c478bdstevel@tonic-gate		 * Block signals -- data structures must not change
4077c478bdstevel@tonic-gate		 * while we're busy dispatching the client's request
4087c478bdstevel@tonic-gate		 */
4097c478bdstevel@tonic-gate		(void) sigprocmask(SIG_BLOCK, &sigmask, NULL);
4107c478bdstevel@tonic-gate		if (rc == -1) {
4117c478bdstevel@tonic-gate			if (err == EAGAIN || err == EINTR)
4127c478bdstevel@tonic-gate				continue;
4137c478bdstevel@tonic-gate			logerr("%s getmsg: %s", tunnam, mystrerror(err));
4147c478bdstevel@tonic-gate			exit(1);
4157c478bdstevel@tonic-gate		}
4167c478bdstevel@tonic-gate		if (rc > 0)
4177c478bdstevel@tonic-gate			logwarn("%s returned truncated data", tunnam);
4187c478bdstevel@tonic-gate		else
4197c478bdstevel@tonic-gate			handle_input(pkt_octl, ctrl.len, pkt_input, data.len);
4207c478bdstevel@tonic-gate	}
4217c478bdstevel@tonic-gate}
4227c478bdstevel@tonic-gate
4237c478bdstevel@tonic-gateint
4247c478bdstevel@tonic-gatemain(int argc, char **argv)
4257c478bdstevel@tonic-gate{
4267c478bdstevel@tonic-gate	prog_name = "pppoed";
4277c478bdstevel@tonic-gate	log_level = 1;		/* Default to error messages only at first */
4287c478bdstevel@tonic-gate
4297c478bdstevel@tonic-gate	time_started = time(NULL);
4307c478bdstevel@tonic-gate
4317c478bdstevel@tonic-gate	if ((myname = argv[0]) == NULL)
4327c478bdstevel@tonic-gate		myname = "pppoed";
4337c478bdstevel@tonic-gate
4347c478bdstevel@tonic-gate	main_argc = argc;
4357c478bdstevel@tonic-gate	main_argv = argv;
4367c478bdstevel@tonic-gate
4377c478bdstevel@tonic-gate	open_tunnel_dev();
4387c478bdstevel@tonic-gate	add_signal_handlers();
4397c478bdstevel@tonic-gate	daemonize();
4407c478bdstevel@tonic-gate
4417c478bdstevel@tonic-gate	parse_options(tunfd, argc, argv);
4427c478bdstevel@tonic-gate	main_loop();
4437c478bdstevel@tonic-gate
4447c478bdstevel@tonic-gate	return (0);
4457c478bdstevel@tonic-gate}
446