/* * fw, the ethernet side. * * To filter a real machine, fw has to be the wire: the card cannot * stay attached to the IP stack, or packets reach it whatever we * decide. So fw opens /net/etherN itself and the protected stack gets * a pkt interface instead - a fake card with fw on the other end. * * ether0 ---- fw ---- pkt ---- the stack ---- programs * * The stack no longer has ethernet, so nobody is doing ARP for it any * more, and fw has to: answer requests for the address we are * protecting, and resolve the next hop for anything we send. That is * all the ethernet fw knows about; everything else it treats as an IP * packet and hands to the rules. * * An ether "bypass" connection looks like it should serve instead, but * it only intercepts the stack's transmissions - etheriq drops what * arrives from the wire while bypass is set - so it cannot filter * inbound at all. */ #include #include #include #include #include #include "rules.h" extern int etherdebug; enum { Eaddrlen = 6, Ehdrlen = 14, Etip4 = 0x0800, Etarp = 0x0806, Etip6 = 0x86DD, Arplen = 28, Eminlen = 60, /* ethernet minimum frame, devether enforces it */ Maxframe = Ehdrlen + 16*1024, Arpreq = 1, Arpreply = 2, Narp = 64, Arplife = 300, }; typedef struct Arpent Arpent; struct Arpent { uchar ip[IPaddrlen]; uchar mac[Eaddrlen]; long when; }; static Arpent arptab[Narp]; static Lock arplock; static uchar ourmac[Eaddrlen]; static uchar ouraddr[IPaddrlen]; static uchar gateway[IPaddrlen]; static int haveg; static int efd = -1; static uchar bcast[Eaddrlen] = { 0xff,0xff,0xff,0xff,0xff,0xff }; /* * Open a card for raw frames of every type. Promiscuous because we * are answering for an address the card does not believe is its own * once the stack has let go of it. */ int etheropen(char *dev, uchar *mac) { char path[128], buf[64]; int cfd, dfd, n, conn; snprint(path, sizeof path, "%s/clone", dev); if((cfd = open(path, ORDWR)) < 0) sysfatal("open %s: %r", path); if((n = read(cfd, buf, sizeof buf - 1)) <= 0) sysfatal("read %s: %r", path); buf[n] = '\0'; conn = atoi(buf); if(fprint(cfd, "connect -1") < 0) sysfatal("%s: connect -1: %r", dev); if(fprint(cfd, "promiscuous") < 0) sysfatal("%s: promiscuous: %r", dev); snprint(path, sizeof path, "%s/%d/data", dev, conn); if((dfd = open(path, ORDWR)) < 0) sysfatal("open %s: %r", path); /* the card's own address, which we answer with */ snprint(path, sizeof path, "%s/addr", dev); if((n = open(path, OREAD)) < 0) sysfatal("open %s: %r", path); if(read(n, buf, 12) != 12) sysfatal("read %s: %r", path); close(n); buf[12] = '\0'; if(parseether(mac, buf) < 0) sysfatal("%s: unparseable address %s", dev, buf); efd = dfd; memmove(ourmac, mac, Eaddrlen); return dfd; } void ethersetaddr(uchar *ip, uchar *gw, int haveit) { ipmove(ouraddr, ip); if(haveit) ipmove(gateway, gw); haveg = haveit; } static void arpput(uchar *ip, uchar *mac) { Arpent *a, *old; long now; int i; now = time(0); lock(&arplock); old = &arptab[0]; for(i = 0; i < Narp; i++){ a = &arptab[i]; if(a->when != 0 && ipcmp(a->ip, ip) == 0){ memmove(a->mac, mac, Eaddrlen); a->when = now; unlock(&arplock); return; } if(a->when < old->when) old = a; } ipmove(old->ip, ip); memmove(old->mac, mac, Eaddrlen); old->when = now; unlock(&arplock); } static int arpget(uchar *ip, uchar *mac) { Arpent *a; long now; int i, r; r = 0; now = time(0); lock(&arplock); for(i = 0; i < Narp; i++){ a = &arptab[i]; if(a->when != 0 && ipcmp(a->ip, ip) == 0){ if(now - a->when <= Arplife){ memmove(mac, a->mac, Eaddrlen); r = 1; } break; } } unlock(&arplock); return r; } /* * Broadcast and multicast are not resolved, they are addressed by rule. * Without this a DHCP renewal, which goes to 255.255.255.255, would be * sent to whatever the gateway's ethernet address happened to be, and * the lease would quietly never renew. */ static int groupmac(uchar *dst, uchar *mask, uchar *mac) { uchar net[IPaddrlen], all[IPaddrlen]; int i; if(isv4(dst)){ /* 255.255.255.255 */ for(i = IPv4off; i < IPaddrlen; i++) if(dst[i] != 0xff) break; if(i == IPaddrlen){ memmove(mac, bcast, Eaddrlen); return 1; } /* the broadcast address of our own network */ maskip(ouraddr, mask, net); for(i = 0; i < IPaddrlen; i++) all[i] = net[i] | ~mask[i]; if(ipcmp(dst, all) == 0){ memmove(mac, bcast, Eaddrlen); return 1; } /* 224.0.0.0/4 */ if(dst[IPv4off] >= 224 && dst[IPv4off] < 240){ mac[0] = 0x01; mac[1] = 0x00; mac[2] = 0x5e; mac[3] = dst[IPv4off+1] & 0x7f; mac[4] = dst[IPv4off+2]; mac[5] = dst[IPv4off+3]; return 1; } return 0; } /* ff00::/8 */ if(dst[0] == 0xff){ mac[0] = 0x33; mac[1] = 0x33; memmove(mac+2, dst+12, 4); return 1; } return 0; } static void puthdr(uchar *f, uchar *dst, int type) { memmove(f, dst, Eaddrlen); memmove(f + Eaddrlen, ourmac, Eaddrlen); f[12] = type >> 8; f[13] = type; } /* ask who has ip; the answer arrives later and goes in the cache */ static void arpask(uchar *ip) { uchar f[Eminlen]; if(!isv4(ip)) return; memset(f, 0, sizeof f); puthdr(f, bcast, Etarp); hnputs(f + 14, 1); /* ethernet */ hnputs(f + 16, Etip4); f[18] = Eaddrlen; f[19] = 4; hnputs(f + 20, Arpreq); memmove(f + 22, ourmac, Eaddrlen); memmove(f + 28, ouraddr + IPv4off, 4); memmove(f + 38, ip + IPv4off, 4); if(etherdebug) fprint(2, "arp: who has %I? (asking)\n", ip); if(write(efd, f, sizeof f) != sizeof f) fprint(2, "arp: write failed: %r\n"); } /* * An arp frame from the wire. Learn from it either way, and answer a * request for the address we are standing in for. */ static void arpin(uchar *f, int n) { uchar sip[IPaddrlen], tip[IPaddrlen], r[Eminlen]; int op; if(n < Ehdrlen + Arplen) return; if(nhgets(f + 16) != Etip4 || f[18] != Eaddrlen || f[19] != 4) return; op = nhgets(f + 20); v4tov6(sip, f + 28); v4tov6(tip, f + 38); if(ipcmp(sip, IPnoaddr) != 0){ arpput(sip, f + 22); if(etherdebug) fprint(2, "arp: learned %I is %E (op %d)\n", sip, f+22, op); } if(op != Arpreq || ipcmp(tip, ouraddr) != 0) return; memset(r, 0, sizeof r); puthdr(r, f + 22, Etarp); hnputs(r + 14, 1); hnputs(r + 16, Etip4); r[18] = Eaddrlen; r[19] = 4; hnputs(r + 20, Arpreply); memmove(r + 22, ourmac, Eaddrlen); memmove(r + 28, ouraddr + IPv4off, 4); memmove(r + 32, f + 22, Eaddrlen); memmove(r + 38, f + 28, 4); write(efd, r, sizeof r); } int etherisarp(uchar *f, int n) { if(n < Ehdrlen) return 0; if(nhgets(f + 12) != Etarp) return 0; arpin(f, n); return 1; } int etherisip(uchar *f, int n) { int t; if(n < Ehdrlen) return 0; t = nhgets(f + 12); return t == Etip4 || t == Etip6; } /* * Send an IP packet out the card. The next hop is the destination if * it is on our own network, otherwise the gateway. If we do not know * its ethernet address yet we ask and drop this one; the sender will * try again, which is what every other stack does too. */ int etherwriteip(uchar *p, int n, uchar *mask) { /* * On a proc stack, which libthread keeps small: 64K here * overran it and corrupted the data segment. pktmedium's maxtu * is 4096, so this is already generous. */ uchar f[Maxframe], dst[IPaddrlen], hop[IPaddrlen]; uchar net[IPaddrlen], ournet[IPaddrlen], mac[Eaddrlen]; int type, len; if(n < 20 || n > Maxframe - Ehdrlen) return -1; switch(p[0] >> 4){ case 4: type = Etip4; v4tov6(dst, p + 16); break; case 6: type = Etip6; ipmove(dst, p + 24); break; default: return -1; } if(groupmac(dst, mask, mac)) goto Send; maskip(dst, mask, net); maskip(ouraddr, mask, ournet); if(ipcmp(net, ournet) == 0) ipmove(hop, dst); else if(haveg) ipmove(hop, gateway); else ipmove(hop, dst); if(!arpget(hop, mac)){ /* * v6 has no ARP; resolving a neighbour needs ICMPv6 * solicitation, which is not implemented. Say so once, * rather than dropping every v6 unicast in silence. */ if(!isv4(hop)){ static int said; if(!said++) fprint(2, "fw: cannot resolve %I: " "IPv6 neighbour discovery is not implemented, " "so v6 unicast is dropped\n", hop); return -1; } if(etherdebug) fprint(2, "arp: no entry for %I, dropping and asking\n", hop); arpask(hop); return 0; } Send: puthdr(f, mac, type); memmove(f + Ehdrlen, p, n); len = Ehdrlen + n; if(len < Eminlen){ memset(f + len, 0, Eminlen - len); len = Eminlen; } if((len = write(efd, f, len)) < 0) fprint(2, "ether: write failed: %r\n"); return len; }