nfs_dlinet.c revision 843e19887f64dde75055cf8842fc4db2171eff45
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/systeminfo.h>
#include <sys/netconfig.h>
#include <sys/ethernet.h>
#include <sys/sysmacros.h>
#include <sys/bootconf.h>
#include <sys/bootprops.h>
#include <netinet/inetutil.h>
#include <dhcp_impl.h>
#include <sys/sunos_dhcp_class.h>
#include <rpc/pmap_clnt.h>
#include <rpc/pmap_rmt.h>
#include <rpc/pmap_prot.h>
#include <rpc/bootparam.h>
#include <rpc/rpcb_prot.h>
#include <nfs/nfs_clnt.h>
/*
* RPC timers and retries
*/
#define PMAP_RETRIES 5
#define DEFAULT_RETRIES 3
#define GETFILE_RETRIES 2
#define DEFAULT_TIMEO 3
#define WHOAMI_TIMEO 20
#define REVARP_TIMEO 5
#define GETFILE_TIMEO 1
/*
* These are from the rpcgen'd version of mount.h XXX
*/
#define MOUNTPROG 100005
#define MOUNTPROC_MNT 1
#define MOUNTVERS 1
#define MOUNTVERS_POSIX 2
#define MOUNTVERS3 3
struct fhstatus {
int fhs_status;
};
#define FHSIZE3 64
struct fhandle3 {
char *fhandle3_val;
};
enum mountstat3 {
MNT_OK = 0,
MNT3ERR_PERM = 1,
MNT3ERR_NOENT = 2,
MNT3ERR_IO = 5,
MNT3ERR_ACCES = 13,
MNT3ERR_NOTDIR = 20,
MNT3ERR_INVAL = 22,
MNT3ERR_NAMETOOLONG = 63,
MNT3ERR_NOTSUPP = 10004,
MNT3ERR_SERVERFAULT = 10006
};
struct mountres3_ok {
struct {
int *auth_flavors_val;
} auth_flavors;
};
struct mountres3 {
enum mountstat3 fhs_status;
union {
struct mountres3_ok mountinfo;
} mountres3_u;
};
/*
* DLPI address format.
*/
struct dladdr {
};
&mod_miscops, "Boot diskless"
};
static struct modlinkage modlinkage = {
};
static int dldebug;
int
_init(void)
{
return (mod_install(&modlinkage));
}
int
_fini(void)
{
return (mod_remove(&modlinkage));
}
int
{
}
struct netbuf *);
struct mountres3_ok *objp);
char *, xdrproc_t, char *, int, int);
fhandle_t *, int *);
nfs_fh3 *, int *);
static int init_mountopts(struct nfs_args *, int,
struct knetconfig **, int *);
static int revarp_myaddr(TIUSER *);
static void init_netbuf(struct netbuf *);
static void free_netbuf(struct netbuf *);
extern int dl_attach(ldi_handle_t, int);
uint32_t);
static void init_config(void);
static void cacheinit(void);
static int isdigit(int);
/*
* Should be in some common
* ethernet source file.
*/
static struct ether_addr etherbroadcastaddr = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
static struct ether_addr myether;
/*
* arguments.
* "ndev" is the major device number of the network interface
* used to boot from.
* "ifunit" it the physical point of attachment for the network
* interface used to boot from.
*
* Both of these are initialized in "init_config()".
*/
static char ndev_path[MAXPATHLEN];
static int ifunit;
/*
* XXX these should be shared
*/
static struct knetconfig dl_udp_netconf = {
NC_TPI_CLTS, /* semantics */
NC_INET, /* family */
NC_UDP, /* protocol */
0, /* device */
};
static struct knetconfig dl_tcp_netconf = {
NC_TPI_COTS, /* semantics */
NC_INET, /* family */
NC_TCP, /* protocol */
0, /* device */
};
/* parameters from DHCP or bootparamd */
static char *server_name_c, *server_path_c;
static char rootopts[256];
/*
* XXX Until we get the nfsmapid deadlocks all fixed, don't allow
* XXX a v4 root mount.
*/
int nfs4_no_diskless_root_support = 1;
int
int *vfsflags)
{
int rc;
int proto;
struct knetconfig *dl_cf;
static int init_done = 0;
if (dldebug)
if (init_done == 0) {
init_config();
init_done = 1;
}
do {
if (rc) {
return (rc);
}
switch (version) {
case NFS_VERSION:
break;
case NFS_V3:
break;
case NFS_V4:
&stat)) {
proto = IPPROTO_TCP;
rc = 0;
} else {
switch (stat) {
case RPC_PROGVERSMISMATCH:
case RPC_XPRTFAILED:
/*
* Common failures if v4 unsupported or no TCP
*/
break;
default:
}
}
break;
default:
break;
}
if (rc)
goto errout;
switch (proto) {
case IPPROTO_TCP:
dl_cf = &dl_tcp_netconf;
break;
case IPPROTO_UDP:
default:
dl_cf = &dl_udp_netconf;
break;
}
/*
* Copy knetconfig information from the template, note that the
* rdev field has been set by init_config above.
*/
if (dldebug) {
if (rc)
else
printf("mount_root: leaving\n");
}
return (rc);
}
/*
* Call mount daemon on server `sa' to mount path.
* `port' is set to nfs port and fh is the fhandle
* returned from the server.
*/
static int
{
if (dldebug)
printf("mountnfs: entered\n");
/*
* Get the port number for the mount program.
* pmap_kgetport first tries a SunOS portmapper
* and, if no reply is received, will try a
* SVR4 rpcbind. Either way, `sa' is set to
* the correct address.
*/
do {
if (stat == RPC_TIMEDOUT) {
"mountnfs: %s:%s portmap not responding",
} else if (stat != RPC_SUCCESS) {
"mountnfs: pmap_kgetport RPC error %d (%s).",
return (ENXIO); /* XXX */
}
} while (stat == RPC_TIMEDOUT);
/*
* The correct port number has been
* put into `sa' by pmap_kgetport().
*/
do {
xdr_bp_path_t, (char *)&path,
myxdr_fhstatus, (char *)&fhs,
if (stat == RPC_TIMEDOUT) {
"mountnfs: %s:%s mount server not responding",
}
} while (stat == RPC_TIMEDOUT);
if (stat != RPC_SUCCESS) {
return (ENXIO); /* XXX */
}
if (fhs.fhs_status != 0) {
if (dldebug)
return (ENXIO); /* XXX */
}
*proto = IPPROTO_UDP;
*proto = IPPROTO_TCP;
if (dldebug)
printf("mountnfs: leaving\n");
return (0);
}
/*
* Call mount daemon on server `sa' to mount path.
* `port' is set to nfs port and fh is the fhandle
* returned from the server.
*/
static int
{
int ret = 0;
if (dldebug)
printf("mountnfs3: entered\n");
/*
* Get the port number for the mount program.
* pmap_kgetport first tries a SunOS portmapper
* and, if no reply is received, will try a
* SVR4 rpcbind. Either way, `sa' is set to
* the correct address.
*/
do {
if (stat == RPC_PROGVERSMISMATCH) {
if (dldebug)
return (EPROTONOSUPPORT); /* XXX */
} else if (stat == RPC_TIMEDOUT) {
"mountnfs3: %s:%s portmap not responding",
} else if (stat != RPC_SUCCESS) {
"mountnfs3: pmap_kgetport RPC error %d (%s).",
return (ENXIO); /* XXX */
}
} while (stat == RPC_TIMEDOUT);
/*
* The correct port number has been
* put into `sa' by pmap_kgetport().
*/
do {
xdr_bp_path_t, (char *)&path,
myxdr_mountres3, (char *)&mountres3,
if (stat == RPC_TIMEDOUT) {
"mountnfs3: %s:%s mount server not responding",
}
} while (stat == RPC_TIMEDOUT);
if (stat == RPC_PROGVERSMISMATCH) {
if (dldebug)
goto out;
}
if (stat != RPC_SUCCESS) {
goto out;
}
if (dldebug)
printf("mountnfs3: fhs_status %d\n",
goto out;
}
*proto = IPPROTO_UDP;
*proto = IPPROTO_TCP;
}
out:
if (dldebug)
printf("mountnfs3: leaving\n");
return (ret);
}
static int
{
struct knetconfig *knconf;
int retries = DEFAULT_RETRIES;
switch (proto) {
case IPPROTO_TCP:
knconf = &dl_tcp_netconf;
break;
case IPPROTO_UDP:
knconf = &dl_udp_netconf;
break;
default:
return (0);
}
do {
if (dldebug)
clnt_sperrno(stat));
/*
* Special case for TCP, it may "timeout" because it failed
* to establish an initial connection but it doesn't
* actually retry, so we do the retry.
* Persistence pays in diskless.
*/
if (stat != RPC_SUCCESS)
return (0);
return (1);
}
static struct netbuf bootparam_addr;
/*
* Returns after filling in the following global variables:
* bootparam_addr,
* utsname.nodename,
* srpc_domain.
*/
static int
whoami(void)
{
struct bp_whoami_arg arg;
struct bp_whoami_res res;
int rc;
int printed_waiting_msg;
}
/*
* Find out our local (IP) address.
*/
return (rc);
}
/* explicitly use the limited broadcast address */
init_netbuf(&sa);
/*
* Pick up our local (IP) address.
*/
init_netbuf(&req);
"whoami: couldn't get my IP address: %m.\n");
free_netbuf(&sa);
free_netbuf(&req);
return (rc);
}
/*
* Set up the arguments expected by bootparamd.
*/
free_netbuf(&req);
/*
* Initial retransmission interval
*/
/*
* Do a broadcast call to find a bootparam daemon that
* will tell us our hostname, domainname and any
* router that we have to use to talk to our NFS server.
*/
printed_waiting_msg = 0;
do {
/*
* pmap_rmt_call will first try the SunOS portmapper
* and if no reply is received will then try the SVR4
* rpcbind.
* Either way, `bootparam_addr' will be set to the
* correct address for the bootparamd that responds.
*/
tv, &bootparam_addr);
"No bootparam server responding; still trying");
printed_waiting_msg = 1;
}
/*
* Retransmission interval for second and subsequent tries.
* We expect first pmap_rmt_call to retransmit and backoff to
* at least this value.
*/
} while (stat == RPC_TIMEDOUT);
if (printed_waiting_msg)
printf("Bootparam response received\n");
if (stat != RPC_SUCCESS) {
/* XXX should get real error here */
"whoami: bootparam RPC failed: error %d (%s).",
goto done;
}
printf("whoami: hostname too long");
rc = ENAMETOOLONG;
goto done;
}
if (namelen != 0) {
} else {
printf("whoami: no host name\n");
goto done;
}
if (namelen != 0) {
printf("whoami: domainname too long");
rc = ENAMETOOLONG;
goto done;
}
} else {
printf("whoami: no domain name\n");
}
struct sockaddr_in *sin;
sizeof (struct in_addr));
"whoami: couldn't add route: %m.\n");
goto done;
}
}
} else {
printf("whoami: unknown gateway addr family %d\n",
}
done:
free_netbuf(&sa);
return (rc);
}
/*
* Returns:
* 1) The ascii form of our root servers name in `server_name'.
* 2) Actual network address of our root server in `server_address'.
* 3) Whatever BOOTPARAMPROC_GETFILE returns for the fileid key, in
* `server_path'. If fileid is "root", it is the pathname of our
* root on the server.
*/
static int
{
struct bp_getfile_arg arg;
struct bp_getfile_res res;
static int using_cache = FALSE;
int timeo = DEFAULT_TIMEO;
int retries = DEFAULT_RETRIES;
if (dldebug)
printf("getfile: entered\n");
/*
* Call cacheinfo() to see whether we can satisfy this request by using
* the information cached in memory by the boot program's DHCP
* implementation or boot properties rather than consult BOOTPARAMS,
* but while preserving the semantics of getfile(). We know that
* the server name is SYS_NMLN in length, and server_path is
* MAXPATHLEN (pn_alloc).
*/
server_path, MAXPATHLEN) == 0) {
using_cache = TRUE;
return (0);
}
}
/*
* If using cache, rootopts is already available.
*/
}
if (bootparam_addr.len == 0) {
return (ENXIO);
}
/*
* If we are not looking up the root file, we are looking
* up a non-critical option that should timeout quickly.
*/
if (!root) {
}
/*
* bootparam_addr was filled in by the call to
* whoami(), so now send an rpc message to the
* bootparam daemon requesting our server information.
* Use UDP to talk to bootparms.
*/
if (stat == RPC_SUCCESS) {
}
if (stat != RPC_SUCCESS) {
if (root)
}
if (*server_path == '\0')
return (EINVAL);
/*
* If the fileid is "root", we must get back a server name, for
* other parameters a server name is not required
*/
if (!root) {
if (dldebug)
printf("getfile: leaving: non-root\n");
return (0);
}
if (*server_name == '\0')
return (EINVAL);
case IP_ADDR_TYPE:
/*
* server_address is where we will get our root
* from.
*/
return (EINVAL);
break;
default:
printf("getfile: unknown address type %d\n",
return (EPROTONOSUPPORT);
}
if (dldebug)
printf("getfile: leaving\n");
return (0);
}
/*
* If the boot property "bootp-response" exists, then inetboot performed a
* successful DHCP lease acquisition for us and left the resultant ACK packet
* encoded at that location.
*
* If no such property exists (or the information is incomplete or garbled),
* the function returns -1.
*/
int
dhcpinit(void)
{
int rc, i;
char *p;
struct sockaddr_in *sin;
int true_dhcacklen;
char *ackp;
static int once_only = 0;
if (once_only == 1) {
return (0);
}
once_only = 1;
return (-1);
}
/*
* Since we expect the "bootp-response" property to have
* been encoded via octet_to_hexascii(), its length should
* always be even.
*/
if (dldebug) {
}
/*
* Store our interface name in the reserved block at the
* head of our packet. For x86, ifname is not initialized
* in the netinstall case and dhcack interface name is
* set in strplumb(). So we only copy the name if ifname
* is set properly.
*/
if (ifname[0])
/* skip over the interface name section */
"dhcp: boot dhcp cache is corrupted.");
return (-1);
}
/* remember the server_ip in dhcack */
sizeof (DHCP_OPT *));
/* garbled packet */
return (-1);
}
/* set node name */
if (i >= SYS_NMLN) {
} else {
if (dldebug) {
printf("hostname is %s\n",
}
}
}
/* Set NIS domain name. */
p = NULL;
}
if (p != NULL) {
if (i > SYS_NMLN) {
"dhcp: NIS domainname too long.");
} else {
bcopy(p, srpc_domain, i);
srpc_domain[i] = '\0';
if (dldebug)
printf("dhcp: NIS domain name is %s\n",
}
}
/* fetch netmask */
} else {
if (dldebug)
printf("dhcp: setting netmask to: %s\n",
}
} else {
else
}
/* and broadcast address */
if (dldebug)
printf("dhcp: broadcast address len %d\n",
} else {
if (dldebug)
printf("dhcp: setting broadcast addr to: %s\n",
}
} else {
if (dldebug)
printf("dhcp: no broadcast address supplied\n");
}
/* and plumb and initialize interface */
&braddr, IFF_DHCPRUNNING)) {
return (-1);
}
/* add routes */
} else {
int nrouters;
i++) {
sizeof (struct in_addr));
continue;
sin = (struct
sin = (struct
&rtentry)) {
"dhcp: couldn't add route "
"to %s: %m.\n",
continue;
}
if (dldebug) {
printf("dhcp: added route %s\n",
}
}
}
}
}
if (dldebug)
printf("dhcpinit: leaving\n");
return (0);
}
/*
* Initialize nfs mount info from properties and dhcp response.
*/
static void
cacheinit(void)
{
char *str;
}
str);
if (dldebug)
printf("server ip is %s\n",
}
return;
/* extract root path in server_path */
if (server_path_c == NULL) {
if (dldebug)
} else {
}
}
/* set server_name */
if (server_name_c == NULL) {
if (dldebug)
printf("dhcp: root server name %s\n",
} else {
}
}
/* set root server_address */
if (doptp) {
if (dldebug) {
printf("dhcp: root server IP address %s\n",
}
} else {
if (dldebug)
"dhcp: file server ip address missing,"
" fallback to dhcp server as file server");
}
}
/* set root file system mount options */
if (rootopts[0] == 0) {
if (dldebug)
} else if (dldebug) {
printf("dhcp: no rootopts or too long\n");
/* not an error */
}
}
/* now we are done with pl, just free it */
}
static int
{
static int init_done = 0;
struct sockaddr_in *sin;
if (init_done == 0) {
cacheinit();
init_done = 1;
}
/* server_path is a reliable indicator of cache availability */
if (server_path_c == NULL)
return (-1);
if (server_name_c) {
} else {
}
return (0);
}
/*
* Set this interface's IP address and netmask, and bring it up.
*/
static int
{
int rc;
struct sockaddr_in sin;
if (dldebug) {
printf("dlifconfig: entered\n");
}
"dlifconfig: couldn't set interface net address: %m\n");
return (rc);
}
"dlifconfig: couldn't set interface broadcast addr: %m\n");
return (rc);
}
}
"dlifconfig: couldn't set interface net address: %m\n");
return (rc);
}
/*
* Now turn on the interface.
*/
"dlifconfig: couldn't enable network interface: %m\n");
return (rc);
}
if (dldebug)
printf("dlifconfig: returned\n");
return (0);
}
static char *
{
static char b[18];
unsigned char *p;
p = (unsigned char *)∈
return (b);
}
/* We only deal with a.b.c.d decimal format. ip points to 4 byte storage */
static int
{
int i = 0;
char c = *ipstr;
for (;;) {
if (!isdigit(c))
return (-1);
for (;;) {
if (!isdigit(c))
break;
c = *++ipstr;
}
i++;
if (i == 4)
break;
if (c != '.')
return (-1);
c = *++ipstr;
}
if (c != 0)
return (-1);
return (0);
}
#define MAX_ADDR_SIZE 128
/*
* Initialize a netbuf suitable for
* describing an address for the
* transport defined by `tiptr'.
*/
static void
{
}
static void
{
}
static int
{
int rc;
if (rc)
return (rc);
}
/*
* Send an ioctl down the stream defined
* by `tiptr'.
*
* We isolate the ifreq dependencies in here. The
* ioctl really ought to take a netbuf and be of
* type TRANSPARENT - one day.
*/
static int
{
int rc;
/*
* Now do the one requested.
*/
if (rc) {
return (rc);
}
/*
* Set reply length.
*/
/*
* GET type.
*/
}
return (0);
}
static int
{
int rc;
return (rc);
}
/*
* REVerse Address Resolution Protocol (revarp)
* is used by a diskless client to find out its
* IP address when all it knows is its Ethernet address.
*
* Open the ethernet driver, attach and bind
* (DL_BIND_REQ) it, and then format a broadcast RARP
* message for it to send. We pick up the reply and
* let the caller set the interface address using SIOCSIFADDR.
*/
static int
{
int rc;
struct sockaddr_in sin;
if (dldebug)
printf("revarp_myaddr: entered\n");
"revarp_myaddr: ldi_ident_from_mod failed: %m\n");
return (rc);
}
if (rc) {
"revarp_myaddr: ldi_open_by_name failed: %m\n");
return (rc);
}
return (rc);
}
return (rc);
}
return (rc);
}
/* Initialize myaddr */
"revarp_myaddr: couldn't set interface net address: %m\n");
return (rc);
}
/* Now turn on the interface */
"revarp_myaddr: couldn't enable network interface: %m\n");
}
return (rc);
}
static void
{
int rc;
static int done = 0;
/* Fallback using per-node address */
"system wide Ethernet address %s\n",
ether_sprintf(&myether));
}
return;
}
if (done++ == 0)
ether_sprintf(&myether));
/*
* Send another RARP request.
*/
return;
}
return;
}
/*
* Format the transmit request part.
*/
sizeof (etherbroadcastaddr));
/*
* Format the actual REVARP request.
*/
return;
}
goto getreply;
}
/*
* Client side Reverse-ARP input
* Server side is handled by user level server
*/
static void
{
int rc;
/*
* Choose the time at which we will give up, and resend our
* request.
*/
wait:
/*
* Compute new timeout value.
*/
gethrestime(&now);
/*
* If we don't have at least one full second remaining, give up.
* This means we might wait only just over 4.0 seconds, but that's
* okay.
*/
return;
goto out;
} else if (rc != 0) {
return;
}
printf("revarpinput: b_cont == NULL\n");
goto out;
}
printf("revarpinput: bad header type %d\n",
goto out;
}
printf("revarpinput: bad data len %d, expect %d\n",
goto out;
}
/* We could have received another broadcast arp packet. */
if (dldebug)
printf("revarpinput: bad type %x\n",
goto wait;
}
/* We could have received a broadcast arp request. */
if (dldebug)
printf("revarpinput: bad op %x\n",
goto wait;
}
} else {
/* We could have gotten a broadcast arp response. */
if (dldebug)
printf("revarpinput: got reply, but not my address\n");
goto wait;
}
out:
}
/*
* From rpcsvc/mountxdr.c in SunOS. We can't
* put this into the rpc directory because
* it calls xdr_fhandle() which is in a
* loadable module.
*/
static bool_t
{
return (FALSE);
if (fhsp->fhs_status == 0) {
return (FALSE);
}
return (TRUE);
}
/*
* From nfs_xdr.c.
*
* File access handle
* The fhandle struct is treated a opaque data on the wire
*/
static bool_t
{
}
static bool_t
{
return (FALSE);
switch (objp->fhs_status) {
case MNT_OK:
return (FALSE);
break;
default:
break;
}
return (TRUE);
}
static bool_t
{
}
static bool_t
{
return (FALSE);
return (FALSE);
return (TRUE);
}
static bool_t
{
}
/*
* From SunOS pmap_clnt.c
*
* Port mapper routines:
* pmap_kgetport() - get port number.
* pmap_rmt_call() - indirect call via port mapper.
*
*/
static enum clnt_stat
{
int tries;
struct pmap pmap_parms;
port = 0;
pmap_parms.pm_port = 0;
myxdr_pmap, (char *)&pmap_parms,
xdr_u_short, (char *)&port,
if (stat != RPC_TIMEDOUT)
break;
"pmap_kgetport: Portmapper not responding; still trying");
}
if (stat == RPC_PROGUNAVAIL) {
"pmap_kgetport: Portmapper failed - trying rpcbind");
xdr_rpcb, (char *)&rpcb_parms,
xdr_wrapstring, (char *)&ua,
if (stat != RPC_TIMEDOUT)
break;
"pmap_kgetport: rpcbind not responding; still trying");
}
if (stat == RPC_SUCCESS) {
} else {
/* Address unknown */
}
}
}
if (stat == RPC_SUCCESS)
return (stat);
}
/*
* pmapper remote-call-service interface.
* This routine is used to call the pmapper remote call service
* which will look up a service program in the port maps, and then
* remotely call that routine with the given parameters. This allows
* programs to do a lookup and call in one step. In addition to the call_addr,
* the caller provides a boolean hint about the destination address (TRUE if
* address is a broadcast address, FALSE otherwise).
*
* On return, `call addr' contains the port number for the
* service requested, and `resp_addr' contains its IP address.
*/
static enum clnt_stat
{
int rc;
struct rmtcallargs pmap_args;
struct rmtcallres pmap_res;
struct rpcb_rmtcallargs rpcb_args;
struct rpcb_rmtcallres rpcb_res;
if (rc != 0) {
"pmap_rmt_call: clnt_tli_kcreate failed: %m\n");
return (RPC_SYSTEMERROR); /* XXX */
}
panic("pmap_rmt_call: clnt_tli_kcreate failed");
/* NOTREACHED */
}
if (stat == RPC_SUCCESS) {
}
if (stat != RPC_PROGUNAVAIL)
return (stat);
if (rc != 0) {
return (RPC_SYSTEMERROR); /* XXX */
}
panic("pmap_rmt_call: clnt_tli_kcreate failed");
/* NOTREACHED */
}
if (stat == RPC_SUCCESS)
return (stat);
}
/*
* XDR remote call arguments
* written for XDR_ENCODE direction only
*/
static bool_t
{
return (FALSE);
return (FALSE);
return (FALSE);
return (TRUE);
}
return (FALSE);
}
/*
* XDR remote call results
* written for XDR_DECODE direction only
*/
static bool_t
{
}
return (FALSE);
}
static bool_t
{
return (FALSE);
}
/*
* From SunOS callrpc.c
*/
static enum clnt_stat
{
int rc;
if (rc) {
return (RPC_SYSTEMERROR); /* XXX */
}
return (cl_stat);
}
static int
{
union DL_primitives *dl_prim;
int error;
return (ENOSR);
}
return (error);
}
switch (dl_prim->dl_primitive) {
case DL_INFO_ACK:
printf("dl_info: DL_INFO_ACK protocol error\n");
break;
}
return (0);
case DL_ERROR_ACK:
printf("dl_info: DL_ERROR_ACK protocol error\n");
break;
}
break;
default:
break;
}
/*
* Error return only.
*/
return (-1);
}
/*
* Configure the 'default' interface based on existing boot properties.
*/
static int
bp_netconfig(void)
{
char *str;
struct sockaddr_in *sin;
int rc;
/*
* No way of getting this right now. Collude with dlifconfig()
* to let the protocol stack choose.
*/
str);
if (dldebug)
printf("host ip is %s\n",
}
str);
if (dldebug)
printf("subnet mask is %s\n",
}
str);
if (dldebug)
printf("router ip is %s\n",
}
}
str);
if (dldebug)
printf("server ip is %s\n",
}
/*
* We need all of these to configure based on properties.
*/
(my_netmask.s_addr == 0) ||
(server_path_c == NULL) ||
(server_name_c == NULL) ||
return (-1);
/*
* Configure the interface.
*/
return (rc);
}
0)) < 0) {
return (rc);
}
/*
* Add a default route.
*/
"bp_netconfig: couldn't add route: %m.\n");
return (rc);
}
}
return (0);
}
/*
* The network device we will use to boot from is plumbed. Extract the details
* from rootfs.
*/
static void
init_config(void)
{
/*
* Assumes only one linkage array element.
*/
/*
* Now we bringup the interface.
* Try cached dhcp response first. If it fails, do rarp.
*/
if ((bp_netconfig() != 0) &&
(dhcpinit() != 0) &&
(whoami() != 0))
"%s: no response from interface", ifname);
else if (dldebug)
}
/*
* Changes must be made to both lists.
*/
static char *optlist[] = {
#define OPT_RO 0
#define OPT_RW 1
#define OPT_QUOTA 2
#define OPT_NOQUOTA 3
#define OPT_SOFT 4
#define OPT_HARD 5
#define OPT_SUID 6
#define OPT_NOSUID 7
#define OPT_GRPID 8
#define OPT_REMOUNT 9
#define OPT_NOSUB 10
#define OPT_INTR 11
#define OPT_NOINTR 12
#define OPT_PORT 13
#define OPT_SECURE 14
#define OPT_RSIZE 15
#define OPT_WSIZE 16
#define OPT_TIMEO 17
#define OPT_RETRANS 18
#define OPT_ACTIMEO 19
#define OPT_ACREGMIN 20
#define OPT_ACREGMAX 21
#define OPT_ACDIRMIN 22
#define OPT_ACDIRMAX 23
#define OPT_BG 24
#define OPT_FG 25
#define OPT_RETRY 26
#define OPT_NOAC 27
#define OPT_NOCTO 28
#define OPT_LLOCK 29
#define OPT_POSIX 30
#define OPT_VERS 31
#define OPT_PROTO 32
#define OPT_SEMISOFT 33
#define OPT_NOPRINT 34
#define OPT_SEC 35
#define OPT_LARGEFILES 36
#define OPT_NOLARGEFILES 37
#define OPT_PUBLIC 38
#define OPT_DIRECTIO 39
#define OPT_NODIRECTIO 40
#define OPT_XATTR 41
#define OPT_NOXATTR 42
#define OPT_DEVICES 43
#define OPT_NODEVICES 44
#define OPT_SETUID 45
#define OPT_NOSETUID 46
#define OPT_EXEC 47
#define OPT_NOEXEC 48
};
static int
{
}
static int
atoi(const char *p)
{
int n;
int c, neg = 0;
if (!isdigit(c = *p)) {
while (isspace(c))
c = *++p;
switch (c) {
case '-':
neg++;
/* FALLTHROUGH */
case '+':
c = *++p;
}
if (!isdigit(c))
return (0);
}
for (n = '0' - c; isdigit(c = *++p); ) {
n *= 10; /* two steps to avoid unnecessary overflow */
n += '0' - c; /* accum neg to avoid surprises at MAX */
}
return (neg ? n : -n);
}
/*
* Default root read tsize XXX
*/
/*
* Default flags: NFSMNT_NOCTO|NFSMNT_LLOCK|NFSMNT_INT
*/
static int
int *vfsflags)
{
char servername[SYS_NMLN];
static int first = 0;
struct netbuf server_address;
int vers;
char rootoptsbuf[256];
/*
* Set default mount options
*/
*vfsflags = 0;
/*
* Only look up the rootopts the first time, we store this in
* a static buffer but we are guaranteed to be single threaded
* and not reentrant.
*/
if (first == 0) {
first++;
rootopts)) {
rootopts[0] = '\0';
goto sanity;
}
}
if (dldebug)
/*
* We have to preserve rootopts for second time.
*/
opts = rootoptsbuf;
while (*opts) {
int opt;
/*
* Options that are defaults or meaningless so ignored
*/
case OPT_QUOTA:
case OPT_NOQUOTA:
case OPT_SUID:
case OPT_DEVICES:
case OPT_SETUID:
case OPT_BG:
case OPT_FG:
case OPT_RETRY:
case OPT_POSIX:
case OPT_LARGEFILES:
case OPT_XATTR:
case OPT_NOXATTR:
case OPT_EXEC:
break;
case OPT_RO:
break;
case OPT_RW:
break;
case OPT_SOFT:
break;
case OPT_SEMISOFT:
break;
case OPT_HARD:
break;
case OPT_NOSUID:
case OPT_NODEVICES:
case OPT_NOSETUID:
case OPT_NOEXEC:
"nfs_dlboot: may not set root partition %s",
break;
case OPT_GRPID:
break;
case OPT_REMOUNT:
"nfs_dlboot: may not remount root partition");
break;
case OPT_INTR:
break;
case OPT_NOINTR:
break;
case OPT_NOAC:
break;
case OPT_PORT:
"nfs_dlboot: may not change root port number");
break;
case OPT_SECURE:
"nfs_dlboot: root mounted auth_unix, secure ignored");
break;
case OPT_NOCTO:
break;
case OPT_RSIZE:
"nfs_dlboot: invalid option: rsize");
break;
}
break;
case OPT_WSIZE:
"nfs_dlboot: invalid option: wsize");
break;
}
break;
case OPT_TIMEO:
"nfs_dlboot: invalid option: timeo");
break;
}
break;
case OPT_RETRANS:
"nfs_dlboot: invalid option: retrans");
break;
}
break;
case OPT_ACTIMEO:
"nfs_dlboot: invalid option: actimeo");
break;
}
break;
case OPT_ACREGMIN:
"nfs_dlboot: invalid option: acregmin");
break;
}
break;
case OPT_ACREGMAX:
"nfs_dlboot: invalid option: acregmax");
break;
}
break;
case OPT_ACDIRMIN:
"nfs_dlboot: invalid option: acdirmin");
break;
}
break;
case OPT_ACDIRMAX:
"nfs_dlboot: invalid option: acdirmax");
break;
}
break;
case OPT_LLOCK:
break;
case OPT_VERS:
"nfs_dlboot: invalid option: vers");
break;
}
/*
* If the requested version is less than what we
* chose, pretend the chosen version doesn't exist
*/
return (EPROTONOSUPPORT);
}
"nfs_dlboot: version %d unavailable",
vers);
return (EINVAL);
}
break;
case OPT_PROTO:
/*
* NFSv4 can only run over TCP, if they requested
* UDP pretend v4 doesn't exist, they might not have
* specified a version allowing a fallback to v2 or v3.
*/
return (EPROTONOSUPPORT);
/*
* TCP is always chosen over UDP, so if the
* requested is the same as the chosen either
* they chose TCP when available or UDP on a UDP
* only server.
*/
break;
/*
* If we chose UDP, they must have requested TCP
*/
"nfs_dlboot: TCP protocol unavailable");
return (EINVAL);
}
/*
* They can only have requested UDP
*/
"nfs_dlboot: unknown protocol");
return (EINVAL);
}
*dl_cf = &dl_udp_netconf;
break;
case OPT_NOPRINT:
break;
case OPT_NOLARGEFILES:
"nfs_dlboot: NFS can't support nolargefiles");
break;
case OPT_SEC:
"nfs_dlboot: root mounted auth_unix, sec ignored");
break;
case OPT_DIRECTIO:
break;
case OPT_NODIRECTIO:
break;
default:
"nfs_dlboot: ignoring invalid option \"%s\"", val);
break;
}
}
/*
* Set some sane limits on read size
*/
/*
* Establish defaults
*/
else
return (0);
}
/*
* No less than 512 bytes, otherwise it will take forever to boot
*/
/*
* If we are running over UDP, we cannot exceed 64KB, trim
* to 56KB to allow room for headers.
*/
return (0);
}