xref: /inferno-os/appl/cmd/ip/dhcp.b (revision 504229f808836f4d74d6611fd666957e41e2a259)
1implement Dhcp;
2
3#
4# configure an interface using DHCP
5#
6
7include "sys.m";
8	sys: Sys;
9
10include "draw.m";
11
12include "ip.m";
13	ip: IP;
14	IPv4off, IPaddrlen: import IP;
15	IPaddr: import ip;
16	get2, get4, put2, put4: import ip;
17
18include "dhcp.m";
19	dhcpclient: Dhcpclient;
20	Bootconf, Lease: import dhcpclient;
21
22include "arg.m";
23
24Dhcp: module
25{
26	init:	fn(nil: ref Draw->Context, nil: list of string);
27};
28
29RetryTime: con 10*1000;	# msec
30
31init(nil: ref Draw->Context, args: list of string)
32{
33	sys = load Sys Sys->PATH;
34	ip = load IP IP->PATH;
35	dhcpclient = load Dhcpclient Dhcpclient->PATH;
36
37	sys->pctl(Sys->NEWFD|Sys->NEWPGRP, 0 :: 1 :: 2 :: nil);
38
39	arg := load Arg Arg->PATH;
40	arg->init(args);
41	arg->setusage("dhcp [-bdmnpr] [-g ipgw] [-h hostname] [-x /net] ifcdir [ip [ipmask]]");
42	trace := 0;
43	pcfg := 0;
44	bootp := 0;
45	monitor := 0;
46	retry := 0;
47	noctl := 0;
48	netdir := "/net";
49	cfg := Bootconf.new();
50	while((o := arg->opt()) != 0)
51		case o {
52		'b' =>	bootp = 1;
53		'd' =>	trace++;
54		'g' =>	cfg.ipgw = arg->earg();
55		'h' =>	cfg.puts(Dhcpclient->Ohostname, arg->earg());
56		'm' =>	monitor = 1;
57		'n' =>	noctl = 1;
58		'p' =>	pcfg = 1;
59		'r' =>		retry = 1;
60		'x' =>	netdir = arg->earg();
61		* =>		arg->usage();
62		}
63	args = arg->argv();
64	if(len args == 0)
65		arg->usage();
66
67	ifcdir := hd args;
68	args = tl args;
69	if(args != nil){
70		cfg.ip = hd args;
71		args = tl args;
72		if(args != nil){
73			cfg.ipmask = hd args;
74			args = tl args;
75			if(args != nil)
76				arg->usage();
77		}
78	}
79	arg = nil;
80
81	ifcctl: ref Sys->FD;
82	if(noctl == 0){
83		ifcctl = sys->open(ifcdir+"/ctl", Sys->OWRITE);
84		if(ifcctl == nil)
85			err(sys->sprint("cannot open %s/ctl: %r", ifcdir));
86	}
87	etherdir := finddev(ifcdir);
88	if(etherdir == nil)
89		err(sys->sprint("cannot find network device in %s/status: %r", ifcdir));
90	if(etherdir[0] != '/' && etherdir[0] != '#')
91		etherdir = netdir+"/"+etherdir;
92
93	ip->init();
94	dhcpclient->init();
95	dhcpclient->tracing(trace);
96	e: string;
97	lease: ref Lease;
98	for(;;){
99		if(bootp){
100			(cfg, e) = dhcpclient->bootp(netdir, ifcctl, etherdir+"/addr", cfg);
101			if(e == nil){
102				if(cfg != nil)
103					dhcpclient->applycfg(netdir, ifcctl, cfg);
104				if(pcfg)
105					printcfg(cfg);
106				break;
107			}
108		}else{
109			(cfg, lease, e) = dhcpclient->dhcp(netdir, ifcctl, etherdir+"/addr", cfg, nil);	# last is array of int options
110			if(e == nil){
111				if(pcfg)
112					printcfg(cfg);
113				if(cfg.lease > 0 && monitor)
114					leasemon(lease.configs, pcfg);
115				break;
116			}
117		}
118		if(!retry)
119			err("failed to configure network: "+e);
120		sys->fprint(sys->fildes(2), "dhcp: failed to configure network: %s; retrying", e);
121		sys->sleep(RetryTime);
122	}
123}
124
125leasemon(configs: chan of (ref Bootconf, string), pcfg: int)
126{
127	for(;;){
128		(cfg, e) := <-configs;
129		if(e != nil)
130			sys->fprint(sys->fildes(2), "dhcp: %s", e);
131		if(pcfg)
132			printcfg(cfg);
133	}
134}
135
136printcfg(cfg: ref Bootconf)
137{
138	sys->print("ip=%s ipmask=%s ipgw=%s iplease=%d\n", cfg.ip, cfg.ipmask, cfg.ipgw, cfg.lease);
139}
140
141finddev(ifcdir: string): string
142{
143	fd := sys->open(ifcdir+"/status", Sys->OREAD);
144	if(fd == nil)
145		return nil;
146	buf := array[1024] of byte;
147	n := sys->read(fd, buf, len buf);
148	if(n < 0)
149		return nil;
150	(nf, l) := sys->tokenize(string buf[0:n], " \n");
151	if(nf < 2){
152		sys->werrstr("unexpected format for status file");
153		return nil;
154	}
155	return hd tl l;
156}
157
158err(s: string)
159{
160	sys->fprint(sys->fildes(2), "dhcp: %s\n", s);
161	raise "fail:error";
162}
163