| /* 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); |
| } |