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]
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.
29 #include <sys/types.h>
39 #include <sys/resource.h>
40 #include <netinet/in.h>
41 #include <net/sppptun.h>
42 #include <net/pppoe.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.
68 early_error(const char *str
)
72 cp
= mystrerror(errno
);
74 (void) fprintf(stderr
, "%s: %s: %s\n", myname
, str
, cp
);
77 logerr("%s: %s", str
, cp
);
83 * Open the sppptun driver.
88 struct ppptun_peer ptp
;
90 tunfd
= open(tunnam
, O_RDWR
);
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
)) <
106 myperror("PPPTUN_SPEER");
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.)
118 fdcloser(void *arg
, int fd
)
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");
147 if (setsid() == (pid_t
)-1) {
148 early_error("setsid");
150 if ((cpid
= fork()) == (pid_t
)-1) {
151 early_error("fork 2");
154 /* Parent just exits */
155 (void) printf("%d\n", (int)cpid
);
156 (void) fflush(stdout
);
161 (void) fdwalk(fdcloser
, NULL
);
166 * Handle SIGHUP -- close and reopen non-syslog log files and reparse
175 last_reread
= time(NULL
);
176 parse_options(tunfd
, main_argc
, main_argv
);
180 * Handle SIGINT -- write current daemon status to /tmp.
187 char dumpname
[MAXPATHLEN
];
189 struct rusage rusage
;
191 (void) snprintf(dumpname
, sizeof (dumpname
), "/tmp/pppoed.%ld",
193 if ((fp
= fopen(dumpname
, "w+")) == NULL
) {
194 logerr("%s: %s", dumpname
, mystrerror(errno
));
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) {
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
);
218 add_signal_handlers(void)
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
;
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)
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.
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
;
266 struct ppptun_control
*ptc
;
268 if (ctrllen
!= sizeof (*ptc
)) {
269 logdbg("bogus %d byte control message from driver",
273 ptc
= (struct ppptun_control
*)ctrlbuf
;
275 /* Switch out on event notifications. */
276 switch (ptc
->ptc_action
) {
278 logdbg("test reply for discriminator %X", ptc
->ptc_discrim
);
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
,
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
));
302 logdbg("%s unplumbed", ptc
->ptc_name
);
306 logwarn("bad control data on %s for session %u", ptc
->ptc_name
,
311 logdbg("unexpected code %d from driver", ptc
->ptc_action
);
315 /* Only PPPoE control messages get here. */
318 if (datalen
< sizeof (*poep
)) {
319 logdbg("incomplete PPPoE message from %s/%s",
320 ehost(&ptc
->ptc_address
), ptc
->ptc_name
);
324 /* Server handles only PADI and PADR; all others are ignored. */
325 if (poep
->poep_code
== POECODE_PADI
) {
327 } else if (poep
->poep_code
== POECODE_PADR
) {
330 loginfo("unexpected %s from %s",
331 poe_codename(poep
->poep_code
), ehost(&ptc
->ptc_address
));
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
,
341 /* Continue formulating reply */
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");
350 (void) poe_add_str((poep_t
*)pkt_output
, POETT_NAMERR
,
353 /* Exactly one service chosen; if it's PADR, then we start. */
354 if (poep
->poep_code
== POECODE_PADR
) {
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
));
367 /* Launch the PPP service */
368 if (launch
&& launch_service(tunfd
, poep
, srvp
, ptc
))
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
));
380 logdbg("Send to %s/%s: %s", ehost(&ptc
->ptc_address
),
381 ptc
->ptc_name
, poe_codename(poep
->poep_code
));
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
);
403 rc
= mygetmsg(tunfd
, &ctrl
, &data
, &flags
);
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
);
411 if (err
== EAGAIN
|| err
== EINTR
)
413 logerr("%s getmsg: %s", tunnam
, mystrerror(err
));
417 logwarn("%s returned truncated data", tunnam
);
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
)
438 add_signal_handlers();
441 parse_options(tunfd
, argc
, argv
);