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.
5 // Written by TheyCallMeLuc(at)yahoo.com.au
6 // I accept no responsiblity for the function of this program if you
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+)
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
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)
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
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.
69 #define _GNU_SOURCE 1 /* strdup() prototype, broken arpa/inet.h */
73 #define __EXTENSIONS__ 1 /* strdup() prototype */
77 #define _XOPEN_SOURCE 500 /* strdup() prototype */
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>
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 */
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
137 #define MAX_IFLOGTOSTR 16
139 /* Local function prototypes */
140 static void showusage(char *prog
);
141 static void showversion();
143 static void my_daemon(int argc
, char **argv
);
145 static void mainloop(int argc
, char **argv
);
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.
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.
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
);
245 static void my_daemon(int argc
, char **argv
)
249 /* Need a smart way to locate the binary -rdv */
250 #define BCRELAY_BIN argv[0]
253 /* need to use vfork - eg, uClinux */
255 extern char **environ
;
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 ) {
270 new_argv
[i
] = argv
[i
+1];
272 new_argv
[i
] = argv
[i
];
275 syslog(LOG_DEBUG
, "Option parse OK, re-execing as daemon");
277 if ((pid
= vfork()) == 0) {
278 if (setsid() < 0) { /* shouldn't fail */
279 syslog(LOG_ERR
, "Setsid failed!");
284 /* execve only returns on an error */
285 execve(BCRELAY_BIN
, new_argv
, environ
);
287 } else if (pid
> 0) {
288 syslog(LOG_DEBUG
, "Success re-execing as daemon!");
291 syslog(LOG_ERR
, "Error vforking");
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!");
309 int main(int argc
, char **argv
) {
311 /* command line options */
318 "bcrelay: pptpd was compiled without support for bcrelay, exiting.\n"
319 " run configure --with-bcrelay, make, and install.\n");
323 /* open a connection to the syslog daemon */
324 openlog("bcrelay", LOG_PID
, PPTP_FACILITY
);
327 int option_index
= 0;
329 static struct option long_options
[] =
334 {"incoming", 1, 0, 0},
335 {"outgoing", 1, 0, 0},
337 {"version", 0, 0, 0},
341 c
= getopt_long(argc
, argv
, "ndhi:o:s:v", long_options
, &option_index
);
344 /* convert long options to short form */
346 c
= "ndhiosv"[option_index
];
358 ifin
= strdup(optarg
);
361 ifout
= strdup(optarg
);
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
);
385 syslog(LOG_INFO
,"Incoming interface required!");
389 if (ifout
== "" && ipsec
== "") {
390 syslog(LOG_INFO
,"Listen-mode or outgoing or IPsec interface required!");
394 sprintf(interfaces
,"%s|%s", ifin
, ifout
);
397 // If specified, become Daemon.
401 freopen("/dev/null", "r", stdin
);
402 /* set noclose, we want stdout/stderr still attached if we can */
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)
414 syslog(LOG_INFO
, "Running as child\n");
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
;
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
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];
436 no_discifs_cntr
= MAX_NODISCOVER_IFS
;
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
);
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
);
481 * Check all (interface) sockets for incoming packets
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
);
502 syslog(LOG_ERR
, "Error, select error! (rv=%d, errno=%d)", rcg
, errno
);
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
);
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
);
527 bind_to_iface(cur_ifsnr
[i
].sock_nr
, iflist
[i
].index
);
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;
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
);
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
));
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))) )
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;
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.
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;
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");
674 syslog(LOG_ERR
, "mainloop: Error, sendto failed! (rv=%d, errno=%d)", nrsent
, errno
);
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
);
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
);
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
);
718 bind_to_iface(cur_ifsnr
[i
].sock_nr
, iflist
[i
].index
);
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;
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
);
765 no_discifs_cntr
-= MAX_SELECT_WAIT
;
771 // Discover active interfaces
773 discoverActiveInterfaces(int s
) {
774 static struct iflist iflist
[MAXIF
+1]; // Allow for MAXIF interfaces
775 static struct ifconf ifs
;
778 struct ifreq ifrflags
, ifr
;
779 struct sockaddr_in
*sin
;
782 memset(iflist
, 0, sizeof(iflist
));
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
);
806 if (ifrflags
.ifr_flags
& IFF_UP
)
809 * Get interface index
811 ioctl(s
, SIOCGIFINDEX
, &ifs
.ifc_req
[i
]);
813 //iflist[cntr].index = (char)ifs.ifc_req[i].ifr_ifindex;
814 iflist
[cntr
].index
= ifs
.ifc_req
[i
].ifr_ifindex
;
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
);
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
);
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
;
863 iflist
[cntr
].ifdstaddr
= 0;
864 iflist
[cntr
].flags1
|= IFLIST_FLAGS1_IF_IS_UNKNOWN
;
865 iflist
[cntr
].bcast
= INADDR_BROADCAST
;
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.
892 unsigned int ip_compute_checksum(unsigned char *ippdu
, int hlen
)
894 unsigned int sum
= 0, temp
;
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;
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
)
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
)
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
);
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
);
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
);
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
);
994 static void copy_ifsnr(struct ifsnr
*from
, struct ifsnr
*to
)
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
)
1009 for (i
=0; i
<MAXIF
; ++i
)
1010 if (l
[i
].ifindex
== ifidx
) return l
[i
].sock_nr
;