📄 nfsroot.c
字号:
/* * linux/fs/nfs/nfsroot.c -- version 2.3 * * Copyright (C) 1995, 1996 Gero Kuhlmann <gero@gkminix.han.de> * * For parts of this file: * Copyright (C) 1996 Martin Mares <mj@k332.feld.cvut.cz> * * 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 * kernel command line. * (2) Handle RPC negotiation with the system which replied to RARP or * was reported as a boot server by BOOTP or manually. * (3) The actual mounting is done later, when init() is running. * * * Changes: * * Alan Cox : Removed get_address name clash with FPU. * Alan Cox : Reformatted a bit. * Gero Kuhlmann : Code cleanup * Michael Rausch : Fixed recognition of an incoming RARP answer. * Martin Mares : (2.0) Auto-configuration via BOOTP supported. * Martin Mares : Manual selection of interface & BOOTP/RARP. * Martin Mares : Using network routes instead of host routes, * allowing the default configuration to be used * for normal operation of the host. * Martin Mares : Randomized timer with exponential backoff * installed to minimize network congestion. * Martin Mares : Code cleanup. * Martin Mares : (2.1) BOOTP and RARP made configuration options. * Martin Mares : Server hostname generation fixed. * Gerd Knorr : Fixed wired inode handling * Martin Mares : (2.2) "0.0.0.0" addresses from command line ignored. * Martin Mares : RARP replies not tested for server address. * Gero Kuhlmann : (2.3) Some bug fixes and code cleanup again (please * send me your new patches _before_ bothering * Linus so that I don' always have to cleanup * _afterwards_ - thanks) * Gero Kuhlmann : Last changes of Martin Mares undone. * Gero Kuhlmann : RARP replies are tested for specified server * again. However, it's now possible to have * different RARP and NFS servers. * Gero Kuhlmann : "0.0.0.0" addresses from command line are * now mapped to INADDR_NONE. * Gero Kuhlmann : Fixed a bug which prevented BOOTP path name * from being used (thanks to Leo Spiekman) * Andy Walker : Allow to specify the NFS server in nfs_root * without giving a path name * Swen Th=FCmmler : Allow to specify the NFS options in nfs_root * without giving a path name. Fix BOOTP request * for domainname (domainname is NIS domain, not * DNS domain!). Skip dummy devices for BOOTP. * Jacek Zapala : Fixed a bug which prevented server-ip address * from nfsroot parameter from being used. * *//* Define this to allow debugging output */#undef NFSROOT_DEBUG#undef NFSROOT_BOOTP_DEBUG#include <linux/config.h>#include <linux/types.h>#include <linux/string.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/fs.h>#include <linux/random.h>#include <linux/fcntl.h>#include <asm/param.h>#include <linux/utsname.h>#include <linux/in.h>#include <linux/if.h>#include <linux/inet.h>#include <linux/net.h>#include <linux/netdevice.h>#include <linux/if_arp.h>#ifdef CONFIG_AX25#include <net/ax25.h> /* For AX25_P_IP */#endif#include <linux/skbuff.h>#include <linux/socket.h>#include <linux/route.h>#include <linux/nfs.h>#include <linux/nfs_fs.h>#include <linux/nfs_mount.h>#include <linux/in.h>#include <net/route.h>#include <net/sock.h>#include <asm/segment.h>/* Range of privileged ports */#define STARTPORT 600#define ENDPORT 1023#define NPORTS (ENDPORT - STARTPORT + 1)/* Define the timeout for waiting for a RARP/BOOTP reply */#define CONF_BASE_TIMEOUT (HZ*5) /* Initial timeout: 5 seconds */#define CONF_RETRIES 10 /* 10 retries */#define CONF_TIMEOUT_RANDOM (HZ) /* Maximum amount of randomization */#define CONF_TIMEOUT_MULT *5/4 /* Speed of timeout growth */#define CONF_TIMEOUT_MAX (HZ*30) /* Maximum allowed timeout *//* List of open devices */struct open_dev { struct device *dev; unsigned short old_flags; struct open_dev *next;};static struct open_dev *open_base = NULL;/* IP configuration */static struct device *root_dev = NULL; /* Device selected for booting */static char user_dev_name[IFNAMSIZ]; /* Name of user-selected boot device */static struct sockaddr_in myaddr; /* My IP address */static struct sockaddr_in server; /* Server IP address */static struct sockaddr_in gateway; /* Gateway IP address */static struct sockaddr_in netmask; /* Netmask for local subnet *//* BOOTP/RARP variables */static int bootp_flag; /* User said: Use BOOTP! */static int rarp_flag; /* User said: Use RARP! */static int bootp_dev_count = 0; /* Number of devices allowing BOOTP */static int rarp_dev_count = 0; /* Number of devices allowing RARP */static struct sockaddr_in rarp_serv; /* IP address of RARP server */#if defined(CONFIG_RNFS_BOOTP) || defined(CONFIG_RNFS_RARP)#define CONFIG_RNFS_DYNAMIC /* Enable dynamic IP config */static volatile int pkt_arrived; /* BOOTP/RARP packet detected */#define ARRIVED_BOOTP 1#define ARRIVED_RARP 2#endif/* NFS-related data */static struct nfs_mount_data nfs_data; /* NFS mount info */static char nfs_path[NFS_MAXPATHLEN] = ""; /* Name of directory to mount */static int nfs_port; /* Port to connect to for NFS *//* Yes, we use sys_socket, but there's no include file for it */extern asmlinkage int sys_socket(int family, int type, int protocol);/*************************************************************************** Device Handling Subroutines ***************************************************************************//* * Setup and initialize all network devices. If there is a user-preferred * interface, ignore all other interfaces. */static int root_dev_open(void){ struct open_dev *openp, **last; struct device *dev; unsigned short old_flags; last = &open_base; for (dev = dev_base; dev != NULL; dev = dev->next) { if (dev->type < ARPHRD_SLIP && dev->family == AF_INET && !(dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) && (0 != strncmp(dev->name, "dummy", 5)) && (!user_dev_name[0] || !strcmp(dev->name, user_dev_name))) { /* First up the interface */ old_flags = dev->flags; dev->flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING; if (!(old_flags & IFF_UP) && dev_open(dev)) { dev->flags = old_flags; continue; } openp = (struct open_dev *) kmalloc(sizeof(struct open_dev), GFP_ATOMIC); if (openp == NULL) continue; openp->dev = dev; openp->old_flags = old_flags; *last = openp; last = &openp->next; bootp_dev_count++; if (!(dev->flags & IFF_NOARP)) rarp_dev_count++;#ifdef NFSROOT_DEBUG printk(KERN_NOTICE "Root-NFS: Opened %s\n", dev->name);#endif } } *last = NULL; if (!bootp_dev_count && !rarp_dev_count) { printk(KERN_ERR "Root-NFS: Unable to open at least one network device\n"); return -1; } return 0;}/* * Restore the state of all devices. However, keep the root device open * for the upcoming mount. */static void root_dev_close(void){ struct open_dev *openp; struct open_dev *nextp; openp = open_base; while (openp != NULL) { nextp = openp->next; openp->next = NULL; if (openp->dev != root_dev) { if (!(openp->old_flags & IFF_UP)) dev_close(openp->dev); openp->dev->flags = openp->old_flags; } kfree_s(openp, sizeof(struct open_dev)); openp = nextp; }}/*************************************************************************** RARP Subroutines ***************************************************************************/#ifdef CONFIG_RNFS_RARPextern void arp_send(int type, int ptype, unsigned long target_ip, struct device *dev, unsigned long src_ip, unsigned char *dest_hw, unsigned char *src_hw, unsigned char *target_hw);static int root_rarp_recv(struct sk_buff *skb, struct device *dev, struct packet_type *pt);static struct packet_type rarp_packet_type = { 0, /* Should be: __constant_htons(ETH_P_RARP) * - but this _doesn't_ come out constant! */ NULL, /* Listen to all devices */ root_rarp_recv, NULL, NULL};/* * Register the packet type for RARP */static void root_rarp_open(void){ rarp_packet_type.type = htons(ETH_P_RARP); dev_add_pack(&rarp_packet_type);}/* * Deregister the RARP packet type */static void root_rarp_close(void){ rarp_packet_type.type = htons(ETH_P_RARP); dev_remove_pack(&rarp_packet_type);}/* * Receive RARP packets. */static int root_rarp_recv(struct sk_buff *skb, struct device *dev, struct packet_type *pt){ struct arphdr *rarp = (struct arphdr *)skb->h.raw; unsigned char *rarp_ptr = (unsigned char *) (rarp + 1); unsigned long sip, tip; 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 (rarp->ar_hln != dev->addr_len || dev->type != ntohs(rarp->ar_hrd)) { kfree_skb(skb, FREE_READ); return 0; } /* If it's not a RARP reply, delete it. */ if (rarp->ar_op != htons(ARPOP_RREPLY)) { kfree_skb(skb, FREE_READ); return 0; } /* If it's not ethernet or AX25, delete it. */ if ((rarp->ar_pro != htons(ETH_P_IP) && dev->type != ARPHRD_AX25) ||#ifdef CONFIG_AX25 (rarp->ar_pro != htons(AX25_P_IP) && dev->type == ARPHRD_AX25) ||#endif rarp->ar_pln != 4) { kfree_skb(skb, FREE_READ); return 0; } /* Extract variable width fields */ sha = rarp_ptr; rarp_ptr += dev->addr_len; memcpy(&sip, rarp_ptr, 4); rarp_ptr += 4; tha = rarp_ptr; rarp_ptr += dev->addr_len; memcpy(&tip, rarp_ptr, 4); /* Discard packets which are not meant for us. */ if (memcmp(tha, dev->dev_addr, dev->addr_len)) { kfree_skb(skb, FREE_READ); return 0; } /* Discard packets which are not from specified server. */ if (rarp_flag && !bootp_flag && rarp_serv.sin_addr.s_addr != INADDR_NONE && rarp_serv.sin_addr.s_addr != sip) { kfree_skb(skb, FREE_READ); return 0; } /* * The packet is what we were looking for. Setup the global * variables. */ cli(); if (pkt_arrived) { sti(); kfree_skb(skb, FREE_READ); return 0; } pkt_arrived = ARRIVED_RARP; sti(); root_dev = dev; if (myaddr.sin_addr.s_addr == INADDR_NONE) { myaddr.sin_family = dev->family; myaddr.sin_addr.s_addr = tip; } if (server.sin_addr.s_addr == INADDR_NONE) { server.sin_family = dev->family; server.sin_addr.s_addr = sip; } kfree_skb(skb, FREE_READ); return 0;}/* * Send RARP request packet over all devices which allow RARP. */static void root_rarp_send(void){ struct open_dev *openp; struct device *dev; int num = 0; for (openp = open_base; openp != NULL; openp = openp->next) { dev = openp->dev; if (!(dev->flags & IFF_NOARP)) { arp_send(ARPOP_RREQUEST, ETH_P_RARP, 0, dev, 0, NULL, dev->dev_addr, dev->dev_addr); num++; } }}#endif/*************************************************************************** BOOTP Subroutines ***************************************************************************/#ifdef CONFIG_RNFS_BOOTPstatic struct device *bootp_dev = NULL; /* Device selected as best BOOTP target */static int bootp_xmit_fd = -1; /* Socket descriptor for transmit */static struct socket *bootp_xmit_sock; /* The socket itself */static int bootp_recv_fd = -1; /* Socket descriptor for receive */static struct socket *bootp_recv_sock; /* The socket itself */struct bootp_pkt { /* BOOTP packet format */ u8 op; /* 1=request, 2=reply */ u8 htype; /* HW address type */ u8 hlen; /* HW address length */ u8 hops; /* Used only by gateways */ u32 xid; /* Transaction ID */ u16 secs; /* Seconds since we started */ u16 flags; /* Just what is says */ u32 client_ip; /* Client's IP address if known */ u32 your_ip; /* Assigned IP address */ u32 server_ip; /* Server's IP address */ u32 relay_ip; /* IP address of BOOTP relay */ u8 hw_addr[16]; /* Client's HW address */ u8 serv_name[64]; /* Server host name */ u8 boot_file[128]; /* Name of boot file */ u8 vendor_area[128]; /* Area for extensions */};#define BOOTP_REQUEST 1#define BOOTP_REPLY 2static struct bootp_pkt *xmit_bootp; /* Packet being transmitted */static struct bootp_pkt *recv_bootp; /* Packet being received */static int bootp_have_route = 0; /* BOOTP route installed *//* * Free BOOTP packet buffers */static void root_free_bootp(void){ if (xmit_bootp) { kfree_s(xmit_bootp, sizeof(struct bootp_pkt)); xmit_bootp = NULL; } if (recv_bootp) { kfree_s(recv_bootp, sizeof(struct bootp_pkt)); recv_bootp = NULL; }}/* * Allocate memory for BOOTP packet buffers */static inline int root_alloc_bootp(void){ if (!(xmit_bootp = kmalloc(sizeof(struct bootp_pkt), GFP_KERNEL)) || !(recv_bootp = kmalloc(sizeof(struct bootp_pkt), GFP_KERNEL))) { printk("BOOTP: Out of memory!"); return -1; } return 0;}/* * Create default route for BOOTP sending */static int root_add_bootp_route(void){ struct rtentry route; memset(&route, 0, sizeof(route)); route.rt_dev = bootp_dev->name; route.rt_mss = bootp_dev->mtu; route.rt_flags = RTF_UP; ((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_genmask)) -> sin_addr.s_addr = 0; ((struct sockaddr_in *) &(route.rt_genmask)) -> sin_family = AF_INET; if (ip_rt_new(&route)) { printk(KERN_ERR "BOOTP: Adding of route failed!\n"); return -1; } bootp_have_route = 1; return 0;}/* * Delete default route for BOOTP sending */static int root_del_bootp_route(void){ struct rtentry route; if (!bootp_have_route) return 0; memset(&route, 0, sizeof(route)); ((struct sockaddr_in *) &(route.rt_dst)) -> sin_addr.s_addr = 0; ((struct sockaddr_in *) &(route.rt_genmask)) -> sin_addr.s_addr = 0; if (ip_rt_kill(&route)) { printk(KERN_ERR "BOOTP: Deleting of route failed!\n"); return -1; } bootp_have_route = 0; return 0;}/* * Open UDP socket. */static int root_open_udp_sock(int *fd, struct socket **sock){ struct file *file; struct inode *inode; *fd = sys_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (*fd >= 0) { file = current->files->fd[*fd]; inode = file->f_inode; *sock = &inode->u.socket_i; return 0; } printk(KERN_ERR "BOOTP: Cannot open UDP socket!\n"); return -1;}/* * Connect UDP socket. */static int root_connect_udp_sock(struct socket *sock, u32 addr, u16 port){ struct sockaddr_in sa; int result; sa.sin_family = AF_INET; sa.sin_addr.s_addr = htonl(addr); sa.sin_port = htons(port); result = sock->ops->connect(sock, (struct sockaddr *) &sa, sizeof(sa), 0); if (result < 0) { printk(KERN_ERR "BOOTP: connect() failed\n"); return -1; } return 0;}/* * Bind UDP socket. */static int root_bind_udp_sock(struct socket *sock, u32 addr, u16 port){ struct sockaddr_in sa; int result; sa.sin_family = AF_INET; sa.sin_addr.s_addr = htonl(addr); sa.sin_port = htons(port); result = sock->ops->bind(sock, (struct sockaddr *) &sa, sizeof(sa)); if (result < 0) { printk(KERN_ERR "BOOTP: bind() failed\n"); return -1; } return 0;}/* * Send UDP packet. */static inline int root_send_udp(struct socket *sock, void *buf, int size){ u32 oldfs; int result; struct msghdr msg; struct iovec iov;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -