M ip/devip.c => ip/devip.c +16 -5
@@ 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;
M ip/gre.c => ip/gre.c +2 -1
@@ 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);
M ip/icmp.c => ip/icmp.c +2 -1
@@ 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);
A ip/igmp.c => ip/igmp.c +272 -0
@@ 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);
+}
M ip/il.c => ip/il.c +3 -2
@@ 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){
M ip/ip.c => ip/ip.c +7 -3
@@ 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
M ip/ip.h => ip/ip.h +34 -6
@@ 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*);
M ip/ipaux.c => ip/ipaux.c +2 -2
@@ 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);
}
M ip/netlog.c => ip/netlog.c +2 -1
@@ 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;
M ip/tcp.c => ip/tcp.c +3 -2
@@ 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);
M ip/udp.c => ip/udp.c +2 -1
@@ 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 */