Watt-32 tcp/ip  2.2 dev-rel.10
pcicmp.c
Go to the documentation of this file.
1 
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 
9 #include "copyrigh.h"
10 #include "wattcp.h"
11 #include "strings.h"
12 #include "language.h"
13 #include "misc.h"
14 #include "timer.h"
15 #include "chksum.h"
16 #include "ip4_in.h"
17 #include "pctcp.h"
18 #include "pcsed.h"
19 #include "pcarp.h"
20 #include "pcstat.h"
21 #include "pcdbug.h"
22 #include "pcqueue.h"
23 #include "pcconfig.h"
24 #include "pcpkt.h"
25 #include "sock_ini.h"
26 #include "netaddr.h"
27 #include "ip4_out.h"
28 #include "pcicmp.h"
29 
30 const char *icmp_type_str [ICMP_MAXTYPE+1] = {
31  __LANG("Echo Reply"), "1", "2",
32  __LANG("Destination Unreachable"),
33  __LANG("Source Quench"),
34  __LANG("Redirect"), "6", "7",
35  __LANG("Echo Request"),
36  __LANG("Router Advert"),
37  __LANG("Router Solic"),
38  __LANG("TTL exceeded"),
39  __LANG("Param Problem"),
40  __LANG("Timestamp Message"),
41  __LANG("Timestamp Reply"),
42  __LANG("Info Request"),
43  __LANG("Info Reply"),
44  __LANG("Mask Request"),
45  __LANG("Mask Reply")
46  };
47 
48 /* code-strings for type ICMP_UNREACH
49  */
50 const char *icmp_unreach_str [16] = {
51  __LANG("Network Unreachable"), /* 0 */
52  __LANG("Host Unreachable"),
53  __LANG("Protocol Unreachable"), /* 2 */
54  __LANG("Port Unreachable"),
55  __LANG("Fragmentation needed and DF set"), /* 4 */
56  __LANG("Source Route Failed"),
57  __LANG("Network unknown"), /* 6 */
58  __LANG("Host unknown"),
59  __LANG("Source host isolated"), /* 8 */
60  __LANG("Net-access denied"),
61  __LANG("Host-access denied"), /* 10 */
62  __LANG("Bad TOS for net"),
63  __LANG("Bad TOS for host"), /* 12 */
64  __LANG("Admin prohibited"),
65  __LANG("Host precedence violation"), /* 14 */
66  __LANG("Precedence cutoff")
67  };
68 
69 /* code-strings for type ICMP_REDIRECT
70  */
71 const char *icmp_redirect_str [4] = {
72  __LANG("Redirect for Network"),
73  __LANG("Redirect for Host"),
74  __LANG("Redirect for TOS and Network"),
75  __LANG("Redirect for TOS and Host")
76  };
77 
78 /* code-strings for type ICMP_TIMXCEED
79  */
80 const char *icmp_exceed_str [2] = {
81  __LANG("TTL exceeded in transit"),
82  __LANG("Fragment reassembly time exceeded")
83  };
84 
85 #include <sys/pack_on.h>
86 
89 struct _pkt {
90  in_Header ip;
91  ICMP_PKT icmp;
92  in_Header data;
93  };
94 
95 #include <sys/pack_off.h>
96 
99 static struct {
100  char icmp;
101  char igmp;
102  char udp;
103  char tcp;
104  } do_redirect = { 1,1,1,1 };
105 
106 
107 static void icmp_print (int dbg_lvl, const char *msg, DWORD src)
108 {
109  if (debug_on < dbg_lvl)
110  return;
111 
112  outs ("\nICMP: ");
113  if (src)
114  {
115  outs ("(");
116  outs (_inet_ntoa(NULL,intel(src)));
117  outs ("): ");
118  }
119  outsnl (_LANG(msg));
120 }
121 
126 static void icmp_bogus (const in_Header *ip, int type, const char *msg)
127 {
128  char buf[100];
129 
130  strcpy (buf, _LANG("Bogus "));
131  strcat (buf, _LANG(icmp_type_str[type]));
132  if (msg)
133  strcat (buf, msg);
134  icmp_print (1, buf, ip->source);
135 }
136 
143 static BOOL icmp_check (const in_Header *ip, int type)
144 {
145  DWORD src, dst;
146  BOOL bcast;
147 
148  src = intel (ip->source);
149  dst = intel (ip->destination);
150  bcast = (~src & ~sin_mask) == 0;
151 
152  if (bcast)
153  {
154  icmp_bogus (ip, type, _LANG(" (broadcast)"));
155  return (FALSE);
156  }
157  if (ip->source == 0UL)
158  {
159  icmp_bogus (ip, type, _LANG(" (network)"));
160  return (FALSE);
161  }
162  if (IN_MULTICAST(dst))
163  {
164  icmp_bogus (ip, type, _LANG(" (multicast)"));
165  return (FALSE);
166  }
167  if (IN_EXPERIMENTAL(dst))
168  {
169  icmp_bogus (ip, type, _LANG(" (experimental)"));
170  return (FALSE);
171  }
172  return (TRUE);
173 }
174 
179 static int icmp_send (struct _pkt *pkt, DWORD src, DWORD dest, int length)
180 {
181  in_Header *ip = &pkt->ip;
182  ICMP_PKT *icmp = &pkt->icmp;
183 
184  icmp->unused.checksum = 0;
185  icmp->unused.checksum = ~CHECKSUM (icmp, length);
186 
187  return IP4_OUTPUT (ip, src, dest, ICMP_PROTO, 0, 0, 0, length, NULL);
188 }
189 
195 {
196  static DWORD next_time = 0UL;
197  struct _pkt *pkt;
198  union ICMP_PKT *unr;
199  unsigned len;
200 
201  if (!icmp_check(ip,ICMP_UNREACH))
202  return (0);
203 
204  if (next_time && !chk_timeout(next_time))
205  return (0);
206 
207  next_time = set_timeout (50);
208 
209  pkt = (struct _pkt*) _eth_formatpacket (MAC_SRC(ip), IP4_TYPE);
210  unr = &pkt->icmp;
211  len = intel16 (ip->length) - in_GetHdrLen (ip);
212  len = min (len, sizeof(*ip)+sizeof(unr->unused.spares));
213 
214  icmp_print (1, _LANG(icmp_unreach_str[code]), ip->destination);
215  memcpy (&unr->unused.ip, ip, len);
216  unr->unused.type = ICMP_UNREACH;
217  unr->unused.code = (BYTE) code;
218 
219  return icmp_send (pkt, ip->destination, ip->source, sizeof(unr->unused));
220 }
221 
225 int icmp_send_timexceed (const in_Header *ip, const void *mac_dest)
226 {
227  struct _pkt *pkt;
228  union ICMP_PKT *icmp;
229 
230  if (!icmp_check(ip,ICMP_TIMXCEED))
231  return (0);
232 
233  pkt = (struct _pkt*) _eth_formatpacket (mac_dest, IP4_TYPE);
234  icmp = &pkt->icmp;
235 
236  icmp_print (1, icmp_exceed_str[1], ip->destination);
237 
238  memset (&icmp->unused, 0, sizeof(icmp->unused));
239 
240  icmp->unused.type = ICMP_TIMXCEED;
241  icmp->unused.code = 1;
242 
243  icmp->unused.ip.hdrlen = sizeof(in_Header) / 4;
244  icmp->unused.ip.ver = 4;
245  icmp->unused.ip.ttl = _default_ttl;
246  icmp->unused.ip.identification = ip->identification;
247  icmp->unused.ip.length = intel16 (sizeof(in_Header));
248  icmp->unused.ip.proto = ip->proto;
249  icmp->unused.ip.checksum = ~CHECKSUM (&icmp->unused.ip, sizeof(in_Header));
250 
251  return icmp_send (pkt, ip->destination, ip->source, sizeof(icmp->unused));
252 }
253 
258 static WORD addr_mask_id = 0;
259 static WORD addr_mask_seq = 0;
260 
261 int icmp_send_mask_req (void)
262 {
263  mac_address *dst = (_pktserial ? NULL : &_eth_brdcast);
264  struct _pkt *pkt = (struct _pkt*) _eth_formatpacket (dst, IP4_TYPE);
265  union ICMP_PKT *icmp = &pkt->icmp;
266 
267  addr_mask_id = (WORD) set_timeout (0); /* get a random ID */
268  icmp->mask.type = ICMP_MASKREQ;
269  icmp->mask.code = 0;
270  icmp->mask.identifier = addr_mask_id;
271  icmp->mask.sequence = addr_mask_seq++;
272  icmp->mask.mask = 0;
273  return icmp_send (pkt, 0, (DWORD)INADDR_BROADCAST, sizeof(icmp->mask));
274 }
275 
276 static int icmp_echo_reply (const in_Header *ip, const union ICMP_PKT *req,
277  unsigned len)
278 {
279  struct _pkt *pkt;
280  union ICMP_PKT *icmp;
281 
282  if (!icmp_check(ip,ICMP_ECHO))
283  return (0);
284 
285  icmp_print (2, _LANG("PING reply sent"), 0);
286 
287 #if defined(USE_FRAGMENTS)
288  if (len > _mtu - sizeof(*ip))
289  {
290  icmp = (union ICMP_PKT*) req; /* reuse input for output */
291  icmp->echo.type = ICMP_ECHOREPLY;
292  icmp->echo.checksum = 0;
293  icmp->echo.checksum = ~CHECKSUM (icmp, len);
294  return _IP4_SEND_FRAGMENTS (NULL, ICMP_PROTO, ip->source, icmp, len);
295  }
296 #endif
297 
298  pkt = (struct _pkt*) _eth_formatpacket (MAC_SRC(ip), IP4_TYPE);
299  icmp = &pkt->icmp;
300 
301  len = min (len, _mtu - sizeof(*ip));
302  memcpy (icmp, req, len);
303  icmp->echo.type = ICMP_ECHOREPLY;
304  icmp->echo.code = req->echo.code; /* Win uses 0, Unix !0 */
305 
306  /* Use supplied ip values in case we ever multi-home.
307  * Note that ip values are still in network order.
308  */
309  return icmp_send (pkt, ip->destination, ip->source, len);
310 }
311 
315 static void icmp_redirect (const union ICMP_PKT *icmp, const in_Header *ip,
316  const in_Header *orig_ip, int code)
317 {
318  DWORD new_ip = intel (icmp->ip.ipaddr);
319  DWORD old_ip = intel (orig_ip->destination);
320  const char *msg;
321 
322  if (new_ip == old_ip)
323  {
324  /* Possibly because we and router use different netmasks
325  */
326  }
327  else if ((new_ip ^ my_ip_addr) & sin_mask) /* new host not on subnet */
328  {
329  char buf[100];
330 
331  strcpy (buf, ", GW = ");
332  strcat (buf, _inet_ntoa(NULL,new_ip));
333  icmp_bogus (ip, ICMP_REDIRECT, buf);
334  return;
335  }
336 
337  msg = icmp_redirect_str[code];
338  icmp_print (1, msg, ip->source);
339 
340  switch (orig_ip->proto)
341  {
342 #if !defined(USE_UDP_ONLY)
343  case TCP_PROTO:
344  if (do_redirect.tcp) /* do it to some socket */
345  _tcp_cancel (orig_ip, ICMP_REDIRECT, code, msg, &new_ip);
346  break;
347 #endif
348  case UDP_PROTO:
349  if (do_redirect.udp)
350  _udp_cancel (orig_ip, ICMP_REDIRECT, code, msg, &new_ip);
351  break;
352 
353  case ICMP_PROTO:
354  if (do_redirect.icmp)
355  {
356  /* _ip_recursion = TRUE; !! */
357  _arp_register (new_ip, old_ip);
358  /* _ip_recursion = FALSE; !! */
359  }
360  break;
361 
362  case IGMP_PROTO:
363  if (do_redirect.igmp)
364  {
365  /* _ip_recursion = TRUE; !! */
366  _arp_register (new_ip, old_ip);
367  /* _ip_recursion = FALSE; !! */
368  }
369  break;
370  }
371 }
372 
376 void icmp_handler (const in_Header *ip, BOOL broadcast)
377 {
378  union ICMP_PKT *icmp;
379  const in_Header *orig_ip;
380  int type, code;
381  unsigned len;
382  DWORD delta_time;
383  BOOL for_me, i_orig; /* is it for me, did I originate it */
384  const char *msg;
385 
386  DEBUG_RX (NULL, ip);
387 
388  if (block_icmp) /* application is handling ICMP on it's own; exit now */
389  return;
390 
391  len = in_GetHdrLen (ip);
392  icmp = (union ICMP_PKT*) ((BYTE*)ip + len);
393  len = intel16 (ip->length) - len;
394  for_me = _ip4_is_multihome_addr (intel(ip->destination));
395 
396  if (!for_me || broadcast) /* drop broadcast pings.. */
397  return;
398 
399  if (len < sizeof(icmp->info))
400  {
401  STAT (icmpstats.icps_tooshort++);
402  return;
403  }
404 
405  if (CHECKSUM(icmp,len) != 0xFFFF)
406  {
407  STAT (icmpstats.icps_checksum++);
408  icmp_print (1, _LANG("bad checksum"), ip->source);
409  return;
410  }
411 
412  type = icmp->unused.type;
413  code = icmp->unused.code;
414  orig_ip = &icmp->ip.ip;
415  i_orig = _ip4_is_local_addr (intel(orig_ip->source));
416 
417  if (type == ICMP_MASKREPLY)
418  {
419  if (!_do_mask_req)
420  return;
421  i_orig = TRUE;
422  }
423 
424  /* !! this needs work
425  */
426  if (!i_orig &&
427  (type != ICMP_ECHOREPLY && type != ICMP_ECHO &&
428  type != ICMP_IREQREPLY && type != ICMP_TSTAMP))
429  {
430  icmp_bogus (ip, type, NULL);
431  return;
432  }
433 
434  switch (type)
435  {
436  case ICMP_ECHOREPLY: /* check if we were waiting for it */
437  delta_time = set_timeout(0) - icmp->echo.identifier;
438  add_ping (intel(ip->source), delta_time, icmp->echo.index);
439  return;
440 
441  case ICMP_UNREACH:
442  if (code < DIM(icmp_unreach_str))
443  {
444  UINT len_needed = 8 + in_GetHdrLen (orig_ip);
445  WORD next_mtu = 0;
446 
447  msg = _LANG (icmp_unreach_str[code]);
448  icmp_print (1, msg, ip->source);
449 
450  if (orig_ip->proto == TCP_PROTO ||
451  orig_ip->proto == UDP_PROTO)
452  len_needed += 4; /* Need the src/dest port numbers */
453 
454  if (len >= len_needed)
455  {
456  if (code == ICMP_UNREACH_NEEDFRAG)
457  next_mtu = intel16 (icmp->needfrag.next_mtu);
458 
459 #if !defined(USE_UDP_ONLY)
460  if (orig_ip->proto == TCP_PROTO)
461  _tcp_cancel (orig_ip, ICMP_UNREACH, code, msg, &next_mtu);
462  else
463 #endif
464  if (orig_ip->proto == UDP_PROTO)
465  _udp_cancel (orig_ip, ICMP_UNREACH, code, msg, &next_mtu);
466 
468 #if defined(USE_BSD_API) && 0
469  else
470  _raw_cancel (orig_ip, ICMP_UNREACH, code, msg);
471 #endif
472  }
473  else
474  STAT (icmpstats.icps_tooshort++);
475  }
476  else
477  STAT (icmpstats.icps_badcode++);
478  return;
479 
480  case ICMP_SOURCEQUENCH:
481 #if !defined(USE_UDP_ONLY)
482  if (orig_ip->proto == TCP_PROTO)
483  {
484  msg = _LANG (icmp_type_str[type]);
485  icmp_print (1, msg, ip->source);
486  _tcp_cancel (orig_ip, ICMP_SOURCEQUENCH, code, msg, NULL);
487  }
488 #endif
489  return;
490 
491  case ICMP_REDIRECT:
492  if (code < DIM(icmp_redirect_str))
493  icmp_redirect (icmp, ip, orig_ip, code);
494  else STAT (icmpstats.icps_badcode++);
495  return;
496 
497  case ICMP_ECHO:
498  icmp_print (2, _LANG("PING requested of us"), ip->source);
499  icmp_echo_reply (ip, icmp, len);
500  return;
501 
502  case ICMP_TIMXCEED:
503  if (code >= DIM(icmp_exceed_str))
504  {
505  STAT (icmpstats.icps_badcode++);
506  return;
507  }
508  if (code == 0) /* "TTL exceeded in transit" */
509  switch (orig_ip->proto)
510  {
511 #if !defined(USE_UDP_ONLY)
512  case TCP_PROTO:
513  msg = _LANG (icmp_exceed_str[0]);
514  icmp_print (1, msg, ip->source);
515  _tcp_cancel (orig_ip, ICMP_TIMXCEED, code, msg, NULL);
516  break;
517 #endif
518  case UDP_PROTO:
519  msg = _LANG (icmp_exceed_str[0]);
520  icmp_print (1, msg, ip->source);
521  _udp_cancel (orig_ip, ICMP_TIMXCEED, code, msg, NULL);
522  break;
523  }
524  return;
525 
526  case ICMP_PARAMPROB:
527  msg = _LANG (icmp_type_str[ICMP_PARAMPROB]);
528  switch (orig_ip->proto)
529  {
530 #if !defined(USE_UDP_ONLY)
531  case TCP_PROTO:
532  icmp_print (0, msg, ip->source);
533  _tcp_cancel (orig_ip, ICMP_PARAMPROB, code, msg, NULL);
534  break;
535 #endif
536  case UDP_PROTO:
537  icmp_print (0, msg, ip->source);
538  _udp_cancel (orig_ip, ICMP_PARAMPROB, code, msg, NULL);
539  break;
540  }
541  return;
542 
543  case ICMP_ROUTERADVERT:
544  msg = _LANG (icmp_type_str[ICMP_ROUTERADVERT]);
545  icmp_print (1, msg, ip->source);
546  return;
547 
548  case ICMP_ROUTERSOLICIT:
549  msg = _LANG (icmp_type_str[ICMP_ROUTERSOLICIT]);
550  icmp_print (1, msg, ip->source);
551  return;
552 
553  case ICMP_TSTAMP:
554  msg = _LANG (icmp_type_str[ICMP_TSTAMP]);
555  icmp_print (1, msg, ip->source);
557  return;
558 
559  case ICMP_TSTAMPREPLY:
560  msg = _LANG (icmp_type_str[ICMP_TSTAMPREPLY]);
561  icmp_print (1, msg, ip->source);
563  return;
564 
565  case ICMP_IREQ:
566  msg = _LANG (icmp_type_str[ICMP_IREQ]);
567  icmp_print (1, msg, ip->source);
569  return;
570 
571  case ICMP_IREQREPLY:
572  msg = _LANG (icmp_type_str[ICMP_IREQREPLY]);
573  icmp_print (1, msg, ip->source);
575  return;
576 
577  case ICMP_MASKREQ:
578  /* might be sent by us, never answer */
579  break;
580 
581  case ICMP_MASKREPLY:
582  msg = _LANG (icmp_type_str[ICMP_MASKREPLY]);
583  icmp_print (0, msg, ip->source);
584  if ((icmp->mask.identifier == addr_mask_id) &&
585  (icmp->mask.sequence == addr_mask_seq-1) &&
586  sin_mask != intel(icmp->mask.mask))
587  outsnl ("Conflicting net-mask from \"ICMP Addr Mask Reply\"\7");
588  addr_mask_id = 0;
589  return;
590  }
591 }
592 
597 void icmp_doredirect (const char *value)
598 {
599  char *val = strdup (value);
600 
601  if (val)
602  {
603  strupr (val);
604  do_redirect.icmp = (strstr(val,"ICMP") != NULL);
605  do_redirect.igmp = (strstr(val,"IGMP") != NULL);
606  do_redirect.udp = (strstr(val,"UDP") != NULL);
607  do_redirect.tcp = (strstr(val,"TCP") != NULL);
608  free (val);
609  }
610 }
mac_address _eth_brdcast
Link-layer broadcast address.
Definition: pcsed.c:51
int _default_ttl
Definition: ip4_out.c:54
BOOL W32_CALL _arp_register(DWORD use_this_gateway_ip, DWORD for_this_host_ip)
Register a new host as gateway.
Definition: pcarp.c:843
BOOL block_icmp
when application handles ICMP itself
Definition: pctcp.c:67
static int icmp_send(struct _pkt *pkt, DWORD src, DWORD dest, int length)
Format and send a ICMP packet.
Definition: pcicmp.c:179
Core definitions.
Definition: ip_icmp.h:62
Definition: igmp.h:62
DWORD W32_CALL set_timeout(DWORD msec)
Return time for when given timeout (msec) expires.
Definition: timer.c:503
void icmp_doredirect(const char *value)
Determine which protocols we shall act upon when ICMP redirect is received.
Definition: pcicmp.c:597
int icmp_send_timexceed(const in_Header *ip, const void *mac_dest)
Send an "ICMP Time Exceeded" (reassebly timeout) back to 'ip->source'.
Definition: pcicmp.c:225
DWORD sin_mask
our net-mask, 255.255.255.0
Definition: pctcp.c:71
void _tcp_cancel(const in_Header *ip, int icmp_type, int icmp_code, const char *msg, const void *arg)
Cancel a TCP socket.
Definition: pctcp.c:1735
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
void icmp_handler(const in_Header *ip, BOOL broadcast)
Handler for incoming ICMP packets.
Definition: pcicmp.c:376
void W32_CALL add_ping(DWORD host, DWORD time, DWORD number)
Add an ICMP echo reply to the ping-cache.
Definition: pcping.c:147
static void icmp_redirect(const union ICMP_PKT *icmp, const in_Header *ip, const in_Header *orig_ip, int code)
Handle ICMP_REDIRECT messages.
Definition: pcicmp.c:315
BOOL _pktserial
using serial driver, SLIP/PPP
Definition: pcpkt.c:54
Definition: zinftree.h:24
Definition: pcicmp.c:89
static WORD addr_mask_id
Send an ICMP Address Mask Request as link-layer + IP broadcast.
Definition: pcicmp.c:258
int icmp_send_unreach(const in_Header *ip, int code)
Send an ICMP destination/protocol unreachable back to 'ip->source'.
Definition: pcicmp.c:194
DWORD my_ip_addr
our IP address
Definition: pctcp.c:70
static void icmp_bogus(const in_Header *ip, int type, const char *msg)
Print info about bogus and possibly dangerous ICMP messages.
Definition: pcicmp.c:126
static BOOL icmp_check(const in_Header *ip, int type)
Check if ip-source is a (directed) broadcast address.
Definition: pcicmp.c:143
BOOL _do_mask_req
do an "ICMP Mask Request" when configured
Definition: sock_ini.c:133
char *W32_CALL _inet_ntoa(char *s, DWORD ip)
Convert an IP-address 'ip' into a string.
Definition: netaddr.c:43
void _udp_cancel(const in_Header *ip, int icmp_type, int icmp_code, const char *msg, const void *arg)
Cancel an UDP socket.
Definition: pctcp.c:1627
BOOL W32_CALL chk_timeout(DWORD value)
Check if milli-sec value has expired:
Definition: timer.c:547