Update and clean Tomato RAF files
[tomato.git] / release / src / router / pptpd / bcrelay.c
blobb345795f00e2302c11355d3a6b5f52b7ee9ed61e
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.
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.
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.
64 #ifdef HAVE_CONFIG_H
65 #include "config.h"
66 #endif
68 #ifdef __linux__
69 #define _GNU_SOURCE 1 /* strdup() prototype, broken arpa/inet.h */
70 #endif
72 #ifdef __svr4__
73 #define __EXTENSIONS__ 1 /* strdup() prototype */
74 #endif
76 #ifdef __sgi__
77 #define _XOPEN_SOURCE 500 /* strdup() prototype */
78 #endif
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>
102 #include "defaults.h"
103 #include "our_syslog.h"
104 #include "our_getopt.h"
106 //#define VERSION "1.0"
108 /* uncomment if you compile this without poptop's configure script */
109 //#define HAVE_FORK
112 * Value-return macros to fields in the IP PDU header
114 #define IP_IPPDU_IHL(ippdu) (*(unsigned char *)(ippdu) & 0x0F)
115 #define IP_IPPDU_PROTO(ippdu) (*((unsigned char *)(ippdu) + 9) & 0xFF)
118 * Pointer macros to fields in the IP PDU header
120 #define IP_IPPDU_CHECKSUM_MSB_PTR(ippdu) ((unsigned char *)(ippdu) + 10 )
121 #define IP_IPPDU_CHECKSUM_LSB_PTR(ippdu) ((unsigned char *)(ippdu) + 11 )
124 * Pointer macros to fields in the UDP PDU header
126 #define IP_UDPPDU_CHECKSUM_MSB_PTR(udppdu) ((unsigned char *)(udppdu) + 6 )
127 #define IP_UDPPDU_CHECKSUM_LSB_PTR(udppdu) ((unsigned char *)(udppdu) + 7 )
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
139 /* Local function prototypes */
140 static void showusage(char *prog);
141 static void showversion();
142 #ifndef HAVE_DAEMON
143 static void my_daemon(int argc, char **argv);
144 #endif
145 static void mainloop(int argc, char **argv);
147 struct packet {
148 struct iphdr ip;
149 struct udphdr udp;
150 char data[ETHERMTU];
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.
161 struct 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;
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
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.
184 struct ifsnr {
185 int sock_nr;
186 int ifindex;
189 static void copy_ifsnr(struct ifsnr *from, struct ifsnr *to);
190 static int find_sock_nr(struct ifsnr *l, int ifidx);
191 struct iflist *discoverActiveInterfaces(int s);
192 void ip_update_checksum(unsigned char *ippdu);
193 static char *IpProtToString( unsigned char prot );
194 static char *iflistToString( struct iflist *ifp );
195 static char *iflistLogIToString( struct iflist *ifp, int idx, struct ifsnr *ifnr );
196 static char *iflistLogRToString( struct iflist *ifp, int idx, struct ifsnr *ifnr );
197 static void bind_to_iface(int fd, int ifindex);
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).
207 static int do_org_info_printfs = 0;
209 static int vnologging = 0;
210 static int vdaemon = 0;
212 #define NVBCR_PRINTF( args ) \
213 if ((vdaemon == 0) && (do_org_info_printfs == 1)) printf args
215 static char interfaces[32];
216 static char log_interfaces[MAX_IFLOGTOSTR*MAXIF];
217 static char log_relayed[(MAX_IFLOGTOSTR-1)*MAXIF+81];
218 static char *ipsec = "";
220 static void showusage(char *prog)
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");
239 static void showversion()
241 printf("BCrelay v%s\n", VERSION);
244 #ifndef HAVE_DAEMON
245 static void my_daemon(int argc, char **argv)
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;
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;
269 if (minusd) {
270 new_argv[i] = argv[i+1];
271 } else {
272 new_argv[i] = argv[i];
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);
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);
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);
304 chdir("/");
305 #endif
307 #endif
309 int main(int argc, char **argv) {
310 regex_t preg;
311 /* command line options */
312 int c;
313 char *ifout = "";
314 char *ifin = "";
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
323 /* open a connection to the syslog daemon */
324 openlog("bcrelay", LOG_PID, PPTP_FACILITY);
326 while (1) {
327 int option_index = 0;
329 static struct option long_options[] =
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}
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;
376 case 'v':
377 showversion();
378 return 0;
379 default:
380 showusage(argv[0]);
381 return 1;
384 if (ifin == "") {
385 syslog(LOG_INFO,"Incoming interface required!");
386 showusage(argv[0]);
387 _exit(1);
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);
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)
412 #endif
413 } else {
414 syslog(LOG_INFO, "Running as child\n");
416 mainloop(argc,argv);
417 _exit(0);
420 static void mainloop(int argc, char **argv)
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 = "";
436 no_discifs_cntr = MAX_NODISCOVER_IFS;
437 ifs_change = 0;
440 * Open general ethernet socket, only used to discover interfaces.
442 if ((s = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL))) < 0)
443 syslog(LOG_INFO,"%s: Error creating socket", *argv);
447 * Discover interfaces (initial set) and create a dedicated socket bound to the interface
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);
457 bind_to_iface(cur_ifsnr[i].sock_nr, iflist[i].index);
458 cur_ifsnr[i].ifindex = iflist[i].index;
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;
466 for (i = 0; iflist[i].index; i++)
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;
474 if (vnologging == 0) syslog(LOG_INFO, "%s", log_interfaces);
476 // Main loop
477 while (1)
481 * Check all (interface) sockets for incoming packets
483 FD_ZERO(&sock_set);
484 for (i=0; iflist[i].index; ++i)
486 if (cur_ifsnr[i].sock_nr >= 0) {
487 FD_SET(cur_ifsnr[i].sock_nr, &sock_set);
492 * Don't wait more than MAX_SELECT_WAIT seconds
494 time_2_wait.tv_sec = MAX_SELECT_WAIT;
495 time_2_wait.tv_usec = 0L;
497 /* select on sockets */
498 rcg = select(MAXIF, &sock_set, (fd_set *) NULL,(fd_set *) NULL, &time_2_wait);
500 if (rcg < 0)
502 syslog(LOG_ERR, "Error, select error! (rv=%d, errno=%d)", rcg, errno);
503 exit(1);
506 if (rcg == 0)
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);
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.
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);
527 bind_to_iface(cur_ifsnr[i].sock_nr, iflist[i].index);
528 ifs_change = 1;
530 else
533 * not a new interface, socket already openen, interface already
534 * bound. Update cur_ifsnr.
536 cur_ifsnr[i].sock_nr = fsnr;
538 cur_ifsnr[i].ifindex = iflist[i].index;
540 /* Close disappeared interfaces */
541 for (i=0; i<MAXIF; ++i)
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;
552 if (ifs_change == 1)
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;
560 for (i = 0; iflist[i].index; i++)
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;
568 if (vnologging == 0) syslog(LOG_INFO, "%s", log_interfaces);
569 ifs_change = 0;
571 continue;
574 if (rcg > 0)
576 /* rcg interfaces have pending input */
577 for (i=0; ((iflist[i].index != 0) && (rcg > 0)); ++i)
579 if ((cur_ifsnr[i].sock_nr != -1) && (FD_ISSET(cur_ifsnr[i].sock_nr,&sock_set)))
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;
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))) )
593 int nrsent;
594 int ifindex_to_exclude = iflist[i].index;
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;
605 /* going to relay a broadcast packet on all the other interfaces */
606 for (j=0; iflist[j].index; ++j)
608 int prepare_ipp = 0; // Changing the incoming UDP broadcast needs to be done once
610 if (iflist[j].index != ifindex_to_exclude)
612 NVBCR_PRINTF(("Going to sent UDP Broadcast on interface: %s, sock_nr=%d\n", iflistToString(&(iflist[j])), cur_ifsnr[j].sock_nr));
614 memset(&sa, 0, salen);
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;
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).
629 sa.sll_protocol = htons(ETH_P_IP); /* ETH_P_PPP_MP */
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;
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;
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))
646 ipp_p->ip.saddr = htonl(iflist[i].ifdstaddr);
647 iflist[i].flags1 |= IFLIST_FLAGS1_CHANGED_INNER_SADDR;
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;
657 prepare_ipp = 1;
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).
665 if ((nrsent = sendto(cur_ifsnr[j].sock_nr, ipp_p, rlen, MSG_DONTWAIT|MSG_TRYHARD, (struct sockaddr *)&sa, salen)) < 0)
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);
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;
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"));
692 * Don't forget to discover new interfaces if we keep getting
693 * incoming packets (on an already discovered interface).
695 if (no_discifs_cntr == 0)
697 no_discifs_cntr = MAX_NODISCOVER_IFS;
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);
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.
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);
718 bind_to_iface(cur_ifsnr[i].sock_nr, iflist[i].index);
719 ifs_change = 1;
721 else
724 * not a new interface, socket already openen, interface already
725 * bound. Update cur_ifsnr.
727 cur_ifsnr[i].sock_nr = fsnr;
729 cur_ifsnr[i].ifindex = iflist[i].index;
731 /* Close disappeared interfaces */
732 for (i=0; i<MAXIF; ++i)
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;
743 if (ifs_change == 1)
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;
751 for (i = 0; iflist[i].index; i++)
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;
759 if (vnologging == 0) syslog(LOG_INFO, "%s", log_interfaces);
760 ifs_change = 0;
763 else
765 no_discifs_cntr -= MAX_SELECT_WAIT;
771 // Discover active interfaces
772 struct iflist *
773 discoverActiveInterfaces(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;
781 /* Reset iflist */
782 memset(iflist, 0, sizeof(iflist));
783 /* Reset ifs */
784 memset(&ifs, 0, sizeof(ifs));
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++)
793 if (regexec(&preg, ifs.ifc_req[i].ifr_name, 0, NULL, 0) == 0) {
796 * Get interface flags and check status and type.
797 * Only if interface is up it will be used.
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);
806 if (ifrflags.ifr_flags & IFF_UP)
809 * Get interface index
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;
817 * Get interface name
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';
824 * Get local IP address
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);
833 sin = (struct sockaddr_in *)&ifr.ifr_addr;
834 iflist[cntr].ifaddr = ntohl(sin->sin_addr.s_addr);
836 iflist[cntr].flags1 = 0;
838 if (ifrflags.ifr_flags & IFF_POINTOPOINT) {
840 * Get remote IP address (only for PPP interfaces)
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);
849 sin = (struct sockaddr_in *)&ifr.ifr_addr;
850 iflist[cntr].ifdstaddr = ntohl(sin->sin_addr.s_addr);
852 iflist[cntr].flags1 |= IFLIST_FLAGS1_IF_IS_PPP;
853 iflist[cntr].bcast = INADDR_BROADCAST;
855 else if (ifrflags.ifr_flags & IFF_BROADCAST)
857 iflist[cntr].ifdstaddr = 0;
858 iflist[cntr].flags1 |= IFLIST_FLAGS1_IF_IS_ETH;
859 iflist[cntr].bcast = INADDR_BROADCAST;
861 else
863 iflist[cntr].ifdstaddr = 0;
864 iflist[cntr].flags1 |= IFLIST_FLAGS1_IF_IS_UNKNOWN;
865 iflist[cntr].bcast = INADDR_BROADCAST;
868 cntr++;
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));
883 iflist[cntr].index = 0; // Terminate list
884 free(ifs.ifc_req); // Stop that leak.
885 regfree(&preg);
887 return iflist;
892 unsigned int ip_compute_checksum(unsigned char *ippdu, int hlen)
894 unsigned int sum = 0, temp;
895 unsigned char *p;
896 unsigned char cs_msb;
897 unsigned char cs_lsb;
899 /* Save original checksum */
900 cs_msb = *IP_IPPDU_CHECKSUM_MSB_PTR(ippdu);
901 cs_lsb = *IP_IPPDU_CHECKSUM_LSB_PTR(ippdu);
903 *IP_IPPDU_CHECKSUM_MSB_PTR(ippdu) = 0;
904 *IP_IPPDU_CHECKSUM_LSB_PTR(ippdu) = 0;
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)); }
911 /* Restore original checksum */
912 *IP_IPPDU_CHECKSUM_MSB_PTR(ippdu) = cs_msb;
913 *IP_IPPDU_CHECKSUM_LSB_PTR(ippdu) = cs_lsb;
915 return(~sum & 0xFFFF);
918 void ip_update_checksum(unsigned char *ippdu)
920 unsigned int cs;
922 cs = ip_compute_checksum(ippdu, 4 * IP_IPPDU_IHL(ippdu));
924 *IP_IPPDU_CHECKSUM_MSB_PTR(ippdu) = (unsigned char)((cs >> 8) & 0xFF);
925 *IP_IPPDU_CHECKSUM_LSB_PTR(ippdu) = (unsigned char)(cs & 0xFF);
929 static char *IpProtToString( unsigned char prot )
931 switch( prot )
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 "???";
946 static char *iflistToString( struct iflist *ifp )
948 static char str_tr[80+1];
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);
956 return str_tr;
959 static char *iflistLogRToString( struct iflist *ifp, int idx, struct ifsnr *ifnr )
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.
965 sprintf(str_tr, "%s", ifp->ifname);
966 return str_tr;
969 static char *iflistLogIToString( struct iflist *ifp, int idx, struct ifsnr *ifnr )
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.
975 sprintf(str_tr, "%s(%d/%d/%d)", ifp->ifname, idx, ifp->index, ifnr->sock_nr);
976 return str_tr;
979 static void bind_to_iface(int fd, int ifindex)
981 struct sockaddr_ll sll;
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);
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);
994 static void copy_ifsnr(struct ifsnr *from, struct ifsnr *to)
996 int i;
998 for (i=0; i<MAXIF; ++i)
1000 to[i].sock_nr = from[i].sock_nr;
1001 to[i].ifindex = from[i].ifindex;
1005 static int find_sock_nr(struct ifsnr *l, int ifidx)
1007 int i;
1009 for (i=0; i<MAXIF; ++i)
1010 if (l[i].ifindex == ifidx) return l[i].sock_nr;
1011 /* not found */
1012 return -1;