2 * Eric Biederman wrote this code originally.
11 static unsigned long last_igmpv1
= 0;
12 static struct igmptable_t igmptable
[MAX_IGMP
];
14 static long rfc1112_sleep_interval ( long base
, int exp
) {
15 unsigned long divisor
, tmo
;
17 if ( exp
> BACKOFF_LIMIT
)
19 divisor
= RAND_MAX
/ ( base
<< exp
);
20 tmo
= random() / divisor
;
24 static void send_igmp_reports ( unsigned long now
) {
25 struct igmp_ip_t igmp
;
28 for ( i
= 0 ; i
< MAX_IGMP
; i
++ ) {
29 if ( ! igmptable
[i
].time
)
31 if ( now
< igmptable
[i
].time
)
34 igmp
.router_alert
[0] = 0x94;
35 igmp
.router_alert
[1] = 0x04;
36 igmp
.router_alert
[2] = 0;
37 igmp
.router_alert
[3] = 0;
38 build_ip_hdr ( igmptable
[i
].group
.s_addr
, 1, IP_IGMP
,
39 sizeof ( igmp
.router_alert
),
40 sizeof ( igmp
), &igmp
);
41 igmp
.igmp
.type
= IGMPv2_REPORT
;
43 ( now
< last_igmpv1
+ IGMPv1_ROUTER_PRESENT_TIMEOUT
) ) {
44 igmp
.igmp
.type
= IGMPv1_REPORT
;
46 igmp
.igmp
.response_time
= 0;
48 igmp
.igmp
.group
.s_addr
= igmptable
[i
].group
.s_addr
;
49 igmp
.igmp
.chksum
= ipchksum ( &igmp
.igmp
,
50 sizeof ( igmp
.igmp
) );
51 ip_transmit ( sizeof ( igmp
), &igmp
);
52 DBG ( "IGMP sent report to %s\n", inet_ntoa ( igmp
.igmp
.group
) );
53 /* Don't send another igmp report until asked */
54 igmptable
[i
].time
= 0;
58 static void process_igmp ( unsigned long now
, unsigned short ptype __unused
,
64 if ( ( ! ip
) || ( ip
->protocol
!= IP_IGMP
) ||
65 ( nic
.packetlen
< ( sizeof ( struct iphdr
) +
66 sizeof ( struct igmp
) ) ) ) {
70 iplen
= ( ip
->verhdrlen
& 0xf ) * 4;
71 igmp
= ( struct igmp
* ) &nic
.packet
[ sizeof( struct iphdr
) ];
72 if ( ipchksum ( igmp
, ntohs ( ip
->len
) - iplen
) != 0 )
75 if ( ( igmp
->type
== IGMP_QUERY
) &&
76 ( ip
->dest
.s_addr
== htonl ( GROUP_ALL_HOSTS
) ) ) {
77 unsigned long interval
= IGMP_INTERVAL
;
79 if ( igmp
->response_time
== 0 ) {
82 interval
= ( igmp
->response_time
* TICKS_PER_SEC
) /10;
85 DBG ( "IGMP received query for %s\n", inet_ntoa ( igmp
->group
) );
86 for ( i
= 0 ; i
< MAX_IGMP
; i
++ ) {
87 uint32_t group
= igmptable
[i
].group
.s_addr
;
88 if ( ( group
== 0 ) ||
89 ( group
== igmp
->group
.s_addr
) ) {
92 rfc1112_sleep_interval ( interval
, 0 );
93 if ( time
< igmptable
[i
].time
) {
94 igmptable
[i
].time
= time
;
99 if ( ( ( igmp
->type
== IGMPv1_REPORT
) ||
100 ( igmp
->type
== IGMPv2_REPORT
) ) &&
101 ( ip
->dest
.s_addr
== igmp
->group
.s_addr
) ) {
102 DBG ( "IGMP received report for %s\n",
103 inet_ntoa ( igmp
->group
) );
104 for ( i
= 0 ; i
< MAX_IGMP
; i
++ ) {
105 if ( ( igmptable
[i
].group
.s_addr
==
106 igmp
->group
.s_addr
) &&
107 ( igmptable
[i
].time
!= 0 ) ) {
108 igmptable
[i
].time
= 0;
114 struct background igmp_background __background
= {
115 .send
= send_igmp_reports
,
116 .process
= process_igmp
,
119 void leave_group ( int slot
) {
120 /* Be very stupid and always send a leave group message if
121 * I have subscribed. Imperfect but it is standards
122 * compliant, easy and reliable to implement.
124 * The optimal group leave method is to only send leave when,
125 * we were the last host to respond to a query on this group,
126 * and igmpv1 compatibility is not enabled.
128 if ( igmptable
[slot
].group
.s_addr
) {
129 struct igmp_ip_t igmp
;
131 igmp
.router_alert
[0] = 0x94;
132 igmp
.router_alert
[1] = 0x04;
133 igmp
.router_alert
[2] = 0;
134 igmp
.router_alert
[3] = 0;
135 build_ip_hdr ( htonl ( GROUP_ALL_HOSTS
), 1, IP_IGMP
,
136 sizeof ( igmp
.router_alert
), sizeof ( igmp
),
138 igmp
.igmp
.type
= IGMP_LEAVE
;
139 igmp
.igmp
.response_time
= 0;
140 igmp
.igmp
.chksum
= 0;
141 igmp
.igmp
.group
.s_addr
= igmptable
[slot
].group
.s_addr
;
142 igmp
.igmp
.chksum
= ipchksum ( &igmp
.igmp
, sizeof ( igmp
) );
143 ip_transmit ( sizeof ( igmp
), &igmp
);
144 DBG ( "IGMP left group %s\n", inet_ntoa ( igmp
.igmp
.group
) );
146 memset ( &igmptable
[slot
], 0, sizeof ( igmptable
[0] ) );
149 void join_group ( int slot
, unsigned long group
) {
150 /* I have already joined */
151 if ( igmptable
[slot
].group
.s_addr
== group
)
153 if ( igmptable
[slot
].group
.s_addr
) {
154 leave_group ( slot
);
156 /* Only join a group if we are given a multicast ip, this way
157 * code can be given a non-multicast (broadcast or unicast ip)
160 if ( ( group
& htonl ( MULTICAST_MASK
) ) ==
161 htonl ( MULTICAST_NETWORK
) ) {
162 igmptable
[slot
].group
.s_addr
= group
;
163 igmptable
[slot
].time
= currticks();