dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / cmd-inet / usr.lib / pppoe / pppoed.c
bloba9479ab1781e5f5a6a0c56eb99aefcb4da93189a
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 * PPPoE Server-mode daemon for use with Solaris PPP 4.0.
24 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
28 #include <stdio.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <unistd.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <fcntl.h>
35 #include <errno.h>
36 #include <signal.h>
37 #include <stropts.h>
38 #include <wait.h>
39 #include <sys/resource.h>
40 #include <netinet/in.h>
41 #include <net/sppptun.h>
42 #include <net/pppoe.h>
44 #include "common.h"
45 #include "pppoed.h"
46 #include "logging.h"
48 static int tunfd; /* Global connection to tunnel device */
50 char *myname; /* Copied from argv[0] for logging */
51 static int main_argc; /* Saved for reparse on SIGHUP */
52 static char **main_argv; /* Saved for reparse on SIGHUP */
54 static time_t time_started; /* Time daemon was started; for debug */
55 static time_t last_reread; /* Last time configuration was read. */
57 /* Various operational statistics. */
58 static unsigned long input_packets, padi_packets, padr_packets;
59 static unsigned long output_packets;
60 static unsigned long sessions_started;
62 static sigset_t sigmask; /* Global signal mask */
65 * Used for handling errors that occur before we daemonize.
67 static void
68 early_error(const char *str)
70 const char *cp;
72 cp = mystrerror(errno);
73 if (isatty(2)) {
74 (void) fprintf(stderr, "%s: %s: %s\n", myname, str, cp);
75 } else {
76 reopen_log();
77 logerr("%s: %s", str, cp);
79 exit(1);
83 * Open the sppptun driver.
85 static void
86 open_tunnel_dev(void)
88 struct ppptun_peer ptp;
90 tunfd = open(tunnam, O_RDWR);
91 if (tunfd == -1) {
92 early_error(tunnam);
96 * Tell the device driver that I'm a daemon handling inbound
97 * connections, not a PPP session.
99 (void) memset(&ptp, '\0', sizeof (ptp));
100 ptp.ptp_style = PTS_PPPOE;
101 ptp.ptp_flags = PTPF_DAEMON;
102 (void) memcpy(ptp.ptp_address.pta_pppoe.ptma_mac, ether_bcast,
103 sizeof (ptp.ptp_address.pta_pppoe.ptma_mac));
104 if (strioctl(tunfd, PPPTUN_SPEER, &ptp, sizeof (ptp), sizeof (ptp)) <
105 0) {
106 myperror("PPPTUN_SPEER");
107 exit(1);
112 * Callback function for fdwalk. Closes everything but the tunnel
113 * file descriptor when becoming daemon. (Log file must be reopened
114 * manually, since syslog file descriptor, if any, is unknown.)
116 /*ARGSUSED*/
117 static int
118 fdcloser(void *arg, int fd)
120 if (fd != tunfd)
121 (void) close(fd);
122 return (0);
126 * Become a daemon.
128 static void
129 daemonize(void)
131 pid_t cpid;
134 * A little bit of magic here. By the first fork+setsid, we
135 * disconnect from our current controlling terminal and become
136 * a session group leader. By forking again without setsid,
137 * we make certain that we're not the session group leader and
138 * can never reacquire a controlling terminal.
140 if ((cpid = fork()) == (pid_t)-1) {
141 early_error("fork 1");
143 if (cpid != 0) {
144 (void) wait(NULL);
145 _exit(0);
147 if (setsid() == (pid_t)-1) {
148 early_error("setsid");
150 if ((cpid = fork()) == (pid_t)-1) {
151 early_error("fork 2");
153 if (cpid != 0) {
154 /* Parent just exits */
155 (void) printf("%d\n", (int)cpid);
156 (void) fflush(stdout);
157 _exit(0);
159 (void) chdir("/");
160 (void) umask(0);
161 (void) fdwalk(fdcloser, NULL);
162 reopen_log();
166 * Handle SIGHUP -- close and reopen non-syslog log files and reparse
167 * options.
169 /*ARGSUSED*/
170 static void
171 handle_hup(int sig)
173 close_log_files();
174 global_logging();
175 last_reread = time(NULL);
176 parse_options(tunfd, main_argc, main_argv);
180 * Handle SIGINT -- write current daemon status to /tmp.
182 /*ARGSUSED*/
183 static void
184 handle_int(int sig)
186 FILE *fp;
187 char dumpname[MAXPATHLEN];
188 time_t now;
189 struct rusage rusage;
191 (void) snprintf(dumpname, sizeof (dumpname), "/tmp/pppoed.%ld",
192 getpid());
193 if ((fp = fopen(dumpname, "w+")) == NULL) {
194 logerr("%s: %s", dumpname, mystrerror(errno));
195 return;
197 now = time(NULL);
198 (void) fprintf(fp, "pppoed running %s", ctime(&now));
199 (void) fprintf(fp, "Started on %s", ctime(&time_started));
200 if (last_reread != 0)
201 (void) fprintf(fp, "Last reconfig %s", ctime(&last_reread));
202 (void) putc('\n', fp);
203 if (getrusage(RUSAGE_SELF, &rusage) == 0) {
204 (void) fprintf(fp,
205 "CPU usage: user %ld.%06ld, system %ld.%06ld\n",
206 rusage.ru_utime.tv_sec, rusage.ru_utime.tv_usec,
207 rusage.ru_stime.tv_sec, rusage.ru_stime.tv_usec);
209 (void) fprintf(fp, "Packets: %lu received (%lu PADI, %lu PADR), ",
210 input_packets, padi_packets, padr_packets);
211 (void) fprintf(fp, "%lu transmitted\n", output_packets);
212 (void) fprintf(fp, "Sessions started: %lu\n\n", sessions_started);
213 dump_configuration(fp);
214 (void) fclose(fp);
217 static void
218 add_signal_handlers(void)
220 struct sigaction sa;
222 (void) sigemptyset(&sigmask);
223 (void) sigaddset(&sigmask, SIGHUP);
224 (void) sigaddset(&sigmask, SIGCHLD);
225 (void) sigaddset(&sigmask, SIGINT);
226 (void) sigprocmask(SIG_BLOCK, &sigmask, NULL);
228 sa.sa_mask = sigmask;
229 sa.sa_flags = 0;
231 /* Signals to handle */
232 sa.sa_handler = handle_hup;
233 if (sigaction(SIGHUP, &sa, NULL) < 0)
234 early_error("sigaction HUP");
235 sa.sa_handler = handle_int;
236 if (sigaction(SIGINT, &sa, NULL) < 0)
237 early_error("sigaction INT");
240 * Signals to ignore. Ignoring SIGCHLD in this way makes the
241 * children exit without ever creating zombies. (No wait(2)
242 * call required.)
244 sa.sa_handler = SIG_IGN;
245 if (sigaction(SIGPIPE, &sa, NULL) < 0)
246 early_error("sigaction PIPE");
247 sa.sa_flags = SA_NOCLDWAIT;
248 if (sigaction(SIGCHLD, &sa, NULL) < 0)
249 early_error("sigaction CHLD");
253 * Dispatch a message from the tunnel driver. It could be an actual
254 * PPPoE message or just an event notification.
256 static void
257 handle_input(uint32_t *ctrlbuf, int ctrllen, uint32_t *databuf, int datalen)
259 poep_t *poep = (poep_t *)databuf;
260 union ppptun_name ptn;
261 int retv;
262 struct strbuf ctrl;
263 struct strbuf data;
264 void *srvp;
265 boolean_t launch;
266 struct ppptun_control *ptc;
268 if (ctrllen != sizeof (*ptc)) {
269 logdbg("bogus %d byte control message from driver",
270 ctrllen);
271 return;
273 ptc = (struct ppptun_control *)ctrlbuf;
275 /* Switch out on event notifications. */
276 switch (ptc->ptc_action) {
277 case PTCA_TEST:
278 logdbg("test reply for discriminator %X", ptc->ptc_discrim);
279 return;
281 case PTCA_CONTROL:
282 break;
284 case PTCA_DISCONNECT:
285 logdbg("session %d disconnected on %s; send PADT",
286 ptc->ptc_rsessid, ptc->ptc_name);
287 poep = poe_mkheader(pkt_output, POECODE_PADT,
288 ptc->ptc_rsessid);
289 ptc->ptc_action = PTCA_CONTROL;
290 ctrl.len = sizeof (*ptc);
291 ctrl.buf = (caddr_t)ptc;
292 data.len = poe_length(poep) + sizeof (*poep);
293 data.buf = (caddr_t)poep;
294 if (putmsg(tunfd, &ctrl, &data, 0) < 0) {
295 logerr("putmsg PADT: %s", mystrerror(errno));
296 } else {
297 output_packets++;
299 return;
301 case PTCA_UNPLUMB:
302 logdbg("%s unplumbed", ptc->ptc_name);
303 return;
305 case PTCA_BADCTRL:
306 logwarn("bad control data on %s for session %u", ptc->ptc_name,
307 ptc->ptc_rsessid);
308 return;
310 default:
311 logdbg("unexpected code %d from driver", ptc->ptc_action);
312 return;
315 /* Only PPPoE control messages get here. */
317 input_packets++;
318 if (datalen < sizeof (*poep)) {
319 logdbg("incomplete PPPoE message from %s/%s",
320 ehost(&ptc->ptc_address), ptc->ptc_name);
321 return;
324 /* Server handles only PADI and PADR; all others are ignored. */
325 if (poep->poep_code == POECODE_PADI) {
326 padi_packets++;
327 } else if (poep->poep_code == POECODE_PADR) {
328 padr_packets++;
329 } else {
330 loginfo("unexpected %s from %s",
331 poe_codename(poep->poep_code), ehost(&ptc->ptc_address));
332 return;
334 logdbg("Recv from %s/%s: %s", ehost(&ptc->ptc_address), ptc->ptc_name,
335 poe_codename(poep->poep_code));
337 /* Parse out service and formulate template reply. */
338 retv = locate_service(poep, datalen, ptc->ptc_name, &ptc->ptc_address,
339 pkt_output, &srvp);
341 /* Continue formulating reply */
342 launch = B_FALSE;
343 if (retv != 1) {
344 /* Ignore initiation if we don't offer a service. */
345 if (retv <= 0 && poep->poep_code == POECODE_PADI) {
346 logdbg("no services; no reply");
347 return;
349 if (retv == 0)
350 (void) poe_add_str((poep_t *)pkt_output, POETT_NAMERR,
351 "No such service.");
352 } else {
353 /* Exactly one service chosen; if it's PADR, then we start. */
354 if (poep->poep_code == POECODE_PADR) {
355 launch = B_TRUE;
358 poep = (poep_t *)pkt_output;
360 /* Select control interface for output. */
361 (void) strncpy(ptn.ptn_name, ptc->ptc_name, sizeof (ptn.ptn_name));
362 if (strioctl(tunfd, PPPTUN_SCTL, &ptn, sizeof (ptn), 0) < 0) {
363 logerr("PPPTUN_SCTL %s: %s", ptn.ptn_name, mystrerror(errno));
364 return;
367 /* Launch the PPP service */
368 if (launch && launch_service(tunfd, poep, srvp, ptc))
369 sessions_started++;
371 /* Send the reply. */
372 ctrl.len = sizeof (*ptc);
373 ctrl.buf = (caddr_t)ptc;
374 data.len = poe_length(poep) + sizeof (*poep);
375 data.buf = (caddr_t)poep;
376 if (putmsg(tunfd, &ctrl, &data, 0) < 0) {
377 logerr("putmsg %s: %s", ptc->ptc_name, mystrerror(errno));
378 } else {
379 output_packets++;
380 logdbg("Send to %s/%s: %s", ehost(&ptc->ptc_address),
381 ptc->ptc_name, poe_codename(poep->poep_code));
385 static void
386 main_loop(void)
388 struct strbuf ctrl;
389 struct strbuf data;
390 int flags;
391 int rc;
392 int err;
394 for (;;) {
395 ctrl.maxlen = PKT_OCTL_LEN;
396 ctrl.buf = (caddr_t)pkt_octl;
397 data.maxlen = PKT_INPUT_LEN;
398 data.buf = (caddr_t)pkt_input;
399 /* Allow signals only while idle */
400 (void) sigprocmask(SIG_UNBLOCK, &sigmask, NULL);
401 errno = 0;
402 flags = 0;
403 rc = mygetmsg(tunfd, &ctrl, &data, &flags);
404 err = errno;
406 * Block signals -- data structures must not change
407 * while we're busy dispatching the client's request
409 (void) sigprocmask(SIG_BLOCK, &sigmask, NULL);
410 if (rc == -1) {
411 if (err == EAGAIN || err == EINTR)
412 continue;
413 logerr("%s getmsg: %s", tunnam, mystrerror(err));
414 exit(1);
416 if (rc > 0)
417 logwarn("%s returned truncated data", tunnam);
418 else
419 handle_input(pkt_octl, ctrl.len, pkt_input, data.len);
424 main(int argc, char **argv)
426 prog_name = "pppoed";
427 log_level = 1; /* Default to error messages only at first */
429 time_started = time(NULL);
431 if ((myname = argv[0]) == NULL)
432 myname = "pppoed";
434 main_argc = argc;
435 main_argv = argv;
437 open_tunnel_dev();
438 add_signal_handlers();
439 daemonize();
441 parse_options(tunfd, argc, argv);
442 main_loop();
444 return (0);