]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/boot/common/dev_net.c
Merge OpenSSL 1.0.2l.
[FreeBSD/FreeBSD.git] / sys / boot / common / dev_net.c
1 /*      $NetBSD: dev_net.c,v 1.23 2008/04/28 20:24:06 martin Exp $      */
2
3 /*-
4  * Copyright (c) 1997 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Gordon W. Ross.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34
35 /*-
36  * This module implements a "raw device" interface suitable for
37  * use by the stand-alone I/O library NFS code.  This interface
38  * does not support any "block" access, and exists only for the
39  * purpose of initializing the network interface, getting boot
40  * parameters, and performing the NFS mount.
41  *
42  * At open time, this does:
43  *
44  * find interface      - netif_open()
45  * RARP for IP address - rarp_getipaddress()
46  * RPC/bootparams      - callrpc(d, RPC_BOOTPARAMS, ...)
47  * RPC/mountd          - nfs_mount(sock, ip, path)
48  *
49  * the root file handle from mountd is saved in a global
50  * for use by the NFS open code (NFS/lookup).
51  */
52
53 #include <machine/stdarg.h>
54 #include <sys/param.h>
55 #include <sys/socket.h>
56 #include <net/if.h>
57 #include <netinet/in.h>
58 #include <netinet/in_systm.h>
59
60 #include <stand.h>
61 #include <stddef.h>
62 #include <string.h>
63 #include <net.h>
64 #include <netif.h>
65 #include <bootp.h>
66 #include <bootparam.h>
67
68 #include "dev_net.h"
69 #include "bootstrap.h"
70
71 #ifdef  NETIF_DEBUG
72 int debug = 0;
73 #endif
74
75 static char *netdev_name;
76 static int netdev_sock = -1;
77 static int netdev_opens;
78
79 static int      net_init(void);
80 static int      net_open(struct open_file *, ...);
81 static int      net_close(struct open_file *);
82 static void     net_cleanup(void);
83 static int      net_strategy(void *, int, daddr_t, size_t, char *, size_t *);
84 static int      net_print(int);
85
86 static int net_getparams(int sock);
87
88 struct devsw netdev = {
89         "net",
90         DEVT_NET,
91         net_init,
92         net_strategy,
93         net_open,
94         net_close,
95         noioctl,
96         net_print,
97         net_cleanup
98 };
99
100 static int
101 net_init(void)
102 {
103
104         return (0);
105 }
106
107 /*
108  * Called by devopen after it sets f->f_dev to our devsw entry.
109  * This opens the low-level device and sets f->f_devdata.
110  * This is declared with variable arguments...
111  */
112 static int
113 net_open(struct open_file *f, ...)
114 {
115         struct iodesc *d;
116         va_list args;
117         char *devname;          /* Device part of file name (or NULL). */
118         int error = 0;
119
120         va_start(args, f);
121         devname = va_arg(args, char*);
122         va_end(args);
123
124         /* Before opening another interface, close the previous one first. */
125         if (netdev_sock >= 0 && strcmp(devname, netdev_name) != 0)
126                 net_cleanup();
127
128         /* On first open, do netif open, mount, etc. */
129         if (netdev_opens == 0) {
130                 /* Find network interface. */
131                 if (netdev_sock < 0) {
132                         netdev_sock = netif_open(devname);
133                         if (netdev_sock < 0) {
134                                 printf("net_open: netif_open() failed\n");
135                                 return (ENXIO);
136                         }
137                         netdev_name = strdup(devname);
138 #ifdef  NETIF_DEBUG
139                         if (debug)
140                                 printf("net_open: netif_open() succeeded\n");
141 #endif
142                 }
143                 /*
144                  * If network params were not set by netif_open(), try to get
145                  * them via bootp, rarp, etc.
146                  */
147                 if (rootip.s_addr == 0) {
148                         /* Get root IP address, and path, etc. */
149                         error = net_getparams(netdev_sock);
150                         if (error) {
151                                 /* getparams makes its own noise */
152                                 free(netdev_name);
153                                 netif_close(netdev_sock);
154                                 netdev_sock = -1;
155                                 return (error);
156                         }
157                 }
158                 /*
159                  * Set the variables required by the kernel's nfs_diskless
160                  * mechanism.  This is the minimum set of variables required to
161                  * mount a root filesystem without needing to obtain additional
162                  * info from bootp or other sources.
163                  */
164                 d = socktodesc(netdev_sock);
165                 setenv("boot.netif.hwaddr", ether_sprintf(d->myea), 1);
166                 setenv("boot.netif.ip", inet_ntoa(myip), 1);
167                 setenv("boot.netif.netmask", intoa(netmask), 1);
168                 setenv("boot.netif.gateway", inet_ntoa(gateip), 1);
169                 setenv("boot.netif.server", inet_ntoa(rootip), 1);
170                 if (netproto == NET_TFTP) {
171                         setenv("boot.tftproot.server", inet_ntoa(rootip), 1);
172                         setenv("boot.tftproot.path", rootpath, 1);
173                 } else if (netproto == NET_NFS) {
174                         setenv("boot.nfsroot.server", inet_ntoa(rootip), 1);
175                         setenv("boot.nfsroot.path", rootpath, 1);
176                 }
177                 if (intf_mtu != 0) {
178                         char mtu[16];
179                         snprintf(mtu, sizeof(mtu), "%u", intf_mtu);
180                         setenv("boot.netif.mtu", mtu, 1);
181                 }
182
183         }
184         netdev_opens++;
185         f->f_devdata = &netdev_sock;
186         return (error);
187 }
188
189 static int
190 net_close(struct open_file *f)
191 {
192
193 #ifdef  NETIF_DEBUG
194         if (debug)
195                 printf("net_close: opens=%d\n", netdev_opens);
196 #endif
197
198         f->f_devdata = NULL;
199
200         return (0);
201 }
202
203 static void
204 net_cleanup(void)
205 {
206
207         if (netdev_sock >= 0) {
208 #ifdef  NETIF_DEBUG
209                 if (debug)
210                         printf("net_cleanup: calling netif_close()\n");
211 #endif
212                 rootip.s_addr = 0;
213                 free(netdev_name);
214                 netif_close(netdev_sock);
215                 netdev_sock = -1;
216         }
217 }
218
219 static int
220 net_strategy(void *devdata, int rw, daddr_t blk, size_t size, char *buf,
221     size_t *rsize)
222 {
223
224         return (EIO);
225 }
226
227 #define SUPPORT_BOOTP
228
229 /*
230  * Get info for NFS boot: our IP address, our hostname,
231  * server IP address, and our root path on the server.
232  * There are two ways to do this:  The old, Sun way,
233  * and the more modern, BOOTP way. (RFC951, RFC1048)
234  *
235  * The default is to use the Sun bootparams RPC
236  * (because that is what the kernel will do).
237  * MD code can make try_bootp initialied data,
238  * which will override this common definition.
239  */
240 #ifdef  SUPPORT_BOOTP
241 int try_bootp = 1;
242 #endif
243
244 extern n_long ip_convertaddr(char *p);
245
246 static int
247 net_getparams(int sock)
248 {
249         char buf[MAXHOSTNAMELEN];
250         n_long rootaddr, smask;
251         struct iodesc *d = socktodesc(sock);
252         extern struct in_addr servip;
253
254 #ifdef  SUPPORT_BOOTP
255         /*
256          * Try to get boot info using BOOTP.  If we succeed, then
257          * the server IP address, gateway, and root path will all
258          * be initialized.  If any remain uninitialized, we will
259          * use RARP and RPC/bootparam (the Sun way) to get them.
260          */
261         if (try_bootp) {
262                 int rc = -1;
263                 if (bootp_response != NULL) {
264                         rc = dhcp_try_rfc1048(bootp_response->bp_vend,
265                             bootp_response_size -
266                             offsetof(struct bootp, bp_vend));
267
268                         if (servip.s_addr == 0)
269                                 servip = bootp_response->bp_siaddr;
270                         if (rootip.s_addr == 0)
271                                 rootip = bootp_response->bp_siaddr;
272                         if (gateip.s_addr == 0)
273                                 gateip = bootp_response->bp_giaddr;
274                         if (myip.s_addr == 0)
275                                 myip = bootp_response->bp_yiaddr;
276                         d->myip = myip;
277                 }
278                 if (rc < 0)
279                         bootp(sock, BOOTP_NONE);
280         }
281         if (myip.s_addr != 0)
282                 goto exit;
283 #ifdef  NETIF_DEBUG
284         if (debug)
285                 printf("net_open: BOOTP failed, trying RARP/RPC...\n");
286 #endif
287 #endif
288
289         /*
290          * Use RARP to get our IP address.  This also sets our
291          * netmask to the "natural" default for our address.
292          */
293         if (rarp_getipaddress(sock)) {
294                 printf("net_open: RARP failed\n");
295                 return (EIO);
296         }
297         printf("net_open: client addr: %s\n", inet_ntoa(myip));
298
299         /* Get our hostname, server IP address, gateway. */
300         if (bp_whoami(sock)) {
301                 printf("net_open: bootparam/whoami RPC failed\n");
302                 return (EIO);
303         }
304 #ifdef  NETIF_DEBUG
305         if (debug)
306                 printf("net_open: client name: %s\n", hostname);
307 #endif
308
309         /*
310          * Ignore the gateway from whoami (unreliable).
311          * Use the "gateway" parameter instead.
312          */
313         smask = 0;
314         gateip.s_addr = 0;
315         if (bp_getfile(sock, "gateway", &gateip, buf) == 0) {
316                 /* Got it!  Parse the netmask. */
317                 smask = ip_convertaddr(buf);
318         }
319         if (smask) {
320                 netmask = smask;
321 #ifdef  NETIF_DEBUG
322                 if (debug)
323                         printf("net_open: subnet mask: %s\n", intoa(netmask));
324 #endif
325         }
326 #ifdef  NETIF_DEBUG
327         if (gateip.s_addr && debug)
328                 printf("net_open: net gateway: %s\n", inet_ntoa(gateip));
329 #endif
330
331         /* Get the root server and pathname. */
332         if (bp_getfile(sock, "root", &rootip, rootpath)) {
333                 printf("net_open: bootparam/getfile RPC failed\n");
334                 return (EIO);
335         }
336 exit:
337         netproto = NET_TFTP;
338         if ((rootaddr = net_parse_rootpath()) != INADDR_NONE) {
339                 netproto = NET_NFS;
340                 rootip.s_addr = rootaddr;
341         }
342
343 #ifdef  NETIF_DEBUG
344         if (debug) {
345                 printf("net_open: server addr: %s\n", inet_ntoa(rootip));
346                 printf("net_open: server path: %s\n", rootpath);
347         }
348 #endif
349
350         return (0);
351 }
352
353 static int
354 net_print(int verbose)
355 {
356         struct netif_driver *drv;
357         int i, d, cnt;
358         int ret = 0;
359
360         if (netif_drivers[0] == NULL)
361                 return (ret);
362
363         printf("%s devices:", netdev.dv_name);
364         if ((ret = pager_output("\n")) != 0)
365                 return (ret);
366
367         cnt = 0;
368         for (d = 0; netif_drivers[d]; d++) {
369                 drv = netif_drivers[d];
370                 for (i = 0; i < drv->netif_nifs; i++) {
371                         printf("\t%s%d:", netdev.dv_name, cnt++);
372                         if (verbose) {
373                                 printf(" (%s%d)", drv->netif_bname,
374                                     drv->netif_ifs[i].dif_unit);
375                         }
376                         if ((ret = pager_output("\n")) != 0)
377                                 return (ret);
378                 }
379         }
380         return (ret);
381 }
382
383 /*
384  * Strip the server's address off of the rootpath if present and return it in
385  * network byte order, leaving just the pathname part in the global rootpath.
386  */
387 uint32_t
388 net_parse_rootpath()
389 {
390         n_long addr = INADDR_NONE;
391         char *ptr;
392
393         ptr = rootpath;
394         (void)strsep(&ptr, ":");
395         if (ptr != NULL) {
396                 addr = inet_addr(rootpath);
397                 bcopy(ptr, rootpath, strlen(ptr) + 1);
398         }
399
400         return (addr);
401 }