igmp.c - vx32 - Local 9vx git repository for patches.
 (HTM) git clone git://r-36.net/vx32
 (DIR) Log
 (DIR) Files
 (DIR) Refs
       ---
       igmp.c (5183B)
       ---
            1 #include "u.h"
            2 #include "lib.h"
            3 #include "mem.h"
            4 #include "dat.h"
            5 #include "fns.h"
            6 #include "error.h"
            7 
            8 #include "ip.h"
            9 
           10 enum
           11 {
           12         IGMP_IPHDRSIZE        = 20,                /* size of ip header */
           13         IGMP_HDRSIZE        = 8,                /* size of IGMP header */
           14         IP_IGMPPROTO        = 2,
           15 
           16         IGMPquery        = 1,
           17         IGMPreport        = 2,
           18 
           19         MSPTICK                = 100,
           20         MAXTIMEOUT        = 10000/MSPTICK,        /* at most 10 secs for a response */
           21 };
           22 
           23 typedef struct IGMPpkt IGMPpkt;
           24 typedef char byte;
           25 
           26 struct IGMPpkt
           27 {
           28         /* ip header */
           29         byte        vihl;                /* Version and header length */
           30         byte        tos;                /* Type of service */
           31         byte        len[2];                /* packet length (including headers) */
           32         byte        id[2];                /* Identification */
           33         byte        frag[2];        /* Fragment information */
           34         byte        Unused;        
           35         byte        proto;                /* Protocol */
           36         byte        cksum[2];        /* checksum of ip portion */
           37         byte        src[IPaddrlen];                /* Ip source */
           38         byte        dst[IPaddrlen];                /* Ip destination */
           39 
           40         /* igmp header */
           41         byte        vertype;        /* version and type */
           42         byte        unused;
           43         byte        igmpcksum[2];                /* checksum of igmp portion */
           44         byte        group[IPaddrlen];        /* multicast group */
           45 };
           46 
           47 /*
           48  *  lists for group reports
           49  */
           50 typedef struct IGMPrep IGMPrep;
           51 struct IGMPrep
           52 {
           53         IGMPrep                *next;
           54         Media                *m;
           55         int                ticks;
           56         Multicast        *multi;
           57 };
           58 
           59 typedef struct IGMP IGMP;
           60 struct IGMP
           61 {
           62         Lock lk;
           63 
           64         Rendez        r;
           65         IGMPrep        *reports;
           66 };
           67 
           68 IGMP igmpalloc;
           69 
           70         Proto        igmp;
           71 extern        Fs        fs;
           72 
           73 static struct Stats
           74 {
           75         ulong         inqueries;
           76         ulong        outqueries;
           77         ulong        inreports;
           78         ulong        outreports;
           79 } stats;
           80 
           81 void
           82 igmpsendreport(Media *m, byte *addr)
           83 {
           84         IGMPpkt *p;
           85         Block *bp;
           86 
           87         bp = allocb(sizeof(IGMPpkt));
           88         if(bp == nil)
           89                 return;
           90         p = (IGMPpkt*)bp->wp;
           91         p->vihl = IP_VER4;
           92         bp->wp += sizeof(IGMPpkt);
           93         memset(bp->rp, 0, sizeof(IGMPpkt));
           94         hnputl(p->src, Mediagetaddr(m));
           95         hnputl(p->dst, Ipallsys);
           96         p->vertype = (1<<4) | IGMPreport;
           97         p->proto = IP_IGMPPROTO;
           98         memmove(p->group, addr, IPaddrlen);
           99         hnputs(p->igmpcksum, ptclcsum(bp, IGMP_IPHDRSIZE, IGMP_HDRSIZE));
          100         netlog(Logigmp, "igmpreport %I\n", p->group);
          101         stats.outreports++;
          102         ipoput4(bp, 0, 1, DFLTTOS, nil);        /* TTL of 1 */
          103 }
          104 
          105 static int
          106 isreport(void *a)
          107 {
          108         USED(a);
          109         return igmpalloc.reports != 0;
          110 }
          111 
          112 
          113 void
          114 igmpproc(void *a)
          115 {
          116         IGMPrep *rp, **lrp;
          117         Multicast *mp, **lmp;
          118         byte ip[IPaddrlen];
          119 
          120         USED(a);
          121 
          122         for(;;){
          123                 sleep(&igmpalloc.r, isreport, 0);
          124                 for(;;){
          125                         lock(&igmpalloc);
          126 
          127                         if(igmpalloc.reports == nil)
          128                                 break;
          129         
          130                         /* look for a single report */
          131                         lrp = &igmpalloc.reports;
          132                         mp = nil;
          133                         for(rp = *lrp; rp; rp = *lrp){
          134                                 rp->ticks++;
          135                                 lmp = &rp->multi;
          136                                 for(mp = *lmp; mp; mp = *lmp){
          137                                         if(rp->ticks >= mp->timeout){
          138                                                 *lmp = mp->next;
          139                                                 break;
          140                                         }
          141                                         lmp = &mp->next;
          142                                 }
          143                                 if(mp != nil)
          144                                         break;
          145 
          146                                 if(rp->multi != nil){
          147                                         lrp = &rp->next;
          148                                         continue;
          149                                 } else {
          150                                         *lrp = rp->next;
          151                                         free(rp);
          152                                 }
          153                         }
          154                         unlock(&igmpalloc);
          155 
          156                         if(mp){
          157                                 /* do a single report and try again */
          158                                 hnputl(ip, mp->addr);
          159                                 igmpsendreport(rp->m, ip);
          160                                 free(mp);
          161                                 continue;
          162                         }
          163 
          164                         tsleep(&up->sleep, return0, 0, MSPTICK);
          165                 }
          166                 unlock(&igmpalloc);
          167         }
          168 
          169 }
          170 
          171 void
          172 igmpiput(Media *m, Ipifc *, Block *bp)
          173 {
          174         int n;
          175         IGMPpkt *ghp;
          176         Ipaddr group;
          177         IGMPrep *rp, **lrp;
          178         Multicast *mp, **lmp;
          179 
          180         ghp = (IGMPpkt*)(bp->rp);
          181         netlog(Logigmp, "igmpiput: %d %I\n", ghp->vertype, ghp->group);
          182 
          183         n = blocklen(bp);
          184         if(n < IGMP_IPHDRSIZE+IGMP_HDRSIZE){
          185                 netlog(Logigmp, "igmpiput: bad len\n");
          186                 goto error;
          187         }
          188         if((ghp->vertype>>4) != 1){
          189                 netlog(Logigmp, "igmpiput: bad igmp type\n");
          190                 goto error;
          191         }
          192         if(ptclcsum(bp, IGMP_IPHDRSIZE, IGMP_HDRSIZE)){
          193                 netlog(Logigmp, "igmpiput: checksum error %I\n", ghp->src);
          194                 goto error;
          195         }
          196 
          197         group = nhgetl(ghp->group);
          198         
          199         lock(&igmpalloc);
          200         switch(ghp->vertype & 0xf){
          201         case IGMPquery:
          202                 /*
          203                  *  start reporting groups that we're a member of.
          204                  */
          205                 stats.inqueries++;
          206                 for(rp = igmpalloc.reports; rp; rp = rp->next)
          207                         if(rp->m == m)
          208                                 break;
          209                 if(rp != nil)
          210                         break;        /* already reporting */
          211 
          212                 mp = Mediacopymulti(m);
          213                 if(mp == nil)
          214                         break;
          215 
          216                 rp = malloc(sizeof(*rp));
          217                 if(rp == nil)
          218                         break;
          219 
          220                 rp->m = m;
          221                 rp->multi = mp;
          222                 rp->ticks = 0;
          223                 for(; mp; mp = mp->next)
          224                         mp->timeout = nrand(MAXTIMEOUT);
          225                 rp->next = igmpalloc.reports;
          226                 igmpalloc.reports = rp;
          227 
          228                 wakeup(&igmpalloc.r);
          229 
          230                 break;
          231         case IGMPreport:
          232                 /*
          233                  *  find report list for this medium
          234                  */
          235                 stats.inreports++;
          236                 lrp = &igmpalloc.reports;
          237                 for(rp = *lrp; rp; rp = *lrp){
          238                         if(rp->m == m)
          239                                 break;
          240                         lrp = &rp->next;
          241                 }
          242                 if(rp == nil)
          243                         break;
          244 
          245                 /*
          246                  *  if someone else has reported a group,
          247                  *  we don't have to.
          248                  */
          249                 lmp = &rp->multi;
          250                 for(mp = *lmp; mp; mp = *lmp){
          251                         if(mp->addr == group){
          252                                 *lmp = mp->next;
          253                                 free(mp);
          254                                 break;
          255                         }
          256                         lmp = &mp->next;
          257                 }
          258 
          259                 break;
          260         }
          261         unlock(&igmpalloc);
          262 
          263 error:
          264         freeb(bp);
          265 }
          266 
          267 int
          268 igmpstats(char *buf, int len)
          269 {
          270         return snprint(buf, len, "\trcvd %d %d\n\tsent %d %d\n",
          271                 stats.inqueries, stats.inreports,
          272                 stats.outqueries, stats.outreports);
          273 }
          274 
          275 void
          276 igmpinit(Fs *fs)
          277 {
          278         igmp.name = "igmp";
          279         igmp.connect = nil;
          280         igmp.announce = nil;
          281         igmp.ctl = nil;
          282         igmp.state = nil;
          283         igmp.close = nil;
          284         igmp.rcv = igmpiput;
          285         igmp.stats = igmpstats;
          286         igmp.ipproto = IP_IGMPPROTO;
          287         igmp.nc = 0;
          288         igmp.ptclsize = 0;
          289 
          290         igmpreportfn = igmpsendreport;
          291         kproc("igmpproc", igmpproc, 0);
          292 
          293         Fsproto(fs, &igmp);
          294 }