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