/*
 *  IFCONFIG Command Implmentation
 *
 *  The license and distribution terms for this file may be
 *  found in the file LICENSE in this distribution or at
 *  http://www.rtems.org/license/LICENSE.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>

#include <netinet/in.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <net/if.h>


#include <rtems.h>
#include <rtems/rtems_bsdnet.h>
#include <rtems/shell.h>
#include "internal.h"


static const char IFCONFIG_USAGE[] = 
  "ifconfig [iface]\n"
  "ifconfig iface options | addr\n"
  "  iface: name of the interface\n"
  "  options: iface \n"
  "   up: bring the interface up\n"
  "   down: take the interface down\n"
  "   netmask addr: network mask\n"
  "   broadcast addr: boardcast address\n"
  "   pointopoint addr: destination address for a PTP link\n"
  "\n"
  "  addr: IP address";


static int rtems_shell_main_ifconfig(
  int   argc,
  char *argv[]
)
{
  struct sockaddr_in  ipaddr;
  struct sockaddr_in  dstaddr;
  struct sockaddr_in  netmask;
  struct sockaddr_in  broadcast;
  char               *iface       = NULL;
  int                 f_ip        = 0;
  int                 f_ptp       = 0;
  int                 f_netmask   = 0;
  int                 f_up        = 0;
  int                 f_down      = 0;
  int                 f_bcast     = 0;
  int                 f_usage      = 0;
  int                 cur_idx;
  int                 rc;
  int                 flags;

  memset(&ipaddr, 0, sizeof(ipaddr));
  memset(&dstaddr, 0, sizeof(dstaddr));
  memset(&netmask, 0, sizeof(netmask));
  memset(&broadcast, 0, sizeof(broadcast));

  ipaddr.sin_len = sizeof(ipaddr);
  ipaddr.sin_family = AF_INET;

  dstaddr.sin_len = sizeof(dstaddr);
  dstaddr.sin_family = AF_INET;

  netmask.sin_len = sizeof(netmask);
  netmask.sin_family = AF_INET;

  broadcast.sin_len = sizeof(broadcast);
  broadcast.sin_family = AF_INET;

  cur_idx = 0;
  if (argc <= 1) {
    /* display all interfaces */
    iface = NULL;
    cur_idx += 1;
  } else {
    if ( 0 == strcmp( "--help", argv[1] ) ) {
      f_usage = 1;
      cur_idx += 2;
    } else if ( 0 == strcmp( "-help", argv[1] ) ) {
      f_usage = 1;
      cur_idx += 2;
    } else {
      iface = argv[1];
      if ( argc >= 3 ) {
        if (isdigit((unsigned char)*argv[2])) {
          if (inet_pton(AF_INET, argv[2], &ipaddr.sin_addr) < 0) {
            printf("bad ip address: %s\n", argv[2]);
            return 0;
          }
          f_ip = 1;
          cur_idx += 3;
        } else {
          cur_idx += 2;
        }
      } else {
        cur_idx += 2;
      }
    }
  }

  if ((f_down !=0) && (f_ip != 0)) {
    f_up = 1;
  }

  while(argc > cur_idx) {
    if (strcmp(argv[cur_idx], "up") == 0) {
      f_up = 1;
      if (f_down != 0) {
        printf("Can't make interface up and down\n");
        return -1;
      }
    } else if(strcmp(argv[cur_idx], "down") == 0) {
      f_down = 1;
      if (f_up != 0) {
        printf("Can't make interface up and down\n");
        return -1;
      }
    } else if(strcmp(argv[cur_idx], "netmask") == 0) {
      if ((cur_idx + 1) >= argc) {
        printf("No netmask address\n");
        return -1;
      }
      if (inet_pton(AF_INET, argv[cur_idx+1], &netmask.sin_addr) < 0) {
        printf("bad netmask: %s\n", argv[cur_idx]);
        return -1;
      }
      f_netmask = 1;
      cur_idx += 1;
    } else if(strcmp(argv[cur_idx], "broadcast") == 0) {
      if ((cur_idx + 1) >= argc) {
        printf("No broadcast address\n");
        return -1;
      }
      if (inet_pton(AF_INET, argv[cur_idx+1], &broadcast.sin_addr) < 0) {
        printf("bad broadcast: %s\n", argv[cur_idx]);
        return -1;
      }
      f_bcast = 1;
      cur_idx += 1;
    } else if(strcmp(argv[cur_idx], "pointopoint") == 0) {
      if ((cur_idx + 1) >= argc) {
        printf("No pointopoint address\n");
        return -1;
      }
      if (inet_pton(AF_INET, argv[cur_idx+1], &dstaddr.sin_addr) < 0) {
        printf("bad pointopoint: %s\n", argv[cur_idx]);
        return -1;
      }
      f_ptp = 1;
      cur_idx += 1;
    } else {
      printf("Bad parameter: %s\n", argv[cur_idx]);
      return -1;
    }
    cur_idx += 1;
  }

  printf("ifconfig ");
  if (iface != NULL) {
    printf("%s ", iface);
    if (f_ip != 0) {
      char str[256];
      inet_ntop(AF_INET, &ipaddr.sin_addr, str, 256);
      printf("%s ", str);
    }

    if (f_netmask != 0) {
      char str[256];
      inet_ntop(AF_INET, &netmask.sin_addr, str, 256);
      printf("netmask %s ", str);
    }

    if (f_bcast != 0) {
      char str[256];
      inet_ntop(AF_INET, &broadcast.sin_addr, str, 256);
      printf("broadcast %s ", str);
    }

    if (f_ptp != 0) {
      char str[256];
      inet_ntop(AF_INET, &dstaddr.sin_addr, str, 256);
      printf("pointopoint %s ", str);
    }

    if (f_up != 0) {
      printf("up\n");
    } else if (f_down != 0) {
      printf("down\n");
    }  else {
      printf("\n");
    }
  } else if (f_usage != 0) {
      printf ( "\n" );
      printf ( IFCONFIG_USAGE );
  }

  if ( ! f_usage ) {
    if ((iface == NULL) || ((f_ip == 0) && (f_down == 0) && (f_up == 0))) {
      rtems_bsdnet_show_if_stats();
      return 0;
    }
  }

  flags = 0;
  if (f_netmask) {
    rc = rtems_bsdnet_ifconfig(iface, SIOCSIFNETMASK, &netmask);
    if (rc < 0) {
      printf("Could not set netmask: %s\n", strerror(errno));
      return -1;
    }
  }

  if (f_bcast) {
    rc = rtems_bsdnet_ifconfig(iface, SIOCSIFBRDADDR, &broadcast);
    if (rc < 0) {
      printf("Could not set broadcast: %s\n", strerror(errno));
      return -1;
    }
  }

  if (f_ptp) {
    rc = rtems_bsdnet_ifconfig(iface, SIOCSIFDSTADDR, &dstaddr);
    if (rc < 0) {
      printf("Could not set destination address: %s\n", strerror(errno));
      return -1;
    }
    flags |= IFF_POINTOPOINT;
  }

  /* This must come _after_ setting the netmask, broadcast addresses */
  if (f_ip) {
    rc = rtems_bsdnet_ifconfig(iface, SIOCSIFADDR, &ipaddr);
    if (rc < 0) {
      printf("Could not set IP address: %s\n", strerror(errno));
      return -1;
    }
  }

  if (f_up != 0) {
    flags |= IFF_UP;
  }

  if ( ! f_usage ) {
    rc = rtems_bsdnet_ifconfig(iface, SIOCSIFFLAGS, &flags);
    if (rc < 0) {
      printf("Could not set interface flags: %s\n", strerror(errno));
      return -1;
    }
  }

  return 0;
}

rtems_shell_cmd_t rtems_shell_IFCONFIG_Command = {
  "ifconfig",                                      /* name */
  IFCONFIG_USAGE,                                  /* usage */
  "network",                                       /* topic */
  rtems_shell_main_ifconfig,                       /* command */
  NULL,                                            /* alias */
  NULL                                             /* next */
};
