📄 iscsi_init.c
字号:
/* * iSCSI boot initialization module for Linux * maintained by Igor Feoktistov <ifeoktistov@users.sourceforge.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * */#define __KERNEL_SYSCALLS__#include <linux/config.h>#include <linux/init.h>#include <linux/version.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/ctype.h>#include <linux/string.h>#include <linux/errno.h>#include <linux/file.h>#include <linux/jiffies.h>#include <linux/random.h>#include <linux/unistd.h>#include <linux/utsname.h>#include <linux/inet.h>#include <linux/netdevice.h>#include <linux/udp.h>#include <linux/proc_fs.h>#include <linux/seq_file.h>#include <net/ip.h>#include <asm/uaccess.h>#include <asm/checksum.h>#include "iscsi_init.h"static char __initdata *version = "4.0.2.1 (March 30, 2005)";static spinlock_t bootp_recv_lock = SPIN_LOCK_UNLOCKED;static struct iscsi_device *iscsi_device_list __initdata = NULL;static struct iscsi_target *iscsi_target_list __initdata = NULL;static struct iscsi_interface *iscsi_interface_list __initdata = NULL;static char *cmdline __initdata = NULL;static char *iscsi_initiator __initdata = NULL;static char *iscsi_timeout __initdata = NULL;static char *iscsi_config_page __initdata = NULL;static char pnp_domain[64];static u32 pnp_nameservers[CONF_NAMESERVERS_MAX];static char __initdata *iscsi_param_set[] ={ ISCSI_CFG_USERNAME, ISCSI_CFG_PASSWORD, ISCSI_CFG_INCOMINGUSERNAME, ISCSI_CFG_INCOMINGPASSWORD, ISCSI_CFG_OUTGOINGUSERNAME, ISCSI_CFG_OUTGOINGPASSWORD, ISCSI_CFG_HEADERDIGEST, ISCSI_CFG_DATADIGEST, ISCSI_CFG_LOGINTIMEOUT, ISCSI_CFG_AUTHTIMEOUT, ISCSI_CFG_IDLETIMEOUT, ISCSI_CFG_CONNFAILTIMEOUT, ISCSI_CFG_PINGTIMEOUT, ISCSI_CFG_INITIALR2T, ISCSI_CFG_IMMEDIATEDATA, ISCSI_CFG_MAXRECVDATASEGLEN, ISCSI_CFG_FIRSTBURSTLENGTH, ISCSI_CFG_MAXFURSTLENGTH, ISCSI_CFG_TCPWINSIZE, NULL};static struct iscsi_param __initdata *iscsi_param_list = NULL;int errno __initdata = 0;static inline _syscall1(long, unlink, const char*, path);static inline _syscall3(long, ioctl, unsigned int, fd, unsigned int, cmd, unsigned long, arg);static inline _syscall3(long, mknod, const char*, name, int, mode, dev_t, dev);#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,8)static inline _syscall3(int, open, const char*, file, int, flag, int, mode);static inline _syscall1(int, close, int, fd);static inline _syscall3(int, read, int, fd, char*, buf, off_t, count);#endifstatic int __init iscsi_open_devs(struct iscsi_interface *iscsi_if){ struct iscsi_device *d, *d2; struct net_device *dev; unsigned short oflags; rtnl_shlock(); for (dev = dev_base; dev; dev = dev->next) { if (iscsi_if->if_name ? !strcmp(dev->name, iscsi_if->if_name) : (!(dev->flags & IFF_LOOPBACK) && (dev->flags & (IFF_POINTOPOINT | IFF_BROADCAST)) && strncmp(dev->name, "dummy", 5))) { if (dev->mtu < 364) continue; oflags = dev->flags; if (dev_change_flags(dev, oflags | IFF_UP) < 0) { printk(KERN_ERR "iSCSI-init(iscsi_open_devs): failed to open %s\n", dev->name); continue; } if ((d = kmalloc(sizeof(struct iscsi_device), GFP_KERNEL))) { d->next = NULL; d->dev = dev; d->flags = oflags; get_random_bytes(&d->xid, sizeof(u32)); d->iscsi_if = iscsi_if; if (iscsi_device_list) { for (d2 = iscsi_device_list; d2; d2 = d2->next) { if (d2->next == NULL) { d2->next = d; break; } } } else iscsi_device_list = d; } else printk(KERN_ERR "iSCSI-init(iscsi_open_devs): memory allocation error\n"); } } rtnl_shunlock(); if (!iscsi_device_list) { printk(KERN_ERR "iSCSI-init(iscsi_open_devs): no network devices available.\n"); return -1; } return 0;}static void __init iscsi_close_devs(struct iscsi_interface *iscsi_if){ struct iscsi_device *d, *next; struct net_device *dev; rtnl_shlock(); for (d = iscsi_device_list; d; d = next) { dev = d->dev; if (dev != d->iscsi_if->dev) dev_change_flags(dev, d->flags); next = d->next; kfree(d); } rtnl_shunlock(); iscsi_device_list = NULL;}/* * Interface to various network functions. */static inline void set_sockaddr(struct sockaddr_in *sin, u32 addr, u16 port){ sin->sin_family = AF_INET; sin->sin_addr.s_addr = addr; sin->sin_port = port;}static int __init iscsi_dev_ioctl(unsigned int cmd, struct ifreq *arg){ int res; mm_segment_t oldfs = get_fs(); set_fs(get_ds()); res = devinet_ioctl(cmd, arg); set_fs(oldfs); return res;}static int __init iscsi_route_ioctl(unsigned int cmd, struct rtentry *arg){ int res; mm_segment_t oldfs = get_fs(); set_fs(get_ds()); res = ip_rt_ioctl(cmd, arg); set_fs(oldfs); return res;}/* * Set up interface addresses and routes. */static int __init iscsi_setup_if(struct iscsi_interface *iscsi_if){ struct ifreq ir; struct sockaddr_in *sin = (void*) &ir.ifr_ifru.ifru_addr; int err; memset(&ir, 0, sizeof(ir)); strcpy(ir.ifr_ifrn.ifrn_name, iscsi_if->dev->name); set_sockaddr(sin, iscsi_if->if_addr, 0); if ((err = iscsi_dev_ioctl(SIOCSIFADDR, &ir)) < 0) { printk(KERN_ERR "iSCSI-init(iscsi_setup_if): unable to set interface address (%d).\n", err); return -1; } if (iscsi_if->if_netmask == INADDR_NONE) { if (IN_CLASSA(ntohl(iscsi_if->if_addr))) iscsi_if->if_netmask = htonl(IN_CLASSA_NET); else if (IN_CLASSB(ntohl(iscsi_if->if_addr))) iscsi_if->if_netmask = htonl(IN_CLASSB_NET); else if (IN_CLASSC(ntohl(iscsi_if->if_addr))) iscsi_if->if_netmask = htonl(IN_CLASSC_NET); else printk(KERN_ERR "iSCSI-init(iscsi_setup_if): unable to guess netmask for address %u.%u.%u.%u\n", NIPQUAD(iscsi_if->if_addr)); if (iscsi_if->if_netmask != INADDR_NONE) printk("iSCSI-init: guessing netmask %u.%u.%u.%u\n", NIPQUAD(iscsi_if->if_netmask)); } set_sockaddr(sin, iscsi_if->if_netmask, 0); if ((err = iscsi_dev_ioctl(SIOCSIFNETMASK, &ir)) < 0) { printk(KERN_ERR "iSCSI-init(iscsi_setup_if): unable to set interface netmask (%d).\n", err); return -1; } set_sockaddr(sin, iscsi_if->if_addr | ~(iscsi_if->if_netmask), 0); if ((err = iscsi_dev_ioctl(SIOCSIFBRDADDR, &ir)) < 0) { printk(KERN_ERR "iSCSI-init(iscsi_setup_if): unable to set interface broadcast address (%d).\n", err); return -1; } return 0;}static int __init iscsi_setup_routes(struct iscsi_interface *iscsi_if, u32 dst){ /* No need to setup device routes, only the default route... */ struct rtentry rm; int err; memset(&rm, 0, sizeof(rm)); if ((iscsi_if->gateway ^ iscsi_if->if_addr) & iscsi_if->if_netmask) { printk(KERN_ERR "iSCSI-init(iscsi_setup_routes): gateway not on directly connected network.\n"); iscsi_if->gateway = INADDR_NONE; return -1; } set_sockaddr((struct sockaddr_in *) &rm.rt_dst, dst, 0); set_sockaddr((struct sockaddr_in *) &rm.rt_genmask, 0, 0); set_sockaddr((struct sockaddr_in *) &rm.rt_gateway, iscsi_if->gateway, 0); rm.rt_flags = RTF_UP | RTF_GATEWAY; if (dst > 0) { if (!((dst ^ iscsi_if->if_addr) & iscsi_if->if_netmask)) return 0; rm.rt_dev = iscsi_if->dev->name; rm.rt_flags |= RTF_HOST; } if ((err = iscsi_route_ioctl(SIOCADDRT, &rm)) < 0) { if (err != -EEXIST) printk(KERN_ERR "iSCSI-init(iscsi_setup_routes): cannot add default route (%d).\n", err); return -1; } return 0;}/* * DHCP/BOOTP support. */struct bootp_pkt { /* BOOTP packet format */ struct iphdr iph; /* IP header */ struct udphdr udph; /* UDP header */ 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 it says */ u32 client_ip; /* Client's IP address if known */ u32 your_ip; /* Assigned IP address */ u32 server_ip; /* (Next, e.g. NFS) 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 exten[312]; /* DHCP options / BOOTP vendor extensions */};/* packet ops */#define BOOTP_REQUEST 1#define BOOTP_REPLY 2/* DHCP message types */#define DHCPDISCOVER 1#define DHCPOFFER 2#define DHCPREQUEST 3#define DHCPDECLINE 4#define DHCPACK 5#define DHCPNAK 6#define DHCPRELEASE 7#define DHCPINFORM 8static int iscsi_bootp_recv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt);static struct packet_type bootp_packet_type = { type: __constant_htons(ETH_P_IP), func: iscsi_bootp_recv,};/* * Initialize DHCP/BOOTP extension fields in the request. */static const u8 iscsi_bootp_cookie[4] = { 99, 130, 83, 99 };static void __init iscsi_dhcp_init_options(u8 *options, struct iscsi_interface *iscsi_if){ u8 mt = ((iscsi_if->servaddr == INADDR_NONE) ? DHCPDISCOVER : DHCPREQUEST); u8 *e = options; memcpy(e, iscsi_bootp_cookie, 4); /* RFC1048 Magic Cookie */ e += 4; *e++ = 53; /* DHCP message type */ *e++ = 1; *e++ = mt; if (mt == DHCPREQUEST) { *e++ = 54; /* Server ID (IP address) */ *e++ = 4; memcpy(e, &(iscsi_if->servaddr), 4); e += 4; *e++ = 50; /* Requested IP address */ *e++ = 4; memcpy(e, &(iscsi_if->if_addr), 4); e += 4; } { static const u8 iscsi_req_params[] = { 1, /* Subnet mask */ 3, /* Default gateway */ 6, /* DNS server */ 12, /* Host name */ 15, /* Domain name */ 17, /* Boot path */ 40, /* NIS domain name */ }; *e++ = 55; /* Parameter request list */ *e++ = sizeof(iscsi_req_params); memcpy(e, iscsi_req_params, sizeof(iscsi_req_params)); e += sizeof(iscsi_req_params); } *e++ = 255; /* End of the list */}/* * Initialize the DHCP/BOOTP mechanism. */static inline void iscsi_bootp_init(void){ dev_add_pack(&bootp_packet_type);}/* * DHCP/BOOTP cleanup. */static inline void iscsi_bootp_cleanup(void){ dev_remove_pack(&bootp_packet_type);}/* * Send DHCP/BOOTP request to single interface. */static void __init iscsi_bootp_send_if(struct iscsi_device *d, unsigned long jiffies_diff){ struct net_device *dev = d->dev; struct sk_buff *skb; struct bootp_pkt *b; int hh_len = LL_RESERVED_SPACE(dev); struct iphdr *h; /* Allocate packet */ skb = alloc_skb(sizeof(struct bootp_pkt) + hh_len + 15, GFP_KERNEL); if (!skb) return; skb_reserve(skb, hh_len); b = (struct bootp_pkt *) skb_put(skb, sizeof(struct bootp_pkt)); memset(b, 0, sizeof(struct bootp_pkt)); /* Construct IP header */ skb->nh.iph = h = &b->iph; h->version = 4; h->ihl = 5; h->tot_len = htons(sizeof(struct bootp_pkt)); h->frag_off = htons(IP_DF); h->ttl = 64; h->protocol = IPPROTO_UDP; h->daddr = INADDR_BROADCAST; h->check = ip_fast_csum((unsigned char *) h, h->ihl); /* Construct UDP header */ b->udph.source = htons(68); b->udph.dest = htons(67); b->udph.len = htons(sizeof(struct bootp_pkt) - sizeof(struct iphdr)); /* UDP checksum not calculated -- explicitly allowed in BOOTP RFC */ /* Construct DHCP/BOOTP header */ b->op = BOOTP_REQUEST; if (dev->type < 256) /* check for false types */ b->htype = dev->type; else if (dev->type == ARPHRD_IEEE802_TR) /* fix for token ring */ b->htype = ARPHRD_IEEE802; else { printk(KERN_ERR "iSCSI-init(iscsi_bootp_send_if): unknown ARP type 0x%04x for device %s\n", dev->type, dev->name); b->htype = dev->type; /* can cause undefined behavior */ } b->hlen = dev->addr_len; b->your_ip = INADDR_NONE; b->server_ip = INADDR_NONE; memcpy(b->hw_addr, dev->dev_addr, dev->addr_len); b->secs = htons(jiffies_diff / HZ); b->xid = d->xid; /* add DHCP options or BOOTP extensions */ iscsi_dhcp_init_options(b->exten, d->iscsi_if); /* Chain packet down the line... */ skb->dev = dev; skb->protocol = htons(ETH_P_IP); if ((dev->hard_header && dev->hard_header(skb, dev, ntohs(skb->protocol), dev->broadcast, dev->dev_addr, skb->len) < 0) || dev_queue_xmit(skb) < 0) printk("E");}/* * Copy BOOTP-supplied string if not already set. */static int __init iscsi_bootp_string(char *dest, char *src, int len, int max){ if (!len) return 0; if (len > max - 1) len = max - 1; memcpy(dest, src, len); dest[len] = '\0'; return 1;}/* * Process BOOTP extensions. */static void __init iscsi_do_bootp_ext(u8 *ext, struct iscsi_interface *iscsi_if){ u8 servers; int i; switch (*ext++) { case 1: /* Subnet mask */ if (iscsi_if->if_netmask == INADDR_NONE) memcpy(&(iscsi_if->if_netmask), ext + 1, 4); break; case 3: /* Default gateway */ if (iscsi_if->gateway == INADDR_NONE) memcpy(&(iscsi_if->gateway), ext + 1, 4); break; case 6: /* DNS server */ servers = *ext/4;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -