Watt-32 tcp/ip  2.2 dev-rel.10
pcigmp.c
Go to the documentation of this file.
1 
19 #include <stdio.h>
20 
21 #include "copyrigh.h"
22 #include "wattcp.h"
23 #include "misc.h"
24 #include "run.h"
25 #include "timer.h"
26 #include "chksum.h"
27 #include "strings.h"
28 #include "ip4_in.h"
29 #include "ip4_out.h"
30 #include "netaddr.h"
31 #include "pcconfig.h"
32 #include "pcpkt.h"
33 #include "pcsed.h"
34 #include "pctcp.h"
35 #include "pcdbug.h"
36 #include "pcstat.h"
37 #include "pcigmp.h"
38 
39 /*
40  * Application must set this to non-zero before calling watt_sock_init().
41  * Not needed if using the BSD-socket API. Set for mcast options in
42  * setsockopt().
43  */
44 int _multicast_on = 0;
45 int _multicast_intvl = 0; /* Send a report every x sec when idling */
46 
47 #if defined(USE_MULTICAST)
48 
49 #include <sys/pack_on.h>
50 
51 struct IGMP_PKT {
52  in_Header in;
54  };
55 
56 #include <sys/pack_off.h>
57 
58 #define ETH_MULTI 0x01005EUL
59 #define CLASS_D_MASK 0xE0000000UL
60 #define IPMULTI_MASK 0x007FFFFFUL
62 static struct MultiCast mcast_list [IPMULTI_SIZE];
63 static BOOL daemon_on = FALSE;
64 
65 static void check_mcast_reports (void);
66 
67 /*
68  * Check the LSB bit 0 of ether destination address.
69  */
70 static __inline BOOL is_eth_multicast (const in_Header *ip)
71 {
72  const BYTE *eth_dst;
73 
74  /* How could this happen? Assume yes anyway
75  */
76  if (_pktdevclass != PDCLASS_ETHER)
77  return (TRUE);
78  eth_dst = (const BYTE*) MAC_DST (ip);
79  return (eth_dst[0] & 1); /* LSB=1 is mcast */
80 }
81 
82 /*
83  * Calculates the proper ethernet address for a given
84  * IP Multicast address.
85  *
86  * Where:
87  * ip: IP address to be converted
88  * eth: pointer to ethernet MAC address
89  */
90 int W32_CALL multi_to_eth (DWORD ip, eth_address *mac)
91 {
92  DWORD top = ETH_MULTI;
93  BYTE *eth = (BYTE*)mac;
94 
95  ip &= IPMULTI_MASK;
96  eth[0] = (BYTE) (top >> 16);
97  eth[1] = (BYTE) ((top >> 8) & 0xFF);
98  eth[2] = (BYTE) (top & 0xFF);
99  eth[3] = (BYTE) (ip >> 16);
100  eth[4] = (BYTE) ((ip >> 8) & 0xFF);
101  eth[5] = (BYTE) (ip & 0xFF);
102  return (1);
103 }
104 
105 /*
106  * Joins a multicast group
107  *
108  * Where:
109  * ip - address of the group to be joined (host order)
110  * Returns:
111  * 1 - if the group was joined successfully
112  * 0 - if attempt failed
113  */
114 int W32_CALL join_mcast_group (DWORD ip)
115 {
116  struct MultiCast *mc;
117  int i;
118 
119  if (!_multicast_on)
120  return (0);
121 
122  /* first verify that it's a valid mcast address
123  */
124  if (!_ip4_is_multicast(ip))
125  {
126  TCP_CONSOLE_MSG (1, ("%s isn't a multicast address\n",
127  _inet_ntoa(NULL,ip)));
128  return (0);
129  }
130 
131  if (!daemon_on)
132  DAEMON_ADD (check_mcast_reports);
133  daemon_on = TRUE;
134 
135  /* Determine if the group has already been joined.
136  * As well as what the first free slot is.
137  */
138  for (i = 0, mc = mcast_list; i < DIM(mcast_list); i++, mc++)
139  {
140  if (mc->active && mc->ip == ip)
141  {
142  if (mc->processes < 255)
143  mc->processes++;
144  return (1);
145  }
146  if (!mc->active)
147  break;
148  }
149 
150  /* alas, no...we need to join it
151  */
152  if (i >= DIM(mcast_list)) /* out of slots! */
153  return (0);
154 
155  /* Fill in the hardware address
156  */
157  multi_to_eth (ip, &mc->ethaddr);
158 
159  if (!_eth_join_mcast_group(mc))
160  return (0);
161 
162  mc->active = TRUE;
163  mc->ip = ip;
164  mc->reply_timer = set_timeout (0); /* report ASAP */
165  mc->processes = 1;
166  return (1);
167 }
168 
169 /*
170  * Leaves a multicast group
171  *
172  * Where:
173  * ip - address of the group to be joined
174  * Returns:
175  * 1 - if the group was left successfully
176  * 0 - if attempt failed
177  */
178 int W32_CALL leave_mcast_group (DWORD ip)
179 {
180  struct MultiCast *mc;
181  int i, rc, num_total = 0;
182 
183  if (!_multicast_on)
184  return (0);
185 
186  /* first verify that it's a valid mcast address
187  */
188  if (!_ip4_is_multicast(ip))
189  {
190  TCP_CONSOLE_MSG (1, ("%s isn't a multicast address\n",
191  _inet_ntoa(NULL,ip)));
192  return (0);
193  }
194 
195  /* Determine if the group has more than one interested
196  * process. If so, then just decrement ref-count and return
197  */
198  for (i = 0, mc = mcast_list; i < DIM(mcast_list); i++, mc++)
199  {
200  if (!mc->active)
201  continue;
202 
203  num_total++;
204  if (mc->ip != ip)
205  continue;
206 
207  if (mc->processes > 1)
208  {
209  mc->processes--;
210  return (1);
211  }
212  break;
213  }
214 
215  /* did the IP-addr they gave match anything in mcast_list ??
216  */
217  if (i >= DIM(mcast_list))
218  return (0);
219 
220  /* alas...we need to physically leave it
221  */
222  rc = _eth_leave_mcast_group (mc);
223  mc->active = FALSE;
224 
225  /* Remove daemon if no longer needed
226  */
227  if (num_total <= 1)
228  {
229  daemon_on = FALSE;
230  DAEMON_DEL (check_mcast_reports);
231  }
232  return (rc);
233 }
234 
235 /*
236  * Count number of active multicast groups
237  */
238 int W32_CALL num_mcast_active (void)
239 {
240  int i, num = 0;
241 
242  for (i = 0; i < DIM(mcast_list); i++)
243  if (mcast_list[i].active)
244  num++;
245  return (num);
246 }
247 
248 /*
249  * Handles the incoming IGMP packets
250  *
251  * Where:
252  * ip - the IP packet in question
253  */
254 void igmp_handler (const in_Header *ip, BOOL broadcast)
255 {
256  BYTE i;
257  DWORD src_ip, host;
258  BOOL found = FALSE;
259  WORD len = in_GetHdrLen (ip);
260  const IGMPv1_packet *igmp = (const IGMPv1_packet*) ((BYTE*)ip + len);
261  struct MultiCast *mc;
262 
263  DEBUG_RX (NULL, ip);
264 
265  if (igmp->version != IGMP_VERSION_1) /* We only speak v1 */
266  return;
267 
268  len = intel16 (ip->length) - len;
269  if (len < sizeof(*igmp))
270  {
271  STAT (igmpstats.igps_rcv_tooshort++);
272  return;
273  }
274 
275  if (CHECKSUM(igmp,sizeof(*igmp)) != 0xFFFF)
276  {
277  STAT (igmpstats.igps_rcv_badsum++);
278  return;
279  }
280 
281  host = intel (igmp->address);
282  src_ip = intel (ip->source);
283 
284  /* drop our own looped packets
285  */
286  if (_ip4_is_local_addr(src_ip))
287  return;
288 
289  /* Determine whether this is a report or a query
290  */
291  switch (igmp->type)
292  {
293  case IGMPv1_QUERY:
294  STAT (igmpstats.igps_rcv_queries++);
295  for (i = 0, mc = mcast_list; i < DIM(mcast_list); i++, mc++)
296  if (mc->active && mc->reply_timer == 0UL &&
297  mc->ip != MCAST_ALL_SYST)
298  {
299  mc->reply_timer = set_timeout (Random(500,1000));
300  found = TRUE;
301  }
302  if (!found && !broadcast && !is_eth_multicast(ip))
303  STAT (igmpstats.igps_rcv_badqueries++);
304  break;
305 
306  case IGMPv1_REPORT:
307  STAT (igmpstats.igps_rcv_reports++);
308  for (i = 0, mc = mcast_list; i < DIM(mcast_list); i++, mc++)
309  if (mc->active && mc->ip == host &&
310  host != MCAST_ALL_SYST)
311  {
312  mc->reply_timer = 0UL;
313  found = TRUE;
314  STAT (igmpstats.igps_rcv_ourreports++);
315  break;
316  }
317  if (!found && !broadcast && !is_eth_multicast(ip))
318  STAT (igmpstats.igps_rcv_badreports++);
319  break;
320 
321  default:
322  break;
323  }
324 }
325 
326 /*
327  * Send a IGMPv1 Report packet
328  *
329  * Where:
330  * ip is the IP address to report (on host order).
331  *
332  * Returns:
333  * 0 - if unable to send report
334  * 1 - report was sent successfully
335  */
336 static int igmp_report (DWORD ip)
337 {
338  struct IGMP_PKT *pkt;
339  struct IGMPv1_packet *igmp;
340  eth_address eth;
341 
342  /* get the ethernet addr of the destination
343  */
344  multi_to_eth (MCAST_ALL_SYST, &eth);
345 
346  /* format the packet with the request's hardware address
347  */
348  pkt = (struct IGMP_PKT*) _eth_formatpacket (eth, IP4_TYPE);
349  igmp = &pkt->igmp;
350  ip = intel (ip);
351 
352  /* fill in the igmp packet
353  */
354  igmp->type = IGMPv1_REPORT;
355  igmp->version = IGMP_VERSION_1;
356  igmp->unused = 0;
357  igmp->address = ip;
358  igmp->checksum = 0;
359  igmp->checksum = ~CHECKSUM (igmp, sizeof(*igmp));
360 
361  return IP4_OUTPUT (&pkt->in, 0, ip, IGMP_PROTO,
362  0, 0, 0, sizeof(*igmp), NULL);
363 }
364 
365 /*
366  * Check to see if we owe any IGMP Reports
367  * Called as a daemon from tcp_tick()
368  */
369 static void check_mcast_reports (void)
370 {
371  struct MultiCast *mc;
372  int i;
373 
374  if (!_multicast_on)
375  return;
376 
377  for (i = 0, mc = mcast_list; i < DIM(mcast_list); i++, mc++)
378  {
379  if (!mc->active || mc->ip == MCAST_ALL_SYST || !chk_timeout(mc->reply_timer))
380  continue;
381 
382  mc->reply_timer = _multicast_intvl > 0 ?
383  set_timeout (1000 * _multicast_intvl) : 0UL;
384  igmp_report (mc->ip);
385  }
386 }
387 #endif /* USE_MULTICAST */
388 
unsigned W32_CALL Random(unsigned a, unsigned b)
Returns a random integer in range [a..b].
Definition: misc.c:742
Multicast internal structure.
Definition: pcigmp.h:115
eth_address ethaddr
Ethernet address of group.
Definition: pcigmp.h:117
Core definitions.
Definition: igmp.h:62
BOOL _eth_join_mcast_group(const struct MultiCast *mc)
Joins a multicast group (at the physical layer).
Definition: pcsed.c:1248
DWORD ip
IP address of group.
Definition: pcigmp.h:116
DWORD W32_CALL set_timeout(DWORD msec)
Return time for when given timeout (msec) expires.
Definition: timer.c:503
void *W32_CALL _eth_formatpacket(const void *mac_dest, WORD eth_type)
_eth_format_packet() places the next packet to be transmitted into the above link-layer output packet...
Definition: pcsed.c:135
Definition: ip.h:67
WORD _pktdevclass
Ethernet, Token, FDDI etc.
Definition: pcpkt.c:51
BOOL _eth_leave_mcast_group(const struct MultiCast *mc)
Leaves a multicast group (at the physical layer)
Definition: pcsed.c:1324
BYTE processes
number of interested processes
Definition: pcigmp.h:118
DWORD reply_timer
IGMP query reply timer.
Definition: pcigmp.h:119
BOOL active
is this an active entry
Definition: pcigmp.h:120
char *W32_CALL _inet_ntoa(char *s, DWORD ip)
Convert an IP-address 'ip' into a string.
Definition: netaddr.c:43
BOOL W32_CALL chk_timeout(DWORD value)
Check if milli-sec value has expired:
Definition: timer.c:547