Adds chaninfo()
[akaros.git] / kern / src / net / dhcp.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 #include "ppp.h"
10
11 Ipaddr pppdns[2];
12
13 static  ulong   fsip;
14 static  ulong   auip;
15 static  ulong   gwip;
16 static  ulong   ipmask;
17 static  ulong   ipaddr;
18 static  ulong   dns1ip;
19 static  ulong   dns2ip;
20
21 int             dhcpmsgtype;
22 int             debug=0;
23 enum
24 {
25         Bootrequest = 1,
26         Bootreply   = 2,
27 };
28
29 typedef struct Bootp
30 {
31         /* udp.c oldheader */
32         uchar   raddr[IPaddrlen];
33         uchar   laddr[IPaddrlen];
34         uchar   rport[2];
35         uchar   lport[2];
36         /* bootp itself */
37         uchar   op;                     /* opcode */
38         uchar   htype;          /* hardware type */
39         uchar   hlen;                   /* hardware address len */
40         uchar   hops;           /* hops */
41         uchar   xid[4];         /* a random number */
42         uchar   secs[2];                /* elapsed snce client started booting */
43         uchar   flags[2];               /* flags */
44         uchar   ciaddr[4];              /* client IP address (client tells server) */
45         uchar   yiaddr[4];              /* client IP address (server tells client) */
46         uchar   siaddr[4];              /* server IP address */
47         uchar   giaddr[4];              /* gateway IP address */
48         uchar   chaddr[16];     /* client hardware address */
49         uchar   sname[64];      /* server host name (optional) */
50         uchar   file[128];              /* boot file name */
51         uchar   vend[128];      /* vendor-specific goo 340 */
52 } Bootp;
53
54 static  Bootp   req;
55 static  Proc*   rcvprocp;
56 static  int     recv;
57 static  int     done;
58 static  Rendez  bootpr;
59 static  char    rcvbuf[512+2*IPaddrlen+2*2];      /* 576 */
60 static  uchar sid[4];
61 static  ulong iplease;
62
63 /*
64  * bootp returns:
65  *
66  * "fsip d.d.d.d
67  * auip d.d.d.d
68  * gwip d.d.d.d
69  * ipmask d.d.d.d
70  * ipaddr d.d.d.d
71  * dns1ip       d.d.d.d
72  * dns2ip       d.d.d.d
73  *
74  * where d.d.d.d is the IP address in dotted decimal notation, and each
75  * address is followed by a newline.
76         Last change:  SUN  13 Sep 2001    4:36 pm
77  */
78
79 /*
80  * Parse the vendor specific fields according to RFC 1084.
81  * We are overloading the "cookie server" to be the Inferno 
82  * authentication server and the "resource location server"
83  * to be the Inferno file server.
84  *
85  * If the vendor specific field is formatted properly, it
86  * will being with the four bytes 99.130.83.99 and end with
87  * an 0xFF byte.
88  */
89 static int
90 parsevend(uchar* pvend)
91 {       
92         uchar *vend=pvend;
93         int dhcpmsg=0;
94         /* The field must start with 99.130.83.99 to be compliant */
95         if ((vend[0] != 99) || (vend[1] != 130) || (vend[2] != 83) || (vend[3] != 99)){
96                 print("bad bootp vendor field: %.2x%.2x%.2x%.2x", vend[0], vend[1], vend[2], vend[3]);
97                 return -1;
98         }
99
100         /* Skip over the magic cookie */
101         vend += 4;
102
103         while ((vend[0] != 0) && (vend[0] != 0xFF)) {
104                 int i;
105 //      
106                 if(debug){
107                         print(">>>Opt[%d] [%d]", vend[0], vend[1]);
108                         for(i=0; i<vend[1]; i++)
109                                 print(" %2.2x", vend[i+2]);
110                         print("\n");
111                 }
112 //
113                 switch (vend[0]) {
114                 case 1: /* Subnet mask field */
115                         /* There must be only one subnet mask */
116                         if (vend[1] == 4)
117                                 ipmask = (vend[2]<<24)|(vend[3]<<16)| (vend[4]<<8)| vend[5];
118                         else{ 
119                                 return -1;
120                         }
121                         break;
122
123                 case 3: /* Gateway/router field */
124                         /* We are only concerned with first address */
125                         if (vend[1] >0 && vend[1]%4==0)
126                                 gwip = (vend[2]<<24)|(vend[3]<<16)|(vend[4]<<8)|vend[5];
127                         else 
128                                 return -1;
129                         break;
130                 case 6: /* domain name server */
131                         if(vend[1]>0 && vend[1] %4==0){
132                                 dns1ip=(vend[2]<<24)|(vend[3]<<16)|(vend[4]<<8)|vend[5];
133                                 if(vend[1]>4)
134                                         dns2ip=(vend[6]<<24)|(vend[7]<<16)|(vend[8]<<8)|vend[9];
135                         }else
136                                 return -1;
137                         break;
138
139                 case 8: /* "Cookie server" (auth server) field */
140                         /* We are only concerned with first address */
141                         if (vend[1] > 0 && vend[1]%4==0)
142                                 auip = (vend[2]<<24)|(vend[3]<<16)|(vend[4]<<8)|vend[5];
143                         else
144                                 return -1;
145                         break;
146
147                 case 11:        /* "Resource loc server" (file server) field */
148                         /* We are only concerned with first address */
149                         if (vend[1] > 0 && vend[1]%4==0)
150                                 fsip = (vend[2]<<24)| (vend[3]<<16)| (vend[4]<<8)| vend[5];
151                         else
152                                 return -1;
153                         break;
154                 case 51:        /* ip lease time */
155                         if(vend[1]==4){
156                                 iplease=(vend[2]<<24)|(vend[3]<<16)|(vend[4]<<8)|vend[5];
157                         }else
158                                 return -1;
159                         break;
160                 case 53:        /* DHCP message type */
161                         if(vend[1]==1)
162                                 dhcpmsg=vend[2];
163                         else
164                                 return -1;
165                         break;
166                 case 54:        /* server identifier */
167                         if(vend[1]==4){
168                                 memmove(sid, vend+2, 4);
169                         }else
170                                 return -1;
171                         break;
172
173                 default:        /* Everything else stops us */
174                         break;
175                 }
176
177                 /* Skip over the field */
178                 vend += vend[1] + 2;
179         }
180         if(debug)
181                 print(">>>Opt[%d] [%d]\n", vend[0], vend[1]);
182         return dhcpmsg;
183 }
184
185 static void
186 dispvend(uchar* pvend)
187 {       
188         uchar *vend=pvend;
189
190         //print("<<<Magic : %2.2x%2.2x%2.2x%2.2x\n", vend[0], vend[1], vend[2], vend[3]);
191         
192         vend += 4;              /* Skip over the magic cookie */
193         while ((vend[0] != 0) && (vend[0] != 0xFF)) {
194         //      int i;
195           //    print("<<<Opt[%d] [%d]", vend[0], vend[1]);
196                 //for(i=0; i<vend[1]; i++)
197                 //      print(" %2.2x", vend[i+2]);
198                 //print("\n");
199         
200                 vend += vend[1] + 2;
201         }
202         //print("<<<Opt[ %2.2x] [%2.2x]\n", vend[0], vend[1]);
203 }
204
205 static void
206 rcvbootp(void *a)
207 {
208         int n, fd, dhcp;
209         Bootp *rp;
210
211         if(waserror())
212                 pexit("", 0);
213         rcvprocp = up;  /* store for postnote below */
214         fd = (int)a;
215         while(done == 0) {
216                 if(debug)
217                         print("rcvbootp:looping\n");
218
219                 n = kread(fd, rcvbuf, sizeof(rcvbuf));
220                 if(n <= 0)
221                         break;
222                 rp = (Bootp*)rcvbuf;
223                 if (memcmp(req.chaddr, rp->chaddr, 6) == 0 && rp->htype == 1 && rp->hlen == 6) {
224                         ipaddr = (rp->yiaddr[0]<<24)| (rp->yiaddr[1]<<16)| (rp->yiaddr[2]<<8)| rp->yiaddr[3];
225                         if(debug)
226                                 print("ipaddr = %2.2x %2.2x %2.2x %2.2x \n", rp->yiaddr[0], rp->yiaddr[1], rp->yiaddr[2], rp->yiaddr[3]);
227                         //memmove(req.siaddr, rp->siaddr, 4);   /* siaddr */
228                         dhcp = parsevend(rp->vend);
229         
230                         if(dhcpmsgtype < dhcp){
231                                 dhcpmsgtype=dhcp;
232                                 recv = 1;
233                                 rendez_wakeup(&bootpr);
234                                 if(dhcp==0 || dhcp ==5 || dhcp == 6 )
235                                         break;
236                         }
237                 }
238         }
239         poperror();
240         rcvprocp = nil;
241
242         if(debug)
243                 print("rcvbootp exit\n");
244         pexit("", 0);
245 }
246
247 static char*
248 rbootp(Ipifc *ifc)
249 {
250         int cfd, dfd, tries, n;
251         char ia[5+3*16], im[16], *av[3];
252         uchar nipaddr[4], ngwip[4], nipmask[4];
253         char dir[Maxpath];
254         static uchar vend_rfc1048[] = { 99, 130, 83, 99 };
255         uchar *vend;
256
257         /*
258          * broadcast bootp's till we get a reply,
259          * or fixed number of tries
260          */
261         if(debug)
262             print("dhcp: bootp() called\n");
263         tries = 0;
264         av[1] = "0.0.0.0";
265         av[2] = "0.0.0.0";
266         ipifcadd(ifc, av, 3, 0, nil);
267
268         cfd = kannounce("udp!*!68", dir);
269         if(cfd < 0)
270                 return "dhcp announce failed";
271         strcat(dir, "/data");
272         if(kwrite(cfd, "headers", 7) < 0){
273                 kclose(cfd);
274                 return "dhcp ctl headers failed";
275         }
276         kwrite(cfd, "oldheaders", 10);
277         dfd = kopen(dir, ORDWR);
278         if(dfd < 0){
279                 kclose(cfd);
280                 return "dhcp open data failed";
281         }
282         kclose(cfd);
283         
284         while(tries<1){
285                 tries++;
286                 memset(sid, 0, 4);
287                 iplease=0;
288                 dhcpmsgtype=-2;
289 /* DHCPDISCOVER*/
290                 done = 0;
291                 recv = 0;
292                 ktask("rcvbootp", rcvbootp, (void *)dfd);
293                 /* Prepare DHCPDISCOVER */      
294                 memset(&req, 0, sizeof(req));
295                 ipmove(req.raddr, IPv4bcast);
296                 hnputs(req.rport, 67);
297                 req.op = Bootrequest;
298                 req.htype = 1;                  /* ethernet (all we know) */
299                 req.hlen = 6;                   /* ethernet (all we know) */
300                 
301                 memmove(req.chaddr, ifc->mac, 6);       /* Hardware MAC address */
302                 //ipv4local(ifc, req.ciaddr);                           /* Fill in the local IP address if we know it */
303                 memset(req.file, 0, sizeof(req.file));
304                 vend=req.vend;
305                 memmove(vend, vend_rfc1048, 4); vend+=4;
306                 *vend++=53; *vend++=1;*vend++=1;                /* dhcp msg type==3, dhcprequest */
307                 
308                 *vend++=61;*vend++=7;*vend++=1;
309                 memmove(vend, ifc->mac, 6);vend+=6;
310                 *vend=0xff;
311
312                 if(debug)
313                         dispvend(req.vend); 
314                 for(n=0;n<4;n++){
315                         if(kwrite(dfd, &req, sizeof(req))<0)    /* SEND DHCPDISCOVER */
316                                 print("DHCPDISCOVER: %r");
317                 
318                         udelay_sched(1000 * 1000);      /* wait DHCPOFFER */
319                         if(debug)
320                                 print("[DHCP] DISCOVER: msgtype = %d\n", dhcpmsgtype);
321
322                         if(dhcpmsgtype==2)              /* DHCPOFFER */
323                                 break;
324                         else if(dhcpmsgtype==0) /* bootp */
325                                 return nil;
326                         else if(dhcpmsgtype== -2)       /* time out */
327                                 continue;
328                         else
329                                 break;
330                         
331                 }
332                 if(dhcpmsgtype!=2)
333                         continue;
334
335 /* DHCPREQUEST */       
336                 memset(req.vend, 0, sizeof(req.vend));
337                 vend=req.vend;
338                 memmove(vend, vend_rfc1048, 4);vend+=4; 
339
340                 *vend++=53; *vend++=1;*vend++=3;                /* dhcp msg type==3, dhcprequest */
341
342                 *vend++=50;     *vend++=4;                              /* requested ip address */
343                 *vend++=(ipaddr >> 24)&0xff;
344                 *vend++=(ipaddr >> 16)&0xff;
345                 *vend++=(ipaddr >> 8) & 0xff;
346                 *vend++=ipaddr & 0xff;
347
348                 *vend++=51;*vend++=4;                                   /* lease time */
349                 *vend++=(iplease>>24)&0xff; *vend++=(iplease>>16)&0xff; *vend++=(iplease>>8)&0xff; *vend++=iplease&0xff;
350
351                 *vend++=54; *vend++=4;                                  /* server identifier */
352                 memmove(vend, sid, 4);  vend+=4;
353         
354                 *vend++=61;*vend++=07;*vend++=01;               /* client identifier */
355                 memmove(vend, ifc->mac, 6);vend+=6;
356                 *vend=0xff;
357                 if(debug) 
358                         dispvend(req.vend); 
359                 if(kwrite(dfd, &req, sizeof(req))<0){
360                         print("DHCPREQUEST: %r");
361                         continue;
362                 }
363                 udelay_sched(2000 * 1000);
364                 if(dhcpmsgtype==5)              /* wait for DHCPACK */
365                         break;
366                 else
367                         continue;
368                 /* CHECK ARP */
369                 /* DHCPDECLINE */
370         }
371         kclose(dfd);
372         done = 1;
373         if(rcvprocp != nil){
374                 postnote(rcvprocp, 1, "timeout", 0);
375                 rcvprocp = nil;
376         }
377
378         av[1] = "0.0.0.0";
379         av[2] = "0.0.0.0";
380         ipifcrem(ifc, av, 3);
381
382         hnputl(nipaddr, ipaddr);
383         sprint(ia, "%V", nipaddr);
384         hnputl(nipmask, ipmask);
385         sprint(im, "%V", nipmask);
386         av[1] = ia;
387         av[2] = im;
388         ipifcadd(ifc, av, 3, 0, nil);
389
390         if(gwip != 0) {
391                 hnputl(ngwip, gwip);
392                 n = sprint(ia, "add 0.0.0.0 0.0.0.0 %V", ngwip);
393                 routewrite(ifc->conv->p->f, nil, ia, n);
394         }
395         return nil;
396 }
397
398 static int
399 rbootpread(char *bp, ulong offset, int len)
400 {
401         int n, i;
402         char *buf;
403         uchar a[4];
404
405         if(debug)
406                 print("dhcp: bootpread() \n");
407         buf = smalloc(READSTR);
408         if(waserror()){
409                 free(buf);
410                 nexterror();
411         }
412
413         hnputl(a, fsip);
414         n = snprint(buf, READSTR, "fsip %15V\n", a);
415         hnputl(a, auip);
416         n += snprint(buf + n, READSTR-n, "auip %15V\n", a);
417         hnputl(a, gwip);
418         n += snprint(buf + n, READSTR-n, "gwip %15V\n", a);
419         hnputl(a, ipmask);
420         n += snprint(buf + n, READSTR-n, "ipmask %15V\n", a);
421         hnputl(a, ipaddr);
422         n += snprint(buf + n, READSTR-n, "ipaddr %15V\n", a);
423         n += snprint(buf+n, READSTR-n, "expired %lu\n", iplease);
424
425         n += snprint(buf + n, READSTR-n, "dns");
426         if(dns2ip){
427                 hnputl(a, dns2ip);
428                 n+=snprint(buf + n, READSTR-n, " %15V", a);
429         }
430         if(dns1ip){
431                 hnputl(a, dns1ip);
432                 n += snprint(buf + n, READSTR-n, " %15V", a);
433         }
434
435         for(i=0; i<2; i++)
436                 if(ipcmp(pppdns[i], IPnoaddr) != 0 && ipcmp(pppdns[i], v4prefix) != 0)
437                         n += snprint(buf + n, READSTR-n, " %15I", pppdns[i]);
438
439         snprint(buf + n, READSTR-n, "\n");
440         len = readstr(offset, bp, len, buf);
441         poperror();
442         free(buf);
443         return len;
444 }
445
446 char*   (*bootp)(Ipifc*) = rbootp;
447 int     (*bootpread)(char*, ulong, int) = rbootpread;