// A broadcast packet repeater. This packet repeater (currently designed for // udp packets) will listen for broadcast packets. // When it receives the packets, it will then re-broadcast the packet. // // Written by TheyCallMeLuc(at)yahoo.com.au // I accept no responsiblity for the function of this program if you // choose to use it. // Modified for Poptop by Richard de Vroede // Ditto on the no responsibility. // // Rewritten by Norbert van Bolhuis bcrelay (v1.0+) // now supports/does: // 1) Relaying from PPP (VPN tunnel) interfaces, hereby creating a virtual // LAN (w.r.t. UDP broadcasts) for VPN clients and ethernet PCs // belonging/matching the subnet portion of the VPN IP addresses. // So now broadcasting to/from all systems of the VPN has been implemented. // Note that bcrelay v0.5 only relayed from LAN to VPN clients. // It doesn't matter whether the systems on the VPN are on the LAN of the // VPN server or have a VPN/PPTP connection (over the internet) to the VPN // server. Broadcasts will always be relayed to/from all given interfaces. And // as long as the subnet portion of the IP addresses of the systems on the VPN // matches, the VPN server will be able to route properly. This means all // networking applications/games that rely on a UDP broadcast from one or // more PPP (VPN tunnel) interfaces will now see eachother and work over // the VPN. // Note that it depends on the networking application/game and whoever // acts as application/game server/host who is sending (UPD) broadcasts // and who is listening. // 2) UDP broadcasts received on a PPP interface (VPN tunnel) sometimes // don't carry the VPN IP address which pptpd provisioned. I've seen // this happening on a WinXP SP1 box, especially when the application // responsible for the UDP broadcasts isn't aware of the PPP interface. // In this case it just uses the LAN IP src address for the IP src // address of the inner (GRE encapsulated) IP packet. This breaks // the "virtual LAN" and therefore bcrelay, as of this version, changes // the src IP address to the VPN IP address (which pptpd provisioned) // before relaying. // 3) To avoid a UDP broadcast loop, bcrelay changes the IP TTL and the // UDP checksum (to 1 and 0 respectively) of the UDP broadcasts it relays. // No relaying will be done for UDP broadcasts with IP TTL=1 and UDP // checksum=0. Could also (mis)use IP identification for this, but IP TTL // and UDP chksum combination is expected to work fine. // 4) bcrelay v0.5 forgot to update IP/UDP checksum when it changed the // dest. IP address (e.g. from 192.168.1.255 to 255.255.255.255). // Of course as of this version bcrelay always updates the IP/UDP // checksum since the IP TTL and src IP address will change also. // 5) Enhanced the (syslog) debugging capabilities. By default bcrelay will // show what it is doing. Bcrelay will syslog the IP interfaces it tries // to read/relay UDP broadcasts from/to. These interfaces are called // the 'active interfaces', bcrelay will syslog the initial set of active // interfaces and whenever the set changes. Currently there is no difference // between an incoming interface (given with -i) and an outgoing interface // (given with -o), so bcrelay won't show this attribute. Also, bcrelay will // syslog a successfully relayed UDP broadcast, including the UDP port numbers, // the incoming interface and the interface(s) to which it was successfully // relayed. The (new) -n option allows to suppress syslog tracing. // If -n is given, bcrelay shows/syslogs nothing, except fatal error // messages. // // This software is completely free. You can use and/or modify this // software to your hearts content. You are free to redistribute it as // long as it is accompanied with the source and my credit is included. #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef __linux__ #define _GNU_SOURCE 1 /* strdup() prototype, broken arpa/inet.h */ #endif #ifdef __svr4__ #define __EXTENSIONS__ 1 /* strdup() prototype */ #endif #ifdef __sgi__ #define _XOPEN_SOURCE 500 /* strdup() prototype */ #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "defaults.h" #include "our_syslog.h" #include "our_getopt.h" //#define VERSION "1.0" /* uncomment if you compile this without poptop's configure script */ //#define HAVE_FORK /* * Value-return macros to fields in the IP PDU header */ #define IP_IPPDU_IHL(ippdu) (*(unsigned char *)(ippdu) & 0x0F) #define IP_IPPDU_PROTO(ippdu) (*((unsigned char *)(ippdu) + 9) & 0xFF) /* * Pointer macros to fields in the IP PDU header */ #define IP_IPPDU_CHECKSUM_MSB_PTR(ippdu) ((unsigned char *)(ippdu) + 10 ) #define IP_IPPDU_CHECKSUM_LSB_PTR(ippdu) ((unsigned char *)(ippdu) + 11 ) /* * Pointer macros to fields in the UDP PDU header */ #define IP_UDPPDU_CHECKSUM_MSB_PTR(udppdu) ((unsigned char *)(udppdu) + 6 ) #define IP_UDPPDU_CHECKSUM_LSB_PTR(udppdu) ((unsigned char *)(udppdu) + 7 ) #define MAXIF 255 // Maximum interfaces to use #define MAX_SELECT_WAIT 3 // Maximum time (in secs) to wait for input on the socket/interfaces // A time-out triggers the discovery of new interfaces. #define MAX_NODISCOVER_IFS 12 // Maximum time (in secs) to elaps before a discovery of new // interfaces is triggered. Only when a packet keeps coming in // (this prevents a select time-out) a variable initialized with // this #define becomes 0 and a rediscovery of the interfaces is // triggered. #define MAX_IFLOGTOSTR 16 /* Local function prototypes */ static void showusage(char *prog); static void showversion(); #ifndef HAVE_DAEMON static void my_daemon(int argc, char **argv); #endif static void mainloop(int argc, char **argv); struct packet { struct iphdr ip; struct udphdr udp; char data[ETHERMTU]; }; /* * struct that keeps track of the interfaces of the system * selected upon usage by bcrelay (with -i and -o option). * An array of this struct is returned by discoverActiveInterfaces. * This array is reset (filled from scratch) each time * discoverActiveInterfaces is called. */ struct iflist { //Fix 3mar2003 //char index; int index; u_int32_t bcast; char ifname[16+1]; unsigned long ifaddr; unsigned long ifdstaddr; unsigned long flags1; }; #define IFLIST_FLAGS1_IF_IS_ETH 0x00000001 #define IFLIST_FLAGS1_IF_IS_PPP 0x00000002 #define IFLIST_FLAGS1_IF_IS_UNKNOWN 0x00000004 #define IFLIST_FLAGS1_CHANGED_INNER_SADDR 0x00010000 /* * struct that keeps track of the socket fd's for every interface * that is in use (and thus present in iflist). * Two permanent arrays of this struct are used, one for the * previous/old list and one for the current list. */ struct ifsnr { int sock_nr; int ifindex; }; static void copy_ifsnr(struct ifsnr *from, struct ifsnr *to); static int find_sock_nr(struct ifsnr *l, int ifidx); struct iflist *discoverActiveInterfaces(int s); void ip_update_checksum(unsigned char *ippdu); static char *IpProtToString( unsigned char prot ); static char *iflistToString( struct iflist *ifp ); static char *iflistLogIToString( struct iflist *ifp, int idx, struct ifsnr *ifnr ); static char *iflistLogRToString( struct iflist *ifp, int idx, struct ifsnr *ifnr ); static void bind_to_iface(int fd, int ifindex); /* * This global variable determines whether NVBCR_PRINTF actually * displays something. While developping v1.0, NVBCR_PRINTF were * printf and a lot of tracing/logging/debugging was done with these. * Of course, by default these 'info' messages have been turned off * now. Enable by setting variable to 1. Note that output will only * appear in non-daemon mode (see also NVBCR_PRINTF). */ static int do_org_info_printfs = 0; static int vnologging = 0; static int vdaemon = 0; #define NVBCR_PRINTF( args ) \ if ((vdaemon == 0) && (do_org_info_printfs == 1)) printf args static char empty[1] = ""; static char interfaces[32]; static char log_interfaces[MAX_IFLOGTOSTR*MAXIF]; static char log_relayed[(MAX_IFLOGTOSTR-1)*MAXIF+81]; static char *ipsec = empty; static void showusage(char *prog) { printf("\nBCrelay v%s\n\n", VERSION); printf("A broadcast packet repeater. This packet repeater (currently designed for udp packets) will listen\n"); printf(" for broadcast packets. When it receives the packets, it will then re-broadcast the packet.\n\n"); printf("Usage: %s [options], where options are:\n\n", prog); printf(" [-d] [--daemon] Run as daemon.\n"); printf(" [-h] [--help] Displays this help message.\n"); printf(" [-i] [--incoming ] Defines from which interface broadcasts will be relayed.\n"); printf(" [-n] [--nolog] No logging/tracing to /var/log/messages.\n"); printf(" [-o] [--outgoing ] Defines to which interface broadcasts will be relayed.\n"); printf(" [-s] [--ipsec ] Defines an ipsec tunnel to be relayed to.\n"); printf(" Since ipsec tunnels terminate on the same interface, we need to define the broadcast\n"); printf(" address of the other end-point of the tunnel. This is done as ipsec0:x.x.x.255\n"); printf(" [-v] [--version] Displays the BCrelay version number.\n"); printf("\nLog messages and debugging go to syslog as DAEMON.\n\n"); printf("\nInterfaces can be specified as regexpressions, ie. ppp[0-9]+\n\n"); } static void showversion() { printf("BCrelay v%s\n", VERSION); } #ifndef HAVE_DAEMON static void my_daemon(int argc, char **argv) { pid_t pid; #ifndef BCRELAY_BIN /* Need a smart way to locate the binary -rdv */ #define BCRELAY_BIN argv[0] #endif #ifndef HAVE_FORK /* need to use vfork - eg, uClinux */ char **new_argv; extern char **environ; int minusd=0; int i; int fdr; /* Strip -d option */ new_argv = malloc((argc) * sizeof(char **)); fdr = open("/dev/null", O_RDONLY); new_argv[0] = BCRELAY_BIN; for (i = 1; argv[i] != NULL; i++) { if (fdr != 0) { dup2(fdr, 0); close(fdr); } if ( (strcmp(argv[i],"-d")) == 0 ) { minusd=1; } if (minusd) { new_argv[i] = argv[i+1]; } else { new_argv[i] = argv[i]; } } syslog(LOG_DEBUG, "Option parse OK, re-execing as daemon"); fflush(stderr); if ((pid = vfork()) == 0) { if (setsid() < 0) { /* shouldn't fail */ syslog(LOG_ERR, "Setsid failed!"); _exit(1); } chdir("/"); umask(0); /* execve only returns on an error */ execve(BCRELAY_BIN, new_argv, environ); exit(1); } else if (pid > 0) { syslog(LOG_DEBUG, "Success re-execing as daemon!"); exit(0); } else { syslog(LOG_ERR, "Error vforking"); exit(1); } #else pid=fork(); if (pid<0) { syslog(LOG_ERR, "Error forking"); _exit(1); } if (pid>0) { syslog(LOG_DEBUG, "Parent exits"); _exit(0); } if (pid==0) { syslog(LOG_DEBUG, "Running as child"); } /* child (daemon) continues */ if (setsid() < 0) { /* shouldn't fail */ syslog(LOG_ERR, "Setsid failed!"); _exit(1); } chdir("/"); #endif } #endif int main(int argc, char **argv) { regex_t preg; /* command line options */ int c; char *ifout = empty; char *ifin = empty; #ifndef BCRELAY fprintf(stderr, "bcrelay: pptpd was compiled without support for bcrelay, exiting.\n" " run configure --with-bcrelay, make, and install.\n"); exit(1); #endif /* open a connection to the syslog daemon */ openlog("bcrelay", LOG_PID, PPTP_FACILITY); while (1) { int option_index = 0; static struct option long_options[] = { {"nolog", 0, 0, 0}, {"daemon", 0, 0, 0}, {"help", 0, 0, 0}, {"incoming", 1, 0, 0}, {"outgoing", 1, 0, 0}, {"ipsec", 1, 0, 0}, {"version", 0, 0, 0}, {0, 0, 0, 0} }; c = getopt_long(argc, argv, "ndhi:o:s:v", long_options, &option_index); if (c == -1) break; /* convert long options to short form */ if (c == 0) c = "ndhiosv"[option_index]; switch (c) { case 'n': vnologging = 1; break; case 'd': vdaemon = 1; break; case 'h': showusage(argv[0]); return 0; case 'i': ifin = strdup(optarg); break; case 'o': ifout = strdup(optarg); break; case 's': ipsec = strdup(optarg); // Validate the ipsec parameters regcomp(&preg, "ipsec[0-9]+:[0-9]+.[0-9]+.[0-9]+.255", REG_EXTENDED); if (regexec(&preg, ipsec, 0, NULL, 0)) { syslog(LOG_INFO,"Bad syntax: %s", ipsec); fprintf(stderr, "\nBad syntax: %s\n", ipsec); showusage(argv[0]); return 0; } else { regfree(&preg); break; } case 'v': showversion(); return 0; default: showusage(argv[0]); return 1; } } if (ifin == empty) { syslog(LOG_INFO,"Incoming interface required!"); showusage(argv[0]); _exit(1); } if (ifout == empty && ipsec == empty) { syslog(LOG_INFO,"Listen-mode or outgoing or IPsec interface required!"); showusage(argv[0]); _exit(1); } else { sprintf(interfaces,"%s|%s", ifin, ifout); } // If specified, become Daemon. if (vdaemon) { #if HAVE_DAEMON closelog(); if (freopen("/dev/null", "r", stdin) == NULL) { syslog(LOG_ERR, "failed to reopen stdin"); } /* set noclose, we want stdout/stderr still attached if we can */ if (daemon(0, 1) == -1) { syslog_perror("daemon"); } /* returns to child only */ /* pid will have changed */ openlog("bcrelay", LOG_PID, PPTP_FACILITY); #else /* !HAVE_DAEMON */ my_daemon(argc, argv); /* returns to child if !HAVE_FORK * never returns if HAVE_FORK (re-execs without -d) */ #endif } else { syslog(LOG_INFO, "Running as child\n"); } mainloop(argc,argv); _exit(0); } static void mainloop(int argc, char **argv) { socklen_t salen = sizeof(struct sockaddr_ll); int i, s, rcg, j, no_discifs_cntr, ifs_change; int logstr_cntr; struct iflist *iflist = NULL; // Initialised after the 1st packet struct sockaddr_ll sa; struct packet *ipp_p; unsigned char *udppdu; fd_set sock_set; struct timeval time_2_wait; static struct ifsnr old_ifsnr[MAXIF+1]; // Old iflist to socket fd's mapping list static struct ifsnr cur_ifsnr[MAXIF+1]; // Current iflist to socket fd's mapping list unsigned char buf[1518]; char *logstr = empty; no_discifs_cntr = MAX_NODISCOVER_IFS; ifs_change = 0; /* * Open general ethernet socket, only used to discover interfaces. */ if ((s = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL))) < 0) syslog(LOG_INFO,"%s: Error creating socket", *argv); /* * Discover interfaces (initial set) and create a dedicated socket bound to the interface */ memset(old_ifsnr, -1, sizeof(old_ifsnr)); memset(cur_ifsnr, -1, sizeof(cur_ifsnr)); iflist = discoverActiveInterfaces(s); for (i=0; iflist[i].index; ++i) { if ((cur_ifsnr[i].sock_nr = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL))) < 0) { syslog(LOG_ERR, "mainloop: Error, socket error! (rv=%d, errno=%d)", cur_ifsnr[i].sock_nr, errno); exit(1); } bind_to_iface(cur_ifsnr[i].sock_nr, iflist[i].index); cur_ifsnr[i].ifindex = iflist[i].index; } NVBCR_PRINTF(("Displaying INITIAL active interfaces..\n")); if (vnologging == 0) { logstr = log_interfaces; logstr_cntr = sprintf(logstr, "Initial active interfaces: "); logstr += logstr_cntr; } for (i = 0; iflist[i].index; i++) { NVBCR_PRINTF(("\t\tactive interface number: %d, if=(%s), sock_nr=%d\n", i, iflistToString(&(iflist[i])), cur_ifsnr[i].sock_nr)); if (vnologging == 0) { logstr_cntr = sprintf(logstr, "%s ", iflistLogIToString(&(iflist[i]), i, &(cur_ifsnr[i]))); logstr += logstr_cntr; } } if (vnologging == 0) syslog(LOG_INFO, "%s", log_interfaces); // Main loop while (1) { /* * Check all (interface) sockets for incoming packets */ FD_ZERO(&sock_set); for (i=0; iflist[i].index; ++i) { if (cur_ifsnr[i].sock_nr >= 0) { FD_SET(cur_ifsnr[i].sock_nr, &sock_set); } } /* * Don't wait more than MAX_SELECT_WAIT seconds */ time_2_wait.tv_sec = MAX_SELECT_WAIT; time_2_wait.tv_usec = 0L; /* select on sockets */ rcg = select(MAXIF, &sock_set, (fd_set *) NULL,(fd_set *) NULL, &time_2_wait); if (rcg < 0) { syslog(LOG_ERR, "Error, select error! (rv=%d, errno=%d)", rcg, errno); exit(1); } if (rcg == 0) { /* TimeOut, rediscover interfaces */ NVBCR_PRINTF(("Select timeout, rediscover interfaces\n")); copy_ifsnr(cur_ifsnr, old_ifsnr); memset(cur_ifsnr, -1, sizeof(cur_ifsnr)); iflist = discoverActiveInterfaces(s); /* * Build new cur_ifsnr list. * Make iflist[i] correspond with cur_ifsnr[i], so iflist[i].index == cur_ifsnr[i].ifindex * The old list (old_ifsnr) is used to compare. */ for (i=0; iflist[i].index; ++i) { /* check to see if it is a NEW interface */ int fsnr = find_sock_nr(old_ifsnr, iflist[i].index); if (fsnr == -1) { /* found new interface, open dedicated socket and bind it to the interface */ if ((cur_ifsnr[i].sock_nr = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL))) < 0) { syslog(LOG_ERR, "mainloop: Error, socket error! (rv=%d, errno=%d)", cur_ifsnr[i].sock_nr, errno); exit(1); } bind_to_iface(cur_ifsnr[i].sock_nr, iflist[i].index); ifs_change = 1; } else { /* * not a new interface, socket already openen, interface already * bound. Update cur_ifsnr. */ cur_ifsnr[i].sock_nr = fsnr; } cur_ifsnr[i].ifindex = iflist[i].index; } /* Close disappeared interfaces */ for (i=0; i displaying current active interfaces..\n")); if (vnologging == 0) { logstr = log_interfaces; logstr_cntr = sprintf(logstr, "Active interface set changed to: "); logstr += logstr_cntr; } for (i = 0; iflist[i].index; i++) { NVBCR_PRINTF(("\t\tactive interface number: %d, if=(%s), sock_nr=%d\n", i, iflistToString(&(iflist[i])), cur_ifsnr[i].sock_nr)); if (vnologging == 0) { logstr_cntr = sprintf(logstr, "%s ", iflistLogIToString(&(iflist[i]), i, &(cur_ifsnr[i]))); logstr += logstr_cntr; } } if (vnologging == 0) syslog(LOG_INFO, "%s", log_interfaces); ifs_change = 0; } continue; } if (rcg > 0) { /* rcg interfaces have pending input */ for (i=0; ((iflist[i].index != 0) && (rcg > 0)); ++i) { if ((cur_ifsnr[i].sock_nr != -1) && (FD_ISSET(cur_ifsnr[i].sock_nr,&sock_set))) { /* Valid socket number and pending input, let's read */ int rlen = read(cur_ifsnr[i].sock_nr, buf, sizeof(buf)); ipp_p = (struct packet *)&(buf[0]); 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)); rcg -= 1; if ( (ipp_p->ip.protocol == IPPROTO_UDP) && (((ntohl(ipp_p->ip.daddr)) & 0x000000ff) == 0x000000ff) && (ipp_p->ip.ttl != 1) && (!((*IP_UDPPDU_CHECKSUM_MSB_PTR((unsigned char *)ipp_p+(4*ipp_p->ip.ihl)) == 0) && (*IP_UDPPDU_CHECKSUM_LSB_PTR((unsigned char *)ipp_p+(4*ipp_p->ip.ihl)) == 0))) ) { int nrsent; int ifindex_to_exclude = iflist[i].index; NVBCR_PRINTF(("is an UDP BROADCAST (dstPort=%d, srcPort=%d) (with TTL!=1 and UDP_CHKSUM!=0)\n\n", ntohs(ipp_p->udp.dest), ntohs(ipp_p->udp.source))); if (vnologging == 0) { logstr = log_relayed; logstr_cntr = sprintf(logstr, "UDP_BroadCast(sp=%d,dp=%d) from: %s relayed to: ", ntohs(ipp_p->udp.source), ntohs(ipp_p->udp.dest), iflistLogRToString(&(iflist[i]), i, &(cur_ifsnr[i]))); logstr += logstr_cntr; } /* going to relay a broadcast packet on all the other interfaces */ for (j=0; iflist[j].index; ++j) { int prepare_ipp = 0; // Changing the incoming UDP broadcast needs to be done once if (iflist[j].index != ifindex_to_exclude) { NVBCR_PRINTF(("Going to sent UDP Broadcast on interface: %s, sock_nr=%d\n", iflistToString(&(iflist[j])), cur_ifsnr[j].sock_nr)); memset(&sa, 0, salen); sa.sll_family = AF_PACKET; sa.sll_ifindex = iflist[j].index; /* Must be the SIOCGIFINDEX number */ // Set the outgoing hardware address to 1's. True Broadcast sa.sll_addr[0] = sa.sll_addr[1] = sa.sll_addr[2] = sa.sll_addr[3] = 0xff; sa.sll_addr[4] = sa.sll_addr[5] = sa.sll_addr[6] = sa.sll_addr[7] = 0xff; sa.sll_halen = 6; /* * htons(ETH_P_IP) is necessary otherwise sendto will * succeed but no packet is actually sent on the wire (this * was the case for PPP interfaces, for ETH interfaces an unknown * LAN frame is sent if htons(ETH_P_IP) is not set as protocol). */ sa.sll_protocol = htons(ETH_P_IP); /* ETH_P_PPP_MP */ if (prepare_ipp == 0) { // change TimeToLive to 1, This is to avoid loops, bcrelay will *NOT* relay // anything with TTL==1. ipp_p->ip.ttl = 1; // The CRC gets broken here when sending over ipsec tunnels but that // should not matter as we reassemble the packet at the other end. ipp_p->ip.daddr = iflist[j].bcast; // check IP srcAddr (on some winXP boxes it is found to be // different from the configured ppp address). // Only do this for PPP interfaces. if ((iflist[i].flags1 & IFLIST_FLAGS1_IF_IS_PPP) && (ntohl(ipp_p->ip.saddr) != iflist[i].ifdstaddr)) { ipp_p->ip.saddr = htonl(iflist[i].ifdstaddr); iflist[i].flags1 |= IFLIST_FLAGS1_CHANGED_INNER_SADDR; } // Update IP checkSum (TTL and src/dest IP Address might have changed) ip_update_checksum((unsigned char *)ipp_p); /* Disable upper layer checksum */ udppdu = (unsigned char *)ipp_p + (4 * ipp_p->ip.ihl); *IP_UDPPDU_CHECKSUM_MSB_PTR(udppdu) = (unsigned char)0; *IP_UDPPDU_CHECKSUM_LSB_PTR(udppdu) = (unsigned char)0; prepare_ipp = 1; } /* * The beauty of sending IP packets on a PACKET socket of type SOCK_DGRAM is that * there is no need to concern about the physical/link layer header because it is * filled in automatically (based on the contents of sa). */ if ((nrsent = sendto(cur_ifsnr[j].sock_nr, ipp_p, rlen, MSG_DONTWAIT|MSG_TRYHARD, (struct sockaddr *)&sa, salen)) < 0) { if (errno == ENETDOWN) { syslog(LOG_NOTICE, "ignored ENETDOWN from sendto(), a network interface was going down?"); } else if (errno == ENXIO) { syslog(LOG_NOTICE, "ignored ENXIO from sendto(), a network interface went down?"); } else if (errno == ENOBUFS) { syslog(LOG_NOTICE, "ignored ENOBUFS from sendto(), temporary shortage of buffer memory"); } else { syslog(LOG_ERR, "mainloop: Error, sendto failed! (rv=%d, errno=%d)", nrsent, errno); exit(1); } } NVBCR_PRINTF(("Successfully relayed %d bytes \n", nrsent)); if (vnologging == 0) { logstr_cntr = sprintf(logstr, "%s ", iflistLogRToString(&(iflist[j]), j, &(cur_ifsnr[j]))); logstr += logstr_cntr; } } } if (vnologging == 0) syslog(LOG_INFO, "%s", log_relayed); } else { NVBCR_PRINTF(("is NOT an UDP BROADCAST (with TTL!=1 and UDP_CHKSUM!=0)\n\n")); } } } /* * Don't forget to discover new interfaces if we keep getting * incoming packets (on an already discovered interface). */ if (no_discifs_cntr == 0) { no_discifs_cntr = MAX_NODISCOVER_IFS; /* no_discifs_cntr became 0, rediscover interfaces */ NVBCR_PRINTF(("no_discifs_cntr became 0, rediscover interfaces\n")); copy_ifsnr(cur_ifsnr, old_ifsnr); memset(cur_ifsnr, -1, sizeof(cur_ifsnr)); iflist = discoverActiveInterfaces(s); /* * Build new cur_ifsnr list. * Make iflist[i] correspond with cur_ifsnr[i], so iflist[i].index == cur_ifsnr[i].ifindex * The old list (old_ifsnr) is used to compare. */ for (i=0; iflist[i].index; ++i) { /* check to see if it is a NEW interface */ int fsnr = find_sock_nr(old_ifsnr, iflist[i].index); if (fsnr == -1) { /* found new interface, open dedicated socket and bind it to the interface */ if ((cur_ifsnr[i].sock_nr = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL))) < 0) { syslog(LOG_ERR, "mainloop: Error, socket error! (rv=%d, errno=%d)", cur_ifsnr[i].sock_nr, errno); exit(1); } bind_to_iface(cur_ifsnr[i].sock_nr, iflist[i].index); ifs_change = 1; } else { /* * not a new interface, socket already openen, interface already * bound. Update cur_ifsnr. */ cur_ifsnr[i].sock_nr = fsnr; } cur_ifsnr[i].ifindex = iflist[i].index; } /* Close disappeared interfaces */ for (i=0; i displaying current active interfaces..\n")); if (vnologging == 0) { logstr = log_interfaces; logstr_cntr = sprintf(logstr, "Active interface set changed to: "); logstr += logstr_cntr; } for (i = 0; iflist[i].index; i++) { NVBCR_PRINTF(("\t\tactive interface number: %d, if=(%s), sock_nr=%d\n", i, iflistToString(&(iflist[i])), cur_ifsnr[i].sock_nr)); if (vnologging == 0) { logstr_cntr = sprintf(logstr, "%s ", iflistLogIToString(&(iflist[i]), i, &(cur_ifsnr[i]))); logstr += logstr_cntr; } } if (vnologging == 0) syslog(LOG_INFO, "%s", log_interfaces); ifs_change = 0; } } else { no_discifs_cntr -= MAX_SELECT_WAIT; } } } } // Discover active interfaces struct iflist * discoverActiveInterfaces(int s) { static struct iflist iflist[MAXIF+1]; // Allow for MAXIF interfaces static struct ifconf ifs; int i, cntr = 0; regex_t preg; struct ifreq ifrflags, ifr; struct sockaddr_in *sin; /* Reset iflist */ memset(iflist, 0, sizeof(iflist)); /* Reset ifs */ memset(&ifs, 0, sizeof(ifs)); //regcomp(&preg, argv[1], REG_ICASE|REG_EXTENDED); regcomp(&preg, interfaces, REG_ICASE|REG_EXTENDED); ifs.ifc_len = MAXIF*sizeof(struct ifreq); ifs.ifc_req = malloc(ifs.ifc_len); ioctl(s, SIOCGIFCONF, &ifs); // Discover active interfaces for (i = 0; i * sizeof(struct ifreq) < ifs.ifc_len && cntr < MAXIF; i++) { if (regexec(&preg, ifs.ifc_req[i].ifr_name, 0, NULL, 0) == 0) { /* * Get interface flags and check status and type. * Only if interface is up it will be used. */ memset(&ifrflags, 0, sizeof(ifrflags)); strncpy(ifrflags.ifr_name, ifs.ifc_req[i].ifr_name, strlen(ifs.ifc_req[i].ifr_name)); if (ioctl(s, SIOCGIFFLAGS, &ifrflags) < 0) { syslog(LOG_ERR, "discoverActiveInterfaces: Error, SIOCGIFFLAGS Failed! (errno=%d)", errno); exit(1); } if (ifrflags.ifr_flags & IFF_UP) { /* * Get interface index */ ioctl(s, SIOCGIFINDEX, &ifs.ifc_req[i]); //Fix 3mar2003 //iflist[cntr].index = (char)ifs.ifc_req[i].ifr_ifindex; iflist[cntr].index = ifs.ifc_req[i].ifr_ifindex; /* * Get interface name */ strncpy(iflist[cntr].ifname, ifs.ifc_req[i].ifr_ifrn.ifrn_name, sizeof(iflist[cntr].ifname)); iflist[cntr].ifname[sizeof(iflist[cntr].ifname)-1] = 0; /* * Get local IP address */ memset(&ifr, 0, sizeof(ifr)); ifr.ifr_addr.sa_family = AF_INET; (void)strncpy(ifr.ifr_name, iflist[cntr].ifname, strlen(iflist[cntr].ifname)+1); if (ioctl(s, SIOCGIFADDR, (char *)&ifr) < 0) { syslog(LOG_ERR, "discoverActiveInterfaces: Error, SIOCGIFADDR Failed! (errno=%d)", errno); exit(1); } sin = (struct sockaddr_in *)&ifr.ifr_addr; iflist[cntr].ifaddr = ntohl(sin->sin_addr.s_addr); iflist[cntr].flags1 = 0; if (ifrflags.ifr_flags & IFF_POINTOPOINT) { /* * Get remote IP address (only for PPP interfaces) */ memset(&ifr, 0, sizeof(ifr)); ifr.ifr_addr.sa_family = AF_INET; (void)strncpy(ifr.ifr_name, iflist[cntr].ifname, strlen(iflist[cntr].ifname)+1); if (ioctl(s, SIOCGIFDSTADDR, (char *)&ifr) < 0) { syslog(LOG_ERR, "discoverActiveInterfaces: Error, SIOCGIFDSTADDR Failed! (errno=%d)", errno); exit(1); } sin = (struct sockaddr_in *)&ifr.ifr_addr; iflist[cntr].ifdstaddr = ntohl(sin->sin_addr.s_addr); iflist[cntr].flags1 |= IFLIST_FLAGS1_IF_IS_PPP; iflist[cntr].bcast = INADDR_BROADCAST; } else if (ifrflags.ifr_flags & IFF_BROADCAST) { iflist[cntr].ifdstaddr = 0; iflist[cntr].flags1 |= IFLIST_FLAGS1_IF_IS_ETH; iflist[cntr].bcast = INADDR_BROADCAST; } else { iflist[cntr].ifdstaddr = 0; iflist[cntr].flags1 |= IFLIST_FLAGS1_IF_IS_UNKNOWN; iflist[cntr].bcast = INADDR_BROADCAST; } cntr++; } // IPSEC tunnels are a fun one. We must change the destination address // so that it will be routed to the correct tunnel end point. // We can define several tunnel end points for the same ipsec interface. } else if (ipsec != empty && strncmp(ifs.ifc_req[i].ifr_name, "ipsec", 5) == 0) { if (strncmp(ifs.ifc_req[i].ifr_name, ipsec, 6) == 0) { struct hostent *hp = gethostbyname(ipsec+7); ioctl(s, SIOCGIFINDEX, &ifs.ifc_req[i]); iflist[cntr].index = ifs.ifc_req[i].ifr_ifindex; /* Store the SIOCGIFINDEX number */ memcpy(&(iflist[cntr++].bcast), hp->h_addr, sizeof(u_int32_t)); } } } iflist[cntr].index = 0; // Terminate list free(ifs.ifc_req); // Stop that leak. regfree(&preg); return iflist; } unsigned int ip_compute_checksum(unsigned char *ippdu, int hlen) { unsigned int sum = 0, temp; unsigned char *p; unsigned char cs_msb; unsigned char cs_lsb; /* Save original checksum */ cs_msb = *IP_IPPDU_CHECKSUM_MSB_PTR(ippdu); cs_lsb = *IP_IPPDU_CHECKSUM_LSB_PTR(ippdu); *IP_IPPDU_CHECKSUM_MSB_PTR(ippdu) = 0; *IP_IPPDU_CHECKSUM_LSB_PTR(ippdu) = 0; p = ippdu; hlen /= 2; /* We'll compute taking two bytes a a time */ while(hlen--) { sum += ((*p * 256) + *(p + 1)); p += 2; } while ((temp = (sum >> 16))) { sum = (temp + (sum & 0xFFFF)); } /* Restore original checksum */ *IP_IPPDU_CHECKSUM_MSB_PTR(ippdu) = cs_msb; *IP_IPPDU_CHECKSUM_LSB_PTR(ippdu) = cs_lsb; return(~sum & 0xFFFF); } void ip_update_checksum(unsigned char *ippdu) { unsigned int cs; cs = ip_compute_checksum(ippdu, 4 * IP_IPPDU_IHL(ippdu)); *IP_IPPDU_CHECKSUM_MSB_PTR(ippdu) = (unsigned char)((cs >> 8) & 0xFF); *IP_IPPDU_CHECKSUM_LSB_PTR(ippdu) = (unsigned char)(cs & 0xFF); } static char *IpProtToString( unsigned char prot ) { switch( prot ) { case 0x11: return "UDP"; case 0x6: return "TCP"; case 0x2f: return "GRE"; case 0x1: return "ICMP"; default: return "???"; } } static char *iflistToString( struct iflist *ifp ) { static char str_tr[80+1]; sprintf(str_tr, "index=%d, ifname=%s, ifaddr=%ld.%ld.%ld.%ld, ifdstaddr=%ld.%ld.%ld.%ld, flags1=0x%04lx", ifp->index, ifp->ifname, (ifp->ifaddr)>>24, ((ifp->ifaddr)&0x00ff0000)>>16, ((ifp->ifaddr)&0x0000ff00)>>8, (ifp->ifaddr)&0x000000ff, (ifp->ifdstaddr)>>24, ((ifp->ifdstaddr)&0x00ff0000)>>16, ((ifp->ifdstaddr)&0x0000ff00)>>8, (ifp->ifdstaddr)&0x000000ff, ifp->flags1); return str_tr; } static char *iflistLogRToString( struct iflist *ifp, int idx, struct ifsnr *ifnr ) { static char str_tr[MAX_IFLOGTOSTR]; /* * This makes function: 1) non-reentrant (doesn't matter). * 2) not useable multiple times by (s)printf. */ sprintf(str_tr, "%s", ifp->ifname); return str_tr; } static char *iflistLogIToString( struct iflist *ifp, int idx, struct ifsnr *ifnr ) { static char str_tr[MAX_IFLOGTOSTR]; /* * This makes function: 1) non-reentrant (doesn't matter). * 2) not useable multiple times by (s)printf. */ sprintf(str_tr, "%s(%d/%d/%d)", ifp->ifname, idx, ifp->index, ifnr->sock_nr); return str_tr; } static void bind_to_iface(int fd, int ifindex) { struct sockaddr_ll sll; memset(&sll, 0, sizeof(sll)); sll.sll_family = AF_PACKET; sll.sll_ifindex = ifindex; sll.sll_protocol = htons(ETH_P_ALL); if (bind(fd, (struct sockaddr *) &sll, sizeof(sll)) == -1) { syslog(LOG_ERR, "bind_to_iface: Error, bind failed! (rv=-1, errno=%d)", errno); exit(1); } } static void copy_ifsnr(struct ifsnr *from, struct ifsnr *to) { int i; for (i=0; i