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