/*
|
/*
|
* linux/fs/nfs/nfsroot.c -- version 2.3
|
* linux/fs/nfs/nfsroot.c -- version 2.3
|
*
|
*
|
* Copyright (C) 1995, 1996 Gero Kuhlmann <gero@gkminix.han.de>
|
* Copyright (C) 1995, 1996 Gero Kuhlmann <gero@gkminix.han.de>
|
*
|
*
|
* For parts of this file:
|
* For parts of this file:
|
* Copyright (C) 1996 Martin Mares <mj@k332.feld.cvut.cz>
|
* Copyright (C) 1996 Martin Mares <mj@k332.feld.cvut.cz>
|
*
|
*
|
* Allow an NFS filesystem to be mounted as root. The way this works is:
|
* Allow an NFS filesystem to be mounted as root. The way this works is:
|
* (1) Determine the local IP address via RARP or BOOTP or from the
|
* (1) Determine the local IP address via RARP or BOOTP or from the
|
* kernel command line.
|
* kernel command line.
|
* (2) Handle RPC negotiation with the system which replied to RARP or
|
* (2) Handle RPC negotiation with the system which replied to RARP or
|
* was reported as a boot server by BOOTP or manually.
|
* was reported as a boot server by BOOTP or manually.
|
* (3) The actual mounting is done later, when init() is running.
|
* (3) The actual mounting is done later, when init() is running.
|
*
|
*
|
*
|
*
|
* Changes:
|
* Changes:
|
*
|
*
|
* R. Drahtmueller : Set IFF_MULTICAST in dev->flags if applicable.
|
* R. Drahtmueller : Set IFF_MULTICAST in dev->flags if applicable.
|
* Alan Cox : Removed get_address name clash with FPU.
|
* Alan Cox : Removed get_address name clash with FPU.
|
* Alan Cox : Reformatted a bit.
|
* Alan Cox : Reformatted a bit.
|
* Gero Kuhlmann : Code cleanup
|
* Gero Kuhlmann : Code cleanup
|
* Michael Rausch : Fixed recognition of an incoming RARP answer.
|
* Michael Rausch : Fixed recognition of an incoming RARP answer.
|
* Martin Mares : (2.0) Auto-configuration via BOOTP supported.
|
* Martin Mares : (2.0) Auto-configuration via BOOTP supported.
|
* Martin Mares : Manual selection of interface & BOOTP/RARP.
|
* Martin Mares : Manual selection of interface & BOOTP/RARP.
|
* Martin Mares : Using network routes instead of host routes,
|
* Martin Mares : Using network routes instead of host routes,
|
* allowing the default configuration to be used
|
* allowing the default configuration to be used
|
* for normal operation of the host.
|
* for normal operation of the host.
|
* Martin Mares : Randomized timer with exponential backoff
|
* Martin Mares : Randomized timer with exponential backoff
|
* installed to minimize network congestion.
|
* installed to minimize network congestion.
|
* Martin Mares : Code cleanup.
|
* Martin Mares : Code cleanup.
|
* Martin Mares : (2.1) BOOTP and RARP made configuration options.
|
* Martin Mares : (2.1) BOOTP and RARP made configuration options.
|
* Martin Mares : Server hostname generation fixed.
|
* Martin Mares : Server hostname generation fixed.
|
* Gerd Knorr : Fixed wired inode handling
|
* Gerd Knorr : Fixed wired inode handling
|
* Martin Mares : (2.2) "0.0.0.0" addresses from command line ignored.
|
* Martin Mares : (2.2) "0.0.0.0" addresses from command line ignored.
|
* Martin Mares : RARP replies not tested for server address.
|
* Martin Mares : RARP replies not tested for server address.
|
* Gero Kuhlmann : (2.3) Some bug fixes and code cleanup again (please
|
* Gero Kuhlmann : (2.3) Some bug fixes and code cleanup again (please
|
* send me your new patches _before_ bothering
|
* send me your new patches _before_ bothering
|
* Linus so that I don' always have to cleanup
|
* Linus so that I don' always have to cleanup
|
* _afterwards_ - thanks)
|
* _afterwards_ - thanks)
|
* Gero Kuhlmann : Last changes of Martin Mares undone.
|
* Gero Kuhlmann : Last changes of Martin Mares undone.
|
* Gero Kuhlmann : RARP replies are tested for specified server
|
* Gero Kuhlmann : RARP replies are tested for specified server
|
* again. However, it's now possible to have
|
* again. However, it's now possible to have
|
* different RARP and NFS servers.
|
* different RARP and NFS servers.
|
* Gero Kuhlmann : "0.0.0.0" addresses from command line are
|
* Gero Kuhlmann : "0.0.0.0" addresses from command line are
|
* now mapped to INADDR_NONE.
|
* now mapped to INADDR_NONE.
|
* Gero Kuhlmann : Fixed a bug which prevented BOOTP path name
|
* Gero Kuhlmann : Fixed a bug which prevented BOOTP path name
|
* from being used (thanks to Leo Spiekman)
|
* from being used (thanks to Leo Spiekman)
|
* Andy Walker : Allow to specify the NFS server in nfs_root
|
* Andy Walker : Allow to specify the NFS server in nfs_root
|
* without giving a path name
|
* without giving a path name
|
* Swen Th=FCmmler : Allow to specify the NFS options in nfs_root
|
* Swen Th=FCmmler : Allow to specify the NFS options in nfs_root
|
* without giving a path name. Fix BOOTP request
|
* without giving a path name. Fix BOOTP request
|
* for domainname (domainname is NIS domain, not
|
* for domainname (domainname is NIS domain, not
|
* DNS domain!). Skip dummy devices for BOOTP.
|
* DNS domain!). Skip dummy devices for BOOTP.
|
* Jacek Zapala : Fixed a bug which prevented server-ip address
|
* Jacek Zapala : Fixed a bug which prevented server-ip address
|
* from nfsroot parameter from being used.
|
* from nfsroot parameter from being used.
|
*
|
*
|
*/
|
*/
|
|
|
|
|
/* Define this to allow debugging output */
|
/* Define this to allow debugging output */
|
#undef NFSROOT_DEBUG
|
#undef NFSROOT_DEBUG
|
#undef NFSROOT_BOOTP_DEBUG
|
#undef NFSROOT_BOOTP_DEBUG
|
|
|
|
|
#include <linux/config.h>
|
#include <linux/config.h>
|
#include <linux/types.h>
|
#include <linux/types.h>
|
#include <linux/string.h>
|
#include <linux/string.h>
|
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
#include <linux/sched.h>
|
#include <linux/sched.h>
|
#include <linux/fs.h>
|
#include <linux/fs.h>
|
#include <linux/random.h>
|
#include <linux/random.h>
|
#include <linux/fcntl.h>
|
#include <linux/fcntl.h>
|
|
|
#include <asm/param.h>
|
#include <asm/param.h>
|
#include <linux/utsname.h>
|
#include <linux/utsname.h>
|
#include <linux/in.h>
|
#include <linux/in.h>
|
#include <linux/if.h>
|
#include <linux/if.h>
|
#include <linux/inet.h>
|
#include <linux/inet.h>
|
#include <linux/net.h>
|
#include <linux/net.h>
|
#include <linux/netdevice.h>
|
#include <linux/netdevice.h>
|
#include <linux/if_arp.h>
|
#include <linux/if_arp.h>
|
#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
|
#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
|
#include <net/ax25.h> /* For AX25_P_IP */
|
#include <net/ax25.h> /* For AX25_P_IP */
|
#endif
|
#endif
|
#include <linux/skbuff.h>
|
#include <linux/skbuff.h>
|
#include <linux/socket.h>
|
#include <linux/socket.h>
|
#include <linux/route.h>
|
#include <linux/route.h>
|
#include <linux/nfs.h>
|
#include <linux/nfs.h>
|
#include <linux/nfs_fs.h>
|
#include <linux/nfs_fs.h>
|
#include <linux/nfs_mount.h>
|
#include <linux/nfs_mount.h>
|
#include <linux/in.h>
|
#include <linux/in.h>
|
#include <net/route.h>
|
#include <net/route.h>
|
#include <net/sock.h>
|
#include <net/sock.h>
|
|
|
#include <asm/segment.h>
|
#include <asm/segment.h>
|
|
|
/* Range of privileged ports */
|
/* Range of privileged ports */
|
#define STARTPORT 600
|
#define STARTPORT 600
|
#define ENDPORT 1023
|
#define ENDPORT 1023
|
#define NPORTS (ENDPORT - STARTPORT + 1)
|
#define NPORTS (ENDPORT - STARTPORT + 1)
|
|
|
|
|
/* Define the timeout for waiting for a RARP/BOOTP reply */
|
/* Define the timeout for waiting for a RARP/BOOTP reply */
|
#define CONF_BASE_TIMEOUT (HZ*5) /* Initial timeout: 5 seconds */
|
#define CONF_BASE_TIMEOUT (HZ*5) /* Initial timeout: 5 seconds */
|
#define CONF_RETRIES 10 /* 10 retries */
|
#define CONF_RETRIES 10 /* 10 retries */
|
#define CONF_TIMEOUT_RANDOM (HZ) /* Maximum amount of randomization */
|
#define CONF_TIMEOUT_RANDOM (HZ) /* Maximum amount of randomization */
|
#define CONF_TIMEOUT_MULT *5/4 /* Speed of timeout growth */
|
#define CONF_TIMEOUT_MULT *5/4 /* Speed of timeout growth */
|
#define CONF_TIMEOUT_MAX (HZ*30) /* Maximum allowed timeout */
|
#define CONF_TIMEOUT_MAX (HZ*30) /* Maximum allowed timeout */
|
|
|
|
|
/* List of open devices */
|
/* List of open devices */
|
struct open_dev {
|
struct open_dev {
|
struct device *dev;
|
struct device *dev;
|
unsigned short old_flags;
|
unsigned short old_flags;
|
struct open_dev *next;
|
struct open_dev *next;
|
};
|
};
|
|
|
static struct open_dev *open_base = NULL;
|
static struct open_dev *open_base = NULL;
|
|
|
|
|
/* IP configuration */
|
/* IP configuration */
|
static struct device *root_dev = NULL; /* Device selected for booting */
|
static struct device *root_dev = NULL; /* Device selected for booting */
|
static char user_dev_name[IFNAMSIZ]; /* Name of user-selected boot device */
|
static char user_dev_name[IFNAMSIZ]; /* Name of user-selected boot device */
|
static struct sockaddr_in myaddr; /* My IP address */
|
static struct sockaddr_in myaddr; /* My IP address */
|
static struct sockaddr_in server; /* Server IP address */
|
static struct sockaddr_in server; /* Server IP address */
|
static struct sockaddr_in gateway; /* Gateway IP address */
|
static struct sockaddr_in gateway; /* Gateway IP address */
|
static struct sockaddr_in netmask; /* Netmask for local subnet */
|
static struct sockaddr_in netmask; /* Netmask for local subnet */
|
|
|
|
|
/* BOOTP/RARP variables */
|
/* BOOTP/RARP variables */
|
static int bootp_flag; /* User said: Use BOOTP! */
|
static int bootp_flag; /* User said: Use BOOTP! */
|
static int rarp_flag; /* User said: Use RARP! */
|
static int rarp_flag; /* User said: Use RARP! */
|
static int bootp_dev_count = 0; /* Number of devices allowing BOOTP */
|
static int bootp_dev_count = 0; /* Number of devices allowing BOOTP */
|
static int rarp_dev_count = 0; /* Number of devices allowing RARP */
|
static int rarp_dev_count = 0; /* Number of devices allowing RARP */
|
static struct sockaddr_in rarp_serv; /* IP address of RARP server */
|
static struct sockaddr_in rarp_serv; /* IP address of RARP server */
|
|
|
#if defined(CONFIG_RNFS_BOOTP) || defined(CONFIG_RNFS_RARP)
|
#if defined(CONFIG_RNFS_BOOTP) || defined(CONFIG_RNFS_RARP)
|
#define CONFIG_RNFS_DYNAMIC /* Enable dynamic IP config */
|
#define CONFIG_RNFS_DYNAMIC /* Enable dynamic IP config */
|
static volatile int pkt_arrived; /* BOOTP/RARP packet detected */
|
static volatile int pkt_arrived; /* BOOTP/RARP packet detected */
|
|
|
#define ARRIVED_BOOTP 1
|
#define ARRIVED_BOOTP 1
|
#define ARRIVED_RARP 2
|
#define ARRIVED_RARP 2
|
#endif
|
#endif
|
|
|
|
|
/* NFS-related data */
|
/* NFS-related data */
|
static struct nfs_mount_data nfs_data; /* NFS mount info */
|
static struct nfs_mount_data nfs_data; /* NFS mount info */
|
static char nfs_path[NFS_MAXPATHLEN] = ""; /* Name of directory to mount */
|
static char nfs_path[NFS_MAXPATHLEN] = ""; /* Name of directory to mount */
|
static int nfs_port; /* Port to connect to for NFS */
|
static int nfs_port; /* Port to connect to for NFS */
|
|
|
|
|
/* Yes, we use sys_socket, but there's no include file for it */
|
/* Yes, we use sys_socket, but there's no include file for it */
|
extern asmlinkage int sys_socket(int family, int type, int protocol);
|
extern asmlinkage int sys_socket(int family, int type, int protocol);
|
|
|
|
|
|
|
/***************************************************************************
|
/***************************************************************************
|
|
|
Device Handling Subroutines
|
Device Handling Subroutines
|
|
|
***************************************************************************/
|
***************************************************************************/
|
|
|
/*
|
/*
|
* Setup and initialize all network devices. If there is a user-preferred
|
* Setup and initialize all network devices. If there is a user-preferred
|
* interface, ignore all other interfaces.
|
* interface, ignore all other interfaces.
|
*/
|
*/
|
static int root_dev_open(void)
|
static int root_dev_open(void)
|
{
|
{
|
struct open_dev *openp, **last;
|
struct open_dev *openp, **last;
|
struct device *dev;
|
struct device *dev;
|
unsigned short old_flags;
|
unsigned short old_flags;
|
|
|
last = &open_base;
|
last = &open_base;
|
for (dev = dev_base; dev != NULL; dev = dev->next) {
|
for (dev = dev_base; dev != NULL; dev = dev->next) {
|
if (dev->type < ARPHRD_SLIP &&
|
if (dev->type < ARPHRD_SLIP &&
|
dev->family == AF_INET &&
|
dev->family == AF_INET &&
|
!(dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) &&
|
!(dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) &&
|
(0 != strncmp(dev->name, "dummy", 5)) &&
|
(0 != strncmp(dev->name, "dummy", 5)) &&
|
(!user_dev_name[0] || !strcmp(dev->name, user_dev_name))) {
|
(!user_dev_name[0] || !strcmp(dev->name, user_dev_name))) {
|
/* First up the interface */
|
/* First up the interface */
|
old_flags = dev->flags;
|
old_flags = dev->flags;
|
#ifdef CONFIG_IP_MULTICAST
|
#ifdef CONFIG_IP_MULTICAST
|
dev->flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING | IFF_MULTICAST;
|
dev->flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING | IFF_MULTICAST;
|
#else
|
#else
|
dev->flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING;
|
dev->flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING;
|
#endif
|
#endif
|
if (!(old_flags & IFF_UP) && dev_open(dev)) {
|
if (!(old_flags & IFF_UP) && dev_open(dev)) {
|
dev->flags = old_flags;
|
dev->flags = old_flags;
|
continue;
|
continue;
|
}
|
}
|
openp = (struct open_dev *) kmalloc(sizeof(struct open_dev),
|
openp = (struct open_dev *) kmalloc(sizeof(struct open_dev),
|
GFP_ATOMIC);
|
GFP_ATOMIC);
|
if (openp == NULL)
|
if (openp == NULL)
|
continue;
|
continue;
|
openp->dev = dev;
|
openp->dev = dev;
|
openp->old_flags = old_flags;
|
openp->old_flags = old_flags;
|
*last = openp;
|
*last = openp;
|
last = &openp->next;
|
last = &openp->next;
|
bootp_dev_count++;
|
bootp_dev_count++;
|
if (!(dev->flags & IFF_NOARP))
|
if (!(dev->flags & IFF_NOARP))
|
rarp_dev_count++;
|
rarp_dev_count++;
|
#ifdef NFSROOT_DEBUG
|
#ifdef NFSROOT_DEBUG
|
printk(KERN_NOTICE "Root-NFS: Opened %s\n", dev->name);
|
printk(KERN_NOTICE "Root-NFS: Opened %s\n", dev->name);
|
#endif
|
#endif
|
}
|
}
|
}
|
}
|
*last = NULL;
|
*last = NULL;
|
|
|
if (!bootp_dev_count && !rarp_dev_count) {
|
if (!bootp_dev_count && !rarp_dev_count) {
|
printk(KERN_ERR "Root-NFS: Unable to open at least one network device\n");
|
printk(KERN_ERR "Root-NFS: Unable to open at least one network device\n");
|
return -1;
|
return -1;
|
}
|
}
|
return 0;
|
return 0;
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Restore the state of all devices. However, keep the root device open
|
* Restore the state of all devices. However, keep the root device open
|
* for the upcoming mount.
|
* for the upcoming mount.
|
*/
|
*/
|
static void root_dev_close(void)
|
static void root_dev_close(void)
|
{
|
{
|
struct open_dev *openp;
|
struct open_dev *openp;
|
struct open_dev *nextp;
|
struct open_dev *nextp;
|
|
|
openp = open_base;
|
openp = open_base;
|
while (openp != NULL) {
|
while (openp != NULL) {
|
nextp = openp->next;
|
nextp = openp->next;
|
openp->next = NULL;
|
openp->next = NULL;
|
if (openp->dev != root_dev) {
|
if (openp->dev != root_dev) {
|
if (!(openp->old_flags & IFF_UP))
|
if (!(openp->old_flags & IFF_UP))
|
dev_close(openp->dev);
|
dev_close(openp->dev);
|
openp->dev->flags = openp->old_flags;
|
openp->dev->flags = openp->old_flags;
|
}
|
}
|
kfree_s(openp, sizeof(struct open_dev));
|
kfree_s(openp, sizeof(struct open_dev));
|
openp = nextp;
|
openp = nextp;
|
}
|
}
|
}
|
}
|
|
|
|
|
|
|
/***************************************************************************
|
/***************************************************************************
|
|
|
RARP Subroutines
|
RARP Subroutines
|
|
|
***************************************************************************/
|
***************************************************************************/
|
|
|
#ifdef CONFIG_RNFS_RARP
|
#ifdef CONFIG_RNFS_RARP
|
|
|
extern void arp_send(int type, int ptype, unsigned long target_ip,
|
extern void arp_send(int type, int ptype, unsigned long target_ip,
|
struct device *dev, unsigned long src_ip,
|
struct device *dev, unsigned long src_ip,
|
unsigned char *dest_hw, unsigned char *src_hw,
|
unsigned char *dest_hw, unsigned char *src_hw,
|
unsigned char *target_hw);
|
unsigned char *target_hw);
|
|
|
static int root_rarp_recv(struct sk_buff *skb, struct device *dev,
|
static int root_rarp_recv(struct sk_buff *skb, struct device *dev,
|
struct packet_type *pt);
|
struct packet_type *pt);
|
|
|
|
|
static struct packet_type rarp_packet_type = {
|
static struct packet_type rarp_packet_type = {
|
0, /* Should be: __constant_htons(ETH_P_RARP)
|
0, /* Should be: __constant_htons(ETH_P_RARP)
|
* - but this _doesn't_ come out constant! */
|
* - but this _doesn't_ come out constant! */
|
NULL, /* Listen to all devices */
|
NULL, /* Listen to all devices */
|
root_rarp_recv,
|
root_rarp_recv,
|
NULL,
|
NULL,
|
NULL
|
NULL
|
};
|
};
|
|
|
|
|
/*
|
/*
|
* Register the packet type for RARP
|
* Register the packet type for RARP
|
*/
|
*/
|
static void root_rarp_open(void)
|
static void root_rarp_open(void)
|
{
|
{
|
rarp_packet_type.type = htons(ETH_P_RARP);
|
rarp_packet_type.type = htons(ETH_P_RARP);
|
dev_add_pack(&rarp_packet_type);
|
dev_add_pack(&rarp_packet_type);
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Deregister the RARP packet type
|
* Deregister the RARP packet type
|
*/
|
*/
|
static void root_rarp_close(void)
|
static void root_rarp_close(void)
|
{
|
{
|
rarp_packet_type.type = htons(ETH_P_RARP);
|
rarp_packet_type.type = htons(ETH_P_RARP);
|
dev_remove_pack(&rarp_packet_type);
|
dev_remove_pack(&rarp_packet_type);
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Receive RARP packets.
|
* Receive RARP packets.
|
*/
|
*/
|
static int root_rarp_recv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
|
static int root_rarp_recv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
|
{
|
{
|
struct arphdr *rarp = (struct arphdr *)skb->h.raw;
|
struct arphdr *rarp = (struct arphdr *)skb->h.raw;
|
unsigned char *rarp_ptr = (unsigned char *) (rarp + 1);
|
unsigned char *rarp_ptr = (unsigned char *) (rarp + 1);
|
unsigned long sip, tip;
|
unsigned long sip, tip;
|
unsigned char *sha, *tha; /* s for "source", t for "target" */
|
unsigned char *sha, *tha; /* s for "source", t for "target" */
|
|
|
/* If this test doesn't pass, it's not IP, or we should ignore it anyway */
|
/* If this test doesn't pass, it's not IP, or we should ignore it anyway */
|
if (rarp->ar_hln != dev->addr_len || dev->type != ntohs(rarp->ar_hrd)) {
|
if (rarp->ar_hln != dev->addr_len || dev->type != ntohs(rarp->ar_hrd)) {
|
kfree_skb(skb, FREE_READ);
|
kfree_skb(skb, FREE_READ);
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* If it's not a RARP reply, delete it. */
|
/* If it's not a RARP reply, delete it. */
|
if (rarp->ar_op != htons(ARPOP_RREPLY)) {
|
if (rarp->ar_op != htons(ARPOP_RREPLY)) {
|
kfree_skb(skb, FREE_READ);
|
kfree_skb(skb, FREE_READ);
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* If it's not ethernet or AX25, delete it. */
|
/* If it's not ethernet or AX25, delete it. */
|
if ((rarp->ar_pro != htons(ETH_P_IP) && dev->type != ARPHRD_AX25) ||
|
if ((rarp->ar_pro != htons(ETH_P_IP) && dev->type != ARPHRD_AX25) ||
|
#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
|
#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
|
(rarp->ar_pro != htons(AX25_P_IP) && dev->type == ARPHRD_AX25) ||
|
(rarp->ar_pro != htons(AX25_P_IP) && dev->type == ARPHRD_AX25) ||
|
#endif
|
#endif
|
rarp->ar_pln != 4) {
|
rarp->ar_pln != 4) {
|
kfree_skb(skb, FREE_READ);
|
kfree_skb(skb, FREE_READ);
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* Extract variable width fields */
|
/* Extract variable width fields */
|
sha = rarp_ptr;
|
sha = rarp_ptr;
|
rarp_ptr += dev->addr_len;
|
rarp_ptr += dev->addr_len;
|
memcpy(&sip, rarp_ptr, 4);
|
memcpy(&sip, rarp_ptr, 4);
|
rarp_ptr += 4;
|
rarp_ptr += 4;
|
tha = rarp_ptr;
|
tha = rarp_ptr;
|
rarp_ptr += dev->addr_len;
|
rarp_ptr += dev->addr_len;
|
memcpy(&tip, rarp_ptr, 4);
|
memcpy(&tip, rarp_ptr, 4);
|
|
|
/* Discard packets which are not meant for us. */
|
/* Discard packets which are not meant for us. */
|
if (memcmp(tha, dev->dev_addr, dev->addr_len)) {
|
if (memcmp(tha, dev->dev_addr, dev->addr_len)) {
|
kfree_skb(skb, FREE_READ);
|
kfree_skb(skb, FREE_READ);
|
return 0;
|
return 0;
|
}
|
}
|
/* Discard packets which are not from specified server. */
|
/* Discard packets which are not from specified server. */
|
if (rarp_flag && !bootp_flag &&
|
if (rarp_flag && !bootp_flag &&
|
rarp_serv.sin_addr.s_addr != INADDR_NONE &&
|
rarp_serv.sin_addr.s_addr != INADDR_NONE &&
|
rarp_serv.sin_addr.s_addr != sip) {
|
rarp_serv.sin_addr.s_addr != sip) {
|
kfree_skb(skb, FREE_READ);
|
kfree_skb(skb, FREE_READ);
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/*
|
/*
|
* The packet is what we were looking for. Setup the global
|
* The packet is what we were looking for. Setup the global
|
* variables.
|
* variables.
|
*/
|
*/
|
cli();
|
cli();
|
if (pkt_arrived) {
|
if (pkt_arrived) {
|
sti();
|
sti();
|
kfree_skb(skb, FREE_READ);
|
kfree_skb(skb, FREE_READ);
|
return 0;
|
return 0;
|
}
|
}
|
pkt_arrived = ARRIVED_RARP;
|
pkt_arrived = ARRIVED_RARP;
|
sti();
|
sti();
|
root_dev = dev;
|
root_dev = dev;
|
|
|
if (myaddr.sin_addr.s_addr == INADDR_NONE) {
|
if (myaddr.sin_addr.s_addr == INADDR_NONE) {
|
myaddr.sin_family = dev->family;
|
myaddr.sin_family = dev->family;
|
myaddr.sin_addr.s_addr = tip;
|
myaddr.sin_addr.s_addr = tip;
|
}
|
}
|
if (server.sin_addr.s_addr == INADDR_NONE) {
|
if (server.sin_addr.s_addr == INADDR_NONE) {
|
server.sin_family = dev->family;
|
server.sin_family = dev->family;
|
server.sin_addr.s_addr = sip;
|
server.sin_addr.s_addr = sip;
|
}
|
}
|
kfree_skb(skb, FREE_READ);
|
kfree_skb(skb, FREE_READ);
|
return 0;
|
return 0;
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Send RARP request packet over all devices which allow RARP.
|
* Send RARP request packet over all devices which allow RARP.
|
*/
|
*/
|
static void root_rarp_send(void)
|
static void root_rarp_send(void)
|
{
|
{
|
struct open_dev *openp;
|
struct open_dev *openp;
|
struct device *dev;
|
struct device *dev;
|
int num = 0;
|
int num = 0;
|
|
|
for (openp = open_base; openp != NULL; openp = openp->next) {
|
for (openp = open_base; openp != NULL; openp = openp->next) {
|
dev = openp->dev;
|
dev = openp->dev;
|
if (!(dev->flags & IFF_NOARP)) {
|
if (!(dev->flags & IFF_NOARP)) {
|
arp_send(ARPOP_RREQUEST, ETH_P_RARP, 0, dev, 0, NULL,
|
arp_send(ARPOP_RREQUEST, ETH_P_RARP, 0, dev, 0, NULL,
|
dev->dev_addr, dev->dev_addr);
|
dev->dev_addr, dev->dev_addr);
|
num++;
|
num++;
|
}
|
}
|
}
|
}
|
}
|
}
|
#endif
|
#endif
|
|
|
|
|
|
|
/***************************************************************************
|
/***************************************************************************
|
|
|
BOOTP Subroutines
|
BOOTP Subroutines
|
|
|
***************************************************************************/
|
***************************************************************************/
|
|
|
#ifdef CONFIG_RNFS_BOOTP
|
#ifdef CONFIG_RNFS_BOOTP
|
|
|
static struct device *bootp_dev = NULL; /* Device selected as best BOOTP target */
|
static struct device *bootp_dev = NULL; /* Device selected as best BOOTP target */
|
|
|
static int bootp_xmit_fd = -1; /* Socket descriptor for transmit */
|
static int bootp_xmit_fd = -1; /* Socket descriptor for transmit */
|
static struct socket *bootp_xmit_sock; /* The socket itself */
|
static struct socket *bootp_xmit_sock; /* The socket itself */
|
static int bootp_recv_fd = -1; /* Socket descriptor for receive */
|
static int bootp_recv_fd = -1; /* Socket descriptor for receive */
|
static struct socket *bootp_recv_sock; /* The socket itself */
|
static struct socket *bootp_recv_sock; /* The socket itself */
|
|
|
struct bootp_pkt { /* BOOTP packet format */
|
struct bootp_pkt { /* BOOTP packet format */
|
u8 op; /* 1=request, 2=reply */
|
u8 op; /* 1=request, 2=reply */
|
u8 htype; /* HW address type */
|
u8 htype; /* HW address type */
|
u8 hlen; /* HW address length */
|
u8 hlen; /* HW address length */
|
u8 hops; /* Used only by gateways */
|
u8 hops; /* Used only by gateways */
|
u32 xid; /* Transaction ID */
|
u32 xid; /* Transaction ID */
|
u16 secs; /* Seconds since we started */
|
u16 secs; /* Seconds since we started */
|
u16 flags; /* Just what is says */
|
u16 flags; /* Just what is says */
|
u32 client_ip; /* Client's IP address if known */
|
u32 client_ip; /* Client's IP address if known */
|
u32 your_ip; /* Assigned IP address */
|
u32 your_ip; /* Assigned IP address */
|
u32 server_ip; /* Server's IP address */
|
u32 server_ip; /* Server's IP address */
|
u32 relay_ip; /* IP address of BOOTP relay */
|
u32 relay_ip; /* IP address of BOOTP relay */
|
u8 hw_addr[16]; /* Client's HW address */
|
u8 hw_addr[16]; /* Client's HW address */
|
u8 serv_name[64]; /* Server host name */
|
u8 serv_name[64]; /* Server host name */
|
u8 boot_file[128]; /* Name of boot file */
|
u8 boot_file[128]; /* Name of boot file */
|
u8 vendor_area[128]; /* Area for extensions */
|
u8 vendor_area[128]; /* Area for extensions */
|
};
|
};
|
|
|
#define BOOTP_REQUEST 1
|
#define BOOTP_REQUEST 1
|
#define BOOTP_REPLY 2
|
#define BOOTP_REPLY 2
|
|
|
static struct bootp_pkt *xmit_bootp; /* Packet being transmitted */
|
static struct bootp_pkt *xmit_bootp; /* Packet being transmitted */
|
static struct bootp_pkt *recv_bootp; /* Packet being received */
|
static struct bootp_pkt *recv_bootp; /* Packet being received */
|
|
|
static int bootp_have_route = 0; /* BOOTP route installed */
|
static int bootp_have_route = 0; /* BOOTP route installed */
|
|
|
|
|
/*
|
/*
|
* Free BOOTP packet buffers
|
* Free BOOTP packet buffers
|
*/
|
*/
|
static void root_free_bootp(void)
|
static void root_free_bootp(void)
|
{
|
{
|
if (xmit_bootp) {
|
if (xmit_bootp) {
|
kfree_s(xmit_bootp, sizeof(struct bootp_pkt));
|
kfree_s(xmit_bootp, sizeof(struct bootp_pkt));
|
xmit_bootp = NULL;
|
xmit_bootp = NULL;
|
}
|
}
|
if (recv_bootp) {
|
if (recv_bootp) {
|
kfree_s(recv_bootp, sizeof(struct bootp_pkt));
|
kfree_s(recv_bootp, sizeof(struct bootp_pkt));
|
recv_bootp = NULL;
|
recv_bootp = NULL;
|
}
|
}
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Allocate memory for BOOTP packet buffers
|
* Allocate memory for BOOTP packet buffers
|
*/
|
*/
|
static inline int root_alloc_bootp(void)
|
static inline int root_alloc_bootp(void)
|
{
|
{
|
if (!(xmit_bootp = kmalloc(sizeof(struct bootp_pkt), GFP_KERNEL)) ||
|
if (!(xmit_bootp = kmalloc(sizeof(struct bootp_pkt), GFP_KERNEL)) ||
|
!(recv_bootp = kmalloc(sizeof(struct bootp_pkt), GFP_KERNEL))) {
|
!(recv_bootp = kmalloc(sizeof(struct bootp_pkt), GFP_KERNEL))) {
|
printk("BOOTP: Out of memory!");
|
printk("BOOTP: Out of memory!");
|
return -1;
|
return -1;
|
}
|
}
|
return 0;
|
return 0;
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Create default route for BOOTP sending
|
* Create default route for BOOTP sending
|
*/
|
*/
|
static int root_add_bootp_route(void)
|
static int root_add_bootp_route(void)
|
{
|
{
|
struct rtentry route;
|
struct rtentry route;
|
|
|
memset(&route, 0, sizeof(route));
|
memset(&route, 0, sizeof(route));
|
route.rt_dev = bootp_dev->name;
|
route.rt_dev = bootp_dev->name;
|
route.rt_mss = bootp_dev->mtu;
|
route.rt_mss = bootp_dev->mtu;
|
route.rt_flags = RTF_UP;
|
route.rt_flags = RTF_UP;
|
((struct sockaddr_in *) &(route.rt_dst)) -> sin_addr.s_addr = 0;
|
((struct sockaddr_in *) &(route.rt_dst)) -> sin_addr.s_addr = 0;
|
((struct sockaddr_in *) &(route.rt_dst)) -> sin_family = AF_INET;
|
((struct sockaddr_in *) &(route.rt_dst)) -> sin_family = AF_INET;
|
((struct sockaddr_in *) &(route.rt_genmask)) -> sin_addr.s_addr = 0;
|
((struct sockaddr_in *) &(route.rt_genmask)) -> sin_addr.s_addr = 0;
|
((struct sockaddr_in *) &(route.rt_genmask)) -> sin_family = AF_INET;
|
((struct sockaddr_in *) &(route.rt_genmask)) -> sin_family = AF_INET;
|
if (ip_rt_new(&route)) {
|
if (ip_rt_new(&route)) {
|
printk(KERN_ERR "BOOTP: Adding of route failed!\n");
|
printk(KERN_ERR "BOOTP: Adding of route failed!\n");
|
return -1;
|
return -1;
|
}
|
}
|
bootp_have_route = 1;
|
bootp_have_route = 1;
|
return 0;
|
return 0;
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Delete default route for BOOTP sending
|
* Delete default route for BOOTP sending
|
*/
|
*/
|
static int root_del_bootp_route(void)
|
static int root_del_bootp_route(void)
|
{
|
{
|
struct rtentry route;
|
struct rtentry route;
|
|
|
if (!bootp_have_route)
|
if (!bootp_have_route)
|
return 0;
|
return 0;
|
memset(&route, 0, sizeof(route));
|
memset(&route, 0, sizeof(route));
|
((struct sockaddr_in *) &(route.rt_dst)) -> sin_addr.s_addr = 0;
|
((struct sockaddr_in *) &(route.rt_dst)) -> sin_addr.s_addr = 0;
|
((struct sockaddr_in *) &(route.rt_genmask)) -> sin_addr.s_addr = 0;
|
((struct sockaddr_in *) &(route.rt_genmask)) -> sin_addr.s_addr = 0;
|
if (ip_rt_kill(&route)) {
|
if (ip_rt_kill(&route)) {
|
printk(KERN_ERR "BOOTP: Deleting of route failed!\n");
|
printk(KERN_ERR "BOOTP: Deleting of route failed!\n");
|
return -1;
|
return -1;
|
}
|
}
|
bootp_have_route = 0;
|
bootp_have_route = 0;
|
return 0;
|
return 0;
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Open UDP socket.
|
* Open UDP socket.
|
*/
|
*/
|
static int root_open_udp_sock(int *fd, struct socket **sock)
|
static int root_open_udp_sock(int *fd, struct socket **sock)
|
{
|
{
|
struct file *file;
|
struct file *file;
|
struct inode *inode;
|
struct inode *inode;
|
|
|
*fd = sys_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
*fd = sys_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
if (*fd >= 0) {
|
if (*fd >= 0) {
|
file = current->files->fd[*fd];
|
file = current->files->fd[*fd];
|
inode = file->f_inode;
|
inode = file->f_inode;
|
*sock = &inode->u.socket_i;
|
*sock = &inode->u.socket_i;
|
return 0;
|
return 0;
|
}
|
}
|
|
|
printk(KERN_ERR "BOOTP: Cannot open UDP socket!\n");
|
printk(KERN_ERR "BOOTP: Cannot open UDP socket!\n");
|
return -1;
|
return -1;
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Connect UDP socket.
|
* Connect UDP socket.
|
*/
|
*/
|
static int root_connect_udp_sock(struct socket *sock, u32 addr, u16 port)
|
static int root_connect_udp_sock(struct socket *sock, u32 addr, u16 port)
|
{
|
{
|
struct sockaddr_in sa;
|
struct sockaddr_in sa;
|
int result;
|
int result;
|
|
|
sa.sin_family = AF_INET;
|
sa.sin_family = AF_INET;
|
sa.sin_addr.s_addr = htonl(addr);
|
sa.sin_addr.s_addr = htonl(addr);
|
sa.sin_port = htons(port);
|
sa.sin_port = htons(port);
|
result = sock->ops->connect(sock, (struct sockaddr *) &sa, sizeof(sa), 0);
|
result = sock->ops->connect(sock, (struct sockaddr *) &sa, sizeof(sa), 0);
|
if (result < 0) {
|
if (result < 0) {
|
printk(KERN_ERR "BOOTP: connect() failed\n");
|
printk(KERN_ERR "BOOTP: connect() failed\n");
|
return -1;
|
return -1;
|
}
|
}
|
return 0;
|
return 0;
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Bind UDP socket.
|
* Bind UDP socket.
|
*/
|
*/
|
static int root_bind_udp_sock(struct socket *sock, u32 addr, u16 port)
|
static int root_bind_udp_sock(struct socket *sock, u32 addr, u16 port)
|
{
|
{
|
struct sockaddr_in sa;
|
struct sockaddr_in sa;
|
int result;
|
int result;
|
|
|
sa.sin_family = AF_INET;
|
sa.sin_family = AF_INET;
|
sa.sin_addr.s_addr = htonl(addr);
|
sa.sin_addr.s_addr = htonl(addr);
|
sa.sin_port = htons(port);
|
sa.sin_port = htons(port);
|
result = sock->ops->bind(sock, (struct sockaddr *) &sa, sizeof(sa));
|
result = sock->ops->bind(sock, (struct sockaddr *) &sa, sizeof(sa));
|
if (result < 0) {
|
if (result < 0) {
|
printk(KERN_ERR "BOOTP: bind() failed\n");
|
printk(KERN_ERR "BOOTP: bind() failed\n");
|
return -1;
|
return -1;
|
}
|
}
|
return 0;
|
return 0;
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Send UDP packet.
|
* Send UDP packet.
|
*/
|
*/
|
static inline int root_send_udp(struct socket *sock, void *buf, int size)
|
static inline int root_send_udp(struct socket *sock, void *buf, int size)
|
{
|
{
|
u32 oldfs;
|
u32 oldfs;
|
int result;
|
int result;
|
struct msghdr msg;
|
struct msghdr msg;
|
struct iovec iov;
|
struct iovec iov;
|
|
|
oldfs = get_fs();
|
oldfs = get_fs();
|
set_fs(get_ds());
|
set_fs(get_ds());
|
iov.iov_base = buf;
|
iov.iov_base = buf;
|
iov.iov_len = size;
|
iov.iov_len = size;
|
msg.msg_name = NULL;
|
msg.msg_name = NULL;
|
msg.msg_iov = &iov;
|
msg.msg_iov = &iov;
|
msg.msg_iovlen = 1;
|
msg.msg_iovlen = 1;
|
msg.msg_control = NULL;
|
msg.msg_control = NULL;
|
result = sock->ops->sendmsg(sock, &msg, size, 0, 0);
|
result = sock->ops->sendmsg(sock, &msg, size, 0, 0);
|
set_fs(oldfs);
|
set_fs(oldfs);
|
return (result != size);
|
return (result != size);
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Try to receive UDP packet.
|
* Try to receive UDP packet.
|
*/
|
*/
|
static inline int root_recv_udp(struct socket *sock, void *buf, int size)
|
static inline int root_recv_udp(struct socket *sock, void *buf, int size)
|
{
|
{
|
u32 oldfs;
|
u32 oldfs;
|
int result;
|
int result;
|
struct msghdr msg;
|
struct msghdr msg;
|
struct iovec iov;
|
struct iovec iov;
|
|
|
oldfs = get_fs();
|
oldfs = get_fs();
|
set_fs(get_ds());
|
set_fs(get_ds());
|
iov.iov_base = buf;
|
iov.iov_base = buf;
|
iov.iov_len = size;
|
iov.iov_len = size;
|
msg.msg_name = NULL;
|
msg.msg_name = NULL;
|
msg.msg_iov = &iov;
|
msg.msg_iov = &iov;
|
msg.msg_iovlen = 1;
|
msg.msg_iovlen = 1;
|
msg.msg_control = NULL;
|
msg.msg_control = NULL;
|
msg.msg_namelen = 0;
|
msg.msg_namelen = 0;
|
result = sock->ops->recvmsg(sock, &msg, size, O_NONBLOCK, 0, &msg.msg_namelen);
|
result = sock->ops->recvmsg(sock, &msg, size, O_NONBLOCK, 0, &msg.msg_namelen);
|
set_fs(oldfs);
|
set_fs(oldfs);
|
return result;
|
return result;
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Initialize BOOTP extension fields in the request.
|
* Initialize BOOTP extension fields in the request.
|
*/
|
*/
|
static void root_bootp_init_ext(u8 *e)
|
static void root_bootp_init_ext(u8 *e)
|
{
|
{
|
*e++ = 99; /* RFC1048 Magic Cookie */
|
*e++ = 99; /* RFC1048 Magic Cookie */
|
*e++ = 130;
|
*e++ = 130;
|
*e++ = 83;
|
*e++ = 83;
|
*e++ = 99;
|
*e++ = 99;
|
*e++ = 1; /* Subnet mask request */
|
*e++ = 1; /* Subnet mask request */
|
*e++ = 4;
|
*e++ = 4;
|
e += 4;
|
e += 4;
|
*e++ = 3; /* Default gateway request */
|
*e++ = 3; /* Default gateway request */
|
*e++ = 4;
|
*e++ = 4;
|
e += 4;
|
e += 4;
|
*e++ = 12; /* Host name request */
|
*e++ = 12; /* Host name request */
|
*e++ = 32;
|
*e++ = 32;
|
e += 32;
|
e += 32;
|
*e++ = 40; /* NIS Domain name request */
|
*e++ = 40; /* NIS Domain name request */
|
*e++ = 32;
|
*e++ = 32;
|
e += 32;
|
e += 32;
|
*e++ = 17; /* Boot path */
|
*e++ = 17; /* Boot path */
|
*e++ = 32;
|
*e++ = 32;
|
e += 32;
|
e += 32;
|
*e = 255; /* End of the list */
|
*e = 255; /* End of the list */
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Deinitialize the BOOTP mechanism.
|
* Deinitialize the BOOTP mechanism.
|
*/
|
*/
|
static void root_bootp_close(void)
|
static void root_bootp_close(void)
|
{
|
{
|
if (bootp_xmit_fd != -1)
|
if (bootp_xmit_fd != -1)
|
sys_close(bootp_xmit_fd);
|
sys_close(bootp_xmit_fd);
|
if (bootp_recv_fd != -1)
|
if (bootp_recv_fd != -1)
|
sys_close(bootp_recv_fd);
|
sys_close(bootp_recv_fd);
|
root_del_bootp_route();
|
root_del_bootp_route();
|
root_free_bootp();
|
root_free_bootp();
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Initialize the BOOTP mechanism.
|
* Initialize the BOOTP mechanism.
|
*/
|
*/
|
static int root_bootp_open(void)
|
static int root_bootp_open(void)
|
{
|
{
|
struct open_dev *openp;
|
struct open_dev *openp;
|
struct device *dev, *best_dev;
|
struct device *dev, *best_dev;
|
|
|
/*
|
/*
|
* Select the best interface for BOOTP. We try to select a first
|
* Select the best interface for BOOTP. We try to select a first
|
* Ethernet-like interface. It's shame I know no simple way how to send
|
* Ethernet-like interface. It's shame I know no simple way how to send
|
* BOOTP's to all interfaces, but it doesn't apply to usual diskless
|
* BOOTP's to all interfaces, but it doesn't apply to usual diskless
|
* stations as they don't have multiple interfaces.
|
* stations as they don't have multiple interfaces.
|
*/
|
*/
|
|
|
best_dev = NULL;
|
best_dev = NULL;
|
for (openp = open_base; openp != NULL; openp = openp->next) {
|
for (openp = open_base; openp != NULL; openp = openp->next) {
|
dev = openp->dev;
|
dev = openp->dev;
|
if (dev->flags & IFF_BROADCAST) {
|
if (dev->flags & IFF_BROADCAST) {
|
if (!best_dev ||
|
if (!best_dev ||
|
((best_dev->flags & IFF_NOARP) && !(dev->flags & IFF_NOARP)))
|
((best_dev->flags & IFF_NOARP) && !(dev->flags & IFF_NOARP)))
|
best_dev = dev;
|
best_dev = dev;
|
}
|
}
|
}
|
}
|
|
|
if (!best_dev) {
|
if (!best_dev) {
|
printk(KERN_ERR "BOOTP: This cannot happen!\n");
|
printk(KERN_ERR "BOOTP: This cannot happen!\n");
|
return -1;
|
return -1;
|
}
|
}
|
bootp_dev = best_dev;
|
bootp_dev = best_dev;
|
|
|
/* Allocate memory for BOOTP packets */
|
/* Allocate memory for BOOTP packets */
|
if (root_alloc_bootp())
|
if (root_alloc_bootp())
|
return -1;
|
return -1;
|
|
|
/* Construct BOOTP request */
|
/* Construct BOOTP request */
|
memset(xmit_bootp, 0, sizeof(struct bootp_pkt));
|
memset(xmit_bootp, 0, sizeof(struct bootp_pkt));
|
xmit_bootp->op = BOOTP_REQUEST;
|
xmit_bootp->op = BOOTP_REQUEST;
|
get_random_bytes(&xmit_bootp->xid, sizeof(xmit_bootp->xid));
|
get_random_bytes(&xmit_bootp->xid, sizeof(xmit_bootp->xid));
|
xmit_bootp->htype = best_dev->type;
|
xmit_bootp->htype = best_dev->type;
|
xmit_bootp->hlen = best_dev->addr_len;
|
xmit_bootp->hlen = best_dev->addr_len;
|
memcpy(xmit_bootp->hw_addr, best_dev->dev_addr, best_dev->addr_len);
|
memcpy(xmit_bootp->hw_addr, best_dev->dev_addr, best_dev->addr_len);
|
root_bootp_init_ext(xmit_bootp->vendor_area);
|
root_bootp_init_ext(xmit_bootp->vendor_area);
|
#ifdef NFSROOT_BOOTP_DEBUG
|
#ifdef NFSROOT_BOOTP_DEBUG
|
{
|
{
|
int x;
|
int x;
|
printk(KERN_NOTICE "BOOTP: XID=%08x, DE=%s, HT=%02x, HL=%02x, HA=",
|
printk(KERN_NOTICE "BOOTP: XID=%08x, DE=%s, HT=%02x, HL=%02x, HA=",
|
xmit_bootp->xid,
|
xmit_bootp->xid,
|
best_dev->name,
|
best_dev->name,
|
xmit_bootp->htype,
|
xmit_bootp->htype,
|
xmit_bootp->hlen);
|
xmit_bootp->hlen);
|
for(x=0; x<xmit_bootp->hlen; x++)
|
for(x=0; x<xmit_bootp->hlen; x++)
|
printk("%02x", xmit_bootp->hw_addr[x]);
|
printk("%02x", xmit_bootp->hw_addr[x]);
|
printk("\n");
|
printk("\n");
|
}
|
}
|
#endif
|
#endif
|
|
|
/* Create default route to that interface */
|
/* Create default route to that interface */
|
if (root_add_bootp_route())
|
if (root_add_bootp_route())
|
return -1;
|
return -1;
|
|
|
/* Open the sockets */
|
/* Open the sockets */
|
if (root_open_udp_sock(&bootp_xmit_fd, &bootp_xmit_sock) ||
|
if (root_open_udp_sock(&bootp_xmit_fd, &bootp_xmit_sock) ||
|
root_open_udp_sock(&bootp_recv_fd, &bootp_recv_sock))
|
root_open_udp_sock(&bootp_recv_fd, &bootp_recv_sock))
|
return -1;
|
return -1;
|
|
|
/* Bind/connect the sockets */
|
/* Bind/connect the sockets */
|
((struct sock *) bootp_xmit_sock->data) -> broadcast = 1;
|
((struct sock *) bootp_xmit_sock->data) -> broadcast = 1;
|
((struct sock *) bootp_xmit_sock->data) -> reuse = 1;
|
((struct sock *) bootp_xmit_sock->data) -> reuse = 1;
|
((struct sock *) bootp_recv_sock->data) -> reuse = 1;
|
((struct sock *) bootp_recv_sock->data) -> reuse = 1;
|
if (root_bind_udp_sock(bootp_recv_sock, INADDR_ANY, 68) ||
|
if (root_bind_udp_sock(bootp_recv_sock, INADDR_ANY, 68) ||
|
root_bind_udp_sock(bootp_xmit_sock, INADDR_ANY, 68) ||
|
root_bind_udp_sock(bootp_xmit_sock, INADDR_ANY, 68) ||
|
root_connect_udp_sock(bootp_xmit_sock, INADDR_BROADCAST, 67))
|
root_connect_udp_sock(bootp_xmit_sock, INADDR_BROADCAST, 67))
|
return -1;
|
return -1;
|
|
|
return 0;
|
return 0;
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Send BOOTP request.
|
* Send BOOTP request.
|
*/
|
*/
|
static int root_bootp_send(u32 jiffies)
|
static int root_bootp_send(u32 jiffies)
|
{
|
{
|
xmit_bootp->secs = htons(jiffies / HZ);
|
xmit_bootp->secs = htons(jiffies / HZ);
|
return root_send_udp(bootp_xmit_sock, xmit_bootp, sizeof(struct bootp_pkt));
|
return root_send_udp(bootp_xmit_sock, xmit_bootp, sizeof(struct bootp_pkt));
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Copy BOOTP-supplied string if not already set.
|
* Copy BOOTP-supplied string if not already set.
|
*/
|
*/
|
static int root_bootp_string(char *dest, char *src, int len, int max)
|
static int root_bootp_string(char *dest, char *src, int len, int max)
|
{
|
{
|
if (*dest || !len)
|
if (*dest || !len)
|
return 0;
|
return 0;
|
if (len > max-1)
|
if (len > max-1)
|
len = max-1;
|
len = max-1;
|
strncpy(dest, src, len);
|
strncpy(dest, src, len);
|
dest[len] = '\0';
|
dest[len] = '\0';
|
return 1;
|
return 1;
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Process BOOTP extension.
|
* Process BOOTP extension.
|
*/
|
*/
|
static void root_do_bootp_ext(u8 *ext)
|
static void root_do_bootp_ext(u8 *ext)
|
{
|
{
|
#ifdef NFSROOT_BOOTP_DEBUG
|
#ifdef NFSROOT_BOOTP_DEBUG
|
u8 *c;
|
u8 *c;
|
|
|
printk("BOOTP: Got extension %02x",*ext);
|
printk("BOOTP: Got extension %02x",*ext);
|
for(c=ext+2; c<ext+2+ext[1]; c++)
|
for(c=ext+2; c<ext+2+ext[1]; c++)
|
printk(" %02x", *c);
|
printk(" %02x", *c);
|
printk("\n");
|
printk("\n");
|
#endif
|
#endif
|
|
|
switch (*ext++) {
|
switch (*ext++) {
|
case 1: /* Subnet mask */
|
case 1: /* Subnet mask */
|
if (netmask.sin_addr.s_addr == INADDR_NONE)
|
if (netmask.sin_addr.s_addr == INADDR_NONE)
|
memcpy(&netmask.sin_addr.s_addr, ext+1, 4);
|
memcpy(&netmask.sin_addr.s_addr, ext+1, 4);
|
break;
|
break;
|
case 3: /* Default gateway */
|
case 3: /* Default gateway */
|
if (gateway.sin_addr.s_addr == INADDR_NONE)
|
if (gateway.sin_addr.s_addr == INADDR_NONE)
|
memcpy(&gateway.sin_addr.s_addr, ext+1, 4);
|
memcpy(&gateway.sin_addr.s_addr, ext+1, 4);
|
break;
|
break;
|
case 12: /* Host name */
|
case 12: /* Host name */
|
root_bootp_string(system_utsname.nodename, ext+1, *ext, __NEW_UTS_LEN);
|
root_bootp_string(system_utsname.nodename, ext+1, *ext, __NEW_UTS_LEN);
|
break;
|
break;
|
case 40: /* NIS Domain name */
|
case 40: /* NIS Domain name */
|
root_bootp_string(system_utsname.domainname, ext+1, *ext, __NEW_UTS_LEN);
|
root_bootp_string(system_utsname.domainname, ext+1, *ext, __NEW_UTS_LEN);
|
break;
|
break;
|
case 17: /* Root path */
|
case 17: /* Root path */
|
root_bootp_string(nfs_path, ext+1, *ext, NFS_MAXPATHLEN);
|
root_bootp_string(nfs_path, ext+1, *ext, NFS_MAXPATHLEN);
|
break;
|
break;
|
}
|
}
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Receive BOOTP request.
|
* Receive BOOTP request.
|
*/
|
*/
|
static void root_bootp_recv(void)
|
static void root_bootp_recv(void)
|
{
|
{
|
int len;
|
int len;
|
u8 *ext, *end, *opt;
|
u8 *ext, *end, *opt;
|
|
|
len = root_recv_udp(bootp_recv_sock, recv_bootp, sizeof(struct bootp_pkt));
|
len = root_recv_udp(bootp_recv_sock, recv_bootp, sizeof(struct bootp_pkt));
|
if (len < 0)
|
if (len < 0)
|
return;
|
return;
|
|
|
/* Check consistency of incoming packet */
|
/* Check consistency of incoming packet */
|
if (len < 300 || /* See RFC 1542:2.1 */
|
if (len < 300 || /* See RFC 1542:2.1 */
|
recv_bootp->op != BOOTP_REPLY ||
|
recv_bootp->op != BOOTP_REPLY ||
|
recv_bootp->htype != xmit_bootp->htype ||
|
recv_bootp->htype != xmit_bootp->htype ||
|
recv_bootp->hlen != xmit_bootp->hlen ||
|
recv_bootp->hlen != xmit_bootp->hlen ||
|
recv_bootp->xid != xmit_bootp->xid) {
|
recv_bootp->xid != xmit_bootp->xid) {
|
#ifdef NFSROOT_BOOTP_DEBUG
|
#ifdef NFSROOT_BOOTP_DEBUG
|
printk("?");
|
printk("?");
|
#endif
|
#endif
|
return;
|
return;
|
}
|
}
|
|
|
/* Record BOOTP packet arrival in the global variables */
|
/* Record BOOTP packet arrival in the global variables */
|
cli();
|
cli();
|
if (pkt_arrived) {
|
if (pkt_arrived) {
|
sti();
|
sti();
|
return;
|
return;
|
}
|
}
|
pkt_arrived = ARRIVED_BOOTP;
|
pkt_arrived = ARRIVED_BOOTP;
|
sti();
|
sti();
|
root_dev = bootp_dev;
|
root_dev = bootp_dev;
|
|
|
/* Extract basic fields */
|
/* Extract basic fields */
|
myaddr.sin_addr.s_addr = recv_bootp->your_ip;
|
myaddr.sin_addr.s_addr = recv_bootp->your_ip;
|
if (server.sin_addr.s_addr==INADDR_NONE)
|
if (server.sin_addr.s_addr==INADDR_NONE)
|
server.sin_addr.s_addr = recv_bootp->server_ip;
|
server.sin_addr.s_addr = recv_bootp->server_ip;
|
|
|
/* Parse extensions */
|
/* Parse extensions */
|
if (recv_bootp->vendor_area[0] == 99 && /* Check magic cookie */
|
if (recv_bootp->vendor_area[0] == 99 && /* Check magic cookie */
|
recv_bootp->vendor_area[1] == 130 &&
|
recv_bootp->vendor_area[1] == 130 &&
|
recv_bootp->vendor_area[2] == 83 &&
|
recv_bootp->vendor_area[2] == 83 &&
|
recv_bootp->vendor_area[3] == 99) {
|
recv_bootp->vendor_area[3] == 99) {
|
ext = &recv_bootp->vendor_area[4];
|
ext = &recv_bootp->vendor_area[4];
|
end = (u8 *) recv_bootp + len;
|
end = (u8 *) recv_bootp + len;
|
while (ext < end && *ext != 255) {
|
while (ext < end && *ext != 255) {
|
if (*ext == 0) /* Padding */
|
if (*ext == 0) /* Padding */
|
ext++;
|
ext++;
|
else {
|
else {
|
opt = ext;
|
opt = ext;
|
ext += ext[1] + 2;
|
ext += ext[1] + 2;
|
if (ext <= end)
|
if (ext <= end)
|
root_do_bootp_ext(opt);
|
root_do_bootp_ext(opt);
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
#endif
|
#endif
|
|
|
|
|
|
|
/***************************************************************************
|
/***************************************************************************
|
|
|
Dynamic configuration of IP.
|
Dynamic configuration of IP.
|
|
|
***************************************************************************/
|
***************************************************************************/
|
|
|
#ifdef CONFIG_RNFS_DYNAMIC
|
#ifdef CONFIG_RNFS_DYNAMIC
|
|
|
/*
|
/*
|
* Determine client and server IP numbers and appropriate device by using
|
* Determine client and server IP numbers and appropriate device by using
|
* the RARP and BOOTP protocols.
|
* the RARP and BOOTP protocols.
|
*/
|
*/
|
static int root_auto_config(void)
|
static int root_auto_config(void)
|
{
|
{
|
int retries;
|
int retries;
|
unsigned long timeout, jiff;
|
unsigned long timeout, jiff;
|
unsigned long start_jiffies;
|
unsigned long start_jiffies;
|
|
|
/*
|
/*
|
* If neither BOOTP nor RARP was selected, return with an error. This
|
* If neither BOOTP nor RARP was selected, return with an error. This
|
* routine gets only called when some pieces of information are mis-
|
* routine gets only called when some pieces of information are mis-
|
* sing, and without BOOTP and RARP we are not able to get that in-
|
* sing, and without BOOTP and RARP we are not able to get that in-
|
* formation.
|
* formation.
|
*/
|
*/
|
if (!bootp_flag && !rarp_flag) {
|
if (!bootp_flag && !rarp_flag) {
|
printk(KERN_ERR "Root-NFS: Neither RARP nor BOOTP selected.\n");
|
printk(KERN_ERR "Root-NFS: Neither RARP nor BOOTP selected.\n");
|
return -1;
|
return -1;
|
}
|
}
|
|
|
#ifdef CONFIG_RNFS_BOOTP
|
#ifdef CONFIG_RNFS_BOOTP
|
if (bootp_flag && !bootp_dev_count) {
|
if (bootp_flag && !bootp_dev_count) {
|
printk(KERN_ERR "Root-NFS: No suitable device for BOOTP found.\n");
|
printk(KERN_ERR "Root-NFS: No suitable device for BOOTP found.\n");
|
bootp_flag = 0;
|
bootp_flag = 0;
|
}
|
}
|
#else
|
#else
|
bootp_flag = 0;
|
bootp_flag = 0;
|
#endif
|
#endif
|
|
|
#ifdef CONFIG_RNFS_RARP
|
#ifdef CONFIG_RNFS_RARP
|
if (rarp_flag && !rarp_dev_count) {
|
if (rarp_flag && !rarp_dev_count) {
|
printk(KERN_ERR "Root-NFS: No suitable device for RARP found.\n");
|
printk(KERN_ERR "Root-NFS: No suitable device for RARP found.\n");
|
rarp_flag = 0;
|
rarp_flag = 0;
|
}
|
}
|
#else
|
#else
|
rarp_flag = 0;
|
rarp_flag = 0;
|
#endif
|
#endif
|
|
|
if (!bootp_flag && !rarp_flag)
|
if (!bootp_flag && !rarp_flag)
|
/* Error message already printed */
|
/* Error message already printed */
|
return -1;
|
return -1;
|
|
|
/*
|
/*
|
* Setup RARP and BOOTP protocols
|
* Setup RARP and BOOTP protocols
|
*/
|
*/
|
#ifdef CONFIG_RNFS_RARP
|
#ifdef CONFIG_RNFS_RARP
|
if (rarp_flag)
|
if (rarp_flag)
|
root_rarp_open();
|
root_rarp_open();
|
#endif
|
#endif
|
#ifdef CONFIG_RNFS_BOOTP
|
#ifdef CONFIG_RNFS_BOOTP
|
if (bootp_flag && root_bootp_open() < 0) {
|
if (bootp_flag && root_bootp_open() < 0) {
|
root_bootp_close();
|
root_bootp_close();
|
return -1;
|
return -1;
|
}
|
}
|
#endif
|
#endif
|
|
|
/*
|
/*
|
* Send requests and wait, until we get an answer. This loop
|
* Send requests and wait, until we get an answer. This loop
|
* seems to be a terrible waste of CPU time, but actually there is
|
* seems to be a terrible waste of CPU time, but actually there is
|
* only one process running at all, so we don't need to use any
|
* only one process running at all, so we don't need to use any
|
* scheduler functions.
|
* scheduler functions.
|
* [Actually we could now, but the nothing else running note still
|
* [Actually we could now, but the nothing else running note still
|
* applies.. - AC]
|
* applies.. - AC]
|
*/
|
*/
|
printk(KERN_NOTICE "Sending %s%s%s requests...",
|
printk(KERN_NOTICE "Sending %s%s%s requests...",
|
bootp_flag ? "BOOTP" : "",
|
bootp_flag ? "BOOTP" : "",
|
bootp_flag && rarp_flag ? " and " : "",
|
bootp_flag && rarp_flag ? " and " : "",
|
rarp_flag ? "RARP" : "");
|
rarp_flag ? "RARP" : "");
|
start_jiffies = jiffies;
|
start_jiffies = jiffies;
|
retries = CONF_RETRIES;
|
retries = CONF_RETRIES;
|
get_random_bytes(&timeout, sizeof(timeout));
|
get_random_bytes(&timeout, sizeof(timeout));
|
timeout = CONF_BASE_TIMEOUT + (timeout % (unsigned) CONF_TIMEOUT_RANDOM);
|
timeout = CONF_BASE_TIMEOUT + (timeout % (unsigned) CONF_TIMEOUT_RANDOM);
|
for(;;) {
|
for(;;) {
|
#ifdef CONFIG_RNFS_BOOTP
|
#ifdef CONFIG_RNFS_BOOTP
|
if (bootp_flag && root_bootp_send(jiffies - start_jiffies) < 0) {
|
if (bootp_flag && root_bootp_send(jiffies - start_jiffies) < 0) {
|
printk(" BOOTP failed!\n");
|
printk(" BOOTP failed!\n");
|
root_bootp_close();
|
root_bootp_close();
|
bootp_flag = 0;
|
bootp_flag = 0;
|
if (!rarp_flag)
|
if (!rarp_flag)
|
break;
|
break;
|
}
|
}
|
#endif
|
#endif
|
#ifdef CONFIG_RNFS_RARP
|
#ifdef CONFIG_RNFS_RARP
|
if (rarp_flag)
|
if (rarp_flag)
|
root_rarp_send();
|
root_rarp_send();
|
#endif
|
#endif
|
printk(".");
|
printk(".");
|
jiff = jiffies + timeout;
|
jiff = jiffies + timeout;
|
while (jiffies < jiff && !pkt_arrived)
|
while (jiffies < jiff && !pkt_arrived)
|
#ifdef CONFIG_RNFS_BOOTP
|
#ifdef CONFIG_RNFS_BOOTP
|
root_bootp_recv();
|
root_bootp_recv();
|
#else
|
#else
|
;
|
;
|
#endif
|
#endif
|
if (pkt_arrived)
|
if (pkt_arrived)
|
break;
|
break;
|
if (! --retries) {
|
if (! --retries) {
|
printk(" timed out!\n");
|
printk(" timed out!\n");
|
break;
|
break;
|
}
|
}
|
timeout = timeout CONF_TIMEOUT_MULT;
|
timeout = timeout CONF_TIMEOUT_MULT;
|
if (timeout > CONF_TIMEOUT_MAX)
|
if (timeout > CONF_TIMEOUT_MAX)
|
timeout = CONF_TIMEOUT_MAX;
|
timeout = CONF_TIMEOUT_MAX;
|
}
|
}
|
|
|
#ifdef CONFIG_RNFS_RARP
|
#ifdef CONFIG_RNFS_RARP
|
if (rarp_flag)
|
if (rarp_flag)
|
root_rarp_close();
|
root_rarp_close();
|
#endif
|
#endif
|
#ifdef CONFIG_RNFS_BOOTP
|
#ifdef CONFIG_RNFS_BOOTP
|
if (bootp_flag)
|
if (bootp_flag)
|
root_bootp_close();
|
root_bootp_close();
|
#endif
|
#endif
|
|
|
if (!pkt_arrived)
|
if (!pkt_arrived)
|
return -1;
|
return -1;
|
|
|
printk(" OK\n");
|
printk(" OK\n");
|
printk(KERN_NOTICE "Root-NFS: Got %s answer from %s, ",
|
printk(KERN_NOTICE "Root-NFS: Got %s answer from %s, ",
|
(pkt_arrived == ARRIVED_BOOTP) ? "BOOTP" : "RARP",
|
(pkt_arrived == ARRIVED_BOOTP) ? "BOOTP" : "RARP",
|
in_ntoa(server.sin_addr.s_addr));
|
in_ntoa(server.sin_addr.s_addr));
|
printk("my address is %s\n", in_ntoa(myaddr.sin_addr.s_addr));
|
printk("my address is %s\n", in_ntoa(myaddr.sin_addr.s_addr));
|
|
|
return 0;
|
return 0;
|
}
|
}
|
#endif
|
#endif
|
|
|
|
|
|
|
/***************************************************************************
|
/***************************************************************************
|
|
|
Parsing of options
|
Parsing of options
|
|
|
***************************************************************************/
|
***************************************************************************/
|
|
|
|
|
/*
|
/*
|
* The following integer options are recognized
|
* The following integer options are recognized
|
*/
|
*/
|
static struct nfs_int_opts {
|
static struct nfs_int_opts {
|
char *name;
|
char *name;
|
int *val;
|
int *val;
|
} root_int_opts[] = {
|
} root_int_opts[] = {
|
{ "port", &nfs_port },
|
{ "port", &nfs_port },
|
{ "rsize", &nfs_data.rsize },
|
{ "rsize", &nfs_data.rsize },
|
{ "wsize", &nfs_data.wsize },
|
{ "wsize", &nfs_data.wsize },
|
{ "timeo", &nfs_data.timeo },
|
{ "timeo", &nfs_data.timeo },
|
{ "retrans", &nfs_data.retrans },
|
{ "retrans", &nfs_data.retrans },
|
{ "acregmin", &nfs_data.acregmin },
|
{ "acregmin", &nfs_data.acregmin },
|
{ "acregmax", &nfs_data.acregmax },
|
{ "acregmax", &nfs_data.acregmax },
|
{ "acdirmin", &nfs_data.acdirmin },
|
{ "acdirmin", &nfs_data.acdirmin },
|
{ "acdirmax", &nfs_data.acdirmax },
|
{ "acdirmax", &nfs_data.acdirmax },
|
{ NULL, NULL }
|
{ NULL, NULL }
|
};
|
};
|
|
|
|
|
/*
|
/*
|
* And now the flag options
|
* And now the flag options
|
*/
|
*/
|
static struct nfs_bool_opts {
|
static struct nfs_bool_opts {
|
char *name;
|
char *name;
|
int and_mask;
|
int and_mask;
|
int or_mask;
|
int or_mask;
|
} root_bool_opts[] = {
|
} root_bool_opts[] = {
|
{ "soft", ~NFS_MOUNT_SOFT, NFS_MOUNT_SOFT },
|
{ "soft", ~NFS_MOUNT_SOFT, NFS_MOUNT_SOFT },
|
{ "hard", ~NFS_MOUNT_SOFT, 0 },
|
{ "hard", ~NFS_MOUNT_SOFT, 0 },
|
{ "intr", ~NFS_MOUNT_INTR, NFS_MOUNT_INTR },
|
{ "intr", ~NFS_MOUNT_INTR, NFS_MOUNT_INTR },
|
{ "nointr", ~NFS_MOUNT_INTR, 0 },
|
{ "nointr", ~NFS_MOUNT_INTR, 0 },
|
{ "posix", ~NFS_MOUNT_POSIX, NFS_MOUNT_POSIX },
|
{ "posix", ~NFS_MOUNT_POSIX, NFS_MOUNT_POSIX },
|
{ "noposix", ~NFS_MOUNT_POSIX, 0 },
|
{ "noposix", ~NFS_MOUNT_POSIX, 0 },
|
{ "cto", ~NFS_MOUNT_NOCTO, 0 },
|
{ "cto", ~NFS_MOUNT_NOCTO, 0 },
|
{ "nocto", ~NFS_MOUNT_NOCTO, NFS_MOUNT_NOCTO },
|
{ "nocto", ~NFS_MOUNT_NOCTO, NFS_MOUNT_NOCTO },
|
{ "ac", ~NFS_MOUNT_NOAC, 0 },
|
{ "ac", ~NFS_MOUNT_NOAC, 0 },
|
{ "noac", ~NFS_MOUNT_NOAC, NFS_MOUNT_NOAC },
|
{ "noac", ~NFS_MOUNT_NOAC, NFS_MOUNT_NOAC },
|
{ NULL, 0, 0 }
|
{ NULL, 0, 0 }
|
};
|
};
|
|
|
|
|
/*
|
/*
|
* Prepare the NFS data structure and parse any options. This tries to
|
* Prepare the NFS data structure and parse any options. This tries to
|
* set as many values in the nfs_data structure as known right now.
|
* set as many values in the nfs_data structure as known right now.
|
*/
|
*/
|
static int root_nfs_name(char *name)
|
static int root_nfs_name(char *name)
|
{
|
{
|
char buf[NFS_MAXPATHLEN];
|
char buf[NFS_MAXPATHLEN];
|
char *cp, *cq, *options, *val;
|
char *cp, *cq, *options, *val;
|
int octets = 0;
|
int octets = 0;
|
|
|
/* It is possible to override the server IP number here */
|
/* It is possible to override the server IP number here */
|
cp = cq = name;
|
cp = cq = name;
|
while (octets < 4) {
|
while (octets < 4) {
|
while (*cp >= '0' && *cp <= '9')
|
while (*cp >= '0' && *cp <= '9')
|
cp++;
|
cp++;
|
if (cp == cq || cp - cq > 3)
|
if (cp == cq || cp - cq > 3)
|
break;
|
break;
|
if (*cp == '.' || octets == 3)
|
if (*cp == '.' || octets == 3)
|
octets++;
|
octets++;
|
if (octets < 4)
|
if (octets < 4)
|
cp++;
|
cp++;
|
cq = cp;
|
cq = cp;
|
}
|
}
|
if (octets == 4 && (*cp == ':' || *cp == '\0')) {
|
if (octets == 4 && (*cp == ':' || *cp == '\0')) {
|
if (*cp == ':')
|
if (*cp == ':')
|
*cp++ = '\0';
|
*cp++ = '\0';
|
server.sin_addr.s_addr = in_aton(name);
|
server.sin_addr.s_addr = in_aton(name);
|
name = cp;
|
name = cp;
|
}
|
}
|
|
|
/* Clear the nfs_data structure and setup the server hostname */
|
/* Clear the nfs_data structure and setup the server hostname */
|
memset(&nfs_data, 0, sizeof(nfs_data));
|
memset(&nfs_data, 0, sizeof(nfs_data));
|
strncpy(nfs_data.hostname, in_ntoa(server.sin_addr.s_addr),
|
strncpy(nfs_data.hostname, in_ntoa(server.sin_addr.s_addr),
|
sizeof(nfs_data.hostname)-1);
|
sizeof(nfs_data.hostname)-1);
|
|
|
/* Set the name of the directory to mount */
|
/* Set the name of the directory to mount */
|
if (nfs_path[0] == '\0' || strncmp(name, "default", 7))
|
if (nfs_path[0] == '\0' || strncmp(name, "default", 7))
|
strncpy(buf, name, NFS_MAXPATHLEN);
|
strncpy(buf, name, NFS_MAXPATHLEN);
|
else
|
else
|
strncpy(buf, nfs_path, NFS_MAXPATHLEN);
|
strncpy(buf, nfs_path, NFS_MAXPATHLEN);
|
if ((options = strchr(buf, ',')))
|
if ((options = strchr(buf, ',')))
|
*options++ = '\0';
|
*options++ = '\0';
|
if (!strcmp(buf, "default"))
|
if (!strcmp(buf, "default"))
|
strcpy(buf, NFS_ROOT);
|
strcpy(buf, NFS_ROOT);
|
cp = in_ntoa(myaddr.sin_addr.s_addr);
|
cp = in_ntoa(myaddr.sin_addr.s_addr);
|
if (strlen(buf) + strlen(cp) > NFS_MAXPATHLEN) {
|
if (strlen(buf) + strlen(cp) > NFS_MAXPATHLEN) {
|
printk(KERN_ERR "Root-NFS: Pathname for remote directory too long.\n");
|
printk(KERN_ERR "Root-NFS: Pathname for remote directory too long.\n");
|
return -1;
|
return -1;
|
}
|
}
|
/* update nfs_path with path from nfsroot=... command line parameter */
|
/* update nfs_path with path from nfsroot=... command line parameter */
|
if (*buf)
|
if (*buf)
|
sprintf(nfs_path, buf, cp);
|
sprintf(nfs_path, buf, cp);
|
|
|
/* Set some default values */
|
/* Set some default values */
|
nfs_port = -1;
|
nfs_port = -1;
|
nfs_data.version = NFS_MOUNT_VERSION;
|
nfs_data.version = NFS_MOUNT_VERSION;
|
nfs_data.flags = 0;
|
nfs_data.flags = 0;
|
nfs_data.rsize = NFS_DEF_FILE_IO_BUFFER_SIZE;
|
nfs_data.rsize = NFS_DEF_FILE_IO_BUFFER_SIZE;
|
nfs_data.wsize = NFS_DEF_FILE_IO_BUFFER_SIZE;
|
nfs_data.wsize = NFS_DEF_FILE_IO_BUFFER_SIZE;
|
nfs_data.timeo = 7;
|
nfs_data.timeo = 7;
|
nfs_data.retrans = 3;
|
nfs_data.retrans = 3;
|
nfs_data.acregmin = 3;
|
nfs_data.acregmin = 3;
|
nfs_data.acregmax = 60;
|
nfs_data.acregmax = 60;
|
nfs_data.acdirmin = 30;
|
nfs_data.acdirmin = 30;
|
nfs_data.acdirmax = 60;
|
nfs_data.acdirmax = 60;
|
|
|
/* Process any options */
|
/* Process any options */
|
if (options) {
|
if (options) {
|
cp = strtok(options, ",");
|
cp = strtok(options, ",");
|
while (cp) {
|
while (cp) {
|
if ((val = strchr(cp, '='))) {
|
if ((val = strchr(cp, '='))) {
|
struct nfs_int_opts *opts = root_int_opts;
|
struct nfs_int_opts *opts = root_int_opts;
|
*val++ = '\0';
|
*val++ = '\0';
|
while (opts->name && strcmp(opts->name, cp))
|
while (opts->name && strcmp(opts->name, cp))
|
opts++;
|
opts++;
|
if (opts->name)
|
if (opts->name)
|
*(opts->val) = (int) simple_strtoul(val, NULL, 10);
|
*(opts->val) = (int) simple_strtoul(val, NULL, 10);
|
} else {
|
} else {
|
struct nfs_bool_opts *opts = root_bool_opts;
|
struct nfs_bool_opts *opts = root_bool_opts;
|
while (opts->name && strcmp(opts->name, cp))
|
while (opts->name && strcmp(opts->name, cp))
|
opts++;
|
opts++;
|
if (opts->name) {
|
if (opts->name) {
|
nfs_data.flags &= opts->and_mask;
|
nfs_data.flags &= opts->and_mask;
|
nfs_data.flags |= opts->or_mask;
|
nfs_data.flags |= opts->or_mask;
|
}
|
}
|
}
|
}
|
cp = strtok(NULL, ",");
|
cp = strtok(NULL, ",");
|
}
|
}
|
}
|
}
|
return 0;
|
return 0;
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Tell the user what's going on.
|
* Tell the user what's going on.
|
*/
|
*/
|
#ifdef NFSROOT_DEBUG
|
#ifdef NFSROOT_DEBUG
|
static void root_nfs_print(void)
|
static void root_nfs_print(void)
|
{
|
{
|
#define IN_NTOA(x) (((x) == INADDR_NONE) ? "none" : in_ntoa(x))
|
#define IN_NTOA(x) (((x) == INADDR_NONE) ? "none" : in_ntoa(x))
|
|
|
printk(KERN_NOTICE "Root-NFS: IP config: dev=%s, ",
|
printk(KERN_NOTICE "Root-NFS: IP config: dev=%s, ",
|
root_dev ? root_dev->name : "none");
|
root_dev ? root_dev->name : "none");
|
printk("local=%s, ", IN_NTOA(myaddr.sin_addr.s_addr));
|
printk("local=%s, ", IN_NTOA(myaddr.sin_addr.s_addr));
|
printk("server=%s, ", IN_NTOA(server.sin_addr.s_addr));
|
printk("server=%s, ", IN_NTOA(server.sin_addr.s_addr));
|
printk("gw=%s, ", IN_NTOA(gateway.sin_addr.s_addr));
|
printk("gw=%s, ", IN_NTOA(gateway.sin_addr.s_addr));
|
printk("mask=%s, ", IN_NTOA(netmask.sin_addr.s_addr));
|
printk("mask=%s, ", IN_NTOA(netmask.sin_addr.s_addr));
|
printk("host=%s, domain=%s\n",
|
printk("host=%s, domain=%s\n",
|
system_utsname.nodename[0] ? system_utsname.nodename : "none",
|
system_utsname.nodename[0] ? system_utsname.nodename : "none",
|
system_utsname.domainname[0] ? system_utsname.domainname : "none");
|
system_utsname.domainname[0] ? system_utsname.domainname : "none");
|
printk(KERN_NOTICE "Root-NFS: Mounting %s on server %s as root\n",
|
printk(KERN_NOTICE "Root-NFS: Mounting %s on server %s as root\n",
|
nfs_path, nfs_data.hostname);
|
nfs_path, nfs_data.hostname);
|
printk(KERN_NOTICE "Root-NFS: rsize = %d, wsize = %d, timeo = %d, retrans = %d\n",
|
printk(KERN_NOTICE "Root-NFS: rsize = %d, wsize = %d, timeo = %d, retrans = %d\n",
|
nfs_data.rsize, nfs_data.wsize, nfs_data.timeo, nfs_data.retrans);
|
nfs_data.rsize, nfs_data.wsize, nfs_data.timeo, nfs_data.retrans);
|
printk(KERN_NOTICE "Root-NFS: acreg (min,max) = (%d,%d), acdir (min,max) = (%d,%d)\n",
|
printk(KERN_NOTICE "Root-NFS: acreg (min,max) = (%d,%d), acdir (min,max) = (%d,%d)\n",
|
nfs_data.acregmin, nfs_data.acregmax,
|
nfs_data.acregmin, nfs_data.acregmax,
|
nfs_data.acdirmin, nfs_data.acdirmax);
|
nfs_data.acdirmin, nfs_data.acdirmax);
|
printk(KERN_NOTICE "Root-NFS: port = %d, flags = %08x\n",
|
printk(KERN_NOTICE "Root-NFS: port = %d, flags = %08x\n",
|
nfs_port, nfs_data.flags);
|
nfs_port, nfs_data.flags);
|
|
|
#undef IN_NTOA
|
#undef IN_NTOA
|
}
|
}
|
#endif
|
#endif
|
|
|
|
|
/*
|
/*
|
* Decode any IP configuration options in the "nfsaddrs" kernel command
|
* Decode any IP configuration options in the "nfsaddrs" kernel command
|
* line parameter. It consists of option fields separated by colons in
|
* line parameter. It consists of option fields separated by colons in
|
* the following order:
|
* the following order:
|
*
|
*
|
* <client-ip>:<server-ip>:<gw-ip>:<netmask>:<host name>:<device>:<bootp|rarp>
|
* <client-ip>:<server-ip>:<gw-ip>:<netmask>:<host name>:<device>:<bootp|rarp>
|
*
|
*
|
* Any of the fields can be empty which means to use a default value:
|
* Any of the fields can be empty which means to use a default value:
|
* <client-ip> - address given by BOOTP or RARP
|
* <client-ip> - address given by BOOTP or RARP
|
* <server-ip> - address of host returning BOOTP or RARP packet
|
* <server-ip> - address of host returning BOOTP or RARP packet
|
* <gw-ip> - none, or the address returned by BOOTP
|
* <gw-ip> - none, or the address returned by BOOTP
|
* <netmask> - automatically determined from <client-ip>, or the
|
* <netmask> - automatically determined from <client-ip>, or the
|
* one returned by BOOTP
|
* one returned by BOOTP
|
* <host name> - <client-ip> in ASCII notation, or the name returned
|
* <host name> - <client-ip> in ASCII notation, or the name returned
|
* by BOOTP
|
* by BOOTP
|
* <device> - use all available devices for RARP and the first
|
* <device> - use all available devices for RARP and the first
|
* one for BOOTP
|
* one for BOOTP
|
* <bootp|rarp> - use both protocols to determine my own address
|
* <bootp|rarp> - use both protocols to determine my own address
|
*/
|
*/
|
static void root_nfs_addrs(char *addrs)
|
static void root_nfs_addrs(char *addrs)
|
{
|
{
|
char *cp, *ip, *dp;
|
char *cp, *ip, *dp;
|
int num = 0;
|
int num = 0;
|
|
|
/* Clear all addresses and strings */
|
/* Clear all addresses and strings */
|
myaddr.sin_family = server.sin_family = rarp_serv.sin_family =
|
myaddr.sin_family = server.sin_family = rarp_serv.sin_family =
|
gateway.sin_family = netmask.sin_family = AF_INET;
|
gateway.sin_family = netmask.sin_family = AF_INET;
|
myaddr.sin_addr.s_addr = server.sin_addr.s_addr = rarp_serv.sin_addr.s_addr =
|
myaddr.sin_addr.s_addr = server.sin_addr.s_addr = rarp_serv.sin_addr.s_addr =
|
gateway.sin_addr.s_addr = netmask.sin_addr.s_addr = INADDR_NONE;
|
gateway.sin_addr.s_addr = netmask.sin_addr.s_addr = INADDR_NONE;
|
system_utsname.nodename[0] = '\0';
|
system_utsname.nodename[0] = '\0';
|
system_utsname.domainname[0] = '\0';
|
system_utsname.domainname[0] = '\0';
|
user_dev_name[0] = '\0';
|
user_dev_name[0] = '\0';
|
bootp_flag = rarp_flag = 1;
|
bootp_flag = rarp_flag = 1;
|
|
|
/* The following is just a shortcut for automatic IP configuration */
|
/* The following is just a shortcut for automatic IP configuration */
|
if (!strcmp(addrs, "bootp")) {
|
if (!strcmp(addrs, "bootp")) {
|
rarp_flag = 0;
|
rarp_flag = 0;
|
return;
|
return;
|
} else if (!strcmp(addrs, "rarp")) {
|
} else if (!strcmp(addrs, "rarp")) {
|
bootp_flag = 0;
|
bootp_flag = 0;
|
return;
|
return;
|
} else if (!strcmp(addrs, "both")) {
|
} else if (!strcmp(addrs, "both")) {
|
return;
|
return;
|
}
|
}
|
|
|
/* Parse the whole string */
|
/* Parse the whole string */
|
ip = addrs;
|
ip = addrs;
|
while (ip && *ip) {
|
while (ip && *ip) {
|
if ((cp = strchr(ip, ':')))
|
if ((cp = strchr(ip, ':')))
|
*cp++ = '\0';
|
*cp++ = '\0';
|
if (strlen(ip) > 0) {
|
if (strlen(ip) > 0) {
|
#ifdef NFSROOT_DEBUG
|
#ifdef NFSROOT_DEBUG
|
printk(KERN_NOTICE "Root-NFS: Config string num %d is \"%s\"\n",
|
printk(KERN_NOTICE "Root-NFS: Config string num %d is \"%s\"\n",
|
num, ip);
|
num, ip);
|
#endif
|
#endif
|
switch (num) {
|
switch (num) {
|
case 0:
|
case 0:
|
if ((myaddr.sin_addr.s_addr = in_aton(ip)) == INADDR_ANY)
|
if ((myaddr.sin_addr.s_addr = in_aton(ip)) == INADDR_ANY)
|
myaddr.sin_addr.s_addr = INADDR_NONE;
|
myaddr.sin_addr.s_addr = INADDR_NONE;
|
break;
|
break;
|
case 1:
|
case 1:
|
if ((server.sin_addr.s_addr = in_aton(ip)) == INADDR_ANY)
|
if ((server.sin_addr.s_addr = in_aton(ip)) == INADDR_ANY)
|
server.sin_addr.s_addr = INADDR_NONE;
|
server.sin_addr.s_addr = INADDR_NONE;
|
break;
|
break;
|
case 2:
|
case 2:
|
if ((gateway.sin_addr.s_addr = in_aton(ip)) == INADDR_ANY)
|
if ((gateway.sin_addr.s_addr = in_aton(ip)) == INADDR_ANY)
|
gateway.sin_addr.s_addr = INADDR_NONE;
|
gateway.sin_addr.s_addr = INADDR_NONE;
|
break;
|
break;
|
case 3:
|
case 3:
|
if ((netmask.sin_addr.s_addr = in_aton(ip)) == INADDR_ANY)
|
if ((netmask.sin_addr.s_addr = in_aton(ip)) == INADDR_ANY)
|
netmask.sin_addr.s_addr = INADDR_NONE;
|
netmask.sin_addr.s_addr = INADDR_NONE;
|
break;
|
break;
|
case 4:
|
case 4:
|
if ((dp = strchr(ip, '.'))) {
|
if ((dp = strchr(ip, '.'))) {
|
*dp++ = '\0';
|
*dp++ = '\0';
|
strncpy(system_utsname.domainname, dp, __NEW_UTS_LEN);
|
strncpy(system_utsname.domainname, dp, __NEW_UTS_LEN);
|
system_utsname.domainname[__NEW_UTS_LEN] = '\0';
|
system_utsname.domainname[__NEW_UTS_LEN] = '\0';
|
}
|
}
|
strncpy(system_utsname.nodename, ip, __NEW_UTS_LEN);
|
strncpy(system_utsname.nodename, ip, __NEW_UTS_LEN);
|
system_utsname.nodename[__NEW_UTS_LEN] = '\0';
|
system_utsname.nodename[__NEW_UTS_LEN] = '\0';
|
break;
|
break;
|
case 5:
|
case 5:
|
strncpy(user_dev_name, ip, IFNAMSIZ);
|
strncpy(user_dev_name, ip, IFNAMSIZ);
|
user_dev_name[IFNAMSIZ-1] = '\0';
|
user_dev_name[IFNAMSIZ-1] = '\0';
|
break;
|
break;
|
case 6:
|
case 6:
|
if (!strcmp(ip, "rarp"))
|
if (!strcmp(ip, "rarp"))
|
bootp_flag = 0;
|
bootp_flag = 0;
|
else if (!strcmp(ip, "bootp"))
|
else if (!strcmp(ip, "bootp"))
|
rarp_flag = 0;
|
rarp_flag = 0;
|
else if (strcmp(ip, "both"))
|
else if (strcmp(ip, "both"))
|
bootp_flag = rarp_flag = 0;
|
bootp_flag = rarp_flag = 0;
|
break;
|
break;
|
default:
|
default:
|
break;
|
break;
|
}
|
}
|
}
|
}
|
ip = cp;
|
ip = cp;
|
num++;
|
num++;
|
}
|
}
|
rarp_serv = server;
|
rarp_serv = server;
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Set the interface address and configure a route to the server.
|
* Set the interface address and configure a route to the server.
|
*/
|
*/
|
static int root_nfs_setup(void)
|
static int root_nfs_setup(void)
|
{
|
{
|
struct rtentry route;
|
struct rtentry route;
|
|
|
/* Set the default system name in case none was previously found */
|
/* Set the default system name in case none was previously found */
|
if (!system_utsname.nodename[0]) {
|
if (!system_utsname.nodename[0]) {
|
strncpy(system_utsname.nodename, in_ntoa(myaddr.sin_addr.s_addr), __NEW_UTS_LEN);
|
strncpy(system_utsname.nodename, in_ntoa(myaddr.sin_addr.s_addr), __NEW_UTS_LEN);
|
system_utsname.nodename[__NEW_UTS_LEN] = '\0';
|
system_utsname.nodename[__NEW_UTS_LEN] = '\0';
|
}
|
}
|
|
|
/* Set the correct netmask */
|
/* Set the correct netmask */
|
if (netmask.sin_addr.s_addr == INADDR_NONE)
|
if (netmask.sin_addr.s_addr == INADDR_NONE)
|
netmask.sin_addr.s_addr = ip_get_mask(myaddr.sin_addr.s_addr);
|
netmask.sin_addr.s_addr = ip_get_mask(myaddr.sin_addr.s_addr);
|
|
|
/* Setup the device correctly */
|
/* Setup the device correctly */
|
root_dev->family = myaddr.sin_family;
|
root_dev->family = myaddr.sin_family;
|
root_dev->pa_addr = myaddr.sin_addr.s_addr;
|
root_dev->pa_addr = myaddr.sin_addr.s_addr;
|
root_dev->pa_mask = netmask.sin_addr.s_addr;
|
root_dev->pa_mask = netmask.sin_addr.s_addr;
|
root_dev->pa_brdaddr = root_dev->pa_addr | ~root_dev->pa_mask;
|
root_dev->pa_brdaddr = root_dev->pa_addr | ~root_dev->pa_mask;
|
root_dev->pa_dstaddr = 0;
|
root_dev->pa_dstaddr = 0;
|
|
|
/*
|
/*
|
* Now add a route to the server. If there is no gateway given,
|
* Now add a route to the server. If there is no gateway given,
|
* the server is on the same subnet, so we establish only a route to
|
* the server is on the same subnet, so we establish only a route to
|
* the local network. Otherwise we create a route to the gateway (the
|
* the local network. Otherwise we create a route to the gateway (the
|
* same local network router as in the former case) and then setup a
|
* same local network router as in the former case) and then setup a
|
* gatewayed default route. Note that this gives sufficient network
|
* gatewayed default route. Note that this gives sufficient network
|
* setup even for full system operation in all common cases.
|
* setup even for full system operation in all common cases.
|
*/
|
*/
|
memset(&route, 0, sizeof(route)); /* Local subnet route */
|
memset(&route, 0, sizeof(route)); /* Local subnet route */
|
route.rt_dev = root_dev->name;
|
route.rt_dev = root_dev->name;
|
route.rt_mss = root_dev->mtu;
|
route.rt_mss = root_dev->mtu;
|
route.rt_flags = RTF_UP;
|
route.rt_flags = RTF_UP;
|
*((struct sockaddr_in *) &(route.rt_dst)) = myaddr;
|
*((struct sockaddr_in *) &(route.rt_dst)) = myaddr;
|
(((struct sockaddr_in *) &(route.rt_dst)))->sin_addr.s_addr &= netmask.sin_addr.s_addr;
|
(((struct sockaddr_in *) &(route.rt_dst)))->sin_addr.s_addr &= netmask.sin_addr.s_addr;
|
*((struct sockaddr_in *) &(route.rt_genmask)) = netmask;
|
*((struct sockaddr_in *) &(route.rt_genmask)) = netmask;
|
if (ip_rt_new(&route)) {
|
if (ip_rt_new(&route)) {
|
printk(KERN_ERR "Root-NFS: Adding of local route failed!\n");
|
printk(KERN_ERR "Root-NFS: Adding of local route failed!\n");
|
return -1;
|
return -1;
|
}
|
}
|
|
|
if (gateway.sin_addr.s_addr != INADDR_NONE) { /* Default route */
|
if (gateway.sin_addr.s_addr != INADDR_NONE) { /* Default route */
|
(((struct sockaddr_in *) &(route.rt_dst)))->sin_addr.s_addr = INADDR_ANY;
|
(((struct sockaddr_in *) &(route.rt_dst)))->sin_addr.s_addr = INADDR_ANY;
|
(((struct sockaddr_in *) &(route.rt_genmask)))->sin_addr.s_addr = INADDR_ANY;
|
(((struct sockaddr_in *) &(route.rt_genmask)))->sin_addr.s_addr = INADDR_ANY;
|
*((struct sockaddr_in *) &(route.rt_gateway)) = gateway;
|
*((struct sockaddr_in *) &(route.rt_gateway)) = gateway;
|
route.rt_flags |= RTF_GATEWAY;
|
route.rt_flags |= RTF_GATEWAY;
|
if ((gateway.sin_addr.s_addr ^ myaddr.sin_addr.s_addr) & netmask.sin_addr.s_addr) {
|
if ((gateway.sin_addr.s_addr ^ myaddr.sin_addr.s_addr) & netmask.sin_addr.s_addr) {
|
printk(KERN_ERR "Root-NFS: Gateway not on local network!\n");
|
printk(KERN_ERR "Root-NFS: Gateway not on local network!\n");
|
return -1;
|
return -1;
|
}
|
}
|
if (ip_rt_new(&route)) {
|
if (ip_rt_new(&route)) {
|
printk(KERN_ERR "Root-NFS: Adding of default route failed!\n");
|
printk(KERN_ERR "Root-NFS: Adding of default route failed!\n");
|
return -1;
|
return -1;
|
}
|
}
|
} else if ((server.sin_addr.s_addr ^ myaddr.sin_addr.s_addr) & netmask.sin_addr.s_addr) {
|
} else if ((server.sin_addr.s_addr ^ myaddr.sin_addr.s_addr) & netmask.sin_addr.s_addr) {
|
printk(KERN_ERR "Root-NFS: Boot server not on local network and no default gateway configured!\n");
|
printk(KERN_ERR "Root-NFS: Boot server not on local network and no default gateway configured!\n");
|
return -1;
|
return -1;
|
}
|
}
|
|
|
return 0;
|
return 0;
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Get the necessary IP addresses and prepare for mounting the required
|
* Get the necessary IP addresses and prepare for mounting the required
|
* NFS filesystem.
|
* NFS filesystem.
|
*/
|
*/
|
int nfs_root_init(char *nfsname, char *nfsaddrs)
|
int nfs_root_init(char *nfsname, char *nfsaddrs)
|
{
|
{
|
/*
|
/*
|
* Decode IP addresses and other configuration info contained
|
* Decode IP addresses and other configuration info contained
|
* in the nfsaddrs string (which came from the kernel command
|
* in the nfsaddrs string (which came from the kernel command
|
* line).
|
* line).
|
*/
|
*/
|
root_nfs_addrs(nfsaddrs);
|
root_nfs_addrs(nfsaddrs);
|
|
|
/*
|
/*
|
* Setup all network devices
|
* Setup all network devices
|
*/
|
*/
|
if (root_dev_open() < 0)
|
if (root_dev_open() < 0)
|
return -1;
|
return -1;
|
|
|
/*
|
/*
|
* If the config information is insufficient (e.g., our IP address or
|
* If the config information is insufficient (e.g., our IP address or
|
* IP address of the boot server is missing or we have multiple network
|
* IP address of the boot server is missing or we have multiple network
|
* interfaces and no default was set), use BOOTP or RARP to get the
|
* interfaces and no default was set), use BOOTP or RARP to get the
|
* missing values.
|
* missing values.
|
*
|
*
|
* Note that we don't try to set up correct routes for multiple
|
* Note that we don't try to set up correct routes for multiple
|
* interfaces (could be solved by trying icmp echo requests), because
|
* interfaces (could be solved by trying icmp echo requests), because
|
* it's only necessary in the rare case of multiple ethernet devices
|
* it's only necessary in the rare case of multiple ethernet devices
|
* in the (diskless) system and if the server is on another subnet.
|
* in the (diskless) system and if the server is on another subnet.
|
* If only one interface is installed, the routing is obvious.
|
* If only one interface is installed, the routing is obvious.
|
*/
|
*/
|
if ((myaddr.sin_addr.s_addr == INADDR_NONE ||
|
if ((myaddr.sin_addr.s_addr == INADDR_NONE ||
|
server.sin_addr.s_addr == INADDR_NONE ||
|
server.sin_addr.s_addr == INADDR_NONE ||
|
(open_base != NULL && open_base->next != NULL))
|
(open_base != NULL && open_base->next != NULL))
|
#ifdef CONFIG_RNFS_DYNAMIC
|
#ifdef CONFIG_RNFS_DYNAMIC
|
&& root_auto_config() < 0
|
&& root_auto_config() < 0
|
#endif
|
#endif
|
) {
|
) {
|
root_dev_close();
|
root_dev_close();
|
return -1;
|
return -1;
|
}
|
}
|
if (root_dev == NULL) {
|
if (root_dev == NULL) {
|
if (open_base != NULL && open_base->next == NULL) {
|
if (open_base != NULL && open_base->next == NULL) {
|
root_dev = open_base->dev;
|
root_dev = open_base->dev;
|
} else {
|
} else {
|
printk(KERN_ERR "Root-NFS: Multiple devices and no server\n");
|
printk(KERN_ERR "Root-NFS: Multiple devices and no server\n");
|
root_dev_close();
|
root_dev_close();
|
return -1;
|
return -1;
|
}
|
}
|
}
|
}
|
|
|
/*
|
/*
|
* Close all network devices except the device which connects to
|
* Close all network devices except the device which connects to
|
* server
|
* server
|
*/
|
*/
|
root_dev_close();
|
root_dev_close();
|
|
|
/*
|
/*
|
* Decode the root directory path name and NFS options from
|
* Decode the root directory path name and NFS options from
|
* the kernel command line. This has to go here in order to
|
* the kernel command line. This has to go here in order to
|
* be able to use the client IP address for the remote root
|
* be able to use the client IP address for the remote root
|
* directory (necessary for pure RARP booting).
|
* directory (necessary for pure RARP booting).
|
*/
|
*/
|
if (root_nfs_name(nfsname) < 0)
|
if (root_nfs_name(nfsname) < 0)
|
return -1;
|
return -1;
|
|
|
/*
|
/*
|
* Setup devices and routes. The server directory is actually
|
* Setup devices and routes. The server directory is actually
|
* mounted after init() has been started.
|
* mounted after init() has been started.
|
*/
|
*/
|
if (root_nfs_setup() < 0)
|
if (root_nfs_setup() < 0)
|
return -1;
|
return -1;
|
|
|
#ifdef NFSROOT_DEBUG
|
#ifdef NFSROOT_DEBUG
|
root_nfs_print();
|
root_nfs_print();
|
#endif
|
#endif
|
|
|
return 0;
|
return 0;
|
}
|
}
|
|
|
|
|
/***************************************************************************
|
/***************************************************************************
|
|
|
Routines to actually mount the root directory
|
Routines to actually mount the root directory
|
|
|
***************************************************************************/
|
***************************************************************************/
|
|
|
static struct file *nfs_file; /* File descriptor pointing to inode */
|
static struct file *nfs_file; /* File descriptor pointing to inode */
|
static struct inode *nfs_sock_inode; /* Inode containing socket */
|
static struct inode *nfs_sock_inode; /* Inode containing socket */
|
static int *rpc_packet = NULL; /* RPC packet */
|
static int *rpc_packet = NULL; /* RPC packet */
|
|
|
|
|
/*
|
/*
|
* Open a UDP socket.
|
* Open a UDP socket.
|
*/
|
*/
|
static int root_nfs_open(void)
|
static int root_nfs_open(void)
|
{
|
{
|
/* Open the socket */
|
/* Open the socket */
|
if ((nfs_data.fd = sys_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
|
if ((nfs_data.fd = sys_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
|
printk(KERN_ERR "Root-NFS: Cannot open UDP socket for NFS!\n");
|
printk(KERN_ERR "Root-NFS: Cannot open UDP socket for NFS!\n");
|
return -1;
|
return -1;
|
}
|
}
|
nfs_file = current->files->fd[nfs_data.fd];
|
nfs_file = current->files->fd[nfs_data.fd];
|
nfs_sock_inode = nfs_file->f_inode;
|
nfs_sock_inode = nfs_file->f_inode;
|
return 0;
|
return 0;
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Close the UDP file descriptor. If nfs_read_super is successful, it
|
* Close the UDP file descriptor. If nfs_read_super is successful, it
|
* increases the reference count, so we can simply close the file, and
|
* increases the reference count, so we can simply close the file, and
|
* the socket keeps open.
|
* the socket keeps open.
|
*/
|
*/
|
static void root_nfs_close(void)
|
static void root_nfs_close(void)
|
{
|
{
|
/*
|
/*
|
* The following close doesn't touch the server structure, which
|
* The following close doesn't touch the server structure, which
|
* now contains a file pointer pointing into nowhere. The system
|
* now contains a file pointer pointing into nowhere. The system
|
* _should_ crash as soon as someone tries to select on the root
|
* _should_ crash as soon as someone tries to select on the root
|
* filesystem. Haven't tried it yet - we can still change it back
|
* filesystem. Haven't tried it yet - we can still change it back
|
* to the old way of keeping a static copy of all important data
|
* to the old way of keeping a static copy of all important data
|
* structures, including their pointers. At least this should be
|
* structures, including their pointers. At least this should be
|
* checked out _carefully_ before going into a public release
|
* checked out _carefully_ before going into a public release
|
* kernel. - GK
|
* kernel. - GK
|
*/
|
*/
|
sys_close(nfs_data.fd);
|
sys_close(nfs_data.fd);
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Find a suitable listening port and bind to it
|
* Find a suitable listening port and bind to it
|
*/
|
*/
|
static int root_nfs_bind(void)
|
static int root_nfs_bind(void)
|
{
|
{
|
int res = -1;
|
int res = -1;
|
short port = STARTPORT;
|
short port = STARTPORT;
|
struct sockaddr_in *sin = &myaddr;
|
struct sockaddr_in *sin = &myaddr;
|
int i;
|
int i;
|
|
|
if (nfs_sock_inode->u.socket_i.ops->bind) {
|
if (nfs_sock_inode->u.socket_i.ops->bind) {
|
for (i = 0; i < NPORTS && res < 0; i++) {
|
for (i = 0; i < NPORTS && res < 0; i++) {
|
sin->sin_port = htons(port++);
|
sin->sin_port = htons(port++);
|
if (port > ENDPORT) {
|
if (port > ENDPORT) {
|
port = STARTPORT;
|
port = STARTPORT;
|
}
|
}
|
res = nfs_sock_inode->u.socket_i.ops->bind(&nfs_sock_inode->u.socket_i,
|
res = nfs_sock_inode->u.socket_i.ops->bind(&nfs_sock_inode->u.socket_i,
|
(struct sockaddr *)sin,
|
(struct sockaddr *)sin,
|
sizeof(struct sockaddr_in));
|
sizeof(struct sockaddr_in));
|
}
|
}
|
}
|
}
|
if (res < 0) {
|
if (res < 0) {
|
printk(KERN_ERR "Root-NFS: Cannot find a suitable listening port\n");
|
printk(KERN_ERR "Root-NFS: Cannot find a suitable listening port\n");
|
root_nfs_close();
|
root_nfs_close();
|
return -1;
|
return -1;
|
}
|
}
|
#ifdef NFSROOT_DEBUG
|
#ifdef NFSROOT_DEBUG
|
printk(KERN_NOTICE "Root-NFS: Binding to listening port %d\n", port);
|
printk(KERN_NOTICE "Root-NFS: Binding to listening port %d\n", port);
|
#endif
|
#endif
|
return 0;
|
return 0;
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Send an RPC request and wait for the answer
|
* Send an RPC request and wait for the answer
|
*/
|
*/
|
static int *root_nfs_call(int *end)
|
static int *root_nfs_call(int *end)
|
{
|
{
|
struct socket *sock;
|
struct socket *sock;
|
int dummylen;
|
int dummylen;
|
static struct nfs_server s = {
|
static struct nfs_server s = {
|
0, /* struct file * */
|
0, /* struct file * */
|
0, /* struct rsock * */
|
0, /* struct rsock * */
|
{
|
{
|
0, "",
|
0, "",
|
}, /* toaddr */
|
}, /* toaddr */
|
0, /* lock */
|
0, /* lock */
|
NULL, /* wait queue */
|
NULL, /* wait queue */
|
NFS_MOUNT_SOFT, /* flags */
|
NFS_MOUNT_SOFT, /* flags */
|
0, 0, /* rsize, wsize */
|
0, 0, /* rsize, wsize */
|
0, /* timeo */
|
0, /* timeo */
|
0, /* retrans */
|
0, /* retrans */
|
3 * HZ, 60 * HZ, 30 * HZ, 60 * HZ, "\0"
|
3 * HZ, 60 * HZ, 30 * HZ, 60 * HZ, "\0"
|
};
|
};
|
|
|
s.file = nfs_file;
|
s.file = nfs_file;
|
sock = &((nfs_file->f_inode)->u.socket_i);
|
sock = &((nfs_file->f_inode)->u.socket_i);
|
|
|
/* Extract the other end of the socket into s->toaddr */
|
/* Extract the other end of the socket into s->toaddr */
|
sock->ops->getname(sock, &(s.toaddr), &dummylen, 1);
|
sock->ops->getname(sock, &(s.toaddr), &dummylen, 1);
|
((struct sockaddr_in *) &s.toaddr)->sin_port = server.sin_port;
|
((struct sockaddr_in *) &s.toaddr)->sin_port = server.sin_port;
|
((struct sockaddr_in *) &s.toaddr)->sin_family = server.sin_family;
|
((struct sockaddr_in *) &s.toaddr)->sin_family = server.sin_family;
|
((struct sockaddr_in *) &s.toaddr)->sin_addr.s_addr = server.sin_addr.s_addr;
|
((struct sockaddr_in *) &s.toaddr)->sin_addr.s_addr = server.sin_addr.s_addr;
|
|
|
s.rsock = rpc_makesock(nfs_file);
|
s.rsock = rpc_makesock(nfs_file);
|
s.flags = nfs_data.flags;
|
s.flags = nfs_data.flags;
|
s.rsize = nfs_data.rsize;
|
s.rsize = nfs_data.rsize;
|
s.wsize = nfs_data.wsize;
|
s.wsize = nfs_data.wsize;
|
s.timeo = nfs_data.timeo * HZ / 10;
|
s.timeo = nfs_data.timeo * HZ / 10;
|
s.retrans = nfs_data.retrans;
|
s.retrans = nfs_data.retrans;
|
strcpy(s.hostname, nfs_data.hostname);
|
strcpy(s.hostname, nfs_data.hostname);
|
|
|
/*
|
/*
|
* First connect the UDP socket to a server port, then send the
|
* First connect the UDP socket to a server port, then send the
|
* packet out, and finally check whether the answer is OK.
|
* packet out, and finally check whether the answer is OK.
|
*/
|
*/
|
if (nfs_sock_inode->u.socket_i.ops->connect &&
|
if (nfs_sock_inode->u.socket_i.ops->connect &&
|
nfs_sock_inode->u.socket_i.ops->connect(&nfs_sock_inode->u.socket_i,
|
nfs_sock_inode->u.socket_i.ops->connect(&nfs_sock_inode->u.socket_i,
|
(struct sockaddr *) &server,
|
(struct sockaddr *) &server,
|
sizeof(struct sockaddr_in),
|
sizeof(struct sockaddr_in),
|
nfs_file->f_flags) < 0)
|
nfs_file->f_flags) < 0)
|
return NULL;
|
return NULL;
|
if (nfs_rpc_call(&s, rpc_packet, end, nfs_data.wsize) < 0)
|
if (nfs_rpc_call(&s, rpc_packet, end, nfs_data.wsize) < 0)
|
return NULL;
|
return NULL;
|
return rpc_verify(rpc_packet);
|
return rpc_verify(rpc_packet);
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Create an RPC packet header
|
* Create an RPC packet header
|
*/
|
*/
|
static int *root_nfs_header(int proc, int program, int version)
|
static int *root_nfs_header(int proc, int program, int version)
|
{
|
{
|
int groups[] = { 0, NOGROUP };
|
int groups[] = { 0, NOGROUP };
|
|
|
if (rpc_packet == NULL) {
|
if (rpc_packet == NULL) {
|
if (!(rpc_packet = kmalloc(nfs_data.wsize + 1024, GFP_NFS))) {
|
if (!(rpc_packet = kmalloc(nfs_data.wsize + 1024, GFP_NFS))) {
|
printk(KERN_ERR "Root-NFS: Cannot allocate UDP buffer\n");
|
printk(KERN_ERR "Root-NFS: Cannot allocate UDP buffer\n");
|
return NULL;
|
return NULL;
|
}
|
}
|
}
|
}
|
return rpc_header(rpc_packet, proc, program, version, 0, 0, groups);
|
return rpc_header(rpc_packet, proc, program, version, 0, 0, groups);
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Query server portmapper for the port of a daemon program
|
* Query server portmapper for the port of a daemon program
|
*/
|
*/
|
static int root_nfs_get_port(int program, int version)
|
static int root_nfs_get_port(int program, int version)
|
{
|
{
|
int *p;
|
int *p;
|
|
|
/* Prepare header for portmap request */
|
/* Prepare header for portmap request */
|
server.sin_port = htons(NFS_PMAP_PORT);
|
server.sin_port = htons(NFS_PMAP_PORT);
|
p = root_nfs_header(NFS_PMAP_PROC, NFS_PMAP_PROGRAM, NFS_PMAP_VERSION);
|
p = root_nfs_header(NFS_PMAP_PROC, NFS_PMAP_PROGRAM, NFS_PMAP_VERSION);
|
if (!p)
|
if (!p)
|
return -1;
|
return -1;
|
|
|
/* Set arguments for portmapper */
|
/* Set arguments for portmapper */
|
*p++ = htonl(program);
|
*p++ = htonl(program);
|
*p++ = htonl(version);
|
*p++ = htonl(version);
|
*p++ = htonl(IPPROTO_UDP);
|
*p++ = htonl(IPPROTO_UDP);
|
*p++ = 0;
|
*p++ = 0;
|
|
|
/* Send request to server portmapper */
|
/* Send request to server portmapper */
|
if ((p = root_nfs_call(p)) == NULL)
|
if ((p = root_nfs_call(p)) == NULL)
|
return -1;
|
return -1;
|
|
|
return ntohl(*p);
|
return ntohl(*p);
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Get portnumbers for mountd and nfsd from server
|
* Get portnumbers for mountd and nfsd from server
|
*/
|
*/
|
static int root_nfs_ports(void)
|
static int root_nfs_ports(void)
|
{
|
{
|
int port;
|
int port;
|
|
|
if (nfs_port < 0) {
|
if (nfs_port < 0) {
|
if ((port = root_nfs_get_port(NFS_NFS_PROGRAM, NFS_NFS_VERSION)) < 0) {
|
if ((port = root_nfs_get_port(NFS_NFS_PROGRAM, NFS_NFS_VERSION)) < 0) {
|
printk(KERN_ERR "Root-NFS: Unable to get nfsd port number from server, using default\n");
|
printk(KERN_ERR "Root-NFS: Unable to get nfsd port number from server, using default\n");
|
port = NFS_NFS_PORT;
|
port = NFS_NFS_PORT;
|
}
|
}
|
nfs_port = port;
|
nfs_port = port;
|
#ifdef NFSROOT_DEBUG
|
#ifdef NFSROOT_DEBUG
|
printk(KERN_NOTICE "Root-NFS: Portmapper on server returned %d as nfsd port\n", port);
|
printk(KERN_NOTICE "Root-NFS: Portmapper on server returned %d as nfsd port\n", port);
|
#endif
|
#endif
|
}
|
}
|
if ((port = root_nfs_get_port(NFS_MOUNT_PROGRAM, NFS_MOUNT_VERSION)) < 0) {
|
if ((port = root_nfs_get_port(NFS_MOUNT_PROGRAM, NFS_MOUNT_VERSION)) < 0) {
|
printk(KERN_ERR "Root-NFS: Unable to get mountd port number from server, using default\n");
|
printk(KERN_ERR "Root-NFS: Unable to get mountd port number from server, using default\n");
|
port = NFS_MOUNT_PORT;
|
port = NFS_MOUNT_PORT;
|
}
|
}
|
server.sin_port = htons(port);
|
server.sin_port = htons(port);
|
#ifdef NFSROOT_DEBUG
|
#ifdef NFSROOT_DEBUG
|
printk(KERN_NOTICE "Root-NFS: Portmapper on server returned %d as mountd port\n", port);
|
printk(KERN_NOTICE "Root-NFS: Portmapper on server returned %d as mountd port\n", port);
|
#endif
|
#endif
|
|
|
return 0;
|
return 0;
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Get a file handle from the server for the directory which is to be
|
* Get a file handle from the server for the directory which is to be
|
* mounted
|
* mounted
|
*/
|
*/
|
static int root_nfs_get_handle(void)
|
static int root_nfs_get_handle(void)
|
{
|
{
|
int len, status, *p;
|
int len, status, *p;
|
|
|
/* Prepare header for mountd request */
|
/* Prepare header for mountd request */
|
p = root_nfs_header(NFS_MOUNT_PROC, NFS_MOUNT_PROGRAM, NFS_MOUNT_VERSION);
|
p = root_nfs_header(NFS_MOUNT_PROC, NFS_MOUNT_PROGRAM, NFS_MOUNT_VERSION);
|
if (!p) {
|
if (!p) {
|
root_nfs_close();
|
root_nfs_close();
|
return -1;
|
return -1;
|
}
|
}
|
|
|
/* Set arguments for mountd */
|
/* Set arguments for mountd */
|
len = strlen(nfs_path);
|
len = strlen(nfs_path);
|
*p++ = htonl(len);
|
*p++ = htonl(len);
|
memcpy(p, nfs_path, len);
|
memcpy(p, nfs_path, len);
|
len = (len + 3) >> 2;
|
len = (len + 3) >> 2;
|
p[len] = 0;
|
p[len] = 0;
|
p += len;
|
p += len;
|
|
|
/* Send request to server mountd */
|
/* Send request to server mountd */
|
if ((p = root_nfs_call(p)) == NULL) {
|
if ((p = root_nfs_call(p)) == NULL) {
|
root_nfs_close();
|
root_nfs_close();
|
return -1;
|
return -1;
|
}
|
}
|
status = ntohl(*p++);
|
status = ntohl(*p++);
|
if (status == 0) {
|
if (status == 0) {
|
nfs_data.root = *((struct nfs_fh *) p);
|
nfs_data.root = *((struct nfs_fh *) p);
|
printk(KERN_NOTICE "Root-NFS: Got file handle for %s via RPC\n", nfs_path);
|
printk(KERN_NOTICE "Root-NFS: Got file handle for %s via RPC\n", nfs_path);
|
} else {
|
} else {
|
printk(KERN_ERR "Root-NFS: Server returned error %d while mounting %s\n",
|
printk(KERN_ERR "Root-NFS: Server returned error %d while mounting %s\n",
|
status, nfs_path);
|
status, nfs_path);
|
root_nfs_close();
|
root_nfs_close();
|
return -1;
|
return -1;
|
}
|
}
|
|
|
return 0;
|
return 0;
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Now actually mount the given directory
|
* Now actually mount the given directory
|
*/
|
*/
|
static int root_nfs_do_mount(struct super_block *sb)
|
static int root_nfs_do_mount(struct super_block *sb)
|
{
|
{
|
/* First connect to the nfsd port on the server */
|
/* First connect to the nfsd port on the server */
|
server.sin_port = htons(nfs_port);
|
server.sin_port = htons(nfs_port);
|
nfs_data.addr = server;
|
nfs_data.addr = server;
|
if (nfs_sock_inode->u.socket_i.ops->connect &&
|
if (nfs_sock_inode->u.socket_i.ops->connect &&
|
nfs_sock_inode->u.socket_i.ops->connect(&nfs_sock_inode->u.socket_i,
|
nfs_sock_inode->u.socket_i.ops->connect(&nfs_sock_inode->u.socket_i,
|
(struct sockaddr *) &server,
|
(struct sockaddr *) &server,
|
sizeof(struct sockaddr_in),
|
sizeof(struct sockaddr_in),
|
nfs_file->f_flags) < 0) {
|
nfs_file->f_flags) < 0) {
|
root_nfs_close();
|
root_nfs_close();
|
return -1;
|
return -1;
|
}
|
}
|
|
|
/* Now (finally ;-)) read the super block for mounting */
|
/* Now (finally ;-)) read the super block for mounting */
|
if (nfs_read_super(sb, &nfs_data, 1) == NULL) {
|
if (nfs_read_super(sb, &nfs_data, 1) == NULL) {
|
root_nfs_close();
|
root_nfs_close();
|
return -1;
|
return -1;
|
}
|
}
|
return 0;
|
return 0;
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Get the NFS port numbers and file handle, and then read the super-
|
* Get the NFS port numbers and file handle, and then read the super-
|
* block for mounting.
|
* block for mounting.
|
*/
|
*/
|
int nfs_root_mount(struct super_block *sb)
|
int nfs_root_mount(struct super_block *sb)
|
{
|
{
|
if (root_nfs_open() < 0)
|
if (root_nfs_open() < 0)
|
return -1;
|
return -1;
|
if (root_nfs_bind() < 0)
|
if (root_nfs_bind() < 0)
|
return -1;
|
return -1;
|
if (root_nfs_ports() < 0)
|
if (root_nfs_ports() < 0)
|
return -1;
|
return -1;
|
if (root_nfs_get_handle() < 0)
|
if (root_nfs_get_handle() < 0)
|
return -1;
|
return -1;
|
if (root_nfs_do_mount(sb) < 0)
|
if (root_nfs_do_mount(sb) < 0)
|
return -1;
|
return -1;
|
root_nfs_close();
|
root_nfs_close();
|
return 0;
|
return 0;
|
}
|
}
|
|
|