igmp.c 5.2 KB

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