summaryrefslogtreecommitdiff
path: root/appl/lib/ip.b
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
commit37da2899f40661e3e9631e497da8dc59b971cbd0 (patch)
treecbc6d4680e347d906f5fa7fca73214418741df72 /appl/lib/ip.b
parent54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff)
20060303a
Diffstat (limited to 'appl/lib/ip.b')
-rw-r--r--appl/lib/ip.b656
1 files changed, 656 insertions, 0 deletions
diff --git a/appl/lib/ip.b b/appl/lib/ip.b
new file mode 100644
index 00000000..49dfdb0a
--- /dev/null
+++ b/appl/lib/ip.b
@@ -0,0 +1,656 @@
+implement IP;
+
+#
+# Copyright © 2003,2004 Vita Nuova Holdings Limited. All rights reserved.
+#
+
+include "sys.m";
+ sys: Sys;
+
+include "ip.m";
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ v4prefix = array[] of {
+ byte 0, byte 0, byte 0, byte 0,
+ byte 0, byte 0, byte 0, byte 0,
+ byte 0, byte 0, byte 16rFF, byte 16rFF,
+ };
+
+ v4bcast = IPaddr(array[] of {
+ byte 0, byte 0, byte 0, byte 0,
+ byte 0, byte 0, byte 0, byte 0,
+ byte 0, byte 0, byte 16rFF, byte 16rFF,
+ byte 16rFF, byte 16rFF, byte 16rFF, byte 16rFF,
+ });
+
+ v4allsys = IPaddr(array[] of {
+ byte 0, byte 0, byte 0, byte 0,
+ byte 0, byte 0, byte 0, byte 0,
+ byte 0, byte 0, byte 16rFF, byte 16rFF,
+ byte 16rE0, byte 0, byte 0, byte 16r01,
+ });
+
+ v4allrouter = IPaddr(array[] of {
+ byte 0, byte 0, byte 0, byte 0,
+ byte 0, byte 0, byte 0, byte 0,
+ byte 0, byte 0, byte 16rFF, byte 16rFF,
+ byte 16rE0, byte 0, byte 0, byte 16r02,
+ });
+
+ v4noaddr = IPaddr(array[] of {
+ byte 0, byte 0, byte 0, byte 0,
+ byte 0, byte 0, byte 0, byte 0,
+ byte 0, byte 0, byte 16rFF, byte 16rFF,
+ byte 0, byte 0, byte 0, byte 0,
+ });
+
+ selfv6 = IPaddr(array[] of {
+ byte 0, byte 0, byte 0, byte 0,
+ byte 0, byte 0, byte 0, byte 0,
+ byte 0, byte 0, byte 0, byte 0,
+ byte 0, byte 0, byte 0, byte 1,
+ });
+
+ selfv4 = IPaddr(array[] of {
+ byte 0, byte 0, byte 0, byte 0,
+ byte 0, byte 0, byte 0, byte 0,
+ byte 0, byte 0, byte 16rFF, byte 16rFF,
+ byte 127, byte 0, byte 0, byte 1,
+ });
+
+ noaddr = IPaddr(array[] of {0 to IPaddrlen-1 => byte 0});
+ allbits = IPaddr(array[] of {0 to IPaddrlen-1 => byte 16rFF});
+}
+
+IPaddr.newv6(a: array of byte): IPaddr
+{
+ b := array[len a] of byte;
+ b[0:] = a[0:IPaddrlen];
+ return IPaddr(b);
+}
+
+IPaddr.newv4(a: array of byte): IPaddr
+{
+ b := array[IPaddrlen] of byte;
+ b[0:] = v4prefix;
+ b[IPv4off:] = a[0:IPv4addrlen];
+ return IPaddr(b);
+}
+
+IPaddr.copy(ip: self IPaddr): IPaddr
+{
+ if(ip.a == nil)
+ return noaddr.copy();
+ a := array[len ip.a] of byte;
+ a[0:] = ip.a;
+ return IPaddr(a);
+}
+
+IPaddr.eq(ip: self IPaddr, v: IPaddr): int
+{
+ a := ip.a;
+ if(a == nil)
+ a = noaddr.a;
+ b := v.a;
+ if(b == nil)
+ b = noaddr.a;
+ for(i := 0; i < IPaddrlen; i++)
+ if(a[i] != b[i])
+ return 0;
+ return 1;
+}
+
+IPaddr.mask(a1: self IPaddr, a2: IPaddr): IPaddr
+{
+ c := array[IPaddrlen] of byte;
+ for(i := 0; i < IPaddrlen; i++)
+ c[i] = a1.a[i] & a2.a[i];
+ return IPaddr(c);
+}
+
+IPaddr.maskn(a1: self IPaddr, a2: IPaddr): IPaddr
+{
+ c := array[IPaddrlen] of byte;
+ for(i := 0; i < IPaddrlen; i++)
+ c[i] = a1.a[i] & ~a2.a[i];
+ return IPaddr(c);
+}
+
+IPaddr.isv4(ip: self IPaddr): int
+{
+ for(i := 0; i < IPv4off; i++)
+ if(ip.a[i] != v4prefix[i])
+ return 0;
+ return 1;
+}
+
+IPaddr.ismulticast(ip: self IPaddr): int
+{
+ if(ip.isv4()){
+ v := int ip.a[IPv4off];
+ return v >= 16rE0 && v < 16rF0 || ip.eq(v4bcast); # rfc1112
+ }
+ return ip.a[0] == byte 16rFF;
+}
+
+IPaddr.isvalid(ip: self IPaddr): int
+{
+ return !ip.eq(noaddr) && !ip.eq(v4noaddr);
+}
+
+IPaddr.v4(ip: self IPaddr): array of byte
+{
+ if(!ip.isv4() && !ip.eq(noaddr))
+ return nil;
+ a := array[4] of byte;
+ for(i := 0; i < 4; i++)
+ a[i] = ip.a[IPv4off+i];
+ return a;
+}
+
+IPaddr.v6(ip: self IPaddr): array of byte
+{
+ a := array[IPaddrlen] of byte;
+ a[0:] = ip.a;
+ return a;
+}
+
+IPaddr.class(ip: self IPaddr): int
+{
+ if(!ip.isv4())
+ return 6;
+ return int ip.a[IPv4off]>>6;
+}
+
+IPaddr.classmask(ip: self IPaddr): IPaddr
+{
+ m := allbits.copy();
+ if(!ip.isv4())
+ return m;
+ if((n := ip.class()) == 0)
+ n = 1;
+ for(i := IPaddrlen-4+n; i < IPaddrlen; i++)
+ m.a[i] = byte 0;
+ return m;
+}
+
+#
+# rfc2373
+#
+
+IPaddr.parse(s: string): (int, IPaddr)
+{
+ a := noaddr.copy();
+ col := 0;
+ gap := 0;
+ for(i:=0; i<IPaddrlen && s != ""; i+=2){
+ c := 'x';
+ v := 0;
+ for(m := 0; m < len s && (c = s[m]) != '.' && c != ':'; m++){
+ d := 0;
+ if(c >= '0' && c <= '9')
+ d = c-'0';
+ else if(c >= 'a' && c <= 'f')
+ d = c-'a'+10;
+ else if(c >= 'A' && c <= 'F')
+ d = c-'A'+10;
+ else
+ return (-1, a);
+ v = (v<<4) | d;
+ }
+ if(c == '.'){
+ if(parseipv4(a.a[i:], s) < 0)
+ return (-1, noaddr.copy());
+ i += IPv4addrlen;
+ break;
+ }
+ if(v > 16rFFFF)
+ return (-1, a);
+ a.a[i] = byte (v>>8);
+ a.a[i+1] = byte v;
+ if(c == ':'){
+ col = 1;
+ if(++m < len s && s[m] == ':'){
+ if(gap > 0)
+ return (-1, a);
+ gap = i+2;
+ m++;
+ }
+ }
+ s = s[m:];
+ }
+ if(i < IPaddrlen){ # mind the gap
+ ns := i-gap;
+ for(j := 1; j <= ns; j++){
+ a.a[IPaddrlen-j] = a.a[i-j];
+ a.a[i-j] = byte 0;
+ }
+ }
+ if(!col)
+ a.a[0:] = v4prefix;
+ return (0, IPaddr(a));
+}
+
+IPaddr.parsemask(s: string): (int, IPaddr)
+{
+ return parsemask(s, 128);
+}
+
+IPaddr.parsecidr(s: string): (int, IPaddr, IPaddr)
+{
+ for(i := 0; i < len s && s[i] != '/'; i++)
+ ;
+ (ok, a) := IPaddr.parse(s[0:i]);
+ if(i < len s){
+ (ok2, m) := IPaddr.parsemask(s[i:]);
+ if(ok < 0 || ok2 < 0)
+ return (-1, a, m);
+ return (0, a, m);
+ }
+ return (ok, a, allbits.copy());
+}
+
+parseipv4(b: array of byte, s: string): int
+{
+ a := array[4] of {* => 0};
+ o := 0;
+ for(i := 0; i < 4 && o < len s; i++){
+ for(m := o; m < len s && (c := s[m]) != '.'; m++)
+ if(!(c >= '0' && c <= '9'))
+ return -1;
+ if(m == o)
+ return -1;
+ a[i] = int big s[o:m];
+ b[i] = byte a[i];
+ if(m < len s && s[m] == '.')
+ m++;
+ o = m;
+ }
+ case i {
+ 1 => # 32 bit
+ b[0] = byte (a[0] >> 24);
+ b[1] = byte (a[0] >> 16);
+ b[2] = byte (a[0] >> 8);
+ b[3] = byte a[0];
+ 2 =>
+ if(a[0] < 256){ # 8/24
+ b[0] = byte a[0];
+ b[1] = byte (a[1]>>16);
+ b[2] = byte (a[1]>>8);
+ }else if(a[0] < 65536){ # 16/16
+ b[0] = byte (a[0]>>8);
+ b[1] = byte a[0];
+ b[2] = byte (a[1]>>16);
+ }else{ # 24/8
+ b[0] = byte (a[0]>>16);
+ b[1] = byte (a[0]>>8);
+ b[2] = byte a[0];
+ }
+ b[3] = byte a[1];
+ 3 => # 8/8/16
+ b[0] = byte a[0];
+ b[1] = byte a[1];
+ b[2] = byte (a[2]>>16);
+ b[3] = byte a[2];
+ }
+ return 0;
+}
+
+parsemask(s: string, abits: int): (int, IPaddr)
+{
+ m := allbits.copy();
+ if(s == nil)
+ return (0, m);
+ if(s[0] != '/'){
+ (ok, a) := IPaddr.parse(s);
+ if(ok < 0)
+ return (0, m);
+ if(a.isv4())
+ a.a[0:] = m.a[0:IPv4off];
+ return (0, a);
+ }
+ if(len s == 1)
+ return (0, m);
+ nbit := int s[1:];
+ if(nbit < 0)
+ return (-1, m);
+ if(nbit > abits)
+ return (0, m);
+ nbit = abits-nbit;
+ i := IPaddrlen;
+ for(; nbit >= 8; nbit -= 8)
+ m.a[--i] = byte 0;
+ if(nbit > 0)
+ m.a[i-1] &= byte (~0<<nbit);
+ return (0, m);
+}
+
+IPaddr.text(a: self IPaddr): string
+{
+ b := a.a;
+ if(b == nil)
+ return "::";
+ if(a.isv4())
+ return sys->sprint("%d.%d.%d.%d", int b[IPv4off], int b[IPv4off+1], int b[IPv4off+2], int b[IPv4off+3]);
+ cs := -1;
+ nc := 0;
+ for(i:=0; i<IPaddrlen; i+=2)
+ if(int b[i] == 0 && int b[i+1] == 0){
+ for(j:=i+2; j<IPaddrlen; j+=2)
+ if(int b[j] != 0 || int b[j+1] != 0)
+ break;
+ if(j-i > nc){
+ nc = j-i;
+ cs = i;
+ }
+ }
+ if(nc <= 2)
+ cs = -1;
+ s := "";
+ for(i=0; i<IPaddrlen; ){
+ if(i == cs){
+ s += "::";
+ i += nc;
+ }else{
+ if(s != "" && s[len s-1]!=':')
+ s[len s] = ':';
+ v := (int a.a[i] << 8) | int a.a[i+1];
+ s += sys->sprint("%ux", v);
+ i += 2;
+ }
+ }
+ return s;
+}
+
+IPaddr.masktext(a: self IPaddr): string
+{
+ b := a.a;
+ if(b == nil)
+ return "/0";
+ for(i:=0; i<IPaddrlen; i++)
+ if(i == IPv4off)
+ return sys->sprint("%d.%d.%d.%d", int b[IPv4off], int b[IPv4off+1], int b[IPv4off+2], int b[IPv4off+3]);
+ else if(b[i] != byte 16rFF)
+ break;
+ for(j:=i+1; j<IPaddrlen; j++)
+ if(b[j] != byte 0)
+ return a.text();
+ nbit := 8*i;
+ if(i < IPaddrlen){
+ v := int b[i];
+ for(m := 16r80; m != 0; m >>= 1){
+ if((v & m) == 0)
+ break;
+ v &= ~m;
+ nbit++;
+ }
+ if(v != 0)
+ return a.text();
+ }
+ return sys->sprint("/%d", nbit);
+}
+
+addressesof(ifcs: list of ref Ipifc, all: int): list of IPaddr
+{
+ ra: list of IPaddr;
+ runi: list of IPaddr;
+ for(; ifcs != nil; ifcs = tl ifcs){
+ for(ifcas :=(hd ifcs).addrs; ifcs != nil; ifcs = tl ifcs){
+ a := (hd ifcas).ip;
+ if(all || !(a.eq(noaddr) || a.eq(v4noaddr))){ # ignore unspecified and loopback
+ if(a.ismulticast() || a.eq(selfv4) || a.eq(selfv6))
+ ra = a :: ra;
+ else
+ runi = a :: runi;
+ }
+ }
+ }
+ # unicast first, then others, both sets in order as found
+ # for ipv6, might want to give priority to unicast other than link- and site-local
+ al: list of IPaddr;
+ for(; ra != nil; ra = tl ra)
+ al = hd ra :: al;
+ for(; runi != nil; runi = tl runi)
+ al = hd runi :: al;
+ return al;
+}
+
+interfaceof(l: list of ref Ipifc, ip: IPaddr): (ref Ipifc, ref Ifcaddr)
+{
+ for(; l != nil; l = tl l){
+ ifc := hd l;
+ for(addrs := ifc.addrs; addrs != nil; addrs = tl addrs){
+ a := hd addrs;
+ if(ip.mask(a.mask).eq(a.net))
+ return (ifc, a);
+ }
+ }
+ return (nil, nil);
+}
+
+ownerof(l: list of ref Ipifc, ip: IPaddr): (ref Ipifc, ref Ifcaddr)
+{
+ for(; l != nil; l = tl l){
+ ifc := hd l;
+ for(addrs := ifc.addrs; addrs != nil; addrs = tl addrs){
+ a := hd addrs;
+ if(ip.eq(a.ip))
+ return (ifc, a);
+ }
+ }
+ return (nil, nil);
+}
+
+readipifc(net: string, index: int): (list of ref Ipifc, string)
+{
+ if(net == nil)
+ net = "/net";
+ if(index < 0){
+ ifcs: list of ref Ipifc;
+ dirfd := sys->open(net+"/ipifc", Sys->OREAD);
+ if(dirfd == nil)
+ return (nil, sys->sprint("%r"));
+ err: string;
+ for(;;){
+ (nd, dirs) := sys->dirread(dirfd);
+ if(nd <= 0){
+ if(nd < 0)
+ err = sys->sprint("%r");
+ break;
+ }
+ for(i:=0; i<nd; i++)
+ if((dn := dirs[i].name) != nil && dn[0]>='0' && dn[0]<='9'){
+ index = int dn;
+ ifc := readstatus(net+"/ipifc/"+dn+"/status", index);
+ if(ifc != nil)
+ ifcs = ifc :: ifcs;
+ }
+ }
+ l := ifcs;
+ for(ifcs = nil; l != nil; l = tl l)
+ ifcs = hd l :: ifcs;
+ return (ifcs, err);
+ }
+ ifc := readstatus(net+"/ipifc/"+string index+"/status", index);
+ if(ifc == nil)
+ return (nil, sys->sprint("%r"));
+ return (ifc :: nil, nil);
+}
+
+#
+# return data structure containing values read from status file:
+#
+# device /net/ether0 maxtu 1514 sendra 0 recvra 0 mflag 0 oflag 0 maxraint 600000 minraint 200000 linkmtu 0 reachtime 0 rxmitra 0 ttl 255 routerlt 1800000 pktin 47609 pktout 42322 errin 0 errout 0
+# 144.32.112.83 /119 144.32.112.0 4294967295 4294967295
+# ...
+#
+
+readstatus(file: string, index: int): ref Ipifc
+{
+ fd := sys->open(file, Sys->OREAD);
+ if(fd == nil)
+ return nil;
+ contents := slurp(fd);
+ fd = nil;
+ (nline, lines) := sys->tokenize(contents, "\n");
+ if(nline <= 0){
+ sys->werrstr("unexpected ipifc status file format");
+ return nil;
+ }
+ (nil, details) := sys->tokenize(hd lines, " \t\n");
+ lines = tl lines;
+ ifc := ref Ipifc;
+ ifc.index = index;
+ ifc.dev = valof(details, "device");
+ ifc.mtu = int valof(details, "maxtu");
+ ifc.pktin = big valof(details, "pktin");
+ ifc.pktout = big valof(details, "pktout");
+ ifc.errin = big valof(details, "errin");
+ ifc.errout = big valof(details, "errout");
+ ifc.sendra = int valof(details, "sendra");
+ ifc.recvra = int valof(details, "recvra");
+ ifc.rp.mflag = int valof(details, "mflag");
+ ifc.rp.oflag = int valof(details, "oflag");
+ ifc.rp.maxraint = int valof(details, "maxraint");
+ ifc.rp.minraint = int valof(details, "minraint");
+ ifc.rp.linkmtu = int valof(details, "linkmtu");
+ ifc.rp.reachtime = int valof(details, "reachtime");
+ ifc.rp.rxmitra = int valof(details, "rxmitra");
+ ifc.rp.ttl = int valof(details, "ttl");
+ ifc.rp.routerlt = int valof(details, "routerlt");
+ addrs: list of ref Ifcaddr;
+ for(; lines != nil; lines = tl lines){
+ (nf, fields) := sys->tokenize(hd lines, " \t\n");
+ if(nf >= 3){
+ addr := ref Ifcaddr;
+ (nil, addr.ip) = IPaddr.parse(hd fields); fields = tl fields;
+ (nil, addr.mask) = IPaddr.parsemask(hd fields); fields = tl fields;
+ (nil, addr.net) = IPaddr.parse(hd fields); fields = tl fields;
+ if(nf >= 5){
+ addr.preflt = big hd fields; fields = tl fields;
+ addr.validlt = big hd fields; fields = tl fields;
+ }else{
+ addr.preflt = big 0;
+ addr.validlt = big 0;
+ }
+ addrs = addr :: addrs;
+ }
+ }
+ for(; addrs != nil; addrs = tl addrs)
+ ifc.addrs = hd addrs :: ifc.addrs;
+ return ifc;
+}
+
+slurp(fd: ref Sys->FD): string
+{
+ buf := array[2048] of byte;
+ s := "";
+ while((n := sys->read(fd, buf, len buf)) > 0)
+ s += string buf[0:n];
+ return s;
+}
+
+valof(l: list of string, attr: string): string
+{
+ while(l != nil){
+ label := hd l;
+ l = tl l;
+ if(label == attr){
+ if(l == nil)
+ return nil;
+ return hd l;
+ }
+ if(l != nil)
+ l = tl l;
+ }
+ return nil;
+}
+
+Udphdr.new(): ref Udphdr
+{
+ return ref Udphdr(noaddr, noaddr, noaddr, 0, 0);
+}
+
+Udphdr.unpack(a: array of byte, n: int): ref Udphdr
+{
+ case n {
+ Udp4hdrlen =>
+ u := ref Udphdr;
+ u.raddr = IPaddr.newv4(a[0:]);
+ u.laddr = IPaddr.newv4(a[IPv4addrlen:]);
+ u.rport = get2(a, 2*IPv4addrlen);
+ u.lport = get2(a, 2*IPv4addrlen+2);
+ u.ifcaddr = u.laddr.copy();
+ return u;
+ OUdphdrlen =>
+ u := ref Udphdr;
+ u.raddr = IPaddr.newv6(a[0:]);
+ u.laddr = IPaddr.newv6(a[IPaddrlen:]);
+ u.rport = get2(a, 2*IPaddrlen);
+ u.lport = get2(a, 2*IPaddrlen+2);
+ u.ifcaddr = u.laddr.copy();
+ return u;
+ Udphdrlen =>
+ u := ref Udphdr;
+ u.raddr = IPaddr.newv6(a[0:]);
+ u.laddr = IPaddr.newv6(a[IPaddrlen:]);
+ u.ifcaddr = IPaddr.newv6(a[2*IPaddrlen:]);
+ u.rport = get2(a, 3*IPaddrlen);
+ u.lport = get2(a, 3*IPaddrlen+2);
+ return u;
+ * =>
+ raise "Udphdr.unpack: bad length";
+ }
+}
+
+Udphdr.pack(u: self ref Udphdr, a: array of byte, n: int)
+{
+ case n {
+ Udp4hdrlen =>
+ a[0:] = u.raddr.v4();
+ a[IPv4addrlen:] = u.laddr.v4();
+ put2(a, 2*IPv4addrlen, u.rport);
+ put2(a, 2*IPv4addrlen+2, u.lport);
+ OUdphdrlen =>
+ a[0:] = u.raddr.v6();
+ a[IPaddrlen:] = u.laddr.v6();
+ put2(a, 2*IPaddrlen, u.rport);
+ put2(a, 2*IPaddrlen+2, u.lport);
+ Udphdrlen =>
+ a[0:] = u.raddr.v6();
+ a[IPaddrlen:] = u.laddr.v6();
+ a[2*IPaddrlen:] = u.ifcaddr.v6();
+ put2(a, 3*IPaddrlen, u.rport);
+ put2(a, 3*IPaddrlen+2, u.lport);
+ * =>
+ raise "Udphdr.pack: bad length";
+ }
+}
+
+get2(a: array of byte, o: int): int
+{
+ return (int a[o] << 8) | int a[o+1];
+}
+
+put2(a: array of byte, o: int, val: int): int
+{
+ a[o] = byte (val>>8);
+ a[o+1] = byte val;
+ return o+2;
+}
+
+get4(a: array of byte, o: int): int
+{
+ return (((((int a[o] << 8)| int a[o+1]) << 8) | int a[o+2]) << 8) | int a[o+3];
+}
+
+put4(a: array of byte, o: int, val: int): int
+{
+ a[o] = byte (val>>24);
+ a[o+1] = byte (val>>16);
+ a[o+2] = byte (val>>8);
+ a[o+3] = byte val;
+ return o+4;
+}