1 /* $OpenBSD: pppoed.c,v 1.7 2011/02/28 02:31:55 dlg Exp $ */ 2 3 /*- 4 * Copyright (c) 2009 Internet Initiative Japan Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 /**@file 29 * This file provides the PPPoE(RFC2516) server(access concentrator) 30 * implementaion. 31 * $Id: pppoed.c,v 1.7 2011/02/28 02:31:55 dlg Exp $ 32 */ 33 #include <sys/types.h> 34 #include <sys/param.h> 35 #include <sys/endian.h> 36 #include <sys/socket.h> 37 #include <sys/ioctl.h> 38 #include <sys/uio.h> 39 #include <netinet/in.h> 40 #include <net/if.h> 41 #include <net/if_types.h> 42 #if defined(__NetBSD__) 43 #include <net/if_ether.h> 44 #else 45 #include <netinet/if_ether.h> 46 #endif 47 #include <net/if_dl.h> 48 #include <net/ethertypes.h> 49 #include <net/bpf.h> 50 #include <string.h> 51 #include <syslog.h> 52 #include <stdio.h> 53 #include <unistd.h> 54 #include <fcntl.h> 55 #include <time.h> 56 #include <event.h> 57 #include <signal.h> 58 #include <stdlib.h> 59 #include <ifaddrs.h> 60 #include <stdarg.h> 61 #include <errno.h> 62 63 #include "debugutil.h" 64 #include "slist.h" 65 #include "bytebuf.h" 66 #include "hash.h" 67 #include "properties.h" 68 #include "config_helper.h" 69 #include "rtev.h" 70 #include "privsep.h" 71 72 #include "pppoe.h" 73 #include "pppoe_local.h" 74 75 static int pppoed_seqno = 0; 76 77 #ifdef PPPOED_DEBUG 78 #define PPPOED_ASSERT(x) ASSERT(x) 79 #define PPPOED_DBG(x) pppoed_log x 80 #else 81 #define PPPOED_ASSERT(x) 82 #define PPPOED_DBG(x) 83 #endif 84 85 static void pppoed_log (pppoed *, int, const char *, ...) __printflike(3,4); 86 static void pppoed_listener_init(pppoed *, pppoed_listener *); 87 static int pppoed_output (pppoed_listener *, u_char *, u_char *, int); 88 static int pppoed_listener_start (pppoed_listener *, int); 89 static void pppoed_io_event (int, short, void *); 90 static void pppoed_input (pppoed_listener *, uint8_t [ETHER_ADDR_LEN], int, u_char *, int); 91 static void pppoed_recv_PADR (pppoed_listener *, uint8_t [ETHER_ADDR_LEN], slist *); 92 static void pppoed_recv_PADI (pppoed_listener *, uint8_t [ETHER_ADDR_LEN], slist *); 93 static int session_id_cmp (void *, void *); 94 static uint32_t session_id_hash (void *, size_t); 95 96 #ifdef PPPOE_TEST 97 static void pppoed_on_sigterm (int, short, void *); 98 static void usage (void); 99 #endif 100 static const char *pppoe_code_string(int); 101 #ifdef PPPOED_DEBUG 102 static const char *pppoe_tag_string(int); 103 #endif 104 105 /* 106 * daemon 107 */ 108 109 /* initialize PPPoE daemon */ 110 int 111 pppoed_init(pppoed *_this) 112 { 113 int i, off, id; 114 115 memset(_this, 0, sizeof(pppoed)); 116 _this->id = pppoed_seqno++; 117 118 if ((_this->session_hash = hash_create( 119 (int (*) (const void *, const void *))session_id_cmp, 120 (uint32_t (*) (const void *, int))session_id_hash, 121 PPPOE_SESSION_HASH_SIZ)) == NULL) { 122 pppoed_log(_this, LOG_ERR, "hash_create() failed on %s(): %m", 123 __func__); 124 goto fail; 125 } 126 127 slist_init(&_this->session_free_list); 128 if (slist_add(&_this->session_free_list, 129 (void *)PPPOED_SESSION_SHUFFLE_MARK) == NULL) { 130 pppoed_log(_this, LOG_ERR, "slist_add() failed on %s(): %m", 131 __func__); 132 goto fail; 133 } 134 135 /* XXX initialize hash of cookies */ 136 if ((_this->acookie_hash = hash_create( 137 (int (*) (const void *, const void *))session_id_cmp, 138 (uint32_t (*) (const void *, int))session_id_hash, 139 PPPOE_SESSION_HASH_SIZ)) == NULL) { 140 pppoed_log(_this, LOG_WARNING, 141 "hash_create() failed on %s(): %m", __func__); 142 pppoed_log(_this, LOG_WARNING, "hash_create() failed on %s(): " 143 "ac-cookie hash create failed.", __func__); 144 _this->acookie_hash = NULL; 145 } 146 _this->acookie_next = random(); 147 148 #if PPPOE_NSESSION > 0xffff 149 #error PPPOE_NSESSION must be less than 65536 150 #endif 151 off = random() % 0xffff; 152 for (i = 0; i < PPPOE_NSESSION; i++) { 153 id = (i + off) % 0xffff; 154 if (id == 0) 155 id = (off - 1) % 0xffff; 156 if (slist_add(&_this->session_free_list, (void *)id) == NULL) { 157 pppoed_log(_this, LOG_ERR, 158 "slist_add() failed on %s(): %m", __func__); 159 goto fail; 160 } 161 } 162 163 _this->state = PPPOED_STATE_INIT; 164 165 return 0; 166 fail: 167 pppoed_uninit(_this); 168 return 1; 169 } 170 171 static void 172 pppoed_listener_init(pppoed *_this, pppoed_listener *listener) 173 { 174 memset(listener, 0, sizeof(pppoed_listener)); 175 listener->bpf = -1; 176 listener->self = _this; 177 listener->index = PPPOED_LISTENER_INVALID_INDEX; 178 } 179 180 /* reload listner */ 181 int 182 pppoed_reload_listeners(pppoed *_this) 183 { 184 int rval = 0; 185 186 if (_this->state == PPPOED_STATE_RUNNING && 187 _this->listen_incomplete != 0) 188 rval = pppoed_start(_this); 189 190 return rval; 191 } 192 193 /* 194 * Reject any packet except the packet to self and broadcasts, 195 * as bpf(4) potentially receive packets for others. 196 */ 197 #define REJECT_FOREIGN_ADDRESS 1 198 199 #define ETHER_FIRST_INT(e) ((e)[0]<<24|(e)[1]<<16|(e)[2]<<8|(e)[3]) 200 #define ETHER_LAST_SHORT(e) ((e)[4]<<8|(e)[5]) 201 202 static int 203 pppoed_listener_start(pppoed_listener *_this, int restart) 204 { 205 int i; 206 int log_level; 207 char buf[BUFSIZ]; 208 struct ifreq ifreq; 209 int ival; 210 int found; 211 struct ifaddrs *ifa0, *ifa; 212 struct sockaddr_dl *sdl; 213 struct bpf_insn insns[] = { 214 /* check etyer type = PPPOEDESC or PPPOE */ 215 BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 12), 216 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ETHERTYPE_PPPOEDISC, 2, 0), 217 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ETHERTYPE_PPPOE, 1, 0), 218 BPF_STMT(BPF_RET+BPF_K, (u_int)0), 219 #ifndef REJECT_FOREIGN_ADDRESS 220 BPF_STMT(BPF_RET+BPF_K, (u_int)-1), 221 #else 222 /* to ff:ff:ff:ff:ff:ff */ 223 BPF_STMT(BPF_LD+BPF_W+BPF_ABS, 0), 224 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0xffffffff, 0, 3), 225 BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 4), 226 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0xffff, 0, 1), 227 BPF_STMT(BPF_RET+BPF_K, (u_int)-1), 228 /* to self */ 229 BPF_STMT(BPF_LD+BPF_W+BPF_ABS, 0), 230 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 231 ETHER_FIRST_INT(_this->ether_addr), 0, 3), 232 BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 4), 233 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 234 ETHER_LAST_SHORT(_this->ether_addr), 0, 1), 235 BPF_STMT(BPF_RET+BPF_K, (u_int)-1), 236 BPF_STMT(BPF_RET+BPF_K, (u_int)0), 237 #endif 238 }; 239 struct bpf_program bf_filter = { 240 .bf_len = countof(insns), 241 .bf_insns = insns 242 }; 243 pppoed *_pppoed; 244 245 if (restart == 0) 246 log_level = LOG_ERR; 247 else 248 log_level = LOG_INFO; 249 250 _pppoed = _this->self; 251 252 ifa0 = NULL; 253 if (getifaddrs(&ifa0) != 0) { 254 pppoed_log(_pppoed, log_level, 255 "getifaddrs() failed on %s(): %m", __func__); 256 return -1; 257 } 258 found = 0; 259 for (ifa = ifa0; ifa != NULL; ifa = ifa->ifa_next) { 260 sdl = (struct sockaddr_dl *)ifa->ifa_addr; 261 if (sdl->sdl_family != AF_LINK || sdl->sdl_type != IFT_ETHER || 262 sdl->sdl_alen != ETHER_ADDR_LEN) 263 continue; 264 if (strcmp(ifa->ifa_name, _this->listen_ifname) == 0) { 265 memcpy(_this->ether_addr, 266 (caddr_t)LLADDR(sdl), ETHER_ADDR_LEN); 267 found = 1; 268 break; 269 } 270 } 271 freeifaddrs(ifa0); 272 if (!found) { 273 pppoed_log(_pppoed, log_level, "%s is not available.", 274 _this->listen_ifname); 275 goto fail; 276 } 277 278 /* Open /dev/bpfXX */ 279 /* FIXME: /dev/bpf of NetBSD3.0 can simultaneity open */ 280 for (i = 0; i < 256; i++) { 281 snprintf(buf, sizeof(buf), "/dev/bpf%d", i); 282 if ((_this->bpf = priv_open(buf, O_RDWR, 0600)) >= 0) { 283 break; 284 } else if (errno == ENXIO || errno == ENOENT) 285 break; /* no more entries */ 286 } 287 if (_this->bpf < 0) { 288 pppoed_log(_pppoed, log_level, "Cannot open bpf"); 289 goto fail; 290 } 291 292 ival = BPF_CAPTURE_SIZ; 293 if (ioctl(_this->bpf, BIOCSBLEN, &ival) != 0) { 294 pppoed_log(_pppoed, log_level, "ioctl(bpf, BIOCSBLEN(%d)): %m", 295 ival); 296 goto fail; 297 } 298 ival = 1; 299 if (ioctl(_this->bpf, BIOCIMMEDIATE, &ival) != 0) { 300 pppoed_log(_pppoed, log_level, "Cannot start bpf on %s: %m", 301 _this->listen_ifname); 302 goto fail; 303 } 304 305 /* bind interface */ 306 memset(&ifreq, 0, sizeof(ifreq)); 307 strlcpy(ifreq.ifr_name, _this->listen_ifname, sizeof(ifreq.ifr_name)); 308 if (ioctl(_this->bpf, BIOCSETIF, &ifreq) != 0) { 309 pppoed_log(_pppoed, log_level, "Cannot start bpf on %s: %m", 310 _this->listen_ifname); 311 goto fail; 312 } 313 314 /* set linklocal address */ 315 #ifdef REJECT_FOREIGN_ADDRESS 316 insns[10].k = ETHER_FIRST_INT(_this->ether_addr); 317 insns[12].k = ETHER_LAST_SHORT(_this->ether_addr); 318 #endif 319 320 /* set filter */ 321 if (ioctl(_this->bpf, BIOCSETF, &bf_filter) != 0) { 322 pppoed_log(_pppoed, log_level, "ioctl(bpf, BIOCSETF()): %m"); 323 goto fail; 324 } 325 326 event_set(&_this->ev_bpf, _this->bpf, EV_READ | EV_PERSIST, 327 pppoed_io_event, _this); 328 event_add(&_this->ev_bpf, NULL); 329 330 pppoed_log(_pppoed, LOG_INFO, "Listening on %s (PPPoE) [%s] using=%s " 331 "address=%02x:%02x:%02x:%02x:%02x:%02x", _this->listen_ifname, 332 _this->phy_label, buf, _this->ether_addr[0], _this->ether_addr[1], 333 _this->ether_addr[2], _this->ether_addr[3], _this->ether_addr[4], 334 _this->ether_addr[5]); 335 336 return 0; 337 fail: 338 if (_this->bpf >= 0) { 339 close(_this->bpf); 340 _this->bpf = -1; 341 } 342 343 return 1; 344 } 345 346 /* start PPPoE daemon */ 347 int 348 pppoed_start(pppoed *_this) 349 { 350 int rval = 0; 351 int nlistener_fail = 0; 352 pppoed_listener *plistener; 353 354 slist_itr_first(&_this->listener); 355 while (slist_itr_has_next(&_this->listener)) { 356 plistener = slist_itr_next(&_this->listener); 357 PPPOED_ASSERT(plistener != NULL); 358 if (plistener->bpf < 0) { 359 if (pppoed_listener_start(plistener, 360 _this->listen_incomplete) != 0) 361 nlistener_fail++; 362 } 363 } 364 if (nlistener_fail > 0) 365 _this->listen_incomplete = 1; 366 else 367 _this->listen_incomplete = 0; 368 369 _this->state = PPPOED_STATE_RUNNING; 370 371 return rval; 372 } 373 374 /* stop listener */ 375 static void 376 pppoed_listener_stop(pppoed_listener *_this) 377 { 378 pppoed *_pppoed; 379 380 PPPOED_ASSERT(_this != NULL); 381 _pppoed = _this->self; 382 PPPOED_ASSERT(_pppoed != NULL); 383 384 if (_this->bpf >= 0) { 385 event_del(&_this->ev_bpf); 386 close(_this->bpf); 387 pppoed_log(_pppoed, LOG_INFO, "Shutdown %s (PPPoE) [%s] " 388 "address=%02x:%02x:%02x:%02x:%02x:%02x", 389 _this->listen_ifname, _this->phy_label, 390 _this->ether_addr[0], _this->ether_addr[1], 391 _this->ether_addr[2], _this->ether_addr[3], 392 _this->ether_addr[4], _this->ether_addr[5]); 393 _this->bpf = -1; 394 } 395 } 396 397 /* stop PPPoE daemon */ 398 void 399 pppoed_stop(pppoed *_this) 400 { 401 pppoed_listener *plistener; 402 hash_link *hl; 403 pppoe_session *session; 404 405 if (!pppoed_is_running(_this)) 406 return; 407 408 _this->state = PPPOED_STATE_STOPPED; 409 if (_this->session_hash != NULL) { 410 for (hl = hash_first(_this->session_hash); 411 hl != NULL; 412 hl = hash_next(_this->session_hash)) { 413 session = (pppoe_session *)hl->item; 414 pppoe_session_disconnect(session); 415 pppoe_session_stop(session); 416 } 417 } 418 for (slist_itr_first(&_this->listener); 419 slist_itr_has_next(&_this->listener);) { 420 plistener = slist_itr_next(&_this->listener); 421 pppoed_listener_stop(plistener); 422 free(plistener); 423 slist_itr_remove(&_this->listener); 424 } 425 pppoed_log(_this, LOG_NOTICE, "Stopped"); 426 } 427 428 /* uninitialize (free) PPPoE daemon */ 429 void 430 pppoed_uninit(pppoed *_this) 431 { 432 if (_this->session_hash != NULL) { 433 hash_free(_this->session_hash); 434 _this->session_hash = NULL; 435 } 436 if (_this->acookie_hash != NULL) { 437 hash_free(_this->acookie_hash); 438 _this->acookie_hash = NULL; 439 } 440 slist_fini(&_this->session_free_list); 441 /* listener themself has been released already */ 442 slist_fini(&_this->listener); 443 444 _this->config = NULL; 445 } 446 447 /* it called when the PPPoE session was closed */ 448 void 449 pppoed_pppoe_session_close_notify(pppoed *_this, pppoe_session *session) 450 { 451 slist_add(&_this->session_free_list, (void *)session->session_id); 452 453 if (_this->acookie_hash != NULL) 454 hash_delete(_this->acookie_hash, (void *)session->acookie, 0); 455 if (_this->session_hash != NULL) 456 hash_delete(_this->session_hash, (void *)session->session_id, 457 0); 458 459 pppoe_session_fini(session); 460 free(session); 461 } 462 463 /* 464 * PPPoE Configuration 465 */ 466 #define CFG_KEY(p, s) config_key_prefix((p), (s)) 467 #define VAL_SEP " \t\r\n" 468 469 CONFIG_FUNCTIONS(pppoed_config, pppoed, config); 470 PREFIXED_CONFIG_FUNCTIONS(pppoed_listener_config, pppoed_listener, self->config, 471 phy_label); 472 473 /* reload configurations for the PPPoE daemon */ 474 int 475 pppoed_reload(pppoed *_this, struct properties *config, const char *name, 476 int default_enabled) 477 { 478 struct sockaddr_dl *sdl; 479 int i, count, found; 480 hash_link *hl; 481 const char *val; 482 char *tok, *cp, buf[PPPOED_CONFIG_BUFSIZ], *label; 483 pppoed_listener *l; 484 int do_start; 485 struct { 486 char ifname[IF_NAMESIZE]; 487 char label[PPPOED_PHY_LABEL_SIZE]; 488 } listeners[PPPOE_NLISTENER]; 489 struct ifaddrs *ifa0, *ifa; 490 slist rmlist, newlist; 491 pppoe_session *session; 492 493 do_start = 0; 494 495 _this->config = config; 496 if (pppoed_config_str_equal(_this, CFG_KEY(name, "enabled"), "true", 497 default_enabled)) { 498 /* avoid false->true flapping */ 499 if (pppoed_is_stopped(_this) || !pppoed_is_running(_this)) 500 do_start = 1; 501 } else { 502 if (!pppoed_is_stopped(_this)) 503 pppoed_stop(_this); 504 return 0; 505 } 506 507 if (do_start) { 508 if (pppoed_init(_this) != 0) 509 return 1; 510 _this->config = config; 511 } 512 513 ifa0 = NULL; 514 slist_init(&rmlist); 515 slist_init(&newlist); 516 517 _this->desc_in_pktdump = pppoed_config_str_equal(_this, 518 "log.pppoe.desc.in.pktdump", "true", 0); 519 _this->desc_out_pktdump = pppoed_config_str_equal(_this, 520 "log.pppoe.desc.out.pktdump", "true", 0); 521 522 _this->session_in_pktdump = pppoed_config_str_equal(_this, 523 "log.pppoe.session.in.pktdump", "true", 0); 524 _this->session_out_pktdump = pppoed_config_str_equal(_this, 525 "log.pppoe.session.out.pktdump", "true", 0); 526 527 if (getifaddrs(&ifa0) != 0) { 528 pppoed_log(_this, LOG_ERR, 529 "getifaddrs() failed on %s(): %m", __func__); 530 goto fail; 531 } 532 count = 0; 533 val = pppoed_config_str(_this, CFG_KEY(name, "interface")); 534 if (val != NULL) { 535 if (strlen(val) >= sizeof(buf)) { 536 log_printf(LOG_ERR, "configuration error at " 537 "%s: too long", CFG_KEY(name, "interface")); 538 return 1; 539 } 540 strlcpy(buf, val, sizeof(buf)); 541 542 label = NULL; 543 /* it accepts multiple entries with tab/space separation */ 544 for (i = 0, cp = buf; 545 (tok = strsep(&cp, VAL_SEP)) != NULL;) { 546 if (*tok == '\0') 547 continue; 548 if (label == NULL) { 549 label = tok; 550 continue; 551 } 552 PPPOED_ASSERT(count < countof(listeners)); 553 if (count >= countof(listeners)) { 554 pppoed_log(_this, LOG_ERR, 555 "Too many listeners"); 556 goto fail; 557 } 558 /* check the interface exist or not */ 559 found = 0; 560 for (ifa = ifa0; ifa != NULL; ifa = ifa->ifa_next) { 561 sdl = (struct sockaddr_dl *)ifa->ifa_addr; 562 if (sdl->sdl_family == AF_LINK && 563 IFTYPE_IS_LAN(sdl->sdl_type) && 564 strcmp(ifa->ifa_name, tok) == 0) { 565 found = 1; 566 break; 567 } 568 } 569 if (!found) { 570 pppoed_log(_this, LOG_ERR, 571 "interface %s is not found", tok); 572 goto fail; 573 } 574 strlcpy(listeners[count].ifname, tok, 575 sizeof(listeners[count].ifname)); 576 strlcpy(listeners[count].label, label, 577 sizeof(listeners[count].label)); 578 579 label = NULL; 580 count++; 581 } 582 if (label != NULL) { 583 log_printf(LOG_ERR, "configuration error at %s: %s", 584 CFG_KEY(name, "interface"), label); 585 return 1; 586 } 587 } 588 589 if (slist_add_all(&rmlist, &_this->listener) != 0) 590 goto fail; 591 592 for (i = 0; i < count; i++) { 593 found = 0; 594 l = NULL; 595 slist_itr_first(&rmlist); 596 while (slist_itr_has_next(&rmlist)) { 597 l = slist_itr_next(&rmlist); 598 if (strcmp(l->listen_ifname, listeners[i].ifname) == 0){ 599 slist_itr_remove(&rmlist); 600 found = 1; 601 break; 602 } 603 } 604 if (!found) { 605 if ((l = malloc(sizeof(pppoed_listener))) == NULL) 606 goto fail; 607 pppoed_listener_init(_this, l); 608 } 609 l->self = _this; 610 strlcpy(l->phy_label, listeners[i].label, 611 sizeof(l->phy_label)); 612 strlcpy(l->listen_ifname, listeners[i].ifname, 613 sizeof(l->listen_ifname)); 614 if (slist_add(&newlist, l) == NULL) { 615 pppoed_log(_this, LOG_ERR, 616 "slist_add() failed in %s(): %m", __func__); 617 goto fail; 618 } 619 } 620 621 if (slist_set_size(&_this->listener, count) != 0) 622 goto fail; 623 624 /* garbage collection of listner context */ 625 slist_itr_first(&rmlist); 626 while (slist_itr_has_next(&rmlist)) { 627 l = slist_itr_next(&rmlist); 628 /* handle child PPPoE session */ 629 if (_this->session_hash != NULL) { 630 for (hl = hash_first(_this->session_hash); hl != NULL; 631 hl = hash_next(_this->session_hash)) { 632 session = (pppoe_session *)hl->item; 633 if (session->listener_index == l->index) 634 pppoe_session_stop(session); 635 } 636 } 637 pppoed_listener_stop(l); 638 free(l); 639 } 640 slist_remove_all(&_this->listener); 641 /* as slist_set_size-ed, it must not fail */ 642 (void)slist_add_all(&_this->listener, &newlist); 643 644 /* reset indexes */ 645 slist_itr_first(&newlist); 646 for (i = 0; slist_itr_has_next(&newlist); i++) { 647 l = slist_itr_next(&newlist); 648 if (l->index != i && l->index != PPPOED_LISTENER_INVALID_INDEX){ 649 PPPOED_DBG((_this, LOG_DEBUG, "listener %d => %d", 650 l->index, i)); 651 for (hl = hash_first(_this->session_hash); hl != NULL; 652 hl = hash_next(_this->session_hash)) { 653 session = (pppoe_session *)hl->item; 654 if (session->listener_index == l->index) 655 session->listener_index = i; 656 } 657 } 658 l->index = i; 659 } 660 661 slist_fini(&rmlist); 662 slist_fini(&newlist); 663 if (ifa0 != NULL) 664 freeifaddrs(ifa0); 665 666 if (pppoed_start(_this) != 0) 667 return 1; 668 669 return 0; 670 fail: 671 slist_fini(&rmlist); 672 slist_fini(&newlist); 673 if (ifa0 != NULL) 674 freeifaddrs(ifa0); 675 676 return 1; 677 } 678 679 /* 680 * I/O 681 */ 682 683 static void 684 pppoed_io_event(int fd, short evmask, void *ctx) 685 { 686 u_char buf[BPF_CAPTURE_SIZ], *pkt; 687 int lpkt, off; 688 pppoed_listener *_this; 689 struct ether_header *ether; 690 struct bpf_hdr *bpf; 691 692 _this = ctx; 693 694 PPPOED_ASSERT(_this != NULL); 695 696 lpkt = read(_this->bpf, buf, sizeof(buf)); 697 pkt = buf; 698 while (lpkt > 0) { 699 if (lpkt < sizeof(struct bpf_hdr)) { 700 pppoed_log(_this->self, LOG_WARNING, 701 "Received bad PPPoE packet: packet too short(%d)", 702 lpkt); 703 break; 704 } 705 bpf = (struct bpf_hdr *)pkt; 706 ether = (struct ether_header *)(pkt + bpf->bh_hdrlen); 707 ether->ether_type = ntohs(ether->ether_type); 708 if (memcmp(ether->ether_shost, _this->ether_addr, 709 ETHER_ADDR_LEN) == 0) 710 /* the packet is from myself */ 711 goto next_pkt; 712 off = bpf->bh_hdrlen + sizeof(struct ether_header); 713 if (lpkt < off + sizeof(struct pppoe_header)) { 714 pppoed_log(_this->self, LOG_WARNING, 715 "Received bad PPPoE packet: packet too short(%d)", 716 lpkt); 717 break; 718 } 719 pppoed_input(_this, ether->ether_shost, 720 (ether->ether_type == ETHERTYPE_PPPOEDISC)? 1 : 0, 721 pkt + off, lpkt - off); 722 next_pkt: 723 pkt = pkt + BPF_WORDALIGN(bpf->bh_hdrlen + 724 bpf->bh_caplen); 725 lpkt -= BPF_WORDALIGN(bpf->bh_hdrlen + bpf->bh_caplen); 726 } 727 return; 728 } 729 730 static void 731 pppoed_input(pppoed_listener *_this, uint8_t shost[ETHER_ADDR_LEN], int is_disc, 732 u_char *pkt, int lpkt) 733 { 734 hash_link *hl; 735 pppoe_session *session; 736 struct pppoe_header *pppoe; 737 struct pppoe_tlv *tlv; 738 u_char tlvspace[2048], *p_tlvspace; 739 int session_id; 740 slist tag_list; 741 const char *reason; 742 #define tlvspace_remaining() (sizeof(tlvspace) - (p_tlvspace - tlvspace)) 743 744 reason = ""; 745 p_tlvspace = tlvspace; 746 session = NULL; 747 748 pppoe = (struct pppoe_header *)pkt; 749 session_id = pppoe->session_id = ntohs(pppoe->session_id); 750 pppoe->length = ntohs(pppoe->length); 751 752 #ifdef PPPOED_DEBUG 753 if (is_disc) { 754 PPPOED_DBG((_this->self, DEBUG_LEVEL_1, 755 "Recv%s(%02x) ver=%d type=%d session-id=%d if=%s", 756 pppoe_code_string(pppoe->code), pppoe->code, 757 pppoe->ver, pppoe->type, pppoe->session_id, 758 _this->listen_ifname)); 759 } 760 #endif 761 pkt += sizeof(struct pppoe_header); 762 lpkt -= sizeof(struct pppoe_header); 763 764 if (lpkt < pppoe->length) { 765 reason = "received packet is shorter than " 766 "pppoe length field."; 767 goto bad_packet; 768 } 769 /* use PPPoE heade value as lpkt */ 770 lpkt = pppoe->length; 771 772 if (pppoe->type != PPPOE_RFC2516_TYPE || 773 pppoe->ver != PPPOE_RFC2516_VER) { 774 reason = "received packet has wrong version or type."; 775 goto bad_packet; 776 } 777 778 if (session_id != 0) { 779 hl = hash_lookup(_this->self->session_hash, (void *)session_id); 780 if (hl != NULL) 781 session = (pppoe_session *)hl->item; 782 } 783 if (!is_disc) { 784 if (session != NULL) 785 pppoe_session_input(session, pkt, pppoe->length); 786 return; 787 } 788 789 /* 790 * PPPoE-Discovery Packet proccessing. 791 */ 792 slist_init(&tag_list); 793 while (lpkt > 0) { 794 if (lpkt < 4) { 795 reason = "tlv list is broken. " 796 "Remaining octet is too short."; 797 goto fail; 798 } 799 if (tlvspace_remaining() < 4) { 800 reason = "parsing TAGs reached the buffer size limit."; 801 goto fail; 802 } 803 tlv = (struct pppoe_tlv *)p_tlvspace; 804 GETSHORT(tlv->type, pkt); 805 GETSHORT(tlv->length, pkt); 806 p_tlvspace += 4; 807 lpkt -= 4; 808 if (tlv->length > lpkt) { 809 reason = "tlv list is broken. length is wrong."; 810 goto fail; 811 } 812 if (tlvspace_remaining() < tlv->length) { 813 reason = "parsing TAGs reached the buffer size limit."; 814 goto fail; 815 } 816 if (tlv->length > 0) { 817 memcpy(tlv->value, pkt, tlv->length); 818 pkt += tlv->length; 819 lpkt -= tlv->length; 820 p_tlvspace += tlv->length; 821 p_tlvspace = (u_char *)ALIGN(p_tlvspace); 822 } 823 #ifdef PPPOED_DEBUG 824 if (debuglevel >= 2) 825 pppoed_log(_this->self, DEBUG_LEVEL_2, 826 "Recv%s tag %s(%04x)=%s", 827 pppoe_code_string(pppoe->code), 828 pppoe_tag_string(tlv->type), tlv->type, 829 pppoed_tlv_value_string(tlv)); 830 #endif 831 if (tlv->type == PPPOE_TAG_END_OF_LIST) 832 break; 833 if (slist_add(&tag_list, tlv) == NULL) { 834 goto fail; 835 } 836 } 837 switch (pppoe->code) { 838 case PPPOE_CODE_PADI: 839 if (_this->self->state != PPPOED_STATE_RUNNING) 840 break; 841 pppoed_recv_PADI(_this, shost, &tag_list); 842 break; 843 case PPPOE_CODE_PADR: 844 if (_this->self->state != PPPOED_STATE_RUNNING) 845 break; 846 pppoed_recv_PADR(_this, shost, &tag_list); 847 break; 848 case PPPOE_CODE_PADT: 849 PPPOED_DBG((_this->self, LOG_DEBUG, "RecvPADT")); 850 if (session != NULL) 851 pppoe_session_recv_PADT(session, &tag_list); 852 break; 853 } 854 slist_fini(&tag_list); 855 856 return; 857 fail: 858 slist_fini(&tag_list); 859 bad_packet: 860 pppoed_log(_this->self, LOG_INFO, 861 "Received a bad packet: code=%s(%02x) ver=%d type=%d session-id=%d" 862 " if=%s: %s", pppoe_code_string(pppoe->code), pppoe->code, 863 pppoe->ver, pppoe->type, pppoe->session_id, _this->listen_ifname, 864 reason); 865 } 866 867 static int 868 pppoed_output(pppoed_listener *_this, u_char *dhost, u_char *pkt, int lpkt) 869 { 870 int sz, iovc; 871 struct iovec iov[3]; 872 struct ether_header ether; 873 struct pppoe_header *pppoe; 874 u_char pad[ETHERMIN]; 875 876 memcpy(ether.ether_dhost, dhost, ETHER_ADDR_LEN); 877 memcpy(ether.ether_shost, _this->ether_addr, ETHER_ADDR_LEN); 878 879 iov[0].iov_base = ðer; 880 iov[0].iov_len = sizeof(struct ether_header); 881 ether.ether_type = htons(ETHERTYPE_PPPOEDISC); 882 iov[1].iov_base = pkt; 883 iov[1].iov_len = lpkt; 884 pppoe = (struct pppoe_header *)pkt; 885 pppoe->length = htons(lpkt - sizeof(struct pppoe_header)); 886 887 iovc = 2; 888 889 if (lpkt < ETHERMIN) { 890 memset(pad, 0, ETHERMIN - lpkt); 891 iov[2].iov_base = pad; 892 iov[2].iov_len = ETHERMIN - lpkt; 893 iovc++; 894 } 895 896 sz = writev(_this->bpf, iov, iovc); 897 898 return (sz > 0)? 0 : -1; 899 } 900 901 static void 902 pppoed_recv_PADR(pppoed_listener *_this, uint8_t shost[ETHER_ADDR_LEN], 903 slist *tag_list) 904 { 905 int session_id, shuffle_cnt; 906 pppoe_session *session; 907 pppoed *_pppoed; 908 909 _pppoed = _this->self; 910 if ((session = malloc(sizeof(pppoe_session))) == NULL) { 911 pppoed_log(_pppoed, LOG_ERR, "malloc() failed on %s(): %m", 912 __func__); 913 goto fail; 914 } 915 916 /* create session_id */ 917 shuffle_cnt = 0; 918 do { 919 session_id = (int)slist_remove_first( 920 &_pppoed->session_free_list); 921 if (session_id != PPPOED_SESSION_SHUFFLE_MARK) 922 break; 923 PPPOED_ASSERT(shuffle_cnt == 0); 924 if (shuffle_cnt++ > 0) { 925 pppoed_log(_pppoed, LOG_ERR, 926 "unexpected errror in %s(): session_free_list full", 927 __func__); 928 slist_add(&_pppoed->session_free_list, 929 (void *)PPPOED_SESSION_SHUFFLE_MARK); 930 goto fail; 931 } 932 slist_shuffle(&_pppoed->session_free_list); 933 slist_add(&_pppoed->session_free_list, 934 (void *)PPPOED_SESSION_SHUFFLE_MARK); 935 } while (1); 936 937 if (pppoe_session_init(session, _pppoed, _this->index, session_id, 938 shost) != 0) 939 goto fail; 940 941 hash_insert(_pppoed->session_hash, (void *)session_id, session); 942 943 if (pppoe_session_recv_PADR(session, tag_list) != 0) 944 goto fail; 945 946 session = NULL; /* don't free */ 947 /* FALLTHROUGH */ 948 fail: 949 if (session != NULL) 950 pppoe_session_fini(session); 951 return; 952 } 953 954 static void 955 pppoed_recv_PADI(pppoed_listener *_this, uint8_t shost[ETHER_ADDR_LEN], 956 slist *tag_list) 957 { 958 int len, accept_any_service_req; 959 const char *val, *service_name, *ac_name; 960 u_char bufspace[2048]; 961 u_char sn[2048], ac_name0[40]; 962 struct pppoe_header pppoe; 963 struct pppoe_tlv tlv, *tlv_hostuniq, *tlv0, *tlv_service_name; 964 bytebuffer *buf; 965 966 if ((buf = bytebuffer_wrap(bufspace, sizeof(bufspace))) == NULL) { 967 pppoed_log(_this->self, LOG_ERR, 968 "bytebuffer_wrap() failed on %s(): %m", __func__); 969 return; 970 } 971 bytebuffer_clear(buf); 972 973 tlv_hostuniq = NULL; 974 tlv_service_name = NULL; 975 976 service_name = ""; 977 if ((val = pppoed_listener_config_str(_this, "pppoe.service_name")) 978 != NULL) 979 service_name = val; 980 accept_any_service_req = pppoed_listener_config_str_equal(_this, 981 "pppoe.accept_any_service_request", "true", 1); 982 983 for (slist_itr_first(tag_list); slist_itr_has_next(tag_list);) { 984 tlv0 = slist_itr_next(tag_list); 985 if (tlv0->type == PPPOE_TAG_HOST_UNIQ) 986 tlv_hostuniq = tlv0; 987 if (tlv0->type == PPPOE_TAG_SERVICE_NAME) { 988 989 len = tlv0->length; 990 if (len >= sizeof(sn)) 991 goto fail; 992 993 memcpy(sn, tlv0->value, len); 994 sn[len] = '\0'; 995 996 if (strcmp(service_name, sn) == 0 || 997 (sn[0] == '\0' && accept_any_service_req)) 998 tlv_service_name = tlv0; 999 } 1000 } 1001 if (tlv_service_name == NULL) { 1002 pppoed_log(_this->self, LOG_INFO, 1003 "Deny PADI from=%02x:%02x:%02x:%02x:%02x:%02x " 1004 "service-name=%s host-uniq=%s if=%s: serviceName is " 1005 "not allowed.", shost[0], shost[1], 1006 shost[2], shost[3], shost[4], shost[5], sn, tlv_hostuniq? 1007 pppoed_tlv_value_string(tlv_hostuniq) : "none", 1008 _this->listen_ifname); 1009 goto fail; 1010 } 1011 1012 pppoed_log(_this->self, LOG_INFO, 1013 "RecvPADI from=%02x:%02x:%02x:%02x:%02x:%02x service-name=%s " 1014 "host-uniq=%s if=%s", shost[0], shost[1], shost[2], shost[3], 1015 shost[4], shost[5], sn, tlv_hostuniq? 1016 pppoed_tlv_value_string(tlv_hostuniq) : "none", 1017 _this->listen_ifname); 1018 1019 /* 1020 * PPPoE Header 1021 */ 1022 memset(&pppoe, 0, sizeof(pppoe)); 1023 pppoe.ver = PPPOE_RFC2516_VER; 1024 pppoe.type = PPPOE_RFC2516_TYPE; 1025 pppoe.code = PPPOE_CODE_PADO; 1026 bytebuffer_put(buf, &pppoe, sizeof(pppoe)); 1027 1028 /* 1029 * Tag - Service-Name 1030 */ 1031 tlv.type = htons(PPPOE_TAG_SERVICE_NAME); 1032 len = strlen(service_name); 1033 tlv.length = htons(len); 1034 bytebuffer_put(buf, &tlv, sizeof(tlv)); 1035 if (len > 0) 1036 bytebuffer_put(buf, service_name, len); 1037 1038 /* 1039 * Tag - Access Concentrator Name 1040 */ 1041 ac_name = pppoed_listener_config_str(_this, "pppoe.ac_name"); 1042 if (ac_name == NULL) { 1043 /* 1044 * use the ethernet address as default AC-Name. 1045 * suggested by RFC 2516. 1046 */ 1047 snprintf(ac_name0, sizeof(ac_name0), 1048 "%02x:%02x:%02x:%02x:%02x:%02x", _this->ether_addr[0], 1049 _this->ether_addr[1], _this->ether_addr[2], 1050 _this->ether_addr[3], _this->ether_addr[4], 1051 _this->ether_addr[5]); 1052 ac_name = ac_name0; 1053 } 1054 1055 tlv.type = htons(PPPOE_TAG_AC_NAME); 1056 len = strlen(ac_name); 1057 tlv.length = htons(len); 1058 bytebuffer_put(buf, &tlv, sizeof(tlv)); 1059 bytebuffer_put(buf, ac_name, len); 1060 1061 /* 1062 * Tag - ac-cookie 1063 */ 1064 if (_this->self->acookie_hash != NULL) { 1065 /* 1066 * search next ac-cookie. 1067 * (XXX it will loop in uint32_t boundaly) 1068 */ 1069 do { 1070 _this->self->acookie_next += 1; 1071 } 1072 while(hash_lookup(_this->self->acookie_hash, 1073 (void *)_this->self->acookie_next) != NULL); 1074 1075 tlv.type = htons(PPPOE_TAG_AC_COOKIE); 1076 tlv.length = ntohs(sizeof(uint32_t)); 1077 bytebuffer_put(buf, &tlv, sizeof(tlv)); 1078 bytebuffer_put(buf, &_this->self->acookie_next, 1079 sizeof(uint32_t)); 1080 } 1081 1082 /* 1083 * Tag - Host-Uniq 1084 */ 1085 if (tlv_hostuniq != NULL) { 1086 tlv.type = htons(PPPOE_TAG_HOST_UNIQ); 1087 tlv.length = ntohs(tlv_hostuniq->length); 1088 bytebuffer_put(buf, &tlv, sizeof(tlv)); 1089 bytebuffer_put(buf, tlv_hostuniq->value, 1090 tlv_hostuniq->length); 1091 } 1092 1093 /* 1094 * Tag - End-Of-List 1095 */ 1096 tlv.type = htons(PPPOE_TAG_END_OF_LIST); 1097 tlv.length = ntohs(0); 1098 bytebuffer_put(buf, &tlv, sizeof(tlv)); 1099 1100 bytebuffer_flip(buf); 1101 1102 if (pppoed_output(_this, shost, bytebuffer_pointer(buf), 1103 bytebuffer_remaining(buf)) != 0) { 1104 pppoed_log(_this->self, LOG_ERR, "pppoed_output() failed:%m"); 1105 } 1106 pppoed_log(_this->self, LOG_INFO, 1107 "SendPADO to=%02x:%02x:%02x:%02x:%02x:%02x serviceName=%s " 1108 "acName=%s hostUniq=%s eol if=%s", shost[0], shost[1], shost[2], 1109 shost[3], shost[4], shost[5], service_name, ac_name, 1110 tlv_hostuniq? pppoed_tlv_value_string(tlv_hostuniq) : "none", 1111 _this->listen_ifname); 1112 /* FALLTHROUGH */ 1113 fail: 1114 bytebuffer_unwrap(buf); 1115 bytebuffer_destroy(buf); 1116 } 1117 1118 /* 1119 * log 1120 */ 1121 static void 1122 pppoed_log(pppoed *_this, int prio, const char *fmt, ...) 1123 { 1124 char logbuf[BUFSIZ]; 1125 va_list ap; 1126 1127 PPPOED_ASSERT(_this != NULL); 1128 va_start(ap, fmt); 1129 #ifdef PPPOED_MULITPLE 1130 snprintf(logbuf, sizeof(logbuf), "pppoed id=%u %s", _this->id, fmt); 1131 #else 1132 snprintf(logbuf, sizeof(logbuf), "pppoed %s", fmt); 1133 #endif 1134 vlog_printf(prio, logbuf, ap); 1135 va_end(ap); 1136 } 1137 1138 #define NAME_VAL(x) { x, #x } 1139 static struct _label_name { 1140 int label; 1141 const char *name; 1142 } pppoe_code_labels[] = { 1143 NAME_VAL(PPPOE_CODE_PADI), 1144 NAME_VAL(PPPOE_CODE_PADO), 1145 NAME_VAL(PPPOE_CODE_PADR), 1146 NAME_VAL(PPPOE_CODE_PADS), 1147 NAME_VAL(PPPOE_CODE_PADT), 1148 #ifdef PPPOED_DEBUG 1149 }, pppoe_tlv_labels[] = { 1150 NAME_VAL(PPPOE_TAG_END_OF_LIST), 1151 NAME_VAL(PPPOE_TAG_SERVICE_NAME), 1152 NAME_VAL(PPPOE_TAG_AC_NAME), 1153 NAME_VAL(PPPOE_TAG_HOST_UNIQ), 1154 NAME_VAL(PPPOE_TAG_AC_COOKIE), 1155 NAME_VAL(PPPOE_TAG_VENDOR_SPECIFIC), 1156 NAME_VAL(PPPOE_TAG_RELAY_SESSION_ID), 1157 NAME_VAL(PPPOE_TAG_SERVICE_NAME_ERROR), 1158 NAME_VAL(PPPOE_TAG_AC_SYSTEM_ERROR), 1159 NAME_VAL(PPPOE_TAG_GENERIC_ERROR) 1160 #endif 1161 }; 1162 #define LABEL_TO_STRING(func_name, label_names, prefix_len) \ 1163 static const char * \ 1164 func_name(int code) \ 1165 { \ 1166 int i; \ 1167 \ 1168 for (i = 0; i < countof(label_names); i++) { \ 1169 if (label_names[i].label == code) \ 1170 return label_names[i].name + prefix_len;\ 1171 } \ 1172 \ 1173 return "UNKNOWN"; \ 1174 } 1175 LABEL_TO_STRING(pppoe_code_string, pppoe_code_labels, 11) 1176 #ifdef PPPOED_DEBUG 1177 LABEL_TO_STRING(pppoe_tag_string, pppoe_tlv_labels, 10) 1178 #endif 1179 1180 const char * 1181 pppoed_tlv_value_string(struct pppoe_tlv *tlv) 1182 { 1183 int i; 1184 char buf[3]; 1185 static char _tlv_string_value[8192]; 1186 1187 _tlv_string_value[0] = '\0'; 1188 for (i = 0; i < tlv->length; i++) { 1189 snprintf(buf, sizeof(buf), "%02x", tlv->value[i]); 1190 strlcat(_tlv_string_value, buf, 1191 sizeof(_tlv_string_value)); 1192 } 1193 return _tlv_string_value; 1194 } 1195 1196 /* 1197 * misc 1198 */ 1199 static int 1200 session_id_cmp(void *a, void *b) 1201 { 1202 int ia, ib; 1203 1204 ia = (int)a; 1205 ib = (int)b; 1206 1207 return ib - ia; 1208 } 1209 1210 static uint32_t 1211 session_id_hash(void *a, size_t siz) 1212 { 1213 int ia; 1214 1215 ia = (int)a; 1216 1217 return ia % siz; 1218 } 1219