Initial revision
diff --git a/zebra/irdp.c b/zebra/irdp.c
new file mode 100644
index 0000000..1b3bf23
--- /dev/null
+++ b/zebra/irdp.c
@@ -0,0 +1,569 @@
+/* ICMP Router Discovery Messages
+ * Copyright (C) 1997, 2000 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include <netinet/ip_icmp.h>
+
+#include "if.h"
+#include "stream.h"
+#include "memory.h"
+#include "command.h"
+#include "log.h"
+#include "sockunion.h"
+#include "sockopt.h"
+
+#include "zebra/irdp.h"
+
+/* Default does nothing. */
+int irdp_mode = IRDP_NONE;
+
+/* Timer interval of irdp. */
+int irdp_timer_interval = IRDP_DEFAULT_INTERVAL;
+
+/* Max solicitations */
+int max_solicitations = MAX_SOLICITATIONS;
+
+#define IRDP_SOLICIT_PACKET_SIZE 8
+
+static struct irdp *irdp_head = NULL;
+
+extern int in_cksum (void *ptr, int nbytes);
+
+char *icmp_type_str[] =
+{
+ "Echo Reply",
+ "ICMP 1",
+ "ICMP 2",
+ "Dest Unreachable",
+ "Source Quench",
+ "Redirect",
+ "ICMP 6",
+ "ICMP 7",
+ "Echo",
+ "Router Advertise",
+ "Router Solicitation",
+ "Time Exceeded",
+ "Parameter Problem",
+ "Timestamp",
+ "Timestamp Reply",
+ "Info Request",
+ "Info Reply",
+ "Netmask Request",
+ "Netmask Reply",
+};
+
+char *
+icmp_type (int type)
+{
+ if (type < 0 || type >= (sizeof icmp_type_str / sizeof (char *))) {
+ return "OUT-OF-RANGE";
+ }
+ return icmp_type_str [type];
+}
+
+/* */
+void
+irdp_add_interface ()
+{
+ ;
+}
+
+/* */
+void
+irdp_delete_interface ()
+{
+
+}
+
+struct irdp *
+irdp_route_new ()
+{
+ struct irdp *new = XMALLOC (0, sizeof (struct irdp));
+ memset (new, 0, sizeof (struct irdp));
+ return new;
+}
+
+void
+irdp_route_free (struct irdp *route)
+{
+ XFREE (0, route);
+}
+
+void
+route_delete ()
+{
+
+}
+
+void
+route_init ()
+{
+
+}
+
+void
+route_add (struct in_addr addr, unsigned long pref)
+{
+ struct irdp *new = irdp_route_new();
+
+ new->prefix = addr;
+ new->pref = pref;
+
+ printf ("address %s\n", inet_ntoa (new->prefix));
+ printf ("pref %ld\n", new->pref);
+}
+
+void
+route_age (int time)
+{
+ struct irdp *p;
+
+ for (p = irdp_head; p != NULL; p = p->next) {
+ if (p->timer < time) {
+ /* fire */
+ } else {
+ p->timer -= time;
+ }
+ }
+}
+
+#define FLAG_TEST(a) ((ifp->flags & (a)) == (a))
+
+void
+send_multicast (struct interface *ifp, int sock, struct stream *s, int size)
+{
+ struct sockaddr_in sin;
+ struct in_addr addr;
+ int nbytes;
+ struct connected *connected;
+ listnode node;
+
+ for (node = listhead (ifp->connected); node; nextnode (node))
+ {
+ connected = getdata (node);
+ }
+
+ if (setsockopt_multicast_ipv4 (sock, IP_MULTICAST_IF,
+ addr, 0, ifp->ifindex) < 0)
+ {
+ perror ("setsockopt");
+ exit (1);
+ }
+
+ sin.sin_addr.s_addr = htonl (INADDR_ALLRTRS_GROUP);
+ sin.sin_family = AF_INET;
+
+ nbytes = sendto (sock, s->data, size, 0,
+ (struct sockaddr *) &sin, sizeof (struct sockaddr));
+
+ if (nbytes != size)
+ {
+ perror ("sendto");
+ exit (1);
+ }
+}
+
+void
+send_broadcast ()
+{
+ struct sockaddr_in sin;
+
+ printf ("broadcast\n");
+ inet_aton ("255.255.255.255", &sin.sin_addr);
+}
+
+void
+irdp_send_solicit (int sock, struct stream *s, int size)
+{
+ struct interface *ifp;
+ listnode node;
+
+ for (node = listhead (iflist); node; nextnode (node))
+ {
+ ifp = getdata (node);
+ if (FLAG_TEST (IFF_UP | IFF_MULTICAST))
+ {
+ send_multicast (ifp, sock, s, size);
+ }
+ else if (FLAG_TEST (IFF_UP | IFF_BROADCAST))
+ {
+ send_broadcast ();
+ }
+ }
+}
+
+int
+ipv4_multicast_join (int sock,
+ struct in_addr group,
+ struct in_addr ifa,
+ unsigned int ifindex)
+{
+ int ret;
+
+ ret = setsockopt_multicast_ipv4 (sock, IP_ADD_MEMBERSHIP,
+ ifa, group.saddr, ifindex);
+
+ if (ret < 0)
+ zlog (NULL, LOG_INFO, "can't setsockopt IP_ADD_MEMBERSHIP");
+
+ return ret;
+}
+
+/* multicast packet recieve socket */
+int
+irdp_multicast_socket (int sock, struct in_addr group)
+{
+ struct interface *ifp;
+ listnode node;
+ struct in_addr addr;
+
+ for (node = listhead (iflist); node; nextnode (node))
+ {
+ ifp = getdata (node);
+
+ if ((ifp->flags & IFF_UP) && (ifp->flags & IFF_MULTICAST))
+ {
+ ipv4_multicast_join (sock, group, addr, ifp->ifindex);
+ }
+ }
+ return 0;
+}
+
+struct
+{
+ u_char type;
+ u_char code;
+ u_int16_t checksum;
+ u_char number;
+ u_char entry;
+ u_int16_t lifetime;
+} radv;
+
+void
+irdp_set (int sock)
+{
+ struct in_addr irdp_group;
+
+ switch (irdp_mode)
+ {
+ case IRDP_HOST:
+ irdp_group.s_addr = htonl (INADDR_ALLHOSTS_GROUP);
+ break;
+ case IRDP_ROUTER:
+ irdp_group.s_addr = htonl (INADDR_ALLRTRS_GROUP);
+ break;
+ case IRDP_NONE:
+ default:
+ return;
+ }
+ irdp_multicast_socket (sock, irdp_group);
+}
+
+/* Make ICMP Router Solicitation Message. */
+int
+make_solicit_packet (struct stream *s)
+{
+ int size;
+ int checksum;
+
+ stream_putc (s, ICMP_ROUTERSOLICIT); /* Type. */
+ stream_putc (s, 0); /* Code. */
+ stream_putw (s, 0); /* Checksum. */
+ stream_putl (s, 0); /* Reserved. */
+
+ /* in_cksum return network byte order value */
+ size = IRDP_SOLICIT_PACKET_SIZE;
+ checksum = in_cksum (s->data, size);
+ stream_putw_at (s, checksum, 2);
+
+ return IRDP_SOLICIT_PACKET_SIZE;
+}
+
+void
+irdp_solicit (int sock)
+{
+ struct stream *s;
+
+ s = stream_new (IRDP_SOLICIT_PACKET_SIZE);
+ make_solicit_packet (s);
+ irdp_send_solicit (sock, s, IRDP_SOLICIT_PACKET_SIZE);
+}
+
+#define ICMP_MINLEN 8
+
+/* check validity of the packet */
+int
+irdp_valid_check (char *packet, size_t size, struct sockaddr_in *from)
+{
+ struct icmp *icmp;
+
+ icmp = (struct icmp *) packet;
+
+ if (in_cksum (packet, size)) {
+ zlog_warn ("ICMP %s packet from %s: Bad checksum, silently ignored",
+ icmp_type (icmp->icmp_type),
+ inet_ntoa (from->sin_addr));
+ return -1;
+ }
+
+ if (icmp->icmp_code != 0) {
+ zlog_warn ("ICMP %s packet from %s: Bad ICMP type code, silently ignored",
+ icmp_type (icmp->icmp_type),
+ inet_ntoa (from->sin_addr));
+ return -1;
+ }
+
+ if (size < ICMP_MINLEN) {
+ zlog_warn ("ICMP %s packet from %s: IMCP message length is short",
+ icmp_type (icmp->icmp_type),
+ inet_ntoa (from->sin_addr));
+ return -1;
+ }
+ return 0;
+}
+
+int
+irdp_solicit_recv (struct stream *s, int size, struct sockaddr_in *from)
+{
+ if (irdp_valid_check (s->data, size, from)) {
+ return 1;
+ }
+ return 0;
+}
+
+void
+irdp_advert_recv (struct stream *s, int size, struct sockaddr_in *from)
+{
+ int i;
+ struct in_addr addr;
+ long pref;
+
+ if (irdp_valid_check (s->data, size, from) < 0) {
+ return;
+ }
+
+ radv.type = stream_getc (s);
+ radv.code = stream_getc (s);
+ radv.checksum = stream_getw (s);
+ radv.number = stream_getc (s);
+ radv.entry = stream_getc (s);
+ radv.lifetime = stream_getw (s);
+
+ printf ("type : %s\n", icmp_type (radv.type));
+ printf ("number: %d\n", radv.number);
+ printf ("entry: %d\n", radv.entry);
+ printf ("lifetime: %d\n", radv.entry);
+
+ for (i = 0; i < radv.number; i++)
+ {
+ addr.s_addr = stream_getl (s);
+ pref = stream_getl (s);
+ route_add (addr, ntohl (pref));
+ }
+ /* Packet size check is needed at here. */
+}
+
+void
+irdp_packet_process (char *buf, int size, struct sockaddr_in *from)
+{
+ struct ip *ip;
+ struct icmp *icmp;
+ int hlen;
+ struct stream *s = NULL;
+
+ ip = (struct ip *)buf;
+ hlen = ip->ip_hl << 2;
+
+ if (size < hlen + ICMP_MINLEN)
+ zlog_err ("ICMP relpy length is short\n");
+
+ icmp = (struct icmp *)(buf + hlen);
+
+ stream_forward (s, hlen);
+
+ switch (icmp->icmp_type)
+ {
+ case ICMP_ROUTERADVERT:
+ irdp_advert_recv (s, size - hlen, from);
+ break;
+ case ICMP_ROUTERSOLICIT:
+ irdp_solicit_recv (s, size - hlen, from);
+ break;
+ }
+}
+
+/* Make socket for ICMP Router Discovery. */
+int
+irdp_make_socket ()
+{
+ int sock;
+ struct protoent *pent;
+
+ if ((pent = getprotobyname ("icmp")) == NULL) {
+ perror ("getprotobyname");
+ exit (1);
+ }
+
+ if ((sock = socket (AF_INET, SOCK_RAW, pent->p_proto)) < 0)
+ {
+ perror ("socket");
+ exit (1);
+ }
+
+ return sock;
+}
+
+/* recv routine */
+int
+irdp_recv (int sock)
+{
+#define PACKET_BUF 4096
+ int nbytes;
+ struct sockaddr_in from;
+ int fromlen;
+ char buf[PACKET_BUF];
+
+ fromlen = sizeof (from);
+ nbytes = recvfrom (sock, (char *)buf, PACKET_BUF, 0,
+ (struct sockaddr *)&from, &fromlen);
+
+ if (nbytes < 0)
+ {
+ perror ("recvfrom");
+ exit (1);
+ }
+
+ irdp_packet_process (buf, nbytes, &from);
+
+ return 0;
+}
+
+/* irdp packet recv loop */
+void
+irdp_loop (int sock)
+{
+ while (1)
+ {
+ irdp_recv (sock);
+ }
+}
+
+DEFUN (ip_irdp,
+ ip_irdp_cmd,
+ "ip irdp",
+ IP_STR
+ "ICMP Router discovery on this interface\n")
+{
+ return CMD_SUCCESS;
+}
+
+DEFUN (ip_irdp_multicast,
+ ip_irdp_multicast_cmd,
+ "ip irdp multicast",
+ IP_STR
+ "ICMP Router discovery on this interface\n"
+ "Send IRDP advertisement to the multicast address\n")
+{
+ return CMD_SUCCESS;
+}
+
+DEFUN (ip_irdp_holdtime,
+ ip_irdp_holdtime_cmd,
+ "ip irdp holdtime <0-9000>",
+ IP_STR
+ "ICMP Router discovery on this interface\n"
+ "Set holdtime value\n"
+ "Holdtime value in seconds. Default is 1800 seconds\n")
+{
+ return CMD_SUCCESS;
+}
+
+DEFUN (ip_irdp_maxadvertinterval,
+ ip_irdp_maxadvertinterval_cmd,
+ "ip irdp maxadvertinterval (0|<4-1800>)",
+ IP_STR
+ "ICMP Router discovery on this interface\n"
+ "Set maximum time between advertisement\n"
+ "Maximum advertisement interval in seconds\n")
+{
+ return CMD_SUCCESS;
+}
+
+DEFUN (ip_irdp_minadvertinterval,
+ ip_irdp_minadvertinterval_cmd,
+ "ip irdp minadvertinterval <3-1800>",
+ IP_STR
+ "ICMP Router discovery on this interface\n"
+ "Set minimum time between advertisement\n"
+ "Minimum advertisement interval in seconds\n")
+{
+ return CMD_SUCCESS;
+}
+
+DEFUN (ip_irdp_preference,
+ ip_irdp_preference_cmd,
+ /* "ip irdp preference <-2147483648-2147483647>", */
+ "ip irdp preference <0-2147483647>",
+ IP_STR
+ "ICMP Router discovery on this interface\n"
+ "Set default preference level for this interface\n"
+ "Preference level\n")
+{
+ return CMD_SUCCESS;
+}
+
+#if 0
+DEFUN (ip_irdp_address,
+ ip_irdp_address_cmd,
+ "ip irdp address A.B.C.D",
+ IP_STR
+ "ICMP Router discovery on this interface\n"
+ "Specify IRDP address and preference to proxy-advertise\n"
+ "Set IRDP address for proxy-advertise\n")
+{
+ return CMD_SUCCESS;
+}
+#endif /* 0 */
+
+DEFUN (ip_irdp_address_preference,
+ ip_irdp_address_preference_cmd,
+ "ip irdp address A.B.C.D <0-2147483647>",
+ IP_STR
+ "ICMP Router discovery on this interface\n"
+ "Specify IRDP address and preference to proxy-advertise\n"
+ "Set IRDP address for proxy-advertise\n"
+ "Preference level\n")
+{
+ return CMD_SUCCESS;
+}
+
+void
+irdp_init ()
+{
+ install_element (INTERFACE_NODE, &ip_irdp_cmd);
+ install_element (INTERFACE_NODE, &ip_irdp_multicast_cmd);
+ install_element (INTERFACE_NODE, &ip_irdp_holdtime_cmd);
+ install_element (INTERFACE_NODE, &ip_irdp_maxadvertinterval_cmd);
+ install_element (INTERFACE_NODE, &ip_irdp_minadvertinterval_cmd);
+ install_element (INTERFACE_NODE, &ip_irdp_preference_cmd);
+ install_element (INTERFACE_NODE, &ip_irdp_address_preference_cmd);
+}