123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291 |
- #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;
- 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, byte *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;
- 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(&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);
- Fsproto(fs, &igmp);
- }
|