ref: 51fdecc65a1ae47d7c8219828af7c990884e159f
parent: 58055b9e3a8c6df1cd128a3d4ed3667eab94e472
author: 9ferno <gophone2015@gmail.com>
date: Sun Aug 15 01:19:51 EDT 2021
changed dhcp module names
--- a/appl/cmd/ip/dhcp.b
+++ b/appl/cmd/ip/dhcp.b
@@ -15,7 +15,7 @@
IPaddr: import ip;
get2, get4, put2, put4: import ip;
-include "dhcp.m";
+include "dhcpclient.m";
dhcpclient: Dhcpclient;
Bootconf, Lease: import dhcpclient;
@@ -56,7 +56,7 @@
'm' => monitor = 1;
'n' => noctl = 1;
'p' => pcfg = 1;
- 'r' => retry = 1;
+ 'r' => retry = 1;
'x' => netdir = arg->earg();
* => arg->usage();
}
--- a/appl/cmd/ip/dhcpd.b
+++ b/appl/cmd/ip/dhcpd.b
@@ -24,7 +24,7 @@
lists: Lists;
include "encoding.m";
base16: Encoding;
-include "dhcpd.m";
+include "dhcpserver.m";
dhcp: Dhcpserver;
Dhcpmsg, Opt: import dhcp;
include "ipval.m";
--- a/appl/cmd/ip/mkfile
+++ b/appl/cmd/ip/mkfile
@@ -19,7 +19,8 @@
attrdb.m\
bufio.m\
daytime.m\
- dhcp.m\
+ dhcpclient.m\
+ dhcpserver.m\
draw.m\
encoding.m\
ether.m\
--- a/appl/lib/dhcp.b
+++ /dev/null
@@ -1,1033 +1,0 @@
-implement Dhcpclient;
-
-#
-# DHCP and BOOTP clients
-# Copyright © 2004-2006 Vita Nuova Holdings Limited
-#
-
-include "sys.m";
- sys: Sys;
-
-include "ip.m";
- ip: IP;
- IPv4off, IPaddrlen, Udphdrlen, Udpraddr, Udpladdr, Udprport, Udplport: import IP;
- IPaddr: import ip;
- get2, get4, put2, put4: import ip;
-
-include "keyring.m";
-include "security.m"; # for Random
-
-include "dial.m";
- dial: Dial;
-
-include "dhcp.m";
-
-debug := 0;
-
-xidgen: int;
-
-init()
-{
- sys = load Sys Sys->PATH;
- random := load Random Random->PATH;
- if(random != nil)
- xidgen = random->randomint(Random->NotQuiteRandom);
- else
- xidgen = sys->pctl(0, nil)*sys->millisec();
- random = nil;
- dial = load Dial Dial->PATH;
- ip = load IP IP->PATH;
- ip->init();
-}
-
-tracing(d: int)
-{
- debug = d;
-}
-
-Bootconf.new(): ref Bootconf
-{
- bc := ref Bootconf;
- bc.lease = 0;
- bc.options = array[256] of array of byte;
- return bc;
-}
-
-Bootconf.get(c: self ref Bootconf, n: int): array of byte
-{
- a := c.options;
- if(n & Ovendor){
- a = c.vendor;
- n &= ~Ovendor;
- }
- if(n < 0 || n >= len a)
- return nil;
- return a[n];
-}
-
-Bootconf.getint(c: self ref Bootconf, n: int): int
-{
- a := c.get(n);
- v := 0;
- for(i := 0; i < len a; i++)
- v = (v<<8) | int a[i];
- return v;
-}
-
-Bootconf.getip(c: self ref Bootconf, n: int): string
-{
- l := c.getips(n);
- if(l == nil)
- return nil;
- return hd l;
-}
-
-Bootconf.getips(c: self ref Bootconf, n: int): list of string
-{
- a := c.get(n);
- rl: list of string;
- while(len a >= 4){
- rl = v4text(a) :: rl;
- a = a[4:];
- }
- l: list of string;
- for(; rl != nil; rl = tl rl)
- l = hd rl :: l;
- return l;
-}
-
-Bootconf.gets(c: self ref Bootconf, n: int): string
-{
- a := c.get(n);
- if(a == nil)
- return nil;
- for(i:=0; i<len a; i++)
- if(a[i] == byte 0)
- break;
- return string a[0:i];
-}
-
-Bootconf.put(c: self ref Bootconf, n: int, a: array of byte)
-{
- if(n < 0 || n >= len c.options)
- return;
- ca := array[len a] of byte;
- ca[0:] = a;
- c.options[n] = ca;
-}
-
-Bootconf.putint(c: self ref Bootconf, n: int, v: int)
-{
- if(n < 0 || n >= len c.options)
- return;
- a := array[4] of byte;
- put4(a, 0, v);
- c.options[n] = a;
-}
-
-Bootconf.putips(c: self ref Bootconf, n: int, ips: list of string)
-{
- if(n < 0 || n >= len c.options)
- return;
- na := len ips;
- a := array[na*4] of byte;
- na = 0;
- for(; ips != nil; ips = tl ips){
- (nil, ipa) := IPaddr.parse(hd ips);
- a[na++:] = ipa.v4();
- }
- c.options[n] = a;
-}
-
-Bootconf.puts(c: self ref Bootconf, n: int, s: string)
-{
- if(n < 0 || n >= len c.options)
- return;
- c.options[n] = array of byte s;
-}
-
-#
-#
-# DHCP
-#
-#
-
-# BOOTP operations
-Bootprequest, Bootpreply: con 1+iota;
-
-# DHCP operations
-NotDHCP, Discover, Offer, Request, Decline, Ack, Nak, Release, Inform: con iota;
-
-Dhcp: adt {
- udphdr: array of byte;
- op: int;
- htype: int;
- hops: int;
- xid: int;
- secs: int;
- flags: int;
- ciaddr: IPaddr;
- yiaddr: IPaddr;
- siaddr: IPaddr;
- giaddr: IPaddr;
- chaddr: array of byte;
- sname: string;
- file: string;
- options: list of (int, array of byte);
- dhcpop: int;
-};
-
-opnames := array[] of {
- Discover => "Discover",
- Offer => "Offer",
- Request => "Request",
- Decline => "Decline",
- Ack => "Ack",
- Nak => "Nak",
- Release => "Release",
- Inform => "Inform"
-};
-
-opname(op: int): string
-{
- if(op >= 0 && op < len opnames)
- return opnames[op];
- return sys->sprint("OP%d", op);
-}
-
-stringget(buf: array of byte): string
-{
- for(x := 0; x < len buf; x++)
- if(buf[x] == byte 0)
- break;
- if(x == 0)
- return nil;
- return string buf[0 : x];
-}
-
-eqbytes(b1: array of byte, b2: array of byte): int
-{
- l := len b1;
- if(l != len b2)
- return 0;
- for(i := 0; i < l; i++)
- if(b1[i] != b2[i])
- return 0;
- return 1;
-}
-
-magic := array[] of {byte 99, byte 130, byte 83, byte 99}; # RFC2132 (replacing RFC1048)
-
-dhcpsend(fd: ref Sys->FD, xid: int, dhcp: ref Dhcp)
-{
- dhcp.xid = xid;
- abuf := array[576+Udphdrlen] of {* => byte 0};
- abuf[0:] = dhcp.udphdr;
- buf := abuf[Udphdrlen:];
- buf[0] = byte dhcp.op;
- buf[1] = byte dhcp.htype;
- buf[2] = byte len dhcp.chaddr;
- buf[3] = byte dhcp.hops;
- put4(buf, 4, xid);
- put2(buf, 8, dhcp.secs);
- put2(buf, 10, dhcp.flags);
- buf[12:] = dhcp.ciaddr.v4();
- buf[16:] = dhcp.yiaddr.v4();
- buf[20:] = dhcp.siaddr.v4();
- buf[24:] = dhcp.giaddr.v4();
- buf[28:] = dhcp.chaddr;
- buf[44:] = array of byte dhcp.sname; # [64]
- buf[108:] = array of byte dhcp.file; # [128]
- o := 236;
- # RFC1542 suggests including magic and Oend as a minimum, even in BOOTP
- buf[o:] = magic;
- o += 4;
- if(dhcp.dhcpop != NotDHCP){
- buf[o++] = byte Otype;
- buf[o++] = byte 1;
- buf[o++] = byte dhcp.dhcpop;
- }
- for(ol := dhcp.options; ol != nil; ol = tl ol){
- (opt, val) := hd ol;
- buf[o++] = byte opt;
- buf[o++] = byte len val;
- if(len val > 0){
- buf[o:] = val;
- o += len val;
- }
- }
- buf[o++] = byte Oend;
- if(debug)
- dumpdhcp(dhcp, "->");
- sys->write(fd, abuf, len abuf);
-}
-
-kill(pid: int, grp: string)
-{
- fd := sys->open("#p/" + string pid + "/ctl", sys->OWRITE);
- if(fd != nil)
- sys->fprint(fd, "kill%s", grp);
-}
-
-v4text(a: array of byte): string
-{
- return sys->sprint("%ud.%ud.%ud.%ud", int a[0], int a[1], int a[2], int a[3]);
-}
-
-parseopt(a: array of byte, isdhcp: int): (int, list of (int, array of byte))
-{
- opts: list of (int, array of byte);
- xop := NotDHCP;
- for(i := 0; i < len a;){
- op := int a[i++];
- if(op == Opad)
- continue;
- if(op == Oend || i >= len a)
- break;
- l := int a[i++];
- if(i+l > len a)
- break;
- if(isdhcp && op == Otype)
- xop = int a[i];
- else
- opts = (op, a[i:i+l]) :: opts;
- i += l;
- }
- rl := opts;
- opts = nil;
- for(; rl != nil; rl = tl rl)
- opts = hd rl :: opts;
- return (xop, opts);
-}
-
-dhcpreader(pidc: chan of int, srv: ref DhcpIO)
-{
- pidc <-= sys->pctl(0, nil);
- for(;;){
- abuf := array [576+Udphdrlen] of byte;
- n := sys->read(srv.fd, abuf, len abuf);
- if(n < 0){
- if(debug)
- sys->print("read error: %r\n");
- sys->sleep(1000);
- continue;
- }
- if(n < Udphdrlen+236){
- if(debug)
- sys->print("short read: %d\n", n);
- continue;
- }
- buf := abuf[Udphdrlen:n];
- n -= Udphdrlen;
- dhcp := ref Dhcp;
- dhcp.op = int buf[0];
- if(dhcp.op != Bootpreply){
- if(debug)
- sys->print("bootp: not reply, discarded\n");
- continue;
- }
- dhcp.dhcpop = NotDHCP;
- if(n >= 240 && eqbytes(buf[236:240], magic)) # otherwise it's something we won't understand
- (dhcp.dhcpop, dhcp.options) = parseopt(buf[240:n], 1);
- case dhcp.dhcpop {
- NotDHCP or Ack or Nak or Offer =>
- ;
- * =>
- if(debug)
- sys->print("dhcp: ignore dhcp op %d\n", dhcp.dhcpop);
- continue;
- }
- dhcp.udphdr = abuf[0:Udphdrlen];
- dhcp.htype = int buf[1];
- hlen := int buf[2];
- dhcp.hops = int buf[3];
- dhcp.xid = get4(buf, 4);
- dhcp.secs = get2(buf, 8);
- dhcp.flags = get2(buf, 10);
- dhcp.ciaddr = IPaddr.newv4(buf[12:]);
- dhcp.yiaddr = IPaddr.newv4(buf[16:]);
- dhcp.siaddr = IPaddr.newv4(buf[20:]);
- dhcp.giaddr = IPaddr.newv4(buf[24:]);
- dhcp.chaddr = buf[28 : 28 + hlen];
- dhcp.sname = stringget(buf[44 : 108]);
- dhcp.file = stringget(buf[108 : 236]);
- srv.dc <-= dhcp;
- }
-}
-
-timeoutstart(msecs: int): (int, chan of int)
-{
- tc := chan of int;
- spawn timeoutproc(tc, msecs);
- return (<-tc, tc);
-}
-
-timeoutproc(c: chan of int, msecs: int)
-{
- c <-= sys->pctl(0, nil);
- sys->sleep(msecs);
- c <-= 1;
-}
-
-hex(b: int): int
-{
- if(b >= '0' && b <= '9')
- return b-'0';
- if(b >= 'A' && b <= 'F')
- return b-'A' + 10;
- if(b >= 'a' && b <= 'f')
- return b-'a' + 10;
- return -1;
-}
-
-gethaddr(device: string): (int, string, array of byte)
-{
- fd := sys->open(device, Sys->OREAD);
- if(fd == nil)
- return (-1, sys->sprint("%r"), nil);
- buf := array [100] of byte;
- n := sys->read(fd, buf, len buf);
- if(n < 0)
- return (-1, sys->sprint("%r"), nil);
- if(n == 0)
- return (-1, "empty address file", nil);
- addr := array [n/2] of byte;
- for(i := 0; i < len addr; i++){
- u := hex(int buf[2*i]);
- l := hex(int buf[2*i+1]);
- if(u < 0 || l < 0)
- return (-1, "bad address syntax", nil);
- addr[i] = byte ((u<<4)|l);
- }
- return (1, nil, addr);
-}
-
-newrequest(dest: IPaddr, bootfile: string, htype: int, haddr: array of byte, ipaddr: IPaddr, options: array of array of byte): ref Dhcp
-{
- dhcp := ref Dhcp;
- dhcp.op = Bootprequest;
- hdr := array[Udphdrlen] of {* => byte 0};
- hdr[Udpraddr:] = dest.v6();
- put2(hdr, Udprport, 67);
- dhcp.udphdr = hdr;
- dhcp.htype = htype;
- dhcp.chaddr = haddr;
- dhcp.hops = 0;
- dhcp.secs = 0;
- dhcp.flags = 0;
- dhcp.xid = 0;
- dhcp.ciaddr = ipaddr;
- dhcp.yiaddr = ip->v4noaddr;
- dhcp.siaddr = ip->v4noaddr;
- dhcp.giaddr = ip->v4noaddr;
- dhcp.file = bootfile;
- dhcp.dhcpop = NotDHCP;
- if(options != nil){
- for(i := 0; i < len options; i++)
- if(options[i] != nil)
- dhcp.options = (i, options[i]) :: dhcp.options;
- }
- clientid := array[len haddr + 1] of byte;
- clientid[0] = byte htype;
- clientid[1:] = haddr;
- dhcp.options = (Oclientid, clientid) :: dhcp.options;
- dhcp.options = (Ovendorclass, array of byte "plan9_386") :: dhcp.options; # 386 will do because type doesn't matter
- return dhcp;
-}
-
-udpannounce(net: string): (ref Sys->FD, string)
-{
- if(net == nil)
- net = "/net";
- conn := dial->announce(net+"/udp!*!68");
- if(conn == nil)
- return (nil, sys->sprint("can't announce dhcp port: %r"));
- if(sys->fprint(conn.cfd, "headers") < 0)
- return (nil, sys->sprint("can't set headers mode on dhcp port: %r"));
- conn.dfd = sys->open(conn.dir+"/data", Sys->ORDWR);
- if(conn.dfd == nil)
- return (nil, sys->sprint("can't open %s: %r", conn.dir+"/data"));
- return (conn.dfd, nil);
-}
-
-ifcnoaddr(fd: ref Sys->FD, s: string)
-{
- if(fd != nil && sys->fprint(fd, "%s %s %s", s, (ip->noaddr).text(), (ip->noaddr).text()) < 0){
- if(debug)
- sys->print("dhcp: ctl %s: %r\n", s);
- }
-}
-
-setup(net: string, device: string, init: ref Bootconf): (ref Dhcp, ref DhcpIO, string)
-{
- (htype, err, mac) := gethaddr(device);
- if(htype < 0)
- return (nil, nil, sys->sprint("can't get hardware MAC address: %s", err));
- ciaddr := ip->v4noaddr;
- if(init != nil && init.ip != nil){
- valid: int;
- (valid, ciaddr) = IPaddr.parse(init.ip);
- if(valid < 0)
- return (nil, nil, sys->sprint("invalid ip address: %s", init.ip));
- }
- (dfd, err2) := udpannounce(net);
- if(err2 != nil)
- return (nil, nil, err);
- bootfile: string;
- options: array of array of byte;
- if(init != nil){
- bootfile = init.bootf;
- options = init.options;
- }
- return (newrequest(ip->v4bcast, bootfile, htype, mac, ciaddr, options), DhcpIO.new(dfd), nil);
-}
-
-#
-# BOOTP (RFC951) is used by Inferno only during net boots, to get initial IP address and TFTP address and parameters
-#
-bootp(net: string, ctlifc: ref Sys->FD, device: string, init: ref Bootconf): (ref Bootconf, string)
-{
- (req, srv, err) := setup(net, device, init);
- if(err != nil)
- return (nil, err);
- ifcnoaddr(ctlifc, "add");
- rdhcp := exchange(srv, ++xidgen, req, 1<<NotDHCP);
- srv.rstop();
- ifcnoaddr(ctlifc, "remove");
- if(rdhcp == nil)
- return (nil, "no response to BOOTP request");
- return (fillbootconf(init, rdhcp), nil);
-}
-
-defparams := array[] of {
- byte Omask, byte Orouter, byte Odnsserver, byte Ohostname, byte Odomainname, byte Ontpserver,
-};
-
-#
-# DHCP (RFC2131)
-#
-dhcp(net: string, ctlifc: ref Sys->FD, device: string, init: ref Bootconf, needparam: array of int): (ref Bootconf, ref Lease, string)
-{
- (req, srv, err) := setup(net, device, init);
- if(err != nil)
- return (nil, nil, err);
- params := defparams;
- if(needparam != nil){
- n := len defparams;
- params = array[n+len needparam] of byte;
- params[0:] = defparams;
- for(i := 0; i < len needparam; i++)
- params[n+i] = byte needparam[i];
- }
- initopt := (Oparams, params) :: req.options; # RFC2131 requires parameters to be repeated each time
- lease := ref Lease(0, chan[1] of (ref Bootconf, string));
- spawn dhcp1(srv, lease, net, ctlifc, req, init, initopt);
- bc: ref Bootconf;
- (bc, err) = <-lease.configs;
- return (bc, lease, err);
-}
-
-dhcp1(srv: ref DhcpIO, lease: ref Lease, net: string, ctlifc: ref Sys->FD, req: ref Dhcp, init: ref Bootconf, initopt: list of (int, array of byte))
-{
- cfd := -1;
- if(ctlifc != nil)
- cfd = ctlifc.fd;
- lease.pid = sys->pctl(Sys->NEWPGRP|Sys->NEWFD, 1 :: srv.fd.fd :: cfd :: nil);
- if(ctlifc != nil)
- ctlifc = sys->fildes(ctlifc.fd);
- srv.fd = sys->fildes(srv.fd.fd);
- rep: ref Dhcp;
- ifcnoaddr(ctlifc, "add");
- if(req.ciaddr.isvalid())
- rep = reacquire(srv, req, initopt, req.ciaddr);
- if(rep == nil)
- rep = askround(srv, req, initopt);
- srv.rstop();
- ifcnoaddr(ctlifc, "remove");
- if(rep == nil){
- lease.pid = 0;
- lease.configs <-= (nil, "no response");
- exit;
- }
- for(;;){
- conf := fillbootconf(init, rep);
- applycfg(net, ctlifc, conf);
- if(conf.lease == 0){
- srv.rstop();
- lease.pid = 0;
- flush(lease.configs);
- lease.configs <-= (conf, nil);
- exit;
- }
- flush(lease.configs);
- lease.configs <-= (conf, nil);
- req.ciaddr = rep.yiaddr;
- while((rep = tenancy(srv, req, conf.lease)) != nil){
- if(rep.dhcpop == Nak || !rep.ciaddr.eq(req.ciaddr))
- break;
- req.udphdr[Udpraddr:] = rep.udphdr[Udpraddr:Udpraddr+IPaddrlen];
- conf = fillbootconf(init, rep);
- }
- removecfg(net, ctlifc, conf);
- ifcnoaddr(ctlifc, "add");
- while((rep = askround(srv, req, initopt)) == nil){
- flush(lease.configs);
- lease.configs <-= (nil, "no response");
- srv.rstop();
- sys->sleep(60*1000);
- }
- ifcnoaddr(ctlifc, "remove");
- }
-}
-
-reacquire(srv: ref DhcpIO, req: ref Dhcp, initopt: list of (int, array of byte), addr: IPaddr): ref Dhcp
-{
- # INIT-REBOOT: know an address; try requesting it (once)
- # TO DO: could use Inform when our address is static but we need a few service parameters
- req.ciaddr = ip->v4noaddr;
- rep := request(srv, ++xidgen, req, (Oipaddr, addr.v4()) :: initopt);
- if(rep != nil && rep.dhcpop == Ack && addr.eq(rep.yiaddr)){
- if(debug)
- sys->print("req: server accepted\n");
- req.udphdr[Udpraddr:] = rep.udphdr[Udpraddr:Udpraddr+IPaddrlen];
- return rep;
- }
- if(debug)
- sys->print("req: cannot reclaim\n");
- return nil;
-}
-
-askround(srv: ref DhcpIO, req: ref Dhcp, initopt: list of (int, array of byte)): ref Dhcp
-{
- # INIT
- req.ciaddr = ip->v4noaddr;
- req.udphdr[Udpraddr:] = (ip->v4bcast).v6();
- for(retries := 0; retries < 5; retries++){
- # SELECTING
- req.dhcpop = Discover;
- req.options = initopt;
- rep := exchange(srv, ++xidgen, req, 1<<Offer);
- if(rep == nil)
- break;
- #
- # could wait a little while and accumulate offers, but is it sensible?
- # we do sometimes see arguments between DHCP servers that could
- # only be resolved by user choice
- #
- if(!rep.yiaddr.isvalid())
- continue; # server has no idea either
- serverid := getopt(rep.options, Oserverid, 4);
- if(serverid == nil)
- continue; # broken server
- # REQUESTING
- options := (Oserverid, serverid) :: (Oipaddr, rep.yiaddr.v4()) :: initopt;
- lease := getlease(rep);
- if(lease != nil)
- options = (Olease, lease) :: options;
- rep = request(srv, rep.xid, req, options);
- if(rep != nil){
- # could probe with ARP here, and if found, Decline
- if(debug)
- sys->print("req: server accepted\n");
- req.udphdr[Udpraddr:] = rep.udphdr[Udpraddr:Udpraddr+IPaddrlen];
- return rep;
- }
- }
- return nil;
-}
-
-request(srv: ref DhcpIO, xid: int, req: ref Dhcp, options: list of (int, array of byte)): ref Dhcp
-{
- req.dhcpop = Request; # Selecting
- req.options = options;
- rep := exchange(srv, xid, req, (1<<Ack)|(1<<Nak));
- if(rep == nil || rep.dhcpop == Nak)
- return nil;
- return rep;
-}
-
-# renew
-# direct to server from T1 to T2 [RENEW]
-# Request must not include
-# requested IP address, server identifier
-# Request must include
-# ciaddr set to client's address
-# Request might include
-# lease time
-# similar, but broadcast, from T2 to T3 [REBIND]
-# at T3, unbind, restart Discover
-
-tenancy(srv: ref DhcpIO, req: ref Dhcp, leasesec: int): ref Dhcp
-{
- # configure address...
- t3 := big leasesec * big 1000; # lease expires; restart
- t2 := (big 3 * t3)/big 4; # broadcast renewal request at ¾time
- t1 := t2/big 2; # renew lease with original server at ½time
- srv.rstop();
- thebigsleep(t1);
- # RENEW
- rep := renewing(srv, req, t1, t2);
- if(rep != nil)
- return rep;
- # REBIND
- req.udphdr[Udpraddr:] = (ip->v4bcast).v6(); # now try broadcast
- return renewing(srv, req, t2, t3);
-}
-
-renewing(srv: ref DhcpIO, req: ref Dhcp, a: big, b: big): ref Dhcp
-{
- Minute: con big(60*1000);
- while(a < b){
- rep := exchange(srv, req.xid, req, (1<<Ack)|(1<<Nak));
- if(rep != nil)
- return rep;
- delta := (b-a)/big 2;
- if(delta < Minute)
- delta = Minute;
- thebigsleep(delta);
- a += delta;
- }
- return nil;
-}
-
-thebigsleep(msec: big)
-{
- Day: con big (24*3600*1000); # 1 day in msec
- while(msec > big 0){
- n := msec;
- if(n > Day)
- n = Day;
- sys->sleep(int n);
- msec -= n;
- }
-}
-
-getlease(m: ref Dhcp): array of byte
-{
- lease := getopt(m.options, Olease, 4);
- if(lease == nil)
- return nil;
- if(get4(lease, 0) == 0){
- lease = array[4] of byte;
- put4(lease, 0, 15*60);
- }
- return lease;
-}
-
-fillbootconf(init: ref Bootconf, pkt: ref Dhcp): ref Bootconf
-{
- bc := ref Bootconf;
- if(init != nil)
- *bc = *init;
- if(bc.options == nil)
- bc.options = array[256] of array of byte;
- for(l := pkt.options; l != nil; l = tl l){
- (c, v) := hd l;
- if(bc.options[c] == nil)
- bc.options[c] = v; # give priority to first occurring
- }
- if((a := bc.get(Ovendorinfo)) != nil){
- if(bc.vendor == nil)
- bc.vendor = array[256] of array of byte;
- for(l = parseopt(a, 0).t1; l != nil; l = tl l){
- (c, v) := hd l;
- if(bc.vendor[c] == nil)
- bc.vendor[c] = v;
- }
- }
- if(pkt.yiaddr.isvalid()){
- bc.ip = pkt.yiaddr.text();
- bc.ipmask = bc.getip(Omask);
- if(bc.ipmask == nil)
- bc.ipmask = pkt.yiaddr.classmask().masktext();
- }
- bc.bootf = pkt.file;
- bc.dhcpip = IPaddr.newv6(pkt.udphdr[Udpraddr:]).text();
- bc.siaddr = pkt.siaddr.text();
- bc.lease = bc.getint(Olease);
- if(bc.lease == Infinite)
- bc.lease = 0;
- else if(debug > 1)
- bc.lease = 2*60; # shorten time, for testing
- bc.dom = bc.gets(Odomainname);
- s := bc.gets(Ohostname);
- for(i:=0; i<len s; i++)
- if(s[i] == '.'){
- if(bc.dom == nil)
- bc.dom = s[i+1:];
- s = s[0:i];
- break;
- }
- bc.sys = s;
- bc.ipgw = bc.getip(Orouter);
- bc.bootip = bc.getip(Otftpserver);
- bc.serverid = bc.getip(Oserverid);
- return bc;
-}
-
-Lease.release(l: self ref Lease)
-{
- # could send a Release message
- # should unconfigure
- if(l.pid){
- kill(l.pid, "grp");
- l.pid = 0;
- }
-}
-
-flush(c: chan of (ref Bootconf, string))
-{
- alt{
- <-c => ;
- * => ;
- }
-}
-
-DhcpIO: adt {
- fd: ref Sys->FD;
- pid: int;
- dc: chan of ref Dhcp;
- new: fn(fd: ref Sys->FD): ref DhcpIO;
- rstart: fn(io: self ref DhcpIO);
- rstop: fn(io: self ref DhcpIO);
-};
-
-DhcpIO.new(fd: ref Sys->FD): ref DhcpIO
-{
- return ref DhcpIO(fd, 0, chan of ref Dhcp);
-}
-
-DhcpIO.rstart(io: self ref DhcpIO)
-{
- if(io.pid == 0){
- pids := chan of int;
- spawn dhcpreader(pids, io);
- io.pid = <-pids;
- }
-}
-
-DhcpIO.rstop(io: self ref DhcpIO)
-{
- if(io.pid != 0){
- kill(io.pid, "");
- io.pid = 0;
- }
-}
-
-getopt(options: list of (int, array of byte), op: int, minlen: int): array of byte
-{
- for(; options != nil; options = tl options){
- (opt, val) := hd options;
- if(opt == op && len val >= minlen)
- return val;
- }
- return nil;
-}
-
-exchange(srv: ref DhcpIO, xid: int, req: ref Dhcp, accept: int): ref Dhcp
-{
- srv.rstart();
- nsec := 3;
- for(count := 0; count < 5; count++) {
- (tpid, tc) := timeoutstart(nsec*1000);
- dhcpsend(srv.fd, xid, req);
- Wait:
- for(;;){
- alt {
- <-tc=>
- break Wait;
- rep := <-srv.dc=>
- if(debug)
- dumpdhcp(rep, "<-");
- if(rep.op == Bootpreply &&
- rep.xid == req.xid &&
- rep.ciaddr.eq(req.ciaddr) &&
- eqbytes(rep.chaddr, req.chaddr)){
- if((accept & (1<<rep.dhcpop)) == 0){
- if(debug)
- sys->print("req: unexpected reply %s to %s\n", opname(rep.dhcpop), opname(req.dhcpop));
- continue;
- }
- kill(tpid, "");
- return rep;
- }
- if(debug)
- sys->print("req: mismatch\n");
- }
- }
- req.secs += nsec;
- nsec++;
- }
- return nil;
-}
-
-applycfg(net: string, ctlfd: ref Sys->FD, bc: ref Bootconf): string
-{
- # write addresses to /net/...
- # local address, mask[or default], remote address [mtu]
- if(net == nil)
- net = "/net";
- if(bc.ip == nil)
- return "invalid address";
- if(ctlfd != nil){
- if(sys->fprint(ctlfd, "add %s %s", bc.ip, bc.ipmask) < 0) # TO DO: [raddr [mtu]]
- return sys->sprint("add interface: %r");
- # could use "mtu n" request to set/change mtu
- }
- # if primary:
- # add default route if gateway valid
- # put ndb entries ip=, ipmask=, ipgw=; sys= dom=; fs=; auth=; dns=; ntp=; other options from bc.options
- if(bc.ipgw != nil){
- fd := sys->open(net+"/iproute", Sys->OWRITE);
- if(fd != nil)
- sys->fprint(fd, "add 0 0 %s", bc.ipgw);
- }
- s := sys->sprint("ip=%s ipmask=%s", bc.ip, bc.ipmask);
- if(bc.ipgw != nil)
- s += sys->sprint(" ipgw=%s", bc.ipgw);
- s += "\n";
- if(bc.sys != nil)
- s += sys->sprint(" sys=%s\n", bc.sys);
- if(bc.dom != nil)
- s += sys->sprint(" dom=%s.%s\n", bc.sys, bc.dom);
- if((addr := bc.getip(OP9auth)) != nil)
- s += sys->sprint(" auth=%s\n", addr); # TO DO: several addresses
- if((addr = bc.getip(OP9fs)) != nil)
- s += sys->sprint(" fs=%s\n", addr);
- if((addr = bc.getip(Odnsserver)) != nil)
- s += sys->sprint(" dns=%s\n", addr);
- fd := sys->open(net+"/ndb", Sys->OWRITE | Sys->OTRUNC);
- if(fd != nil){
- a := array of byte s;
- sys->write(fd, a, len a);
- }
- return nil;
-}
-
-removecfg(nil: string, ctlfd: ref Sys->FD, bc: ref Bootconf): string
-{
- # remove localaddr, localmask[or default]
- if(ctlfd != nil){
- if(sys->fprint(ctlfd, "remove %s %s", bc.ip, bc.ipmask) < 0)
- return sys->sprint("remove address: %r");
- }
- bc.ip = nil;
- bc.ipgw = nil;
- bc.ipmask = nil;
- # remote address?
- # clear net+"/ndb"?
- return nil;
-}
-
-#
-# the following is just for debugging
-#
-
-dumpdhcp(m: ref Dhcp, dir: string)
-{
- s := "";
- sys->print("%s %s/%ud: ", dir, IPaddr.newv6(m.udphdr[Udpraddr:]).text(), get2(m.udphdr, Udprport));
- if(m.dhcpop != NotDHCP)
- s = " "+opname(m.dhcpop);
- sys->print("op %d%s htype %d hops %d xid %ud\n", m.op, s, m.htype, m.hops, m.xid);
- sys->print("\tsecs %d flags 0x%.4ux\n", m.secs, m.flags);
- sys->print("\tciaddr %s\n", m.ciaddr.text());
- sys->print("\tyiaddr %s\n", m.yiaddr.text());
- sys->print("\tsiaddr %s\n", m.siaddr.text());
- sys->print("\tgiaddr %s\n", m.giaddr.text());
- sys->print("\tchaddr ");
- for(x := 0; x < len m.chaddr; x++)
- sys->print("%2.2ux", int m.chaddr[x]);
- sys->print("\n");
- if(m.sname != nil)
- sys->print("\tsname %s\n", m.sname);
- if(m.file != nil)
- sys->print("\tfile %s\n", m.file);
- if(m.options != nil){
- sys->print("\t");
- printopts(m.options, opts);
- sys->print("\n");
- }
-}
-
-Optbytes, Optaddr, Optmask, Optint, Optstr, Optopts, Opthex: con iota;
-
-Opt: adt
-{
- code: int;
- name: string;
- otype: int;
-};
-
-opts: array of Opt = array[] of {
- (Omask, "ipmask", Optmask),
- (Orouter, "ipgw", Optaddr),
- (Odnsserver, "dns", Optaddr),
- (Ohostname, "hostname", Optstr),
- (Odomainname, "domain", Optstr),
- (Ontpserver, "ntp", Optaddr),
- (Oipaddr, "requestedip", Optaddr),
- (Olease, "lease", Optint),
- (Oserverid, "serverid", Optaddr),
- (Otype, "dhcpop", Optint),
- (Ovendorclass, "vendorclass", Optstr),
- (Ovendorinfo, "vendorinfo", Optopts),
- (Onetbiosns, "wins", Optaddr),
- (Opop3server, "pop3", Optaddr),
- (Osmtpserver, "smtp", Optaddr),
- (Owwwserver, "www", Optaddr),
- (Oparams, "params", Optbytes),
- (Otftpserver, "tftp", Optaddr),
- (Oclientid, "clientid", Opthex),
-};
-
-p9opts: array of Opt = array[] of {
- (OP9fs, "fs", Optaddr),
- (OP9auth, "auth", Optaddr),
-};
-
-lookopt(optab: array of Opt, code: int): (int, string, int)
-{
- for(i:=0; i<len optab; i++)
- if(opts[i].code == code)
- return opts[i];
- return (-1, nil, 0);
-}
-
-printopts(options: list of (int, array of byte), opts: array of Opt)
-{
- for(; options != nil; options = tl options){
- (code, val) := hd options;
- sys->print("(%d %d", code, len val);
- (nil, name, otype) := lookopt(opts, code);
- if(name == nil){
- for(v := 0; v < len val; v++)
- sys->print(" %d", int val[v]);
- }else{
- sys->print(" %s", name);
- case otype {
- Optbytes =>
- for(v := 0; v < len val; v++)
- sys->print(" %d", int val[v]);
- Opthex =>
- for(v := 0; v < len val; v++)
- sys->print(" %#.2ux", int val[v]);
- Optaddr or Optmask =>
- while(len val >= 4){
- sys->print(" %s", v4text(val));
- val = val[4:];
- }
- Optstr =>
- sys->print(" \"%s\"", string val);
- Optint =>
- n := 0;
- for(v := 0; v < len val; v++)
- n = (n<<8) | int val[v];
- sys->print(" %d", n);
- Optopts =>
- printopts(parseopt(val, 0).t1, p9opts);
- }
- }
- sys->print(")");
- }
-}
--- /dev/null
+++ b/appl/lib/dhcpclient.b
@@ -1,0 +1,1033 @@
+implement Dhcpclient;
+
+#
+# DHCP and BOOTP clients
+# Copyright © 2004-2006 Vita Nuova Holdings Limited
+#
+
+include "sys.m";
+ sys: Sys;
+
+include "ip.m";
+ ip: IP;
+ IPv4off, IPaddrlen, Udphdrlen, Udpraddr, Udpladdr, Udprport, Udplport: import IP;
+ IPaddr: import ip;
+ get2, get4, put2, put4: import ip;
+
+include "keyring.m";
+include "security.m"; # for Random
+
+include "dial.m";
+ dial: Dial;
+
+include "dhcpclient.m";
+
+debug := 0;
+
+xidgen: int;
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ random := load Random Random->PATH;
+ if(random != nil)
+ xidgen = random->randomint(Random->NotQuiteRandom);
+ else
+ xidgen = sys->pctl(0, nil)*sys->millisec();
+ random = nil;
+ dial = load Dial Dial->PATH;
+ ip = load IP IP->PATH;
+ ip->init();
+}
+
+tracing(d: int)
+{
+ debug = d;
+}
+
+Bootconf.new(): ref Bootconf
+{
+ bc := ref Bootconf;
+ bc.lease = 0;
+ bc.options = array[256] of array of byte;
+ return bc;
+}
+
+Bootconf.get(c: self ref Bootconf, n: int): array of byte
+{
+ a := c.options;
+ if(n & Ovendor){
+ a = c.vendor;
+ n &= ~Ovendor;
+ }
+ if(n < 0 || n >= len a)
+ return nil;
+ return a[n];
+}
+
+Bootconf.getint(c: self ref Bootconf, n: int): int
+{
+ a := c.get(n);
+ v := 0;
+ for(i := 0; i < len a; i++)
+ v = (v<<8) | int a[i];
+ return v;
+}
+
+Bootconf.getip(c: self ref Bootconf, n: int): string
+{
+ l := c.getips(n);
+ if(l == nil)
+ return nil;
+ return hd l;
+}
+
+Bootconf.getips(c: self ref Bootconf, n: int): list of string
+{
+ a := c.get(n);
+ rl: list of string;
+ while(len a >= 4){
+ rl = v4text(a) :: rl;
+ a = a[4:];
+ }
+ l: list of string;
+ for(; rl != nil; rl = tl rl)
+ l = hd rl :: l;
+ return l;
+}
+
+Bootconf.gets(c: self ref Bootconf, n: int): string
+{
+ a := c.get(n);
+ if(a == nil)
+ return nil;
+ for(i:=0; i<len a; i++)
+ if(a[i] == byte 0)
+ break;
+ return string a[0:i];
+}
+
+Bootconf.put(c: self ref Bootconf, n: int, a: array of byte)
+{
+ if(n < 0 || n >= len c.options)
+ return;
+ ca := array[len a] of byte;
+ ca[0:] = a;
+ c.options[n] = ca;
+}
+
+Bootconf.putint(c: self ref Bootconf, n: int, v: int)
+{
+ if(n < 0 || n >= len c.options)
+ return;
+ a := array[4] of byte;
+ put4(a, 0, v);
+ c.options[n] = a;
+}
+
+Bootconf.putips(c: self ref Bootconf, n: int, ips: list of string)
+{
+ if(n < 0 || n >= len c.options)
+ return;
+ na := len ips;
+ a := array[na*4] of byte;
+ na = 0;
+ for(; ips != nil; ips = tl ips){
+ (nil, ipa) := IPaddr.parse(hd ips);
+ a[na++:] = ipa.v4();
+ }
+ c.options[n] = a;
+}
+
+Bootconf.puts(c: self ref Bootconf, n: int, s: string)
+{
+ if(n < 0 || n >= len c.options)
+ return;
+ c.options[n] = array of byte s;
+}
+
+#
+#
+# DHCP
+#
+#
+
+# BOOTP operations
+Bootprequest, Bootpreply: con 1+iota;
+
+# DHCP operations
+NotDHCP, Discover, Offer, Request, Decline, Ack, Nak, Release, Inform: con iota;
+
+Dhcp: adt {
+ udphdr: array of byte;
+ op: int;
+ htype: int;
+ hops: int;
+ xid: int;
+ secs: int;
+ flags: int;
+ ciaddr: IPaddr;
+ yiaddr: IPaddr;
+ siaddr: IPaddr;
+ giaddr: IPaddr;
+ chaddr: array of byte;
+ sname: string;
+ file: string;
+ options: list of (int, array of byte);
+ dhcpop: int;
+};
+
+opnames := array[] of {
+ Discover => "Discover",
+ Offer => "Offer",
+ Request => "Request",
+ Decline => "Decline",
+ Ack => "Ack",
+ Nak => "Nak",
+ Release => "Release",
+ Inform => "Inform"
+};
+
+opname(op: int): string
+{
+ if(op >= 0 && op < len opnames)
+ return opnames[op];
+ return sys->sprint("OP%d", op);
+}
+
+stringget(buf: array of byte): string
+{
+ for(x := 0; x < len buf; x++)
+ if(buf[x] == byte 0)
+ break;
+ if(x == 0)
+ return nil;
+ return string buf[0 : x];
+}
+
+eqbytes(b1: array of byte, b2: array of byte): int
+{
+ l := len b1;
+ if(l != len b2)
+ return 0;
+ for(i := 0; i < l; i++)
+ if(b1[i] != b2[i])
+ return 0;
+ return 1;
+}
+
+magic := array[] of {byte 99, byte 130, byte 83, byte 99}; # RFC2132 (replacing RFC1048)
+
+dhcpsend(fd: ref Sys->FD, xid: int, dhcp: ref Dhcp)
+{
+ dhcp.xid = xid;
+ abuf := array[576+Udphdrlen] of {* => byte 0};
+ abuf[0:] = dhcp.udphdr;
+ buf := abuf[Udphdrlen:];
+ buf[0] = byte dhcp.op;
+ buf[1] = byte dhcp.htype;
+ buf[2] = byte len dhcp.chaddr;
+ buf[3] = byte dhcp.hops;
+ put4(buf, 4, xid);
+ put2(buf, 8, dhcp.secs);
+ put2(buf, 10, dhcp.flags);
+ buf[12:] = dhcp.ciaddr.v4();
+ buf[16:] = dhcp.yiaddr.v4();
+ buf[20:] = dhcp.siaddr.v4();
+ buf[24:] = dhcp.giaddr.v4();
+ buf[28:] = dhcp.chaddr;
+ buf[44:] = array of byte dhcp.sname; # [64]
+ buf[108:] = array of byte dhcp.file; # [128]
+ o := 236;
+ # RFC1542 suggests including magic and Oend as a minimum, even in BOOTP
+ buf[o:] = magic;
+ o += 4;
+ if(dhcp.dhcpop != NotDHCP){
+ buf[o++] = byte Otype;
+ buf[o++] = byte 1;
+ buf[o++] = byte dhcp.dhcpop;
+ }
+ for(ol := dhcp.options; ol != nil; ol = tl ol){
+ (opt, val) := hd ol;
+ buf[o++] = byte opt;
+ buf[o++] = byte len val;
+ if(len val > 0){
+ buf[o:] = val;
+ o += len val;
+ }
+ }
+ buf[o++] = byte Oend;
+ if(debug)
+ dumpdhcp(dhcp, "->");
+ sys->write(fd, abuf, len abuf);
+}
+
+kill(pid: int, grp: string)
+{
+ fd := sys->open("#p/" + string pid + "/ctl", sys->OWRITE);
+ if(fd != nil)
+ sys->fprint(fd, "kill%s", grp);
+}
+
+v4text(a: array of byte): string
+{
+ return sys->sprint("%ud.%ud.%ud.%ud", int a[0], int a[1], int a[2], int a[3]);
+}
+
+parseopt(a: array of byte, isdhcp: int): (int, list of (int, array of byte))
+{
+ opts: list of (int, array of byte);
+ xop := NotDHCP;
+ for(i := 0; i < len a;){
+ op := int a[i++];
+ if(op == Opad)
+ continue;
+ if(op == Oend || i >= len a)
+ break;
+ l := int a[i++];
+ if(i+l > len a)
+ break;
+ if(isdhcp && op == Otype)
+ xop = int a[i];
+ else
+ opts = (op, a[i:i+l]) :: opts;
+ i += l;
+ }
+ rl := opts;
+ opts = nil;
+ for(; rl != nil; rl = tl rl)
+ opts = hd rl :: opts;
+ return (xop, opts);
+}
+
+dhcpreader(pidc: chan of int, srv: ref DhcpIO)
+{
+ pidc <-= sys->pctl(0, nil);
+ for(;;){
+ abuf := array [576+Udphdrlen] of byte;
+ n := sys->read(srv.fd, abuf, len abuf);
+ if(n < 0){
+ if(debug)
+ sys->print("read error: %r\n");
+ sys->sleep(1000);
+ continue;
+ }
+ if(n < Udphdrlen+236){
+ if(debug)
+ sys->print("short read: %d\n", n);
+ continue;
+ }
+ buf := abuf[Udphdrlen:n];
+ n -= Udphdrlen;
+ dhcp := ref Dhcp;
+ dhcp.op = int buf[0];
+ if(dhcp.op != Bootpreply){
+ if(debug)
+ sys->print("bootp: not reply, discarded\n");
+ continue;
+ }
+ dhcp.dhcpop = NotDHCP;
+ if(n >= 240 && eqbytes(buf[236:240], magic)) # otherwise it's something we won't understand
+ (dhcp.dhcpop, dhcp.options) = parseopt(buf[240:n], 1);
+ case dhcp.dhcpop {
+ NotDHCP or Ack or Nak or Offer =>
+ ;
+ * =>
+ if(debug)
+ sys->print("dhcp: ignore dhcp op %d\n", dhcp.dhcpop);
+ continue;
+ }
+ dhcp.udphdr = abuf[0:Udphdrlen];
+ dhcp.htype = int buf[1];
+ hlen := int buf[2];
+ dhcp.hops = int buf[3];
+ dhcp.xid = get4(buf, 4);
+ dhcp.secs = get2(buf, 8);
+ dhcp.flags = get2(buf, 10);
+ dhcp.ciaddr = IPaddr.newv4(buf[12:]);
+ dhcp.yiaddr = IPaddr.newv4(buf[16:]);
+ dhcp.siaddr = IPaddr.newv4(buf[20:]);
+ dhcp.giaddr = IPaddr.newv4(buf[24:]);
+ dhcp.chaddr = buf[28 : 28 + hlen];
+ dhcp.sname = stringget(buf[44 : 108]);
+ dhcp.file = stringget(buf[108 : 236]);
+ srv.dc <-= dhcp;
+ }
+}
+
+timeoutstart(msecs: int): (int, chan of int)
+{
+ tc := chan of int;
+ spawn timeoutproc(tc, msecs);
+ return (<-tc, tc);
+}
+
+timeoutproc(c: chan of int, msecs: int)
+{
+ c <-= sys->pctl(0, nil);
+ sys->sleep(msecs);
+ c <-= 1;
+}
+
+hex(b: int): int
+{
+ if(b >= '0' && b <= '9')
+ return b-'0';
+ if(b >= 'A' && b <= 'F')
+ return b-'A' + 10;
+ if(b >= 'a' && b <= 'f')
+ return b-'a' + 10;
+ return -1;
+}
+
+gethaddr(device: string): (int, string, array of byte)
+{
+ fd := sys->open(device, Sys->OREAD);
+ if(fd == nil)
+ return (-1, sys->sprint("%r"), nil);
+ buf := array [100] of byte;
+ n := sys->read(fd, buf, len buf);
+ if(n < 0)
+ return (-1, sys->sprint("%r"), nil);
+ if(n == 0)
+ return (-1, "empty address file", nil);
+ addr := array [n/2] of byte;
+ for(i := 0; i < len addr; i++){
+ u := hex(int buf[2*i]);
+ l := hex(int buf[2*i+1]);
+ if(u < 0 || l < 0)
+ return (-1, "bad address syntax", nil);
+ addr[i] = byte ((u<<4)|l);
+ }
+ return (1, nil, addr);
+}
+
+newrequest(dest: IPaddr, bootfile: string, htype: int, haddr: array of byte, ipaddr: IPaddr, options: array of array of byte): ref Dhcp
+{
+ dhcp := ref Dhcp;
+ dhcp.op = Bootprequest;
+ hdr := array[Udphdrlen] of {* => byte 0};
+ hdr[Udpraddr:] = dest.v6();
+ put2(hdr, Udprport, 67);
+ dhcp.udphdr = hdr;
+ dhcp.htype = htype;
+ dhcp.chaddr = haddr;
+ dhcp.hops = 0;
+ dhcp.secs = 0;
+ dhcp.flags = 0;
+ dhcp.xid = 0;
+ dhcp.ciaddr = ipaddr;
+ dhcp.yiaddr = ip->v4noaddr;
+ dhcp.siaddr = ip->v4noaddr;
+ dhcp.giaddr = ip->v4noaddr;
+ dhcp.file = bootfile;
+ dhcp.dhcpop = NotDHCP;
+ if(options != nil){
+ for(i := 0; i < len options; i++)
+ if(options[i] != nil)
+ dhcp.options = (i, options[i]) :: dhcp.options;
+ }
+ clientid := array[len haddr + 1] of byte;
+ clientid[0] = byte htype;
+ clientid[1:] = haddr;
+ dhcp.options = (Oclientid, clientid) :: dhcp.options;
+ dhcp.options = (Ovendorclass, array of byte "plan9_386") :: dhcp.options; # 386 will do because type doesn't matter
+ return dhcp;
+}
+
+udpannounce(net: string): (ref Sys->FD, string)
+{
+ if(net == nil)
+ net = "/net";
+ conn := dial->announce(net+"/udp!*!68");
+ if(conn == nil)
+ return (nil, sys->sprint("can't announce dhcp port: %r"));
+ if(sys->fprint(conn.cfd, "headers") < 0)
+ return (nil, sys->sprint("can't set headers mode on dhcp port: %r"));
+ conn.dfd = sys->open(conn.dir+"/data", Sys->ORDWR);
+ if(conn.dfd == nil)
+ return (nil, sys->sprint("can't open %s: %r", conn.dir+"/data"));
+ return (conn.dfd, nil);
+}
+
+ifcnoaddr(fd: ref Sys->FD, s: string)
+{
+ if(fd != nil && sys->fprint(fd, "%s %s %s", s, (ip->noaddr).text(), (ip->noaddr).text()) < 0){
+ if(debug)
+ sys->print("dhcp: ctl %s: %r\n", s);
+ }
+}
+
+setup(net: string, device: string, init: ref Bootconf): (ref Dhcp, ref DhcpIO, string)
+{
+ (htype, err, mac) := gethaddr(device);
+ if(htype < 0)
+ return (nil, nil, sys->sprint("can't get hardware MAC address: %s", err));
+ ciaddr := ip->v4noaddr;
+ if(init != nil && init.ip != nil){
+ valid: int;
+ (valid, ciaddr) = IPaddr.parse(init.ip);
+ if(valid < 0)
+ return (nil, nil, sys->sprint("invalid ip address: %s", init.ip));
+ }
+ (dfd, err2) := udpannounce(net);
+ if(err2 != nil)
+ return (nil, nil, err);
+ bootfile: string;
+ options: array of array of byte;
+ if(init != nil){
+ bootfile = init.bootf;
+ options = init.options;
+ }
+ return (newrequest(ip->v4bcast, bootfile, htype, mac, ciaddr, options), DhcpIO.new(dfd), nil);
+}
+
+#
+# BOOTP (RFC951) is used by Inferno only during net boots, to get initial IP address and TFTP address and parameters
+#
+bootp(net: string, ctlifc: ref Sys->FD, device: string, init: ref Bootconf): (ref Bootconf, string)
+{
+ (req, srv, err) := setup(net, device, init);
+ if(err != nil)
+ return (nil, err);
+ ifcnoaddr(ctlifc, "add");
+ rdhcp := exchange(srv, ++xidgen, req, 1<<NotDHCP);
+ srv.rstop();
+ ifcnoaddr(ctlifc, "remove");
+ if(rdhcp == nil)
+ return (nil, "no response to BOOTP request");
+ return (fillbootconf(init, rdhcp), nil);
+}
+
+defparams := array[] of {
+ byte Omask, byte Orouter, byte Odnsserver, byte Ohostname, byte Odomainname, byte Ontpserver,
+};
+
+#
+# DHCP (RFC2131)
+#
+dhcp(net: string, ctlifc: ref Sys->FD, device: string, init: ref Bootconf, needparam: array of int): (ref Bootconf, ref Lease, string)
+{
+ (req, srv, err) := setup(net, device, init);
+ if(err != nil)
+ return (nil, nil, err);
+ params := defparams;
+ if(needparam != nil){
+ n := len defparams;
+ params = array[n+len needparam] of byte;
+ params[0:] = defparams;
+ for(i := 0; i < len needparam; i++)
+ params[n+i] = byte needparam[i];
+ }
+ initopt := (Oparams, params) :: req.options; # RFC2131 requires parameters to be repeated each time
+ lease := ref Lease(0, chan[1] of (ref Bootconf, string));
+ spawn dhcp1(srv, lease, net, ctlifc, req, init, initopt);
+ bc: ref Bootconf;
+ (bc, err) = <-lease.configs;
+ return (bc, lease, err);
+}
+
+dhcp1(srv: ref DhcpIO, lease: ref Lease, net: string, ctlifc: ref Sys->FD, req: ref Dhcp, init: ref Bootconf, initopt: list of (int, array of byte))
+{
+ cfd := -1;
+ if(ctlifc != nil)
+ cfd = ctlifc.fd;
+ lease.pid = sys->pctl(Sys->NEWPGRP|Sys->NEWFD, 1 :: srv.fd.fd :: cfd :: nil);
+ if(ctlifc != nil)
+ ctlifc = sys->fildes(ctlifc.fd);
+ srv.fd = sys->fildes(srv.fd.fd);
+ rep: ref Dhcp;
+ ifcnoaddr(ctlifc, "add");
+ if(req.ciaddr.isvalid())
+ rep = reacquire(srv, req, initopt, req.ciaddr);
+ if(rep == nil)
+ rep = askround(srv, req, initopt);
+ srv.rstop();
+ ifcnoaddr(ctlifc, "remove");
+ if(rep == nil){
+ lease.pid = 0;
+ lease.configs <-= (nil, "no response");
+ exit;
+ }
+ for(;;){
+ conf := fillbootconf(init, rep);
+ applycfg(net, ctlifc, conf);
+ if(conf.lease == 0){
+ srv.rstop();
+ lease.pid = 0;
+ flush(lease.configs);
+ lease.configs <-= (conf, nil);
+ exit;
+ }
+ flush(lease.configs);
+ lease.configs <-= (conf, nil);
+ req.ciaddr = rep.yiaddr;
+ while((rep = tenancy(srv, req, conf.lease)) != nil){
+ if(rep.dhcpop == Nak || !rep.ciaddr.eq(req.ciaddr))
+ break;
+ req.udphdr[Udpraddr:] = rep.udphdr[Udpraddr:Udpraddr+IPaddrlen];
+ conf = fillbootconf(init, rep);
+ }
+ removecfg(net, ctlifc, conf);
+ ifcnoaddr(ctlifc, "add");
+ while((rep = askround(srv, req, initopt)) == nil){
+ flush(lease.configs);
+ lease.configs <-= (nil, "no response");
+ srv.rstop();
+ sys->sleep(60*1000);
+ }
+ ifcnoaddr(ctlifc, "remove");
+ }
+}
+
+reacquire(srv: ref DhcpIO, req: ref Dhcp, initopt: list of (int, array of byte), addr: IPaddr): ref Dhcp
+{
+ # INIT-REBOOT: know an address; try requesting it (once)
+ # TO DO: could use Inform when our address is static but we need a few service parameters
+ req.ciaddr = ip->v4noaddr;
+ rep := request(srv, ++xidgen, req, (Oipaddr, addr.v4()) :: initopt);
+ if(rep != nil && rep.dhcpop == Ack && addr.eq(rep.yiaddr)){
+ if(debug)
+ sys->print("req: server accepted\n");
+ req.udphdr[Udpraddr:] = rep.udphdr[Udpraddr:Udpraddr+IPaddrlen];
+ return rep;
+ }
+ if(debug)
+ sys->print("req: cannot reclaim\n");
+ return nil;
+}
+
+askround(srv: ref DhcpIO, req: ref Dhcp, initopt: list of (int, array of byte)): ref Dhcp
+{
+ # INIT
+ req.ciaddr = ip->v4noaddr;
+ req.udphdr[Udpraddr:] = (ip->v4bcast).v6();
+ for(retries := 0; retries < 5; retries++){
+ # SELECTING
+ req.dhcpop = Discover;
+ req.options = initopt;
+ rep := exchange(srv, ++xidgen, req, 1<<Offer);
+ if(rep == nil)
+ break;
+ #
+ # could wait a little while and accumulate offers, but is it sensible?
+ # we do sometimes see arguments between DHCP servers that could
+ # only be resolved by user choice
+ #
+ if(!rep.yiaddr.isvalid())
+ continue; # server has no idea either
+ serverid := getopt(rep.options, Oserverid, 4);
+ if(serverid == nil)
+ continue; # broken server
+ # REQUESTING
+ options := (Oserverid, serverid) :: (Oipaddr, rep.yiaddr.v4()) :: initopt;
+ lease := getlease(rep);
+ if(lease != nil)
+ options = (Olease, lease) :: options;
+ rep = request(srv, rep.xid, req, options);
+ if(rep != nil){
+ # could probe with ARP here, and if found, Decline
+ if(debug)
+ sys->print("req: server accepted\n");
+ req.udphdr[Udpraddr:] = rep.udphdr[Udpraddr:Udpraddr+IPaddrlen];
+ return rep;
+ }
+ }
+ return nil;
+}
+
+request(srv: ref DhcpIO, xid: int, req: ref Dhcp, options: list of (int, array of byte)): ref Dhcp
+{
+ req.dhcpop = Request; # Selecting
+ req.options = options;
+ rep := exchange(srv, xid, req, (1<<Ack)|(1<<Nak));
+ if(rep == nil || rep.dhcpop == Nak)
+ return nil;
+ return rep;
+}
+
+# renew
+# direct to server from T1 to T2 [RENEW]
+# Request must not include
+# requested IP address, server identifier
+# Request must include
+# ciaddr set to client's address
+# Request might include
+# lease time
+# similar, but broadcast, from T2 to T3 [REBIND]
+# at T3, unbind, restart Discover
+
+tenancy(srv: ref DhcpIO, req: ref Dhcp, leasesec: int): ref Dhcp
+{
+ # configure address...
+ t3 := big leasesec * big 1000; # lease expires; restart
+ t2 := (big 3 * t3)/big 4; # broadcast renewal request at ¾time
+ t1 := t2/big 2; # renew lease with original server at ½time
+ srv.rstop();
+ thebigsleep(t1);
+ # RENEW
+ rep := renewing(srv, req, t1, t2);
+ if(rep != nil)
+ return rep;
+ # REBIND
+ req.udphdr[Udpraddr:] = (ip->v4bcast).v6(); # now try broadcast
+ return renewing(srv, req, t2, t3);
+}
+
+renewing(srv: ref DhcpIO, req: ref Dhcp, a: big, b: big): ref Dhcp
+{
+ Minute: con big(60*1000);
+ while(a < b){
+ rep := exchange(srv, req.xid, req, (1<<Ack)|(1<<Nak));
+ if(rep != nil)
+ return rep;
+ delta := (b-a)/big 2;
+ if(delta < Minute)
+ delta = Minute;
+ thebigsleep(delta);
+ a += delta;
+ }
+ return nil;
+}
+
+thebigsleep(msec: big)
+{
+ Day: con big (24*3600*1000); # 1 day in msec
+ while(msec > big 0){
+ n := msec;
+ if(n > Day)
+ n = Day;
+ sys->sleep(int n);
+ msec -= n;
+ }
+}
+
+getlease(m: ref Dhcp): array of byte
+{
+ lease := getopt(m.options, Olease, 4);
+ if(lease == nil)
+ return nil;
+ if(get4(lease, 0) == 0){
+ lease = array[4] of byte;
+ put4(lease, 0, 15*60);
+ }
+ return lease;
+}
+
+fillbootconf(init: ref Bootconf, pkt: ref Dhcp): ref Bootconf
+{
+ bc := ref Bootconf;
+ if(init != nil)
+ *bc = *init;
+ if(bc.options == nil)
+ bc.options = array[256] of array of byte;
+ for(l := pkt.options; l != nil; l = tl l){
+ (c, v) := hd l;
+ if(bc.options[c] == nil)
+ bc.options[c] = v; # give priority to first occurring
+ }
+ if((a := bc.get(Ovendorinfo)) != nil){
+ if(bc.vendor == nil)
+ bc.vendor = array[256] of array of byte;
+ for(l = parseopt(a, 0).t1; l != nil; l = tl l){
+ (c, v) := hd l;
+ if(bc.vendor[c] == nil)
+ bc.vendor[c] = v;
+ }
+ }
+ if(pkt.yiaddr.isvalid()){
+ bc.ip = pkt.yiaddr.text();
+ bc.ipmask = bc.getip(Omask);
+ if(bc.ipmask == nil)
+ bc.ipmask = pkt.yiaddr.classmask().masktext();
+ }
+ bc.bootf = pkt.file;
+ bc.dhcpip = IPaddr.newv6(pkt.udphdr[Udpraddr:]).text();
+ bc.siaddr = pkt.siaddr.text();
+ bc.lease = bc.getint(Olease);
+ if(bc.lease == Infinite)
+ bc.lease = 0;
+ else if(debug > 1)
+ bc.lease = 2*60; # shorten time, for testing
+ bc.dom = bc.gets(Odomainname);
+ s := bc.gets(Ohostname);
+ for(i:=0; i<len s; i++)
+ if(s[i] == '.'){
+ if(bc.dom == nil)
+ bc.dom = s[i+1:];
+ s = s[0:i];
+ break;
+ }
+ bc.sys = s;
+ bc.ipgw = bc.getip(Orouter);
+ bc.bootip = bc.getip(Otftpserver);
+ bc.serverid = bc.getip(Oserverid);
+ return bc;
+}
+
+Lease.release(l: self ref Lease)
+{
+ # could send a Release message
+ # should unconfigure
+ if(l.pid){
+ kill(l.pid, "grp");
+ l.pid = 0;
+ }
+}
+
+flush(c: chan of (ref Bootconf, string))
+{
+ alt{
+ <-c => ;
+ * => ;
+ }
+}
+
+DhcpIO: adt {
+ fd: ref Sys->FD;
+ pid: int;
+ dc: chan of ref Dhcp;
+ new: fn(fd: ref Sys->FD): ref DhcpIO;
+ rstart: fn(io: self ref DhcpIO);
+ rstop: fn(io: self ref DhcpIO);
+};
+
+DhcpIO.new(fd: ref Sys->FD): ref DhcpIO
+{
+ return ref DhcpIO(fd, 0, chan of ref Dhcp);
+}
+
+DhcpIO.rstart(io: self ref DhcpIO)
+{
+ if(io.pid == 0){
+ pids := chan of int;
+ spawn dhcpreader(pids, io);
+ io.pid = <-pids;
+ }
+}
+
+DhcpIO.rstop(io: self ref DhcpIO)
+{
+ if(io.pid != 0){
+ kill(io.pid, "");
+ io.pid = 0;
+ }
+}
+
+getopt(options: list of (int, array of byte), op: int, minlen: int): array of byte
+{
+ for(; options != nil; options = tl options){
+ (opt, val) := hd options;
+ if(opt == op && len val >= minlen)
+ return val;
+ }
+ return nil;
+}
+
+exchange(srv: ref DhcpIO, xid: int, req: ref Dhcp, accept: int): ref Dhcp
+{
+ srv.rstart();
+ nsec := 3;
+ for(count := 0; count < 5; count++) {
+ (tpid, tc) := timeoutstart(nsec*1000);
+ dhcpsend(srv.fd, xid, req);
+ Wait:
+ for(;;){
+ alt {
+ <-tc=>
+ break Wait;
+ rep := <-srv.dc=>
+ if(debug)
+ dumpdhcp(rep, "<-");
+ if(rep.op == Bootpreply &&
+ rep.xid == req.xid &&
+ rep.ciaddr.eq(req.ciaddr) &&
+ eqbytes(rep.chaddr, req.chaddr)){
+ if((accept & (1<<rep.dhcpop)) == 0){
+ if(debug)
+ sys->print("req: unexpected reply %s to %s\n", opname(rep.dhcpop), opname(req.dhcpop));
+ continue;
+ }
+ kill(tpid, "");
+ return rep;
+ }
+ if(debug)
+ sys->print("req: mismatch\n");
+ }
+ }
+ req.secs += nsec;
+ nsec++;
+ }
+ return nil;
+}
+
+applycfg(net: string, ctlfd: ref Sys->FD, bc: ref Bootconf): string
+{
+ # write addresses to /net/...
+ # local address, mask[or default], remote address [mtu]
+ if(net == nil)
+ net = "/net";
+ if(bc.ip == nil)
+ return "invalid address";
+ if(ctlfd != nil){
+ if(sys->fprint(ctlfd, "add %s %s", bc.ip, bc.ipmask) < 0) # TO DO: [raddr [mtu]]
+ return sys->sprint("add interface: %r");
+ # could use "mtu n" request to set/change mtu
+ }
+ # if primary:
+ # add default route if gateway valid
+ # put ndb entries ip=, ipmask=, ipgw=; sys= dom=; fs=; auth=; dns=; ntp=; other options from bc.options
+ if(bc.ipgw != nil){
+ fd := sys->open(net+"/iproute", Sys->OWRITE);
+ if(fd != nil)
+ sys->fprint(fd, "add 0 0 %s", bc.ipgw);
+ }
+ s := sys->sprint("ip=%s ipmask=%s", bc.ip, bc.ipmask);
+ if(bc.ipgw != nil)
+ s += sys->sprint(" ipgw=%s", bc.ipgw);
+ s += "\n";
+ if(bc.sys != nil)
+ s += sys->sprint(" sys=%s\n", bc.sys);
+ if(bc.dom != nil)
+ s += sys->sprint(" dom=%s.%s\n", bc.sys, bc.dom);
+ if((addr := bc.getip(OP9auth)) != nil)
+ s += sys->sprint(" auth=%s\n", addr); # TO DO: several addresses
+ if((addr = bc.getip(OP9fs)) != nil)
+ s += sys->sprint(" fs=%s\n", addr);
+ if((addr = bc.getip(Odnsserver)) != nil)
+ s += sys->sprint(" dns=%s\n", addr);
+ fd := sys->open(net+"/ndb", Sys->OWRITE | Sys->OTRUNC);
+ if(fd != nil){
+ a := array of byte s;
+ sys->write(fd, a, len a);
+ }
+ return nil;
+}
+
+removecfg(nil: string, ctlfd: ref Sys->FD, bc: ref Bootconf): string
+{
+ # remove localaddr, localmask[or default]
+ if(ctlfd != nil){
+ if(sys->fprint(ctlfd, "remove %s %s", bc.ip, bc.ipmask) < 0)
+ return sys->sprint("remove address: %r");
+ }
+ bc.ip = nil;
+ bc.ipgw = nil;
+ bc.ipmask = nil;
+ # remote address?
+ # clear net+"/ndb"?
+ return nil;
+}
+
+#
+# the following is just for debugging
+#
+
+dumpdhcp(m: ref Dhcp, dir: string)
+{
+ s := "";
+ sys->print("%s %s/%ud: ", dir, IPaddr.newv6(m.udphdr[Udpraddr:]).text(), get2(m.udphdr, Udprport));
+ if(m.dhcpop != NotDHCP)
+ s = " "+opname(m.dhcpop);
+ sys->print("op %d%s htype %d hops %d xid %ud\n", m.op, s, m.htype, m.hops, m.xid);
+ sys->print("\tsecs %d flags 0x%.4ux\n", m.secs, m.flags);
+ sys->print("\tciaddr %s\n", m.ciaddr.text());
+ sys->print("\tyiaddr %s\n", m.yiaddr.text());
+ sys->print("\tsiaddr %s\n", m.siaddr.text());
+ sys->print("\tgiaddr %s\n", m.giaddr.text());
+ sys->print("\tchaddr ");
+ for(x := 0; x < len m.chaddr; x++)
+ sys->print("%2.2ux", int m.chaddr[x]);
+ sys->print("\n");
+ if(m.sname != nil)
+ sys->print("\tsname %s\n", m.sname);
+ if(m.file != nil)
+ sys->print("\tfile %s\n", m.file);
+ if(m.options != nil){
+ sys->print("\t");
+ printopts(m.options, opts);
+ sys->print("\n");
+ }
+}
+
+Optbytes, Optaddr, Optmask, Optint, Optstr, Optopts, Opthex: con iota;
+
+Opt: adt
+{
+ code: int;
+ name: string;
+ otype: int;
+};
+
+opts: array of Opt = array[] of {
+ (Omask, "ipmask", Optmask),
+ (Orouter, "ipgw", Optaddr),
+ (Odnsserver, "dns", Optaddr),
+ (Ohostname, "hostname", Optstr),
+ (Odomainname, "domain", Optstr),
+ (Ontpserver, "ntp", Optaddr),
+ (Oipaddr, "requestedip", Optaddr),
+ (Olease, "lease", Optint),
+ (Oserverid, "serverid", Optaddr),
+ (Otype, "dhcpop", Optint),
+ (Ovendorclass, "vendorclass", Optstr),
+ (Ovendorinfo, "vendorinfo", Optopts),
+ (Onetbiosns, "wins", Optaddr),
+ (Opop3server, "pop3", Optaddr),
+ (Osmtpserver, "smtp", Optaddr),
+ (Owwwserver, "www", Optaddr),
+ (Oparams, "params", Optbytes),
+ (Otftpserver, "tftp", Optaddr),
+ (Oclientid, "clientid", Opthex),
+};
+
+p9opts: array of Opt = array[] of {
+ (OP9fs, "fs", Optaddr),
+ (OP9auth, "auth", Optaddr),
+};
+
+lookopt(optab: array of Opt, code: int): (int, string, int)
+{
+ for(i:=0; i<len optab; i++)
+ if(opts[i].code == code)
+ return opts[i];
+ return (-1, nil, 0);
+}
+
+printopts(options: list of (int, array of byte), opts: array of Opt)
+{
+ for(; options != nil; options = tl options){
+ (code, val) := hd options;
+ sys->print("(%d %d", code, len val);
+ (nil, name, otype) := lookopt(opts, code);
+ if(name == nil){
+ for(v := 0; v < len val; v++)
+ sys->print(" %d", int val[v]);
+ }else{
+ sys->print(" %s", name);
+ case otype {
+ Optbytes =>
+ for(v := 0; v < len val; v++)
+ sys->print(" %d", int val[v]);
+ Opthex =>
+ for(v := 0; v < len val; v++)
+ sys->print(" %#.2ux", int val[v]);
+ Optaddr or Optmask =>
+ while(len val >= 4){
+ sys->print(" %s", v4text(val));
+ val = val[4:];
+ }
+ Optstr =>
+ sys->print(" \"%s\"", string val);
+ Optint =>
+ n := 0;
+ for(v := 0; v < len val; v++)
+ n = (n<<8) | int val[v];
+ sys->print(" %d", n);
+ Optopts =>
+ printopts(parseopt(val, 0).t1, p9opts);
+ }
+ }
+ sys->print(")");
+ }
+}
--- a/appl/lib/dhcpd.b
+++ /dev/null
@@ -1,375 +1,0 @@
-implement Dhcpserver;
-
-include "sys.m";
- sys: Sys;
- sprint: import sys;
-include "ip.m";
- ip: IP;
- IPaddr: import ip;
-include "encoding.m";
- base16: Encoding;
-include "lists.m";
- lists: Lists;
-include "ether.m";
- ether: Ether;
-include "dhcpd.m";
-
-debug: con 0;
-
-tdnames := array[] of {
- nil, "discover", "offer", "request", "decline", "ack", "nak", "release", "inform",
-};
-
-# option value types, for converting to text
-OTstr, OTips, OTint16, OTint32, OTother: con iota;
-optstring := array[] of {
- Ohostname, Odomainname, Orootpath, Omessage, Otftpservername, Obootfile,
-};
-optips := array[] of {
- Osubnetmask, Orouters, Odns, Obroadcast, Oreqipaddr, Oserverid,
-};
-optint16 := array[] of {
- Omaxmsgsize,
-};
-optint32 := array[] of {
- Oleasetime, Orenewaltime, Orebindingtime,
-};
-
-optnames := array[] of {
-Opad => "pad",
-Oend => "end",
-Osubnetmask => "ipmask",
-Orouters => "ipgw",
-Odns => "dns",
-Ohostname => "sys",
-Odomainname => "dnsdomain",
-Orootpath => "rootpath",
-Obroadcast => "ipbroadcast",
-
-Oreqipaddr => "reqipaddr",
-Oleasetime => "leasetime",
-Ooptionoverload => "optionoverload",
-Odhcpmsgtype => "dhcpmsgtype",
-Oserverid => "serverid",
-Oparamreq => "paramreq",
-Omessage => "message",
-Omaxmsgsize => "maxmsgsize",
-Orenewaltime => "renewaltime",
-Orebindingtime => "rebindingtime",
-Ovendorclass => "vendorclass",
-Oclientid => "clientid",
-Otftpservername => "tftp",
-Obootfile => "bootf",
-};
-
-
-init()
-{
- sys = load Sys Sys->PATH;
- ip = load IP IP->PATH;
- ip->init();
- base16 = load Encoding Encoding->BASE16PATH;
- lists = load Lists Lists->PATH;
- ether = load Ether Ether->PATH;
- ether->init();
-}
-
-optsparse(d: array of byte): list of ref Opt
-{
- if(g32(d, 0).t0 != Moptions)
- return nil;
-
- l: list of ref Opt;
- o := 4;
- while(o < len d) {
- code := int d[o++];
- if(code == Oend)
- break;
- if(code == Opad)
- continue;
- if(o >= len d) {
- say("bad options, length outside packet");
- return nil;
- }
- n := int d[o++];
- if(o+n > len d) {
- say("bad options, value outside packet");
- return nil;
- }
- l = ref Opt(code, d[o:o+n])::l;
- o += n;
- }
- return lists->reverse(l);
-}
-
-optssize(l: list of ref Opt): int
-{
- o := 4;
- for(; l != nil; l = tl l)
- case (hd l).code {
- Opad or
- Oend =>
- o += 1;
- * =>
- o += 1+1+len (hd l).v;
- }
- return o;
-}
-
-optspack(l: list of ref Opt, buf: array of byte)
-{
- if(l == nil)
- return;
-
- o := 0;
- o = p32(buf, o, Moptions);
- for(; l != nil; l = tl l) {
- opt := hd l;
- if(o >= len buf)
- return warn("response options too long (code)");
- buf[o++] = byte opt.code;
- if(opt.code == Opad)
- continue;
- if(opt.code == Oend) {
- if(len l != 1)
- raise "option 'end' not last";
- break;
- }
-
- if(o+1+len opt.v > len buf)
- return warn("response options too long (len/value)");
- buf[o++] = byte len opt.v;
- buf[o:] = opt.v;
- o += len opt.v;
- }
-}
-
-optname(code: int): string
-{
- if(code >= 0 && code < len optnames)
- s := optnames[code];
- if(s == nil)
- s = string code;
- return s;
-}
-
-has(a: array of int, v: int): int
-{
- for(i := 0; i < len a; i++)
- if(a[i] == v)
- return 1;
- return 0;
-}
-
-opttype(code: int): int
-{
- if(has(optstring, code))
- return OTstr;
- if(has(optips, code))
- return OTips;
- if(has(optint16, code))
- return OTint16;
- if(has(optint32, code))
- return OTint32;
- return OTother;
-}
-
-optstext(l: list of ref Opt): string
-{
- s := "";
- for(; l != nil; l = tl l) {
- o := hd l;
- s += "\t\t"+optname(o.code);
- if(o.v == nil) {
- s += "\n";
- continue;
- }
- s += "=";
- ot := opttype(o.code);
- if(o.code == Odhcpmsgtype && len o.v == 1 && (t := int o.v[0]) >= 0 && t < len tdnames && tdnames[t] != nil) {
- s += sprint("%s", tdnames[t]);
- } else if(ot == OTstr) {
- s += sprint("\"%s\"", string o.v);
- } else if(ot == OTips && len o.v % 4 == 0) {
- v := o.v;
- ips := "";
- while(len v >= 4) {
- ips += ","+IPaddr.newv4(v[:4]).text();
- v = v[4:];
- }
- if(ips != nil)
- ips = ips[1:];
- s += sprint("%s", ips);
- } else if(ot == OTint16 && len o.v == 2) {
- s += sprint("%d", g16(o.v, 0).t0);
- } else if(ot == OTint32 && len o.v == 4) {
- s += sprint("%d", g32(o.v, 0).t0);
- } else {
- s += sprint("'%s'", base16->enc(o.v));
- }
- s += "\n";
- }
- return s;
-}
-
-Dhcpmsg.unpack(buf: array of byte): (ref Dhcpmsg, string)
-{
- if(len buf < Minmsglen)
- return (nil, sprint("too short, %d < minimum %d", len buf, Minmsglen));
-
- m := ref Dhcpmsg;
- o := 0;
- m.op = int buf[o++];
- m.htype = int buf[o++];
- m.hlen = int buf[o++];
- if(m.hlen > 16)
- return (nil, sprint("bad hlen %d", m.hlen));
- m.hops = int buf[o++];
- (m.xid, o) = g32(buf, o);
- (m.secs, o) = g16(buf, o);
- (m.flags, o) = g16(buf, o);
- m.ciaddr = IPaddr.newv4(buf[o:o+4]);
- o += 4;
- m.yiaddr = IPaddr.newv4(buf[o:o+4]);
- o += 4;
- m.siaddr = IPaddr.newv4(buf[o:o+4]);
- o += 4;
- m.giaddr = IPaddr.newv4(buf[o:o+4]);
- o += 4;
- m.chaddr = buf[o:o+m.hlen];
- o += 16;
- m.sname = cstr(buf[o:o+64]);
- o += 64;
- m.file = cstr(buf[o:o+128]);
- o += 128;
- m.options = buf[o:];
- m.opts = optsparse(m.options);
- return (m, nil);
-}
-
-Dhcpmsg.pack(m: self ref Dhcpmsg): array of byte
-{
- size := 7*4+16+64+128;
-
- # for dhcp, no minimum length. bootp needs 64 bytes options.
- optsize := len m.options;
- if(len m.options >= 4 && g32(m.options, 0).t0 == Moptions)
- optsize = optssize(m.opts);
- if(0 && optsize < 64)
- optsize = 64;
- size += optsize;
-
- buf := array[size] of {* => byte 0};
- o := 0;
- buf[o++] = byte m.op;
- buf[o++] = byte m.htype;
- buf[o++] = byte m.hlen;
- buf[o++] = byte m.hops;
- o = p32(buf, o, m.xid);
- o = p16(buf, o, m.secs);
- o = p16(buf, o, m.flags);
- o = pbuf(buf, o, m.ciaddr.v4());
- o = pbuf(buf, o, m.yiaddr.v4());
- o = pbuf(buf, o, m.siaddr.v4());
- o = pbuf(buf, o, m.giaddr.v4());
- buf[o:] = m.chaddr;
- o += 16;
- buf[o:] = array of byte m.sname;
- o += 64;
- buf[o:] = array of byte m.file;
- o += 128;
- if(len m.options >= 4 && g32(m.options, 0).t0 == Moptions)
- optspack(m.opts, buf[o:]);
- else if(m.options != nil)
- buf[o:] = m.options;
- return buf;
-}
-
-Dhcpmsg.text(m: self ref Dhcpmsg): string
-{
- s := "Dhcpmsg(\n";
- s += sprint(" op=%d\n", int m.op);
- s += sprint(" htype=%d\n", m.htype);
- s += sprint(" hlen=%d\n", m.hlen);
- s += sprint(" hops=%d\n", m.hops);
- s += sprint(" xid=%ud\n", m.xid);
- s += sprint(" secs=%ud\n", m.secs);
- s += sprint(" ciaddr=%s\n", m.ciaddr.text());
- s += sprint(" yiaddr=%s\n", m.yiaddr.text());
- s += sprint(" siaddr=%s\n", m.siaddr.text());
- s += sprint(" giaddr=%s\n", m.giaddr.text());
- s += sprint(" hwaddr=%q\n", ether->text(m.chaddr));
- s += sprint(" sname=%q\n", m.sname);
- s += sprint(" file=%q\n", m.file);
- if(len m.options >= 4) {
- magic := g32(m.options, 0).t0;
- s += sprint(" magic=%#ux\n", magic);
- if(magic == Moptions) {
- s += "\toptions=(\n";
- s += optstext(m.opts);
- s += "\t)\n";
- } else if (magic == Mp9)
- s += sprint("\toptions=%q\n", cstr(m.options[4:]));
- }
- s += ")";
- return s;
-}
-
-cstr(buf: array of byte): string
-{
- for(i := 0; i < len buf; i++)
- if(buf[i] == byte 0)
- break;
- return string buf[:i];
-}
-
-g32(d: array of byte, o: int): (int, int)
-{
- v := 0;
- v |= int d[o++]<<24;
- v |= int d[o++]<<16;
- v |= int d[o++]<<8;
- v |= int d[o++]<<0;
- return (v, o);
-}
-
-g16(d: array of byte, o: int): (int, int)
-{
- v := 0;
- v |= int d[o++]<<8;
- v |= int d[o++]<<0;
- return (v, o);
-}
-
-p32(d: array of byte, o: int, v: int): int
-{
- d[o++] = byte (v>>24);
- d[o++] = byte (v>>16);
- d[o++] = byte (v>>8);
- d[o++] = byte (v>>0);
- return o;
-}
-
-p16(d: array of byte, o: int, v: int): int
-{
- d[o++] = byte (v>>8);
- d[o++] = byte (v>>0);
- return o;
-}
-
-pbuf(d: array of byte, o: int, buf: array of byte): int
-{
- d[o:] = buf;
- return o+len buf;
-}
-
-warn(s: string)
-{
- say(s);
-}
-
-say(s: string)
-{
- if(debug)
- sys->fprint(sys->fildes(2), "%s\n", s);
-}
--- /dev/null
+++ b/appl/lib/dhcpserver.b
@@ -1,0 +1,375 @@
+implement Dhcpserver;
+
+include "sys.m";
+ sys: Sys;
+ sprint: import sys;
+include "ip.m";
+ ip: IP;
+ IPaddr: import ip;
+include "encoding.m";
+ base16: Encoding;
+include "lists.m";
+ lists: Lists;
+include "ether.m";
+ ether: Ether;
+include "dhcpserver.m";
+
+debug: con 0;
+
+tdnames := array[] of {
+ nil, "discover", "offer", "request", "decline", "ack", "nak", "release", "inform",
+};
+
+# option value types, for converting to text
+OTstr, OTips, OTint16, OTint32, OTother: con iota;
+optstring := array[] of {
+ Ohostname, Odomainname, Orootpath, Omessage, Otftpservername, Obootfile,
+};
+optips := array[] of {
+ Osubnetmask, Orouters, Odns, Obroadcast, Oreqipaddr, Oserverid,
+};
+optint16 := array[] of {
+ Omaxmsgsize,
+};
+optint32 := array[] of {
+ Oleasetime, Orenewaltime, Orebindingtime,
+};
+
+optnames := array[] of {
+Opad => "pad",
+Oend => "end",
+Osubnetmask => "ipmask",
+Orouters => "ipgw",
+Odns => "dns",
+Ohostname => "sys",
+Odomainname => "dnsdomain",
+Orootpath => "rootpath",
+Obroadcast => "ipbroadcast",
+
+Oreqipaddr => "reqipaddr",
+Oleasetime => "leasetime",
+Ooptionoverload => "optionoverload",
+Odhcpmsgtype => "dhcpmsgtype",
+Oserverid => "serverid",
+Oparamreq => "paramreq",
+Omessage => "message",
+Omaxmsgsize => "maxmsgsize",
+Orenewaltime => "renewaltime",
+Orebindingtime => "rebindingtime",
+Ovendorclass => "vendorclass",
+Oclientid => "clientid",
+Otftpservername => "tftp",
+Obootfile => "bootf",
+};
+
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ ip = load IP IP->PATH;
+ ip->init();
+ base16 = load Encoding Encoding->BASE16PATH;
+ lists = load Lists Lists->PATH;
+ ether = load Ether Ether->PATH;
+ ether->init();
+}
+
+optsparse(d: array of byte): list of ref Opt
+{
+ if(g32(d, 0).t0 != Moptions)
+ return nil;
+
+ l: list of ref Opt;
+ o := 4;
+ while(o < len d) {
+ code := int d[o++];
+ if(code == Oend)
+ break;
+ if(code == Opad)
+ continue;
+ if(o >= len d) {
+ say("bad options, length outside packet");
+ return nil;
+ }
+ n := int d[o++];
+ if(o+n > len d) {
+ say("bad options, value outside packet");
+ return nil;
+ }
+ l = ref Opt(code, d[o:o+n])::l;
+ o += n;
+ }
+ return lists->reverse(l);
+}
+
+optssize(l: list of ref Opt): int
+{
+ o := 4;
+ for(; l != nil; l = tl l)
+ case (hd l).code {
+ Opad or
+ Oend =>
+ o += 1;
+ * =>
+ o += 1+1+len (hd l).v;
+ }
+ return o;
+}
+
+optspack(l: list of ref Opt, buf: array of byte)
+{
+ if(l == nil)
+ return;
+
+ o := 0;
+ o = p32(buf, o, Moptions);
+ for(; l != nil; l = tl l) {
+ opt := hd l;
+ if(o >= len buf)
+ return warn("response options too long (code)");
+ buf[o++] = byte opt.code;
+ if(opt.code == Opad)
+ continue;
+ if(opt.code == Oend) {
+ if(len l != 1)
+ raise "option 'end' not last";
+ break;
+ }
+
+ if(o+1+len opt.v > len buf)
+ return warn("response options too long (len/value)");
+ buf[o++] = byte len opt.v;
+ buf[o:] = opt.v;
+ o += len opt.v;
+ }
+}
+
+optname(code: int): string
+{
+ if(code >= 0 && code < len optnames)
+ s := optnames[code];
+ if(s == nil)
+ s = string code;
+ return s;
+}
+
+has(a: array of int, v: int): int
+{
+ for(i := 0; i < len a; i++)
+ if(a[i] == v)
+ return 1;
+ return 0;
+}
+
+opttype(code: int): int
+{
+ if(has(optstring, code))
+ return OTstr;
+ if(has(optips, code))
+ return OTips;
+ if(has(optint16, code))
+ return OTint16;
+ if(has(optint32, code))
+ return OTint32;
+ return OTother;
+}
+
+optstext(l: list of ref Opt): string
+{
+ s := "";
+ for(; l != nil; l = tl l) {
+ o := hd l;
+ s += "\t\t"+optname(o.code);
+ if(o.v == nil) {
+ s += "\n";
+ continue;
+ }
+ s += "=";
+ ot := opttype(o.code);
+ if(o.code == Odhcpmsgtype && len o.v == 1 && (t := int o.v[0]) >= 0 && t < len tdnames && tdnames[t] != nil) {
+ s += sprint("%s", tdnames[t]);
+ } else if(ot == OTstr) {
+ s += sprint("\"%s\"", string o.v);
+ } else if(ot == OTips && len o.v % 4 == 0) {
+ v := o.v;
+ ips := "";
+ while(len v >= 4) {
+ ips += ","+IPaddr.newv4(v[:4]).text();
+ v = v[4:];
+ }
+ if(ips != nil)
+ ips = ips[1:];
+ s += sprint("%s", ips);
+ } else if(ot == OTint16 && len o.v == 2) {
+ s += sprint("%d", g16(o.v, 0).t0);
+ } else if(ot == OTint32 && len o.v == 4) {
+ s += sprint("%d", g32(o.v, 0).t0);
+ } else {
+ s += sprint("'%s'", base16->enc(o.v));
+ }
+ s += "\n";
+ }
+ return s;
+}
+
+Dhcpmsg.unpack(buf: array of byte): (ref Dhcpmsg, string)
+{
+ if(len buf < Minmsglen)
+ return (nil, sprint("too short, %d < minimum %d", len buf, Minmsglen));
+
+ m := ref Dhcpmsg;
+ o := 0;
+ m.op = int buf[o++];
+ m.htype = int buf[o++];
+ m.hlen = int buf[o++];
+ if(m.hlen > 16)
+ return (nil, sprint("bad hlen %d", m.hlen));
+ m.hops = int buf[o++];
+ (m.xid, o) = g32(buf, o);
+ (m.secs, o) = g16(buf, o);
+ (m.flags, o) = g16(buf, o);
+ m.ciaddr = IPaddr.newv4(buf[o:o+4]);
+ o += 4;
+ m.yiaddr = IPaddr.newv4(buf[o:o+4]);
+ o += 4;
+ m.siaddr = IPaddr.newv4(buf[o:o+4]);
+ o += 4;
+ m.giaddr = IPaddr.newv4(buf[o:o+4]);
+ o += 4;
+ m.chaddr = buf[o:o+m.hlen];
+ o += 16;
+ m.sname = cstr(buf[o:o+64]);
+ o += 64;
+ m.file = cstr(buf[o:o+128]);
+ o += 128;
+ m.options = buf[o:];
+ m.opts = optsparse(m.options);
+ return (m, nil);
+}
+
+Dhcpmsg.pack(m: self ref Dhcpmsg): array of byte
+{
+ size := 7*4+16+64+128;
+
+ # for dhcp, no minimum length. bootp needs 64 bytes options.
+ optsize := len m.options;
+ if(len m.options >= 4 && g32(m.options, 0).t0 == Moptions)
+ optsize = optssize(m.opts);
+ if(0 && optsize < 64)
+ optsize = 64;
+ size += optsize;
+
+ buf := array[size] of {* => byte 0};
+ o := 0;
+ buf[o++] = byte m.op;
+ buf[o++] = byte m.htype;
+ buf[o++] = byte m.hlen;
+ buf[o++] = byte m.hops;
+ o = p32(buf, o, m.xid);
+ o = p16(buf, o, m.secs);
+ o = p16(buf, o, m.flags);
+ o = pbuf(buf, o, m.ciaddr.v4());
+ o = pbuf(buf, o, m.yiaddr.v4());
+ o = pbuf(buf, o, m.siaddr.v4());
+ o = pbuf(buf, o, m.giaddr.v4());
+ buf[o:] = m.chaddr;
+ o += 16;
+ buf[o:] = array of byte m.sname;
+ o += 64;
+ buf[o:] = array of byte m.file;
+ o += 128;
+ if(len m.options >= 4 && g32(m.options, 0).t0 == Moptions)
+ optspack(m.opts, buf[o:]);
+ else if(m.options != nil)
+ buf[o:] = m.options;
+ return buf;
+}
+
+Dhcpmsg.text(m: self ref Dhcpmsg): string
+{
+ s := "Dhcpmsg(\n";
+ s += sprint(" op=%d\n", int m.op);
+ s += sprint(" htype=%d\n", m.htype);
+ s += sprint(" hlen=%d\n", m.hlen);
+ s += sprint(" hops=%d\n", m.hops);
+ s += sprint(" xid=%ud\n", m.xid);
+ s += sprint(" secs=%ud\n", m.secs);
+ s += sprint(" ciaddr=%s\n", m.ciaddr.text());
+ s += sprint(" yiaddr=%s\n", m.yiaddr.text());
+ s += sprint(" siaddr=%s\n", m.siaddr.text());
+ s += sprint(" giaddr=%s\n", m.giaddr.text());
+ s += sprint(" hwaddr=%q\n", ether->text(m.chaddr));
+ s += sprint(" sname=%q\n", m.sname);
+ s += sprint(" file=%q\n", m.file);
+ if(len m.options >= 4) {
+ magic := g32(m.options, 0).t0;
+ s += sprint(" magic=%#ux\n", magic);
+ if(magic == Moptions) {
+ s += "\toptions=(\n";
+ s += optstext(m.opts);
+ s += "\t)\n";
+ } else if (magic == Mp9)
+ s += sprint("\toptions=%q\n", cstr(m.options[4:]));
+ }
+ s += ")";
+ return s;
+}
+
+cstr(buf: array of byte): string
+{
+ for(i := 0; i < len buf; i++)
+ if(buf[i] == byte 0)
+ break;
+ return string buf[:i];
+}
+
+g32(d: array of byte, o: int): (int, int)
+{
+ v := 0;
+ v |= int d[o++]<<24;
+ v |= int d[o++]<<16;
+ v |= int d[o++]<<8;
+ v |= int d[o++]<<0;
+ return (v, o);
+}
+
+g16(d: array of byte, o: int): (int, int)
+{
+ v := 0;
+ v |= int d[o++]<<8;
+ v |= int d[o++]<<0;
+ return (v, o);
+}
+
+p32(d: array of byte, o: int, v: int): int
+{
+ d[o++] = byte (v>>24);
+ d[o++] = byte (v>>16);
+ d[o++] = byte (v>>8);
+ d[o++] = byte (v>>0);
+ return o;
+}
+
+p16(d: array of byte, o: int, v: int): int
+{
+ d[o++] = byte (v>>8);
+ d[o++] = byte (v>>0);
+ return o;
+}
+
+pbuf(d: array of byte, o: int, buf: array of byte): int
+{
+ d[o:] = buf;
+ return o+len buf;
+}
+
+warn(s: string)
+{
+ say(s);
+}
+
+say(s: string)
+{
+ if(debug)
+ sys->fprint(sys->fildes(2), "%s\n", s);
+}
--- a/appl/lib/mkfile
+++ b/appl/lib/mkfile
@@ -36,8 +36,8 @@
debug.dis\
deflate.dis\
devpointer.dis\
- dhcp.dis\
- dhcpd.dis\
+ dhcpclient.dis\
+ dhcpserver.dis\
dial.dis\
dialog.dis\
dict.dis\
@@ -170,8 +170,8 @@
db.m\
debug.m\
devpointer.m\
- dhcp.m\
- dhcpd.m\
+ dhcpclient.m\
+ dhcpserver.m\
dict.m\
draw.m\
env.m\
@@ -239,7 +239,8 @@
names.dis: $ROOT/module/names.m
disks.dis: $ROOT/module/disks.m
scsiio.dis: $ROOT/module/scsiio.m
-dhcpclient.dis: $ROOT/module/dhcp.m
+dhcpclient.dis: $ROOT/module/dhcpclient.m
+dhcpserver.dis: $ROOT/module/dhcpserver.m
ubfa.dis: $ROOT/module/ubfa.m
secstore.dis: $ROOT/module/secstore.m
ida.dis: $ROOT/module/ida.m
--- a/module/dhcp.m
+++ /dev/null
@@ -1,84 +1,0 @@
-Dhcpclient: module
-{
- PATH: con "/dis/lib/dhcpclient.dis";
-
- Bootconf: adt {
- ip: string;
- ipgw: string;
- ipmask: string;
- bootf: string;
- bootip: string;
- dhcpip: string;
- siaddr: string;
- serverid: string;
- sys: string;
- dom: string;
- lease: int;
- options: array of array of byte;
- vendor: array of array of byte;
-
- new: fn(): ref Bootconf;
- get: fn(c: self ref Bootconf, n: int): array of byte;
- getint: fn(c: self ref Bootconf, n: int): int;
- getip: fn(c: self ref Bootconf, n: int): string;
- getips: fn(c: self ref Bootconf, n: int): list of string;
- gets: fn(c: self ref Bootconf, n: int): string;
- put: fn(c: self ref Bootconf, n: int, a: array of byte);
- putint: fn(c: self ref Bootconf, n: int, v: int);
- putips: fn(c: self ref Bootconf, n: int, ips: list of string);
- puts: fn(c: self ref Bootconf, n: int, s: string);
- };
-
- Lease: adt {
- pid: int;
- configs: chan of (ref Bootconf, string);
-
- release: fn(l: self ref Lease);
- };
-
- init: fn();
- tracing: fn(debug: int);
- bootp: fn(net: string, ctlifc: ref Sys->FD, device: string, init: ref Bootconf): (ref Bootconf, string);
- dhcp: fn(net: string, ctlifc: ref Sys->FD, device: string, init: ref Bootconf, options: array of int): (ref Bootconf, ref Lease, string);
-
- applycfg: fn(net: string, ctlifc: ref Sys->FD, conf: ref Bootconf): string;
- removecfg: fn(net: string, ctlifc: ref Sys->FD, conf: ref Bootconf): string;
-
- # bootp options used here
- Opad: con 0;
- Oend: con 255;
- Omask: con 1;
- Orouter: con 3;
- Odnsserver: con 6;
- Ocookieserver: con 8;
- Ohostname: con 12;
- Odomainname: con 15;
- Ontpserver: con 42;
- Ovendorinfo: con 43;
- Onetbiosns: con 44;
- Osmtpserver: con 69;
- Opop3server: con 70;
- Owwwserver: con 72;
-
- # dhcp options
- Oipaddr: con 50;
- Olease: con 51;
- Ooverload: con 52;
- Otype: con 53;
- Oserverid: con 54;
- Oparams: con 55;
- Omessage: con 56;
- Omaxmsg: con 57;
- Orenewaltime: con 58;
- Orebindingtime: con 59;
- Ovendorclass: con 60;
- Oclientid: con 61;
- Otftpserver: con 66;
- Obootfile: con 67;
-
- Ovendor: con (1<<8);
- OP9fs: con Ovendor|128; # plan 9 file server
- OP9auth: con Ovendor|129; # plan 9 auth server
-
- Infinite: con ~0; # lease
-};
--- /dev/null
+++ b/module/dhcpclient.m
@@ -1,0 +1,84 @@
+Dhcpclient: module
+{
+ PATH: con "/dis/lib/dhcpclient.dis";
+
+ Bootconf: adt {
+ ip: string;
+ ipgw: string;
+ ipmask: string;
+ bootf: string;
+ bootip: string;
+ dhcpip: string;
+ siaddr: string;
+ serverid: string;
+ sys: string;
+ dom: string;
+ lease: int;
+ options: array of array of byte;
+ vendor: array of array of byte;
+
+ new: fn(): ref Bootconf;
+ get: fn(c: self ref Bootconf, n: int): array of byte;
+ getint: fn(c: self ref Bootconf, n: int): int;
+ getip: fn(c: self ref Bootconf, n: int): string;
+ getips: fn(c: self ref Bootconf, n: int): list of string;
+ gets: fn(c: self ref Bootconf, n: int): string;
+ put: fn(c: self ref Bootconf, n: int, a: array of byte);
+ putint: fn(c: self ref Bootconf, n: int, v: int);
+ putips: fn(c: self ref Bootconf, n: int, ips: list of string);
+ puts: fn(c: self ref Bootconf, n: int, s: string);
+ };
+
+ Lease: adt {
+ pid: int;
+ configs: chan of (ref Bootconf, string);
+
+ release: fn(l: self ref Lease);
+ };
+
+ init: fn();
+ tracing: fn(debug: int);
+ bootp: fn(net: string, ctlifc: ref Sys->FD, device: string, init: ref Bootconf): (ref Bootconf, string);
+ dhcp: fn(net: string, ctlifc: ref Sys->FD, device: string, init: ref Bootconf, options: array of int): (ref Bootconf, ref Lease, string);
+
+ applycfg: fn(net: string, ctlifc: ref Sys->FD, conf: ref Bootconf): string;
+ removecfg: fn(net: string, ctlifc: ref Sys->FD, conf: ref Bootconf): string;
+
+ # bootp options used here
+ Opad: con 0;
+ Oend: con 255;
+ Omask: con 1;
+ Orouter: con 3;
+ Odnsserver: con 6;
+ Ocookieserver: con 8;
+ Ohostname: con 12;
+ Odomainname: con 15;
+ Ontpserver: con 42;
+ Ovendorinfo: con 43;
+ Onetbiosns: con 44;
+ Osmtpserver: con 69;
+ Opop3server: con 70;
+ Owwwserver: con 72;
+
+ # dhcp options
+ Oipaddr: con 50;
+ Olease: con 51;
+ Ooverload: con 52;
+ Otype: con 53;
+ Oserverid: con 54;
+ Oparams: con 55;
+ Omessage: con 56;
+ Omaxmsg: con 57;
+ Orenewaltime: con 58;
+ Orebindingtime: con 59;
+ Ovendorclass: con 60;
+ Oclientid: con 61;
+ Otftpserver: con 66;
+ Obootfile: con 67;
+
+ Ovendor: con (1<<8);
+ OP9fs: con Ovendor|128; # plan 9 file server
+ OP9auth: con Ovendor|129; # plan 9 auth server
+
+ Infinite: con ~0; # lease
+};
--- a/module/dhcpd.m
+++ /dev/null
@@ -1,72 +1,0 @@
-Dhcpserver: module
-{
- PATH: con "/dis/lib/dhcpd.dis";
-
- init: fn();
-
- Minmsglen: con 7*8+16+64+128; # dhcp clients may have no options
-
- # flags
- Fbroadcast: con 1<<15;
-
- Mp9: con 16r70392020; # "p9 "
- Moptions: con 16r63825363; # rfc2132
-
- # bootp/dhcp
- Opad: con 0;
- Oend: con 255;
- Osubnetmask: con 1;
- Orouters: con 3;
- Odns: con 6;
- Ohostname: con 12;
- Odomainname: con 15;
- Orootpath: con 17;
- Obroadcast: con 28;
- # dhcp only
- Oreqipaddr,
- Oleasetime,
- Ooptionoverload,
- Odhcpmsgtype,
- Oserverid,
- Oparamreq,
- Omessage,
- Omaxmsgsize,
- Orenewaltime,
- Orebindingtime,
- Ovendorclass,
- Oclientid: con 50+iota;
- Otftpservername,
- Obootfile: con 66+iota;
-
- Trequest, Treply: con 1+iota;
- TDdiscover, TDoffer, TDrequest, TDdecline, TDack, TDnak, TDrelease, TDinform: con 1+iota;
-
- Opt: adt {
- code: int;
- v: array of byte;
- };
-
- Dhcpmsg: adt
- {
- op: int;
- htype: int;
- hlen: int;
- hops: int;
- xid: int;
- secs: int;
- flags: int;
- ciaddr: IPaddr;
- yiaddr: IPaddr;
- siaddr: IPaddr;
- giaddr: IPaddr;
- chaddr: array of byte;
- sname: string;
- file: string;
- options: array of byte;
- opts: list of ref Opt;
-
- unpack: fn(a: array of byte): (ref Dhcpmsg, string);
- pack: fn(bp: self ref Dhcpmsg): array of byte;
- text: fn(bp: self ref Dhcpmsg): string;
- };
-};
--- /dev/null
+++ b/module/dhcpserver.m
@@ -1,0 +1,72 @@
+Dhcpserver: module
+{
+ PATH: con "/dis/lib/dhcpserver.dis";
+
+ init: fn();
+
+ Minmsglen: con 7*8+16+64+128; # dhcp clients may have no options
+
+ # flags
+ Fbroadcast: con 1<<15;
+
+ Mp9: con 16r70392020; # "p9 "
+ Moptions: con 16r63825363; # rfc2132
+
+ # bootp/dhcp
+ Opad: con 0;
+ Oend: con 255;
+ Osubnetmask: con 1;
+ Orouters: con 3;
+ Odns: con 6;
+ Ohostname: con 12;
+ Odomainname: con 15;
+ Orootpath: con 17;
+ Obroadcast: con 28;
+ # dhcp only
+ Oreqipaddr,
+ Oleasetime,
+ Ooptionoverload,
+ Odhcpmsgtype,
+ Oserverid,
+ Oparamreq,
+ Omessage,
+ Omaxmsgsize,
+ Orenewaltime,
+ Orebindingtime,
+ Ovendorclass,
+ Oclientid: con 50+iota;
+ Otftpservername,
+ Obootfile: con 66+iota;
+
+ Trequest, Treply: con 1+iota;
+ TDdiscover, TDoffer, TDrequest, TDdecline, TDack, TDnak, TDrelease, TDinform: con 1+iota;
+
+ Opt: adt {
+ code: int;
+ v: array of byte;
+ };
+
+ Dhcpmsg: adt
+ {
+ op: int;
+ htype: int;
+ hlen: int;
+ hops: int;
+ xid: int;
+ secs: int;
+ flags: int;
+ ciaddr: IPaddr;
+ yiaddr: IPaddr;
+ siaddr: IPaddr;
+ giaddr: IPaddr;
+ chaddr: array of byte;
+ sname: string;
+ file: string;
+ options: array of byte;
+ opts: list of ref Opt;
+
+ unpack: fn(a: array of byte): (ref Dhcpmsg, string);
+ pack: fn(bp: self ref Dhcpmsg): array of byte;
+ text: fn(bp: self ref Dhcpmsg): string;
+ };
+};