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