1/*********************************************************************** 2* 3* relay.c 4* 5* Implementation of PPPoE relay 6* 7* Copyright (C) 2001 Roaring Penguin Software Inc. 8* 9* This program may be distributed according to the terms of the GNU 10* General Public License, version 2 or (at your option) any later version. 11* 12* LIC: GPL 13* 14* $Id: relay.c,v 1.1.1.1 2008/10/15 03:30:51 james26_jang Exp $ 15* 16***********************************************************************/ 17static char const RCSID[] = 18"$Id: relay.c,v 1.1.1.1 2008/10/15 03:30:51 james26_jang Exp $"; 19 20#define _GNU_SOURCE 1 /* For SA_RESTART */ 21 22#include "relay.h" 23 24#include <signal.h> 25 26#ifdef HAVE_SYSLOG_H 27#include <syslog.h> 28#endif 29 30#ifdef HAVE_GETOPT_H 31#include <getopt.h> 32#endif 33 34#include <stdlib.h> 35#include <string.h> 36#include <errno.h> 37 38#ifdef HAVE_SYS_TIME_H 39#include <sys/time.h> 40#endif 41 42#ifdef HAVE_SYS_UIO_H 43#include <sys/uio.h> 44#endif 45 46#ifdef HAVE_UNISTD_H 47#include <unistd.h> 48#endif 49 50// 2009.12 James. { 51#include <bcmnvram.h> 52 53struct RelayClientStruct { 54 unsigned char mac[18]; // MAC address of the relaying client 55 struct RelayClientStruct *next; 56}; 57 58typedef struct RelayClientStruct RelayClient; 59// 2009.12 James. } 60 61 62/* Interfaces (max MAX_INTERFACES) */ 63PPPoEInterface Interfaces[MAX_INTERFACES]; 64int NumInterfaces; 65 66/* Relay info */ 67int NumSessions; 68int MaxSessions; 69PPPoESession *AllSessions; 70PPPoESession *FreeSessions; 71PPPoESession *ActiveSessions; 72 73SessionHash *AllHashes; 74SessionHash *FreeHashes; 75SessionHash *Buckets[HASHTAB_SIZE]; 76 77volatile unsigned int Epoch = 0; 78volatile unsigned int CleanCounter = 0; 79 80/* How often to clean up stale sessions? */ 81#define MIN_CLEAN_PERIOD 30 /* Minimum period to run cleaner */ 82#define TIMEOUT_DIVISOR 20 /* How often to run cleaner per timeout period */ 83unsigned int CleanPeriod = MIN_CLEAN_PERIOD; 84 85/* How long a session can be idle before it is cleaned up? */ 86unsigned int IdleTimeout = MIN_CLEAN_PERIOD * TIMEOUT_DIVISOR; 87 88/* Pipe for breaking select() to initiate periodic cleaning */ 89int CleanPipe[2]; 90 91/* Our relay: if_index followed by peer_mac */ 92#define MY_RELAY_TAG_LEN (sizeof(int) + ETH_ALEN) 93 94/* Hack for daemonizing */ 95#define CLOSEFD 64 96 97/********************************************************************** 98*%FUNCTION: keepDescriptor 99*%ARGUMENTS: 100* fd -- a file descriptor 101*%RETURNS: 102* 1 if descriptor should NOT be closed during daemonizing; 0 otherwise. 103***********************************************************************/ 104static int 105keepDescriptor(int fd) 106{ 107 int i; 108 if (fd == CleanPipe[0] || fd == CleanPipe[1]) return 1; 109 for (i=0; i<NumInterfaces; i++) { 110 if (fd == Interfaces[i].discoverySock || 111 fd == Interfaces[i].sessionSock) return 1; 112 } 113 return 0; 114} 115 116/********************************************************************** 117*%FUNCTION: addTag 118*%ARGUMENTS: 119* packet -- a PPPoE packet 120* tag -- tag to add 121*%RETURNS: 122* -1 if no room in packet; number of bytes added otherwise. 123*%DESCRIPTION: 124* Inserts a tag as the first tag in a PPPoE packet. 125***********************************************************************/ 126int 127addTag(PPPoEPacket *packet, PPPoETag const *tag) 128{ 129 return insertBytes(packet, packet->payload, tag, 130 ntohs(tag->length) + TAG_HDR_SIZE); 131} 132 133/********************************************************************** 134*%FUNCTION: insertBytes 135*%ARGUMENTS: 136* packet -- a PPPoE packet 137* loc -- location at which to insert bytes of data 138* bytes -- the data to insert 139* len -- length of data to insert 140*%RETURNS: 141* -1 if no room in packet; len otherwise. 142*%DESCRIPTION: 143* Inserts "len" bytes of data at location "loc" in "packet", moving all 144* other data up to make room. 145***********************************************************************/ 146int 147insertBytes(PPPoEPacket *packet, 148 unsigned char *loc, 149 void const *bytes, 150 int len) 151{ 152 int toMove; 153 int plen = ntohs(packet->length); 154 /* Sanity checks */ 155 if (loc < packet->payload || 156 loc > packet->payload + plen || 157 len + plen > MAX_PPPOE_PAYLOAD) { 158 return -1; 159 } 160 161 toMove = (packet->payload + plen) - loc; 162 memmove(loc+len, loc, toMove); 163 memcpy(loc, bytes, len); 164 packet->length = htons(plen + len); 165 return len; 166} 167 168/********************************************************************** 169*%FUNCTION: removeBytes 170*%ARGUMENTS: 171* packet -- a PPPoE packet 172* loc -- location at which to remove bytes of data 173* len -- length of data to remove 174*%RETURNS: 175* -1 if there was a problem, len otherwise 176*%DESCRIPTION: 177* Removes "len" bytes of data from location "loc" in "packet", moving all 178* other data down to close the gap 179***********************************************************************/ 180int 181removeBytes(PPPoEPacket *packet, 182 unsigned char *loc, 183 int len) 184{ 185 int toMove; 186 int plen = ntohs(packet->length); 187 /* Sanity checks */ 188 if (len < 0 || len > plen || 189 loc < packet->payload || 190 loc + len > packet->payload + plen) { 191 return -1; 192 } 193 194 toMove = ((packet->payload + plen) - loc) - len; 195 memmove(loc, loc+len, toMove); 196 packet->length = htons(plen - len); 197 return len; 198} 199 200/********************************************************************** 201*%FUNCTION: usage 202*%ARGUMENTS: 203* argv0 -- program name 204*%RETURNS: 205* Nothing 206*%DESCRIPTION: 207* Prints usage information and exits. 208***********************************************************************/ 209void 210usage(char const *argv0) 211{ 212 fprintf(stderr, "Usage: %s [options]\n", argv0); 213 fprintf(stderr, "Options:\n"); 214 fprintf(stderr, " -S if_name -- Specify interface for PPPoE Server\n"); 215 fprintf(stderr, " -C if_name -- Specify interface for PPPoE Client\n"); 216 fprintf(stderr, " -B if_name -- Specify interface for both clients and server\n"); 217 fprintf(stderr, " -n nsess -- Maxmimum number of sessions to relay\n"); 218 fprintf(stderr, " -i timeout -- Idle timeout in seconds (0 = no timeout)\n"); 219 fprintf(stderr, " -F -- Do not fork into background\n"); 220 fprintf(stderr, " -h -- Print this help message\n"); 221 222 fprintf(stderr, "\nPPPoE Version %s, Copyright (C) 2001 Roaring Penguin Software Inc.\n", VERSION); 223 fprintf(stderr, "PPPoE comes with ABSOLUTELY NO WARRANTY.\n"); 224 fprintf(stderr, "This is free software, and you are welcome to redistribute it under the terms\n"); 225 fprintf(stderr, "of the GNU General Public License, version 2 or any later version.\n"); 226 fprintf(stderr, "http://www.roaringpenguin.com\n"); 227 exit(EXIT_SUCCESS); 228} 229 230// 2009.12 James. { 231RelayClient *head = NULL; 232 233void build_mac(char *seed){ 234 RelayClient **client_list = &head, *new_client, *shown_client; 235 int i, len; 236 237 if(seed == NULL || strlen(seed) != 17) 238 return; 239 240 len = sizeof(RelayClient); 241 new_client = (RelayClient *)malloc(len); 242 if(new_client == NULL) 243 return; 244 memset(new_client, 0, len); 245 246 strcpy(new_client->mac, seed); 247 new_client->next = NULL; 248 249 while(*client_list != NULL){ 250 if(!strcmp((*client_list)->mac, new_client->mac)){ 251 free(new_client); 252 return; 253 } 254 255 client_list = &((*client_list)->next); 256 } 257 *client_list = new_client; 258 259 build_redirect_rules(); 260} 261 262int build_redirect_rules(){ 263 FILE *nat_fp, *redirect_fp, *del_redirect_fp; 264 char tmp_buf[1024]; 265 char lan_ipaddr_t[16], lan_netmask_t[16]; 266 RelayClient *follow_client; 267 268 if(head == NULL) 269 return 0; 270 271 if((redirect_fp = fopen("/tmp/redirect_rules", "w+")) == NULL){ 272 fprintf(stderr, "*** Can't make the file of the redirect rules! ***\n"); 273 return 0; 274 } 275 276 if((nat_fp = fopen("/tmp/nat_rules", "r")) != NULL){ 277 memset(tmp_buf, 0, sizeof(tmp_buf)); 278 while((fgets(tmp_buf, sizeof(tmp_buf), nat_fp)) != NULL 279 && strncmp(tmp_buf, "COMMIT", 6) != 0){ 280 fprintf(redirect_fp, "%s", tmp_buf); 281 memset(tmp_buf, 0, sizeof(tmp_buf)); 282 } 283 284 fclose(nat_fp); 285 } 286 else{ 287 if((del_redirect_fp = fopen("/tmp/del_redirect_rules", "w+")) != NULL){ 288 fprintf(del_redirect_fp, "*nat\n"); 289 fprintf(del_redirect_fp, "-F\n"); 290 fprintf(del_redirect_fp, "COMMIT\n"); 291 292 fclose(del_redirect_fp); 293 } 294 295 fprintf(redirect_fp, "*nat\n"); 296 fprintf(redirect_fp, ":PREROUTING ACCEPT [0:0]\n"); 297 } 298 299 memset(lan_ipaddr_t, 0, sizeof(lan_ipaddr_t)); 300 memset(lan_netmask_t, 0, sizeof(lan_netmask_t)); 301 302 strcpy(lan_ipaddr_t, nvram_safe_get("lan_ipaddr_t")); 303 strcpy(lan_netmask_t, nvram_safe_get("lan_netmask_t")); 304 305 fprintf(redirect_fp, "-A PREROUTING -d ! %s/%s -p tcp --dport 80 -j DNAT --to-destination %s:18017\n", lan_ipaddr_t, lan_netmask_t, lan_ipaddr_t); 306 307 if(head != NULL){ 308 follow_client = head; 309 for(; follow_client != NULL; follow_client = follow_client->next) 310 fprintf(redirect_fp, "-A PREROUTING -m mac --mac-source %s -p udp --dport 53 -j ACCEPT\n", follow_client->mac); 311 } 312 313 fprintf(redirect_fp, "-A PREROUTING -p udp --dport 53 -j DNAT --to-destination %s:18018\n", lan_ipaddr_t); 314 315 fprintf(redirect_fp, "COMMIT\n"); 316 317 fclose(redirect_fp); 318 319 nvram_set("add_relay_client", "1"); 320 321 return 1; 322}//*/ 323// 2009.12 James. } 324 325/********************************************************************** 326*%FUNCTION: main 327*%ARGUMENTS: 328* argc, argv -- usual suspects 329*%RETURNS: 330* EXIT_SUCCESS or EXIT_FAILURE 331*%DESCRIPTION: 332* Main program. Options: 333* -C ifname -- Use interface for PPPoE clients 334* -S ifname -- Use interface for PPPoE servers 335* -B ifname -- Use interface for both clients and servers 336* -n sessions -- Maximum of "n" sessions 337***********************************************************************/ 338int 339main(int argc, char *argv[]) 340{ 341 int opt; 342 int nsess = DEFAULT_SESSIONS; 343 struct sigaction sa; 344 int beDaemon = 1; 345 openlog("pppoe-relay", LOG_PID, LOG_DAEMON); 346 347 while((opt = getopt(argc, argv, "hC:S:B:n:i:F")) != -1) { 348 switch(opt) { 349 case 'h': 350 usage(argv[0]); 351 break; 352 case 'F': 353 beDaemon = 0; 354 break; 355 case 'C': 356 addInterface(optarg, 1, 0); 357 break; 358 case 'S': 359 addInterface(optarg, 0, 1); 360 break; 361 case 'B': 362 addInterface(optarg, 1, 1); 363 break; 364 case 'i': 365 if (sscanf(optarg, "%u", &IdleTimeout) != 1) { 366 fprintf(stderr, "Illegal argument to -i: should be -i timeout\n"); 367 exit(EXIT_FAILURE); 368 } 369 CleanPeriod = IdleTimeout / TIMEOUT_DIVISOR; 370 if (CleanPeriod < MIN_CLEAN_PERIOD) CleanPeriod = MIN_CLEAN_PERIOD; 371 break; 372 case 'n': 373 if (sscanf(optarg, "%d", &nsess) != 1) { 374 fprintf(stderr, "Illegal argument to -n: should be -n #sessions\n"); 375 exit(EXIT_FAILURE); 376 } 377 if (nsess < 1 || nsess > 65534) { 378 fprintf(stderr, "Illegal argument to -n: must range from 1 to 65534\n"); 379 exit(EXIT_FAILURE); 380 } 381 break; 382 default: 383 usage(argv[0]); 384 } 385 } 386 387#ifdef USE_LINUX_PACKET 388#ifndef HAVE_STRUCT_SOCKADDR_LL 389 fprintf(stderr, "The PPPoE relay does not work on Linux 2.0 kernels.\n"); 390 exit(EXIT_FAILURE); 391#endif 392#endif 393 394 /* Check that at least two interfaces were defined */ 395 if (NumInterfaces < 2) { 396 fprintf(stderr, "%s: Must define at least two interfaces\n", 397 argv[0]); 398 exit(EXIT_FAILURE); 399 } 400 401 /* Make a pipe for the cleaner */ 402 if (pipe(CleanPipe) < 0) { 403 fatalSys("pipe"); 404 } 405 406 /* Set up alarm handler */ 407 sa.sa_handler = alarmHandler; 408 sigemptyset(&sa.sa_mask); 409 sa.sa_flags = SA_RESTART; 410 if (sigaction(SIGALRM, &sa, NULL) < 0) { 411 fatalSys("sigaction"); 412 } 413 414 /* Allocate memory for sessions, etc. */ 415 initRelay(nsess); 416 417 /* Daemonize -- UNIX Network Programming, Vol. 1, Stevens */ 418 if (beDaemon) { 419 int i; 420 i = fork(); 421 if (i < 0) { 422 fatalSys("fork"); 423 } else if (i != 0) { 424 /* parent */ 425 exit(0); 426 } 427 setsid(); 428 signal(SIGHUP, SIG_IGN); 429 i = fork(); 430 if (i < 0) { 431 fatalSys("fork"); 432 } else if (i != 0) { 433 exit(0); 434 } 435 436 chdir("/"); 437 closelog(); 438 for (i=0; i<CLOSEFD; i++) { 439 if (!keepDescriptor(i)) { 440 close(i); 441 } 442 } 443 /* We nuked our syslog descriptor... */ 444 openlog("pppoe-relay", LOG_PID, LOG_DAEMON); 445 } 446 447 /* Kick off SIGALRM if there is an idle timeout */ 448 if (IdleTimeout) alarm(1); 449 450 /* Enter the relay loop */ 451 relayLoop(); 452 453 /* Shouldn't ever get here... */ 454 return EXIT_FAILURE; 455} 456 457/********************************************************************** 458*%FUNCTION: addInterface 459*%ARGUMENTS: 460* ifname -- interface name 461* clientOK -- true if this interface should relay PADI, PADR packets. 462* acOK -- true if this interface should relay PADO, PADS packets. 463*%RETURNS: 464* Nothing 465*%DESCRIPTION: 466* Opens an interface; sets up discovery and session sockets. 467***********************************************************************/ 468void 469addInterface(char const *ifname, 470 int clientOK, 471 int acOK) 472{ 473 PPPoEInterface *i; 474 int j; 475 for (j=0; j<NumInterfaces; j++) { 476 if (!strncmp(Interfaces[j].name, ifname, IFNAMSIZ)) { 477 fprintf(stderr, "Interface %s specified more than once.\n", ifname); 478 exit(EXIT_FAILURE); 479 } 480 } 481 482 if (NumInterfaces >= MAX_INTERFACES) { 483 fprintf(stderr, "Too many interfaces (%d max)\n", 484 MAX_INTERFACES); 485 exit(EXIT_FAILURE); 486 } 487 i = &Interfaces[NumInterfaces++]; 488 strncpy(i->name, ifname, IFNAMSIZ); 489 i->name[IFNAMSIZ] = 0; 490 491 i->discoverySock = openInterface(ifname, Eth_PPPOE_Discovery, i->mac); 492 i->sessionSock = openInterface(ifname, Eth_PPPOE_Session, NULL); 493 i->clientOK = clientOK; 494 i->acOK = acOK; 495} 496 497/********************************************************************** 498*%FUNCTION: initRelay 499*%ARGUMENTS: 500* nsess -- maximum allowable number of sessions 501*%RETURNS: 502* Nothing 503*%DESCRIPTION: 504* Initializes relay hash table and session tables. 505***********************************************************************/ 506void 507initRelay(int nsess) 508{ 509 int i; 510 NumSessions = 0; 511 MaxSessions = nsess; 512 513 AllSessions = calloc(MaxSessions, sizeof(PPPoESession)); 514 if (!AllSessions) { 515 rp_fatal("Unable to allocate memory for PPPoE session table"); 516 } 517 AllHashes = calloc(MaxSessions*2, sizeof(SessionHash)); 518 if (!AllHashes) { 519 rp_fatal("Unable to allocate memory for PPPoE hash table"); 520 } 521 522 /* Initialize sessions in a linked list */ 523 AllSessions[0].prev = NULL; 524 if (MaxSessions > 1) { 525 AllSessions[0].next = &AllSessions[1]; 526 } else { 527 AllSessions[0].next = NULL; 528 } 529 for (i=1; i<MaxSessions-1; i++) { 530 AllSessions[i].prev = &AllSessions[i-1]; 531 AllSessions[i].next = &AllSessions[i+1]; 532 } 533 if (MaxSessions > 1) { 534 AllSessions[MaxSessions-1].prev = &AllSessions[MaxSessions-2]; 535 AllSessions[MaxSessions-1].next = NULL; 536 } 537 538 FreeSessions = AllSessions; 539 ActiveSessions = NULL; 540 541 /* Initialize session numbers which we hand out */ 542 for (i=0; i<MaxSessions; i++) { 543 AllSessions[i].sesNum = htons((UINT16_t) i+1); 544 } 545 546 /* Initialize hashes in a linked list */ 547 AllHashes[0].prev = NULL; 548 AllHashes[0].next = &AllHashes[1]; 549 for (i=1; i<2*MaxSessions-1; i++) { 550 AllHashes[i].prev = &AllHashes[i-1]; 551 AllHashes[i].next = &AllHashes[i+1]; 552 } 553 AllHashes[2*MaxSessions-1].prev = &AllHashes[2*MaxSessions-2]; 554 AllHashes[2*MaxSessions-1].next = NULL; 555 556 FreeHashes = AllHashes; 557} 558 559/********************************************************************** 560*%FUNCTION: createSession 561*%ARGUMENTS: 562* ac -- Ethernet interface on access-concentrator side 563* cli -- Ethernet interface on client side 564* acMac -- Access concentrator's MAC address 565* cliMac -- Client's MAC address 566* acSess -- Access concentrator's session ID. 567*%RETURNS: 568* PPPoESession structure; NULL if one could not be allocated 569*%DESCRIPTION: 570* Initializes relay hash table and session tables. 571***********************************************************************/ 572PPPoESession * 573createSession(PPPoEInterface const *ac, 574 PPPoEInterface const *cli, 575 unsigned char const *acMac, 576 unsigned char const *cliMac, 577 UINT16_t acSes) 578{ 579 PPPoESession *sess; 580 SessionHash *acHash, *cliHash; 581 582 if (NumSessions >= MaxSessions) { 583 printErr("Maximum number of sessions reached -- cannot create new session"); 584 return NULL; 585 } 586 587 /* Grab a free session */ 588 sess = FreeSessions; 589 FreeSessions = sess->next; 590 NumSessions++; 591 592 /* Link it to the active list */ 593 sess->next = ActiveSessions; 594 if (sess->next) { 595 sess->next->prev = sess; 596 } 597 ActiveSessions = sess; 598 sess->prev = NULL; 599 600 sess->epoch = Epoch; 601 602 /* Get two hash entries */ 603 acHash = FreeHashes; 604 cliHash = acHash->next; 605 FreeHashes = cliHash->next; 606 607 acHash->peer = cliHash; 608 cliHash->peer = acHash; 609 610 sess->acHash = acHash; 611 sess->clientHash = cliHash; 612 613 acHash->interface = ac; 614 cliHash->interface = cli; 615 616 memcpy(acHash->peerMac, acMac, ETH_ALEN); 617 acHash->sesNum = acSes; 618 acHash->ses = sess; 619 620 memcpy(cliHash->peerMac, cliMac, ETH_ALEN); 621 cliHash->sesNum = sess->sesNum; 622 cliHash->ses = sess; 623 624 addHash(acHash); 625 addHash(cliHash); 626 627 /* Log */ 628 syslog(LOG_INFO, 629 "Opened session: server=%02x:%02x:%02x:%02x:%02x:%02x(%s:%d), client=%02x:%02x:%02x:%02x:%02x:%02x(%s:%d)", 630 acHash->peerMac[0], acHash->peerMac[1], 631 acHash->peerMac[2], acHash->peerMac[3], 632 acHash->peerMac[4], acHash->peerMac[5], 633 acHash->interface->name, 634 ntohs(acHash->sesNum), 635 cliHash->peerMac[0], cliHash->peerMac[1], 636 cliHash->peerMac[2], cliHash->peerMac[3], 637 cliHash->peerMac[4], cliHash->peerMac[5], 638 cliHash->interface->name, 639 ntohs(cliHash->sesNum)); 640 641 return sess; 642} 643 644/********************************************************************** 645*%FUNCTION: freeSession 646*%ARGUMENTS: 647* ses -- session to free 648* msg -- extra message to log on syslog. 649*%RETURNS: 650* Nothing 651*%DESCRIPTION: 652* Frees data used by a PPPoE session -- adds hashes and session back 653* to the free list 654***********************************************************************/ 655void 656freeSession(PPPoESession *ses, char const *msg) 657{ 658 syslog(LOG_INFO, 659 "Closed session: server=%02x:%02x:%02x:%02x:%02x:%02x(%s:%d), client=%02x:%02x:%02x:%02x:%02x:%02x(%s:%d): %s", 660 ses->acHash->peerMac[0], ses->acHash->peerMac[1], 661 ses->acHash->peerMac[2], ses->acHash->peerMac[3], 662 ses->acHash->peerMac[4], ses->acHash->peerMac[5], 663 ses->acHash->interface->name, 664 ntohs(ses->acHash->sesNum), 665 ses->clientHash->peerMac[0], ses->clientHash->peerMac[1], 666 ses->clientHash->peerMac[2], ses->clientHash->peerMac[3], 667 ses->clientHash->peerMac[4], ses->clientHash->peerMac[5], 668 ses->clientHash->interface->name, 669 ntohs(ses->clientHash->sesNum), msg); 670 671 /* Unlink from active sessions */ 672 if (ses->prev) { 673 ses->prev->next = ses->next; 674 } else { 675 ActiveSessions = ses->next; 676 } 677 if (ses->next) { 678 ses->next->prev = ses->prev; 679 } 680 681 /* Link onto free list -- this is a singly-linked list, so 682 we do not care about prev */ 683 ses->next = FreeSessions; 684 FreeSessions = ses; 685 686 unhash(ses->acHash); 687 unhash(ses->clientHash); 688 NumSessions--; 689} 690 691/********************************************************************** 692*%FUNCTION: unhash 693*%ARGUMENTS: 694* sh -- session hash to free 695*%RETURNS: 696* Nothing 697*%DESCRIPTION: 698* Frees a session hash -- takes it out of hash table and puts it on 699* free list. 700***********************************************************************/ 701void 702unhash(SessionHash *sh) 703{ 704 unsigned int b = hash(sh->peerMac, sh->sesNum) % HASHTAB_SIZE; 705 if (sh->prev) { 706 sh->prev->next = sh->next; 707 } else { 708 Buckets[b] = sh->next; 709 } 710 711 if (sh->next) { 712 sh->next->prev = sh->prev; 713 } 714 715 /* Add to free list (singly-linked) */ 716 sh->next = FreeHashes; 717 FreeHashes = sh; 718} 719 720/********************************************************************** 721*%FUNCTION: addHash 722*%ARGUMENTS: 723* sh -- a session hash 724*%RETURNS: 725* Nothing 726*%DESCRIPTION: 727* Adds a SessionHash to the hash table 728***********************************************************************/ 729void 730addHash(SessionHash *sh) 731{ 732 unsigned int b = hash(sh->peerMac, sh->sesNum) % HASHTAB_SIZE; 733 sh->next = Buckets[b]; 734 sh->prev = NULL; 735 if (sh->next) { 736 sh->next->prev = sh; 737 } 738 Buckets[b] = sh; 739} 740 741/********************************************************************** 742*%FUNCTION: hash 743*%ARGUMENTS: 744* mac -- an Ethernet address 745* sesNum -- a session number 746*%RETURNS: 747* A hash value combining Ethernet address with session number. 748* Currently very simplistic; we may need to experiment with different 749* hash values. 750***********************************************************************/ 751unsigned int 752hash(unsigned char const *mac, UINT16_t sesNum) 753{ 754 unsigned int ans1 = 755 ((unsigned int) mac[0]) | 756 (((unsigned int) mac[1]) << 8) | 757 (((unsigned int) mac[2]) << 16) | 758 (((unsigned int) mac[3]) << 24); 759 unsigned int ans2 = 760 ((unsigned int) sesNum) | 761 (((unsigned int) mac[4]) << 16) | 762 (((unsigned int) mac[5]) << 24); 763 return ans1 ^ ans2; 764} 765 766/********************************************************************** 767*%FUNCTION: findSession 768*%ARGUMENTS: 769* mac -- an Ethernet address 770* sesNum -- a session number 771*%RETURNS: 772* The session hash for peer address "mac", session number sesNum 773***********************************************************************/ 774SessionHash * 775findSession(unsigned char const *mac, UINT16_t sesNum) 776{ 777 unsigned int b = hash(mac, sesNum) % HASHTAB_SIZE; 778 SessionHash *sh = Buckets[b]; 779 while(sh) { 780 if (!memcmp(mac, sh->peerMac, ETH_ALEN) && sesNum == sh->sesNum) { 781 return sh; 782 } 783 sh = sh->next; 784 } 785 return NULL; 786} 787 788/********************************************************************** 789*%FUNCTION: fatalSys 790*%ARGUMENTS: 791* str -- error message 792*%RETURNS: 793* Nothing 794*%DESCRIPTION: 795* Prints a message plus the errno value to stderr and syslog and exits. 796***********************************************************************/ 797void 798fatalSys(char const *str) 799{ 800 char buf[1024]; 801 sprintf(buf, "%.256s: %.256s", str, strerror(errno)); 802 printErr(buf); 803 exit(EXIT_FAILURE); 804} 805 806/********************************************************************** 807*%FUNCTION: sysErr 808*%ARGUMENTS: 809* str -- error message 810*%RETURNS: 811* Nothing 812*%DESCRIPTION: 813* Prints a message plus the errno value to syslog. 814***********************************************************************/ 815void 816sysErr(char const *str) 817{ 818 char buf[1024]; 819 sprintf(buf, "%.256s: %.256s", str, strerror(errno)); 820 printErr(buf); 821} 822 823/********************************************************************** 824*%FUNCTION: rp_fatal 825*%ARGUMENTS: 826* str -- error message 827*%RETURNS: 828* Nothing 829*%DESCRIPTION: 830* Prints a message to stderr and syslog and exits. 831***********************************************************************/ 832void 833rp_fatal(char const *str) 834{ 835 printErr(str); 836 exit(EXIT_FAILURE); 837} 838 839/********************************************************************** 840*%FUNCTION: relayLoop 841*%ARGUMENTS: 842* None 843*%RETURNS: 844* Nothing 845*%DESCRIPTION: 846* Runs the relay loop. This function never returns 847***********************************************************************/ 848void 849relayLoop() 850{ 851 fd_set readable, readableCopy; 852 int maxFD; 853 int i, r; 854 int sock; 855 856 /* Build the select set */ 857 FD_ZERO(&readable); 858 maxFD = 0; 859 for (i=0; i<NumInterfaces; i++) { 860 sock = Interfaces[i].discoverySock; 861 if (sock > maxFD) maxFD = sock; 862 FD_SET(sock, &readable); 863 sock = Interfaces[i].sessionSock; 864 if (sock > maxFD) maxFD = sock; 865 FD_SET(sock, &readable); 866 if (CleanPipe[0] > maxFD) maxFD = CleanPipe[0]; 867 FD_SET(CleanPipe[0], &readable); 868 } 869 maxFD++; 870 for(;;) { 871 readableCopy = readable; 872 for(;;) { 873 r = select(maxFD, &readableCopy, NULL, NULL, NULL); 874 if (r >= 0 || errno != EINTR) break; 875 } 876 if (r < 0) { 877 sysErr("select (relayLoop)"); 878 continue; 879 } 880 881 /* Handle session packets first */ 882 for (i=0; i<NumInterfaces; i++) { 883 if (FD_ISSET(Interfaces[i].sessionSock, &readableCopy)) { 884 relayGotSessionPacket(&Interfaces[i]); 885 } 886 } 887 888 /* Now handle discovery packets */ 889 for (i=0; i<NumInterfaces; i++) { 890 if (FD_ISSET(Interfaces[i].discoverySock, &readableCopy)) { 891 relayGotDiscoveryPacket(&Interfaces[i]); 892 } 893 } 894 895 /* Handle the session-cleaning process */ 896 if (FD_ISSET(CleanPipe[0], &readableCopy)) { 897 char dummy; 898 CleanCounter = 0; 899 read(CleanPipe[0], &dummy, 1); 900 if (IdleTimeout) cleanSessions(); 901 } 902 } 903} 904 905/********************************************************************** 906*%FUNCTION: relayGotDiscoveryPacket 907*%ARGUMENTS: 908* iface -- interface on which packet is waiting 909*%RETURNS: 910* Nothing 911*%DESCRIPTION: 912* Receives and processes a discovery packet. 913***********************************************************************/ 914void 915relayGotDiscoveryPacket(PPPoEInterface const *iface) 916{ 917 PPPoEPacket packet; 918 int size; 919 920 if (receivePacket(iface->discoverySock, &packet, &size) < 0) { 921 return; 922 } 923 924 /* Ignore unknown code/version */ 925 if (packet.ver != 1 || packet.type != 1) { 926 return; 927 } 928 929 /* Validate length */ 930 if (ntohs(packet.length) + HDR_SIZE > size) { 931 syslog(LOG_ERR, "Bogus PPPoE length field (%u)", 932 (unsigned int) ntohs(packet.length)); 933 return; 934 } 935 936 /* Drop Ethernet frame padding */ 937 if (size > ntohs(packet.length) + HDR_SIZE) { 938 size = ntohs(packet.length) + HDR_SIZE; 939 } 940 941 switch(packet.code) { 942 case CODE_PADT: 943 relayHandlePADT(iface, &packet, size); 944 break; 945 case CODE_PADI: 946 relayHandlePADI(iface, &packet, size); 947 break; 948 case CODE_PADO: 949 relayHandlePADO(iface, &packet, size); 950 break; 951 case CODE_PADR: 952 relayHandlePADR(iface, &packet, size); 953 break; 954 case CODE_PADS: 955 relayHandlePADS(iface, &packet, size); 956 break; 957 default: 958 syslog(LOG_ERR, "Discovery packet on %s with unknown code %d", 959 iface->name, (int) packet.code); 960 } 961} 962 963/********************************************************************** 964*%FUNCTION: relayGotSessionPacket 965*%ARGUMENTS: 966* iface -- interface on which packet is waiting 967*%RETURNS: 968* Nothing 969*%DESCRIPTION: 970* Receives and processes a session packet. 971***********************************************************************/ 972void 973relayGotSessionPacket(PPPoEInterface const *iface) 974{ 975 PPPoEPacket packet; 976 int size; 977 SessionHash *sh; 978 PPPoESession *ses; 979 980 if (receivePacket(iface->sessionSock, &packet, &size) < 0) { 981 return; 982 } 983 984 /* Ignore unknown code/version */ 985 if (packet.ver != 1 || packet.type != 1) { 986 return; 987 } 988 989 /* Must be a session packet */ 990 if (packet.code != CODE_SESS) { 991 syslog(LOG_ERR, "Session packet with code %d", (int) packet.code); 992 return; 993 } 994 995 /* Ignore session packets whose destination address isn't ours */ 996 if (memcmp(packet.ethHdr.h_dest, iface->mac, ETH_ALEN)) { 997 return; 998 } 999 1000// 2009.12 James. { 1001if(packet.code == 0x00 && (packet.payload)[0] == 128 && (packet.payload)[1] == 33){ 1002 char src_mac[18], dst_mac[18]; 1003 1004 memset(src_mac, 0, 18); 1005 sprintf(src_mac, "%02x:%02x:%02x:%02x:%02x:%02x", (packet.ethHdr.h_source)[0], (packet.ethHdr.h_source)[1], (packet.ethHdr.h_source)[2], (packet.ethHdr.h_source)[3], (packet.ethHdr.h_source)[4], (packet.ethHdr.h_source)[5]); 1006 1007 build_mac(src_mac); 1008} 1009// 2009.12 James. } 1010 /* Validate length */ 1011 if (ntohs(packet.length) + HDR_SIZE > size) { 1012 syslog(LOG_ERR, "Bogus PPPoE length field (%u)", 1013 (unsigned int) ntohs(packet.length)); 1014 return; 1015 } 1016 1017 /* Drop Ethernet frame padding */ 1018 if (size > ntohs(packet.length) + HDR_SIZE) { 1019 size = ntohs(packet.length) + HDR_SIZE; 1020 } 1021 1022 /* We're in business! Find the hash */ 1023 sh = findSession(packet.ethHdr.h_source, packet.session); 1024 if (!sh) { 1025 /* Don't log this. Someone could be running the client and the 1026 relay on the same box. */ 1027 return; 1028 } 1029 1030 /* Relay it */ 1031 ses = sh->ses; 1032 ses->epoch = Epoch; 1033 sh = sh->peer; 1034 packet.session = sh->sesNum; 1035 memcpy(packet.ethHdr.h_source, sh->interface->mac, ETH_ALEN); 1036 memcpy(packet.ethHdr.h_dest, sh->peerMac, ETH_ALEN); 1037#if 0 1038 fprintf(stderr, "Relaying %02x:%02x:%02x:%02x:%02x:%02x(%s:%d) to %02x:%02x:%02x:%02x:%02x:%02x(%s:%d)\n", 1039 sh->peer->peerMac[0], sh->peer->peerMac[1], sh->peer->peerMac[2], 1040 sh->peer->peerMac[3], sh->peer->peerMac[4], sh->peer->peerMac[5], 1041 sh->peer->interface->name, ntohs(sh->peer->sesNum), 1042 sh->peerMac[0], sh->peerMac[1], sh->peerMac[2], 1043 sh->peerMac[3], sh->peerMac[4], sh->peerMac[5], 1044 sh->interface->name, ntohs(sh->sesNum)); 1045#endif 1046 sendPacket(NULL, sh->interface->sessionSock, &packet, size); 1047} 1048 1049/********************************************************************** 1050*%FUNCTION: relayHandlePADT 1051*%ARGUMENTS: 1052* iface -- interface on which packet was received 1053* packet -- the PADT packet 1054*%RETURNS: 1055* Nothing 1056*%DESCRIPTION: 1057* Receives and processes a PADT packet. 1058***********************************************************************/ 1059void 1060relayHandlePADT(PPPoEInterface const *iface, 1061 PPPoEPacket *packet, 1062 int size) 1063{ 1064 SessionHash *sh; 1065 PPPoESession *ses; 1066 1067 sh = findSession(packet->ethHdr.h_source, packet->session); 1068 if (!sh) { 1069 return; 1070 } 1071 /* Relay the PADT to the peer */ 1072 sh = sh->peer; 1073 ses = sh->ses; 1074 packet->session = sh->sesNum; 1075 memcpy(packet->ethHdr.h_source, sh->interface->mac, ETH_ALEN); 1076 memcpy(packet->ethHdr.h_dest, sh->peerMac, ETH_ALEN); 1077 sendPacket(NULL, sh->interface->sessionSock, packet, size); 1078 1079 /* Destroy the session */ 1080 freeSession(ses, "Received PADT"); 1081} 1082 1083/********************************************************************** 1084*%FUNCTION: relayHandlePADI 1085*%ARGUMENTS: 1086* iface -- interface on which packet was received 1087* packet -- the PADI packet 1088*%RETURNS: 1089* Nothing 1090*%DESCRIPTION: 1091* Receives and processes a PADI packet. 1092***********************************************************************/ 1093void 1094relayHandlePADI(PPPoEInterface const *iface, 1095 PPPoEPacket *packet, 1096 int size) 1097{ 1098 PPPoETag tag; 1099 unsigned char *loc; 1100 int i, r; 1101 1102 int ifIndex; 1103 1104 /* Can a client legally be behind this interface? */ 1105 if (!iface->clientOK) { 1106 syslog(LOG_ERR, 1107 "PADI packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s not permitted", 1108 packet->ethHdr.h_source[0], 1109 packet->ethHdr.h_source[1], 1110 packet->ethHdr.h_source[2], 1111 packet->ethHdr.h_source[3], 1112 packet->ethHdr.h_source[4], 1113 packet->ethHdr.h_source[5], 1114 iface->name); 1115 return; 1116 } 1117 1118 /* Source address must be unicast */ 1119 if (NOT_UNICAST(packet->ethHdr.h_source)) { 1120 syslog(LOG_ERR, 1121 "PADI packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s not from a unicast address", 1122 packet->ethHdr.h_source[0], 1123 packet->ethHdr.h_source[1], 1124 packet->ethHdr.h_source[2], 1125 packet->ethHdr.h_source[3], 1126 packet->ethHdr.h_source[4], 1127 packet->ethHdr.h_source[5], 1128 iface->name); 1129 return; 1130 } 1131 1132 /* Destination address must be broadcast */ 1133 if (NOT_BROADCAST(packet->ethHdr.h_dest)) { 1134 syslog(LOG_ERR, 1135 "PADI packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s not to a broadcast address", 1136 packet->ethHdr.h_source[0], 1137 packet->ethHdr.h_source[1], 1138 packet->ethHdr.h_source[2], 1139 packet->ethHdr.h_source[3], 1140 packet->ethHdr.h_source[4], 1141 packet->ethHdr.h_source[5], 1142 iface->name); 1143 return; 1144 } 1145 1146 /* Get array index of interface */ 1147 ifIndex = iface - Interfaces; 1148 1149 loc = findTag(packet, TAG_RELAY_SESSION_ID, &tag); 1150 if (!loc) { 1151 tag.type = htons(TAG_RELAY_SESSION_ID); 1152 tag.length = htons(MY_RELAY_TAG_LEN); 1153 memcpy(tag.payload, &ifIndex, sizeof(ifIndex)); 1154 memcpy(tag.payload+sizeof(ifIndex), packet->ethHdr.h_source, ETH_ALEN); 1155 /* Add a relay tag if there's room */ 1156 r = addTag(packet, &tag); 1157 if (r < 0) return; 1158 size += r; 1159 } else { 1160 /* We do not re-use relay-id tags. Drop the frame. The RFC says the 1161 relay agent SHOULD return a Generic-Error tag, but this does not 1162 make sense for PADI packets. */ 1163 return; 1164 } 1165 1166 /* Broadcast the PADI on all AC-capable interfaces except the interface 1167 on which it came */ 1168 for (i=0; i < NumInterfaces; i++) { 1169 if (iface == &Interfaces[i]) continue; 1170 if (!Interfaces[i].acOK) continue; 1171 memcpy(packet->ethHdr.h_source, Interfaces[i].mac, ETH_ALEN); 1172 sendPacket(NULL, Interfaces[i].discoverySock, packet, size); 1173 } 1174 1175} 1176 1177/********************************************************************** 1178*%FUNCTION: relayHandlePADO 1179*%ARGUMENTS: 1180* iface -- interface on which packet was received 1181* packet -- the PADO packet 1182*%RETURNS: 1183* Nothing 1184*%DESCRIPTION: 1185* Receives and processes a PADO packet. 1186***********************************************************************/ 1187void 1188relayHandlePADO(PPPoEInterface const *iface, 1189 PPPoEPacket *packet, 1190 int size) 1191{ 1192 PPPoETag tag; 1193 unsigned char *loc; 1194 int ifIndex; 1195 int acIndex; 1196 1197 /* Can a server legally be behind this interface? */ 1198 if (!iface->acOK) { 1199 syslog(LOG_ERR, 1200 "PADO packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s not permitted", 1201 packet->ethHdr.h_source[0], 1202 packet->ethHdr.h_source[1], 1203 packet->ethHdr.h_source[2], 1204 packet->ethHdr.h_source[3], 1205 packet->ethHdr.h_source[4], 1206 packet->ethHdr.h_source[5], 1207 iface->name); 1208 return; 1209 } 1210 1211 acIndex = iface - Interfaces; 1212 1213 /* Source address must be unicast */ 1214 if (NOT_UNICAST(packet->ethHdr.h_source)) { 1215 syslog(LOG_ERR, 1216 "PADO packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s not from a unicast address", 1217 packet->ethHdr.h_source[0], 1218 packet->ethHdr.h_source[1], 1219 packet->ethHdr.h_source[2], 1220 packet->ethHdr.h_source[3], 1221 packet->ethHdr.h_source[4], 1222 packet->ethHdr.h_source[5], 1223 iface->name); 1224 return; 1225 } 1226 1227 /* Destination address must be interface's MAC address */ 1228 if (memcmp(packet->ethHdr.h_dest, iface->mac, ETH_ALEN)) { 1229 return; 1230 } 1231 1232 /* Find relay tag */ 1233 loc = findTag(packet, TAG_RELAY_SESSION_ID, &tag); 1234 if (!loc) { 1235 syslog(LOG_ERR, 1236 "PADO packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s does not have Relay-Session-Id tag", 1237 packet->ethHdr.h_source[0], 1238 packet->ethHdr.h_source[1], 1239 packet->ethHdr.h_source[2], 1240 packet->ethHdr.h_source[3], 1241 packet->ethHdr.h_source[4], 1242 packet->ethHdr.h_source[5], 1243 iface->name); 1244 return; 1245 } 1246 1247 /* If it's the wrong length, ignore it */ 1248 if (ntohs(tag.length) != MY_RELAY_TAG_LEN) { 1249 syslog(LOG_ERR, 1250 "PADO packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s does not have correct length Relay-Session-Id tag", 1251 packet->ethHdr.h_source[0], 1252 packet->ethHdr.h_source[1], 1253 packet->ethHdr.h_source[2], 1254 packet->ethHdr.h_source[3], 1255 packet->ethHdr.h_source[4], 1256 packet->ethHdr.h_source[5], 1257 iface->name); 1258 return; 1259 } 1260 1261 /* Extract interface index */ 1262 memcpy(&ifIndex, tag.payload, sizeof(ifIndex)); 1263 1264 if (ifIndex < 0 || ifIndex >= NumInterfaces || 1265 !Interfaces[ifIndex].clientOK || 1266 iface == &Interfaces[ifIndex]) { 1267 syslog(LOG_ERR, 1268 "PADO packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s has invalid interface in Relay-Session-Id tag", 1269 packet->ethHdr.h_source[0], 1270 packet->ethHdr.h_source[1], 1271 packet->ethHdr.h_source[2], 1272 packet->ethHdr.h_source[3], 1273 packet->ethHdr.h_source[4], 1274 packet->ethHdr.h_source[5], 1275 iface->name); 1276 return; 1277 } 1278 1279 /* Replace Relay-ID tag with opposite-direction tag */ 1280 memcpy(loc+TAG_HDR_SIZE, &acIndex, sizeof(acIndex)); 1281 memcpy(loc+TAG_HDR_SIZE+sizeof(ifIndex), packet->ethHdr.h_source, ETH_ALEN); 1282 1283 /* Set destination address to MAC address in relay ID */ 1284 memcpy(packet->ethHdr.h_dest, tag.payload + sizeof(ifIndex), ETH_ALEN); 1285 1286 /* Set source address to MAC address of interface */ 1287 memcpy(packet->ethHdr.h_source, Interfaces[ifIndex].mac, ETH_ALEN); 1288 1289 /* Send the PADO to the proper client */ 1290 sendPacket(NULL, Interfaces[ifIndex].discoverySock, packet, size); 1291} 1292 1293/********************************************************************** 1294*%FUNCTION: relayHandlePADR 1295*%ARGUMENTS: 1296* iface -- interface on which packet was received 1297* packet -- the PADR packet 1298*%RETURNS: 1299* Nothing 1300*%DESCRIPTION: 1301* Receives and processes a PADR packet. 1302***********************************************************************/ 1303void 1304relayHandlePADR(PPPoEInterface const *iface, 1305 PPPoEPacket *packet, 1306 int size) 1307{ 1308 PPPoETag tag; 1309 unsigned char *loc; 1310 int ifIndex; 1311 int cliIndex; 1312 1313 /* Can a client legally be behind this interface? */ 1314 if (!iface->clientOK) { 1315 syslog(LOG_ERR, 1316 "PADR packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s not permitted", 1317 packet->ethHdr.h_source[0], 1318 packet->ethHdr.h_source[1], 1319 packet->ethHdr.h_source[2], 1320 packet->ethHdr.h_source[3], 1321 packet->ethHdr.h_source[4], 1322 packet->ethHdr.h_source[5], 1323 iface->name); 1324 return; 1325 } 1326 1327 cliIndex = iface - Interfaces; 1328 1329 /* Source address must be unicast */ 1330 if (NOT_UNICAST(packet->ethHdr.h_source)) { 1331 syslog(LOG_ERR, 1332 "PADR packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s not from a unicast address", 1333 packet->ethHdr.h_source[0], 1334 packet->ethHdr.h_source[1], 1335 packet->ethHdr.h_source[2], 1336 packet->ethHdr.h_source[3], 1337 packet->ethHdr.h_source[4], 1338 packet->ethHdr.h_source[5], 1339 iface->name); 1340 return; 1341 } 1342 1343 /* Destination address must be interface's MAC address */ 1344 if (memcmp(packet->ethHdr.h_dest, iface->mac, ETH_ALEN)) { 1345 return; 1346 } 1347 1348 /* Find relay tag */ 1349 loc = findTag(packet, TAG_RELAY_SESSION_ID, &tag); 1350 if (!loc) { 1351 syslog(LOG_ERR, 1352 "PADR packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s does not have Relay-Session-Id tag", 1353 packet->ethHdr.h_source[0], 1354 packet->ethHdr.h_source[1], 1355 packet->ethHdr.h_source[2], 1356 packet->ethHdr.h_source[3], 1357 packet->ethHdr.h_source[4], 1358 packet->ethHdr.h_source[5], 1359 iface->name); 1360 return; 1361 } 1362 1363 /* If it's the wrong length, ignore it */ 1364 if (ntohs(tag.length) != MY_RELAY_TAG_LEN) { 1365 syslog(LOG_ERR, 1366 "PADR packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s does not have correct length Relay-Session-Id tag", 1367 packet->ethHdr.h_source[0], 1368 packet->ethHdr.h_source[1], 1369 packet->ethHdr.h_source[2], 1370 packet->ethHdr.h_source[3], 1371 packet->ethHdr.h_source[4], 1372 packet->ethHdr.h_source[5], 1373 iface->name); 1374 return; 1375 } 1376 1377 /* Extract interface index */ 1378 memcpy(&ifIndex, tag.payload, sizeof(ifIndex)); 1379 1380 if (ifIndex < 0 || ifIndex >= NumInterfaces || 1381 !Interfaces[ifIndex].acOK || 1382 iface == &Interfaces[ifIndex]) { 1383 syslog(LOG_ERR, 1384 "PADR packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s has invalid interface in Relay-Session-Id tag", 1385 packet->ethHdr.h_source[0], 1386 packet->ethHdr.h_source[1], 1387 packet->ethHdr.h_source[2], 1388 packet->ethHdr.h_source[3], 1389 packet->ethHdr.h_source[4], 1390 packet->ethHdr.h_source[5], 1391 iface->name); 1392 return; 1393 } 1394 1395 /* Replace Relay-ID tag with opposite-direction tag */ 1396 memcpy(loc+TAG_HDR_SIZE, &cliIndex, sizeof(cliIndex)); 1397 memcpy(loc+TAG_HDR_SIZE+sizeof(ifIndex), packet->ethHdr.h_source, ETH_ALEN); 1398 1399 /* Set destination address to MAC address in relay ID */ 1400 memcpy(packet->ethHdr.h_dest, tag.payload + sizeof(ifIndex), ETH_ALEN); 1401 1402 /* Set source address to MAC address of interface */ 1403 memcpy(packet->ethHdr.h_source, Interfaces[ifIndex].mac, ETH_ALEN); 1404 1405 /* Send the PADR to the proper access concentrator */ 1406 sendPacket(NULL, Interfaces[ifIndex].discoverySock, packet, size); 1407} 1408 1409/********************************************************************** 1410*%FUNCTION: relayHandlePADS 1411*%ARGUMENTS: 1412* iface -- interface on which packet was received 1413* packet -- the PADS packet 1414*%RETURNS: 1415* Nothing 1416*%DESCRIPTION: 1417* Receives and processes a PADS packet. 1418***********************************************************************/ 1419void 1420relayHandlePADS(PPPoEInterface const *iface, 1421 PPPoEPacket *packet, 1422 int size) 1423{ 1424 PPPoETag tag; 1425 unsigned char *loc; 1426 int ifIndex; 1427 int acIndex; 1428 PPPoESession *ses = NULL; 1429 SessionHash *sh; 1430 1431 /* Can a server legally be behind this interface? */ 1432 if (!iface->acOK) { 1433 syslog(LOG_ERR, 1434 "PADS packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s not permitted", 1435 packet->ethHdr.h_source[0], 1436 packet->ethHdr.h_source[1], 1437 packet->ethHdr.h_source[2], 1438 packet->ethHdr.h_source[3], 1439 packet->ethHdr.h_source[4], 1440 packet->ethHdr.h_source[5], 1441 iface->name); 1442 return; 1443 } 1444 1445 acIndex = iface - Interfaces; 1446 1447 /* Source address must be unicast */ 1448 if (NOT_UNICAST(packet->ethHdr.h_source)) { 1449 syslog(LOG_ERR, 1450 "PADS packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s not from a unicast address", 1451 packet->ethHdr.h_source[0], 1452 packet->ethHdr.h_source[1], 1453 packet->ethHdr.h_source[2], 1454 packet->ethHdr.h_source[3], 1455 packet->ethHdr.h_source[4], 1456 packet->ethHdr.h_source[5], 1457 iface->name); 1458 return; 1459 } 1460 1461 /* Destination address must be interface's MAC address */ 1462 if (memcmp(packet->ethHdr.h_dest, iface->mac, ETH_ALEN)) { 1463 return; 1464 } 1465 1466 /* Find relay tag */ 1467 loc = findTag(packet, TAG_RELAY_SESSION_ID, &tag); 1468 if (!loc) { 1469 syslog(LOG_ERR, 1470 "PADS packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s does not have Relay-Session-Id tag", 1471 packet->ethHdr.h_source[0], 1472 packet->ethHdr.h_source[1], 1473 packet->ethHdr.h_source[2], 1474 packet->ethHdr.h_source[3], 1475 packet->ethHdr.h_source[4], 1476 packet->ethHdr.h_source[5], 1477 iface->name); 1478 return; 1479 } 1480 1481 /* If it's the wrong length, ignore it */ 1482 if (ntohs(tag.length) != MY_RELAY_TAG_LEN) { 1483 syslog(LOG_ERR, 1484 "PADS packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s does not have correct length Relay-Session-Id tag", 1485 packet->ethHdr.h_source[0], 1486 packet->ethHdr.h_source[1], 1487 packet->ethHdr.h_source[2], 1488 packet->ethHdr.h_source[3], 1489 packet->ethHdr.h_source[4], 1490 packet->ethHdr.h_source[5], 1491 iface->name); 1492 return; 1493 } 1494 1495 /* Extract interface index */ 1496 memcpy(&ifIndex, tag.payload, sizeof(ifIndex)); 1497 1498 if (ifIndex < 0 || ifIndex >= NumInterfaces || 1499 !Interfaces[ifIndex].clientOK || 1500 iface == &Interfaces[ifIndex]) { 1501 syslog(LOG_ERR, 1502 "PADS packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s has invalid interface in Relay-Session-Id tag", 1503 packet->ethHdr.h_source[0], 1504 packet->ethHdr.h_source[1], 1505 packet->ethHdr.h_source[2], 1506 packet->ethHdr.h_source[3], 1507 packet->ethHdr.h_source[4], 1508 packet->ethHdr.h_source[5], 1509 iface->name); 1510 return; 1511 } 1512 1513 /* If session ID is zero, it's the AC respoding with an error. 1514 Just relay it; do not create a session */ 1515 if (packet->session != htons(0)) { 1516 /* Check for existing session */ 1517 sh = findSession(packet->ethHdr.h_source, packet->session); 1518 if (sh) ses = sh->ses; 1519 1520 /* If already an existing session, assume it's a duplicate PADS. Send 1521 the frame, but do not create a new session. Is this the right 1522 thing to do? Arguably, should send an error to the client and 1523 a PADT to the server, because this could happen due to a 1524 server crash and reboot. */ 1525 1526 if (!ses) { 1527 /* Create a new session */ 1528 ses = createSession(iface, &Interfaces[ifIndex], 1529 packet->ethHdr.h_source, 1530 loc + TAG_HDR_SIZE + sizeof(ifIndex), packet->session); 1531 if (!ses) { 1532 /* Can't allocate session -- send error PADS to client and 1533 PADT to server */ 1534 PPPoETag hostUniq, *hu; 1535 if (findTag(packet, TAG_HOST_UNIQ, &hostUniq)) { 1536 hu = &hostUniq; 1537 } else { 1538 hu = NULL; 1539 } 1540 relaySendError(CODE_PADS, htons(0), &Interfaces[ifIndex], 1541 loc + TAG_HDR_SIZE + sizeof(ifIndex), 1542 hu, "RP-PPPoE: Relay: Unable to allocate session"); 1543 relaySendError(CODE_PADT, packet->session, iface, 1544 packet->ethHdr.h_source, NULL, 1545 "RP-PPPoE: Relay: Unable to allocate session"); 1546 return; 1547 } 1548 } 1549 /* Replace session number */ 1550 packet->session = ses->sesNum; 1551 } 1552 1553 /* Remove relay-ID tag */ 1554 removeBytes(packet, loc, MY_RELAY_TAG_LEN + TAG_HDR_SIZE); 1555 size -= (MY_RELAY_TAG_LEN + TAG_HDR_SIZE); 1556 1557 /* Set destination address to MAC address in relay ID */ 1558 memcpy(packet->ethHdr.h_dest, tag.payload + sizeof(ifIndex), ETH_ALEN); 1559 1560 /* Set source address to MAC address of interface */ 1561 memcpy(packet->ethHdr.h_source, Interfaces[ifIndex].mac, ETH_ALEN); 1562 1563 /* Send the PADS to the proper client */ 1564 sendPacket(NULL, Interfaces[ifIndex].discoverySock, packet, size); 1565} 1566 1567/********************************************************************** 1568*%FUNCTION: relaySendError 1569*%ARGUMENTS: 1570* code -- PPPoE packet code (PADS or PADT, typically) 1571* session -- PPPoE session number 1572* iface -- interface on which to send frame 1573* mac -- Ethernet address to which frame should be sent 1574* hostUniq -- if non-NULL, a hostUniq tag to add to error frame 1575* errMsg -- error message to insert into Generic-Error tag. 1576*%RETURNS: 1577* Nothing 1578*%DESCRIPTION: 1579* Sends either a PADS or PADT packet with a Generic-Error tag and an 1580* error message. 1581***********************************************************************/ 1582void 1583relaySendError(unsigned char code, 1584 UINT16_t session, 1585 PPPoEInterface const *iface, 1586 unsigned char const *mac, 1587 PPPoETag const *hostUniq, 1588 char const *errMsg) 1589{ 1590 PPPoEPacket packet; 1591 PPPoETag errTag; 1592 int size; 1593 1594 memcpy(packet.ethHdr.h_source, iface->mac, ETH_ALEN); 1595 memcpy(packet.ethHdr.h_dest, mac, ETH_ALEN); 1596 packet.ethHdr.h_proto = htons(Eth_PPPOE_Discovery); 1597 packet.type = 1; 1598 packet.ver = 1; 1599 packet.code = code; 1600 packet.session = session; 1601 packet.length = htons(0); 1602 if (hostUniq) { 1603 if (addTag(&packet, hostUniq) < 0) return; 1604 } 1605 errTag.type = htons(TAG_GENERIC_ERROR); 1606 errTag.length = htons(strlen(errMsg)); 1607 strcpy(errTag.payload, errMsg); 1608 if (addTag(&packet, &errTag) < 0) return; 1609 size = ntohs(packet.length) + HDR_SIZE; 1610 if (code == CODE_PADT) { 1611 sendPacket(NULL, iface->discoverySock, &packet, size); 1612 } else { 1613 sendPacket(NULL, iface->sessionSock, &packet, size); 1614 } 1615} 1616 1617/********************************************************************** 1618*%FUNCTION: alarmHandler 1619*%ARGUMENTS: 1620* sig -- signal number 1621*%RETURNS: 1622* Nothing 1623*%DESCRIPTION: 1624* SIGALRM handler. Increments Epoch; if necessary, writes a byte of 1625* data to the alarm pipe to trigger the stale-session cleaner. 1626***********************************************************************/ 1627void 1628alarmHandler(int sig) 1629{ 1630 alarm(1); 1631 Epoch++; 1632 CleanCounter++; 1633 if (CleanCounter == CleanPeriod) { 1634 write(CleanPipe[1], "", 1); 1635 } 1636} 1637 1638/********************************************************************** 1639*%FUNCTION: cleanSessions 1640*%ARGUMENTS: 1641* None 1642*%RETURNS: 1643* Nothing 1644*%DESCRIPTION: 1645* Goes through active sessions and cleans sessions idle for longer 1646* than IdleTimeout seconds. 1647***********************************************************************/ 1648void cleanSessions(void) 1649{ 1650 PPPoESession *cur, *next; 1651 cur = ActiveSessions; 1652 while(cur) { 1653 next = cur->next; 1654 if (Epoch - cur->epoch > IdleTimeout) { 1655 /* Send PADT to each peer */ 1656 relaySendError(CODE_PADT, cur->acHash->sesNum, 1657 cur->acHash->interface, 1658 cur->acHash->peerMac, NULL, 1659 "RP-PPPoE: Relay: Session exceeded idle timeout"); 1660 relaySendError(CODE_PADT, cur->clientHash->sesNum, 1661 cur->clientHash->interface, 1662 cur->clientHash->peerMac, NULL, 1663 "RP-PPPoE: Relay: Session exceeded idle timeout"); 1664 freeSession(cur, "Idle Timeout"); 1665 } 1666 cur = next; 1667 } 1668} 1669