aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/ip
diff options
context:
space:
mode:
authorrsc <devnull@localhost>2005-12-26 04:48:52 +0000
committerrsc <devnull@localhost>2005-12-26 04:48:52 +0000
commit87a52e0485d3281ebea6bf4b725aa8023690e96f (patch)
tree0abc2d2ddb875196177231639d3cb4519e814b9d /src/cmd/ip
parent35d26aa32167e84326cdb745c0e906393b8de71d (diff)
downloadplan9port-87a52e0485d3281ebea6bf4b725aa8023690e96f.tar.gz
plan9port-87a52e0485d3281ebea6bf4b725aa8023690e96f.tar.bz2
plan9port-87a52e0485d3281ebea6bf4b725aa8023690e96f.zip
new goodies
Diffstat (limited to 'src/cmd/ip')
-rwxr-xr-xsrc/cmd/ip/dhcp.h151
-rwxr-xr-xsrc/cmd/ip/dhcpd/dat.h85
-rwxr-xr-xsrc/cmd/ip/dhcpd/db.c452
-rwxr-xr-xsrc/cmd/ip/dhcpd/dhcpd.c1632
-rwxr-xr-xsrc/cmd/ip/dhcpd/dhcpleases.c43
-rwxr-xr-xsrc/cmd/ip/dhcpd/mkfile22
-rwxr-xr-xsrc/cmd/ip/dhcpd/ndb.c318
-rwxr-xr-xsrc/cmd/ip/dhcpd/ping.c112
-rwxr-xr-xsrc/cmd/ip/dhcpd/testlook.c222
-rwxr-xr-xsrc/cmd/ip/dhcpd/testlookup.c168
-rwxr-xr-xsrc/cmd/ip/dhcpd/testping.c22
-rw-r--r--src/cmd/ip/snoopy/Linux.c58
-rwxr-xr-xsrc/cmd/ip/snoopy/arp.c128
-rwxr-xr-xsrc/cmd/ip/snoopy/bootp.c176
-rwxr-xr-xsrc/cmd/ip/snoopy/dat.h106
-rwxr-xr-xsrc/cmd/ip/snoopy/dhcp.c483
-rwxr-xr-xsrc/cmd/ip/snoopy/dump.c92
-rwxr-xr-xsrc/cmd/ip/snoopy/ether.c121
-rwxr-xr-xsrc/cmd/ip/snoopy/gre.c83
-rwxr-xr-xsrc/cmd/ip/snoopy/hdlc.c174
-rwxr-xr-xsrc/cmd/ip/snoopy/icmp.c196
-rwxr-xr-xsrc/cmd/ip/snoopy/icmp6.c428
-rwxr-xr-xsrc/cmd/ip/snoopy/il.c146
-rwxr-xr-xsrc/cmd/ip/snoopy/ip.c236
-rwxr-xr-xsrc/cmd/ip/snoopy/ip6.c309
-rwxr-xr-xsrc/cmd/ip/snoopy/main.c841
-rwxr-xr-xsrc/cmd/ip/snoopy/mkfile69
-rwxr-xr-xsrc/cmd/ip/snoopy/ninep.c55
-rwxr-xr-xsrc/cmd/ip/snoopy/ospf.c404
-rwxr-xr-xsrc/cmd/ip/snoopy/ppp.c629
-rwxr-xr-xsrc/cmd/ip/snoopy/ppp_ccp.c1
-rwxr-xr-xsrc/cmd/ip/snoopy/ppp_chap.c1
-rwxr-xr-xsrc/cmd/ip/snoopy/ppp_comp.c1
-rwxr-xr-xsrc/cmd/ip/snoopy/ppp_ipcp.c1
-rwxr-xr-xsrc/cmd/ip/snoopy/ppp_lcp.c1
-rwxr-xr-xsrc/cmd/ip/snoopy/pppoe_disc.c172
-rwxr-xr-xsrc/cmd/ip/snoopy/pppoe_sess.c1
-rw-r--r--src/cmd/ip/snoopy/protos.c33
-rw-r--r--src/cmd/ip/snoopy/protos.h25
-rwxr-xr-xsrc/cmd/ip/snoopy/rarp.c1
-rwxr-xr-xsrc/cmd/ip/snoopy/rtcp.c97
-rwxr-xr-xsrc/cmd/ip/snoopy/rtp.c76
-rwxr-xr-xsrc/cmd/ip/snoopy/tcp.c221
-rwxr-xr-xsrc/cmd/ip/snoopy/udp.c131
44 files changed, 8723 insertions, 0 deletions
diff --git a/src/cmd/ip/dhcp.h b/src/cmd/ip/dhcp.h
new file mode 100755
index 00000000..8b517958
--- /dev/null
+++ b/src/cmd/ip/dhcp.h
@@ -0,0 +1,151 @@
+
+enum
+{
+ OfferTimeout= 60, /* when an offer times out */
+ MaxLease= 60*60, /* longest lease for dynamic binding */
+ MinLease= 15*60, /* shortest lease for dynamic binding */
+ StaticLease= 30*60, /* lease for static binding */
+
+ IPUDPHDRSIZE= 28, /* size of an IP plus UDP header */
+ MINSUPPORTED= 576, /* biggest IP message the client must support */
+
+ /* lengths of some bootp fields */
+ Maxhwlen= 16,
+ Maxfilelen= 128,
+ Maxoptlen= 312-4,
+
+ /* bootp types */
+ Bootrequest= 1,
+ Bootreply= 2,
+
+ /* bootp flags */
+ Fbroadcast= 1<<15,
+
+ /* dhcp types */
+ Discover= 1,
+ Offer= 2,
+ Request= 3,
+ Decline= 4,
+ Ack= 5,
+ Nak= 6,
+ Release= 7,
+ Inform= 8,
+
+ /* bootp option types */
+ OBend= 255,
+ OBpad= 0,
+ OBmask= 1,
+ OBtimeoff= 2,
+ OBrouter= 3,
+ OBtimeserver= 4,
+ OBnameserver= 5,
+ OBdnserver= 6,
+ OBlogserver= 7,
+ OBcookieserver= 8,
+ OBlprserver= 9,
+ OBimpressserver= 10,
+ OBrlserver= 11,
+ OBhostname= 12, /* 0xc0 */
+ OBbflen= 13,
+ OBdumpfile= 14,
+ OBdomainname= 15,
+ OBswapserver= 16, /* 0x10 */
+ OBrootpath= 17,
+ OBextpath= 18,
+ OBipforward= 19,
+ OBnonlocal= 20,
+ OBpolicyfilter= 21,
+ OBmaxdatagram= 22,
+ OBttl= 23,
+ OBpathtimeout= 24,
+ OBpathplateau= 25,
+ OBmtu= 26,
+ OBsubnetslocal= 27,
+ OBbaddr= 28,
+ OBdiscovermask= 29,
+ OBsupplymask= 30,
+ OBdiscoverrouter= 31,
+ OBrsserver= 32, /* 0x20 */
+ OBstaticroutes= 33,
+ OBtrailerencap= 34,
+ OBarptimeout= 35,
+ OBetherencap= 36,
+ OBtcpttl= 37,
+ OBtcpka= 38,
+ OBtcpkag= 39,
+ OBnisdomain= 40,
+ OBniserver= 41,
+ OBntpserver= 42,
+ OBvendorinfo= 43, /* 0x2b */
+ OBnetbiosns= 44,
+ OBnetbiosdds= 45,
+ OBnetbiostype= 46,
+ OBnetbiosscope= 47,
+ OBxfontserver= 48, /* 0x30 */
+ OBxdispmanager= 49,
+ OBnisplusdomain= 64, /* 0x40 */
+ OBnisplusserver= 65,
+ OBhomeagent= 68,
+ OBsmtpserver= 69,
+ OBpop3server= 70,
+ OBnntpserver= 71,
+ OBwwwserver= 72,
+ OBfingerserver= 73,
+ OBircserver= 74,
+ OBstserver= 75,
+ OBstdaserver= 76,
+
+ /* dhcp options */
+ ODipaddr= 50, /* 0x32 */
+ ODlease= 51,
+ ODoverload= 52,
+ ODtype= 53, /* 0x35 */
+ ODserverid= 54, /* 0x36 */
+ ODparams= 55, /* 0x37 */
+ ODmessage= 56,
+ ODmaxmsg= 57,
+ ODrenewaltime= 58,
+ ODrebindingtime= 59,
+ ODvendorclass= 60,
+ ODclientid= 61, /* 0x3d */
+ ODtftpserver= 66,
+ ODbootfile= 67,
+
+ /* plan9 vendor info options */
+ OP9fs= 128, // plan9 file servers
+ OP9auth= 129, // plan9 auth servers
+};
+
+/* a lease that never expires */
+#define Lforever 0xffffffffU
+
+/* dhcp states */
+enum {
+ Sinit,
+ Sselecting,
+ Srequesting,
+ Sbound,
+ Srenewing,
+ Srebinding,
+};
+
+typedef struct Bootp Bootp;
+struct Bootp
+{
+ uchar op; /* opcode */
+ uchar htype; /* hardware type */
+ uchar hlen; /* hardware address len */
+ uchar hops; /* hops */
+ uchar xid[4]; /* a random number */
+ uchar secs[2]; /* elapsed since client started booting */
+ uchar flags[2];
+ uchar ciaddr[IPv4addrlen]; /* client IP address (client tells server) */
+ uchar yiaddr[IPv4addrlen]; /* client IP address (server tells client) */
+ uchar siaddr[IPv4addrlen]; /* server IP address */
+ uchar giaddr[IPv4addrlen]; /* gateway IP address */
+ uchar chaddr[Maxhwlen]; /* client hardware address */
+ char sname[64]; /* server host name (optional) */
+ char file[Maxfilelen]; /* boot file name */
+ uchar optmagic[4];
+ uchar optdata[Maxoptlen];
+};
diff --git a/src/cmd/ip/dhcpd/dat.h b/src/cmd/ip/dhcpd/dat.h
new file mode 100755
index 00000000..28604c1b
--- /dev/null
+++ b/src/cmd/ip/dhcpd/dat.h
@@ -0,0 +1,85 @@
+#include "../dhcp.h"
+
+enum
+{
+ Maxstr= 256,
+};
+
+typedef struct Binding Binding;
+struct Binding
+{
+ Binding *next;
+ uchar ip[IPaddrlen];
+
+ char *boundto; /* id last bound to */
+ char *offeredto; /* id we've offered this to */
+
+ long lease; /* absolute time at which binding expires */
+ long expoffer; /* absolute time at which offer times out */
+ long offer; /* lease offered */
+ long lasttouched; /* time this entry last assigned/unassigned */
+ long lastcomplained; /* last time we complained about a used but not leased */
+ long tried; /* last time we tried this entry */
+
+ Qid q; /* qid at the last syncbinding */
+};
+
+typedef struct Info Info;
+struct Info
+{
+ int indb; /* true if found in database */
+ char domain[Maxstr]; /* system domain name */
+ char bootf[Maxstr]; /* boot file */
+ char bootf2[Maxstr]; /* alternative boot file */
+ uchar tftp[NDB_IPlen]; /* ip addr of tftp server */
+ uchar tftp2[NDB_IPlen]; /* ip addr of alternate server */
+ uchar ipaddr[NDB_IPlen]; /* ip address of system */
+ uchar ipmask[NDB_IPlen]; /* ip network mask */
+ uchar ipnet[NDB_IPlen]; /* ip network address (ipaddr & ipmask) */
+ uchar etheraddr[6]; /* ethernet address */
+ uchar gwip[NDB_IPlen]; /* gateway ip address */
+ uchar fsip[NDB_IPlen]; /* file system ip address */
+ uchar auip[NDB_IPlen]; /* authentication server ip address */
+ char rootpath[Maxstr]; /* rootfs for diskless nfs clients */
+ char dhcpgroup[Maxstr];
+ char vendor[Maxstr]; /* vendor info */
+};
+
+
+/* from dhcp.c */
+extern int validip(uchar*);
+extern void warning(int, char*, ...);
+extern int minlease;
+
+/* from db.c */
+extern char* tohex(char*, uchar*, int);
+extern char* toid(uchar*, int);
+extern void initbinding(uchar*, int);
+extern Binding* iptobinding(uchar*, int);
+extern Binding* idtobinding(char*, Info*, int);
+extern Binding* idtooffer(char*, Info*);
+extern int commitbinding(Binding*);
+extern int releasebinding(Binding*, char*);
+extern int samenet(uchar *ip, Info *iip);
+extern void mkoffer(Binding*, char*, long);
+extern int syncbinding(Binding*, int);
+
+/* from ndb.c */
+extern int lookup(Bootp*, Info*, Info*);
+extern int lookupip(uchar*, Info*, int);
+extern void lookupname(char*, Ndbtuple*);
+extern Iplifc* findlifc(uchar*);
+extern int forme(uchar*);
+extern int lookupserver(char*, uchar**, Ndbtuple *t);
+extern Ndbtuple* lookupinfo(uchar *ipaddr, char **attr, int n);
+
+/* from icmp.c */
+extern int icmpecho(uchar*);
+
+extern char *binddir;
+extern int debug;
+extern char *blog;
+extern Ipifc *ipifcs;
+extern long now;
+extern char *ndbfile;
+
diff --git a/src/cmd/ip/dhcpd/db.c b/src/cmd/ip/dhcpd/db.c
new file mode 100755
index 00000000..414d85b9
--- /dev/null
+++ b/src/cmd/ip/dhcpd/db.c
@@ -0,0 +1,452 @@
+#include <u.h>
+#include <libc.h>
+#include <ip.h>
+#include <bio.h>
+#include <ndb.h>
+#include <ctype.h>
+#include "dat.h"
+
+/*
+ * format of a binding entry:
+ * char ipaddr[32];
+ * char id[32];
+ * char hwa[32];
+ * char otime[10];
+ */
+Binding *bcache;
+uchar bfirst[IPaddrlen];
+char *binddir = nil;
+char *xbinddir = "#9/ndb/dhcp";
+
+/*
+ * convert a byte array to hex
+ */
+static char
+hex(int x)
+{
+ if(x < 10)
+ return x + '0';
+ return x - 10 + 'a';
+}
+extern char*
+tohex(char *hdr, uchar *p, int len)
+{
+ char *s, *sp;
+ int hlen;
+
+ hlen = strlen(hdr);
+ s = malloc(hlen + 2*len + 1);
+ sp = s;
+ strcpy(sp, hdr);
+ sp += hlen;
+ for(; len > 0; len--){
+ *sp++ = hex(*p>>4);
+ *sp++ = hex(*p & 0xf);
+ p++;
+ }
+ *sp = 0;
+ return s;
+}
+
+/*
+ * convert a client id to a string. If it's already
+ * ascii, leave it be. Otherwise, convert it to hex.
+ */
+extern char*
+toid(uchar *p, int n)
+{
+ int i;
+ char *s;
+
+ for(i = 0; i < n; i++)
+ if(!isprint(p[i]))
+ return tohex("id", p, n);
+ s = malloc(n + 1);
+ memmove(s, p, n);
+ s[n] = 0;
+ return s;
+}
+
+/*
+ * increment an ip address
+ */
+static void
+incip(uchar *ip)
+{
+ int i, x;
+
+ for(i = IPaddrlen-1; i >= 0; i--){
+ x = ip[i];
+ x++;
+ ip[i] = x;
+ if((x & 0x100) == 0)
+ break;
+ }
+}
+
+/*
+ * find a binding for an id or hardware address
+ */
+static int
+lockopen(char *file)
+{
+ char err[ERRMAX];
+ int fd, tries;
+
+ for(tries = 0; tries < 5; tries++){
+ fd = open(file, OLOCK|ORDWR);
+ if(fd >= 0)
+ return fd;
+print("open %s: %r\n", file);
+ errstr(err, sizeof err);
+ if(strstr(err, "lock")){
+ /* wait for other process to let go of lock */
+ sleep(250);
+
+ /* try again */
+ continue;
+ }
+ if(strstr(err, "exist") || strstr(err, "No such")){
+ /* no file, create an exclusive access file */
+ fd = create(file, ORDWR, DMEXCL|0666);
+ chmod(file, 0666);
+ if(fd >= 0)
+ return fd;
+ }
+ }
+ return -1;
+}
+
+void
+setbinding(Binding *b, char *id, long t)
+{
+ if(b->boundto)
+ free(b->boundto);
+
+ b->boundto = strdup(id);
+ b->lease = t;
+}
+
+static void
+parsebinding(Binding *b, char *buf)
+{
+ long t;
+ char *id, *p;
+
+ /* parse */
+ t = atoi(buf);
+ id = strchr(buf, '\n');
+ if(id){
+ *id++ = 0;
+ p = strchr(id, '\n');
+ if(p)
+ *p = 0;
+ } else
+ id = "";
+
+ /* replace any past info */
+ setbinding(b, id, t);
+}
+
+static int
+writebinding(int fd, Binding *b)
+{
+ Dir *d;
+
+ seek(fd, 0, 0);
+ if(fprint(fd, "%ld\n%s\n", b->lease, b->boundto) < 0)
+ return -1;
+ d = dirfstat(fd);
+ if(d == nil)
+ return -1;
+ b->q.type = d->qid.type;
+ b->q.path = d->qid.path;
+ b->q.vers = d->qid.vers;
+ free(d);
+ return 0;
+}
+
+/*
+ * synchronize cached binding with file. the file always wins.
+ */
+int
+syncbinding(Binding *b, int returnfd)
+{
+ char buf[512];
+ int i, fd;
+ Dir *d;
+
+ if(binddir == nil)
+ binddir = unsharp(xbinddir);
+
+ snprint(buf, sizeof(buf), "%s/%I", binddir, b->ip);
+ fd = lockopen(buf);
+ if(fd < 0){
+ /* assume someone else is using it */
+ b->lease = time(0) + OfferTimeout;
+ return -1;
+ }
+
+ /* reread if changed */
+ d = dirfstat(fd);
+ if(d != nil) /* BUG? */
+ if(d->qid.type != b->q.type || d->qid.path != b->q.path || d->qid.vers != b->q.vers){
+ i = read(fd, buf, sizeof(buf)-1);
+ if(i < 0)
+ i = 0;
+ buf[i] = 0;
+ parsebinding(b, buf);
+ b->lasttouched = d->mtime;
+ b->q.path = d->qid.path;
+ b->q.vers = d->qid.vers;
+ }
+
+ free(d);
+
+ if(returnfd)
+ return fd;
+
+ close(fd);
+ return 0;
+}
+
+extern int
+samenet(uchar *ip, Info *iip)
+{
+ uchar x[IPaddrlen];
+
+ maskip(iip->ipmask, ip, x);
+ return ipcmp(x, iip->ipnet) == 0;
+}
+
+/*
+ * create a record for each binding
+ */
+extern void
+initbinding(uchar *first, int n)
+{
+ while(n-- > 0){
+ iptobinding(first, 1);
+ incip(first);
+ }
+}
+
+/*
+ * find a binding for a specific ip address
+ */
+extern Binding*
+iptobinding(uchar *ip, int mk)
+{
+ Binding *b;
+
+ for(b = bcache; b; b = b->next){
+ if(ipcmp(b->ip, ip) == 0){
+ syncbinding(b, 0);
+ return b;
+ }
+ }
+
+ if(mk == 0)
+ return 0;
+ b = malloc(sizeof(*b));
+ memset(b, 0, sizeof(*b));
+ ipmove(b->ip, ip);
+ b->next = bcache;
+ bcache = b;
+ syncbinding(b, 0);
+ return b;
+}
+
+static void
+lognolease(Binding *b)
+{
+ /* renew the old binding, and hope it eventually goes away */
+ b->offer = 5*60;
+ commitbinding(b);
+
+ /* complain if we haven't in the last 5 minutes */
+ if(now - b->lastcomplained < 5*60)
+ return;
+ syslog(0, blog, "dhcp: lease for %I to %s ended at %ld but still in use\n",
+ b->ip, b->boundto != nil ? b->boundto : "?", b->lease);
+ b->lastcomplained = now;
+}
+
+/*
+ * find a free binding for a hw addr or id on the same network as iip
+ */
+extern Binding*
+idtobinding(char *id, Info *iip, int ping)
+{
+ Binding *b, *oldest;
+ int oldesttime;
+
+ /*
+ * first look for an old binding that matches. that way
+ * clients will tend to keep the same ip addresses.
+ */
+ for(b = bcache; b; b = b->next){
+ if(b->boundto && strcmp(b->boundto, id) == 0){
+ if(!samenet(b->ip, iip))
+ continue;
+
+ /* check with the other servers */
+ syncbinding(b, 0);
+ if(strcmp(b->boundto, id) == 0)
+ return b;
+ }
+ }
+
+print("looking for old for %I\n", iip->ipnet);
+
+ /*
+ * look for oldest binding that we think is unused
+ */
+ for(;;){
+ oldest = nil;
+ oldesttime = 0;
+ for(b = bcache; b; b = b->next){
+print("tried %d now %d lease %d exp %d %I\n", b->tried, now, b->lease, b->expoffer, b->ip);
+ if(b->tried != now)
+ if(b->lease < now && b->expoffer < now && samenet(b->ip, iip))
+ if(oldest == nil || b->lasttouched < oldesttime){
+ /* sync and check again */
+ syncbinding(b, 0);
+ if(b->lease < now && b->expoffer < now && samenet(b->ip, iip))
+ if(oldest == nil || b->lasttouched < oldesttime){
+ oldest = b;
+print("have oldest\n");
+ oldesttime = b->lasttouched;
+ }
+ }
+ }
+ if(oldest == nil)
+ break;
+
+ /* make sure noone is still using it */
+ oldest->tried = now;
+print("return oldest\n");
+ if(ping == 0 || icmpecho(oldest->ip) == 0)
+ return oldest;
+
+ lognolease(oldest); /* sets lastcomplained */
+ }
+
+ /* try all bindings */
+ for(b = bcache; b; b = b->next){
+ syncbinding(b, 0);
+ if(b->tried != now)
+ if(b->lease < now && b->expoffer < now && samenet(b->ip, iip)){
+ b->tried = now;
+ if(ping == 0 || icmpecho(b->ip) == 0)
+ return b;
+
+ lognolease(b);
+ }
+ }
+
+ /* nothing worked, give up */
+ return 0;
+}
+
+/*
+ * create an offer
+ */
+extern void
+mkoffer(Binding *b, char *id, long leasetime)
+{
+ if(leasetime <= 0){
+ if(b->lease > now + minlease)
+ leasetime = b->lease - now;
+ else
+ leasetime = minlease;
+ }
+ if(b->offeredto)
+ free(b->offeredto);
+ b->offeredto = strdup(id);
+ b->offer = leasetime;
+ b->expoffer = now + OfferTimeout;
+}
+
+/*
+ * find an offer for this id
+ */
+extern Binding*
+idtooffer(char *id, Info *iip)
+{
+ Binding *b;
+
+ /* look for an offer to this id */
+ for(b = bcache; b; b = b->next){
+print("%I %I ? offeredto %s id %s\n", b->ip, iip->ipnet, b->offeredto, id);
+ if(b->offeredto && strcmp(b->offeredto, id) == 0 && samenet(b->ip, iip)){
+ /* make sure some other system hasn't stolen it */
+ syncbinding(b, 0);
+print("b->lease %d now %d boundto %s offered %s\n", b->lease, now, b->boundto, b->offeredto);
+ if(b->lease < now
+ || (b->boundto && strcmp(b->boundto, b->offeredto) == 0))
+ return b;
+ }
+ }
+ return 0;
+}
+
+/*
+ * commit a lease, this could fail
+ */
+extern int
+commitbinding(Binding *b)
+{
+ int fd;
+ long now;
+
+ now = time(0);
+
+ if(b->offeredto == 0)
+ return -1;
+ fd = syncbinding(b, 1);
+ if(fd < 0)
+ return -1;
+ if(b->lease > now && b->boundto && strcmp(b->boundto, b->offeredto) != 0){
+ close(fd);
+ return -1;
+ }
+ setbinding(b, b->offeredto, now + b->offer);
+ b->lasttouched = now;
+
+ if(writebinding(fd, b) < 0){
+ close(fd);
+ return -1;
+ }
+ close(fd);
+ return 0;
+}
+
+/*
+ * commit a lease, this could fail
+ */
+extern int
+releasebinding(Binding *b, char *id)
+{
+ int fd;
+ long now;
+
+ now = time(0);
+
+ fd = syncbinding(b, 1);
+ if(fd < 0)
+ return -1;
+ if(b->lease > now && b->boundto && strcmp(b->boundto, id) != 0){
+ close(fd);
+ return -1;
+ }
+ b->lease = 0;
+ b->expoffer = 0;
+
+ if(writebinding(fd, b) < 0){
+ close(fd);
+ return -1;
+ }
+ close(fd);
+ return 0;
+}
diff --git a/src/cmd/ip/dhcpd/dhcpd.c b/src/cmd/ip/dhcpd/dhcpd.c
new file mode 100755
index 00000000..3b0873c2
--- /dev/null
+++ b/src/cmd/ip/dhcpd/dhcpd.c
@@ -0,0 +1,1632 @@
+#include <u.h>
+#include <sys/socket.h>
+#include <net/if_arp.h>
+#include <netinet/ip.h>
+#include <sys/ioctl.h>
+#include <libc.h>
+#include <ip.h>
+#include <bio.h>
+#include <ndb.h>
+#include "dat.h"
+
+int bwfd;
+int wfd;
+
+//
+// ala rfc2131
+//
+
+typedef struct Req Req;
+struct Req
+{
+ int fd; /* for reply */
+ Bootp *bp;
+ Udphdr uh;
+ Udphdr *up;
+ uchar *e; /* end of received message */
+ uchar *p; /* options pointer */
+ uchar *max; /* max end of reply */
+
+ /* expanded to v6 */
+ uchar ciaddr[IPaddrlen];
+ uchar giaddr[IPaddrlen];
+
+ /* parsed options */
+ int p9request; /* true if this is a bootp with plan9 options */
+ int genrequest; /* true if this is a bootp with generic options */
+ int dhcptype; /* dhcp message type */
+ int leasetime; /* dhcp lease */
+ uchar ip[IPaddrlen]; /* requested address */
+ uchar server[IPaddrlen]; /* server address */
+ char msg[ERRMAX]; /* error message */
+ char vci[32]; /* vendor class id */
+ char *id; /* client id */
+ uchar requested[32]; /* requested params */
+ uchar vendorclass[32];
+ char cputype[32-3];
+
+ Info gii; /* about target network */
+ Info ii; /* about target system */
+ int staticbinding;
+
+ uchar buf[2*1024]; /* message buffer */
+};
+
+#define TFTP "/lib/tftpd"
+char *blog = "ipboot";
+char mysysname[64];
+Ipifc *ipifcs;
+int debug;
+int nobootp;
+long now;
+int slow;
+char net[256];
+uchar xmyipaddr[IPaddrlen];
+
+int pptponly; // only answer request that came from the pptp server
+int mute;
+int minlease = MinLease;
+
+ulong start;
+
+/* option magic */
+char plan9opt[4] = { 'p', '9', ' ', ' ' };
+char genericopt[4] = { 0x63, 0x82, 0x53, 0x63 };
+
+/* well known addresses */
+uchar zeros[Maxhwlen];
+
+/* option debug buffer */
+char optbuf[1024];
+char *op;
+char *oe = optbuf + sizeof(optbuf);
+
+char *optname[256] =
+{
+[OBend] "end",
+[OBpad] "pad",
+[OBmask] "mask",
+[OBtimeoff] "timeoff",
+[OBrouter] "router",
+[OBtimeserver] "time",
+[OBnameserver] "name",
+[OBdnserver] "dns",
+[OBlogserver] "log",
+[OBcookieserver] "cookie",
+[OBlprserver] "lpr",
+[OBimpressserver] "impress",
+[OBrlserver] "rl",
+[OBhostname] "host",
+[OBbflen] "bflen",
+[OBdumpfile] "dumpfile",
+[OBdomainname] "dom",
+[OBswapserver] "swap",
+[OBrootpath] "rootpath",
+[OBextpath] "extpath",
+[OBipforward] "ipforward",
+[OBnonlocal] "nonlocal",
+[OBpolicyfilter] "policyfilter",
+[OBmaxdatagram] "maxdatagram",
+[OBttl] "ttl",
+[OBpathtimeout] "pathtimeout",
+[OBpathplateau] "pathplateau",
+[OBmtu] "mtu",
+[OBsubnetslocal] "subnetslocal",
+[OBbaddr] "baddr",
+[OBdiscovermask] "discovermask",
+[OBsupplymask] "supplymask",
+[OBdiscoverrouter] "discoverrouter",
+[OBrsserver] "rsserver",
+[OBstaticroutes] "staticroutes",
+[OBtrailerencap] "trailerencap",
+[OBarptimeout] "arptimeout",
+[OBetherencap] "etherencap",
+[OBtcpttl] "tcpttl",
+[OBtcpka] "tcpka",
+[OBtcpkag] "tcpkag",
+[OBnisdomain] "nisdomain",
+[OBniserver] "niserver",
+[OBntpserver] "ntpserver",
+[OBvendorinfo] "vendorinfo",
+[OBnetbiosns] "NBns",
+[OBnetbiosdds] "NBdds",
+[OBnetbiostype] "NBtype",
+[OBnetbiosscope] "NBscope",
+[OBxfontserver] "xfont",
+[OBxdispmanager] "xdisp",
+[OBnisplusdomain] "NPdomain",
+[OBnisplusserver] "NP",
+[OBhomeagent] "homeagent",
+[OBsmtpserver] "smtp",
+[OBpop3server] "pop3",
+[OBnntpserver] "nntp",
+[OBwwwserver] "www",
+[OBfingerserver] "finger",
+[OBircserver] "ircserver",
+[OBstserver] "stserver",
+[OBstdaserver] "stdaserver",
+
+/* dhcp options */
+[ODipaddr] "ip",
+[ODlease] "leas",
+[ODoverload] "overload",
+[ODtype] "typ",
+[ODserverid] "sid",
+[ODparams] "params",
+[ODmessage] "message",
+[ODmaxmsg] "maxmsg",
+[ODrenewaltime] "renewaltime",
+[ODrebindingtime] "rebindingtime",
+[ODvendorclass] "vendorclass",
+[ODclientid] "cid",
+[ODtftpserver] "tftpserver",
+[ODbootfile] "bf",
+};
+
+void addropt(Req*, int, uchar*);
+void addrsopt(Req*, int, uchar**, int);
+void arpenter(uchar*, uchar*);
+void bootp(Req*);
+void byteopt(Req*, int, uchar);
+void dhcp(Req*);
+void fatal(int, char*, ...);
+void hexopt(Req*, int, char*);
+void longopt(Req*, int, long);
+void maskopt(Req*, int, uchar*);
+void miscoptions(Req*, uchar*);
+int openlisten(char *net);
+void parseoptions(Req*);
+void proto(Req*, int);
+void rcvdecline(Req*);
+void rcvdiscover(Req*);
+void rcvinform(Req*);
+void rcvrelease(Req*);
+void rcvrequest(Req*);
+char* readsysname(void);
+void remrequested(Req*, int);
+void sendack(Req*, uchar*, int, int);
+void sendnak(Req*, char*);
+void sendoffer(Req*, uchar*, int);
+void stringopt(Req*, int, char*);
+void termopt(Req*);
+int validip(uchar*);
+void vectoropt(Req*, int, uchar*, int);
+void warning(int, char*, ...);
+void logdhcp(Req*);
+void logdhcpout(Req *, char *);
+int readlast(int, Udphdr*, uchar*, int);
+
+void
+timestamp(char *tag)
+{
+ ulong t;
+
+ t = nsec()/1000;
+ syslog(0, blog, "%s %lud", tag, t - start);
+}
+
+void
+usage(void)
+{
+ fprint(2, "usage: dhcp [-dmsnp] [-f directory] [-x netmtpt] [-M minlease] addr n [addr n ...]\n");
+ exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+ int i, n, fd;
+ char *p;
+ uchar ip[IPaddrlen];
+ Req r;
+
+ fmtinstall('E', eipfmt);
+ fmtinstall('I', eipfmt);
+ fmtinstall('V', eipfmt);
+ fmtinstall('M', eipfmt);
+ ARGBEGIN {
+ case 'm':
+ mute = 1;
+ break;
+ case 'd':
+ debug = 1;
+ break;
+ case 'f':
+ p = ARGF();
+ if(p == nil)
+ usage();
+ ndbfile = p;
+ break;
+ case 's':
+ slow = 1;
+ break;
+ case 'n':
+ nobootp = 1;
+ break;
+ case 'i':
+ parseip(xmyipaddr,EARGF(usage()));
+ break;
+ case 'p':
+ pptponly = 1;
+ break;
+ case 'M':
+ p = ARGF();
+ if(p == nil)
+ usage();
+ minlease = atoi(p);
+ if(minlease <= 0)
+ minlease = MinLease;
+ break;
+ } ARGEND;
+
+ while(argc > 1){
+ parseip(ip, argv[0]);
+ if(!validip(ip))
+ usage();
+ n = atoi(argv[1]);
+ if(n <= 0)
+ usage();
+ initbinding(ip, n);
+ argc -= 2;
+ argv += 2;
+ }
+
+ /* for debugging */
+ for(i = 0; i < 256; i++)
+ if(optname[i] == 0)
+ optname[i] = smprint("%d", i);
+
+ /* what is my name? */
+ p = readsysname();
+ strcpy(mysysname, p);
+
+ /* put process in background */
+ if(!debug) switch(rfork(RFNOTEG|RFPROC|RFFDG)) {
+ case -1:
+ fatal(1, "fork");
+ case 0:
+ break;
+ default:
+ exits(0);
+ }
+
+ chdir(TFTP);
+ fd = openlisten(net);
+ wfd = fd;
+ bwfd = fd;
+ if(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, "eth0", 5) < 0)
+ print("setsockopt: %r\n");
+
+ for(;;){
+ memset(&r, 0, sizeof(r));
+ r.fd = fd;
+ n = readlast(fd, &r.uh, r.buf, sizeof(r.buf));
+ if(n < 0)
+ fatal(1, "error reading requests");
+ start = nsec()/1000;
+ op = optbuf;
+ *op = 0;
+ proto(&r, n);
+ if(r.id != nil)
+ free(r.id);
+ }
+}
+
+void
+proto(Req *rp, int n)
+{
+ uchar relip[IPaddrlen];
+ char buf[64];
+
+ now = time(0);
+
+ rp->e = rp->buf + n;
+ rp->bp = (Bootp*)rp->buf;
+ rp->up = &rp->uh;
+ rp->max = rp->buf + MINSUPPORTED - IPUDPHDRSIZE;
+ rp->p = rp->bp->optdata;
+ v4tov6(rp->giaddr, rp->bp->giaddr);
+ v4tov6(rp->ciaddr, rp->bp->ciaddr);
+
+ if(pptponly && rp->bp->htype != 0)
+ return;
+
+ ipifcs = readipifc(net, ipifcs, -1);
+ if(validip(rp->giaddr))
+ ipmove(relip, rp->giaddr);
+ else if(validip(rp->up->raddr))
+ ipmove(relip, rp->up->raddr);
+ else
+ ipmove(relip, xmyipaddr);
+ ipmove(rp->up->laddr, xmyipaddr);
+ if(rp->e < (uchar*)rp->bp->sname){
+ warning(0, "packet too short");
+ return;
+ }
+ if(rp->bp->op != Bootrequest){
+ warning(0, "not bootrequest");
+ return;
+ }
+
+ if(rp->e >= rp->bp->optdata){
+ if(memcmp(rp->bp->optmagic, plan9opt, sizeof(rp->bp->optmagic)) == 0)
+ rp->p9request = 1;
+ if(memcmp(rp->bp->optmagic, genericopt, sizeof(rp->bp->optmagic)) == 0) {
+ rp->genrequest = 1;
+ parseoptions(rp);
+ }
+ }
+ rp->p = rp->bp->optdata;
+
+ /* If no id is specified, make one from the hardware address
+ * of the target. We assume all zeros is not a hardware address
+ * which could be a mistake.
+ */
+ if(rp->id == nil){
+ if(rp->bp->hlen > Maxhwlen){
+ warning(0, "hlen %d", rp->bp->hlen);
+ return;
+ }
+ if(memcmp(zeros, rp->bp->chaddr, rp->bp->hlen) == 0){
+ warning(0, "no chaddr");
+ return;
+ }
+ sprint(buf, "hwa%2.2ux_", rp->bp->htype);
+ rp->id = tohex(buf, rp->bp->chaddr, rp->bp->hlen);
+ }
+
+ /* info about gateway */
+ if(lookupip(relip, &rp->gii, 1) < 0){
+ warning(0, "lookupip failed");
+ return;
+ }
+
+ /* info about target system */
+ if(lookup(rp->bp, &rp->ii, &rp->gii) == 0)
+ if(rp->ii.indb && rp->ii.dhcpgroup[0] == 0)
+ rp->staticbinding = 1;
+
+ if(rp->dhcptype)
+ dhcp(rp);
+ else
+ bootp(rp);
+timestamp("done");
+}
+
+void
+dhcp(Req *rp)
+{
+ logdhcp(rp);
+
+ switch(rp->dhcptype){
+ case Discover:
+ if(slow)
+ sleep(500);
+ rcvdiscover(rp);
+ break;
+ case Request:
+ rcvrequest(rp);
+ break;
+ case Decline:
+ rcvdecline(rp);
+ break;
+ case Release:
+ rcvrelease(rp);
+ break;
+ case Inform:
+ rcvinform(rp);
+ break;
+ }
+}
+
+void
+rcvdiscover(Req *rp)
+{
+ Binding *b, *nb;
+
+ if(rp->staticbinding){
+ sendoffer(rp, rp->ii.ipaddr, StaticLease);
+ return;
+ }
+
+ /*
+ * first look for an outstanding offer
+ */
+ b = idtooffer(rp->id, &rp->gii);
+
+ /*
+ * rfc2131 says:
+ * If an address is available, the new address
+ * SHOULD be chosen as follows:
+ *
+ * o The client's current address as recorded in the client's current
+ * binding, ELSE
+ *
+ * o The client's previous address as recorded in the client's (now
+ * expired or released) binding, if that address is in the server's
+ * pool of available addresses and not already allocated, ELSE
+ *
+ * o The address requested in the 'Requested IP Address' option, if that
+ * address is valid and not already allocated, ELSE
+ *
+ * o A new address allocated from the server's pool of available
+ * addresses; the address is selected based on the subnet from which
+ * the message was received (if 'giaddr' is 0) or on the address of
+ * the relay agent that forwarded the message ('giaddr' when not 0).
+ */
+ if(b == nil){
+ b = idtobinding(rp->id, &rp->gii, 1);
+ if(b && b->boundto && strcmp(b->boundto, rp->id) != 0)
+ if(validip(rp->ip) && samenet(rp->ip, &rp->gii)){
+ nb = iptobinding(rp->ip, 0);
+ if(nb && nb->lease < now)
+ b = nb;
+ }
+ }
+ if(b == nil){
+ warning(0, "!Discover(%s via %I): no binding %I",
+ rp->id, rp->gii.ipaddr, rp->ip);
+ return;
+ }
+ mkoffer(b, rp->id, rp->leasetime);
+ sendoffer(rp, b->ip, b->offer);
+}
+
+void
+rcvrequest(Req *rp)
+{
+ Binding *b;
+
+ if(validip(rp->server)){
+ /* this is a reply to an offer - SELECTING */
+
+ /* check for hard assignment */
+ if(rp->staticbinding){
+ if(forme(rp->server))
+ sendack(rp, rp->ii.ipaddr, StaticLease, 1);
+ else
+ warning(0, "!Request(%s via %I): for server %I not me",
+ rp->id, rp->gii.ipaddr, rp->server);
+ return;
+ }
+
+ b = idtooffer(rp->id, &rp->gii);
+
+ /* if we don't have an offer, nak */
+ if(b == nil){
+ warning(0, "!Request(%s via %I): no offer",
+ rp->id, rp->gii.ipaddr);
+ if(forme(rp->server))
+ sendnak(rp, "no offer for you");
+ return;
+ }
+
+ /* if not for me, retract offer */
+ if(!forme(rp->server)){
+ b->expoffer = 0;
+ warning(0, "!Request(%s via %I): for server %I not me",
+ rp->id, rp->gii.ipaddr, rp->server);
+ return;
+ }
+
+ /*
+ * if the client is confused about what we offered, nak.
+ * client really shouldn't be specifying this when selecting
+ */
+ if(validip(rp->ip) && ipcmp(rp->ip, b->ip) != 0){
+ warning(0, "!Request(%s via %I): requests %I, not %I",
+ rp->id, rp->gii.ipaddr, rp->ip, b->ip);
+ sendnak(rp, "bad ip address option");
+ return;
+ }
+ if(commitbinding(b) < 0){
+ warning(0, "!Request(%s via %I): can't commit %I",
+ rp->id, rp->gii.ipaddr, b->ip);
+ sendnak(rp, "can't commit binding");
+ return;
+ }
+ sendack(rp, b->ip, b->offer, 1);
+ } else if(validip(rp->ip)){
+ /*
+ * checking address/net - INIT-REBOOT
+ *
+ * This is a rebooting client that remembers its old
+ * address.
+ */
+ /* check for hard assignment */
+ if(rp->staticbinding){
+ if(memcmp(rp->ip, rp->ii.ipaddr, IPaddrlen) != 0){
+ warning(0, "!Request(%s via %I): %I not valid for %E",
+ rp->id, rp->gii.ipaddr, rp->ip, rp->bp->chaddr);
+ sendnak(rp, "not valid");
+ }
+ sendack(rp, rp->ii.ipaddr, StaticLease, 1);
+ return;
+ }
+
+ /* make sure the network makes sense */
+ if(!samenet(rp->ip, &rp->gii)){
+ warning(0, "!Request(%s via %I): bad forward of %I",
+ rp->id, rp->gii.ipaddr, rp->ip);
+ sendnak(rp, "wrong network");
+ return;
+ }
+ b = iptobinding(rp->ip, 0);
+ if(b == nil){
+ warning(0, "!Request(%s via %I): no binding for %I for",
+ rp->id, rp->gii.ipaddr, rp->ip);
+ return;
+ }
+ if(memcmp(rp->ip, b->ip, IPaddrlen) != 0 || now > b->lease){
+ warning(0, "!Request(%s via %I): %I not valid",
+ rp->id, rp->gii.ipaddr, rp->ip);
+ sendnak(rp, "not valid");
+ return;
+ }
+ b->offer = b->lease - now;
+ sendack(rp, b->ip, b->offer, 1);
+ } else if(validip(rp->ciaddr)){
+ /*
+ * checking address - RENEWING or REBINDING
+ *
+ * these states are indistinguishable in our action. The only
+ * difference is how close to lease expiration the client is.
+ * If it is really close, it broadcasts the request hoping that
+ * some server will answer.
+ */
+
+ /* check for hard assignment */
+ if(rp->staticbinding){
+ if(ipcmp(rp->ciaddr, rp->ii.ipaddr) != 0){
+ warning(0, "!Request(%s via %I): %I not valid",
+ rp->id, rp->gii.ipaddr, rp->ciaddr);
+ sendnak(rp, "not valid");
+ }
+ sendack(rp, rp->ii.ipaddr, StaticLease, 1);
+ return;
+ }
+
+ /* make sure the network makes sense */
+ if(!samenet(rp->ciaddr, &rp->gii)){
+ warning(0, "!Request(%s via %I): bad forward of %I",
+ rp->id, rp->gii.ipaddr, rp->ip);
+ sendnak(rp, "wrong network");
+ return;
+ }
+ b = iptobinding(rp->ciaddr, 0);
+ if(b == nil){
+ warning(0, "!Request(%s via %I): no binding for %I",
+ rp->id, rp->gii.ipaddr, rp->ciaddr);
+ return;
+ }
+ if(ipcmp(rp->ciaddr, b->ip) != 0){
+ warning(0, "!Request(%I via %s): %I not valid",
+ rp->id, rp->gii.ipaddr, rp->ciaddr);
+ sendnak(rp, "invalid ip address");
+ return;
+ }
+ mkoffer(b, rp->id, rp->leasetime);
+ if(commitbinding(b) < 0){
+ warning(0, "!Request(%s via %I): can't commit %I",
+ rp->id, rp->gii.ipaddr, b->ip);
+ sendnak(rp, "can't commit binding");
+ return;
+ }
+ sendack(rp, b->ip, b->offer, 1);
+ }
+}
+
+void
+rcvdecline(Req *rp)
+{
+ Binding *b;
+ char buf[64];
+
+ if(rp->staticbinding)
+ return;
+
+ b = idtooffer(rp->id, &rp->gii);
+ if(b == nil){
+ warning(0, "!Decline(%s via %I): no binding",
+ rp->id, rp->gii.ipaddr);
+ return;
+ }
+
+ /* mark ip address as in use */
+ snprint(buf, sizeof(buf), "declined by %s", rp->id);
+ mkoffer(b, buf, 0x7fffffff);
+ commitbinding(b);
+}
+
+void
+rcvrelease(Req *rp)
+{
+ Binding *b;
+
+ if(rp->staticbinding)
+ return;
+
+ b = idtobinding(rp->id, &rp->gii, 0);
+ if(b == nil){
+ warning(0, "!Release(%s via %I): no binding",
+ rp->id, rp->gii.ipaddr);
+ return;
+ }
+ if(strcmp(rp->id, b->boundto) != 0){
+ warning(0, "!Release(%s via %I): invalid release of %I",
+ rp->id, rp->gii.ipaddr, rp->ip);
+ return;
+ }
+ warning(0, "Release(%s via %I): releasing %I", b->boundto, rp->gii.ipaddr, b->ip);
+ if(releasebinding(b, rp->id) < 0)
+ warning(0, "release: couldn't release");
+}
+
+void
+rcvinform(Req *rp)
+{
+ Binding *b;
+
+ if(rp->staticbinding){
+ sendack(rp, rp->ii.ipaddr, 0, 0);
+ return;
+ }
+
+ b = iptobinding(rp->ciaddr, 0);
+ if(b == nil){
+ warning(0, "!Inform(%s via %I): no binding for %I",
+ rp->id, rp->gii.ipaddr, rp->ip);
+ return;
+ }
+ sendack(rp, b->ip, 0, 0);
+}
+
+int
+setsiaddr(uchar *siaddr, uchar *saddr, uchar *laddr)
+{
+ if(ipcmp(saddr, IPnoaddr) != 0){
+ v6tov4(siaddr, saddr);
+ return 0;
+ } else {
+ v6tov4(siaddr, laddr);
+ return 1;
+ }
+}
+
+void
+sendoffer(Req *rp, uchar *ip, int offer)
+{
+ int n;
+ int fd;
+ ushort flags;
+ Bootp *bp;
+ Udphdr *up;
+
+ bp = rp->bp;
+ up = rp->up;
+
+ /*
+ * set destination
+ */
+ flags = nhgets(bp->flags);
+ fd = wfd;
+ if(validip(rp->giaddr)){
+ ipmove(up->raddr, rp->giaddr);
+ hnputs(up->rport, 67);
+ } else if(flags & Fbroadcast){
+ fd = bwfd;
+ ipmove(up->raddr, IPv4bcast);
+ hnputs(up->rport, 68);
+ } else {
+ ipmove(up->raddr, ip);
+ if(bp->htype == 1)
+ arpenter(up->raddr, bp->chaddr);
+ hnputs(up->rport, 68);
+ }
+
+ /*
+ * fill in standard bootp part
+ */
+ bp->op = Bootreply;
+ bp->hops = 0;
+ hnputs(bp->secs, 0);
+ memset(bp->ciaddr, 0, sizeof(bp->ciaddr));
+ v6tov4(bp->giaddr, rp->giaddr);
+ v6tov4(bp->yiaddr, ip);
+ setsiaddr(bp->siaddr, rp->ii.tftp, up->laddr);
+ strncpy(bp->sname, mysysname, sizeof(bp->sname));
+ strncpy(bp->file, rp->ii.bootf, sizeof(bp->file));
+
+ /*
+ * set options
+ */
+ byteopt(rp, ODtype, Offer);
+ longopt(rp, ODlease, offer);
+ addropt(rp, ODserverid, up->laddr);
+ miscoptions(rp, ip);
+ termopt(rp);
+
+ logdhcpout(rp, "Offer");
+
+ /*
+ * send
+ */
+ n = rp->p - rp->buf;
+print("OFFER: %I %I %d %d\n", rp->up->laddr, rp->up->raddr, nhgets(rp->up->lport), nhgets(rp->up->rport));
+ if(!mute && udpwrite(fd, rp->up, rp->buf, n) != n)
+ warning(0, "offer: write failed: %r");
+}
+
+void
+sendack(Req *rp, uchar *ip, int offer, int sendlease)
+{
+ int n, fd;
+ ushort flags;
+ Bootp *bp;
+ Udphdr *up;
+
+ bp = rp->bp;
+ up = rp->up;
+
+ /*
+ * set destination
+ */
+ fd = wfd;
+ flags = nhgets(bp->flags);
+ if(validip(rp->giaddr)){
+ ipmove(up->raddr, rp->giaddr);
+ hnputs(up->rport, 67);
+ } else if(flags & Fbroadcast){
+ fd = bwfd;
+ ipmove(up->raddr, IPv4bcast);
+ hnputs(up->rport, 68);
+ } else {
+ ipmove(up->raddr, ip);
+ if(bp->htype == 1)
+ arpenter(up->raddr, bp->chaddr);
+ hnputs(up->rport, 68);
+ }
+
+ /*
+ * fill in standard bootp part
+ */
+ bp->op = Bootreply;
+ bp->hops = 0;
+ hnputs(bp->secs, 0);
+ v6tov4(bp->giaddr, rp->giaddr);
+ v6tov4(bp->yiaddr, ip);
+ setsiaddr(bp->siaddr, rp->ii.tftp, up->laddr);
+ strncpy(bp->sname, mysysname, sizeof(bp->sname));
+ strncpy(bp->file, rp->ii.bootf, sizeof(bp->file));
+
+ /*
+ * set options
+ */
+ byteopt(rp, ODtype, Ack);
+ if(sendlease){
+ longopt(rp, ODlease, offer);
+ }
+ addropt(rp, ODserverid, up->laddr);
+ miscoptions(rp, ip);
+ termopt(rp);
+
+ logdhcpout(rp, "Ack");
+
+ /*
+ * send
+ */
+ n = rp->p - rp->buf;
+ if(!mute && udpwrite(fd, rp->up, rp->buf, n) != n)
+ warning(0, "ack: write failed: %r");
+}
+
+void
+sendnak(Req *rp, char *msg)
+{
+ int n, fd;
+ Bootp *bp;
+ Udphdr *up;
+
+ bp = rp->bp;
+ up = rp->up;
+
+ /*
+ * set destination (always broadcast)
+ */
+ fd = wfd;
+ if(validip(rp->giaddr)){
+ ipmove(up->raddr, rp->giaddr);
+ hnputs(up->rport, 67);
+ } else {
+ fd = bwfd;
+ ipmove(up->raddr, IPv4bcast);
+ hnputs(up->rport, 68);
+ }
+
+ /*
+ * fill in standard bootp part
+ */
+ bp->op = Bootreply;
+ bp->hops = 0;
+ hnputs(bp->secs, 0);
+ v6tov4(bp->giaddr, rp->giaddr);
+ memset(bp->ciaddr, 0, sizeof(bp->ciaddr));
+ memset(bp->yiaddr, 0, sizeof(bp->yiaddr));
+ memset(bp->siaddr, 0, sizeof(bp->siaddr));
+
+ /*
+ * set options
+ */
+ byteopt(rp, ODtype, Nak);
+ addropt(rp, ODserverid, up->laddr);
+ if(msg)
+ stringopt(rp, ODmessage, msg);
+ if(strncmp(rp->id, "id", 2) == 0)
+ hexopt(rp, ODclientid, rp->id+2);
+ termopt(rp);
+
+ logdhcpout(rp, "Nak");
+
+ /*
+ * send nak
+ */
+ n = rp->p - rp->buf;
+ if(!mute && udpwrite(fd, rp->up, rp->buf, n) != n)
+ warning(0, "nak: write failed: %r");
+}
+
+void
+bootp(Req *rp)
+{
+ int n, fd;
+ Bootp *bp;
+ Udphdr *up;
+ ushort flags;
+ Iplifc *lifc;
+ Info *iip;
+
+ warning(0, "bootp %s %I->%I from %s via %I, file %s",
+ rp->genrequest ? "generic" : (rp->p9request ? "p9" : ""),
+ rp->up->raddr, rp->up->laddr,
+ rp->id, rp->gii.ipaddr,
+ rp->bp->file);
+
+ if(nobootp)
+ return;
+
+ bp = rp->bp;
+ up = rp->up;
+ iip = &rp->ii;
+
+ if(rp->staticbinding == 0){
+ warning(0, "bootp from unknown %s via %I", rp->id, rp->gii.ipaddr);
+ return;
+ }
+
+ /* ignore if not for us */
+ if(*bp->sname){
+ if(strcmp(bp->sname, mysysname) != 0){
+ bp->sname[20] = 0;
+ warning(0, "bootp for server %s", bp->sname);
+ return;
+ }
+ } else if(slow)
+ sleep(500);
+
+ /* ignore if we don't know what file to load */
+ if(*bp->file == 0){
+ if(rp->genrequest && *iip->bootf2) /* if not plan 9 and we have an alternate file... */
+ strncpy(bp->file, iip->bootf2, sizeof(bp->file));
+ else if(*iip->bootf)
+ strncpy(bp->file, iip->bootf, sizeof(bp->file));
+ else if(*bp->sname) /* if we were asked, respond no matter what */
+ bp->file[0] = '\0';
+ else {
+ warning(0, "no bootfile for %I", iip->ipaddr);
+ return;
+ }
+ }
+
+ /* ignore if the file is unreadable */
+ if((!rp->genrequest) && bp->file[0] && access(bp->file, 4) < 0){
+ warning(0, "inaccessible bootfile1 %s", bp->file);
+ return;
+ }
+
+ bp->op = Bootreply;
+ v6tov4(bp->yiaddr, iip->ipaddr);
+ if(rp->p9request){
+ warning(0, "p9bootp: %I", iip->ipaddr);
+ memmove(bp->optmagic, plan9opt, 4);
+ if(iip->gwip == 0)
+ v4tov6(iip->gwip, bp->giaddr);
+ rp->p += sprint((char*)rp->p, "%V %I %I %I", iip->ipmask+IPv4off, iip->fsip,
+ iip->auip, iip->gwip);
+ sprint(optbuf, "%s", (char*)(bp->optmagic));
+ } else if(rp->genrequest){
+ warning(0, "genericbootp: %I", iip->ipaddr);
+ memmove(bp->optmagic, genericopt, 4);
+ miscoptions(rp, iip->ipaddr);
+ termopt(rp);
+ } else if(iip->vendor[0] != 0) {
+ warning(0, "bootp vendor field: %s", iip->vendor);
+ memset(rp->p, 0, 128-4);
+ rp->p += sprint((char*)bp->optmagic, "%s", iip->vendor);
+ } else {
+ memset(rp->p, 0, 128-4);
+ rp->p += 128-4;
+ }
+
+ /*
+ * set destination
+ */
+ fd = wfd;
+ flags = nhgets(bp->flags);
+ if(validip(rp->giaddr)){
+ ipmove(up->raddr, rp->giaddr);
+ hnputs(up->rport, 67);
+ } else if(flags & Fbroadcast){
+ fd = bwfd;
+ ipmove(up->raddr, IPv4bcast);
+ hnputs(up->rport, 68);
+ } else {
+ v4tov6(up->raddr, bp->yiaddr);
+ if(bp->htype == 1)
+ arpenter(up->raddr, bp->chaddr);
+ hnputs(up->rport, 68);
+ }
+
+ /*
+ * select best local address if destination is directly connected
+ */
+ lifc = findlifc(up->raddr);
+ if(lifc)
+ ipmove(up->laddr, lifc->ip);
+
+ /*
+ * our identity
+ */
+ strncpy(bp->sname, mysysname, sizeof(bp->sname));
+
+ /*
+ * set tftp server
+ */
+ setsiaddr(bp->siaddr, iip->tftp, up->laddr);
+ if(rp->genrequest && *iip->bootf2)
+ setsiaddr(bp->siaddr, iip->tftp2, up->laddr);
+
+ /*
+ * RFC 1048 says that we must pad vendor field with
+ * zeros until we have a 64 byte field.
+ */
+ n = rp->p - rp->bp->optdata;
+ if(n < 64-4) {
+ memset(rp->p, 0, (64-4)-n);
+ rp->p += (64-4)-n;
+ }
+
+ /*
+ * send
+ */
+ n = rp->p - rp->buf;
+ if(!mute && udpwrite(fd, rp->up, rp->buf, n) != n)
+ warning(0, "bootp: write failed: %r");
+
+ warning(0, "bootp via %I: file %s xid(%ux)flag(%ux)ci(%V)gi(%V)yi(%V)si(%V) %s",
+ up->raddr, bp->file, nhgetl(bp->xid), nhgets(bp->flags),
+ bp->ciaddr, bp->giaddr, bp->yiaddr, bp->siaddr,
+ optbuf);
+}
+
+void
+parseoptions(Req *rp)
+{
+ int n, c, code;
+ uchar *o, *p;
+
+ p = rp->p;
+
+ while(p < rp->e){
+ code = *p++;
+ if(code == 255)
+ break;
+ if(code == 0)
+ continue;
+
+ /* ignore anything that's too long */
+ n = *p++;
+ o = p;
+ p += n;
+ if(p > rp->e)
+ return;
+
+ switch(code){
+ case ODipaddr: /* requested ip address */
+ if(n == IPv4addrlen)
+ v4tov6(rp->ip, o);
+ break;
+ case ODlease: /* requested lease time */
+ rp->leasetime = nhgetl(o);
+ if(rp->leasetime > MaxLease || rp->leasetime < 0)
+ rp->leasetime = MaxLease;
+ break;
+ case ODtype:
+ c = *o;
+ if(c < 10 && c > 0)
+ rp->dhcptype = c;
+ break;
+ case ODserverid:
+ if(n == IPv4addrlen)
+ v4tov6(rp->server, o);
+ break;
+ case ODmessage:
+ if(n > sizeof rp->msg-1)
+ n = sizeof rp->msg-1;
+ memmove(rp->msg, o, n);
+ rp->msg[n] = 0;
+ break;
+ case ODmaxmsg:
+ c = nhgets(o);
+ c -= 28;
+ if(c > 0)
+ rp->max = rp->buf + c;
+ break;
+ case ODclientid:
+ if(n <= 1)
+ break;
+ rp->id = toid( o, n);
+ break;
+ case ODparams:
+ if(n > sizeof(rp->requested))
+ n = sizeof(rp->requested);
+ memmove(rp->requested, o, n);
+ break;
+ case ODvendorclass:
+ if(n >= sizeof(rp->vendorclass))
+ n = sizeof(rp->vendorclass)-1;
+ memmove(rp->vendorclass, o, n);
+ rp->vendorclass[n] = 0;
+ if(strncmp((char*)rp->vendorclass, "p9-", 3) == 0)
+ strcpy(rp->cputype, (char*)rp->vendorclass+3);
+ break;
+ case OBend:
+ return;
+ }
+ }
+}
+
+void
+remrequested(Req *rp, int opt)
+{
+ uchar *p;
+
+ p = memchr(rp->requested, opt, sizeof(rp->requested));
+ if(p != nil)
+ *p = OBpad;
+}
+
+void
+miscoptions(Req *rp, uchar *ip)
+{
+ char *p;
+ int i, j;
+ uchar *addrs[2];
+ uchar x[2*IPaddrlen];
+ uchar vopts[64];
+ uchar *op, *omax;
+ char *attr[100], **a;
+ int na;
+ Ndbtuple *t;
+
+ addrs[0] = x;
+ addrs[1] = x+IPaddrlen;
+
+ /* always supply these */
+ maskopt(rp, OBmask, rp->gii.ipmask);
+ if(validip(rp->gii.gwip)){
+ remrequested(rp, OBrouter);
+ addropt(rp, OBrouter, rp->gii.gwip);
+ } else if(validip(rp->giaddr)){
+ remrequested(rp, OBrouter);
+ addropt(rp, OBrouter, rp->giaddr);
+ }
+
+ // OBhostname for the HP4000M switches
+ // (this causes NT to log infinite errors - tough shit )
+ if(*rp->ii.domain){
+ remrequested(rp, OBhostname);
+ stringopt(rp, OBhostname, rp->ii.domain);
+ }
+ if(*rp->ii.rootpath)
+ stringopt(rp, OBrootpath, rp->ii.rootpath);
+
+ /* figure out what we need to lookup */
+ na = 0;
+ a = attr;
+ if(*rp->ii.domain == 0)
+ a[na++] = "dom";
+ for(i = 0; i < sizeof(rp->requested); i++)
+ switch(rp->requested[i]){
+ case OBrouter:
+ a[na++] = "@ipgw";
+ break;
+ case OBdnserver:
+ a[na++] = "@dns";
+ break;
+ case OBnetbiosns:
+ a[na++] = "@wins";
+ break;
+ case OBsmtpserver:
+ a[na++] = "@smtp";
+ break;
+ case OBpop3server:
+ a[na++] = "@pop3";
+ break;
+ case OBwwwserver:
+ a[na++] = "@www";
+ break;
+ case OBntpserver:
+ a[na++] = "@ntp";
+ break;
+ case OBtimeserver:
+ a[na++] = "@time";
+ break;
+ }
+ if(strncmp((char*)rp->vendorclass, "plan9_", 6) == 0
+ || strncmp((char*)rp->vendorclass, "p9-", 3) == 0){
+ a[na++] = "@fs";
+ a[na++] = "@auth";
+ }
+ t = lookupinfo(ip, a, na);
+
+ /* lookup anything we might be missing */
+ if(*rp->ii.domain == 0)
+ lookupname(rp->ii.domain, t);
+
+ /* add any requested ones that we know about */
+ for(i = 0; i < sizeof(rp->requested); i++)
+ switch(rp->requested[i]){
+ case OBrouter:
+ j = lookupserver("ipgw", addrs, t);
+ addrsopt(rp, OBrouter, addrs, j);
+ break;
+ case OBdnserver:
+ j = lookupserver("dns", addrs, t);
+ addrsopt(rp, OBdnserver, addrs, j);
+ break;
+ case OBhostname:
+ if(*rp->ii.domain)
+ stringopt(rp, OBhostname, rp->ii.domain);
+ break;
+ case OBdomainname:
+ p = strchr(rp->ii.domain, '.');
+ if(p)
+ stringopt(rp, OBdomainname, p+1);
+ break;
+ case OBnetbiosns:
+ j = lookupserver("wins", addrs, t);
+ addrsopt(rp, OBnetbiosns, addrs, j);
+ break;
+ case OBnetbiostype:
+ /* p-node: peer to peer WINS queries */
+ byteopt(rp, OBnetbiostype, 0x2);
+ break;
+ case OBsmtpserver:
+ j = lookupserver("smtp", addrs, t);
+ addrsopt(rp, OBsmtpserver, addrs, j);
+ break;
+ case OBpop3server:
+ j = lookupserver("pop3", addrs, t);
+ addrsopt(rp, OBpop3server, addrs, j);
+ break;
+ case OBwwwserver:
+ j = lookupserver("www", addrs, t);
+ addrsopt(rp, OBwwwserver, addrs, j);
+ break;
+ case OBntpserver:
+ j = lookupserver("ntp", addrs, t);
+ addrsopt(rp, OBntpserver, addrs, j);
+ break;
+ case OBtimeserver:
+ j = lookupserver("time", addrs, t);
+ addrsopt(rp, OBtimeserver, addrs, j);
+ break;
+ case OBttl:
+ byteopt(rp, OBttl, 255);
+ break;
+ }
+
+ // add plan9 specific options
+ if(strncmp((char*)rp->vendorclass, "plan9_", 6) == 0
+ || strncmp((char*)rp->vendorclass, "p9-", 3) == 0){
+ // point to temporary area
+ op = rp->p;
+ omax = rp->max;
+ rp->p = vopts;
+ rp->max = vopts + sizeof(vopts) - 1;
+
+ j = lookupserver("fs", addrs, t);
+ addrsopt(rp, OP9fs, addrs, j);
+ j = lookupserver("auth", addrs, t);
+ addrsopt(rp, OP9auth, addrs, j);
+
+ // point back
+ j = rp->p - vopts;
+ rp->p = op;
+ rp->max = omax;
+ vectoropt(rp, OBvendorinfo, vopts, j);
+ }
+
+ ndbfree(t);
+}
+
+int
+openlisten(char *net)
+{
+ int fd;
+ char data[128];
+ char devdir[40];
+ int yes;
+
+ sprint(data, "udp!*!bootps");
+ fd = announce(data, devdir);
+ if(fd < 0)
+ fatal(1, "can't announce");
+ yes = 1;
+ if(setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &yes, sizeof yes) < 0)
+ fatal(1, "can't broadcast");
+ return fd;
+}
+
+void
+fatal(int syserr, char *fmt, ...)
+{
+ char buf[ERRMAX];
+ va_list arg;
+
+ va_start(arg, fmt);
+ vseprint(buf, buf+sizeof(buf), fmt, arg);
+ va_end(arg);
+ if(syserr)
+ syslog(1, blog, "%s: %r", buf);
+ else
+ syslog(1, blog, "%s", buf);
+ exits(buf);
+}
+
+extern void
+warning(int syserr, char *fmt, ...)
+{
+ char buf[256];
+ va_list arg;
+
+ va_start(arg, fmt);
+ vseprint(buf, buf+sizeof(buf), fmt, arg);
+ va_end(arg);
+ if(syserr){
+ syslog(0, blog, "%s: %r", buf);
+ if(debug)
+ fprint(2, "%s: %r\n", buf);
+ } else {
+ syslog(0, blog, "%s", buf);
+ if(debug)
+ fprint(2, "%s\n", buf);
+ }
+}
+
+char*
+readsysname(void)
+{
+ static char name[128];
+ char *p;
+ int n, fd;
+
+ fd = open("/dev/sysname", OREAD);
+ if(fd >= 0){
+ n = read(fd, name, sizeof(name)-1);
+ close(fd);
+ if(n > 0){
+ name[n] = 0;
+ return name;
+ }
+ }
+ p = getenv("sysname");
+ if(p == nil || *p == 0)
+ return "unknown";
+ return p;
+}
+
+extern int
+validip(uchar *ip)
+{
+ if(ipcmp(ip, IPnoaddr) == 0)
+ return 0;
+ if(ipcmp(ip, v4prefix) == 0)
+ return 0;
+ return 1;
+}
+
+void
+longopt(Req *rp, int t, long v)
+{
+ if(rp->p + 6 > rp->max)
+ return;
+ *rp->p++ = t;
+ *rp->p++ = 4;
+ hnputl(rp->p, v);
+ rp->p += 4;
+
+ op = seprint(op, oe, "%s(%ld)", optname[t], v);
+}
+
+void
+addropt(Req *rp, int t, uchar *ip)
+{
+ if(rp->p + 6 > rp->max)
+ return;
+ *rp->p++ = t;
+ *rp->p++ = 4;
+ memmove(rp->p, ip+IPv4off, 4);
+ rp->p += 4;
+
+ op = seprint(op, oe, "%s(%I)", optname[t], ip);
+}
+
+void
+maskopt(Req *rp, int t, uchar *ip)
+{
+ if(rp->p + 6 > rp->max)
+ return;
+ *rp->p++ = t;
+ *rp->p++ = 4;
+ memmove(rp->p, ip+IPv4off, 4);
+ rp->p += 4;
+
+ op = seprint(op, oe, "%s(%M)", optname[t], ip);
+}
+
+void
+addrsopt(Req *rp, int t, uchar **ip, int i)
+{
+ if(i <= 0)
+ return;
+ if(rp->p + 2 + 4*i > rp->max)
+ return;
+ *rp->p++ = t;
+ *rp->p++ = 4*i;
+ op = seprint(op, oe, "%s(", optname[t]);
+ while(i-- > 0){
+ v6tov4(rp->p, *ip);
+ rp->p += 4;
+ op = seprint(op, oe, "%I", *ip);
+ ip++;
+ if(i > 0)
+ op = seprint(op, oe, " ");
+ }
+ op = seprint(op, oe, ")");
+}
+
+void
+byteopt(Req *rp, int t, uchar v)
+{
+ if(rp->p + 3 > rp->max)
+ return;
+ *rp->p++ = t;
+ *rp->p++ = 1;
+ *rp->p++ = v;
+
+ op = seprint(op, oe, "%s(%d)", optname[t], v);
+}
+
+void
+termopt(Req *rp)
+{
+ if(rp->p + 1 > rp->max)
+ return;
+ *rp->p++ = OBend;
+}
+
+void
+stringopt(Req *rp, int t, char *str)
+{
+ int n;
+
+ n = strlen(str);
+ if(n > 255)
+ n = 255;
+ if(rp->p+n+2 > rp->max)
+ return;
+ *rp->p++ = t;
+ *rp->p++ = n;
+ memmove(rp->p, str, n);
+ rp->p += n;
+
+ op = seprint(op, oe, "%s(%s)", optname[t], str);
+}
+
+void
+vectoropt(Req *rp, int t, uchar *v, int n)
+{
+ int i;
+
+ if(n > 255)
+ n = 255;
+ if(rp->p+n+2 > rp->max)
+ return;
+ *rp->p++ = t;
+ *rp->p++ = n;
+ memmove(rp->p, v, n);
+ rp->p += n;
+
+ op = seprint(op, oe, "%s(", optname[t]);
+ if(n > 0)
+ op = seprint(op, oe, "%ud", 0);
+ for(i = 1; i < n; i++)
+ op = seprint(op, oe, " %ud", v[i]);
+}
+
+int
+fromhex(int x)
+{
+ if(x >= '0' && x <= '9')
+ return x - '0';
+ return x - 'a';
+}
+
+void
+hexopt(Req *rp, int t, char *str)
+{
+ int n;
+
+ n = strlen(str);
+ n /= 2;
+ if(n > 255)
+ n = 255;
+ if(rp->p+n+2 > rp->max)
+ return;
+ *rp->p++ = t;
+ *rp->p++ = n;
+ while(n-- > 0){
+ *rp->p++ = (fromhex(str[0])<<4)|fromhex(str[1]);
+ str += 2;
+ }
+
+ op = seprint(op, oe, "%s(%s)", optname[t], str);
+}
+
+/*
+ * What a crock it is to do this for real.
+ * A giant hairy mess of ioctls that differ from
+ * system to system. Don't get sucked in.
+ * This need not be fast.
+ */
+void
+arpenter(uchar *ip, uchar *ether)
+{
+ int pid;
+ char xip[100], xether[100];
+
+ switch(pid=fork()){
+ case -1:
+ break;
+ default:
+ waitpid();
+ break;
+ case 0:
+ snprint(xip, sizeof xip, "%I", ip);
+ snprint(xether, sizeof xether, "%#E", ether);
+ execl("arp", "arp", "-s", xip, xether, "temp", nil);
+ _exits("execl");
+ }
+/*
+ for comfort - ah, the good old days
+
+ int f;
+ char buf[256];
+
+ sprint(buf, "%s/arp", net);
+ f = open(buf, OWRITE);
+ if(f < 0){
+ syslog(debug, blog, "open %s: %r", buf);
+ return;
+ }
+ fprint(f, "add ether %I %E", ip, ether);
+ close(f);
+*/
+}
+
+char *dhcpmsgname[] =
+{
+ [Discover] "Discover",
+ [Offer] "Offer",
+ [Request] "Request",
+ [Decline] "Decline",
+ [Ack] "Ack",
+ [Nak] "Nak",
+ [Release] "Release",
+ [Inform] "Inform",
+};
+
+void
+logdhcp(Req *rp)
+{
+ char buf[4096];
+ char *p, *e;
+ int i;
+
+ p = buf;
+ e = buf + sizeof(buf);
+ if(rp->dhcptype > 0 && rp->dhcptype <= Inform)
+ p = seprint(p, e, "%s(", dhcpmsgname[rp->dhcptype]);
+ else
+ p = seprint(p, e, "%d(", rp->dhcptype);
+ p = seprint(p, e, "%I->%I) xid(%ux)flag(%ux)", rp->up->raddr, rp->up->laddr,
+ nhgetl(rp->bp->xid), nhgets(rp->bp->flags));
+ if(rp->bp->htype == 1)
+ p = seprint(p, e, "ea(%E)", rp->bp->chaddr);
+ if(validip(rp->ciaddr))
+ p = seprint(p, e, "ci(%I)", rp->ciaddr);
+ if(validip(rp->giaddr))
+ p = seprint(p, e, "gi(%I)", rp->giaddr);
+ if(validip(rp->ip))
+ p = seprint(p, e, "ip(%I)", rp->ip);
+ if(rp->id != nil)
+ p = seprint(p, e, "id(%s)", rp->id);
+ if(rp->leasetime)
+ p = seprint(p, e, "leas(%d)", rp->leasetime);
+ if(validip(rp->server))
+ p = seprint(p, e, "sid(%I)", rp->server);
+ p = seprint(p, e, "need(");
+ for(i = 0; i < sizeof(rp->requested); i++)
+ if(rp->requested[i] != 0)
+ p = seprint(p, e, "%s ", optname[rp->requested[i]]);
+ p = seprint(p, e, ")");
+
+ USED(p);
+ syslog(0, blog, "%s", buf);
+}
+
+void
+logdhcpout(Req *rp, char *type)
+{
+ syslog(0, blog, "%s(%I->%I)id(%s)ci(%V)gi(%V)yi(%V)si(%V) %s",
+ type, rp->up->laddr, rp->up->raddr, rp->id,
+ rp->bp->ciaddr, rp->bp->giaddr, rp->bp->yiaddr, rp->bp->siaddr, optbuf);
+}
+
+/*
+ * if we get behind, it's useless to try answering since the sender
+ * will probably have retransmitted with a differnt sequence number.
+ * So dump all the last message in the queue.
+ */
+void ding(void *x, char *msg)
+{
+ USED(x);
+
+ if(strstr(msg, "alarm"))
+ noted(NCONT);
+ else
+ noted(NDFLT);
+}
+
+int
+readlast(int fd, Udphdr *hdr, uchar *buf, int len)
+{
+ int lastn, n;
+
+ notify(ding);
+
+ lastn = 0;
+ for(;;){
+ alarm(20);
+ n = udpread(fd, hdr, buf, len);
+ alarm(0);
+ if(n < 0){
+ if(lastn > 0)
+ return lastn;
+ break;
+ }
+ lastn = n;
+ }
+ return udpread(fd, hdr, buf, len);
+}
diff --git a/src/cmd/ip/dhcpd/dhcpleases.c b/src/cmd/ip/dhcpd/dhcpleases.c
new file mode 100755
index 00000000..71719649
--- /dev/null
+++ b/src/cmd/ip/dhcpd/dhcpleases.c
@@ -0,0 +1,43 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ndb.h>
+#include <ip.h>
+#include "dat.h"
+
+extern char *binddir;
+ long now;
+ char *blog = "ipboot";
+ int minlease = MinLease;
+
+void
+main(void)
+{
+ Dir *all;
+ int i, nall, fd;
+ Binding b;
+
+ fmtinstall('E', eipfmt);
+ fmtinstall('I', eipfmt);
+ fmtinstall('V', eipfmt);
+ fmtinstall('M', eipfmt);
+
+ fd = open(binddir, OREAD);
+ if(fd < 0)
+ sysfatal("opening %s: %r", binddir);
+ nall = dirreadall(fd, &all);
+ if(nall < 0)
+ sysfatal("reading %s: %r", binddir);
+ close(fd);
+
+ b.boundto = 0;
+ b.lease = b.offer = 0;
+ now = time(0);
+ for(i = 0; i < nall; i++){
+ parseip(b.ip, all[i].name);
+ if(syncbinding(&b, 0) < 0)
+ continue;
+ if(b.lease > now)
+ print("%I leased by %s until %s", b.ip, b.boundto, ctime(b.lease));
+ }
+}
diff --git a/src/cmd/ip/dhcpd/mkfile b/src/cmd/ip/dhcpd/mkfile
new file mode 100755
index 00000000..90e40a6d
--- /dev/null
+++ b/src/cmd/ip/dhcpd/mkfile
@@ -0,0 +1,22 @@
+<$PLAN9/src/mkhdr
+
+TARG=dhcpd\
+ dhcpleases\
+
+DOFILES=\
+ db.$O\
+ ndb.$O\
+ ping.$O\
+
+IOFILES=\
+ db.$O\
+ ping.$O\
+
+HFILES=dat.h ../dhcp.h
+
+<$PLAN9/src/mkmany
+
+$O.dhcpd: $DOFILES
+$O.dhcpleases: $IOFILES
+$O.testping: ping.$O
+
diff --git a/src/cmd/ip/dhcpd/ndb.c b/src/cmd/ip/dhcpd/ndb.c
new file mode 100755
index 00000000..53ffb58f
--- /dev/null
+++ b/src/cmd/ip/dhcpd/ndb.c
@@ -0,0 +1,318 @@
+/*
+ * this currently only works for ethernet bootp's -- presotto
+ */
+#include <u.h>
+#include <libc.h>
+#include <ip.h>
+#include <bio.h>
+#include <ndb.h>
+#include "dat.h"
+
+static void check72(Info *iip);
+
+Ndb *db;
+char *ndbfile;
+
+Iplifc*
+findlifc(uchar *ip)
+{
+ uchar x[IPaddrlen];
+ Ipifc *ifc;
+ Iplifc *lifc;
+
+ for(ifc = ipifcs; ifc; ifc = ifc->next){
+ for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
+ if(lifc->net[0] == 0)
+ continue;
+ maskip(ip, lifc->mask, x);
+ if(memcmp(x, lifc->net, IPaddrlen) == 0)
+ return lifc;
+ }
+ }
+ return nil;
+}
+
+int
+forme(uchar *ip)
+{
+ Ipifc *ifc;
+ Iplifc *lifc;
+
+extern uchar xmyipaddr[IPaddrlen];
+
+if(memcmp(ip, xmyipaddr, IPaddrlen) == 0)
+ return 1;
+
+ for(ifc = ipifcs; ifc; ifc = ifc->next){
+ for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next)
+ if(memcmp(ip, lifc->ip, IPaddrlen) == 0)
+ return 1;
+ }
+ return 0;
+}
+
+uchar noetheraddr[6];
+
+static void
+setipaddr(uchar *addr, char *ip)
+{
+ if(ipcmp(addr, IPnoaddr) == 0)
+ parseip(addr, ip);
+}
+
+static void
+setipmask(uchar *mask, char *ip)
+{
+ if(ipcmp(mask, IPnoaddr) == 0)
+ parseipmask(mask, ip);
+}
+
+/*
+ * do an ipinfo with defaults
+ */
+int
+lookupip(uchar *ipaddr, Info *iip, int gate)
+{
+ char ip[32];
+ Ndbtuple *t, *nt;
+ char *attrs[32], **p;
+
+ if(db == 0)
+ db = ndbopen(ndbfile);
+ if(db == 0){
+ fprint(2, "can't open db\n");
+ return -1;
+ }
+
+ p = attrs;
+ *p++ = "ip";
+ *p++ = "ipmask";
+ *p++ = "@ipgw";
+ if(!gate){
+ *p++ = "bootf";
+ *p++ = "bootf2";
+ *p++ = "@tftp";
+ *p++ = "@tftp2";
+ *p++ = "rootpath";
+ *p++ = "dhcp";
+ *p++ = "vendorclass";
+ *p++ = "ether";
+ *p++ = "dom";
+ *p++ = "@fs";
+ *p++ = "@auth";
+ }
+ *p = 0;
+
+ memset(iip, 0, sizeof(*iip));
+ snprint(ip, sizeof(ip), "%I", ipaddr);
+ t = ndbipinfo(db, "ip", ip, attrs, p - attrs);
+ if(t == nil)
+ return -1;
+
+ for(nt = t; nt != nil; nt = nt->entry){
+ if(strcmp(nt->attr, "ip") == 0)
+ setipaddr(iip->ipaddr, nt->val);
+ else
+ if(strcmp(nt->attr, "ipmask") == 0)
+ setipmask(iip->ipmask, nt->val);
+ else
+ if(strcmp(nt->attr, "fs") == 0)
+ setipaddr(iip->fsip, nt->val);
+ else
+ if(strcmp(nt->attr, "auth") == 0)
+ setipaddr(iip->auip, nt->val);
+ else
+ if(strcmp(nt->attr, "tftp") == 0)
+ setipaddr(iip->tftp, nt->val);
+ else
+ if(strcmp(nt->attr, "tftp2") == 0)
+ setipaddr(iip->tftp2, nt->val);
+ else
+ if(strcmp(nt->attr, "ipgw") == 0)
+ setipaddr(iip->gwip, nt->val);
+ else
+ if(strcmp(nt->attr, "ether") == 0){
+ if(memcmp(iip->etheraddr, noetheraddr, 6) == 0)
+ parseether(iip->etheraddr, nt->val);
+ iip->indb = 1;
+ }
+ else
+ if(strcmp(nt->attr, "dhcp") == 0){
+ if(iip->dhcpgroup[0] == 0)
+ strcpy(iip->dhcpgroup, nt->val);
+ }
+ else
+ if(strcmp(nt->attr, "bootf") == 0){
+ if(iip->bootf[0] == 0)
+ strcpy(iip->bootf, nt->val);
+ }
+ else
+ if(strcmp(nt->attr, "bootf2") == 0){
+ if(iip->bootf2[0] == 0)
+ strcpy(iip->bootf2, nt->val);
+ }
+ else
+ if(strcmp(nt->attr, "vendor") == 0){
+ if(iip->vendor[0] == 0)
+ strcpy(iip->vendor, nt->val);
+ }
+ else
+ if(strcmp(nt->attr, "dom") == 0){
+ if(iip->domain[0] == 0)
+ strcpy(iip->domain, nt->val);
+ }
+ else
+ if(strcmp(nt->attr, "rootpath") == 0){
+ if(iip->rootpath[0] == 0)
+ strcpy(iip->rootpath, nt->val);
+ }
+ }
+ ndbfree(t);
+ maskip(iip->ipaddr, iip->ipmask, iip->ipnet);
+ return 0;
+}
+
+static uchar zeroes[6];
+
+/*
+ * lookup info about a client in the database. Find an address on the
+ * same net as riip.
+ */
+int
+lookup(Bootp *bp, Info *iip, Info *riip)
+{
+ Ndbtuple *t, *nt;
+ Ndbs s;
+ char *hwattr;
+ char *hwval, hwbuf[33];
+ uchar ciaddr[IPaddrlen];
+
+ if(db == 0)
+ db = ndbopen(ndbfile);
+ if(db == 0){
+ fprint(2, "can't open db\n");
+ return -1;
+ }
+
+ memset(iip, 0, sizeof(*iip));
+
+ /* client knows its address? */
+ v4tov6(ciaddr, bp->ciaddr);
+ if(validip(ciaddr)){
+ if(lookupip(ciaddr, iip, 0) < 0)
+ return -1; /* don't know anything about it */
+
+check72(iip);
+
+ if(!samenet(riip->ipaddr, iip)){
+ warning(0, "%I not on %I", ciaddr, riip->ipnet);
+ return -1;
+ }
+
+ /*
+ * see if this is a masquerade, i.e., if the ether
+ * address doesn't match what we expected it to be.
+ */
+ if(memcmp(iip->etheraddr, zeroes, 6) != 0)
+ if(memcmp(bp->chaddr, iip->etheraddr, 6) != 0)
+ warning(0, "ciaddr %I rcvd from %E instead of %E",
+ ciaddr, bp->chaddr, iip->etheraddr);
+
+ return 0;
+ }
+
+ if(bp->hlen > Maxhwlen)
+ return -1;
+ switch(bp->htype){
+ case 1:
+ hwattr = "ether";
+ hwval = hwbuf;
+ snprint(hwbuf, sizeof(hwbuf), "%E", bp->chaddr);
+ break;
+ default:
+ syslog(0, blog, "not ethernet %E, htype %d, hlen %d",
+ bp->chaddr, bp->htype, bp->hlen);
+ return -1;
+ }
+
+ /*
+ * use hardware address to find an ip address on
+ * same net as riip
+ */
+ t = ndbsearch(db, &s, hwattr, hwval);
+ while(t){
+ for(nt = t; nt; nt = nt->entry){
+ if(strcmp(nt->attr, "ip") != 0)
+ continue;
+ parseip(ciaddr, nt->val);
+ if(lookupip(ciaddr, iip, 0) < 0)
+ continue;
+ if(samenet(riip->ipaddr, iip)){
+ ndbfree(t);
+ return 0;
+ }
+ }
+ ndbfree(t);
+ t = ndbsnext(&s, hwattr, hwval);
+ }
+ return -1;
+}
+
+/*
+ * interface to ndbipinfo
+ */
+Ndbtuple*
+lookupinfo(uchar *ipaddr, char **attr, int n)
+{
+ char ip[32];
+
+ sprint(ip, "%I", ipaddr);
+ return ndbipinfo(db, "ip", ip, attr, n);
+}
+
+/*
+ * return the ip addresses for a type of server for system ip
+ */
+int
+lookupserver(char *attr, uchar **ipaddrs, Ndbtuple *t)
+{
+ Ndbtuple *nt;
+ int rv = 0;
+
+ for(nt = t; rv < 2 && nt != nil; nt = nt->entry)
+ if(strcmp(nt->attr, attr) == 0){
+ parseip(ipaddrs[rv], nt->val);
+ rv++;
+ }
+ return rv;
+}
+
+/*
+ * just lookup the name
+ */
+void
+lookupname(char *val, Ndbtuple *t)
+{
+ Ndbtuple *nt;
+
+ for(nt = t; nt != nil; nt = nt->entry)
+ if(strcmp(nt->attr, "dom") == 0){
+ strcpy(val, nt->val);
+ break;
+ }
+}
+
+uchar slash120[IPaddrlen] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0 };
+uchar net72[IPaddrlen] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0xff, 0xff, 135, 104, 72, 0 };
+
+static void
+check72(Info *iip)
+{
+ uchar net[IPaddrlen];
+
+ maskip(iip->ipaddr, slash120, net);
+ if(ipcmp(net, net72) == 0)
+ syslog(0, blog, "check72 %I %M gw %I", iip->ipaddr, iip->ipmask, iip->gwip);
+}
diff --git a/src/cmd/ip/dhcpd/ping.c b/src/cmd/ip/dhcpd/ping.c
new file mode 100755
index 00000000..7fa7a424
--- /dev/null
+++ b/src/cmd/ip/dhcpd/ping.c
@@ -0,0 +1,112 @@
+#include <u.h>
+#include <libc.h>
+#include <ip.h>
+
+
+typedef struct Icmp Icmp;
+struct Icmp
+{
+ uchar vihl; /* Version and header length */
+ uchar tos; /* Type of service */
+ uchar length[2]; /* packet length */
+ uchar id[2]; /* Identification */
+ uchar frag[2]; /* Fragment information */
+ uchar ttl; /* Time to live */
+ uchar proto; /* Protocol */
+ uchar ipcksum[2]; /* Header checksum */
+ uchar src[4]; /* Ip source */
+ uchar dst[4]; /* Ip destination */
+ uchar type;
+ uchar code;
+ uchar cksum[2];
+ uchar icmpid[2];
+ uchar seq[2];
+ uchar data[1];
+};
+
+enum
+{ /* Packet Types */
+ EchoReply = 0,
+ Unreachable = 3,
+ SrcQuench = 4,
+ EchoRequest = 8,
+ TimeExceed = 11,
+ Timestamp = 13,
+ TimestampReply = 14,
+ InfoRequest = 15,
+ InfoReply = 16,
+
+ ICMP_IPSIZE = 20,
+ ICMP_HDRSIZE = 8,
+};
+
+static void
+catch(void *a, char *msg)
+{
+ USED(a);
+ if(strstr(msg, "alarm"))
+ noted(NCONT);
+ else
+ noted(NDFLT);
+}
+
+#define MSG "dhcp probe"
+
+/*
+ * make sure noone is using the address
+ */
+int
+icmpecho(uchar *a)
+{
+ int fd;
+ char buf[512];
+ Icmp *ip;
+ int i, n, len;
+ ushort sseq, x;
+ int rv;
+
+return 0;
+ rv = 0;
+
+ sprint(buf, "%I", a);
+ fd = dial(netmkaddr(buf, "icmp", "1"), 0, 0, 0);
+ if(fd < 0){
+ return 0;
+ }
+
+ sseq = getpid()*time(0);
+
+ ip = (Icmp*)buf;
+ notify(catch);
+ for(i = 0; i < 3; i++){
+ ip->type = EchoRequest;
+ ip->code = 0;
+ strcpy((char*)ip->data, MSG);
+ ip->seq[0] = sseq;
+ ip->seq[1] = sseq>>8;
+ len = ICMP_IPSIZE+ICMP_HDRSIZE+sizeof(MSG);
+
+ /* send a request */
+ if(write(fd, buf, len) < len)
+ break;
+
+ /* wait 1/10th second for a reply and try again */
+ alarm(100);
+ n = read(fd, buf, sizeof(buf));
+ alarm(0);
+ if(n <= 0)
+ continue;
+
+ /* an answer to our echo request? */
+ x = (ip->seq[1]<<8)|ip->seq[0];
+ if(n >= len)
+ if(ip->type == EchoReply)
+ if(x == sseq)
+ if(strcmp((char*)ip->data, MSG) == 0){
+ rv = 1;
+ break;
+ }
+ }
+ close(fd);
+ return rv;
+}
diff --git a/src/cmd/ip/dhcpd/testlook.c b/src/cmd/ip/dhcpd/testlook.c
new file mode 100755
index 00000000..4937e55b
--- /dev/null
+++ b/src/cmd/ip/dhcpd/testlook.c
@@ -0,0 +1,222 @@
+#include <u.h>
+#include <libc.h>
+#include <ip.h>
+#include <bio.h>
+#include <ndb.h>
+
+static uchar noether[6];
+
+/*
+ * Look for a pair with the given attribute. look first on the same line,
+ * then in the whole entry.
+ */
+static Ndbtuple*
+lookval(Ndbtuple *entry, Ndbtuple *line, char *attr, char *to)
+{
+ Ndbtuple *nt;
+
+ /* first look on same line (closer binding) */
+ for(nt = line;;){
+ if(strcmp(attr, nt->attr) == 0){
+ strncpy(to, nt->val, Ndbvlen);
+ return nt;
+ }
+ nt = nt->line;
+ if(nt == line)
+ break;
+ }
+ /* search whole tuple */
+ for(nt = entry; nt; nt = nt->entry)
+ if(strcmp(attr, nt->attr) == 0){
+ strncpy(to, nt->val, Ndbvlen);
+ return nt;
+ }
+ return 0;
+}
+
+/*
+ * lookup an ip address
+ */
+static uchar*
+lookupip(Ndb *db, char *name, uchar *to, Ipinfo *iip)
+{
+ Ndbtuple *t, *nt;
+ char buf[Ndbvlen];
+ uchar subnet[IPaddrlen];
+ Ndbs s;
+ char *attr;
+
+ attr = ipattr(name);
+ if(strcmp(attr, "ip") == 0){
+ parseip(to, name);
+ return to;
+ }
+
+ t = ndbgetval(db, &s, attr, name, "ip", buf);
+ if(t){
+ /* first look for match on same subnet */
+ for(nt = t; nt; nt = nt->entry){
+ if(strcmp(nt->attr, "ip") != 0)
+ continue;
+ parseip(to, nt->val);
+ maskip(to, iip->ipmask, subnet);
+ if(memcmp(subnet, iip->ipnet, sizeof(subnet)) == 0)
+ return to;
+ }
+
+ /* otherwise, just take what we have */
+ ndbfree(t);
+ parseip(to, buf);
+ return to;
+ }
+ return 0;
+}
+
+/*
+ * lookup a subnet and fill in anything we can
+ */
+static void
+recursesubnet(Ndb *db, uchar *mask, Ipinfo *iip, char *fs, char *gw, char *au)
+{
+ Ndbs s;
+ Ndbtuple *t;
+ uchar submask[IPaddrlen];
+ char ip[Ndbvlen];
+
+ memmove(iip->ipmask, mask, 4);
+ maskip(iip->ipaddr, iip->ipmask, iip->ipnet);
+ sprint(ip, "%I", iip->ipnet);
+ t = ndbsearch(db, &s, "ip", ip);
+print("%s->", ip);
+ if(t){
+ /* look for a further subnet */
+ if(lookval(t, s.t, "ipmask", ip)){
+ parseip(submask, ip);
+
+ /* recurse only if it has changed */
+ if(!equivip(submask, mask))
+ recursesubnet(db, submask, iip, fs, gw, au);
+
+ }
+
+ /* fill in what we don't have */
+ if(gw[0] == 0)
+ lookval(t, s.t, "ipgw", gw);
+ if(fs[0] == 0)
+ lookval(t, s.t, "fs", fs);
+ if(au[0] == 0)
+ lookval(t, s.t, "auth", au);
+
+ ndbfree(t);
+ }
+}
+#ifdef foo
+/*
+ * find out everything we can about a system from what has been
+ * specified.
+ */
+int
+ipinfo(Ndb *db, char *etherin, char *ipin, char *name, Ipinfo *iip)
+{
+ Ndbtuple *t;
+ Ndbs s;
+ char ether[Ndbvlen];
+ char ip[Ndbvlen];
+ char fsname[Ndbvlen];
+ char gwname[Ndbvlen];
+ char auname[Ndbvlen];
+
+ memset(iip, 0, sizeof(Ipinfo));
+ fsname[0] = 0;
+ gwname[0] = 0;
+ auname[0] = 0;
+
+ /*
+ * look for a matching entry
+ */
+ t = 0;
+ if(etherin)
+ t = ndbgetval(db, &s, "ether", etherin, "ip", ip);
+ if(t == 0 && ipin)
+ t = ndbsearch(db, &s, "ip", ipin);
+ if(t == 0 && name)
+ t = ndbgetval(db, &s, ipattr(name), name, "ip", ip);
+ if(t){
+ /*
+ * copy in addresses and name
+ */
+ if(lookval(t, s.t, "ip", ip))
+ parseip(iip->ipaddr, ip);
+ if(lookval(t, s.t, "ether", ether))
+ parseether(iip->etheraddr, ether);
+ lookval(t, s.t, "dom", iip->domain);
+
+ /*
+ * Look for bootfile, fs, and gateway.
+ * If necessary, search through all entries for
+ * this ip address.
+ */
+ while(t){
+ if(iip->bootf[0] == 0)
+ lookval(t, s.t, "bootf", iip->bootf);
+ if(fsname[0] == 0)
+ lookval(t, s.t, "fs", fsname);
+ if(gwname[0] == 0)
+ lookval(t, s.t, "ipgw", gwname);
+ if(auname[0] == 0)
+ lookval(t, s.t, "auth", auname);
+ ndbfree(t);
+ if(iip->bootf[0] && fsname[0] && gwname[0] && auname[0])
+ break;
+ t = ndbsnext(&s, "ether", ether);
+ }
+ } else if(ipin) {
+ /*
+ * copy in addresses (all we know)
+ */
+ parseip(iip->ipaddr, ipin);
+ if(etherin)
+ parseether(iip->etheraddr, etherin);
+ } else
+ return -1;
+
+ /*
+ * Look up the client's network and find a subnet mask for it.
+ * Fill in from the subnet (or net) entry anything we can't figure
+ * out from the client record.
+ */
+ recursesubnet(db, classmask[CLASS(iip->ipaddr)], iip, fsname, gwname, auname);
+
+ /* lookup fs's and gw's ip addresses */
+
+ if(fsname[0])
+ lookupip(db, fsname, iip->fsip, iip);
+ if(gwname[0])
+ lookupip(db, gwname, iip->gwip, iip);
+ if(auname[0])
+ lookupip(db, auname, iip->auip, iip);
+ return 0;
+}
+#endif
+void
+main(int argc, char **argv)
+{
+ Ipinfo ii;
+ Ndb *db;
+
+ db = ndbopen(0);
+
+ fmtinstall('E', eipconv);
+ fmtinstall('I', eipconv);
+ if(argc < 2)
+ exits(0);
+ if(strchr(argv[1], '.')){
+ if(ipinfo(db, 0, argv[1], 0, &ii) < 0)
+ exits(0);
+ } else {
+ if(ipinfo(db, argv[1], 0, 0, &ii) < 0)
+ exits(0);
+ }
+ fprint(2, "a %I m %I n %I f %s e %E\n", ii.ipaddr,
+ ii.ipmask, ii.ipnet, ii.bootf, ii.etheraddr);
+}
diff --git a/src/cmd/ip/dhcpd/testlookup.c b/src/cmd/ip/dhcpd/testlookup.c
new file mode 100755
index 00000000..c69624c5
--- /dev/null
+++ b/src/cmd/ip/dhcpd/testlookup.c
@@ -0,0 +1,168 @@
+#include <u.h>
+#include <libc.h>
+#include <ip.h>
+#include <bio.h>
+#include <ndb.h>
+
+static uchar noether[6];
+ Ndb *db;
+
+static void
+recursesubnet(Ndb *db, uchar *addr, uchar *mask, char *attr, char *name, char *name1)
+{
+ Ndbs s;
+ Ndbtuple *t, *nt;
+ uchar submask[IPaddrlen], net[IPaddrlen];
+ char ip[Ndbvlen];
+ int found;
+
+ maskip(addr, mask, net);
+ sprint(ip, "%I", net);
+ t = ndbsearch(db, &s, "ip", ip);
+ if(t == 0)
+ return;
+
+ for(nt = t; nt; nt = nt->entry){
+ if(strcmp(nt->attr, "ipmask") == 0){
+ parseip(submask, nt->val);
+ if(memcmp(submask, mask, IPaddrlen) != 0)
+ recursesubnet(db, addr, submask, attr, name, name1);
+ break;
+ }
+ }
+
+ if(name[0] == 0){
+ found = 0;
+ for(nt = t; nt; nt = nt->entry){
+ if(strcmp(nt->attr, attr) == 0){
+ if(found){
+ strcpy(name, nt->val);
+ name1[0] = 0;
+ found = 1;
+ } else {
+ strcpy(name1, nt->val);
+ break;
+ }
+ }
+ }
+ }
+
+ ndbfree(t);
+}
+
+/*
+ * lookup an ip address
+ */
+static int
+getipaddr(Ndb *db, char *name, uchar *to, Ipinfo *iip)
+{
+ Ndbtuple *t, *nt;
+ char buf[Ndbvlen];
+ uchar subnet[IPaddrlen];
+ Ndbs s;
+ char *attr;
+
+ attr = ipattr(name);
+ if(strcmp(attr, "ip") == 0){
+ parseip(to, name);
+ return 1;
+ }
+
+ t = ndbgetval(db, &s, attr, name, "ip", buf);
+ if(t){
+ /* first look for match on same subnet */
+ for(nt = t; nt; nt = nt->entry){
+ if(strcmp(nt->attr, "ip") != 0)
+ continue;
+ parseip(to, nt->val);
+ maskip(to, iip->ipmask, subnet);
+ if(memcmp(subnet, iip->ipnet, sizeof(subnet)) == 0)
+ return 1;
+ }
+
+ /* otherwise, just take what we have */
+ ndbfree(t);
+ parseip(to, buf);
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * return the ip addresses for a type of server for system ip
+ */
+int
+lookupserver(char *attr, uchar ipaddrs[2][IPaddrlen], Ipinfo *iip)
+{
+ Ndbtuple *t, *nt;
+ Ndbs s;
+ char ip[32];
+ char name[Ndbvlen];
+ char name1[Ndbvlen];
+ int i;
+
+ name[0] = name1[0] = 0;
+
+ snprint(ip, sizeof(ip), "%I", iip->ipaddr);
+ t = ndbsearch(db, &s, "ip", ip);
+ while(t){
+ for(nt = t; nt; nt = nt->entry){
+ if(strcmp(attr, nt->attr) == 0){
+ if(*name == 0)
+ strcpy(name, nt->val);
+ else {
+ strcpy(name1, nt->val);
+ break;
+ }
+ }
+ }
+ if(name[0])
+ break;
+ t = ndbsnext(&s, "ip", ip);
+ }
+
+ if(name[0] == 0)
+ recursesubnet(db, iip->ipaddr, classmask[CLASS(iip->ipaddr)], attr, name, name1);
+
+ i = 0;
+ if(name[0] && getipaddr(db, name, *ipaddrs, iip) == 1){
+ ipaddrs++;
+ i++;
+ }
+ if(name1[0] && getipaddr(db, name1, *ipaddrs, iip) == 1)
+ i++;
+ return i;
+}
+
+void
+main(int argc, char **argv)
+{
+ Ipinfo ii;
+ uchar addrs[2][IPaddrlen];
+ int i, j;
+
+ db = ndbopen(0);
+
+ fmtinstall('E', eipconv);
+ fmtinstall('I', eipconv);
+ if(argc < 2)
+ exits(0);
+ if(strchr(argv[1], '.')){
+ if(ipinfo(db, 0, argv[1], 0, &ii) < 0)
+ exits(0);
+ } else {
+ if(ipinfo(db, argv[1], 0, 0, &ii) < 0)
+ exits(0);
+ }
+ print("a %I m %I n %I f %s e %E a %I\n", ii.ipaddr,
+ ii.ipmask, ii.ipnet, ii.bootf, ii.etheraddr, ii.auip);
+
+ i = lookupserver("auth", addrs, &ii);
+ print("lookupserver returns %d\n", i);
+ for(j = 0; j < i; j++)
+ print("%I\n", addrs[j]);
+ i = lookupserver("dns", addrs, &ii);
+ print("lookupserver returns %d\n", i);
+ for(j = 0; j < i; j++)
+ print("%I\n", addrs[j]);
+}
diff --git a/src/cmd/ip/dhcpd/testping.c b/src/cmd/ip/dhcpd/testping.c
new file mode 100755
index 00000000..b5ca90c5
--- /dev/null
+++ b/src/cmd/ip/dhcpd/testping.c
@@ -0,0 +1,22 @@
+#include <u.h>
+#include <libc.h>
+#include <ip.h>
+#include <bio.h>
+#include <ndb.h>
+#include "dat.h"
+
+char *blog = "ipboot";
+
+void
+main(int argc, char **argv)
+{
+ fmtinstall('E', eipconv);
+ fmtinstall('I', eipconv);
+
+ if(argc < 2)
+ exits(0);
+ if(icmpecho(argv[1]))
+ fprint(2, "%s live\n", argv[1]);
+ else
+ fprint(2, "%s doesn't answer\n", argv[1]);
+}
diff --git a/src/cmd/ip/snoopy/Linux.c b/src/cmd/ip/snoopy/Linux.c
new file mode 100644
index 00000000..20bc899c
--- /dev/null
+++ b/src/cmd/ip/snoopy/Linux.c
@@ -0,0 +1,58 @@
+#include <u.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netpacket/packet.h>
+#include <net/ethernet.h>
+#include <netinet/in.h>
+#include <sys/ioctl.h>
+#include <libc.h>
+#include <ip.h>
+#include <bio.h>
+#include <fcall.h>
+#include <libsec.h>
+#include "dat.h"
+#include "protos.h"
+#include "y.tab.h"
+
+int
+opendevice(char *dev, int promisc)
+{
+ int fd;
+ struct ifreq ifr;
+ struct sockaddr_ll sa;
+
+ if((fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) < 0)
+ return -1;
+
+ if(dev){
+ memset(&ifr, 0, sizeof ifr);
+ strncpy(ifr.ifr_name, dev, sizeof ifr.ifr_name);
+ if(ioctl(fd, SIOCGIFINDEX, &ifr) < 0){
+ close(fd);
+ return -1;
+ }
+ memset(&sa, 0, sizeof sa);
+ sa.sll_family = AF_PACKET;
+ sa.sll_protocol = htons(ETH_P_ALL);
+ sa.sll_ifindex = ifr.ifr_ifindex;
+ if(bind(fd, (struct sockaddr*)&sa, sizeof sa) < 0){
+ close(fd);
+ return -1;
+ }
+ }
+
+ if(promisc){
+ memset(&ifr, 0, sizeof ifr);
+ strncpy(ifr.ifr_name, dev, sizeof ifr.ifr_name);
+ if(ioctl(fd, SIOCGIFFLAGS, &ifr) < 0){
+ close(fd);
+ return -1;
+ }
+ ifr.ifr_flags |= IFF_PROMISC;
+ if(ioctl(fd, SIOCSIFFLAGS, &ifr) < 0){
+ close(fd);
+ return -1;
+ }
+ }
+ return fd;
+}
diff --git a/src/cmd/ip/snoopy/arp.c b/src/cmd/ip/snoopy/arp.c
new file mode 100755
index 00000000..3433ebc6
--- /dev/null
+++ b/src/cmd/ip/snoopy/arp.c
@@ -0,0 +1,128 @@
+#include <u.h>
+#include <libc.h>
+#include <ip.h>
+#include "dat.h"
+#include "protos.h"
+
+typedef struct Hdr Hdr;
+struct Hdr
+{
+ uchar hrd[2];
+ uchar pro[2];
+ uchar hln;
+ uchar pln;
+ uchar op[2];
+ uchar sha[6];
+ uchar spa[4];
+ uchar tha[6];
+ uchar tpa[4];
+};
+
+enum
+{
+ ARPLEN= 28,
+};
+
+enum
+{
+ Ospa,
+ Otpa,
+ Ostpa,
+ Osha,
+ Otha,
+ Ostha,
+ Opa,
+};
+
+static Field p_fields[] =
+{
+ {"spa", Fv4ip, Ospa, "protocol source", } ,
+ {"tpa", Fv4ip, Otpa, "protocol target", } ,
+ {"a", Fv4ip, Ostpa, "protocol source/target", } ,
+ {"sha", Fba, Osha, "hardware source", } ,
+ {"tha", Fba, Otha, "hardware target", } ,
+ {"ah", Fba, Ostha, "hardware source/target", } ,
+ {0}
+};
+
+static void
+p_compile(Filter *f)
+{
+ if(f->op == '='){
+ compile_cmp(arp.name, f, p_fields);
+ return;
+ }
+ sysfatal("unknown arp field: %s", f->s);
+}
+
+static int
+p_filter(Filter *f, Msg *m)
+{
+ Hdr *h;
+
+ if(m->pe - m->ps < ARPLEN)
+ return 0;
+
+ h = (Hdr*)m->ps;
+ m->ps += ARPLEN;
+
+ switch(f->subop){
+ case Ospa:
+ return h->pln == 4 && NetL(h->spa) == f->ulv;
+ case Otpa:
+ return h->pln == 4 && NetL(h->tpa) == f->ulv;
+ case Ostpa:
+ return h->pln == 4 && (NetL(h->tpa) == f->ulv ||
+ NetL(h->spa) == f->ulv);
+ case Osha:
+ return memcmp(h->sha, f->a, h->hln) == 0;
+ case Otha:
+ return memcmp(h->tha, f->a, h->hln) == 0;
+ case Ostha:
+ return memcmp(h->sha, f->a, h->hln)==0
+ ||memcmp(h->tha, f->a, h->hln)==0;
+ }
+ return 0;
+}
+
+static int
+p_seprint(Msg *m)
+{
+ Hdr *h;
+
+ if(m->pe - m->ps < ARPLEN)
+ return -1;
+
+ h = (Hdr*)m->ps;
+ m->ps += ARPLEN;
+
+ /* no next protocol */
+ m->pr = nil;
+
+ m->p = seprint(m->p, m->e, "op=%1d len=%1d/%1d spa=%V sha=%E tpa=%V tha=%E",
+ NetS(h->op), h->pln, h->hln,
+ h->spa, h->sha, h->tpa, h->tha);
+ return 0;
+}
+
+Proto arp =
+{
+ "arp",
+ p_compile,
+ p_filter,
+ p_seprint,
+ nil,
+ p_fields,
+ defaultframer,
+};
+
+Proto rarp =
+{
+ "rarp",
+ p_compile,
+ p_filter,
+ p_seprint,
+ nil,
+ p_fields,
+ defaultframer,
+};
diff --git a/src/cmd/ip/snoopy/bootp.c b/src/cmd/ip/snoopy/bootp.c
new file mode 100755
index 00000000..1adfdad1
--- /dev/null
+++ b/src/cmd/ip/snoopy/bootp.c
@@ -0,0 +1,176 @@
+#include <u.h>
+#include <libc.h>
+#include <ip.h>
+#include "dat.h"
+#include "protos.h"
+
+enum
+{
+ OfferTimeout= 60, /* when an offer times out */
+ MaxLease= 60*60, /* longest lease for dynamic binding */
+ MinLease= 15*60, /* shortest lease for dynamic binding */
+ StaticLease= 30*60, /* lease for static binding */
+
+ IPUDPHDRSIZE= 28, /* size of an IP plus UDP header */
+ MINSUPPORTED= 576, /* biggest IP message the client must support */
+
+ /* lengths of some bootp fields */
+ Maxhwlen= 16,
+ Maxfilelen= 128,
+ Maxoptlen= 312-4,
+
+ /* bootp types */
+ Bootrequest= 1,
+ Bootreply= 2,
+
+ /* bootp flags */
+ Fbroadcast= 1<<15,
+};
+
+typedef struct Hdr Hdr;
+struct Hdr
+{
+ uchar op; /* opcode */
+ uchar htype; /* hardware type */
+ uchar hlen; /* hardware address len */
+ uchar hops; /* hops */
+ uchar xid[4]; /* a random number */
+ uchar secs[2]; /* elapsed since client started booting */
+ uchar flags[2];
+ uchar ciaddr[IPv4addrlen]; /* client IP address (client tells server) */
+ uchar yiaddr[IPv4addrlen]; /* client IP address (server tells client) */
+ uchar siaddr[IPv4addrlen]; /* server IP address */
+ uchar giaddr[IPv4addrlen]; /* gateway IP address */
+ uchar chaddr[Maxhwlen]; /* client hardware address */
+ char sname[64]; /* server host name (optional) */
+ char file[Maxfilelen]; /* boot file name */
+ uchar optmagic[4];
+ uchar optdata[Maxoptlen];
+};
+
+enum
+{
+ Oca,
+ Osa,
+ Ot,
+};
+
+static Field p_fields[] =
+{
+ {"ca", Fv4ip, Oca, "client IP addr", } ,
+ {"sa", Fv4ip, Osa, "server IP addr", } ,
+ {0}
+};
+
+#define plan9opt ((ulong)(('p'<<24) | ('9'<<16) | (' '<<8) | ' '))
+#define genericopt (0x63825363UL)
+
+static Mux p_mux[] =
+{
+ {"dhcp", genericopt,},
+ {"plan9bootp", plan9opt,},
+ {"dump", 0,},
+ {0}
+};
+
+static void
+p_compile(Filter *f)
+{
+ Mux *m;
+
+ if(f->op == '='){
+ compile_cmp(arp.name, f, p_fields);
+ return;
+ }
+ for(m = p_mux; m->name != nil; m++)
+ if(strcmp(f->s, m->name) == 0){
+ f->pr = m->pr;
+ f->ulv = m->val;
+ f->subop = Ot;
+ return;
+ }
+ sysfatal("unknown bootp field: %s", f->s);
+}
+
+static int
+p_filter(Filter *f, Msg *m)
+{
+ Hdr *h;
+
+ h = (Hdr*)m->ps;
+
+ if(m->pe < (uchar*)h->sname)
+ return 0;
+ m->ps = h->optdata;
+
+ switch(f->subop){
+ case Oca:
+ return NetL(h->ciaddr) == f->ulv || NetL(h->yiaddr) == f->ulv;
+ case Osa:
+ return NetL(h->siaddr) == f->ulv;
+ case Ot:
+ return NetL(h->optmagic) == f->ulv;
+ }
+ return 0;
+}
+
+static char*
+op(int i)
+{
+ static char x[20];
+
+ switch(i){
+ case Bootrequest:
+ return "Req";
+ case Bootreply:
+ return "Rep";
+ default:
+ sprint(x, "%d", i);
+ return x;
+ }
+}
+
+
+static int
+p_seprint(Msg *m)
+{
+ Hdr *h;
+ ulong x;
+
+ h = (Hdr*)m->ps;
+
+ if(m->pe < (uchar*)h->sname)
+ return -1;
+
+ /* point past data */
+ m->ps = h->optdata;
+
+ /* next protocol */
+ m->pr = nil;
+ if(m->pe >= (uchar*)h->optdata){
+ x = NetL(h->optmagic);
+ demux(p_mux, x, x, m, &dump);
+ }
+
+ m->p = seprint(m->p, m->e, "t=%s ht=%d hl=%d hp=%d xid=%ux sec=%d fl=%4.4ux ca=%V ya=%V sa=%V ga=%V cha=%E magic=%lux",
+ op(h->op), h->htype, h->hlen, h->hops,
+ NetL(h->xid), NetS(h->secs), NetS(h->flags),
+ h->ciaddr, h->yiaddr, h->siaddr, h->giaddr, h->chaddr,
+ (ulong)NetL(h->optmagic));
+ if(m->pe > (uchar*)h->sname && *h->sname)
+ m->p = seprint(m->p, m->e, " snam=%s", h->sname);
+ if(m->pe > (uchar*)h->file && *h->file)
+ m->p = seprint(m->p, m->e, " file=%s", h->file);
+ return 0;
+}
+
+Proto bootp =
+{
+ "bootp",
+ p_compile,
+ p_filter,
+ p_seprint,
+ p_mux,
+ p_fields,
+ defaultframer,
+};
diff --git a/src/cmd/ip/snoopy/dat.h b/src/cmd/ip/snoopy/dat.h
new file mode 100755
index 00000000..a4258c25
--- /dev/null
+++ b/src/cmd/ip/snoopy/dat.h
@@ -0,0 +1,106 @@
+typedef struct Field Field;
+typedef struct Filter Filter;
+typedef struct Msg Msg;
+typedef struct Mux Mux;
+typedef struct Proto Proto;
+
+#define NetS(x) ((((uchar*)x)[0]<<8) | ((uchar*)x)[1])
+#define Net3(x) ((((uchar*)x)[0]<<16) | (((uchar*)x)[1]<<8) | ((uchar*)x)[2])
+#define NetL(x) ((((uchar*)x)[0]<<24) | (((uchar*)x)[1]<<16) | (((uchar*)x)[2]<<8) | ((uchar*)x)[3])
+
+/*
+ * one per protocol module
+ */
+struct Proto
+{
+ char* name;
+ void (*compile)(Filter*);
+ int (*filter)(Filter*, Msg*);
+ int (*seprint)(Msg*);
+ Mux* mux;
+ Field* field;
+ int (*framer)(int, uchar*, int);
+};
+extern Proto *protos[];
+
+/*
+ * one per protocol module, pointed to by Proto.mux
+ */
+struct Mux
+{
+ char* name;
+ ulong val;
+ Proto* pr;
+};
+
+/*
+ * a field defining a comparison filter
+ */
+struct Field
+{
+ char* name;
+ int ftype;
+ int subop;
+ char* help;
+};
+
+/*
+ * the status of the current message walk
+ */
+struct Msg
+{
+ uchar *ps; /* packet ptr */
+ uchar *pe; /* packet end */
+
+ char *p; /* buffer start */
+ char *e; /* buffer end */
+
+ int needroot; /* pr is root, need to see in expression */
+ Proto *pr; /* current/next protocol */
+};
+
+enum
+{
+ Fnum, /* just a number */
+ Fether, /* ethernet address */
+ Fv4ip, /* v4 ip address */
+ Fv6ip, /* v6 ip address */
+ Fba, /* byte array */
+};
+
+/*
+ * a node in the filter tree
+ */
+struct Filter {
+ int op; /* token type */
+ char *s; /* string */
+ Filter *l;
+ Filter *r;
+
+ Proto *pr; /* next protocol;
+
+ /* protocol specific */
+ int subop;
+ ulong param;
+ union {
+ ulong ulv;
+ vlong vlv;
+ uchar a[32];
+ };
+};
+
+extern void yyinit(char*);
+extern int yyparse(void);
+extern Filter* newfilter(void);
+extern void compile_cmp(char*, Filter*, Field*);
+extern void demux(Mux*, ulong, ulong, Msg*, Proto*);
+extern int defaultframer(int, uchar*, int);
+extern int opendevice(char*, int);
+
+extern int Nflag;
+extern int dflag;
+extern int Cflag;
+
+typedef Filter *Filterptr;
+#define YYSTYPE Filterptr
+extern Filter *filter;
diff --git a/src/cmd/ip/snoopy/dhcp.c b/src/cmd/ip/snoopy/dhcp.c
new file mode 100755
index 00000000..8b7649cf
--- /dev/null
+++ b/src/cmd/ip/snoopy/dhcp.c
@@ -0,0 +1,483 @@
+#include <u.h>
+#include <libc.h>
+#include <ip.h>
+#include "dat.h"
+#include "protos.h"
+
+enum
+{
+ Maxoptlen= 312-4,
+
+ /* dhcp types */
+ Discover= 1,
+ Offer= 2,
+ Request= 3,
+ Decline= 4,
+ Ack= 5,
+ Nak= 6,
+ Release= 7,
+ Inform= 8,
+
+ /* bootp option types */
+ OBend= 255,
+ OBpad= 0,
+ OBmask= 1,
+ OBtimeoff= 2,
+ OBrouter= 3,
+ OBtimeserver= 4,
+ OBnameserver= 5,
+ OBdnserver= 6,
+ OBlogserver= 7,
+ OBcookieserver= 8,
+ OBlprserver= 9,
+ OBimpressserver= 10,
+ OBrlserver= 11,
+ OBhostname= 12, /* 0xc0 */
+ OBbflen= 13,
+ OBdumpfile= 14,
+ OBdomainname= 15,
+ OBswapserver= 16, /* 0x10 */
+ OBrootpath= 17,
+ OBextpath= 18,
+ OBipforward= 19,
+ OBnonlocal= 20,
+ OBpolicyfilter= 21,
+ OBmaxdatagram= 22,
+ OBttl= 23,
+ OBpathtimeout= 24,
+ OBpathplateau= 25,
+ OBmtu= 26,
+ OBsubnetslocal= 27,
+ OBbaddr= 28,
+ OBdiscovermask= 29,
+ OBsupplymask= 30,
+ OBdiscoverrouter= 31,
+ OBrsserver= 32, /* 0x20 */
+ OBstaticroutes= 33,
+ OBtrailerencap= 34,
+ OBarptimeout= 35,
+ OBetherencap= 36,
+ OBtcpttl= 37,
+ OBtcpka= 38,
+ OBtcpkag= 39,
+ OBnisdomain= 40,
+ OBniserver= 41,
+ OBntpserver= 42,
+ OBvendorinfo= 43, /* 0x2b */
+ OBnetbiosns= 44,
+ OBnetbiosdds= 45,
+ OBnetbiostype= 46,
+ OBnetbiosscope= 47,
+ OBxfontserver= 48, /* 0x30 */
+ OBxdispmanager= 49,
+ OBnisplusdomain= 64, /* 0x40 */
+ OBnisplusserver= 65,
+ OBhomeagent= 68,
+ OBsmtpserver= 69,
+ OBpop3server= 70,
+ OBnntpserver= 71,
+ OBwwwserver= 72,
+ OBfingerserver= 73,
+ OBircserver= 74,
+ OBstserver= 75,
+ OBstdaserver= 76,
+
+ /* dhcp options */
+ ODipaddr= 50, /* 0x32 */
+ ODlease= 51,
+ ODoverload= 52,
+ ODtype= 53, /* 0x35 */
+ ODserverid= 54, /* 0x36 */
+ ODparams= 55, /* 0x37 */
+ ODmessage= 56,
+ ODmaxmsg= 57,
+ ODrenewaltime= 58,
+ ODrebindingtime= 59,
+ ODvendorclass= 60,
+ ODclientid= 61, /* 0x3d */
+ ODtftpserver= 66,
+ ODbootfile= 67,
+
+ /* plan9 vendor info options */
+ OP9fs= 128, // plan9 file servers
+ OP9auth= 129, // plan9 auth servers
+};
+
+static void
+p_compile(Filter *f)
+{
+ sysfatal("unknown bootp field: %s", f->s);
+}
+
+static int
+p_filter(Filter *f, Msg *m)
+{
+ USED(f);
+ USED(m);
+ return 0;
+}
+
+/*
+ * convert a byte array to hex
+ */
+static char
+hex(int x)
+{
+ if(x < 10)
+ return x + '0';
+ return x - 10 + 'a';
+}
+static char*
+phex(char *p, char *e, char *tag, uchar *o, int n)
+{
+ p = seprint(p, e, "%s=", tag);
+
+ for(; p+2 < e && n > 0; n--){
+ *p++ = hex(*o>>4);
+ *p++ = hex(*o & 0xf);
+ o++;
+ }
+ return p;
+}
+
+static char*
+pstring(char *p, char *e, char *tag, uchar *o, int n)
+{
+ char msg[256];
+
+ if(n > sizeof msg - 1)
+ n = sizeof msg - 1;
+ memmove(msg, o, n);
+ msg[n] = 0;
+ return seprint(p, e, "%s=%s", tag, msg);
+}
+
+static char*
+pint(char *p, char *e, char *tag, uchar *o, int n)
+{
+ int x;
+
+ x = *(char*)o++;
+ for(; n > 1; n--)
+ x = (x<<8)|*o++;
+ return seprint(p, e, "%s=%d", tag, x);
+}
+
+static char*
+puint(char *p, char *e, char *tag, uchar *o, int n)
+{
+ uint x;
+
+ x = *o++;
+ for(; n > 1; n--)
+ x = (x<<8)|*o++;
+ return seprint(p, e, "%s=%ud", tag, x);
+}
+
+static char*
+pserver(char *p, char *e, char *tag, uchar *o, int n)
+{
+ p = seprint(p, e, "%s=(", tag);
+ while(n >= 4){
+ p = seprint(p, e, " %V", o);
+ n -= 4;
+ o += 4;
+ }
+ p = seprint(p, e, ")");
+ return p;
+}
+
+static char *dhcptype[256] =
+{
+[Discover] "Discover",
+[Offer] "Offer",
+[Request] "Request",
+[Decline] "Decline",
+[Ack] "Ack",
+[Nak] "Nak",
+[Release] "Release",
+[Inform] "Inform",
+};
+
+
+static char*
+ptype(char *p, char *e, uchar val)
+{
+ char *x;
+
+ x = dhcptype[val];
+ if(x != nil)
+ return seprint(p, e, "t=%s", x);
+ else
+ return seprint(p, e, "t=%d", val);
+}
+
+static int
+p_seprint(Msg *m)
+{
+ int i, n, code;
+ uchar *o, *ps;
+ char *p, *e;
+ char msg[64];
+
+ /* no next proto */
+ m->pr = nil;
+
+ p = m->p;
+ e = m->e;
+ ps = m->ps;
+
+ while(ps < m->pe){
+ code = *ps++;
+ if(code == 255)
+ break;
+ if(code == 0)
+ continue;
+
+ /* ignore anything that's too long */
+ n = *ps++;
+ o = ps;
+ ps += n;
+ if(ps > m->pe)
+ break;
+
+ switch(code){
+ case ODipaddr: /* requested ip address */
+ p = pserver(p, e, "ipaddr", o, n);
+ break;
+ case ODlease: /* requested lease time */
+ p = pint(p, e, "lease", o, n);
+ break;
+ case ODtype:
+ p = ptype(p, e, *o);
+ break;
+ case ODserverid:
+ p = pserver(p, e, "serverid", o, n);
+ break;
+ case ODmessage:
+ p = pstring(p, e, "message", o, n);
+ break;
+ case ODmaxmsg:
+ p = puint(p, e, "maxmsg", o, n);
+ break;
+ case ODclientid:
+ p = phex(p, e, "clientid", o, n);
+ break;
+ case ODparams:
+ p = seprint(p, e, " requested=(");
+ for(i = 0; i < n; i++){
+ if(i != 0)
+ p = seprint(p, e, " ");
+ p = seprint(p, e, "%ud", o[i]);
+ }
+ p = seprint(p, e, ")");
+ break;
+ case ODvendorclass:
+ p = pstring(p, e, "vendorclass", o, n);
+ break;
+ case OBmask:
+ p = pserver(p, e, "mask", o, n);
+ break;
+ case OBtimeoff:
+ p = pint(p, e, "timeoff", o, n);
+ break;
+ case OBrouter:
+ p = pserver(p, e, "router", o, n);
+ break;
+ case OBtimeserver:
+ p = pserver(p, e, "timesrv", o, n);
+ break;
+ case OBnameserver:
+ p = pserver(p, e, "namesrv", o, n);
+ break;
+ case OBdnserver:
+ p = pserver(p, e, "dnssrv", o, n);
+ break;
+ case OBlogserver:
+ p = pserver(p, e, "logsrv", o, n);
+ break;
+ case OBcookieserver:
+ p = pserver(p, e, "cookiesrv", o, n);
+ break;
+ case OBlprserver:
+ p = pserver(p, e, "lprsrv", o, n);
+ break;
+ case OBimpressserver:
+ p = pserver(p, e, "impresssrv", o, n);
+ break;
+ case OBrlserver:
+ p = pserver(p, e, "rlsrv", o, n);
+ break;
+ case OBhostname:
+ p = pstring(p, e, "hostname", o, n);
+ break;
+ case OBbflen:
+ break;
+ case OBdumpfile:
+ p = pstring(p, e, "dumpfile", o, n);
+ break;
+ case OBdomainname:
+ p = pstring(p, e, "domname", o, n);
+ break;
+ case OBswapserver:
+ p = pserver(p, e, "swapsrv", o, n);
+ break;
+ case OBrootpath:
+ p = pstring(p, e, "rootpath", o, n);
+ break;
+ case OBextpath:
+ p = pstring(p, e, "extpath", o, n);
+ break;
+ case OBipforward:
+ p = phex(p, e, "ipforward", o, n);
+ break;
+ case OBnonlocal:
+ p = phex(p, e, "nonlocal", o, n);
+ break;
+ case OBpolicyfilter:
+ p = phex(p, e, "policyfilter", o, n);
+ break;
+ case OBmaxdatagram:
+ p = phex(p, e, "maxdatagram", o, n);
+ break;
+ case OBttl:
+ p = puint(p, e, "ttl", o, n);
+ break;
+ case OBpathtimeout:
+ p = puint(p, e, "pathtimeout", o, n);
+ break;
+ case OBpathplateau:
+ p = phex(p, e, "pathplateau", o, n);
+ break;
+ case OBmtu:
+ p = puint(p, e, "mtu", o, n);
+ break;
+ case OBsubnetslocal:
+ p = pserver(p, e, "subnet", o, n);
+ break;
+ case OBbaddr:
+ p = pserver(p, e, "baddr", o, n);
+ break;
+ case OBdiscovermask:
+ p = pserver(p, e, "discovermsak", o, n);
+ break;
+ case OBsupplymask:
+ p = pserver(p, e, "rousupplymaskter", o, n);
+ break;
+ case OBdiscoverrouter:
+ p = pserver(p, e, "discoverrouter", o, n);
+ break;
+ case OBrsserver:
+ p = pserver(p, e, "rsrouter", o, n);
+ break;
+ case OBstaticroutes:
+ p = phex(p, e, "staticroutes", o, n);
+ break;
+ case OBtrailerencap:
+ p = phex(p, e, "trailerencap", o, n);
+ break;
+ case OBarptimeout:
+ p = puint(p, e, "arptimeout", o, n);
+ break;
+ case OBetherencap:
+ p = phex(p, e, "etherencap", o, n);
+ break;
+ case OBtcpttl:
+ p = puint(p, e, "tcpttl", o, n);
+ break;
+ case OBtcpka:
+ p = puint(p, e, "tcpka", o, n);
+ break;
+ case OBtcpkag:
+ p = phex(p, e, "tcpkag", o, n);
+ break;
+ case OBnisdomain:
+ p = pstring(p, e, "nisdomain", o, n);
+ break;
+ case OBniserver:
+ p = pserver(p, e, "nisrv", o, n);
+ break;
+ case OBntpserver:
+ p = pserver(p, e, "ntpsrv", o, n);
+ break;
+ case OBvendorinfo:
+ p = phex(p, e, "vendorinfo", o, n);
+ break;
+ case OBnetbiosns:
+ p = pserver(p, e, "biosns", o, n);
+ break;
+ case OBnetbiosdds:
+ p = phex(p, e, "biosdds", o, n);
+ break;
+ case OBnetbiostype:
+ p = phex(p, e, "biostype", o, n);
+ break;
+ case OBnetbiosscope:
+ p = phex(p, e, "biosscope", o, n);
+ break;
+ case OBxfontserver:
+ p = pserver(p, e, "fontsrv", o, n);
+ break;
+ case OBxdispmanager:
+ p = pserver(p, e, "xdispmgr", o, n);
+ break;
+ case OBnisplusdomain:
+ p = pstring(p, e, "nisplusdomain", o, n);
+ break;
+ case OBnisplusserver:
+ p = pserver(p, e, "nisplussrv", o, n);
+ break;
+ case OBhomeagent:
+ p = pserver(p, e, "homeagent", o, n);
+ break;
+ case OBsmtpserver:
+ p = pserver(p, e, "smtpsrv", o, n);
+ break;
+ case OBpop3server:
+ p = pserver(p, e, "pop3srv", o, n);
+ break;
+ case OBnntpserver:
+ p = pserver(p, e, "ntpsrv", o, n);
+ break;
+ case OBwwwserver:
+ p = pserver(p, e, "wwwsrv", o, n);
+ break;
+ case OBfingerserver:
+ p = pserver(p, e, "fingersrv", o, n);
+ break;
+ case OBircserver:
+ p = pserver(p, e, "ircsrv", o, n);
+ break;
+ case OBstserver:
+ p = pserver(p, e, "stsrv", o, n);
+ break;
+ case OBstdaserver:
+ p = pserver(p, e, "stdasrv", o, n);
+ break;
+ case OBend:
+ goto out;
+ default:
+ snprint(msg, sizeof msg, " T%ud", code);
+ p = phex(p, e, msg, o, n);
+ break;
+ }
+ if(*ps != OBend)
+ p = seprint(p, e, " ");
+ }
+out:
+ m->p = p;
+ m->ps = ps;
+ return 0;
+}
+
+Proto dhcp =
+{
+ "dhcp",
+ p_compile,
+ p_filter,
+ p_seprint,
+ nil,
+ nil,
+ defaultframer,
+};
+
diff --git a/src/cmd/ip/snoopy/dump.c b/src/cmd/ip/snoopy/dump.c
new file mode 100755
index 00000000..5174e4fc
--- /dev/null
+++ b/src/cmd/ip/snoopy/dump.c
@@ -0,0 +1,92 @@
+#include <u.h>
+#include <libc.h>
+#include <ip.h>
+#include <ctype.h>
+#include "dat.h"
+#include "protos.h"
+
+static void
+p_compile(Filter *f)
+{
+ USED(f);
+}
+
+static int
+p_filter(Filter *f, Msg *m)
+{
+ USED(f);
+ USED(m);
+ return 0;
+}
+
+static char tohex[16] = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ 'a', 'b', 'c', 'd', 'e', 'f'
+};
+
+static int
+p_seprint(Msg *m)
+{
+ int c, i, n, isstring;
+ uchar *ps = m->ps;
+ char *p = m->p;
+ char *e = m->e;
+
+ n = m->pe - ps;
+ if(n > Nflag)
+ n = Nflag;
+
+ isstring = 1;
+ for(i = 0; i < n; i++){
+ c = ps[i];
+ if(!isprint(c) && !isspace(c)){
+ isstring = 0;
+ break;
+ }
+ }
+
+ if(isstring){
+ for(i = 0; i < n && p+1<e; i++){
+ c = ps[i];
+ switch(c){
+ case '\t':
+ *p++ = '\\';
+ *p++ = 't';
+ break;
+ case '\r':
+ *p++ = '\\';
+ *p++ = 'r';
+ break;
+ case '\n':
+ *p++ = '\\';
+ *p++ = 'n';
+ break;
+ default:
+ *p++ = c;
+ }
+ }
+ } else {
+ for(i = 0; i < n && p+1<e; i++){
+ c = ps[i];
+ *p++ = tohex[c>>4];
+ *p++ = tohex[c&0xf];
+ }
+ }
+
+ m->pr = nil;
+ m->p = p;
+ m->ps = ps;
+
+ return 0;
+}
+
+Proto dump =
+{
+ "dump",
+ p_compile,
+ p_filter,
+ p_seprint,
+ nil,
+ nil,
+ defaultframer,
+};
diff --git a/src/cmd/ip/snoopy/ether.c b/src/cmd/ip/snoopy/ether.c
new file mode 100755
index 00000000..62ec485d
--- /dev/null
+++ b/src/cmd/ip/snoopy/ether.c
@@ -0,0 +1,121 @@
+#include <u.h>
+#include <libc.h>
+#include <ip.h>
+#include "dat.h"
+#include "protos.h"
+
+typedef struct Hdr Hdr;
+struct Hdr {
+ uchar d[6];
+ uchar s[6];
+ uchar type[2];
+ char data[1500];
+};
+#define ETHERMINTU 60 /* minimum transmit size */
+#define ETHERMAXTU 1514 /* maximum transmit size */
+#define ETHERHDRSIZE 14 /* size of an ethernet header */
+
+static Mux p_mux[] =
+{
+ {"ip", 0x0800, } ,
+ {"arp", 0x0806, } ,
+ {"rarp", 0x0806, } ,
+ {"ip6", 0x86dd, } ,
+ {"pppoe_disc", 0x8863, },
+ {"pppoe_sess", 0x8864, },
+ {0}
+};
+
+enum
+{
+ Os, // source
+ Od, // destination
+ Oa, // source or destination
+ Ot, // type
+};
+
+static Field p_fields[] =
+{
+ {"s", Fether, Os, "source address", } ,
+ {"d", Fether, Od, "destination address", } ,
+ {"a", Fether, Oa, "source|destination address" } ,
+ {"sd", Fether, Oa, "source|destination address" } ,
+ {"t", Fnum, Ot, "type" } ,
+ {0}
+};
+
+static void
+p_compile(Filter *f)
+{
+ Mux *m;
+
+ if(f->op == '='){
+ compile_cmp(ether.name, f, p_fields);
+ return;
+ }
+ for(m = p_mux; m->name != nil; m++)
+ if(strcmp(f->s, m->name) == 0){
+ f->pr = m->pr;
+ f->ulv = m->val;
+ f->subop = Ot;
+ return;
+ }
+ sysfatal("unknown ethernet field or protocol: %s", f->s);
+}
+
+static int
+p_filter(Filter *f, Msg *m)
+{
+ Hdr *h;
+
+ if(m->pe - m->ps < ETHERHDRSIZE)
+ return 0;
+
+ h = (Hdr*)m->ps;
+ m->ps += ETHERHDRSIZE;
+
+ switch(f->subop){
+ case Os:
+ return !memcmp(h->s, f->a, 6);
+ case Od:
+ return !memcmp(h->d, f->a, 6);
+ case Oa:
+ return memcmp(h->s, f->a, 6) == 0 || memcmp(h->d, f->a, 6) == 0;
+ case Ot:
+ return NetS(h->type) == f->ulv;
+ }
+ return 0;
+}
+
+static int
+p_seprint(Msg *m)
+{
+ Hdr *h;
+ uint t;
+ int len;
+
+ len = m->pe - m->ps;
+ if(len < ETHERHDRSIZE)
+ return -1;
+
+ h = (Hdr*)m->ps;
+ m->ps += ETHERHDRSIZE;
+
+ t = NetS(h->type);
+ demux(p_mux, t, t, m, &dump);
+
+ m->p = seprint(m->p, m->e, "s=%E d=%E pr=%4.4ux ln=%d", h->s, h->d,
+ t, len);
+ return 0;
+}
+
+Proto ether =
+{
+ "ether",
+ p_compile,
+ p_filter,
+ p_seprint,
+ p_mux,
+ p_fields,
+ defaultframer
+};
diff --git a/src/cmd/ip/snoopy/gre.c b/src/cmd/ip/snoopy/gre.c
new file mode 100755
index 00000000..908d1553
--- /dev/null
+++ b/src/cmd/ip/snoopy/gre.c
@@ -0,0 +1,83 @@
+
+/* GRE flag bits */
+enum {
+ GRE_chksum = (1<<15),
+ GRE_routing = (1<<14),
+ GRE_key = (1<<13),
+ GRE_seq = (1<<12),
+ GRE_srcrt = (1<<11),
+ GRE_recur = (7<<8),
+ GRE_ack = (1<<7),
+ GRE_ver = 0x7,
+};
+
+/* GRE protocols */
+enum {
+ GRE_sna = 0x0004,
+ GRE_osi = 0x00fe,
+ GRE_pup = 0x0200,
+ GRE_xns = 0x0600,
+ GRE_ip = 0x0800,
+ GRE_chaos = 0x0804,
+ GRE_rfc826 = 0x0806,
+ GRE_frarp = 0x0808,
+ GRE_vines = 0x0bad,
+ GRE_vinesecho = 0x0bae,
+ GRE_vinesloop = 0x0baf,
+ GRE_decnetIV = 0x6003,
+ GRE_ppp = 0x880b,
+};
+
+int
+sprintgre(void *a, char *buf, int len)
+{
+ int flag, prot, chksum, offset, key, seq, ack;
+ int n;
+ uchar *p = a;
+
+ chksum = offset = key = seq = ack = 0;
+
+ flag = NetS(p);
+ prot = NetS(p+2);
+ p += 4; len -= 4;
+ if(flag & (GRE_chksum|GRE_routing)){
+ chksum = NetS(p);
+ offset = NetS(p+2);
+ p += 4; len -= 4;
+ }
+ if(flag&GRE_key){
+ key = NetL(p);
+ p += 4; len -= 4;
+ }
+ if(flag&GRE_seq){
+ seq = NetL(p);
+ p += 4; len -= 4;
+ }
+ if(flag&GRE_ack){
+ ack = NetL(p);
+ p += 4; len -= 4;
+ }
+ /* skip routing if present */
+ if(flag&GRE_routing) {
+ while(len >= 4 && (n=p[3]) != 0) {
+ len -= n;
+ p += n;
+ }
+ }
+
+ USED(offset);
+ USED(chksum);
+
+ n = sprint(buf, "GRE(f %4.4ux p %ux k %ux", flag, prot, key);
+ if(flag&GRE_seq)
+ n += sprint(buf+n, " s %ux", seq);
+ if(flag&GRE_ack)
+ n += sprint(buf+n, " a %ux", ack);
+ n += sprint(buf+n, " len = %d/%d) ", len, key>>16);
+ if(prot == GRE_ppp && len > 0)
+ n += sprintppp(p, buf+n, len);
+ else
+ n += sprintx(p, buf+n, len);
+
+ return n;
+}
diff --git a/src/cmd/ip/snoopy/hdlc.c b/src/cmd/ip/snoopy/hdlc.c
new file mode 100755
index 00000000..bed0c35a
--- /dev/null
+++ b/src/cmd/ip/snoopy/hdlc.c
@@ -0,0 +1,174 @@
+#include <u.h>
+#include <libc.h>
+#include <ip.h>
+#include "dat.h"
+#include "protos.h"
+
+enum {
+ HDLC_frame= 0x7e,
+ HDLC_esc= 0x7d,
+
+ /* PPP frame fields */
+ PPP_addr= 0xff,
+ PPP_ctl= 0x3,
+ PPP_initfcs= 0xffff,
+ PPP_goodfcs= 0xf0b8,
+};
+
+/*
+ * Calculate FCS - rfc 1331
+ */
+ushort fcstab[256] =
+{
+ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+ 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+ 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+ 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+ 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+ 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+ 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+ 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+ 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+ 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+ 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+ 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+ 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+ 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+ 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+ 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+ 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+ 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+ 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+ 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+ 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+ 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+ 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+ 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+ 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+ 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+ 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+ 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+ 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+ 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+ 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+ 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
+
+static uchar inbuf[16*1024];
+static int inlen;
+
+static Mux p_mux[] =
+{
+ {"ppp", (PPP_addr<<8)|PPP_ctl, } ,
+ {0}
+};
+
+enum
+{
+ Ot = 1
+};
+
+static void
+p_compile(Filter *f)
+{
+ Mux *m;
+
+ for(m = p_mux; m->name != nil; m++)
+ if(strcmp(f->s, m->name) == 0){
+ f->pr = m->pr;
+ f->ulv = m->val;
+ f->subop = Ot;
+ return;
+ }
+ sysfatal("unknown ethernet field or protocol: %s", f->s);
+}
+
+static int
+p_filter(Filter *f, Msg *m)
+{
+ ulong t;
+
+ if(m->pe-m->ps < 2)
+ return 0;
+
+ switch(f->subop){
+ case Ot:
+ t = (m->ps[0]<<8)|m->ps[1];
+ if(t != f->ulv)
+ return 0;
+ break;
+ }
+ return 1;
+}
+
+static int
+p_seprint(Msg *m)
+{
+ ulong t;
+
+ if(m->pe-m->ps < 2)
+ return -1;
+
+ t = (m->ps[0]<<8)|m->ps[1];
+ m->ps += 2;
+ demux(p_mux, t, t, m, &dump);
+
+ return 0;
+}
+
+static int
+p_framer(int fd, uchar *pkt, int pktlen)
+{
+ ushort fcs;
+ uchar *from, *efrom, *to, *eto;
+ int n;
+ ulong c;
+
+ eto = pkt+pktlen;
+ for(;;){
+ efrom = memchr(inbuf, HDLC_frame, inlen);
+ if(efrom == nil){
+ if(inlen >= sizeof(inbuf))
+ inlen = 0;
+ n = read(fd, inbuf+inlen, sizeof(inbuf)-inlen);
+ if(n <= 0)
+ break;
+ inlen += n;
+ continue;
+ }
+
+ /* checksum and unescape the frame */
+ fcs = PPP_initfcs;
+ to = pkt;
+ for(from = inbuf; from < efrom;){
+ c = *from++;
+ if(c == HDLC_esc)
+ c = (*from++) ^ 0x20;
+ if(to < eto)
+ *to++ = c;
+ fcs = (fcs >> 8) ^ fcstab[(fcs ^ c) & 0xff];
+ }
+
+ /* move down anything that's left */
+ inlen -= efrom+1-inbuf;
+ memmove(inbuf, efrom+1, inlen);
+
+ /* accept if this is a good packet */
+ if(fcs != PPP_goodfcs)
+ print("bad frame %ld %2.2ux %2.2ux!\n", to-pkt, pkt[0], pkt[1]);
+ else
+ return to-pkt;
+ }
+ return -1;
+}
+
+Proto hdlc =
+{
+ "hdlc",
+ p_compile,
+ p_filter,
+ p_seprint,
+ p_mux,
+ nil,
+ p_framer,
+};
diff --git a/src/cmd/ip/snoopy/icmp.c b/src/cmd/ip/snoopy/icmp.c
new file mode 100755
index 00000000..cf286d87
--- /dev/null
+++ b/src/cmd/ip/snoopy/icmp.c
@@ -0,0 +1,196 @@
+#include <u.h>
+#include <libc.h>
+#include <ip.h>
+#include "dat.h"
+#include "protos.h"
+
+typedef struct Hdr Hdr;
+struct Hdr
+{ uchar type;
+ uchar code;
+ uchar cksum[2]; /* Checksum */
+ uchar data[1];
+};
+
+enum
+{
+ ICMPLEN= 4,
+};
+
+enum
+{
+ Ot, /* type */
+ Op, /* next protocol */
+};
+
+static Field p_fields[] =
+{
+ {"t", Fnum, Ot, "type", } ,
+ {0}
+};
+
+enum
+{
+ EchoRep= 0,
+ Unreachable= 3,
+ SrcQuench= 4,
+ Redirect= 5,
+ EchoReq= 8,
+ TimeExceed= 11,
+ ParamProb= 12,
+ TSreq= 13,
+ TSrep= 14,
+ InfoReq= 15,
+ InfoRep= 16,
+};
+
+static Mux p_mux[] =
+{
+ {"ip", Unreachable, },
+ {"ip", SrcQuench, },
+ {"ip", Redirect, },
+ {"ip", TimeExceed, },
+ {"ip", ParamProb, },
+ {0},
+};
+
+char *icmpmsg[236] =
+{
+[EchoRep] "EchoRep",
+[Unreachable] "Unreachable",
+[SrcQuench] "SrcQuench",
+[Redirect] "Redirect",
+[EchoReq] "EchoReq",
+[TimeExceed] "TimeExceed",
+[ParamProb] "ParamProb",
+[TSreq] "TSreq",
+[TSrep] "TSrep",
+[InfoReq] "InfoReq",
+[InfoRep] "InfoRep",
+};
+
+static void
+p_compile(Filter *f)
+{
+ if(f->op == '='){
+ compile_cmp(udp.name, f, p_fields);
+ return;
+ }
+ if(strcmp(f->s, "ip") == 0){
+ f->pr = p_mux->pr;
+ f->subop = Op;
+ return;
+ }
+ sysfatal("unknown icmp field or protocol: %s", f->s);
+}
+
+static int
+p_filter(Filter *f, Msg *m)
+{
+ Hdr *h;
+
+ if(m->pe - m->ps < ICMPLEN)
+ return 0;
+
+ h = (Hdr*)m->ps;
+ m->ps += ICMPLEN;
+
+ switch(f->subop){
+ case Ot:
+ if(h->type == f->ulv)
+ return 1;
+ break;
+ case Op:
+ switch(h->type){
+ case Unreachable:
+ case TimeExceed:
+ case SrcQuench:
+ case Redirect:
+ case ParamProb:
+ m->ps += 4;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int
+p_seprint(Msg *m)
+{
+ Hdr *h;
+ char *tn;
+ char *p = m->p;
+ char *e = m->e;
+ ushort cksum2, cksum;
+
+ h = (Hdr*)m->ps;
+ m->ps += ICMPLEN;
+ m->pr = &dump;
+
+ if(m->pe - m->ps < ICMPLEN)
+ return -1;
+
+ tn = icmpmsg[h->type];
+ if(tn == nil)
+ p = seprint(p, e, "t=%ud c=%d ck=%4.4ux", h->type,
+ h->code, (ushort)NetS(h->cksum));
+ else
+ p = seprint(p, e, "t=%s c=%d ck=%4.4ux", tn,
+ h->code, (ushort)NetS(h->cksum));
+ if(Cflag){
+ cksum = NetS(h->cksum);
+ h->cksum[0] = 0;
+ h->cksum[1] = 0;
+ cksum2 = ~ptclbsum((uchar*)h, m->pe - m->ps + ICMPLEN) & 0xffff;
+ if(cksum != cksum2)
+ p = seprint(p,e, " !ck=%4.4ux", cksum2);
+ }
+ switch(h->type){
+ case EchoRep:
+ case EchoReq:
+ m->ps += 4;
+ p = seprint(p, e, " id=%ux seq=%ux",
+ NetS(h->data), NetS(h->data+2));
+ break;
+ case TSreq:
+ case TSrep:
+ m->ps += 12;
+ p = seprint(p, e, " orig=%ud rcv=%ux xmt=%ux",
+ NetL(h->data), NetL(h->data+4),
+ NetL(h->data+8));
+ m->pr = nil;
+ break;
+ case InfoReq:
+ case InfoRep:
+ break;
+ case Unreachable:
+ case TimeExceed:
+ case SrcQuench:
+ m->ps += 4;
+ m->pr = &ip;
+ break;
+ case Redirect:
+ m->ps += 4;
+ m->pr = &ip;
+ p = seprint(p, e, "gw=%V", h->data);
+ break;
+ case ParamProb:
+ m->ps += 4;
+ m->pr = &ip;
+ p = seprint(p, e, "ptr=%2.2ux", h->data[0]);
+ break;
+ }
+ m->p = p;
+ return 0;
+}
+
+Proto icmp =
+{
+ "icmp",
+ p_compile,
+ p_filter,
+ p_seprint,
+ p_mux,
+ p_fields,
+ defaultframer,
+};
diff --git a/src/cmd/ip/snoopy/icmp6.c b/src/cmd/ip/snoopy/icmp6.c
new file mode 100755
index 00000000..9c154f88
--- /dev/null
+++ b/src/cmd/ip/snoopy/icmp6.c
@@ -0,0 +1,428 @@
+#include <u.h>
+#include <libc.h>
+#include <ip.h>
+#include "dat.h"
+#include "protos.h"
+
+typedef struct Hdr Hdr;
+struct Hdr
+{ uchar type;
+ uchar code;
+ uchar cksum[2]; /* Checksum */
+ uchar data[1];
+};
+
+enum
+{
+ ICMP6LEN= 4,
+};
+
+enum
+{
+ Ot, /* type */
+ Op, /* next protocol */};
+
+static Field p_fields[] =
+{
+ {"t", Fnum, Ot, "type", } ,
+ {0}
+};
+
+enum
+{
+ // ICMPv6 types
+ EchoReply = 0,
+ UnreachableV6 = 1,
+ PacketTooBigV6 = 2,
+ TimeExceedV6 = 3,
+ ParamProblemV6 = 4,
+ Redirect = 5,
+ EchoRequest = 8,
+ TimeExceed = 11,
+ InParmProblem = 12,
+ Timestamp = 13,
+ TimestampReply = 14,
+ InfoRequest = 15,
+ InfoReply = 16,
+ AddrMaskRequest = 17,
+ AddrMaskReply = 18,
+ EchoRequestV6 = 128,
+ EchoReplyV6 = 129,
+ RouterSolicit = 133,
+ RouterAdvert = 134,
+ NbrSolicit = 135,
+ NbrAdvert = 136,
+ RedirectV6 = 137,
+
+ Maxtype6 = 137,
+};
+
+static Mux p_mux[] =
+{
+ {"ip6", UnreachableV6, },
+ {"ip6", RedirectV6, },
+ {"ip6", TimeExceedV6, },
+ {0},
+};
+
+char *icmpmsg6[256] =
+{
+[EchoReply] "EchoReply",
+[UnreachableV6] "UnreachableV6",
+[PacketTooBigV6] "PacketTooBigV6",
+[TimeExceedV6] "TimeExceedV6",
+[Redirect] "Redirect",
+[EchoRequest] "EchoRequest",
+[TimeExceed] "TimeExceed",
+[InParmProblem] "InParmProblem",
+[Timestamp] "Timestamp",
+[TimestampReply] "TimestampReply",
+[InfoRequest] "InfoRequest",
+[InfoReply] "InfoReply",
+[AddrMaskRequest] "AddrMaskRequest",
+[AddrMaskReply] "AddrMaskReply",
+[EchoRequestV6] "EchoRequestV6",
+[EchoReplyV6] "EchoReplyV6",
+[RouterSolicit] "RouterSolicit",
+[RouterAdvert] "RouterAdvert",
+[NbrSolicit] "NbrSolicit",
+[NbrAdvert] "NbrAdvert",
+[RedirectV6] "RedirectV6",
+};
+
+static char *unreachcode[] =
+{
+[0] "no route to destination",
+[1] "comm with destination administratively prohibited",
+[2] "icmp unreachable: unassigned error code (2)",
+[3] "address unreachable",
+[4] "port unreachable",
+[5] "icmp unreachable: unknown code",
+};
+
+static char *timexcode[] =
+{
+[0] "hop limit exc",
+[1] "reassmbl time exc",
+[2] "icmp time exc: unknown code",
+};
+
+static char *parpcode[] =
+{
+[0] "erroneous header field encountered",
+[1] "unrecognized Next Header type encountered",
+[2] "unrecognized IPv6 option encountered",
+[3] "icmp par prob: unknown code",
+};
+enum
+{
+ sll = 1,
+ tll = 2,
+ pref = 3,
+ redir = 4,
+ mtu = 5,
+};
+
+static char *icmp6opts[256] =
+{
+[0] "unknown opt",
+[1] "sll_addr",
+[2] "tll_addr",
+[3] "pref_opt",
+[4] "redirect",
+[5] "mtu_opt",
+};
+
+static void
+p_compile(Filter *f)
+{
+ if(f->op == '='){
+ compile_cmp(icmp6.name, f, p_fields);
+ return;
+ }
+ if(strcmp(f->s, "ip6") == 0){
+ f->pr = p_mux->pr;
+ f->subop = Op;
+ return;
+ }
+ sysfatal("unknown icmp field or protocol: %s", f->s);
+}
+
+static int
+p_filter(Filter *f, Msg *m)
+{
+ Hdr *h;
+
+ if(m->pe - m->ps < ICMP6LEN)
+ return 0;
+
+ h = (Hdr*)m->ps;
+ m->ps += ICMP6LEN;
+
+ switch(f->subop){
+
+ case Ot:
+ if(h->type == f->ulv)
+ return 1;
+ break;
+ case Op:
+ switch(h->type){
+ case UnreachableV6:
+ case RedirectV6:
+ case TimeExceedV6:
+ m->ps += 4;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static char*
+opt_seprint(Msg *m)
+{
+ int otype, osz, pktsz;
+ uchar *a;
+ char *p = m->p;
+ char *e = m->e;
+ char *opt;
+ char optbuf[12];
+
+ pktsz = m->pe - m->ps;
+ a = m->ps;
+ while (pktsz > 0) {
+ otype = *a;
+ opt = icmp6opts[otype];
+ if(opt == nil){
+ sprint(optbuf, "0x%ux", otype);
+ opt = optbuf;
+ }
+ osz = (*(a+1)) * 8;
+
+ switch (otype) {
+ default:
+ p = seprint(p, e, "\n option=%s ", opt);
+ m->pr = &dump;
+ return p;
+
+ case sll:
+ case tll:
+ if ((pktsz < osz) || (osz != 8)) {
+ p = seprint(p, e, "\n option=%s bad size=%d", opt, osz);
+ m->pr = &dump;
+ return p;
+ }
+ p = seprint(p, e, "\n option=%s maddr=%E", opt, a+2);
+ pktsz -= osz;
+ a += osz;
+ break;
+
+ case pref:
+ if ((pktsz < osz) || (osz != 32)) {
+ p = seprint(p, e, "\n option=%s: bad size=%d", opt, osz);
+ m->pr = &dump;
+ return p;
+ }
+
+ p = seprint(p, e, "\n option=%s pref=%I preflen=%3.3d lflag=%1.1d aflag=%1.1d unused1=%1.1d validlt=%d preflt=%d unused2=%1.1d",
+ opt,
+ a+16,
+ (int) (*(a+2)),
+ (*(a+3) & (1 << 7))!=0,
+ (*(a+3) & (1 << 6))!=0,
+ (*(a+3) & 63) != 0,
+ NetL(a+4),
+ NetL(a+8),
+ NetL(a+12)!=0);
+
+ pktsz -= osz;
+ a += osz;
+ break;
+
+ case redir:
+ if (pktsz < osz) {
+ p = seprint(p, e, "\n option=%s: bad size=%d", opt, osz);
+ m->pr = &dump;
+ return p;
+ }
+
+ p = seprint(p, e, "\n option=%s len %d", opt, osz);
+ a += osz;
+ m->ps = a;
+ return p;
+ break;
+
+ case mtu:
+ if ((pktsz < osz) || (osz != 8)) {
+ p = seprint(p, e, "\n option=%s: bad size=%d", opt, osz);
+ m->pr = &dump;
+ return p;
+ }
+
+ p = seprint(p, e, "\n option=%s unused=%1.1d mtu=%d", opt, NetL(a+2)!=0, NetL(a+4));
+ pktsz -= osz;
+ a += osz;
+ break;
+ }
+ }
+
+ m->ps = a;
+ return p;
+}
+
+static int
+p_seprint(Msg *m)
+{
+ Hdr *h;
+ char *tn;
+ char *p = m->p;
+ char *e = m->e;
+ int i;
+ uchar *a;
+// ushort cksum2, cksum;
+
+ h = (Hdr*)m->ps;
+ m->ps += ICMP6LEN;
+ m->pr = &dump;
+ a = m->ps;
+
+ if(m->pe - m->ps < ICMP6LEN)
+ return -1;
+
+ tn = icmpmsg6[h->type];
+ if(tn == nil)
+ p = seprint(p, e, "t=%ud c=%d ck=%4.4ux", h->type,
+ h->code, (ushort)NetS(h->cksum));
+ else
+ p = seprint(p, e, "t=%s c=%d ck=%4.4ux", tn,
+ h->code, (ushort)NetS(h->cksum));
+
+ /*
+ if(Cflag){
+ cksum = NetS(h->cksum);
+ h->cksum[0] = 0;
+ h->cksum[1] = 0;
+ cksum2 = ~ptclbsum((uchar*)h, m->pe - m->ps + ICMP6LEN) & 0xffff;
+ if(cksum != cksum2)
+ p = seprint(p,e, " !ck=%4.4ux", cksum2);
+ }
+ */
+
+ switch(h->type){
+
+ case UnreachableV6:
+ m->ps += 4;
+ m->pr = &ip6;
+ if (h->code >= nelem(unreachcode))
+ i = nelem(unreachcode)-1;
+ else
+ i = h->code;
+ p = seprint(p, e, " code=%s unused=%1.1d ", unreachcode[i], NetL(a)!=0);
+ break;
+
+ case PacketTooBigV6:
+ m->ps += 4;
+ m->pr = &ip6;
+ p = seprint(p, e, " mtu=%4.4d ", NetL(a));
+ break;
+
+ case TimeExceedV6:
+ m->ps += 4;
+ m->pr = &ip6;
+ if (h->code >= nelem(timexcode))
+ i = nelem(timexcode)-1;
+ else
+ i = h->code;
+ p = seprint(p, e, " code=%s unused=%1.1d ", timexcode[i], NetL(a)!=0);
+ break;
+
+ case ParamProblemV6:
+ m->ps += 4;
+ m->pr = &ip6;
+ if (h->code > nelem(parpcode))
+ i = nelem(parpcode)-1;
+ else
+ i = h->code;
+ p = seprint(p, e, " code=%s ptr=%2.2ux", parpcode[i], h->data[0]);
+ break;
+
+ case EchoReplyV6:
+ case EchoRequestV6:
+ m->ps += 4;
+ p = seprint(p, e, " id=%ux seq=%ux",
+ NetS(h->data), NetS(h->data+2));
+ break;
+
+ case RouterSolicit:
+ m->ps += 4;
+ m->pr = nil;
+ m->p = seprint(p, e, " unused=%1.1d ", NetL(a)!=0);
+ p = opt_seprint(m);
+ break;
+
+ case RouterAdvert:
+ m->ps += 12;
+ m->pr = nil;
+ m->p = seprint(p, e, " hoplim=%3.3d mflag=%1.1d oflag=%1.1d unused=%1.1d routerlt=%8.8d reachtime=%d rxmtimer=%d",
+ (int) *a,
+ (*(a+1) & (1 << 7)) != 0,
+ (*(a+1) & (1 << 6)) != 0,
+ (*(a+1) & 63) != 0,
+ NetS(a+2),
+ NetL(a+4),
+ NetL(a+8));
+ p = opt_seprint(m);
+ break;
+
+ case NbrSolicit:
+ m->ps += 20;
+ m->pr = nil;
+ m->p = seprint(p, e, " unused=%1.1d targ %I", NetL(a)!=0, a+4);
+ p = opt_seprint(m);
+ break;
+
+ case NbrAdvert:
+ m->ps += 20;
+ m->pr = nil;
+ m->p = seprint(p, e, " rflag=%1.1d sflag=%1.1d oflag=%1.1d targ=%I",
+ (*a & (1 << 7)) != 0,
+ (*a & (1 << 6)) != 0,
+ (*a & (1 << 5)) != 0,
+ a+4);
+ p = opt_seprint(m);
+ break;
+
+ case RedirectV6:
+ m->ps += 36;
+ m->pr = &ip6;
+ m->p = seprint(p, e, " unused=%1.1d targ=%I dest=%I", NetL(a)!=0, a+4, a+20);
+ p = opt_seprint(m);
+ break;
+
+ case Timestamp:
+ case TimestampReply:
+ m->ps += 12;
+ p = seprint(p, e, " orig=%ud rcv=%ux xmt=%ux",
+ NetL(h->data), NetL(h->data+4),
+ NetL(h->data+8));
+ m->pr = nil;
+ break;
+
+ case InfoRequest:
+ case InfoReply:
+ break;
+
+ }
+ m->p = p;
+ return 0;
+}
+
+Proto icmp6 =
+{
+ "icmp6",
+ p_compile,
+ p_filter,
+ p_seprint,
+ p_mux,
+ p_fields,
+ defaultframer,
+};
diff --git a/src/cmd/ip/snoopy/il.c b/src/cmd/ip/snoopy/il.c
new file mode 100755
index 00000000..d75a80cd
--- /dev/null
+++ b/src/cmd/ip/snoopy/il.c
@@ -0,0 +1,146 @@
+#include <u.h>
+#include <libc.h>
+#include <ip.h>
+#include "dat.h"
+#include "protos.h"
+
+typedef struct Hdr Hdr;
+struct Hdr
+{
+ uchar sum[2]; /* Checksum including header */
+ uchar len[2]; /* Packet length */
+ uchar type; /* Packet type */
+ uchar spec; /* Special */
+ uchar sport[2]; /* Src port */
+ uchar dport[2]; /* Dst port */
+ uchar id[4]; /* Sequence id */
+ uchar ack[4]; /* Acked sequence */
+};
+
+enum
+{
+ ILLEN= 18,
+};
+
+enum
+{
+ Os,
+ Od,
+ Osd,
+};
+
+static Field p_fields[] =
+{
+ {"s", Fnum, Os, "source port", } ,
+ {"d", Fnum, Od, "dest port", } ,
+ {"a", Fnum, Osd, "source/dest port", } ,
+ {"sd", Fnum, Osd, "source/dest port", } ,
+ {0}
+};
+
+static Mux p_mux[] =
+{
+ {"ninep", 17007, }, /* exportfs */
+ {"ninep", 17008, }, /* 9fs */
+ {"ninep", 17005, }, /* ocpu */
+ {"ninep", 17010, }, /* ncpu */
+ {"ninep", 17013, }, /* cpu */
+ {0},
+};
+
+static void
+p_compile(Filter *f)
+{
+ Mux *m;
+
+ if(f->op == '='){
+ compile_cmp(udp.name, f, p_fields);
+ return;
+ }
+ for(m = p_mux; m->name != nil; m++)
+ if(strcmp(f->s, m->name) == 0){
+ f->pr = m->pr;
+ f->ulv = m->val;
+ f->subop = Osd;
+ return;
+ }
+ sysfatal("unknown il field or protocol: %s", f->s);
+}
+
+static int
+p_filter(Filter *f, Msg *m)
+{
+ Hdr *h;
+
+ if(m->pe - m->ps < ILLEN)
+ return 0;
+ h = (Hdr*)m->ps;
+ m->ps += ILLEN;
+
+ switch(f->subop){
+ case Os:
+ return NetS(h->sport) == f->ulv;
+ case Od:
+ return NetS(h->dport) == f->ulv;
+ case Osd:
+ return NetS(h->sport) == f->ulv || NetS(h->dport) == f->ulv;
+ }
+ return 0;
+}
+
+char *pktnames[] =
+{
+ "Sync",
+ "Data",
+ "Dataquery",
+ "Ack",
+ "Query",
+ "State",
+ "Close"
+};
+
+static char*
+pkttype(int t)
+{
+ static char b[10];
+
+ if(t > 6){
+ sprint(b, "%d", t);
+ return b;
+ }
+ return pktnames[t];
+}
+
+static int
+p_seprint(Msg *m)
+{
+ Hdr *h;
+ int dport, sport;
+
+ if(m->pe - m->ps < ILLEN)
+ return -1;
+ h = (Hdr*)m->ps;
+ m->ps += ILLEN;
+
+ dport = NetS(h->dport);
+ sport = NetS(h->sport);
+ demux(p_mux, sport, dport, m, &dump);
+
+ m->p = seprint(m->p, m->e, "s=%d d=%d t=%s id=%lud ack=%lud spec=%d ck=%4.4ux ln=%d",
+ sport, dport, pkttype(h->type),
+ (ulong)NetL(h->id), (ulong)NetL(h->ack),
+ h->spec,
+ NetS(h->sum), NetS(h->len));
+ return 0;
+}
+
+Proto il =
+{
+ "il",
+ p_compile,
+ p_filter,
+ p_seprint,
+ p_mux,
+ p_fields,
+ defaultframer,
+};
diff --git a/src/cmd/ip/snoopy/ip.c b/src/cmd/ip/snoopy/ip.c
new file mode 100755
index 00000000..e321e2fe
--- /dev/null
+++ b/src/cmd/ip/snoopy/ip.c
@@ -0,0 +1,236 @@
+#include <u.h>
+#include <libc.h>
+#include <ip.h>
+#include "dat.h"
+#include "protos.h"
+
+typedef struct Hdr Hdr;
+struct Hdr
+{
+ uchar vihl; /* Version and header length */
+ uchar tos; /* Type of service */
+ uchar length[2]; /* packet length */
+ uchar id[2]; /* ip->identification */
+ uchar frag[2]; /* Fragment information */
+ uchar ttl; /* Time to live */
+ uchar proto; /* Protocol */
+ uchar cksum[2]; /* Header checksum */
+ uchar src[4]; /* IP source */
+ uchar dst[4]; /* IP destination */
+};
+
+enum
+{
+ IPHDR = 20, /* sizeof(Iphdr) */
+ IP_VER = 0x40, /* Using IP version 4 */
+ IP_DF = 0x4000, /* Don't fragment */
+ IP_MF = 0x2000, /* More fragments */
+};
+
+static Mux p_mux[] =
+{
+ { "icmp", 1, },
+ { "igmp", 2, },
+ { "ggp", 3, },
+ { "ip", 4, },
+ { "st", 5, },
+ { "tcp", 6, },
+ { "ucl", 7, },
+ { "egp", 8, },
+ { "igp", 9, },
+ { "bbn-rcc-mon", 10, },
+ { "nvp-ii", 11, },
+ { "pup", 12, },
+ { "argus", 13, },
+ { "emcon", 14, },
+ { "xnet", 15, },
+ { "chaos", 16, },
+ { "udp", 17, },
+ { "mux", 18, },
+ { "dcn-meas", 19, },
+ { "hmp", 20, },
+ { "prm", 21, },
+ { "xns-idp", 22, },
+ { "trunk-1", 23, },
+ { "trunk-2", 24, },
+ { "leaf-1", 25, },
+ { "leaf-2", 26, },
+ { "rdp", 27, },
+ { "irtp", 28, },
+ { "iso-tp4", 29, },
+ { "netblt", 30, },
+ { "mfe-nsp", 31, },
+ { "merit-inp", 32, },
+ { "sep", 33, },
+ { "3pc", 34, },
+ { "idpr", 35, },
+ { "xtp", 36, },
+ { "ddp", 37, },
+ { "idpr-cmtp", 38, },
+ { "tp++", 39, },
+ { "il", 40, },
+ { "sip", 41, },
+ { "sdrp", 42, },
+ { "sip-sr", 43, },
+ { "sip-frag", 44, },
+ { "idrp", 45, },
+ { "rsvp", 46, },
+ { "gre", 47, },
+ { "mhrp", 48, },
+ { "bna", 49, },
+ { "sipp-esp", 50, },
+ { "sipp-ah", 51, },
+ { "i-nlsp", 52, },
+ { "swipe", 53, },
+ { "nhrp", 54, },
+ { "any", 61, },
+ { "cftp", 62, },
+ { "any", 63, },
+ { "sat-expak", 64, },
+ { "kryptolan", 65, },
+ { "rvd", 66, },
+ { "ippc", 67, },
+ { "any", 68, },
+ { "sat-mon", 69, },
+ { "visa", 70, },
+ { "ipcv", 71, },
+ { "cpnx", 72, },
+ { "cphb", 73, },
+ { "wsn", 74, },
+ { "pvp", 75, },
+ { "br-sat-mon", 76, },
+ { "sun-nd", 77, },
+ { "wb-mon", 78, },
+ { "wb-expak", 79, },
+ { "iso-ip", 80, },
+ { "vmtp", 81, },
+ { "secure-vmtp", 82, },
+ { "vines", 83, },
+ { "ttp", 84, },
+ { "nsfnet-igp", 85, },
+ { "dgp", 86, },
+ { "tcf", 87, },
+ { "igrp", 88, },
+ { "ospf", 89, },
+ { "sprite-rpc", 90, },
+ { "larp", 91, },
+ { "mtp", 92, },
+ { "ax.25", 93, },
+ { "ipip", 94, },
+ { "micp", 95, },
+ { "scc-sp", 96, },
+ { "etherip", 97, },
+ { "encap", 98, },
+ { "any", 99, },
+ { "gmtp", 100, },
+ { "rudp", 254, },
+ { 0 }
+};
+
+enum
+{
+ Os, // source
+ Od, // destination
+ Osd, // source or destination
+ Ot, // type
+};
+
+static Field p_fields[] =
+{
+ {"s", Fv4ip, Os, "source address", } ,
+ {"d", Fv4ip, Od, "destination address", } ,
+ {"a", Fv4ip, Osd, "source|destination address",} ,
+ {"sd", Fv4ip, Osd, "source|destination address",} ,
+ {"t", Fnum, Ot, "sub protocol number", } ,
+ {0}
+};
+
+static void
+p_compile(Filter *f)
+{
+ Mux *m;
+
+ if(f->op == '='){
+ compile_cmp(ip.name, f, p_fields);
+ return;
+ }
+ for(m = p_mux; m->name != nil; m++)
+ if(strcmp(f->s, m->name) == 0){
+ f->pr = m->pr;
+ f->ulv = m->val;
+ f->subop = Ot;
+ return;
+ }
+ sysfatal("unknown ip field or protocol: %s", f->s);
+}
+
+static int
+p_filter(Filter *f, Msg *m)
+{
+ Hdr *h;
+
+ if(m->pe - m->ps < IPHDR)
+ return 0;
+
+ h = (Hdr*)m->ps;
+ m->ps += ((h->vihl&0xf)<<2);
+
+ switch(f->subop){
+ case Os:
+ return NetL(h->src) == f->ulv;
+ case Od:
+ return NetL(h->dst) == f->ulv;
+ case Osd:
+ return NetL(h->src) == f->ulv || NetL(h->dst) == f->ulv;
+ case Ot:
+ return h->proto == f->ulv;
+ }
+ return 0;
+}
+
+static int
+p_seprint(Msg *m)
+{
+ Hdr *h;
+ int f;
+ int len;
+
+ if(m->pe - m->ps < IPHDR)
+ return -1;
+ h = (Hdr*)m->ps;
+
+ /* next protocol, just dump unless this is the first fragment */
+ m->pr = &dump;
+ f = NetS(h->frag);
+ if((f & ~(IP_DF|IP_MF)) == 0)
+ demux(p_mux, h->proto, h->proto, m, &dump);
+
+ /* truncate the message if there's extra */
+ len = NetS(h->length);
+ if(len < m->pe - m->ps)
+ m->pe = m->ps + len;
+
+ /* next header */
+ m->ps += ((h->vihl&0xf)<<2);
+
+ m->p = seprint(m->p, m->e, "s=%V d=%V id=%4.4ux frag=%4.4ux ttl=%3d pr=%d ln=%d",
+ h->src, h->dst,
+ NetS(h->id),
+ NetS(h->frag),
+ h->ttl,
+ h->proto,
+ NetS(h->length)
+ );
+ return 0;
+}
+
+Proto ip =
+{
+ "ip",
+ p_compile,
+ p_filter,
+ p_seprint,
+ p_mux,
+ p_fields,
+ defaultframer,
+};
diff --git a/src/cmd/ip/snoopy/ip6.c b/src/cmd/ip/snoopy/ip6.c
new file mode 100755
index 00000000..7fdc9679
--- /dev/null
+++ b/src/cmd/ip/snoopy/ip6.c
@@ -0,0 +1,309 @@
+#include <u.h>
+#include <libc.h>
+#include <ip.h>
+#include "dat.h"
+#include "protos.h"
+
+typedef struct Hdr Hdr;
+struct Hdr
+{
+ uchar vcf[4]; /* Version and header length */
+ uchar length[2]; /* packet length */
+ uchar proto; /* Protocol */
+ uchar ttl; /* Time to live */
+ uchar src[IPaddrlen]; /* IP source */
+ uchar dst[IPaddrlen]; /* IP destination */
+};
+
+enum
+{
+ IP6HDR = 40, /* sizeof(Iphdr) */
+ IP_VER = 0x60, /* Using IP version 4 */
+ HBH_HDR = 0,
+ ROUT_HDR = 43,
+ FRAG_HDR = 44,
+ FRAG_HSZ = 8, /* in bytes */
+ DEST_HDR = 60,
+};
+
+static Mux p_mux[] =
+{
+ { "igmp", 2, },
+ { "ggp", 3, },
+ { "ip", 4, },
+ { "st", 5, },
+ { "tcp", 6, },
+ { "ucl", 7, },
+ { "egp", 8, },
+ { "igp", 9, },
+ { "bbn-rcc-mon", 10, },
+ { "nvp-ii", 11, },
+ { "pup", 12, },
+ { "argus", 13, },
+ { "emcon", 14, },
+ { "xnet", 15, },
+ { "chaos", 16, },
+ { "udp", 17, },
+ { "mux", 18, },
+ { "dcn-meas", 19, },
+ { "hmp", 20, },
+ { "prm", 21, },
+ { "xns-idp", 22, },
+ { "trunk-1", 23, },
+ { "trunk-2", 24, },
+ { "leaf-1", 25, },
+ { "leaf-2", 26, },
+ { "rdp", 27, },
+ { "irtp", 28, },
+ { "iso-tp4", 29, },
+ { "netblt", 30, },
+ { "mfe-nsp", 31, },
+ { "merit-inp", 32, },
+ { "sep", 33, },
+ { "3pc", 34, },
+ { "idpr", 35, },
+ { "xtp", 36, },
+ { "ddp", 37, },
+ { "idpr-cmtp", 38, },
+ { "tp++", 39, },
+ { "il", 40, },
+ { "sip", 41, },
+ { "sdrp", 42, },
+ { "idrp", 45, },
+ { "rsvp", 46, },
+ { "gre", 47, },
+ { "mhrp", 48, },
+ { "bna", 49, },
+ { "sipp-esp", 50, },
+ { "sipp-ah", 51, },
+ { "i-nlsp", 52, },
+ { "swipe", 53, },
+ { "nhrp", 54, },
+ { "icmp6", 58, },
+ { "any", 61, },
+ { "cftp", 62, },
+ { "any", 63, },
+ { "sat-expak", 64, },
+ { "kryptolan", 65, },
+ { "rvd", 66, },
+ { "ippc", 67, },
+ { "any", 68, },
+ { "sat-mon", 69, },
+ { "visa", 70, },
+ { "ipcv", 71, },
+ { "cpnx", 72, },
+ { "cphb", 73, },
+ { "wsn", 74, },
+ { "pvp", 75, },
+ { "br-sat-mon", 76, },
+ { "sun-nd", 77, },
+ { "wb-mon", 78, },
+ { "wb-expak", 79, },
+ { "iso-ip", 80, },
+ { "vmtp", 81, },
+ { "secure-vmtp", 82, },
+ { "vines", 83, },
+ { "ttp", 84, },
+ { "nsfnet-igp", 85, },
+ { "dgp", 86, },
+ { "tcf", 87, },
+ { "igrp", 88, },
+ { "ospf", 89, },
+ { "sprite-rpc", 90, },
+ { "larp", 91, },
+ { "mtp", 92, },
+ { "ax.25", 93, },
+ { "ipip", 94, },
+ { "micp", 95, },
+ { "scc-sp", 96, },
+ { "etherip", 97, },
+ { "encap", 98, },
+ { "any", 99, },
+ { "gmtp", 100, },
+ { "rudp", 254, },
+ { 0 }
+};
+
+enum
+{
+ Os, // source
+ Od, // destination
+ Osd, // source or destination
+ Ot, // type
+};
+
+static Field p_fields[] =
+{
+ {"s", Fv6ip, Os, "source address", } ,
+ {"d", Fv6ip, Od, "destination address", } ,
+ {"a", Fv6ip, Osd, "source|destination address",} ,
+ {"t", Fnum, Ot, "sub protocol number", } ,
+ {0}
+};
+
+static void
+p_compile(Filter *f)
+{
+ Mux *m;
+
+ if(f->op == '='){
+ compile_cmp(ip6.name, f, p_fields);
+ return;
+ }
+ for(m = p_mux; m->name != nil; m++)
+ if(strcmp(f->s, m->name) == 0){
+ f->pr = m->pr;
+ f->ulv = m->val;
+ f->subop = Ot;
+ return;
+ }
+ sysfatal("unknown ip6 field or protocol: %s", f->s);
+}
+
+static int
+v6hdrlen(Hdr *h)
+{
+ int plen, len = IP6HDR;
+ int pktlen = IP6HDR + NetS(h->length);
+ uchar nexthdr = h->proto;
+ uchar *pkt = (uchar*) h;
+
+ pkt += len;
+ plen = len;
+
+ while ( (nexthdr == HBH_HDR) || (nexthdr == ROUT_HDR) ||
+ (nexthdr == FRAG_HDR) || (nexthdr == DEST_HDR) ) {
+
+ if (nexthdr == FRAG_HDR)
+ len = FRAG_HSZ;
+ else
+ len = ( ((int) *(pkt+1)) + 1) * 8;
+
+ if (plen + len > pktlen)
+ return -1;
+
+ pkt += len;
+ nexthdr = *pkt;
+ plen += len;
+ }
+ return plen;
+}
+
+static int
+p_filter(Filter *f, Msg *m)
+{
+ Hdr *h;
+ int hlen;
+
+ if(m->pe - m->ps < IP6HDR)
+ return 0;
+
+ h = (Hdr*)m->ps;
+
+ if ((hlen = v6hdrlen(h)) < 0)
+ return 0;
+ else
+ m->ps += hlen;
+ switch(f->subop){
+ case Os:
+ return !memcmp(h->src, f->a, IPaddrlen);
+ case Od:
+ return !memcmp(h->dst, f->a, IPaddrlen);
+ case Osd:
+ return !memcmp(h->src, f->a, IPaddrlen) || !memcmp(h->dst, f->a, IPaddrlen);
+ case Ot:
+ return h->proto == f->ulv;
+ }
+ return 0;
+}
+
+static int
+v6hdr_seprint(Msg *m)
+{
+ int len = IP6HDR;
+ uchar *pkt = m->ps;
+ Hdr *h = (Hdr *) pkt;
+ int pktlen = IP6HDR + NetS(h->length);
+ uchar nexthdr = h->proto;
+ int plen;
+
+ pkt += len;
+ plen = len;
+
+ while ( (nexthdr == HBH_HDR) || (nexthdr == ROUT_HDR) ||
+ (nexthdr == FRAG_HDR) || (nexthdr == DEST_HDR) ) {
+
+ switch (nexthdr) {
+ case FRAG_HDR:
+ m->p = seprint(m->p, m->e, "\n xthdr=frag id=%d offset=%d pr=%d more=%d res1=%d res2=%d",
+ NetL(pkt+4),
+ NetS(pkt+2) & ~7,
+ (int) (*pkt),
+ (int) (*(pkt+3) & 0x1),
+ (int) *(pkt+1),
+ (int) (*(pkt+3) & 0x6)
+ );
+ len = FRAG_HSZ;
+ break;
+
+ case HBH_HDR:
+ case ROUT_HDR:
+ case DEST_HDR:
+ len = ( ((int) *(pkt+1)) + 1) * 8;
+ break;
+ }
+
+ if (plen + len > pktlen) {
+ m->p = seprint(m->p, m->e, "bad pkt");
+ m->pr = &dump;
+ return -1;
+ }
+ plen += len;
+ pkt += len;
+ nexthdr = *pkt;
+ }
+
+ m->ps = pkt;
+ return 1;
+
+}
+
+static int
+p_seprint(Msg *m)
+{
+ Hdr *h;
+ int len;
+
+ if(m->pe - m->ps < IP6HDR)
+ return -1;
+ h = (Hdr*)m->ps;
+
+ demux(p_mux, h->proto, h->proto, m, &dump);
+
+ /* truncate the message if there's extra */
+ len = NetS(h->length) + IP6HDR;
+ if(len < m->pe - m->ps)
+ m->pe = m->ps + len;
+
+ m->p = seprint(m->p, m->e, "s=%I d=%I ttl=%3d pr=%d ln=%d",
+ h->src, h->dst,
+ h->ttl,
+ h->proto,
+ NetS(h->length)
+ );
+
+ v6hdr_seprint(m);
+
+ return 0;
+}
+
+Proto ip6 =
+{
+ "ip6",
+ p_compile,
+ p_filter,
+ p_seprint,
+ p_mux,
+ p_fields,
+ defaultframer,
+};
diff --git a/src/cmd/ip/snoopy/main.c b/src/cmd/ip/snoopy/main.c
new file mode 100755
index 00000000..73c1e27b
--- /dev/null
+++ b/src/cmd/ip/snoopy/main.c
@@ -0,0 +1,841 @@
+#include <u.h>
+#include <libc.h>
+#include <ip.h>
+#include <bio.h>
+#include <fcall.h>
+#include <libsec.h>
+#include "dat.h"
+#include "protos.h"
+#include "y.tab.h"
+
+int Cflag;
+int pflag;
+int Nflag;
+int sflag;
+int tiflag;
+int toflag;
+
+char *prom = "promiscuous";
+
+enum
+{
+ Pktlen= 64*1024,
+ Blen= 16*1024,
+};
+
+Filter *filter;
+Proto *root;
+Biobuf out;
+vlong starttime, pkttime;
+int pcap;
+
+int filterpkt(Filter *f, uchar *ps, uchar *pe, Proto *pr, int);
+void printpkt(char *p, char *e, uchar *ps, uchar *pe);
+void mkprotograph(void);
+Proto* findproto(char *name);
+Filter* compile(Filter *f);
+void printfilter(Filter *f, char *tag);
+void printhelp(void);
+void tracepkt(uchar*, int);
+void pcaphdr(void);
+
+void
+usage(void)
+{
+ fprint(2, "usage: %s [-std?] [-c] [-N n] [-f filter] [-h first-header] path", argv0);
+ exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+ uchar *pkt;
+ char *buf, *file, *p, *e;
+ int fd;
+ int n;
+
+ Binit(&out, 1, OWRITE);
+
+ fmtinstall('E', eipfmt);
+ fmtinstall('V', eipfmt);
+ fmtinstall('I', eipfmt);
+ fmtinstall('H', encodefmt);
+ fmtinstall('F', fcallfmt);
+
+ pkt = malloc(Pktlen+16);
+ pkt += 16;
+ buf = malloc(Blen);
+ e = buf+Blen-1;
+
+ pflag = 1;
+ Nflag = 32;
+ sflag = 0;
+
+ mkprotograph();
+
+ ARGBEGIN{
+ case '?':
+ printhelp();
+ exits(0);
+ break;
+ case 'N':
+ p = ARGF();
+ if(p == nil)
+ usage();
+ Nflag = atoi(p);
+ break;
+ case 'f':
+ p = ARGF();
+ if(p == nil)
+ usage();
+ yyinit(p);
+ yyparse();
+ break;
+ case 's':
+ sflag = 1;
+ break;
+ case 'h':
+ p = ARGF();
+ if(p == nil)
+ usage();
+ root = findproto(p);
+ if(root == nil)
+ sysfatal("unknown protocol: %s", p);
+ break;
+ case 'd':
+ toflag = 1;
+ break;
+ case 'D':
+ toflag = 1;
+ pcap = 1;
+ break;
+ case 't':
+ tiflag = 1;
+ break;
+ case 'C':
+ Cflag = 1;
+ break;
+ case 'p':
+ pflag = 0;
+ break;
+ }ARGEND;
+
+ if(pcap)
+ pcaphdr();
+
+ if(argc == 0)
+ file = nil;
+ else
+ file = argv[0];
+
+ if(tiflag){
+ fd = open(file, OREAD);
+ if(fd < 0)
+ sysfatal("opening %s: %r", file);
+ }else{
+ fd = opendevice(file, pflag);
+ if(fd < 0)
+ sysfatal("opening device %s: %r", file ? file : "(all)");
+ }
+ if(root == nil)
+ root = &ether;
+ filter = compile(filter);
+
+ if(tiflag){
+ /* read a trace file */
+ for(;;){
+ n = read(fd, pkt, 10);
+ if(n != 10)
+ break;
+ pkttime = NetL(pkt+2);
+ pkttime = (pkttime<<32) | NetL(pkt+6);
+ if(starttime == 0LL)
+ starttime = pkttime;
+ n = NetS(pkt);
+ if(readn(fd, pkt, n) != n)
+ break;
+ if(filterpkt(filter, pkt, pkt+n, root, 1))
+ if(toflag)
+ tracepkt(pkt, n);
+ else
+ printpkt(buf, e, pkt, pkt+n);
+ }
+ } else {
+ /* read a real time stream */
+ starttime = nsec();
+ for(;;){
+ n = root->framer(fd, pkt, Pktlen);
+ if(n <= 0)
+ break;
+ pkttime = nsec();
+ if(filterpkt(filter, pkt, pkt+n, root, 1))
+ if(toflag)
+ tracepkt(pkt, n);
+ else
+ printpkt(buf, e, pkt, pkt+n);
+ }
+ }
+}
+
+/* create a new filter node */
+Filter*
+newfilter(void)
+{
+ Filter *f;
+
+ f = mallocz(sizeof(*f), 1);
+ if(f == nil)
+ sysfatal("newfilter: %r");
+ return f;
+}
+
+/*
+ * apply filter to packet
+ */
+int
+_filterpkt(Filter *f, Msg *m)
+{
+ Msg ma;
+
+ if(f == nil)
+ return 1;
+
+ switch(f->op){
+ case '!':
+ return !_filterpkt(f->l, m);
+ case LAND:
+ ma = *m;
+ return _filterpkt(f->l, &ma) && _filterpkt(f->r, m);
+ case LOR:
+ ma = *m;
+ return _filterpkt(f->l, &ma) || _filterpkt(f->r, m);
+ case WORD:
+ if(m->needroot){
+ if(m->pr != f->pr)
+ return 0;
+ m->needroot = 0;
+ }else{
+ if(m->pr != nil && !(m->pr->filter)(f, m))
+ return 0;
+ }
+ if(f->l == nil)
+ return 1;
+ m->pr = f->pr;
+ return _filterpkt(f->l, m);
+ }
+ sysfatal("internal error: filterpkt op: %d", f->op);
+ return 0;
+}
+int
+filterpkt(Filter *f, uchar *ps, uchar *pe, Proto *pr, int needroot)
+{
+ Msg m;
+
+ if(f == nil)
+ return 1;
+
+ m.needroot = needroot;
+ m.ps = ps;
+ m.pe = pe;
+ m.pr = pr;
+ return _filterpkt(f, &m);
+}
+
+/*
+ * from the Unix world
+ */
+#define PCAP_VERSION_MAJOR 2
+#define PCAP_VERSION_MINOR 4
+#define TCPDUMP_MAGIC 0xa1b2c3d4
+
+struct pcap_file_header {
+ ulong magic;
+ ushort version_major;
+ ushort version_minor;
+ long thiszone; /* gmt to local correction */
+ ulong sigfigs; /* accuracy of timestamps */
+ ulong snaplen; /* max length saved portion of each pkt */
+ ulong linktype; /* data link type (DLT_*) */
+};
+
+struct pcap_pkthdr {
+ uvlong ts; /* time stamp */
+ ulong caplen; /* length of portion present */
+ ulong len; /* length this packet (off wire) */
+};
+
+/*
+ * pcap trace header
+ */
+void
+pcaphdr(void)
+{
+ struct pcap_file_header hdr;
+
+ hdr.magic = TCPDUMP_MAGIC;
+ hdr.version_major = PCAP_VERSION_MAJOR;
+ hdr.version_minor = PCAP_VERSION_MINOR;
+
+ hdr.thiszone = 0;
+ hdr.snaplen = 1500;
+ hdr.sigfigs = 0;
+ hdr.linktype = 1;
+
+ write(1, &hdr, sizeof(hdr));
+}
+
+/*
+ * write out a packet trace
+ */
+void
+tracepkt(uchar *ps, int len)
+{
+ struct pcap_pkthdr *goo;
+
+ if(pcap){
+ goo = (struct pcap_pkthdr*)(ps-16);
+ goo->ts = pkttime;
+ goo->caplen = len;
+ goo->len = len;
+ write(1, goo, len+16);
+ } else {
+ hnputs(ps-10, len);
+ hnputl(ps-8, pkttime>>32);
+ hnputl(ps-4, pkttime);
+ write(1, ps-10, len+10);
+ }
+}
+
+/*
+ * format and print a packet
+ */
+void
+printpkt(char *p, char *e, uchar *ps, uchar *pe)
+{
+ Msg m;
+ uvlong dt;
+ Tm tm;
+
+ tm = *localtime(pkttime/1000000000LL);
+ m.p = seprint(p, e, "%02d/%02d/%04d %02d:%02d:%02d.%09lld",
+ tm.mon+1, tm.mday, tm.year+1900, tm.hour, tm.min, tm.sec,
+ pkttime%1000000000LL);
+ m.ps = ps;
+ m.pe = pe;
+ m.e = e;
+ m.pr = root;
+ while(m.p < m.e){
+ if(!sflag)
+ m.p = seprint(m.p, m.e, "\n\t");
+ m.p = seprint(m.p, m.e, "%s(", m.pr->name);
+ if((*m.pr->seprint)(&m) < 0){
+ m.p = seprint(m.p, m.e, "TOO SHORT");
+ m.ps = m.pe;
+ }
+ m.p = seprint(m.p, m.e, ")");
+ if(m.pr == nil || m.ps >= m.pe)
+ break;
+ }
+ *m.p++ = '\n';
+
+ if(write(1, p, m.p - p) < 0)
+ sysfatal("stdout: %r");
+}
+
+Proto **xprotos;
+int nprotos;
+
+/* look up a protocol by its name */
+Proto*
+findproto(char *name)
+{
+ int i;
+
+ for(i = 0; i < nprotos; i++)
+ if(strcmp(xprotos[i]->name, name) == 0)
+ return xprotos[i];
+ return nil;
+}
+
+/*
+ * add an undefined protocol to protos[]
+ */
+Proto*
+addproto(char *name)
+{
+ Proto *pr;
+
+ xprotos = realloc(xprotos, (nprotos+1)*sizeof(Proto*));
+ pr = malloc(sizeof *pr);
+ *pr = dump;
+ pr->name = name;
+ xprotos[nprotos++] = pr;
+ return pr;
+}
+
+/*
+ * build a graph of protocols, this could easily be circular. This
+ * links together all the multiplexing in the protocol modules.
+ */
+void
+mkprotograph(void)
+{
+ Proto **l;
+ Proto *pr;
+ Mux *m;
+
+ /* copy protos into a reallocable area */
+ for(nprotos = 0; protos[nprotos] != nil; nprotos++)
+ ;
+ xprotos = malloc(nprotos*sizeof(Proto*));
+ memmove(xprotos, protos, nprotos*sizeof(Proto*));
+
+ for(l = protos; *l != nil; l++){
+ pr = *l;
+ for(m = pr->mux; m != nil && m->name != nil; m++){
+ m->pr = findproto(m->name);
+ if(m->pr == nil)
+ m->pr = addproto(m->name);
+ }
+ }
+}
+
+/*
+ * add in a protocol node
+ */
+static Filter*
+addnode(Filter *f, Proto *pr)
+{
+ Filter *nf;
+ nf = newfilter();
+ nf->pr = pr;
+ nf->s = pr->name;
+ nf->l = f;
+ nf->op = WORD;
+ return nf;
+}
+
+/*
+ * recurse through the protocol graph adding missing nodes
+ * to the filter if we reach the filter's protocol
+ */
+static Filter*
+_fillin(Filter *f, Proto *last, int depth)
+{
+ Mux *m;
+ Filter *nf;
+
+ if(depth-- <= 0)
+ return nil;
+
+ for(m = last->mux; m != nil && m->name != nil; m++){
+ if(m->pr == nil)
+ continue;
+ if(f->pr == m->pr)
+ return f;
+ nf = _fillin(f, m->pr, depth);
+ if(nf != nil)
+ return addnode(nf, m->pr);
+ }
+ return nil;
+}
+
+static Filter*
+fillin(Filter *f, Proto *last)
+{
+ int i;
+ Filter *nf;
+
+ /* hack to make sure top level node is the root */
+ if(last == nil){
+ if(f->pr == root)
+ return f;
+ f = fillin(f, root);
+ if(f == nil)
+ return nil;
+ return addnode(f, root);
+ }
+
+ /* breadth first search though the protocol graph */
+ nf = f;
+ for(i = 1; i < 20; i++){
+ nf = _fillin(f, last, i);
+ if(nf != nil)
+ break;
+ }
+ return nf;
+}
+
+/*
+ * massage tree so that all paths from the root to a leaf
+ * contain a filter node for each header.
+ *
+ * also, set f->pr where possible
+ */
+Filter*
+complete(Filter *f, Proto *last)
+{
+ Proto *pr;
+
+ if(f == nil)
+ return f;
+
+ /* do a depth first traversal of the filter tree */
+ switch(f->op){
+ case '!':
+ f->l = complete(f->l, last);
+ break;
+ case LAND:
+ case LOR:
+ f->l = complete(f->l, last);
+ f->r = complete(f->r, last);
+ break;
+ case '=':
+ break;
+ case WORD:
+ pr = findproto(f->s);
+ f->pr = pr;
+ if(pr == nil){
+ if(f->l != nil){
+ fprint(2, "%s unknown proto, ignoring params\n",
+ f->s);
+ f->l = nil;
+ }
+ } else {
+ f->l = complete(f->l, pr);
+ f = fillin(f, last);
+ if(f == nil)
+ sysfatal("internal error: can't get to %s", pr->name);
+ }
+ break;
+ }
+ return f;
+}
+
+/*
+ * merge common nodes under | and & moving the merged node
+ * above the | or &.
+ *
+ * do some constant foldong, e.g. `true & x' becomes x and
+ * 'true | x' becomes true.
+ */
+static int changed;
+
+static Filter*
+_optimize(Filter *f)
+{
+ Filter *l;
+
+ if(f == nil)
+ return f;
+
+ switch(f->op){
+ case '!':
+ /* is child also a not */
+ if(f->l->op == '!'){
+ changed = 1;
+ return f->l->l;
+ }
+ break;
+ case LOR:
+ /* are two children the same protocol? */
+ if(f->l->op != f->r->op || f->r->op != WORD
+ || f->l->pr != f->r->pr || f->l->pr == nil)
+ break; /* no optimization */
+
+ changed = 1;
+
+ /* constant folding */
+ /* if either child is childless, just return that */
+ if(f->l->l == nil)
+ return f->l;
+ else if(f->r->l == nil)
+ return f->r;
+
+ /* move the common node up, thow away one node */
+ l = f->l;
+ f->l = l->l;
+ f->r = f->r->l;
+ l->l = f;
+ return l;
+ case LAND:
+ /* are two children the same protocol? */
+ if(f->l->op != f->r->op || f->r->op != WORD
+ || f->l->pr != f->r->pr || f->l->pr == nil)
+ break; /* no optimization */
+
+ changed = 1;
+
+ /* constant folding */
+ /* if either child is childless, ignore it */
+ if(f->l->l == nil)
+ return f->r;
+ else if(f->r->l == nil)
+ return f->l;
+
+ /* move the common node up, thow away one node */
+ l = f->l;
+ f->l = _optimize(l->l);
+ f->r = _optimize(f->r->l);
+ l->l = f;
+ return l;
+ }
+ f->l = _optimize(f->l);
+ f->r = _optimize(f->r);
+ return f;
+}
+
+Filter*
+optimize(Filter *f)
+{
+ do{
+ changed = 0;
+ f = _optimize(f);
+ }while(changed);
+
+ return f;
+}
+
+/*
+ * find any top level nodes that aren't the root
+ */
+int
+findbogus(Filter *f)
+{
+ int rv;
+
+ if(f->op != WORD){
+ rv = findbogus(f->l);
+ if(f->r)
+ rv |= findbogus(f->r);
+ return rv;
+ } else if(f->pr != root){
+ fprint(2, "bad top-level protocol: %s\n", f->s);
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * compile the filter
+ */
+static void
+_compile(Filter *f, Proto *last)
+{
+ if(f == nil)
+ return;
+
+ switch(f->op){
+ case '!':
+ _compile(f->l, last);
+ break;
+ case LOR:
+ case LAND:
+ _compile(f->l, last);
+ _compile(f->r, last);
+ break;
+ case WORD:
+ if(last != nil)
+ (*last->compile)(f);
+ if(f->l)
+ _compile(f->l, f->pr);
+ break;
+ case '=':
+ if(last == nil)
+ sysfatal("internal error: compilewalk: badly formed tree");
+ (*last->compile)(f);
+ break;
+ default:
+ sysfatal("internal error: compilewalk op: %d", f->op);
+ }
+}
+
+Filter*
+compile(Filter *f)
+{
+ if(f == nil)
+ return f;
+
+ /* fill in the missing header filters */
+ f = complete(f, nil);
+
+ /* constant folding */
+ f = optimize(f);
+ if(!toflag)
+ printfilter(f, "after optimize");
+
+ /* protocol specific compilations */
+ _compile(f, nil);
+
+ /* at this point, the root had better be the root proto */
+ if(findbogus(f)){
+ fprint(2, "bogus filter\n");
+ exits("bad filter");
+ }
+
+ return f;
+}
+
+/*
+ * parse a byte array
+ */
+int
+parseba(uchar *to, char *from)
+{
+ char nip[4];
+ char *p;
+ int i;
+
+ p = from;
+ for(i = 0; i < 16; i++){
+ if(*p == 0)
+ return -1;
+ nip[0] = *p++;
+ if(*p == 0)
+ return -1;
+ nip[1] = *p++;
+ nip[2] = 0;
+ to[i] = strtoul(nip, 0, 16);
+ }
+ return i;
+}
+
+/*
+ * compile WORD = WORD, becomes a single node with a subop
+ */
+void
+compile_cmp(char *proto, Filter *f, Field *fld)
+{
+ uchar x[IPaddrlen];
+
+ if(f->op != '=')
+ sysfatal("internal error: compile_cmp %s: not a cmp", proto);
+
+ for(; fld->name != nil; fld++){
+ if(strcmp(f->l->s, fld->name) == 0){
+ f->op = WORD;
+ f->subop = fld->subop;
+ switch(fld->ftype){
+ case Fnum:
+ f->ulv = atoi(f->r->s);
+ break;
+ case Fether:
+ parseether(f->a, f->r->s);
+ break;
+ case Fv4ip:
+ f->ulv = parseip(x, f->r->s);
+ break;
+ case Fv6ip:
+ parseip(f->a, f->r->s);
+ break;
+ case Fba:
+ parseba(f->a, f->r->s);
+ break;
+ default:
+ sysfatal("internal error: compile_cmp %s: %d",
+ proto, fld->ftype);
+ }
+ f->l = f->r = nil;
+ return;
+ }
+ }
+ sysfatal("unknown %s field in: %s = %s", proto, f->l->s, f->r->s);
+}
+
+void
+_pf(Filter *f)
+{
+ char *s;
+
+ if(f == nil)
+ return;
+
+ s = nil;
+ switch(f->op){
+ case '!':
+ fprint(2, "!");
+ _pf(f->l);
+ break;
+ case WORD:
+ fprint(2, "%s", f->s);
+ if(f->l != nil){
+ fprint(2, "( ");
+ _pf(f->l);
+ fprint(2, " )");
+ }
+ break;
+ case LAND:
+ s = "&&";
+ goto print;
+ case LOR:
+ s = "||";
+ goto print;
+ case '=':
+ print:
+ _pf(f->l);
+ if(s)
+ fprint(2, " %s ", s);
+ else
+ fprint(2, " %c ", f->op);
+ _pf(f->r);
+ break;
+ default:
+ fprint(2, "???");
+ break;
+ }
+}
+
+void
+printfilter(Filter *f, char *tag)
+{
+ fprint(2, "%s: ", tag);
+ _pf(f);
+ fprint(2, "\n");
+}
+
+void
+printhelp(void)
+{
+ Proto *pr, **l;
+ Mux *m;
+ Field *f;
+
+ for(l = protos; *l != nil; l++){
+ pr = *l;
+ if(pr->field != nil){
+ print("%s's filter attr:\n", pr->name);
+ for(f = pr->field; f->name != nil; f++)
+ print("\t%s\t- %s\n", f->name, f->help);
+ }
+ if(pr->mux != nil){
+ print("%s's subprotos:\n", pr->name);
+ for(m = pr->mux; m->name != nil; m++)
+ print("\t%s\n", m->name);
+ }
+ }
+}
+
+/*
+ * demultiplex to next prototol header
+ */
+void
+demux(Mux *mx, ulong val1, ulong val2, Msg *m, Proto *def)
+{
+ m->pr = def;
+ for(mx = mx; mx->name != nil; mx++){
+ if(val1 == mx->val || val2 == mx->val){
+ m->pr = mx->pr;
+ break;
+ }
+ }
+}
+
+/*
+ * default framer just assumes the input packet is
+ * a single read
+ */
+int
+defaultframer(int fd, uchar *pkt, int pktlen)
+{
+ return read(fd, pkt, pktlen);
+}
diff --git a/src/cmd/ip/snoopy/mkfile b/src/cmd/ip/snoopy/mkfile
new file mode 100755
index 00000000..5a76591f
--- /dev/null
+++ b/src/cmd/ip/snoopy/mkfile
@@ -0,0 +1,69 @@
+<$PLAN9/src/mkhdr
+
+TARG=snoopy
+PROTOS=\
+ ether\
+ ip\
+ ip6\
+ dump\
+ arp\
+ rarp\
+ udp\
+ bootp\
+ dhcp\
+ hdlc\
+ rtp\
+ rtcp\
+ tcp\
+ il\
+ icmp\
+ icmp6\
+ ninep\
+ ospf\
+ ppp\
+ ppp_ccp\
+ ppp_lcp\
+ ppp_chap\
+ ppp_ipcp\
+ pppoe_sess\
+ pppoe_disc\
+
+POBJS=${PROTOS:%=%.$O}
+
+OFILES= main.$O\
+ y.tab.$O\
+ protos.$O\
+ $SYSNAME.$O\
+ $POBJS
+
+HFILES=dat.h\
+ protos.h\
+ y.tab.h\
+
+<$PLAN9/src/mkone
+
+protos.h: mkfile
+ (
+ for i in $PROTOS
+ do
+ echo extern Proto $i';'
+ done
+ ) > protos.h
+
+protos.c: mkfile
+ (
+ echo '#include <u.h>'
+ echo '#include <libc.h>'
+ echo '#include "dat.h"'
+ echo '#include "protos.h"'
+ echo 'Proto *protos[] ='
+ echo '{'
+ for i in $PROTOS
+ do
+ echo ' &'$i','
+ done
+ echo ' 0,'
+ echo '};'
+ ) > protos.c
+
+y.tab.c: filter.y
diff --git a/src/cmd/ip/snoopy/ninep.c b/src/cmd/ip/snoopy/ninep.c
new file mode 100755
index 00000000..8e7a8d4e
--- /dev/null
+++ b/src/cmd/ip/snoopy/ninep.c
@@ -0,0 +1,55 @@
+#include <u.h>
+#include <libc.h>
+#include <ip.h>
+#include <fcall.h>
+#include "dat.h"
+#include "protos.h"
+
+static void
+p_compile(Filter *f)
+{
+ sysfatal("unknown ninep field: %s", f->s);
+}
+
+static int
+p_filter(Filter *f, Msg *m)
+{
+ USED(f);
+ USED(m);
+ return 0;
+}
+
+static int
+p_seprint(Msg *m)
+{
+ Fcall f;
+ char *p;
+
+ memset(&f, 0, sizeof(f));
+ f.type = 0;
+ f.data = 0; /* protection for %F */
+ if(convM2S(m->ps, m->pe-m->ps, &f)){
+ p = m->p;
+ m->p = seprint(m->p, m->e, "%F", &f);
+ while(p < m->p){
+ p = strchr(p, '\n');
+ if(p == nil)
+ break;
+ *p = '\\';
+ }
+ } else
+ dump.seprint(m);
+ m->pr = nil;
+ return 0;
+}
+
+Proto ninep =
+{
+ "ninep",
+ p_compile,
+ p_filter,
+ p_seprint,
+ nil,
+ nil,
+ defaultframer,
+};
diff --git a/src/cmd/ip/snoopy/ospf.c b/src/cmd/ip/snoopy/ospf.c
new file mode 100755
index 00000000..35ed55e1
--- /dev/null
+++ b/src/cmd/ip/snoopy/ospf.c
@@ -0,0 +1,404 @@
+#include <u.h>
+#include <libc.h>
+#include <ip.h>
+#include <libsec.h>
+#include "dat.h"
+#include "protos.h"
+
+
+/*
+ * OSPF packets
+ */
+typedef struct Ospfpkt Ospfpkt;
+struct Ospfpkt
+{
+ uchar version;
+ uchar type;
+ uchar length[2];
+ uchar router[4];
+ uchar area[4];
+ uchar sum[2];
+ uchar autype[2];
+ uchar auth[8];
+ uchar data[1];
+};
+#define OSPF_HDRSIZE 24
+
+enum
+{
+ OSPFhello= 1,
+ OSPFdd= 2,
+ OSPFlsrequest= 3,
+ OSPFlsupdate= 4,
+ OSPFlsack= 5,
+};
+
+
+char *ospftype[] = {
+ [OSPFhello] "hello",
+ [OSPFdd] "data definition",
+ [OSPFlsrequest] "link state request",
+ [OSPFlsupdate] "link state update",
+ [OSPFlsack] "link state ack",
+};
+
+char*
+ospfpkttype(int x)
+{
+ static char type[16];
+
+ if(x > 0 && x <= OSPFlsack)
+ return ospftype[x];
+ sprint(type, "type %d", x);
+ return type;
+}
+
+char*
+ospfauth(Ospfpkt *ospf)
+{
+ static char auth[100];
+
+ switch(ospf->type){
+ case 0:
+ return "no authentication";
+ case 1:
+ sprint(auth, "password(%8.8ux %8.8ux)", NetL(ospf->auth),
+ NetL(ospf->auth+4));
+ break;
+ case 2:
+ sprint(auth, "crypto(plen %d id %d dlen %d)", NetS(ospf->auth),
+ ospf->auth[2], ospf->auth[3]);
+ break;
+ default:
+ sprint(auth, "auth%d(%8.8ux %8.8ux)", NetS(ospf->autype), NetL(ospf->auth),
+ NetL(ospf->auth+4));
+ }
+ return auth;
+}
+
+typedef struct Ospfhello Ospfhello;
+struct Ospfhello
+{
+ uchar mask[4];
+ uchar interval[2];
+ uchar options;
+ uchar pri;
+ uchar deadint[4];
+ uchar designated[4];
+ uchar bdesignated[4];
+ uchar neighbor[1];
+};
+
+char*
+seprintospfhello(char *p, char *e, void *a, int x)
+{
+ Ospfhello *h = a;
+
+ USED(x);
+ return seprint(p, e, "%s(mask %V interval %d opt %ux pri %ux deadt %d designated %V bdesignated %V)",
+ ospftype[OSPFhello],
+ h->mask, NetS(h->interval), h->options, h->pri,
+ NetL(h->deadint), h->designated, h->bdesignated);
+}
+
+enum
+{
+ LSARouter= 1,
+ LSANetwork= 2,
+ LSASummN= 3,
+ LSASummR= 4,
+ LSAASext= 5
+};
+
+
+char *lsatype[] = {
+ [LSARouter] "Router LSA",
+ [LSANetwork] "Network LSA",
+ [LSASummN] "Summary LSA (Network)",
+ [LSASummR] "Summary LSA (Router)",
+ [LSAASext] "LSA AS external",
+};
+
+char*
+lsapkttype(int x)
+{
+ static char type[16];
+
+ if(x > 0 && x <= LSAASext)
+ return lsatype[x];
+ sprint(type, "type %d", x);
+ return type;
+}
+
+/* OSPF Link State Advertisement Header */
+/* rfc2178 section 12.1 */
+/* data of Ospfpkt point to a 4-uchar value that is the # of LSAs */
+struct OspfLSAhdr {
+ uchar lsage[2];
+ uchar options; /* 0x2=stub area, 0x1=TOS routing capable */
+
+ uchar lstype; /* 1=Router-LSAs
+ * 2=Network-LSAs
+ * 3=Summary-LSAs (to network)
+ * 4=Summary-LSAs (to AS boundary routers)
+ * 5=AS-External-LSAs
+ */
+ uchar lsid[4];
+ uchar advtrt[4];
+
+ uchar lsseqno[4];
+ uchar lscksum[2];
+ uchar lsalen[2]; /* includes the 20 byte lsa header */
+};
+
+struct Ospfrt {
+ uchar linkid[4];
+ uchar linkdata[4];
+ uchar typ;
+ uchar numtos;
+ uchar metric[2];
+
+};
+
+struct OspfrtLSA {
+ struct OspfLSAhdr hdr;
+ uchar netmask[4];
+};
+
+struct OspfntLSA {
+ struct OspfLSAhdr hdr;
+ uchar netmask[4];
+ uchar attrt[4];
+};
+
+/* Summary Link State Advertisement info */
+struct Ospfsumm {
+ uchar flag; /* always zero */
+ uchar metric[3];
+};
+
+struct OspfsummLSA {
+ struct OspfLSAhdr hdr;
+ uchar netmask[4];
+ struct Ospfsumm lsa;
+};
+
+/* AS external Link State Advertisement info */
+struct OspfASext {
+ uchar flag; /* external */
+ uchar metric[3];
+ uchar fwdaddr[4];
+ uchar exrttag[4];
+};
+
+struct OspfASextLSA {
+ struct OspfLSAhdr hdr;
+ uchar netmask[4];
+ struct OspfASext lsa;
+};
+
+/* OSPF Link State Update Packet */
+struct OspfLSupdpkt {
+ uchar lsacnt[4];
+ union {
+ uchar hdr[1];
+ struct OspfrtLSA rt[1];
+ struct OspfntLSA nt[1];
+ struct OspfsummLSA sum[1];
+ struct OspfASextLSA as[1];
+ };
+};
+
+char*
+seprintospflsaheader(char *p, char *e, struct OspfLSAhdr *h)
+{
+ return seprint(p, e, "age %d opt %ux type %ux lsid %V adv_rt %V seqno %ux c %4.4ux l %d",
+ NetS(h->lsage), h->options&0xff, h->lstype,
+ h->lsid, h->advtrt, NetL(h->lsseqno), NetS(h->lscksum),
+ NetS(h->lsalen));
+}
+
+/* OSPF Database Description Packet */
+struct OspfDDpkt {
+ uchar intMTU[2];
+ uchar options;
+ uchar bits;
+ uchar DDseqno[4];
+ struct OspfLSAhdr hdr[1]; /* LSA headers... */
+};
+
+char*
+seprintospfdatadesc(char *p, char *e, void *a, int len)
+{
+ int nlsa, i;
+ struct OspfDDpkt *g;
+
+ g = (struct OspfDDpkt *)a;
+ nlsa = len/sizeof(struct OspfLSAhdr);
+ for (i=0; i<nlsa; i++) {
+ p = seprint(p, e, "lsa%d(", i);
+ p = seprintospflsaheader(p, e, &(g->hdr[i]));
+ p = seprint(p, e, ")");
+ }
+ return seprint(p, e, ")");
+}
+
+char*
+seprintospflsupdate(char *p, char *e, void *a, int len)
+{
+ int nlsa, i;
+ struct OspfLSupdpkt *g;
+ struct OspfLSAhdr *h;
+
+ g = (struct OspfLSupdpkt *)a;
+ nlsa = NetL(g->lsacnt);
+ h = (struct OspfLSAhdr *)(g->hdr);
+ p = seprint(p, e, "%d-%s(", nlsa, ospfpkttype(OSPFlsupdate));
+
+ switch(h->lstype) {
+ case LSARouter:
+ {
+/* struct OspfrtLSA *h;
+ */
+ }
+ break;
+ case LSANetwork:
+ {
+ struct OspfntLSA *h;
+
+ for (i=0; i<nlsa; i++) {
+ h = &(g->nt[i]);
+ p = seprint(p, e, "lsa%d(", i);
+ p = seprintospflsaheader(p, e, &(h->hdr));
+ p = seprint(p, e, " mask %V attrt %V)",
+ h->netmask, h->attrt);
+ }
+ }
+ break;
+ case LSASummN:
+ case LSASummR:
+ {
+ struct OspfsummLSA *h;
+
+ for (i=0; i<nlsa; i++) {
+ h = &(g->sum[i]);
+ p = seprint(p, e, "lsa%d(", i);
+ p = seprintospflsaheader(p, e, &(h->hdr));
+ p = seprint(p, e, " mask %V met %d)",
+ h->netmask, Net3(h->lsa.metric));
+ }
+ }
+ break;
+ case LSAASext:
+ {
+ struct OspfASextLSA *h;
+
+ for (i=0; i<nlsa; i++) {
+ h = &(g->as[i]);
+ p = seprint(p, e, " lsa%d(", i);
+ p = seprintospflsaheader(p, e, &(h->hdr));
+ p = seprint(p, e, " mask %V extflg %1.1ux met %d fwdaddr %V extrtflg %ux)",
+ h->netmask, h->lsa.flag, Net3(h->lsa.metric),
+ h->lsa.fwdaddr, NetL(h->lsa.exrttag));
+ }
+ }
+ break;
+ default:
+ p = seprint(p, e, "Not an LS update, lstype %d ", h->lstype);
+ p = seprint(p, e, " %.*H", len>64?64:len, a);
+ break;
+ }
+ return seprint(p, e, ")");
+}
+
+char*
+seprintospflsack(char *p, char *e, void *a, int len)
+{
+ int nlsa, i;
+ struct OspfLSAhdr *h;
+
+ h = (struct OspfLSAhdr *)a;
+ nlsa = len/sizeof(struct OspfLSAhdr);
+ p = seprint(p, e, "%d-%s(", nlsa, ospfpkttype(OSPFlsack));
+ for (i=0; i<nlsa; i++) {
+ p = seprint(p, e, " lsa%d(", i);
+ p = seprintospflsaheader(p, e, &(h[i]));
+ p = seprint(p, e, ")");
+ }
+ return seprint(p, e, ")");
+}
+
+static void
+p_compile(Filter *f)
+{
+ sysfatal("unknown ospf field: %s", f->s);
+}
+
+static int
+p_filter(Filter *f, Msg *m)
+{
+ USED(f);
+ USED(m);
+ return 0;
+}
+
+int
+p_seprint(Msg *m)
+{
+ Ospfpkt *ospf;
+ int len, x;
+ char *p, *e;
+
+ len = m->pe - m->ps;
+ if(len < OSPF_HDRSIZE)
+ return -1;
+ p = m->p;
+ e = m->e;
+
+ /* adjust packet size */
+ ospf = (Ospfpkt*)m->ps;
+ x = NetS(ospf->length);
+ if(x < len)
+ return -1;
+ x -= OSPF_HDRSIZE;
+
+ p = seprint(p, e, "ver=%d type=%d len=%d r=%V a=%V c=%4.4ux %s ",
+ ospf->version, ospf->type, x,
+ ospf->router, ospf->area, NetS(ospf->sum),
+ ospfauth(ospf));
+
+ switch (ospf->type) {
+ case OSPFhello:
+ p = seprintospfhello(p, e, ospf->data, x);
+ break;
+ case OSPFdd:
+ p = seprintospfdatadesc(p, e, ospf->data, x);
+ break;
+ case OSPFlsrequest:
+ p = seprint(p, e, " %s->", ospfpkttype(ospf->type));
+ goto Default;
+ case OSPFlsupdate:
+ p = seprintospflsupdate(p, e, ospf->data, x);
+ break;
+ case OSPFlsack:
+ p = seprintospflsack(p, e, ospf->data, x);
+ break;
+ default:
+Default:
+ p = seprint(p, e, " data=%.*H", x>64?64:x, ospf->data);
+ }
+ m->p = p;
+ m->pr = nil;
+ return 0;
+}
+
+Proto ospf =
+{
+ "ospf",
+ p_compile,
+ p_filter,
+ p_seprint,
+ nil,
+ nil,
+ defaultframer,
+};
diff --git a/src/cmd/ip/snoopy/ppp.c b/src/cmd/ip/snoopy/ppp.c
new file mode 100755
index 00000000..5c497a8c
--- /dev/null
+++ b/src/cmd/ip/snoopy/ppp.c
@@ -0,0 +1,629 @@
+#include <u.h>
+#include <libc.h>
+#include <ip.h>
+#include <libsec.h>
+#include "dat.h"
+#include "protos.h"
+
+/* PPP stuff */
+enum {
+ PPP_addr= 0xff,
+ PPP_ctl= 0x3,
+ PPP_period= 3*1000, /* period of retransmit process (in ms) */
+};
+
+/* PPP protocols */
+enum {
+ PPP_ip= 0x21, /* internet */
+ PPP_vjctcp= 0x2d, /* compressing van jacobson tcp */
+ PPP_vjutcp= 0x2f, /* uncompressing van jacobson tcp */
+ PPP_ml= 0x3d, /* multi link */
+ PPP_comp= 0xfd, /* compressed packets */
+ PPP_ipcp= 0x8021, /* ip control */
+ PPP_ccp= 0x80fd, /* compression control */
+ PPP_passwd= 0xc023, /* passwd authentication */
+ PPP_lcp= 0xc021, /* link control */
+ PPP_lqm= 0xc025, /* link quality monitoring */
+ PPP_chap= 0xc223, /* challenge/response */
+};
+
+/* LCP protocol (and IPCP) */
+
+
+typedef struct Lcppkt Lcppkt;
+struct Lcppkt
+{
+ uchar code;
+ uchar id;
+ uchar len[2];
+ uchar data[1];
+};
+
+typedef struct Lcpopt Lcpopt;
+struct Lcpopt
+{
+ uchar type;
+ uchar len;
+ uchar data[1];
+};
+
+enum
+{
+ /* LCP codes */
+ Lconfreq= 1,
+ Lconfack= 2,
+ Lconfnak= 3,
+ Lconfrej= 4,
+ Ltermreq= 5,
+ Ltermack= 6,
+ Lcoderej= 7,
+ Lprotorej= 8,
+ Lechoreq= 9,
+ Lechoack= 10,
+ Ldiscard= 11,
+ Lresetreq= 14, /* for ccp only */
+ Lresetack= 15, /* for ccp only */
+
+ /* Lcp configure options */
+ Omtu= 1,
+ Octlmap= 2,
+ Oauth= 3,
+ Oquality= 4,
+ Omagic= 5,
+ Opc= 7,
+ Oac= 8,
+
+ /* authentication protocols */
+ APmd5= 5,
+ APmschap= 128,
+
+ /* Chap codes */
+ Cchallenge= 1,
+ Cresponse= 2,
+ Csuccess= 3,
+ Cfailure= 4,
+
+ /* ipcp configure options */
+ Oipaddrs= 1,
+ Oipcompress= 2,
+ Oipaddr= 3,
+ Oipdns= 129,
+ Oipwins= 130,
+ Oipdns2= 131,
+ Oipwins2= 132,
+};
+
+char *
+lcpcode[] = {
+ 0,
+ "confreq",
+ "confack",
+ "confnak",
+ "confrej",
+ "termreq",
+ "termack",
+ "coderej",
+ "protorej",
+ "echoreq",
+ "echoack",
+ "discard",
+ "id",
+ "timeremain",
+ "resetreq",
+ "resetack",
+};
+
+static Mux p_mux[] =
+{
+ {"ip", PPP_ip, },
+ {"ppp_vjctcp", PPP_vjctcp, },
+ {"ppp_vjutcp", PPP_vjutcp, },
+ {"ppp_ml", PPP_ml, },
+ {"ppp_comp", PPP_comp, },
+ {"ppp_ipcp", PPP_ipcp, },
+ {"ppp_ccp", PPP_ccp, },
+ {"ppp_passwd", PPP_passwd, },
+ {"ppp_lcp", PPP_lcp, },
+ {"ppp_lqm", PPP_lqm, },
+ {"ppp_chap", PPP_chap, },
+ {0},
+};
+
+enum
+{
+ OOproto,
+};
+
+static void
+p_compile(Filter *f)
+{
+ Mux *m;
+
+ for(m = p_mux; m->name != nil; m++)
+ if(strcmp(f->s, m->name) == 0){
+ f->pr = m->pr;
+ f->ulv = m->val;
+ f->subop = OOproto;
+ return;
+ }
+
+ sysfatal("unknown ppp field or protocol: %s", f->s);
+}
+
+static int
+p_filter(Filter *f, Msg *m)
+{
+ int proto;
+ int len;
+
+ if(f->subop != OOproto)
+ return 0;
+
+ len = m->pe - m->ps;
+ if(len < 3)
+ return -1;
+
+ if(m->ps[0] == PPP_addr && m->ps[1] == PPP_ctl)
+ m->ps += 2;
+
+ proto = *m->ps++;
+ if((proto&1) == 0)
+ proto = (proto<<8) | *m->ps++;
+
+ if(proto == f->ulv)
+ return 1;
+
+ return 0;
+}
+
+static int
+p_seprint(Msg *m)
+{
+ int proto;
+ int len;
+
+ len = m->pe - m->ps;
+ if(len < 3)
+ return -1;
+
+ if(m->ps[0] == PPP_addr && m->ps[1] == PPP_ctl)
+ m->ps += 2;
+
+ proto = *m->ps++;
+ if((proto&1) == 0)
+ proto = (proto<<8) | *m->ps++;
+
+ m->p = seprint(m->p, m->e, "pr=%ud len=%d", proto, len);
+ demux(p_mux, proto, proto, m, &dump);
+
+ return 0;
+}
+
+static int
+p_seprintchap(Msg *m)
+{
+ Lcppkt *lcp;
+ char *p, *e;
+ int len;
+
+ if(m->pe-m->ps < 4)
+ return -1;
+
+ p = m->p;
+ e = m->e;
+ m->pr = nil;
+
+ /* resize packet */
+ lcp = (Lcppkt*)m->ps;
+ len = NetS(lcp->len);
+ if(m->ps+len < m->pe)
+ m->pe = m->ps+len;
+ else if(m->ps+len > m->pe)
+ return -1;
+
+ p = seprint(p, e, "id=%d code=%d", lcp->id, lcp->code);
+ switch(lcp->code) {
+ default:
+ p = seprint(p, e, " data=%.*H", len>64?64:len, lcp->data);
+ break;
+ case 1:
+ case 2:
+ if(lcp->data[0] > len-4){
+ p = seprint(p, e, "%.*H", len-4, lcp->data);
+ } else {
+ p = seprint(p, e, " %s=", lcp->code==1?"challenge ":"response ");
+ p = seprint(p, e, "%.*H", lcp->data[0], lcp->data+1);
+ p = seprint(p, e, " name=");
+ p = seprint(p, e, "%.*H", len-4-lcp->data[0]-1, lcp->data+lcp->data[0]+1);
+ }
+ break;
+ case 3:
+ case 4:
+ if(len > 64)
+ len = 64;
+ p = seprint(p, e, " %s=%.*H", lcp->code==3?"success ":"failure",
+ len>64?64:len, lcp->data);
+ break;
+ }
+ m->p = seprint(p, e, " len=%d", len);
+ return 0;
+}
+
+static char*
+seprintlcpopt(char *p, char *e, void *a, int len)
+{
+ Lcpopt *o;
+ int proto, x, period;
+ uchar *cp, *ecp;
+
+ cp = a;
+ ecp = cp+len;
+
+ for(; cp < ecp; cp += o->len){
+ o = (Lcpopt*)cp;
+ if(cp + o->len > ecp || o->len == 0){
+ p = seprint(p, e, " bad-opt-len=%d", o->len);
+ return p;
+ }
+
+ switch(o->type){
+ default:
+ p = seprint(p, e, " (type=%d len=%d)", o->type, o->len);
+ break;
+ case Omtu:
+ p = seprint(p, e, " mtu=%d", NetS(o->data));
+ break;
+ case Octlmap:
+ p = seprint(p, e, " ctlmap=%ux", NetL(o->data));
+ break;
+ case Oauth:
+ proto = NetS(o->data);
+ switch(proto) {
+ default:
+ p = seprint(p, e, " auth=%d", proto);
+ break;
+ case PPP_passwd:
+ p = seprint(p, e, " auth=passwd");
+ break;
+ case PPP_chap:
+ p = seprint(p, e, " (auth=chap data=%2.2ux)", o->data[2]);
+ break;
+ }
+ break;
+ case Oquality:
+ proto = NetS(o->data);
+ switch(proto) {
+ default:
+ p = seprint(p, e, " qproto=%d", proto);
+ break;
+ case PPP_lqm:
+ x = NetL(o->data+2)*10;
+ period = (x+(PPP_period-1))/PPP_period;
+ p = seprint(p, e, " (qproto=lqm period=%d)", period);
+ break;
+ }
+ case Omagic:
+ p = seprint(p, e, " magic=%ux", NetL(o->data));
+ break;
+ case Opc:
+ p = seprint(p, e, " protocol-compress");
+ break;
+ case Oac:
+ p = seprint(p, e, " addr-compress");
+ break;
+ }
+ }
+ return p;
+}
+
+
+static int
+p_seprintlcp(Msg *m)
+{
+ Lcppkt *lcp;
+ char *p, *e;
+ int len;
+
+ if(m->pe-m->ps < 4)
+ return -1;
+
+ p = m->p;
+ e = m->e;
+ m->pr = nil;
+
+ lcp = (Lcppkt*)m->ps;
+ len = NetS(lcp->len);
+ if(m->ps+len < m->pe)
+ m->pe = m->ps+len;
+ else if(m->ps+len > m->pe)
+ return -1;
+
+ p = seprint(p, e, "id=%d code=%d", lcp->id, lcp->code);
+ switch(lcp->code) {
+ default:
+ p = seprint(p, e, " data=%.*H", len>64?64:len, lcp->data);
+ break;
+ case Lconfreq:
+ case Lconfack:
+ case Lconfnak:
+ case Lconfrej:
+ p = seprint(p, e, "=%s", lcpcode[lcp->code]);
+ p = seprintlcpopt(p, e, lcp->data, len-4);
+ break;
+ case Ltermreq:
+ case Ltermack:
+ case Lcoderej:
+ case Lprotorej:
+ case Lechoreq:
+ case Lechoack:
+ case Ldiscard:
+ p = seprint(p, e, "=%s", lcpcode[lcp->code]);
+ break;
+ }
+ m->p = seprint(p, e, " len=%d", len);
+ return 0;
+}
+
+static char*
+seprintipcpopt(char *p, char *e, void *a, int len)
+{
+ Lcpopt *o;
+ uchar *cp, *ecp;
+
+ cp = a;
+ ecp = cp+len;
+
+ for(; cp < ecp; cp += o->len){
+ o = (Lcpopt*)cp;
+ if(cp + o->len > ecp){
+ p = seprint(p, e, " bad opt len %ux", o->type);
+ return p;
+ }
+
+ switch(o->type){
+ default:
+ p = seprint(p, e, " (type=%d len=%d)", o->type, o->len);
+ break;
+ case Oipaddrs:
+ p = seprint(p, e, " ipaddrs(deprecated)");
+ break;
+ case Oipcompress:
+ p = seprint(p, e, " ipcompress");
+ break;
+ case Oipaddr:
+ p = seprint(p, e, " ipaddr=%V", o->data);
+ break;
+ case Oipdns:
+ p = seprint(p, e, " dnsaddr=%V", o->data);
+ break;
+ case Oipwins:
+ p = seprint(p, e, " winsaddr=%V", o->data);
+ break;
+ case Oipdns2:
+ p = seprint(p, e, " dns2addr=%V", o->data);
+ break;
+ case Oipwins2:
+ p = seprint(p, e, " wins2addr=%V", o->data);
+ break;
+ }
+ }
+ return p;
+}
+
+static int
+p_seprintipcp(Msg *m)
+{
+ Lcppkt *lcp;
+ char *p, *e;
+ int len;
+
+ if(m->pe-m->ps < 4)
+ return -1;
+
+ p = m->p;
+ e = m->e;
+ m->pr = nil;
+
+ lcp = (Lcppkt*)m->ps;
+ len = NetS(lcp->len);
+ if(m->ps+len < m->pe)
+ m->pe = m->ps+len;
+ else if(m->ps+len > m->pe)
+ return -1;
+
+ p = seprint(p, e, "id=%d code=%d", lcp->id, lcp->code);
+ switch(lcp->code) {
+ default:
+ p = seprint(p, e, " data=%.*H", len>64?64:len, lcp->data);
+ break;
+ case Lconfreq:
+ case Lconfack:
+ case Lconfnak:
+ case Lconfrej:
+ p = seprint(p, e, "=%s", lcpcode[lcp->code]);
+ p = seprintipcpopt(p, e, lcp->data, len-4);
+ break;
+ case Ltermreq:
+ case Ltermack:
+ p = seprint(p, e, "=%s", lcpcode[lcp->code]);
+ break;
+ }
+ m->p = seprint(p, e, " len=%d", len);
+ return 0;
+}
+
+static char*
+seprintccpopt(char *p, char *e, void *a, int len)
+{
+ Lcpopt *o;
+ uchar *cp, *ecp;
+
+ cp = a;
+ ecp = cp+len;
+
+ for(; cp < ecp; cp += o->len){
+ o = (Lcpopt*)cp;
+ if(cp + o->len > ecp){
+ p = seprint(p, e, " bad opt len %ux", o->type);
+ return p;
+ }
+
+ switch(o->type){
+ default:
+ p = seprint(p, e, " type=%d ", o->type);
+ break;
+ case 0:
+ p = seprint(p, e, " OUI=(%d %.2ux%.2ux%.2ux) ", o->type,
+ o->data[0], o->data[1], o->data[2]);
+ break;
+ case 17:
+ p = seprint(p, e, " Stac-LZS");
+ break;
+ case 18:
+ p = seprint(p, e, " Microsoft-PPC=%ux", NetL(o->data));
+ break;
+ }
+ }
+ return p;
+}
+
+static int
+p_seprintccp(Msg *m)
+{
+ Lcppkt *lcp;
+ char *p, *e;
+ int len;
+
+ if(m->pe-m->ps < 4)
+ return -1;
+
+ p = m->p;
+ e = m->e;
+ m->pr = nil;
+
+ lcp = (Lcppkt*)m->ps;
+ len = NetS(lcp->len);
+ if(m->ps+len < m->pe)
+ m->pe = m->ps+len;
+ else if(m->ps+len > m->pe)
+ return -1;
+
+ p = seprint(p, e, "id=%d code=%d", lcp->id, lcp->code);
+ switch(lcp->code) {
+ default:
+ p = seprint(p, e, " data=%.*H", len>64?64:len, lcp->data);
+ break;
+ case Lconfreq:
+ case Lconfack:
+ case Lconfnak:
+ case Lconfrej:
+ p = seprint(p, e, "=%s", lcpcode[lcp->code]);
+ p = seprintccpopt(p, e, lcp->data, len-4);
+ break;
+ case Ltermreq:
+ case Ltermack:
+ case Lresetreq:
+ case Lresetack:
+ p = seprint(p, e, "=%s", lcpcode[lcp->code]);
+ break;
+ }
+ m->p = seprint(p, e, " len=%d", len);
+
+ return 0;
+}
+
+static int
+p_seprintcomp(Msg *m)
+{
+ char compflag[5];
+ ushort x;
+ int i;
+ int len;
+
+ len = m->pe-m->ps;
+ if(len < 2)
+ return -1;
+
+ x = NetS(m->ps);
+ m->ps += 2;
+ i = 0;
+ if(x & (1<<15))
+ compflag[i++] = 'r';
+ if(x & (1<<14))
+ compflag[i++] = 'f';
+ if(x & (1<<13))
+ compflag[i++] = 'c';
+ if(x & (1<<12))
+ compflag[i++] = 'e';
+ compflag[i] = 0;
+ m->p = seprint(m->p, m->e, "flag=%s count=%.3ux", compflag, x&0xfff);
+ m->p = seprint(m->p, m->e, " data=%.*H", len>64?64:len, m->ps);
+ m->pr = nil;
+ return 0;
+}
+
+Proto ppp =
+{
+ "ppp",
+ p_compile,
+ p_filter,
+ p_seprint,
+ p_mux,
+ nil,
+ defaultframer,
+};
+
+Proto ppp_ipcp =
+{
+ "ppp_ipcp",
+ p_compile,
+ p_filter,
+ p_seprintipcp,
+ nil,
+ nil,
+ defaultframer,
+};
+
+Proto ppp_lcp =
+{
+ "ppp_lcp",
+ p_compile,
+ p_filter,
+ p_seprintlcp,
+ nil,
+ nil,
+ defaultframer,
+};
+
+Proto ppp_ccp =
+{
+ "ppp_ccp",
+ p_compile,
+ p_filter,
+ p_seprintccp,
+ nil,
+ nil,
+ defaultframer,
+};
+
+Proto ppp_chap =
+{
+ "ppp_chap",
+ p_compile,
+ p_filter,
+ p_seprintchap,
+ nil,
+ nil,
+ defaultframer,
+};
+
+Proto ppp_comp =
+{
+ "ppp_comp",
+ p_compile,
+ p_filter,
+ p_seprintcomp,
+ nil,
+ nil,
+ defaultframer,
+};
diff --git a/src/cmd/ip/snoopy/ppp_ccp.c b/src/cmd/ip/snoopy/ppp_ccp.c
new file mode 100755
index 00000000..dab65f8f
--- /dev/null
+++ b/src/cmd/ip/snoopy/ppp_ccp.c
@@ -0,0 +1 @@
+/* place holder, this stuff is really in ppp.c */
diff --git a/src/cmd/ip/snoopy/ppp_chap.c b/src/cmd/ip/snoopy/ppp_chap.c
new file mode 100755
index 00000000..dab65f8f
--- /dev/null
+++ b/src/cmd/ip/snoopy/ppp_chap.c
@@ -0,0 +1 @@
+/* place holder, this stuff is really in ppp.c */
diff --git a/src/cmd/ip/snoopy/ppp_comp.c b/src/cmd/ip/snoopy/ppp_comp.c
new file mode 100755
index 00000000..dab65f8f
--- /dev/null
+++ b/src/cmd/ip/snoopy/ppp_comp.c
@@ -0,0 +1 @@
+/* place holder, this stuff is really in ppp.c */
diff --git a/src/cmd/ip/snoopy/ppp_ipcp.c b/src/cmd/ip/snoopy/ppp_ipcp.c
new file mode 100755
index 00000000..dab65f8f
--- /dev/null
+++ b/src/cmd/ip/snoopy/ppp_ipcp.c
@@ -0,0 +1 @@
+/* place holder, this stuff is really in ppp.c */
diff --git a/src/cmd/ip/snoopy/ppp_lcp.c b/src/cmd/ip/snoopy/ppp_lcp.c
new file mode 100755
index 00000000..dab65f8f
--- /dev/null
+++ b/src/cmd/ip/snoopy/ppp_lcp.c
@@ -0,0 +1 @@
+/* place holder, this stuff is really in ppp.c */
diff --git a/src/cmd/ip/snoopy/pppoe_disc.c b/src/cmd/ip/snoopy/pppoe_disc.c
new file mode 100755
index 00000000..4f17ebd7
--- /dev/null
+++ b/src/cmd/ip/snoopy/pppoe_disc.c
@@ -0,0 +1,172 @@
+#include <u.h>
+#include <libc.h>
+#include <ip.h>
+#include "dat.h"
+#include "protos.h"
+
+typedef struct Hdr Hdr;
+struct Hdr {
+ uchar verstype;
+ uchar code;
+ uchar sessid[2];
+ uchar length[2]; /* of payload */
+};
+enum
+{
+ HDRSIZE = 1+1+2+2
+};
+
+static Mux p_mux[] =
+{
+ {"ppp", 0, } ,
+ {0}
+};
+
+enum
+{
+ Overs,
+ Otype,
+ Ocode,
+ Osess,
+};
+
+static Field p_fields[] =
+{
+ {"v", Fnum, Overs, "version", } ,
+ {"t", Fnum, Otype, "type", } ,
+ {"c", Fnum, Ocode, "code" } ,
+ {"s", Fnum, Osess, "sessid" } ,
+ {0}
+};
+
+static void
+p_compilesess(Filter *f)
+{
+// Mux *m;
+
+ if(f->op == '='){
+ compile_cmp(pppoe_sess.name, f, p_fields);
+ return;
+ }
+/*
+ for(m = p_mux; m->name != nil; m++)
+ if(strcmp(f->s, m->name) == 0){
+ f->pr = m->pr;
+ f->ulv = m->val;
+ f->subop = Ot;
+ return;
+ }
+*/
+ sysfatal("unknown pppoe field or protocol: %s", f->s);
+}
+static void
+p_compiledisc(Filter *f)
+{
+// Mux *m;
+
+ if(f->op == '='){
+ compile_cmp(pppoe_disc.name, f, p_fields);
+ return;
+ }
+/*
+ for(m = p_mux; m->name != nil; m++)
+ if(strcmp(f->s, m->name) == 0){
+ f->pr = m->pr;
+ f->ulv = m->val;
+ f->subop = Ot;
+ return;
+ }
+*/
+ sysfatal("unknown pppoe field or protocol: %s", f->s);
+}
+
+static int
+p_filter(Filter *f, Msg *m)
+{
+ Hdr *h;
+
+ if(m->pe - m->ps < HDRSIZE)
+ return 0;
+
+ h = (Hdr*)m->ps;
+ m->ps += HDRSIZE;
+
+ switch(f->subop){
+ case Overs:
+ return (h->verstype>>4) == f->ulv;
+ case Otype:
+ return (h->verstype&0xF) == f->ulv;
+ case Ocode:
+ return h->code == f->ulv;
+ case Osess:
+ return NetS(h->sessid) == f->ulv;
+ }
+ return 0;
+}
+
+/* BUG: print all the discovery types */
+static int
+p_seprintdisc(Msg *m)
+{
+ Hdr *h;
+ int len;
+
+ len = m->pe - m->ps;
+ if(len < HDRSIZE)
+ return -1;
+
+ h = (Hdr*)m->ps;
+ m->ps += HDRSIZE;
+
+ m->pr = nil;
+
+ m->p = seprint(m->p, m->e, "v=%d t=%d c=0x%x s=0x%ux, len=%d",
+ h->verstype>>4, h->verstype&0xF, h->code, NetS(h->sessid), NetS(h->length));
+
+ return 0;
+}
+
+static int
+p_seprintsess(Msg *m)
+{
+ Hdr *h;
+ int len;
+
+ len = m->pe - m->ps;
+ if(len < HDRSIZE)
+ return -1;
+
+ h = (Hdr*)m->ps;
+ m->ps += HDRSIZE;
+
+ /* this will call ppp for me */
+ demux(p_mux, 0, 0, m, &dump);
+
+ m->p = seprint(m->p, m->e, "v=%d t=%d c=0x%x s=0x%ux, len=%d",
+ h->verstype>>4, h->verstype&0xF, h->code, NetS(h->sessid), NetS(h->length));
+
+ return 0;
+}
+
+Proto pppoe_disc =
+{
+ "pppoe_disc",
+ p_compiledisc,
+ p_filter,
+ p_seprintdisc,
+ p_mux,
+ p_fields,
+ defaultframer
+};
+
+Proto pppoe_sess =
+{
+ "pppoe_sess",
+ p_compilesess,
+ p_filter,
+ p_seprintsess,
+ p_mux,
+ p_fields,
+ defaultframer
+};
+
diff --git a/src/cmd/ip/snoopy/pppoe_sess.c b/src/cmd/ip/snoopy/pppoe_sess.c
new file mode 100755
index 00000000..0472a907
--- /dev/null
+++ b/src/cmd/ip/snoopy/pppoe_sess.c
@@ -0,0 +1 @@
+/* placeholder; see pppoe_disc.c */
diff --git a/src/cmd/ip/snoopy/protos.c b/src/cmd/ip/snoopy/protos.c
new file mode 100644
index 00000000..4cf78e27
--- /dev/null
+++ b/src/cmd/ip/snoopy/protos.c
@@ -0,0 +1,33 @@
+#include <u.h>
+#include <libc.h>
+#include "dat.h"
+#include "protos.h"
+Proto *protos[] =
+{
+ &ether,
+ &ip,
+ &ip6,
+ &dump,
+ &arp,
+ &rarp,
+ &udp,
+ &bootp,
+ &dhcp,
+ &hdlc,
+ &rtp,
+ &rtcp,
+ &tcp,
+ &il,
+ &icmp,
+ &icmp6,
+ &ninep,
+ &ospf,
+ &ppp,
+ &ppp_ccp,
+ &ppp_lcp,
+ &ppp_chap,
+ &ppp_ipcp,
+ &pppoe_sess,
+ &pppoe_disc,
+ 0,
+};
diff --git a/src/cmd/ip/snoopy/protos.h b/src/cmd/ip/snoopy/protos.h
new file mode 100644
index 00000000..501b59ec
--- /dev/null
+++ b/src/cmd/ip/snoopy/protos.h
@@ -0,0 +1,25 @@
+extern Proto ether;
+extern Proto ip;
+extern Proto ip6;
+extern Proto dump;
+extern Proto arp;
+extern Proto rarp;
+extern Proto udp;
+extern Proto bootp;
+extern Proto dhcp;
+extern Proto hdlc;
+extern Proto rtp;
+extern Proto rtcp;
+extern Proto tcp;
+extern Proto il;
+extern Proto icmp;
+extern Proto icmp6;
+extern Proto ninep;
+extern Proto ospf;
+extern Proto ppp;
+extern Proto ppp_ccp;
+extern Proto ppp_lcp;
+extern Proto ppp_chap;
+extern Proto ppp_ipcp;
+extern Proto pppoe_sess;
+extern Proto pppoe_disc;
diff --git a/src/cmd/ip/snoopy/rarp.c b/src/cmd/ip/snoopy/rarp.c
new file mode 100755
index 00000000..149c8207
--- /dev/null
+++ b/src/cmd/ip/snoopy/rarp.c
@@ -0,0 +1 @@
+/* place holder, this stuff is really in arp.c */
diff --git a/src/cmd/ip/snoopy/rtcp.c b/src/cmd/ip/snoopy/rtcp.c
new file mode 100755
index 00000000..1036caaf
--- /dev/null
+++ b/src/cmd/ip/snoopy/rtcp.c
@@ -0,0 +1,97 @@
+#include <u.h>
+#include <libc.h>
+#include <ip.h>
+#include "dat.h"
+#include "protos.h"
+
+typedef struct Hdr Hdr;
+struct Hdr {
+ uchar hdr; // RTCP header
+ uchar pt; // Packet type
+ uchar len[2]; // Report length
+ uchar ssrc[4]; // Synchronization source identifier
+ uchar ntp[8]; // NTP time stamp
+ uchar rtp[4]; // RTP time stamp
+ uchar pktc[4]; // Sender's packet count
+ uchar octc[4]; // Sender's octect count
+};
+
+typedef struct Report Report;
+struct Report {
+ uchar ssrc[4]; // SSRC identifier
+ uchar lost[4]; // Fraction + cumu lost
+ uchar seqhi[4]; // Highest seq number received
+ uchar jitter[4]; // Interarrival jitter
+ uchar lsr[4]; // Last SR
+ uchar dlsr[4]; // Delay since last SR
+};
+
+enum{
+ RTCPLEN = 28, // Minimum size of an RTCP header
+ REPORTLEN = 24,
+};
+
+
+static void
+p_compile(Filter *f)
+{
+ sysfatal("unknown rtcp field: %s", f->s);
+}
+
+static int
+p_filter(Filter *f, Msg *m)
+{
+ USED(f);
+ USED(m);
+ return 0;
+}
+
+static int
+p_seprint(Msg *m)
+{
+ Hdr*h;
+ Report*r;
+ int rc, i, frac;
+ float dlsr;
+
+ if(m->pe - m->ps < RTCPLEN)
+ return -1;
+
+ h = (Hdr*)m->ps;
+ if(m->pe - m->ps < (NetS(h->len) + 1) * 4)
+ return -1;
+
+ rc = h->hdr & 0x1f;
+ m->ps += RTCPLEN;
+ m->p = seprint(m->p, m->e, "version=%d rc=%d tp=%d ssrc=%8ux ntp=%d.%.10ud rtp=%d pktc=%d octc=%d hlen=%d",
+ (h->hdr >> 6) & 3, rc, h->pt, NetL(h->ssrc),
+ NetL(h->ntp), (uint)NetL(&h->ntp[4]), NetL(h->rtp),
+ NetL(h->pktc), NetL(h->octc),
+ (NetS(h->len) + 1) * 4);
+
+ for(i = 0; i < rc; i++){
+ r = (Report*)m->ps;
+ m->ps += REPORTLEN;
+
+ frac = (int)(((float)r->lost[0] * 100.) / 256.);
+ r->lost[0] = 0;
+ dlsr = (float)NetL(r->dlsr) / 65536.;
+
+ m->p = seprint(m->p, m->e, "\n\trr(csrc=%8ux frac=%3d%% cumu=%10d seqhi=%10ud jitter=%10d lsr=%8ux dlsr=%f)",
+ NetL(r->ssrc), frac, NetL(r->lost), NetL(r->seqhi),
+ NetL(r->jitter), NetL(r->lsr),
+ dlsr);
+ }
+ m->pr = nil;
+ return 0;
+}
+
+Proto rtcp = {
+ "rtcp",
+ p_compile,
+ p_filter,
+ p_seprint,
+ nil,
+ nil,
+ defaultframer,
+};
diff --git a/src/cmd/ip/snoopy/rtp.c b/src/cmd/ip/snoopy/rtp.c
new file mode 100755
index 00000000..1e8e5ab8
--- /dev/null
+++ b/src/cmd/ip/snoopy/rtp.c
@@ -0,0 +1,76 @@
+#include <u.h>
+#include <libc.h>
+#include <ip.h>
+#include "dat.h"
+#include "protos.h"
+
+typedef struct Hdr Hdr;
+struct Hdr {
+ uchar hdr; // RTP header
+ uchar marker; // Payload and marker
+ uchar seq[2]; // Sequence number
+ uchar ts[4]; // Time stamp
+ uchar ssrc[4]; // Synchronization source identifier
+};
+
+enum{
+ RTPLEN = 12, // Minimum size of an RTP header
+};
+
+
+static void
+p_compile(Filter *f)
+{
+ sysfatal("unknown rtp field: %s", f->s);
+}
+
+static int
+p_filter(Filter *f, Msg *m)
+{
+ USED(f);
+ USED(m);
+ return 0;
+}
+
+static int
+p_seprint(Msg *m)
+{
+ Hdr*h;
+ ushort seq;
+ ulong ssrc, ts;
+ int cc, i;
+
+ if(m->pe - m->ps < RTPLEN)
+ return -1;
+
+ h = (Hdr*)m->ps;
+ cc = h->hdr & 0xf;
+ if(m->pe - m->ps < RTPLEN + cc * 4)
+ return -1;
+
+ m->ps += RTPLEN;
+
+ seq = NetS(h->seq);
+ ts = NetL(h->ts);
+ ssrc = NetL(h->ssrc);
+
+ m->p = seprint(m->p, m->e, "version=%d x=%d cc=%d seq=%d ts=%ld ssrc=%ulx",
+ (h->hdr >> 6) & 3, (h->hdr >> 4) & 1, cc, seq, ts, ssrc);
+ for(i = 0; i < cc; i++){
+ m->p = seprint(m->p, m->e, " csrc[%d]=%d",
+ i, NetL(m->ps));
+ m->ps += 4;
+ }
+ m->pr = nil;
+ return 0;
+}
+
+Proto rtp = {
+ "rtp",
+ p_compile,
+ p_filter,
+ p_seprint,
+ nil,
+ nil,
+ defaultframer,
+};
diff --git a/src/cmd/ip/snoopy/tcp.c b/src/cmd/ip/snoopy/tcp.c
new file mode 100755
index 00000000..31ad5663
--- /dev/null
+++ b/src/cmd/ip/snoopy/tcp.c
@@ -0,0 +1,221 @@
+#include <u.h>
+#include <libc.h>
+#include <ip.h>
+#include "dat.h"
+#include "protos.h"
+
+typedef struct Hdr Hdr;
+struct Hdr
+{
+ uchar sport[2];
+ uchar dport[2];
+ uchar seq[4];
+ uchar ack[4];
+ uchar flag[2];
+ uchar win[2];
+ uchar cksum[2];
+ uchar urg[2];
+ uchar opt[1];
+};
+
+typedef struct PseudoHdr{
+ uchar src[4];
+ uchar dst[4];
+ uchar zero;
+ uchar proto;
+ uchar length[2];
+ uchar hdrdata[1580];
+} PseudoHdr;
+
+enum
+{
+ TCPLEN= 20,
+};
+
+enum
+{
+ Os,
+ Od,
+ Osd,
+};
+
+static Field p_fields[] =
+{
+ {"s", Fnum, Os, "source port", } ,
+ {"d", Fnum, Od, "dest port", } ,
+ {"a", Fnum, Osd, "source/dest port", } ,
+ {"sd", Fnum, Osd, "source/dest port", } ,
+ {0}
+};
+
+static Mux p_mux[] =
+{
+ {"ninep", 17007, }, /* exportfs */
+ {"ninep", 564, }, /* 9fs */
+ {"ninep", 17005, }, /* ocpu */
+ {"ninep", 17010, }, /* ncpu */
+ {"ninep", 17013, }, /* cpu */
+ {0},
+};
+
+enum
+{
+ EOLOPT = 0,
+ NOOPOPT = 1,
+ MSSOPT = 2,
+ MSS_LENGTH = 4, /* Mean segment size */
+ WSOPT = 3,
+ WS_LENGTH = 3, /* Bits to scale window size by */
+};
+
+static void
+p_compile(Filter *f)
+{
+ Mux *m;
+
+ if(f->op == '='){
+ compile_cmp(udp.name, f, p_fields);
+ return;
+ }
+ for(m = p_mux; m->name != nil; m++)
+ if(strcmp(f->s, m->name) == 0){
+ f->pr = m->pr;
+ f->ulv = m->val;
+ f->subop = Osd;
+ return;
+ }
+ sysfatal("unknown tcp field or protocol: %s", f->s);
+}
+
+static int
+p_filter(Filter *f, Msg *m)
+{
+ Hdr *h;
+
+ if(m->pe - m->ps < TCPLEN)
+ return 0;
+
+ h = (Hdr*)m->ps;
+ m->ps += ((NetS(h->flag)>>10)&0x3f);
+
+ switch(f->subop){
+ case Os:
+ return NetS(h->sport) == f->ulv;
+ case Od:
+ return NetS(h->dport) == f->ulv;
+ case Osd:
+ return NetS(h->sport) == f->ulv || NetS(h->dport) == f->ulv;
+ }
+ return 0;
+}
+
+enum
+{
+ URG = 0x20, /* Data marked urgent */
+ ACK = 0x10, /* Aknowledge is valid */
+ PSH = 0x08, /* Whole data pipe is pushed */
+ RST = 0x04, /* Reset connection */
+ SYN = 0x02, /* Pkt. is synchronise */
+ FIN = 0x01, /* Start close down */
+};
+
+static char*
+flags(int f)
+{
+ static char fl[20];
+ char *p;
+
+ p = fl;
+ if(f & URG)
+ *p++ = 'U';
+ if(f & ACK)
+ *p++ = 'A';
+ if(f & PSH)
+ *p++ = 'P';
+ if(f & RST)
+ *p++ = 'R';
+ if(f & SYN)
+ *p++ = 'S';
+ if(f & FIN)
+ *p++ = 'F';
+ *p = 0;
+ return fl;
+}
+
+
+static int
+p_seprint(Msg *m)
+{
+ Hdr *h;
+ int dport, sport;
+ int len, flag, optlen;
+ uchar *optr;
+
+ if(m->pe - m->ps < TCPLEN)
+ return -1;
+ h = (Hdr*)m->ps;
+
+ /* get tcp header length */
+ flag = NetS(h->flag);
+ len = (flag>>10)&~3;
+ flag &= 0x3ff;
+ m->ps += len;
+
+ /* next protocol */
+ dport = NetS(h->dport);
+ sport = NetS(h->sport);
+ demux(p_mux, sport, dport, m, &dump);
+
+ m->p = seprint(m->p, m->e, "s=%d d=%d seq=%lud ack=%lud fl=%s win=%d ck=%4.4ux",
+ NetS(h->sport), dport,
+ (ulong)NetL(h->seq), (ulong)NetL(h->ack),
+ flags(flag), NetS(h->win),
+ NetS(h->cksum));
+
+ /* tcp options */
+ len -= TCPLEN;
+ optr = h->opt;
+ while(len > 0) {
+ if(*optr == EOLOPT){
+ m->p = seprint(m->p, m->e, " opt=EOL");
+ break;
+ }
+ if(*optr == NOOPOPT) {
+ m->p = seprint(m->p, m->e, " opt=NOOP");
+ len--;
+ optr++;
+ continue;
+ }
+ optlen = optr[1];
+ if(optlen < 2 || optlen > len)
+ break;
+ switch(*optr) {
+ case MSSOPT:
+ m->p = seprint(m->p, m->e, " opt%d=(mss %ud)", optlen, nhgets(optr+2));
+ break;
+ case WSOPT:
+ m->p = seprint(m->p, m->e, " opt%d=(wscale %ud)", optlen, *(optr+2));
+ break;
+ default:
+ m->p = seprint(m->p, m->e, " opt%d=(%ud %.*H)", optlen, *optr, optlen-2,optr+2);
+ }
+ len -= optlen;
+ optr += optlen;
+ }
+
+ if(Cflag){
+ // editing in progress by ehg
+ }
+ return 0;
+}
+
+Proto tcp =
+{
+ "tcp",
+ p_compile,
+ p_filter,
+ p_seprint,
+ p_mux,
+ p_fields,
+ defaultframer,
+};
diff --git a/src/cmd/ip/snoopy/udp.c b/src/cmd/ip/snoopy/udp.c
new file mode 100755
index 00000000..43d2b6f6
--- /dev/null
+++ b/src/cmd/ip/snoopy/udp.c
@@ -0,0 +1,131 @@
+#include <u.h>
+#include <libc.h>
+#include <ip.h>
+#include "dat.h"
+#include "protos.h"
+
+typedef struct Hdr Hdr;
+struct Hdr
+{
+ uchar sport[2]; /* Source port */
+ uchar dport[2]; /* Destination port */
+ uchar len[2]; /* data length */
+ uchar cksum[2]; /* Checksum */
+};
+
+enum
+{
+ UDPLEN= 8,
+};
+
+enum
+{
+ Os,
+ Od,
+ Osd,
+ Osetport,
+};
+
+static Field p_fields[] =
+{
+ {"s", Fnum, Os, "source port", } ,
+ {"d", Fnum, Od, "dest port", } ,
+ {"a", Fnum, Osd, "source/dest port", } ,
+ {"sd", Fnum, Osd, "source/dest port", } ,
+ {0}
+};
+
+#define ANYPORT ~0UL
+
+static Mux p_mux[] =
+{
+ {"bootp", 67, },
+ {"ninep", 6346, }, /* tvs */
+ {"rtp", ANYPORT, },
+ {"rtcp", ANYPORT, },
+ {0},
+};
+
+/* default next protocol, can be changed by p_filter, reset by p_compile */
+static Proto *defproto = &dump;
+
+static void
+p_compile(Filter *f)
+{
+ Mux *m;
+
+ if(f->op == '='){
+ compile_cmp(udp.name, f, p_fields);
+ return;
+ }
+ for(m = p_mux; m->name != nil; m++)
+ if(strcmp(f->s, m->name) == 0){
+ f->pr = m->pr;
+ f->ulv = m->val;
+ f->subop = Osd;
+ return;
+ }
+
+ sysfatal("unknown udp field or protocol: %s", f->s);
+}
+
+static int
+p_filter(Filter *f, Msg *m)
+{
+ Hdr *h;
+
+ if(m->pe - m->ps < UDPLEN)
+ return 0;
+
+ h = (Hdr*)m->ps;
+ m->ps += UDPLEN;
+
+ switch(f->subop){
+ case Os:
+ return NetS(h->sport) == f->ulv;
+ case Od:
+ return NetS(h->dport) == f->ulv;
+ case Osd:
+ if(f->ulv == ANYPORT){
+ defproto = f->pr;
+ return 1;
+ }
+ return NetS(h->sport) == f->ulv || NetS(h->dport) == f->ulv;
+ }
+ return 0;
+}
+
+static int
+p_seprint(Msg *m)
+{
+ Hdr *h;
+ int dport, sport;
+
+
+ if(m->pe - m->ps < UDPLEN)
+ return -1;
+ h = (Hdr*)m->ps;
+ m->ps += UDPLEN;
+
+ /* next protocol */
+ sport = NetS(h->sport);
+ dport = NetS(h->dport);
+ demux(p_mux, sport, dport, m, defproto);
+ defproto = &dump;
+
+ m->p = seprint(m->p, m->e, "s=%d d=%d ck=%4.4ux ln=%4d",
+ NetS(h->sport), dport,
+ NetS(h->cksum), NetS(h->len));
+ return 0;
+}
+
+Proto udp =
+{
+ "udp",
+ p_compile,
+ p_filter,
+ p_seprint,
+ p_mux,
+ p_fields,
+ defaultframer,
+};