You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

igmp.c 4.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. /*
  2. * Eric Biederman wrote this code originally.
  3. *
  4. */
  5. #include "ip.h"
  6. #include "igmp.h"
  7. #include "background.h"
  8. #include "nic.h"
  9. #include "etherboot.h"
  10. static unsigned long last_igmpv1 = 0;
  11. static struct igmptable_t igmptable[MAX_IGMP];
  12. static long rfc1112_sleep_interval ( long base, int exp ) {
  13. unsigned long divisor, tmo;
  14. if ( exp > BACKOFF_LIMIT )
  15. exp = BACKOFF_LIMIT;
  16. divisor = RAND_MAX / ( base << exp );
  17. tmo = random() / divisor;
  18. return tmo;
  19. }
  20. static void send_igmp_reports ( unsigned long now ) {
  21. struct igmp_ip_t igmp;
  22. int i;
  23. for ( i = 0 ; i < MAX_IGMP ; i++ ) {
  24. if ( ! igmptable[i].time )
  25. continue;
  26. if ( now < igmptable[i].time )
  27. continue;
  28. igmp.router_alert[0] = 0x94;
  29. igmp.router_alert[1] = 0x04;
  30. igmp.router_alert[2] = 0;
  31. igmp.router_alert[3] = 0;
  32. build_ip_hdr ( igmptable[i].group.s_addr, 1, IP_IGMP,
  33. sizeof ( igmp.router_alert ),
  34. sizeof ( igmp ), &igmp );
  35. igmp.igmp.type = IGMPv2_REPORT;
  36. if ( last_igmpv1 &&
  37. ( now < last_igmpv1 + IGMPv1_ROUTER_PRESENT_TIMEOUT ) ) {
  38. igmp.igmp.type = IGMPv1_REPORT;
  39. }
  40. igmp.igmp.response_time = 0;
  41. igmp.igmp.chksum = 0;
  42. igmp.igmp.group.s_addr = igmptable[i].group.s_addr;
  43. igmp.igmp.chksum = ipchksum ( &igmp.igmp,
  44. sizeof ( igmp.igmp ) );
  45. ip_transmit ( sizeof ( igmp ), &igmp );
  46. DBG ( "IGMP sent report to %@\n",
  47. igmp.igmp.group.s_addr );
  48. /* Don't send another igmp report until asked */
  49. igmptable[i].time = 0;
  50. }
  51. }
  52. static void process_igmp ( unsigned long now, unsigned short ptype __unused,
  53. struct iphdr *ip ) {
  54. struct igmp *igmp;
  55. int i;
  56. unsigned iplen;
  57. if ( ( ! ip ) || ( ip->protocol != IP_IGMP ) ||
  58. ( nic.packetlen < ( sizeof ( struct iphdr ) +
  59. sizeof ( struct igmp ) ) ) ) {
  60. return;
  61. }
  62. iplen = ( ip->verhdrlen & 0xf ) * 4;
  63. igmp = ( struct igmp * ) &nic.packet[ sizeof( struct iphdr ) ];
  64. if ( ipchksum ( igmp, ntohs ( ip->len ) - iplen ) != 0 )
  65. return;
  66. if ( ( igmp->type == IGMP_QUERY ) &&
  67. ( ip->dest.s_addr == htonl ( GROUP_ALL_HOSTS ) ) ) {
  68. unsigned long interval = IGMP_INTERVAL;
  69. if ( igmp->response_time == 0 ) {
  70. last_igmpv1 = now;
  71. } else {
  72. interval = ( igmp->response_time * TICKS_PER_SEC ) /10;
  73. }
  74. DBG ( "IGMP received query for %@\n", igmp->group.s_addr );
  75. for ( i = 0 ; i < MAX_IGMP ; i++ ) {
  76. uint32_t group = igmptable[i].group.s_addr;
  77. if ( ( group == 0 ) ||
  78. ( group == igmp->group.s_addr ) ) {
  79. unsigned long time;
  80. time = currticks() +
  81. rfc1112_sleep_interval ( interval, 0 );
  82. if ( time < igmptable[i].time ) {
  83. igmptable[i].time = time;
  84. }
  85. }
  86. }
  87. }
  88. if ( ( ( igmp->type == IGMPv1_REPORT ) ||
  89. ( igmp->type == IGMPv2_REPORT ) ) &&
  90. ( ip->dest.s_addr == igmp->group.s_addr ) ) {
  91. DBG ( "IGMP received report for %@\n", igmp->group.s_addr);
  92. for ( i = 0 ; i < MAX_IGMP ; i++ ) {
  93. if ( ( igmptable[i].group.s_addr ==
  94. igmp->group.s_addr ) &&
  95. ( igmptable[i].time != 0 ) ) {
  96. igmptable[i].time = 0;
  97. }
  98. }
  99. }
  100. }
  101. struct background igmp_background __background = {
  102. .send = send_igmp_reports,
  103. .process = process_igmp,
  104. };
  105. void leave_group ( int slot ) {
  106. /* Be very stupid and always send a leave group message if
  107. * I have subscribed. Imperfect but it is standards
  108. * compliant, easy and reliable to implement.
  109. *
  110. * The optimal group leave method is to only send leave when,
  111. * we were the last host to respond to a query on this group,
  112. * and igmpv1 compatibility is not enabled.
  113. */
  114. if ( igmptable[slot].group.s_addr ) {
  115. struct igmp_ip_t igmp;
  116. igmp.router_alert[0] = 0x94;
  117. igmp.router_alert[1] = 0x04;
  118. igmp.router_alert[2] = 0;
  119. igmp.router_alert[3] = 0;
  120. build_ip_hdr ( htonl ( GROUP_ALL_HOSTS ), 1, IP_IGMP,
  121. sizeof ( igmp.router_alert ), sizeof ( igmp ),
  122. &igmp);
  123. igmp.igmp.type = IGMP_LEAVE;
  124. igmp.igmp.response_time = 0;
  125. igmp.igmp.chksum = 0;
  126. igmp.igmp.group.s_addr = igmptable[slot].group.s_addr;
  127. igmp.igmp.chksum = ipchksum ( &igmp.igmp, sizeof ( igmp ) );
  128. ip_transmit ( sizeof ( igmp ), &igmp );
  129. DBG ( "IGMP left group %@\n", igmp.igmp.group.s_addr );
  130. }
  131. memset ( &igmptable[slot], 0, sizeof ( igmptable[0] ) );
  132. }
  133. void join_group ( int slot, unsigned long group ) {
  134. /* I have already joined */
  135. if ( igmptable[slot].group.s_addr == group )
  136. return;
  137. if ( igmptable[slot].group.s_addr ) {
  138. leave_group ( slot );
  139. }
  140. /* Only join a group if we are given a multicast ip, this way
  141. * code can be given a non-multicast (broadcast or unicast ip)
  142. * and still work...
  143. */
  144. if ( ( group & htonl ( MULTICAST_MASK ) ) ==
  145. htonl ( MULTICAST_NETWORK ) ) {
  146. igmptable[slot].group.s_addr = group;
  147. igmptable[slot].time = currticks();
  148. }
  149. }