Watt-32 tcp/ip  2.2 dev-rel.10
pcicmp6.c
Go to the documentation of this file.
1 
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <arpa/inet.h>
42 
43 #include "wattcp.h"
44 #include "misc.h"
45 #include "timer.h"
46 #include "chksum.h"
47 #include "strings.h"
48 #include "netaddr.h"
49 #include "language.h"
50 #include "ip6_in.h"
51 #include "ip6_out.h"
52 #include "ip4_out.h"
53 #include "split.h"
54 #include "pctcp.h"
55 #include "pcsed.h"
56 #include "pcstat.h"
57 #include "pcdbug.h"
58 #include "pcpkt.h"
59 #include "pcarp.h"
60 #include "pcconfig.h"
61 #include "pcicmp6.h"
62 
64 #if defined(USE_IPV6)
65 
66 struct prefix_table {
67  size_t len;
68  ip6_address prefix;
69  };
70 
71 struct prefix_table prefix_list [10];
72 struct icmp6_cache neighbor_cache [ND_CACHE_SIZE];
73 struct icmp6_cache destin_cache [ND_CACHE_SIZE];
74 
75 int icmp6_prefix_len;
76 ip6_address icmp6_prefix;
77 DWORD icmp6_6to4_gateway = 0; /* host order */
78 
79 #include <sys/pack_on.h>
80 
81 struct _pkt {
82  in6_Header in;
84  BYTE options[1];
85  };
86 
87 #include <sys/pack_off.h>
88 
89 #if 0
90 static const char *icmp6_options_str [] = {
91  "",
92  __LANG ("source link-layer addr"),
93  __LANG ("target link-layer addr"),
94  __LANG ("prefix information"),
95  __LANG ("redirected header"),
96  __LANG ("MTU")
97  };
98 #endif
99 
100 static WORD route_lifetime;
101 
102 static void icmp6_print (int dbg_lvl, const char *msg, const void *src);
103 static int icmp6_send (in6_Header *ip, ICMP6_PKT *icmp, UINT len);
104 static void router_advert (const union ICMP6_PKT *icmp, unsigned len);
105 static int neighbor_advert (const eth_address *eth);
106 static void echo_reply (const in6_Header *ip, const union ICMP6_PKT *icmp);
107 
108 /*
109  * Make a Ethernet destination address from a (solicited node) ip-address.
110  * From /etc/manuf: 33:33:0:0:0:0/16, IPv6 neighbor discovery.
111  */
112 static const void *icmp6_mac_addr (const void *ip_addr)
113 {
114  static mac_address mac;
115  const BYTE *addr = (const BYTE*)ip_addr;
116 
117  mac[0] = 0x33;
118  mac[1] = 0x33;
119  mac[2] = 0xFF;
120  mac[3] = addr[13];
121  mac[4] = addr[14];
122  mac[5] = addr[15];
123  return (&mac[0]);
124 }
125 
126 int icmp6_router_solicitation (void)
127 {
128  struct _pkt *pkt;
129  struct in6_Header *ip;
130  struct ICMP6_route_sol *icmp;
131 
132  pkt = (struct _pkt*) _eth_formatpacket (_eth_brdcast, IP6_TYPE);
133  ip = &pkt->in;
134  icmp = &pkt->icmp.rsolic;
135  icmp->type = ND_ROUTER_SOLICIT;
136  icmp->code = 0;
137  icmp->reserved = 0;
138 
139  memcpy (&ip->source, &in6addr_my_ip, sizeof(ip->source));
140  memcpy (&ip->destination, &in6addr_allr_mc, sizeof(ip->destination));
141  return icmp6_send (ip, (ICMP6_PKT*)icmp, sizeof(*icmp));
142 }
143 
144 /*
145  * Add the IPv4 'icmp6_6to4_gateway' address to the ND cache
146  */
147 BOOL icmp6_add_gateway4 (void)
148 {
149  struct icmp6_cache *ce = NULL;
150  ip6_address ip;
151  mac_address eth;
152  BOOL arp_ok;
153 
154  arp_ok = _arp_resolve (icmp6_6to4_gateway, &eth);
155  if (arp_ok)
156  {
157  memcpy (&ip, in6addr_mapped, sizeof(in6addr_mapped));
158  *(DWORD*) &ip[12] = intel (icmp6_6to4_gateway);
159  ce = icmp6_ncache_insert_fix (&ip, eth);
160  }
161  TCP_CONSOLE_MSG (1, ("Adding %s as 6-to-4 gateway, ARP %s, Insert %s\n",
162  _inet_ntoa(NULL,intel(icmp6_6to4_gateway)),
163  arp_ok ? "OK" : "failed",
164  ce ? "OK" : "failed"));
165  return (arp_ok && ce);
166 }
167 
168 /*
169  * Perform an "ICMPv6 Neighbor Solicitation" on 'addr' to resolve it's
170  * MAC address (the role of ARP in IPv4).
171  */
172 int icmp6_neigh_solic (const void *addr, eth_address *eth)
173 {
174  struct _pkt *pkt;
175  struct in6_Header *ip;
176  struct ICMP6_nd_sol *icmp;
177  struct icmp6_cache *cache;
178  struct in6_addr dest;
179  BYTE *options;
180 
181  WATT_ASSERT (eth != NULL);
182 
183  if (_ip6_is_local_addr(addr))
184  {
185  memcpy (eth, _eth_addr, sizeof(*eth));
186  return (1);
187  }
188 
189  cache = icmp6_ncache_lookup (addr);
190  if (cache)
191  {
192  memcpy (eth, &cache->eth, sizeof(*eth));
193  return (1);
194  }
195 
196  pkt = (struct _pkt*) _eth_formatpacket (icmp6_mac_addr(addr), IP6_TYPE);
197  ip = &pkt->in;
198  icmp = &pkt->icmp.nd_solic;
199  icmp->type = ND_NEIGHBOR_SOLICIT;
200  icmp->code = 0;
201  icmp->reserved = 0;
202 
203  memcpy (&dest, &in6addr_alln_mc, sizeof(dest)); /* All nodes MC address */
204  dest.s6_addr[11] ^= 1; /* toggle G-bit */
205  dest.s6_addr[12] = 0xFF;
206  dest.s6_addr[13] = ((BYTE*)addr)[13];
207  dest.s6_addr[14] = ((BYTE*)addr)[14];
208  dest.s6_addr[15] = ((BYTE*)addr)[15];
209 
210  memcpy (&icmp->target, addr, sizeof(icmp->target));
211  memcpy (&ip->source, &in6addr_my_ip, sizeof(ip->source));
212  memcpy (&ip->destination, &dest, sizeof(ip->destination));
213 
214  /* Insert "src-mac address" option
215  */
216  options = (BYTE*) (icmp + 1);
217  *options++ = ND_OPT_SOURCE_LINKADDR;
218  *options++ = 1; /* multiple of 8 bytes */
219  memcpy (options, _eth_addr, sizeof(_eth_addr));
220  options += sizeof(_eth_addr);
221  *options++ = 0; /* End option */
222  *options++ = 0;
223 
224  icmp6_send (ip, (ICMP6_PKT*)icmp, options - (BYTE*)icmp);
225 
226 #if 0
227  while (1)
228  {
229  eth = ..
230  }
231 #endif
232 
233  icmp6_ncache_insert (addr, eth);
234 
236  return (0);
237 }
238 
239 void icmp6_handler (const in6_Header *ip)
240 {
241  const struct pkt_split *pkt;
242  const union ICMP6_PKT *icmp;
243  int type, code;
244  unsigned len;
245  BOOL for_me;
246 
247  DEBUG_RX (NULL, ip);
248 
249  pkt = pkt_get_type_in (TYPE_IP6_ICMP);
250  icmp = (const union ICMP6_PKT*) pkt->data;
251  len = pkt->len;
252  for_me = !memcmp (ip->destination, &in6addr_my_ip, sizeof(ip->destination));
253 
254  if (len < sizeof(icmp->unused))
255  {
256  STAT (icmp6stats.icp6s_tooshort++);
257  return;
258  }
259 
260  if (!_ip6_icmp_checksum(ip,icmp,intel16(ip->len)))
261  {
262  STAT (icmp6stats.icp6s_checksum++);
263  icmp6_print (1, _LANG("bad checksum"), ip->source);
264  return;
265  }
266 
267  type = icmp->unused.type;
268  code = icmp->unused.code;
269 
270  switch (type)
271  {
272  case ICMP6_DST_UNREACH:
273  break;
274 
275  case ICMP6_PACKET_TOO_BIG:
276  break;
277 
278  case ICMP6_TIME_EXCEEDED:
279  break;
280 
281  case ICMP6_PARAM_PROB:
282  break;
283 
284  case ICMP6_ECHO_REQUEST:
285  if (for_me)
286  {
287  icmp6_print (2, _LANG("PING6 requested of us"), ip->source);
288  echo_reply (ip, icmp);
289  }
290  break;
291 
292  case ICMP6_ECHO_REPLY:
293  break;
294 
295  case ICMP6_MEMBERSHIP_QUERY:
296  break;
297 
298  case ICMP6_MEMBERSHIP_REPORT:
299  break;
300 
301  case ICMP6_MEMBERSHIP_REDUCTION:
302  break;
303 
304  case ICMP6_ROUTER_RENUMBERING:
305  break;
306 
307  case ICMP6_WRUREQUEST:
308  break;
309 
310  case ICMP6_WRUREPLY:
311  break;
312 
313  case ND_ROUTER_SOLICIT: /* silently discard */
314  break;
315 
316  case ND_ROUTER_ADVERT:
317  if (IN6_IS_ADDR_LINKLOCAL(ip->source) && ip->hop_limit == 255)
318  router_advert (icmp, len);
319  break;
320 
321  case ND_NEIGHBOR_SOLICIT:
322  if (IN6_ARE_ADDR_EQUAL(icmp->nd_solic.target, &in6addr_my_ip))
323  neighbor_advert ((const eth_address*)MAC_SRC(ip));
324  break;
325 
326  case ND_NEIGHBOR_ADVERT:
327  break;
328 
329  case ND_REDIRECT:
330  break;
331  }
332  ARGSUSED (code);
333 }
334 
335 void icmp6_unreach (const in6_Header *ip, int code)
336 {
337  ARGSUSED (ip);
338  ARGSUSED (code);
339 }
340 
341 struct icmp6_cache *icmp6_ncache_lookup (const void *ip)
342 {
343  struct icmp6_cache *entry = &neighbor_cache[0];
344  time_t now = time (NULL);
345  int i;
346 
347  for (i = 0; i < DIM(neighbor_cache); i++, entry++)
348  {
349 #if 0 /* !! test */
350  printf ("%2d: ip '%s', entry '%s'\n",
351  i, _inet6_ntoa(ip), _inet6_ntoa(entry->ip));
352 #endif
353 
354  if (entry->state != ND_CACHE_REACHABLE)
355  continue;
356 
357  if (entry->expiry && now >= entry->expiry)
358  {
359  entry->state = ND_CACHE_UNUSED;
360  entry->expiry = 0;
361  continue;
362  }
363 
364 #if 0 /* test !! */
365  if (*(DWORD*)&entry->ip[12] == intel(icmp6_6to4_gateway))
366  return (entry);
367 #endif
368 
369  if (IN6_IS_ADDR_V4MAPPED(ip) &&
370  *(DWORD*)&entry->ip[12] == intel(icmp6_6to4_gateway))
371  return (entry);
372 
373  if (IN6_ARE_ADDR_EQUAL(ip,entry->ip))
374  return (entry);
375  }
376  return (NULL);
377 }
378 
379 struct icmp6_cache *icmp6_ncache_insert (const void *ip, const void *eth)
380 {
381  struct icmp6_cache *found = NULL;
382  struct icmp6_cache *entry = &neighbor_cache[0];
383  static WORD index = 0; /* rotates round-robin */
384  int i;
385 
386  for (i = 0; i < DIM(neighbor_cache); i++, entry++)
387  {
388  if (entry->state == ND_CACHE_PROBE || IN6_IS_ADDR_UNSPECIFIED(entry->ip))
389  {
390  found = entry;
391  break;
392  }
393  }
394 
395  if (!found) /* not found, grab a "random" slot */
396  {
397  index++;
398  index %= DIM (neighbor_cache);
399  found = &neighbor_cache [index];
400  }
401  memcpy (found->ip, ip, sizeof(found->ip));
402  memcpy (found->eth, eth, sizeof(found->eth));
403  found->state = ND_CACHE_INCOMPLETE;
404  found->expiry = time (NULL);
405  return (found);
406 }
407 
408 struct icmp6_cache *icmp6_ncache_insert_fix (const void *ip, const void *eth)
409 {
410  struct icmp6_cache *ce = icmp6_ncache_insert (ip, eth);
411 
412  if (ce)
413  {
414  ce->state = ND_CACHE_REACHABLE; /* always reachable */
415  ce->expiry = 0; /* never expire */
416  }
417  return (ce);
418 }
419 
420 static void icmp6_print (int dbg_lvl, const char *msg, const void *src)
421 {
422  if (debug_on < dbg_lvl)
423  return;
424 
425  outs ("\nICMP6: ");
426  if (src)
427  {
428  const char *addr = _inet6_ntoa (src);
429  outs ("(");
430  outs (addr ? addr : "(null?)");
431  outs ("): ");
432  }
433  outsnl (_LANG(msg));
434 }
435 
436 static int icmp6_send (in6_Header *ip, ICMP6_PKT *icmp, UINT len)
437 {
438  icmp->unused.checksum = 0;
439  icmp->unused.checksum = _ip6_checksum (ip, IP6_NEXT_ICMP, icmp, len);
440  return IP6_OUTPUT (ip, &ip->source, &ip->destination, IP6_NEXT_ICMP,
441  len, 0, NULL);
442 }
443 
444 static void parse_options (const BYTE *opt, unsigned max)
445 {
446  const BYTE *start = opt;
447 
448  while (opt < start + max)
449  {
450  const struct nd_opt_prefix_info *pi = (const struct nd_opt_prefix_info*) opt;
451  BYTE type = opt[0];
452  int len = opt[1] << 3;
453 
454  switch (type)
455  {
456  case ND_OPT_PREFIX_INFORMATION:
457  if (pi->nd_opt_pi_len == 4)
458  {
459  icmp6_prefix_len = pi->nd_opt_pi_prefix_len;
460  memcpy (&icmp6_prefix, &pi->nd_opt_pi_prefix, sizeof(icmp6_prefix));
461  }
462  break;
463  case ND_OPT_SOURCE_LINKADDR:
464  case ND_OPT_TARGET_LINKADDR:
465  case ND_OPT_REDIRECTED_HEADER:
466  case ND_OPT_MTU:
467  case 0: /* EOL */
468  break;
469  default:
470  icmp6_print (1, _LANG("Illegal option"), NULL);
471  break;
472  }
473  opt += len;
474  }
475 }
476 
477 static void router_advert (const union ICMP6_PKT *icmp, unsigned len)
478 {
479  if (icmp->radvert.hop_limit)
480  _default6_ttl = icmp->radvert.hop_limit;
481  if (icmp->radvert.lifetime)
482  route_lifetime = intel16 (icmp->radvert.lifetime);
483  if (len > sizeof(icmp->radvert))
484  parse_options ((const BYTE*)icmp + sizeof(icmp->radvert),
485  len - sizeof(icmp->radvert));
486 }
487 
488 static int neighbor_advert (const eth_address *eth)
489 {
490  struct _pkt *pkt;
491  struct in6_Header *ip;
492  struct ICMP6_nd_adv *icmp;
493 
494  pkt = (struct _pkt*) _eth_formatpacket (eth, IP6_TYPE);
495  ip = &pkt->in;
496  icmp = &pkt->icmp.nd_adv;
497  icmp->type = ND_NEIGHBOR_ADVERT;
498  icmp->code = 0;
499  icmp->reserved1 = 0;
500  icmp->reserved2 = 0;
501  icmp->reserved3 = 0;
502  icmp->router = 0;
503  icmp->solicited = 1;
504  icmp->override = 1;
505 
506  memcpy (&icmp->target, &in6addr_my_ip, sizeof(icmp->target));
507  memcpy (&ip->source, &in6addr_my_ip, sizeof(ip->source));
508  memcpy (&ip->destination, &in6addr_allr_mc, sizeof(ip->destination));
509  return icmp6_send (ip, (ICMP6_PKT*)icmp, sizeof(*icmp));
510 }
511 
512 static void echo_reply (const in6_Header *orig_ip,
513  const union ICMP6_PKT *orig_icmp)
514 {
515  struct _pkt *pkt;
516  struct in6_Header *ip;
517  union ICMP6_PKT *icmp;
518  WORD len = intel16 (orig_ip->len);
519 
520  pkt = (struct _pkt*) _eth_formatpacket (MAC_SRC(orig_ip), IP6_TYPE);
521  ip = &pkt->in;
522  icmp = &pkt->icmp;
523 
524  /* Don't let a huge reassembled ICMP-packet kill us.
525  */
526  len = min (len, _mtu - sizeof(*ip));
527  memcpy (icmp, orig_icmp, len);
528  icmp->echo.type = ICMP6_ECHO_REPLY;
529  icmp->echo.code = orig_icmp->echo.code;
530 
531  /* Use supplied ip values in case we ever multi-home.
532  * Note that ip values are still in network order.
533  */
534  memcpy (&ip->source, &in6addr_my_ip, sizeof(ip->source));
535  memcpy (&ip->destination, &orig_ip->source, sizeof(ip->destination));
536  ip->hop_limit = orig_ip->hop_limit;
537 
538  icmp6_print (2, _LANG("PING reply sent"), NULL);
539  icmp6_send (ip, (ICMP6_PKT*)icmp, len);
540 }
541 #endif /* USE_IPV6 */
542 
mac_address _eth_brdcast
Link-layer broadcast address.
Definition: pcsed.c:51
int icmp6_neigh_solic(const void *addr, eth_address *eth)
Definition: pcicmp6.c:172
Core definitions.
Definition: ip_icmp.h:62
mac_address _eth_addr
Local link-layer source address.
Definition: pcsed.c:50
Definition: in.h:153
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
Definition: zinftree.h:24
Definition: pcicmp.c:89
const char * _inet6_ntoa(const void *ip)
Convert an IPv6-address 'ip' into a string.
Definition: netaddr.c:401
WORD _ip6_checksum(const in6_Header *ip, WORD proto, const void *payload, unsigned payloadlen)
Generic IPv6 checksum function.
Definition: chksum.c:58
BOOL W32_CALL _arp_resolve(DWORD ip, eth_address *eth)
The blocking lookup function visible to higher functions.
Definition: pcarp.c:1279
char *W32_CALL _inet_ntoa(char *s, DWORD ip)
Convert an IP-address 'ip' into a string.
Definition: netaddr.c:43