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 "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; 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 operationsBootprequest, Bootpreply: con 1+iota; # DHCP operationsNotDHCP, 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"; (ok, conn) := sys->announce(net+"/udp!*!68"); if(ok < 0) 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,};Lines 1–500 of 1029. Load more
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 "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; 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"; (ok, conn) := sys->announce(net+"/udp!*!68"); if(ok < 0) 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, };