bug-inetutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[bug-inetutils] route


From: Debarshi Ray
Subject: [bug-inetutils] route
Date: Tue, 19 Aug 2008 09:55:10 +0530

Here is a proof-of-concept for implementing route for Inetutils.
Unlike some route implementations for GNU/Linux, this does not indulge
in parsing /proc/net/... or using SIOCxxxx ioctl commands. Instead I
have used the PF_NETLINK socket interface provided by the Linux
kernel. I also plan to make this work with the *BSD kernels, since
atleast FreeBSD and NetBSD seem to have a similar socket driven
interface.

Please note that all uses of malloc and realloc will be replaced by
xmalloc and xrealloc once the code moves into Inetutils.

#include <errno.h>
#include <error.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h> // malloc, realloc, free
#include <string.h>
#include <unistd.h>

#include <asm/types.h>
#include <net/if.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
^L
struct _msghdr_t
{
  struct nlmsghdr nlmsghdr;
  struct rtmsg rtmsg;
};
typedef struct _msghdr_t msghdr_t;

struct _rtinfo_t
{
  char dest[16];
  char gateway[16];
  char iface[IFNAMSIZ];
  int metrics;
  int ref;
  int use;
};
typedef struct _rtinfo_t rtinfo_t;

uint32_t seqn = 0;

void print_header (void);
void print_route (const rtinfo_t *const rtinfo);
void rtinfo_init (rtinfo_t *const rtinfo);
void linux_route (void);
size_t linux_recvnl (int sockfd, void **buffer);
size_t linux_sendnl (int sockfd);
void linux_parsenl (void *buffer, size_t size);
size_t recvbuf (int sockfd, void **buffer, size_t *size);

int
main (int argc, char *argv[])
{
  print_header ();
  linux_route ();
  return 0;
}

void
print_header (void)
{
  puts ("Kernel IP routing table");
  printf ("%-15s %-15s %-6s %-6s %-6s %-15s\n",
          "Destination",
          "Gateway",
          "Metric",
          "Ref",
          "Use",
          "Iface");
}

void
print_route (const rtinfo_t *const rtinfo)
{
  printf ("%-15s %-15s %-6d %-6d %-6d %-15s\n",
          rtinfo->dest,
          rtinfo->gateway,
          rtinfo->metrics,
          rtinfo->ref,
          rtinfo->use,
          rtinfo->iface);
}

void rtinfo_init (rtinfo_t *const rtinfo)
{
  strcpy (rtinfo->dest, "0.0.0.0");
  strcpy (rtinfo->gateway, "0.0.0.0");
  strcpy (rtinfo->iface, "");
  rtinfo->metrics = 0;
  rtinfo->ref = 0;
  rtinfo->use = 0;
}

void
linux_route (void)
{
  int sockfd;
  size_t nread;
  void *buffer = NULL;

  sockfd = socket (PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
  if (sockfd == -1)
    error (EXIT_FAILURE, errno, "socket");

  linux_sendnl (sockfd);
  nread = linux_recvnl (sockfd, &buffer);
  linux_parsenl (buffer, nread);

  close (sockfd);
  free (buffer);
}

void
linux_parsenl (void *buffer, size_t nread)
{
  unsigned int size;
  msghdr_t *msghdr;
  rtinfo_t rtinfo;
  struct rtattr *attr;

  for (msghdr = (msghdr_t *) buffer;
       NLMSG_OK (&msghdr->nlmsghdr, nread) != 0;
       msghdr = (msghdr_t *) NLMSG_NEXT (&msghdr->nlmsghdr, nread))
    {
      if ((msghdr->rtmsg.rtm_family != AF_INET)
          || (msghdr->rtmsg.rtm_table != RT_TABLE_MAIN))
        continue;

      rtinfo_init (&rtinfo);

      attr = (struct rtattr *) RTM_RTA (&msghdr->rtmsg);
      size = RTM_PAYLOAD (&msghdr->nlmsghdr);
      while (RTA_OK (attr, size))
        {
          switch(attr->rta_type)
            {
            case RTA_DST:
              inet_ntop(AF_INET, RTA_DATA (attr), rtinfo.dest,
                        sizeof (rtinfo.dest));
              break;

            case RTA_GATEWAY:
              inet_ntop(AF_INET, RTA_DATA (attr), rtinfo.gateway,
                        sizeof (rtinfo.gateway));
              break;

            case RTA_OIF:
              if_indextoname (*(int *) RTA_DATA (attr), rtinfo.iface);
              break;

            case RTA_METRICS:
              rtinfo.metrics = *(int *) RTA_DATA (attr);
              break;

            default:
              break;
            }

          attr = RTA_NEXT (attr, size);
        }

      print_route (&rtinfo);
    }
}

size_t
linux_recvnl (int sockfd, void **buffer)
{
  msghdr_t *msghdr;
  size_t nread = 0;
  size_t size = 0;

  if (*buffer != NULL)
    return -1;

  nread = recvbuf (sockfd, buffer, &size);

  size = nread;
  for (msghdr = (msghdr_t *) *buffer; NLMSG_OK (&msghdr->nlmsghdr, size);
       msghdr = (msghdr_t *) NLMSG_NEXT (&msghdr->nlmsghdr, size))
    {
      if (msghdr->nlmsghdr.nlmsg_type == NLMSG_ERROR)
        error (EXIT_FAILURE, 0,
               "netlink message truncated and can not be parsed");

      if (msghdr->nlmsghdr.nlmsg_type == NLMSG_DONE)
        break;

      if ((msghdr->nlmsghdr.nlmsg_flags & NLM_F_MULTI) == 0)
        break;
    }

  return nread;
}

size_t
linux_sendnl (int sockfd)
{
  size_t nsent;
  msghdr_t msghdr;

  memset ((void *) &msghdr, 0, sizeof (msghdr));
  msghdr.nlmsghdr.nlmsg_len = NLMSG_LENGTH (sizeof (msghdr.rtmsg));
  msghdr.nlmsghdr.nlmsg_type = RTM_GETROUTE;
  msghdr.nlmsghdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
  msghdr.nlmsghdr.nlmsg_seq = seqn;
  msghdr.nlmsghdr.nlmsg_pid = getpid();

  nsent = send (sockfd, (void *) &msghdr, sizeof (msghdr), 0);
  if (nsent == -1)
    error (EXIT_FAILURE, errno, "send");

  seqn++;
  return nsent;
}

size_t
recvbuf (int sockfd, void **buffer, size_t *size)
{
  size_t count = 0;
  size_t nread;

  if (*buffer == NULL)
    *size = BUFSIZ;

  for (;;)
    {
      *buffer = realloc (*buffer, *size);
      nread = recv (sockfd, *buffer + count, BUFSIZ, 0);

      if (nread == -1)
        error (EXIT_FAILURE, errno, "recv");

      count += nread;

      if (nread < BUFSIZ)
        break;
      else
        *size += BUFSIZ;
    }

  return count;
}


Comments?

Happy hacking,
Debarshi




reply via email to

[Prev in Thread] Current Thread [Next in Thread]