tnew goodies - plan9port - [fork] Plan 9 from user space | |
git clone git://src.adamsgaard.dk/plan9port | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
commit 87a52e0485d3281ebea6bf4b725aa8023690e96f | |
parent 35d26aa32167e84326cdb745c0e906393b8de71d | |
Author: rsc <devnull@localhost> | |
Date: Mon, 26 Dec 2005 04:48:52 +0000 | |
new goodies | |
Diffstat: | |
A src/cmd/ip/dhcp.h | 151 +++++++++++++++++++++++++++++… | |
A src/cmd/ip/dhcpd/dat.h | 85 +++++++++++++++++++++++++++++… | |
A src/cmd/ip/dhcpd/db.c | 452 +++++++++++++++++++++++++++++… | |
A src/cmd/ip/dhcpd/dhcpd.c | 1632 +++++++++++++++++++++++++++++… | |
A src/cmd/ip/dhcpd/dhcpleases.c | 43 ++++++++++++++++++++++++++++++ | |
A src/cmd/ip/dhcpd/mkfile | 22 ++++++++++++++++++++++ | |
A src/cmd/ip/dhcpd/ndb.c | 318 +++++++++++++++++++++++++++++… | |
A src/cmd/ip/dhcpd/ping.c | 112 +++++++++++++++++++++++++++++… | |
A src/cmd/ip/dhcpd/testlook.c | 222 ++++++++++++++++++++++++++++++ | |
A src/cmd/ip/dhcpd/testlookup.c | 168 +++++++++++++++++++++++++++++… | |
A src/cmd/ip/dhcpd/testping.c | 22 ++++++++++++++++++++++ | |
A src/cmd/ip/snoopy/Linux.c | 58 ++++++++++++++++++++++++++++++ | |
A src/cmd/ip/snoopy/arp.c | 128 +++++++++++++++++++++++++++++… | |
A src/cmd/ip/snoopy/bootp.c | 176 +++++++++++++++++++++++++++++… | |
A src/cmd/ip/snoopy/dat.h | 106 ++++++++++++++++++++++++++++++ | |
A src/cmd/ip/snoopy/dhcp.c | 483 +++++++++++++++++++++++++++++… | |
A src/cmd/ip/snoopy/dump.c | 92 +++++++++++++++++++++++++++++… | |
A src/cmd/ip/snoopy/ether.c | 121 +++++++++++++++++++++++++++++… | |
A src/cmd/ip/snoopy/gre.c | 83 +++++++++++++++++++++++++++++… | |
A src/cmd/ip/snoopy/hdlc.c | 174 +++++++++++++++++++++++++++++… | |
A src/cmd/ip/snoopy/icmp.c | 196 +++++++++++++++++++++++++++++… | |
A src/cmd/ip/snoopy/icmp6.c | 428 +++++++++++++++++++++++++++++… | |
A src/cmd/ip/snoopy/il.c | 146 +++++++++++++++++++++++++++++… | |
A src/cmd/ip/snoopy/ip.c | 236 +++++++++++++++++++++++++++++… | |
A src/cmd/ip/snoopy/ip6.c | 309 +++++++++++++++++++++++++++++… | |
A src/cmd/ip/snoopy/main.c | 841 +++++++++++++++++++++++++++++… | |
A src/cmd/ip/snoopy/mkfile | 69 ++++++++++++++++++++++++++++++ | |
A src/cmd/ip/snoopy/ninep.c | 55 +++++++++++++++++++++++++++++… | |
A src/cmd/ip/snoopy/ospf.c | 404 +++++++++++++++++++++++++++++… | |
A src/cmd/ip/snoopy/ppp.c | 629 +++++++++++++++++++++++++++++… | |
A src/cmd/ip/snoopy/ppp_ccp.c | 1 + | |
A src/cmd/ip/snoopy/ppp_chap.c | 1 + | |
A src/cmd/ip/snoopy/ppp_comp.c | 1 + | |
A src/cmd/ip/snoopy/ppp_ipcp.c | 1 + | |
A src/cmd/ip/snoopy/ppp_lcp.c | 1 + | |
A src/cmd/ip/snoopy/pppoe_disc.c | 172 ++++++++++++++++++++++++++++++ | |
A src/cmd/ip/snoopy/pppoe_sess.c | 1 + | |
A src/cmd/ip/snoopy/protos.c | 33 +++++++++++++++++++++++++++++… | |
A src/cmd/ip/snoopy/protos.h | 25 +++++++++++++++++++++++++ | |
A src/cmd/ip/snoopy/rarp.c | 1 + | |
A src/cmd/ip/snoopy/rtcp.c | 97 ++++++++++++++++++++++++++++++ | |
A src/cmd/ip/snoopy/rtp.c | 76 +++++++++++++++++++++++++++++… | |
A src/cmd/ip/snoopy/tcp.c | 221 +++++++++++++++++++++++++++++… | |
A src/cmd/ip/snoopy/udp.c | 131 +++++++++++++++++++++++++++++… | |
44 files changed, 8723 insertions(+), 0 deletions(-) | |
--- | |
diff --git a/src/cmd/ip/dhcp.h b/src/cmd/ip/dhcp.h | |
t@@ -0,0 +1,151 @@ | |
+ | |
+enum | |
+{ | |
+ OfferTimeout= 60, /* when an offer times out */ | |
+ MaxLease= 60*60, /* longest lease for dynamic bi… | |
+ MinLease= 15*60, /* shortest lease for dynamic b… | |
+ StaticLease= 30*60, /* lease for static binding … | |
+ | |
+ IPUDPHDRSIZE= 28, /* size of an IP plus UDP head… | |
+ MINSUPPORTED= 576, /* biggest IP message the cli… | |
+ | |
+ /* 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 b… | |
+ uchar flags[2]; | |
+ uchar ciaddr[IPv4addrlen]; /* client IP address (client … | |
+ uchar yiaddr[IPv4addrlen]; /* client IP address (server … | |
+ 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 | |
t@@ -0,0 +1,85 @@ | |
+#include "../dhcp.h" | |
+ | |
+enum | |
+{ | |
+ Maxstr= 256, | |
+}; | |
+ | |
+typedef struct Binding Binding; | |
+struct Binding | |
+{ | |
+ Binding *next; | |
+ uchar ip[IPaddrlen]; | |
+ | |
+ char *boundto; /* id last bound to */ | |
+ char *offeredto; /* id we've offered this to */ | |
+ | |
+ long lease; /* absolute time at which binding ex… | |
+ long expoffer; /* absolute time at which offer times out… | |
+ long offer; /* lease offered */ | |
+ long lasttouched; /* time this entry last assigned/unass… | |
+ long lastcomplained; /* last time we complained about a … | |
+ 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 & … | |
+ 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 addre… | |
+ char rootpath[Maxstr]; /* rootfs for diskless nfs client… | |
+ 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 | |
t@@ -0,0 +1,452 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <ip.h> | |
+#include <bio.h> | |
+#include <ndb.h> | |
+#include <ctype.h> | |
+#include "dat.h" | |
+ | |
+/* | |
+ * format of a binding entry: | |
+ * char ipaddr[32]; | |
+ * char id[32]; | |
+ * char hwa[32]; | |
+ * char otime[10]; | |
+ */ | |
+Binding *bcache; | |
+uchar bfirst[IPaddrlen]; | |
+char *binddir = nil; | |
+char *xbinddir = "#9/ndb/dhcp"; | |
+ | |
+/* | |
+ * convert a byte array to hex | |
+ */ | |
+static char | |
+hex(int x) | |
+{ | |
+ if(x < 10) | |
+ return x + '0'; | |
+ return x - 10 + 'a'; | |
+} | |
+extern char* | |
+tohex(char *hdr, uchar *p, int len) | |
+{ | |
+ char *s, *sp; | |
+ int hlen; | |
+ | |
+ hlen = strlen(hdr); | |
+ s = malloc(hlen + 2*len + 1); | |
+ sp = s; | |
+ strcpy(sp, hdr); | |
+ sp += hlen; | |
+ for(; len > 0; len--){ | |
+ *sp++ = hex(*p>>4); | |
+ *sp++ = hex(*p & 0xf); | |
+ p++; | |
+ } | |
+ *sp = 0; | |
+ return s; | |
+} | |
+ | |
+/* | |
+ * convert a client id to a string. If it's already | |
+ * ascii, leave it be. Otherwise, convert it to hex. | |
+ */ | |
+extern char* | |
+toid(uchar *p, int n) | |
+{ | |
+ int i; | |
+ char *s; | |
+ | |
+ for(i = 0; i < n; i++) | |
+ if(!isprint(p[i])) | |
+ return tohex("id", p, n); | |
+ s = malloc(n + 1); | |
+ memmove(s, p, n); | |
+ s[n] = 0; | |
+ return s; | |
+} | |
+ | |
+/* | |
+ * increment an ip address | |
+ */ | |
+static void | |
+incip(uchar *ip) | |
+{ | |
+ int i, x; | |
+ | |
+ for(i = IPaddrlen-1; i >= 0; i--){ | |
+ x = ip[i]; | |
+ x++; | |
+ ip[i] = x; | |
+ if((x & 0x100) == 0) | |
+ break; | |
+ } | |
+} | |
+ | |
+/* | |
+ * find a binding for an id or hardware address | |
+ */ | |
+static int | |
+lockopen(char *file) | |
+{ | |
+ char err[ERRMAX]; | |
+ int fd, tries; | |
+ | |
+ for(tries = 0; tries < 5; tries++){ | |
+ fd = open(file, OLOCK|ORDWR); | |
+ if(fd >= 0) | |
+ return fd; | |
+print("open %s: %r\n", file); | |
+ errstr(err, sizeof err); | |
+ if(strstr(err, "lock")){ | |
+ /* wait for other process to let go of lock */ | |
+ sleep(250); | |
+ | |
+ /* try again */ | |
+ continue; | |
+ } | |
+ if(strstr(err, "exist") || strstr(err, "No such")){ | |
+ /* no file, create an exclusive access file */ | |
+ fd = create(file, ORDWR, DMEXCL|0666); | |
+ chmod(file, 0666); | |
+ if(fd >= 0) | |
+ return fd; | |
+ } | |
+ } | |
+ return -1; | |
+} | |
+ | |
+void | |
+setbinding(Binding *b, char *id, long t) | |
+{ | |
+ if(b->boundto) | |
+ free(b->boundto); | |
+ | |
+ b->boundto = strdup(id); | |
+ b->lease = t; | |
+} | |
+ | |
+static void | |
+parsebinding(Binding *b, char *buf) | |
+{ | |
+ long t; | |
+ char *id, *p; | |
+ | |
+ /* parse */ | |
+ t = atoi(buf); | |
+ id = strchr(buf, '\n'); | |
+ if(id){ | |
+ *id++ = 0; | |
+ p = strchr(id, '\n'); | |
+ if(p) | |
+ *p = 0; | |
+ } else | |
+ id = ""; | |
+ | |
+ /* replace any past info */ | |
+ setbinding(b, id, t); | |
+} | |
+ | |
+static int | |
+writebinding(int fd, Binding *b) | |
+{ | |
+ Dir *d; | |
+ | |
+ seek(fd, 0, 0); | |
+ if(fprint(fd, "%ld\n%s\n", b->lease, b->boundto) < 0) | |
+ return -1; | |
+ d = dirfstat(fd); | |
+ if(d == nil) | |
+ return -1; | |
+ b->q.type = d->qid.type; | |
+ b->q.path = d->qid.path; | |
+ b->q.vers = d->qid.vers; | |
+ free(d); | |
+ return 0; | |
+} | |
+ | |
+/* | |
+ * synchronize cached binding with file. the file always wins. | |
+ */ | |
+int | |
+syncbinding(Binding *b, int returnfd) | |
+{ | |
+ char buf[512]; | |
+ int i, fd; | |
+ Dir *d; | |
+ | |
+ if(binddir == nil) | |
+ binddir = unsharp(xbinddir); | |
+ | |
+ snprint(buf, sizeof(buf), "%s/%I", binddir, b->ip); | |
+ fd = lockopen(buf); | |
+ if(fd < 0){ | |
+ /* assume someone else is using it */ | |
+ b->lease = time(0) + OfferTimeout; | |
+ return -1; | |
+ } | |
+ | |
+ /* reread if changed */ | |
+ d = dirfstat(fd); | |
+ if(d != nil) /* BUG? */ | |
+ if(d->qid.type != b->q.type || d->qid.path != b->q.path || d->qid.vers… | |
+ 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 us… | |
+ 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->expo… | |
+ if(b->tried != now) | |
+ if(b->lease < now && b->expoffer < now && samenet(b->i… | |
+ if(oldest == nil || b->lasttouched < oldesttime){ | |
+ /* sync and check again */ | |
+ syncbinding(b, 0); | |
+ if(b->lease < now && b->expoffer < now && same… | |
+ if(oldest == nil || b->lasttouched < oldesttim… | |
+ 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-… | |
+ /* 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,… | |
+ if(b->lease < now | |
+ || (b->boundto && strcmp(b->boundto, b->offeredto) == … | |
+ 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) !=… | |
+ 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 | |
t@@ -0,0 +1,1632 @@ | |
+#include <u.h> | |
+#include <sys/socket.h> | |
+#include <net/if_arp.h> | |
+#include <netinet/ip.h> | |
+#include <sys/ioctl.h> | |
+#include <libc.h> | |
+#include <ip.h> | |
+#include <bio.h> | |
+#include <ndb.h> | |
+#include "dat.h" | |
+ | |
+int bwfd; | |
+int wfd; | |
+ | |
+// | |
+// ala rfc2131 | |
+// | |
+ | |
+typedef struct Req Req; | |
+struct Req | |
+{ | |
+ int fd; /* for reply */ | |
+ Bootp *bp; | |
+ Udphdr uh; | |
+ Udphdr *up; | |
+ uchar *e; /* end of received message */ | |
+ uchar *p; /* options pointer */ | |
+ uchar *max; /* max end of reply */ | |
+ | |
+ /* expanded to v6 */ | |
+ uchar ciaddr[IPaddrlen]; | |
+ uchar giaddr[IPaddrlen]; | |
+ | |
+ /* parsed options */ | |
+ int p9request; /* true if this is a bootp with p… | |
+ int genrequest; /* true if this is a bootp with … | |
+ 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 ser… | |
+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 minlea… | |
+ 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)… | |
+ rp->p9request = 1; | |
+ if(memcmp(rp->bp->optmagic, genericopt, sizeof(rp->bp->optmagi… | |
+ 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 cur… | |
+ * binding, ELSE | |
+ * | |
+ * o The client's previous address as recorded in the client's (n… | |
+ * expired or released) binding, if that address is in the serv… | |
+ * pool of available addresses and not already allocated, ELSE | |
+ * | |
+ * o The address requested in the 'Requested IP Address' option, … | |
+ * 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 … | |
+ * the message was received (if 'giaddr' is 0) or on the addres… | |
+ * the relay agent that forwarded the message ('giaddr' when no… | |
+ */ | |
+ 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… | |
+ 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 … | |
+ rp->id, rp->gii.ipaddr, rp->ip, rp->bp… | |
+ 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 th… | |
+ * 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.ipa… | |
+ 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->lpo… | |
+ 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.ipa… | |
+ 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… | |
+ 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, respon… | |
+ 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+IPv4o… | |
+ 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)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->… | |
+ 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… | |
+} | |
+ | |
+/* | |
+ * 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 | |
t@@ -0,0 +1,43 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <bio.h> | |
+#include <ndb.h> | |
+#include <ip.h> | |
+#include "dat.h" | |
+ | |
+extern char *binddir; | |
+ long now; | |
+ char *blog = "ipboot"; | |
+ int minlease = MinLease; | |
+ | |
+void | |
+main(void) | |
+{ | |
+ Dir *all; | |
+ int i, nall, fd; | |
+ Binding b; | |
+ | |
+ fmtinstall('E', eipfmt); | |
+ fmtinstall('I', eipfmt); | |
+ fmtinstall('V', eipfmt); | |
+ fmtinstall('M', eipfmt); | |
+ | |
+ fd = open(binddir, OREAD); | |
+ if(fd < 0) | |
+ sysfatal("opening %s: %r", binddir); | |
+ nall = dirreadall(fd, &all); | |
+ if(nall < 0) | |
+ sysfatal("reading %s: %r", binddir); | |
+ close(fd); | |
+ | |
+ b.boundto = 0; | |
+ b.lease = b.offer = 0; | |
+ now = time(0); | |
+ for(i = 0; i < nall; i++){ | |
+ parseip(b.ip, all[i].name); | |
+ if(syncbinding(&b, 0) < 0) | |
+ continue; | |
+ if(b.lease > now) | |
+ print("%I leased by %s until %s", b.ip, b.boundto, cti… | |
+ } | |
+} | |
diff --git a/src/cmd/ip/dhcpd/mkfile b/src/cmd/ip/dhcpd/mkfile | |
t@@ -0,0 +1,22 @@ | |
+<$PLAN9/src/mkhdr | |
+ | |
+TARG=dhcpd\ | |
+ dhcpleases\ | |
+ | |
+DOFILES=\ | |
+ db.$O\ | |
+ ndb.$O\ | |
+ ping.$O\ | |
+ | |
+IOFILES=\ | |
+ db.$O\ | |
+ ping.$O\ | |
+ | |
+HFILES=dat.h ../dhcp.h | |
+ | |
+<$PLAN9/src/mkmany | |
+ | |
+$O.dhcpd: $DOFILES | |
+$O.dhcpleases: $IOFILES | |
+$O.testping: ping.$O | |
+ | |
diff --git a/src/cmd/ip/dhcpd/ndb.c b/src/cmd/ip/dhcpd/ndb.c | |
t@@ -0,0 +1,318 @@ | |
+/* | |
+ * this currently only works for ethernet bootp's -- presotto | |
+ */ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <ip.h> | |
+#include <bio.h> | |
+#include <ndb.h> | |
+#include "dat.h" | |
+ | |
+static void check72(Info *iip); | |
+ | |
+Ndb *db; | |
+char *ndbfile; | |
+ | |
+Iplifc* | |
+findlifc(uchar *ip) | |
+{ | |
+ uchar x[IPaddrlen]; | |
+ Ipifc *ifc; | |
+ Iplifc *lifc; | |
+ | |
+ for(ifc = ipifcs; ifc; ifc = ifc->next){ | |
+ for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){ | |
+ if(lifc->net[0] == 0) | |
+ continue; | |
+ maskip(ip, lifc->mask, x); | |
+ if(memcmp(x, lifc->net, IPaddrlen) == 0) | |
+ return lifc; | |
+ } | |
+ } | |
+ return nil; | |
+} | |
+ | |
+int | |
+forme(uchar *ip) | |
+{ | |
+ Ipifc *ifc; | |
+ Iplifc *lifc; | |
+ | |
+extern uchar xmyipaddr[IPaddrlen]; | |
+ | |
+if(memcmp(ip, xmyipaddr, IPaddrlen) == 0) | |
+ return 1; | |
+ | |
+ for(ifc = ipifcs; ifc; ifc = ifc->next){ | |
+ for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next) | |
+ if(memcmp(ip, lifc->ip, IPaddrlen) == 0) | |
+ return 1; | |
+ } | |
+ return 0; | |
+} | |
+ | |
+uchar noetheraddr[6]; | |
+ | |
+static void | |
+setipaddr(uchar *addr, char *ip) | |
+{ | |
+ if(ipcmp(addr, IPnoaddr) == 0) | |
+ parseip(addr, ip); | |
+} | |
+ | |
+static void | |
+setipmask(uchar *mask, char *ip) | |
+{ | |
+ if(ipcmp(mask, IPnoaddr) == 0) | |
+ parseipmask(mask, ip); | |
+} | |
+ | |
+/* | |
+ * do an ipinfo with defaults | |
+ */ | |
+int | |
+lookupip(uchar *ipaddr, Info *iip, int gate) | |
+{ | |
+ char ip[32]; | |
+ Ndbtuple *t, *nt; | |
+ char *attrs[32], **p; | |
+ | |
+ if(db == 0) | |
+ db = ndbopen(ndbfile); | |
+ if(db == 0){ | |
+ fprint(2, "can't open db\n"); | |
+ return -1; | |
+ } | |
+ | |
+ p = attrs; | |
+ *p++ = "ip"; | |
+ *p++ = "ipmask"; | |
+ *p++ = "@ipgw"; | |
+ if(!gate){ | |
+ *p++ = "bootf"; | |
+ *p++ = "bootf2"; | |
+ *p++ = "@tftp"; | |
+ *p++ = "@tftp2"; | |
+ *p++ = "rootpath"; | |
+ *p++ = "dhcp"; | |
+ *p++ = "vendorclass"; | |
+ *p++ = "ether"; | |
+ *p++ = "dom"; | |
+ *p++ = "@fs"; | |
+ *p++ = "@auth"; | |
+ } | |
+ *p = 0; | |
+ | |
+ memset(iip, 0, sizeof(*iip)); | |
+ snprint(ip, sizeof(ip), "%I", ipaddr); | |
+ t = ndbipinfo(db, "ip", ip, attrs, p - attrs); | |
+ if(t == nil) | |
+ return -1; | |
+ | |
+ for(nt = t; nt != nil; nt = nt->entry){ | |
+ if(strcmp(nt->attr, "ip") == 0) | |
+ setipaddr(iip->ipaddr, nt->val); | |
+ else | |
+ if(strcmp(nt->attr, "ipmask") == 0) | |
+ setipmask(iip->ipmask, nt->val); | |
+ else | |
+ if(strcmp(nt->attr, "fs") == 0) | |
+ setipaddr(iip->fsip, nt->val); | |
+ else | |
+ if(strcmp(nt->attr, "auth") == 0) | |
+ setipaddr(iip->auip, nt->val); | |
+ else | |
+ if(strcmp(nt->attr, "tftp") == 0) | |
+ setipaddr(iip->tftp, nt->val); | |
+ else | |
+ if(strcmp(nt->attr, "tftp2") == 0) | |
+ setipaddr(iip->tftp2, nt->val); | |
+ else | |
+ if(strcmp(nt->attr, "ipgw") == 0) | |
+ setipaddr(iip->gwip, nt->val); | |
+ else | |
+ if(strcmp(nt->attr, "ether") == 0){ | |
+ if(memcmp(iip->etheraddr, noetheraddr, 6) == 0) | |
+ parseether(iip->etheraddr, nt->val); | |
+ iip->indb = 1; | |
+ } | |
+ else | |
+ if(strcmp(nt->attr, "dhcp") == 0){ | |
+ if(iip->dhcpgroup[0] == 0) | |
+ strcpy(iip->dhcpgroup, nt->val); | |
+ } | |
+ else | |
+ if(strcmp(nt->attr, "bootf") == 0){ | |
+ if(iip->bootf[0] == 0) | |
+ strcpy(iip->bootf, nt->val); | |
+ } | |
+ else | |
+ if(strcmp(nt->attr, "bootf2") == 0){ | |
+ if(iip->bootf2[0] == 0) | |
+ strcpy(iip->bootf2, nt->val); | |
+ } | |
+ else | |
+ if(strcmp(nt->attr, "vendor") == 0){ | |
+ if(iip->vendor[0] == 0) | |
+ strcpy(iip->vendor, nt->val); | |
+ } | |
+ else | |
+ if(strcmp(nt->attr, "dom") == 0){ | |
+ if(iip->domain[0] == 0) | |
+ strcpy(iip->domain, nt->val); | |
+ } | |
+ else | |
+ if(strcmp(nt->attr, "rootpath") == 0){ | |
+ if(iip->rootpath[0] == 0) | |
+ strcpy(iip->rootpath, nt->val); | |
+ } | |
+ } | |
+ ndbfree(t); | |
+ maskip(iip->ipaddr, iip->ipmask, iip->ipnet); | |
+ return 0; | |
+} | |
+ | |
+static uchar zeroes[6]; | |
+ | |
+/* | |
+ * lookup info about a client in the database. Find an address on the | |
+ * same net as riip. | |
+ */ | |
+int | |
+lookup(Bootp *bp, Info *iip, Info *riip) | |
+{ | |
+ Ndbtuple *t, *nt; | |
+ Ndbs s; | |
+ char *hwattr; | |
+ char *hwval, hwbuf[33]; | |
+ uchar ciaddr[IPaddrlen]; | |
+ | |
+ if(db == 0) | |
+ db = ndbopen(ndbfile); | |
+ if(db == 0){ | |
+ fprint(2, "can't open db\n"); | |
+ return -1; | |
+ } | |
+ | |
+ memset(iip, 0, sizeof(*iip)); | |
+ | |
+ /* client knows its address? */ | |
+ v4tov6(ciaddr, bp->ciaddr); | |
+ if(validip(ciaddr)){ | |
+ if(lookupip(ciaddr, iip, 0) < 0) | |
+ return -1; /* don't know anything about it */ | |
+ | |
+check72(iip); | |
+ | |
+ if(!samenet(riip->ipaddr, iip)){ | |
+ warning(0, "%I not on %I", ciaddr, riip->ipnet); | |
+ return -1; | |
+ } | |
+ | |
+ /* | |
+ * see if this is a masquerade, i.e., if the ether | |
+ * address doesn't match what we expected it to be. | |
+ */ | |
+ if(memcmp(iip->etheraddr, zeroes, 6) != 0) | |
+ if(memcmp(bp->chaddr, iip->etheraddr, 6) != 0) | |
+ warning(0, "ciaddr %I rcvd from %E instead of %E", | |
+ ciaddr, bp->chaddr, iip->etheraddr); | |
+ | |
+ return 0; | |
+ } | |
+ | |
+ if(bp->hlen > Maxhwlen) | |
+ return -1; | |
+ switch(bp->htype){ | |
+ case 1: | |
+ hwattr = "ether"; | |
+ hwval = hwbuf; | |
+ snprint(hwbuf, sizeof(hwbuf), "%E", bp->chaddr); | |
+ break; | |
+ default: | |
+ syslog(0, blog, "not ethernet %E, htype %d, hlen %d", | |
+ bp->chaddr, bp->htype, bp->hlen); | |
+ return -1; | |
+ } | |
+ | |
+ /* | |
+ * use hardware address to find an ip address on | |
+ * same net as riip | |
+ */ | |
+ t = ndbsearch(db, &s, hwattr, hwval); | |
+ while(t){ | |
+ for(nt = t; nt; nt = nt->entry){ | |
+ if(strcmp(nt->attr, "ip") != 0) | |
+ continue; | |
+ parseip(ciaddr, nt->val); | |
+ if(lookupip(ciaddr, iip, 0) < 0) | |
+ continue; | |
+ if(samenet(riip->ipaddr, iip)){ | |
+ ndbfree(t); | |
+ return 0; | |
+ } | |
+ } | |
+ ndbfree(t); | |
+ t = ndbsnext(&s, hwattr, hwval); | |
+ } | |
+ return -1; | |
+} | |
+ | |
+/* | |
+ * interface to ndbipinfo | |
+ */ | |
+Ndbtuple* | |
+lookupinfo(uchar *ipaddr, char **attr, int n) | |
+{ | |
+ char ip[32]; | |
+ | |
+ sprint(ip, "%I", ipaddr); | |
+ return ndbipinfo(db, "ip", ip, attr, n); | |
+} | |
+ | |
+/* | |
+ * return the ip addresses for a type of server for system ip | |
+ */ | |
+int | |
+lookupserver(char *attr, uchar **ipaddrs, Ndbtuple *t) | |
+{ | |
+ Ndbtuple *nt; | |
+ int rv = 0; | |
+ | |
+ for(nt = t; rv < 2 && nt != nil; nt = nt->entry) | |
+ if(strcmp(nt->attr, attr) == 0){ | |
+ parseip(ipaddrs[rv], nt->val); | |
+ rv++; | |
+ } | |
+ return rv; | |
+} | |
+ | |
+/* | |
+ * just lookup the name | |
+ */ | |
+void | |
+lookupname(char *val, Ndbtuple *t) | |
+{ | |
+ Ndbtuple *nt; | |
+ | |
+ for(nt = t; nt != nil; nt = nt->entry) | |
+ if(strcmp(nt->attr, "dom") == 0){ | |
+ strcpy(val, nt->val); | |
+ break; | |
+ } | |
+} | |
+ | |
+uchar slash120[IPaddrlen] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | |
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0 }; | |
+uchar net72[IPaddrlen] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, | |
+ 0x0, 0x0, 0xff, 0xff, 135, 104, 72, 0 }; | |
+ | |
+static void | |
+check72(Info *iip) | |
+{ | |
+ uchar net[IPaddrlen]; | |
+ | |
+ maskip(iip->ipaddr, slash120, net); | |
+ if(ipcmp(net, net72) == 0) | |
+ syslog(0, blog, "check72 %I %M gw %I", iip->ipaddr, iip->ipmas… | |
+} | |
diff --git a/src/cmd/ip/dhcpd/ping.c b/src/cmd/ip/dhcpd/ping.c | |
t@@ -0,0 +1,112 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <ip.h> | |
+ | |
+ | |
+typedef struct Icmp Icmp; | |
+struct Icmp | |
+{ | |
+ uchar vihl; /* Version and header length */ | |
+ uchar tos; /* Type of service */ | |
+ uchar length[2]; /* packet length */ | |
+ uchar id[2]; /* Identification */ | |
+ uchar frag[2]; /* Fragment information */ | |
+ uchar ttl; /* Time to live */ | |
+ uchar proto; /* Protocol */ | |
+ uchar ipcksum[2]; /* Header checksum */ | |
+ uchar src[4]; /* Ip source */ | |
+ uchar dst[4]; /* Ip destination */ | |
+ uchar type; | |
+ uchar code; | |
+ uchar cksum[2]; | |
+ uchar icmpid[2]; | |
+ uchar seq[2]; | |
+ uchar data[1]; | |
+}; | |
+ | |
+enum | |
+{ /* Packet Types */ | |
+ EchoReply = 0, | |
+ Unreachable = 3, | |
+ SrcQuench = 4, | |
+ EchoRequest = 8, | |
+ TimeExceed = 11, | |
+ Timestamp = 13, | |
+ TimestampReply = 14, | |
+ InfoRequest = 15, | |
+ InfoReply = 16, | |
+ | |
+ ICMP_IPSIZE = 20, | |
+ ICMP_HDRSIZE = 8, | |
+}; | |
+ | |
+static void | |
+catch(void *a, char *msg) | |
+{ | |
+ USED(a); | |
+ if(strstr(msg, "alarm")) | |
+ noted(NCONT); | |
+ else | |
+ noted(NDFLT); | |
+} | |
+ | |
+#define MSG "dhcp probe" | |
+ | |
+/* | |
+ * make sure noone is using the address | |
+ */ | |
+int | |
+icmpecho(uchar *a) | |
+{ | |
+ int fd; | |
+ char buf[512]; | |
+ Icmp *ip; | |
+ int i, n, len; | |
+ ushort sseq, x; | |
+ int rv; | |
+ | |
+return 0; | |
+ rv = 0; | |
+ | |
+ sprint(buf, "%I", a); | |
+ fd = dial(netmkaddr(buf, "icmp", "1"), 0, 0, 0); | |
+ if(fd < 0){ | |
+ return 0; | |
+ } | |
+ | |
+ sseq = getpid()*time(0); | |
+ | |
+ ip = (Icmp*)buf; | |
+ notify(catch); | |
+ for(i = 0; i < 3; i++){ | |
+ ip->type = EchoRequest; | |
+ ip->code = 0; | |
+ strcpy((char*)ip->data, MSG); | |
+ ip->seq[0] = sseq; | |
+ ip->seq[1] = sseq>>8; | |
+ len = ICMP_IPSIZE+ICMP_HDRSIZE+sizeof(MSG); | |
+ | |
+ /* send a request */ | |
+ if(write(fd, buf, len) < len) | |
+ break; | |
+ | |
+ /* wait 1/10th second for a reply and try again */ | |
+ alarm(100); | |
+ n = read(fd, buf, sizeof(buf)); | |
+ alarm(0); | |
+ if(n <= 0) | |
+ continue; | |
+ | |
+ /* an answer to our echo request? */ | |
+ x = (ip->seq[1]<<8)|ip->seq[0]; | |
+ if(n >= len) | |
+ if(ip->type == EchoReply) | |
+ if(x == sseq) | |
+ if(strcmp((char*)ip->data, MSG) == 0){ | |
+ rv = 1; | |
+ break; | |
+ } | |
+ } | |
+ close(fd); | |
+ return rv; | |
+} | |
diff --git a/src/cmd/ip/dhcpd/testlook.c b/src/cmd/ip/dhcpd/testlook.c | |
t@@ -0,0 +1,222 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <ip.h> | |
+#include <bio.h> | |
+#include <ndb.h> | |
+ | |
+static uchar noether[6]; | |
+ | |
+/* | |
+ * Look for a pair with the given attribute. look first on the same line, | |
+ * then in the whole entry. | |
+ */ | |
+static Ndbtuple* | |
+lookval(Ndbtuple *entry, Ndbtuple *line, char *attr, char *to) | |
+{ | |
+ Ndbtuple *nt; | |
+ | |
+ /* first look on same line (closer binding) */ | |
+ for(nt = line;;){ | |
+ if(strcmp(attr, nt->attr) == 0){ | |
+ strncpy(to, nt->val, Ndbvlen); | |
+ return nt; | |
+ } | |
+ nt = nt->line; | |
+ if(nt == line) | |
+ break; | |
+ } | |
+ /* search whole tuple */ | |
+ for(nt = entry; nt; nt = nt->entry) | |
+ if(strcmp(attr, nt->attr) == 0){ | |
+ strncpy(to, nt->val, Ndbvlen); | |
+ return nt; | |
+ } | |
+ return 0; | |
+} | |
+ | |
+/* | |
+ * lookup an ip address | |
+ */ | |
+static uchar* | |
+lookupip(Ndb *db, char *name, uchar *to, Ipinfo *iip) | |
+{ | |
+ Ndbtuple *t, *nt; | |
+ char buf[Ndbvlen]; | |
+ uchar subnet[IPaddrlen]; | |
+ Ndbs s; | |
+ char *attr; | |
+ | |
+ attr = ipattr(name); | |
+ if(strcmp(attr, "ip") == 0){ | |
+ parseip(to, name); | |
+ return to; | |
+ } | |
+ | |
+ t = ndbgetval(db, &s, attr, name, "ip", buf); | |
+ if(t){ | |
+ /* first look for match on same subnet */ | |
+ for(nt = t; nt; nt = nt->entry){ | |
+ if(strcmp(nt->attr, "ip") != 0) | |
+ continue; | |
+ parseip(to, nt->val); | |
+ maskip(to, iip->ipmask, subnet); | |
+ if(memcmp(subnet, iip->ipnet, sizeof(subnet)) == 0) | |
+ return to; | |
+ } | |
+ | |
+ /* otherwise, just take what we have */ | |
+ ndbfree(t); | |
+ parseip(to, buf); | |
+ return to; | |
+ } | |
+ return 0; | |
+} | |
+ | |
+/* | |
+ * lookup a subnet and fill in anything we can | |
+ */ | |
+static void | |
+recursesubnet(Ndb *db, uchar *mask, Ipinfo *iip, char *fs, char *gw, char *au) | |
+{ | |
+ Ndbs s; | |
+ Ndbtuple *t; | |
+ uchar submask[IPaddrlen]; | |
+ char ip[Ndbvlen]; | |
+ | |
+ memmove(iip->ipmask, mask, 4); | |
+ maskip(iip->ipaddr, iip->ipmask, iip->ipnet); | |
+ sprint(ip, "%I", iip->ipnet); | |
+ t = ndbsearch(db, &s, "ip", ip); | |
+print("%s->", ip); | |
+ if(t){ | |
+ /* look for a further subnet */ | |
+ if(lookval(t, s.t, "ipmask", ip)){ | |
+ parseip(submask, ip); | |
+ | |
+ /* recurse only if it has changed */ | |
+ if(!equivip(submask, mask)) | |
+ recursesubnet(db, submask, iip, fs, gw, au); | |
+ | |
+ } | |
+ | |
+ /* fill in what we don't have */ | |
+ if(gw[0] == 0) | |
+ lookval(t, s.t, "ipgw", gw); | |
+ if(fs[0] == 0) | |
+ lookval(t, s.t, "fs", fs); | |
+ if(au[0] == 0) | |
+ lookval(t, s.t, "auth", au); | |
+ | |
+ ndbfree(t); | |
+ } | |
+} | |
+#ifdef foo | |
+/* | |
+ * find out everything we can about a system from what has been | |
+ * specified. | |
+ */ | |
+int | |
+ipinfo(Ndb *db, char *etherin, char *ipin, char *name, Ipinfo *iip) | |
+{ | |
+ Ndbtuple *t; | |
+ Ndbs s; | |
+ char ether[Ndbvlen]; | |
+ char ip[Ndbvlen]; | |
+ char fsname[Ndbvlen]; | |
+ char gwname[Ndbvlen]; | |
+ char auname[Ndbvlen]; | |
+ | |
+ memset(iip, 0, sizeof(Ipinfo)); | |
+ fsname[0] = 0; | |
+ gwname[0] = 0; | |
+ auname[0] = 0; | |
+ | |
+ /* | |
+ * look for a matching entry | |
+ */ | |
+ t = 0; | |
+ if(etherin) | |
+ t = ndbgetval(db, &s, "ether", etherin, "ip", ip); | |
+ if(t == 0 && ipin) | |
+ t = ndbsearch(db, &s, "ip", ipin); | |
+ if(t == 0 && name) | |
+ t = ndbgetval(db, &s, ipattr(name), name, "ip", ip); | |
+ if(t){ | |
+ /* | |
+ * copy in addresses and name | |
+ */ | |
+ if(lookval(t, s.t, "ip", ip)) | |
+ parseip(iip->ipaddr, ip); | |
+ if(lookval(t, s.t, "ether", ether)) | |
+ parseether(iip->etheraddr, ether); | |
+ lookval(t, s.t, "dom", iip->domain); | |
+ | |
+ /* | |
+ * Look for bootfile, fs, and gateway. | |
+ * If necessary, search through all entries for | |
+ * this ip address. | |
+ */ | |
+ while(t){ | |
+ if(iip->bootf[0] == 0) | |
+ lookval(t, s.t, "bootf", iip->bootf); | |
+ if(fsname[0] == 0) | |
+ lookval(t, s.t, "fs", fsname); | |
+ if(gwname[0] == 0) | |
+ lookval(t, s.t, "ipgw", gwname); | |
+ if(auname[0] == 0) | |
+ lookval(t, s.t, "auth", auname); | |
+ ndbfree(t); | |
+ if(iip->bootf[0] && fsname[0] && gwname[0] && auname[0… | |
+ break; | |
+ t = ndbsnext(&s, "ether", ether); | |
+ } | |
+ } else if(ipin) { | |
+ /* | |
+ * copy in addresses (all we know) | |
+ */ | |
+ parseip(iip->ipaddr, ipin); | |
+ if(etherin) | |
+ parseether(iip->etheraddr, etherin); | |
+ } else | |
+ return -1; | |
+ | |
+ /* | |
+ * Look up the client's network and find a subnet mask for it. | |
+ * Fill in from the subnet (or net) entry anything we can't figure | |
+ * out from the client record. | |
+ */ | |
+ recursesubnet(db, classmask[CLASS(iip->ipaddr)], iip, fsname, gwname, … | |
+ | |
+ /* 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 | |
t@@ -0,0 +1,168 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <ip.h> | |
+#include <bio.h> | |
+#include <ndb.h> | |
+ | |
+static uchar noether[6]; | |
+ Ndb *db; | |
+ | |
+static void | |
+recursesubnet(Ndb *db, uchar *addr, uchar *mask, char *attr, char *name, char … | |
+{ | |
+ 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, n… | |
+ 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)], … | |
+ | |
+ 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 | |
t@@ -0,0 +1,22 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <ip.h> | |
+#include <bio.h> | |
+#include <ndb.h> | |
+#include "dat.h" | |
+ | |
+char *blog = "ipboot"; | |
+ | |
+void | |
+main(int argc, char **argv) | |
+{ | |
+ fmtinstall('E', eipconv); | |
+ fmtinstall('I', eipconv); | |
+ | |
+ if(argc < 2) | |
+ exits(0); | |
+ if(icmpecho(argv[1])) | |
+ fprint(2, "%s live\n", argv[1]); | |
+ else | |
+ fprint(2, "%s doesn't answer\n", argv[1]); | |
+} | |
diff --git a/src/cmd/ip/snoopy/Linux.c b/src/cmd/ip/snoopy/Linux.c | |
t@@ -0,0 +1,58 @@ | |
+#include <u.h> | |
+#include <sys/socket.h> | |
+#include <net/if.h> | |
+#include <netpacket/packet.h> | |
+#include <net/ethernet.h> | |
+#include <netinet/in.h> | |
+#include <sys/ioctl.h> | |
+#include <libc.h> | |
+#include <ip.h> | |
+#include <bio.h> | |
+#include <fcall.h> | |
+#include <libsec.h> | |
+#include "dat.h" | |
+#include "protos.h" | |
+#include "y.tab.h" | |
+ | |
+int | |
+opendevice(char *dev, int promisc) | |
+{ | |
+ int fd; | |
+ struct ifreq ifr; | |
+ struct sockaddr_ll sa; | |
+ | |
+ if((fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) < 0) | |
+ return -1; | |
+ | |
+ if(dev){ | |
+ memset(&ifr, 0, sizeof ifr); | |
+ strncpy(ifr.ifr_name, dev, sizeof ifr.ifr_name); | |
+ if(ioctl(fd, SIOCGIFINDEX, &ifr) < 0){ | |
+ close(fd); | |
+ return -1; | |
+ } | |
+ memset(&sa, 0, sizeof sa); | |
+ sa.sll_family = AF_PACKET; | |
+ sa.sll_protocol = htons(ETH_P_ALL); | |
+ sa.sll_ifindex = ifr.ifr_ifindex; | |
+ if(bind(fd, (struct sockaddr*)&sa, sizeof sa) < 0){ | |
+ close(fd); | |
+ return -1; | |
+ } | |
+ } | |
+ | |
+ if(promisc){ | |
+ memset(&ifr, 0, sizeof ifr); | |
+ strncpy(ifr.ifr_name, dev, sizeof ifr.ifr_name); | |
+ if(ioctl(fd, SIOCGIFFLAGS, &ifr) < 0){ | |
+ close(fd); | |
+ return -1; | |
+ } | |
+ ifr.ifr_flags |= IFF_PROMISC; | |
+ if(ioctl(fd, SIOCSIFFLAGS, &ifr) < 0){ | |
+ close(fd); | |
+ return -1; | |
+ } | |
+ } | |
+ return fd; | |
+} | |
diff --git a/src/cmd/ip/snoopy/arp.c b/src/cmd/ip/snoopy/arp.c | |
t@@ -0,0 +1,128 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <ip.h> | |
+#include "dat.h" | |
+#include "protos.h" | |
+ | |
+typedef struct Hdr Hdr; | |
+struct Hdr | |
+{ | |
+ uchar hrd[2]; | |
+ uchar pro[2]; | |
+ uchar hln; | |
+ uchar pln; | |
+ uchar op[2]; | |
+ uchar sha[6]; | |
+ uchar spa[4]; | |
+ uchar tha[6]; | |
+ uchar tpa[4]; | |
+}; | |
+ | |
+enum | |
+{ | |
+ ARPLEN= 28, | |
+}; | |
+ | |
+enum | |
+{ | |
+ Ospa, | |
+ Otpa, | |
+ Ostpa, | |
+ Osha, | |
+ Otha, | |
+ Ostha, | |
+ Opa, | |
+}; | |
+ | |
+static Field p_fields[] = | |
+{ | |
+ {"spa", Fv4ip, Ospa, "protocol source", … | |
+ {"tpa", Fv4ip, Otpa, "protocol target", … | |
+ {"a", Fv4ip, Ostpa, "protocol source/targ… | |
+ {"sha", Fba, Osha, "hardware source", … | |
+ {"tha", Fba, Otha, "hardware target", … | |
+ {"ah", Fba, Ostha, "hardware source/targ… | |
+ {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 th… | |
+ 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 | |
t@@ -0,0 +1,176 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <ip.h> | |
+#include "dat.h" | |
+#include "protos.h" | |
+ | |
+enum | |
+{ | |
+ OfferTimeout= 60, /* when an offer times out */ | |
+ MaxLease= 60*60, /* longest lease for dynamic bi… | |
+ MinLease= 15*60, /* shortest lease for dynamic b… | |
+ StaticLease= 30*60, /* lease for static binding … | |
+ | |
+ IPUDPHDRSIZE= 28, /* size of an IP plus UDP head… | |
+ MINSUPPORTED= 576, /* biggest IP message the cli… | |
+ | |
+ /* 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 b… | |
+ uchar flags[2]; | |
+ uchar ciaddr[IPv4addrlen]; /* client IP address (client … | |
+ uchar yiaddr[IPv4addrlen]; /* client IP address (server … | |
+ 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=%… | |
+ 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 | |
t@@ -0,0 +1,106 @@ | |
+typedef struct Field Field; | |
+typedef struct Filter Filter; | |
+typedef struct Msg Msg; | |
+typedef struct Mux Mux; | |
+typedef struct Proto Proto; | |
+ | |
+#define NetS(x) ((((uchar*)x)[0]<<8) | ((uchar*)x)[1]) | |
+#define Net3(x) ((((uchar*)x)[0]<<16) | (((uchar*)x)[1]<<8) | ((uchar*)x)[2]) | |
+#define NetL(x) ((((uchar*)x)[0]<<24) | (((uchar*)x)[1]<<16) | (((uchar*)x)[2]… | |
+ | |
+/* | |
+ * 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 | |
t@@ -0,0 +1,483 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <ip.h> | |
+#include "dat.h" | |
+#include "protos.h" | |
+ | |
+enum | |
+{ | |
+ Maxoptlen= 312-4, | |
+ | |
+ /* dhcp types */ | |
+ Discover= 1, | |
+ Offer= 2, | |
+ Request= 3, | |
+ Decline= 4, | |
+ Ack= 5, | |
+ Nak= 6, | |
+ Release= 7, | |
+ Inform= 8, | |
+ | |
+ /* bootp option types */ | |
+ OBend= 255, | |
+ OBpad= 0, | |
+ OBmask= 1, | |
+ OBtimeoff= 2, | |
+ OBrouter= 3, | |
+ OBtimeserver= 4, | |
+ OBnameserver= 5, | |
+ OBdnserver= 6, | |
+ OBlogserver= 7, | |
+ OBcookieserver= 8, | |
+ OBlprserver= 9, | |
+ OBimpressserver= 10, | |
+ OBrlserver= 11, | |
+ OBhostname= 12, /* 0xc0 */ | |
+ OBbflen= 13, | |
+ OBdumpfile= 14, | |
+ OBdomainname= 15, | |
+ OBswapserver= 16, /* 0x10 */ | |
+ OBrootpath= 17, | |
+ OBextpath= 18, | |
+ OBipforward= 19, | |
+ OBnonlocal= 20, | |
+ OBpolicyfilter= 21, | |
+ OBmaxdatagram= 22, | |
+ OBttl= 23, | |
+ OBpathtimeout= 24, | |
+ OBpathplateau= 25, | |
+ OBmtu= 26, | |
+ OBsubnetslocal= 27, | |
+ OBbaddr= 28, | |
+ OBdiscovermask= 29, | |
+ OBsupplymask= 30, | |
+ OBdiscoverrouter= 31, | |
+ OBrsserver= 32, /* 0x20 */ | |
+ OBstaticroutes= 33, | |
+ OBtrailerencap= 34, | |
+ OBarptimeout= 35, | |
+ OBetherencap= 36, | |
+ OBtcpttl= 37, | |
+ OBtcpka= 38, | |
+ OBtcpkag= 39, | |
+ OBnisdomain= 40, | |
+ OBniserver= 41, | |
+ OBntpserver= 42, | |
+ OBvendorinfo= 43, /* 0x2b */ | |
+ OBnetbiosns= 44, | |
+ OBnetbiosdds= 45, | |
+ OBnetbiostype= 46, | |
+ OBnetbiosscope= 47, | |
+ OBxfontserver= 48, /* 0x30 */ | |
+ OBxdispmanager= 49, | |
+ OBnisplusdomain= 64, /* 0x40 */ | |
+ OBnisplusserver= 65, | |
+ OBhomeagent= 68, | |
+ OBsmtpserver= 69, | |
+ OBpop3server= 70, | |
+ OBnntpserver= 71, | |
+ OBwwwserver= 72, | |
+ OBfingerserver= 73, | |
+ OBircserver= 74, | |
+ OBstserver= 75, | |
+ OBstdaserver= 76, | |
+ | |
+ /* dhcp options */ | |
+ ODipaddr= 50, /* 0x32 */ | |
+ ODlease= 51, | |
+ ODoverload= 52, | |
+ ODtype= 53, /* 0x35 */ | |
+ ODserverid= 54, /* 0x36 */ | |
+ ODparams= 55, /* 0x37 */ | |
+ ODmessage= 56, | |
+ ODmaxmsg= 57, | |
+ ODrenewaltime= 58, | |
+ ODrebindingtime= 59, | |
+ ODvendorclass= 60, | |
+ ODclientid= 61, /* 0x3d */ | |
+ ODtftpserver= 66, | |
+ ODbootfile= 67, | |
+ | |
+ /* plan9 vendor info options */ | |
+ OP9fs= 128, // plan9 file servers | |
+ OP9auth= 129, // plan9 auth servers | |
+}; | |
+ | |
+static void | |
+p_compile(Filter *f) | |
+{ | |
+ sysfatal("unknown bootp field: %s", f->s); | |
+} | |
+ | |
+static int | |
+p_filter(Filter *f, Msg *m) | |
+{ | |
+ USED(f); | |
+ USED(m); | |
+ return 0; | |
+} | |
+ | |
+/* | |
+ * convert a byte array to hex | |
+ */ | |
+static char | |
+hex(int x) | |
+{ | |
+ if(x < 10) | |
+ return x + '0'; | |
+ return x - 10 + 'a'; | |
+} | |
+static char* | |
+phex(char *p, char *e, char *tag, uchar *o, int n) | |
+{ | |
+ p = seprint(p, e, "%s=", tag); | |
+ | |
+ for(; p+2 < e && n > 0; n--){ | |
+ *p++ = hex(*o>>4); | |
+ *p++ = hex(*o & 0xf); | |
+ o++; | |
+ } | |
+ return p; | |
+} | |
+ | |
+static char* | |
+pstring(char *p, char *e, char *tag, uchar *o, int n) | |
+{ | |
+ char msg[256]; | |
+ | |
+ if(n > sizeof msg - 1) | |
+ n = sizeof msg - 1; | |
+ memmove(msg, o, n); | |
+ msg[n] = 0; | |
+ return seprint(p, e, "%s=%s", tag, msg); | |
+} | |
+ | |
+static char* | |
+pint(char *p, char *e, char *tag, uchar *o, int n) | |
+{ | |
+ int x; | |
+ | |
+ x = *(char*)o++; | |
+ for(; n > 1; n--) | |
+ x = (x<<8)|*o++; | |
+ return seprint(p, e, "%s=%d", tag, x); | |
+} | |
+ | |
+static char* | |
+puint(char *p, char *e, char *tag, uchar *o, int n) | |
+{ | |
+ uint x; | |
+ | |
+ x = *o++; | |
+ for(; n > 1; n--) | |
+ x = (x<<8)|*o++; | |
+ return seprint(p, e, "%s=%ud", tag, x); | |
+} | |
+ | |
+static char* | |
+pserver(char *p, char *e, char *tag, uchar *o, int n) | |
+{ | |
+ p = seprint(p, e, "%s=(", tag); | |
+ while(n >= 4){ | |
+ p = seprint(p, e, " %V", o); | |
+ n -= 4; | |
+ o += 4; | |
+ } | |
+ p = seprint(p, e, ")"); | |
+ return p; | |
+} | |
+ | |
+static char *dhcptype[256] = | |
+{ | |
+[Discover] "Discover", | |
+[Offer] "Offer", | |
+[Request] "Request", | |
+[Decline] "Decline", | |
+[Ack] "Ack", | |
+[Nak] "Nak", | |
+[Release] "Release", | |
+[Inform] "Inform", | |
+}; | |
+ | |
+ | |
+static char* | |
+ptype(char *p, char *e, uchar val) | |
+{ | |
+ char *x; | |
+ | |
+ x = dhcptype[val]; | |
+ if(x != nil) | |
+ return seprint(p, e, "t=%s", x); | |
+ else | |
+ return seprint(p, e, "t=%d", val); | |
+} | |
+ | |
+static int | |
+p_seprint(Msg *m) | |
+{ | |
+ int i, n, code; | |
+ uchar *o, *ps; | |
+ char *p, *e; | |
+ char msg[64]; | |
+ | |
+ /* no next proto */ | |
+ m->pr = nil; | |
+ | |
+ p = m->p; | |
+ e = m->e; | |
+ ps = m->ps; | |
+ | |
+ while(ps < m->pe){ | |
+ code = *ps++; | |
+ if(code == 255) | |
+ break; | |
+ if(code == 0) | |
+ continue; | |
+ | |
+ /* ignore anything that's too long */ | |
+ n = *ps++; | |
+ o = ps; | |
+ ps += n; | |
+ if(ps > m->pe) | |
+ break; | |
+ | |
+ switch(code){ | |
+ case ODipaddr: /* requested ip address */ | |
+ p = pserver(p, e, "ipaddr", o, n); | |
+ break; | |
+ case ODlease: /* requested lease time */ | |
+ p = pint(p, e, "lease", o, n); | |
+ break; | |
+ case ODtype: | |
+ p = ptype(p, e, *o); | |
+ break; | |
+ case ODserverid: | |
+ p = pserver(p, e, "serverid", o, n); | |
+ break; | |
+ case ODmessage: | |
+ p = pstring(p, e, "message", o, n); | |
+ break; | |
+ case ODmaxmsg: | |
+ p = puint(p, e, "maxmsg", o, n); | |
+ break; | |
+ case ODclientid: | |
+ p = phex(p, e, "clientid", o, n); | |
+ break; | |
+ case ODparams: | |
+ p = seprint(p, e, " requested=("); | |
+ for(i = 0; i < n; i++){ | |
+ if(i != 0) | |
+ p = seprint(p, e, " "); | |
+ p = seprint(p, e, "%ud", o[i]); | |
+ } | |
+ p = seprint(p, e, ")"); | |
+ break; | |
+ case ODvendorclass: | |
+ p = pstring(p, e, "vendorclass", o, n); | |
+ break; | |
+ case OBmask: | |
+ p = pserver(p, e, "mask", o, n); | |
+ break; | |
+ case OBtimeoff: | |
+ p = pint(p, e, "timeoff", o, n); | |
+ break; | |
+ case OBrouter: | |
+ p = pserver(p, e, "router", o, n); | |
+ break; | |
+ case OBtimeserver: | |
+ p = pserver(p, e, "timesrv", o, n); | |
+ break; | |
+ case OBnameserver: | |
+ p = pserver(p, e, "namesrv", o, n); | |
+ break; | |
+ case OBdnserver: | |
+ p = pserver(p, e, "dnssrv", o, n); | |
+ break; | |
+ case OBlogserver: | |
+ p = pserver(p, e, "logsrv", o, n); | |
+ break; | |
+ case OBcookieserver: | |
+ p = pserver(p, e, "cookiesrv", o, n); | |
+ break; | |
+ case OBlprserver: | |
+ p = pserver(p, e, "lprsrv", o, n); | |
+ break; | |
+ case OBimpressserver: | |
+ p = pserver(p, e, "impresssrv", o, n); | |
+ break; | |
+ case OBrlserver: | |
+ p = pserver(p, e, "rlsrv", o, n); | |
+ break; | |
+ case OBhostname: | |
+ p = pstring(p, e, "hostname", o, n); | |
+ break; | |
+ case OBbflen: | |
+ break; | |
+ case OBdumpfile: | |
+ p = pstring(p, e, "dumpfile", o, n); | |
+ break; | |
+ case OBdomainname: | |
+ p = pstring(p, e, "domname", o, n); | |
+ break; | |
+ case OBswapserver: | |
+ p = pserver(p, e, "swapsrv", o, n); | |
+ break; | |
+ case OBrootpath: | |
+ p = pstring(p, e, "rootpath", o, n); | |
+ break; | |
+ case OBextpath: | |
+ p = pstring(p, e, "extpath", o, n); | |
+ break; | |
+ case OBipforward: | |
+ p = phex(p, e, "ipforward", o, n); | |
+ break; | |
+ case OBnonlocal: | |
+ p = phex(p, e, "nonlocal", o, n); | |
+ break; | |
+ case OBpolicyfilter: | |
+ p = phex(p, e, "policyfilter", o, n); | |
+ break; | |
+ case OBmaxdatagram: | |
+ p = phex(p, e, "maxdatagram", o, n); | |
+ break; | |
+ case OBttl: | |
+ p = puint(p, e, "ttl", o, n); | |
+ break; | |
+ case OBpathtimeout: | |
+ p = puint(p, e, "pathtimeout", o, n); | |
+ break; | |
+ case OBpathplateau: | |
+ p = phex(p, e, "pathplateau", o, n); | |
+ break; | |
+ case OBmtu: | |
+ p = puint(p, e, "mtu", o, n); | |
+ break; | |
+ case OBsubnetslocal: | |
+ p = pserver(p, e, "subnet", o, n); | |
+ break; | |
+ case OBbaddr: | |
+ p = pserver(p, e, "baddr", o, n); | |
+ break; | |
+ case OBdiscovermask: | |
+ p = pserver(p, e, "discovermsak", o, n); | |
+ break; | |
+ case OBsupplymask: | |
+ p = pserver(p, e, "rousupplymaskter", o, n); | |
+ break; | |
+ case OBdiscoverrouter: | |
+ p = pserver(p, e, "discoverrouter", o, n); | |
+ break; | |
+ case OBrsserver: | |
+ p = pserver(p, e, "rsrouter", o, n); | |
+ break; | |
+ case OBstaticroutes: | |
+ p = phex(p, e, "staticroutes", o, n); | |
+ break; | |
+ case OBtrailerencap: | |
+ p = phex(p, e, "trailerencap", o, n); | |
+ break; | |
+ case OBarptimeout: | |
+ p = puint(p, e, "arptimeout", o, n); | |
+ break; | |
+ case OBetherencap: | |
+ p = phex(p, e, "etherencap", o, n); | |
+ break; | |
+ case OBtcpttl: | |
+ p = puint(p, e, "tcpttl", o, n); | |
+ break; | |
+ case OBtcpka: | |
+ p = puint(p, e, "tcpka", o, n); | |
+ break; | |
+ case OBtcpkag: | |
+ p = phex(p, e, "tcpkag", o, n); | |
+ break; | |
+ case OBnisdomain: | |
+ p = pstring(p, e, "nisdomain", o, n); | |
+ break; | |
+ case OBniserver: | |
+ p = pserver(p, e, "nisrv", o, n); | |
+ break; | |
+ case OBntpserver: | |
+ p = pserver(p, e, "ntpsrv", o, n); | |
+ break; | |
+ case OBvendorinfo: | |
+ p = phex(p, e, "vendorinfo", o, n); | |
+ break; | |
+ case OBnetbiosns: | |
+ p = pserver(p, e, "biosns", o, n); | |
+ break; | |
+ case OBnetbiosdds: | |
+ p = phex(p, e, "biosdds", o, n); | |
+ break; | |
+ case OBnetbiostype: | |
+ p = phex(p, e, "biostype", o, n); | |
+ break; | |
+ case OBnetbiosscope: | |
+ p = phex(p, e, "biosscope", o, n); | |
+ break; | |
+ case OBxfontserver: | |
+ p = pserver(p, e, "fontsrv", o, n); | |
+ break; | |
+ case OBxdispmanager: | |
+ p = pserver(p, e, "xdispmgr", o, n); | |
+ break; | |
+ case OBnisplusdomain: | |
+ p = pstring(p, e, "nisplusdomain", o, n); | |
+ break; | |
+ case OBnisplusserver: | |
+ p = pserver(p, e, "nisplussrv", o, n); | |
+ break; | |
+ case OBhomeagent: | |
+ p = pserver(p, e, "homeagent", o, n); | |
+ break; | |
+ case OBsmtpserver: | |
+ p = pserver(p, e, "smtpsrv", o, n); | |
+ break; | |
+ case OBpop3server: | |
+ p = pserver(p, e, "pop3srv", o, n); | |
+ break; | |
+ case OBnntpserver: | |
+ p = pserver(p, e, "ntpsrv", o, n); | |
+ break; | |
+ case OBwwwserver: | |
+ p = pserver(p, e, "wwwsrv", o, n); | |
+ break; | |
+ case OBfingerserver: | |
+ p = pserver(p, e, "fingersrv", o, n); | |
+ break; | |
+ case OBircserver: | |
+ p = pserver(p, e, "ircsrv", o, n); | |
+ break; | |
+ case OBstserver: | |
+ p = pserver(p, e, "stsrv", o, n); | |
+ break; | |
+ case OBstdaserver: | |
+ p = pserver(p, e, "stdasrv", o, n); | |
+ break; | |
+ case OBend: | |
+ goto out; | |
+ default: | |
+ snprint(msg, sizeof msg, " T%ud", code); | |
+ p = phex(p, e, msg, o, n); | |
+ break; | |
+ } | |
+ if(*ps != OBend) | |
+ p = seprint(p, e, " "); | |
+ } | |
+out: | |
+ m->p = p; | |
+ m->ps = ps; | |
+ return 0; | |
+} | |
+ | |
+Proto dhcp = | |
+{ | |
+ "dhcp", | |
+ p_compile, | |
+ p_filter, | |
+ p_seprint, | |
+ nil, | |
+ nil, | |
+ defaultframer, | |
+}; | |
+ | |
diff --git a/src/cmd/ip/snoopy/dump.c b/src/cmd/ip/snoopy/dump.c | |
t@@ -0,0 +1,92 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <ip.h> | |
+#include <ctype.h> | |
+#include "dat.h" | |
+#include "protos.h" | |
+ | |
+static void | |
+p_compile(Filter *f) | |
+{ | |
+ USED(f); | |
+} | |
+ | |
+static int | |
+p_filter(Filter *f, Msg *m) | |
+{ | |
+ USED(f); | |
+ USED(m); | |
+ return 0; | |
+} | |
+ | |
+static char tohex[16] = { | |
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', | |
+ 'a', 'b', 'c', 'd', 'e', 'f' | |
+}; | |
+ | |
+static int | |
+p_seprint(Msg *m) | |
+{ | |
+ int c, i, n, isstring; | |
+ uchar *ps = m->ps; | |
+ char *p = m->p; | |
+ char *e = m->e; | |
+ | |
+ n = m->pe - ps; | |
+ if(n > Nflag) | |
+ n = Nflag; | |
+ | |
+ isstring = 1; | |
+ for(i = 0; i < n; i++){ | |
+ c = ps[i]; | |
+ if(!isprint(c) && !isspace(c)){ | |
+ isstring = 0; | |
+ break; | |
+ } | |
+ } | |
+ | |
+ if(isstring){ | |
+ for(i = 0; i < n && p+1<e; i++){ | |
+ c = ps[i]; | |
+ switch(c){ | |
+ case '\t': | |
+ *p++ = '\\'; | |
+ *p++ = 't'; | |
+ break; | |
+ case '\r': | |
+ *p++ = '\\'; | |
+ *p++ = 'r'; | |
+ break; | |
+ case '\n': | |
+ *p++ = '\\'; | |
+ *p++ = 'n'; | |
+ break; | |
+ default: | |
+ *p++ = c; | |
+ } | |
+ } | |
+ } else { | |
+ for(i = 0; i < n && p+1<e; i++){ | |
+ c = ps[i]; | |
+ *p++ = tohex[c>>4]; | |
+ *p++ = tohex[c&0xf]; | |
+ } | |
+ } | |
+ | |
+ m->pr = nil; | |
+ m->p = p; | |
+ m->ps = ps; | |
+ | |
+ return 0; | |
+} | |
+ | |
+Proto dump = | |
+{ | |
+ "dump", | |
+ p_compile, | |
+ p_filter, | |
+ p_seprint, | |
+ nil, | |
+ nil, | |
+ defaultframer, | |
+}; | |
diff --git a/src/cmd/ip/snoopy/ether.c b/src/cmd/ip/snoopy/ether.c | |
t@@ -0,0 +1,121 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <ip.h> | |
+#include "dat.h" | |
+#include "protos.h" | |
+ | |
+typedef struct Hdr Hdr; | |
+struct Hdr { | |
+ uchar d[6]; | |
+ uchar s[6]; | |
+ uchar type[2]; | |
+ char data[1500]; | |
+}; | |
+#define ETHERMINTU 60 /* minimum transmit size */ | |
+#define ETHERMAXTU 1514 /* maximum transmit size */ | |
+#define ETHERHDRSIZE 14 /* size of an ethernet header */ | |
+ | |
+static Mux p_mux[] = | |
+{ | |
+ {"ip", 0x0800, } , | |
+ {"arp", 0x0806, } , | |
+ {"rarp", 0x0806, } , | |
+ {"ip6", 0x86dd, } , | |
+ {"pppoe_disc", 0x8863, }, | |
+ {"pppoe_sess", 0x8864, }, | |
+ {0} | |
+}; | |
+ | |
+enum | |
+{ | |
+ Os, // source | |
+ Od, // destination | |
+ Oa, // source or destination | |
+ Ot, // type | |
+}; | |
+ | |
+static Field p_fields[] = | |
+{ | |
+ {"s", Fether, Os, "source address", } , | |
+ {"d", Fether, Od, "destination address", }… | |
+ {"a", Fether, Oa, "source|destination address" } , | |
+ {"sd", Fether, Oa, "source|destination address" }… | |
+ {"t", Fnum, Ot, "type" } , | |
+ {0} | |
+}; | |
+ | |
+static void | |
+p_compile(Filter *f) | |
+{ | |
+ Mux *m; | |
+ | |
+ if(f->op == '='){ | |
+ compile_cmp(ether.name, f, p_fields); | |
+ return; | |
+ } | |
+ for(m = p_mux; m->name != nil; m++) | |
+ if(strcmp(f->s, m->name) == 0){ | |
+ f->pr = m->pr; | |
+ f->ulv = m->val; | |
+ f->subop = Ot; | |
+ return; | |
+ } | |
+ sysfatal("unknown ethernet field or protocol: %s", f->s); | |
+} | |
+ | |
+static int | |
+p_filter(Filter *f, Msg *m) | |
+{ | |
+ Hdr *h; | |
+ | |
+ if(m->pe - m->ps < ETHERHDRSIZE) | |
+ return 0; | |
+ | |
+ h = (Hdr*)m->ps; | |
+ m->ps += ETHERHDRSIZE; | |
+ | |
+ switch(f->subop){ | |
+ case Os: | |
+ return !memcmp(h->s, f->a, 6); | |
+ case Od: | |
+ return !memcmp(h->d, f->a, 6); | |
+ case Oa: | |
+ return memcmp(h->s, f->a, 6) == 0 || memcmp(h->d, f->a, 6) == … | |
+ 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 | |
t@@ -0,0 +1,83 @@ | |
+ | |
+/* GRE flag bits */ | |
+enum { | |
+ GRE_chksum = (1<<15), | |
+ GRE_routing = (1<<14), | |
+ GRE_key = (1<<13), | |
+ GRE_seq = (1<<12), | |
+ GRE_srcrt = (1<<11), | |
+ GRE_recur = (7<<8), | |
+ GRE_ack = (1<<7), | |
+ GRE_ver = 0x7, | |
+}; | |
+ | |
+/* GRE protocols */ | |
+enum { | |
+ GRE_sna = 0x0004, | |
+ GRE_osi = 0x00fe, | |
+ GRE_pup = 0x0200, | |
+ GRE_xns = 0x0600, | |
+ GRE_ip = 0x0800, | |
+ GRE_chaos = 0x0804, | |
+ GRE_rfc826 = 0x0806, | |
+ GRE_frarp = 0x0808, | |
+ GRE_vines = 0x0bad, | |
+ GRE_vinesecho = 0x0bae, | |
+ GRE_vinesloop = 0x0baf, | |
+ GRE_decnetIV = 0x6003, | |
+ GRE_ppp = 0x880b, | |
+}; | |
+ | |
+int | |
+sprintgre(void *a, char *buf, int len) | |
+{ | |
+ int flag, prot, chksum, offset, key, seq, ack; | |
+ int n; | |
+ uchar *p = a; | |
+ | |
+ chksum = offset = key = seq = ack = 0; | |
+ | |
+ flag = NetS(p); | |
+ prot = NetS(p+2); | |
+ p += 4; len -= 4; | |
+ if(flag & (GRE_chksum|GRE_routing)){ | |
+ chksum = NetS(p); | |
+ offset = NetS(p+2); | |
+ p += 4; len -= 4; | |
+ } | |
+ if(flag&GRE_key){ | |
+ key = NetL(p); | |
+ p += 4; len -= 4; | |
+ } | |
+ if(flag&GRE_seq){ | |
+ seq = NetL(p); | |
+ p += 4; len -= 4; | |
+ } | |
+ if(flag&GRE_ack){ | |
+ ack = NetL(p); | |
+ p += 4; len -= 4; | |
+ } | |
+ /* skip routing if present */ | |
+ if(flag&GRE_routing) { | |
+ while(len >= 4 && (n=p[3]) != 0) { | |
+ len -= n; | |
+ p += n; | |
+ } | |
+ } | |
+ | |
+ USED(offset); | |
+ USED(chksum); | |
+ | |
+ n = sprint(buf, "GRE(f %4.4ux p %ux k %ux", flag, prot, key); | |
+ if(flag&GRE_seq) | |
+ n += sprint(buf+n, " s %ux", seq); | |
+ if(flag&GRE_ack) | |
+ n += sprint(buf+n, " a %ux", ack); | |
+ n += sprint(buf+n, " len = %d/%d) ", len, key>>16); | |
+ if(prot == GRE_ppp && len > 0) | |
+ n += sprintppp(p, buf+n, len); | |
+ else | |
+ n += sprintx(p, buf+n, len); | |
+ | |
+ return n; | |
+} | |
diff --git a/src/cmd/ip/snoopy/hdlc.c b/src/cmd/ip/snoopy/hdlc.c | |
t@@ -0,0 +1,174 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <ip.h> | |
+#include "dat.h" | |
+#include "protos.h" | |
+ | |
+enum { | |
+ HDLC_frame= 0x7e, | |
+ HDLC_esc= 0x7d, | |
+ | |
+ /* PPP frame fields */ | |
+ PPP_addr= 0xff, | |
+ PPP_ctl= 0x3, | |
+ PPP_initfcs= 0xffff, | |
+ PPP_goodfcs= 0xf0b8, | |
+}; | |
+ | |
+/* | |
+ * Calculate FCS - rfc 1331 | |
+ */ | |
+ushort fcstab[256] = | |
+{ | |
+ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, | |
+ 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, | |
+ 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, | |
+ 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, | |
+ 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, | |
+ 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, | |
+ 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, | |
+ 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, | |
+ 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, | |
+ 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, | |
+ 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, | |
+ 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, | |
+ 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, | |
+ 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, | |
+ 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, | |
+ 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, | |
+ 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, | |
+ 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, | |
+ 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, | |
+ 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, | |
+ 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, | |
+ 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, | |
+ 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, | |
+ 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, | |
+ 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, | |
+ 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, | |
+ 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, | |
+ 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, | |
+ 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, | |
+ 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, | |
+ 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, | |
+ 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 | |
+}; | |
+ | |
+static uchar inbuf[16*1024]; | |
+static int inlen; | |
+ | |
+static Mux p_mux[] = | |
+{ | |
+ {"ppp", (PPP_addr<<8)|PPP_ctl, } , | |
+ {0} | |
+}; | |
+ | |
+enum | |
+{ | |
+ Ot = 1 | |
+}; | |
+ | |
+static void | |
+p_compile(Filter *f) | |
+{ | |
+ Mux *m; | |
+ | |
+ for(m = p_mux; m->name != nil; m++) | |
+ if(strcmp(f->s, m->name) == 0){ | |
+ f->pr = m->pr; | |
+ f->ulv = m->val; | |
+ f->subop = Ot; | |
+ return; | |
+ } | |
+ sysfatal("unknown ethernet field or protocol: %s", f->s); | |
+} | |
+ | |
+static int | |
+p_filter(Filter *f, Msg *m) | |
+{ | |
+ ulong t; | |
+ | |
+ if(m->pe-m->ps < 2) | |
+ return 0; | |
+ | |
+ switch(f->subop){ | |
+ case Ot: | |
+ t = (m->ps[0]<<8)|m->ps[1]; | |
+ if(t != f->ulv) | |
+ return 0; | |
+ break; | |
+ } | |
+ return 1; | |
+} | |
+ | |
+static int | |
+p_seprint(Msg *m) | |
+{ | |
+ ulong t; | |
+ | |
+ if(m->pe-m->ps < 2) | |
+ return -1; | |
+ | |
+ t = (m->ps[0]<<8)|m->ps[1]; | |
+ m->ps += 2; | |
+ demux(p_mux, t, t, m, &dump); | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int | |
+p_framer(int fd, uchar *pkt, int pktlen) | |
+{ | |
+ ushort fcs; | |
+ uchar *from, *efrom, *to, *eto; | |
+ int n; | |
+ ulong c; | |
+ | |
+ eto = pkt+pktlen; | |
+ for(;;){ | |
+ efrom = memchr(inbuf, HDLC_frame, inlen); | |
+ if(efrom == nil){ | |
+ if(inlen >= sizeof(inbuf)) | |
+ inlen = 0; | |
+ n = read(fd, inbuf+inlen, sizeof(inbuf)-inlen); | |
+ if(n <= 0) | |
+ break; | |
+ inlen += n; | |
+ continue; | |
+ } | |
+ | |
+ /* checksum and unescape the frame */ | |
+ fcs = PPP_initfcs; | |
+ to = pkt; | |
+ for(from = inbuf; from < efrom;){ | |
+ c = *from++; | |
+ if(c == HDLC_esc) | |
+ c = (*from++) ^ 0x20; | |
+ if(to < eto) | |
+ *to++ = c; | |
+ fcs = (fcs >> 8) ^ fcstab[(fcs ^ c) & 0xff]; | |
+ } | |
+ | |
+ /* move down anything that's left */ | |
+ inlen -= efrom+1-inbuf; | |
+ memmove(inbuf, efrom+1, inlen); | |
+ | |
+ /* accept if this is a good packet */ | |
+ if(fcs != PPP_goodfcs) | |
+ print("bad frame %ld %2.2ux %2.2ux!\n", to-pkt, pkt[0]… | |
+ 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 | |
t@@ -0,0 +1,196 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <ip.h> | |
+#include "dat.h" | |
+#include "protos.h" | |
+ | |
+typedef struct Hdr Hdr; | |
+struct Hdr | |
+{ uchar type; | |
+ uchar code; | |
+ uchar cksum[2]; /* Checksum */ | |
+ uchar data[1]; | |
+}; | |
+ | |
+enum | |
+{ | |
+ ICMPLEN= 4, | |
+}; | |
+ | |
+enum | |
+{ | |
+ Ot, /* type */ | |
+ Op, /* next protocol */ | |
+}; | |
+ | |
+static Field p_fields[] = | |
+{ | |
+ {"t", Fnum, Ot, "type", } , | |
+ {0} | |
+}; | |
+ | |
+enum | |
+{ | |
+ EchoRep= 0, | |
+ Unreachable= 3, | |
+ SrcQuench= 4, | |
+ Redirect= 5, | |
+ EchoReq= 8, | |
+ TimeExceed= 11, | |
+ ParamProb= 12, | |
+ TSreq= 13, | |
+ TSrep= 14, | |
+ InfoReq= 15, | |
+ InfoRep= 16, | |
+}; | |
+ | |
+static Mux p_mux[] = | |
+{ | |
+ {"ip", Unreachable, }, | |
+ {"ip", SrcQuench, }, | |
+ {"ip", Redirect, }, | |
+ {"ip", TimeExceed, }, | |
+ {"ip", ParamProb, }, | |
+ {0}, | |
+}; | |
+ | |
+char *icmpmsg[236] = | |
+{ | |
+[EchoRep] "EchoRep", | |
+[Unreachable] "Unreachable", | |
+[SrcQuench] "SrcQuench", | |
+[Redirect] "Redirect", | |
+[EchoReq] "EchoReq", | |
+[TimeExceed] "TimeExceed", | |
+[ParamProb] "ParamProb", | |
+[TSreq] "TSreq", | |
+[TSrep] "TSrep", | |
+[InfoReq] "InfoReq", | |
+[InfoRep] "InfoRep", | |
+}; | |
+ | |
+static void | |
+p_compile(Filter *f) | |
+{ | |
+ if(f->op == '='){ | |
+ compile_cmp(udp.name, f, p_fields); | |
+ return; | |
+ } | |
+ if(strcmp(f->s, "ip") == 0){ | |
+ f->pr = p_mux->pr; | |
+ f->subop = Op; | |
+ return; | |
+ } | |
+ sysfatal("unknown icmp field or protocol: %s", f->s); | |
+} | |
+ | |
+static int | |
+p_filter(Filter *f, Msg *m) | |
+{ | |
+ Hdr *h; | |
+ | |
+ if(m->pe - m->ps < ICMPLEN) | |
+ return 0; | |
+ | |
+ h = (Hdr*)m->ps; | |
+ m->ps += ICMPLEN; | |
+ | |
+ switch(f->subop){ | |
+ case Ot: | |
+ if(h->type == f->ulv) | |
+ return 1; | |
+ break; | |
+ case Op: | |
+ switch(h->type){ | |
+ case Unreachable: | |
+ case TimeExceed: | |
+ case SrcQuench: | |
+ case Redirect: | |
+ case ParamProb: | |
+ m->ps += 4; | |
+ return 1; | |
+ } | |
+ } | |
+ return 0; | |
+} | |
+ | |
+static int | |
+p_seprint(Msg *m) | |
+{ | |
+ Hdr *h; | |
+ char *tn; | |
+ char *p = m->p; | |
+ char *e = m->e; | |
+ ushort cksum2, cksum; | |
+ | |
+ h = (Hdr*)m->ps; | |
+ m->ps += ICMPLEN; | |
+ m->pr = &dump; | |
+ | |
+ if(m->pe - m->ps < ICMPLEN) | |
+ return -1; | |
+ | |
+ tn = icmpmsg[h->type]; | |
+ if(tn == nil) | |
+ p = seprint(p, e, "t=%ud c=%d ck=%4.4ux", h->type, | |
+ h->code, (ushort)NetS(h->cksum)); | |
+ else | |
+ p = seprint(p, e, "t=%s c=%d ck=%4.4ux", tn, | |
+ h->code, (ushort)NetS(h->cksum)); | |
+ if(Cflag){ | |
+ cksum = NetS(h->cksum); | |
+ h->cksum[0] = 0; | |
+ h->cksum[1] = 0; | |
+ cksum2 = ~ptclbsum((uchar*)h, m->pe - m->ps + ICMPLEN) & 0xfff… | |
+ 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 | |
t@@ -0,0 +1,428 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <ip.h> | |
+#include "dat.h" | |
+#include "protos.h" | |
+ | |
+typedef struct Hdr Hdr; | |
+struct Hdr | |
+{ uchar type; | |
+ uchar code; | |
+ uchar cksum[2]; /* Checksum */ | |
+ uchar data[1]; | |
+}; | |
+ | |
+enum | |
+{ | |
+ ICMP6LEN= 4, | |
+}; | |
+ | |
+enum | |
+{ | |
+ Ot, /* type */ | |
+ Op, /* next protocol */}; | |
+ | |
+static Field p_fields[] = | |
+{ | |
+ {"t", Fnum, Ot, "type", } , | |
+ {0} | |
+}; | |
+ | |
+enum | |
+{ | |
+ // ICMPv6 types | |
+ EchoReply = 0, | |
+ UnreachableV6 = 1, | |
+ PacketTooBigV6 = 2, | |
+ TimeExceedV6 = 3, | |
+ ParamProblemV6 = 4, | |
+ Redirect = 5, | |
+ EchoRequest = 8, | |
+ TimeExceed = 11, | |
+ InParmProblem = 12, | |
+ Timestamp = 13, | |
+ TimestampReply = 14, | |
+ InfoRequest = 15, | |
+ InfoReply = 16, | |
+ AddrMaskRequest = 17, | |
+ AddrMaskReply = 18, | |
+ EchoRequestV6 = 128, | |
+ EchoReplyV6 = 129, | |
+ RouterSolicit = 133, | |
+ RouterAdvert = 134, | |
+ NbrSolicit = 135, | |
+ NbrAdvert = 136, | |
+ RedirectV6 = 137, | |
+ | |
+ Maxtype6 = 137, | |
+}; | |
+ | |
+static Mux p_mux[] = | |
+{ | |
+ {"ip6", UnreachableV6, }, | |
+ {"ip6", RedirectV6, }, | |
+ {"ip6", TimeExceedV6, }, | |
+ {0}, | |
+}; | |
+ | |
+char *icmpmsg6[256] = | |
+{ | |
+[EchoReply] "EchoReply", | |
+[UnreachableV6] "UnreachableV6", | |
+[PacketTooBigV6] "PacketTooBigV6", | |
+[TimeExceedV6] "TimeExceedV6", | |
+[Redirect] "Redirect", | |
+[EchoRequest] "EchoRequest", | |
+[TimeExceed] "TimeExceed", | |
+[InParmProblem] "InParmProblem", | |
+[Timestamp] "Timestamp", | |
+[TimestampReply] "TimestampReply", | |
+[InfoRequest] "InfoRequest", | |
+[InfoReply] "InfoReply", | |
+[AddrMaskRequest] "AddrMaskRequest", | |
+[AddrMaskReply] "AddrMaskReply", | |
+[EchoRequestV6] "EchoRequestV6", | |
+[EchoReplyV6] "EchoReplyV6", | |
+[RouterSolicit] "RouterSolicit", | |
+[RouterAdvert] "RouterAdvert", | |
+[NbrSolicit] "NbrSolicit", | |
+[NbrAdvert] "NbrAdvert", | |
+[RedirectV6] "RedirectV6", | |
+}; | |
+ | |
+static char *unreachcode[] = | |
+{ | |
+[0] "no route to destination", | |
+[1] "comm with destination administratively prohibited", | |
+[2] "icmp unreachable: unassigned error code (2)", | |
+[3] "address unreachable", | |
+[4] "port unreachable", | |
+[5] "icmp unreachable: unknown code", | |
+}; | |
+ | |
+static char *timexcode[] = | |
+{ | |
+[0] "hop limit exc", | |
+[1] "reassmbl time exc", | |
+[2] "icmp time exc: unknown code", | |
+}; | |
+ | |
+static char *parpcode[] = | |
+{ | |
+[0] "erroneous header field encountered", | |
+[1] "unrecognized Next Header type encountered", | |
+[2] "unrecognized IPv6 option encountered", | |
+[3] "icmp par prob: unknown code", | |
+}; | |
+enum | |
+{ | |
+ sll = 1, | |
+ tll = 2, | |
+ pref = 3, | |
+ redir = 4, | |
+ mtu = 5, | |
+}; | |
+ | |
+static char *icmp6opts[256] = | |
+{ | |
+[0] "unknown opt", | |
+[1] "sll_addr", | |
+[2] "tll_addr", | |
+[3] "pref_opt", | |
+[4] "redirect", | |
+[5] "mtu_opt", | |
+}; | |
+ | |
+static void | |
+p_compile(Filter *f) | |
+{ | |
+ if(f->op == '='){ | |
+ compile_cmp(icmp6.name, f, p_fields); | |
+ return; | |
+ } | |
+ if(strcmp(f->s, "ip6") == 0){ | |
+ f->pr = p_mux->pr; | |
+ f->subop = Op; | |
+ return; | |
+ } | |
+ sysfatal("unknown icmp field or protocol: %s", f->s); | |
+} | |
+ | |
+static int | |
+p_filter(Filter *f, Msg *m) | |
+{ | |
+ Hdr *h; | |
+ | |
+ if(m->pe - m->ps < ICMP6LEN) | |
+ return 0; | |
+ | |
+ h = (Hdr*)m->ps; | |
+ m->ps += ICMP6LEN; | |
+ | |
+ switch(f->subop){ | |
+ | |
+ case Ot: | |
+ if(h->type == f->ulv) | |
+ return 1; | |
+ break; | |
+ case Op: | |
+ switch(h->type){ | |
+ case UnreachableV6: | |
+ case RedirectV6: | |
+ case TimeExceedV6: | |
+ m->ps += 4; | |
+ return 1; | |
+ } | |
+ } | |
+ return 0; | |
+} | |
+ | |
+static char* | |
+opt_seprint(Msg *m) | |
+{ | |
+ int otype, osz, pktsz; | |
+ uchar *a; | |
+ char *p = m->p; | |
+ char *e = m->e; | |
+ char *opt; | |
+ char optbuf[12]; | |
+ | |
+ pktsz = m->pe - m->ps; | |
+ a = m->ps; | |
+ while (pktsz > 0) { | |
+ otype = *a; | |
+ opt = icmp6opts[otype]; | |
+ if(opt == nil){ | |
+ sprint(optbuf, "0x%ux", otype); | |
+ opt = optbuf; | |
+ } | |
+ osz = (*(a+1)) * 8; | |
+ | |
+ switch (otype) { | |
+ default: | |
+ p = seprint(p, e, "\n option=%s ", opt); | |
+ m->pr = &dump; | |
+ return p; | |
+ | |
+ case sll: | |
+ case tll: | |
+ if ((pktsz < osz) || (osz != 8)) { | |
+ p = seprint(p, e, "\n option=%s bad s… | |
+ m->pr = &dump; | |
+ return p; | |
+ } | |
+ p = seprint(p, e, "\n option=%s maddr=%E", op… | |
+ pktsz -= osz; | |
+ a += osz; | |
+ break; | |
+ | |
+ case pref: | |
+ if ((pktsz < osz) || (osz != 32)) { | |
+ p = seprint(p, e, "\n option=%s: bad … | |
+ m->pr = &dump; | |
+ return p; | |
+ } | |
+ | |
+ p = seprint(p, e, "\n option=%s pref=%I prefl… | |
+ 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 … | |
+ m->pr = &dump; | |
+ return p; | |
+ } | |
+ | |
+ p = seprint(p, e, "\n option=%s len %d", opt,… | |
+ a += osz; | |
+ m->ps = a; | |
+ return p; | |
+ break; | |
+ | |
+ case mtu: | |
+ if ((pktsz < osz) || (osz != 8)) { | |
+ p = seprint(p, e, "\n option=%s: bad … | |
+ m->pr = &dump; | |
+ return p; | |
+ } | |
+ | |
+ p = seprint(p, e, "\n option=%s unused=%1.1d … | |
+ 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) & 0xff… | |
+ 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], Ne… | |
+ 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… | |
+ 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[… | |
+ 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 un… | |
+ (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 tar… | |
+ (*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)!… | |
+ 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 | |
t@@ -0,0 +1,146 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <ip.h> | |
+#include "dat.h" | |
+#include "protos.h" | |
+ | |
+typedef struct Hdr Hdr; | |
+struct Hdr | |
+{ | |
+ uchar sum[2]; /* Checksum including header */ | |
+ uchar len[2]; /* Packet length */ | |
+ uchar type; /* Packet type */ | |
+ uchar spec; /* Special */ | |
+ uchar sport[2]; /* Src port */ | |
+ uchar dport[2]; /* Dst port */ | |
+ uchar id[4]; /* Sequence id */ | |
+ uchar ack[4]; /* Acked sequence */ | |
+}; | |
+ | |
+enum | |
+{ | |
+ ILLEN= 18, | |
+}; | |
+ | |
+enum | |
+{ | |
+ Os, | |
+ Od, | |
+ Osd, | |
+}; | |
+ | |
+static Field p_fields[] = | |
+{ | |
+ {"s", Fnum, Os, "source port", } , | |
+ {"d", Fnum, Od, "dest port", } , | |
+ {"a", Fnum, Osd, "source/dest port", … | |
+ {"sd", Fnum, Osd, "source/dest port", … | |
+ {0} | |
+}; | |
+ | |
+static Mux p_mux[] = | |
+{ | |
+ {"ninep", 17007, }, /* exportfs */ | |
+ {"ninep", 17008, }, /* 9fs */ | |
+ {"ninep", 17005, }, /* ocpu */ | |
+ {"ninep", 17010, }, /* ncpu */ | |
+ {"ninep", 17013, }, /* cpu */ | |
+ {0}, | |
+}; | |
+ | |
+static void | |
+p_compile(Filter *f) | |
+{ | |
+ Mux *m; | |
+ | |
+ if(f->op == '='){ | |
+ compile_cmp(udp.name, f, p_fields); | |
+ return; | |
+ } | |
+ for(m = p_mux; m->name != nil; m++) | |
+ if(strcmp(f->s, m->name) == 0){ | |
+ f->pr = m->pr; | |
+ f->ulv = m->val; | |
+ f->subop = Osd; | |
+ return; | |
+ } | |
+ sysfatal("unknown il field or protocol: %s", f->s); | |
+} | |
+ | |
+static int | |
+p_filter(Filter *f, Msg *m) | |
+{ | |
+ Hdr *h; | |
+ | |
+ if(m->pe - m->ps < ILLEN) | |
+ return 0; | |
+ h = (Hdr*)m->ps; | |
+ m->ps += ILLEN; | |
+ | |
+ switch(f->subop){ | |
+ case Os: | |
+ return NetS(h->sport) == f->ulv; | |
+ case Od: | |
+ return NetS(h->dport) == f->ulv; | |
+ case Osd: | |
+ return NetS(h->sport) == f->ulv || NetS(h->dport) == f->ulv; | |
+ } | |
+ return 0; | |
+} | |
+ | |
+char *pktnames[] = | |
+{ | |
+ "Sync", | |
+ "Data", | |
+ "Dataquery", | |
+ "Ack", | |
+ "Query", | |
+ "State", | |
+ "Close" | |
+}; | |
+ | |
+static char* | |
+pkttype(int t) | |
+{ | |
+ static char b[10]; | |
+ | |
+ if(t > 6){ | |
+ sprint(b, "%d", t); | |
+ return b; | |
+ } | |
+ return pktnames[t]; | |
+} | |
+ | |
+static int | |
+p_seprint(Msg *m) | |
+{ | |
+ Hdr *h; | |
+ int dport, sport; | |
+ | |
+ if(m->pe - m->ps < ILLEN) | |
+ return -1; | |
+ h = (Hdr*)m->ps; | |
+ m->ps += ILLEN; | |
+ | |
+ dport = NetS(h->dport); | |
+ sport = NetS(h->sport); | |
+ demux(p_mux, sport, dport, m, &dump); | |
+ | |
+ m->p = seprint(m->p, m->e, "s=%d d=%d t=%s id=%lud ack=%lud spec=%d ck… | |
+ 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 | |
t@@ -0,0 +1,236 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <ip.h> | |
+#include "dat.h" | |
+#include "protos.h" | |
+ | |
+typedef struct Hdr Hdr; | |
+struct Hdr | |
+{ | |
+ uchar vihl; /* Version and header length */ | |
+ uchar tos; /* Type of service */ | |
+ uchar length[2]; /* packet length */ | |
+ uchar id[2]; /* ip->identification */ | |
+ uchar frag[2]; /* Fragment information */ | |
+ uchar ttl; /* Time to live */ | |
+ uchar proto; /* Protocol */ | |
+ uchar cksum[2]; /* Header checksum */ | |
+ uchar src[4]; /* IP source */ | |
+ uchar dst[4]; /* IP destination */ | |
+}; | |
+ | |
+enum | |
+{ | |
+ IPHDR = 20, /* sizeof(Iphdr) */ | |
+ IP_VER = 0x40, /* Using IP version 4 */ | |
+ IP_DF = 0x4000, /* Don't fragment */ | |
+ IP_MF = 0x2000, /* More fragments */ | |
+}; | |
+ | |
+static Mux p_mux[] = | |
+{ | |
+ { "icmp", 1, }, | |
+ { "igmp", 2, }, | |
+ { "ggp", 3, }, | |
+ { "ip", 4, }, | |
+ { "st", 5, }, | |
+ { "tcp", 6, }, | |
+ { "ucl", 7, }, | |
+ { "egp", 8, }, | |
+ { "igp", 9, }, | |
+ { "bbn-rcc-mon", 10, }, | |
+ { "nvp-ii", 11, }, | |
+ { "pup", 12, }, | |
+ { "argus", 13, }, | |
+ { "emcon", 14, }, | |
+ { "xnet", 15, }, | |
+ { "chaos", 16, }, | |
+ { "udp", 17, }, | |
+ { "mux", 18, }, | |
+ { "dcn-meas", 19, }, | |
+ { "hmp", 20, }, | |
+ { "prm", 21, }, | |
+ { "xns-idp", 22, }, | |
+ { "trunk-1", 23, }, | |
+ { "trunk-2", 24, }, | |
+ { "leaf-1", 25, }, | |
+ { "leaf-2", 26, }, | |
+ { "rdp", 27, }, | |
+ { "irtp", 28, }, | |
+ { "iso-tp4", 29, }, | |
+ { "netblt", 30, }, | |
+ { "mfe-nsp", 31, }, | |
+ { "merit-inp", 32, }, | |
+ { "sep", 33, }, | |
+ { "3pc", 34, }, | |
+ { "idpr", 35, }, | |
+ { "xtp", 36, }, | |
+ { "ddp", 37, }, | |
+ { "idpr-cmtp", 38, }, | |
+ { "tp++", 39, }, | |
+ { "il", 40, }, | |
+ { "sip", 41, }, | |
+ { "sdrp", 42, }, | |
+ { "sip-sr", 43, }, | |
+ { "sip-frag", 44, }, | |
+ { "idrp", 45, }, | |
+ { "rsvp", 46, }, | |
+ { "gre", 47, }, | |
+ { "mhrp", 48, }, | |
+ { "bna", 49, }, | |
+ { "sipp-esp", 50, }, | |
+ { "sipp-ah", 51, }, | |
+ { "i-nlsp", 52, }, | |
+ { "swipe", 53, }, | |
+ { "nhrp", 54, }, | |
+ { "any", 61, }, | |
+ { "cftp", 62, }, | |
+ { "any", 63, }, | |
+ { "sat-expak", 64, }, | |
+ { "kryptolan", 65, }, | |
+ { "rvd", 66, }, | |
+ { "ippc", 67, }, | |
+ { "any", 68, }, | |
+ { "sat-mon", 69, }, | |
+ { "visa", 70, }, | |
+ { "ipcv", 71, }, | |
+ { "cpnx", 72, }, | |
+ { "cphb", 73, }, | |
+ { "wsn", 74, }, | |
+ { "pvp", 75, }, | |
+ { "br-sat-mon", 76, }, | |
+ { "sun-nd", 77, }, | |
+ { "wb-mon", 78, }, | |
+ { "wb-expak", 79, }, | |
+ { "iso-ip", 80, }, | |
+ { "vmtp", 81, }, | |
+ { "secure-vmtp", 82, }, | |
+ { "vines", 83, }, | |
+ { "ttp", 84, }, | |
+ { "nsfnet-igp", 85, }, | |
+ { "dgp", 86, }, | |
+ { "tcf", 87, }, | |
+ { "igrp", 88, }, | |
+ { "ospf", 89, }, | |
+ { "sprite-rpc", 90, }, | |
+ { "larp", 91, }, | |
+ { "mtp", 92, }, | |
+ { "ax.25", 93, }, | |
+ { "ipip", 94, }, | |
+ { "micp", 95, }, | |
+ { "scc-sp", 96, }, | |
+ { "etherip", 97, }, | |
+ { "encap", 98, }, | |
+ { "any", 99, }, | |
+ { "gmtp", 100, }, | |
+ { "rudp", 254, }, | |
+ { 0 } | |
+}; | |
+ | |
+enum | |
+{ | |
+ Os, // source | |
+ Od, // destination | |
+ Osd, // source or destination | |
+ Ot, // type | |
+}; | |
+ | |
+static Field p_fields[] = | |
+{ | |
+ {"s", Fv4ip, Os, "source address", } , | |
+ {"d", Fv4ip, Od, "destination address", } , | |
+ {"a", Fv4ip, Osd, "source|destination address",} , | |
+ {"sd", Fv4ip, Osd, "source|destination address",}… | |
+ {"t", Fnum, Ot, "sub protocol number", } , | |
+ {0} | |
+}; | |
+ | |
+static void | |
+p_compile(Filter *f) | |
+{ | |
+ Mux *m; | |
+ | |
+ if(f->op == '='){ | |
+ compile_cmp(ip.name, f, p_fields); | |
+ return; | |
+ } | |
+ for(m = p_mux; m->name != nil; m++) | |
+ if(strcmp(f->s, m->name) == 0){ | |
+ f->pr = m->pr; | |
+ f->ulv = m->val; | |
+ f->subop = Ot; | |
+ return; | |
+ } | |
+ sysfatal("unknown ip field or protocol: %s", f->s); | |
+} | |
+ | |
+static int | |
+p_filter(Filter *f, Msg *m) | |
+{ | |
+ Hdr *h; | |
+ | |
+ if(m->pe - m->ps < IPHDR) | |
+ return 0; | |
+ | |
+ h = (Hdr*)m->ps; | |
+ m->ps += ((h->vihl&0xf)<<2); | |
+ | |
+ switch(f->subop){ | |
+ case Os: | |
+ return NetL(h->src) == f->ulv; | |
+ case Od: | |
+ return NetL(h->dst) == f->ulv; | |
+ case Osd: | |
+ return NetL(h->src) == f->ulv || NetL(h->dst) == f->ulv; | |
+ case Ot: | |
+ return h->proto == f->ulv; | |
+ } | |
+ return 0; | |
+} | |
+ | |
+static int | |
+p_seprint(Msg *m) | |
+{ | |
+ Hdr *h; | |
+ int f; | |
+ int len; | |
+ | |
+ if(m->pe - m->ps < IPHDR) | |
+ return -1; | |
+ h = (Hdr*)m->ps; | |
+ | |
+ /* next protocol, just dump unless this is the first fragment */ | |
+ m->pr = &dump; | |
+ f = NetS(h->frag); | |
+ if((f & ~(IP_DF|IP_MF)) == 0) | |
+ demux(p_mux, h->proto, h->proto, m, &dump); | |
+ | |
+ /* truncate the message if there's extra */ | |
+ len = NetS(h->length); | |
+ if(len < m->pe - m->ps) | |
+ m->pe = m->ps + len; | |
+ | |
+ /* next header */ | |
+ m->ps += ((h->vihl&0xf)<<2); | |
+ | |
+ m->p = seprint(m->p, m->e, "s=%V d=%V id=%4.4ux frag=%4.4ux ttl=%3d pr… | |
+ 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 | |
t@@ -0,0 +1,309 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <ip.h> | |
+#include "dat.h" | |
+#include "protos.h" | |
+ | |
+typedef struct Hdr Hdr; | |
+struct Hdr | |
+{ | |
+ uchar vcf[4]; /* Version and header leng… | |
+ 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->… | |
+ 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=… | |
+ 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 | |
t@@ -0,0 +1,841 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <ip.h> | |
+#include <bio.h> | |
+#include <fcall.h> | |
+#include <libsec.h> | |
+#include "dat.h" | |
+#include "protos.h" | |
+#include "y.tab.h" | |
+ | |
+int Cflag; | |
+int pflag; | |
+int Nflag; | |
+int sflag; | |
+int tiflag; | |
+int toflag; | |
+ | |
+char *prom = "promiscuous"; | |
+ | |
+enum | |
+{ | |
+ Pktlen= 64*1024, | |
+ Blen= 16*1024, | |
+}; | |
+ | |
+Filter *filter; | |
+Proto *root; | |
+Biobuf out; | |
+vlong starttime, pkttime; | |
+int pcap; | |
+ | |
+int filterpkt(Filter *f, uchar *ps, uchar *pe, Proto *pr, int); | |
+void printpkt(char *p, char *e, uchar *ps, uchar *pe); | |
+void mkprotograph(void); | |
+Proto* findproto(char *name); | |
+Filter* compile(Filter *f); | |
+void printfilter(Filter *f, char *tag); | |
+void printhelp(void); | |
+void tracepkt(uchar*, int); | |
+void pcaphdr(void); | |
+ | |
+void | |
+usage(void) | |
+{ | |
+ fprint(2, "usage: %s [-std?] [-c] [-N n] [-f filter] [-h first-header]… | |
+ 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 p… | |
+ 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… | |
+ } | |
+ 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 tr… | |
+ (*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 | |
t@@ -0,0 +1,69 @@ | |
+<$PLAN9/src/mkhdr | |
+ | |
+TARG=snoopy | |
+PROTOS=\ | |
+ ether\ | |
+ ip\ | |
+ ip6\ | |
+ dump\ | |
+ arp\ | |
+ rarp\ | |
+ udp\ | |
+ bootp\ | |
+ dhcp\ | |
+ hdlc\ | |
+ rtp\ | |
+ rtcp\ | |
+ tcp\ | |
+ il\ | |
+ icmp\ | |
+ icmp6\ | |
+ ninep\ | |
+ ospf\ | |
+ ppp\ | |
+ ppp_ccp\ | |
+ ppp_lcp\ | |
+ ppp_chap\ | |
+ ppp_ipcp\ | |
+ pppoe_sess\ | |
+ pppoe_disc\ | |
+ | |
+POBJS=${PROTOS:%=%.$O} | |
+ | |
+OFILES= main.$O\ | |
+ y.tab.$O\ | |
+ protos.$O\ | |
+ $SYSNAME.$O\ | |
+ $POBJS | |
+ | |
+HFILES=dat.h\ | |
+ protos.h\ | |
+ y.tab.h\ | |
+ | |
+<$PLAN9/src/mkone | |
+ | |
+protos.h: mkfile | |
+ ( | |
+ for i in $PROTOS | |
+ do | |
+ echo extern Proto $i';' | |
+ done | |
+ ) > protos.h | |
+ | |
+protos.c: mkfile | |
+ ( | |
+ echo '#include <u.h>' | |
+ echo '#include <libc.h>' | |
+ echo '#include "dat.h"' | |
+ echo '#include "protos.h"' | |
+ echo 'Proto *protos[] =' | |
+ echo '{' | |
+ for i in $PROTOS | |
+ do | |
+ echo ' &'$i',' | |
+ done | |
+ echo ' 0,' | |
+ echo '};' | |
+ ) > protos.c | |
+ | |
+y.tab.c: filter.y | |
diff --git a/src/cmd/ip/snoopy/ninep.c b/src/cmd/ip/snoopy/ninep.c | |
t@@ -0,0 +1,55 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <ip.h> | |
+#include <fcall.h> | |
+#include "dat.h" | |
+#include "protos.h" | |
+ | |
+static void | |
+p_compile(Filter *f) | |
+{ | |
+ sysfatal("unknown ninep field: %s", f->s); | |
+} | |
+ | |
+static int | |
+p_filter(Filter *f, Msg *m) | |
+{ | |
+ USED(f); | |
+ USED(m); | |
+ return 0; | |
+} | |
+ | |
+static int | |
+p_seprint(Msg *m) | |
+{ | |
+ Fcall f; | |
+ char *p; | |
+ | |
+ memset(&f, 0, sizeof(f)); | |
+ f.type = 0; | |
+ f.data = 0; /* protection for %F */ | |
+ if(convM2S(m->ps, m->pe-m->ps, &f)){ | |
+ p = m->p; | |
+ m->p = seprint(m->p, m->e, "%F", &f); | |
+ while(p < m->p){ | |
+ p = strchr(p, '\n'); | |
+ if(p == nil) | |
+ break; | |
+ *p = '\\'; | |
+ } | |
+ } else | |
+ dump.seprint(m); | |
+ m->pr = nil; | |
+ return 0; | |
+} | |
+ | |
+Proto ninep = | |
+{ | |
+ "ninep", | |
+ p_compile, | |
+ p_filter, | |
+ p_seprint, | |
+ nil, | |
+ nil, | |
+ defaultframer, | |
+}; | |
diff --git a/src/cmd/ip/snoopy/ospf.c b/src/cmd/ip/snoopy/ospf.c | |
t@@ -0,0 +1,404 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <ip.h> | |
+#include <libsec.h> | |
+#include "dat.h" | |
+#include "protos.h" | |
+ | |
+ | |
+/* | |
+ * OSPF packets | |
+ */ | |
+typedef struct Ospfpkt Ospfpkt; | |
+struct Ospfpkt | |
+{ | |
+ uchar version; | |
+ uchar type; | |
+ uchar length[2]; | |
+ uchar router[4]; | |
+ uchar area[4]; | |
+ uchar sum[2]; | |
+ uchar autype[2]; | |
+ uchar auth[8]; | |
+ uchar data[1]; | |
+}; | |
+#define OSPF_HDRSIZE 24 | |
+ | |
+enum | |
+{ | |
+ OSPFhello= 1, | |
+ OSPFdd= 2, | |
+ OSPFlsrequest= 3, | |
+ OSPFlsupdate= 4, | |
+ OSPFlsack= 5, | |
+}; | |
+ | |
+ | |
+char *ospftype[] = { | |
+ [OSPFhello] "hello", | |
+ [OSPFdd] "data definition", | |
+ [OSPFlsrequest] "link state request", | |
+ [OSPFlsupdate] "link state update", | |
+ [OSPFlsack] "link state ack", | |
+}; | |
+ | |
+char* | |
+ospfpkttype(int x) | |
+{ | |
+ static char type[16]; | |
+ | |
+ if(x > 0 && x <= OSPFlsack) | |
+ return ospftype[x]; | |
+ sprint(type, "type %d", x); | |
+ return type; | |
+} | |
+ | |
+char* | |
+ospfauth(Ospfpkt *ospf) | |
+{ | |
+ static char auth[100]; | |
+ | |
+ switch(ospf->type){ | |
+ case 0: | |
+ return "no authentication"; | |
+ case 1: | |
+ sprint(auth, "password(%8.8ux %8.8ux)", NetL(ospf->auth), … | |
+ NetL(ospf->auth+4)); | |
+ break; | |
+ case 2: | |
+ sprint(auth, "crypto(plen %d id %d dlen %d)", NetS(ospf->auth)… | |
+ ospf->auth[2], ospf->auth[3]); | |
+ break; | |
+ default: | |
+ sprint(auth, "auth%d(%8.8ux %8.8ux)", NetS(ospf->autype), NetL… | |
+ 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 … | |
+ 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 bound… | |
+ * 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 … | |
+ 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 … | |
+ h->netmask, h->lsa.flag, Net3(h->lsa.m… | |
+ 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 | |
t@@ -0,0 +1,629 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <ip.h> | |
+#include <libsec.h> | |
+#include "dat.h" | |
+#include "protos.h" | |
+ | |
+/* PPP stuff */ | |
+enum { | |
+ PPP_addr= 0xff, | |
+ PPP_ctl= 0x3, | |
+ PPP_period= 3*1000, /* period of retransmit process (in … | |
+}; | |
+ | |
+/* PPP protocols */ | |
+enum { | |
+ PPP_ip= 0x21, /* internet */ | |
+ PPP_vjctcp= 0x2d, /* compressing van jacobson tc… | |
+ PPP_vjutcp= 0x2f, /* uncompressing van jacobson … | |
+ 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 ":"r… | |
+ 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->d… | |
+ } | |
+ 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)", … | |
+ 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)", p… | |
+ 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->typ… | |
+ 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 | |
t@@ -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 | |
t@@ -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 | |
t@@ -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 | |
t@@ -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 | |
t@@ -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 | |
t@@ -0,0 +1,172 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <ip.h> | |
+#include "dat.h" | |
+#include "protos.h" | |
+ | |
+typedef struct Hdr Hdr; | |
+struct Hdr { | |
+ uchar verstype; | |
+ uchar code; | |
+ uchar sessid[2]; | |
+ uchar length[2]; /* of payload */ | |
+}; | |
+enum | |
+{ | |
+ HDRSIZE = 1+1+2+2 | |
+}; | |
+ | |
+static Mux p_mux[] = | |
+{ | |
+ {"ppp", 0, } , | |
+ {0} | |
+}; | |
+ | |
+enum | |
+{ | |
+ Overs, | |
+ Otype, | |
+ Ocode, | |
+ Osess, | |
+}; | |
+ | |
+static Field p_fields[] = | |
+{ | |
+ {"v", Fnum, Overs, "version", } , | |
+ {"t", Fnum, Otype, "type", } , | |
+ {"c", Fnum, Ocode, "code" } , | |
+ {"s", Fnum, Osess, "sessid" } , | |
+ {0} | |
+}; | |
+ | |
+static void | |
+p_compilesess(Filter *f) | |
+{ | |
+// Mux *m; | |
+ | |
+ if(f->op == '='){ | |
+ compile_cmp(pppoe_sess.name, f, p_fields); | |
+ return; | |
+ } | |
+/* | |
+ for(m = p_mux; m->name != nil; m++) | |
+ if(strcmp(f->s, m->name) == 0){ | |
+ f->pr = m->pr; | |
+ f->ulv = m->val; | |
+ f->subop = Ot; | |
+ return; | |
+ } | |
+*/ | |
+ sysfatal("unknown pppoe field or protocol: %s", f->s); | |
+} | |
+static void | |
+p_compiledisc(Filter *f) | |
+{ | |
+// Mux *m; | |
+ | |
+ if(f->op == '='){ | |
+ compile_cmp(pppoe_disc.name, f, p_fields); | |
+ return; | |
+ } | |
+/* | |
+ for(m = p_mux; m->name != nil; m++) | |
+ if(strcmp(f->s, m->name) == 0){ | |
+ f->pr = m->pr; | |
+ f->ulv = m->val; | |
+ f->subop = Ot; | |
+ return; | |
+ } | |
+*/ | |
+ sysfatal("unknown pppoe field or protocol: %s", f->s); | |
+} | |
+ | |
+static int | |
+p_filter(Filter *f, Msg *m) | |
+{ | |
+ Hdr *h; | |
+ | |
+ if(m->pe - m->ps < HDRSIZE) | |
+ return 0; | |
+ | |
+ h = (Hdr*)m->ps; | |
+ m->ps += HDRSIZE; | |
+ | |
+ switch(f->subop){ | |
+ case Overs: | |
+ return (h->verstype>>4) == f->ulv; | |
+ case Otype: | |
+ return (h->verstype&0xF) == f->ulv; | |
+ case Ocode: | |
+ return h->code == f->ulv; | |
+ case Osess: | |
+ return NetS(h->sessid) == f->ulv; | |
+ } | |
+ return 0; | |
+} | |
+ | |
+/* BUG: print all the discovery types */ | |
+static int | |
+p_seprintdisc(Msg *m) | |
+{ | |
+ Hdr *h; | |
+ int len; | |
+ | |
+ len = m->pe - m->ps; | |
+ if(len < HDRSIZE) | |
+ return -1; | |
+ | |
+ h = (Hdr*)m->ps; | |
+ m->ps += HDRSIZE; | |
+ | |
+ m->pr = nil; | |
+ | |
+ m->p = seprint(m->p, m->e, "v=%d t=%d c=0x%x s=0x%ux, len=%d", | |
+ h->verstype>>4, h->verstype&0xF, h->code, NetS(h->sessid), Net… | |
+ | |
+ 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), Net… | |
+ | |
+ 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 | |
t@@ -0,0 +1 @@ | |
+/* placeholder; see pppoe_disc.c */ | |
diff --git a/src/cmd/ip/snoopy/protos.c b/src/cmd/ip/snoopy/protos.c | |
t@@ -0,0 +1,33 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include "dat.h" | |
+#include "protos.h" | |
+Proto *protos[] = | |
+{ | |
+ ð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 | |
t@@ -0,0 +1,25 @@ | |
+extern Proto ether; | |
+extern Proto ip; | |
+extern Proto ip6; | |
+extern Proto dump; | |
+extern Proto arp; | |
+extern Proto rarp; | |
+extern Proto udp; | |
+extern Proto bootp; | |
+extern Proto dhcp; | |
+extern Proto hdlc; | |
+extern Proto rtp; | |
+extern Proto rtcp; | |
+extern Proto tcp; | |
+extern Proto il; | |
+extern Proto icmp; | |
+extern Proto icmp6; | |
+extern Proto ninep; | |
+extern Proto ospf; | |
+extern Proto ppp; | |
+extern Proto ppp_ccp; | |
+extern Proto ppp_lcp; | |
+extern Proto ppp_chap; | |
+extern Proto ppp_ipcp; | |
+extern Proto pppoe_sess; | |
+extern Proto pppoe_disc; | |
diff --git a/src/cmd/ip/snoopy/rarp.c b/src/cmd/ip/snoopy/rarp.c | |
t@@ -0,0 +1 @@ | |
+/* place holder, this stuff is really in arp.c */ | |
diff --git a/src/cmd/ip/snoopy/rtcp.c b/src/cmd/ip/snoopy/rtcp.c | |
t@@ -0,0 +1,97 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <ip.h> | |
+#include "dat.h" | |
+#include "protos.h" | |
+ | |
+typedef struct Hdr Hdr; | |
+struct Hdr { | |
+ uchar hdr; // RTCP header | |
+ uchar pt; // Packet type | |
+ uchar len[2]; // Report length | |
+ uchar ssrc[4]; // Synchronization source identif… | |
+ 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.%.… | |
+ (h->hdr >> 6) & 3, rc, h->pt, NetL(h->ssrc), | |
+ NetL(h->ntp), (uint)NetL(&h->ntp[4]), NetL(h->… | |
+ 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=%… | |
+ NetL(r->ssrc), frac, NetL(r->lost), NetL(r->se… | |
+ 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 | |
t@@ -0,0 +1,76 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <ip.h> | |
+#include "dat.h" | |
+#include "protos.h" | |
+ | |
+typedef struct Hdr Hdr; | |
+struct Hdr { | |
+ uchar hdr; // RTP header | |
+ uchar marker; // Payload and marker | |
+ uchar seq[2]; // Sequence number | |
+ uchar ts[4]; // Time stamp | |
+ uchar ssrc[4]; // Synchronization source identifier | |
+}; | |
+ | |
+enum{ | |
+ RTPLEN = 12, // Minimum size of an RTP header | |
+}; | |
+ | |
+ | |
+static void | |
+p_compile(Filter *f) | |
+{ | |
+ sysfatal("unknown rtp field: %s", f->s); | |
+} | |
+ | |
+static int | |
+p_filter(Filter *f, Msg *m) | |
+{ | |
+ USED(f); | |
+ USED(m); | |
+ return 0; | |
+} | |
+ | |
+static int | |
+p_seprint(Msg *m) | |
+{ | |
+ Hdr*h; | |
+ ushort seq; | |
+ ulong ssrc, ts; | |
+ int cc, i; | |
+ | |
+ if(m->pe - m->ps < RTPLEN) | |
+ return -1; | |
+ | |
+ h = (Hdr*)m->ps; | |
+ cc = h->hdr & 0xf; | |
+ if(m->pe - m->ps < RTPLEN + cc * 4) | |
+ return -1; | |
+ | |
+ m->ps += RTPLEN; | |
+ | |
+ seq = NetS(h->seq); | |
+ ts = NetL(h->ts); | |
+ ssrc = NetL(h->ssrc); | |
+ | |
+ m->p = seprint(m->p, m->e, "version=%d x=%d cc=%d seq=%d ts=%ld ssrc=%… | |
+ (h->hdr >> 6) & 3, (h->hdr >> 4) & 1, cc, seq,… | |
+ 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 | |
t@@ -0,0 +1,221 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <ip.h> | |
+#include "dat.h" | |
+#include "protos.h" | |
+ | |
+typedef struct Hdr Hdr; | |
+struct Hdr | |
+{ | |
+ uchar sport[2]; | |
+ uchar dport[2]; | |
+ uchar seq[4]; | |
+ uchar ack[4]; | |
+ uchar flag[2]; | |
+ uchar win[2]; | |
+ uchar cksum[2]; | |
+ uchar urg[2]; | |
+ uchar opt[1]; | |
+}; | |
+ | |
+typedef struct PseudoHdr{ | |
+ uchar src[4]; | |
+ uchar dst[4]; | |
+ uchar zero; | |
+ uchar proto; | |
+ uchar length[2]; | |
+ uchar hdrdata[1580]; | |
+} PseudoHdr; | |
+ | |
+enum | |
+{ | |
+ TCPLEN= 20, | |
+}; | |
+ | |
+enum | |
+{ | |
+ Os, | |
+ Od, | |
+ Osd, | |
+}; | |
+ | |
+static Field p_fields[] = | |
+{ | |
+ {"s", Fnum, Os, "source port", } , | |
+ {"d", Fnum, Od, "dest port", } , | |
+ {"a", Fnum, Osd, "source/dest port", … | |
+ {"sd", Fnum, Osd, "source/dest port", … | |
+ {0} | |
+}; | |
+ | |
+static Mux p_mux[] = | |
+{ | |
+ {"ninep", 17007, }, /* exportfs */ | |
+ {"ninep", 564, }, /* 9fs */ | |
+ {"ninep", 17005, }, /* ocpu */ | |
+ {"ninep", 17010, }, /* ncpu */ | |
+ {"ninep", 17013, }, /* cpu */ | |
+ {0}, | |
+}; | |
+ | |
+enum | |
+{ | |
+ EOLOPT = 0, | |
+ NOOPOPT = 1, | |
+ MSSOPT = 2, | |
+ MSS_LENGTH = 4, /* Mean segment size */ | |
+ WSOPT = 3, | |
+ WS_LENGTH = 3, /* Bits to scale window size by */ | |
+}; | |
+ | |
+static void | |
+p_compile(Filter *f) | |
+{ | |
+ Mux *m; | |
+ | |
+ if(f->op == '='){ | |
+ compile_cmp(udp.name, f, p_fields); | |
+ return; | |
+ } | |
+ for(m = p_mux; m->name != nil; m++) | |
+ if(strcmp(f->s, m->name) == 0){ | |
+ f->pr = m->pr; | |
+ f->ulv = m->val; | |
+ f->subop = Osd; | |
+ return; | |
+ } | |
+ sysfatal("unknown tcp field or protocol: %s", f->s); | |
+} | |
+ | |
+static int | |
+p_filter(Filter *f, Msg *m) | |
+{ | |
+ Hdr *h; | |
+ | |
+ if(m->pe - m->ps < TCPLEN) | |
+ return 0; | |
+ | |
+ h = (Hdr*)m->ps; | |
+ m->ps += ((NetS(h->flag)>>10)&0x3f); | |
+ | |
+ switch(f->subop){ | |
+ case Os: | |
+ return NetS(h->sport) == f->ulv; | |
+ case Od: | |
+ return NetS(h->dport) == f->ulv; | |
+ case Osd: | |
+ return NetS(h->sport) == f->ulv || NetS(h->dport) == f->ulv; | |
+ } | |
+ return 0; | |
+} | |
+ | |
+enum | |
+{ | |
+ URG = 0x20, /* Data marked urgent */ | |
+ ACK = 0x10, /* Aknowledge is valid */ | |
+ PSH = 0x08, /* Whole data pipe is pushed… | |
+ RST = 0x04, /* Reset connection */ | |
+ SYN = 0x02, /* Pkt. is synchronise */ | |
+ FIN = 0x01, /* Start close down */ | |
+}; | |
+ | |
+static char* | |
+flags(int f) | |
+{ | |
+ static char fl[20]; | |
+ char *p; | |
+ | |
+ p = fl; | |
+ if(f & URG) | |
+ *p++ = 'U'; | |
+ if(f & ACK) | |
+ *p++ = 'A'; | |
+ if(f & PSH) | |
+ *p++ = 'P'; | |
+ if(f & RST) | |
+ *p++ = 'R'; | |
+ if(f & SYN) | |
+ *p++ = 'S'; | |
+ if(f & FIN) | |
+ *p++ = 'F'; | |
+ *p = 0; | |
+ return fl; | |
+} | |
+ | |
+ | |
+static int | |
+p_seprint(Msg *m) | |
+{ | |
+ Hdr *h; | |
+ int dport, sport; | |
+ int len, flag, optlen; | |
+ uchar *optr; | |
+ | |
+ if(m->pe - m->ps < TCPLEN) | |
+ return -1; | |
+ h = (Hdr*)m->ps; | |
+ | |
+ /* get tcp header length */ | |
+ flag = NetS(h->flag); | |
+ len = (flag>>10)&~3; | |
+ flag &= 0x3ff; | |
+ m->ps += len; | |
+ | |
+ /* next protocol */ | |
+ dport = NetS(h->dport); | |
+ sport = NetS(h->sport); | |
+ demux(p_mux, sport, dport, m, &dump); | |
+ | |
+ m->p = seprint(m->p, m->e, "s=%d d=%d seq=%lud ack=%lud fl=%s win=%d c… | |
+ 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,… | |
+ break; | |
+ case WSOPT: | |
+ m->p = seprint(m->p, m->e, " opt%d=(wscale %ud)", optl… | |
+ break; | |
+ default: | |
+ m->p = seprint(m->p, m->e, " opt%d=(%ud %.*H)", optlen… | |
+ } | |
+ 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 | |
t@@ -0,0 +1,131 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <ip.h> | |
+#include "dat.h" | |
+#include "protos.h" | |
+ | |
+typedef struct Hdr Hdr; | |
+struct Hdr | |
+{ | |
+ uchar sport[2]; /* Source port */ | |
+ uchar dport[2]; /* Destination port */ | |
+ uchar len[2]; /* data length */ | |
+ uchar cksum[2]; /* Checksum */ | |
+}; | |
+ | |
+enum | |
+{ | |
+ UDPLEN= 8, | |
+}; | |
+ | |
+enum | |
+{ | |
+ Os, | |
+ Od, | |
+ Osd, | |
+ Osetport, | |
+}; | |
+ | |
+static Field p_fields[] = | |
+{ | |
+ {"s", Fnum, Os, "source port", } , | |
+ {"d", Fnum, Od, "dest port", } , | |
+ {"a", Fnum, Osd, "source/dest port", … | |
+ {"sd", Fnum, Osd, "source/dest port", … | |
+ {0} | |
+}; | |
+ | |
+#define ANYPORT ~0UL | |
+ | |
+static Mux p_mux[] = | |
+{ | |
+ {"bootp", 67, }, | |
+ {"ninep", 6346, }, /* tvs */ | |
+ {"rtp", ANYPORT, }, | |
+ {"rtcp", ANYPORT, }, | |
+ {0}, | |
+}; | |
+ | |
+/* default next protocol, can be changed by p_filter, reset by p_compile */ | |
+static Proto *defproto = &dump; | |
+ | |
+static void | |
+p_compile(Filter *f) | |
+{ | |
+ Mux *m; | |
+ | |
+ if(f->op == '='){ | |
+ compile_cmp(udp.name, f, p_fields); | |
+ return; | |
+ } | |
+ for(m = p_mux; m->name != nil; m++) | |
+ if(strcmp(f->s, m->name) == 0){ | |
+ f->pr = m->pr; | |
+ f->ulv = m->val; | |
+ f->subop = Osd; | |
+ return; | |
+ } | |
+ | |
+ sysfatal("unknown udp field or protocol: %s", f->s); | |
+} | |
+ | |
+static int | |
+p_filter(Filter *f, Msg *m) | |
+{ | |
+ Hdr *h; | |
+ | |
+ if(m->pe - m->ps < UDPLEN) | |
+ return 0; | |
+ | |
+ h = (Hdr*)m->ps; | |
+ m->ps += UDPLEN; | |
+ | |
+ switch(f->subop){ | |
+ case Os: | |
+ return NetS(h->sport) == f->ulv; | |
+ case Od: | |
+ return NetS(h->dport) == f->ulv; | |
+ case Osd: | |
+ if(f->ulv == ANYPORT){ | |
+ defproto = f->pr; | |
+ return 1; | |
+ } | |
+ return NetS(h->sport) == f->ulv || NetS(h->dport) == f->ulv; | |
+ } | |
+ return 0; | |
+} | |
+ | |
+static int | |
+p_seprint(Msg *m) | |
+{ | |
+ Hdr *h; | |
+ int dport, sport; | |
+ | |
+ | |
+ if(m->pe - m->ps < UDPLEN) | |
+ return -1; | |
+ h = (Hdr*)m->ps; | |
+ m->ps += UDPLEN; | |
+ | |
+ /* next protocol */ | |
+ sport = NetS(h->sport); | |
+ dport = NetS(h->dport); | |
+ demux(p_mux, sport, dport, m, defproto); | |
+ defproto = &dump; | |
+ | |
+ m->p = seprint(m->p, m->e, "s=%d d=%d ck=%4.4ux ln=%4d", | |
+ NetS(h->sport), dport, | |
+ NetS(h->cksum), NetS(h->len)); | |
+ return 0; | |
+} | |
+ | |
+Proto udp = | |
+{ | |
+ "udp", | |
+ p_compile, | |
+ p_filter, | |
+ p_seprint, | |
+ p_mux, | |
+ p_fields, | |
+ defaultframer, | |
+}; |