1// A broadcast packet repeater. This packet repeater (currently designed for 2// udp packets) will listen for broadcast packets. 3// When it receives the packets, it will then re-broadcast the packet. 4// 5// Written by TheyCallMeLuc(at)yahoo.com.au 6// I accept no responsiblity for the function of this program if you 7// choose to use it. 8// Modified for Poptop by Richard de Vroede <r.devroede@linvision.com> 9// Ditto on the no responsibility. 10// 11// Rewritten by Norbert van Bolhuis <norbert@vanbolhuis.demon.nl> bcrelay (v1.0+) 12// now supports/does: 13// 1) Relaying from PPP (VPN tunnel) interfaces, hereby creating a virtual 14// LAN (w.r.t. UDP broadcasts) for VPN clients and ethernet PCs 15// belonging/matching the subnet portion of the VPN IP addresses. 16// So now broadcasting to/from all systems of the VPN has been implemented. 17// Note that bcrelay v0.5 only relayed from LAN to VPN clients. 18// It doesn't matter whether the systems on the VPN are on the LAN of the 19// VPN server or have a VPN/PPTP connection (over the internet) to the VPN 20// server. Broadcasts will always be relayed to/from all given interfaces. And 21// as long as the subnet portion of the IP addresses of the systems on the VPN 22// matches, the VPN server will be able to route properly. This means all 23// networking applications/games that rely on a UDP broadcast from one or 24// more PPP (VPN tunnel) interfaces will now see eachother and work over 25// the VPN. 26// Note that it depends on the networking application/game and whoever 27// acts as application/game server/host who is sending (UPD) broadcasts 28// and who is listening. 29// 2) UDP broadcasts received on a PPP interface (VPN tunnel) sometimes 30// don't carry the VPN IP address which pptpd provisioned. I've seen 31// this happening on a WinXP SP1 box, especially when the application 32// responsible for the UDP broadcasts isn't aware of the PPP interface. 33// In this case it just uses the LAN IP src address for the IP src 34// address of the inner (GRE encapsulated) IP packet. This breaks 35// the "virtual LAN" and therefore bcrelay, as of this version, changes 36// the src IP address to the VPN IP address (which pptpd provisioned) 37// before relaying. 38// 3) To avoid a UDP broadcast loop, bcrelay changes the IP TTL and the 39// UDP checksum (to 1 and 0 respectively) of the UDP broadcasts it relays. 40// No relaying will be done for UDP broadcasts with IP TTL=1 and UDP 41// checksum=0. Could also (mis)use IP identification for this, but IP TTL 42// and UDP chksum combination is expected to work fine. 43// 4) bcrelay v0.5 forgot to update IP/UDP checksum when it changed the 44// dest. IP address (e.g. from 192.168.1.255 to 255.255.255.255). 45// Of course as of this version bcrelay always updates the IP/UDP 46// checksum since the IP TTL and src IP address will change also. 47// 5) Enhanced the (syslog) debugging capabilities. By default bcrelay will 48// show what it is doing. Bcrelay will syslog the IP interfaces it tries 49// to read/relay UDP broadcasts from/to. These interfaces are called 50// the 'active interfaces', bcrelay will syslog the initial set of active 51// interfaces and whenever the set changes. Currently there is no difference 52// between an incoming interface (given with -i) and an outgoing interface 53// (given with -o), so bcrelay won't show this attribute. Also, bcrelay will 54// syslog a successfully relayed UDP broadcast, including the UDP port numbers, 55// the incoming interface and the interface(s) to which it was successfully 56// relayed. The (new) -n option allows to suppress syslog tracing. 57// If -n is given, bcrelay shows/syslogs nothing, except fatal error 58// messages. 59// 60// This software is completely free. You can use and/or modify this 61// software to your hearts content. You are free to redistribute it as 62// long as it is accompanied with the source and my credit is included. 63 64#ifdef HAVE_CONFIG_H 65#include "config.h" 66#endif 67 68#ifdef __linux__ 69#define _GNU_SOURCE 1 /* strdup() prototype, broken arpa/inet.h */ 70#endif 71 72#ifdef __svr4__ 73#define __EXTENSIONS__ 1 /* strdup() prototype */ 74#endif 75 76#ifdef __sgi__ 77#define _XOPEN_SOURCE 500 /* strdup() prototype */ 78#endif 79 80#include <fcntl.h> 81#include <stdio.h> 82#include <stdlib.h> 83#include <netdb.h> 84#include <unistd.h> 85#include <string.h> 86#include <libgen.h> 87#include <time.h> 88#include <sys/time.h> 89#include <regex.h> 90#include <net/if.h> 91#include <sys/ioctl.h> 92#include <sys/socket.h> 93#include <sys/types.h> 94#include <netinet/in.h> 95#include <netpacket/packet.h> 96#include <net/ethernet.h> 97#include <netinet/ip.h> 98#include <netinet/udp.h> 99#include <netinet/tcp.h> 100#include <dirent.h> 101 102#include "defaults.h" 103#include "our_syslog.h" 104#include "our_getopt.h" 105 106//#define VERSION "1.0" 107 108/* uncomment if you compile this without poptop's configure script */ 109//#define HAVE_FORK 110 111/* 112 * Value-return macros to fields in the IP PDU header 113 */ 114#define IP_IPPDU_IHL(ippdu) (*(unsigned char *)(ippdu) & 0x0F) 115#define IP_IPPDU_PROTO(ippdu) (*((unsigned char *)(ippdu) + 9) & 0xFF) 116 117/* 118 * Pointer macros to fields in the IP PDU header 119 */ 120#define IP_IPPDU_CHECKSUM_MSB_PTR(ippdu) ((unsigned char *)(ippdu) + 10 ) 121#define IP_IPPDU_CHECKSUM_LSB_PTR(ippdu) ((unsigned char *)(ippdu) + 11 ) 122 123/* 124 * Pointer macros to fields in the UDP PDU header 125 */ 126#define IP_UDPPDU_CHECKSUM_MSB_PTR(udppdu) ((unsigned char *)(udppdu) + 6 ) 127#define IP_UDPPDU_CHECKSUM_LSB_PTR(udppdu) ((unsigned char *)(udppdu) + 7 ) 128 129#define MAXIF 255 // Maximum interfaces to use 130#define MAX_SELECT_WAIT 3 // Maximum time (in secs) to wait for input on the socket/interfaces 131 // A time-out triggers the discovery of new interfaces. 132#define MAX_NODISCOVER_IFS 12 // Maximum time (in secs) to elaps before a discovery of new 133 // interfaces is triggered. Only when a packet keeps coming in 134 // (this prevents a select time-out) a variable initialized with 135 // this #define becomes 0 and a rediscovery of the interfaces is 136 // triggered. 137#define MAX_IFLOGTOSTR 16 138 139/* Local function prototypes */ 140static void showusage(char *prog); 141static void showversion(); 142#ifndef HAVE_DAEMON 143static void my_daemon(int argc, char **argv); 144#endif 145static void mainloop(int argc, char **argv); 146 147struct packet { 148 struct iphdr ip; 149 struct udphdr udp; 150 char data[ETHERMTU]; 151}; 152 153 154/* 155 * struct that keeps track of the interfaces of the system 156 * selected upon usage by bcrelay (with -i and -o option). 157 * An array of this struct is returned by discoverActiveInterfaces. 158 * This array is reset (filled from scratch) each time 159 * discoverActiveInterfaces is called. 160 */ 161struct iflist { 162//Fix 3mar2003 163 //char index; 164 int index; 165 u_int32_t bcast; 166 char ifname[16+1]; 167 unsigned long ifaddr; 168 unsigned long ifdstaddr; 169 unsigned long flags1; 170}; 171 172#define IFLIST_FLAGS1_IF_IS_ETH 0x00000001 173#define IFLIST_FLAGS1_IF_IS_PPP 0x00000002 174#define IFLIST_FLAGS1_IF_IS_UNKNOWN 0x00000004 175#define IFLIST_FLAGS1_CHANGED_INNER_SADDR 0x00010000 176 177 178/* 179 * struct that keeps track of the socket fd's for every interface 180 * that is in use (and thus present in iflist). 181 * Two permanent arrays of this struct are used, one for the 182 * previous/old list and one for the current list. 183 */ 184struct ifsnr { 185 int sock_nr; 186 int ifindex; 187}; 188 189static void copy_ifsnr(struct ifsnr *from, struct ifsnr *to); 190static int find_sock_nr(struct ifsnr *l, int ifidx); 191struct iflist *discoverActiveInterfaces(int s); 192void ip_update_checksum(unsigned char *ippdu); 193static char *IpProtToString( unsigned char prot ); 194static char *iflistToString( struct iflist *ifp ); 195static char *iflistLogIToString( struct iflist *ifp, int idx, struct ifsnr *ifnr ); 196static char *iflistLogRToString( struct iflist *ifp, int idx, struct ifsnr *ifnr ); 197static void bind_to_iface(int fd, int ifindex); 198 199/* 200 * This global variable determines whether NVBCR_PRINTF actually 201 * displays something. While developping v1.0, NVBCR_PRINTF were 202 * printf and a lot of tracing/logging/debugging was done with these. 203 * Of course, by default these 'info' messages have been turned off 204 * now. Enable by setting variable to 1. Note that output will only 205 * appear in non-daemon mode (see also NVBCR_PRINTF). 206 */ 207static int do_org_info_printfs = 0; 208 209static int vnologging = 0; 210static int vdaemon = 0; 211 212#define NVBCR_PRINTF( args ) \ 213 if ((vdaemon == 0) && (do_org_info_printfs == 1)) printf args 214 215static char interfaces[32]; 216static char log_interfaces[MAX_IFLOGTOSTR*MAXIF]; 217static char log_relayed[(MAX_IFLOGTOSTR-1)*MAXIF+81]; 218static char *ipsec = ""; 219 220static void showusage(char *prog) 221{ 222 printf("\nBCrelay v%s\n\n", VERSION); 223 printf("A broadcast packet repeater. This packet repeater (currently designed for udp packets) will listen\n"); 224 printf(" for broadcast packets. When it receives the packets, it will then re-broadcast the packet.\n\n"); 225 printf("Usage: %s [options], where options are:\n\n", prog); 226 printf(" [-d] [--daemon] Run as daemon.\n"); 227 printf(" [-h] [--help] Displays this help message.\n"); 228 printf(" [-i] [--incoming <ifin>] Defines from which interface broadcasts will be relayed.\n"); 229 printf(" [-n] [--nolog] No logging/tracing to /var/log/messages.\n"); 230 printf(" [-o] [--outgoing <ifout>] Defines to which interface broadcasts will be relayed.\n"); 231 printf(" [-s] [--ipsec <arg>] Defines an ipsec tunnel to be relayed to.\n"); 232 printf(" Since ipsec tunnels terminate on the same interface, we need to define the broadcast\n"); 233 printf(" address of the other end-point of the tunnel. This is done as ipsec0:x.x.x.255\n"); 234 printf(" [-v] [--version] Displays the BCrelay version number.\n"); 235 printf("\nLog messages and debugging go to syslog as DAEMON.\n\n"); 236 printf("\nInterfaces can be specified as regexpressions, ie. ppp[0-9]+\n\n"); 237} 238 239static void showversion() 240{ 241 printf("BCrelay v%s\n", VERSION); 242} 243 244#ifndef HAVE_DAEMON 245static void my_daemon(int argc, char **argv) 246{ 247 pid_t pid; 248#ifndef BCRELAY_BIN 249/* Need a smart way to locate the binary -rdv */ 250#define BCRELAY_BIN argv[0] 251#endif 252#ifndef HAVE_FORK 253 /* need to use vfork - eg, uClinux */ 254 char **new_argv; 255 extern char **environ; 256 int minusd=0; 257 int i; 258 int fdr; 259 260 /* Strip -d option */ 261 new_argv = malloc((argc) * sizeof(char **)); 262 fdr = open("/dev/null", O_RDONLY); 263 new_argv[0] = BCRELAY_BIN; 264 for (i = 1; argv[i] != NULL; i++) { 265 if (fdr != 0) { dup2(fdr, 0); close(fdr); } 266 if ( (strcmp(argv[i],"-d")) == 0 ) { 267 minusd=1; 268 } 269 if (minusd) { 270 new_argv[i] = argv[i+1]; 271 } else { 272 new_argv[i] = argv[i]; 273 } 274 } 275 syslog(LOG_DEBUG, "Option parse OK, re-execing as daemon"); 276 fflush(stderr); 277 if ((pid = vfork()) == 0) { 278 if (setsid() < 0) { /* shouldn't fail */ 279 syslog(LOG_ERR, "Setsid failed!"); 280 _exit(1); 281 } 282 chdir("/"); 283 umask(0); 284 /* execve only returns on an error */ 285 execve(BCRELAY_BIN, new_argv, environ); 286 exit(1); 287 } else if (pid > 0) { 288 syslog(LOG_DEBUG, "Success re-execing as daemon!"); 289 exit(0); 290 } else { 291 syslog(LOG_ERR, "Error vforking"); 292 exit(1); 293 } 294#else 295 pid=fork(); 296 if (pid<0) { syslog(LOG_ERR, "Error forking"); _exit(1); } 297 if (pid>0) { syslog(LOG_DEBUG, "Parent exits"); _exit(0); } 298 if (pid==0) { syslog(LOG_DEBUG, "Running as child"); } 299 /* child (daemon) continues */ 300 if (setsid() < 0) { /* shouldn't fail */ 301 syslog(LOG_ERR, "Setsid failed!"); 302 _exit(1); 303 } 304 chdir("/"); 305#endif 306} 307#endif 308 309int main(int argc, char **argv) { 310 regex_t preg; 311 /* command line options */ 312 int c; 313 char *ifout = ""; 314 char *ifin = ""; 315 316#ifndef BCRELAY 317 fprintf(stderr, 318 "bcrelay: pptpd was compiled without support for bcrelay, exiting.\n" 319 " run configure --with-bcrelay, make, and install.\n"); 320 exit(1); 321#endif 322 323 /* open a connection to the syslog daemon */ 324 openlog("bcrelay", LOG_PID, PPTP_FACILITY); 325 326 while (1) { 327 int option_index = 0; 328 329 static struct option long_options[] = 330 { 331 {"nolog", 0, 0, 0}, 332 {"daemon", 0, 0, 0}, 333 {"help", 0, 0, 0}, 334 {"incoming", 1, 0, 0}, 335 {"outgoing", 1, 0, 0}, 336 {"ipsec", 1, 0, 0}, 337 {"version", 0, 0, 0}, 338 {0, 0, 0, 0} 339 }; 340 341 c = getopt_long(argc, argv, "ndhi:o:s:v", long_options, &option_index); 342 if (c == -1) 343 break; 344 /* convert long options to short form */ 345 if (c == 0) 346 c = "ndhiosv"[option_index]; 347 switch (c) { 348 case 'n': 349 vnologging = 1; 350 break; 351 case 'd': 352 vdaemon = 1; 353 break; 354 case 'h': 355 showusage(argv[0]); 356 return 0; 357 case 'i': 358 ifin = strdup(optarg); 359 break; 360 case 'o': 361 ifout = strdup(optarg); 362 break; 363 case 's': 364 ipsec = strdup(optarg); 365 // Validate the ipsec parameters 366 regcomp(&preg, "ipsec[0-9]+:[0-9]+.[0-9]+.[0-9]+.255", REG_EXTENDED); 367 if (regexec(&preg, ipsec, 0, NULL, 0)) { 368 syslog(LOG_INFO,"Bad syntax: %s", ipsec); 369 fprintf(stderr, "\nBad syntax: %s\n", ipsec); 370 showusage(argv[0]); 371 return 0; 372 } else { 373 regfree(&preg); 374 break; 375 } 376 case 'v': 377 showversion(); 378 return 0; 379 default: 380 showusage(argv[0]); 381 return 1; 382 } 383 } 384 if (ifin == "") { 385 syslog(LOG_INFO,"Incoming interface required!"); 386 showusage(argv[0]); 387 _exit(1); 388 } 389 if (ifout == "" && ipsec == "") { 390 syslog(LOG_INFO,"Listen-mode or outgoing or IPsec interface required!"); 391 showusage(argv[0]); 392 _exit(1); 393 } else { 394 sprintf(interfaces,"%s|%s", ifin, ifout); 395 } 396 397 // If specified, become Daemon. 398 if (vdaemon) { 399#if HAVE_DAEMON 400 closelog(); 401 freopen("/dev/null", "r", stdin); 402 /* set noclose, we want stdout/stderr still attached if we can */ 403 daemon(0, 1); 404 /* returns to child only */ 405 /* pid will have changed */ 406 openlog("bcrelay", LOG_PID, PPTP_FACILITY); 407#else /* !HAVE_DAEMON */ 408 my_daemon(argc, argv); 409 /* returns to child if !HAVE_FORK 410 * never returns if HAVE_FORK (re-execs without -d) 411 */ 412#endif 413 } else { 414 syslog(LOG_INFO, "Running as child\n"); 415 } 416 mainloop(argc,argv); 417 _exit(0); 418} 419 420static void mainloop(int argc, char **argv) 421{ 422 socklen_t salen = sizeof(struct sockaddr_ll); 423 int i, s, rcg, j, no_discifs_cntr, ifs_change; 424 int logstr_cntr; 425 struct iflist *iflist = NULL; // Initialised after the 1st packet 426 struct sockaddr_ll sa; 427 struct packet *ipp_p; 428 char *udppdu; // FIXME: warning: pointer targets in assignment differ in signedness 429 fd_set sock_set; 430 struct timeval time_2_wait; 431 static struct ifsnr old_ifsnr[MAXIF+1]; // Old iflist to socket fd's mapping list 432 static struct ifsnr cur_ifsnr[MAXIF+1]; // Current iflist to socket fd's mapping list 433 unsigned char buf[1518]; 434 char *logstr = ""; 435 436 no_discifs_cntr = MAX_NODISCOVER_IFS; 437 ifs_change = 0; 438 439 /* 440 * Open general ethernet socket, only used to discover interfaces. 441 */ 442 if ((s = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL))) < 0) 443 syslog(LOG_INFO,"%s: Error creating socket", *argv); 444 445 446 /* 447 * Discover interfaces (initial set) and create a dedicated socket bound to the interface 448 */ 449 memset(old_ifsnr, -1, sizeof(old_ifsnr)); 450 memset(cur_ifsnr, -1, sizeof(cur_ifsnr)); 451 iflist = discoverActiveInterfaces(s); 452 for (i=0; iflist[i].index; ++i) { 453 if ((cur_ifsnr[i].sock_nr = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL))) < 0) { 454 syslog(LOG_ERR, "mainloop: Error, socket error! (rv=%d, errno=%d)", cur_ifsnr[i].sock_nr, errno); 455 exit(1); 456 } 457 bind_to_iface(cur_ifsnr[i].sock_nr, iflist[i].index); 458 cur_ifsnr[i].ifindex = iflist[i].index; 459 } 460 NVBCR_PRINTF(("Displaying INITIAL active interfaces..\n")); 461 if (vnologging == 0) { 462 logstr = log_interfaces; 463 logstr_cntr = sprintf(logstr, "Initial active interfaces: "); 464 logstr += logstr_cntr; 465 } 466 for (i = 0; iflist[i].index; i++) 467 { 468 NVBCR_PRINTF(("\t\tactive interface number: %d, if=(%s), sock_nr=%d\n", i, iflistToString(&(iflist[i])), cur_ifsnr[i].sock_nr)); 469 if (vnologging == 0) { 470 logstr_cntr = sprintf(logstr, "%s ", iflistLogIToString(&(iflist[i]), i, &(cur_ifsnr[i]))); 471 logstr += logstr_cntr; 472 } 473 } 474 if (vnologging == 0) syslog(LOG_INFO, "%s", log_interfaces); 475 476 // Main loop 477 while (1) 478 { 479 480 /* 481 * Check all (interface) sockets for incoming packets 482 */ 483 FD_ZERO(&sock_set); 484 for (i=0; iflist[i].index; ++i) 485 { 486 if (cur_ifsnr[i].sock_nr >= 0) { 487 FD_SET(cur_ifsnr[i].sock_nr, &sock_set); 488 } 489 } 490 491 /* 492 * Don't wait more than MAX_SELECT_WAIT seconds 493 */ 494 time_2_wait.tv_sec = MAX_SELECT_WAIT; 495 time_2_wait.tv_usec = 0L; 496 497 /* select on sockets */ 498 rcg = select(MAXIF, &sock_set, (fd_set *) NULL,(fd_set *) NULL, &time_2_wait); 499 500 if (rcg < 0) 501 { 502 syslog(LOG_ERR, "Error, select error! (rv=%d, errno=%d)", rcg, errno); 503 exit(1); 504 } 505 506 if (rcg == 0) 507 { 508 /* TimeOut, rediscover interfaces */ 509 NVBCR_PRINTF(("Select timeout, rediscover interfaces\n")); 510 copy_ifsnr(cur_ifsnr, old_ifsnr); 511 memset(cur_ifsnr, -1, sizeof(cur_ifsnr)); 512 iflist = discoverActiveInterfaces(s); 513 /* 514 * Build new cur_ifsnr list. 515 * Make iflist[i] correspond with cur_ifsnr[i], so iflist[i].index == cur_ifsnr[i].ifindex 516 * The old list (old_ifsnr) is used to compare. 517 */ 518 for (i=0; iflist[i].index; ++i) { 519 /* check to see if it is a NEW interface */ 520 int fsnr = find_sock_nr(old_ifsnr, iflist[i].index); 521 if (fsnr == -1) { 522 /* found new interface, open dedicated socket and bind it to the interface */ 523 if ((cur_ifsnr[i].sock_nr = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL))) < 0) { 524 syslog(LOG_ERR, "mainloop: Error, socket error! (rv=%d, errno=%d)", cur_ifsnr[i].sock_nr, errno); 525 exit(1); 526 } 527 bind_to_iface(cur_ifsnr[i].sock_nr, iflist[i].index); 528 ifs_change = 1; 529 } 530 else 531 { 532 /* 533 * not a new interface, socket already openen, interface already 534 * bound. Update cur_ifsnr. 535 */ 536 cur_ifsnr[i].sock_nr = fsnr; 537 } 538 cur_ifsnr[i].ifindex = iflist[i].index; 539 } 540 /* Close disappeared interfaces */ 541 for (i=0; i<MAXIF; ++i) 542 { 543 if ((old_ifsnr[i].sock_nr != -1) && (old_ifsnr[i].ifindex != -1) && 544 (find_sock_nr(cur_ifsnr, old_ifsnr[i].ifindex) == -1)) { 545 NVBCR_PRINTF(("Closing an interface (it disappeared), namely: (s_nr=%d, ifidx=%d)\n", old_ifsnr[i].sock_nr, old_ifsnr[i].ifindex)); 546 close(old_ifsnr[i].sock_nr); 547 old_ifsnr[i].sock_nr = -1; 548 old_ifsnr[i].ifindex = -1; 549 ifs_change = 1; 550 } 551 } 552 if (ifs_change == 1) 553 { 554 NVBCR_PRINTF(("Active interface set changed --> displaying current active interfaces..\n")); 555 if (vnologging == 0) { 556 logstr = log_interfaces; 557 logstr_cntr = sprintf(logstr, "Active interface set changed to: "); 558 logstr += logstr_cntr; 559 } 560 for (i = 0; iflist[i].index; i++) 561 { 562 NVBCR_PRINTF(("\t\tactive interface number: %d, if=(%s), sock_nr=%d\n", i, iflistToString(&(iflist[i])), cur_ifsnr[i].sock_nr)); 563 if (vnologging == 0) { 564 logstr_cntr = sprintf(logstr, "%s ", iflistLogIToString(&(iflist[i]), i, &(cur_ifsnr[i]))); 565 logstr += logstr_cntr; 566 } 567 } 568 if (vnologging == 0) syslog(LOG_INFO, "%s", log_interfaces); 569 ifs_change = 0; 570 } 571 continue; 572 } 573 574 if (rcg > 0) 575 { 576 /* rcg interfaces have pending input */ 577 for (i=0; ((iflist[i].index != 0) && (rcg > 0)); ++i) 578 { 579 if ((cur_ifsnr[i].sock_nr != -1) && (FD_ISSET(cur_ifsnr[i].sock_nr,&sock_set))) 580 { 581 /* Valid socket number and pending input, let's read */ 582 int rlen = read(cur_ifsnr[i].sock_nr, buf, sizeof(buf)); 583 ipp_p = (struct packet *)&(buf[0]); 584 NVBCR_PRINTF(("IP_Packet=(tot_len=%d, id=%02x%02x, ttl=%d, prot=%s, src_ip=%d.%d.%d.%d, dst_ip=%d.%d.%d.%d) (on if: %d/%d) ", ntohs(ipp_p->ip.tot_len), (ntohs(ipp_p->ip.id))>>8, (ntohs(ipp_p->ip.id))&0x00ff, ipp_p->ip.ttl, IpProtToString(ipp_p->ip.protocol), (ntohl(ipp_p->ip.saddr))>>24, ((ntohl(ipp_p->ip.saddr))&0x00ff0000)>>16, ((ntohl(ipp_p->ip.saddr))&0x0000ff00)>>8, (ntohl(ipp_p->ip.saddr))&0x000000ff, (ntohl(ipp_p->ip.daddr))>>24, ((ntohl(ipp_p->ip.daddr))&0x00ff0000)>>16, ((ntohl(ipp_p->ip.daddr))&0x0000ff00)>>8, (ntohl(ipp_p->ip.daddr))&0x000000ff, i, iflist[i].index)); 585 rcg -= 1; 586 587 if ( (ipp_p->ip.protocol == IPPROTO_UDP) && 588 (((ntohl(ipp_p->ip.daddr)) & 0x000000ff) == 0x000000ff) && 589 (ipp_p->ip.ttl != 1) && 590 (!((*IP_UDPPDU_CHECKSUM_MSB_PTR((unsigned char *)ipp_p+(4*ipp_p->ip.ihl)) == 0) && 591 (*IP_UDPPDU_CHECKSUM_LSB_PTR((unsigned char *)ipp_p+(4*ipp_p->ip.ihl)) == 0))) ) 592 { 593 int nrsent; 594 int ifindex_to_exclude = iflist[i].index; 595 596 NVBCR_PRINTF(("is an UDP BROADCAST (dstPort=%d, srcPort=%d) (with TTL!=1 and UDP_CHKSUM!=0)\n\n", 597 ntohs(ipp_p->udp.dest), ntohs(ipp_p->udp.source))); 598 if (vnologging == 0) { 599 logstr = log_relayed; 600 logstr_cntr = sprintf(logstr, "UDP_BroadCast(sp=%d,dp=%d) from: %s relayed to: ", ntohs(ipp_p->udp.source), 601 ntohs(ipp_p->udp.dest), iflistLogRToString(&(iflist[i]), i, &(cur_ifsnr[i]))); 602 logstr += logstr_cntr; 603 } 604 605 /* going to relay a broadcast packet on all the other interfaces */ 606 for (j=0; iflist[j].index; ++j) 607 { 608 int prepare_ipp = 0; // Changing the incoming UDP broadcast needs to be done once 609 610 if (iflist[j].index != ifindex_to_exclude) 611 { 612 NVBCR_PRINTF(("Going to sent UDP Broadcast on interface: %s, sock_nr=%d\n", iflistToString(&(iflist[j])), cur_ifsnr[j].sock_nr)); 613 614 memset(&sa, 0, salen); 615 616 sa.sll_family = AF_PACKET; 617 sa.sll_ifindex = iflist[j].index; /* Must be the SIOCGIFINDEX number */ 618 // Set the outgoing hardware address to 1's. True Broadcast 619 sa.sll_addr[0] = sa.sll_addr[1] = sa.sll_addr[2] = sa.sll_addr[3] = 0xff; 620 sa.sll_addr[4] = sa.sll_addr[5] = sa.sll_addr[6] = sa.sll_addr[7] = 0xff; 621 sa.sll_halen = 6; 622 623 /* 624 * htons(ETH_P_IP) is necessary otherwise sendto will 625 * succeed but no packet is actually sent on the wire (this 626 * was the case for PPP interfaces, for ETH interfaces an unknown 627 * LAN frame is sent if htons(ETH_P_IP) is not set as protocol). 628 */ 629 sa.sll_protocol = htons(ETH_P_IP); /* ETH_P_PPP_MP */ 630 631 if (prepare_ipp == 0) { 632 // change TimeToLive to 1, This is to avoid loops, bcrelay will *NOT* relay 633 // anything with TTL==1. 634 ipp_p->ip.ttl = 1; 635 636 // The CRC gets broken here when sending over ipsec tunnels but that 637 // should not matter as we reassemble the packet at the other end. 638 ipp_p->ip.daddr = iflist[j].bcast; 639 640 // check IP srcAddr (on some winXP boxes it is found to be 641 // different from the configured ppp address). 642 // Only do this for PPP interfaces. 643 if ((iflist[i].flags1 & IFLIST_FLAGS1_IF_IS_PPP) && 644 (ntohl(ipp_p->ip.saddr) != iflist[i].ifdstaddr)) 645 { 646 ipp_p->ip.saddr = htonl(iflist[i].ifdstaddr); 647 iflist[i].flags1 |= IFLIST_FLAGS1_CHANGED_INNER_SADDR; 648 } 649 650 // Update IP checkSum (TTL and src/dest IP Address might have changed) 651 ip_update_checksum((unsigned char *)ipp_p); 652 /* Disable upper layer checksum */ 653 udppdu = (unsigned char *)ipp_p + (4 * ipp_p->ip.ihl); 654 *IP_UDPPDU_CHECKSUM_MSB_PTR(udppdu) = (unsigned char)0; 655 *IP_UDPPDU_CHECKSUM_LSB_PTR(udppdu) = (unsigned char)0; 656 657 prepare_ipp = 1; 658 } 659 660 /* 661 * The beauty of sending IP packets on a PACKET socket of type SOCK_DGRAM is that 662 * there is no need to concern about the physical/link layer header because it is 663 * filled in automatically (based on the contents of sa). 664 */ 665 if ((nrsent = sendto(cur_ifsnr[j].sock_nr, ipp_p, rlen, MSG_DONTWAIT|MSG_TRYHARD, (struct sockaddr *)&sa, salen)) < 0) 666 { 667 if (errno == ENETDOWN) { 668 syslog(LOG_NOTICE, "ignored ENETDOWN from sendto(), a network interface was going down?"); 669 } else if (errno == ENXIO) { 670 syslog(LOG_NOTICE, "ignored ENXIO from sendto(), a network interface went down?"); 671 } else if (errno == ENOBUFS) { 672 syslog(LOG_NOTICE, "ignored ENOBUFS from sendto(), temporary shortage of buffer memory"); 673 } else { 674 syslog(LOG_ERR, "mainloop: Error, sendto failed! (rv=%d, errno=%d)", nrsent, errno); 675 exit(1); 676 } 677 } 678 NVBCR_PRINTF(("Successfully relayed %d bytes \n", nrsent)); 679 if (vnologging == 0) { 680 logstr_cntr = sprintf(logstr, "%s ", iflistLogRToString(&(iflist[j]), j, &(cur_ifsnr[j]))); 681 logstr += logstr_cntr; 682 } 683 } 684 } 685 if (vnologging == 0) syslog(LOG_INFO, "%s", log_relayed); 686 } else { 687 NVBCR_PRINTF(("is NOT an UDP BROADCAST (with TTL!=1 and UDP_CHKSUM!=0)\n\n")); 688 } 689 } 690 } 691 /* 692 * Don't forget to discover new interfaces if we keep getting 693 * incoming packets (on an already discovered interface). 694 */ 695 if (no_discifs_cntr == 0) 696 { 697 no_discifs_cntr = MAX_NODISCOVER_IFS; 698 699 /* no_discifs_cntr became 0, rediscover interfaces */ 700 NVBCR_PRINTF(("no_discifs_cntr became 0, rediscover interfaces\n")); 701 copy_ifsnr(cur_ifsnr, old_ifsnr); 702 memset(cur_ifsnr, -1, sizeof(cur_ifsnr)); 703 iflist = discoverActiveInterfaces(s); 704 /* 705 * Build new cur_ifsnr list. 706 * Make iflist[i] correspond with cur_ifsnr[i], so iflist[i].index == cur_ifsnr[i].ifindex 707 * The old list (old_ifsnr) is used to compare. 708 */ 709 for (i=0; iflist[i].index; ++i) { 710 /* check to see if it is a NEW interface */ 711 int fsnr = find_sock_nr(old_ifsnr, iflist[i].index); 712 if (fsnr == -1) { 713 /* found new interface, open dedicated socket and bind it to the interface */ 714 if ((cur_ifsnr[i].sock_nr = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL))) < 0) { 715 syslog(LOG_ERR, "mainloop: Error, socket error! (rv=%d, errno=%d)", cur_ifsnr[i].sock_nr, errno); 716 exit(1); 717 } 718 bind_to_iface(cur_ifsnr[i].sock_nr, iflist[i].index); 719 ifs_change = 1; 720 } 721 else 722 { 723 /* 724 * not a new interface, socket already openen, interface already 725 * bound. Update cur_ifsnr. 726 */ 727 cur_ifsnr[i].sock_nr = fsnr; 728 } 729 cur_ifsnr[i].ifindex = iflist[i].index; 730 } 731 /* Close disappeared interfaces */ 732 for (i=0; i<MAXIF; ++i) 733 { 734 if ((old_ifsnr[i].sock_nr != -1) && (old_ifsnr[i].ifindex != -1) && 735 (find_sock_nr(cur_ifsnr, old_ifsnr[i].ifindex) == -1)) { 736 NVBCR_PRINTF(("Closing an interface (it disappeared), namely: (s_nr=%d, ifidx=%d)\n", old_ifsnr[i].sock_nr, old_ifsnr[i].ifindex)); 737 close(old_ifsnr[i].sock_nr); 738 old_ifsnr[i].sock_nr = -1; 739 old_ifsnr[i].ifindex = -1; 740 ifs_change = 1; 741 } 742 } 743 if (ifs_change == 1) 744 { 745 NVBCR_PRINTF(("Active interface set changed --> displaying current active interfaces..\n")); 746 if (vnologging == 0) { 747 logstr = log_interfaces; 748 logstr_cntr = sprintf(logstr, "Active interface set changed to: "); 749 logstr += logstr_cntr; 750 } 751 for (i = 0; iflist[i].index; i++) 752 { 753 NVBCR_PRINTF(("\t\tactive interface number: %d, if=(%s), sock_nr=%d\n", i, iflistToString(&(iflist[i])), cur_ifsnr[i].sock_nr)); 754 if (vnologging == 0) { 755 logstr_cntr = sprintf(logstr, "%s ", iflistLogIToString(&(iflist[i]), i, &(cur_ifsnr[i]))); 756 logstr += logstr_cntr; 757 } 758 } 759 if (vnologging == 0) syslog(LOG_INFO, "%s", log_interfaces); 760 ifs_change = 0; 761 } 762 } 763 else 764 { 765 no_discifs_cntr -= MAX_SELECT_WAIT; 766 } 767 } 768 } 769} 770 771// Discover active interfaces 772struct iflist * 773discoverActiveInterfaces(int s) { 774 static struct iflist iflist[MAXIF+1]; // Allow for MAXIF interfaces 775 static struct ifconf ifs; 776 int i, j, cntr = 0; 777 regex_t preg; 778 struct ifreq ifrflags, ifr; 779 struct sockaddr_in *sin; 780 781 /* Reset iflist */ 782 memset(iflist, 0, sizeof(iflist)); 783 /* Reset ifs */ 784 memset(&ifs, 0, sizeof(ifs)); 785 786 //regcomp(&preg, argv[1], REG_ICASE|REG_EXTENDED); 787 regcomp(&preg, interfaces, REG_ICASE|REG_EXTENDED); 788 ifs.ifc_len = MAXIF*sizeof(struct ifreq); 789 ifs.ifc_req = malloc(ifs.ifc_len); 790 ioctl(s, SIOCGIFCONF, &ifs); // Discover active interfaces 791 for (i = 0; i * sizeof(struct ifreq) < ifs.ifc_len && cntr < MAXIF; i++) 792 { 793 if (regexec(&preg, ifs.ifc_req[i].ifr_name, 0, NULL, 0) == 0) { 794 795 /* 796 * Get interface flags and check status and type. 797 * Only if interface is up it will be used. 798 */ 799 memset(&ifrflags, 0, sizeof(ifrflags)); 800 strncpy(ifrflags.ifr_name, ifs.ifc_req[i].ifr_name, strlen(ifs.ifc_req[i].ifr_name)); 801 if (ioctl(s, SIOCGIFFLAGS, &ifrflags) < 0) { 802 syslog(LOG_ERR, "discoverActiveInterfaces: Error, SIOCGIFFLAGS Failed! (errno=%d)", errno); 803 exit(1); 804 } 805 806 if (ifrflags.ifr_flags & IFF_UP) 807 { 808 /* 809 * Get interface index 810 */ 811 ioctl(s, SIOCGIFINDEX, &ifs.ifc_req[i]); 812//Fix 3mar2003 813 //iflist[cntr].index = (char)ifs.ifc_req[i].ifr_ifindex; 814 iflist[cntr].index = ifs.ifc_req[i].ifr_ifindex; 815 816 /* 817 * Get interface name 818 */ 819 for (j=0; (j<sizeof(iflist[cntr].ifname) && j<strlen(ifs.ifc_req[i].ifr_ifrn.ifrn_name)); ++j) 820 iflist[cntr].ifname[j] = ifs.ifc_req[i].ifr_ifrn.ifrn_name[j]; 821 iflist[cntr].ifname[j+1] = '\0'; 822 823 /* 824 * Get local IP address 825 */ 826 memset(&ifr, 0, sizeof(ifr)); 827 ifr.ifr_addr.sa_family = AF_INET; 828 (void)strncpy(ifr.ifr_name, iflist[cntr].ifname, strlen(iflist[cntr].ifname)+1); 829 if (ioctl(s, SIOCGIFADDR, (char *)&ifr) < 0) { 830 syslog(LOG_ERR, "discoverActiveInterfaces: Error, SIOCGIFADDR Failed! (errno=%d)", errno); 831 exit(1); 832 } 833 sin = (struct sockaddr_in *)&ifr.ifr_addr; 834 iflist[cntr].ifaddr = ntohl(sin->sin_addr.s_addr); 835 836 iflist[cntr].flags1 = 0; 837 838 if (ifrflags.ifr_flags & IFF_POINTOPOINT) { 839 /* 840 * Get remote IP address (only for PPP interfaces) 841 */ 842 memset(&ifr, 0, sizeof(ifr)); 843 ifr.ifr_addr.sa_family = AF_INET; 844 (void)strncpy(ifr.ifr_name, iflist[cntr].ifname, strlen(iflist[cntr].ifname)+1); 845 if (ioctl(s, SIOCGIFDSTADDR, (char *)&ifr) < 0) { 846 syslog(LOG_ERR, "discoverActiveInterfaces: Error, SIOCGIFDSTADDR Failed! (errno=%d)", errno); 847 exit(1); 848 } 849 sin = (struct sockaddr_in *)&ifr.ifr_addr; 850 iflist[cntr].ifdstaddr = ntohl(sin->sin_addr.s_addr); 851 852 iflist[cntr].flags1 |= IFLIST_FLAGS1_IF_IS_PPP; 853 iflist[cntr].bcast = INADDR_BROADCAST; 854 } 855 else if (ifrflags.ifr_flags & IFF_BROADCAST) 856 { 857 iflist[cntr].ifdstaddr = 0; 858 iflist[cntr].flags1 |= IFLIST_FLAGS1_IF_IS_ETH; 859 iflist[cntr].bcast = INADDR_BROADCAST; 860 } 861 else 862 { 863 iflist[cntr].ifdstaddr = 0; 864 iflist[cntr].flags1 |= IFLIST_FLAGS1_IF_IS_UNKNOWN; 865 iflist[cntr].bcast = INADDR_BROADCAST; 866 } 867 868 cntr++; 869 } 870 // IPSEC tunnels are a fun one. We must change the destination address 871 // so that it will be routed to the correct tunnel end point. 872 // We can define several tunnel end points for the same ipsec interface. 873 } else if (ipsec != "" && strncmp(ifs.ifc_req[i].ifr_name, "ipsec", 5) == 0) { 874 if (strncmp(ifs.ifc_req[i].ifr_name, ipsec, 6) == 0) { 875 struct hostent *hp = gethostbyname(ipsec+7); 876 ioctl(s, SIOCGIFINDEX, &ifs.ifc_req[i]); 877 iflist[cntr].index = ifs.ifc_req[i].ifr_ifindex; /* Store the SIOCGIFINDEX number */ 878 memcpy(&(iflist[cntr++].bcast), hp->h_addr, sizeof(u_int32_t)); 879 } 880 } 881 } 882 883 iflist[cntr].index = 0; // Terminate list 884 free(ifs.ifc_req); // Stop that leak. 885 regfree(&preg); 886 887 return iflist; 888} 889 890 891 892unsigned int ip_compute_checksum(unsigned char *ippdu, int hlen) 893{ 894 unsigned int sum = 0, temp; 895 unsigned char *p; 896 unsigned char cs_msb; 897 unsigned char cs_lsb; 898 899 /* Save original checksum */ 900 cs_msb = *IP_IPPDU_CHECKSUM_MSB_PTR(ippdu); 901 cs_lsb = *IP_IPPDU_CHECKSUM_LSB_PTR(ippdu); 902 903 *IP_IPPDU_CHECKSUM_MSB_PTR(ippdu) = 0; 904 *IP_IPPDU_CHECKSUM_LSB_PTR(ippdu) = 0; 905 906 p = ippdu; 907 hlen /= 2; /* We'll compute taking two bytes a a time */ 908 while(hlen--) { sum += ((*p * 256) + *(p + 1)); p += 2; } 909 while ((temp = (sum >> 16))) { sum = (temp + (sum & 0xFFFF)); } 910 911 /* Restore original checksum */ 912 *IP_IPPDU_CHECKSUM_MSB_PTR(ippdu) = cs_msb; 913 *IP_IPPDU_CHECKSUM_LSB_PTR(ippdu) = cs_lsb; 914 915 return(~sum & 0xFFFF); 916} 917 918void ip_update_checksum(unsigned char *ippdu) 919{ 920 unsigned int cs; 921 922 cs = ip_compute_checksum(ippdu, 4 * IP_IPPDU_IHL(ippdu)); 923 924 *IP_IPPDU_CHECKSUM_MSB_PTR(ippdu) = (unsigned char)((cs >> 8) & 0xFF); 925 *IP_IPPDU_CHECKSUM_LSB_PTR(ippdu) = (unsigned char)(cs & 0xFF); 926} 927 928 929static char *IpProtToString( unsigned char prot ) 930{ 931 switch( prot ) 932 { 933 case 0x11: 934 return "UDP"; 935 case 0x6: 936 return "TCP"; 937 case 0x2f: 938 return "GRE"; 939 case 0x1: 940 return "ICMP"; 941 default: 942 return "???"; 943 } 944} 945 946static char *iflistToString( struct iflist *ifp ) 947{ 948 static char str_tr[80+1]; 949 950 sprintf(str_tr, "index=%d, ifname=%s, ifaddr=%ld.%ld.%ld.%ld, ifdstaddr=%ld.%ld.%ld.%ld, flags1=0x%04lx", 951 ifp->index, ifp->ifname, 952 (ifp->ifaddr)>>24, ((ifp->ifaddr)&0x00ff0000)>>16, ((ifp->ifaddr)&0x0000ff00)>>8, (ifp->ifaddr)&0x000000ff, 953 (ifp->ifdstaddr)>>24, ((ifp->ifdstaddr)&0x00ff0000)>>16, ((ifp->ifdstaddr)&0x0000ff00)>>8, 954 (ifp->ifdstaddr)&0x000000ff, ifp->flags1); 955 956 return str_tr; 957} 958 959static char *iflistLogRToString( struct iflist *ifp, int idx, struct ifsnr *ifnr ) 960{ 961 static char str_tr[MAX_IFLOGTOSTR]; /* 962 * This makes function: 1) non-reentrant (doesn't matter). 963 * 2) not useable multiple times by (s)printf. 964 */ 965 sprintf(str_tr, "%s", ifp->ifname); 966 return str_tr; 967} 968 969static char *iflistLogIToString( struct iflist *ifp, int idx, struct ifsnr *ifnr ) 970{ 971 static char str_tr[MAX_IFLOGTOSTR]; /* 972 * This makes function: 1) non-reentrant (doesn't matter). 973 * 2) not useable multiple times by (s)printf. 974 */ 975 sprintf(str_tr, "%s(%d/%d/%d)", ifp->ifname, idx, ifp->index, ifnr->sock_nr); 976 return str_tr; 977} 978 979static void bind_to_iface(int fd, int ifindex) 980{ 981 struct sockaddr_ll sll; 982 983 memset(&sll, 0, sizeof(sll)); 984 sll.sll_family = AF_PACKET; 985 sll.sll_ifindex = ifindex; 986 sll.sll_protocol = htons(ETH_P_ALL); 987 988 if (bind(fd, (struct sockaddr *) &sll, sizeof(sll)) == -1) { 989 syslog(LOG_ERR, "bind_to_iface: Error, bind failed! (rv=-1, errno=%d)", errno); 990 exit(1); 991 } 992} 993 994static void copy_ifsnr(struct ifsnr *from, struct ifsnr *to) 995{ 996 int i; 997 998 for (i=0; i<MAXIF; ++i) 999 { 1000 to[i].sock_nr = from[i].sock_nr; 1001 to[i].ifindex = from[i].ifindex; 1002 } 1003} 1004 1005static int find_sock_nr(struct ifsnr *l, int ifidx) 1006{ 1007 int i; 1008 1009 for (i=0; i<MAXIF; ++i) 1010 if (l[i].ifindex == ifidx) return l[i].sock_nr; 1011 /* not found */ 1012 return -1; 1013} 1014 1015