From 7d83f3fbd673283e2cc82e30df9623dda554856e Mon Sep 17 00:00:00 2001 From: Konrad Rosenbaum Date: Fri, 20 Apr 2012 11:30:20 +0200 Subject: [PATCH] split into several modules and extended calculations --- Makefile | 7 +- calculate.c | 168 +++++++++++++++++++++++ help.c | 83 ++++++++++++ ip6calc.c | 431 ++++++++--------------------------------------------------- ip6calc.h | 86 ++++++++++++ output.c | 127 +++++++++++++++++ parseaddr.c | 243 +++++++++++++++++++++++++++++++++ 7 files changed, 767 insertions(+), 378 deletions(-) create mode 100644 calculate.c create mode 100644 help.c create mode 100644 ip6calc.h create mode 100644 output.c create mode 100644 parseaddr.c diff --git a/Makefile b/Makefile index 08e4588..e356d72 100644 --- a/Makefile +++ b/Makefile @@ -5,5 +5,8 @@ CC=gcc CFLAGS=-Wall -g LDFLAGS= -ip6calc: ip6calc.c - $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ +ip6calc: ip6calc.o calculate.o help.o output.o parseaddr.o + $(CC) $(LDFLAGS) -o $@ $^ + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $^ diff --git a/calculate.c b/calculate.c new file mode 100644 index 0000000..c43f2d5 --- /dev/null +++ b/calculate.c @@ -0,0 +1,168 @@ +/* +* C Implementation: ip6calc +* +* Description: a simple tool to calculate IPv6 addresses +* +* +* Author: Konrad Rosenbaum , (C) 2009,2012 +* +* Copyright: See COPYING file that comes with this distribution +* +*/ + +#include +#include +#include + +#include "ip6calc.h" + +void masknet(ip6addr caddr,int m) +{ + int i; + for(i=0;i<8;i++){ + if(m<=0)caddr[i]=0; + else switch(m){ + case 1:caddr[i]&=0x8000;break; + case 2:caddr[i]&=0xc000;break; + case 3:caddr[i]&=0xe000;break; + case 4:caddr[i]&=0xf000;break; + + case 5:caddr[i]&=0xf800;break; + case 6:caddr[i]&=0xfc00;break; + case 7:caddr[i]&=0xfe00;break; + case 8:caddr[i]&=0xff00;break; + + case 9:caddr[i]&=0xff80;break; + case 10:caddr[i]&=0xffc0;break; + case 11:caddr[i]&=0xffe0;break; + case 12:caddr[i]&=0xfff0;break; + + case 13:caddr[i]&=0xfff8;break; + case 14:caddr[i]&=0xfffc;break; + case 15:caddr[i]&=0xfffe;break; + + default:break; + } + m-=16; + } +} + +void maskhost(ip6addr ip,int m) +{ + int i; + for(i=0;i<8;i++){ + if(m>=16)ip[i]=0; + else switch(m){ + case 1:ip[i]&=0x7fff;break; + case 2:ip[i]&=0x3fff;break; + case 3:ip[i]&=0x1fff;break; + case 4:ip[i]&=0x0fff;break; + + case 5:ip[i]&=0x07ff;break; + case 6:ip[i]&=0x03ff;break; + case 7:ip[i]&=0x01ff;break; + case 8:ip[i]&=0x00ff;break; + + case 9:ip[i]&=0x007f;break; + case 10:ip[i]&=0x003f;break; + case 11:ip[i]&=0x001f;break; + case 12:ip[i]&=0x000f;break; + + case 13:ip[i]&=0x0007;break; + case 14:ip[i]&=0x0003;break; + case 15:ip[i]&=0x0001;break; + } + m-=16; + } +} + +void splitaddr(ip6addr ip1,ip6addr ip2,int mask) +{ + memcpy(ip2,ip1,sizeof(ip6addr)); + masknet(ip1,mask); + maskhost(ip2,mask); +} + +void shiftaddr(int ri) +{ +} + +int getnum(const char*s) +{ + int num; + char*end; + if(s==0){ + fprintf(stderr,"No shift/rotate distance given, must be a number between 0 and 128.\n"); + exit(1); + } + num=strtol(s,&end,10); + if(num<0 || num>128 || *end){ + fprintf(stderr,"Invalid shift/rotate distance %s, must be a number between 0 and 128.\n",s); + } + return num; +} + +void shiftright_(int num) +{ + int seg=num>>4,bit=num&0xf; + ip6addr ip; + //shift by segments + if(seg){ + int i; + for(i=0;i<8;i++) + ip[(i+seg)&7]=caddr[i]; + }else memcpy(ip,caddr,sizeof(ip)); + //shift by bits + if(bit){ + int i; + for(i=0;i<7;i++) + caddr[i+1]=ip[i+1]>>bit | ip[i]<<(16-bit); + caddr[0]=ip[0]>>bit | ip[7]<<(16-bit); + }else memcpy(caddr,ip,sizeof(ip)); +} + +void shiftright(const char*s) +{ + int ri=getnum(s); + //base checks + if(ri==0)return; + if(ri>=128){ + memset(caddr,0,sizeof(ip6addr)); + return; + } + //shift + shiftright_(ri); + maskhost(caddr,ri); +} + +void shiftleft(const char*s) +{ + int le=getnum(s); + //base checks + if(le==0)return; + if(le>=128){ + memset(caddr,0,sizeof(ip6addr)); + return; + } + //shift + shiftright_(128-le); + masknet(caddr,128-le); +} + +void rolright(const char*s) +{ + int ri=getnum(s); + //base checks + if(ri==0||ri==128)return; + //roll + shiftright_(ri); +} + +void rolleft(const char*s) +{ + int le=getnum(s); + //base checks + if(le==0||le==128)return; + //roll + shiftright_(128-le); +} diff --git a/help.c b/help.c new file mode 100644 index 0000000..3d869f7 --- /dev/null +++ b/help.c @@ -0,0 +1,83 @@ +/* +* C Implementation: ip6calc +* +* Description: a simple tool to calculate IPv6 addresses +* +* +* Author: Konrad Rosenbaum , (C) 2009,2012 +* +* Copyright: See COPYING file that comes with this distribution +* +*/ + +#include "ip6calc.h" + +#include + +#define HELP \ + "Usage: %1$s [options] address/mask...\n" \ + "IPv6 calculator\n"\ + "(c) Konrad Rosenbaum, 2009, 2012\n"\ + "this program is protected under the GNU GPLv3 or at your option any newer\n"\ + "\n"\ + "Address parameters:\n"\ + " 2001:db8::1/64 - address and mask in standard IPv6 notation\n" \ + " 2001:db8::1 - same address, mask is assumed to be /128\n" \ + " /64 - keeps the currently calculated address, but changes the netmask\n"\ + " mac:00-11-22-33-44-55 - the mac will be translated into a link local\n"\ + " address fe80::211:22ff:fe33:4455/64\n"\ + " mac:00-11-22-33-44-55/128 - same translation, but explicit netmask\n"\ + " if multiple addresses are given the program attempts to merge them\n"\ + "\n"\ + "Output options:\n" \ + " -h | --help\n" \ + " display this help text and exit\n" \ + \ + " -s | --short\n" \ + " print short IPv6 output (eg. 2001:db8::1/128) - this is the default\n"\ + \ + " -d | --detail\n" \ + " print detailed IPv6 output (eg. 2001:db8:0:0:0:0:0:1/128)\n"\ + \ + " -f | --full\n" \ + " print full IPv6 output (eg. 2001:0db8:0000:0000:0000:0000:0000:0001)\n"\ + \ + " -n | --netid\n" \ + " include the network ID into the output\n"\ + \ + " -H | --hostid\n" \ + " include the host ID into the output\n"\ + \ + " -m | --mask\n"\ + " include the network mask of the last component\n"\ + \ + " -M | --mac\n"\ + " attempt to extract a MAC address from the resulting host ID\n"\ + \ + " -6 | --ipv6\n"\ + " alias for -Hnm, this is the default if there are no other output parameters\n"\ + \ + " -4 | --ipv4\n"\ + " print the last 32 bits of the address as an IPv4 address\n"\ + \ + "\n"\ + "Examples:\n"\ + " %1$s 2001:db8::1/64\n -> 2001:db8::/64\n"\ + " %1$s 2001:db8::1/64 fe80::211:22ff:fe33:4455\n -> 2001:db8::211:22ff:fe33:4455/128\n"\ + " %1$s 2001:db8::1/64 fe80::211:22ff:fe33:4455 /64\n -> 2001:db8::211:22ff:fe33:4455/64\n"\ + " %1$s --netid 2001:db8::211:22ff:fe33:4455/64\n -> 2001:db8::\n"\ + " %1$s --hostid 2001:db8::211:22ff:fe33:4455/64\n -> ::211:22ff:fe33:4455\n"\ + " %1$s --mask 2001:db8::211:22ff:fe33:4455/64\n -> /64\n"\ + " %1$s --netid --hostid --mask 2001:db8::211:22ff:fe33:4455/64\n -> 2001:db8::211:22ff:fe33:4455/64\n"\ + " %1$s 2001:db8::211:22ff:fe33:4455/64\n -> 2001:db8::211:22ff:fe33:4455/64\n"\ + " %1$s --mac 2001:db8::211:22ff:fe33:4455\n -> MAC=00-11-22-33-44-55\n" + +void printhelp(const char*arg) +{ + fprintf(stderr,HELP,arg); +} + +void printerror(const char*arg) +{ + fprintf(stderr,"Syntax error. Use '%s --help' to see options.\n",arg); +} diff --git a/ip6calc.c b/ip6calc.c index 734e7ef..f6ae811 100644 --- a/ip6calc.c +++ b/ip6calc.c @@ -4,7 +4,7 @@ * Description: a simple tool to calculate IPv6 addresses * * -* Author: Konrad Rosenbaum , (C) 2009 +* Author: Konrad Rosenbaum , (C) 2009,2012 * * Copyright: See COPYING file that comes with this distribution * @@ -15,15 +15,15 @@ #include #include +#include "ip6calc.h" -#define FMT_SHORT 0 -#define FMT_DETAIL 1 -#define FMT_FULL 2 +//obsolete! #define OUT_NET 1 #define OUT_HOST 2 #define OUT_MASK 4 #define OUT_MAC 8 +#define OUT_IP4 0x10 #define OUT_IPM 7 #define OUT_IP 3 @@ -31,376 +31,37 @@ #define OUT_INIT 0x8007 -char shortopt[]="hsdfnHmM"; -struct option longopt[]= { +static char shortopt[]="-hsdfnNHmiIM64DR:r:L:l:eE"; +static struct option longopt[]= { {"short",0,0,'s'}, {"detail",0,0,'d'}, {"full",0,0,'f'}, {"netid",0,0,'n'}, + {"netmask",0,0,'N'}, {"hostid",0,0,'H'}, + {"hostmask",0,0,'I'}, {"mask",0,0,'m'}, {"mac",0,0,'M'}, {"help",0,0,'h'}, + {"debug",0,0,'D'}, + {"ipaddr",0,0,'i'}, + {"ipv6",0,0,'6'}, + {"ipv4",0,0,'4'}, + {"shr",1,0,'r'}, + {"shift-right",1,0,'r'}, + {"shl",1,0,'l'}, + {"shift-left",1,0,'l'}, + {"ror",1,0,'R'}, + {"rotate-right",1,0,'R'}, + {"rol",1,0,'L'}, + {"rotate-left",1,0,'L'}, + {"merge-host",0,0,'e'}, + {"merge-net",0,0,'E'}, {0,0,0,0} }; -#define HELP \ - "Usage: %1$s [options] address/mask...\n" \ - "IPv6 calculator\n"\ - "(c) Konrad Rosenbaum, 2009\n"\ - "this program is protected under the GNU GPLv3 or at your option any newer\n"\ - "\n"\ - "Address parameters:\n"\ - " 2001:db8::1/64 - address and mask in standard IPv6 notation\n" \ - " 2001:db8::1 - same address, mask is assumed to be /128\n" \ - " /64 - keeps the currently calculated address, but changes the netmask\n"\ - " mac:00-11-22-33-44-55 - the mac will be translated into a link local\n"\ - " address fe80::211:22ff:fe33:4455/64\n"\ - " mac:00-11-22-33-44-55/128 - same translation, but explicit netmask\n"\ - " if multiple addresses are given the program attempts to merge them\n"\ - "\n"\ - "Output options:\n" \ - " -h | --help\n" \ - " displays this help text and exit\n" \ - \ - " -s | --short\n" \ - " print short IPv6 output (eg. 2001:db8::1/128) - this is the default\n"\ - \ - " -d | --detail\n" \ - " print detailed IPv6 output (eg. 2001:db8:0:0:0:0:0:1/128)\n"\ - \ - " -f | --full\n" \ - " print full IPv6 output (eg. 2001:0db8:0000:0000:0000:0000:0000:0001)\n"\ - \ - " -n | --netid\n" \ - " print the network ID\n"\ - \ - " -H | --hostid\n" \ - " print the host ID\n"\ - \ - " -m | --mask\n"\ - " print the network mask of the last component\n"\ - \ - " -M | --mac\n"\ - " attempt to extract a MAC address from the resulting host ID\n"\ - "\n"\ - "Examples:\n"\ - " %1$s 2001:db8::1/64\n -> 2001:db8::/64\n"\ - " %1$s 2001:db8::1/64 fe80::211:22ff:fe33:4455\n -> 2001:db8::211:22ff:fe33:4455/128\n"\ - " %1$s 2001:db8::1/64 fe80::211:22ff:fe33:4455 /64\n -> 2001:db8::211:22ff:fe33:4455/64\n"\ - " %1$s --netid 2001:db8::211:22ff:fe33:4455/64\n -> 2001:db8::\n"\ - " %1$s --hostid 2001:db8::211:22ff:fe33:4455/64\n -> ::211:22ff:fe33:4455\n"\ - " %1$s --mask 2001:db8::211:22ff:fe33:4455/64\n -> /64\n"\ - " %1$s --netid --hostid --mask 2001:db8::211:22ff:fe33:4455/64\n -> 2001:db8::211:22ff:fe33:4455/64\n"\ - " %1$s 2001:db8::211:22ff:fe33:4455/64\n -> 2001:db8::211:22ff:fe33:4455/64\n"\ - " %1$s --mac 2001:db8::211:22ff:fe33:4455\n -> MAC=00-11-22-33-44-55\n" - -int format=FMT_SHORT,outputs=OUT_INIT; - -void setoutput(int o) -{ - if(outputs&OUT_UNTOUCH)outputs=0; - outputs|=o; -} - -typedef unsigned short ip6addr[8]; ip6addr caddr={0,0,0,0, 0,0,0,0}; -short cmask=0; - -void printmac() -{ - if((outputs&OUT_MAC)==0)return; - if((caddr[5]&0xff)==0xff && (caddr[6]&0xff00)==0xfe00){ - printf("MAC=%02x-%02x-%02x-%02x-%02x-%02x\n", - (caddr[4]>>8)^0x02, - caddr[4]&0xff, - caddr[5]>>8, - caddr[6]&0xff, - caddr[7]>>8, - caddr[7]&0xff - ); - }else{ - fprintf(stderr,"Result does not contain a MAC address.\n"); - exit(1); - } -} - -void printdetail() -{ - if(format==FMT_FULL) - printf("%04hx:%04hx:%04hx:%04hx:%04hx:%04hx:%04hx:%04hx", caddr[0], caddr[1], caddr[2], caddr[3], caddr[4], caddr[5], caddr[6], caddr[7]); - else - printf("%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx", caddr[0], caddr[1], caddr[2], caddr[3], caddr[4], caddr[5], caddr[6], caddr[7]); -} - -void printshort() -{ - int zs=-1,zl=0,mzs=-1,mzl=0,i; - /*shortcut: is it all zero?*/ - if(!caddr[0] && !caddr[1] && !caddr[2] && !caddr[3] && !caddr[4] && !caddr[5] && !caddr[6] && !caddr[7]){ - printf("::"); - return; - } - /*find longest stretch of zeroes*/ - for(i=0;i<7;i++){ - if(caddr[i]){ - /*check for maximum*/ - if(zl>mzl){ - mzl=zl; - mzs=zs; - } - /*reset*/ - zl=0;zs=-1; - }else{ - /*if a zero and previous not, remember pos*/ - if(!zl)zs=i; - /*increase length*/ - zl++; - } - } - /*check again*/ - if(zl>mzl){mzl=zl;mzs=zs;} - /*print*/ - if(mzs<0 || mzl<2){ - printdetail(); - return; - } - for(i=0;i=7)return; - printf("::"); - for(i=mzs+mzl;i<8;i++){ - printf("%hx",caddr[i]); - if(i<7)printf(":"); - } -} - -void printaddr() -{ - int m,i; - ip6addr ip; - if((outputs&OUT_IPM)==0)return; - /*print address*/ - if(outputs&OUT_IP){ - /*null out unwanted parts*/ - if((outputs&OUT_IP)!=OUT_IP){ - memcpy(ip,caddr,sizeof(ip)); - m=cmask; - for(i=0;i<8;i++){ - if(m>=16)ip[i]=0;else - if(m<=0)caddr[i]=0; - else switch(m){ - case 1:caddr[i]&=0x8000;ip[i]&=0x7fff;break; - case 2:caddr[i]&=0xc000;ip[i]&=0x3fff;break; - case 3:caddr[i]&=0xe000;ip[i]&=0x1fff;break; - case 4:caddr[i]&=0xf000;ip[i]&=0x0fff;break; - - case 5:caddr[i]&=0xf800;ip[i]&=0x07ff;break; - case 6:caddr[i]&=0xfc00;ip[i]&=0x03ff;break; - case 7:caddr[i]&=0xfe00;ip[i]&=0x01ff;break; - case 8:caddr[i]&=0xff00;ip[i]&=0x00ff;break; - - case 9:caddr[i]&=0xff80;ip[i]&=0x007f;break; - case 10:caddr[i]&=0xffc0;ip[i]&=0x003f;break; - case 11:caddr[i]&=0xffe0;ip[i]&=0x001f;break; - case 12:caddr[i]&=0xfff0;ip[i]&=0x000f;break; - - case 13:caddr[i]&=0xfff8;ip[i]&=0x0007;break; - case 14:caddr[i]&=0xfffc;ip[i]&=0x0003;break; - case 15:caddr[i]&=0xfffe;ip[i]&=0x0001;break; - } - m-=16; - } - if((outputs&OUT_IP)==OUT_HOST) - memcpy(caddr,ip,sizeof(ip)); - } - /*actually print*/ - if(format==FMT_SHORT) - printshort(); - else - printdetail(); - } - /*print mask*/ - if(outputs&OUT_MASK)printf("/%hi",cmask); - /*newline*/ - printf("\n"); -} - -void mergeaddr(ip6addr ip,int mask) -{ - int i,m; - /*stage 2: abbrev addrs*/ - m=cmask; - for(i=0;i<8;i++){ - if(m>=16)ip[i]=0;else - if(m<=0)caddr[i]=0; - else switch(m){ - case 1:caddr[i]&=0x8000;ip[i]&=0x7fff;break; - case 2:caddr[i]&=0xc000;ip[i]&=0x3fff;break; - case 3:caddr[i]&=0xe000;ip[i]&=0x1fff;break; - case 4:caddr[i]&=0xf000;ip[i]&=0x0fff;break; - - case 5:caddr[i]&=0xf800;ip[i]&=0x07ff;break; - case 6:caddr[i]&=0xfc00;ip[i]&=0x03ff;break; - case 7:caddr[i]&=0xfe00;ip[i]&=0x01ff;break; - case 8:caddr[i]&=0xff00;ip[i]&=0x00ff;break; - - case 9:caddr[i]&=0xff80;ip[i]&=0x007f;break; - case 10:caddr[i]&=0xffc0;ip[i]&=0x003f;break; - case 11:caddr[i]&=0xffe0;ip[i]&=0x001f;break; - case 12:caddr[i]&=0xfff0;ip[i]&=0x000f;break; - - case 13:caddr[i]&=0xfff8;ip[i]&=0x0007;break; - case 14:caddr[i]&=0xfffc;ip[i]&=0x0003;break; - case 15:caddr[i]&=0xfffe;ip[i]&=0x0001;break; - } - m-=16; - } - /*stage 3: merge*/ - for(i=0;i<8;i++) - caddr[i]^=ip[i]; - cmask=mask; -} - -void parsemac(const char*a) -{ - int i; - ip6addr ip={0xfe80,0,0,0, 0,0,0,0}; - unsigned char mac[6]={0,0,0, 0,0,0}; - const char*s=a+4; - /*parse*/ - if(strlen(s)!=17){ - fprintf(stderr,"address %s is not a valid MAC: must be 12 '-' or ':' separated hex digits\n",a); - exit(1); - } - for(i=0;i<17;i++){ - if((i%3)==2){ - if(s[i]!='-' && s[i]!=':'){ - fprintf(stderr,"address %s is not a valid MAC: invalid separator\n",a); - exit(1); - } - continue; - } - mac[i/3]<<=4; - if(s[i]>='0' && s[i]<='9')mac[i/3]|=s[i]-'0';else - if(s[i]>='a' && s[i]<='f')mac[i/3]|=s[i]-'a'+10;else - if(s[i]>='A' && s[i]<='F')mac[i/3]|=s[i]-'A'+10; - else{ - fprintf(stderr,"address %s is not a valid MAC: it contains non-hex digits\n",a); - exit(1); - } - } - /*convert*/ - ip[4]=0x200^((mac[0]<<8)|mac[1]); - ip[5]=(mac[2]<<8)|0xff; - ip[6]=0xfe00 | mac[3]; - ip[7]=(mac[4]<<8)|mac[5]; - /*merge*/ - mergeaddr(ip,64); -} - -void parseaddr(const char*a) -{ - int i,j,k,l,mask=128,m,semcnt=0,haddblc=0; - ip6addr ip={0,0,0,0, 0,0,0,0}; - /*stage 0: is it an IP or a MAC?*/ - if(strncmp("mac:",a,4)==0){ - parsemac(a); - return; - } - /*stage 1: parse local addr*/ - if(a[0]==':' && a[1]!=':'){ - fprintf(stderr,"address %s is not valid: it must start with a hex digit or ::\n",a); - exit(1); - } - m=-1; - for(i=j=0;a[i];i++){ - if(j>7){ - fprintf(stderr,"address %s is not valid: more than 8 segments\n",a); - exit(1); - } - if(a[i]=='/')break; - if(a[i]==':'){ - semcnt++; - if(!i)continue; - if(m<0){ - /*its :: */ - if(haddblc){ - fprintf(stderr,"address %s is not valid: only one :: is allowed\n",a); - exit(1); - } - haddblc++; - l=0; - for(k=i+1;a[k];k++)if(a[k]==':')l++; - if((7-l)='0'&&a[i]<='9')m|=a[i]-'0';else - if(a[i]>='a'&&a[i]<='f')m|=a[i]-'a'+10;else - if(a[i]>='A'&&a[i]<='F')m|=a[i]-'A'+10; - else{ - fprintf(stderr,"address %s is not valid: %c is not a hex digit\n",a,a[i]); - exit(1); - } - if(m>0xffff){ - fprintf(stderr,"address %s is not valid: max. 4 hex digits per segment\n",a); - exit(1); - } - } - } - /*sanity checks*/ - if(i){ - if(semcnt<2 || semcnt>7){ - fprintf(stderr,"address %s is not valid: not enough or too many segments\n",a); - exit(1); - } - if(i>2) - if(a[i-1]==':'&&a[i-2]!=':'){ - fprintf(stderr,"address %s is not valid: it must not end with a single :\n",a); - exit(1); - } - if(j!=7){ - fprintf(stderr,"address %s is not valid: wrong number of segments\n",a); - exit(1); - } - if(m>=0)ip[j]=m; - } - if(a[i]=='/'){ - /*parse mask*/ - char*p; - i++; - if(a[i]==0){ - fprintf(stderr,"invalid mask: must contain a decimal number 0<=mask<=128\n"); - exit(1); - } - mask=strtol(a+i,&p,10); - if(*p!=0){ - fprintf(stderr,"invalid mask: not a decimal number\n"); - exit(1); - } - if(mask<0 || mask>128){ - fprintf(stderr,"invalid mask: must be between 0 and 128\n"); - exit(1); - } - } - /*stage 1.5: if it is just a mask: skip stage 2*/ - if(a[0]=='/'){ - cmask=mask; - return; - } - /*stage 2: merge*/ - mergeaddr(ip,mask); -} +short cmask=64; int main(int argc,char**argv) { @@ -410,31 +71,49 @@ int main(int argc,char**argv) c=getopt_long(argc,argv,shortopt,longopt,&optindex); if(c==-1)break; switch(c){ - case 's':format=FMT_SHORT;break; - case 'd':format=FMT_DETAIL;break; - case 'f':format=FMT_FULL;break; - case 'n':setoutput(OUT_NET);break; - case 'H':setoutput(OUT_HOST);break; - case 'm':setoutput(OUT_MASK);break; - case 'M':setoutput(OUT_MAC);break; + //formatting + case 's':setformat(FMT_SHORT);break; + case 'd':setformat(FMT_DETAIL);break; + case 'f':setformat(FMT_FULL);break; + //output + case 'n':printaddr(IP6_NETID);break; + case 'H':printaddr(IP6_HOSTID);break; + case 'm':printaddr(IP6_PREFIX);break; + case 'N':printaddr(IP6_NETID|IP6_PREFIX);break; + case 'I':printaddr(IP6_HOSTID|IP6_PREFIX);break; + case 'i':printaddr(IP6_NETID|IP6_HOSTID);break; + case '6':printaddr(IP6_NETID|IP6_HOSTID|IP6_PREFIX);break; + case '4':printip4();break; + case 'M':printmac();break; + //mode + case 'e':setmergemode(MERGE_HOST);break; + case 'E':setmergemode(MERGE_NET);break; + case 'D':break; + //shifty stuff + case 'r':shiftright(optarg);break; + case 'R':rolright(optarg);break; + case 'l':shiftleft(optarg);break; + case 'L':rolleft(optarg);break; + //parsing + case 1:parseaddr(optarg);break; + //calculation + //errors default: - fprintf(stderr,"Syntax error in arguments.\n"); - fprintf(stderr,HELP,argv[0]); + printerror(argv[0]); return 1; break; case 'h': - fprintf(stderr,HELP,argv[0]); + printhelp(argv[0]); return 0; break; } } - /*parse addresses*/ + /*parse addresses after regular arguments*/ while(optind, (C) 2009,2012 +* +* Copyright: See COPYING file that comes with this distribution +* +*/ + +#ifndef IP6CALC_H +#define IP6CALC_H + +//output.c + +///efficient formatting: 2001:db8::1 +#define FMT_SHORT 0 +///more extensive formatting: 2001:db8:0:0:0:0:0:1 +#define FMT_DETAIL 1 +///extreme formatting: 2001:0db8:0000:0000:0000:0000:0000:0001 +#define FMT_FULL 2 + +///print the network ID +#define IP6_NETID 1 +///print the host ID +#define IP6_HOSTID 2 +///print the prefix +#define IP6_PREFIX 4 + +///sets the output formatting of IPv6 addresses (use FMT_* constants as argument) +void setformat(int o); +///print the MAC part of the address +void printmac(); +///print the last 32 bits as IPv4 address +void printip4(); +///print the IPv6 address with the given components (see IP6_* constants) +void printaddr(int comp); + +//ip6calc.c + +///storage type for IPv6 addresses +typedef unsigned short ip6addr[8]; +///buffer for current address +extern ip6addr caddr; +///current network mask +extern short cmask; + +//parseaddr.c + +///merge the host ID of the new address into the net of the old one +#define MERGE_HOST 0 +///merge the net ID of the new address into the host of the old one +#define MERGE_NET 1 +///sets the merge mode: see MERGE_* constants +void setmergemode(int mode); +///interface function for parsing an IP address and merging it into the current buffer +void parseaddr(const char*a); + +//calculate.c + +///mask the ip address, so only the network ID remains +void masknet(ip6addr,int); +///mask the ip address, so only the host ID remains +void maskhost(ip6addr,int); +///splits the ip address, leaves the net ID in the first one, moves the host ID into the second one +void splitaddr(ip6addr,ip6addr,int); + +///shift the address to the right +void shiftright(const char*); +///roll the address to the right (bits shifted off reappear on the left) +void rolright(const char*); +///shift the address to the left +void shiftleft(const char*); +///roll the address to the left (bits shifted off reappear on the right) +void rolleft(const char*); + + +//help.c +///prints the help pages +void printhelp(const char*); +///prints a short syntactic error message +void printerror(const char*); + +#endif diff --git a/output.c b/output.c new file mode 100644 index 0000000..1187925 --- /dev/null +++ b/output.c @@ -0,0 +1,127 @@ +/* +* C Implementation: ip6calc +* +* Description: a simple tool to calculate IPv6 addresses +* +* +* Author: Konrad Rosenbaum , (C) 2009,2012 +* +* Copyright: See COPYING file that comes with this distribution +* +*/ + +#include +#include +#include + +#include "ip6calc.h" + +//efficient formatting: 2001:db8::1 +#define FMT_SHORT 0 +//more extensive formatting: 2001:db8:0:0:0:0:0:1 +#define FMT_DETAIL 1 +//extreme formatting: 2001:0db8:0000:0000:0000:0000:0000:0001 +#define FMT_FULL 2 + + +int format=FMT_SHORT; + +void setformat(int f) +{ + format=f; +} + +void printmac() +{ + if((caddr[5]&0xff)==0xff && (caddr[6]&0xff00)==0xfe00){ + printf("%02x-%02x-%02x-%02x-%02x-%02x\n", + (caddr[4]>>8)^0x02, + caddr[4]&0xff, + caddr[5]>>8, + caddr[6]&0xff, + caddr[7]>>8, + caddr[7]&0xff + ); + }else{ + fprintf(stderr,"Result does not contain a MAC address.\n"); + exit(1); + } +} + +void printip4() +{ + printf("%i.%i.%i.%i\n",caddr[6]>>8,caddr[6]&0xff,caddr[7]>>8,caddr[7]&0xff); +} + +void printdetail(ip6addr caddr) +{ + if(format==FMT_FULL) + printf("%04hx:%04hx:%04hx:%04hx:%04hx:%04hx:%04hx:%04hx", caddr[0], caddr[1], caddr[2], caddr[3], caddr[4], caddr[5], caddr[6], caddr[7]); + else + printf("%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx", caddr[0], caddr[1], caddr[2], caddr[3], caddr[4], caddr[5], caddr[6], caddr[7]); +} + +void printshort(ip6addr caddr) +{ + int zs=-1,zl=0,mzs=-1,mzl=0,i; + /*shortcut: is it all zero?*/ + if(!caddr[0] && !caddr[1] && !caddr[2] && !caddr[3] && !caddr[4] && !caddr[5] && !caddr[6] && !caddr[7]){ + printf("::"); + return; + } + /*find longest stretch of zeroes*/ + for(i=0;i<7;i++){ + if(caddr[i]){ + /*check for maximum*/ + if(zl>mzl){ + mzl=zl; + mzs=zs; + } + /*reset*/ + zl=0;zs=-1; + }else{ + /*if a zero and previous not, remember pos*/ + if(!zl)zs=i; + /*increase length*/ + zl++; + } + } + /*check again*/ + if(zl>mzl){mzl=zl;mzs=zs;} + /*print*/ + if(mzs<0 || mzl<2){ + printdetail(caddr); + return; + } + for(i=0;i=7)return; + printf("::"); + for(i=mzs+mzl;i<8;i++){ + printf("%hx",caddr[i]); + if(i<7)printf(":"); + } +} + +void printaddr(int comp) +{ + ip6addr ip; + memcpy(ip,caddr,sizeof(ip)); + /*print address*/ + if(comp&3){ + //just part of it? + if((comp&3)==IP6_HOSTID)maskhost(ip,cmask); + if((comp&3)==IP6_NETID)masknet(ip,cmask); + /*actually print*/ + if(format==FMT_SHORT) + printshort(ip); + else + printdetail(ip); + } + /*print mask*/ + if(comp&IP6_PREFIX)printf("/%hi",cmask); + /*newline*/ + printf("\n"); +} diff --git a/parseaddr.c b/parseaddr.c new file mode 100644 index 0000000..c2acc5f --- /dev/null +++ b/parseaddr.c @@ -0,0 +1,243 @@ +/* +* C Implementation: ip6calc +* +* Description: a simple tool to calculate IPv6 addresses +* +* +* Author: Konrad Rosenbaum , (C) 2009,2012 +* +* Copyright: See COPYING file that comes with this distribution +* +*/ + +#include +#include +#include + +#include "ip6calc.h" + +//switches merge to pass-on +static int mergefirst=1; + +static int mergenet=0; +void setmergemode(int mode) +{ + mergenet=mode; +} + +void mergeaddr(ip6addr ip,int mask) +{ + int i; + //stage 1: shortcut for the first addr + if(mergefirst){ + memcpy(caddr,ip,sizeof(caddr)); + if(mask>=0 && mask<=128)cmask=mask; + mergefirst=0; + return; + } + /*stage 2: decide which way to merge; mask the addresses*/ + if(mergenet){ + maskhost(caddr,cmask); + masknet(ip,cmask); + }else{ + masknet(caddr,cmask); + maskhost(ip,cmask); + } + /*stage 3: merge*/ + for(i=0;i<8;i++) + caddr[i]|=ip[i]; + /*stage 4: change the mask, if one was given*/ + if(mask>=0 && mask<=128) + cmask=mask; +} + +void parsemac(const char*a) +{ + int i; + ip6addr ip={0xfe80,0,0,0, 0,0,0,0}; + unsigned char mac[6]={0,0,0, 0,0,0}; + const char*s=a+4; + /*parse*/ + if(strlen(s)!=17){ + fprintf(stderr,"address %s is not a valid MAC: must be 12 '-' or ':' separated hex digits\n",a); + exit(1); + } + for(i=0;i<17;i++){ + if((i%3)==2){ + if(s[i]!='-' && s[i]!=':'){ + fprintf(stderr,"address %s is not a valid MAC: invalid separator\n",a); + exit(1); + } + continue; + } + mac[i/3]<<=4; + if(s[i]>='0' && s[i]<='9')mac[i/3]|=s[i]-'0';else + if(s[i]>='a' && s[i]<='f')mac[i/3]|=s[i]-'a'+10;else + if(s[i]>='A' && s[i]<='F')mac[i/3]|=s[i]-'A'+10; + else{ + fprintf(stderr,"address %s is not a valid MAC: it contains non-hex digits\n",a); + exit(1); + } + } + /*convert*/ + ip[4]=0x200^((mac[0]<<8)|mac[1]); + ip[5]=(mac[2]<<8)|0xff; + ip[6]=0xfe00 | mac[3]; + ip[7]=(mac[4]<<8)|mac[5]; + /*merge, do not change mask*/ + mergeaddr(ip,640); +} + +void parseip4(const char*s) +{ + int i; + ip6addr ip={0,0,0,0, 0,0,0,0}; + /*check*/ + int len=strlen(s),cnt,num; + if(len<7 || len>16){ + fprintf(stderr,"address %s is not a valid IPv4 address: must be 4 dot separated decimals\n",s); + exit(1); + } + for(i=cnt=0;i255){ + fprintf(stderr,"address %s contains an illegal number %i\n",s,num); + exit(1); + } + //flush + if(cnt==0)ip[6]=num<<8;else + if(cnt==1)ip[6]|=num; + else ip[7]=num<<8; + //continue + cnt++;num=0; + continue; + } + //a digit + if(s[i]>='0' && s[i]<='9'){ + num*=10; + num+=s[i]-'0'; + continue; + } + //something else + fprintf(stderr,"address %s contains illegal character '%c'\n",s,s[i]); + exit(1); + } + //flush last component + ip[7]|=num; + /*merge, don't change mask*/ + mergeaddr(ip,300); +} + +void parseaddr(const char*a) +{ + int i,j,k,l,mask=255,m,semcnt=0,haddblc=0; + ip6addr ip={0,0,0,0, 0,0,0,0}; + /*stage 0: is it an IP or a MAC?*/ + if(strncmp("mac:",a,4)==0){ + parsemac(a); + return; + } + if(strncmp("ip4:",a,4)==0){ + parseip4(a+4); + return; + } + /*stage 1: parse local addr*/ + if(a[0]==':' && a[1]!=':'){ + fprintf(stderr,"address %s is not valid: it must start with a hex digit or ::\n",a); + exit(1); + } + m=-1; + for(i=j=0;a[i];i++){ + if(j>7){ + fprintf(stderr,"address %s is not valid: more than 8 segments\n",a); + exit(1); + } + if(a[i]=='/')break; + if(a[i]==':'){ + semcnt++; + if(!i)continue; + if(m<0){ + /*its :: */ + if(haddblc){ + fprintf(stderr,"address %s is not valid: only one :: is allowed\n",a); + exit(1); + } + haddblc++; + l=0; + for(k=i+1;a[k];k++)if(a[k]==':')l++; + if((7-l)='0'&&a[i]<='9')m|=a[i]-'0';else + if(a[i]>='a'&&a[i]<='f')m|=a[i]-'a'+10;else + if(a[i]>='A'&&a[i]<='F')m|=a[i]-'A'+10; + else{ + fprintf(stderr,"address %s is not valid: %c is not a hex digit\n",a,a[i]); + exit(1); + } + if(m>0xffff){ + fprintf(stderr,"address %s is not valid: max. 4 hex digits per segment\n",a); + exit(1); + } + } + } + /*sanity checks*/ + if(i){ + if(semcnt<2 || semcnt>7){ + fprintf(stderr,"address %s is not valid: not enough or too many segments\n",a); + exit(1); + } + if(i>2) + if(a[i-1]==':'&&a[i-2]!=':'){ + fprintf(stderr,"address %s is not valid: it must not end with a single :\n",a); + exit(1); + } + if(j!=7){ + fprintf(stderr,"address %s is not valid: wrong number of segments\n",a); + exit(1); + } + if(m>=0)ip[j]=m; + } + if(a[i]=='/'){ + /*parse mask*/ + char*p; + i++; + if(a[i]==0){ + fprintf(stderr,"invalid mask: must contain a decimal number 0<=mask<=128\n"); + exit(1); + } + mask=strtol(a+i,&p,10); + if(*p!=0){ + fprintf(stderr,"invalid mask: not a decimal number\n"); + exit(1); + } + if(mask<0 || mask>128){ + fprintf(stderr,"invalid mask: must be between 0 and 128\n"); + exit(1); + } + } + /*stage 1.5: if it is just a mask: skip stage 2*/ + if(a[0]=='/'){ + cmask=mask; + return; + } + /*stage 2: merge*/ + mergeaddr(ip,mask); +} -- 1.7.2.5