Refactored icmpkick6
[akaros.git] / kern / src / net / bootp.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
16 enum {
17         Bootrequest = 1,
18         Bootreply = 2,
19 };
20
21 typedef struct Bootp {
22         /* udp.c oldheader */
23         uchar raddr[IPaddrlen];
24         uchar laddr[IPaddrlen];
25         uchar rport[2];
26         uchar lport[2];
27         /* bootp itself */
28         uchar op;                                       /* opcode */
29         uchar htype;                            /* hardware type */
30         uchar hlen;                                     /* hardware address len */
31         uchar hops;                                     /* hops */
32         uchar xid[4];                           /* a random number */
33         uchar secs[2];                          /* elapsed snce client started booting */
34         uchar pad[2];
35         uchar ciaddr[4];                        /* client IP address (client tells server) */
36         uchar yiaddr[4];                        /* client IP address (server tells client) */
37         uchar siaddr[4];                        /* server IP address */
38         uchar giaddr[4];                        /* gateway IP address */
39         uchar chaddr[16];                       /* client hardware address */
40         uchar sname[64];                        /* server host name (optional) */
41         uchar file[128];                        /* boot file name */
42         uchar vend[128];                        /* vendor-specific goo */
43 } Bootp;
44
45 /*
46  * bootp returns:
47  *
48  * "fsip d.d.d.d
49  * auip d.d.d.d
50  * gwip d.d.d.d
51  * ipmask d.d.d.d
52  * ipaddr d.d.d.d"
53  *
54  * where d.d.d.d is the IP address in dotted decimal notation, and each
55  * address is followed by a newline.
56  */
57
58 static Bootp req;
59 static Proc *rcvprocp;
60 static int recv;
61 static int done;
62 static Rendez bootpr;
63 static char rcvbuf[512 + 2 * IPaddrlen + 2 * 2];
64
65 static void rcvbootp(void *a)
66 {
67         int n, fd;
68         Bootp *rp;
69         char *field[4];
70         uchar ip[IPaddrlen];
71
72         if (waserror())
73                 pexit("", 0);
74         rcvprocp = up;  /* store for postnote below */
75         fd = (int)a;
76         while (done == 0) {
77                 n = kread(fd, rcvbuf, sizeof(rcvbuf));
78                 if (n <= 0)
79                         break;
80                 rp = (Bootp *) rcvbuf;
81                 /* currently ignore udp's header */
82                 if (memcmp(req.chaddr, rp->chaddr, 6) == 0
83                         && rp->htype == 1 && rp->hlen == 6
84                         && getfields((char *)rp->vend + 4, field, 4, 1, " ") == 4
85                         && strncmp((char *)rp->vend, "p9  ", 4) == 0) {
86                         if (ipaddr == 0)
87                                 ipaddr = nhgetl(rp->yiaddr);
88                         if (ipmask == 0)
89                                 ipmask = parseip(ip, field[0]);
90                         if (fsip == 0)
91                                 fsip = parseip(ip, field[1]);
92                         if (auip == 0)
93                                 auip = parseip(ip, field[2]);
94                         if (gwip == 0)
95                                 gwip = parseip(ip, field[3]);
96                         break;
97                 }
98         }
99         poperror();
100         rcvprocp = nil;
101
102         recv = 1;
103         rendez_wakeup(&bootpr);
104         pexit("", 0);
105 }
106
107 static char *rbootp(Ipifc * ifc)
108 {
109         int cfd, dfd, tries, n;
110         char ia[5 + 3 * 24], im[16], *av[3];
111         uchar nipaddr[4], ngwip[4], nipmask[4];
112         char dir[Maxpath];
113
114         av[1] = "0.0.0.0";
115         av[2] = "0.0.0.0";
116         ipifcadd(ifc, av, 3, 0, nil);
117
118         cfd = kannounce("udp!*!68", dir);
119         if (cfd < 0)
120                 return "bootp announce failed";
121         strcat(dir, "/data");
122         if (kwrite(cfd, "headers", 7) < 0) {
123                 kclose(cfd);
124                 return "bootp ctl headers failed";
125         }
126         kwrite(cfd, "oldheaders", 10);
127         dfd = kopen(dir, ORDWR);
128         if (dfd < 0) {
129                 kclose(cfd);
130                 return "bootp open data failed";
131         }
132         kclose(cfd);
133
134         /* create request */
135         memset(&req, 0, sizeof(req));
136         ipmove(req.raddr, IPv4bcast);
137         hnputs(req.rport, 67);
138         req.op = Bootrequest;
139         req.htype = 1;  /* ethernet (all we know) */
140         req.hlen = 6;   /* ethernet (all we know) */
141
142         /* Hardware MAC address */
143         memmove(req.chaddr, ifc->mac, 6);
144         /* Fill in the local IP address if we know it */
145         ipv4local(ifc, req.ciaddr);
146         memset(req.file, 0, sizeof(req.file));
147         strcpy((char *)req.vend, "p9  ");
148
149         done = 0;
150         recv = 0;
151
152         ktask("rcvbootp", rcvbootp, (void *)dfd);
153
154         /*
155          * broadcast bootp's till we get a reply,
156          * or fixed number of tries
157          */
158         tries = 0;
159         while (recv == 0) {
160                 if (kwrite(dfd, &req, sizeof(req)) < 0)
161                         print("bootp: write: %s\n", commonerror());
162
163                 udelay_sched(1000 * 1000);
164                 if (++tries > 10) {
165                         print("bootp: timed out\n");
166                         break;
167                 }
168         }
169         kclose(dfd);
170         done = 1;
171         if (rcvprocp != nil) {
172                 postnote(rcvprocp, 1, "timeout", 0);
173                 rcvprocp = nil;
174         }
175
176         av[1] = "0.0.0.0";
177         av[2] = "0.0.0.0";
178         ipifcrem(ifc, av, 3);
179
180         hnputl(nipaddr, ipaddr);
181         sprint(ia, "%V", nipaddr);
182         hnputl(nipmask, ipmask);
183         sprint(im, "%V", nipmask);
184         av[1] = ia;
185         av[2] = im;
186         ipifcadd(ifc, av, 3, 0, nil);
187
188         if (gwip != 0) {
189                 hnputl(ngwip, gwip);
190                 n = snprint(ia, sizeof(ia), "add 0.0.0.0 0.0.0.0 %V", ngwip);
191                 routewrite(ifc->conv->p->f, nil, ia, n);
192         }
193         return nil;
194 }
195
196 static int rbootpread(char *bp, ulong offset, int len)
197 {
198         int n;
199         char *buf;
200         uchar a[4];
201
202         buf = smalloc(READSTR);
203         if (waserror()) {
204                 free(buf);
205                 nexterror();
206         }
207         hnputl(a, fsip);
208         n = snprint(buf, READSTR, "fsip %15V\n", a);
209         hnputl(a, auip);
210         n += snprint(buf + n, READSTR - n, "auip %15V\n", a);
211         hnputl(a, gwip);
212         n += snprint(buf + n, READSTR - n, "gwip %15V\n", a);
213         hnputl(a, ipmask);
214         n += snprint(buf + n, READSTR - n, "ipmask %15V\n", a);
215         hnputl(a, ipaddr);
216         snprint(buf + n, READSTR - n, "ipaddr %15V\n", a);
217
218         len = readstr(offset, bp, len, buf);
219         poperror();
220         free(buf);
221         return len;
222 }
223
224 char *(*bootp) (Ipifc *) = rbootp;
225 int (*bootpread) (char *, ulong, int) = rbootpread;