Kaynağa Gözat

IGMP functions separated out from nic.c

tags/v0.9.3
Michael Brown 19 yıl önce
ebeveyn
işleme
e0cf144218
1 değiştirilmiş dosya ile 160 ekleme ve 0 silme
  1. 160
    0
      src/proto/igmp.c

+ 160
- 0
src/proto/igmp.c Dosyayı Görüntüle

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

Loading…
İptal
Kaydet