diff options
Diffstat (limited to 'zebra/irdp.c')
-rw-r--r-- | zebra/irdp.c | 569 |
1 files changed, 569 insertions, 0 deletions
diff --git a/zebra/irdp.c b/zebra/irdp.c new file mode 100644 index 000000000..1b3bf2323 --- /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); +} |