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 }