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