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