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 (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 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. 12 * 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] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * PPPoE Server-mode daemon for use with Solaris PPP 4.0. 23 * 24 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 25 * Use is subject to license terms. 26 */ 27 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> 43 44 #include "common.h" 45 #include "pppoed.h" 46 #include "logging.h" 47 48 static int tunfd; /* Global connection to tunnel device */ 49 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 */ 53 54 static time_t time_started; /* Time daemon was started; for debug */ 55 static time_t last_reread; /* Last time configuration was read. */ 56 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; 61 62 static sigset_t sigmask; /* Global signal mask */ 63 64 /* 65 * Used for handling errors that occur before we daemonize. 66 */ 67 static void 68 early_error(const char *str) 69 { 70 const char *cp; 71 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); 78 } 79 exit(1); 80 } 81 82 /* 83 * Open the sppptun driver. 84 */ 85 static void 86 open_tunnel_dev(void) 87 { 88 struct ppptun_peer ptp; 89 90 tunfd = open(tunnam, O_RDWR); 91 if (tunfd == -1) { 92 early_error(tunnam); 93 } 94 95 /* 96 * Tell the device driver that I'm a daemon handling inbound 97 * connections, not a PPP session. 98 */ 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); 108 } 109 } 110 111 /* 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.) 115 */ 116 /*ARGSUSED*/ 117 static int 118 fdcloser(void *arg, int fd) 119 { 120 if (fd != tunfd) 121 (void) close(fd); 122 return (0); 123 } 124 125 /* 126 * Become a daemon. 127 */ 128 static void 129 daemonize(void) 130 { 131 pid_t cpid; 132 133 /* 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. 139 */ 140 if ((cpid = fork()) == (pid_t)-1) { 141 early_error("fork 1"); 142 } 143 if (cpid != 0) { 144 (void) wait(NULL); 145 _exit(0); 146 } 147 if (setsid() == (pid_t)-1) { 148 early_error("setsid"); 149 } 150 if ((cpid = fork()) == (pid_t)-1) { 151 early_error("fork 2"); 152 } 153 if (cpid != 0) { 154 /* Parent just exits */ 155 (void) printf("%d\n", (int)cpid); 156 (void) fflush(stdout); 157 _exit(0); 158 } 159 (void) chdir("/"); 160 (void) umask(0); 161 (void) fdwalk(fdcloser, NULL); 162 reopen_log(); 163 } 164 165 /* 166 * Handle SIGHUP -- close and reopen non-syslog log files and reparse 167 * options. 168 */ 169 /*ARGSUSED*/ 170 static void 171 handle_hup(int sig) 172 { 173 close_log_files(); 174 global_logging(); 175 last_reread = time(NULL); 176 parse_options(tunfd, main_argc, main_argv); 177 } 178 179 /* 180 * Handle SIGINT -- write current daemon status to /tmp. 181 */ 182 /*ARGSUSED*/ 183 static void 184 handle_int(int sig) 185 { 186 FILE *fp; 187 char dumpname[MAXPATHLEN]; 188 time_t now; 189 struct rusage rusage; 190 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; 196 } 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); 208 } 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); 215 } 216 217 static void 218 add_signal_handlers(void) 219 { 220 struct sigaction sa; 221 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); 227 228 sa.sa_mask = sigmask; 229 sa.sa_flags = 0; 230 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"); 238 239 /* 240 * Signals to ignore. Ignoring SIGCHLD in this way makes the 241 * children exit without ever creating zombies. (No wait(2) 242 * call required.) 243 */ 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"); 250 } 251 252 /* 253 * Dispatch a message from the tunnel driver. It could be an actual 254 * PPPoE message or just an event notification. 255 */ 256 static void 257 handle_input(uint32_t *ctrlbuf, int ctrllen, uint32_t *databuf, int datalen) 258 { 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; 267 268 if (ctrllen != sizeof (*ptc)) { 269 logdbg("bogus %d byte control message from driver", 270 ctrllen); 271 return; 272 } 273 ptc = (struct ppptun_control *)ctrlbuf; 274 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; 280 281 case PTCA_CONTROL: 282 break; 283 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++; 298 } 299 return; 300 301 case PTCA_UNPLUMB: 302 logdbg("%s unplumbed", ptc->ptc_name); 303 return; 304 305 case PTCA_BADCTRL: 306 logwarn("bad control data on %s for session %u", ptc->ptc_name, 307 ptc->ptc_rsessid); 308 return; 309 310 default: 311 logdbg("unexpected code %d from driver", ptc->ptc_action); 312 return; 313 } 314 315 /* Only PPPoE control messages get here. */ 316 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; 322 } 323 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; 333 } 334 logdbg("Recv from %s/%s: %s", ehost(&ptc->ptc_address), ptc->ptc_name, 335 poe_codename(poep->poep_code)); 336 337 /* Parse out service and formulate template reply. */ 338 retv = locate_service(poep, datalen, ptc->ptc_name, &ptc->ptc_address, 339 pkt_output, &srvp); 340 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; 348 } 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; 356 } 357 } 358 poep = (poep_t *)pkt_output; 359 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; 365 } 366 367 /* Launch the PPP service */ 368 if (launch && launch_service(tunfd, poep, srvp, ptc)) 369 sessions_started++; 370 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)); 382 } 383 } 384 385 static void 386 main_loop(void) 387 { 388 struct strbuf ctrl; 389 struct strbuf data; 390 int flags; 391 int rc; 392 int err; 393 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; 405 /* 406 * Block signals -- data structures must not change 407 * while we're busy dispatching the client's request 408 */ 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); 415 } 416 if (rc > 0) 417 logwarn("%s returned truncated data", tunnam); 418 else 419 handle_input(pkt_octl, ctrl.len, pkt_input, data.len); 420 } 421 } 422 423 int 424 main(int argc, char **argv) 425 { 426 prog_name = "pppoed"; 427 log_level = 1; /* Default to error messages only at first */ 428 429 time_started = time(NULL); 430 431 if ((myname = argv[0]) == NULL) 432 myname = "pppoed"; 433 434 main_argc = argc; 435 main_argv = argv; 436 437 open_tunnel_dev(); 438 add_signal_handlers(); 439 daemonize(); 440 441 parse_options(tunfd, argc, argv); 442 main_loop(); 443 444 return (0); 445 } 446