blob: 1b3bf23232c98c20ac255284e5932cde0f858fbb [file] [log] [blame]
/* 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);
}