2 * Eric Biederman wrote this code originally.
8 #include "background.h"
10 #include "etherboot.h"
12 static unsigned long last_igmpv1 = 0;
13 static struct igmptable_t igmptable[MAX_IGMP];
15 static long rfc1112_sleep_interval ( long base, int exp ) {
16 unsigned long divisor, tmo;
18 if ( exp > BACKOFF_LIMIT )
20 divisor = RAND_MAX / ( base << exp );
21 tmo = random() / divisor;
25 static void send_igmp_reports ( unsigned long now ) {
26 struct igmp_ip_t igmp;
29 for ( i = 0 ; i < MAX_IGMP ; i++ ) {
30 if ( ! igmptable[i].time )
32 if ( now < igmptable[i].time )
35 igmp.router_alert[0] = 0x94;
36 igmp.router_alert[1] = 0x04;
37 igmp.router_alert[2] = 0;
38 igmp.router_alert[3] = 0;
39 build_ip_hdr ( igmptable[i].group.s_addr, 1, IP_IGMP,
40 sizeof ( igmp.router_alert ),
41 sizeof ( igmp ), &igmp );
42 igmp.igmp.type = IGMPv2_REPORT;
44 ( now < last_igmpv1 + IGMPv1_ROUTER_PRESENT_TIMEOUT ) ) {
45 igmp.igmp.type = IGMPv1_REPORT;
47 igmp.igmp.response_time = 0;
49 igmp.igmp.group.s_addr = igmptable[i].group.s_addr;
50 igmp.igmp.chksum = ipchksum ( &igmp.igmp,
51 sizeof ( igmp.igmp ) );
52 ip_transmit ( sizeof ( igmp ), &igmp );
53 DBG ( "IGMP sent report to %@\n",
54 igmp.igmp.group.s_addr );
55 /* Don't send another igmp report until asked */
56 igmptable[i].time = 0;
60 static void process_igmp ( unsigned long now, unsigned short ptype __unused,
66 if ( ( ! ip ) || ( ip->protocol != IP_IGMP ) ||
67 ( nic.packetlen < ( sizeof ( struct iphdr ) +
68 sizeof ( struct igmp ) ) ) ) {
72 iplen = ( ip->verhdrlen & 0xf ) * 4;
73 igmp = ( struct igmp * ) &nic.packet[ sizeof( struct iphdr ) ];
74 if ( ipchksum ( igmp, ntohs ( ip->len ) - iplen ) != 0 )
77 if ( ( igmp->type == IGMP_QUERY ) &&
78 ( ip->dest.s_addr == htonl ( GROUP_ALL_HOSTS ) ) ) {
79 unsigned long interval = IGMP_INTERVAL;
81 if ( igmp->response_time == 0 ) {
84 interval = ( igmp->response_time * TICKS_PER_SEC ) /10;
87 DBG ( "IGMP received query for %@\n", igmp->group.s_addr );
88 for ( i = 0 ; i < MAX_IGMP ; i++ ) {
89 uint32_t group = igmptable[i].group.s_addr;
90 if ( ( group == 0 ) ||
91 ( group == igmp->group.s_addr ) ) {
94 rfc1112_sleep_interval ( interval, 0 );
95 if ( time < igmptable[i].time ) {
96 igmptable[i].time = time;
101 if ( ( ( igmp->type == IGMPv1_REPORT ) ||
102 ( igmp->type == IGMPv2_REPORT ) ) &&
103 ( ip->dest.s_addr == igmp->group.s_addr ) ) {
104 DBG ( "IGMP received report for %@\n", igmp->group.s_addr);
105 for ( i = 0 ; i < MAX_IGMP ; i++ ) {
106 if ( ( igmptable[i].group.s_addr ==
107 igmp->group.s_addr ) &&
108 ( igmptable[i].time != 0 ) ) {
109 igmptable[i].time = 0;
115 static struct background igmp_background __background = {
116 .send = send_igmp_reports,
117 .process = process_igmp,
120 void leave_group ( int slot ) {
121 /* Be very stupid and always send a leave group message if
122 * I have subscribed. Imperfect but it is standards
123 * compliant, easy and reliable to implement.
125 * The optimal group leave method is to only send leave when,
126 * we were the last host to respond to a query on this group,
127 * and igmpv1 compatibility is not enabled.
129 if ( igmptable[slot].group.s_addr ) {
130 struct igmp_ip_t igmp;
132 igmp.router_alert[0] = 0x94;
133 igmp.router_alert[1] = 0x04;
134 igmp.router_alert[2] = 0;
135 igmp.router_alert[3] = 0;
136 build_ip_hdr ( htonl ( GROUP_ALL_HOSTS ), 1, IP_IGMP,
137 sizeof ( igmp.router_alert ), sizeof ( igmp ),
139 igmp.igmp.type = IGMP_LEAVE;
140 igmp.igmp.response_time = 0;
141 igmp.igmp.chksum = 0;
142 igmp.igmp.group.s_addr = igmptable[slot].group.s_addr;
143 igmp.igmp.chksum = ipchksum ( &igmp.igmp, sizeof ( igmp ) );
144 ip_transmit ( sizeof ( igmp ), &igmp );
145 DBG ( "IGMP left group %@\n", igmp.igmp.group.s_addr );
147 memset ( &igmptable[slot], 0, sizeof ( igmptable[0] ) );
150 void join_group ( int slot, unsigned long group ) {
151 /* I have already joined */
152 if ( igmptable[slot].group.s_addr == group )
154 if ( igmptable[slot].group.s_addr ) {
155 leave_group ( slot );
157 /* Only join a group if we are given a multicast ip, this way
158 * code can be given a non-multicast (broadcast or unicast ip)
161 if ( ( group & htonl ( MULTICAST_MASK ) ) ==
162 htonl ( MULTICAST_NETWORK ) ) {
163 igmptable[slot].group.s_addr = group;
164 igmptable[slot].time = currticks();