igmp.c 5.6 KB

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