mirror of
				https://git.proxmox.com/git/mirror_frr
				synced 2025-11-04 10:07:04 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			570 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			570 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* 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);
 | 
						||
}
 |