On Fri, Jul 07, 2006 at 07:00:10PM +0200, Peter Philipp wrote: > You may even do it cheaper than that with a bit of programming and it doesn't > require a purchase of any network gear, however the functionality may not be > there in the tun(4) driver. > > Basically what I'm thinking of is the following: The x.y.z.w/29 and > a.b.c.d/29 interfaces have a rdr pf rule that redirects everything inbound > into a daemon that runs a tun(4) interface in layer 3 mode, this daemon > writes the incoming packets out another tun(4) interface that is in layer 2 > mode which is also bridged within a set of ethernet interfaces > (192.168.0.0/24) > that also have CARP devices on each end. This is where I'm unsure if this is > functional, (bridging a layer 2 tun(4) device), anyhow the MAC address that > it writes to is the CARP virtual Address (or you could implement rudimentary > ARP into the daemon as well) and you should have failover as long as the > firewalls themselves don't fail. Required on each firewall is 4 ethernet > interfaces and the tun(4) userland daemon. You should see some overhead > with this due to copying the packets into userland and then back to kernel > via the tun(4) interfaces. > > Gee I'm feeling really creative today. Let the imagination flow.
Uhm. I had some time and because I was working on similar code I created what I conceived in this. Basically tun(2) in link-layer mode are able to be bridged (yay), didn't know it could be done. The code proved it. However the CARP doesn't ARP when it's on another interface so it won't work. I've submitted a bug report regarding this, it is labelled as "kernel/5178". Not sure if it's deemed as important to make functional. Anyhow perhaps carp'ing isn't the right solution for this but perhaps ospfd, dunno, I don't have multiple gateways nor enough machines to give that a try so I likely have a misconception about it, like most things. Either way, if you like trying out new things, playing with code here is what I have created. It does almost exactly what I described in the previous mail. And best of all it's under a BSD license. It takes input on the first tun(4) device which has the 192.168.40.0/24 block and spits it out the second tun(4) device with netblock 192.168.41.0/24 which has a configurable (-r) router IP as gateway. Perhaps with a route-to rule it would work as I had imagined. The last argument to this program is the bridge group you want to join (ie. bridge1), you don't have one make one. Regards, -peter /* * Copyright (c) 2002-2006 Peter Philipp * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include <sys/param.h> #include <sys/socket.h> #include <sys/time.h> #include <sys/wait.h> #include <sys/sysctl.h> #include <sys/ioctl.h> #include <sys/stat.h> #include <net/if.h> #include <net/if_tun.h> #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/if_ether.h> #include <net/if_bridge.h> #include <net/if_arp.h> #include <net/ethertypes.h> #include <netinet/ip.h> #include <netinet/ip_var.h> #include <netinet/if_ether.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <signal.h> #include <time.h> #define POINT_A "192.168.40.1" #define POINT_B "192.168.40.2" #define AB_NETMASK "255.255.255.0" #define POINT_C "192.168.41.1" #define POINT_D "192.168.41.2" #define CD_NETMASK "255.255.255.0" #define ROUTER POINT_D #define TUNNEL_P2P 0x1 #define TUNNEL_BROADCAST 0x2 char *pointa = POINT_A , *pointb = POINT_B; char *ab_netmask = AB_NETMASK; char *cd_netmask = CD_NETMASK; char *pointc = POINT_C , *pointd = POINT_D; char *router = ROUTER; u_char mymac[ETHER_ADDR_LEN]; u_char routermac[ETHER_ADDR_LEN]; u_char broadcastmac[ETHER_ADDR_LEN]; u_char bridgeif[IFNAMSIZ]; in_addr_t myip; in_addr_t routerip; int get_ifmtu(char *name); int open_tunnel(int); int open_tun(char *name, int len); int set_tunmode(int fd, int mode); int check_tundev(int fd); int del_ifip(char *name, char *ip); int set_ifip(char *name, char *, char *, int af); int set_ifpop(char *name, char *point1, char *point2, char *netmask); short get_ifflags(char *name); int set_ifflags(char *name, short flags); int set_ifup(char *name); int set_ifdown(char *name); int set_ifmtu(char *name, int mtu); int get_ifmtu(char *name); in_addr_t get_ifaddr(char *name); void arplookup(int); int write_frame(int, char *, int); void onalrm(int); /* mainly nonsense */ int main(int argc, char *argv[]) { int mtu; int infd, outfd; u_char tbuf[2000]; int len; int ch; u_int32_t *af; int mode; memset(broadcastmac, 0xff, sizeof(broadcastmac)); memset(mymac, 0x0, sizeof(mymac)); myip = inet_addr(pointc); memcpy(&mymac[1], &myip, sizeof(myip)); mymac[5] = 0x1; routerip = inet_addr(router); signal(SIGALRM, onalrm); while ((ch = getopt(argc, argv, "a:b:c:d:r:")) != -1 ) { switch(ch) { case 'a': pointa = optarg; break; case 'b': pointb = optarg; break; case 'c': pointc = optarg; memset(mymac, 0x0, sizeof(mymac)); myip = inet_addr(pointc); memcpy(&mymac[1], &myip, sizeof(myip)); mymac[5] = 0x1; break; case 'd': pointd = optarg; break; case 'r': router = optarg; routerip = inet_addr(optarg); break; default: fprintf(stderr, "usage: outerpipe [-a XXX] [-b XXX] [-c XXX] [-d XXX] [-r XXX] bridgename\n"); exit(1); } } argc -= optind; argv += optind; if (argc != 1) { fprintf(stderr, "usage: outerpipe [-a XXX] [-b XXX] [-c XXX] [-d XXX] [-r XXX] bridgename\n"); exit(1); } strlcpy(bridgeif, argv[0], sizeof(bridgeif)); infd = open_tunnel(TUNNEL_P2P); if (mode = fcntl(infd, F_GETFL, 0) < 0) perror("fcntl"); if (fcntl(infd, F_SETFL, mode | O_NONBLOCK) < 0) perror("fcntl"); outfd = open_tunnel(TUNNEL_BROADCAST); if (mode = fcntl(outfd, F_GETFL, 0) < 0) perror("fcntl"); if (fcntl(outfd, F_SETFL, mode | O_NONBLOCK) < 0) perror("fcntl"); #if 0 daemon(0,0); #endif sleep(2); arplookup(outfd); for (;;) { do { if ((len = read(infd, tbuf, sizeof(tbuf))) < 0) { if (errno != EWOULDBLOCK) perror("read"); } else break; usleep(20); } while (len < 0 && errno == EWOULDBLOCK); af = (u_int32_t *)&tbuf[0]; if (*af != AF_INET) { continue; } write_frame(outfd, tbuf + sizeof(u_int32_t), len - sizeof(u_int32_t)); } } /* * GET_IFMTU - get the specified MTU of the interface, -1 on error */ int get_ifmtu(char *name) { int so; struct ifreq ifr; if ((so = socket(AF_INET, SOCK_DGRAM, 0)) < 0) return -1; memset((char *)&ifr, 0, sizeof(ifr)); strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); if (ioctl(so, SIOCGIFMTU, &ifr, sizeof(ifr)) < 0) { close(so); return -1; } close(so); return (ifr.ifr_mtu); } /* END get_ifmtu */ int open_tunnel(int mode) { int fd; int mtu = 1500; char tunif[IFNAMSIZ]; int so; struct ifbreq ifb; if ((fd = open_tun((char *)&tunif, sizeof(tunif))) < 0) { perror("open_tun"); exit(1); } if (mode == TUNNEL_P2P) { if (set_tunmode(fd, IFF_POINTOPOINT) < 0) { perror("set_tunmode"); exit(1); } if (set_ifpop(tunif, pointa, pointb, ab_netmask) < 0) { perror("set_ifpop"); exit(1); } if (set_ifup(tunif) < 0) { fprintf(stderr, "can't set interface up\n"); exit(1); } } else if (mode == TUNNEL_BROADCAST) { if (set_tunmode(fd, IFF_BROADCAST | IFF_LINK0) < 0) { perror("set_tunmode"); exit(1); } if (set_ifup(tunif) < 0) { fprintf(stderr, "can't set interface up\n"); exit(1); } #if 0 if (set_ifip(tunif, pointd, cd_netmask, AF_INET) < 0) { perror("set_ifip"); exit(1); } #endif /* add this interface to the specified bridge */ so = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if ( so < 0 ) { perror("socket"); exit(1); } memset(&ifb, 0, sizeof(ifb)); strlcpy((char *)&ifb.ifbr_name, (char *)&bridgeif, IFNAMSIZ); strlcpy((char *)&ifb.ifbr_ifsname, (char *)&tunif, IFNAMSIZ); if (ioctl(so, SIOCBRDGADD, &ifb, sizeof(ifb)) < 0) { perror("ioctl"); } close(so); } else return; if ((mtu = get_ifmtu(tunif)) < 0) { perror("get_ifmtu"); exit(1); } if (mtu > 1500) { mtu = 1500; if (set_ifmtu(tunif, mtu) < 0) { perror("set_ifmtu"); exit(1); } } return fd; } /* BEGIN open_tun */ /* * open_tun - search existing tun(4) device and find one that is open return * file descriptor or -1 on error. If an argument is given * try to open that device explicitly otherwise the selected * device is written to that pointer, for max len bytes */ int open_tun(char *name, int len) { #define MAX_TUN_DEVICES 16 char tmp[MAXPATHLEN]; int dn; /* device number */ int fd; /* file descriptor to be returned */ if (len > IFNAMSIZ) { errno = ENAMETOOLONG; return -1; } /* * XXX look carefully, this is a little tricky cruft */ snprintf(tmp, sizeof(tmp), "/dev/%s", name); if ((fd = open(tmp, O_RDWR, 0600)) != -1) return fd; for (dn = 0; dn < MAX_TUN_DEVICES; dn++) { errno = 0; snprintf(name, len, "tun%d", dn); snprintf(tmp, sizeof(tmp), "/dev/%s", name); fd = open(tmp, O_RDWR, 0600); if (fd < 0) { if (errno == EBUSY) continue; break; } else return fd; } return -1; } /* END open_tun */ /* BEGIN set_tunmode */ /* * SET_TUNMODE - set the mode of the specified tunnel device * IFF_POINTOPOINT or IFF_BROADCAST, return -1 on err */ int set_tunmode(int fd, int mode) { if (mode != IFF_POINTOPOINT && (mode & ( IFF_BROADCAST | IFF_LINK0 )) != (IFF_BROADCAST | IFF_LINK0)) return -1; if (check_tundev(fd) < 0) return -1; if (ioctl(fd, TUNSIFMODE, &mode, sizeof(mode)) < 0) return -1; return mode; } /* END set_tunmode */ /* BEGIN check_tundev */ /* * CHECK_TUNDEV - check the type of device, return 1 if device -1 else */ int check_tundev(int fd) { #define TUN_DEV "/dev/tun" struct stat sb; if (fstat(fd, &sb) < 0) return -1; if (S_ISCHR(sb.st_mode)) return 0; return -1; } /* END check_tundev */ /* BEGIN del_ifip */ /* * DEL_IFIP - delete an IP alias from a certain interface, takes IP as second * argument, returns 0 on success, -1 on error */ int del_ifip(char *name, char *ip) { int so; struct ifaliasreq ifra; struct sockaddr_in sin; if ((so = socket(AF_INET, SOCK_DGRAM, 0)) < 0) return -1; sin.sin_addr.s_addr = inet_addr(ip); memset(&ifra, 0, sizeof(ifra)); strlcpy(ifra.ifra_name, name, sizeof(ifra.ifra_name)); memcpy(&ifra.ifra_addr, &sin.sin_addr.s_addr, sizeof(struct sockaddr)); if (ioctl(so, SIOCDIFADDR, &ifra, sizeof(ifra)) < 0) { close(so); return -1; } close(so); return 0; } /* END del_ifip */ /* BEGIN set_ifip */ /* * SET_IFIP - sets an IP address on the specified interface, returns 0 on * success, -1 on error */ int set_ifip(char *name, char *address, char *netmask, int af) { int so; struct ifaliasreq ifra; struct sockaddr_in sin; if (af == AF_INET6) return -1; if ((so = socket(AF_INET, SOCK_DGRAM, 0)) < 0) return -1; memset((char *)&ifra, 0, sizeof(ifra)); strlcpy(ifra.ifra_name, name, sizeof(ifra.ifra_name)); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = inet_addr(address); sin.sin_len = sizeof(sin); memcpy(&ifra.ifra_addr,(struct sockaddr *)&sin,sizeof(struct sockaddr)); sin.sin_addr.s_addr = inet_addr(netmask); memcpy(&ifra.ifra_mask, (struct sockaddr *)&sin, sizeof(struct sockaddr)); if (ioctl(so, SIOCAIFADDR, &ifra, sizeof(ifra)) < 0) { close(so); return -1; } close(so); return 0; } /* END set_ifip */ /* BEGIN set_ifpop */ /* * SET_IFPOP - set 2 points on a point to point interface, returns 0 on * smooth sails, and -1 on error */ int set_ifpop(char *name, char *point1, char *point2, char *netmask) { int so; struct ifaliasreq ifra; struct sockaddr_in sin; if ((so = socket(AF_INET, SOCK_DGRAM, 0)) < 0) return -1; memset((char *)&ifra, 0, sizeof(ifra)); strlcpy(ifra.ifra_name, name, sizeof(ifra.ifra_name)); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = inet_addr(point1); sin.sin_len = sizeof(sin); memcpy(&ifra.ifra_addr,(struct sockaddr *)&sin,sizeof(struct sockaddr)); sin.sin_addr.s_addr = inet_addr(point2); #ifdef __OpenBSD__ memcpy(&ifra.ifra_dstaddr,(struct sockaddr *)&sin,sizeof(struct sockaddr)); #else memcpy(&ifra.ifra_broadaddr,(struct sockaddr *)&sin,sizeof(struct sockaddr)); #endif if (netmask) { sin.sin_addr.s_addr = inet_addr(netmask); memcpy(&ifra.ifra_mask,(struct sockaddr*)&sin,sizeof(struct sockaddr)); } if (ioctl(so, SIOCAIFADDR, &ifra, sizeof(ifra)) < 0) { close(so); return -1; } close(so); return 0; } /* END set_ifpop */ /* BEGIN get_ifflags */ /* * GET_IFFLAGS - get flags of specified interface, return -1 on error */ short get_ifflags(char *name) { int so; struct ifreq ifr; if ((so = socket(AF_INET, SOCK_DGRAM, 0)) < 0) return -1; memset((char *)&ifr, 0, sizeof(ifr)); strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); if (ioctl(so, SIOCGIFFLAGS, &ifr, sizeof(ifr)) < 0) { close(so); return -1; } close(so); return(ifr.ifr_flags); } /* END get_ifflags */ /* BEGIN set_ifflags */ /* * SET_IFFLAGS - set flags of specified interface, return -1 on error */ int set_ifflags(char *name, short flags) { int so; struct ifreq ifr; if ((so = socket(AF_INET, SOCK_DGRAM, 0)) < 0) return -1; memset((char *)&ifr, 0, sizeof(ifr)); strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); ifr.ifr_flags = flags; if (ioctl(so, SIOCSIFFLAGS, &ifr, sizeof(ifr)) < 0) { close(so); return -1; } close(so); return(0); } /* END set_ifflags */ /* BEGIN set_ifup */ /* * SET_IFUP - turn an interface into the IFF_UP state */ int set_ifup(char *name) { short flags; flags = get_ifflags(name); flags |= IFF_UP; return (set_ifflags(name, flags)); } /* END set_ifup */ /* BEGIN set_ifdown */ /* * SET_IFDOWN - turn an interface off */ int set_ifdown(char *name) { short flags; flags = get_ifflags(name); flags &= ~IFF_UP; return (set_ifflags(name, flags)); } /* END set_ifdown */ /* BEGIN set_ifmtu */ /* * SET_IFMTU - set the specified interface MTU and return 0 on success, -1 * on error. */ int set_ifmtu(char *name, int mtu) { int so; struct ifreq ifr; if ((so = socket(AF_INET, SOCK_DGRAM, 0)) < 0) return -1; memset((char *)&ifr, 0, sizeof(ifr)); strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); ifr.ifr_mtu = mtu; if (ioctl(so, SIOCSIFMTU, &ifr, sizeof(ifr)) < 0) { close(so); return -1; } close(so); return 0; } /* END set_ifmtu */ /* BEGIN get_ifaddr */ /* * GET_IFADDR - gets the interface address which is returned in an * in_addr_t */ in_addr_t get_ifaddr(char *name) { int so; struct ifreq ifr; struct sockaddr_in *sin; if ((so = socket(AF_INET, SOCK_DGRAM, 0)) < 0) return -1; memset((char *)&ifr, 0, sizeof(ifr)); strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); if (ioctl(so, SIOCGIFADDR, &ifr, sizeof(ifr)) < 0) { close(so); return -1; } close(so); sin = (struct sockaddr_in *)&ifr.ifr_addr; return (sin->sin_addr.s_addr); } /* END get_ifaddr */ void arplookup(int fd) { u_char buf[2500]; int len; int i; int save_errno; struct ether_header *eh; struct ether_arp *ah; time_t now, before; for (i = 0; i < 5; i++) { memset(&buf, 0, sizeof(buf)); eh = (struct ether_header *)&buf[0]; ah = (struct ether_arp *)&buf[sizeof(struct ether_header)]; len = sizeof(struct ether_header) + sizeof(struct ether_arp); eh->ether_type = htons(ETHERTYPE_ARP); memset(eh->ether_dhost, 0xff, sizeof(eh->ether_dhost)); memcpy(eh->ether_shost, &mymac, sizeof(mymac)); ah->arp_hrd = htons(ARPHRD_ETHER); ah->arp_pro = htons(ETHERTYPE_IP); ah->arp_hln = sizeof(ah->arp_sha); ah->arp_pln = sizeof(ah->arp_spa); ah->arp_op = htons(ARPOP_REQUEST); memcpy(ah->arp_sha, &mymac, sizeof(mymac)); memcpy(ah->arp_spa, &myip, sizeof(myip)); memcpy(ah->arp_tpa, &routerip, sizeof(routerip)); if (write(fd, buf, len) < 0) { perror("write"); } before = time(NULL); now = time(NULL); while (difftime(now, before) < 10) { if ((len = read(fd, buf, sizeof(buf))) < 0) { if (errno != EWOULDBLOCK) perror("read"); } if (len < (sizeof(struct ether_header) + sizeof(struct ether_arp))) { now = time(NULL); continue; } if (memcmp(&eh->ether_dhost, &mymac, sizeof(mymac)) == 0 && ntohs(eh->ether_type) == ETHERTYPE_ARP && ntohs(ah->arp_op) == ARPOP_REPLY) { memcpy(&routermac, ah->arp_sha, sizeof(routermac)); goto out; } now = time(NULL); } /* for difftime */ continue; /* back to writing a new ARP request */ } out: for (i = 0; i < sizeof(routermac); i++) { printf("%02x:", routermac[i]); } printf("\n"); return; } void reply_arp(int fd, u_char *data, int len) { u_char buf[2500]; int newlen; int i; struct ether_header *eh, *eh2; struct ether_arp *ah, *ah2; eh = (struct ether_header *)data; ah = (struct ether_arp *)(data + sizeof(struct ether_header)); if (memcmp(broadcastmac, eh->ether_dhost, sizeof(broadcastmac)) == 0 && ntohs(ah->arp_hrd) == ARPHRD_ETHER && ntohs(ah->arp_pro) == ETHERTYPE_IP && ah->arp_hln == sizeof(ah->arp_sha) && ah->arp_pln == sizeof(ah->arp_spa) && ntohs(ah->arp_op) == ARPOP_REQUEST) { if (memcmp(&ah->arp_tpa, &myip, sizeof(myip)) != 0) { return; } memset(&buf, 0, sizeof(buf)); eh2 = (struct ether_header *)&buf[0]; ah2 = (struct ether_arp *)&buf[sizeof(struct ether_header)]; newlen = sizeof(struct ether_header) + sizeof(struct ether_arp); eh2->ether_type = htons(ETHERTYPE_ARP); memcpy(eh2->ether_dhost, eh->ether_shost, sizeof(eh->ether_shost)); memcpy(eh2->ether_shost, &mymac, sizeof(mymac)); ah2->arp_hrd = htons(ARPHRD_ETHER); ah2->arp_pro = htons(ETHERTYPE_IP); ah2->arp_hln = sizeof(ah2->arp_sha); ah2->arp_pln = sizeof(ah2->arp_spa); ah2->arp_op = htons(ARPOP_REPLY); memcpy(ah2->arp_sha, &mymac, sizeof(mymac)); memcpy(ah2->arp_spa, &myip, sizeof(myip)); memcpy(ah2->arp_tpa, ah->arp_spa, sizeof(ah->arp_spa)); memcpy(ah2->arp_tha, ah->arp_sha, sizeof(ah->arp_sha)); if (write(fd, buf, newlen) < 0) { perror("write"); } } return; } int write_frame(int fd, char *data, int len) { char buf[2500]; struct ether_header *eh = (struct ether_header *) &buf[0]; char *concat; int newlen; newlen = len + sizeof(struct ether_header); if (newlen > sizeof(buf)) { errno = ENOBUFS; return -1; } concat = &buf[sizeof(struct ether_header)]; eh->ether_type = htons(ETHERTYPE_IP); memcpy(eh->ether_dhost, &routermac, sizeof(routermac)); memcpy(eh->ether_shost, &mymac, sizeof(mymac)); memcpy(concat, data, len); return (write(fd, buf, newlen)); } void onalrm(int sig) { return; } -- Here my ticker tape .signature #### My name is Peter Philipp #### lynx -dump "http://en.wikipedia.org/w/index.php?title=Pufferfish&oldid=20768394"; | sed -n 131,136p #### So long and thanks for all the fish!!!