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

Re: Blocking sub-range of IP addresses



> It would be useful to have something that would take
> an IP address range and return the minimum coverage
> CIDR for that block (for use in feeding to iptables).
> 
> For example, if I want to allow access for hosts
> 1.2.3.1 - 1.2.3.4, I currently can allow them
> individually or just allow the entire /24. But is
> there any easier way to allow ip ranges in iptables,
> short of doing each individual IP or generalizing to a
> class boundary? Can ipsc do this easily?
> 
> Thanks,
> Josh
...

 I don't really have that, but attached program gives you the longest
common prefix for a few ip's.

 $ ./ipnumber -p 192.168.93.3 192.168.93.2 192.168.93.1
 192.168.93.0/30 (255.255.255.252)
 $ ./ipnumber -p 192.168.90.3 192.168.2.28             
 192.168.0.0/17 (255.255.128.0)

Regards,
/Karl

-----------------------------------------------------------------------
Karl Hammar                    Aspö Data           karl@kalle.csb.ki.se
Lilla Aspö 2340                                                Networks
S-742 94 Östhammar          +46  173 140 57                   Computers
Sweden                     +46  70 511 97 84                 Consulting
-----------------------------------------------------------------------

/** Copyright: Karl Hammar, Aspö Data
 ** Copyright terms: GPL
 **/

#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

/* int function return value: 0 == SUCCESS, else error */

/*
 * Ip numbers (or addresses, same thing differnet names)
 * are just 32 bit unsigned integers
 * the numbers we are used to (e.g. "192.168.1.3")
 * are only a way to present thoose ip numbers for humans.
 * That format is called dotted quad, since it consists of
 * four ("quad") numbers with dots between.
 *
 * Theese two routinges convert between the human and computer
 * way of seeing the ip numbers
 */
int dot2num( char *dotted_quad, uint32_t *num);

 /* len is length of dotted_quad buffer.
  * len >= INET_ADDRSTRLEN, see man inet_ntop
  */
int num2dot( uint32_t  num, char *dotted_quad, size_t len);

/* convert so can print/read binary numbers, sorry printf/scanf don't do this */
int str2num( char *str, uint32_t *num, char **ptr);
 /* the buffers length (len) must be at least 33 characters long (32 digits + one '\0')  */
int num2str( uint32_t  num, char *buffer, size_t buflen);

/*
to help routers, ip numbers are split in two parts:
the first is a network prefix and
the latter is computer (or host, well actually interface) number on that network

It works like ip_address = network_number + computer_number_on_that_network
You can compare it to a memory buffer, address (aka. ip number) = buffer_pointer (aka. network) + offset (aka. host number on that network)

This helps routers since they don't have to store routes to all hosts
they only have to keep records of networks.
Also "network" is not necceserely the same thing as a LAN.
Network is just all computers with some common top bits in their ip numbers
(note: "common top bits" i.e. ALL bits before the split, and
remember ip numbers is a simple unsigned integer)
that you can reach if you go along a given route.

Subnetting is really that simple!
But the dotted quad format makes it hard see and understand.
Why -- because the dot makes the split between network and host part hard to see.

By counting number of bits in the prefix we get the prefix length,
which is the same number as used in the cidr notation.

 Public example:
hostname    ip number      as binary
www.ibm.com 129.42.17.99   10000001001010100001000101100011
www.ge.com  216.74.139.56  11011000010010101000101100111000
common prefix              1
prefix length              1

 Local example:
calcit      192.168.93.1   11000000101010000101110100000001
hematit     192.168.93.2   11000000101010000101110100000010
granat      192.168.93.37  11000000101010000101110100100101
common prefix              11000000101010000101110100
prefix length              26

The bit positions where the prefix is, are called network bits,
and the others (representing the host part) are called the host bits.

The ip number with address 0 on a network is called the "network address"
and it is that number which goes into the routing table along with the prefix length.
Another related number is the broadcast address.
It is useful on a ethernet LAN.
The broadcast address is by convention the last address of a network.

The network address is only meaningful for routing, i.e. in the IP-layer,
and the broadcast address have the same meaning as the ip number.
A given host accept packets to that address as destined to itself and have
no meaning besides that and that all hosts on a given physical (or end) network
should have the same broadcast address so you easily can address them all.
So, the broadcast address do not have a meaning for all networks.

To tell the world about a network, we use the network address
(or any address on the same network, since they have the same prefix)
and the prefix length.
It is usually presented like "192.168.93.0/26" or
"network: 192.168.93.0, subnet mask: 255.255.255.192"
The first variant is called the CIDR notation and the second is presented with a netmask (or subnet mask).
In the CIDR notation we uses the prefix length directly,
The netmask is an 32 bit unsigned integer with all bits in the prefix set to high (1)
and all other set to low (0).
It is then converted to dotted quad notation just as eny other ip number.

 Example:
network   192.168.93.0    11000000101010000101110100000000
granat    192.168.93.37   11000000101010000101110100100101
broadcast 192.168.93.63   11000000101010000101110100111111

prefix                    11000000101010000101110100
netmask   255.255.255.192 11111111111111111111111111000000
*/

uint32_t get_network_bits(uint32_t num, uint32_t mask);
uint32_t get_host_bits(   uint32_t num, uint32_t mask);
uint32_t get_network_number(uint32_t num, uint32_t mask);
uint32_t get_broadcast_address(uint32_t num, uint32_t mask);

int update_common_prefix(uint32_t *prefix, int *cidr, uint32_t num);
int cidr2mask(int cidr, uint32_t *mask);
int mask2cidr(uint32_t mask, int *cidr);

/************************************************************************/
struct {
  char *cmd;
} global;

int Usage(void);
int do_b(int argc, char *argv[]);
int do_B(int argc, char *argv[]);
int do_m(int argc, char *argv[]);
int do_M(int argc, char *argv[]);
int do_p(int argc, char *argv[]);
int do_i(int argc, char *argv[]);
int do_r(int argc, char *argv[]);

int Usage(void) {
  printf("Usage:\n");
  printf("	%s -b ip.nr\n", global.cmd);
  printf("	%s -B binary_number\n", global.cmd);
  printf("	%s -m netmask\n", global.cmd);
  printf("	%s -M cidr\n", global.cmd);
  printf("	%s -p ip.nr ...\n", global.cmd);
  printf("	%s -i ip.nr subnetmask\n", global.cmd);
  printf("	%s -i ip.nr/cidr\n", global.cmd);
  printf("	%s -r ip.nr subnetmask\n", global.cmd);
  printf("	%s -r ip.nr/cidr\n", global.cmd);
  printf("Synopsis:\n");
  printf("	-b, convert dotted quad to binary\n");
  printf("	-B, convert binary to dotted quad\n");
  printf("	-m, convert netmask to cidr\n");
  printf("	-M, convert cidr to netmask\n");
  printf("	-i, print line suitable for ifconfig\n");
  printf("	-r, print line suitable for route\n");
  printf("Examples:\n");
  printf("	# ifconfig eth0 `%s -i 192.263.3.35/27`\n", global.cmd);
  printf("	# route add `%s -i 192.263.3.35/27`\n", global.cmd);
  return -1;
}

int do_b(int argc, char *argv[]) {
  /* given a dotted quad, print it as an binary number */
  uint32_t num;
  char buf[33];
  if (argc != 1) return Usage();
  if (dot2num( argv[0], &num)) return -1;
  if (num2str(num, buf, 33)) return -1;
  fputs(buf, stdout);
  putc('\n', stdout);
  return 0;
}

int do_B(int argc, char *argv[]) {
  /* given a binary number, print it as an dotted quad */
  uint32_t num;
  char buf[INET_ADDRSTRLEN];
  char *ptr;
  if (argc != 1) return Usage();
  if (str2num(argv[0], &num, &ptr)) return -1;
  if (num2dot(num, buf, INET_ADDRSTRLEN)) return -1;
  fputs(buf, stdout);
  putc('\n', stdout);
  return 0;
}

int do_m(int argc, char *argv[]) {
  /* given a netmask, print it as cidr */
  uint32_t mask;
  int cidr;
  if (argc != 1) return Usage();
  if (dot2num(argv[0], &mask)) return -1;
  if (mask2cidr(mask, &cidr)) return -1;
  printf("%d\n", cidr);
  return 0;
}

int do_M(int argc, char *argv[]) {
  /* given a cidr, print it as netmask */
  uint32_t mask;
  char buf[INET_ADDRSTRLEN];
  char *ptr;
  long value;

  if (argc != 1) return Usage();
  value = strtol(argv[0], &ptr, 10);
  if (ptr == argv[0] || *ptr != '\0') return -1;
  if (value > 32 || value < 0) return -1;
  if (cidr2mask(value, &mask)) return -1;
  if (num2dot(mask, buf, INET_ADDRSTRLEN)) return -1;
  fputs(buf, stdout);
  putc('\n', stdout);
  return 0;
}

static void printit( uint32_t num, int cidr) {
  char b1[INET_ADDRSTRLEN];
  char b2[INET_ADDRSTRLEN];
  uint32_t mask;

  (void) num2dot(num, b1, INET_ADDRSTRLEN);
  if (cidr2mask(cidr, &mask)) {
    printf("%s/%d (\?\?\?)\n", b1, cidr);
  } else {
    (void) num2dot(mask, b2, INET_ADDRSTRLEN);
    printf("%s/%d (%s)\n", b1, cidr, b2);
  }
}

int do_p(int argc, char *argv[]) {
  /* given a list of ip numbers, print their common prefix */
  uint32_t prefix;
  int cidr = 32;
  uint32_t num;

  if (argc < 1) return Usage();

  if (dot2num( argv[0], &prefix)) return Usage();
  argc--;
  argv++;

  while (argc) {
    if (dot2num( argv[0], &num)) return Usage();
    if (update_common_prefix( &prefix, &cidr, num )) return -1;
    argc--;
    argv++;
  }

  printit( prefix, cidr);
  return 0;
}

static int parse(int argc, char *argv[], uint32_t *num, uint32_t *mask) {
  if (argc == 1) {
    char *pb;
    char *pc;
    long val;

    pb = strchr(argv[0], '/');
    if (pb == NULL) return -1;
    *pb = '\0';
    pb++;

    if (dot2num(argv[0], num)) return -1;
    val = strtoul( pb, &pc, 10);
    if (pb == pc || *pc != '\0') return -1;

    if (cidr2mask( val, mask)) return -1;
  } else if (argc == 2) {
    if (dot2num( argv[0], num))  return -1;
    if (dot2num( argv[1], mask)) return -1;
  } else return -1;
  return 0;
}

int do_i(int argc, char *argv[]) {
  uint32_t num;
  uint32_t mask;
  uint32_t bc;
  char Num[INET_ADDRSTRLEN];
  char Mask[INET_ADDRSTRLEN];
  char Bc[INET_ADDRSTRLEN];
  if (parse(argc, argv, &num, &mask)) return -1;

  bc = get_broadcast_address( num, mask);
  (void) num2dot( num , Num , 33);
  (void) num2dot( mask, Mask, 33);
  (void) num2dot( bc  , Bc  , 33);

  printf("%s netmask %s broadcast %s\n", Num, Mask, Bc);
  return 0;
}

int do_r(int argc, char *argv[]) {
  uint32_t num;
  uint32_t mask;
  uint32_t net;
  char Mask[INET_ADDRSTRLEN];
  char Net[INET_ADDRSTRLEN];

  if (parse(argc, argv, &num, &mask)) return -1;
  net = get_network_number( num, mask);
  (void) num2dot( net , Net , 33);
  (void) num2dot( mask, Mask, 33);

  printf("-net %s netmask %s\n", Net, Mask);
  return 0;
}

int main(int argc, char *argv[]) {
  global.cmd = argv[0];
  int opt = '\0';
  int status = 0;		/* all well so far */
  /*
  uint32_t nip, nsm, nna, nbc;
  int cidr;
  char ip[16];
  char sm[16];
  char na[16];
  char bc[16];
  */
  argc--; argv++;		/* shift out global.cmd */
  if (argc == 0) return Usage();

  if (argv[0][0] == '-') {
    opt = argv[0][1];
    if (!strchr("bBmMpir", opt)) return Usage();
    if (argv[0][2] != '\0') return Usage();
    argc--; argv++; /* shift out option */
  } else return Usage();

  switch (opt) {
  case 'b': status = do_b(argc, argv); break;
  case 'B': status = do_B(argc, argv); break;
  case 'm': status = do_m(argc, argv); break;
  case 'M': status = do_M(argc, argv); break;
  case 'p': status = do_p(argc, argv); break;
  case 'i': status = do_i(argc, argv); break;
  case 'r': status = do_r(argc, argv); break;
  }
  return status;
}

/************************************************************************/

int dot2num( char *dotted_quad, uint32_t *num) {
  uint32_t num_n;		/* ip number in network byte order */
  if (inet_pton (AF_INET, dotted_quad, &num_n) < 1) return -1;
  *num = ntohl(num_n);
  return 0;
}

int num2dot( uint32_t  num, char *dotted_quad, size_t len) {
  uint32_t num_n = ntohl(num);
  if (inet_ntop(AF_INET, &num_n, dotted_quad, len) == NULL) return -1;
  return 0;
}

int str2num( char *str, uint32_t *num, char **ptr) {
  uint32_t tmp;
  errno = 0;
  tmp = strtoul( str, ptr, 2);
  if (*ptr == str) return -1;
  if (errno) return errno;
  if (**ptr == '\0' || isspace(**ptr)) {
    *num = tmp;
    return 0;
  }
  return -1;
}

int num2str( uint32_t  num, char *buffer, size_t buflen) {
  int ix;
  if (buflen < 33) return -1;
  for (ix = 31; ix >= 0; ix--) {
    buffer[ix] = num & 1 ? '1' : '0';
    num >>= 1;
  }
  buffer[32] = '\0';
  return 0;
}

/************************************************************************/

uint32_t get_network_bits(uint32_t num, uint32_t mask) {
  return num & mask;
}

uint32_t get_host_bits(   uint32_t num, uint32_t mask) {
  return num & ~mask;
}

uint32_t get_network_number(uint32_t num, uint32_t mask) {
  return num & mask;
}

uint32_t get_broadcast_address(uint32_t num, uint32_t mask) {
  return ( num & mask ) | ~mask;
}

static const uint32_t tab[] = {
  0x00000000,			/* 0 */
  0x80000000,			/* 1 */
  0xC0000000,			/* 2 */
  0xE0000000,			/* 3 */
  0xF0000000,			/* 4 */
  0xF8000000,			/* 5 */
  0xFC000000,			/* 6 */
  0xFE000000,			/* 7 */
  0xFF000000,			/* 8 */
  0xFF800000,			/* 9 */
  0xFFC00000,			/* 10 */
  0xFFE00000,			/* 11 */
  0xFFF00000,			/* 12 */
  0xFFF80000,			/* 13 */
  0xFFFC0000,			/* 14 */
  0xFFFE0000,			/* 15 */
  0xFFFF0000,			/* 16 */
  0xFFFF8000,			/* 17 */
  0xFFFFC000,			/* 18 */
  0xFFFFE000,			/* 19 */
  0xFFFFF000,			/* 20 */
  0xFFFFF800,			/* 21 */
  0xFFFFFC00,			/* 22 */
  0xFFFFFE00,			/* 23 */
  0xFFFFFF00,			/* 24 */
  0xFFFFFF80,			/* 25 */
  0xFFFFFFC0,			/* 26 */
  0xFFFFFFE0,			/* 27 */
  0xFFFFFFF0,			/* 28 */
  0xFFFFFFF8,			/* 29 */
  0xFFFFFFFC,			/* 30 */
  0xFFFFFFFE,			/* 31 */
  0xFFFFFFFF			/* 32 */
};

int update_common_prefix(uint32_t *prefix, int *cidr, uint32_t num) {
  uint32_t mask;

  if (*cidr < 0 || 32 < *cidr) return -1;

  for (; *cidr >= 0; (*cidr)--) {
    mask = tab[*cidr];
    *prefix &= mask;
    if (*prefix  == ( num & mask ) ) break;
  }
  return 0;
}

int cidr2mask(int cidr, uint32_t *mask) {
  if (0 <= cidr && cidr <= 32) {
    *mask = tab[cidr];
    return 0;
  } else {
    return -1;
  }
}

int mask2cidr(uint32_t mask, int *cidr) {
  int ix;
  for (ix = 32; ix >= 0; ix--) {
    if (mask == tab[ix]) {
      *cidr = ix;
      return 0;
    }
  }
  return -1;
}

Reply to: