From c334856610642f1e49f6177b9866629f0293b78f Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Wed, 23 Apr 1997 00:00:00 +0000 Subject: [PATCH] Plan 9 from Bell Labs 1997-04-23 --- ip/devip.c | 21 +++- ip/gre.c | 3 +- ip/icmp.c | 3 +- ip/igmp.c | 272 ++++++++++++++++++++++++++++++++++++++++++++++++++++ ip/il.c | 5 +- ip/ip.c | 10 +- ip/ip.h | 40 ++++++-- ip/ipaux.c | 4 +- ip/netlog.c | 3 +- ip/tcp.c | 5 +- ip/udp.c | 3 +- 11 files changed, 345 insertions(+), 24 deletions(-) create mode 100644 ip/igmp.c diff --git a/ip/devip.c b/ip/devip.c index add08890128760d38c0f46971597cd3d3a3d5c95..fce852f6f2ece87920f5619369fad6917a42d798 100644 --- a/ip/devip.c +++ b/ip/devip.c @@ -137,6 +137,8 @@ ipgen(Chan *c, Dirtab*, int, int s, Dir *dp) switch(TYPE(c->qid)) { case Qtopdir: if(s < fs.np) { + if(fs.p[s]->connect == nil) + return 0; /* protocol with no user interface */ q = (Qid){QID(s, 0, Qprotodir)|CHDIR, 0}; devdir(c, q, fs.p[s]->name, 0, network, CHDIR|0555, dp); return 1; @@ -270,6 +272,9 @@ ipopen(Chan* c, int omode) switch(TYPE(c->qid)) { default: break; + case Qipifc: + c->aux = newifcconv(); + break; case Qlog: netlogopen(); break; @@ -402,6 +407,10 @@ ipclose(Chan* c) switch(TYPE(c->qid)) { default: break; + case Qipifc: + closeifcconv(c->aux); + c->aux = nil; + break; case Qlog: netlogclose(); break; @@ -530,7 +539,8 @@ setlport(Conv* c) static void setladdrport(Conv* c, char* str) { - char *p, addr[4]; + char *p; + uchar addr[Ipaddrlen]; p = strchr(str, '!'); if(p == nil) { @@ -540,7 +550,7 @@ setladdrport(Conv* c, char* str) else { *p++ = 0; parseip(addr, str); - c->laddr = nhgetl((byte*)addr); + c->laddr = nhgetl(addr); } if(*p == '*') c->lport = 0; @@ -554,14 +564,15 @@ setladdrport(Conv* c, char* str) static char* setraddrport(Conv* c, char* str) { - char *p, addr[4]; + char *p; + uchar addr[Ipaddrlen]; p = strchr(str, '!'); if(p == nil) return "malformed address"; *p++ = 0; parseip(addr, str); - c->raddr = nhgetl((byte*)addr); + c->raddr = nhgetl(addr); c->rport = atoi(p); p = strchr(p, '!'); if(p){ @@ -600,7 +611,7 @@ ipwrite(Chan* ch, char* a, long n, ulong) error(p); return n; case Qipifc: - p = Mediaifcwrite(a, n); + p = Mediaifcwrite(ch->aux, a, n); if(p != nil) error(p); return n; diff --git a/ip/gre.c b/ip/gre.c index 76245b27695b345af50a7fa3316ce4e8d51e5e22..e85bb04859521a5c6c1d89c0bbdf3f130b3ecac0 100644 --- a/ip/gre.c +++ b/ip/gre.c @@ -163,7 +163,7 @@ grekick(Conv *c, int l) } static void -greiput(Block *bp) +greiput(Media *m, Block *bp) { int len; GREhdr *ghp; @@ -171,6 +171,7 @@ greiput(Block *bp) Conv *c, **p; ushort eproto; + USED(m); ghp = (GREhdr*)(bp->rp); eproto = nhgets(ghp->eproto); diff --git a/ip/icmp.c b/ip/icmp.c index f211314c6e96ec3d69002e1ed80d823c843132fb..8863dfee25b900f30f196afe3bb23d4265791b8a 100644 --- a/ip/icmp.c +++ b/ip/icmp.c @@ -173,7 +173,7 @@ static char *unreachcode[] = }; static void -icmpiput(Block *bp) +icmpiput(Media *m, Block *bp) { int n, iplen; Icmp *p; @@ -182,6 +182,7 @@ icmpiput(Block *bp) char *msg; char m2[128]; + USED(m); p = (Icmp *)bp->rp; netlog(Logicmp, "icmpiput %d %d\n", p->type, p->code); n = blocklen(bp); diff --git a/ip/igmp.c b/ip/igmp.c new file mode 100644 index 0000000000000000000000000000000000000000..7af4d4a126273899c686bc00bf28adf96933b0a8 --- /dev/null +++ b/ip/igmp.c @@ -0,0 +1,272 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "ip.h" + +enum +{ + IGMP_IPHDRSIZE = 20, /* size of ip header */ + IGMP_HDRSIZE = 8, /* size of IGMP header */ + IP_IGMPPROTO = 2, + + IGMPquery = 1, + IGMPreport = 2, + + MSPTICK = 100, + MAXTIMEOUT = 10000/MSPTICK, /* at most 10 secs for a response */ +}; + +typedef struct IGMPpkt IGMPpkt; +struct IGMPpkt +{ + /* ip header */ + byte vihl; /* Version and header length */ + byte tos; /* Type of service */ + byte len[2]; /* packet length (including headers) */ + byte id[2]; /* Identification */ + byte frag[2]; /* Fragment information */ + byte Unused; + byte proto; /* Protocol */ + byte cksum[2]; /* checksum of ip portion */ + byte src[Ipaddrlen]; /* Ip source */ + byte dst[Ipaddrlen]; /* Ip destination */ + + /* igmp header */ + byte vertype; /* version and type */ + byte unused; + byte igmpcksum[2]; /* checksum of igmp portion */ + byte group[Ipaddrlen]; /* multicast group */ +}; + +/* + * lists for group reports + */ +typedef struct IGMPrep IGMPrep; +struct IGMPrep +{ + IGMPrep *next; + Media *m; + int ticks; + Multicast *multi; +}; + +typedef struct IGMP IGMP; +struct IGMP +{ + Lock; + Rendez r; + Rendez r0; + IGMPrep *reports; +}; + +IGMP igmpalloc; + + Proto igmp; +extern Fs fs; + +void +igmpsendreport(Media *m, byte *addr) +{ + IGMPpkt *p; + Block *bp; + + bp = allocb(sizeof(IGMPpkt)); + if(bp == nil) + return; + p = (IGMPpkt*)bp->wp; + bp->wp += sizeof(IGMPpkt); + memset(bp->rp, 0, sizeof(IGMPpkt)); + hnputl(p->src, Mediagetaddr(m)); + hnputl(p->dst, Ipallsys); + p->vertype = (1<<4) | IGMPreport; + p->proto = IP_IGMPPROTO; + memmove(p->group, addr, Ipaddrlen); + hnputs(p->igmpcksum, ptclcsum(bp, IGMP_IPHDRSIZE, IGMP_HDRSIZE)); + netlog(Logigmp, "igmpreport %I\n", p->group); + ipoput(bp, 0, 1); /* TTL of 1 */ +} + +static int +isreport(void *a) +{ + USED(a); + return igmpalloc.reports != 0; +} + + +void +igmpproc(void *a) +{ + IGMPrep *rp, **lrp; + Multicast *mp, **lmp; + byte ip[Ipaddrlen]; + + USED(a); + + for(;;){ + sleep(&igmpalloc.r, isreport, 0); + for(;;){ + lock(&igmpalloc); + + if(igmpalloc.reports == nil) + break; + + /* look for a single report */ + lrp = &igmpalloc.reports; + mp = nil; + for(rp = *lrp; rp; rp = *lrp){ + rp->ticks++; + lmp = &rp->multi; + for(mp = *lmp; mp; mp = *lmp){ + if(rp->ticks >= mp->timeout){ + *lmp = mp->next; + break; + } + lmp = &mp->next; + } + if(mp != nil) + break; + + if(rp->multi != nil){ + lrp = &rp->next; + continue; + } else { + *lrp = rp->next; + free(rp); + } + } + unlock(&igmpalloc); + + if(mp){ + /* do a single report and try again */ + hnputl(ip, mp->addr); + igmpsendreport(rp->m, ip); + free(mp); + continue; + } + + tsleep(&igmpalloc.r0, return0, 0, MSPTICK); + } + unlock(&igmpalloc); + } + +} + +void +igmpiput(Media *m, Block *bp) +{ + int n; + IGMPpkt *ghp; + Ipaddr group; + IGMPrep *rp, **lrp; + Multicast *mp, **lmp; + + ghp = (IGMPpkt*)(bp->rp); + netlog(Logigmp, "igmpiput: %d %I\n", ghp->vertype, ghp->group); + + n = blocklen(bp); + if(n < IGMP_IPHDRSIZE+IGMP_HDRSIZE){ + netlog(Logigmp, "igmpiput: bad len\n"); + goto error; + } + if((ghp->vertype>>4) != 1){ + netlog(Logigmp, "igmpiput: bad igmp type\n"); + goto error; + } + if(ptclcsum(bp, IGMP_IPHDRSIZE, IGMP_HDRSIZE)){ + netlog(Logigmp, "igmpiput: checksum error %I\n", ghp->src); + goto error; + } + + group = nhgetl(ghp->group); + + lock(&igmpalloc); + switch(ghp->vertype & 0xf){ + case IGMPquery: + /* + * start reporting groups that we're a member of. + */ + for(rp = igmpalloc.reports; rp; rp = rp->next) + if(rp->m == m) + break; + if(rp != nil) + break; /* already reporting */ + + mp = Mediacopymulti(m); + if(mp == nil) + break; + + rp = malloc(sizeof(*rp)); + if(rp == 0) + break; + + rp->m = m; + rp->multi = mp; + rp->ticks = 0; + for(; mp; mp = mp->next) + mp->timeout = nrand(MAXTIMEOUT); + rp->next = igmpalloc.reports; + igmpalloc.reports = rp; + + wakeup(&igmpalloc.r); + + break; + case IGMPreport: + /* + * find report list for this medium + */ + lrp = &igmpalloc.reports; + for(rp = *lrp; rp; rp = *lrp){ + if(rp->m == m) + break; + lrp = &rp->next; + } + if(rp == nil) + break; + + /* + * if someone else has reported a group, + * we don't have to. + */ + lmp = &rp->multi; + for(mp = *lmp; mp; mp = *lmp){ + if(mp->addr == group){ + *lmp = mp->next; + free(mp); + break; + } + lmp = &mp->next; + } + + break; + } + unlock(&igmpalloc); + +error: + freeb(bp); +} + +void +igmpinit(Fs *fs) +{ + igmp.name = "igmp"; + igmp.kick = nil; + igmp.connect = nil; + igmp.announce = nil; + igmp.ctl = nil; + igmp.state = nil; + igmp.close = nil; + igmp.rcv = igmpiput; + igmp.ipproto = IP_IGMPPROTO; + igmp.nc = 0; + igmp.ptclsize = 0; + + igmpreportfn = igmpsendreport; + kproc("igmpproc", igmpproc, 0); + + Fsproto(fs, &igmp); +} diff --git a/ip/il.c b/ip/il.c index 171632466f7b5a51f7c3158638b141b84a32a1b8..a8a9264cc1f14b633b1c7ab2d89aea27a1a16b85 100644 --- a/ip/il.c +++ b/ip/il.c @@ -140,7 +140,7 @@ void iltimers(Ilcb*); char* ilstart(Conv*, int, int); void ilackproc(); void iloutoforder(Conv*, Ilhdr*, Block*); -void iliput(Block*); +void iliput(Media*, Block*); void iladvise(Block*, char*); #define DBG(x) if((logmask & Logilmsg) && (iponly == 0 || x == iponly))netlog @@ -379,7 +379,7 @@ ilackto(Ilcb *ic, ulong ackto) } void -iliput(Block *bp) +iliput(Media *m, Block *bp) { char *st; Ilcb *ic; @@ -389,6 +389,7 @@ iliput(Block *bp) int plen, illen; Conv *s, **p, *new, *spec, *gen; + USED(m); ih = (Ilhdr *)bp->rp; plen = blocklen(bp); if(plen < IL_IPSIZE+IL_HDRSIZE){ diff --git a/ip/ip.c b/ip/ip.c index d5f506fb6c05afd3b1bbf165aba5fc8d78614c35..bfc1430005ae4ce5bd8aaa510afe962415afc8d4 100644 --- a/ip/ip.c +++ b/ip/ip.c @@ -104,7 +104,11 @@ ipoput(Block *bp, int gating, int ttl) goto raise; } - m = Mediaroute(eh->dst, gate); + if(isbmcast(eh->dst)){ + m = Mediaroute(eh->src, nil); + memmove(gate, eh->dst, Ipaddrlen); + } else + m = Mediaroute(eh->dst, gate); if(m == nil){ netlog(Logip, "no interface %I\n", eh->dst); goto raise; @@ -222,7 +226,7 @@ initfrag(int size) void (*ipextprotoiput)(Block*); void -ipiput(Block *bp) +ipiput(Media *m, Block *bp) { Iphdr *h; Proto *p; @@ -288,7 +292,7 @@ ipiput(Block *bp) p = Fsrcvpcol(&fs, h->proto); if(p != nil && p->rcv != nil) - (*p->rcv)(bp); + (*p->rcv)(m, bp); else if(ipextprotoiput != nil) ipextprotoiput(bp); else diff --git a/ip/ip.h b/ip/ip.h index ba908a2c2fb732b7b161d59e424123013a628477..31589bcffb4cec23efdc6d2f968c9879328c6a54 100644 --- a/ip/ip.h +++ b/ip/ip.h @@ -3,8 +3,10 @@ typedef uchar byte; typedef struct Conv Conv; typedef struct Fs Fs; typedef union Hwaddr Hwaddr; +typedef struct Ifcconv Ifcconv; typedef struct Iproute Iproute; typedef struct Media Media; +typedef struct Multicast Multicast; typedef struct Proto Proto; typedef struct Pstate Pstate; typedef struct Tcpc Tcpc; @@ -19,8 +21,11 @@ enum MAXTTL= 255, + Ipaddrlen= 4, Ipbcast= 0xffffffff, /* ip broadcast address */ Ipbcastobs= 0, /* obsolete (but still used) ip broadcast addr */ + Ipallsys= 0xe0000001, /* multicast for all systems */ + Ipallrouter= 0xe0000002, /* multicast for all routers */ }; enum @@ -87,11 +92,21 @@ enum MPACKET, }; +/* one per multicast address per medium */ +struct Multicast +{ + Ipaddr addr; + int ref; + int timeout; + Multicast *next; +}; + struct Media { int type; /* Media type */ Chan* mchan; /* Data channel */ Chan* achan; /* Arp channel */ + Chan* cchan; /* Control channel */ char* dev; /* device mfd points to */ Ipaddr myip[5]; Ipaddr mymask; @@ -108,6 +123,10 @@ struct Media int inuse; Conv *c; /* for packet interface */ + QLock mlock; /* lock for changing *multi */ + Multicast *multi; /* list of multicast addresses we're listening to */ + int mactive; /* number of active multicast addresses */ + Media* link; }; int Mediaforme(byte*); @@ -122,11 +141,14 @@ Ipaddr Mediagetaddr(Media*); Ipaddr Mediagetraddr(Media*); void Mediawrite(Media*, Block*, byte*); int Mediaifcread(char*, ulong, int); -char* Mediaifcwrite(char*, int); +char* Mediaifcwrite(Ifcconv*, char*, int); void Mediaresolver(Media*); void Mediaread(Media*); int Mediaarp(Media*, Block*, byte*, Hwaddr*); Media* Mediafind(Iproute*); +Multicast* Mediacopymulti(Media*); +void Mediamulticastadd(Media*, Ifcconv*, Ipaddr); +void Mediamulticastrem(Media*, Ipaddr); /* * one per multiplexed protocol @@ -144,7 +166,7 @@ struct Proto int (*state)(char**, Conv*); void (*create)(Conv*); void (*close)(Conv*); - void (*rcv)(Block*); + void (*rcv)(Media*, Block*); char* (*ctl)(Conv*, char**, int); void (*advise)(Block*, char*); @@ -195,6 +217,7 @@ enum Loggre= 1<<9, Logppp= 1<<10, Logtcpmsg= 1<<11, + Logigmp= 1<<12, }; extern int logmask; /* mask of things to debug */ @@ -213,9 +236,11 @@ extern int debug; extern Fs fs; extern Media* media; extern int iprouting; /* true if routing turned on */ +extern void (*igmpreportfn)(Media*, byte*); int arpread(byte*, ulong, int); char* arpwrite(char*, int); +void closeifcconv(Ifcconv*); Ipaddr defmask(Ipaddr); int eipconv(va_list*, Fconv*); int equivip(byte*, byte*); @@ -226,19 +251,22 @@ void initfrag(int); ushort ipcsum(byte*); void (*ipextprotoiput)(Block*); Ipaddr ipgetsrc(byte*); -void ipiput(Block*); +void ipiput(Media*, Block*); void ipoput(Block*, int, int); int ipstats(char*, int); +int ismcast(byte*); +int isbmcast(byte*); byte* logctl(byte*); void maskip(byte*, byte*, byte*); +Ifcconv* newifcconv(void); ulong nhgetl(byte*); ushort nhgets(byte*); void (*pktifcrcv)(Conv*, Block*); ushort ptclcsum(Block*, int, int); int pullblock(Block**, int); Block* pullupblock(Block*, int); -char* routeadd(Ipaddr, Ipaddr, Ipaddr); -void routedelete(ulong, ulong); +char* routeadd(Ipaddr, Ipaddr, Ipaddr, Media *m); +void routedelete(ulong, ulong, Media *m); int routeread(byte*, ulong, int); char* routewrite(char*, int); @@ -246,4 +274,4 @@ char* routewrite(char*, int); * ipaux.c */ int myetheraddr(uchar*, char*); -ulong parseip(char*, char*); +ulong parseip(uchar*, char*); diff --git a/ip/ipaux.c b/ip/ipaux.c index 7abb3f1a55d9ee12ef68d040f11dfa94d0a7376d..9425a685a8455b6390623e004bf2cb5b938cfca2 100644 --- a/ip/ipaux.c +++ b/ip/ipaux.c @@ -212,7 +212,7 @@ eipconv(va_list *arg, Fconv *f) #define CLASS(p) ((*(uchar*)(p))>>6) ulong -parseip(char *to, char *from) +parseip(uchar *to, char *from) { int i; char *p; @@ -243,5 +243,5 @@ parseip(char *to, char *from) } break; } - return nhgetl((uchar*)to); + return nhgetl(to); } diff --git a/ip/netlog.c b/ip/netlog.c index 8672b7af0c2b44803298b04b82239c8c348e3194..a8c3306e42bd570636497bf199117878efe209ed 100644 --- a/ip/netlog.c +++ b/ip/netlog.c @@ -148,7 +148,8 @@ netlogctl(char* s, int len) { int i, n, set; Logflag *f; - char *fields[10], *p, addr[4], buf[256]; + char *fields[10], *p, buf[256]; + uchar addr[Ipaddrlen]; if(len == 0) return Ebadnetctl; diff --git a/ip/tcp.c b/ip/tcp.c index 955ffd0d424924638d7e6455358468df9dd87bbc..03664ded516c5a87990c69fa1f3cde214de2c896 100644 --- a/ip/tcp.c +++ b/ip/tcp.c @@ -190,7 +190,7 @@ void addreseq(Tcpctl*, Tcp*, Block*, ushort); void getreseq(Tcpctl*, Tcp*, Block**, ushort*); void localclose(Conv*, char*); void procsyn(Conv*, Tcp*); -void tcpiput(Block*); +void tcpiput(Media*, Block*); void tcpoutput(Conv*); int tcptrim(Tcpctl*, Tcp*, Block**, ushort*); void tcpstart(Conv*, int, ushort); @@ -1052,7 +1052,7 @@ update(Conv *s, Tcp *seg) } void -tcpiput(Block *bp) +tcpiput(Media *m, Block *bp) { Tcp seg; Tcphdr *h; @@ -1062,6 +1062,7 @@ tcpiput(Block *bp) Ipaddr source, dest; Conv *spec, *gen, *s, **p; + USED(m); h = (Tcphdr*)(bp->rp); dest = nhgetl(h->tcpdst); diff --git a/ip/udp.c b/ip/udp.c index 9331ad8562b1451ee69da28243bdc1708adc3c99..aec04e5652ff209fc881a51605593be930d37de7 100644 --- a/ip/udp.c +++ b/ip/udp.c @@ -200,7 +200,7 @@ udpkick(Conv *c, int l) } void -udpiput(Block *bp) +udpiput(Media *m, Block *bp) { int len; Udphdr *uh; @@ -209,6 +209,7 @@ udpiput(Block *bp) Udpcb *ucb; ushort dport, sport; + USED(m); uh = (Udphdr*)(bp->rp); /* Put back pseudo header for checksum */