1/* ICMP Router Discovery Messages
2 * Copyright (C) 1997, 2000 Kunihiro Ishiguro
3 *
4 * This file is part of GNU Zebra.
5 *
6 * GNU Zebra is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2, or (at your option) any
9 * later version.
10 *
11 * GNU Zebra is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with GNU Zebra; see the file COPYING.  If not, write to the Free
18 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19 * 02111-1307, USA.
20 */
21
22#include <zebra.h>
23
24#include <netinet/ip_icmp.h>
25
26#include "if.h"
27#include "stream.h"
28#include "memory.h"
29#include "command.h"
30#include "log.h"
31#include "sockunion.h"
32#include "sockopt.h"
33
34#include "zebra/irdp.h"
35
36/* Default does nothing. */
37int irdp_mode = IRDP_NONE;
38
39/* Timer interval of irdp. */
40int irdp_timer_interval = IRDP_DEFAULT_INTERVAL;
41
42/* Max solicitations */
43int max_solicitations = MAX_SOLICITATIONS;
44
45#define IRDP_SOLICIT_PACKET_SIZE 8
46
47static struct irdp *irdp_head = NULL;
48
49extern int in_cksum (void *ptr, int nbytes);
50
51char *icmp_type_str[] =
52{
53  "Echo Reply",
54  "ICMP 1",
55  "ICMP 2",
56  "Dest Unreachable",
57  "Source Quench",
58  "Redirect",
59  "ICMP 6",
60  "ICMP 7",
61  "Echo",
62  "Router Advertise",
63  "Router Solicitation",
64  "Time Exceeded",
65  "Parameter Problem",
66  "Timestamp",
67  "Timestamp Reply",
68  "Info Request",
69  "Info Reply",
70  "Netmask Request",
71  "Netmask Reply",
72};
73
74char *
75icmp_type (int type)
76{
77  if (type < 0 || type >= (sizeof icmp_type_str / sizeof (char *))) {
78    return "OUT-OF-RANGE";
79  }
80  return icmp_type_str [type];
81}
82
83/* */
84void
85irdp_add_interface ()
86{
87  ;
88}
89
90/* */
91void
92irdp_delete_interface ()
93{
94
95}
96
97struct irdp *
98irdp_route_new ()
99{
100  struct irdp *new = XMALLOC (0, sizeof (struct irdp));
101  memset (new, 0, sizeof (struct irdp));
102  return new;
103}
104
105void
106irdp_route_free (struct irdp *route)
107{
108  XFREE (0, route);
109}
110
111void
112route_delete ()
113{
114
115}
116
117void
118route_init ()
119{
120
121}
122
123void
124route_add (struct in_addr addr, unsigned long pref)
125{
126  struct irdp *new = irdp_route_new();
127
128  new->prefix = addr;
129  new->pref = pref;
130
131  printf ("address %s\n", inet_ntoa (new->prefix));
132  printf ("pref %ld\n", new->pref);
133}
134
135void
136route_age (int time)
137{
138  struct irdp *p;
139
140  for (p = irdp_head; p != NULL; p = p->next) {
141    if (p->timer < time) {
142      /* fire */
143    } else {
144      p->timer -= time;
145    }
146  }
147}
148
149#define FLAG_TEST(a)  ((ifp->flags & (a)) == (a))
150
151void
152send_multicast (struct interface *ifp, int sock, struct stream *s, int size)
153{
154  struct sockaddr_in sin;
155  struct in_addr addr;
156  int nbytes;
157  struct connected *connected;
158  listnode node;
159
160  for (node = listhead (ifp->connected); node; nextnode (node))
161    {
162      connected = getdata (node);
163    }
164
165  if (setsockopt_multicast_ipv4 (sock, IP_MULTICAST_IF,
166		  addr, 0, ifp->ifindex) < 0)
167    {
168      perror ("setsockopt");
169      exit (1);
170    }
171
172  sin.sin_addr.s_addr = htonl (INADDR_ALLRTRS_GROUP);
173  sin.sin_family = AF_INET;
174
175  nbytes = sendto (sock, s->data, size, 0,
176		   (struct sockaddr *) &sin, sizeof (struct sockaddr));
177
178  if (nbytes != size)
179    {
180      perror ("sendto");
181      exit (1);
182    }
183}
184
185void
186send_broadcast ()
187{
188  struct sockaddr_in sin;
189
190  printf ("broadcast\n");
191  inet_aton ("255.255.255.255", &sin.sin_addr);
192}
193
194void
195irdp_send_solicit (int sock, struct stream *s, int size)
196{
197  struct interface *ifp;
198  listnode node;
199
200  for (node = listhead (iflist); node; nextnode (node))
201    {
202      ifp = getdata (node);
203      if (FLAG_TEST (IFF_UP | IFF_MULTICAST))
204	{
205	  send_multicast (ifp, sock, s, size);
206	}
207      else if (FLAG_TEST (IFF_UP | IFF_BROADCAST))
208	{
209	  send_broadcast ();
210	}
211    }
212}
213
214int
215ipv4_multicast_join (int sock,
216		     struct in_addr group,
217		     struct in_addr ifa,
218		     unsigned int ifindex)
219{
220  int ret;
221
222  ret = setsockopt_multicast_ipv4 (sock, IP_ADD_MEMBERSHIP,
223		    ifa, group.saddr, ifindex);
224
225  if (ret < 0)
226    zlog (NULL, LOG_INFO, "can't setsockopt IP_ADD_MEMBERSHIP");
227
228  return ret;
229}
230
231/* multicast packet recieve socket */
232int
233irdp_multicast_socket (int sock, struct in_addr group)
234{
235  struct interface *ifp;
236  listnode node;
237  struct in_addr addr;
238
239  for (node = listhead (iflist); node; nextnode (node))
240    {
241      ifp = getdata (node);
242
243      if ((ifp->flags & IFF_UP) && (ifp->flags & IFF_MULTICAST))
244	{
245	  ipv4_multicast_join (sock, group, addr, ifp->ifindex);
246	}
247    }
248  return 0;
249}
250
251struct
252{
253  u_char type;
254  u_char code;
255  u_int16_t checksum;
256  u_char number;
257  u_char entry;
258  u_int16_t lifetime;
259} radv;
260
261void
262irdp_set (int sock)
263{
264  struct in_addr irdp_group;
265
266  switch (irdp_mode)
267    {
268    case IRDP_HOST:
269      irdp_group.s_addr = htonl (INADDR_ALLHOSTS_GROUP);
270      break;
271    case IRDP_ROUTER:
272      irdp_group.s_addr = htonl (INADDR_ALLRTRS_GROUP);
273      break;
274    case IRDP_NONE:
275    default:
276      return;
277    }
278  irdp_multicast_socket (sock, irdp_group);
279}
280
281/* Make ICMP Router Solicitation Message. */
282int
283make_solicit_packet (struct stream *s)
284{
285  int size;
286  int checksum;
287
288  stream_putc (s, ICMP_ROUTERSOLICIT); /* Type. */
289  stream_putc (s, 0);		/* Code. */
290  stream_putw (s, 0);		/* Checksum. */
291  stream_putl (s, 0);		/* Reserved. */
292
293  /* in_cksum return network byte order value */
294  size = IRDP_SOLICIT_PACKET_SIZE;
295  checksum = in_cksum (s->data, size);
296  stream_putw_at (s, checksum, 2);
297
298  return IRDP_SOLICIT_PACKET_SIZE;
299}
300
301void
302irdp_solicit (int sock)
303{
304  struct stream *s;
305
306  s = stream_new (IRDP_SOLICIT_PACKET_SIZE);
307  make_solicit_packet (s);
308  irdp_send_solicit (sock, s, IRDP_SOLICIT_PACKET_SIZE);
309}
310
311#define ICMP_MINLEN 8
312
313/* check validity of the packet */
314int
315irdp_valid_check (char *packet, size_t size, struct sockaddr_in *from)
316{
317  struct icmp *icmp;
318
319  icmp = (struct icmp *) packet;
320
321  if (in_cksum (packet, size)) {
322    zlog_warn ("ICMP %s packet from %s: Bad checksum, silently ignored",
323	       icmp_type (icmp->icmp_type),
324	       inet_ntoa (from->sin_addr));
325    return -1;
326  }
327
328  if (icmp->icmp_code != 0) {
329    zlog_warn ("ICMP %s packet from %s: Bad ICMP type code, silently ignored",
330	       icmp_type (icmp->icmp_type),
331	       inet_ntoa (from->sin_addr));
332    return -1;
333  }
334
335  if (size < ICMP_MINLEN) {
336    zlog_warn ("ICMP %s packet from %s: IMCP message length is short",
337	       icmp_type (icmp->icmp_type),
338	       inet_ntoa (from->sin_addr));
339    return -1;
340  }
341  return 0;
342}
343
344int
345irdp_solicit_recv (struct stream *s, int size, struct sockaddr_in *from)
346{
347  if (irdp_valid_check (s->data, size, from)) {
348    return 1;
349  }
350  return 0;
351}
352
353void
354irdp_advert_recv (struct stream *s, int size, struct sockaddr_in *from)
355{
356  int i;
357  struct in_addr addr;
358  long pref;
359
360  if (irdp_valid_check (s->data, size, from) < 0) {
361    return;
362  }
363
364  radv.type = stream_getc (s);
365  radv.code =  stream_getc (s);
366  radv.checksum = stream_getw (s);
367  radv.number = stream_getc (s);
368  radv.entry = stream_getc (s);
369  radv.lifetime = stream_getw (s);
370
371  printf ("type : %s\n", icmp_type (radv.type));
372  printf ("number: %d\n", radv.number);
373  printf ("entry: %d\n", radv.entry);
374  printf ("lifetime: %d\n", radv.entry);
375
376  for (i = 0; i < radv.number; i++)
377    {
378      addr.s_addr = stream_getl (s);
379      pref = stream_getl (s);
380      route_add (addr, ntohl (pref));
381    }
382  /* Packet size check is needed at here. */
383}
384
385void
386irdp_packet_process (char *buf, int size, struct sockaddr_in *from)
387{
388  struct ip *ip;
389  struct icmp *icmp;
390  int hlen;
391  struct stream *s = NULL;
392
393  ip = (struct ip *)buf;
394  hlen = ip->ip_hl << 2;
395
396  if (size < hlen + ICMP_MINLEN)
397    zlog_err ("ICMP relpy length is short\n");
398
399  icmp = (struct icmp *)(buf + hlen);
400
401  stream_forward (s, hlen);
402
403  switch (icmp->icmp_type)
404    {
405    case ICMP_ROUTERADVERT:
406      irdp_advert_recv (s, size - hlen, from);
407      break;
408    case ICMP_ROUTERSOLICIT:
409      irdp_solicit_recv (s, size - hlen, from);
410      break;
411    }
412}
413
414/* Make socket for ICMP Router Discovery. */
415int
416irdp_make_socket ()
417{
418  int sock;
419  struct protoent *pent;
420
421  if ((pent = getprotobyname ("icmp")) == NULL) {
422    perror ("getprotobyname");
423    exit (1);
424  }
425
426  if ((sock = socket (AF_INET, SOCK_RAW, pent->p_proto)) < 0)
427    {
428      perror ("socket");
429      exit (1);
430    }
431
432  return sock;
433}
434
435/* recv routine */
436int
437irdp_recv (int sock)
438{
439#define PACKET_BUF 4096
440  int nbytes;
441  struct sockaddr_in from;
442  int fromlen;
443  char buf[PACKET_BUF];
444
445  fromlen = sizeof (from);
446  nbytes = recvfrom (sock, (char *)buf, PACKET_BUF, 0,
447		     (struct sockaddr *)&from, &fromlen);
448
449  if (nbytes < 0)
450    {
451      perror ("recvfrom");
452      exit (1);
453    }
454
455  irdp_packet_process (buf, nbytes, &from);
456
457  return 0;
458}
459
460/* irdp packet recv loop */
461void
462irdp_loop (int sock)
463{
464  while (1)
465    {
466      irdp_recv (sock);
467    }
468}
469
470DEFUN (ip_irdp,
471       ip_irdp_cmd,
472       "ip irdp",
473       IP_STR
474       "ICMP Router discovery on this interface\n")
475{
476  return CMD_SUCCESS;
477}
478
479DEFUN (ip_irdp_multicast,
480       ip_irdp_multicast_cmd,
481       "ip irdp multicast",
482       IP_STR
483       "ICMP Router discovery on this interface\n"
484       "Send IRDP advertisement to the multicast address\n")
485{
486  return CMD_SUCCESS;
487}
488
489DEFUN (ip_irdp_holdtime,
490       ip_irdp_holdtime_cmd,
491       "ip irdp holdtime <0-9000>",
492       IP_STR
493       "ICMP Router discovery on this interface\n"
494       "Set holdtime value\n"
495       "Holdtime value in seconds. Default is 1800 seconds\n")
496{
497  return CMD_SUCCESS;
498}
499
500DEFUN (ip_irdp_maxadvertinterval,
501       ip_irdp_maxadvertinterval_cmd,
502       "ip irdp maxadvertinterval (0|<4-1800>)",
503       IP_STR
504       "ICMP Router discovery on this interface\n"
505       "Set maximum time between advertisement\n"
506       "Maximum advertisement interval in seconds\n")
507{
508  return CMD_SUCCESS;
509}
510
511DEFUN (ip_irdp_minadvertinterval,
512       ip_irdp_minadvertinterval_cmd,
513       "ip irdp minadvertinterval <3-1800>",
514       IP_STR
515       "ICMP Router discovery on this interface\n"
516       "Set minimum time between advertisement\n"
517       "Minimum advertisement interval in seconds\n")
518{
519  return CMD_SUCCESS;
520}
521
522DEFUN (ip_irdp_preference,
523       ip_irdp_preference_cmd,
524       /* "ip irdp preference <-2147483648-2147483647>", */
525       "ip irdp preference <0-2147483647>",
526       IP_STR
527       "ICMP Router discovery on this interface\n"
528       "Set default preference level for this interface\n"
529       "Preference level\n")
530{
531  return CMD_SUCCESS;
532}
533
534#if 0
535DEFUN (ip_irdp_address,
536       ip_irdp_address_cmd,
537       "ip irdp address A.B.C.D",
538       IP_STR
539       "ICMP Router discovery on this interface\n"
540       "Specify IRDP address and preference to proxy-advertise\n"
541       "Set IRDP address for proxy-advertise\n")
542{
543  return CMD_SUCCESS;
544}
545#endif /* 0 */
546
547DEFUN (ip_irdp_address_preference,
548       ip_irdp_address_preference_cmd,
549       "ip irdp address A.B.C.D <0-2147483647>",
550       IP_STR
551       "ICMP Router discovery on this interface\n"
552       "Specify IRDP address and preference to proxy-advertise\n"
553       "Set IRDP address for proxy-advertise\n"
554       "Preference level\n")
555{
556  return CMD_SUCCESS;
557}
558
559void
560irdp_init ()
561{
562#ifdef FOX_CMD_SUPPORT
563  install_element (INTERFACE_NODE, &ip_irdp_cmd);
564  install_element (INTERFACE_NODE, &ip_irdp_multicast_cmd);
565  install_element (INTERFACE_NODE, &ip_irdp_holdtime_cmd);
566  install_element (INTERFACE_NODE, &ip_irdp_maxadvertinterval_cmd);
567  install_element (INTERFACE_NODE, &ip_irdp_minadvertinterval_cmd);
568  install_element (INTERFACE_NODE, &ip_irdp_preference_cmd);
569  install_element (INTERFACE_NODE, &ip_irdp_address_preference_cmd);
570#endif /* FOX_CMD_SUPPORT */
571}
572