2014年2月22日

[C] 程式範例-切割 subnet


為了學校作業寫的程式,用途是可將一大 IP 網段切割為數個小 subnet 輸出

問題敘述:試設計一程式,Input 為 IP與subnet的大小,Output為所有的 subnets 與 netmask。

編譯環境:

於UNIX, Linux, BSD, OS 上編譯:

gcc cidr.c -o cidr -Wall -lm
>> 執行格式說明:

$ ./cidr [IP address] [number of hosts per subnet]

[ IP address ] 為IPv4位址

[ number of hosts per subnet ] 為每個子網域中的主機數目

範例:./cidr 192.168.0.0 8

流程說明
>> 取得INPUT資料:

1. 取得IP與每個subnet的host數目。

2. 檢查IP的格式是否正確。

3. 將十進位IP轉為二進位格式。

4. 檢查該IP屬於哪一種 class (A/B/C)。

5. 根據該IP的class取得預設之netmask。

6. 根據使用者輸入的 host 數目來求切割後subnet的netmask。

7. 計算該網段可切割為幾個subnet。

8. 輸出所有的subnet與netmask。

>> 輸出的資料儲存於”output.txt”。

執行結果:

說明:

1.$ ./cidr 192.168.1.0 8
>> 代表192.168.1.0此網段要切成每個subnet有8個hosts。

2.IP: 192.168.0.0

3.IP (bin): 11000000 10101000 00000000 00000000 >> 轉成二進位的IP。

4.Class: C >> 代表該IP屬於Class C。

5.Default netmaek: 255.255.255.0 >> class C預設 netmask 為 255.255.255.0。

6.Default netmask (bin): 11111111 11111111 11111111 00000000 >> 轉成二進位。

7.[Input] Host number/subnet: 5: >> 輸入每個subnet的hosts數目。

8.[FixTo] Host number/subnet: 8 >> 將 5 修正為最接近的hosts合法數目 8。

9.Host id: 3 >> host id 的 bit 數。

10.Subnet netmaek: 255.255.255.248 >> 計算出的每個subnet netmask。

11.Subnet netmask (bin):
11111111 11111111 11111111 11111000 >> 轉成二進

12.Subnet number: 32 >> 總共可以切割成 32 個 subnet。

/* Date: May, 2006
*
* Input:
* IP and the size of subnet
* Output:
* All subnets and the netmask
*
* Ref.
* [1] TCP/IP Protocol suite 2nd, chap 4 and chap5
* [2] http://www.study-area.org/network/network_ip_addr.htm
*
* Aaron Liao
*/

#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>


#define DEBUG 1
#define IP_ADDR_CLEN 16
#define IP_ADDR_BLEN 32

enum 
{ TRUE = 0, FALSE };

/* class A 0
* class B 10
* class C 110
* Class D 1110
* Class E 1111
*/

enum ip_type
{ classA = 0, classB, classC, classD, classE };

int chk_class (char *bip);
int out_class (unsigned char class);
int get_orig_mask (unsigned int class, char *mask, char *bmask);
int get_new_mask (unsigned int host_bit, char *mask, char *bmask);
int no_buf (void);
int chk_ip (char *ip);
int get_host_num (unsigned long long *host, char *num,
    unsigned int class, unsigned int *host_num);
int get_subnet_num (unsigned int class, unsigned int host_id,
      unsigned int *sub);
int get_IP (char *ip, char *arg, char *bip);
int dec2bin (unsigned char *ip, unsigned char *bip);
int bin2dec (unsigned char *bip, unsigned char *ip);
int out_bin (char *str);
int out_subnet (char *bip, char *mask, unsigned int subnet_num,
  unsigned int host_num, unsigned int host_id);
int usage (void);

int
chk_class (char *bip)
{
  if (!bip[0])
    return classA;

  if (!bip[1])
    return classB;

  if (!bip[2])
    return classC;

  if (!bip[3])
    return classD;

  return classE;
}

int
out_class (unsigned char class)
{
  printf ("\tClass: ");

  switch (class)
  {
    case classA:
      printf ("A\r\n");
      break;
    case classB:
      printf ("B\r\n");
      break;
    case classC:
      printf ("C\r\n");
      break;
    case classD:
      printf ("D\r\n");
      break;
    case classE:
      printf ("E\r\n");
      break;
    default:
/* should never */
      printf ("?\r\n");
      break;
    }

  return 0;
}

int
get_orig_mask (unsigned int class, char *mask, char *bmask)
{
  switch (class)
  {
    case classA:
      strcpy (mask, "255.0.0.0");
      break;
    case classB:
      strcpy (mask, "255.255.0.0");
      break;
    case classC:
      strcpy (mask, "255.255.255.0");
      break;
    default:
      break;
    }

  printf ("\tDefault netmaek: %s\r\n", mask);

  dec2bin (mask, bmask);
  printf ("\tDefault netmask (bin): ");
  out_bin (bmask);

  return 0;
}

int
get_new_mask (unsigned int host_bit, char *mask, char *bmask)
{
  unsigned int count, i;

  for (count = 0; count < IP_ADDR_BLEN; count++)
       bmask[count] = 1;

  for (count = (IP_ADDR_BLEN - 1), i = host_bit; i > 0; i--, count--)
      bmask[count] = 0;

  bin2dec (bmask, mask);

  printf ("\tSubnet netmaek: %s\r\n", mask);

  printf ("\tSubnet netmask (bin): ");
  out_bin (bmask);

  return 0;
}

int
no_buf (void)
{
  setbuf (stdout, 0);
  return 0;
}


int
chk_ip (char *ip)
{
  unsigned int count, i, state, error, count_num;
  char *ptr = NULL, tmp[IP_ADDR_CLEN];


/* check there are three '.' */
  for (i = 0, count = 0; i < IP_ADDR_CLEN; i++)
    {
      if (ip[i] == '.')
          count++;
    }

  if (count != 3)
    {
      printf ("\t--> Error: [IP][.][%d] Incorrect IP format.\n\n", count);
      return FALSE;
    }

/* Check [0-9] and '.' */
  for (i = 0; i < IP_ADDR_CLEN; i++)
    {

      if (ip[i] == '.' || (ip[i] >= '0' && ip[i] <= '9'))
           continue;

      if (ip[i] == '\0') /* End of string */
           break;

      printf ("\t--> Error: [IP] only [0-9] and '.'\r\n");
      return FALSE;
    }


  strcpy (tmp, ip);
  ptr = strtok (tmp, ".");

  while (ptr != NULL)
  {
      if (atoi (ptr) > 255)
      {
         printf ("\t--> Error: [IP][%d] Incorrect IP format.\n\n",
         atoi (ptr));
         return FALSE;
      }

      ptr = strtok (NULL, ".");

    }

/* check the order of number and '.'
* Format: number.number.number.number
*/
  for (i = 0, error = 0, state = 0, count = 0, count_num = 0; i <
       strlen (ip); i++)
  {


      switch (state)
      {

           case 0:  /* '.' at the head */
             if (ip[i] == '.')
               error = 5;
             else
               {
                 state = 1;
                 count_num++;
               }

             break;

           case 1:  /* mid-number */

             if (ip[i] == '.')
             {
                 state = 2;
                 count++;
                 count_num = 0;
             }
             else
             {
                 count_num++;
             }

     /* number is limited to three digits */
            if (count_num > 3)
               error = 1;

             break;

           case 2:  /* . */
          
             if (ip[i] == '.' || count > 3 || count_num > 3)
                  error = 2;

             state = 1;
             count_num++;
             break;

           default:
             error = 4;
             break;
      }

      if (error)
      {
        printf ("\t--> Error: [IP][Regular][%d] ", error);
        printf ("There are some errors.\r\n");
        return FALSE;
      }

    }

  if (ip[i - 1] == '.')
    {
      printf ("\t--> Error: [IP][.] Can't end with '.'\r\n");
      return FALSE;
    }

  return TRUE;
}

/*
* ip[]: "210.125.0.0"
* bip[]: 11010010011111010000000000000000
*/
int
dec2bin (unsigned char *ip, unsigned char *bip)
{
  char *ptr = NULL;
  unsigned char dec_ip[4] = { 0, 0, 0, 0 };
  int count, j;

  ptr = strtok (ip, ".");
  for (count = 0; count < 4; count++)
  {

      if (ptr != NULL)
      {

        dec_ip[count] = atoi (ptr);
        ptr = strtok (NULL, ".");

      }
      else
      {
          /* Error */
          return FALSE;
      }
   }

/* dec-to-binary */
  for (j = 3; j > (-1); j--)
  {
      for (count = 7; count > (-1); count--)
      {
        bip[8 * (j + 1) - count - 1] = ((dec_ip[j] & (1 <<(count))) > 0 ? 1 : 0);
      }
  }

       return TRUE;
  }

int
bin2dec (unsigned char *bip, unsigned char *ip)
{
  int i, j;
  unsigned int dec_ip[4] = { 0, 0, 0, 0 };

/* Support unsigned only */
  for (i = 0, j = 0; i < IP_ADDR_BLEN; i++)
  {
      unsigned char _offset = i % 8;

      if (_offset == 0 && i)
      {
        j++;
      }

      dec_ip[j] += (bip[i] <<(7 - _offset));
  }

  sprintf (ip, "%d.%d.%d.%d", dec_ip[0], dec_ip[1], dec_ip[2], dec_ip[3]);

  return 0;
}


/* Input: the number of hosts per subnet
* The number of host(s) must be the power of 2.
* 2^0, 2^1, ... , 2^32
*/
int
chk_host_num (unsigned long long num)
{
  unsigned int error, count;

/* Must be even */
  if (num % 2)
    return FALSE;

/* check if the power of 2 */
  for (count = 0, error = 1; count < 32; count++)
  {
      if (num == (1 << count))
           error = 0;
  }

  if (error)
    return FALSE;

  return 0;
}

int
get_host_num (unsigned long long *host, char *num,
       unsigned int class, unsigned int *host_id)
{
  unsigned int count;
  const unsigned int max_len = 32;
  unsigned long long max_hosts;

  switch (class)
  {
    case classA:
      max_hosts = pow (2, 24); /* powl */
      break;
    case classB:
      max_hosts = pow (2, 16);
      break;
    case classC:
      max_hosts = pow (2, 8);
      break;
    default:
      return FALSE;
      break;
  }

  for (count = 0; count < max_len && num[count] != '\0'; count++)
  {
      if (num[count] < '0' || num[count] > '9')
      {
         printf ("\t--> Error: [Host_num] limited to be [0-9]\r\n");
         return FALSE;
      }
  }

  (*host) = atoll (num);

  if ((*host) < 2)
  {
      printf ("\t--> Error: [Host_num][%llu] is too small.\r\n", (*host));
      return FALSE;
  }

  if ((*host) > pow (2, 32))
  {
      printf ("\t--> Error: [Host_num][%llu] is too large.\r\n", (*host));
      return FALSE;
  }

  if ((*host) > max_hosts)
  {
      printf ("\t--> Error: [Host_num][Max: %llu] %llu is too large.\r\n",max_hosts, (*host));
      return FALSE;
  }

  printf ("\t[Input] Host number/subnet: %llu\r\n", (*host));

  for (count = 0; count < 32; count++)
  {

      if ((*host) <= pow (2, count))
      {
        (*host) = pow (2, count);
        (*host_id) = count;
        break;
      }
  }

  printf ("\t[FixTo] Host number/subnet: %llu\r\n", (*host));
  printf ("\tHost id: %d\r\n", (*host_id));

  return 0;
}

int
get_IP (char *ip, char *arg, char *bip)
{

  if (strlen (arg) > IP_ADDR_CLEN)
  {
      printf ("\t--> Error: [IP] %s incorrect format.\r\n", arg);
      return FALSE;
  }


  if (strlen (arg) > IP_ADDR_CLEN)
       strncpy (ip, arg, IP_ADDR_CLEN);
  else
       strncpy (ip, arg, strlen (arg));

  if (chk_ip (ip))
       return FALSE;

  printf ("\tIP: %s\r\n", ip);

  dec2bin (ip, bip);
  printf ("\tIP (bin): ");
  out_bin (bip);

  return 0;
}

int
get_subnet_num (unsigned int class, unsigned int host_id, unsigned int *sub)
{
  unsigned int tmp;

  switch (class)
  {

    case classA:
      tmp = (24 - host_id);
      break;
    case classB:
      tmp = (16 - host_id);
      break;
    case classC:
      tmp = (8 - host_id);
      break;
    default:
      return FALSE;
      break;
  }

  (*sub) = pow (2, tmp);
  printf ("\tSubnet number: %d\r\n", (*sub));

  return 0;
}

int
out_bin (char *str)
{
  unsigned int count;

  for (count = 0; count < IP_ADDR_BLEN; count++)
  {

      printf ("%d", str[count]);

      if (count > 0 && !((count + 1) % 8))
           printf (" ");
  }

  printf ("\r\n");
  return 0;
}

int
out_subnet (char *bip, char *mask, unsigned int subnet_num,
     unsigned int host_num, unsigned int host_id)
{
  unsigned char ip[IP_ADDR_CLEN];
  unsigned int i, j, k;
  FILE *fp = NULL;

  if ((fp = fopen ("output.txt", "w")) == NULL)
    printf ("Can't open output.txt\r\n");

  if (fp)
  {
      fprintf (fp, "Output: \r\n");
      fprintf (fp, "netmask: [ %s ]\r\n", mask);
  }

  printf ("Output: \r\n");
  printf ("netmask: [ %s ]\r\n", mask);

  for (i = 0; i < subnet_num; i++)
  {

      bin2dec (bip, ip);

      if (fp)
          fprintf (fp, "Subnet %d: [ %s - ", i + 1, ip);

      printf ("Subnet %d: [ %s - ", i + 1, ip);

      for (j = 0; j < host_num - 1; j++)
      {

          /* binary increment */
          for (k = (IP_ADDR_BLEN - 1); k > 0; k--)
          {

             bip[k] = (!bip[k]);

             if (bip[k] == 1)
             break;
          }
      }

      bin2dec (bip, ip);

      if (fp)
          fprintf (fp, "%s ]\r\n", ip);

      printf ("%s ]\r\n", ip);

/* add 1 */
      for (k = (IP_ADDR_BLEN - 1); k > 0; k--)
      {

        bip[k] = (!bip[k]);

        if (bip[k] == 1)
          break;
      }

    }

  if (fp)
    fclose (fp);

  return 0;
}

int
usage (void)
{
  printf ("Usage: cidr [IP address] [number of hosts per subnet]\r\n");
  printf ("\t ex. cidr 192.168.0.0 8\r\n");
  return 0;
}

int
main (int argc, char *argv[])
{

  unsigned char ip[IP_ADDR_CLEN], bip[IP_ADDR_BLEN], mask[IP_ADDR_CLEN], bmask[IP_ADDR_BLEN];

/* The number of hosts should be even, and the power of two,
* But I allow the user to use any number of hosts between
* 2^0 and 2^32. Then I calculate the approach value. ex.
* They want 5 hosts per subnet, and I find 8 hosts per subnet.
*/
  unsigned long long host_num;
  unsigned int host_bit, ip_class, subnet_num;

/* clean the output.txt */
  fclose (fopen ("output.txt", "w"));

  memset (ip, '\0', IP_ADDR_CLEN);
  no_buf ();   /* Don't buffer the standard output */


  if (argc < 3 || argv[1] == NULL || argv[2] == NULL)
  {
      usage ();
      return FALSE;
  }


  if (get_IP (ip, argv[1], bip))
     return FALSE;

/* Get class of IP and normal netmask */
  ip_class = chk_class (bip);
  out_class (ip_class);

  if (ip_class == classD || ip_class == classE)
    return FALSE;

  get_orig_mask (ip_class, mask, bmask);

/* Get the number of hosts */
  if (get_host_num (&host_num, argv[2], ip_class, &host_bit))
    return FALSE;

/* calculate the new netmask of subnet */
  get_new_mask (host_bit, mask, bmask);

  get_subnet_num (ip_class, host_bit, &subnet_num);

  out_subnet (bip, mask, subnet_num, host_num, host_bit);

  return 0;
}


執行結果範例:
./cidr 192.168.0.0 87

IP: 192.168.0.0
IP (bin): 11000000 10101000 00000000 00000000
Class: C
Default netmaek: 255.255.255.0
Default netmask (bin): 11111111 11111111 11111111 00000000
[Input] Host number/subnet: 87
[FixTo] Host number/subnet: 128
Host id: 7
Subnet netmaek: 255.255.255.128
Subnet netmask (bin): 11111111 11111111 11111111 10000000
Subnet number: 2
Output:
netmask: [ 255.255.255.128 ]
Subnet 1: [ 192.168.0.0 - 192.168.0.127 ]
Subnet 2: [ 192.168.0.128 - 192.168.0.255 ]

沒有留言:

張貼留言

歡迎留言