summaryrefslogtreecommitdiff
path: root/sys/src/9/ip/igmp.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/9/ip/igmp.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/9/ip/igmp.c')
-rwxr-xr-xsys/src/9/ip/igmp.c299
1 files changed, 299 insertions, 0 deletions
diff --git a/sys/src/9/ip/igmp.c b/sys/src/9/ip/igmp.c
new file mode 100755
index 000000000..c0c330fac
--- /dev/null
+++ b/sys/src/9/ip/igmp.c
@@ -0,0 +1,299 @@
+/*
+ * igmp - internet group management protocol
+ * unfinished.
+ */
+#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 */
+
+ uchar payload[];
+};
+
+#define IGMPPKTSZ offsetof(IGMPpkt, payload[0])
+
+/*
+ * lists for group reports
+ */
+typedef struct IGMPrep IGMPrep;
+struct IGMPrep
+{
+ IGMPrep *next;
+ Medium *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(Medium *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 += IGMPPKTSZ;
+ memset(bp->rp, 0, IGMPPKTSZ);
+ hnputl(p->src, Mediumgetaddr(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(Medium *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 = Mediumcopymulti(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);
+}