chiark / gitweb /
5c17706b635c373fa0a264edfb91395c2b06fd4c
[elogind.git] / klibc / klibc / tests / nfs_no_rpc.c
1 #include <sys/types.h>
2 #include <sys/socket.h>
3 #include <sys/utsname.h>
4 #include <sys/mount.h>
5 #include <netinet/in.h>
6 #include <unistd.h>
7 #include <signal.h>
8 #include <string.h>
9 #include <stdlib.h>
10 #include <stdio.h>
11
12 /* Default path we try to mount. "%s" gets replaced by our IP address */
13 #define NFS_ROOT                "/tftpboot/%s"
14 #define NFS_DEF_FILE_IO_BUFFER_SIZE     4096
15 #define NFS_MAXPATHLEN  1024
16 #define NFS_MNT_PROGRAM 100005
17 #define NFS_MNT_PORT    627
18 #define NFS_PROGRAM     100003
19 #define NFS_PORT        2049
20 #define NFS2_VERSION            2
21 #define NFS3_VERSION            3
22 #define NFS_MNT_PROGRAM         100005
23 #define NFS_MNT_VERSION         1
24 #define NFS_MNT3_VERSION        3
25 #define MNTPROC_MNT             1
26 #define MOUNTPROC3_MNT          1
27 #define RPC_PMAP_PROGRAM        100000
28 #define RPC_PMAP_VERSION        2
29 #define RPC_PMAP_PORT           111
30
31 #define NFS2_FHSIZE     32
32 #define NFS3_FHSIZE     64
33
34 #define RPC_VERSION 2
35
36 enum rpc_msg_type {
37         RPC_CALL = 0,
38         RPC_REPLY = 1
39 };
40
41 enum rpc_auth_flavor {
42         RPC_AUTH_NULL  = 0,
43         RPC_AUTH_UNIX  = 1,
44         RPC_AUTH_SHORT = 2,
45         RPC_AUTH_DES   = 3,
46         RPC_AUTH_KRB   = 4,
47 };
48
49 enum rpc_reply_stat {
50         RPC_MSG_ACCEPTED = 0,
51         RPC_MSG_DENIED = 1
52 };
53
54 #define NFS_MAXFHSIZE           64
55 struct nfs_fh {
56         unsigned short          size;
57         unsigned char           data[NFS_MAXFHSIZE];
58 };
59
60 struct nfs2_fh {
61         char                    data[NFS2_FHSIZE];
62 };
63
64 #define NFS_MOUNT_VERSION       4
65
66 struct nfs_mount_data {
67         int             version;
68         int             fd;
69         struct nfs2_fh  old_root;
70         int             flags;
71         int             rsize;
72         int             wsize;
73         int             timeo;
74         int             retrans;
75         int             acregmin;
76         int             acregmax;
77         int             acdirmin;
78         int             acdirmax;
79         struct sockaddr_in addr;
80         char            hostname[256];
81         int             namlen;
82         unsigned int    bsize;
83         struct nfs_fh   root;
84 };
85
86 #define NFS_MOUNT_SOFT          0x0001  /* 1 */
87 #define NFS_MOUNT_INTR          0x0002  /* 1 */
88 #define NFS_MOUNT_SECURE        0x0004  /* 1 */
89 #define NFS_MOUNT_POSIX         0x0008  /* 1 */
90 #define NFS_MOUNT_NOCTO         0x0010  /* 1 */
91 #define NFS_MOUNT_NOAC          0x0020  /* 1 */
92 #define NFS_MOUNT_TCP           0x0040  /* 2 */
93 #define NFS_MOUNT_VER3          0x0080  /* 3 */
94 #define NFS_MOUNT_KERBEROS      0x0100  /* 3 */
95 #define NFS_MOUNT_NONLM         0x0200  /* 3 */
96 #define NFS_MOUNT_BROKEN_SUID   0x0400  /* 4 */
97 #define NFS_MOUNT_FLAGMASK      0xFFFF
98
99 static char nfs_root_name[256];
100 static u_int32_t root_server_addr;
101 static char root_server_path[256];
102
103 /* Address of NFS server */
104 static u_int32_t servaddr;
105
106 /* Name of directory to mount */
107 static char nfs_path[NFS_MAXPATHLEN];
108
109 /* NFS-related data */
110 static struct nfs_mount_data nfs_data = {
111         .version  =     NFS_MOUNT_VERSION,
112         .flags    =     NFS_MOUNT_NONLM,        /* No lockd in nfs root yet */
113         .rsize    =     NFS_DEF_FILE_IO_BUFFER_SIZE,
114         .wsize    =     NFS_DEF_FILE_IO_BUFFER_SIZE,
115         .bsize    =     0,
116         .timeo    =     7,
117         .retrans  =     3,
118         .acregmin =     3,
119         .acregmax =     60,
120         .acdirmin =     30,
121         .acdirmax =     60,
122 };
123 static int nfs_port = -1;
124 static int mount_port;
125
126 /***************************************************************************
127
128                              Parsing of options
129
130  ***************************************************************************/
131
132 /*
133  *  The following integer options are recognized
134  */
135 static struct nfs_int_opts {
136         const char *name;
137         int  *val;
138 } root_int_opts[] = {
139         { "port",       &nfs_port },
140         { "rsize",      &nfs_data.rsize },
141         { "wsize",      &nfs_data.wsize },
142         { "timeo",      &nfs_data.timeo },
143         { "retrans",    &nfs_data.retrans },
144         { "acregmin",   &nfs_data.acregmin },
145         { "acregmax",   &nfs_data.acregmax },
146         { "acdirmin",   &nfs_data.acdirmin },
147         { "acdirmax",   &nfs_data.acdirmax },
148         { NULL,         NULL }
149 };
150
151 /*
152  *  And now the flag options
153  */
154 static struct nfs_bool_opts {
155         const char *name;
156         int  and_mask;
157         int  or_mask;
158 } root_bool_opts[] = {
159         { "soft",       ~NFS_MOUNT_SOFT,        NFS_MOUNT_SOFT },
160         { "hard",       ~NFS_MOUNT_SOFT,        0 },
161         { "intr",       ~NFS_MOUNT_INTR,        NFS_MOUNT_INTR },
162         { "nointr",     ~NFS_MOUNT_INTR,        0 },
163         { "posix",      ~NFS_MOUNT_POSIX,       NFS_MOUNT_POSIX },
164         { "noposix",    ~NFS_MOUNT_POSIX,       0 },
165         { "cto",        ~NFS_MOUNT_NOCTO,       0 },
166         { "nocto",      ~NFS_MOUNT_NOCTO,       NFS_MOUNT_NOCTO },
167         { "ac",         ~NFS_MOUNT_NOAC,        0 },
168         { "noac",       ~NFS_MOUNT_NOAC,        NFS_MOUNT_NOAC },
169         { "lock",       ~NFS_MOUNT_NONLM,       0 },
170         { "nolock",     ~NFS_MOUNT_NONLM,       NFS_MOUNT_NONLM },
171 #ifdef CONFIG_NFS_V3
172         { "v2",         ~NFS_MOUNT_VER3,        0 },
173         { "v3",         ~NFS_MOUNT_VER3,        NFS_MOUNT_VER3 },
174 #endif
175         { "udp",        ~NFS_MOUNT_TCP,         0 },
176         { "tcp",        ~NFS_MOUNT_TCP,         NFS_MOUNT_TCP },
177         { "broken_suid",~NFS_MOUNT_BROKEN_SUID, NFS_MOUNT_BROKEN_SUID },
178         { NULL,         0,                      0 }
179 };
180 /*
181  *  Parse option string.
182  */
183 static void root_nfs_parse(char *name, char *buf)
184 {
185         char *options, *val, *cp;
186
187         if ((options = strchr(name, ','))) {
188                 *options++ = 0;
189                 cp = strtok(options, ",");
190                 while (cp) {
191                         if ((val = strchr(cp, '='))) {
192                                 struct nfs_int_opts *opts = root_int_opts;
193                                 *val++ = '\0';
194                                 while (opts->name && strcmp(opts->name, cp))
195                                         opts++;
196                                 if (opts->name)
197                                         *(opts->val) = (int) strtoul(val, NULL, 10);
198                         } else {
199                                 struct nfs_bool_opts *opts = root_bool_opts;
200                                 while (opts->name && strcmp(opts->name, cp))
201                                         opts++;
202                                 if (opts->name) {
203                                         nfs_data.flags &= opts->and_mask;
204                                         nfs_data.flags |= opts->or_mask;
205                                 }
206                         }
207                         cp = strtok(NULL, ",");
208                 }
209         }
210         if (name[0] && strcmp(name, "default")) {
211                 strncpy(buf, name, NFS_MAXPATHLEN-1);
212                 buf[NFS_MAXPATHLEN-1] = 0;
213         }
214 }
215
216 /*
217  *  Prepare the NFS data structure and parse all options.
218  */
219 static int root_nfs_name(char *name)
220 {
221         char buf[NFS_MAXPATHLEN];
222         struct utsname uname_buf;
223
224         /* Set some default values */
225         strcpy(buf, NFS_ROOT);
226
227         /* Process options received from the remote server */
228         root_nfs_parse(root_server_path, buf);
229
230         /* Override them by options set on kernel command-line */
231         root_nfs_parse(name, buf);
232
233         uname(&uname_buf);
234         if (strlen(buf) + strlen(uname_buf.nodename) > NFS_MAXPATHLEN) {
235                 printf("nfsroot: Pathname for remote directory too long.\n");
236                 return -1;
237         }
238         sprintf(nfs_path, buf, uname_buf.nodename);
239
240         return 1;
241 }
242
243 /***************************************************************************
244
245                Routines to actually mount the root directory
246
247  ***************************************************************************/
248
249 /*
250  *  Construct sockaddr_in from address and port number.
251  */
252 static inline void
253 set_sockaddr(struct sockaddr_in *sin, u_int32_t addr, u_int16_t port)
254 {
255         memset(sin, 0, sizeof(*sin));
256         sin->sin_family = AF_INET;
257         sin->sin_addr.s_addr = addr;
258         sin->sin_port = port;
259 }
260
261 /*
262  * Extremely crude RPC-over-UDP call. We get an already encoded request
263  * to pass, we do that and put the reply into buffer. That (and callers
264  * below - getport, getfh2 and getfh3) should be replaced with proper
265  * librpc use. Now, if we only had one that wasn't bloated as a dead
266  * gnu that had lied for a while under the sun...
267  */
268
269 static u_int32_t XID;
270 static int flag;
271 static void timeout(int n)
272 {
273         (void)n;
274         flag = 1;
275 }
276 static int do_call(struct sockaddr_in *sin, u_int32_t msg[], u_int32_t rmsg[],
277                 u_int32_t len, u_int32_t rlen)
278 {
279         struct sockaddr_in from;
280         int slen = sizeof(struct sockaddr_in);
281         struct timeval tv = {1, 0};
282         int n;
283         int fd;
284
285         sysv_signal(SIGALRM, timeout);
286         fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
287         if (fd < 0)
288                 goto Esocket;
289         setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (void*)&tv, sizeof(tv));
290         len *= 4;
291         if (sendto(fd, msg, len, 0, (struct sockaddr *)sin, slen)!=(int)len)
292                 goto Esend;
293         setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (void*)&tv, sizeof(tv));
294         alarm(0);
295         flag = 0;
296         alarm(5);
297         rlen *= 4;
298         do  {
299                 slen = sizeof(from);
300                 n = recvfrom(fd, rmsg, rlen, 0, (struct sockaddr*)&from, &slen);
301                 if (flag || n < 0)
302                         goto Erecv;
303         } while (memcmp(&from, sin, sizeof(from)) || rmsg[0] != msg[0]);
304
305         if (n < 6*4 || n % 4 || ntohl(rmsg[1]) != 1 || rmsg[2] ||
306                                         rmsg[3] || rmsg[4] || rmsg[5])
307                 goto Einval;
308         alarm(0);
309         close(fd);
310         return n / 4 - 6;
311
312 Esend:  printf("rpc: write failed\n");
313         goto out;
314 Erecv:  printf("rpc: read failed\n");
315         goto out;
316 Einval: printf("rpc: invalid response\n");
317         goto out;
318 Esocket:printf("rpc: can't create socket\n");
319         return -1;
320 out:
321         alarm(0);
322         close(fd);
323         return -1;
324 }
325
326 enum {
327         PMAP_GETPORT = 3
328 };
329
330 static void do_header(u_int32_t msg[], u_int32_t prog, u_int32_t vers, u_int32_t proc)
331 {
332         msg[0] = XID++;
333         msg[1] = htonl(RPC_CALL);
334         msg[2] = htonl(RPC_VERSION);
335         msg[3] = htonl(prog);
336         msg[4] = htonl(vers);
337         msg[5] = htonl(proc);
338         msg[6] = htonl(RPC_AUTH_NULL);
339         msg[7] = htonl(0);
340         msg[8] = htonl(RPC_AUTH_NULL);
341         msg[9] = htonl(0);
342 }
343
344 static int getport(u_int32_t prog, u_int32_t vers, u_int32_t prot)
345 {
346         struct sockaddr_in sin;
347         unsigned msg[14];
348         unsigned rmsg[7];
349         int n;
350         set_sockaddr(&sin, servaddr, htons(RPC_PMAP_PORT));
351         do_header(msg, RPC_PMAP_PROGRAM, RPC_PMAP_VERSION, PMAP_GETPORT);
352         msg[10] = htonl(prog);
353         msg[11] = htonl(vers);
354         msg[12] = htonl(prot);
355         msg[13] = htonl(0);
356         n = do_call(&sin, msg, rmsg, 14, 7);
357         if (n <= 0)
358                 return -1;
359         else
360                 return ntohl(rmsg[6]);
361 }
362
363 static int getfh2(void)
364 {
365         struct sockaddr_in sin;
366         unsigned msg[10+1+256/4];
367         unsigned rmsg[6 + 1 + NFS2_FHSIZE/4];
368         int n;
369         int len = strlen(nfs_path);
370         set_sockaddr(&sin, servaddr, mount_port);
371
372         if (len > 255) {
373                 printf("nfsroot: pathname is too long");
374                 return -1;
375         }
376         memset(msg, 0, sizeof(msg));
377         do_header(msg, NFS_MNT_PROGRAM, NFS_MNT_VERSION, MNTPROC_MNT);
378         msg[10] = htonl(len);
379         strcpy((char*)&msg[11], nfs_path);
380         n = do_call(&sin, msg, rmsg, 11 + (len + 3)/4, 7 + NFS2_FHSIZE/4);
381         if (n < 0)
382                 return -1;
383         if (n != NFS2_FHSIZE/4 + 1)
384                 goto Esize;
385         if (rmsg[6]) {
386                 printf("nfsroot: mountd returned an error (%d)",htonl(rmsg[6]));
387                 return -1;
388         }
389         nfs_data.root.size = NFS2_FHSIZE;
390         memcpy(nfs_data.root.data, &rmsg[7], NFS2_FHSIZE);
391         return 0;
392 Esize:
393         printf("nfsroot: bad fhandle size");
394         return -1;
395 }
396
397 static int getfh3(void)
398 {
399         struct sockaddr_in sin;
400         unsigned msg[10+1+256/4];
401         unsigned rmsg[6 + 1 + 1 + NFS3_FHSIZE/4];
402         int n;
403         int len = strlen(nfs_path);
404         int size;
405         set_sockaddr(&sin, servaddr, mount_port);
406
407         if (len > 255) {
408                 printf("nfsroot: pathname is too long");
409                 return -1;
410         }
411         memset(msg, 0, sizeof(msg));
412         do_header(msg, NFS_MNT_PROGRAM, NFS_MNT3_VERSION, MOUNTPROC3_MNT);
413         msg[10] = htonl(len);
414         strcpy((char*)&msg[11], nfs_path);
415         n = do_call(&sin, msg, rmsg, 11 + (len + 3)/4, 8 + NFS3_FHSIZE/4);
416         if (n < 0)
417                 return -1;
418         if (n <= 2)
419                 goto Esize;
420         if (rmsg[6]) {
421                 printf("nfsroot: mountd returned an error (%d)",htonl(rmsg[6]));
422                 return -1;
423         }
424         size = ntohl(rmsg[7]);
425         if (size > NFS3_FHSIZE || n != 2 + size/4)
426                 goto Esize;
427         nfs_data.root.size = size;
428         memcpy(nfs_data.root.data, &rmsg[8], size);
429         return 0;
430 Esize:
431         printf("nfsroot: bad fhandle size");
432         return -1;
433 }
434
435 /*
436  *  Use portmapper to find mountd and nfsd port numbers if not overriden
437  *  by the user. Use defaults if portmapper is not available.
438  *  XXX: Is there any nfs server with no portmapper?
439  */
440 static int root_nfs_ports(void)
441 {
442         int port;
443         int nfsd_ver, mountd_ver;
444         int proto;
445
446         if (nfs_data.flags & NFS_MOUNT_VER3) {
447                 nfsd_ver = NFS3_VERSION;
448                 mountd_ver = NFS_MNT3_VERSION;
449         } else {
450                 nfsd_ver = NFS2_VERSION;
451                 mountd_ver = NFS_MNT_VERSION;
452         }
453
454         proto = (nfs_data.flags & NFS_MOUNT_TCP) ? IPPROTO_TCP : IPPROTO_UDP;
455
456         if (nfs_port < 0) {
457                 if ((port = getport(NFS_PROGRAM, nfsd_ver, proto)) < 0) {
458                         printf("nfsroot: Unable to get nfsd port "
459                                         "number from server, using default\n");
460                         port = NFS_PORT;
461                 }
462                 nfs_port = htons(port);
463                 printf("nfsroot: Portmapper on server returned %d "
464                         "as nfsd port\n", port);
465         }
466
467         if ((port = getport(NFS_MNT_PROGRAM, mountd_ver, proto)) < 0) {
468                 printf("nfsroot: Unable to get mountd port "
469                                 "number from server, using default\n");
470                 port = NFS_MNT_PORT;
471         }
472         mount_port = htons(port);
473         printf("nfsroot: mountd port is %d\n", port);
474
475         return 0;
476 }
477
478 int main(void)
479 {
480         unsigned char *p;
481         struct timeval tv;
482         char *s;
483
484         /* FIX: use getopt() instead of this */
485
486         s = getenv("root_server_addr");
487         if (s)
488                 root_server_addr = strtoul(s, NULL, 10);
489         s = getenv("root_server_path");
490         if (s)
491                 strncpy(root_server_path, s, 255);
492         s = getenv("nfs_root_name");
493         if (s)
494                 strncpy(nfs_root_name, s, 255);
495
496         /*
497          * Decode the root directory path name and NFS options from
498          * the kernel command line. This has to go here in order to
499          * be able to use the client IP address for the remote root
500          * directory (necessary for pure RARP booting).
501          */
502         if (root_nfs_name(nfs_root_name) < 0)
503                 return 0;
504         if ((servaddr = root_server_addr) == INADDR_NONE) {
505                 printf("nfsroot: No NFS server available, giving up.\n");
506                 return 0;
507         }
508
509         p = (char *) &servaddr;
510         sprintf(nfs_data.hostname, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
511
512 #ifdef NFSROOT_DEBUG
513         printf("nfsroot: Mounting %s on server %s as root\n",
514                 nfs_path, nfs_data.hostname);
515         printf("nfsroot:     rsize = %d, wsize = %d, timeo = %d, retrans = %d\n",
516                 nfs_data.rsize, nfs_data.wsize, nfs_data.timeo, nfs_data.retrans);
517         printf("nfsroot:     acreg (min,max) = (%d,%d), acdir (min,max) = (%d,%d)\n",
518                 nfs_data.acregmin, nfs_data.acregmax,
519                 nfs_data.acdirmin, nfs_data.acdirmax);
520         printf("nfsroot:     nfsd port = %d, mountd port = %d, flags = %08x\n",
521                 nfs_port, mount_port, nfs_data.flags);
522 #endif
523
524         gettimeofday(&tv, NULL);
525         XID = (tv.tv_sec << 15) ^ tv.tv_usec;
526
527         if (root_nfs_ports() < 0)
528                 return 0;
529         if (nfs_data.flags & NFS_MOUNT_VER3) {
530                 if (getfh3())
531                         return 0;
532         } else {
533                 if (getfh2())
534                         return 0;
535         }
536         set_sockaddr((struct sockaddr_in *) &nfs_data.addr, servaddr, nfs_port);
537         return mount("/dev/root", "/mnt", "nfs", 0, &nfs_data) == 0;
538 }