Adds chaninfo()
[akaros.git] / kern / src / net / ihbootp.c
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "../port/error.h"
7 #include "kernel.h"
8 #include "ip.h"
9
10 static  ulong   fsip;
11 static  ulong   auip;
12 static  ulong   gwip;
13 static  ulong   ipmask;
14 static  ulong   ipaddr;
15 static  ulong   dnsip;
16
17 enum
18 {
19         Bootrequest = 1,
20         Bootreply   = 2,
21 };
22
23 typedef struct Bootp
24 {
25         /* udp.c oldheader */
26         uchar   raddr[IPaddrlen];
27         uchar   laddr[IPaddrlen];
28         uchar   rport[2];
29         uchar   lport[2];
30         /* bootp itself */
31         uchar   op;             /* opcode */
32         uchar   htype;          /* hardware type */
33         uchar   hlen;           /* hardware address len */
34         uchar   hops;           /* hops */
35         uchar   xid[4];         /* a random number */
36         uchar   secs[2];        /* elapsed snce client started booting */
37         uchar   pad[2];
38         uchar   ciaddr[4];      /* client IP address (client tells server) */
39         uchar   yiaddr[4];      /* client IP address (server tells client) */
40         uchar   siaddr[4];      /* server IP address */
41         uchar   giaddr[4];      /* gateway IP address */
42         uchar   chaddr[16];     /* client hardware address */
43         uchar   sname[64];      /* server host name (optional) */
44         uchar   file[128];      /* boot file name */
45         uchar   vend[128];      /* vendor-specific goo */
46 } Bootp;
47
48 /*
49  * bootp returns:
50  *
51  * "fsip d.d.d.d
52  * auip d.d.d.d
53  * gwip d.d.d.d
54  * ipmask d.d.d.d
55  * ipaddr d.d.d.d
56  * dnsip d.d.d.d"
57  *
58  * where d.d.d.d is the IP address in dotted decimal notation, and each
59  * address is followed by a newline.
60  */
61
62 static  Bootp   req;
63 static  Proc*   rcvprocp;
64 static  int     recv;
65 static  int     done;
66 static  Rendez  bootpr;
67 static  char    rcvbuf[512];
68 static  int     bootpdebug;
69
70 /*
71  * Parse the vendor specific fields according to RFC 1084.
72  * We are overloading the "cookie server" to be the Inferno 
73  * authentication server and the "resource location server"
74  * to be the Inferno file server.
75  *
76  * If the vendor specific field is formatted properly, it
77  * will begin with the four bytes 99.130.83.99 and end with
78  * an 0xFF byte.
79  */
80 static void
81 parsevend(uchar* vend)
82 {
83         /* The field must start with 99.130.83.99 to be compliant */
84         if ((vend[0] != 99) || (vend[1] != 130) ||
85             (vend[2] != 83) || (vend[3] != 99)){
86                 if(bootpdebug)
87                         print("bad bootp vendor field: %.2x%.2x%.2x%.2x", vend[0], vend[1], vend[2], vend[3]);
88                 return;
89         }
90
91         /* Skip over the magic cookie */
92         vend += 4;
93
94         while ((vend[0] != 0) && (vend[0] != 0xFF)) {
95                 if(bootpdebug){
96                         int i;
97                         print("vend %d [%d]", vend[0], vend[1]);
98                         for(i=0; i<vend[1]; i++)
99                                 print(" %2.2x", vend[i]);
100                         print("\n");
101                 }
102                 switch (vend[0]) {
103                 case 1: /* Subnet mask field */
104                         /* There must be only one subnet mask */
105                         if (vend[1] != 4)
106                                 return;
107
108                         ipmask = (vend[2]<<24)|
109                                  (vend[3]<<16)|
110                                  (vend[4]<<8)|
111                                   vend[5];
112                         break;
113
114                 case 3: /* Gateway/router field */
115                         /* We are only concerned with first address */
116                         if (vend[1] < 4)
117                                 break;
118
119                         gwip =  (vend[2]<<24)|
120                                 (vend[3]<<16)|
121                                 (vend[4]<<8)|
122                                  vend[5];
123                         break;
124
125                 case 6: /* DNS server */
126                         /* We are only concerned with first address */
127                         if (vend[1] < 4)
128                                 break;
129
130                         dnsip = (vend[2]<<24)|
131                                 (vend[3]<<16)|
132                                 (vend[4]<<8)|
133                                  vend[5];
134                         break;
135
136                 case 8: /* "Cookie server" (auth server) field */
137                         /* We are only concerned with first address */
138                         if (vend[1] < 4)
139                                 break;
140
141                         auip =  (vend[2]<<24)|
142                                 (vend[3]<<16)|
143                                 (vend[4]<<8)|
144                                  vend[5];
145                         break;
146
147                 case 11:        /* "Resource loc server" (file server) field */
148                         /* We are only concerned with first address */
149                         if (vend[1] < 4)
150                                 break;
151
152                         fsip =  (vend[2]<<24)|
153                                 (vend[3]<<16)|
154                                 (vend[4]<<8)|
155                                  vend[5];
156                         break;
157
158                 default:        /* Ignore everything else */
159                         break;
160                 }
161
162                 /* Skip over the field */
163                 vend += vend[1] + 2;
164         }
165 }
166
167 static void
168 rcvbootp(void *a)
169 {
170         int n, fd;
171         Bootp *rp;
172
173         if(waserror())
174                 pexit("", 0);
175         rcvprocp = up;  /* store for postnote below */
176         fd = (int)a;
177         while(done == 0) {
178                 n = kread(fd, rcvbuf, sizeof(rcvbuf));
179                 if(n <= 0)
180                         break;
181                 rp = (Bootp*)rcvbuf;
182                 if (memcmp(req.chaddr, rp->chaddr, 6) == 0 &&
183                    rp->htype == 1 && rp->hlen == 6) {
184                         ipaddr = (rp->yiaddr[0]<<24)|
185                                  (rp->yiaddr[1]<<16)|
186                                  (rp->yiaddr[2]<<8)|
187                                   rp->yiaddr[3];
188                         parsevend(rp->vend);
189                         break;
190                 }
191         }
192         poperror();
193         rcvprocp = nil;
194
195         recv = 1;
196         rendez_wakeup(&bootpr);
197         pexit("", 0);
198 }
199
200 static char*
201 rbootp(Ipifc *ifc)
202 {
203         int cfd, dfd, tries, n;
204         char ia[5+3*16], im[16], *av[3];
205         uchar nipaddr[4], ngwip[4], nipmask[4];
206         char dir[Maxpath];
207         static uchar vend_rfc1048[] = { 99, 130, 83, 99 };
208
209         av[1] = "0.0.0.0";
210         av[2] = "0.0.0.0";
211         ipifcadd(ifc, av, 3, 0, nil);
212
213         cfd = kannounce("udp!*!68", dir);
214         if(cfd < 0)
215                 return "bootp announce failed";
216         strcat(dir, "/data");
217         if(kwrite(cfd, "headers", 7) < 0){
218                 kclose(cfd);
219                 return "bootp ctl headers failed";
220         }
221         kwrite(cfd, "oldheaders", 10);
222         dfd = kopen(dir, ORDWR);
223         if(dfd < 0){
224                 kclose(cfd);
225                 return "bootp open data failed";
226         }
227         kclose(cfd);
228
229         /* create request */
230         memset(&req, 0, sizeof(req));
231         ipmove(req.raddr, IPv4bcast);
232         hnputs(req.rport, 67);
233         req.op = Bootrequest;
234         req.htype = 1;                  /* ethernet (all we know) */
235         req.hlen = 6;                   /* ethernet (all we know) */
236
237         /* Hardware MAC address */
238         memmove(req.chaddr, ifc->mac, 6);
239         /* Fill in the local IP address if we know it */
240         ipv4local(ifc, req.ciaddr);
241         memset(req.file, 0, sizeof(req.file));
242         memmove(req.vend, vend_rfc1048, 4);
243
244         done = 0;
245         recv = 0;
246
247         ktask("rcvbootp", rcvbootp, (void *)dfd);
248
249         /*
250          * broadcast bootp's till we get a reply,
251          * or fixed number of tries
252          */
253         tries = 0;
254         while(recv == 0) {
255                 if(kwrite(dfd, &req, sizeof(req)) < 0)
256                         print("bootp: write: %r");
257
258                 udelay_sched(1000 * 1000);
259                 if(++tries > 10) {
260                         print("bootp: timed out\n");
261                         break;
262                 }
263         }
264         kclose(dfd);
265         done = 1;
266         if(rcvprocp != nil){
267                 postnote(rcvprocp, 1, "timeout", 0);
268                 rcvprocp = nil;
269         }
270
271         av[1] = "0.0.0.0";
272         av[2] = "0.0.0.0";
273         ipifcrem(ifc, av, 3);
274
275         hnputl(nipaddr, ipaddr);
276         sprint(ia, "%V", nipaddr);
277         hnputl(nipmask, ipmask);
278         sprint(im, "%V", nipmask);
279         av[1] = ia;
280         av[2] = im;
281         ipifcadd(ifc, av, 3, 0, nil);
282
283         if(gwip != 0) {
284                 hnputl(ngwip, gwip);
285                 n = sprint(ia, "add 0.0.0.0 0.0.0.0 %V", ngwip);
286                 routewrite(ifc->conv->p->f, nil, ia, n);
287         }
288         return nil;
289 }
290
291 static int
292 rbootpread(char *bp, ulong offset, int len)
293 {
294         int n;
295         char *buf;
296         uchar a[4];
297
298         buf = smalloc(READSTR);
299         if(waserror()){
300                 free(buf);
301                 nexterror();
302         }
303         hnputl(a, fsip);
304         n = snprint(buf, READSTR, "fsip %15V\n", a);
305         hnputl(a, auip);
306         n += snprint(buf + n, READSTR-n, "auip %15V\n", a);
307         hnputl(a, gwip);
308         n += snprint(buf + n, READSTR-n, "gwip %15V\n", a);
309         hnputl(a, ipmask);
310         n += snprint(buf + n, READSTR-n, "ipmask %15V\n", a);
311         hnputl(a, ipaddr);
312         n += snprint(buf + n, READSTR-n, "ipaddr %15V\n", a);
313         hnputl(a, dnsip);
314         snprint(buf + n, READSTR-n, "dnsip %15V\n", a);
315
316         len = readstr(offset, bp, len, buf);
317         poperror();
318         free(buf);
319         return len;
320 }
321
322 char*   (*bootp)(Ipifc*) = rbootp;
323 int     (*bootpread)(char*, ulong, int) = rbootpread;