#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 */ uchar vihl; /* Version and header length */ uchar tos; /* Type of service */ uchar len[2]; /* packet length (including headers) */ uchar id[2]; /* Identification */ uchar frag[2]; /* Fragment information */ uchar Unused; uchar proto; /* Protocol */ uchar cksum[2]; /* checksum of ip portion */ uchar src[IPaddrlen]; /* Ip source */ uchar dst[IPaddrlen]; /* Ip destination */ /* igmp header */ uchar vertype; /* version and type */ uchar unused; uchar igmpcksum[2]; /* checksum of igmp portion */ uchar 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; IGMPrep *reports;}; IGMP igmpalloc; Proto igmp;extern Fs fs; static struct Stats{ ulong inqueries; ulong outqueries; ulong inreports; ulong outreports;} stats; voidigmpsendreport(Media *m, uchar *addr){ IGMPpkt *p; Block *bp; bp = allocb(sizeof(IGMPpkt)); if(bp == nil) return; p = (IGMPpkt*)bp->wp; p->vihl = IP_VER4; 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); stats.outreports++; ipoput4(bp, 0, 1, DFLTTOS, nil); /* TTL of 1 */} static intisreport(void *a){ USED(a); return igmpalloc.reports != 0;} voidigmpproc(void *a){ IGMPrep *rp, **lrp; Multicast *mp, **lmp; uchar 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(&up->sleep, return0, 0, MSPTICK); } unlock(&igmpalloc); } } voidigmpiput(Media *m, Ipifc *, 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. */ stats.inqueries++; 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 == nil) 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 */ stats.inreports++; 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);} intigmpstats(char *buf, int len){ return snprint(buf, len, "\trcvd %d %d\n\tsent %d %d\n", stats.inqueries, stats.inreports, stats.outqueries, stats.outreports);} voidigmpinit(Fs *fs){ igmp.name = "igmp"; igmp.connect = nil; igmp.announce = nil; igmp.ctl = nil; igmp.state = nil; igmp.close = nil; igmp.rcv = igmpiput; igmp.stats = igmpstats; igmp.ipproto = IP_IGMPPROTO; igmp.nc = 0; igmp.ptclsize = 0; igmpreportfn = igmpsendreport; kproc("igmpproc", igmpproc, 0, 0); Fsproto(fs, &igmp);}
#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 */
uchar vihl; /* Version and header length */
uchar tos; /* Type of service */
uchar len[2]; /* packet length (including headers) */
uchar id[2]; /* Identification */
uchar frag[2]; /* Fragment information */
uchar Unused;
uchar proto; /* Protocol */
uchar cksum[2]; /* checksum of ip portion */
uchar src[IPaddrlen]; /* Ip source */
uchar dst[IPaddrlen]; /* Ip destination */
/* igmp header */
uchar vertype; /* version and type */
uchar unused;
uchar igmpcksum[2]; /* checksum of igmp portion */
uchar 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;
IGMPrep *reports;
};
IGMP igmpalloc;
Proto igmp;
extern Fs fs;
static struct Stats
{
ulong inqueries;
ulong outqueries;
ulong inreports;
ulong outreports;
} stats;
void
igmpsendreport(Media *m, uchar *addr)
{
IGMPpkt *p;
Block *bp;
bp = allocb(sizeof(IGMPpkt));
if(bp == nil)
return;
p = (IGMPpkt*)bp->wp;
p->vihl = IP_VER4;
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);
stats.outreports++;
ipoput4(bp, 0, 1, DFLTTOS, nil); /* TTL of 1 */
}
static int
isreport(void *a)
{
USED(a);
return igmpalloc.reports != 0;
}
void
igmpproc(void *a)
{
IGMPrep *rp, **lrp;
Multicast *mp, **lmp;
uchar 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(&up->sleep, return0, 0, MSPTICK);
}
unlock(&igmpalloc);
}
}
void
igmpiput(Media *m, Ipifc *, 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.
*/
stats.inqueries++;
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 == nil)
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
*/
stats.inreports++;
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);
}
int
igmpstats(char *buf, int len)
{
return snprint(buf, len, "\trcvd %d %d\n\tsent %d %d\n",
stats.inqueries, stats.inreports,
stats.outqueries, stats.outreports);
}
void
igmpinit(Fs *fs)
{
igmp.name = "igmp";
igmp.connect = nil;
igmp.announce = nil;
igmp.ctl = nil;
igmp.state = nil;
igmp.close = nil;
igmp.rcv = igmpiput;
igmp.stats = igmpstats;
igmp.ipproto = IP_IGMPPROTO;
igmp.nc = 0;
igmp.ptclsize = 0;
igmpreportfn = igmpsendreport;
kproc("igmpproc", igmpproc, 0, 0);
Fsproto(fs, &igmp);
}