Logo Search packages:      
Sourcecode: laptop-net version File versions  Download package

arp-discovery.c

/* -*-C-*-

$Id: arp-discovery.c,v 1.5 2003/04/17 03:23:46 cph Exp $

Copyright 2002,2003 Massachusetts Institute of Technology

This file is part of laptop-net.

Laptop-net 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 of the License, or (at your
option) any later version.

Laptop-net 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 laptop-net; if not, write to the Free Software Foundation,
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

*/

#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <setjmp.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <libnet.h>
#include <pcap.h>
#include "parse-ipmap.h"
#include "if-params.h"

#ifndef N_RETRIES
#  define N_RETRIES 8
#endif

#define PACKET_LENGTH                                       \
  (LIBNET_ETH_H                                             \
   + LIBNET_ARP_H                                     \
   + (ETHER_ADDR_LEN * 2)                                   \
   + ((sizeof (struct in_addr)) * 2))

static u_char enet_bcast [ETHER_ADDR_LEN]
  = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
static u_char enet_nulls [ETHER_ADDR_LEN] = {0, 0, 0, 0, 0, 0};
static u_char enet_local [ETHER_ADDR_LEN];
static libnet_t * libnet_context;
static sigjmp_buf abort_read_arp_reply;

struct arp_reply
{
  int valid_p;
  struct in_addr ip_address;
  char mac_address [18];
};

static int initialize_interface (const char *, struct if_params *);
static struct ip_map * find_ip_map_entry
  (struct in_addr *, unsigned int, struct ip_map *);
static pcap_t * initialize_libpcap (const char *);
static int initialize_libnet (const char *);
static int send_arp_requests (unsigned int, struct ip_map *);
static int send_arp_request (struct in_addr *, struct in_addr *);
static int read_arp_reply (pcap_t *, struct arp_reply *);
static void handle_alarm (int);
static void read_arp_reply_1
  (u_char *, const struct pcap_pkthdr *, const u_char *);

int
main (int argc, const char ** argv)
{
  const char * if_name = 0;
  const char * ip_map_filename;
  struct ip_map * ip_map_entries;
  int n_ip_map_entries;
  unsigned int n_addresses;
  struct if_params original_if_params;
  int restore_if_params = 0;
  int result_code = 1;
  pcap_t * pd;
  unsigned int n_retries = N_RETRIES;

  if (argc != 3)
    {
      fprintf (stderr, "usage: %s INTERFACE FILENAME\n", (argv[0]));
      goto finished;
    }
  if_name = (argv[1]);
  ip_map_filename = (argv[2]);

  /* If file doesn't exist, act as though it did but was empty.  */
  if ((access (ip_map_filename, R_OK)) < 0)
    {
      if ((errno == ENOENT) || (errno == ENOTDIR))
      {
        fprintf (stdout, "@NO-CHOICES@\n");
        goto successful;
      }
      perror (ip_map_filename);
      goto finished;
    }

  n_ip_map_entries = (parse_ip_map (ip_map_filename, (&ip_map_entries)));
  if (n_ip_map_entries < 0)
    /* Error message was printed by parse_ip_map.  */
    goto finished;
  if (n_ip_map_entries == 0)
    {
      fprintf (stdout, "@NO-CHOICES@\n");
      goto successful;
    }
  {
    struct ip_map * scan = ip_map_entries;
    struct ip_map * end = (scan + n_ip_map_entries);
    n_addresses = 0;
    while (scan < end)
      n_addresses += ((scan++) -> n_addresses);
  }

  if ((initialize_interface (if_name, (&original_if_params))) < 0)
    goto finished;
  restore_if_params = 1;
  pd = (initialize_libpcap (if_name));
  if (pd == 0)
    goto finished;
  if ((initialize_libnet (if_name)) < 0)
    goto finished;

  while (n_retries > 0)
    {
      unsigned int i = 0;

      if ((send_arp_requests (n_ip_map_entries, ip_map_entries)) < 0)
      goto finished;
      while (i < (2 * n_addresses))
      {
        struct arp_reply reply;
        int result = (read_arp_reply (pd, (&reply)));
        if (result < 0) /* read timed out */
          goto retry_done;
        if (result > 0)
          {
            struct ip_map * entry
            = (find_ip_map_entry ((& (reply . ip_address)),
                              n_ip_map_entries,
                              ip_map_entries));
            if (entry != 0)
            {
              fprintf (stdout, "%s\n", (entry -> key));
              goto successful;
            }
          }
        i += 1;
      }
      sleep (1);
    retry_done:
      n_retries -= 1;
    }
  fprintf (stdout, "@NO-RESPONSES@\n");
 successful:
  result_code = 0;
 finished:
  if (restore_if_params)
    write_interface_configuration (if_name, (&original_if_params));
  return (result_code);
}

#define STORE_IP(string, field, addr_flag)                        \
  ((memset ((& (ifp . field)), 0, (sizeof (ifp . field)))),       \
   ((ifp . field . sin_family) = AF_INET),                        \
   ((inet_aton ((string), (& (ifp . field . sin_addr))))          \
    ? (((ifp . addr_flags) |= (addr_flag)), 1)                    \
    : (((ifp . addr_flags) &=~ (addr_flag)), 0)))

static int
initialize_interface (const char * if_name, struct if_params * original_ifp)
{
  struct if_params ifp;

  if ((read_interface_configuration (if_name, (&ifp))) < 0)
    return (-1);
  (*original_ifp) = ifp;
  if ((((ifp . flags) & IFF_UP) != 0)
      && (((ifp . flags) & IFF_RUNNING) != 0)
      && (IFP_ALL_ADDRESSES_VALID (&ifp)))
    return (0);
  (ifp . flags) |= (IFF_UP | IFF_RUNNING);
  if (!IFP_ALL_ADDRESSES_VALID (&ifp))
    {
      if (! ((STORE_IP ("192.168.254.254", addr, IFP_VALID_ADDR))
           && (STORE_IP ("192.168.254.0", dstaddr, IFP_VALID_DSTADDR))
           && (STORE_IP ("192.168.254.255", broadaddr, IFP_VALID_BROADADDR))
           && (STORE_IP ("255.255.255.0", netmask, IFP_VALID_NETMASK))))
      {
        fprintf (stderr, "Unable to set network interface addresses.\n");
        return (-1);
      }
    }
  return (write_interface_configuration (if_name, (&ifp)));
}

static struct ip_map *
find_ip_map_entry (struct in_addr * address,
               unsigned int n_ip_map_entries,
               struct ip_map * ip_map_entries)
{
  struct ip_map * scan_map = ip_map_entries;
  struct ip_map * end_map = (scan_map + n_ip_map_entries);
  while (scan_map < end_map)
    {
      struct in_addr * scan_addr = (scan_map -> addresses);
      struct in_addr * end_addr = (scan_addr + (scan_map -> n_addresses));
      while (scan_addr < end_addr)
      {
        if ((scan_addr -> s_addr) == (address -> s_addr))
          return (scan_map);
        scan_addr += 1;
      }
      scan_map += 1;
    }
  return (0);
}

static pcap_t *
initialize_libpcap (const char * if_name)
{
  char error_buffer [PCAP_ERRBUF_SIZE];
  pcap_t * pd
    = (pcap_open_live (((char *) if_name),
                   PACKET_LENGTH,
                   0,
                   20,
                   error_buffer));
  if (pd == 0)
    {
      fprintf (stderr, "pcap_open_live failed: %s\n", error_buffer);
      return (0);
    }
  return (pd);
}

static int
initialize_libnet (const char * if_name)
{
  {
    char error_buffer [LIBNET_ERRBUF_SIZE];
    libnet_context
      = (libnet_init (LIBNET_LINK, ((char *) if_name), error_buffer));
    if (libnet_context == 0)
      {
      fprintf (stderr, "libnet_init failed: %s\n", error_buffer);
      return (-1);
      }
  }
  {
    struct libnet_ether_addr * ea = (libnet_get_hwaddr (libnet_context));
    if (ea == 0)
      {
      fprintf (stderr, "libnet_get_hwaddr failed: %s\n",
             (libnet_geterror (libnet_context)));
      return (-1);
      }
    memcpy (enet_local, (ea -> ether_addr_octet), (sizeof (enet_local)));
#ifdef DEBUGGING_OUTPUT
    {
      unsigned int i = 0;
      while (i < ETHER_ADDR_LEN)
      {
        if (i > 0)
          fprintf (stderr, " ");
        fprintf (stderr, "%02x", (enet_local[i]));
        i += 1;
      }
      fprintf (stderr, "\n");
      fflush (stderr);
    }
#endif /* DEBUGGING_OUTPUT */
  }
  return (0);
}

static int
send_arp_requests (unsigned int n_ip_map_entries,
               struct ip_map * ip_map_entries)
{
  struct ip_map * scan_map = ip_map_entries;
  struct ip_map * end_map = (scan_map + n_ip_map_entries);
  struct in_addr null_addr;
  struct in_addr * scan_addr;
  struct in_addr * end_addr;

  inet_aton ("0.0.0.0", (&null_addr));
  while (scan_map < end_map)
    {
      scan_addr = (scan_map -> addresses);
      end_addr = (scan_addr + (scan_map -> n_addresses));
      while (scan_addr < end_addr)
      {
        if ((send_arp_request ((&null_addr), scan_addr)) < 0)
          return (-1);
        scan_addr += 1;
      }
      scan_map += 1;
    }
  return (0);
}

static int
send_arp_request (struct in_addr * source, struct in_addr * target)
{
  libnet_clear_packet (libnet_context);
  if ((libnet_build_arp (ARPHRD_ETHER, ETHERTYPE_IP,
                   ETHER_ADDR_LEN, (sizeof (struct in_addr)),
                   ARPOP_REQUEST,
                   enet_local, ((u_char *) (& (source -> s_addr))),
                   /* This makes it a DHCP ARP request.  */
                   enet_nulls, ((u_char *) (& (target -> s_addr))),
                   0, 0,
                   libnet_context, 0))
      < 0)
    {
      fprintf (stderr, "Can't build ARP header: %s\n",
             (libnet_geterror (libnet_context)));
      return (-1);
    }
  if ((libnet_build_ethernet (enet_bcast, enet_local, ETHERTYPE_ARP,
                        0, 0, libnet_context, 0))
      < 0)
    {
      fprintf (stderr, "Can't build ethernet header: %s\n",
             (libnet_geterror (libnet_context)));
      return (-1);
    }
  if ((libnet_write (libnet_context)) < 0)
    {
      fprintf (stderr, "Error transmitting ARP packet: %s\n",
             (libnet_geterror (libnet_context)));
      return (-1);
    }
  return (0);
}

/* Use alarm timer here to deal with fact that pcap_dispatch might not
   return.  */

static int
read_arp_reply (pcap_t * pd, struct arp_reply * reply)
{
  if (sigsetjmp (abort_read_arp_reply, 1))
    return (-1);
  signal (SIGALRM, handle_alarm);
  alarm (1);
  (void) (pcap_dispatch (pd, 0, read_arp_reply_1, ((u_char *) reply)));
  signal (SIGALRM, SIG_IGN);
  alarm (0);
  return (reply -> valid_p);
}

static void
handle_alarm (int signo)
{
  siglongjmp (abort_read_arp_reply, 1);
}

static void
read_arp_reply_1 (u_char * r,
              const struct pcap_pkthdr * pc,
              const u_char * packet)
{
  struct arp_reply * reply = ((struct arp_reply *) r);
  struct libnet_ethernet_hdr * p = ((struct libnet_ethernet_hdr *) packet);
  struct libnet_arp_hdr * a
    = ((struct libnet_arp_hdr *) (packet + LIBNET_ETH_H));

#ifdef DEBUGGING_OUTPUT
  if ((ntohs (p -> ether_type)) == ETHERTYPE_ARP)
    {
      unsigned int i = 0;
      unsigned int m16 = 0;
      while (i < PACKET_LENGTH)
      {
        fprintf (stderr, ((m16 == 0) ? "\n" : " "));
        fprintf (stderr, "%.2x", (packet[i]));
        i += 1;
        m16 += 1;
        m16 %= 16;
      }
      fprintf (stderr, "\n");
      fflush (stderr);
    }
#endif /* DEBUGGING_OUTPUT */
  if (((ntohs (p -> ether_type)) == ETHERTYPE_ARP)
      && ((ntohs (a -> ar_op)) == ARPOP_REPLY)
      && ((ntohs (a -> ar_hrd)) == ARPHRD_ETHER))
    {
      const u_char * sha = (((const u_char *) a) + LIBNET_ARP_H);
      (reply -> valid_p) = 1;
      ((reply -> ip_address) . s_addr)
      = (* ((in_addr_t *) (sha + (a -> ar_hln))));
      sprintf ((reply -> mac_address),
             "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
             (sha[0]),
             (sha[1]),
             (sha[2]),
             (sha[3]),
             (sha[4]),
             (sha[5]));
#ifdef DEBUGGING_OUTPUT
      fprintf (stderr, "%s %02x %02x %02x %02x\n",
             (reply -> mac_address),
             ((sha + (a -> ar_hln)) [0]),
             ((sha + (a -> ar_hln)) [1]),
             ((sha + (a -> ar_hln)) [2]),
             ((sha + (a -> ar_hln)) [3]));
      fflush (stderr);
#endif /* DEBUGGING_OUTPUT */
    }
  else
    (reply -> valid_p) = 0;
}

Generated by  Doxygen 1.6.0   Back to index