tnew goodies - plan9port - [fork] Plan 9 from user space
 (HTM) git clone git://src.adamsgaard.dk/plan9port
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 87a52e0485d3281ebea6bf4b725aa8023690e96f
 (DIR) parent 35d26aa32167e84326cdb745c0e906393b8de71d
 (HTM) Author: rsc <devnull@localhost>
       Date:   Mon, 26 Dec 2005 04:48:52 +0000
       
       new goodies
       
       Diffstat:
         A src/cmd/ip/dhcp.h                   |     151 +++++++++++++++++++++++++++++++
         A src/cmd/ip/dhcpd/dat.h              |      85 +++++++++++++++++++++++++++++++
         A src/cmd/ip/dhcpd/db.c               |     452 +++++++++++++++++++++++++++++++
         A src/cmd/ip/dhcpd/dhcpd.c            |    1632 +++++++++++++++++++++++++++++++
         A src/cmd/ip/dhcpd/dhcpleases.c       |      43 ++++++++++++++++++++++++++++++
         A src/cmd/ip/dhcpd/mkfile             |      22 ++++++++++++++++++++++
         A src/cmd/ip/dhcpd/ndb.c              |     318 +++++++++++++++++++++++++++++++
         A src/cmd/ip/dhcpd/ping.c             |     112 +++++++++++++++++++++++++++++++
         A src/cmd/ip/dhcpd/testlook.c         |     222 ++++++++++++++++++++++++++++++
         A src/cmd/ip/dhcpd/testlookup.c       |     168 +++++++++++++++++++++++++++++++
         A src/cmd/ip/dhcpd/testping.c         |      22 ++++++++++++++++++++++
         A src/cmd/ip/snoopy/Linux.c           |      58 ++++++++++++++++++++++++++++++
         A src/cmd/ip/snoopy/arp.c             |     128 +++++++++++++++++++++++++++++++
         A src/cmd/ip/snoopy/bootp.c           |     176 +++++++++++++++++++++++++++++++
         A src/cmd/ip/snoopy/dat.h             |     106 ++++++++++++++++++++++++++++++
         A src/cmd/ip/snoopy/dhcp.c            |     483 +++++++++++++++++++++++++++++++
         A src/cmd/ip/snoopy/dump.c            |      92 +++++++++++++++++++++++++++++++
         A src/cmd/ip/snoopy/ether.c           |     121 +++++++++++++++++++++++++++++++
         A src/cmd/ip/snoopy/gre.c             |      83 +++++++++++++++++++++++++++++++
         A src/cmd/ip/snoopy/hdlc.c            |     174 +++++++++++++++++++++++++++++++
         A src/cmd/ip/snoopy/icmp.c            |     196 +++++++++++++++++++++++++++++++
         A src/cmd/ip/snoopy/icmp6.c           |     428 +++++++++++++++++++++++++++++++
         A src/cmd/ip/snoopy/il.c              |     146 +++++++++++++++++++++++++++++++
         A src/cmd/ip/snoopy/ip.c              |     236 +++++++++++++++++++++++++++++++
         A src/cmd/ip/snoopy/ip6.c             |     309 +++++++++++++++++++++++++++++++
         A src/cmd/ip/snoopy/main.c            |     841 +++++++++++++++++++++++++++++++
         A src/cmd/ip/snoopy/mkfile            |      69 ++++++++++++++++++++++++++++++
         A src/cmd/ip/snoopy/ninep.c           |      55 +++++++++++++++++++++++++++++++
         A src/cmd/ip/snoopy/ospf.c            |     404 +++++++++++++++++++++++++++++++
         A src/cmd/ip/snoopy/ppp.c             |     629 +++++++++++++++++++++++++++++++
         A src/cmd/ip/snoopy/ppp_ccp.c         |       1 +
         A src/cmd/ip/snoopy/ppp_chap.c        |       1 +
         A src/cmd/ip/snoopy/ppp_comp.c        |       1 +
         A src/cmd/ip/snoopy/ppp_ipcp.c        |       1 +
         A src/cmd/ip/snoopy/ppp_lcp.c         |       1 +
         A src/cmd/ip/snoopy/pppoe_disc.c      |     172 ++++++++++++++++++++++++++++++
         A src/cmd/ip/snoopy/pppoe_sess.c      |       1 +
         A src/cmd/ip/snoopy/protos.c          |      33 +++++++++++++++++++++++++++++++
         A src/cmd/ip/snoopy/protos.h          |      25 +++++++++++++++++++++++++
         A src/cmd/ip/snoopy/rarp.c            |       1 +
         A src/cmd/ip/snoopy/rtcp.c            |      97 ++++++++++++++++++++++++++++++
         A src/cmd/ip/snoopy/rtp.c             |      76 +++++++++++++++++++++++++++++++
         A src/cmd/ip/snoopy/tcp.c             |     221 +++++++++++++++++++++++++++++++
         A src/cmd/ip/snoopy/udp.c             |     131 +++++++++++++++++++++++++++++++
       
       44 files changed, 8723 insertions(+), 0 deletions(-)
       ---
 (DIR) diff --git a/src/cmd/ip/dhcp.h b/src/cmd/ip/dhcp.h
       t@@ -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];
       +};
 (DIR) diff --git a/src/cmd/ip/dhcpd/dat.h b/src/cmd/ip/dhcpd/dat.h
       t@@ -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;
       +
 (DIR) diff --git a/src/cmd/ip/dhcpd/db.c b/src/cmd/ip/dhcpd/db.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/ip/dhcpd/dhcpd.c b/src/cmd/ip/dhcpd/dhcpd.c
       t@@ -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);
       +}
 (DIR) diff --git a/src/cmd/ip/dhcpd/dhcpleases.c b/src/cmd/ip/dhcpd/dhcpleases.c
       t@@ -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));
       +        }                
       +}
 (DIR) diff --git a/src/cmd/ip/dhcpd/mkfile b/src/cmd/ip/dhcpd/mkfile
       t@@ -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
       +
 (DIR) diff --git a/src/cmd/ip/dhcpd/ndb.c b/src/cmd/ip/dhcpd/ndb.c
       t@@ -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);
       +}
 (DIR) diff --git a/src/cmd/ip/dhcpd/ping.c b/src/cmd/ip/dhcpd/ping.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/ip/dhcpd/testlook.c b/src/cmd/ip/dhcpd/testlook.c
       t@@ -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);
       +}
 (DIR) diff --git a/src/cmd/ip/dhcpd/testlookup.c b/src/cmd/ip/dhcpd/testlookup.c
       t@@ -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]);
       +}
 (DIR) diff --git a/src/cmd/ip/dhcpd/testping.c b/src/cmd/ip/dhcpd/testping.c
       t@@ -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]);
       +}
 (DIR) diff --git a/src/cmd/ip/snoopy/Linux.c b/src/cmd/ip/snoopy/Linux.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/ip/snoopy/arp.c b/src/cmd/ip/snoopy/arp.c
       t@@ -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,
       +};
 (DIR) diff --git a/src/cmd/ip/snoopy/bootp.c b/src/cmd/ip/snoopy/bootp.c
       t@@ -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,
       +};
 (DIR) diff --git a/src/cmd/ip/snoopy/dat.h b/src/cmd/ip/snoopy/dat.h
       t@@ -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;
 (DIR) diff --git a/src/cmd/ip/snoopy/dhcp.c b/src/cmd/ip/snoopy/dhcp.c
       t@@ -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,
       +};
       +
 (DIR) diff --git a/src/cmd/ip/snoopy/dump.c b/src/cmd/ip/snoopy/dump.c
       t@@ -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,
       +};
 (DIR) diff --git a/src/cmd/ip/snoopy/ether.c b/src/cmd/ip/snoopy/ether.c
       t@@ -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
       +};
 (DIR) diff --git a/src/cmd/ip/snoopy/gre.c b/src/cmd/ip/snoopy/gre.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/ip/snoopy/hdlc.c b/src/cmd/ip/snoopy/hdlc.c
       t@@ -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,
       +};
 (DIR) diff --git a/src/cmd/ip/snoopy/icmp.c b/src/cmd/ip/snoopy/icmp.c
       t@@ -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,
       +};
 (DIR) diff --git a/src/cmd/ip/snoopy/icmp6.c b/src/cmd/ip/snoopy/icmp6.c
       t@@ -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,
       +};
 (DIR) diff --git a/src/cmd/ip/snoopy/il.c b/src/cmd/ip/snoopy/il.c
       t@@ -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,
       +};
 (DIR) diff --git a/src/cmd/ip/snoopy/ip.c b/src/cmd/ip/snoopy/ip.c
       t@@ -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,
       +};
 (DIR) diff --git a/src/cmd/ip/snoopy/ip6.c b/src/cmd/ip/snoopy/ip6.c
       t@@ -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,
       +};
 (DIR) diff --git a/src/cmd/ip/snoopy/main.c b/src/cmd/ip/snoopy/main.c
       t@@ -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);
       +}
 (DIR) diff --git a/src/cmd/ip/snoopy/mkfile b/src/cmd/ip/snoopy/mkfile
       t@@ -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
 (DIR) diff --git a/src/cmd/ip/snoopy/ninep.c b/src/cmd/ip/snoopy/ninep.c
       t@@ -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,
       +};
 (DIR) diff --git a/src/cmd/ip/snoopy/ospf.c b/src/cmd/ip/snoopy/ospf.c
       t@@ -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,
       +};
 (DIR) diff --git a/src/cmd/ip/snoopy/ppp.c b/src/cmd/ip/snoopy/ppp.c
       t@@ -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,
       +};
 (DIR) diff --git a/src/cmd/ip/snoopy/ppp_ccp.c b/src/cmd/ip/snoopy/ppp_ccp.c
       t@@ -0,0 +1 @@
       +/* place holder, this stuff is really in ppp.c */
 (DIR) diff --git a/src/cmd/ip/snoopy/ppp_chap.c b/src/cmd/ip/snoopy/ppp_chap.c
       t@@ -0,0 +1 @@
       +/* place holder, this stuff is really in ppp.c */
 (DIR) diff --git a/src/cmd/ip/snoopy/ppp_comp.c b/src/cmd/ip/snoopy/ppp_comp.c
       t@@ -0,0 +1 @@
       +/* place holder, this stuff is really in ppp.c */
 (DIR) diff --git a/src/cmd/ip/snoopy/ppp_ipcp.c b/src/cmd/ip/snoopy/ppp_ipcp.c
       t@@ -0,0 +1 @@
       +/* place holder, this stuff is really in ppp.c */
 (DIR) diff --git a/src/cmd/ip/snoopy/ppp_lcp.c b/src/cmd/ip/snoopy/ppp_lcp.c
       t@@ -0,0 +1 @@
       +/* place holder, this stuff is really in ppp.c */
 (DIR) diff --git a/src/cmd/ip/snoopy/pppoe_disc.c b/src/cmd/ip/snoopy/pppoe_disc.c
       t@@ -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
       +};
       +
 (DIR) diff --git a/src/cmd/ip/snoopy/pppoe_sess.c b/src/cmd/ip/snoopy/pppoe_sess.c
       t@@ -0,0 +1 @@
       +/* placeholder; see pppoe_disc.c */
 (DIR) diff --git a/src/cmd/ip/snoopy/protos.c b/src/cmd/ip/snoopy/protos.c
       t@@ -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,
       +};
 (DIR) diff --git a/src/cmd/ip/snoopy/protos.h b/src/cmd/ip/snoopy/protos.h
       t@@ -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;
 (DIR) diff --git a/src/cmd/ip/snoopy/rarp.c b/src/cmd/ip/snoopy/rarp.c
       t@@ -0,0 +1 @@
       +/* place holder, this stuff is really in arp.c */
 (DIR) diff --git a/src/cmd/ip/snoopy/rtcp.c b/src/cmd/ip/snoopy/rtcp.c
       t@@ -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,
       +};
 (DIR) diff --git a/src/cmd/ip/snoopy/rtp.c b/src/cmd/ip/snoopy/rtp.c
       t@@ -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,
       +};
 (DIR) diff --git a/src/cmd/ip/snoopy/tcp.c b/src/cmd/ip/snoopy/tcp.c
       t@@ -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,
       +};
 (DIR) diff --git a/src/cmd/ip/snoopy/udp.c b/src/cmd/ip/snoopy/udp.c
       t@@ -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,
       +};