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