1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * PPPoE Server-mode daemon for use with Solaris PPP 4.0.
24  *
25  * Copyright (c) 2000-2001 by Sun Microsystems, Inc.
26  * All rights reserved.
27  */
28 
29 #pragma ident	"%Z%%M%	%I%	%E% SMI"
30 
31 #include <stdio.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <unistd.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <fcntl.h>
38 #include <errno.h>
39 #include <signal.h>
40 #include <stropts.h>
41 #include <wait.h>
42 #include <sys/resource.h>
43 #include <netinet/in.h>
44 #include <net/sppptun.h>
45 #include <net/pppoe.h>
46 
47 #include "common.h"
48 #include "pppoed.h"
49 #include "logging.h"
50 
51 static int tunfd;		/* Global connection to tunnel device */
52 
53 char *myname;			/* Copied from argv[0] for logging */
54 static int main_argc;		/* Saved for reparse on SIGHUP */
55 static char **main_argv;	/* Saved for reparse on SIGHUP */
56 
57 static time_t time_started;	/* Time daemon was started; for debug */
58 static time_t last_reread;	/* Last time configuration was read. */
59 
60 /* Various operational statistics. */
61 static unsigned long input_packets, padi_packets, padr_packets;
62 static unsigned long output_packets;
63 static unsigned long sessions_started;
64 
65 static sigset_t sigmask;	/* Global signal mask */
66 
67 /*
68  * Used for handling errors that occur before we daemonize.
69  */
70 static void
71 early_error(const char *str)
72 {
73 	const char *cp;
74 
75 	cp = mystrerror(errno);
76 	if (isatty(2)) {
77 		(void) fprintf(stderr, "%s: %s: %s\n", myname, str, cp);
78 	} else {
79 		reopen_log();
80 		logerr("%s: %s", str, cp);
81 	}
82 	exit(1);
83 }
84 
85 /*
86  * Open the sppptun driver.
87  */
88 static void
89 open_tunnel_dev(void)
90 {
91 	struct ppptun_peer ptp;
92 
93 	tunfd = open(tunnam, O_RDWR);
94 	if (tunfd == -1) {
95 		early_error(tunnam);
96 	}
97 
98 	/*
99 	 * Tell the device driver that I'm a daemon handling inbound
100 	 * connections, not a PPP session.
101 	 */
102 	(void) memset(&ptp, '\0', sizeof (ptp));
103 	ptp.ptp_style = PTS_PPPOE;
104 	ptp.ptp_flags = PTPF_DAEMON;
105 	(void) memcpy(ptp.ptp_address.pta_pppoe.ptma_mac, ether_bcast,
106 	    sizeof (ptp.ptp_address.pta_pppoe.ptma_mac));
107 	if (strioctl(tunfd, PPPTUN_SPEER, &ptp, sizeof (ptp), sizeof (ptp)) <
108 	    0) {
109 		myperror("PPPTUN_SPEER");
110 		exit(1);
111 	}
112 }
113 
114 /*
115  * Callback function for fdwalk.  Closes everything but the tunnel
116  * file descriptor when becoming daemon.  (Log file must be reopened
117  * manually, since syslog file descriptor, if any, is unknown.)
118  */
119 /*ARGSUSED*/
120 static int
121 fdcloser(void *arg, int fd)
122 {
123 	if (fd != tunfd)
124 		(void) close(fd);
125 	return (0);
126 }
127 
128 /*
129  * Become a daemon.
130  */
131 static void
132 daemonize(void)
133 {
134 	pid_t cpid;
135 
136 	/*
137 	 * A little bit of magic here.  By the first fork+setsid, we
138 	 * disconnect from our current controlling terminal and become
139 	 * a session group leader.  By forking again without setsid,
140 	 * we make certain that we're not the session group leader and
141 	 * can never reacquire a controlling terminal.
142 	 */
143 	if ((cpid = fork()) == (pid_t)-1) {
144 		early_error("fork 1");
145 	}
146 	if (cpid != 0) {
147 		(void) wait(NULL);
148 		_exit(0);
149 	}
150 	if (setsid() == (pid_t)-1) {
151 		early_error("setsid");
152 	}
153 	if ((cpid = fork()) == (pid_t)-1) {
154 		early_error("fork 2");
155 	}
156 	if (cpid != 0) {
157 		/* Parent just exits */
158 		(void) printf("%d\n", (int)cpid);
159 		(void) fflush(stdout);
160 		_exit(0);
161 	}
162 	(void) chdir("/");
163 	(void) umask(0);
164 	(void) fdwalk(fdcloser, NULL);
165 	reopen_log();
166 }
167 
168 /*
169  * Handle SIGHUP -- close and reopen non-syslog log files and reparse
170  * options.
171  */
172 /*ARGSUSED*/
173 static void
174 handle_hup(int sig)
175 {
176 	close_log_files();
177 	global_logging();
178 	last_reread = time(NULL);
179 	parse_options(tunfd, main_argc, main_argv);
180 }
181 
182 /*
183  * Handle SIGINT -- write current daemon status to /tmp.
184  */
185 /*ARGSUSED*/
186 static void
187 handle_int(int sig)
188 {
189 	FILE *fp;
190 	char dumpname[MAXPATHLEN];
191 	time_t now;
192 	struct rusage rusage;
193 
194 	(void) snprintf(dumpname, sizeof (dumpname), "/tmp/pppoed.%ld",
195 	    getpid());
196 	if ((fp = fopen(dumpname, "w+")) == NULL) {
197 		logerr("%s: %s", dumpname, mystrerror(errno));
198 		return;
199 	}
200 	now = time(NULL);
201 	(void) fprintf(fp, "pppoed running %s", ctime(&now));
202 	(void) fprintf(fp, "Started on     %s", ctime(&time_started));
203 	if (last_reread != 0)
204 		(void) fprintf(fp, "Last reconfig  %s", ctime(&last_reread));
205 	(void) putc('\n', fp);
206 	if (getrusage(RUSAGE_SELF, &rusage) == 0) {
207 		(void) fprintf(fp,
208 		    "CPU usage:  user %ld.%06ld, system %ld.%06ld\n",
209 		    rusage.ru_utime.tv_sec, rusage.ru_utime.tv_usec,
210 		    rusage.ru_stime.tv_sec, rusage.ru_stime.tv_usec);
211 	}
212 	(void) fprintf(fp, "Packets:  %lu received (%lu PADI, %lu PADR), ",
213 	    input_packets, padi_packets, padr_packets);
214 	(void) fprintf(fp, "%lu transmitted\n", output_packets);
215 	(void) fprintf(fp, "Sessions started:  %lu\n\n", sessions_started);
216 	dump_configuration(fp);
217 	(void) fclose(fp);
218 }
219 
220 static void
221 add_signal_handlers(void)
222 {
223 	struct sigaction sa;
224 
225 	(void) sigemptyset(&sigmask);
226 	(void) sigaddset(&sigmask, SIGHUP);
227 	(void) sigaddset(&sigmask, SIGCHLD);
228 	(void) sigaddset(&sigmask, SIGINT);
229 	(void) sigprocmask(SIG_BLOCK, &sigmask, NULL);
230 
231 	sa.sa_mask = sigmask;
232 	sa.sa_flags = 0;
233 
234 	/* Signals to handle */
235 	sa.sa_handler = handle_hup;
236 	if (sigaction(SIGHUP, &sa, NULL) < 0)
237 		early_error("sigaction HUP");
238 	sa.sa_handler = handle_int;
239 	if (sigaction(SIGINT, &sa, NULL) < 0)
240 		early_error("sigaction INT");
241 
242 	/*
243 	 * Signals to ignore.  Ignoring SIGCHLD in this way makes the
244 	 * children exit without ever creating zombies.  (No wait(2)
245 	 * call required.)
246 	 */
247 	sa.sa_handler = SIG_IGN;
248 	if (sigaction(SIGPIPE, &sa, NULL) < 0)
249 		early_error("sigaction PIPE");
250 	sa.sa_flags = SA_NOCLDWAIT;
251 	if (sigaction(SIGCHLD, &sa, NULL) < 0)
252 		early_error("sigaction CHLD");
253 }
254 
255 /*
256  * Dispatch a message from the tunnel driver.  It could be an actual
257  * PPPoE message or just an event notification.
258  */
259 static void
260 handle_input(uint32_t *ctrlbuf, int ctrllen, uint32_t *databuf, int datalen)
261 {
262 	poep_t *poep = (poep_t *)databuf;
263 	union ppptun_name ptn;
264 	int retv;
265 	struct strbuf ctrl;
266 	struct strbuf data;
267 	void *srvp;
268 	boolean_t launch;
269 	struct ppptun_control *ptc;
270 
271 	if (ctrllen != sizeof (*ptc)) {
272 		logdbg("bogus %d byte control message from driver",
273 		    ctrllen);
274 		return;
275 	}
276 	ptc = (struct ppptun_control *)ctrlbuf;
277 
278 	/* Switch out on event notifications. */
279 	switch (ptc->ptc_action) {
280 	case PTCA_TEST:
281 		logdbg("test reply for discriminator %X", ptc->ptc_discrim);
282 		return;
283 
284 	case PTCA_CONTROL:
285 		break;
286 
287 	case PTCA_DISCONNECT:
288 		logdbg("session %d disconnected on %s; send PADT",
289 		    ptc->ptc_rsessid, ptc->ptc_name);
290 		poep = poe_mkheader(pkt_output, POECODE_PADT,
291 		    ptc->ptc_rsessid);
292 		ptc->ptc_action = PTCA_CONTROL;
293 		ctrl.len = sizeof (*ptc);
294 		ctrl.buf = (caddr_t)ptc;
295 		data.len = poe_length(poep) + sizeof (*poep);
296 		data.buf = (caddr_t)poep;
297 		if (putmsg(tunfd, &ctrl, &data, 0) < 0) {
298 			logerr("putmsg PADT: %s", mystrerror(errno));
299 		} else {
300 			output_packets++;
301 		}
302 		return;
303 
304 	case PTCA_UNPLUMB:
305 		logdbg("%s unplumbed", ptc->ptc_name);
306 		return;
307 
308 	default:
309 		logdbg("unexpected code %d from driver", ptc->ptc_action);
310 		return;
311 	}
312 
313 	/* Only PPPoE control messages get here. */
314 
315 	input_packets++;
316 	if (datalen < sizeof (*poep)) {
317 		logdbg("incomplete PPPoE message from %s/%s",
318 		    ehost(&ptc->ptc_address), ptc->ptc_name);
319 		return;
320 	}
321 
322 	/* Server handles only PADI and PADR; all others are ignored. */
323 	if (poep->poep_code == POECODE_PADI) {
324 		padi_packets++;
325 	} else if (poep->poep_code == POECODE_PADR) {
326 		padr_packets++;
327 	} else {
328 		loginfo("unexpected %s from %s",
329 		    poe_codename(poep->poep_code), ehost(&ptc->ptc_address));
330 		return;
331 	}
332 	logdbg("Recv from %s/%s: %s", ehost(&ptc->ptc_address), ptc->ptc_name,
333 	    poe_codename(poep->poep_code));
334 
335 	/* Parse out service and formulate template reply. */
336 	retv = locate_service(poep, datalen, ptc->ptc_name, &ptc->ptc_address,
337 	    pkt_output, &srvp);
338 
339 	/* Continue formulating reply */
340 	launch = B_FALSE;
341 	if (retv != 1) {
342 		/* Ignore initiation if we don't offer a service. */
343 		if (retv <= 0 && poep->poep_code == POECODE_PADI) {
344 			logdbg("no services; no reply");
345 			return;
346 		}
347 		if (retv == 0)
348 			(void) poe_add_str((poep_t *)pkt_output, POETT_NAMERR,
349 			    "No such service.");
350 	} else {
351 		/* Exactly one service chosen; if it's PADR, then we start. */
352 		if (poep->poep_code == POECODE_PADR) {
353 			launch = B_TRUE;
354 		}
355 	}
356 	poep = (poep_t *)pkt_output;
357 
358 	/* Select control interface for output. */
359 	(void) strncpy(ptn.ptn_name, ptc->ptc_name, sizeof (ptn.ptn_name));
360 	if (strioctl(tunfd, PPPTUN_SCTL, &ptn, sizeof (ptn), 0) < 0) {
361 		logerr("PPPTUN_SCTL %s: %s", ptn.ptn_name, mystrerror(errno));
362 		return;
363 	}
364 
365 	/* Launch the PPP service */
366 	if (launch && launch_service(tunfd, poep, srvp, ptc))
367 		sessions_started++;
368 
369 	/* Send the reply. */
370 	ctrl.len = sizeof (*ptc);
371 	ctrl.buf = (caddr_t)ptc;
372 	data.len = poe_length(poep) + sizeof (*poep);
373 	data.buf = (caddr_t)poep;
374 	if (putmsg(tunfd, &ctrl, &data, 0) < 0) {
375 		logerr("putmsg %s: %s", ptc->ptc_name, mystrerror(errno));
376 	} else {
377 		output_packets++;
378 		logdbg("Send to   %s/%s: %s", ehost(&ptc->ptc_address),
379 		    ptc->ptc_name, poe_codename(poep->poep_code));
380 	}
381 }
382 
383 static void
384 main_loop(void)
385 {
386 	struct strbuf ctrl;
387 	struct strbuf data;
388 	int flags;
389 	int rc;
390 	int err;
391 
392 	for (;;) {
393 		ctrl.maxlen = PKT_OCTL_LEN;
394 		ctrl.buf = (caddr_t)pkt_octl;
395 		data.maxlen = PKT_INPUT_LEN;
396 		data.buf = (caddr_t)pkt_input;
397 		/* Allow signals only while idle */
398 		(void) sigprocmask(SIG_UNBLOCK, &sigmask, NULL);
399 		errno = 0;
400 		flags = 0;
401 		rc = mygetmsg(tunfd, &ctrl, &data, &flags);
402 		err = errno;
403 		/*
404 		 * Block signals -- data structures must not change
405 		 * while we're busy dispatching the client's request
406 		 */
407 		(void) sigprocmask(SIG_BLOCK, &sigmask, NULL);
408 		if (rc == -1) {
409 			if (err == EAGAIN || err == EINTR)
410 				continue;
411 			logerr("%s getmsg: %s", tunnam, mystrerror(err));
412 			exit(1);
413 		}
414 		if (rc > 0)
415 			logwarn("%s returned truncated data", tunnam);
416 		else
417 			handle_input(pkt_octl, ctrl.len, pkt_input, data.len);
418 	}
419 }
420 
421 int
422 main(int argc, char **argv)
423 {
424 	prog_name = "pppoed";
425 	log_level = 1;		/* Default to error messages only at first */
426 
427 	time_started = time(NULL);
428 
429 	if ((myname = argv[0]) == NULL)
430 		myname = "pppoed";
431 
432 	main_argc = argc;
433 	main_argv = argv;
434 
435 	open_tunnel_dev();
436 	add_signal_handlers();
437 	daemonize();
438 
439 	parse_options(tunfd, argc, argv);
440 	main_loop();
441 
442 	return (0);
443 }
444