Logo Search packages:      
Sourcecode: yaird version File versions  Download package

main.c

/* ----------------------------------------------------------------------- *
 *   
 *   Copyright 2004 H. Peter Anvin - All Rights Reserved
 *
 *   Permission is hereby granted, free of charge, to any person
 *   obtaining a copy of this software and associated documentation
 *   files (the "Software"), to deal in the Software without
 *   restriction, including without limitation the rights to use,
 *   copy, modify, merge, publish, distribute, sublicense, and/or
 *   sell copies of the Software, and to permit persons to whom
 *   the Software is furnished to do so, subject to the following
 *   conditions:
 *   
 *   The above copyright notice and this permission notice shall
 *   be included in all copies or substantial portions of the Software.
 *   
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *   OTHER DEALINGS IN THE SOFTWARE.
 *
 * ----------------------------------------------------------------------- */

/*
 * ipconfig/main.c
 */
#define _XOPEN_SOURCE   /* for POLLRDNORM */
#define _SVID_SOURCE    /* for SYS_NMLN */
#include <errno.h>
#include <poll.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/time.h>
#include <dirent.h>
#include <fcntl.h>
#include <unistd.h>     /* for getopts */

#include <net/if_arp.h>

#include "ipconfig.h"
#include "netdev.h"
#include "bootp_packet.h"
#include "bootp_proto.h"
#include "dhcp_proto.h"
#include "packet.h"

static const char sysfs_class_net[] = "/sys/class/net";
static const char *progname;
static char do_not_config;
static unsigned int default_caps = CAP_DHCP | CAP_BOOTP | CAP_RARP;
static int loop_timeout = -1;
static int configured;

struct state {
      int         state;
      int         restart_state;
      time_t            expire;
      int         retry_period;

      struct netdev     *dev;
      struct state      *next;
};

static inline const char *my_inet_ntoa(__u32 addr)
{
      struct in_addr a;

      a.s_addr = addr;

      return inet_ntoa(a);
}

static void print_device_config(struct netdev *dev)
{
      printf("IP-Config: %s complete (from %s):\n", dev->name,
            my_inet_ntoa(dev->serverid ? dev->serverid : dev->ip_server));
      printf(" address: %-16s ", my_inet_ntoa(dev->ip_addr));
      printf("broadcast: %-16s ", my_inet_ntoa(dev->ip_broadcast));
      printf("netmask: %-16s\n", my_inet_ntoa(dev->ip_netmask));
      printf(" gateway: %-16s ", my_inet_ntoa(dev->ip_gateway));
      printf("dns0     : %-16s ", my_inet_ntoa(dev->ip_nameserver[0]));
      printf("dns1   : %-16s\n", my_inet_ntoa(dev->ip_nameserver[1]));
      if (dev->hostname[0])
            printf(" host   : %-64s\n", dev->hostname);
      if (dev->dnsdomainname[0])
            printf(" domain : %-64s\n", dev->dnsdomainname);
      if (dev->nisdomainname[0])
            printf(" nisdomain: %-64s\n", dev->nisdomainname);
      printf(" rootserver: %s ", my_inet_ntoa(dev->ip_server));
      printf("rootpath: %s\n", dev->bootpath);
}

static void configure_device(struct netdev *dev)
{
      if (do_not_config)
            return;

      if (netdev_setmtu(dev))
            printf("IP-Config: failed to set MTU on %s to %u\n",
                   dev->name, dev->mtu);

      if (netdev_setaddress(dev))
            printf("IP-Config: failed to set addresses on %s\n", dev->name);
      if (netdev_setdefaultroute(dev))
            printf("IP-Config: failed to set default route on %s\n", dev->name);
}

static void dump_device_config(struct netdev *dev)
{
      char fn[40];
      FILE *f;

      snprintf(fn, sizeof(fn), "/tmp/net-%s.conf", dev->name);
      f = fopen(fn, "w");
      if (f) {
            fprintf(f, "DEVICE=%s\n", dev->name);
            fprintf(f, "IPV4ADDR=%s\n", my_inet_ntoa(dev->ip_addr));
            fprintf(f, "IPV4BROADCAST=%s\n", my_inet_ntoa(dev->ip_broadcast));
            fprintf(f, "IPV4NETMASK=%s\n", my_inet_ntoa(dev->ip_netmask));
            fprintf(f, "IPV4GATEWAY=%s\n", my_inet_ntoa(dev->ip_gateway));
            fprintf(f, "IPV4DNS0=%s\n", my_inet_ntoa(dev->ip_nameserver[0]));
            fprintf(f, "IPV4DNS1=%s\n", my_inet_ntoa(dev->ip_nameserver[1]));
            fprintf(f, "HOSTNAME=%s\n", dev->hostname);
            fprintf(f, "DNSDOMAIN=%s\n", dev->dnsdomainname);
            fprintf(f, "NISDOMAIN=%s\n", dev->nisdomainname);
            fprintf(f, "ROOTSERVER=%s\n", my_inet_ntoa(dev->ip_server));
            fprintf(f, "ROOTPATH=%s\n", dev->bootpath);
            fclose(f);
      }
}

static __u32 inet_class_netmask(__u32 ip)
{
      ip = ntohl(ip);
      if (IN_CLASSA(ip))
            return htonl(IN_CLASSA_NET);
      if (IN_CLASSB(ip))
            return htonl(IN_CLASSB_NET);
      if (IN_CLASSC(ip))
            return htonl(IN_CLASSC_NET);
      return INADDR_ANY;
}

static void postprocess_device(struct netdev *dev)
{
      if (dev->ip_netmask == INADDR_ANY) {
            dev->ip_netmask = inet_class_netmask(dev->ip_addr);
            printf("IP-Config: %s guessed netmask %s\n",
                  dev->name, my_inet_ntoa(dev->ip_netmask));
      }
      if (dev->ip_broadcast == INADDR_ANY) {
            dev->ip_broadcast = (dev->ip_addr & dev->ip_netmask) | ~dev->ip_netmask;
            printf("IP-Config: %s guessed broadcast address %s\n",
                  dev->name, my_inet_ntoa(dev->ip_broadcast));
      }
      if (dev->ip_nameserver[0] == INADDR_ANY) {
            dev->ip_nameserver[0] = dev->ip_server;
            printf("IP-Config: %s guessed nameserver address %s\n",
                  dev->name, my_inet_ntoa(dev->ip_nameserver[0]));
      }
}

static void complete_device(struct netdev *dev)
{
      postprocess_device(dev);
      configure_device(dev);
      dump_device_config(dev);
      print_device_config(dev);

      ++configured;

      dev->next = ifaces;
      ifaces = dev;
}

static int process_receive_event(struct state *s, time_t now)
{
      int handled = 1;

      switch (s->state) {
      case DEVST_BOOTP:
            s->restart_state = DEVST_BOOTP;
            switch (bootp_recv_reply(s->dev)) {
            case -1:
                  s->state = DEVST_ERROR;
                  break;
            case 1:
                  s->state = DEVST_COMPLETE;
                  DEBUG(("\n   bootp reply\n"));
                  break;
            }
            break;

      case DEVST_DHCPDISC:
            s->restart_state = DEVST_DHCPDISC;
            switch (dhcp_recv_offer(s->dev)) {
            case -1:
                  s->state = DEVST_ERROR;
                  break;
            case 1: /* Offer received */
                  s->state = DEVST_DHCPREQ;
                  dhcp_send_request(s->dev);
                  break;
            }
            break;

      case DEVST_DHCPREQ:
            s->restart_state = DEVST_DHCPDISC;
            switch (dhcp_recv_ack(s->dev)) {
            case -1: /* error */
                  s->state = DEVST_ERROR;
                  break;
            case 1:     /* ACK received */
                  s->state = DEVST_COMPLETE;
                  break;
            case 2:     /* NAK received */
                  s->state = DEVST_DHCPDISC;
                  break;
            }
            break;
      }

      switch (s->state) {
      case DEVST_COMPLETE:
            complete_device(s->dev);
            break;

      case DEVST_ERROR:
            /* error occurred, try again in 10 seconds */
            s->expire = now + 10;
      default:
            DEBUG(("\n"));
            handled = 0;
            break;
      }

      return handled;
}

static void process_timeout_event(struct state *s, time_t now)
{
      int ret = 0;

      /*
       * Is the link up?  If not, try again in 1 second.
       */
      if (!netdev_running(s->dev)) {
            s->expire = now + 1;
            s->state = s->restart_state;
            return;
      }

      /*
       * If we had an error, restore a sane state to
       * restart from.
       */
      if (s->state == DEVST_ERROR)
            s->state = s->restart_state;

      /*
       * Now send a packet depending on our state.
       */
      switch (s->state) {
      case DEVST_BOOTP:
            ret = bootp_send_request(s->dev);
            s->restart_state = DEVST_BOOTP;
            break;

      case DEVST_DHCPDISC:
            ret = dhcp_send_discover(s->dev);
            s->restart_state = DEVST_DHCPDISC;
            break;

      case DEVST_DHCPREQ:
            ret = dhcp_send_request(s->dev);
            s->restart_state = DEVST_DHCPDISC;
            break;
      }

      if (ret == -1) {
            s->state = DEVST_ERROR;
            s->expire = now + 10;
      } else {
            s->expire = now + s->retry_period;

            s->retry_period *= 2;
            if (s->retry_period > 60)
                  s->retry_period = 60;
      }
}

static struct state *slist;
struct netdev *ifaces;

static int do_pkt_recv(int pkt_fd, time_t now)
{
      int ifindex, ret;
      struct state *s;

      ret = packet_peek(&ifindex);
      if (ret < 0)
            goto bail;

      for (s = slist; s; s = s->next) {
            if (s->dev->ifindex == ifindex) {
                  ret |= process_receive_event(s, now);
                  break;
            }
      }

 bail:
      return ret;
}

static int loop(void)
{
#define NR_FDS    1
      struct pollfd fds[NR_FDS];
      struct state *s;
      int pkt_fd;
      int nr = 0;
      struct timeval now, prev;
      time_t start;

      pkt_fd = packet_open();
      if (pkt_fd == -1) {
            perror("packet_open");
            return -1;
      }

      fds[0].fd = pkt_fd;
      fds[0].events = POLLRDNORM;

      gettimeofday(&now, NULL);
      start = now.tv_sec;
      while (1) {
            int timeout = 60;
            int pending = 0;
            int timeout_ms;
            int x;

            for (s = slist; s; s = s->next) {
                  /*
                   * pick the first working interface,
                   * bootpath or not.  To get all interfaces
                   * up, use 'continue'.
                   */
                  if (s->state == DEVST_COMPLETE)
                        goto bail;

                  pending++;

                  if (s->expire - now.tv_sec <= 0)
                        process_timeout_event(s, now.tv_sec);

                  if (timeout > s->expire - now.tv_sec)
                        timeout = s->expire - now.tv_sec;
            }

            if (pending == 0)
                  break;

            timeout_ms = timeout * 1000;

            for (x = 0; x < 2; x++) {
                  int delta_ms;

                  if (timeout_ms <= 0)
                        timeout_ms = 100;

                  nr = poll(fds, NR_FDS, timeout_ms);
                  prev = now;
                  gettimeofday(&now, NULL);

                  if ((fds[0].revents & POLLRDNORM)) {
                        nr = do_pkt_recv(pkt_fd, now.tv_sec);
                        if (nr == 1)
                              break;
                        else if (nr == 0)
                              packet_discard();
                  }

                  if (loop_timeout >= 0 &&
                      now.tv_sec - start >= loop_timeout) {
                        printf("IP-Config: no response after %d "
                               "secs - giving up\n", loop_timeout);
                        goto bail;
                  }

                  delta_ms = (now.tv_sec - prev.tv_sec) * 1000;
                  delta_ms += (now.tv_usec - prev.tv_usec) / 1000;

                  DEBUG(("Delta: %d ms\n", delta_ms));

                  timeout_ms -= delta_ms;
            }
      }
 bail:
      packet_close();

      return 0;
}

static int add_one_dev(struct netdev *dev)
{
      struct state *state;

      state = malloc(sizeof(struct state));
      if (!state)
            return -1;

      state->dev = dev;
      state->expire = time(NULL);
      state->retry_period = 1;

      /*
       * Select the state that we start from.
       */
      if (dev->caps & CAP_DHCP && dev->ip_addr == INADDR_ANY) {
            state->restart_state = state->state = DEVST_DHCPDISC;
      } else if (dev->caps & CAP_DHCP) {
            state->restart_state = state->state = DEVST_DHCPREQ;
      } else if (dev->caps & CAP_BOOTP) {
            state->restart_state = state->state = DEVST_BOOTP;
      }

      state->next = slist;
      slist = state;

      return 0;
}

static void parse_addr(__u32 *addr, const char *ip)
{
      struct in_addr in;
      if (inet_aton(ip, &in) == 0) {
            fprintf(stderr, "%s: can't parse IP address '%s'\n",
                  progname, ip);
            exit(1);
      }
      *addr = in.s_addr;
}

static unsigned int parse_proto(const char *ip)
{
      unsigned int caps = 0;

      if (*ip == '\0' || strcmp(ip, "on") == 0 || strcmp(ip, "any") == 0)
            caps = CAP_BOOTP | CAP_DHCP | CAP_RARP;
      else if (strcmp(ip, "both") == 0)
            caps = CAP_BOOTP | CAP_RARP;
      else if (strcmp(ip, "dhcp") == 0)
            caps = CAP_BOOTP | CAP_DHCP;
      else if (strcmp(ip, "bootp") == 0)
            caps = CAP_BOOTP;
      else if (strcmp(ip, "rarp") == 0)
            caps = CAP_RARP;
      else if (strcmp(ip, "none") == 0 || strcmp(ip, "static") == 0 || strcmp(ip, "off") == 0)
            goto bail;
      else {
            fprintf(stderr, "%s: invalid protocol '%s'\n",
                  progname, ip);
            exit(1);
      }
 bail:
      return caps;
}

static int add_all_devices(struct netdev *template);

static int parse_device(struct netdev *dev, const char *ip)
{
      char *cp;
      int i, opt;

      if (strncmp(ip, "ip=", 3) == 0) {
            ip += 3;
      }
      else if (strncmp(ip, "nfsaddrs=", 9) == 0) {
            ip += 9;
      }

      if (strchr(ip, ':') == NULL) {
            dev->name = ip;
            goto done;
      }

      for (i = opt = 0; ip && *ip; ip = cp, opt++) {
            if ((cp = strchr(ip, ':'))) {
                  *cp++ = '\0';
            }
            if (opt > 6) {
                  fprintf(stderr, "%s: too many options for %s\n",
                        progname, dev->name);
                  exit(1);
            }

            if (*ip == '\0')
                  continue;
            DEBUG(("IP-Config: opt #%d: '%s'\n", opt, ip));
            switch (opt) {
            case 0:
                  parse_addr(&dev->ip_addr, ip);
                  dev->caps = 0;
                  break;
            case 1:
                  parse_addr(&dev->ip_server, ip);
                  break;
            case 2:
                  parse_addr(&dev->ip_gateway, ip);
                  break;
            case 3:
                  parse_addr(&dev->ip_netmask, ip);
                  break;
            case 4:
                  strncpy(dev->hostname, ip, SYS_NMLN - 1);
                  dev->hostname[SYS_NMLN - 1] = '\0';
                  break;
            case 5:
                  dev->name = ip;
                  break;
            case 6:
                  dev->caps = parse_proto(ip);
                  break;
            }
      }
 done:
      if (dev->name == NULL ||
          dev->name[0] == '\0' ||
          strcmp(dev->name, "all") == 0) {
            add_all_devices(dev);
            return 0;
      }
      return 1;
}

static void bringup_device(struct netdev *dev)
{
      if (netdev_up(dev) == 0) {
            if (dev->caps) {
                  add_one_dev(dev);
            } else {
                  complete_device(dev);
            }
      }
}

static void bringup_one_dev(struct netdev *template, struct netdev *dev)
{
            if (template->ip_addr != INADDR_NONE)
                  dev->ip_addr = template->ip_addr;
            if (template->ip_server != INADDR_NONE)
                  dev->ip_server = template->ip_server;
            if (template->ip_gateway != INADDR_NONE)
                  dev->ip_gateway = template->ip_gateway;
            if (template->ip_netmask != INADDR_NONE)
                  dev->ip_netmask = template->ip_netmask;
            if (template->ip_nameserver[0] != INADDR_NONE)
                  dev->ip_nameserver[0] = template->ip_nameserver[0];
            if (template->ip_nameserver[1] != INADDR_NONE)
                  dev->ip_nameserver[1] = template->ip_nameserver[1];
            if (template->hostname[0] != '\0')
                  strcpy(dev->hostname, template->hostname);
            dev->caps &= template->caps;

            bringup_device(dev);
}

static struct netdev *add_device(const char *info)
{
      struct netdev *dev;
      int i;

      dev = malloc(sizeof(struct netdev));
      if (dev == NULL) {
            fprintf(stderr, "%s: out of memory\n", progname);
            exit(1);
      }

      memset(dev, 0, sizeof(struct netdev));
      dev->caps = default_caps;

      if (parse_device(dev, info) == 0)
            goto bail;

      if (netdev_init_if(dev) == -1)
            goto bail;

      if (bootp_init_if(dev) == -1)
            goto bail;

      printf("IP-Config: %s hardware address", dev->name);
      for (i = 0; i < dev->hwlen; i++)
            printf("%c%02x", i == 0 ? ' ' : ':', dev->hwaddr[i]);
      printf(" mtu %d%s%s\n", dev->mtu,
            dev->caps & CAP_DHCP  ? " DHCP"  :
            dev->caps & CAP_BOOTP ? " BOOTP" : "",
             dev->caps & CAP_RARP  ? " RARP"  : "");
      return dev;
 bail:
      free(dev);
      return NULL;
}

static int add_all_devices(struct netdev *template)
{
      DIR *d;
      struct dirent *de;
      struct netdev *dev;
      char t[PATH_MAX], p[255];
      int i, fd;
      unsigned long flags;

      d = opendir(sysfs_class_net);
      if (!d)
            return 0;

      while((de = readdir(d)) != NULL ) {
            /* This excludes devices beginning with dots or "dummy", as well as . or .. */
            if ( de->d_name[0] == '.' || !strcmp(de->d_name, "..") )
                  continue;
            i = snprintf(t, PATH_MAX-1, "%s/%s/flags", sysfs_class_net, de->d_name);
            if (i < 0 || i >= PATH_MAX-1)
                  continue;
            t[i] = '\0';
            fd = open(t, O_RDONLY);
            if (fd < 0) {
                  perror(t);
                  continue;
            }
            i = read(fd, &p, sizeof(p) - 1);
            close(fd);
            if (i < 0) {
                  perror(t);
                  continue;
            }
            p[i] = '\0';
            flags = strtoul(p, NULL, 0);
            /* Heuristic for if this is a reasonable boot interface.
               This is the same
               logic the in-kernel ipconfig uses... */
            if ( !(flags & IFF_LOOPBACK) &&
                 (flags & (IFF_BROADCAST|IFF_POINTOPOINT)) )
            {
                  if ( !(dev = add_device(de->d_name)) )
                        continue;
                  bringup_one_dev(template, dev);
            }
      }
      closedir(d);
      return 1;
}

static int check_autoconfig(void)
{
      int ndev = 0, nauto = 0;
      struct state *s;

      for (s = slist; s; s = s->next) {
            ndev++;
            if (s->dev->caps)
                  nauto++;
      }

      if (ndev == 0) {
            if (configured == 0) {
                  fprintf(stderr, "%s: no devices to configure\n",
                        progname);
                  exit(1);
            }
      }

      return nauto;
}

int main(int argc, char *argv[])
      __attribute__ ((weak, alias ("ipconfig_main")));

int ipconfig_main(int argc, char *argv[])
{
      struct netdev *dev;
      struct timeval now;
      int c, port;

      progname = argv[0];

      gettimeofday(&now, NULL);
      srand48(now.tv_usec ^ (now.tv_sec << 24));

      do {
            c = getopt(argc, argv, "c:d:np:t:");
            if (c == EOF)
                  break;

            switch (c) {
            case 'c':
                  default_caps = parse_proto(optarg);
                  break;
            case 'p':
                  port = atoi(optarg);
                  if (port <= 0 || port > USHRT_MAX) {
                        fprintf(stderr,
                              "%s: invalid port number %d\n",
                              progname, port);
                        exit(1);
                  }
                  cfg_local_port = port;
                  cfg_remote_port = cfg_local_port - 1;
                  break;
            case 't':
                  loop_timeout = atoi(optarg);
                  if (loop_timeout < 0) {
                        fprintf(stderr,
                              "%s: invalid timeout %d\n",
                              progname, loop_timeout);
                        exit(1);
                  }
                  break;
            case 'n':
                  do_not_config = 1;
                  break;
            case 'd':
                  dev = add_device(optarg);
                  if (dev)
                        bringup_device(dev);
                  break;
            case '?':
                  fprintf(stderr, "%s: invalid option -%c\n",
                        progname, optopt);
                  exit(1);
            }
      } while (1);

      for (c = optind; c < argc; c++) {
            dev = add_device(argv[c]);
            if (dev)
                  bringup_device(dev);
      }

      if (check_autoconfig()) {
            if (cfg_local_port != LOCAL_PORT) {
                  printf("IP-Config: binding source port to %d, "
                         "dest to %d\n",
                         cfg_local_port,
                         cfg_remote_port);
            }
            loop();
      }

      return 0;
}

Generated by  Doxygen 1.6.0   Back to index