From 8353acb54ebc03da23b5fc904ff58513bd90a8b3 Mon Sep 17 00:00:00 2001 From: Chris Leech Date: Tue, 9 Jun 2015 10:09:25 -0700 Subject: [PATCH] sync to 2.0.873-134-g6aa2c9b 71cd021 iscsid: fix iscsid segfault during qla4xxx login f0a8c95 ISCSISTART: Bring up the corresponding network interface for iboot d81fd49 iscsi tools: fix compile error when OFFLOAD_BOOT_SUPPORT defined 13d08e7 ISCSID: Passing more net params from ibft to iface 9dd181d iscsi tools: Convert '-r' argument to an integer before checking if it is a path 97db3db Update README for removal of DBM requirement 2d086a8 iscsid,iscsiadm: fix abstract socket length in bind() call 5d0e19f iscsid: implement systemd-compatible socket activation c34e0bd iscsid: add example unit files for systemd a7afdf4 iscsi tools: fix get_random_bytes error handling d571cdf ISCSID: Added socket communication hooks for uip b255332 ISCSID: Modified the Makefile for iscsiuio compilation fcab9b7 ISCSID: Added iscsiuio source to the open-iscsi pkg 42fa258 From: Adheer Chandravanshi d64f50d Manpage changes for flashnode submode support for host mode. 3584b98 README changes for flashnode submode support for host mode. fdac390 PATCH 1 of 1] correctly check return value of nice() 6674fd4 Allow firmware mode to use debug flag fc2a8e9 iscsiadm: return error when login fails 8b33814 iscsiadm: bind ifaces to portals found using isns ff4b2c1 iscsiadm: Check for mode is not required when creating params list 83d31c7 iscsid/iscsiadm: add support for emulex one connect storage 9a2bc38 ISCSIUIO: Updated iscsiuio to version 0.7.8.1b for perf optimization d9e26d3 Fix discovery error return without return value b0ba4d6 iscsid: Fix strlen parameter 3d7df63 iscsiuio: Change socket bind to use the same struct size as iscsid bf39941 [PATCH] Make rescan run in parallel 3256b93 iscsiadm: Correctly check for invalid hostno and flashnode index 181af9a iscsi tools: Print additional session info for flashnode session 82c8533 iscsi tools: sync iscsi_if.h with kernel space 5992173 [PATCH v5 1/3] ISCSISTART: Saved ibft boot info to the session 487c312 ISCSID: Added the extraction of the session boot info 3b4b450 ISCSID: Added iface content override fix 1fa1b51 iscsi tools: Bug fix on IPC address copy (version 2) 360a40f flashnode: Add support to set ISCSI_FLASHNODE_CHAP_OUT_IDX param b3913c5 iscsiadm: Use '-x' option instead of '-v' to specify chap_tbl_idx 0c4022d iscsiadm: Man page changes to use -x option for chap_tbl_idx 38b2993 README changes to use long option --index instead of --flashnode_idx 062718a iscsiadm: Add support to set CHAP entry using host chap mode 65ce3a2 iscsi tools: Correctly get username_in and password_in flashnode params f1ed1f7 README changes for adding support to set CHAP entry 0a95bc4 iscsi tools: Setup iface conf file with all iface attrs exported in sysfs 026c8d7 iscsi_if.h: Remove numbers used for network parameter settings d1e07af iscsi_if.h: Additional parameters for network param settings 466efaa iscsi tools: Use macro to set IPv4/IPv6 IP addresses 2220ee4 iscsi tools: Use single function to enable/disable network parameters d8991c9 iscsi tools: Use single function to set integer network parameters da404f2 iscsi tools: Ignore network parameter if not enabled/disabled 66d9f12 iscsi tools: Additional parameters for network settings 9260457 iscsi tools: iface params should be updated for node_rec as well. 75ee9d0 iscsi tools: Let default type of iface be ipv4 4a5e9e2 iscsi tools: Show iface params based on iface type 072d8b9 iscsiadm: Added document for description of iface attributes fe66238 iscsi_tool: Add offload host statistics support. d1e8e68 README: Updated for host statistics. 4a3076b iscsiadm.8: Updated man page for host statistics. 77245b9 ISCSIUIO: Added tx doorbell override mechanism df68365 ISCSIUIO: Added fix for the iface.subnet_mask decoding for IPv6 817a083 ISCSIUIO: Added fix for the ARP cache flush mechanism eb1d275 ISCSIUIO: Updated RELEASE note and version 9bd6fba ISCSIUIO: Updated the configure file to reflect the new version 505ed9d iscsi tools: Fix the iscsiadm help options for host mode bd91b81 Man page correction for host mode options of iscsiadm 5f2f3d7 ISCSIUIO: Removed the auto-generated COPYING file 2b4a011 iscsiuio: Remove autogenerated files from tracking 91cc9c3 iscsiuio: Update automake files c8113ad iscsiuio: Add .gitignore files a877c9d iscsiuio: fix compilation 9ef01a7 Add missing DESTDIR ea05be3 iscsi tools: set non negotiated params early. f2ecc22 iscsiadm: Fix the hostno check for stats submode of host mode b152630 iscsiadm: Fix the compile time warning fabe160 ISCSIUIO: Fixed a pthread resc leak from excessive session recovery 36a8b41 iscsid: Fix handling of iscsi async events. 5f28b8b be2iscsi: Fix MaxXmitDataLenght of the driver. e696b94 Fix StatSN in Open-iSCSI Stack. c0e509e iscsid: retry login for ISCSI_ERR_HOST_NOT_FOUND 134f8dc iscsid: Fix double close of mgmt ipc fd 5762ac0 iscsiadm: Initialize param_count in set_host_chap_info 33cb16e iscsiuio: Rebranding iscsiuio 96eaaac iscsiadm : make iface.ipaddress optional in iface configs for transports that don't have a hard requirement on it. 21a7923 Remove unused variable 'path' 78e24f5 Parse 'origin' value from iBFT c9d830b isns: Add docs for deregistering discovery domains. 147db3c Added new utility script to generate initiator name 76a441b Added new util script to aid in CNA setup f6d7d30 iscsid: don't round up when modifying padding len 06309ad Fix build warnings for unused variables 9c5be24 Fix warning about possibly-uninitialized variable e253c9a Fix bad sizeof in memset 6f6f2a0 Fix missing header 769b480 iscsiuio: Fix warning about non-matching types d58864c iscsiuio: Fix strict-aliasing warning with struct mac_address f7d4c02 iscsiuio: Resolve strict aliasing issue in iscsiuio/src/unix/nic.c cd3d245 iscsiuio: Fix aliasing issue with IPV6_IS_ADDR_UNSPECIFIED e2c9c58 iscsiuio: Use attribute(unused) for variables that are unused but needed 0a253d7 iscsiuio: Use attribute(unused) for *icmpv6_hdr 366f2f8 iscsiuio: Change nic_disable to return void 957ce0b iscsiuio: Remove set but unused variables 0fbf555 iscsiuio: Check return value from nic_queue_tx_packet a125761 Code cleanup: no functional changes 4959a89 Represent DHCP "origin" as an enum, not a string. defd640 fwparam_ibft: Check iBFT target and NIC flags b8bb7ba Allow modifications for iface.gateway and iface.subnet_mask 6c14016 actor: Mark actor_check static 22c5038 actor: simplify actor_check 5e7f696 actor: s/ACTOR_TICKS/actor_jiffies/ 86b919e actor: Remove ACTOR_TICKS_10MS() 4190b2d actor: Unobfuscate ACTOR_MAX_LOOPS 4b5c01e actor: Simplify actor_poll a little 9981b06 Remove actor_init and rename actor_new to actor_init 6385e2d Make running actors event-driven 052fa50 Wake up to reap children 2966a92 fix regression in iscsi_tcp iface binding 51c0b6e Supply strings for newly-added error numbers 892ddaf Allow setting host params to return EAGAIN errors. c7fbcd7 guard against NULL ptr during discovery from unexpected event 7acba59 add discovery as a valid mode in iscsiadm.8 bc9b5c3 iscsid: fix order of setting uid/gid and drop supplementary groups 40616d1 iscsiuio CFLAGS fixes 32e2349 iscsiuio systemd socket activation support def3161 Fix incorrect list operation leading to out-of-order items on pend_list 9297a47 Prevent spinning over poll() when reconnecting to an inaccessible target c02b558 Add some more debug logging to actor.c 63d086c iscsid safe session logout 006270c iscsid: don't re-read config file for every session logout a277d31 Remove duplicate newlines in log messages. 8da14e6 Fix iBFT target flags check. 3d04497 Fix small typo in iscsid.conf 4a4498b buildsys: make 'make clean' idempotent da2473d buildsys: respect CFLAGS and LDFLAGS from the outside 312e904 Remove outdated Debian packaging code. df602f0 Spelling and escaping error fixes. 6aa2c9b Reformat man page synopsis sections --- Makefile | 39 +- README | 142 +- debian/README.Debian | 44 - debian/changelog | 6 - debian/compat | 1 - debian/control | 27 - debian/control.modules.in | 20 - debian/copyright | 12 - debian/dirs | 3 - debian/docs | 4 - debian/postinst.modules.in | 11 - debian/rules | 152 -- debian/rules.modules | 39 - doc/iscsi_discovery.8 | 12 +- doc/iscsiadm.8 | 210 +- etc/iscsid.conf | 5 +- etc/systemd/iscsid.service | 13 + etc/systemd/iscsid.socket | 9 + include/fw_context.h | 15 + include/iscsi_err.h | 4 + include/iscsi_if.h | 468 ++++- include/iscsi_proto.h | 1 + include/list.h | 6 + iscsiuio/.gitignore | 25 + iscsiuio/AUTHORS | 0 iscsiuio/ChangeLog | 7 + iscsiuio/INSTALL | 290 +++ iscsiuio/Makefile.am | 25 + iscsiuio/NEWS | 0 iscsiuio/README | 224 ++ iscsiuio/RELEASE.TXT | 2032 ++++++++++++++++++ iscsiuio/configure.ac | 78 + iscsiuio/docs/iscsiuio.8 | 89 + iscsiuio/iscsiuiolog | 10 + iscsiuio/src/Makefile.am | 1 + iscsiuio/src/README | 13 + iscsiuio/src/apps/Makefile.am | 1 + iscsiuio/src/apps/README | 2 + iscsiuio/src/apps/brcm-iscsi/Makefile.am | 13 + iscsiuio/src/apps/brcm-iscsi/Makefile.brcm-iscsi | 1 + iscsiuio/src/apps/brcm-iscsi/brcm_iscsi.c | 89 + iscsiuio/src/apps/brcm-iscsi/brcm_iscsi.h | 91 + iscsiuio/src/apps/dhcpc/Makefile.am | 13 + iscsiuio/src/apps/dhcpc/Makefile.dhcpc | 1 + iscsiuio/src/apps/dhcpc/dhcpc.c | 417 ++++ iscsiuio/src/apps/dhcpc/dhcpc.h | 86 + iscsiuio/src/apps/dhcpc/dhcpv6.c | 512 +++++ iscsiuio/src/apps/dhcpc/dhcpv6.h | 253 +++ iscsiuio/src/uip-1.0-changelog.txt | 98 + iscsiuio/src/uip/Makefile.am | 18 + iscsiuio/src/uip/Makefile.include | 47 + iscsiuio/src/uip/clock.h | 87 + iscsiuio/src/uip/debug.h | 13 + iscsiuio/src/uip/icmpv6.h | 302 +++ iscsiuio/src/uip/ipv6.c | 1297 ++++++++++++ iscsiuio/src/uip/ipv6.h | 332 +++ iscsiuio/src/uip/ipv6_ndpc.c | 427 ++++ iscsiuio/src/uip/ipv6_ndpc.h | 98 + iscsiuio/src/uip/ipv6_pkt.h | 50 + iscsiuio/src/uip/lc-addrlabels.h | 80 + iscsiuio/src/uip/lc-switch.h | 73 + iscsiuio/src/uip/lc.h | 130 ++ iscsiuio/src/uip/psock.c | 339 +++ iscsiuio/src/uip/psock.h | 383 ++++ iscsiuio/src/uip/pt.h | 322 +++ iscsiuio/src/uip/timer.c | 127 ++ iscsiuio/src/uip/timer.h | 84 + iscsiuio/src/uip/uip-neighbor.c | 219 ++ iscsiuio/src/uip/uip-neighbor.h | 105 + iscsiuio/src/uip/uip.c | 2405 ++++++++++++++++++++++ iscsiuio/src/uip/uip.h | 1569 ++++++++++++++ iscsiuio/src/uip/uip_arch.h | 137 ++ iscsiuio/src/uip/uip_arp.c | 479 +++++ iscsiuio/src/uip/uip_arp.h | 197 ++ iscsiuio/src/uip/uip_eth.c | 50 + iscsiuio/src/uip/uip_eth.h | 43 + iscsiuio/src/uip/uipopt.h | 536 +++++ iscsiuio/src/unix/.gitignore | 2 + iscsiuio/src/unix/Makefile.am | 39 + iscsiuio/src/unix/clock-arch.c | 54 + iscsiuio/src/unix/clock-arch.h | 39 + iscsiuio/src/unix/iscsid_ipc.c | 1075 ++++++++++ iscsiuio/src/unix/iscsid_ipc.h | 52 + iscsiuio/src/unix/libs/Makefile.am | 13 + iscsiuio/src/unix/libs/bnx2.c | 1165 +++++++++++ iscsiuio/src/unix/libs/bnx2.h | 304 +++ iscsiuio/src/unix/libs/bnx2x.c | 1634 +++++++++++++++ iscsiuio/src/unix/libs/bnx2x.h | 712 +++++++ iscsiuio/src/unix/libs/cnic.c | 662 ++++++ iscsiuio/src/unix/libs/cnic.h | 55 + iscsiuio/src/unix/logger.c | 181 ++ iscsiuio/src/unix/logger.h | 129 ++ iscsiuio/src/unix/main.c | 399 ++++ iscsiuio/src/unix/nic.c | 1533 ++++++++++++++ iscsiuio/src/unix/nic.h | 384 ++++ iscsiuio/src/unix/nic_id.c | 362 ++++ iscsiuio/src/unix/nic_id.h | 47 + iscsiuio/src/unix/nic_nl.c | 678 ++++++ iscsiuio/src/unix/nic_nl.h | 54 + iscsiuio/src/unix/nic_utils.c | 1640 +++++++++++++++ iscsiuio/src/unix/nic_utils.h | 102 + iscsiuio/src/unix/nic_vlan.c | 337 +++ iscsiuio/src/unix/nic_vlan.h | 89 + iscsiuio/src/unix/options.h | 117 ++ iscsiuio/src/unix/packet.c | 143 ++ iscsiuio/src/unix/packet.h | 76 + iscsiuio/src/unix/uip-conf.h | 160 ++ sysfs-documentation | 514 +++++ usr/Makefile | 13 +- usr/actor.c | 325 ++- usr/actor.h | 14 +- usr/auth.c | 22 +- usr/be2iscsi.c | 5 - usr/config.h | 53 +- usr/discovery.c | 56 +- usr/discoveryd.c | 10 +- usr/event_poll.c | 60 +- usr/flashnode.c | 615 ++++++ usr/flashnode.h | 129 ++ usr/host.c | 112 +- usr/host.h | 4 + usr/idbm.c | 505 ++++- usr/idbm.h | 8 + usr/idbm_fields.h | 112 + usr/iface.c | 1275 +++++++++--- usr/initiator.c | 582 +++++- usr/initiator.h | 6 + usr/initiator_common.c | 208 +- usr/io.c | 10 +- usr/iscsi_err.c | 2 + usr/iscsi_ipc.h | 19 + usr/iscsi_net_util.c | 14 +- usr/iscsi_sysfs.c | 590 +++++- usr/iscsi_sysfs.h | 20 +- usr/iscsi_util.c | 23 +- usr/iscsi_util.h | 3 + usr/iscsiadm.c | 1077 +++++++++- usr/iscsid.c | 47 +- usr/iscsid.h | 1 + usr/iscsid_req.c | 98 +- usr/iscsid_req.h | 2 + usr/iscsistart.c | 13 +- usr/login.c | 12 +- usr/md5.c | 2 +- usr/mgmt_ipc.c | 40 +- usr/mgmt_ipc.h | 1 + usr/netlink.c | 275 ++- usr/session_info.c | 28 +- usr/session_mgmt.c | 11 +- usr/strings.c | 2 +- usr/sysfs.c | 106 +- usr/sysfs.h | 4 + usr/transport.c | 31 +- usr/transport.h | 11 + usr/types.h | 1 + usr/uip_mgmt_ipc.c | 41 + usr/uip_mgmt_ipc.h | 73 + utils/Makefile | 5 +- utils/fwparam_ibft/Makefile | 4 +- utils/fwparam_ibft/fw_entry.c | 17 +- utils/fwparam_ibft/fwparam_ibft_sysfs.c | 2 + utils/fwparam_ibft/fwparam_sysfs.c | 37 +- utils/iscsi-gen-initiatorname | 73 + utils/iscsi_offload | 378 ++++ utils/md5.c | 2 +- utils/open-isns/bitvector.c | 15 +- utils/open-isns/dd.c | 3 +- utils/open-isns/doc/isnsadm.8 | 16 + utils/open-isns/isnsadm.c | 2 + utils/sysdeps/Makefile | 3 +- 170 files changed, 34076 insertions(+), 1466 deletions(-) delete mode 100644 debian/README.Debian delete mode 100644 debian/changelog delete mode 100644 debian/compat delete mode 100644 debian/control delete mode 100644 debian/control.modules.in delete mode 100644 debian/copyright delete mode 100644 debian/dirs delete mode 100644 debian/docs delete mode 100644 debian/postinst.modules.in delete mode 100644 debian/rules delete mode 100644 debian/rules.modules create mode 100644 etc/systemd/iscsid.service create mode 100644 etc/systemd/iscsid.socket create mode 100644 iscsiuio/.gitignore create mode 100644 iscsiuio/AUTHORS create mode 100644 iscsiuio/ChangeLog create mode 100644 iscsiuio/INSTALL create mode 100644 iscsiuio/Makefile.am create mode 100644 iscsiuio/NEWS create mode 100644 iscsiuio/README create mode 100644 iscsiuio/RELEASE.TXT create mode 100644 iscsiuio/configure.ac create mode 100644 iscsiuio/docs/iscsiuio.8 create mode 100644 iscsiuio/iscsiuiolog create mode 100644 iscsiuio/src/Makefile.am create mode 100644 iscsiuio/src/README create mode 100644 iscsiuio/src/apps/Makefile.am create mode 100644 iscsiuio/src/apps/README create mode 100644 iscsiuio/src/apps/brcm-iscsi/Makefile.am create mode 100644 iscsiuio/src/apps/brcm-iscsi/Makefile.brcm-iscsi create mode 100644 iscsiuio/src/apps/brcm-iscsi/brcm_iscsi.c create mode 100644 iscsiuio/src/apps/brcm-iscsi/brcm_iscsi.h create mode 100644 iscsiuio/src/apps/dhcpc/Makefile.am create mode 100644 iscsiuio/src/apps/dhcpc/Makefile.dhcpc create mode 100644 iscsiuio/src/apps/dhcpc/dhcpc.c create mode 100644 iscsiuio/src/apps/dhcpc/dhcpc.h create mode 100644 iscsiuio/src/apps/dhcpc/dhcpv6.c create mode 100644 iscsiuio/src/apps/dhcpc/dhcpv6.h create mode 100644 iscsiuio/src/uip-1.0-changelog.txt create mode 100644 iscsiuio/src/uip/Makefile.am create mode 100644 iscsiuio/src/uip/Makefile.include create mode 100644 iscsiuio/src/uip/clock.h create mode 100644 iscsiuio/src/uip/debug.h create mode 100644 iscsiuio/src/uip/icmpv6.h create mode 100644 iscsiuio/src/uip/ipv6.c create mode 100644 iscsiuio/src/uip/ipv6.h create mode 100644 iscsiuio/src/uip/ipv6_ndpc.c create mode 100644 iscsiuio/src/uip/ipv6_ndpc.h create mode 100644 iscsiuio/src/uip/ipv6_pkt.h create mode 100644 iscsiuio/src/uip/lc-addrlabels.h create mode 100644 iscsiuio/src/uip/lc-switch.h create mode 100644 iscsiuio/src/uip/lc.h create mode 100644 iscsiuio/src/uip/psock.c create mode 100644 iscsiuio/src/uip/psock.h create mode 100644 iscsiuio/src/uip/pt.h create mode 100644 iscsiuio/src/uip/timer.c create mode 100644 iscsiuio/src/uip/timer.h create mode 100644 iscsiuio/src/uip/uip-neighbor.c create mode 100644 iscsiuio/src/uip/uip-neighbor.h create mode 100644 iscsiuio/src/uip/uip.c create mode 100644 iscsiuio/src/uip/uip.h create mode 100644 iscsiuio/src/uip/uip_arch.h create mode 100644 iscsiuio/src/uip/uip_arp.c create mode 100644 iscsiuio/src/uip/uip_arp.h create mode 100644 iscsiuio/src/uip/uip_eth.c create mode 100644 iscsiuio/src/uip/uip_eth.h create mode 100644 iscsiuio/src/uip/uipopt.h create mode 100644 iscsiuio/src/unix/.gitignore create mode 100644 iscsiuio/src/unix/Makefile.am create mode 100644 iscsiuio/src/unix/clock-arch.c create mode 100644 iscsiuio/src/unix/clock-arch.h create mode 100644 iscsiuio/src/unix/iscsid_ipc.c create mode 100644 iscsiuio/src/unix/iscsid_ipc.h create mode 100644 iscsiuio/src/unix/libs/Makefile.am create mode 100644 iscsiuio/src/unix/libs/bnx2.c create mode 100644 iscsiuio/src/unix/libs/bnx2.h create mode 100644 iscsiuio/src/unix/libs/bnx2x.c create mode 100644 iscsiuio/src/unix/libs/bnx2x.h create mode 100644 iscsiuio/src/unix/libs/cnic.c create mode 100644 iscsiuio/src/unix/libs/cnic.h create mode 100644 iscsiuio/src/unix/logger.c create mode 100644 iscsiuio/src/unix/logger.h create mode 100644 iscsiuio/src/unix/main.c create mode 100644 iscsiuio/src/unix/nic.c create mode 100644 iscsiuio/src/unix/nic.h create mode 100644 iscsiuio/src/unix/nic_id.c create mode 100644 iscsiuio/src/unix/nic_id.h create mode 100644 iscsiuio/src/unix/nic_nl.c create mode 100644 iscsiuio/src/unix/nic_nl.h create mode 100644 iscsiuio/src/unix/nic_utils.c create mode 100644 iscsiuio/src/unix/nic_utils.h create mode 100644 iscsiuio/src/unix/nic_vlan.c create mode 100644 iscsiuio/src/unix/nic_vlan.h create mode 100644 iscsiuio/src/unix/options.h create mode 100644 iscsiuio/src/unix/packet.c create mode 100644 iscsiuio/src/unix/packet.h create mode 100644 iscsiuio/src/unix/uip-conf.h create mode 100644 sysfs-documentation create mode 100644 usr/flashnode.c create mode 100644 usr/flashnode.h create mode 100644 usr/uip_mgmt_ipc.c create mode 100644 usr/uip_mgmt_ipc.h create mode 100755 utils/iscsi-gen-initiatorname create mode 100755 utils/iscsi_offload diff --git a/Makefile b/Makefile index c5d9700..1ef9273 100644 --- a/Makefile +++ b/Makefile @@ -14,35 +14,54 @@ mandir = $(prefix)/share/man etcdir = /etc initddir = $(etcdir)/init.d -MANPAGES = doc/iscsid.8 doc/iscsiadm.8 doc/iscsi_discovery.8 -PROGRAMS = usr/iscsid usr/iscsiadm utils/iscsi_discovery utils/iscsi-iname +MANPAGES = doc/iscsid.8 doc/iscsiadm.8 doc/iscsi_discovery.8 iscsiuio/docs/iscsiuio.8 +PROGRAMS = usr/iscsid usr/iscsiadm utils/iscsi_discovery utils/iscsi-iname iscsiuio/src/unix/iscsiuio INSTALL = install ETCFILES = etc/iscsid.conf IFACEFILES = etc/iface.example +# Compatibility: parse old OPTFLAGS argument +ifdef OPTFLAGS +CFLAGS = $(OPTFLAGS) +endif + +# Export it so configure of iscsiuio & open-isns will +# pick it up. +ifneq (,$(CFLAGS)) +export CFLAGS +endif + # Random comments: # using '$(MAKE)' instead of just 'make' allows make to run in parallel # over multiple makefile. all: user -user: utils/open-isns/Makefile +user: utils/open-isns/Makefile iscsiuio/Makefile $(MAKE) -C utils/open-isns $(MAKE) -C utils/sysdeps $(MAKE) -C utils/fwparam_ibft $(MAKE) -C usr $(MAKE) -C utils + $(MAKE) -C iscsiuio @echo @echo "Compilation complete Output file" @echo "----------------------------------- ----------------" @echo "Built iSCSI daemon: usr/iscsid" @echo "Built management application: usr/iscsiadm" @echo "Built boot tool: usr/iscsistart" + @echo "Built iscsiuio daemon: iscsiuio/src/unix/iscsiuio" @echo @echo "Read README file for detailed information." utils/open-isns/Makefile: utils/open-isns/configure utils/open-isns/Makefile.in - cd utils/open-isns; ./configure CFLAGS="$(OPTFLAGS)" --with-security=no + cd utils/open-isns; ./configure --with-security=no + +iscsiuio/Makefile: iscsiuio/configure iscsiuio/Makefile.in + cd iscsiuio; ./configure + +iscsiuio/configure iscsiuio/Makefile.in: iscsiuio/configure.ac iscsiuio/Makefile.am + cd iscsiuio; autoreconf --install kernel: force $(MAKE) -C kernel @@ -61,8 +80,10 @@ clean: $(MAKE) -C utils clean $(MAKE) -C usr clean $(MAKE) -C kernel clean - $(MAKE) -C utils/open-isns clean - $(MAKE) -C utils/open-isns distclean + [ ! -f iscsiuio/Makefile ] || $(MAKE) -C iscsiuio clean + [ ! -f iscsiuio/Makefile ] || $(MAKE) -C iscsiuio distclean + [ ! -f utils/open-isns/Makefile ] || $(MAKE) -C utils/open-isns clean + [ ! -f utils/open-isns/Makefile ] || $(MAKE) -C utils/open-isns distclean # this is for safety # now -jXXX will still be safe @@ -115,7 +136,7 @@ install_iface: $(IFACEFILES) $(INSTALL) -m 644 $^ $(DESTDIR)$(etcdir)/iscsi/ifaces install_etc: $(ETCFILES) - if [ ! -f /etc/iscsi/iscsid.conf ]; then \ + if [ ! -f $(DESTDIR)/etc/iscsi/iscsid.conf ]; then \ $(INSTALL) -d $(DESTDIR)$(etcdir)/iscsi ; \ $(INSTALL) -m 644 $^ $(DESTDIR)$(etcdir)/iscsi ; \ fi @@ -128,11 +149,11 @@ install_kernel: $(MAKE) -C kernel install_kernel install_iname: - if [ ! -f /etc/iscsi/initiatorname.iscsi ]; then \ + if [ ! -f $(DESTDIR)/etc/iscsi/initiatorname.iscsi ]; then \ echo "InitiatorName=`$(DESTDIR)/sbin/iscsi-iname`" > $(DESTDIR)/etc/iscsi/initiatorname.iscsi ; \ echo "***************************************************" ; \ echo "Setting InitiatorName to `cat $(DESTDIR)/etc/iscsi/initiatorname.iscsi`" ; \ - echo "To override edit /etc/iscsi/initiatorname.iscsi" ; \ + echo "To override edit $(DESTDIR)/etc/iscsi/initiatorname.iscsi" ; \ echo "***************************************************" ; \ fi diff --git a/README b/README index 7364b2d..06d1b6f 100644 --- a/README +++ b/README @@ -159,15 +159,20 @@ Usage: iscsid [OPTION] 5. Open-iSCSI Configuration Utility =================================== -Open-iSCSI persistent configuration is implemented as a DBM database -available on all Linux installations. +Open-iSCSI persistent configuration is stored in a number of +directories under a configuration root directory, using a flat-file +format. This configuration root directory is /etc/iscsi by default, +but may also commonly be in /var/lib/iscsi. -The database contains two tables: +Configuration is contained in directories for: -- Discovery table (/etc/iscsi/send_targets); -- Node table (/etc/iscsi/nodes). - -The regular place for iSCSI database files: /etc/iscsi/nodes +- nodes +- slp +- isns +- static +- fw +- send_targets +- ifaces The iscsiadm utility is a command-line tool to manage (update, delete, insert, query) the persistent database. @@ -388,7 +393,7 @@ Usage: iscsiadm [OPTION] See below for examples. -m iface --interface=iscsi_ifacename -C ping --ip=[ipaddr] --packetsize=[size] --count=[count] --interval=[interval] - -m host --host=hostno|MAC --print=level -C chap --op=[op] --value=[chap_tbl_idx] + -m host --host=hostno|MAC --print=level -C chap --op=[SHOW] Display information for a specific host. The host can be passed in by host number or by MAC address. If a host is not passed in then info @@ -401,6 +406,37 @@ Usage: iscsiadm [OPTION] is connected to. 3 = Print iscsi params used. 4 = Print SCSI info like LUNs, device state. + -m host --host=hostno|MAC -C chap --op=[DELETE] --index=[chap_tbl_idx] + Delete chap entry at the given index from chap table. + -m host --host=hostno|MAC -C chap --op=[NEW | UPDATE] --index=[chap_tbl_idx] \ + --name=[name] --value=[value] + Add new or update existing chap entry at the given + index with given username and password pair. If index + is not passed then entry is added at the first free + index in chap table. + -m host --host=hostno|MAC -C flashnode + Display list of all the targets in adapter's + flash (flash node), for the specified host, + with ip, port, tpgt and iqn. + -m host --host=hostno|MAC -C flashnode --op=[NEW] --portal_type=[ipv4|ipv6] + Create new flash node entry for the given host of the + specified portal_type. This returns the index of the + newly created entry on success. + -m host --host=hostno|MAC -C flashnode --index=[flashnode index] \ + --op=[UPDATE] --name=[name] --value=[value] + Update the params of the speficied flash node. + The [name] and [value] pairs must be provided for the + params that need to be updated. Multiple params can + be updated using a single command. + -m host --host=hostno|MAC -C flashnode --index=[flashnode index] \ + --op=[SHOW | DELETE | LOGIN | LOGOUT] + op=DELETE|LOGIN|LOGOUT will perform deletion/login/ + logout operation on the specified flash node. + + op=SHOW will list all params with the values for the + specified flash node. This is the default operation. + + See the iscsiadm example section for more info. -d, --debug debuglevel print debugging information -V, --version display version and exit -h, --help display this help and exit @@ -955,6 +991,96 @@ To now log into targets it is the same as with sofware iscsi. See section ./iscsiadm -m session -P 1 + + Host mode with flashnode submode: + + - Display list of flash nodes for a host + + ./iscsiadm -m host -H 6 -C flashnode + + This will print list of all the flash node entries for the given host 6 + along with their ip, port, tpgt and iqn values. + + - Display all parameters of a flash node entry for a host + + ./iscsiadm -m host -H 6 -C flashnode -x 0 + + This will list all the parameter name,value pairs for flash node entry at + index 0 of host 6. + + - Add a new flash node entry for a host + + ./iscsiadm -m host -H 6 -C flashnode -o new -A ipv4 + or + ./iscsiadm -m host -H 6 -C flashnode -o new -A ipv6 + + This will add new flash node entry for the given host 6 with portal + type of either ipv4 or ipv6. The new operation returns the index of + the newly created flash node entry. + + - Update a flashnode entry + ./iscsiadm -m host -H 6 -C flashnode -x 1 -o update \ + -n flashnode.conn[0].ipaddress -v 192.168.1.12 \ + -n flashnode.session.targetname \ + -v iqn.2002-03.com.compellent:5000d310004b0716 + + This will update the values of ipaddress and targetname params of + flash node entry at index 1 of host 6. + + - Login to a flash node entry + ./iscsiadm -m host -H 6 -C flashnode -x 1 -o login + + - Logout from a flash node entry + ./iscsiadm -m host -H 6 -C flashnode -x 1 -o logout + or + ./iscsiadm -m session -r $sid -u + + Logout can be performed either using the flash node index or using the + corresponding session index. + + - Delete a flash node entry + ./iscsiadm -m host -H 6 -C flashnode -x 1 -o delete + + Host mode with chap submode: + + - Display list of chap entries for a host + + ./iscsiadm -m host -H 6 -C chap -o show + + This will list all the chap entries for the given host. + + - Delete a chap entry for a host + + ./iscsiadm -m host -H 6 -C chap -o delete -x 5 + + This will delete any chap entry present at given index 5. + + - Add/Update a local chap entry for a host + + ./iscsiadm -m host -H 6 -C chap -o update -x 4 -n username \ + -v value -n password -v value + + This will update the local chap entry present at index 4. If index 4 + is free then entry of type local chap will be created at that index + with given username and password values. + + - Add/Update a bidi chap entry for a host + + ./iscsiadm -m host -H 6 -C chap -o update -x 5 -n username_in \ + -v value -n password_in -v value + + This will update the bidi chap entry present at index 5. If index 5 + is free then entry of type bidi chap will be created at that index + with given username_in and password_in values. + + Host mode with stats submode: + + - Display host statistics: + ./iscsiadm -m host -H 6 -C stats + + This will print the aggregate statistics on the host adapter port. + This includes MAC, TCP/IP, ECC & iSCSI statistics. + 6. Configuration ================ diff --git a/debian/README.Debian b/debian/README.Debian deleted file mode 100644 index 98ebd23..0000000 --- a/debian/README.Debian +++ /dev/null @@ -1,44 +0,0 @@ -linux-iscsi for Debian ------------------------------------ - -The linux-iscsi package contains the userspace portion the Linux iSCSI project. -It has a dependency on the linux-iscsi-modules package, which needs to be built from the linux-iscsi-modules-source against the specific kernel version running -on your system. - -Building --------- -Modules cannot be built against the kernel-headers alone. You will need -to extract and configure your kernel tree, then use the make-kpkg command -(from the kernel-package package) to build a new kernel and modules. -See the make-kpkg man page, particularly the modules-image section. The -following example shows how to build the linux-iscsi-modules package; just -substitute the appropriate version strings. Follow these instructions -(as root) in order to build the linux-iscsi-modules package for your kernel: - -dpkg -i linux-iscsi-modules-source_5.0.0.0.3rc6-363_all.deb -cd /usr/src -rm -rf modules/linux-iscsi -tar jxpvf linux-iscsi-modules-source.tar.bz2 -cd linux-2.6.11.11 (or your appropriate version) -make-kpkg --added-modules linux-iscsi modules-image - -By default, make-kpkg will assume /usr/src/linux-iscsi-modules-source.tar.bz2 -has been extracted under /usr/src. However, that also requires building as -root. If you want to do the build as a non-root user, you need to use the -MODULE_LOC environment variable. For example: - -cd ~/builds -export MODULES_LOC=$PWD/modules -tar jxpvf /usr/src/linux-iscsi-modules-source.tar.bz2 -cd ~/builds/linux-2.6.11.11 (or your appropriate version) -make-kpkg --added-modules linux-iscsi modules-image - -Installing ----------- - -Once you have built the linux-iscsi-modules package, you can install the -binaries: - -dpkg -i linux-iscsi_5.0.0.0.3rc6-363_i386.deb linux-iscsi-modules-2.6.11.11_5.0.0.0.3rc6-363+10.00.Custom_i386.deb - - -- Chad Tindel , Mon, 30 May 2005 15:17:53 -0600 diff --git a/debian/changelog b/debian/changelog deleted file mode 100644 index f71577e..0000000 --- a/debian/changelog +++ /dev/null @@ -1,6 +0,0 @@ -linux-iscsi (5.0.0.0.3rc6-363) unstable; urgency=low - - * Initial Release. - - -- Chad Tindel Mon, 30 May 2005 15:17:53 -0600 - diff --git a/debian/compat b/debian/compat deleted file mode 100644 index b8626c4..0000000 --- a/debian/compat +++ /dev/null @@ -1 +0,0 @@ -4 diff --git a/debian/control b/debian/control deleted file mode 100644 index d333569..0000000 --- a/debian/control +++ /dev/null @@ -1,27 +0,0 @@ -Source: linux-iscsi -Section: net -Priority: optional -Maintainer: Chad Tindel -Build-Depends: debhelper (>= 4.0.0), libdb4.3, libdb4.3-dev -Standards-Version: 3.6.1 - -Package: linux-iscsi -Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends}, linux-iscsi-modules -Description: high performance, transport independent implementation of RFC3720. - linux-iscsi is a high performance, transport independent, implementation of - RFC3720. - -#Package: linux-iscsi -#Architecture: all -#Description: Documentation for linux-iscsi -#linux-iscsi is a high performance, transport independent, implementation of -#RFC3720. - -Package: linux-iscsi-modules-source -Architecture: all -Depends: ${shlibs:Depends}, ${misc:Depends}, module-assistant, debhelper (>= 4.0.0), bzip2 -Description: Source Code for the Linux iSCSI Kernel Modules - Along with make-kpkg, this package maybe used to build a linux-iscsi-modules - package for a kernel-image package. - diff --git a/debian/control.modules.in b/debian/control.modules.in deleted file mode 100644 index 9371b34..0000000 --- a/debian/control.modules.in +++ /dev/null @@ -1,20 +0,0 @@ -Source: linux-iscsi-modules -Section: net -Priority: optional -Maintainer: Chad Tindel -Build-Depends: debhelper (>> 4.1.0), bzip2 -Standards-Version: 3.6.1 - -Package: linux-iscsi-modules-_KVERS_ -Architecture: any -Depends: modutils, linux-iscsi -Provides: linux-iscsi-modules -Description: Linux Kernel Driver Modules for Linux iSCSI (kernel _KVERS_) - This is a Linux driver for iSCSI initiator functionality. - . - This package contains the compiled kernel modules for _KVERS_ - . - If you have compiled your own kernel, you will most likely need to build - your own linux-iscsi-modules. The linux-iscsi-source package has been - provided for use with the Debian kernel-package utility to produce a version - of linux-iscsi-module for your kernel. diff --git a/debian/copyright b/debian/copyright deleted file mode 100644 index 11c0945..0000000 --- a/debian/copyright +++ /dev/null @@ -1,12 +0,0 @@ -This package was debianized by Chad Tindel on -Mon, 30 May 2005 15:17:53 -0600. - -It was downloaded from http://sourceforge.net/projects/linux-iscsi - -Copyright Holder: Dmitry Yusupov - -License: - -You are free to distribute this software under the terms of the GNU General -Public License. On Debian systems, the complete text of the GNU General Public -License can be found in the file `/usr/share/common-licenses/GPL'. diff --git a/debian/dirs b/debian/dirs deleted file mode 100644 index c386116..0000000 --- a/debian/dirs +++ /dev/null @@ -1,3 +0,0 @@ -usr/bin -usr/sbin -etc/init.d diff --git a/debian/docs b/debian/docs deleted file mode 100644 index 9b70221..0000000 --- a/debian/docs +++ /dev/null @@ -1,4 +0,0 @@ -README -COPYING -THANKS -TODO diff --git a/debian/postinst.modules.in b/debian/postinst.modules.in deleted file mode 100644 index 70dc20d..0000000 --- a/debian/postinst.modules.in +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh - -set -e - -if [ "`uname -r`" = "_KVERS_" ]; then - /sbin/depmod -a & -fi - -#DEBHELPER# - -exit 0 diff --git a/debian/rules b/debian/rules deleted file mode 100644 index f4695c6..0000000 --- a/debian/rules +++ /dev/null @@ -1,152 +0,0 @@ -#!/usr/bin/make -f -# -*- makefile -*- -# Sample debian/rules that uses debhelper. -# -# This file was originally written by Joey Hess and Craig Small. -# As a special exception, when this file is copied by dh-make into a -# dh-make output file, you may use that output file without restriction. -# This special exception was added by Craig Small in version 0.37 of dh-make. -# -# Modified to make a template file for a multi-binary package with separated -# build-arch and build-indep targets by Bill Allombert 2001 - -# Uncomment this to turn on verbose mode. -#export DH_VERBOSE=1 - -# This has to be exported to make some magic below work. -export DH_OPTIONS - - - -CFLAGS = -Wall -g - -ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) - CFLAGS += -O0 -else - CFLAGS += -O2 -endif - -configure: configure-stamp -configure-stamp: - dh_testdir - # Add here commands to configure the package. - - touch configure-stamp - - -#Architecture -build: build-arch build-indep - -build-arch: build-arch-stamp -build-arch-stamp: configure-stamp - - # Add here commands to compile the arch part of the package. - $(MAKE) -C usr - - touch build-arch-stamp - -build-indep: build-indep-stamp -build-indep-stamp: configure-stamp - - # Add here commands to compile the indep part of the package. - #$(MAKE) doc - touch build-indep-stamp - -clean: - dh_testdir - dh_testroot - rm -f build-arch-stamp build-indep-stamp #CONFIGURE-STAMP# - - # Add here commands to clean up after the build process. - $(MAKE) -C usr clean - rm -rf modules - - dh_clean - -install: install-indep install-arch -install-indep: - dh_testdir - dh_testroot - dh_clean -k -i - dh_installdirs -i - - # create needed directories - dh_installdirs -i usr/src/modules/linux-iscsi - - mkdir -p modules/linux-iscsi/debian - - # copy the driver source - tar --exclude=debian -c * | (cd modules/linux-iscsi && tar xv) - - # copy all relevant debian/ files - cp debian/{compat,copyright} modules/linux-iscsi/debian - cat debian/changelog | sed -e 's/linux-iscsi/linux-iscsi-modules/' > modules/linux-iscsi/debian/changelog - cp debian/*.modules.in modules/linux-iscsi/debian - install -m755 debian/rules.modules modules/linux-iscsi/debian/rules - - # entar the source - tar jcf debian/linux-iscsi-modules-source/usr/src/linux-iscsi-modules-source.tar.bz2 modules - - # Add here commands to install the indep part of the package into - # debian/-doc. - #INSTALLDOC# - - dh_install -i - -install-arch: - dh_testdir - dh_testroot - dh_clean -k -s - dh_installdirs -s - - # Add here commands to install the arch part of the package into - # debian/linux-iscsi. - install -m 755 usr/iscsiadm $(CURDIR)/debian/linux-iscsi/usr/bin - install -m 755 usr/iscsid $(CURDIR)/debian/linux-iscsi/usr/sbin - install -m 644 etc/iscsid.conf $(CURDIR)/debian/linux-iscsi/etc/iscsid.conf.example - install -m 755 etc/initd/initd.debian $(CURDIR)/debian/linux-iscsi/etc/init.d/iscsid - make clean - - dh_install -s - -# Must not depend on anything. This is to be called by -# binary-arch/binary-indep -# in another 'make' thread. -binary-common: - dh_testdir - dh_testroot - dh_installchangelogs - dh_installdocs - dh_installexamples -# dh_installmenu -# dh_installdebconf -# dh_installlogrotate -# dh_installemacsen -# dh_installpam -# dh_installmime -# dh_installinit -# dh_installcron -# dh_installinfo - dh_installman - dh_link - dh_strip - dh_compress - dh_fixperms -# dh_perl -# dh_python - dh_makeshlibs - dh_installdeb - dh_shlibdeps - dh_gencontrol - dh_md5sums - dh_builddeb -# Build architecture independant packages using the common target. -binary-indep: build-indep install-indep - $(MAKE) -f debian/rules DH_OPTIONS=-i binary-common - -# Build architecture dependant packages using the common target. -binary-arch: build-arch install-arch - $(MAKE) -f debian/rules DH_OPTIONS=-a binary-common - -binary: binary-arch binary-indep -.PHONY: build clean binary-indep binary-arch binary install install-indep install-arch configure diff --git a/debian/rules.modules b/debian/rules.modules deleted file mode 100644 index 1c4dabd..0000000 --- a/debian/rules.modules +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/make -f - -# module-assistant stuff -PACKAGE = linux-iscsi-modules -MA_DIR ?= /usr/share/modass --include $(MA_DIR)/include/generic.make --include $(MA_DIR)/include/common-rules.make - -kdist_clean: prep-deb-files - dh_clean - #$(MAKE) clean KERNEL_PATH=$(KSRC) - make clean - -kdist_config: prep-deb-files - -TARGET = $(CURDIR)/debian/linux-iscsi-modules-$(KVERS) -MODULES_TARGET = $(TARGET)/lib/modules/$(KVERS)/kernel/net/iscsi - -binary-modules: kdist_config - dh_testdir - dh_testroot - dh_clean -k - dh_installdirs lib/modules/$(KVERS) - - # build and install the module - patch -d kernel < kernel/backward-compile-2.6.11.patch - make -C kernel KSRC=$(KSRC) - mkdir -p $(MODULES_TARGET) - install -m 755 kernel/iscsi_tcp.ko $(MODULES_TARGET) - install -m 755 kernel/scsi_transport_iscsi.ko $(MODULES_TARGET) - - dh_installdocs - dh_installchangelogs - dh_compress - dh_fixperms - dh_installdeb - dh_gencontrol -- -v$(VERSION) - dh_md5sums - dh_builddeb --destdir=$(DEB_DESTDIR) diff --git a/doc/iscsi_discovery.8 b/doc/iscsi_discovery.8 index a4affc6..e28065c 100644 --- a/doc/iscsi_discovery.8 +++ b/doc/iscsi_discovery.8 @@ -8,7 +8,17 @@ .SH NAME iscsi_discovery \- discover iSCSI targets .SH SYNOPSIS -.B iscsi_discovery [-p ] [-d] [-t [-f]] [-m] [-l] +.B iscsi_discovery +.I +.RB [ -p +.IR ] +.RB [ -d ] +.RB [\ -t +.IR +.RB [ -f ] +.R ] +.RB [ -m ] +.RB [ -l ] .SH DESCRIPTION Perform send-targets discovery to the specified IP. If a discovery record diff --git a/doc/iscsiadm.8 b/doc/iscsiadm.8 index 7c209f6..f046236 100644 --- a/doc/iscsiadm.8 +++ b/doc/iscsiadm.8 @@ -2,23 +2,161 @@ .SH NAME iscsiadm \- open-iscsi administration utility .SH SYNOPSIS -\fBiscsiadm\fR \-m discoverydb [ \-hV ] [ \-d debug_level ] [ \-P printlevel ] [ \-I iface \-t type \-p ip:port [ \-lD ] ] | [ [ -p ip:port -t type ] \ -[ \-o operation ] [ \-n name ] [ \-v value ] [ \-lD ] ] +.B iscsiadm +.B \-m discoverydb +.RB [ \-hV ] +.RB [ \-d +.IR debug_level ] +.RB [ \-P +.IR printlevel ] +.R [\ +.BI \-I\ iface\ \-t\ type\ \-p\ ip:port +.RB [ \-lD ] +.R ] | [ +.RB [ \-p +.I ip:port +.B \-t +.IR type ] +.RB [ \-o +.IR operation ] +.RB [ \-n +.IR name ] +.RB [ \-v +.IR value ] +.RB [ \-lD ] +.R ] -\fBiscsiadm\fR \-m discovery [ \-hV ] [ \-d debug_level ] [ \-P printlevel ] [ \-I iface \-t type \-p ip:port [ \-l ] ] | [ [ -p ip:port ] [ \-l | \-D ] ] +.B iscsiadm +.B \-m discovery +.RB [ \-hV ] +.RB [ \-d +.IR debug_level ] +.RB [ \-P +.IR printlevel ] +.R [\ +.BI \-I\ iface\ \-t\ type\ \-p\ ip:port +.RB [ \-l ] +.R ] | [ +.RB [ \-p +.IR ip:port ] +.RB [ \-l | \-D ] +.R ] -\fBiscsiadm\fR \-m node [ \-hV ] [ \-d debug_level ] [ \-P printlevel ] [ \-L all,manual,automatic ] [ \-U all,manual,automatic ] [ \-S ] [ [ \-T targetname \-p ip:port \-I iface ] [ \-l | \-u | \-R | \-s] ] -[ [ \-o operation ] [ \-n name ] [ \-v value ] [ \-p ip:port ] ] +.B iscsiadm +.B \-m node +.RB [ \-hV ] +.RB [ \-d +.IR debug_level ] +.RB [ \-P +.IR printlevel ] +.RB [ \-L +.IR all,manual,automatic ] +.RB [ \-U +.IR all,manual,automatic ] +.RB [ \-S ] +.R [ +.RB [ \-T +.IB targetname\ \-p\ ip:port\ \-I\ iface +.R ] +.RB [ \-l | \-u | \-R | \-s ] +.R ] +.R [ +.RB [ \-o +.IR operation ] +.RB [ \-n +.IR name ] +.RB [ \-v +.IR value ] +.RB [ \-p +.IR ip:port ] +.R ] -\fBiscsiadm\fR \-m session [ \-hV ] [ \-d debug_level ] [ \-P printlevel ] [ \-r sessionid | sysfsdir [ \-R ] [ \-u | \-s | \-o new ] ] +.B iscsiadm +.B \-m session +.RB[ \-hV ] +.RB [ \-d +.IR debug_level ] +.RB [ \-P +.IR printlevel ] +.R [ +.B \-r +.IR sessionid | sysfsdir +.RB [ \-R ] +.RB [ \-u | \-s | \-o +.IR new ] +.R ] -\fBiscsiadm\fR \-m iface [ \-hV ] [ \-d debug_level ] [ \-P printlevel ] [ \-I ifacename | \-H hostno|MAC ] [ [ \-o operation ] [ \-n name ] [ \-v value ] ] [ \-C ping [ \-a ip ] [ \-b packetsize ] [ \-c count ] [ \-i interval ] ] +.B iscsiadm +.B \-m iface +.RB [ \-hV ] +.RB [ \-d +.IR debug_level ] +.RB [ \-P +.IR printlevel ] +.R [ +.BI \-I\ ifacename +.R | +.BI \-H\ hostno|MAC +.R ] +.R [ +.RB [ \-o +.IR operation ] +.RB [ \-n +.IR name ] +.RB [ \-v +.IR value ] +.R ] +.R [ +.BI \-C\ ping +.RB [ \-a +.IR ip ] +.RB [ \-b +.IR packetsize ] +.RB [ \-c +.IR count ] +.RB [ \-i +.IR interval ] +.R ] -\fBiscsiadm\fR \-m fw [\-l] +.B iscsiadm +.B \-m fw +.RB [ \-d +.IR debug_level ] +.RB [ \-l ] -\fBiscsiadm\fR \-m host [ \-P printlevel ] [ \-H hostno|MAC ] [ -C chap [ -o operation ] [ -v chap_tbl_idx ] ] +.B iscsiadm +.B \-m host +.RB [ \-P +.IR printlevel ] +.RB [ \-H +.IR hostno|MAC ] +.R [ +.RB [\ \-C +.IR chap +.RB [ \-x +.IR chap_tbl_idx ] +.R ] | +.RB [\ \-C +.IR flashnode +.RB [ \-A +.IR portal_type ] +.RB [ \-x +.IR flashnode_idx ] +.R ] | +.RB [\ \-C +.IR stats \ ] +.R ] +.R [ +.RB [ \-o +.IR operation ] +.RB [ \-n +.IR name ] +.RB [ \-v +.IR value ] +.R ] -\fBiscsiadm\fR \-k priority +.B iscsiadm +.B \-k priority .SH "DESCRIPTION" The iscsiadm utility is a command-line tool allowing discovery and login @@ -47,6 +185,12 @@ daemon (iscsid) be running. This option is only valid for ping submode. .TP +\fB\-A\fR, \fB\-\-portal_type=\fI[ipv4|ipv6]\fR +Specify the portal type for the new flash node entry to be created. +.IP +This option is only valid for flashnode submode of host mode and only with \fInew\fR operation. + +.TP \fB\-b\fR, \fB\-\-packetsize=\fIpacketsize\fP Specify the ping \fIpacketsize\fR. @@ -64,7 +208,15 @@ Specify the submode for mode. op must be name of submode. Currently iscsiadm support ping as submode for iface. For example, -iscsiadm -m iface -I ifacename -C ping -a ipaddr -b packetsize -c count -i interval +iscsiadm \-m iface \-I ifacename \-C ping \-a ipaddr \-b packetsize \-c count \-i interval + +For host, it supports chap , flashnode and stats as submodes. For example, + +iscsiadm \-m host \-H hostno \-C chap \-x chap_tbl_idx \-o operation + +iscsiadm \-m host \-H hostno \-C flashnode \-x flashnode_idx \-o operation + +iscsiadm \-m host \-H hostno \-C stats .TP \fB\-d\fR, \fB\-\-debug=\fIdebug_level\fP @@ -113,7 +265,7 @@ are experimental and the use is not supported as a stable interface yet. In discovery mode multiple interfaces can be specified by passing in multiple \-I/\-\-interface instances. For example, -"iscsiadm \-m discoverydb \-t st \-p ip:port \-I iface0 \-I iface2 --discover" +"iscsiadm \-m discoverydb \-t st \-p ip:port \-I iface0 \-I iface2 \-\-discover" Will direct iscsiadm to setup the node db to create records which will create sessions though the two intefaces passed in. @@ -160,18 +312,19 @@ for session mode). .TP \fB\-m, \-\-mode \fIop\fR specify the mode. \fIop\fR -must be one of \fIdiscoverydb\fR, \fInode\fR, \fIfw\fR, \fIhost\fR \fIiface\fR or \fIsession\fR. +must be one of \fIdiscovery\fR, \fIdiscoverydb\fR, \fInode\fR, \fIfw\fR, \fIhost\fR \fIiface\fR or \fIsession\fR. .IP -If no other options are specified: for \fIdiscoverydb\fR and \fInode\fR, all -of their respective records are displayed; for \fIsession\fR, all active -sessions and connections are displayed; for \fIfw\fR, all boot firmware -values are displayed; for \fIhost\fR, all iSCSI hosts are displayed; and -for \fIiface\fR, all ifaces setup in /etc/iscsi/ifaces are displayed. +If no other options are specified: for \fIdiscovery\fR, \fIdiscoverydb\fR and +\fInode\fR, all of their respective records are displayed; for \fIsession\fR, +all active sessions and connections are displayed; for \fIfw\fR, all boot +firmware values are displayed; for \fIhost\fR, all iSCSI hosts are displayed; +and for \fIiface\fR, all ifaces setup in /etc/iscsi/ifaces are displayed. .TP \fB\-n\fR, \fB\-\-name=\fIname\fR -Specify a field \fIname\fR in a record. For use with the \fIupdate\fR -operator. +In node mode, specify a field \fIname\fR in a record. In flashnode submode of host mode, specify name of the flash node parameter. + +For use with the \fIupdate\fR operator. .IP .TP @@ -181,6 +334,8 @@ Specifies a database operator \fIop\fR. \fIop\fR must be one of .IP For iface mode, \fIapply\fR and \fIapplyall\fR are also applicable. .IP +For flashnode submode of host mode, \fIlogin\fR and \fIlogout\fR are also applicable. +.IP This option is valid for all modes except fw. Delete should not be used on a running session. If it is iscsiadm will stop the session and then delete the record. .IP @@ -210,6 +365,12 @@ sid is passed in. .IP \fIapplyall\fR will cause the network settings to take effect on all the ifaces whose MAC address or host number matches that of the specific host. +.IP +\fIlogin\fR will log into the specified flash node entry. + +.IP +\fIlogout\fR does the logout from the given flash node entry. + .TP \fB\-p\fR, \fB\-\-portal=\fIip[:port]\fR Use target portal with ip-address \fIip\fR and \fIport\fR. If port is not passed @@ -258,6 +419,7 @@ tuple passed in. .TP \fB\-s\fR, \fB\-\-stats\fR Display session statistics. +This option when used with host mode, displays host statistics. .TP \fB\-S\fR, \fB\-\-show\fR @@ -292,12 +454,18 @@ for session mode). \fB\-v\fR, \fB\-\-value=\fIvalue\fR Specify a \fIvalue\fR for use with the \fIupdate\fR operator. .IP -This option is only valid for node mode. +This option is only valid for node mode and flashnode submode of host mode. .TP \fB\-V\fR, \fB\-\-version\fR display version and exit +.TP +\fB\-x\fR, \fB\-\-index=\fIindex\fR +Specify the \fIindex\fR of the entity to operate on. +.IP +This option is only valid for chap and flashnode submodes of host mode. + .SH DISCOVERY TYPES iSCSI defines 3 discovery types: SendTargets, SLP, and iSNS. diff --git a/etc/iscsid.conf b/etc/iscsid.conf index ef76dc0..c30a7dc 100644 --- a/etc/iscsid.conf +++ b/etc/iscsid.conf @@ -22,6 +22,9 @@ # Default for upstream open-iscsi scripts (uncomment to activate). iscsid.startup = /sbin/iscsid +# Check for active mounts on devices reachable through a session +# and refuse to logout if there are any. Defaults to "No". +# iscsid.safe_logout = Yes ############################# # NIC/HBA and driver settings @@ -80,7 +83,7 @@ node.leading_login = No # Timeouts # ******** # -# See the iSCSI REAME's Advanced Configuration section for tips +# See the iSCSI README's Advanced Configuration section for tips # on setting timeouts when using multipath or doing root over iSCSI. # # To specify the length of time to wait for session re-establishment diff --git a/etc/systemd/iscsid.service b/etc/systemd/iscsid.service new file mode 100644 index 0000000..028e0b3 --- /dev/null +++ b/etc/systemd/iscsid.service @@ -0,0 +1,13 @@ +[Unit] +Description=Open-iSCSI +Documentation=man:iscsid(8) man:iscsiuio(8) man:iscsiadm(8) +After=network.target NetworkManager-wait-online.service iscsiuio.service tgtd.service targetcli.service + +[Service] +Type=forking +PIDFile=/var/run/iscsid.pid +ExecStart=/usr/sbin/iscsid +ExecStop=/sbin/iscsiadm -k 0 2 + +[Install] +WantedBy=multi-user.target diff --git a/etc/systemd/iscsid.socket b/etc/systemd/iscsid.socket new file mode 100644 index 0000000..832451d --- /dev/null +++ b/etc/systemd/iscsid.socket @@ -0,0 +1,9 @@ +[Unit] +Description=Open-iSCSI iscsid Socket +Documentation=man:iscsid(8) man:iscsiuio(8) man:iscsiadm(8) + +[Socket] +ListenStream=@ISCSIADM_ABSTRACT_NAMESPACE + +[Install] +WantedBy=sockets.target diff --git a/include/fw_context.h b/include/fw_context.h index 1640859..44053d8 100644 --- a/include/fw_context.h +++ b/include/fw_context.h @@ -28,10 +28,23 @@ #include "list.h" #include "auth.h" +enum ibft_ip_prefix_origin { + IBFT_IP_PREFIX_ORIGIN_OTHER = 0, + IBFT_IP_PREFIX_ORIGIN_MANUAL, + IBFT_IP_PREFIX_ORIGIN_WELL_KNOWN, + IBFT_IP_PREFIX_ORIGIN_DHCP, + IBFT_IP_PREFIX_ORIGIN_ROUTER_ADVERTISEMENT, + IBFT_IP_PREFIX_ORIGIN_UNCHANGED = 16 +}; + struct boot_context { struct list_head list; + char boot_root[BOOT_NAME_MAXLEN]; + char boot_nic[BOOT_NAME_MAXLEN]; + char boot_target[BOOT_NAME_MAXLEN]; /* target settings */ + int target_flags; int target_port; char targetname[TARGET_NAME_MAXLEN + 1]; char target_ipaddr[NI_MAXHOST]; @@ -45,6 +58,8 @@ struct boot_context { char initiatorname[TARGET_NAME_MAXLEN + 1]; /* network settings */ + int nic_flags; + enum ibft_ip_prefix_origin origin; char dhcp[NI_MAXHOST]; char iface[IF_NAMESIZE]; char mac[18]; diff --git a/include/iscsi_err.h b/include/iscsi_err.h index aabea4e..125f443 100644 --- a/include/iscsi_err.h +++ b/include/iscsi_err.h @@ -62,6 +62,10 @@ enum { ISCSI_ERR_OP_NOT_SUPP = 27, /* device or resource in use */ ISCSI_ERR_BUSY = 28, + /* Operation failed, but retrying layer may succeed */ + ISCSI_ERR_AGAIN = 29, + /* unknown discovery type */ + ISCSI_ERR_UNKNOWN_DISCOVERY_TYPE = 30, /* Always last. Indicates end of error code space */ ISCSI_MAX_ERR_VAL, diff --git a/include/iscsi_if.h b/include/iscsi_if.h index dad9fd8..9d15811 100644 --- a/include/iscsi_if.h +++ b/include/iscsi_if.h @@ -68,8 +68,15 @@ enum iscsi_uevent_e { ISCSI_UEVENT_PING = UEVENT_BASE + 22, ISCSI_UEVENT_GET_CHAP = UEVENT_BASE + 23, ISCSI_UEVENT_DELETE_CHAP = UEVENT_BASE + 24, - - ISCSI_UEVENT_MAX = ISCSI_UEVENT_DELETE_CHAP, + ISCSI_UEVENT_SET_FLASHNODE_PARAMS = UEVENT_BASE + 25, + ISCSI_UEVENT_NEW_FLASHNODE = UEVENT_BASE + 26, + ISCSI_UEVENT_DEL_FLASHNODE = UEVENT_BASE + 27, + ISCSI_UEVENT_LOGIN_FLASHNODE = UEVENT_BASE + 28, + ISCSI_UEVENT_LOGOUT_FLASHNODE = UEVENT_BASE + 29, + ISCSI_UEVENT_LOGOUT_FLASHNODE_SID = UEVENT_BASE + 30, + ISCSI_UEVENT_SET_CHAP = UEVENT_BASE + 31, + ISCSI_UEVENT_GET_HOST_STATS = UEVENT_BASE + 32, + ISCSI_UEVENT_MAX = ISCSI_UEVENT_GET_HOST_STATS, /* up events */ ISCSI_KEVENT_RECV_PDU = KEVENT_BASE + 1, @@ -219,6 +226,35 @@ struct iscsi_uevent { uint32_t host_no; uint16_t chap_tbl_idx; } delete_chap; + struct msg_set_flashnode_param { + uint32_t host_no; + uint32_t flashnode_idx; + uint32_t count; + } set_flashnode; + struct msg_new_flashnode { + uint32_t host_no; + uint32_t len; + } new_flashnode; + struct msg_del_flashnode { + uint32_t host_no; + uint32_t flashnode_idx; + } del_flashnode; + struct msg_login_flashnode { + uint32_t host_no; + uint32_t flashnode_idx; + } login_flashnode; + struct msg_logout_flashnode { + uint32_t host_no; + uint32_t flashnode_idx; + } logout_flashnode; + struct msg_logout_flashnode_sid { + uint32_t host_no; + uint32_t sid; + } logout_flashnode_sid; + struct msg_get_host_stats { + uint32_t host_no; + } get_host_stats; + } u; union { /* messages k -> u */ @@ -276,6 +312,9 @@ struct iscsi_uevent { with each ping request */ uint32_t data_size; } ping_comp; + struct msg_new_flashnode_ret { + uint32_t flashnode_idx; + } new_flashnode_ret; } r; } __attribute__ ((aligned (sizeof(uint64_t)))); @@ -283,8 +322,18 @@ enum iscsi_param_type { ISCSI_PARAM, /* iscsi_param (session, conn, target, LU) */ ISCSI_HOST_PARAM, /* iscsi_host_param */ ISCSI_NET_PARAM, /* iscsi_net_param */ + ISCSI_FLASHNODE_PARAM, /* iscsi_flashnode_param */ + ISCSI_CHAP_PARAM, /* iscsi_chap_param */ + ISCSI_IFACE_PARAM, /* iscsi_iface_param */ }; +/* structure for minimalist usecase */ +struct iscsi_param_info { + uint32_t len; /* Actual length of the param value */ + uint16_t param; /* iscsi param */ + uint8_t value[0]; /* length sized value follows */ +} __attribute__((__packed__)); + struct iscsi_iface_param_info { uint32_t iface_num; /* iface number, 0 - n */ uint32_t len; /* Actual length of the param */ @@ -348,28 +397,106 @@ struct iscsi_path { #define ISCSI_VLAN_DISABLE 0x01 #define ISCSI_VLAN_ENABLE 0x02 +/* iscsi generic enable/disabled setting for various features */ +#define ISCSI_NET_PARAM_DISABLE 0x01 +#define ISCSI_NET_PARAM_ENABLE 0x02 + /* iSCSI network params */ enum iscsi_net_param { ISCSI_NET_PARAM_IPV4_ADDR = 1, - ISCSI_NET_PARAM_IPV4_SUBNET = 2, - ISCSI_NET_PARAM_IPV4_GW = 3, - ISCSI_NET_PARAM_IPV4_BOOTPROTO = 4, - ISCSI_NET_PARAM_MAC = 5, - ISCSI_NET_PARAM_IPV6_LINKLOCAL = 6, - ISCSI_NET_PARAM_IPV6_ADDR = 7, - ISCSI_NET_PARAM_IPV6_ROUTER = 8, - ISCSI_NET_PARAM_IPV6_ADDR_AUTOCFG = 9, - ISCSI_NET_PARAM_IPV6_LINKLOCAL_AUTOCFG = 10, - ISCSI_NET_PARAM_IPV6_ROUTER_AUTOCFG = 11, - ISCSI_NET_PARAM_IFACE_ENABLE = 12, - ISCSI_NET_PARAM_VLAN_ID = 13, - ISCSI_NET_PARAM_VLAN_PRIORITY = 14, - ISCSI_NET_PARAM_VLAN_ENABLED = 15, - ISCSI_NET_PARAM_VLAN_TAG = 16, - ISCSI_NET_PARAM_IFACE_TYPE = 17, - ISCSI_NET_PARAM_IFACE_NAME = 18, - ISCSI_NET_PARAM_MTU = 19, - ISCSI_NET_PARAM_PORT = 20, + ISCSI_NET_PARAM_IPV4_SUBNET, + ISCSI_NET_PARAM_IPV4_GW, + ISCSI_NET_PARAM_IPV4_BOOTPROTO, + ISCSI_NET_PARAM_MAC, + ISCSI_NET_PARAM_IPV6_LINKLOCAL, + ISCSI_NET_PARAM_IPV6_ADDR, + ISCSI_NET_PARAM_IPV6_ROUTER, + ISCSI_NET_PARAM_IPV6_ADDR_AUTOCFG, + ISCSI_NET_PARAM_IPV6_LINKLOCAL_AUTOCFG, + ISCSI_NET_PARAM_IPV6_ROUTER_AUTOCFG, + ISCSI_NET_PARAM_IFACE_ENABLE, + ISCSI_NET_PARAM_VLAN_ID, + ISCSI_NET_PARAM_VLAN_PRIORITY, + ISCSI_NET_PARAM_VLAN_ENABLED, + ISCSI_NET_PARAM_VLAN_TAG, + ISCSI_NET_PARAM_IFACE_TYPE, + ISCSI_NET_PARAM_IFACE_NAME, + ISCSI_NET_PARAM_MTU, + ISCSI_NET_PARAM_PORT, + ISCSI_NET_PARAM_IPADDR_STATE, + ISCSI_NET_PARAM_IPV6_LINKLOCAL_STATE, + ISCSI_NET_PARAM_IPV6_ROUTER_STATE, + ISCSI_NET_PARAM_DELAYED_ACK_EN, + ISCSI_NET_PARAM_TCP_NAGLE_DISABLE, + ISCSI_NET_PARAM_TCP_WSF_DISABLE, + ISCSI_NET_PARAM_TCP_WSF, + ISCSI_NET_PARAM_TCP_TIMER_SCALE, + ISCSI_NET_PARAM_TCP_TIMESTAMP_EN, + ISCSI_NET_PARAM_CACHE_ID, + ISCSI_NET_PARAM_IPV4_DHCP_DNS_ADDR_EN, + ISCSI_NET_PARAM_IPV4_DHCP_SLP_DA_EN, + ISCSI_NET_PARAM_IPV4_TOS_EN, + ISCSI_NET_PARAM_IPV4_TOS, + ISCSI_NET_PARAM_IPV4_GRAT_ARP_EN, + ISCSI_NET_PARAM_IPV4_DHCP_ALT_CLIENT_ID_EN, + ISCSI_NET_PARAM_IPV4_DHCP_ALT_CLIENT_ID, + ISCSI_NET_PARAM_IPV4_DHCP_REQ_VENDOR_ID_EN, + ISCSI_NET_PARAM_IPV4_DHCP_USE_VENDOR_ID_EN, + ISCSI_NET_PARAM_IPV4_DHCP_VENDOR_ID, + ISCSI_NET_PARAM_IPV4_DHCP_LEARN_IQN_EN, + ISCSI_NET_PARAM_IPV4_FRAGMENT_DISABLE, + ISCSI_NET_PARAM_IPV4_IN_FORWARD_EN, + ISCSI_NET_PARAM_IPV4_TTL, + ISCSI_NET_PARAM_IPV6_GRAT_NEIGHBOR_ADV_EN, + ISCSI_NET_PARAM_IPV6_MLD_EN, + ISCSI_NET_PARAM_IPV6_FLOW_LABEL, + ISCSI_NET_PARAM_IPV6_TRAFFIC_CLASS, + ISCSI_NET_PARAM_IPV6_HOP_LIMIT, + ISCSI_NET_PARAM_IPV6_ND_REACHABLE_TMO, + ISCSI_NET_PARAM_IPV6_ND_REXMIT_TIME, + ISCSI_NET_PARAM_IPV6_ND_STALE_TMO, + ISCSI_NET_PARAM_IPV6_DUP_ADDR_DETECT_CNT, + ISCSI_NET_PARAM_IPV6_RTR_ADV_LINK_MTU, + ISCSI_NET_PARAM_REDIRECT_EN, +}; + +enum iscsi_ipaddress_state { + ISCSI_IPDDRESS_STATE_UNCONFIGURED, + ISCSI_IPDDRESS_STATE_ACQUIRING, + ISCSI_IPDDRESS_STATE_TENTATIVE, + ISCSI_IPDDRESS_STATE_VALID, + ISCSI_IPDDRESS_STATE_DISABLING, + ISCSI_IPDDRESS_STATE_INVALID, + ISCSI_IPDDRESS_STATE_DEPRECATED, +}; + +enum iscsi_router_state { + ISCSI_ROUTER_STATE_UNKNOWN, + ISCSI_ROUTER_STATE_ADVERTISED, + ISCSI_ROUTER_STATE_MANUAL, + ISCSI_ROUTER_STATE_STALE, +}; + +/* iSCSI specific settings params for iface */ +enum iscsi_iface_param { + ISCSI_IFACE_PARAM_DEF_TASKMGMT_TMO, + ISCSI_IFACE_PARAM_HDRDGST_EN, + ISCSI_IFACE_PARAM_DATADGST_EN, + ISCSI_IFACE_PARAM_IMM_DATA_EN, + ISCSI_IFACE_PARAM_INITIAL_R2T_EN, + ISCSI_IFACE_PARAM_DATASEQ_INORDER_EN, + ISCSI_IFACE_PARAM_PDU_INORDER_EN, + ISCSI_IFACE_PARAM_ERL, + ISCSI_IFACE_PARAM_MAX_RECV_DLENGTH, + ISCSI_IFACE_PARAM_FIRST_BURST, + ISCSI_IFACE_PARAM_MAX_R2T, + ISCSI_IFACE_PARAM_MAX_BURST, + ISCSI_IFACE_PARAM_CHAP_AUTH_EN, + ISCSI_IFACE_PARAM_BIDI_CHAP_EN, + ISCSI_IFACE_PARAM_DISCOVERY_AUTH_OPTIONAL, + ISCSI_IFACE_PARAM_DISCOVERY_LOGOUT_EN, + ISCSI_IFACE_PARAM_STRICT_LOGIN_COMP_EN, + ISCSI_IFACE_PARAM_INITIATOR_NAME, }; enum iscsi_conn_state { @@ -460,61 +587,157 @@ enum iscsi_param { ISCSI_PARAM_TGT_RESET_TMO, ISCSI_PARAM_TARGET_ALIAS, + + ISCSI_PARAM_CHAP_IN_IDX, + ISCSI_PARAM_CHAP_OUT_IDX, + + ISCSI_PARAM_BOOT_ROOT, + ISCSI_PARAM_BOOT_NIC, + ISCSI_PARAM_BOOT_TARGET, + + ISCSI_PARAM_AUTO_SND_TGT_DISABLE, + ISCSI_PARAM_DISCOVERY_SESS, + ISCSI_PARAM_PORTAL_TYPE, + ISCSI_PARAM_CHAP_AUTH_EN, + ISCSI_PARAM_DISCOVERY_LOGOUT_EN, + ISCSI_PARAM_BIDI_CHAP_EN, + ISCSI_PARAM_DISCOVERY_AUTH_OPTIONAL, + + ISCSI_PARAM_DEF_TIME2WAIT, + ISCSI_PARAM_DEF_TIME2RETAIN, + ISCSI_PARAM_MAX_SEGMENT_SIZE, + ISCSI_PARAM_STATSN, + ISCSI_PARAM_KEEPALIVE_TMO, + ISCSI_PARAM_LOCAL_PORT, + ISCSI_PARAM_TSID, + ISCSI_PARAM_DEF_TASKMGMT_TMO, + + ISCSI_PARAM_TCP_TIMESTAMP_STAT, + ISCSI_PARAM_TCP_WSF_DISABLE, + ISCSI_PARAM_TCP_NAGLE_DISABLE, + ISCSI_PARAM_TCP_TIMER_SCALE, + ISCSI_PARAM_TCP_TIMESTAMP_EN, + ISCSI_PARAM_TCP_XMIT_WSF, + ISCSI_PARAM_TCP_RECV_WSF, + ISCSI_PARAM_IP_FRAGMENT_DISABLE, + ISCSI_PARAM_IPV4_TOS, + ISCSI_PARAM_IPV6_TC, + ISCSI_PARAM_IPV6_FLOW_LABEL, + ISCSI_PARAM_IS_FW_ASSIGNED_IPV6, + + ISCSI_PARAM_DISCOVERY_PARENT_IDX, + ISCSI_PARAM_DISCOVERY_PARENT_TYPE, /* must always be last */ ISCSI_PARAM_MAX, }; -#define ISCSI_MAX_RECV_DLENGTH (1ULL << ISCSI_PARAM_MAX_RECV_DLENGTH) -#define ISCSI_MAX_XMIT_DLENGTH (1ULL << ISCSI_PARAM_MAX_XMIT_DLENGTH) -#define ISCSI_HDRDGST_EN (1ULL << ISCSI_PARAM_HDRDGST_EN) -#define ISCSI_DATADGST_EN (1ULL << ISCSI_PARAM_DATADGST_EN) -#define ISCSI_INITIAL_R2T_EN (1ULL << ISCSI_PARAM_INITIAL_R2T_EN) -#define ISCSI_MAX_R2T (1ULL << ISCSI_PARAM_MAX_R2T) -#define ISCSI_IMM_DATA_EN (1ULL << ISCSI_PARAM_IMM_DATA_EN) -#define ISCSI_FIRST_BURST (1ULL << ISCSI_PARAM_FIRST_BURST) -#define ISCSI_MAX_BURST (1ULL << ISCSI_PARAM_MAX_BURST) -#define ISCSI_PDU_INORDER_EN (1ULL << ISCSI_PARAM_PDU_INORDER_EN) -#define ISCSI_DATASEQ_INORDER_EN (1ULL << ISCSI_PARAM_DATASEQ_INORDER_EN) -#define ISCSI_ERL (1ULL << ISCSI_PARAM_ERL) -#define ISCSI_IFMARKER_EN (1ULL << ISCSI_PARAM_IFMARKER_EN) -#define ISCSI_OFMARKER_EN (1ULL << ISCSI_PARAM_OFMARKER_EN) -#define ISCSI_EXP_STATSN (1ULL << ISCSI_PARAM_EXP_STATSN) -#define ISCSI_TARGET_NAME (1ULL << ISCSI_PARAM_TARGET_NAME) -#define ISCSI_TPGT (1ULL << ISCSI_PARAM_TPGT) -#define ISCSI_PERSISTENT_ADDRESS (1ULL << ISCSI_PARAM_PERSISTENT_ADDRESS) -#define ISCSI_PERSISTENT_PORT (1ULL << ISCSI_PARAM_PERSISTENT_PORT) -#define ISCSI_SESS_RECOVERY_TMO (1ULL << ISCSI_PARAM_SESS_RECOVERY_TMO) -#define ISCSI_CONN_PORT (1ULL << ISCSI_PARAM_CONN_PORT) -#define ISCSI_CONN_ADDRESS (1ULL << ISCSI_PARAM_CONN_ADDRESS) -#define ISCSI_USERNAME (1ULL << ISCSI_PARAM_USERNAME) -#define ISCSI_USERNAME_IN (1ULL << ISCSI_PARAM_USERNAME_IN) -#define ISCSI_PASSWORD (1ULL << ISCSI_PARAM_PASSWORD) -#define ISCSI_PASSWORD_IN (1ULL << ISCSI_PARAM_PASSWORD_IN) -#define ISCSI_FAST_ABORT (1ULL << ISCSI_PARAM_FAST_ABORT) -#define ISCSI_ABORT_TMO (1ULL << ISCSI_PARAM_ABORT_TMO) -#define ISCSI_LU_RESET_TMO (1ULL << ISCSI_PARAM_LU_RESET_TMO) -#define ISCSI_HOST_RESET_TMO (1ULL << ISCSI_PARAM_HOST_RESET_TMO) -#define ISCSI_PING_TMO (1ULL << ISCSI_PARAM_PING_TMO) -#define ISCSI_RECV_TMO (1ULL << ISCSI_PARAM_RECV_TMO) -#define ISCSI_IFACE_NAME (1ULL << ISCSI_PARAM_IFACE_NAME) -#define ISCSI_ISID (1ULL << ISCSI_PARAM_ISID) -#define ISCSI_INITIATOR_NAME (1ULL << ISCSI_PARAM_INITIATOR_NAME) -#define ISCSI_TGT_RESET_TMO (1ULL << ISCSI_PARAM_TGT_RESET_TMO) -#define ISCSI_TARGET_ALIAS (1ULL << ISCSI_PARAM_TARGET_ALIAS) - /* iSCSI HBA params */ enum iscsi_host_param { ISCSI_HOST_PARAM_HWADDRESS, ISCSI_HOST_PARAM_INITIATOR_NAME, ISCSI_HOST_PARAM_NETDEV_NAME, ISCSI_HOST_PARAM_IPADDRESS, + ISCSI_HOST_PARAM_PORT_STATE, + ISCSI_HOST_PARAM_PORT_SPEED, ISCSI_HOST_PARAM_MAX, }; -#define ISCSI_HOST_HWADDRESS (1ULL << ISCSI_HOST_PARAM_HWADDRESS) -#define ISCSI_HOST_INITIATOR_NAME (1ULL << ISCSI_HOST_PARAM_INITIATOR_NAME) -#define ISCSI_HOST_NETDEV_NAME (1ULL << ISCSI_HOST_PARAM_NETDEV_NAME) -#define ISCSI_HOST_IPADDRESS (1ULL << ISCSI_HOST_PARAM_IPADDRESS) +/* portal type */ +#define PORTAL_TYPE_IPV4 "ipv4" +#define PORTAL_TYPE_IPV6 "ipv6" + +/* iSCSI Flash Target params */ +enum iscsi_flashnode_param { + ISCSI_FLASHNODE_IS_FW_ASSIGNED_IPV6, + ISCSI_FLASHNODE_PORTAL_TYPE, + ISCSI_FLASHNODE_AUTO_SND_TGT_DISABLE, + ISCSI_FLASHNODE_DISCOVERY_SESS, + ISCSI_FLASHNODE_ENTRY_EN, + ISCSI_FLASHNODE_HDR_DGST_EN, + ISCSI_FLASHNODE_DATA_DGST_EN, + ISCSI_FLASHNODE_IMM_DATA_EN, + ISCSI_FLASHNODE_INITIAL_R2T_EN, + ISCSI_FLASHNODE_DATASEQ_INORDER, + ISCSI_FLASHNODE_PDU_INORDER, + ISCSI_FLASHNODE_CHAP_AUTH_EN, + ISCSI_FLASHNODE_SNACK_REQ_EN, + ISCSI_FLASHNODE_DISCOVERY_LOGOUT_EN, + ISCSI_FLASHNODE_BIDI_CHAP_EN, + /* make authentication for discovery sessions optional */ + ISCSI_FLASHNODE_DISCOVERY_AUTH_OPTIONAL, + ISCSI_FLASHNODE_ERL, + ISCSI_FLASHNODE_TCP_TIMESTAMP_STAT, + ISCSI_FLASHNODE_TCP_NAGLE_DISABLE, + ISCSI_FLASHNODE_TCP_WSF_DISABLE, + ISCSI_FLASHNODE_TCP_TIMER_SCALE, + ISCSI_FLASHNODE_TCP_TIMESTAMP_EN, + ISCSI_FLASHNODE_IP_FRAG_DISABLE, + ISCSI_FLASHNODE_MAX_RECV_DLENGTH, + ISCSI_FLASHNODE_MAX_XMIT_DLENGTH, + ISCSI_FLASHNODE_FIRST_BURST, + ISCSI_FLASHNODE_DEF_TIME2WAIT, + ISCSI_FLASHNODE_DEF_TIME2RETAIN, + ISCSI_FLASHNODE_MAX_R2T, + ISCSI_FLASHNODE_KEEPALIVE_TMO, + ISCSI_FLASHNODE_ISID, + ISCSI_FLASHNODE_TSID, + ISCSI_FLASHNODE_PORT, + ISCSI_FLASHNODE_MAX_BURST, + ISCSI_FLASHNODE_DEF_TASKMGMT_TMO, + ISCSI_FLASHNODE_IPADDR, + ISCSI_FLASHNODE_ALIAS, + ISCSI_FLASHNODE_REDIRECT_IPADDR, + ISCSI_FLASHNODE_MAX_SEGMENT_SIZE, + ISCSI_FLASHNODE_LOCAL_PORT, + ISCSI_FLASHNODE_IPV4_TOS, + ISCSI_FLASHNODE_IPV6_TC, + ISCSI_FLASHNODE_IPV6_FLOW_LABEL, + ISCSI_FLASHNODE_NAME, + ISCSI_FLASHNODE_TPGT, + ISCSI_FLASHNODE_LINK_LOCAL_IPV6, + ISCSI_FLASHNODE_DISCOVERY_PARENT_IDX, + ISCSI_FLASHNODE_DISCOVERY_PARENT_TYPE, + ISCSI_FLASHNODE_TCP_XMIT_WSF, + ISCSI_FLASHNODE_TCP_RECV_WSF, + ISCSI_FLASHNODE_CHAP_IN_IDX, + ISCSI_FLASHNODE_CHAP_OUT_IDX, + ISCSI_FLASHNODE_USERNAME, + ISCSI_FLASHNODE_USERNAME_IN, + ISCSI_FLASHNODE_PASSWORD, + ISCSI_FLASHNODE_PASSWORD_IN, + ISCSI_FLASHNODE_STATSN, + ISCSI_FLASHNODE_EXP_STATSN, + ISCSI_FLASHNODE_IS_BOOT_TGT, + + ISCSI_FLASHNODE_MAX, +}; + +struct iscsi_flashnode_param_info { + uint32_t len; /* Actual length of the param */ + uint16_t param; /* iscsi param value */ + uint8_t value[0]; /* length sized value follows */ +} __attribute__((__packed__)); + +enum iscsi_discovery_parent_type { + ISCSI_DISC_PARENT_UNKNOWN = 0x1, + ISCSI_DISC_PARENT_SENDTGT = 0x2, + ISCSI_DISC_PARENT_ISNS = 0x3, +}; + +/* iSCSI port Speed */ +enum iscsi_port_speed { + ISCSI_PORT_SPEED_UNKNOWN = 0x1, + ISCSI_PORT_SPEED_10MBPS = 0x2, + ISCSI_PORT_SPEED_100MBPS = 0x4, + ISCSI_PORT_SPEED_1GBPS = 0x8, + ISCSI_PORT_SPEED_10GBPS = 0x10, +}; + +/* iSCSI port state */ +enum iscsi_port_state { + ISCSI_PORT_STATE_DOWN = 0x1, + ISCSI_PORT_STATE_UP = 0x2, +}; /* iSCSI PING status/error code */ enum iscsi_ping_status_code { @@ -551,7 +774,7 @@ enum iscsi_ping_status_code { #define CAP_DIGEST_OFFLOAD 0x1000 /* offload hdr and data digests */ #define CAP_PADDING_OFFLOAD 0x2000 /* offload padding insertion, removal, and verification */ -#define CAP_LOGIN_OFFLOAD 0x4000 /* offload normal session login */ +#define CAP_LOGIN_OFFLOAD 0x4000 /* offload session login */ /* * These flags describes reason of stop_conn() call @@ -617,9 +840,16 @@ enum chap_type_e { CHAP_TYPE_IN, }; +enum iscsi_chap_param { + ISCSI_CHAP_PARAM_INDEX, + ISCSI_CHAP_PARAM_CHAP_TYPE, + ISCSI_CHAP_PARAM_USERNAME, + ISCSI_CHAP_PARAM_PASSWORD, + ISCSI_CHAP_PARAM_PASSWORD_LEN +}; + #define ISCSI_CHAP_AUTH_NAME_MAX_LEN 256 #define ISCSI_CHAP_AUTH_SECRET_MAX_LEN 256 - struct iscsi_chap_rec { uint16_t chap_tbl_idx; enum chap_type_e chap_type; @@ -628,4 +858,112 @@ struct iscsi_chap_rec { uint8_t password_length; }; +#define ISCSI_HOST_STATS_CUSTOM_MAX 32 +#define ISCSI_HOST_STATS_CUSTOM_DESC_MAX 64 +struct iscsi_host_stats_custom { + char desc[ISCSI_HOST_STATS_CUSTOM_DESC_MAX]; + uint64_t value; +}; + +/* struct iscsi_offload_host_stats: Host statistics, + * Include statistics for MAC, IP, TCP & iSCSI. + */ +struct iscsi_offload_host_stats { + /* MAC */ + uint64_t mactx_frames; + uint64_t mactx_bytes; + uint64_t mactx_multicast_frames; + uint64_t mactx_broadcast_frames; + uint64_t mactx_pause_frames; + uint64_t mactx_control_frames; + uint64_t mactx_deferral; + uint64_t mactx_excess_deferral; + uint64_t mactx_late_collision; + uint64_t mactx_abort; + uint64_t mactx_single_collision; + uint64_t mactx_multiple_collision; + uint64_t mactx_collision; + uint64_t mactx_frames_dropped; + uint64_t mactx_jumbo_frames; + uint64_t macrx_frames; + uint64_t macrx_bytes; + uint64_t macrx_unknown_control_frames; + uint64_t macrx_pause_frames; + uint64_t macrx_control_frames; + uint64_t macrx_dribble; + uint64_t macrx_frame_length_error; + uint64_t macrx_jabber; + uint64_t macrx_carrier_sense_error; + uint64_t macrx_frame_discarded; + uint64_t macrx_frames_dropped; + uint64_t mac_crc_error; + uint64_t mac_encoding_error; + uint64_t macrx_length_error_large; + uint64_t macrx_length_error_small; + uint64_t macrx_multicast_frames; + uint64_t macrx_broadcast_frames; + /* IP */ + uint64_t iptx_packets; + uint64_t iptx_bytes; + uint64_t iptx_fragments; + uint64_t iprx_packets; + uint64_t iprx_bytes; + uint64_t iprx_fragments; + uint64_t ip_datagram_reassembly; + uint64_t ip_invalid_address_error; + uint64_t ip_error_packets; + uint64_t ip_fragrx_overlap; + uint64_t ip_fragrx_outoforder; + uint64_t ip_datagram_reassembly_timeout; + uint64_t ipv6tx_packets; + uint64_t ipv6tx_bytes; + uint64_t ipv6tx_fragments; + uint64_t ipv6rx_packets; + uint64_t ipv6rx_bytes; + uint64_t ipv6rx_fragments; + uint64_t ipv6_datagram_reassembly; + uint64_t ipv6_invalid_address_error; + uint64_t ipv6_error_packets; + uint64_t ipv6_fragrx_overlap; + uint64_t ipv6_fragrx_outoforder; + uint64_t ipv6_datagram_reassembly_timeout; + /* TCP */ + uint64_t tcptx_segments; + uint64_t tcptx_bytes; + uint64_t tcprx_segments; + uint64_t tcprx_byte; + uint64_t tcp_duplicate_ack_retx; + uint64_t tcp_retx_timer_expired; + uint64_t tcprx_duplicate_ack; + uint64_t tcprx_pure_ackr; + uint64_t tcptx_delayed_ack; + uint64_t tcptx_pure_ack; + uint64_t tcprx_segment_error; + uint64_t tcprx_segment_outoforder; + uint64_t tcprx_window_probe; + uint64_t tcprx_window_update; + uint64_t tcptx_window_probe_persist; + /* ECC */ + uint64_t ecc_error_correction; + /* iSCSI */ + uint64_t iscsi_pdu_tx; + uint64_t iscsi_data_bytes_tx; + uint64_t iscsi_pdu_rx; + uint64_t iscsi_data_bytes_rx; + uint64_t iscsi_io_completed; + uint64_t iscsi_unexpected_io_rx; + uint64_t iscsi_format_error; + uint64_t iscsi_hdr_digest_error; + uint64_t iscsi_data_digest_error; + uint64_t iscsi_sequence_error; + /* + * iSCSI Custom Host Statistics support, i.e. Transport could + * extend existing host statistics with its own specific statistics + * up to ISCSI_HOST_STATS_CUSTOM_MAX + */ + uint32_t custom_length; + struct iscsi_host_stats_custom custom[0] + __attribute__ ((aligned (sizeof(uint64_t)))); +}; + #endif diff --git a/include/iscsi_proto.h b/include/iscsi_proto.h index 1c69feb..56f757b 100644 --- a/include/iscsi_proto.h +++ b/include/iscsi_proto.h @@ -619,6 +619,7 @@ struct iscsi_reject { #define KEY_MAXLEN 64 #define VALUE_MAXLEN 255 #define TARGET_NAME_MAXLEN VALUE_MAXLEN +#define BOOT_NAME_MAXLEN 256 #define ISCSI_DEF_MAX_RECV_SEG_LEN 8192 #define ISCSI_MIN_MAX_RECV_SEG_LEN 512 diff --git a/include/list.h b/include/list.h index cccc3c3..94ad99b 100644 --- a/include/list.h +++ b/include/list.h @@ -38,6 +38,12 @@ static inline int list_empty(const struct list_head *head) #define list_entry(ptr, type, member) \ list_container_of(ptr, type, member) +#define list_first_entry(ptr, type, member) \ + list_entry((ptr)->next, type, member) + +#define list_first_entry_or_null(ptr, type, member) \ + (!list_empty(ptr) ? list_first_entry(ptr, type, member) : NULL) + #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); pos = pos->next) diff --git a/iscsiuio/.gitignore b/iscsiuio/.gitignore new file mode 100644 index 0000000..a27452a --- /dev/null +++ b/iscsiuio/.gitignore @@ -0,0 +1,25 @@ +# Autogenerated files +stamp-h1 +Makefile.in +Makefile +configure +config.h.in +config.h +config.guess +config.log +config.status +config.sub +COPYING + +.deps +autom4te.cache + +# autotools +aclocal.m4 +compile +depcomp +install-sh +libtool +ltmain.sh +missing + diff --git a/iscsiuio/AUTHORS b/iscsiuio/AUTHORS new file mode 100644 index 0000000..e69de29 diff --git a/iscsiuio/ChangeLog b/iscsiuio/ChangeLog new file mode 100644 index 0000000..a91b4d5 --- /dev/null +++ b/iscsiuio/ChangeLog @@ -0,0 +1,7 @@ +Version 0.4.1 (July 20, 2009) + * Fix from Mike Christie to determine page size from getpagesize() + rather then the constant PAGE_SIZE. PAGE_SIZE is not defined om + ia64 and ppc. + * Update documentation to indicate IPv6 is not supported + * Fix code to catch the message from the CNIC that the network + interface is going down. diff --git a/iscsiuio/INSTALL b/iscsiuio/INSTALL new file mode 100644 index 0000000..c9fd2c0 --- /dev/null +++ b/iscsiuio/INSTALL @@ -0,0 +1,290 @@ +Installation Instructions +************************* + +Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, +2006, 2007, 2008 Free Software Foundation, Inc. + + This file is free documentation; the Free Software Foundation gives +unlimited permission to copy, distribute and modify it. + +Basic Installation +================== + + Briefly, the shell commands `./configure; make; make install' should +configure, build, and install this package. The following +more-detailed instructions are generic; see the `README' file for +instructions specific to this package. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. Caching is +disabled by default to prevent problems with accidental use of stale +cache files. + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You need `configure.ac' if +you want to change it or regenerate `configure' using a newer version +of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. + + Running `configure' might take a while. While running, it prints + some messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + + 6. Often, you can also type `make uninstall' to remove the installed + files again. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. Run `./configure --help' +for details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c99 CFLAGS=-g LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you can use GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + With a non-GNU `make', it is safer to compile the package for one +architecture at a time in the source code directory. After you have +installed the package for one architecture, use `make distclean' before +reconfiguring for another architecture. + + On MacOS X 10.5 and later systems, you can create libraries and +executables that work on multiple system types--known as "fat" or +"universal" binaries--by specifying multiple `-arch' options to the +compiler but only a single `-arch' option to the preprocessor. Like +this: + + ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CPP="gcc -E" CXXCPP="g++ -E" + + This is not guaranteed to produce working output in all cases, you +may have to build one architecture at a time and combine the results +using the `lipo' tool if you have problems. + +Installation Names +================== + + By default, `make install' installs the package's commands under +`/usr/local/bin', include files under `/usr/local/include', etc. You +can specify an installation prefix other than `/usr/local' by giving +`configure' the option `--prefix=PREFIX'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +pass the option `--exec-prefix=PREFIX' to `configure', the package uses +PREFIX as the prefix for installing programs and libraries. +Documentation and other data files still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=DIR' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Particular systems +================== + + On HP-UX, the default C compiler is not ANSI C compatible. If GNU +CC is not installed, it is recommended to use the following options in +order to use an ANSI C compiler: + + ./configure CC="cc -Ae" + +and if that doesn't work, install pre-built binaries of GCC for HP-UX. + + On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot +parse its `' header file. The option `-nodtk' can be used as +a workaround. If GNU CC is not installed, it is therefore recommended +to try + + ./configure CC="cc" + +and if that doesn't work, try + + ./configure CC="cc -nodtk" + +Specifying the System Type +========================== + + There may be some features `configure' cannot figure out +automatically, but needs to determine by the type of machine the package +will run on. Usually, assuming the package is built to be run on the +_same_ architectures, `configure' can figure that out, but if it prints +a message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the option `--target=TYPE' to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + + Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +causes the specified `gcc' to be used as the C compiler (unless it is +overridden in the site shell script). + +Unfortunately, this technique does not work for `CONFIG_SHELL' due to +an Autoconf bug. Until the bug is fixed you can use this workaround: + + CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash + +`configure' Invocation +====================== + + `configure' recognizes the following options to control how it +operates. + +`--help' +`-h' + Print a summary of all of the options to `configure', and exit. + +`--help=short' +`--help=recursive' + Print a summary of the options unique to this package's + `configure', and exit. The `short' variant lists options used + only in the top level, while the `recursive' variant lists options + also present in any nested packages. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--prefix=DIR' + Use DIR as the installation prefix. *Note Installation Names:: + for more details, including other options available for fine-tuning + the installation locations. + +`--no-create' +`-n' + Run the configure checks, but stop before creating any output + files. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. diff --git a/iscsiuio/Makefile.am b/iscsiuio/Makefile.am new file mode 100644 index 0000000..28dd776 --- /dev/null +++ b/iscsiuio/Makefile.am @@ -0,0 +1,25 @@ +SUBDIRS= src + +EXTRA_DIST = build_date + +build_date: + echo 'char *build_date = "'`date`'";' > build_date.c + echo 'char *build_date;'> build_date.h + +manprefix = /usr/share +mandir = ${manprefix}/man +logdir = /etc/logrotate.d + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am install-man install-log install-brcm + +install-man: + cat docs/iscsiuio.8 | GZIP=$(GZIP_ENV) gzip -c > iscsiuio.8.gz + $(INSTALL_PROGRAM) iscsiuio.8.gz $(mandir)/man8 + +install-log: + $(INSTALL_PROGRAM) iscsiuiolog $(logdir) + +install-brcm: + -rm -f $(sbindir)/brcm_iscsiuio + -ln -s $(sbindir)/iscsiuio $(sbindir)/brcm_iscsiuio diff --git a/iscsiuio/NEWS b/iscsiuio/NEWS new file mode 100644 index 0000000..e69de29 diff --git a/iscsiuio/README b/iscsiuio/README new file mode 100644 index 0000000..9ae1411 --- /dev/null +++ b/iscsiuio/README @@ -0,0 +1,224 @@ +Iscsiuio Userspace Tool +Version 0.7.8.2 +Dec 10, 2013 +------------------------------------------------------ + +This tool is to be used in conjunction with the QLogic NetXtreme II Linux +driver (Kernel module name: 'bnx2' and 'bnx2x'), QLogic CNIC driver, +and the QLogic iSCSI driver (Kernel module name: 'bnx2i'). +This user space tool is used in conjunction with the following +QLogic Network Controllers: + bnx2: BCM5706, BCM5708, BCM5709 devices + bnx2x: BCM57710, BCM57711, BCM57711E, BCM57712, BCM57712E, + BCM57800, BCM57810, BCM57840 devices + +This utility will provide the ARP and DHCP functionality for the iSCSI offload. +The communication to the driver is done via Userspace I/O (Kernel module name +'uio'). + +There is one component to this application: + +1. 'iscsiuio' - This is the daemon which aids in creating iSCSI offloaded + connections. + +Dependencies: +======================================= + +Linux Kernel Dependencies: +1. QLogic CNIC driver (cnic) +1. QLogic iSCSI offload driver (bnx2i) +2. Userspace I/O driver (uio) + +Directory Structure of this Package: +======================================= + + + | + +-doc (documentation directory: man pages) + | + +-src + | + +- uip - the uIP stack + | + +- unix - iscsiuio source + + + +Compiling / Installing +======================================= + +1. Please untar the tarball. +2. Run the configure script. This will create the Makefiles and proper + header files needed for the build. +3. Run 'make'. This will create the binary, 'iscsiuio' +4. Run 'make install' to place the binaries in their installed location. + (The default location is '/sbin') + +iscsid IFACE Configuration File: +======================================= +The network interface configuration files are driven by the iscsid iface +files. The configuration data is parsed by iscsid and passed to the uIP +stack when the connection is established. + +One can use the following iscsiadm commands to create/set the configuration +using the iface files: + +1. Create the iface file: + + iscsiadm -m iface -I --op=new + +2. Discover the targets associated with the new iface + + iscsiadm -m discovery -t st -p -I + +3. Update the iface file: + + To use a static IPv4 address: + iscsiadm -m iface -I --op=update --name=iface.ipaddress --value= + + To use a DHCP address: + iscsiadm -m iface -I --op=update --name=iface.ipaddress --value=0.0.0.0 + + The following values are required. + + To specify the bnx2i as the transport: + iscsiadm -m iface -I --op=update --name=iface.transport_name --value=bnx2i + + To specify the network interface to offload with: + + a. Specify the physical network interface name + iscsiadm -m iface -I --op=update --name=iface.net_ifacename --value= + + b. Specify the iSCSI MAC address of the iSCSI HBA + iscsiadm -m iface -I --op=update --name=iface.hwaddress --value= + +4. Now all the settings should be set so that one could connect to their + desired iSCSI target. + + iscsiadm -m node -p -T -I --login + +bnx2 Limitations: +======================================= +* RX iSCSI ring: + * default ring size is 3 entries + * default buffer size is 0x400 bytes +* TX iSCSI ring: + * default ring size of 1 entry + * default buffer size is 0x400 bytes + +bnx2x Limitations: +======================================= +* RX iSCSI ring: + * default ring size is 15 entries + * default buffer size is 0x400 bytes +* TX iSCSI ring: + * default ring size of 1 entry + * default buffer size is 0x400 bytes + +Other Limiations: + +Any packets larger then the buffer size will not be sent/received by the +hardware and will be dropped. + +IPv6 support: + +IPv6 NDP (neighbor discovery protocol), DHCPv6 and Static IPv6 are now +supported. The IPv6 address used for the connection will be matched against +the DHCPv6/static IPv6 address, the RA (router advertise) address, and the +assigned link local address. + +VLAN support: + +VLAN support is only supported when using static IP addresses. +Also, currently only 1 VLAN is supported per physical network interface. +Either non-VLAN offloaded traffic is allowed or VLAN offloaded traffic +is allowed. The current implementation does not support both at the +same time. + +Currently there is no explicit VLAN attributes in the iface file. +To configure the VLAN offload, the iface.hwaddress attribute or +physical net_ifacename (without the VLAN identifier) must be used +to specify the HBA device. For the proper CNIC routing, the +corresponding L2 interface which has the associated VLAN interface must +have an IP address on the same subnet. + +The following attributes need to be filled when offloading via the +VLAN interface: + + iface.iscsi_ifacename = + iface.hwaddress = XX:XX:XX:XX:XX:XX + iface.ipaddress = XX.XX.XX.XX + iface.transport_name = bnx2i + +Setting IP address: + +On RHEL5.4, RHEL5.5+, RHEL6.0+, and SLES11SP1 distributions, +discovery login is done over the Linux TCP/IP stack and L2 network +interface. The ethx interface corresponding to the HBA must +therefore be in the same IP subnet in order to reach the iSCSI +target during discovery. However, the HBA's IP address should not +be the same as the L2 ethx's IP address. + +Starting with RHEL6.1 and all other newer distributions, discovery +using SendTargets is done over the HBA interface, so there is no +need for the HBA and L2 network to be on the same subnet. However, +if VLAN is used on the HBA, they still have to be on the same subnet +as described above. + + +Setting Netmask and Gateway addresses: + +With the current limitations of the iface file, there are no entries +to allow the user to enter a netmask or gateway IP address. + +The only way to explicitly configure these options is to use DHCP +addressing. Then the netmask/gateway are set on the DHCP server. +These settings are then sent to uIP via the DHCPOFFERs. + +If the netmask is not defined then the netmask are automatically +generated depending on the destination IP address. + +Debugging: +======================================= + +By default, the iscsiuio daemon does not output any messages to the log file, +'/var/log/iscsiuio.log'. Message logging is only enabled when the daemon is +run under debug mode. + +To run the daemon in debug mode please pass the parameter '-d ' + +where the following debug levels are defined: + +DEBUG 4 - Print all messages +INFO 3 - Print messages needed to follow the uIP code (default) +WARN 2 - Print warning messages +ERROR 1 - Only print critical errors + +A sample banner message: + +INFO [Mon Jun 20 11:23:14 2011]Started iSCSI uio stack: Ver 0.7.0.6 +INFO [Mon Jun 20 11:23:14 2011]Build date: Mon Jun 20 11:22:05 PDT 2011 +INFO [Mon Jun 20 11:23:14 2011]Debug mode enabled + +These messages can be used to help debug any issues. + +When debugging issues like the iscsid, the iscsiuio daemon can be run +in the foreground and the maximum debugging level should be used. + +To place the daemon in foreground mode please pass the parameter '-f' + +Note: The messages to the log file are not flushed unless debugging is enabled. + +Note: If the daemon iscsiuio is running, one will not be able to + trample over the existing binary. One might see the following message: + + 'cannot create regular file `/sbin/iscsiuio': Text file busy' + + The solve this, please stop the iscsid service and then install. + +Warning: If full debug is enabled, this may quickly fill the partition +containing the iscsiuio logs. This is because full debugging will log +packet activity which on a busy network will quickly fill the logs. + +Note: If the bnx2i and cnic drivers are unloaded, then iscsiuio will also +need to be restarted so that it can determine the iscsid version. diff --git a/iscsiuio/RELEASE.TXT b/iscsiuio/RELEASE.TXT new file mode 100644 index 0000000..44d67f9 --- /dev/null +++ b/iscsiuio/RELEASE.TXT @@ -0,0 +1,2032 @@ + Release Notes + QLogic uIP Linux Driver + Version 0.7.8.2 + 12/10/2013 + + QLogic Corporation + 26650 Aliso Viejo Pkwy, + Aliso Viejo, CA 92656 + + Copyright (c) 2004 - 2013 Broadcom Corporation + Copyright (c) 2014, QLogic Corporation + All rights reserved + +uIP v0.7.10.2 (Feb 12, 2014) +======================================================= + Fixes + ----- + 1. Problem: Cont00072504 - ifconfig shows allocation failure after + up/down few hours with iSCSI + L2 traffic + Cause: A memory leak was discovered in the ongoing pthread creation + destruction code during the connection recovery process + Change: Fixed the pthread creation code + Impact: All + + +uIP v0.7.8.2 (Dec 10, 2013) +======================================================= + Fixes + ----- + 1. Problem: Cont00072053 - Some hardware iSCSI paths fail during test + Cause: The test exercised a corner case where the ARP cache flush + mechanism didn't work properly + Change: Fixed the ARP cache flush mechanism + Impact: All + + Enhancements + ------------ + 1. Change: Added a new tx doorbell field in the uio path to work with + the new bnx2x/cnic drivers that supports VF_RSS + Impact: 10G only + + 2. Change: Fixed the iface.subnet_mask decoding for IPv6 + Impact: IPv6 + + +uIP v0.7.8.1b (May 01, 2013) +======================================================= + Enhancements + ------------ + 1. Change: Performance optimization by caching the page size + Impact: All + + 2. Change: Fixed a bug in the tx completion interrupt handler + Impact: 10G only + + +uIP v0.7.6.1g (Jan 14, 2013) +======================================================= + Fixes + ----- + 1. Problem: Cont00067316 - IPv6 address prefix length < 32 + bits fails to connect + Cause: CIDR notation has an order bug in the IPv6 section + whenever the prefix length specified is < 32 + Change: Fixed the network order bug + Impact: IPv6 only + + +uIP v0.7.6.1f (Nov 14, 2012) +======================================================= + Fixes + ----- + 1. Problem: Cont00065768 - RHEL5.X iscsiuio segfault possible + if there is a specific 1024 byte size broadcast + packet + Cause: This is another corner case where the packet size + is also exactly 1024 bytes + padding that exceeded + the DMA rx buffer. The previous fix was not + sufficient + Change: Ensure that the packet size + padding do not + exceed this limit. + Impact: 10G only. 1G already has the guard against it. + + +uIP v0.7.6.1e (Nov 07, 2012) +======================================================= + Fixes + ----- + 1. Problem: Cont00066397 - Unable to connect to iSCSI target + with NPAR enabled on 57840 + Cause: The PCI device ID for 57840_MF has been changed from + 0x16ab to 0x16a4 + Change: Updated the PCI id table to match exactly what the + bnx2x 1.76 indicates + Impact: 57840 MF + + +uIP v0.7.6.1d (Oct 31, 2012) +======================================================= + Enhancements + ------------ + 1. Change: Added support for open-iscsi-2.0.873 + Impact: All + + +uIP v0.7.6.1c (Oct 15, 2012) +======================================================= + Enhancements + ------------ + 1. Change: Added support for 10G 57840 4x10 and 2x20 + Impact: 10G 57840 + + +uIP v0.7.6.1b (Oct 09, 2012) +======================================================= + Fixes + ----- + 1. Problem: Cont00065690 - Vconfig method of connecting over + tagged vlan with IPv6 failed + Cause: The new net param support changes has prevented + the old vconfig method from execising the IPv6 + acquisition engine properly + Change: Ensure that this old vconfig method to run the IPv6 + acquisition engine properly and to its entirety + Impact: IPv6 + VLAN using the network VLAN configuration + method + + 2. Problem: Cont00065768 - RHEL5.X iscsiuio segfault possible + if there is a specific 1024 byte size broadcast + packet + Cause: This is a corner case where the packet size is + exactly 1024 bytes + padding that exceeded the + DMA rx buffer. This has been there since day 1. + Change: Ensure that the packet size + padding do not + exceed this limit. + Impact: 10G only. 1G already has the guard against it. + + + Enhancements + ------------ + 1. Change: Source optimization - backported source code fixes + as reported from the upstream submission patch + Impact: ALL + + +uIP v0.7.4.2k (Aug 10, 2012) +======================================================= + Enhancements + ------------ + 1. Change: Enable HP SD mode + Impact: 577XX/578XX + + +uIP v0.7.4.2j (Jul 18, 2012) +======================================================= + Fixes + ----- + 1. Problem: Cont00064665 - Linux iSCSI connects via gateway address + on the wrong subnet + Cause: The gateway address used was not checked against the + subnet mask specified before the ARP requests. Since + this behavior deters from how L2 operates, therefore, + a change was made to correct this. + Change: Added check of the gateway specified against the subnet + specified. + Impact: Static IPv4 operation + + 2. Problem: Cont00064722 - Linux iSCSI unable to force IPv6 LL + override (advanced iface parameters) + Cause: The override LL address was not being populated to the + IPv6 address database correctly + Change: Added this correctly to the IPv6 initialization + Impact: Static/DHCP IPv6 LL address override only + + +uIP v0.7.4.2i (Jul 11, 2012) +======================================================= + Fixes + ----- + 1. Problem: Cont00064604 - Fails to connect to routed IPv6 target + via RA + Cause: The default router IPv6 address was not being retrieved + correctly. + Change: Fixed the default router IPv6 address read + Impact: All + + +uIP v0.7.4.2h (Jun 15, 2012) +======================================================= + Fixes + ----- + 1. Problem: Cont00063863 - can't boot into offload image + when VLAN is enabled + Cause: During the iSCSI login exchange, certain iSCSI targets + will send an ARP request even though the TCP connection + has been made. The bug was in this ARP reply where + the local MAC was corrupted when VLAN is enabled. + Change: Fixed the ARP reply packet + Impact: All + + +uIP v0.7.4.2g (Jun 08, 2012) +======================================================= + Fixes + ----- + 1. Problem: Cont00063816 - The initiator is not able to connect + to the iSCSI targets over VLAN + Cause: The process packet routine did not consider the PCP + of the VLAN tag to be non-zero. This created a + mismatch when this VLAN tag was compared against the + nic_iface->vlan_id which doesn't include the PCP. + Change: Added the consideration of non-zero PCP + Impact: All + + +uIP v0.7.4.2f (Jun 04, 2012) +======================================================= + Fixes + ----- + 1. Problem: Cont00063626 - Static IPv6 does not connect when + the prefix len is not set explicitly + Cause: The IPv6 prefix length was not set correctly + for Static IPv6 operation when CIDR notation is + not specified + Change: Fixed the default prefix length + Impact: Static IPv6 + + 2. Problem: Cont00063651 - Cannot connect to iSCSI targets + HP PTM/SF + Cause: Switch-Dependent mode + invalid Outer VLAN was + not supported + Change: Allow SD+invalid OV to fallback to SF operation mode + Impact: 5771X/578XX + + +uIP v0.7.4.2e (May 30, 2012) +======================================================= + Fixes + ----- + 1. Problem: Cont00063443 - Compilation error on SLES11sp1 + Cause: The iface_num field was not defined + Change: Fixed all references to iface_num + Impact: SLES11sp1 + + 2. Problem: Cont00063518 - HBA fails to connect across router + using iface.gateway address + Cause: The gateway override code did not populate the + address into the lower level engine + Change: Fixed the gateway override code + Impact: IPv4 Static IP operation + + 3. Problem: Cont00063567 - IPv6 LL and RA override does not work + Cause: The IPv6 LL/RA override addresses were overwritten + by the NDP engine + Change: Fixed the LL/RA override code + Impact: IPv6 operation + + Enhancements + ------------ + 1. Added support for jumbo MTU (independent from the L2 MTU) + + +uIP v0.7.4.2d (May 21, 2012) +======================================================= + Fixes + ----- + 1. Problem: Cont00063421 - Static IPv6 cannot connect via RA/LL + Cause: The router advertise and the linklocal address + were corrupted due to the override capabilities + added for the newer open-iscsi util + Change: Fixed the address override code + Impact: Static IPv6 + + Enhancements + ------------ + 1. Allow VLAN tag = 1 (router management) to connect offload + + +uIP v0.7.4.2c (May 09, 2012) +======================================================= + Fixes + ----- + 1. Problem: RHEL BZ 734010/804580 - issues found by the Coverity + scan + Cause: 10 code issues were flagged for revision + Change: Fixed all area of concern + Impact: All + + 2. Problem: Cont00063177 - IPv4 DHCP with VLAN specification in + iface file gets wrong address + Cause: The DHCPv4 handler was not discriminating the VLAN tag + associated with the DHCP offers from multiple DHCP + servers + Change: Changed the DHCPv4 handler to drop DHCP offer packets + that doesn't match the VLAN tag of the intended DHCP + discovery packet + Impact: DHCPv4 operation + + +uIP v0.7.4.2b (May 01, 2012) +======================================================= + Fixes + ----- + 1. Problem: Cont00062993 - IPv6 DHCP with VLAN specification in + iface file gets wrong address + Cause: The DHCPv6 request was using the same DUID as always + so the non-VLAN DHCP server responded to our broadcast + instead + Change: Changed the DHCPv6 request DUID to link address + time + instead of link address alone + Impact: DHCPv6 operation + + +uIP v0.7.4.1j (Apr 24, 2012) +======================================================= + Fixes + ----- + 1. Problem: Cont00062805 - Cannot login to iSCSI targets on RHEL6.3 + Cause: The problem was caused by a change made to the iface_rec + structure in the RHEL6.3 inbox open-iscsi util + Change: The new changes is now incorporated + Impact: All + + +uIP v0.7.4.1i (Apr 16, 2012) +======================================================= + Fixes + ----- + 1. Problem: Cont00062660 - Unable to login with VLAN iscsiuio + on RHEL6.2 + Cause: The open-iscsi util in RHEL6.2 has a bug which + does not pass the correct iface_num to iscsiuio + Change: Added workaround to fall back to do the legacy + VLAN support if iface_num and vlan_id = 0 + Impact: RHEL6.2 + + +uIP v0.7.4.1h (Apr 13, 2012) +======================================================= + Enhancements + ------------ + 1. Added support for the new iface_num field in the iscsi_uevent + path + + 2. Fixed bug in the nic_iface search engine based on iface_num + + +uIP v0.7.4.1g (Mar 22, 2012) +======================================================= + Fixes + ----- + 1. Problem: Cont00061869 - Unable to setup an offload iSCSI + connection with FLR/NPAR under ESX5.0:PDA + Cause: The physical function ID was previously extracted + from the sysfs of the VM which might not be consistent + to the actual physical setup due to the function + remapping in the hypervisor + Change: Read the physical function ID directly from the BAR0 + ME register + Impact: All + + 2. Problem: Cont00062170 - IPv6 login/logout stress fails + Cause: The packet interrupt was lost after running the test + for a much longer period of time. A bug in the + packet processing routine was found to exit prematurely + Change: Fixed the packet processing routine to process all + packets before exiting + Impact: All + + +uIP v0.7.4.1f (Mar 19, 2012) +======================================================= + Fixes + ----- + 1. Problem: Cont00062170 - IPv6 login/logout stress fails + Cause: The packet buffer routine for IPv6 did not take + network order <-> host order into consideration + Change: Added a htons call to compensate for the ntohs pair + Impact: All + + +uIP v0.7.4.1e (Mar 08, 2012) +======================================================= + Fixes + ----- + 1. Problem: Cont00061978 - Load/unload stress test fails + Cause: The bnx2x open request was failing due to the module + request procedure. However, the open failure was + not being handled correctly. + Change: Fixed the device open error handling + Impact: 5771X/578XX + + +uIP v0.7.4.1d (Mar 02, 2012) +======================================================= + Fixes + ----- + 1. Problem: Cont00061708 - Unable to log into target after running + driver load/unload + Cause: A bug was introduced in the previous bug fix (CQ61459) + where a pthread_cond_broadcast call was erroneously + enabled + Change: Restored this back + Impact: All + + +uIP v0.7.4.1c (Feb 16, 2012) +======================================================= + Fixes + ----- + 1. Problem: Cont00061529 - Unable to connect to target after an + initial failed login attempt until iscsi service is + restarted + Cause: Upon a failed DHCPv4 acquisition due to the wrong VLAN + tag in the initial iface setup, any iscsid connect request + from the same NIC will get dropped due to a bug. + Change: Fixed the bug which prevented new iscsid connect requests + from getting honored + Impact: All + + Enhancements + ------------ + 1. Updated README + + +uIP v0.7.4.1b (Feb 08, 2012) +======================================================= + Fixes + ----- + 1. Problem: Cont00061513 - Unable to connect to target over VLAN + interface + Cause: The VLAN id was not properly passed back to the CNIC + driver for the offload request + Change: Fixed the VLAN id being passed back to the CNIC driver + Impact: All + + +uIP v0.7.4.1a (Feb 01, 2012) +======================================================= + Fixes + ----- + 1. Problem: Cont00049383 - No mechanism in iface file to support + gateway/routing + Change: Added support for the additional network parameters + as passed from the newer iscsi-util. + These parameters include: + IPv4: subnet_mask, gateway + IPv6: ipv6_linklocal, ipv6_router, + ipv6_autocfg, linklocal_autocfg, router_autocfg + VLAN: vlan_id, vlan_priority, vlan_state + Other: mtu, port + Impact: All + + 2. Problem: Cont00060806 - Unable to connect target using DHCP over + tagged VLAN + Change: DHCP+VLAN is a new feature enhancement that was added + alongside all other new iface parameters. + Impact: All + + + Enhancements + ------------ + 1. Lock iscsid's connect request with path_req so connect requests + with DHCP/Static will no longer override each other + + 2. Fixed the if_down handler from global to nic specific + + 3. Fixed various synchronization issues + + +uIP v0.7.2.1e (Jan 05, 2012) +======================================================= + Fixes + ----- + 1. Problem: Cont00060734 - ifupdown-mtu change stress with active + session causes iscsiuio to fail + Change: Fixed a race condition between the nic enable thread + and when DHCP fails + Impact: All + + +uIP v0.7.2.1d (Dec 28, 2011) +======================================================= + Fixes + ----- + 1. Problem: Cont00060368 - segfault observed after failing both + mpio paths + Change: Various memory leaks were identified and resolved in + the nic cleanup path + Impact: All + + +uIP v0.7.2.1c (Dec 16, 2011) +======================================================= + Enhancements + ------------ + 1. Change: Disable HP SD mode + + +uIP v0.7.2.1b (Dec 14, 2011) +======================================================= + Enhancements + ------------ + 1. Change: Default iscsiuio logging to off. Use the '-d' + option to enable + + +uIP v0.7.0.14g (Oct 25, 2011) +======================================================= + Enhancements + ------------ + 1. Change: Fixed the compilation under RHEL6.2 + 2. Change: Added oom_adjust call to prevent OOM Killer from killing + iscsiuio when memory is low + 3. Change: Added mlockall setting to prevent page swap + + +uIP v0.7.0.14f (Oct 20, 2011) +======================================================= + Fixes + ----- + 1. Problem: Cont00058994 - DOS vulnerability in uip during UDP flood + Cause: The warning messages from the UDP handler was logging + at a rate faster than the log file logrotate rate + Therefore, the system's OOM eventually got kicked in to + start terminating running processes which includes iscsiuio + Change: Moved several UDP warning messages from the default log + level to the debug log level + Impact: All (minor) + + 2. Problem: Cont00059288 - Show segfault w/ SLES11 SP1 Xen kernel + Cause: The bnx2x chip_id was not read correctly from the PCIe BAR1 + under the Xen kernel. The error was in the mmap area. + Change: Corrected the mmapping of the PCI MMIO space. + Impact: Xen kernels + + Enhancements + ------------ + 1. Change: Changed the log file open error to a warning and let + the daemon progress. This was only observed under iSCSI boot + + +uIP v0.7.0.14e (Sep 19, 2011) +======================================================= + Fixes + ----- + 1. Problem: Cont00058678 - Can not iboot target from ipv6 path + using VLAN + Cause: A bug was found in the path request path where the vlan + iface's protocol family was not used correctly in the + iface search + Change: This has been corrected + + +uIP v0.7.0.14d (Sep 16, 2011) +======================================================= + Fixes + ----- + 1. Problem: Cont00058602 - Can't iboot using IPv6 offload path + Cause: The bug was exposed by a fix in 0.7.0.14c where the + IPv6 router solicitation timeout exceeded the nic + enable thread timeout. + Change: The IPv6 router solicitation timeout has been adjusted + + +uIP v0.7.0.14c (Sep 01, 2011) +======================================================= + Fixes + ----- + 1. Problem: Cont00058256 - Sessions fail after loginstress to via + simultaneous ipv4 and ipv6 dhcp + Cause: Switching between DHCPv4/v6 coupled with VLAN exposed + a drawback in our nic_iface architecture design where + VLAN is not specified by iscsid. + Change: The code was optimized and improved the performance when + switching between DHCPv4/v6+VLAN. However, the ultimate + fix is to make use of the net config parameters introduced + in the newer open-iscsi util which will identify the + specific VLAN nic_iface to use. + + Enhancements + ------------ + 1. Change: Added support for bnx2x-1.71.00 + + +uIP v0.7.0.14b (Aug 23, 2011) +======================================================= + Fixes + ----- + 1. Problem: Cont00057840 - RHEL6.2 inbox: Unable to connect to + targets with 5709 + Cause: For cases when the bnx2/bnx2x driver gets removed, the + uio database that was built by cnic would have the device + ->net reference removed. This has caused an unnecessary + timeout of 5s for each stale uio entry in the database. + Change: Adjusted the routine which seeks the device->net entry + to include more logic instead of hard waiting for 5s. + + Enhancements + ------------ + 1. Change: Added support for RHEL6.2 for out-of-box release + 2. Change: Updated the man page with -h and -p info + 3. Change: Updated the -h info + + +uIP v0.7.0.13 (Aug 10, 2011) +======================================================= + Fixes + ----- + 1. Problem: Cont00057768 - iscsiuio logrotate causes daemon failure + Cause: The logrotate script will send a SIGUSR1 signal to notify + the iscsiuio daemon of such action. However, the daemon + wasn't programmed to catch this signal. + Change: Restored the catching of this signal + + +uIP v0.7.0.12 (Aug 04, 2011) +======================================================= + Fixes + ----- + 1. Problem: Cont00050634 - brcm_iscsiuio Tainted: running IoZone, + Iometer and receiving a UDP flood on 3260 + Cause: Upon iscsiuio termination, because of the UDP flood, + the nic thread will be busy servicing those UDP packets + while the signal handling thread will free up all nic + resources. The two threads were not in sync. + Change: Added a nic_remove_all routine to destroy all nic threads + before the nic resources get freed. + + Enhancements + ------------ + 1. Change: Fixed all warnings as reported by RHELS' Coverity testing. + + +uIP v0.7.0.11 (Aug 02, 2011) +======================================================= + Fixes + ----- + 1. Problem: Erroneous VLAN tag was being passed by iscsid for connect + request + Cause: The iscsid's iface_rec_t ipc message does not contain this + vlan field. This field was added in uIP for future vlan + support. Since the buffer allocated to receive such message + in uIP didn't get initialized, therefore, garbled up VLAN + tag was getting used. + Change: Added the initialization of this buffer. + + +uIP v0.7.0.10 (Jul 26, 2011) +======================================================= + Fixes + ----- + 1. Problem: Can't offload when switching from Static to DHCP then back to + Static IPv4 when connecting through a VLAN interface + Cause: The VLAN processing code did not reinstall the IP address + from the default nic_iface to the associated VLAN nic_iface. + This was only done on the very first time when the VLAN + interface was created and not on subsequent instances. + Change: Added code to mirror the default nic_iface IP/netmask/ip_config + on the VLAN nic_iface on every new connection request. + + +uIP v0.7.0.9 (Jul 19, 2011) +======================================================= + Fixes + ----- + 1. Problem: Can't offload to 57810 NPAR NIC + Cause: The MF/VF variant of the PCI IDs were not supported previously + Change: Added support for the MF/VF variants for 57800/57810/57840 + + +uIP v0.7.0.8 (Jun 30, 2011) +======================================================= + Fixes + ----- + 1. Problem: Cont00056522 - Unable to connect to iSCSI target using + netxtreme2 package 7.0.9 + Cause: The iSCSI L2 ring's CID has changed from 17 to 49 + Change: The code now gets L2 iSCSI ring CID from the l2_buf directly. + This will work with any version of the cnic driver because + the location is a zero before this change. + + +uIP v0.7.0.7 (Jun 23, 2011) +======================================================= + Fixes + ----- + 1. Problem: Cont00056460 - iSCSI Offload boot RHEL5u5 x64 dropped tagged + packets with iSCSI Offload Boot with untagged + Cause: The ICMP echo replies to the target was corrupted in both + 1g and 10g mode + Change: The code will now handle both VLAN stripped and no VLAN stripped + incoming packets correctly. Also modified the transmit routine + to strip out any inline VLAN tag before setting up the hw to + insert VLAN tag. + + +uIP v0.7.0.6 (Jun 21, 2011) +======================================================= + Fixes + ----- + 1. Problem: Cont00056231 - DHCPv4 not working with iSCSI HBA w/ + linux-nx2 v7.0.7 + Cause: The 10g L2 FW HSI has been modified for PCIe performance + enhancement in the 7.0.7 package (FW 1.70.20) which uIP + has not adapted to. + Change: The eth_rx_cqe size has been increased from 32B to 64B. + + Enhancements + ------------ + 1. Change: The utility name has changed from brcm_iscsiuio to iscsiuio + as preparation for upstream submission. + 2. Change: Updated README + + +uIP v0.7.0.5 (Jun 02, 2011) +======================================================= + Fixes + ----- + 1. Problem: Cont00055915 - iSCSI does not connect on 57800 in 4-port mode + Cause: The 4-port mode was not being determined correctly + Change: Fixed the PORT4MODE register offset and the QZONE_ID macros + + +uIP v0.7.0.4 (May 24, 2011) +======================================================= + Fixes + ----- + 1. Problem: Cont00055832 - linux iscsiboot can not login to target using + offload path (57800) + Cause: The device ID comparison routine did not take care of the case + when one device ID is bitwise superset of another. + Change: Fixed the device ID comparison routine. + + +uIP v0.7.0.3 (May. 19, 2011) +======================================================= + Enhancements + ------------ + 1. Change: Updated all fixes to match the released uIP 0.6.4.17 + + 2. Change: Modified source and Copyright info as preparation for upstream + submission + + +uIP v0.7.0.2 (May. 03, 2011) +======================================================= + Fixes + ----- + 1. Problem: Cont00048972 - brcm-iscsi.log has no max size and would grow + to consume all free space on hard disk + Cause: There was no mechanism to rotate the log + Change: Added logrotate entry and SIGUSR1 signal handling for log rotate + action + + 2. Problem: Cont00054996 - Multi-session, multi-protocol mtu stress + does not recover all sessions + Cause: A segfault was observed during the load/unload module. The + problem was caused by an illegal dereference of a pointer + when IPv6 couldn't find the longest match address from + the ARP (Neighbor) table. + Change: Fixed the dereferencing error + + 3. Problem: Cont00054900 - Linux uIP - Please add ability to connect + to routed target with static iface IPv6 + Cause: Static IPv6 never runs the IPv6 NDP router sol/adv engine. + Change: IPv6 NDP router sol/adv has now been added to static IPv6 + operation. + + 4. Problem: Cont00054996 - Multi-session, multi-protocol mtu stress + does not recover all sessions + Cause: Segfaults were observed caused by the accessing of the IPv6 + NDP structure while the nic is undergoing a reset either + due to a DHCPv4 request from iscsid or the handling of + if_down due to the NL handler from CNIC. + Change: The fix involves the following: + - Fixed the handling of staggered IPv4/v6 DHCP/static requests + - Fixed memory leak due to reallocation of IPv4 and IPv6 + DHCP structs + - Fixed the pthread join stuck problem in the handling + of the if_down NL message + + 5. Problem: Cont00054810 - Linux NMI - bnx2x_init_hw_common:PXP2 CFG + failed running iSCSI MTU stress test + Cause: This only happens in DHCPv4 mode. The problem was caused + by contention between the elongated window of performing + DHCP in the enable_nic thread while receiving the asynchronous + if_down NL message (from the MTU change event) from the + CNIC NL thread. The problem occurs when the enable_nic + thread tries to call bnx2x_open while the other thread + calls the bnx2x_close routine. + Change: Fixed mutex lock bugs for the enable_nic thread. Also + extended the nic_disable timeout to 10s to compensate for + the DHCP operation. + + 6. Problem: Cont00054818 - RH6.0 - Unable to logout of iSCSI session + after running PQA baseline scripts + Cause: This was caused by the call to cancel the enable_nic + thread when disabling the nic but failed to unlock the + nic mutex that the enable_nic thread held. + Change: Wake up the enable_nic thread and wait for it to complete + instead of canceling it in the nic_disable path. + + 7. Problem: Cont00054725 - Previous static HBA IP will be used after + a new static HBA IP has been created + Cause: There was an assumption in the code where if the same + nic_iface structure was found based on the nic/vlan pair, + the specified IP address would not be used. Instead, it + will continue to use the previous defined IP address. + Change: The previous IP address will now be compared against the + the specified IP address before finishing the parce + iface request from iscsid. If different, the current + nic will be disabled and then re-enabled with the newly + specified IP address. + + 8. Problem: Cont00054571 - Unable to connect to routed ipv6 target + with RA address and iface DHCPv6 + Cause: The default router address was not being employed for + the IPv6 neighbor negotiation. Additionally, the return + address of our neighbor advertisement was incorrect as + it should use the best matched src address instead. + Change: Fixed both the IPv6 neighbor solicitation and advertisement + transmission and handling. + + 9. Problem: Cont00054510 - fails to login to 32 session with blanket + login IPv6 + Cause: A bug was introduced in uIP 0.6.4.6 where the NIC_RUNNING + flag might not be set when entering the main loop under + certain situations depending on the nic bring up. + Change: A new NIC_STARTED_RUNNING flag is now defined to fix CQ53511. + + 10. Problem: Cont00053807 - RA and link local are unable to connect if DHCPv6 + fails + Cause: The host link local address was not being searched as one of + the host address to be replied to CNIC for the connect request. + Change: The path reply now includes the search of host link local + address as well. + + 11. Problem: Cont00054236 - iSCSI service must be restarted before an IPv6 + connection can be made to the Equalogic target + Cause: The problem was intermittent as it depends on which IPv6 address + the target was redirecting to. Since uIP was only extracting + the target's IPv6 address + MAC from the target's neighbor + advertisement packet itself and not from the ICMPv6 option, so + the wrong or no MAC address will get send down to CNIC for the + connection establishment; hence the no connect. + Change: Added the updating of the neighbor discovery table to also use + the Target IPv6 address + MAC specified in the incoming neighbor + advertisement's ICMPv6 option field. + + 12. Problem: Cont00053255 - bnx2x panic dump logging into multiple + discovered IPv6 nodes (Equalogic IPv6 target) + Cause: The bnx2x panic was fixed in the 10g fw 6.4.29. + A IPv6 connectivity issue was then found and led to different + kernel/uIP crashes. This was caused by the same IPv6 + connectivity problem mentioned above. + Change: Same as above + + 13. Problem: Cont00053728 - Sessions never recover after doing initiator-side + cable pull test with IPv6 traffic against Equalogic targets + Cause: It was discovered that the Equalogic would send out periodic + neighbor solicitation to maintain the connection to the + initiator. Since uIP was responding with the assigned IPv6 + link local address in the neighbor advertisement + unconditionally, the target was observed to stop transmitting on + the connection specified. + Change: The neighbor advertisement generated will now use the dst IPv6 + address from the input neighbor solicitation packet instead of + the assigned IPv6 link local address for both the packet and the + ICMPv6 source IPv6 address. + + 14. Problem: Compile error under 32-bit OS + Cause: A bug was introduced in the previous release 0.6.4.6 which + caused a compilation error in 32-bit OS (64-bit compiles + fine) + Change: Fixed the bug + + 15. Problem: Cont00053807 - RA and Link local are unable to connect if dhcpv6 + fails + Cause: There was a bug in the nl reply where the RA address will never + be sent back to CNIC for the connection request + Change: The best matched address to the dst will now be sent back to + CNIC in the path rsp. + + Enhancements + ------------ + 1. Change: Updated README to remove the 57713/E references + + 2. Change: Allow the ICMP option field in the IPv6 Neighbor Advertisement + response to be included without discrimination. This fixes + an issue connecting against the EQL via RA for DHCPv6. + + 3. Change: Updated README for the IPv6 operation, VLAN, and discovery. + + +uIP v0.7.0.1 (Mar. 29, 2011) +======================================================= + Fixes + ----- + 1. Problem: Cont00053511 - bnx2x panic dump during ifup/down stress with + iSCSI traffic + Cause: The panic dump was resolved by the driver's rq dbell size fix. + After that, uIP crashed due to the asynchronous if_down event + that took the chip resources away while the nic thread is still + continuing to try to send DHCP request. + Change: Added synchronization between the two threads so proper clean up + of the threads can occur. + + Enhancements + ------------ + 1. Change: Added support for E3 (57800, 57810, and 57840) + + +uIP v0.6.4.5 (Mar. 23, 2011) +======================================================= + Enhancements + ------------ + 1. Change: Optimized the double VLAN fix of CQ53870 to match + what will be submitted for RHELS5.7 and RHELS6.1 inbox + + +uIP v0.6.4.4 (Mar. 17, 2011) +======================================================= + Fixes + ----- + 1. Problem: Cont00053870 - Unable to login to iSCSI target via offload + through a Nexus 5020 switch with DCBx enabled + Cause: Double VLAN tagging was observed due to DCBx enabled. + The chip actually adds a VLAN tag if the txbd does not have + VLAN tag enabled under the DCBx environment for PRI setting. + Since uIP does not make use of hw assisted VLAN tagging, + 2 VLAN tag was observed in the data stream. + Change: Enabled hw assisted VLAN tagging in uIP for both 1g and 10g. + + 2. Problem: Cont00053792 - maxconnections intermittently fail and + recover using iface DHCPv4 + Cause: The DHCPv4 engine erroneously keeps on requesting for a + new lease which tremendously hamper normal path_req + operation. The problem is that the lease time parameter + has overflowed when converted to ticks count. + Change: Expanded the lease timer ticks count parameter from 16 to + 32 bits. + + 3. Problem: Cont00053807 - RA and link local are unable to connect if + DHCPv6 fails + Cause: The DHCPv6 engine does not have the failover to use RA + mechanism + Change: Expanded to use best match address instead regardless of + DHCPv6 success or not, or using static v6. + + Enhancements + ------------ + 1. Change: Cont00051823 - Added man page for brcm_iscsiuio + + +uIP v0.6.4.3 (Mar. 15, 2011) +======================================================= + Fixes + ----- + 1. Problem: Cont00053719 - intermittent logging into targets that + are not in the same subnet as defined in the iface + Cause: The default route was used erroneously due to a miscompare + Change: Fixed this comparison so if the requested dst is not in + in the same subnet, uIP would not even ARP out. + + 2. Problem: Cont00053580 - Unable to do iSCSI boot into Linux OS using + 57710 adapters + Cause: The E1 iro USTORM_RX_PROD_OFFSET doesn't match the t6.4 fw + Change: This is now fixed + + +uIP v0.6.4.2 (Feb. 24, 2011) +======================================================= + Fixes + ----- + 1. Problem: Cont00050343 - HBA does not follow RFC2131 spec for IPv4 + DHCP lease expiration + Cause: The dhcp engine did not have this feature implemented + Change: Added lease time tracking and renewal + + 2. Problem: Cont00050801 - Unable to connect to target after switching + between DHCPv4 to static v4 + Cause: The configuration flags got corrupted when switching between + dhcp and static or vice versa. + Change: Fixed the flag handling. Also needed to zero out the static + ip address in the host memory when switching to dhcp. + Otherwise, the static ip address will get used mistakenly. + + Enhancements + ------------ + 1. Change: Cont00051936 - Added IPv6 NDP and DHCPv6 support. + + +uIP v0.6.4.1 (Jan. 27, 2011) +======================================================= + Fixes + ----- + 1. Problem: Cont00049766 - segfault seen while stopping iscsi service + Cause: The logger output routine was accessing the log resource + while another thread calls fini_logger to free the same + resources + Change: Added pthread mutex lock to the logger routine to exclude + the initializer, user, and finisher + + Enhancements + ------------ + 1. Change: Added new t6.4 HSI and 57713 support. + + +uIP v0.6.2.13 (Jan. 04, 2011) +======================================================= + Fixes + ----- + 1. Problem: Cont00049665 - iscsiboot:linux failed to boot into iscsi + boot image in offload path after 5 iterations + Cause: The hw consumer index for the uIP ring got out of sync + with the producer index. This has led to the xmit mutex + lock be held forever so subsequent ARP requests will not + get transmitted to the wire + Change: Added this out of sync detection and rescue the xmit mutex + lock + +uIP v0.6.2.12 (Dec. 21, 2010) +======================================================= + Fixes + ----- + 1. Problem: Cont00051820 - Session fails to reconnect after gateway + fallback + Cause: Under the HSRP test scenario, it was found that an ARP + request from the SUT is required in order for the HSRP + router to begin sending packets downstream to the SUT. + The default ARP age was originally set to 20 minutes + before a new ARP request will get sent, + Change: Changed the ARP age default to Linux default at 5 minutes + +uIP v0.6.2.11 (Dec. 17, 2010) +======================================================= + Fixes + ----- + 1. Problem: For IPv4, the gateway route was not being utilized + when the subnet mask given or calculated does not + match. This resulted in many unwanted connection + attempts. + Cause: A bug was found in the default gateway calculation + logic which prevented the gateway address from being + used. + Change: Fixed the default gateway logic + + 2. Problem: For IPv6, there are scenarios where it won't connect + Cause: The IPv6 subnet mask as extracted from the CIDR + format might contain garbage data. This garbage data + was then used as part of the subnet mask which would + prevent the correct address mask. + Change: Fixed the subnet mask + +uIP v0.6.2.10 (Dec. 15, 2010) +======================================================= + Fixes + ----- + 1. Problem: IPv6 does not connect for non-CIDR iface.ipaddress + specification + Cause: A bug where all ones was used as the IPv6 netmask + instead of all zeroes. This prevented all IPv6 + path requests from being honored + Change: Fixed the subnet mask used + +uIP v0.6.2.9 (Dec. 14, 2010) +======================================================= + Enhancements + ------------ + 1. Change: Added IP address CIDR notation support for the + iface.ipaddress field in the iface file. + This will allow subnet mask to be defined and used. + +uIP v0.6.2.8 (Dec. 9, 2010) +======================================================= + Fixes + ----- + 1. Problem: ipv6 + ifup/down fails to reconnect + + Cause: There were 2 problems found: + - the xmit_mutex lock was being held indefinitely + - the nl_process_if_down flag for 10g doorbell ringing + did not get reinitialized + + Change: Fixed the xmit_mutex deadlock via trylock + Added nl_process_if_down initialization in the IF_DOWN + process + + 2. Problem: Added fix for the NPAR disabled for 57712 + + Cause: The mac address was not handled correctly + + Change: Fixed the mac address handling. Also requires corresponding + kernel component for the complete fix + +uIP v0.6.2.7 (Dec. 7, 2010) +======================================================= + Enhancements + ------------ + 1. Change: Use the gateway address from the DHCP server the + destination IP address is not in the current subnet. + +uIP v0.6.2.6 (Nov. 16, 2010) +======================================================= + Fixes + ----- + 1. Problem: Warning message seen in the kernel logs, + "uio uio2: uevent: unsupported action string" + + Cause: The improper string was echo'ed into the UIO trigger + field. With an improper string, this message would + appear in the kernel logs. + + Change: uIP will now write the string "online" to the UIO + trigger field. This is the string expected by the + Linux kernel base driver. + + 2. Problem: uIP would segfault during a heavily login/logout + iSCSI subsystem reset senario + + Cause: A double free occurred in the logging portion of the + uIP code, but this was root cause to a double free when + manipulating the NetLink buffers. + + Change: Properly look at the return code from the routine which + will read NetLink messages. Also only free buffers + if they are allocated. + + Enhancements + ------------ + 1. Change: Add ability to print kernel version and machine + architecture to further help debug problems. + + 2. Change: Apply the netmask from DHCP if provided. + +uIP v0.6.2.5 (Nov. 10, 2010) +======================================================= + Fixes + ----- + 1. Problem: iscsid would try to conenct with unintended iSCSI + targets + + Cause: uIP would blindly return the iSCSI target MAC address + regardless if the iSCSI target is reachable via the + given port. + + Change: uIP will try to filter the requests coming from CNIC + by automatically generating a network mask based off + the configured IP addressed. Then this netmask is + masked with the destination IP address. If there is + a match, then the path_req is allowed through. + + 2. Problem: Problems reconnecting back to the target when running + MTU stress tests. + + Cause: cnic/bnx2i and uIP could possibly get out of sync when + an if_down message is sent. + + Change: uIP will now immediately react to the if_down message, + and flush all the path req's and then to process to + if_close. + + Enhancements + ------------ + 1. Change: Fix compile warnings for src/unix/nic_nl.c, + and src/unix/main.c + +uIP v0.6.2.4 (Nov. 4, 2010) +======================================================= + Fixes + ----- + 1. Problem: iSCSI HBA: brcm_iscsiuio segfault during ifdown + with many active sessions + + Cause: uIP will segfault when traversing the error path when + an iSCSI connection is starting but the sysfs entries + have not been created yet. + + Change: Use the errno value rather then the one from the file + descriptor because the file descriptor will be NULL and + the NULL dereference will cause a segfault. + + Enhancements + ------------ + 1. Change: Added initial changes for iSCSI multi-function support for + 10G NIC's. + 2. Change: Add more detailed messages for error pathes in nic_utils + +uIP v0.6.2.3 (October 28, 2010) +======================================================= + Enhancements + ------------ + 1. Change: Add support for bnx2x-1.62.x drivers + +uIP v0.6.2.2 (October 18, 2010) +======================================================= + Enhancements + ------------ + 1. Change: Only allow iSCSI connections with known bnx2x HSI's. + +uIP v0.6.2.1 (October 7, 2010) +======================================================= + Fixes + ----- + 1. Problem: After multiple MTU changes, the ethtool IOCTL used to + determine the bnx2x driver version fails and eventually + iSCSI connections would not reconnect. + + Cause: The socket file descriptor used during the ethtool IOCTL + call was never closed and leaked. + + Change: On the error path when calling the ethtool IOCTL, the + file descriptor is now properly closed. + +uIP v0.5.39 (September 15, 2010) +======================================================= + Fixes + ----- + 1. Problem: Could not offload IPv4 VLAN connection when the target tries + to ARP the iSCSI initiator + + Cause: In the ARP reply, the ether field was incorrect. + + Change: Properly set the ether field to 802.1Q type (0x8100) + +uIP v0.5.38 (September 14, 2010) +======================================================= + Fixes + ----- + 1. Problem: uIP would cause a panic dump when the NIC was going down + + Cause: uIP and CNIC where not synchonized on NIC state + + Change: Check if the RX BD's which are zero'ed by CNIC when the + NIC is going down. If the BD addresses are zero, then + uIP will drop the TX packets. + +uIP v0.5.37 (August 21, 2010) +======================================================= + Fixes + ----- + 1. Problem: uIP would segfault on ifup/ifdown stress test when using + DHCP to determine local IP address. + + Cause: The uIP would use a NULL buffer during data transmission. + + Change: Drop packets when there are no buffer avaliable. + +uIP v0.5.36 (August 21, 2010) +======================================================= + Fixes + ----- + 1. Problem: iSCSI boot would not completely login after the pivot + root operation. + + Cause: The uIP would not properly start the NIC interface. + + Change: uIP should only check the NIC state to determine whether + to start the NIC thread or not. + + 2. Problem: uIP would segfault during if'up if'down testing. + + Cause: The uIP would improperly start 2 NIC threads for the + same NIC interface. + + Change: uIP should properly lock the NIC list when disabling/removing + the NIC threads. + + +uIP v0.5.35 (August 20, 2010) +======================================================= + Fixes + ----- + 1. Problem: Sessions would hang with ethtool self-test + + Cause: The uIP would hang because the socket layer was stuck + because there is much contention for that socket. This + would hang the CNIC thread. + + Change: Remove any IOCTL calls in uIP which may colide with + the ethtool self test. The driver version is only + capture during uIP initialization. + + 2. Problem: There were session recovery issue when using DHCP + if up/down tests. + + Cause: The uIP would hang because the DHCP requests would + timeout if the network interface is downed which would + hang all the other uIP threads. + + Change: Ensure that the DHCP state machine had exit points + if the network interface was down'ed. + + +uIP v0.5.34 (August 18, 2010) +======================================================= + Fixes + ----- + 1. Problem: Sessions would not recover with ethtool self-test + + Cause: The uIP would hang because either the NetLink buffer is + full or that any socket operations used to manipulate + multicast addresses would block. + + Change: Ensure that the socket used for multicast addressing is + set to nonblocking. Drain the NetLink buffer without + using the eventing, but with a more aggressive poll routine. + + 2. Problem: Sessions would not recover with L2 driver load/unload on + RHEL 6.0 SS9 + + Cause: The uIP would close the NIC thread too early and would + deadlock on cloing the NIC thread. + + Change: Ensure that the NIC thread is canceled/closed only in one + location, in the NIC remove routine. + + +uIP v0.5.33 (August 17, 2010) +======================================================= + Fixes + ----- + 1. Problem: Error message seen from the uIP stack for valid packets. + + Cause: The uIP was incorrectly marking logging messages for valid + packets as errors because it didn't know how to parase them. + + Change: Changed the following from error to debug message + ipv6: invalid version + ipv4: invalid version or header length. + icmpv6: unknown ICMP message. + ip: neither tcp nor icmp + Changed the following from error to warn message + udp: bad checksum + tcp: bad checksum + tcp: got reset, aborting connection. + + 2. Problem: After multiple iterations the loading and unloading of + the Broadcom Linux drivers with active connections + would not cause the sessions to recover on RHEL 6.0 + snapshot 9. + + Cause: There was a deadlock in the nic mutex + + Change: Lock ordering for the nic mutex and nic list mutex must + be inforced. + + 3. Problem: After multiple iterations of running the ethtool selftest + the Broadcom Linux drivers with active connections + would not cause the sessions to recover on RHEL 5.5. + + Cause: The Netlink buffer between uIP and CNIC would get full. + + Change: Poll more regularly for packets in the Netlink buffer + from 4 times a second to 100 times a 1 second. + Drain packets during the PATH_REQ packet pull. + + +uIP v0.5.32 (August 14, 2010) +======================================================= + Fixes + ----- + 1. Problem: Error message 'nic eth0: Didn't find type 0xaa bb' seen. + + Cause: Valid non-DIX Ethernet packets as being passed to the + uIP. uIP will drop these packets but should be logged + correctly. + + Change: These packets are valid, and should only be logged for + debugging purposes. + + 2. Problem: Error message 'Dropped previous transmitted packet' seen. + + Cause: The TX ring is full, and here uIP is trying to transmit a + packet which will be dropped. This is a valid state but + the log message is marked incorrectly + + Change: These messages are not warnings and should be logging when + debugging is enabled. + + 3. Problem: Error message: "iscsi_ipc eth0 Transport name is not + equal expected: got: bnx2i" seen. + + Cause: The iface_rec structure is different between iscsid version. + For RHEL 5.5, iscsid is versioned 871, for RHEL 6.0 is + versioned 872. + + Change: Allow uIP to compile against a different version of iscsid. + + +uIP v0.5.31 (August 12, 2010) +======================================================= + Fixes + ----- + 1. Problem: Softlock would occur showing that the NetLink table + lock was taken but never released. + + Cause: NetLink socket buffer would fill with constant PATH_REQ + messages preventing PATH_REQ response from libiscsi + + Change: Now uIP will drain the NetLink buffer while looking for + a response. + + Enhancements + ------------ + 1. Change: Add documentation for VLAN configuration and restrictions. + + +uIP v0.5.30 (August 6, 2010) +======================================================= + Fixes + ----- + 1. Problem: iscsid thread will stall if closing the uio files nodes + is stuck + + Cause: uIP would indefinitely block waiting for the mutex shared + by the close routine. + + Change: Now uIP will try and poll a bit for the mutex. If it can't + get this mutex in the iscsid thread then an error is return + rather then hold the thread. + + 2. Problem: IPv6 Unicast Neighbor Adveriserments would have the + ICMPv6 option header specifying a MAC. + + Cause: uIP should use the source IPv6 address to detmine whether + to strip the option header or not and not the target address + in the ICMPv6 field. + + Change: The uIP stack return a unicast IPv6 Neighbor Advertisement + without the ICMPv6 option as a response to unicast + IPv6 Neighbor Solicitations. + + 3. Problem: There would be TCP SYN packets with improper MAC address. + + Cause: A zero'ed MAC address was not passed to CNIC to indicate an + error or if the IP address didn't resolve. + + Change: The uIP stack will now return a zero'ed MAC address if it + can't find any entries. + + +uIP v0.5.29 (August 6, 2010) +======================================================= + Fixes + ----- + 1. Problem: "uip udp: no matching connection found: lport: 35072" + seen numerous times in the brcm_iscsiuio log file + + Cause: This message was incorrectly marked as an error + + Change: These messages are valid log entries especially if the + packet was a broadcast UDP packet not destined for the SUT + I will change the code to mark these logs entries as debug. + + +uIP v0.5.28 (August 5, 2010) +======================================================= + Fixes + ----- + 1. Problem: Can't login into a redirected Equilogic Target + + Cause: The Equilogic Target uses a unicast IPv6 Neighbor + Solicitation to test if the host is up. The uIP stack + would return a Neighbor Advertisement with an unneeded + ICMPv6 option. + + Change: Only have the uIP stack return a unicast IPv6 Neighbor + Advertisement without the ICMPv6 option. + + 2. Problem: With older bnx2/bnx2x/cnic/bnx2i driver combinations + uIP would segfault when these drivers were unloaded. + + Cause: When the older drivers were removed, the underlying uio + instance was removed causing uIP to have a stale file handle. + When uIP finally closes using this stale file handle, either + uIP would segfault, or there would be an error in the + uio_release() path. + + Change: Only have the uIP close if the UIO file node exists. + + +uIP v0.5.27 (July 31, 2010) +======================================================= + Fixes + ----- + 1. Problem: iSCSI HBA: Unable to use DHCP address for iSCSI interface + if a connection was previously made with a static address + on bnx2 devices. + + Cause: Because the device is closed and reopen'ed the TX consumer + indexes were not persisted + + Change: Only discard the TX consumer indexes only when the devices + will be discarded or closed + + Enhancements + ------------ + 1. Change: Change CNIC references to bnx2 in the bnx2 user space + driver. + + +uIP v0.5.26 (July 30, 2010) +======================================================= + Fixes + ----- + 1. Problem: iSCSI HBA: Unable to use DHCP address for iSCSI interface + if a connection was previously made with a static address on + bnx2x devices. + + Cause: Because the device is closed and reopen'ed the TX consumer + indexes were not persisted + + Change: Only discard the TX consumer indexes only when the devices + will be discarded + + 2. Problem: IPv6 using VLAN's didn't login + + Cause: The uIP code used to determine if the packet was an IPv6 + or not was not working. This VLAN packets for IPv6 were + being mis-interpreted. + + Change: Make the function is_ipv6() VLAN aware + + 3. Problem: Persistant targets was not loggin in during boot + + Cause: If udev was slow and the /dev/uio* were creatly slowly + uIP would fail. + + Change: Poll uIP waiting for /dev/uio* file nodes. + +uIP v0.5.25 (July 27, 2010) +======================================================= + Fixes + ----- + 1. Problem: When using IPv4 DHCP, there are no initial DHCP Discover + packets were not seen on the wire. + + Cause: Packets generated from the app handler from the uIP stack + were not placed on the wire. + + Change: Packets originating from the uIP stack are now always placed + on the wire. + +uIP v0.5.24 (July 25, 2010) +======================================================= + Fixes + ----- + 1. Problem: One would see invalid packet packets flow through the + uIP stack, where the logs would indicate there is a packet + with an invalid length + + Cause: The BD and CQE consumer indexes were not properly incremented + and masked. + + Change: The BD index is now properly masked. The CQE index is not + incremented using the CQE index rather the mistaken BD index. + + Impact: 10G only + + 2. Problem: uIP would segfault during the booting of the machine. + + Cause: uIP was using a NULL data pointer because there was an + incorrect packet passed to the stack. + + Change: Only allow uIP to process data if the packet exists. + + 3. Problem: uIP would stop processing packets + + Cause: The uIP code would not properly drain the CQE ring causing + it to eventually be full + + Change: Consume all the CQE elements even if they are ethernet types + or not. + + Impact: 10G only + + 4. Problem: uIP would stop after if/down of the network interface. + + Cause: uIP was not kick starting the NIC loop thread properly. + + Change: Ensure that the NIC loop thread is started by when iscsid + request that the interface start the offload. Mark the NIC + only if the thread is truly canceled. + + +uIP v0.5.23 (July 20, 2010) +======================================================= + Fixes + ----- + 1. Problem: Segfault during brcm_iscsiuio initialization + + Cause: uIP was using a NULL data pointer, because a different + thread re-initialized the uIP stack + + Change: Properly synchronize the initialization of the stack + + 2. Problem: Deadlock during the printing of heavy debug messages + + Cause: The variable macro structures would point to invalid + data + + Change: With each invocation of va_copy() a corresponding + invocation of va_end() in the same function for the proper + cleanup + + 3. Problem: uIP would hang when the interface could go up/down + + Cause: uIP would get out of sync with the state of the network + interface + + Change: Instead of detriving state from the UIO file nodes, uIP + will take direction from iscsid on when interfaces will be + started. + +uIP v0.5.22 (July 15, 2010) +======================================================= + Fixes + ----- + 1. Problem: Unable to reconnect via iSCSI offload after + ifup/ifdown + + Cause: uIP was stuck on the thread when closing the NIC main + loop + + Change: Properly synchronize the NetLink CNIC and uevent threads + + 2. Problem: uIP would crash during boot up. + + Cause: uIP would overwrite a memory location which was already + freed during nic_remove(). + + Change: Since the NIC is freed there is no need to write to + update the NIC flags + + Enhancements + ------------ + + 1. Change: Added IPv6 Link Local support + + +uIP v0.5.21 (July 5, 2010) +======================================================= + Fixes + ----- + 1. Problem: Unable to connect via iSCSI offload after + changing L2 address + + Cause: uIP didn't notice the network inferface going down + + Change: Allow uIP to persist the stack's IP address after + a reset + + 2. Problem: Unable to connect via IPv4 and IPv6 concurrently + + Cause: uIP didn't notice the network inferface going down + + Change: Allow uIP to persist the stack's IP address after + a reset and properly bring up the interface + + 3. Problem: Unable to connect via VLAN + + Cause: IP address was no persisted after a device reset + + Change: When CNIC requests a path request, uIP will use the + VLAN passed by the CNIC. + + +uIP v0.5.20 (June 24, 2010) + + +uIP v0.5.20 (June 24, 2010) +======================================================= + Fixes + ----- + 1. Problem: Certain IPv6 addresses are not repsonded to by + the target. + + Cause: The MAC was generated from the target's IPv6 + address not the deterived multicast IPv6 address. + + Change: The destination MAC address should be deterived + from the packet's destination IPv6 address and + not the target. + + 2. Problem: brcm_iscsiuio would segfault when L2 interface is + bought up and down after being logged into + + Cause: The NIC thread was not stopped properly + + Change: When the UIO device is remove and when the + cooresponding NIC tracked by brcm_iscsiuio, the + daemon would properly wait for the NIC thread to + stop. + + +uIP v0.5.19 (June 22, 2010) +======================================================= + Fixes + ----- + 1. Problem: Can't login after boot + + Cause: If NIC interfaces are brough up and down quickly + uIP wait on an invalid NIC thread + + Change: Only wait for the NIC thread if the NIC thread + exists. + +uIP v0.5.18 (June 21, 2010) +======================================================= + Fixes + ----- + 1. Problem: Does not compile on SLES 11 SP1 + + Cause: Automake cached files were included as part of the + uIP-0.5.17 package + + Change: Remove automake cached files, and allow these files + to be generated each time the source is compiled + + 2. Problem: Does not always receive multicast packets + + Cause: Multicast bit was not set in SORT USER 2 register + + Change: brcm_iscsiuio will now set the SORT USER 2 registers + with both the broadcast and multicast bits. + + 3. Problem: Existing iSCSI connections do not reconnect after + operations which require equivalent driver + load/unload operations + + Cause: Multiple path requests would trample NIC configurations + + Change: Allow only one path request at a time + +uIP v0.5.17 (June 16, 2010) +======================================================= + Fixes + ----- + 1. Problem: IPv6 neighbor solicitations from brcm_iscsiuio could + not be responded to + + Cause: The IPv6 neighbor solicitation packet had an invalid + multicast MAC address + + Change: Properly set the MAC address multicast bit and OR + with the IPv6 destination address + + 2. Problem: NIC state was not properly synchronized and noticed + by Shyam Iyer + + Change: Properly lock the NIC device when changing state + + Enhancements + ------------ + + 1. Change: Listen for iscsid before daemonizing to close a timing + gap which might allow iscsid to start before uIP is + completely initialized. + +uIP v0.5.16 (June 2, 2010) +======================================================= + + Enhancements + ------------ + + 1. Change: Formally add IPv6 support. Only a static IPv6 address + is supported. + +uIP v0.5.15 (May 20, 2010) +======================================================= + + Fixes + ----- + 1. Problem: brcm_iscsiuio would echo packets off the wire + + Cause: Stale packets from the uIP stack could potentially + make it onto the wire causing a network flood + + Change: Only place on the wire packets uIP intended to place + on the wire. Drop all other packets. + +uIP v0.5.14 (May 18, 2010) +======================================================= + + Fixes + ----- + 1. Problem: brcm_iscsiuio would crash when offloading using a + bnx2x device /dev/mem could not be + opened, (ie. SE Linux enabled) + + Cause: /dev/mem could not be opened, (ie. SE Linux enabled) + and then the NIC would be improperly initialized. + + Change: If /dev/mem is not able to be opened, then the device + is closed + + 2. Problem: brcm_iscsiuio would crash when brcm_iscsiuio is + being shutdown + + Cause: The NIC mutex was deferenced imporperly when the NIC + is being closed + + Change: Take the NIC mutex lock only when the NIC is closed. + +uIP v0.5.13 (May 16, 2010) +======================================================= + + Fixes + ----- + 1. Problem: brcm_iscsiuio would crash with heavy traffic directed + at the iSCSI traffic + + Cause: Packets which are sized between 1006-1024 bytes would + crash brcm_iscsiuio because brcm_iscsiuio is not sized + to handle such large packets + + Change: Drop large packets, properly hold the NIC mutex lock + for the duration when NIC fields are being used. + + +uIP v0.5.12 (May 13, 2010) +======================================================= + + Fixes + ----- + 1. Problem: brcm_iscsiuio could crash on when L2 interface is + ifdown'ed + + Cause: The local NIC pointer was not initialized properly + in the routine parse_iface() + + Change: Properly initialize the NIC pointer + + 2. Problem: Documentation referred to older admin_client which + doesn't exist any more because brcm_iscsiuio uses + the iscsid iface file + + Change: Remove the stale references + + +uIP v0.5.11 (May 11, 2010) +======================================================= + + Fixes + ----- + 1. Problem: brcm_iscsiuio could crash on invalid packet sizes + + Cause: The hardware BD could be a large value because of a + hardware error + + Change: Limit the size of the packet dumped to the MTU size + + Enhancements + ------------ + + 1. Change: During the running of the configure script now + the script will check for ar and ranlib binaries + + +uIP v0.5.10 (May 03, 2010) +======================================================= + + Fixes + ----- + 1. Problem: BCM57712 not recognized + + Cause: The PCI ID's in the bnx2x file were missing. + + Change: Added proper BCM57712, BCM57712E, BCM57713, BCM57713E + PCI ID's + + 2. Problem: (CQ 47481) brcm_iscsiuio not installed in correct location + + Cause: Default install path for autoconf is /usr/local + + Change: Change the default prefix to '/' so the brcm_iscsiuio + binary is installed to /sbin/ + + Enhancements + ------------ + + 1. Change: Remove dependency on Yacc and Lex + + +uIP v0.5.9 (April 28, 2010) +======================================================= + + Fixes + ----- + 1. Problem: bnx2x T6.0 driver would not login + + Cause: The bnx2x code was not using the T6.0 HSI offsets + + Change: Determine to bnx2x driver version eariler to properly use the + T4.8 or T6.0 HSI + + Enhancements + ------------ + + 1. Change: Collapse all the various locks to use the NIC lock to shrink + memory footprint + + 2. Change: Consolidate upper layer checksumming code + + +uIP v0.5.5 (March 02, 2010) +======================================================= + + Enhancements + ------------ + + 1. Change: Add support for T6.0 bnx2x HSI and 57712. + + 2. Change: Initial support for IPv6 + +uIP v0.5.8 (April 22, 2010) +======================================================= + + Enhancements + ------------ + + 1. Change: Add support for T6.0 bnx2x HSI and 57712. + + 2. Change: Initial support for IPv6 + +uIP v0.5.7 (March 17, 2010) +======================================================= + + Enhancements + ------------ + + 1. Change: Add to documentation on discovering on a particular + iface before logging in + +uIP v0.5.6 (Mar 05, 2009) +======================================================= + Fixes + ----- + 1. Problem: bnx2x panic dump would be seen when sending + traffic to uIP + + Cause: The TX producer index was not properly + incrementing when the wrapping occured + + Change: Do not skip the last TX producer index like the + TX BD's + + Impact: None. + +uIP v0.5.5 (March 02, 2010) +======================================================= + Initial release + + Enhancements + ------------ + + 1. Change: Add to documentation on debugging/logging for uIP + + +uIP v0.5.4 (Feb 22, 2010) +======================================================= + Fixes + ----- + 1. Problem: Compile error where 'ETHERTYPE_VLAN' define + is missing + + Cause: Certain distributions do not define 'ETHERTYPE_VLAN' + in the header file "net/ethernet.h". + + Change: Added proper defines for ETHERTYPE_VLAN when necessary + + Impact: None. + + +uIP v0.5.3 (Feb 18, 2010) +======================================================= + Fixes + ----- + 1. Problem: Using VLAN's on offloaded iSCSI connections + + Cause: (CQ45983) VLAN tags were not being properly inserted + when sending the ARP request packets + + Change: Added VLAN tags when sending ARP request packets + + Impact: None. + + +uIP v0.5.2 (Dec 10, 2009) +======================================================= + Fixes + ----- + 1. Problem: Switching between 10G and 1G iSCSI offloaded + devices caused login connectivity problems + + Cause: The NIC devices within uIP were not cleanup + properly. + + Change: The NIC structure is not re-initialized and the + NIC thread is destroyed when the host network + interface is brought down. + + Impact: None. + + +uIP v0.5.1 (Dec 9, 2009) +======================================================= + Fixes + ----- + 1. Problem: 10G devices behind PCI bridges would not collect + + Cause: PCI bus:slot.func string was parsed incorrectly + because the bridge string was used + + Change: Parse the proper PCI bus:slot.func string. + + Impact: None. + + +uIP v0.5.0b (Nov 24, 2009) +======================================================= + Initial release + + Enhancements + ------------ + + 1. Change: Add Broadcom 10G iSCSI offload support + + Impact: Linux + diff --git a/iscsiuio/configure.ac b/iscsiuio/configure.ac new file mode 100644 index 0000000..6c0013b --- /dev/null +++ b/iscsiuio/configure.ac @@ -0,0 +1,78 @@ +dnl iscsiuio uIP user space stack configure.ac file +dnl +dnl Copyright (c) 2004-2013 Broadcom Corporation +dnl Copyright (c) 2014, QLogic Corporation +dnl +dnl This program is free software; you can redistribute it and/or modify +dnl it under the terms of the GNU General Public License as published by +dnl the Free Software Foundation. +dnl +dnl Written by: Eddie Wai (eddie.wai@broadcom.com) +dnl Benjamin Li (benli@broadcom.com) +dnl + +PACKAGE=iscsiuio +VERSION=0.7.8.2 + +AC_INIT([iscsiuio], [0.7.8.2], [QLogic-Storage-Upstream@qlogic.com]) + +AM_INIT_AUTOMAKE +AC_CONFIG_HEADER(config.h) +AC_PATH_PROGS(BASH, bash) + +AC_PROG_CC +AM_PROG_CC_C_O + +AC_PROG_RANLIB + +AC_GNU_SOURCE +AC_PROG_INSTALL +AC_PROG_GCC_TRADITIONAL + +# Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_C_INLINE +AC_TYPE_OFF_T +AC_TYPE_SIZE_T +AC_CHECK_TYPES(int8_t) +AC_CHECK_TYPES(uint8_t) +AC_CHECK_TYPES(int16_t) +AC_CHECK_TYPES(uint16_t) +AC_CHECK_TYPES(int32_t) +AC_CHECK_TYPES(uint32_t) +AC_CHECK_TYPES(int64_t) +AC_CHECK_TYPES(uint64_t) +AC_CHECK_SIZEOF(short, 2) +AC_CHECK_SIZEOF(int, 4) +AC_CHECK_SIZEOF(long, 4) + +AC_C_BIGENDIAN(AC_SUBST([ENDIAN],[BIG]),AC_SUBST([ENDIAN],[LITTLE])) + +AC_LIBTOOL_DLOPEN + +# libtool stuff +AC_PROG_LIBTOOL + +: ${CFLAGS:="-O2"} +CFLAGS="${CFLAGS} -Wall" +## check for --enable-debug first before checking CFLAGS before +## so that we don't mix -O and -g +AC_ARG_ENABLE(debug, +[ --enable-debug Turn on compiler debugging information (default=no)], + [if eval "test x$enable_debug = xyes"; then + CFLAGS="${CFLAGS} -g -O0" + fi]) +AM_CONDITIONAL([DEBUG], [test x$debug = xtrue]) + +AC_CONFIG_COMMANDS([default],[[ echo 'char *build_date = "'`date`'";' > src/unix/build_date.c && echo 'char *build_date;'> src/unix/build_date.h]],[[]]) + +AC_PREFIX_DEFAULT() + +AC_OUTPUT([Makefile +src/Makefile +src/apps/Makefile +src/apps/dhcpc/Makefile +src/apps/brcm-iscsi/Makefile +src/uip/Makefile +src/unix/Makefile +src/unix/libs/Makefile]) diff --git a/iscsiuio/docs/iscsiuio.8 b/iscsiuio/docs/iscsiuio.8 new file mode 100644 index 0000000..b329979 --- /dev/null +++ b/iscsiuio/docs/iscsiuio.8 @@ -0,0 +1,89 @@ +.\" Copyright (c) 2010-2013 Broadcom Corporation +.\" Copyright (c) 2014, QLogic Corporation +.\" This is free documentation; you can redistribute it and/or +.\" modify it under the terms of the GNU General Public License as +.\" published by the Free Software Foundation. +.\" +.\" bnx2.4,v 0.7.8.1b +.\" +.TH iscsiuio 8 "12/10/2013" "QLogic Corporation" +.\" +.\" NAME part +.\" +.SH NAME +iscsiuio \- iSCSI UserSpace I/O driver +.\" +.\" SYNOPSIS part +.\" +.SH SYNOPSIS +.B iscsiuio +.RB [ -d -f -v ] +.PP +.\" +.\" DESCRIPTION part +.\" +.SH DESCRIPTION +iscsiuio is the UserSpace I/O driver for the QLogic NetXtreme II +BCM5706/5708/5709 series PCI/PCI-X Gigabit Ethernet Network Interface Card +(NIC) and for the QLogic NetXtreme II BCM57710/57711/57712/57800/57810/57840 +series PCI-E 10 Gigabit Ethernet Network Interface Card. +The driver has been tested on 2.6.28 kernels and above. +.PP +Refer to the README.TXT from the driver package on how to +compile and install the driver. +.PP +Refer to various Linux documentations +on how to configure network protocol and address. +.\" +.\" DRIVER DEPENDENCIES part +.\" +.SH DRIVER DEPENDENCIES + +.\" +.\" PARAMETER part +.\" +.SH PARAMETERS +There are very few parameters when running this application. +.TP +.BI -d +This is to enable debug mode where debug messages will be sent to stdout +The following debug modes are supported +.P +.RS +DEBUG 4 - Print all messages +.P +INFO 3 - Print messages needed to follow the uIP code (default) +.P +WARN 2 - Print warning messages +.P +ERROR 1 - Only print critical errors +.RE +.PP +.TP +.TP +.BI -f +This is to enable forground mode so that this application doesn't get sent +into the background. +.PP +.TP +.BI -v +This is to print the version. +.PP +.TP +.BI -p +Use pidfile (default /var/run/iscsiuio.pid ) +.PP +.TP +.BI -h +Display this help and exit. + + +.\" +.\" AUTHOR part +.\" +.SH AUTHOR +Benjamin Li \- benli@broadcom.com +.P +Eddie Wai \- eddie.wai@broadcom.com +.SH Maintained by +QLogic-Storage-Upstream@qlogic.com diff --git a/iscsiuio/iscsiuiolog b/iscsiuio/iscsiuiolog new file mode 100644 index 0000000..360947c --- /dev/null +++ b/iscsiuio/iscsiuiolog @@ -0,0 +1,10 @@ +/var/log/iscsiuio.log { + weekly + missingok + notifempty + rotate 4 + sharedscripts + postrotate + pkill -USR1 iscsiuio 2> /dev/null || true + endscript +} diff --git a/iscsiuio/src/Makefile.am b/iscsiuio/src/Makefile.am new file mode 100644 index 0000000..44b0085 --- /dev/null +++ b/iscsiuio/src/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = apps uip unix diff --git a/iscsiuio/src/README b/iscsiuio/src/README new file mode 100644 index 0000000..9fca6fb --- /dev/null +++ b/iscsiuio/src/README @@ -0,0 +1,13 @@ +uIP is a very small implementation of the TCP/IP stack that is written +by Adam Dunkels . More information can be obtained +at the uIP homepage at http://www.sics.se/~adam/uip/. + +This is version $Name: uip-1-0 $. + +The directory structure look as follows: + +apps/ - Example applications +doc/ - Documentation +lib/ - Library code used by some applications +uip/ - uIP TCP/IP stack code +unix/ - uIP as a user space process under FreeBSD or Linux diff --git a/iscsiuio/src/apps/Makefile.am b/iscsiuio/src/apps/Makefile.am new file mode 100644 index 0000000..08ed18d --- /dev/null +++ b/iscsiuio/src/apps/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = dhcpc brcm-iscsi diff --git a/iscsiuio/src/apps/README b/iscsiuio/src/apps/README new file mode 100644 index 0000000..0096c4e --- /dev/null +++ b/iscsiuio/src/apps/README @@ -0,0 +1,2 @@ +This directory contains a few example applications. They are not all +heavily tested, however. diff --git a/iscsiuio/src/apps/brcm-iscsi/Makefile.am b/iscsiuio/src/apps/brcm-iscsi/Makefile.am new file mode 100644 index 0000000..00cbd8e --- /dev/null +++ b/iscsiuio/src/apps/brcm-iscsi/Makefile.am @@ -0,0 +1,13 @@ +AM_CFLAGS = -I${top_srcdir}/src/unix \ + -I${top_srcdir}/src/uip \ + -I${top_srcdir}/src/apps/dhcpc \ + -I${top_srcdir}/src/apps/brcm-iscsi \ + -I${top_srcdir}/../include \ + -I${top_srcdir}/../usr + +noinst_LIBRARIES = lib_apps_brcm_iscsi.a + +lib_apps_brcm_iscsi_a_SOURCES = brcm_iscsi.c + +lib_apps_brcm_iscsi_a_CFLAGS = $(AM_CFLAGS) \ + -DBYTE_ORDER=@ENDIAN@ diff --git a/iscsiuio/src/apps/brcm-iscsi/Makefile.brcm-iscsi b/iscsiuio/src/apps/brcm-iscsi/Makefile.brcm-iscsi new file mode 100644 index 0000000..732275f --- /dev/null +++ b/iscsiuio/src/apps/brcm-iscsi/Makefile.brcm-iscsi @@ -0,0 +1 @@ +APP_SOURCES += brcm-iscsi.c diff --git a/iscsiuio/src/apps/brcm-iscsi/brcm_iscsi.c b/iscsiuio/src/apps/brcm-iscsi/brcm_iscsi.c new file mode 100644 index 0000000..4223ca4 --- /dev/null +++ b/iscsiuio/src/apps/brcm-iscsi/brcm_iscsi.c @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2009-2011, Broadcom Corporation + * Copyright (c) 2014, QLogic Corporation + * + * Written by: Benjamin Li + * Based on code example from Adam Dunkels + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam Dunkels. + * 4. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** + * \addtogroup brcm-iscsi + * @{ + */ + +/** + * \file + * An example of how to write uIP applications + * with protosockets + * \author + * Benjamin Li + */ + +/* + * This is a short example of how to write uIP applications using + * protosockets. + */ + +/* + * We define the application state (struct hello_world_state) in the + * hello-world.h file, so we need to include it here. We also include + * uip.h (since this cannot be included in hello-world.h) and + * , since we use the memcpy() function in the code. + */ +#include "brcm_iscsi.h" +#include "uip.h" +#include +#include + +#include "uip_arp.h" + +/*---------------------------------------------------------------------------*/ +/* + * The initialization function. We must explicitly call this function + * from the system initialization code, some time after uip_init() is + * called. + */ +void brcm_iscsi_init(void) +{ +} + +/*---------------------------------------------------------------------------*/ +/* + * In hello-world.h we have defined the UIP_APPCALL macro to + * hello_world_appcall so that this funcion is uIP's application + * function. This function is called whenever an uIP event occurs + * (e.g. when a new connection is established, new data arrives, sent + * data is acknowledged, data needs to be retransmitted, etc.). + */ +void brcm_iscsi_appcall(struct uip_stack *ustack) +{ +} diff --git a/iscsiuio/src/apps/brcm-iscsi/brcm_iscsi.h b/iscsiuio/src/apps/brcm-iscsi/brcm_iscsi.h new file mode 100644 index 0000000..10bfc95 --- /dev/null +++ b/iscsiuio/src/apps/brcm-iscsi/brcm_iscsi.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2009-2011, Broadcom Corporation + * Copyright (c) 2014, QLogic Corporation + * + * Written by: Benjamin Li + * Based on code example from Adam Dunkels + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam Dunkels. + * 4. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** + * \addtogroup apps + * @{ + */ + +/** + * \defgroup helloworld Hello, world + * @{ + * + * A small example showing how to write applications with + * \ref psock "protosockets". + */ + +/** + * \file + * Header file for an example of how to write uIP applications + * with protosockets. + * \author + * Benjamin Li + */ + +#ifndef __BRCM_ISCSI_H__ +#define __BRCM_ISCSI_H__ + +/* Since this file will be included by uip.h, we cannot include uip.h + here. But we might need to include uipopt.h if we need the u8_t and + u16_t datatypes. */ +#include "uipopt.h" +#include "uip.h" +#include "psock.h" + +/* Next, we define the hello_world_state structure. This is the state + of our application, and the memory required for this state is + allocated together with each TCP connection. One application state + for each TCP connection. */ +struct hello_world_state { + struct psock p; + u8_t inputbuffer[32]; + u8_t name[40]; + + struct uip_udp_conn *conn; +}; + +/* Finally we define the application function to be called by uIP. */ +void brcm_iscsi_appcall(struct uip_stack *ustack); +#ifndef UIP_APPCALL +#define UIP_APPCALL brcm_iscsi_appcall +#endif /* UIP_APPCALL */ + +void brcm_iscsi_init(void); + +#endif /* __BRCM_ISCSI_H__ */ +/** @} */ +/** @} */ diff --git a/iscsiuio/src/apps/dhcpc/Makefile.am b/iscsiuio/src/apps/dhcpc/Makefile.am new file mode 100644 index 0000000..1c97993 --- /dev/null +++ b/iscsiuio/src/apps/dhcpc/Makefile.am @@ -0,0 +1,13 @@ +AM_CFLAGS = -I${top_srcdir}/src/unix \ + -I${top_srcdir}/src/uip \ + -I${top_srcdir}/src/apps/dhcpc \ + -I${top_srcdir}/src/apps/brcm-iscsi \ + -I${top_srcdir}/../include \ + -I${top_srcdir}/../usr + +noinst_LIBRARIES = lib_apps_dhcpc.a + +lib_apps_dhcpc_a_SOURCES = dhcpc.c dhcpv6.c + +lib_apps_dhcpc_a_CFLAGS = $(AM_CFLAGS) \ + -DBYTE_ORDER=@ENDIAN@ diff --git a/iscsiuio/src/apps/dhcpc/Makefile.dhcpc b/iscsiuio/src/apps/dhcpc/Makefile.dhcpc new file mode 100644 index 0000000..f84c84f --- /dev/null +++ b/iscsiuio/src/apps/dhcpc/Makefile.dhcpc @@ -0,0 +1 @@ +APP_SOURCES += dhcpc.c timer.c diff --git a/iscsiuio/src/apps/dhcpc/dhcpc.c b/iscsiuio/src/apps/dhcpc/dhcpc.c new file mode 100644 index 0000000..f4a9994 --- /dev/null +++ b/iscsiuio/src/apps/dhcpc/dhcpc.c @@ -0,0 +1,417 @@ +/* + * Copyright (c) 2005, Swedish Institute of Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack + * + */ + +#include +#include +#include +#include +#include +#include + +#include "uip.h" +#include "dhcpc.h" +#include "timer.h" +#include "pt.h" + +#include "debug.h" +#include "logger.h" +#include "nic.h" +#include "nic_utils.h" + +struct __attribute__ ((__packed__)) dhcp_msg { + u8_t op, htype, hlen, hops; + u8_t xid[4]; + u16_t secs, flags; + u8_t ciaddr[4]; + u8_t yiaddr[4]; + u8_t siaddr[4]; + u8_t giaddr[4]; + u8_t chaddr[16]; +#ifndef UIP_CONF_DHCP_LIGHT + u8_t sname[64]; + u8_t file[128]; +#endif + u8_t options[312]; +}; + +#define BOOTP_BROADCAST 0x8000 + +#define DHCP_REQUEST 1 +#define DHCP_REPLY 2 +#define DHCP_HTYPE_ETHERNET 1 +#define DHCP_HLEN_ETHERNET 6 +#define DHCP_MSG_LEN 236 + +#define DHCPC_SERVER_PORT 67 +#define DHCPC_CLIENT_PORT 68 + +#define DHCPDISCOVER 1 +#define DHCPOFFER 2 +#define DHCPREQUEST 3 +#define DHCPDECLINE 4 +#define DHCPACK 5 +#define DHCPNAK 6 +#define DHCPRELEASE 7 + +#define DHCP_OPTION_SUBNET_MASK 1 +#define DHCP_OPTION_ROUTER 3 +#define DHCP_OPTION_DNS_SERVER 6 +#define DHCP_OPTION_REQ_IPADDR 50 +#define DHCP_OPTION_LEASE_TIME 51 +#define DHCP_OPTION_MSG_TYPE 53 +#define DHCP_OPTION_SERVER_ID 54 +#define DHCP_OPTION_REQ_LIST 55 +#define DHCP_OPTION_END 255 + +static u8_t xid[4] = { 0xad, 0xde, 0x12, 0x23 }; +static const u8_t magic_cookie[4] = { 99, 130, 83, 99 }; + +struct dhcpc_options dhcpc_opt = { + .enable_random_xid = 1, +}; + +/*---------------------------------------------------------------------------*/ +static u8_t *add_msg_type(u8_t *optptr, u8_t type) +{ + *optptr++ = DHCP_OPTION_MSG_TYPE; + *optptr++ = 1; + *optptr++ = type; + return optptr; +} + +/*---------------------------------------------------------------------------*/ +static u8_t *add_server_id(struct dhcpc_state *s, u8_t *optptr) +{ + *optptr++ = DHCP_OPTION_SERVER_ID; + *optptr++ = 4; + memcpy(optptr, s->serverid, 4); + return optptr + 4; +} + +/*---------------------------------------------------------------------------*/ +static u8_t *add_req_ipaddr(struct dhcpc_state *s, u8_t *optptr) +{ + *optptr++ = DHCP_OPTION_REQ_IPADDR; + *optptr++ = 4; + memcpy(optptr, s->ipaddr, 4); + return optptr + 4; +} + +/*---------------------------------------------------------------------------*/ +static u8_t *add_req_options(u8_t *optptr) +{ + *optptr++ = DHCP_OPTION_REQ_LIST; + *optptr++ = 3; + *optptr++ = DHCP_OPTION_SUBNET_MASK; + *optptr++ = DHCP_OPTION_ROUTER; + *optptr++ = DHCP_OPTION_DNS_SERVER; + return optptr; +} + +/*---------------------------------------------------------------------------*/ +static u8_t *add_end(u8_t *optptr) +{ + *optptr++ = DHCP_OPTION_END; + return optptr; +} + +/*---------------------------------------------------------------------------*/ +static void create_msg(struct dhcpc_state *s, struct dhcp_msg *m) +{ + m->op = DHCP_REQUEST; + m->htype = DHCP_HTYPE_ETHERNET; + m->hlen = s->mac_len; + m->hops = 0; + memcpy(m->xid, xid, sizeof(m->xid)); + m->secs = 0; + m->flags = const_htons(BOOTP_BROADCAST); /* Broadcast bit. */ + /* uip_ipaddr_copy(m->ciaddr, uip_hostaddr); */ + memcpy(m->ciaddr, s->ustack->hostaddr, sizeof(m->ciaddr)); + memset(m->yiaddr, 0, sizeof(m->yiaddr)); + memset(m->siaddr, 0, sizeof(m->siaddr)); + memset(m->giaddr, 0, sizeof(m->giaddr)); + memcpy(m->chaddr, s->mac_addr, s->mac_len); + memset(&m->chaddr[s->mac_len], 0, sizeof(m->chaddr) - s->mac_len); +#ifndef UIP_CONF_DHCP_LIGHT + memset(m->sname, 0, sizeof(m->sname)); + memset(m->file, 0, sizeof(m->file)); +#endif + + memcpy(m->options, magic_cookie, sizeof(magic_cookie)); +} + +/*---------------------------------------------------------------------------*/ +static void send_discover(struct dhcpc_state *s) +{ + u8_t *end; + struct dhcp_msg *m = (struct dhcp_msg *)s->ustack->uip_appdata; + + create_msg(s, m); + + end = add_msg_type(&m->options[4], DHCPDISCOVER); + end = add_req_options(end); + end = add_end(end); + + uip_appsend(s->ustack, s->ustack->uip_appdata, + end - (u8_t *) s->ustack->uip_appdata); +} + +/*---------------------------------------------------------------------------*/ +static void send_request(struct dhcpc_state *s) +{ + u8_t *end; + struct dhcp_msg *m = (struct dhcp_msg *)s->ustack->uip_appdata; + + create_msg(s, m); + + end = add_msg_type(&m->options[4], DHCPREQUEST); + end = add_server_id(s, end); + end = add_req_ipaddr(s, end); + end = add_end(end); + + uip_appsend(s->ustack, s->ustack->uip_appdata, + end - (u8_t *) s->ustack->uip_appdata); +} + +/*---------------------------------------------------------------------------*/ +static u8_t parse_options(struct dhcpc_state *s, u8_t *optptr, int len) +{ + u8_t *end = optptr + len; + u8_t type = 0; + + while (optptr < end) { + switch (*optptr) { + case DHCP_OPTION_SUBNET_MASK: + memcpy(s->netmask, optptr + 2, 4); + break; + case DHCP_OPTION_ROUTER: + memcpy(s->default_router, optptr + 2, 4); + break; + case DHCP_OPTION_DNS_SERVER: + memcpy(s->dnsaddr, optptr + 2, 4); + break; + case DHCP_OPTION_MSG_TYPE: + type = *(optptr + 2); + break; + case DHCP_OPTION_SERVER_ID: + memcpy(s->serverid, optptr + 2, 4); + break; + case DHCP_OPTION_LEASE_TIME: + memcpy(s->lease_time, optptr + 2, 4); + break; + case DHCP_OPTION_END: + return type; + } + + optptr += optptr[1] + 2; + } + return type; +} + +/*---------------------------------------------------------------------------*/ +static u8_t parse_msg(struct dhcpc_state *s) +{ + struct dhcp_msg *m = (struct dhcp_msg *)s->ustack->uip_appdata; + + if (m->op == DHCP_REPLY && + memcmp(m->xid, xid, sizeof(xid)) == 0 && + memcmp(m->chaddr, s->mac_addr, s->mac_len) == 0) { + memcpy(s->ipaddr, m->yiaddr, 4); + return parse_options(s, &m->options[4], uip_datalen(s->ustack)); + } + return 0; +} + +/*---------------------------------------------------------------------------*/ +static PT_THREAD(handle_dhcp(struct uip_stack *ustack)) +{ + struct dhcpc_state *s; + s = ustack->dhcpc; + + if (s == NULL) { + LOG_WARN("Could not find dhcpc state"); + return PT_ENDED; + } + + PT_BEGIN(&s->pt); + + /* try_again: */ + s->state = STATE_SENDING; + s->ticks = CLOCK_SECOND; + + do { + send_discover(s); + timer_set(&s->timer, s->ticks); + PT_WAIT_UNTIL(&s->pt, uip_newdata(s->ustack) + || timer_expired(&s->timer)); + + if (uip_newdata(s->ustack) && parse_msg(s) == DHCPOFFER) { + s->state = STATE_OFFER_RECEIVED; + break; + } + + if (s->ticks < CLOCK_SECOND * 60) + s->ticks += CLOCK_SECOND; + else + PT_RESTART(&s->pt); + } while (s->state != STATE_OFFER_RECEIVED); + + s->ticks = CLOCK_SECOND; + + do { + send_request(s); + timer_set(&s->timer, s->ticks); + s->ustack->uip_flags &= ~UIP_NEWDATA; + PT_WAIT_UNTIL(&s->pt, uip_newdata(s->ustack) + || timer_expired(&s->timer)); + + if (uip_newdata(s->ustack) && parse_msg(s) == DHCPACK) { + s->state = STATE_CONFIG_RECEIVED; + break; + } + + if (s->ticks <= CLOCK_SECOND * 10) + s->ticks += CLOCK_SECOND; + else + PT_RESTART(&s->pt); + } while (s->state != STATE_CONFIG_RECEIVED); + + LOG_INFO("Got IP address %d.%d.%d.%d", + uip_ipaddr1(s->ipaddr), uip_ipaddr2(s->ipaddr), + uip_ipaddr3(s->ipaddr), uip_ipaddr4(s->ipaddr)); + LOG_INFO("Got netmask %d.%d.%d.%d", + uip_ipaddr1(s->netmask), uip_ipaddr2(s->netmask), + uip_ipaddr3(s->netmask), uip_ipaddr4(s->netmask)); + LOG_INFO("Got DNS server %d.%d.%d.%d", + uip_ipaddr1(s->dnsaddr), uip_ipaddr2(s->dnsaddr), + uip_ipaddr3(s->dnsaddr), uip_ipaddr4(s->dnsaddr)); + LOG_INFO("Got default router %d.%d.%d.%d", + uip_ipaddr1(s->default_router), uip_ipaddr2(s->default_router), + uip_ipaddr3(s->default_router), + uip_ipaddr4(s->default_router)); + s->lease_time_nl32 = + ntohs(s->lease_time[0]) * 65536ul + ntohs(s->lease_time[1]); + LOG_INFO("Lease expires in %ld seconds", s->lease_time_nl32); + + s->last_update = time(NULL); + + set_uip_stack(s->ustack, + (uip_ip4addr_t *) s->ipaddr, + (uip_ip4addr_t *) s->netmask, + (uip_ip4addr_t *) s->default_router, + (uint8_t *) s->mac_addr); + + /* Put the stack thread back into a long sleep */ + s->nic->flags |= NIC_LONG_SLEEP; + + /* timer_stop(&s.timer); */ + + /* Handle DHCP lease expiration */ + s->ticks = CLOCK_SECOND * s->lease_time_nl32; + timer_set(&s->timer, s->ticks); + PT_WAIT_UNTIL(&s->pt, timer_expired(&s->timer)); + LOG_INFO("Lease expired, re-acquire IP address"); + s->nic->flags &= ~NIC_LONG_SLEEP; + PT_RESTART(&s->pt); + + /* + * PT_END restarts the thread so we do this instead. Eventually we + * should reacquire expired leases here. + */ + + while (1) + PT_YIELD(&s->pt); + + PT_END(&(s->pt)); +} + +/*---------------------------------------------------------------------------*/ +int dhcpc_init(nic_t *nic, struct uip_stack *ustack, + const void *mac_addr, int mac_len) +{ + uip_ip4addr_t addr; + struct dhcpc_state *s = ustack->dhcpc; + + if (s) { + LOG_DEBUG("DHCP: DHCP context already allocated"); + return -EALREADY; + } + s = malloc(sizeof(*s)); + if (s == NULL) { + LOG_ERR("Couldn't allocate size for dhcpc info"); + return -ENOMEM; + } + + memset(s, 0, sizeof(*s)); + s->nic = nic; + s->ustack = ustack; + s->mac_addr = mac_addr; + s->mac_len = mac_len; + s->state = STATE_INITIAL; + + /* Initialize XID to randomly */ + if (dhcpc_opt.enable_random_xid == 1) { + u32_t gen_xid; + gen_xid = random(); + memcpy(xid, &gen_xid, sizeof(gen_xid)); + } + uip_ipaddr(addr, 255, 255, 255, 255); + s->conn = uip_udp_new(ustack, &addr, const_htons(DHCPC_SERVER_PORT)); + if (s->conn != NULL) + uip_udp_bind(s->conn, const_htons(DHCPC_CLIENT_PORT)); + + ustack->dhcpc = s; + + /* Let the RX poll value take over */ + nic->flags &= ~NIC_LONG_SLEEP; + + PT_INIT(&s->pt); + + return 0; +} + +/*---------------------------------------------------------------------------*/ +void dhcpc_appcall(struct uip_stack *ustack) +{ + handle_dhcp(ustack); +} + +/*---------------------------------------------------------------------------*/ +void dhcpc_request(struct uip_stack *ustack) +{ + struct dhcpc_state *s = ustack->dhcpc; + + if (s != NULL && s->state == STATE_INITIAL) + handle_dhcp(ustack); +} + +/*---------------------------------------------------------------------------*/ diff --git a/iscsiuio/src/apps/dhcpc/dhcpc.h b/iscsiuio/src/apps/dhcpc/dhcpc.h new file mode 100644 index 0000000..89cf086 --- /dev/null +++ b/iscsiuio/src/apps/dhcpc/dhcpc.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2005, Swedish Institute of Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack + * + */ +#ifndef __DHCPC_H__ +#define __DHCPC_H__ + +#include + +#include "nic.h" +#include "timer.h" +#include "pt.h" +#include "uip.h" + +#define STATE_INITIAL 0 +#define STATE_SENDING 1 +#define STATE_OFFER_RECEIVED 2 +#define STATE_CONFIG_RECEIVED 3 + +struct dhcpc_state { + struct pt pt; + + nic_t *nic; + struct uip_stack *ustack; + char state; + struct uip_udp_conn *conn; + struct timer timer; + u32_t ticks; + const void *mac_addr; + int mac_len; + + u8_t serverid[4]; + + u16_t lease_time[2]; + u32_t lease_time_nl32; + u16_t ipaddr[2]; + u16_t netmask[2]; + u16_t dnsaddr[2]; + u16_t default_router[2]; + + time_t last_update; +}; + +struct dhcpc_options { + u8_t enable_random_xid; + u8_t xid[4]; +}; + +int dhcpc_init(nic_t *nic, struct uip_stack *ustack, + const void *mac_addr, int mac_len); +void dhcpc_request(struct uip_stack *ustack); + +void dhcpc_appcall(struct uip_stack *ustack); + +void dhcpc_configured(const struct dhcpc_state *s); + +#define UIP_UDP_APPCALL dhcpc_appcall + +#endif /* __DHCPC_H__ */ diff --git a/iscsiuio/src/apps/dhcpc/dhcpv6.c b/iscsiuio/src/apps/dhcpc/dhcpv6.c new file mode 100644 index 0000000..a4e25f0 --- /dev/null +++ b/iscsiuio/src/apps/dhcpc/dhcpv6.c @@ -0,0 +1,512 @@ +/* + * Copyright (c) 2011, Broadcom Corporation + * Copyright (c) 2014, QLogic Corporation + * + * Written by: Eddie Wai + * Based on code from Kevin Tran's iSCSI boot code + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam Dunkels. + * 4. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * dhcpv6.c - DHCPv6 engine + * + */ +#include +#include + +#include "ipv6.h" +#include "ipv6_pkt.h" +#include "dhcpv6.h" +#include "logger.h" + +/* Local function prototypes */ +static int dhcpv6_send_solicit_packet(struct dhcpv6_context *context); +static int dhcpv6_send_request_packet(struct dhcpv6_context *context); +static u16_t dhcpv6_init_packet(struct dhcpv6_context *context, u8_t type); +static void dhcpv6_init_dhcpv6_server_addr(struct ipv6_addr *addr); +static void dhcpv6_handle_advertise(struct dhcpv6_context *context, + u16_t dhcpv6_len); +static void dhcpv6_handle_reply(struct dhcpv6_context *context, + u16_t dhcpv6_len); +static int dhcpv6_process_opt_ia_na(struct dhcpv6_context *context, + struct dhcpv6_opt_hdr *opt_hdr); +static void dhcpv6_process_opt_dns_servers(struct dhcpv6_context *context, + struct dhcpv6_opt_hdr *opt_hdr); +static void dhcpv6_parse_vendor_option(struct dhcpv6_context *context, + u8_t *option, int len); + +void dhcpv6_init(struct dhcpv6_context *context) +{ + context->seconds = 0; + context->our_mac_addr = + ipv6_get_link_addr(context->ipv6_context); + + /* Use the last four bytes of MAC address as base of the transaction + ID */ + context->dhcpv6_transaction_id = context->our_mac_addr->last_4_bytes; + + context->dhcpv6_done = FALSE; + strcpy(context->dhcp_vendor_id, "BRCM ISAN"); +} + +int dhcpv6_do_discovery(struct dhcpv6_context *context) +{ + int retc = ISCSI_FAILURE; + + context->eth = + (struct eth_hdr *)context->ipv6_context->ustack->data_link_layer; + context->ipv6 = + (struct ipv6_hdr *)context->ipv6_context->ustack->network_layer; + context->udp = + (struct udp_hdr *)((u8_t *)context->ipv6 + sizeof(struct ipv6_hdr)); + + /* Send out DHCPv6 Solicit packet. */ + dhcpv6_send_solicit_packet(context); + + return retc; +} + +static int dhcpv6_send_solicit_packet(struct dhcpv6_context *context) +{ + u16_t packet_len; + + LOG_DEBUG("DHCPV6: Send solicit"); + packet_len = dhcpv6_init_packet(context, DHCPV6_SOLICIT); + context->dhcpv6_state = DHCPV6_STATE_SOLICIT_SENT; + ipv6_send_udp_packet(context->ipv6_context, packet_len); + + return 0; +} + +static int dhcpv6_send_request_packet(struct dhcpv6_context *context) +{ + u16_t packet_len; + + LOG_DEBUG("DHCPV6: Send request"); + packet_len = dhcpv6_init_packet(context, DHCPV6_REQUEST); + + context->dhcpv6_state = DHCPV6_STATE_REQ_SENT; + ipv6_send_udp_packet(context->ipv6_context, packet_len); + + return 0; +} + +static u16_t dhcpv6_init_packet(struct dhcpv6_context *context, u8_t type) +{ + u16_t pkt_len; + struct udp_hdr *udp = context->udp; + union dhcpv6_hdr *dhcpv6; + struct dhcpv6_option *opt; + u16_t len; + + /* Initialize dest IP with well-known DHCP server address */ + dhcpv6_init_dhcpv6_server_addr(&context->ipv6->ipv6_dst); + /* Initialize dest MAC based on MC dest IP */ + ipv6_mc_init_dest_mac(context->eth, context->ipv6); + + /* Initialize UDP header */ + udp->src_port = HOST_TO_NET16(DHCPV6_CLIENT_PORT); + udp->dest_port = HOST_TO_NET16(DHCPV6_SERVER_PORT); + + /* + * DHCPv6 section has the following format per RFC 3315 + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | msg-type | transaction-id | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * . options . + * . (variable) . + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + dhcpv6 = (union dhcpv6_hdr *)((u8_t *)udp + sizeof(struct udp_hdr)); + + if (dhcpv6->dhcpv6_type != type) + context->dhcpv6_transaction_id++; + + dhcpv6->dhcpv6_trans_id = context->dhcpv6_transaction_id; + dhcpv6->dhcpv6_type = type; + + /* Keep track of length of all DHCP options. */ + pkt_len = sizeof(union dhcpv6_hdr); + + if (dhcpv6->dhcpv6_type == DHCPV6_REQUEST) { + /* We will send back whatever DHCPv6 sent us */ + return ((u8_t *)udp - (u8_t *)context->eth + + NET_TO_HOST16(udp->length)); + } + + opt = (struct dhcpv6_option *)((u8_t *)dhcpv6 + + sizeof(union dhcpv6_hdr)); + /* Add client ID option */ + opt->hdr.type = HOST_TO_NET16(DHCPV6_OPT_CLIENTID); + opt->hdr.length = HOST_TO_NET16(sizeof(struct dhcpv6_opt_client_id)); + opt->type.client_id.duid_type = + HOST_TO_NET16(DHCPV6_DUID_TYPE_LINK_LAYER_AND_TIME); + opt->type.client_id.hw_type = HOST_TO_NET16(DHCPV6_HW_TYPE_ETHERNET); + opt->type.client_id.time = HOST_TO_NET32(clock_time()/1000 - + 0x3A4FC880); + memcpy((char *)&opt->type.client_id.link_layer_addr, + (char *)context->our_mac_addr, sizeof(struct mac_address)); + pkt_len += sizeof(struct dhcpv6_opt_client_id) + + sizeof(struct dhcpv6_opt_hdr); + opt = (struct dhcpv6_option *)((u8_t *)opt + + sizeof(struct dhcpv6_opt_client_id) + + sizeof(struct dhcpv6_opt_hdr)); + + /* Add Vendor Class option if it's configured */ + len = strlen(context->dhcp_vendor_id); + if (len > 0) { + opt->hdr.type = HOST_TO_NET16(DHCPV6_OPT_VENDOR_CLASS); + opt->hdr.length = + HOST_TO_NET16(sizeof(struct dhcpv6_vendor_class) + + len - 1); + opt->type.vendor_class.enterprise_number = + HOST_TO_NET32(IANA_ENTERPRISE_NUM_BROADCOM); + opt->type.vendor_class.vendor_class_length = HOST_TO_NET16(len); + memcpy((char *)&opt->type.vendor_class. + vendor_class_data[0], + (char *)context->dhcp_vendor_id, len); + pkt_len += + sizeof(struct dhcpv6_vendor_class) - 1 + len + + sizeof(struct dhcpv6_opt_hdr); + opt = + (struct dhcpv6_option *)((u8_t *)opt + + sizeof(struct dhcpv6_vendor_class) - 1 + len + + sizeof(struct dhcpv6_opt_hdr)); + } + + /* Add IA_NA option */ + opt->hdr.type = HOST_TO_NET16(DHCPV6_OPT_IA_NA); + opt->hdr.length = HOST_TO_NET16(sizeof(struct dhcpv6_opt_id_assoc_na)); + opt->type.ida_na.iaid = htonl(context->our_mac_addr->last_4_bytes); + opt->type.ida_na.t1 = 0; + opt->type.ida_na.t2 = 0; + pkt_len += sizeof(struct dhcpv6_opt_id_assoc_na) + + sizeof(struct dhcpv6_opt_hdr); + opt = (struct dhcpv6_option *)((u8_t *)opt + + sizeof(struct dhcpv6_opt_id_assoc_na) + + sizeof(struct dhcpv6_opt_hdr)); + /* Add Elapsed Time option */ + opt->hdr.type = HOST_TO_NET16(DHCPV6_OPT_ELAPSED_TIME); + opt->hdr.length = HOST_TO_NET16(sizeof(struct dhcpv6_opt_elapse_time)); + opt->type.elapsed_time.time = HOST_TO_NET16(context->seconds); + pkt_len += sizeof(struct dhcpv6_opt_elapse_time) + + sizeof(struct dhcpv6_opt_hdr); + + /* Add Option Request List */ + opt = (struct dhcpv6_option *)((u8_t *)opt + + sizeof(struct dhcpv6_opt_elapse_time) + + sizeof(struct dhcpv6_opt_hdr)); + opt->hdr.type = HOST_TO_NET16(DHCPV6_OPT_ORO); + opt->hdr.length = HOST_TO_NET16(3 * + sizeof(struct dhcpv6_opt_request_list)); + opt->type.list.request_code[0] = HOST_TO_NET16(DHCPV6_OPT_VENDOR_CLASS); + opt->type.list.request_code[1] = HOST_TO_NET16(DHCPV6_OPT_VENDOR_OPTS); + opt->type.list.request_code[2] = HOST_TO_NET16(DHCPV6_OPT_DNS_SERVERS); + pkt_len += 3 * sizeof(struct dhcpv6_opt_request_list) + + sizeof(struct dhcpv6_opt_hdr); + + udp->length = HOST_TO_NET16(sizeof(struct udp_hdr) + pkt_len); + + pkt_len += + ((u8_t *)udp - (u8_t *)context->eth) + sizeof(struct udp_hdr); + + return pkt_len; +} + +static void dhcpv6_init_dhcpv6_server_addr(struct ipv6_addr *addr) +{ + /* Well-known DHCPv6 server address is ff02::1:2 */ + memset((char *)addr, 0, sizeof(struct ipv6_addr)); + addr->addr8[0] = 0xff; + addr->addr8[1] = 0x02; + addr->addr8[13] = 0x01; + addr->addr8[15] = 0x02; +} + +void ipv6_udp_handle_dhcp(struct dhcpv6_context *context) +{ + union dhcpv6_hdr *dhcpv6; + u16_t dhcpv6_len; + + if (context->dhcpv6_done == TRUE) + return; + + dhcpv6 = (union dhcpv6_hdr *)((u8_t *)context->udp + + sizeof(struct udp_hdr)); + + if (dhcpv6->dhcpv6_trans_id != context->dhcpv6_transaction_id) + return; + + dhcpv6_len = + NET_TO_HOST16(context->udp->length) - sizeof(struct udp_hdr); + + switch (dhcpv6->dhcpv6_type) { + case DHCPV6_ADVERTISE: + dhcpv6_handle_advertise(context, dhcpv6_len); + break; + + case DHCPV6_REPLY: + dhcpv6_handle_reply(context, dhcpv6_len); + break; + + default: + break; + } +} + +static void dhcpv6_handle_advertise(struct dhcpv6_context *context, + u16_t dhcpv6_len) +{ + union dhcpv6_hdr *dhcpv6 = + (union dhcpv6_hdr *)((u8_t *)context->udp + + sizeof(struct udp_hdr)); + struct dhcpv6_opt_hdr *opt; + u16_t type; + int i; + int opt_len; + u8_t *vendor_id = NULL; + u16_t vendor_id_len = 0; + u8_t *vendor_opt_data = NULL; + int vendor_opt_len = 0; + int addr_cnt = 0; + + /* We only handle DHCPv6 advertise if we recently sent DHCPv6 solicit */ + if (context->dhcpv6_state != DHCPV6_STATE_SOLICIT_SENT) + return; + + LOG_DEBUG("DHCPV6: handle advertise"); + context->dhcpv6_state = DHCPV6_STATE_ADV_RCVD; + + i = 0; + while (i < (dhcpv6_len - sizeof(union dhcpv6_hdr))) { + opt = (struct dhcpv6_opt_hdr *)((u8_t *)dhcpv6 + + sizeof(union dhcpv6_hdr) + i); + opt_len = NET_TO_HOST16(opt->length); + + type = NET_TO_HOST16(opt->type); + + /* We only care about some of the options */ + switch (type) { + case DHCPV6_OPT_IA_NA: + if (context-> + dhcpv6_task & DHCPV6_TASK_GET_IP_ADDRESS) { + addr_cnt += + dhcpv6_process_opt_ia_na(context, opt); + } + break; + + case DHCPV6_OPT_VENDOR_CLASS: + vendor_id_len = + NET_TO_HOST16(((struct dhcpv6_option *)opt)->type. + vendor_class.vendor_class_length); + vendor_id = + &((struct dhcpv6_option *)opt)->type.vendor_class. + vendor_class_data[0]; + break; + + case DHCPV6_OPT_VENDOR_OPTS: + vendor_opt_len = opt_len - 4; + vendor_opt_data = + &((struct dhcpv6_option *)opt)->type.vendor_opts. + vendor_opt_data[0]; + break; + + case DHCPV6_OPT_DNS_SERVERS: + if (context->dhcpv6_task & DHCPV6_TASK_GET_OTHER_PARAMS) + dhcpv6_process_opt_dns_servers(context, opt); + break; + + default: + break; + } + + i += NET_TO_HOST16(opt->length) + sizeof(struct dhcpv6_opt_hdr); + } + + if (context->dhcpv6_task & DHCPV6_TASK_GET_OTHER_PARAMS) { + if ((vendor_id_len > 0) && + (strncmp((char *)vendor_id, + (char *)context->dhcp_vendor_id, + vendor_id_len) == 0)) { + dhcpv6_parse_vendor_option(context, + vendor_opt_data, + vendor_opt_len); + context->dhcpv6_done = TRUE; + } + } + + if (context->dhcpv6_task & DHCPV6_TASK_GET_IP_ADDRESS) { + if (addr_cnt > 0) { + /* + * If we need to acquire IP address from the server, + * we need to send Request to server to confirm. + */ + dhcpv6_send_request_packet(context); + context->dhcpv6_done = TRUE; + } + } + + if (context->dhcpv6_done) { + /* Keep track of IPv6 address of DHCHv6 server */ + memcpy((char *)&context->dhcp_server, + (char *)&context->ipv6->ipv6_src, + sizeof(struct ipv6_addr)); + } +} + +static int dhcpv6_process_opt_ia_na(struct dhcpv6_context *context, + struct dhcpv6_opt_hdr *opt_hdr) +{ + int i; + int opt_len; + struct dhcpv6_option *opt; + int len; + int addr_cnt; + opt_len = NET_TO_HOST16(opt_hdr->length) - + sizeof(struct dhcpv6_opt_id_assoc_na); + + i = 0; + addr_cnt = 0; + while (i < opt_len) { + opt = + (struct dhcpv6_option *)((u8_t *)opt_hdr + + sizeof(struct dhcpv6_opt_hdr) + + sizeof(struct dhcpv6_opt_id_assoc_na) + i); + + len = NET_TO_HOST16(opt->hdr.length); + switch (NET_TO_HOST16(opt->hdr.type)) { + case DHCPV6_OPT_IAADDR: + if (len > + (sizeof(struct dhcpv6_opt_hdr) + + sizeof(struct dhcpv6_opt_iaa_addr))) { + struct dhcpv6_option *in_opt; + + in_opt = (struct dhcpv6_option *)((u8_t *)opt + + sizeof(struct dhcpv6_opt_hdr) + + sizeof(struct dhcpv6_opt_iaa_addr)); + if (in_opt->hdr.type == + HOST_TO_NET16(DHCPV6_OPT_STATUS_CODE)) { + /* This entry has error! */ + if (in_opt->type.sts.status != 0) + break; + } + } + LOG_INFO("DHCPv6: Got IP Addr"); + /* Status is OK, let's add this addr to our address + list */ + ipv6_add_prefix_entry(context->ipv6_context, + &opt->type.iaa_addr.addr, 64); + + /* Add multicast address for this address */ + ipv6_add_solit_node_address(context-> + ipv6_context, + &opt->type.iaa_addr.addr); + addr_cnt++; + break; + + default: + break; + } + + i += len + sizeof(struct dhcpv6_opt_hdr); + } + + return addr_cnt; +} + +static void dhcpv6_process_opt_dns_servers(struct dhcpv6_context *context, + struct dhcpv6_opt_hdr *opt_hdr) +{ + int opt_len; + + opt_len = NET_TO_HOST16(opt_hdr->length); + + if (opt_len >= sizeof(struct ipv6_addr)) + memcpy((char *)&context->primary_dns_server, + (char *)&((struct dhcpv6_option *)opt_hdr)->type.dns. + primary_addr, sizeof(struct ipv6_addr)); + + if (opt_len >= 2 * sizeof(struct ipv6_addr)) + memcpy((char *)&context->secondary_dns_server, + (char *)&((struct dhcpv6_option *)opt_hdr)->type.dns. + secondary_addr, sizeof(struct ipv6_addr)); +} + +static void dhcpv6_handle_reply(struct dhcpv6_context *context, + u16_t dhcpv6_len) +{ + if (context->dhcpv6_state != DHCPV6_STATE_REQ_SENT) + return; + + context->dhcpv6_done = TRUE; +} + +static void dhcpv6_parse_vendor_option(struct dhcpv6_context *context, + u8_t *option, int len) +{ + struct dhcpv6_option *opt; + u16_t type; + int opt_len; + int data_len; + int i; + u8_t *data; + + for (i = 0; i < len; i += opt_len + sizeof(struct dhcpv6_opt_hdr)) { + opt = (struct dhcpv6_option *)((u8_t *)option + i); + type = HOST_TO_NET16(opt->hdr.type); + opt_len = HOST_TO_NET16(opt->hdr.length); + data = &opt->type.data[0]; + data_len = strlen((char *)data); + + switch (type) { + case 201: + /* iSCSI target 1 */ + break; + + case 202: + /* iSCSI target 2 */ + break; + + case 203: + if (data_len > ISCSI_MAX_ISCSI_NAME_LENGTH) + data_len = ISCSI_MAX_ISCSI_NAME_LENGTH; + data[data_len] = '\0'; + strcpy(context->initiatorName, (char *)data); + break; + + default: + break; + } + } +} diff --git a/iscsiuio/src/apps/dhcpc/dhcpv6.h b/iscsiuio/src/apps/dhcpc/dhcpv6.h new file mode 100644 index 0000000..d4ec4b9 --- /dev/null +++ b/iscsiuio/src/apps/dhcpc/dhcpv6.h @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2011, Broadcom Corporation + * Copyright (c) 2014, QLogic Corporation + * + * Written by: Eddie Wai + * Based on code from Kevin Tran's iSCSI boot code + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam Dunkels. + * 4. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * dhcpv6.h - DHCPv6 engine header + * + */ +#ifndef __IDHCPV6_H__ +#define __IDHCPV6_H__ + +#include "ipv6_ndpc.h" +#include "ipv6.h" + +#define ISCSI_MAX_ISCSI_NAME_LENGTH 128 +/* DHCPv6 Message types. */ +#define DHCPV6_SOLICIT 1 +#define DHCPV6_ADVERTISE 2 +#define DHCPV6_REQUEST 3 +#define DHCPV6_CONFIRM 4 +#define DHCPV6_RENEW 5 +#define DHCPV6_REBIND 6 +#define DHCPV6_REPLY 7 +#define DHCPV6_RELEASE 8 +#define DHCPV6_DECLINE 9 +#define DHCPV6_RECONFIGURE 10 +#define DHCPV6_INFO_REQUEST 11 +#define DHCPV6_RELAY_FORW 12 +#define DHCPV6_RELAY_REPL 13 + +/* Option codes. */ +#define DHCPV6_OPT_CLIENTID 1 /* Client ID option - built by stack */ +#define DHCPV6_OPT_SERVERID 2 /* Server ID option - built by stack */ +#define DHCPV6_OPT_IA_NA 3 /* IA_NA option - built by user */ +#define DHCPV6_OPT_IA_TA 4 /* IA_TA option - not supported */ +#define DHCPV6_OPT_IAADDR 5 /* IA_ADDR option - built by user */ +#define DHCPV6_OPT_ORO 6 /* Option Request Option - built by + stack */ +#define DHCPV6_OPT_PREFERENCE 7 /* Preference option - built by server + */ +#define DHCPV6_OPT_ELAPSED_TIME 8 /* Elapsed Time option - built by stack + */ +#define DHCPV6_OPT_RELAY_MSG 9 /* Relay Message option - not supported + */ +#define DHCPV6_OPT_AUTH 11 /* Authentication option - built by + stack */ +#define DHCPV6_OPT_UNICAST 12 /* Server Unicast option - built by + server */ +#define DHCPV6_OPT_STATUS_CODE 13 /* Status Code option - built by stack + */ +#define DHCPV6_OPT_RAPID_COMMIT 14 /* Rapid Commit option - built by user + */ +#define DHCPV6_OPT_USER_CLASS 15 /* User Class option - built by user */ +#define DHCPV6_OPT_VENDOR_CLASS 16 /* Vendor Class option - built by user + */ +#define DHCPV6_OPT_VENDOR_OPTS 17 /* Vendor-Specific Information option - + build by user */ +#define DHCPV6_OPT_INTERFACE_ID 18 /* Interface ID option - not supported + */ +#define DHCPV6_OPT_RECONF_MSG 19 /* Reconfigure Message option - built + by server */ +#define DHCPV6_OPT_RECONF_ACCEPT 20 /* Reconfigure Accept option - built by + user */ +#define DHCPV6_OPT_SIP_SERVER_D 21 /* NOT SUPPORTED - included for + completeness only */ +#define DHCPV6_OPT_SIP_SERVER_A 22 /* NOT SUPPORTED - included for + completeness only */ +#define DHCPV6_OPT_DNS_SERVERS 23 /* DNS Recursive Name Server option - + built by server */ +#define DHCPV6_OPT_DOMAIN_LIST 24 /* Domain Search List option - not + supported */ +#define DHCPV6_MAX_OPT_CODES 25 /* This will be the count + 1 since + the parsing array starts + at [1] instead of [0] */ + +/* Authentication protocol types. */ +#define DHCPV6_DELAYED_AUTH_PROT 2 /* Delayed Authentication protocol. */ +#define DHCPV6_RECON_KEY_AUTH_PROT 3 /* Reconfigure Key Authentication + protocol. */ + +struct dhcpv6_context { +#define DHCP_VENDOR_ID_LEN 128 + char dhcp_vendor_id[DHCP_VENDOR_ID_LEN]; + struct mac_address *our_mac_addr; + u32_t dhcpv6_transaction_id; + u16_t seconds; + int timeout; + int dhcpv6_done; + +#define DHCPV6_STATE_UNKNOWN 0 +#define DHCPV6_STATE_SOLICIT_SENT 1 +#define DHCPV6_STATE_ADV_RCVD 2 +#define DHCPV6_STATE_REQ_SENT 3 +#define DHCPV6_STATE_CONFIRM_SENT 4 + int dhcpv6_state; + u16_t dhcpv6_task; + struct ipv6_context *ipv6_context; + struct eth_hdr *eth; + struct ipv6_hdr *ipv6; + struct udp_hdr *udp; + + char initiatorName[ISCSI_MAX_ISCSI_NAME_LENGTH]; + struct ipv6_addr dhcp_server; + struct ipv6_addr primary_dns_server; + struct ipv6_addr secondary_dns_server; + +}; + +union dhcpv6_hdr { + struct { + u32_t type:8; + u32_t trans_id:24; + } field; + + u32_t type_transaction; +}; + +#define dhcpv6_type field.type +#define dhcpv6_trans_id field.trans_id + +struct dhcpv6_opt_hdr { + u16_t type; + u16_t length; +}; + +struct dhcpv6_opt_client_id { + u16_t duid_type; +#define DHCPV6_DUID_TYPE_LINK_LAYER_AND_TIME 1 +#define DHCPV6_DUID_TYPE_VENDOR_BASED 2 +#define DHCPV6_DUID_TYPE_LINK_LAYER 3 + u16_t hw_type; +#define DHCPV6_HW_TYPE_ETHERNET 1 + u32_t time; + struct mac_address link_layer_addr; +}; + +struct dhcpv6_opt_id_assoc_na { + u32_t iaid; +#define DHCPV6_OPT_IA_NA_IAID 0x306373L + u32_t t1; + u32_t t2; +}; + +struct dhcpv6_opt_elapse_time { + u16_t time; +}; + +struct dhcpv6_opt_iaa_addr { + struct ipv6_addr addr; + u32_t preferred_lifetime; + u32_t valid_lifetime; +}; + +struct dhcpv6_opt_status { + u16_t status; +}; + +struct dhcpv6_opt_request_list { + u16_t request_code[1]; +}; + +struct dhcpv6_opt_dns { + struct ipv6_addr primary_addr; + struct ipv6_addr secondary_addr; +}; + +struct dhcpv6_vendor_class { + u32_t enterprise_number; + u16_t vendor_class_length; + u8_t vendor_class_data[1]; +}; + +struct dhcpv6_vendor_opts { + u32_t enterprise_number; + u8_t vendor_opt_data[1]; +}; + +struct dhcpv6_option { + struct dhcpv6_opt_hdr hdr; + union { + struct dhcpv6_vendor_opts vendor_opts; + struct dhcpv6_vendor_class vendor_class; + struct dhcpv6_opt_client_id client_id; + struct dhcpv6_opt_id_assoc_na ida_na; + struct dhcpv6_opt_elapse_time elapsed_time; + struct dhcpv6_opt_iaa_addr iaa_addr; + struct dhcpv6_opt_status sts; + struct dhcpv6_opt_request_list list; + struct dhcpv6_opt_dns dns; + u8_t data[1]; + } type; +}; + +#define DHCPV6_NUM_OF_RETRY 4 + +#define DHCPV6_ACK_TIMEOUT 2 + +#define IANA_ENTERPRISE_NUM_BROADCOM 0x113d + +/* QLogic Extended DHCP options used in iSCSI boot */ +#define DHCPV6_TAG_FIRST_ISCSI_TARGET_NAME 201 +#define DHCPV6_TAG_SECOND_ISCSI_TARGET_NAME 202 +#define DHCPV6_TAG_ISCSI_INITIATOR_NAME 203 + +#define MAX_DHCP_RX_OFFERS 4 +#define MAX_DHCP_OPTION43_LENGTH 1024 + +#define DHCPV6_TASK_GET_IP_ADDRESS 0x1 +#define DHCPV6_TASK_GET_OTHER_PARAMS 0x2 + +enum { + ISCSI_FAILURE, + ISCSI_USER_ABORT, + ISCSI_SUCCESS +}; + +/* Function prototypes */ +int dhcpv6_do_discovery(struct dhcpv6_context *context); +void ipv6_udp_handle_dhcp(struct dhcpv6_context *context); +void dhcpv6_init(struct dhcpv6_context *context); + +#endif /* __IDHCPV6_H__ */ diff --git a/iscsiuio/src/uip-1.0-changelog.txt b/iscsiuio/src/uip-1.0-changelog.txt new file mode 100644 index 0000000..800e444 --- /dev/null +++ b/iscsiuio/src/uip-1.0-changelog.txt @@ -0,0 +1,98 @@ +* A new API: protosockets that are similar to BSD sockets but does not + require any underlying multithreading system. + +* Very rudimentary IPv6 support + +* New application: DHCP client. Web server rewritten with protosockets. + +* Removed uIP zero-copy functionality in order to simplify uIP device + driver coding: outbound packets are now *always* stored in full in + the uip_buf buffer. + +* Checksum computation is now part of uip.c, but it still is possible + to implement them in assembly code by specifying a configuration + option. Checksum code now runs on architectures with 2-byte alignment. + +* Added TCP persistent timer. + +* Made all IP address representations use the new uip_ipaddr_ip + datatype for clarity. + +* Updated window behavior so that sending to a host with a small open + window works better now. + +* UDP API change: uip_udp_new() now takes port numbers in network byte + order like TCP functions. + +* Allow reception of packets when no IP address is configured to make + DHCP work. + +* Moved Ethernet address into main uIP module from ARP module. + +* Made constants explicit #defines and moved them out of the code + (header sizes, TCP options, TCP header length field). + +* If uip_len is less than that reported by the IP header, the packet + is discarded. If uip_len is greater than the length reported by the + IP header, uip_len is adjusted. + +* Moved header size definitions into header file. + +* Added uIP call for polling an application without triggering any + timer events. Removed redundant assignments of uip_len and uip_slen. + +* Removed compiler warning about icmp_input label being defined when + UIP_PINGADDRCONF was not used. + +* Added UIP_APPDATA_SIZE macro that holds the available buffer size + for user data. + +* Added uip_udp_bind() call. + +* Moved checksum code into main uIP module. + +* Switched the TCP, UDP and IP header structures to be structs rather + than typedefs. + +* Prefixed TCP state names with UIP_ to avoid name space + contamination. + +* Changed declarations of uip_appdatap and friends to void * to avoid + explicit typecasts. + +* Bugfixes + + o TCP: Fixed bug with high byte of peer window size. + + o TCP: Fixed bug that in some cases prevented concurrent reception and + transmission of TCP data. + + o TCP: uip_connect() didn't correctly calculate age of TIME_WAIT + connections. + + o TCP: Array index for uip_conns[] array was out of bounds in + comparison. Comparison changed to make index within bounds. + + o TCP: if the remote host crashes and tries to reestablish an old + connection, uIP should respond with an ACK with the correct + sequence and acknowledgment numbers, to which the remote host + should respond with an ACK. uIP did not respond with the correct + ACK. + + o TCP: Fixed check for SYNACK segment: now checks only relevant TCP + control flags and discards flags reserved for future expansion. + + o TCP: Fixed bug where uIP did not inform application that a connection + had been aborted during an active open. + + o TCP: FIN segment was accepted even though application had stopped + incoming data with uip_stop(). + + o TCP: A FINACK segment would not always correctly acknowledge data. + + o UDP: checksums are now calculated after all fields have been + filled in. + + o UDP: network byte order on lastport in uip_udp_new(). + + o IP: memset() bugs in IP fragment reassembly code fixed. diff --git a/iscsiuio/src/uip/Makefile.am b/iscsiuio/src/uip/Makefile.am new file mode 100644 index 0000000..16170d7 --- /dev/null +++ b/iscsiuio/src/uip/Makefile.am @@ -0,0 +1,18 @@ +AM_CFLAGS = -I${top_srcdir}/src/unix \ + -I${top_srcdir}/src/apps/dhcpc \ + -I${top_srcdir}/src/apps/brcm-iscsi \ + -I${top_srcdir}/../include \ + -I${top_srcdir}/../usr + +noinst_LIBRARIES = lib_iscsi_uip.a + +lib_iscsi_uip_a_SOURCES = uip.c \ + uip_arp.c \ + psock.c \ + timer.c \ + uip-neighbor.c \ + uip_eth.c \ + ipv6_ndpc.c \ + ipv6.c + +lib_iscsi_uip_a_CFLAGS = -DBYTE_ORDER=@ENDIAN@ $(AM_CFLAGS) diff --git a/iscsiuio/src/uip/Makefile.include b/iscsiuio/src/uip/Makefile.include new file mode 100644 index 0000000..aa3ac63 --- /dev/null +++ b/iscsiuio/src/uip/Makefile.include @@ -0,0 +1,47 @@ + + +ifdef APPS + APPDIRS = $(foreach APP, $(APPS), ../apps/$(APP)) + -include $(foreach APP, $(APPS), ../apps/$(APP)/Makefile.$(APP)) + CFLAGS += $(addprefix -I../apps/,$(APPS)) +endif + +ifndef CCDEP + CCDEP = $(CC) +endif +ifndef CCDEPCFLAGS + CCDEPCFLAGS = $(CFLAGS) +endif +ifndef OBJECTDIR + OBJECTDIR = obj +endif + +ifeq (${wildcard $(OBJECTDIR)},) + DUMMY := ${shell mkdir $(OBJECTDIR)} +endif + + +vpath %.c . ../uip ../lib $(APPDIRS) + +$(OBJECTDIR)/%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +$(OBJECTDIR)/%.d: %.c + @set -e; rm -f $@; \ + $(CCDEP) -MM $(CCDEPCFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,$(OBJECTDIR)/\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +UIP_SOURCES=uip.c uip_arp.c uiplib.c psock.c timer.c uip-neighbor.c uip_eth.c ipv6_ndp.c ipv6.c + + +ifneq ($(MAKECMDGOALS),clean) +-include $(addprefix $(OBJECTDIR)/,$(UIP_SOURCES:.c=.d) \ + $(APP_SOURCES:.c=.d)) +endif + +libuip.a: ${addprefix $(OBJECTDIR)/, $(UIP_SOURCES:.c=.o)} + $(AR) rc $@ $^ + +libapps.a: ${addprefix $(OBJECTDIR)/, $(APP_SOURCES:.c=.o)} + $(AR) rc $@ $^ diff --git a/iscsiuio/src/uip/clock.h b/iscsiuio/src/uip/clock.h new file mode 100644 index 0000000..d79326b --- /dev/null +++ b/iscsiuio/src/uip/clock.h @@ -0,0 +1,87 @@ +/** + * \defgroup clock Clock interface + * + * The clock interface is the interface between the \ref timer "timer library" + * and the platform specific clock functionality. The clock + * interface must be implemented for each platform that uses the \ref + * timer "timer library". + * + * The clock interface does only one this: it measures time. The clock + * interface provides a macro, CLOCK_SECOND, which corresponds to one + * second of system time. + * + * \sa \ref timer "Timer library" + * + * @{ + */ + +/* + * Copyright (c) 2004, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack + * + * Author: Adam Dunkels + * + */ +#ifndef __CLOCK_H__ +#define __CLOCK_H__ + +#include "clock-arch.h" + +/** + * Initialize the clock library. + * + * This function initializes the clock library and should be called + * from the main() function of the system. + * + */ +void clock_init(void); + +/** + * Get the current clock time. + * + * This function returns the current system clock time. + * + * \return The current clock time, measured in system ticks. + */ +clock_time_t clock_time(void); + +/** + * A second, measured in system clock time. + * + * \hideinitializer + */ +#ifdef CLOCK_CONF_SECOND +#define CLOCK_SECOND CLOCK_CONF_SECOND +#else +#define CLOCK_SECOND (clock_time_t)32 +#endif + +#endif /* __CLOCK_H__ */ + +/** @} */ diff --git a/iscsiuio/src/uip/debug.h b/iscsiuio/src/uip/debug.h new file mode 100644 index 0000000..a58fa7a --- /dev/null +++ b/iscsiuio/src/uip/debug.h @@ -0,0 +1,13 @@ +#ifndef __DEBUG_H__ +#define __DEBUG_H__ + +#ifdef DEBUG +#define UIP_DEBUG(args...) \ + do { \ + fprintf(stdout, args); \ + fflush(stdout); \ + } while (0); +#else +#endif + +#endif diff --git a/iscsiuio/src/uip/icmpv6.h b/iscsiuio/src/uip/icmpv6.h new file mode 100644 index 0000000..cbf9aa8 --- /dev/null +++ b/iscsiuio/src/uip/icmpv6.h @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2011, Broadcom Corporation + * Copyright (c) 2014, QLogic Corporation + * + * Written by: Eddie Wai (eddie.wai@broadcom.com) + * Based on Kevin Tran's iSCSI boot code + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam Dunkels. + * 4. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * icmpv6.h - This file contains macro definitions pertaining to ICMPv6 + * + * RFC 2463 : ICMPv6 Specification + * RFC 2461 : Neighbor Discovery for IPv6 + * + */ +#ifndef __ICMPV6_H__ +#define __ICMPV6_H__ + +/* Base ICMP Header sizes */ +#define IPV6_RTR_SOL_HDR_SIZE 8 +#define IPV6_RTR_ADV_HDR_SIZE 16 +#define IPV6_NEIGH_SOL_HDR_SIZE 24 +#define IPV6_NEIGH_ADV_HDR_SIZE 24 +#define IPV6_LINK_LAYER_OPT_SIZE 2 +#define IPV6_LINK_LAYER_OPT_LENGTH 8 +#define IPV6_MTU_OPT_SIZE 8 +#define IPV6_PREFIX_OPT_SIZE 32 +#define IPV6_ECHO_REQUEST_HDR_SIZE 8 +#define IPV6_ECHO_REPLY_HDR_SIZE 8 +#define IPV6_REDIRECT_SIZE 40 +#define IPV6_DHAAD_REQ_HDR_SIZE 8 +#define IPV6_DHAAD_REPLY_HDR_SIZE 8 +#define IPV6_PRFXSOL_HDR_SIZE 8 +#define IPV6_PRFXADV_HDR_SIZE 8 +#define IPV6_RTR_ADV_INT_OPT_SIZE 8 + +/* ICMP Message Types */ +/* Error messages are always less than 128 */ +#define ICMPV6_DST_UNREACH 1 /* Destination Unreachable */ +#define ICMPV6_PACKET_TOO_BIG 2 /* Packet Too Big */ +#define ICMPV6_TIME_EXCEEDED 3 /* Time Exceeded */ +#define ICMPV6_PARAM_PROB 4 /* Parameter Problem */ + +#define ICMPV6_RTR_SOL 133 /* Router Solicitation */ +#define ICMPV6_RTR_ADV 134 /* Router Advertisement */ +#define ICMPV6_NEIGH_SOL 135 /* Neighbor Solicitation */ +#define ICMPV6_NEIGH_ADV 136 /* Neighbor Advertisement */ +#define ICMPV6_REDIRECT 137 /* Redirect */ +#define ICMPV6_ECHO_REQUEST 128 /* Echo Request */ +#define ICMPV6_ECHO_REPLY 129 /* Echo Reply */ +#define ICMPV6_WRUREQUEST 139 /* Who Are You Request */ +#define ICMPV6_WRUREPLY 140 /* Who Are You Reply */ +#define ICMPV6_ROUTER_RENUMBERING 138 /* Router Renumbering */ +#define ICMPV6_HA_ADDR_DISC_REQ 144 /* Dynamic Home Agent Address + Discovery Request */ +#define ICMPV6_HA_ADDR_DISC_REPLY 145 /* Dynamic Home Agent Address + Discovery Reply */ +#define ICMPV6_MP_SOLICIT 146 /* Mobile Prefix Solicitation */ +#define ICMPV6_MP_ADV 147 /* Mobile Prefix Reply */ + +/* Destination Unreachable Codes */ +#define ICMPV6_DST_UNREACH_NOROUTE 0 +#define ICMPV6_DST_UNREACH_ADMIN 1 +#define ICMPV6_DST_UNREACH_ADDRESS 3 +#define ICMPV6_DST_UNREACH_PORT 4 + +/* Time Exceeded Codes */ +#define ICMPV6_TIME_EXCD_HPLMT 0 /* Hop Limit exceeded in transit */ +#define ICMPV6_TIME_EXCD_REASM 1 /* Fragment reassembly time exceeded */ + +/* Parameter Problem Codes */ +#define ICMPV6_PARM_PROB_HEADER 0 +#define ICMPV6_PARM_PROB_NEXT_HDR 1 +#define ICMPV6_PARM_PROB_OPTION 2 + +/* ICMP Option Types */ +#define IPV6_ICMP_OPTION_SRC_ADDR 1 /* Source Link-Layer Address */ +#define IPV6_ICMP_OPTION_TAR_ADDR 2 /* Target Link-Layer Address */ +#define IPV6_ICMP_OPTION_PREFIX 3 /* Prefix */ +#define IPV6_ICMP_OPTION_RED_HDR 4 /* Redirect Header */ +#define IPV6_ICMP_OPTION_MTU 5 /* Link MTU */ +#define IPV6_ICMP_OPTION_RTR_ADV_INT 7 /* Rtr Advertisement Interval */ + +/* ICMP Offsets */ +#define IPV6_ICMP_TYPE_OFFSET 0 +#define IPV6_ICMP_CODE_OFFSET 1 +#define IPV6_ICMP_CKSUM_OFFSET 2 +#define IPV6_ICMP_RESERVED_OFFSET 4 +#define IPV6_ICMP_DATA_OFFSET 8 + +/* ICMP Router Solicitation Offsets */ +#define IPV6_ICMP_RTR_SOL_RES_OFFSET 4 +#define IPV6_ICMP_RTR_SOL_OPTIONS_OFFSET 8 + +/* ICMP Router Advertisement Offsets */ +#define IPV6_ICMP_RTR_ADV_CURHOPLMT_OFFSET 4 +#define IPV6_ICMP_RTR_ADV_MGDANDCFG_BIT_OFFSET 5 +#define IPV6_ICMP_RTR_ADV_RTR_LIFETIME_OFFSET 6 +#define IPV6_ICMP_RTR_ADV_RCHBL_TIME_OFFSET 8 +#define IPV6_ICMP_RTR_ADV_RTRNS_TMR_OFFSET 12 +#define IPV6_ICMP_RTR_ADV_OPTIONS_OFFSET 16 + +/* ICMP Neighbor Solicitation Offsets */ +#define IPV6_ICMP_NEIGH_SOL_RES_OFFSET 4 +#define IPV6_ICMP_NEIGH_SOL_TRGT_ADDRS_OFFSET 8 +#define IPV6_ICMP_NEIGH_SOL_OPTIONS_OFFSET 24 + +/* ICMP Neighbor Advertisement Offsets */ +#define IPV6_ICMP_NEIGH_ADV_FLAG_OFFSET 4 +#define IPV6_ICMP_NEIGH_ADV_TRGT_ADDRS_OFFSET 8 +#define IPV6_ICMP_NEIGH_ADV_OPTIONS_OFFSET 24 + +/* ICMP Redirect Offsets */ +#define IPV6_ICMP_REDIRECT_TRGT_ADDRS_OFFSET 8 +#define IPV6_ICMP_REDIRECT_DEST_ADDRS_OFFSET 24 +#define IPV6_ICMP_REDIRECT_OPTIONS_OFFSET 40 + +/* ICMP Option Offsets */ +#define IPV6_ICMP_OPTION_TYPE_OFFSET 0 +#define IPV6_ICMP_OPTION_LENGTH_OFFSET 1 + +/* ICMP Link-Layer Address Option Offsets */ +#define IPV6_ICMP_LL_OPTION_ADDRESS_OFFSET 2 + +/* ICMP Prefix Option Offsets */ +#define IPV6_ICMP_PREFIX_PRE_LENGTH_OFFSET 2 +#define IPV6_ICMP_PREFIX_FLAG_OFFSET 3 +#define IPV6_ICMP_PREFIX_VALID_LIFETIME_OFFSET 4 +#define IPV6_ICMP_PREFIX_PREF_LIFETIME_OFFSET 8 +#define IPV6_ICMP_PREFIX_RES2_OFFSET 12 +#define IPV6_ICMP_PREFIX_PREFIX_OFFSET 16 + +/* ICMP Redirected Header Option Offsets */ +#define IPV6_ICMP_RED_OPTION_TYPE_OFFSET 0 +#define IPV6_ICMP_RED_OPTION_LEN_OFFSET 1 +#define IPV6_ICMP_RED_OPTION_RES1_OFFSET 2 +#define IPV6_ICMP_RED_OPTION_RES2_OFFSET 4 +#define IPV6_ICMP_RED_OPTION_DATA_OFFSET 8 + +/* ICMP MTU Option Offsets */ +#define IPV6_ICMP_MTU_RESERVED_OFFSET 2 +#define IPV6_ICMP_MTU_OFFSET 4 + +/* ICMP Echo Request Offsets */ +#define IPV6_ICMP_ECHO_ID 4 +#define IPV6_ICMP_ECHO_SEQ 6 +#define IPV6_ICMP_ECHO_DATA 8 + +/* ICMP Destination Unreachable Offsets */ +#define IPV6_DST_UNREACH_UNUSED 4 +#define IPV6_DST_UNREACH_DATA 8 + +/* ICMP Parameter Problem Offsets */ +#define IPV6_PARAM_PROB_PTR 4 +#define IPV6_PARAM_PROT_DATA 8 + +/* ICMP Time Exceeded Offsets */ +#define IPV6_TIME_EXCEEDED_DATA 8 + +/* ICMP Packet Too Big Offsets */ +#define IPV6_PKT_TOO_BIG_MTU 4 +#define IPV6_PKT_TOO_BIG_DATA 8 + +/* Home Agent Address Discovery Request Header Offsets */ +#define ICMPV6_HA_ADDR_DISC_REQ_ID_OFFSET 4 +#define ICMPV6_HA_ADDR_DISC_REQ_RSVD_OFFSET 6 + +/* Home Agent Address Discovery Reply Header Offsets */ +#define ICMPV6_HA_ADDR_DISC_REPLY_ID_OFFSET 4 +#define ICMPV6_HA_ADDR_DISC_REPLY_RSVD_OFFSET 6 +#define ICMPV6_HA_ADDR_DISC_REPLY_HA_ADDR_OFFSET 8 + +/* Mobile Prefix Solicitation Header Offsets */ +#define ICMPV6_MP_SOLICIT_ID_OFFSET 4 +#define ICMPV6_MP_SOLICIT_RSVD_OFFSET 6 + +/* Mobile Prefix Advertisement Header Offsets */ +#define ICMPV6_MP_ADV_ID_OFFSET 4 +#define ICMPV6_MP_ADV_MGDANDCFG_BIT_OFFSET 6 +#define ICMPV6_MP_ADV_OPT_OFFSET 8 + +/* Advertisement Interval Option Header Offsets */ +#define ICMPV6_ADV_INT_TYPE_OFFSET 0 +#define ICMPV6_ADV_INT_LEN_OFFSET 1 +#define ICMPV6_ADV_INT_RSVD_OFFSET 2 +#define ICMPV6_ADV_INT_ADV_INT_OFFSET 4 + +#define ICMPV6_HEADER_LEN 4 + +#define IPV6_PREFIX_FLAG_ONLINK 0x80 +#define IPV6_PREFIX_FLAG_AUTO 0x40 +#define IPV6_PREFIX_FLAG_ROUTER 0x20 + +#define IPV6_NA_FLAG_ROUTER 0x80 +#define IPV6_NA_FLAG_SOLICITED 0x40 +#define IPV6_NA_FLAG_OVERRIDE 0x20 + +/* Router Advertisement Flags */ +#define IPV6_RA_MANAGED_FLAG 0x80 +#define IPV6_RA_CONFIG_FLAG 0x40 + +/* Mobile Prefix Advertisement Flags */ +#define IPV6_PA_MANAGED_FLAG 0x80 +#define IPV6_PA_CONFIG_FLAG 0x40 + +/* Validation Values */ +#define ICMPV6_VALID_HOP_LIMIT 255 /* Valid Hop Limit */ +#define ICMPV6_VALID_CODE 0 /* Valid Code */ +#define ICMPV6_RTRSOL_MIN_LENGTH 8 /* Minimum valid length for + Router Solicitation */ +#define ICMPV6_RTRADV_MIN_LENGTH 16 /* Minimum valid length for + Router Advertisement */ +#define ICMPV6_NEIGHSOL_MIN_LENGTH 24 /* Minimum valid length for + Neighbor Solicitation */ +#define ICMPV6_NEIGHADV_MIN_LENGTH 24 /* Minimum valid length for + Neighbor Advertisement */ +#define ICMPV6_REDIRECT_MIN_LENGTH 40 /* Minimum valid length for + Neighbor Advertisement */ + +/* ICMPV6 Header */ +struct icmpv6_hdr { + u8_t icmpv6_type; /* type field */ + u8_t icmpv6_code; /* code field */ + u16_t icmpv6_cksum; /* checksum field */ + union { + u32_t icmpv6_un_data32[1]; /* type-specific field */ + u16_t icmpv6_un_data16[2]; /* type-specific field */ + u8_t icmpv6_un_data8[4]; /* type-specific field */ + } data; +}; + +#define icmpv6_data data.icmpv6_un_data32[0] + +struct icmpv6_opt_hdr { + u8_t type; + u8_t len; +}; + +struct icmpv6_opt_link_addr { + struct icmpv6_opt_hdr hdr; + u8_t link_addr[6]; +}; + +struct icmpv6_opt_prefix { + struct icmpv6_opt_hdr hdr; + u8_t prefix_len; + u8_t flags; +#define ICMPV6_OPT_PREFIX_FLAG_ON_LINK (1 << 7) +#define ICMPV6_OPT_PREFIX_FLAG_BIT_A (1 << 6) + u32_t valid_lifetime; + u32_t preferred_lifetime; + u32_t reserved; + struct ipv6_addr prefix; +}; + +/* Neighbor Solicitation */ +struct icmpv6_nd_solicit { + struct icmpv6_hdr nd_ns_hdr; +}; + +/* Router Advertisement */ +struct icmpv6_router_advert { + struct icmpv6_hdr header; + u32_t reachable_time; + u32_t retransmit_timer; +}; + +#define nd_ra_type header.icmpv6_type +#define nd_ra_code header.icmpv6_code +#define nd_ra_cksum header.icmpv6_cksum +#define nd_ra_curhoplimit header.data.icmpv6_un_data8[0] +#define nd_ra_flags_reserved header.data.icmpv6_un_data8[1] +#define nd_ra_router_lifetime header.data.icmpv6_un_data16[1] + +#endif /* __ICMPV6_H__ */ diff --git a/iscsiuio/src/uip/ipv6.c b/iscsiuio/src/uip/ipv6.c new file mode 100644 index 0000000..5c627ac --- /dev/null +++ b/iscsiuio/src/uip/ipv6.c @@ -0,0 +1,1297 @@ +/* + * Copyright (c) 2011, Broadcom Corporation + * Copyright (c) 2014, QLogic Corporation + * + * Written by: Eddie Wai (eddie.wai@broadcom.com) + * Based on Kevin Tran's iSCSI boot code + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam Dunkels. + * 4. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ipv6.c - This file contains simplifed IPv6 processing code. + * + */ +#include +#include +#include +#include "logger.h" +#include "uip.h" +#include "ipv6.h" +#include "ipv6_pkt.h" +#include "icmpv6.h" +#include "uipopt.h" +#include "dhcpv6.h" + +inline int best_match_bufcmp(u8_t *a, u8_t *b, int len) +{ + int i; + + for (i = 0; i < len; i++) { + if (a[i] != b[i]) + break; + } + return i; +} + +/* Local function prototypes */ +static int ipv6_is_it_our_address(struct ipv6_context *context, + struct ipv6_addr *ip_addr); +static void ipv6_insert_protocol_chksum(struct ipv6_hdr *ipv6); +static void ipv6_update_arp_table(struct ipv6_context *context, + struct ipv6_addr *ip_addr, + struct mac_address *mac_addr); +static void ipv6_icmp_init_link_option(struct ipv6_context *context, + struct icmpv6_opt_link_addr *link_opt, + u8_t type); +static void ipv6_icmp_rx(struct ipv6_context *context); +static void ipv6_icmp_handle_nd_adv(struct ipv6_context *context); +static void ipv6_icmp_handle_nd_sol(struct ipv6_context *context); +static void ipv6_icmp_handle_echo_request(struct ipv6_context *context); +static void ipv6_icmp_handle_router_adv(struct ipv6_context *context); +static void ipv6_icmp_process_prefix(struct ipv6_context *context, + struct icmpv6_opt_prefix *icmp_prefix); +static void ipv6_udp_rx(struct ipv6_context *context); + +int iscsiL2Send(struct ipv6_context *context, int pkt_len) +{ + LOG_DEBUG("IPv6: iscsiL2Send"); + uip_send(context->ustack, + (void *)context->ustack->data_link_layer, pkt_len); + + return pkt_len; +} + +int iscsiL2AddMcAddr(struct ipv6_context *context, + struct mac_address *new_mc_addr) +{ + int i; + struct mac_address *mc_addr; + const struct mac_address all_zeroes_mc = { { { 0, 0, 0, 0, 0, 0 } } }; + + mc_addr = context->mc_addr; + for (i = 0; i < MAX_MCADDR_TABLE; i++, mc_addr++) + if (!memcmp((char *)mc_addr, + (char *)new_mc_addr, sizeof(struct mac_address))) + return TRUE; /* Already in the mc table */ + + mc_addr = context->mc_addr; + for (i = 0; i < MAX_MCADDR_TABLE; i++, mc_addr++) { + if (!memcmp((char *)mc_addr, + (char *)&all_zeroes_mc, sizeof(struct mac_address))) { + memcpy((char *)mc_addr, + (char *)new_mc_addr, sizeof(struct mac_address)); + LOG_DEBUG("IPv6: mc_addr added " + "%02x:%02x:%02x:%02x:%02x:%02x", + *(u8_t *)new_mc_addr, + *((u8_t *)new_mc_addr + 1), + *((u8_t *)new_mc_addr + 2), + *((u8_t *)new_mc_addr + 3), + *((u8_t *)new_mc_addr + 4), + *((u8_t *)new_mc_addr + 5)); + return TRUE; + } + } + return FALSE; +} + +int iscsiL2IsOurMcAddr(struct ipv6_context *context, + struct mac_address *dest_mac) +{ + int i; + struct mac_address *mc_addr; + + mc_addr = context->mc_addr; + for (i = 0; i < MAX_MCADDR_TABLE; i++, mc_addr++) + if (!memcmp((char *)mc_addr, + (char *)dest_mac->addr, sizeof(struct mac_address))) + return TRUE; + return FALSE; +} + +void ipv6_init(struct ndpc_state *ndp, int cfg) +{ + int i; + struct ipv6_context *context = (struct ipv6_context *)ndp->ipv6_context; + struct mac_address *mac_addr = (struct mac_address *)ndp->mac_addr; + struct ipv6_arp_entry *ipv6_arp_table; + struct ipv6_prefix_entry *ipv6_prefix_table; + struct mac_address mc_addr; + + if (context == NULL) { + LOG_ERR("IPV6: INIT ipv6_context is NULL"); + return; + } + + memset((char *)context, 0, sizeof(struct ipv6_context)); + + /* Associate the nic_iface's ustack to this ipv6_context */ + context->ustack = ndp->ustack; + + ipv6_arp_table = &context->ipv6_arp_table[0]; + ipv6_prefix_table = &context->ipv6_prefix_table[0]; + + memset((char *)ipv6_arp_table, 0, sizeof(*ipv6_arp_table)); + memset((char *)ipv6_prefix_table, 0, sizeof(*ipv6_prefix_table)); + memcpy((char *)&context->mac_addr, + (char *)mac_addr, sizeof(struct mac_address)); + /* + * Per RFC 2373. + * There are two types of local-use unicast addresses defined. These + * are Link-Local and Site-Local. The Link-Local is for use on a single + * link and the Site-Local is for use in a single site. Link-Local + * addresses have the following format: + * + * | 10 | + * | bits | 54 bits | 64 bits | + * +----------+-------------------------+----------------------------+ + * |1111111010| 0 | interface ID | + * +----------+-------------------------+----------------------------+ + */ + if (context->ustack->linklocal_autocfg != IPV6_LL_AUTOCFG_OFF) { + context->link_local_addr.addr8[0] = 0xfe; + context->link_local_addr.addr8[1] = 0x80; + /* Bit 1 is 1 to indicate universal scope. */ + context->link_local_addr.addr8[8] = mac_addr->addr[0] | 0x2; + context->link_local_addr.addr8[9] = mac_addr->addr[1]; + context->link_local_addr.addr8[10] = mac_addr->addr[2]; + context->link_local_addr.addr8[11] = 0xff; + context->link_local_addr.addr8[12] = 0xfe; + context->link_local_addr.addr8[13] = mac_addr->addr[3]; + context->link_local_addr.addr8[14] = mac_addr->addr[4]; + context->link_local_addr.addr8[15] = mac_addr->addr[5]; + + context->link_local_multi.addr8[0] = 0xff; + context->link_local_multi.addr8[1] = 0x02; + context->link_local_multi.addr8[11] = 0x01; + context->link_local_multi.addr8[12] = 0xff; + context->link_local_multi.addr8[13] |= + context->link_local_addr.addr8[13]; + context->link_local_multi.addr16[7] = + context->link_local_addr.addr16[7]; + + /* Default Prefix length is 64 */ + /* Add Link local address to the head of the ipv6 address + list */ + ipv6_add_prefix_entry(context, + &context->link_local_addr, 64); + } + /* + * Convert Multicast IP address to Multicast MAC adress per + * RFC 2464: Transmission of IPv6 Packets over Ethernet Networks + * + * An IPv6 packet with a multicast destination address DST, consisting + * of the sixteen octets DST[1] through DST[16], is transmitted to the + * Ethernet multicast address whose first two octets are the value 3333 + * hexadecimal and whose last four octets are the last four octets of + * DST. + * + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |0 0 1 1 0 0 1 1|0 0 1 1 0 0 1 1| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | DST[13] | DST[14] | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | DST[15] | DST[16] | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * IPv6 requires the following Multicast IP addresses setup per node. + */ + for (i = 0; i < 3; i++) { + mc_addr.addr[0] = 0x33; + mc_addr.addr[1] = 0x33; + mc_addr.addr[2] = 0x0; + mc_addr.addr[3] = 0x0; + mc_addr.addr[4] = 0x0; + + switch (i) { + case 0: + /* All Nodes Multicast IPv6 address : ff02::1 */ + mc_addr.addr[5] = 0x1; + break; + + case 1: + /* All Host Multicast IPv6 address : ff02::3 */ + mc_addr.addr[5] = 0x3; + break; + + case 2: + /* Solicited Node Multicast Address: ff02::01:ffxx:yyzz + */ + mc_addr.addr[2] = 0xff; + mc_addr.addr[3] = mac_addr->addr[3]; + mc_addr.addr[4] = mac_addr->addr[4]; + mc_addr.addr[5] = mac_addr->addr[5]; + break; + + default: + break; + } + iscsiL2AddMcAddr(context, &mc_addr); + } + + /* Default HOP number */ + context->hop_limit = IPV6_HOP_LIMIT; +} + +int ipv6_add_prefix_entry(struct ipv6_context *context, + struct ipv6_addr *ip_addr, u8_t prefix_len) +{ + int i; + struct ipv6_prefix_entry *prefix_entry; + struct ipv6_prefix_entry *ipv6_prefix_table = + context->ipv6_prefix_table; + char addr_str[INET6_ADDRSTRLEN]; + + /* Check if there is an valid entry already. */ + for (i = 0; i < IPV6_NUM_OF_ADDRESS_ENTRY; i++) { + prefix_entry = &ipv6_prefix_table[i]; + + if (prefix_entry->prefix_len != 0) { + if (memcmp((char *)&prefix_entry->ip_addr, + (char *)ip_addr, + sizeof(struct ipv6_addr)) == 0) { + /* We already initialize on this interface. + There is nothing to do */ + return 0; + } + } + } + + /* Find an unused entry */ + for (i = 0; i < IPV6_NUM_OF_ADDRESS_ENTRY; i++) { + prefix_entry = &ipv6_prefix_table[i]; + + if (prefix_entry->prefix_len == 0) + break; + } + + if (prefix_entry->prefix_len != 0) + return -1; + + prefix_entry->prefix_len = prefix_len / 8; + + memcpy((char *)&prefix_entry->ip_addr, + (char *)ip_addr, sizeof(struct ipv6_addr)); + + inet_ntop(AF_INET6, &prefix_entry->ip_addr.addr8, addr_str, + sizeof(addr_str)); + + LOG_DEBUG("IPv6: add prefix IP addr %s", addr_str); + + /* Put it on the list on head of the list. */ + if (context->addr_list != NULL) + prefix_entry->next = context->addr_list; + else + prefix_entry->next = NULL; + + context->addr_list = prefix_entry; + + return 0; +} + +void ipv6_rx_packet(struct ipv6_context *context, u16_t len) +{ + struct ipv6_hdr *ipv6; + u16_t protocol; + + if (!context->ustack) { + LOG_WARN("ipv6 rx pkt ipv6_context = %p ustack = %p", context, + context->ustack); + return; + } + ipv6 = (struct ipv6_hdr *)context->ustack->network_layer; + /* Make sure it's an IPv6 packet */ + if ((ipv6->ipv6_version_fc & 0xf0) != IPV6_VERSION) { + /* It's not an IPv6 packet. Drop it. */ + LOG_WARN("IPv6 version 0x%x not IPv6", ipv6->ipv6_version_fc); + return; + } + protocol = ipv6_process_rx(ipv6); + + switch (protocol) { + case IPPROTO_ICMPV6: + ipv6_icmp_rx(context); + break; + + case IPPROTO_UDP: + /* Indicate to UDP processing code */ + ipv6_udp_rx(context); + break; + + default: + break; + } +} + +void ipv6_mc_init_dest_mac(struct eth_hdr *eth, struct ipv6_hdr *ipv6) +{ + int i; + /* + * Initialize address mapping of IPV6 Multicast to multicast MAC + * address per RFC 2464. + * + * An IPv6 packet with a multicast destination address DST, consisting + * of the sixteen octets DST[1] through DST[16], is transmitted to the + * Ethernet multicast address whose first two octets are the value 3333 + * hexadecimal and whose last four octets are the last four octets of + * DST. + * + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |0 0 1 1 0 0 1 1|0 0 1 1 0 0 1 1| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | DST[13] | DST[14] | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | DST[15] | DST[16] | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + eth->dest_mac.addr[0] = 0x33; + eth->dest_mac.addr[1] = 0x33; + for (i = 0; i < 4; i++) + eth->dest_mac.addr[2 + i] = ipv6->ipv6_dst.addr8[12 + i]; +} + +int ipv6_autoconfig(struct ipv6_context *context) +{ + return ipv6_discover_address(context); +} + +int ipv6_discover_address(struct ipv6_context *context) +{ + struct eth_hdr *eth = + (struct eth_hdr *)context->ustack->data_link_layer; + struct ipv6_hdr *ipv6 = + (struct ipv6_hdr *)context->ustack->network_layer; + struct icmpv6_hdr *icmp = (struct icmpv6_hdr *)((u8_t *)ipv6 + + sizeof(struct ipv6_hdr)); + int rc = 0; + + /* Retrieve tx buffer */ + if (eth == NULL || ipv6 == NULL) + return -EAGAIN; + + /* Setup IPv6 All Routers Multicast address : ff02::2 */ + memset((char *)&ipv6->ipv6_dst, 0, sizeof(struct ipv6_addr)); + ipv6->ipv6_dst.addr8[0] = 0xff; + ipv6->ipv6_dst.addr8[1] = 0x02; + ipv6->ipv6_dst.addr8[15] = 0x02; + ipv6->ipv6_hop_limit = 255; + + /* Initialize MAC header based on destination MAC address */ + ipv6_mc_init_dest_mac(eth, ipv6); + ipv6->ipv6_nxt_hdr = IPPROTO_ICMPV6; + + icmp->icmpv6_type = ICMPV6_RTR_SOL; + icmp->icmpv6_code = 0; + icmp->icmpv6_data = 0; + icmp->icmpv6_cksum = 0; + ipv6_icmp_init_link_option(context, + (struct icmpv6_opt_link_addr *)((u8_t *)icmp + + sizeof(struct icmpv6_hdr)), + IPV6_ICMP_OPTION_SRC_ADDR); + ipv6->ipv6_plen = HOST_TO_NET16((sizeof(struct icmpv6_hdr) + + sizeof(struct icmpv6_opt_link_addr))); + memcpy((char *)&ipv6->ipv6_src, + (char *)&context->link_local_addr, + sizeof(struct ipv6_addr)); + + icmp->icmpv6_cksum = 0; + LOG_DEBUG("IPv6: Send rtr sol"); + ipv6_send(context, (u8_t *) icmp - (u8_t *) eth + + sizeof(struct icmpv6_hdr) + + sizeof(struct icmpv6_opt_link_addr)); + return rc; +} + +u16_t ipv6_process_rx(struct ipv6_hdr *ipv6) +{ + return ipv6->ipv6_nxt_hdr; +} + +int ipv6_send(struct ipv6_context *context, u16_t packet_len) +{ + struct eth_hdr *eth = + (struct eth_hdr *)context->ustack->data_link_layer; + struct ipv6_hdr *ipv6 = + (struct ipv6_hdr *)context->ustack->network_layer; + + ipv6_setup_hdrs(context, eth, ipv6, packet_len); + + return iscsiL2Send(context, packet_len); +} + +void ipv6_send_udp_packet(struct ipv6_context *context, u16_t packet_len) +{ + struct eth_hdr *eth = + (struct eth_hdr *)context->ustack->data_link_layer; + struct ipv6_hdr *ipv6 = + (struct ipv6_hdr *)context->ustack->network_layer; + struct udp_hdr *udp = (struct udp_hdr *)((u8_t *)ipv6 + + sizeof(struct ipv6_hdr)); + + ipv6->ipv6_nxt_hdr = IPPROTO_UDP; + ipv6->ipv6_plen = + HOST_TO_NET16(packet_len - ((u8_t *)udp - (u8_t *)eth)); + + udp->chksum = 0; + + /* + * We only use UDP packet for DHCPv6. The source address is always + * link-local address. + */ + ipv6->ipv6_src.addr[0] = 0; + + /* Hop limit is always 1 for DHCPv6 packet. */ + ipv6->ipv6_hop_limit = 1; + + ipv6_send(context, packet_len); +} + +void ipv6_setup_hdrs(struct ipv6_context *context, struct eth_hdr *eth, + struct ipv6_hdr *ipv6, u16_t packet_len) +{ + struct ipv6_addr *our_address; + + /* VLAN will be taken cared of in the nic layer */ + eth->len_type = HOST_TO_NET16(LAYER2_TYPE_IPV6); + memcpy((char *)ð->src_mac, + (char *)&context->mac_addr, sizeof(struct mac_address)); + + /* Put the traffic class into the packet. */ + memset(&ipv6->ipv6_version_fc, 0, sizeof(u32_t)); + ipv6->ipv6_version_fc = IPV6_VERSION; + if (ipv6->ipv6_hop_limit == 0) + ipv6->ipv6_hop_limit = context->hop_limit; + + if (ipv6->ipv6_src.addr[0] == 0) { + /* Need to initialize source IP address. */ + our_address = ipv6_our_address(context); + if (our_address != NULL) { + /* Assume that caller has filled in the destination + IP address */ + memcpy((char *)&ipv6->ipv6_src, + (char *)our_address, sizeof(struct ipv6_addr)); + } + } + + ipv6_insert_protocol_chksum(ipv6); +} + +static void ipv6_insert_protocol_chksum(struct ipv6_hdr *ipv6) +{ + u32_t sum; + u16_t *ptr; + u16_t *protocol_data_ptr; + int i; + u16_t protocol_data_len; + u16_t checksum; + + /* + * This routine assumes that there is no extension header. This driver + * doesn't user extension header to keep driver small and simple. + * + * Pseudo check consists of the following: + * SRC IP, DST IP, Protocol Data Length, and Next Header. + */ + sum = 0; + ptr = (u16_t *)&ipv6->ipv6_src; + + for (i = 0; i < sizeof(struct ipv6_addr); i++) { + sum += HOST_TO_NET16(*ptr); + ptr++; + } + + /* Keep track where the layer header is */ + protocol_data_ptr = ptr; + + protocol_data_len = HOST_TO_NET16(ipv6->ipv6_plen); + sum += protocol_data_len; + sum += ipv6->ipv6_nxt_hdr; + /* Sum now contains sum of IPv6 pseudo header. Let's add the data + streams. */ + if (protocol_data_len & 1) { + /* Length of data is odd */ + *((u8_t *) ptr + protocol_data_len) = 0; + protocol_data_len++; + } + + for (i = 0; i < protocol_data_len / 2; i++) { + sum += HOST_TO_NET16(*ptr); + ptr++; + } + + sum = (sum >> 16) + (sum & 0xffff); + sum += (sum >> 16); + sum &= 0xffff; + checksum = (u16_t) (~sum); + checksum = HOST_TO_NET16(checksum); + + switch (ipv6->ipv6_nxt_hdr) { + case IPPROTO_ICMPV6: + /* Insert correct ICMPv6 checksum */ + ((struct icmpv6_hdr *)(protocol_data_ptr))->icmpv6_cksum = + checksum; + break; + case IPPROTO_UDP: + /* Insert correct UDP checksum */ + ((struct udp_hdr *)protocol_data_ptr)->chksum = checksum; + break; + default: + break; + } +} + +int ipv6_is_it_our_link_local_address(struct ipv6_context *context, + struct ipv6_addr *ip_addr) +{ + u8_t *test_addr = (u8_t *) ip_addr->addr8; + u8_t test_remainder; + + if (test_addr[0] != context->link_local_addr.addr8[0]) + return FALSE; + + test_remainder = (test_addr[1] & 0xC0) >> 6; + if (test_remainder != 2) + return FALSE; + + return TRUE; +} + +static int ipv6_is_it_our_address(struct ipv6_context *context, + struct ipv6_addr *ipv6_addr) +{ + struct ipv6_prefix_entry *ipv6_prefix; + + for (ipv6_prefix = context->addr_list; ipv6_prefix != NULL; + ipv6_prefix = ipv6_prefix->next) { + if (IPV6_ARE_ADDR_EQUAL(&ipv6_prefix->ip_addr, ipv6_addr)) + return TRUE; + } + + return FALSE; +} + +struct ipv6_addr *ipv6_our_address(struct ipv6_context *context) +{ + return &context->link_local_addr; +} + +int ipv6_ip_in_arp_table(struct ipv6_context *context, + struct ipv6_addr *ip_addr, + struct mac_address *mac_addr) +{ + struct ipv6_arp_entry *arp_entry; + int i; + + for (i = 0; i < UIP_ARPTAB_SIZE; i++) { + arp_entry = &context->ipv6_arp_table[i]; + + if (IPV6_ARE_ADDR_EQUAL(&arp_entry->ip_addr, ip_addr)) { + memcpy((char *)mac_addr, &arp_entry->mac_addr, + sizeof(struct mac_address)); + return 1; + } + } + return 0; +} + +struct ipv6_addr *ipv6_find_longest_match(struct ipv6_context *context, + struct ipv6_addr *ip_addr) +{ + struct ipv6_prefix_entry *ipv6_prefix; + struct ipv6_prefix_entry *best_match = NULL; + int longest_len = -1; + int len; + + for (ipv6_prefix = context->addr_list; ipv6_prefix != NULL; + ipv6_prefix = ipv6_prefix->next) { + if (!IPV6_IS_ADDR_LINKLOCAL(&ipv6_prefix->ip_addr)) { + len = best_match_bufcmp((u8_t *)&ipv6_prefix->ip_addr, + (u8_t *)ip_addr, + sizeof(struct ipv6_addr)); + if (len > longest_len) { + best_match = ipv6_prefix; + longest_len = len; + } + } + } + + if (best_match) + return &best_match->ip_addr; + + return NULL; +} + +void ipv6_arp_out(struct ipv6_context *context, int *uip_len) +{ + /* Empty routine */ +} + + +static void ipv6_update_arp_table(struct ipv6_context *context, + struct ipv6_addr *ip_addr, + struct mac_address *mac_addr) +{ + struct ipv6_arp_entry *arp_entry; + int i; + struct ipv6_arp_entry *ipv6_arp_table = context->ipv6_arp_table; + + LOG_DEBUG("IPv6: Neighbor update"); + /* + * Walk through the ARP mapping table and try to find an entry to + * update. If none is found, the IP -> MAC address mapping is + * inserted in the ARP table. + */ + for (i = 0; i < UIP_ARPTAB_SIZE; i++) { + arp_entry = &ipv6_arp_table[i]; + + /* Only check those entries that are actually in use. */ + if (arp_entry->ip_addr.addr[0] != 0) { + /* + * Check if the source IP address of the incoming + * packet matches the IP address in this ARP table + * entry. + */ + if (IPV6_ARE_ADDR_EQUAL(&arp_entry->ip_addr, ip_addr)) { + /* An old entry found, update this and return */ + memcpy((char *)&arp_entry->mac_addr, + (char *)mac_addr, + sizeof(struct mac_address)); + arp_entry->time = context->arptime; + return; + } + } + } + + /* + * If we get here, no existing ARP table entry was found, so we + * create one. + * + * First, we try to find an unused entry in the ARP table. + */ + for (i = 0; i < UIP_ARPTAB_SIZE; i++) { + arp_entry = &ipv6_arp_table[i]; + + if (arp_entry->ip_addr.addr[0] == 0) + break; + } + + if (i == UIP_ARPTAB_SIZE) + return; + + /* Index j is the entry that is least used */ + arp_entry = &ipv6_arp_table[i]; + memcpy((char *)&arp_entry->ip_addr, (char *)ip_addr, + sizeof(struct ipv6_addr)); + memcpy((char *)&arp_entry->mac_addr, + (char *)mac_addr, sizeof(struct mac_address)); + + arp_entry->time = context->arptime; +} + +/* DestIP is intact */ +int ipv6_send_nd_solicited_packet(struct ipv6_context *context, + struct eth_hdr *eth, struct ipv6_hdr *ipv6) +{ + struct icmpv6_hdr *icmp; + int pkt_len = 0; + struct ipv6_addr *longest_match_addr; + char addr_str[INET6_ADDRSTRLEN]; + + ipv6->ipv6_nxt_hdr = IPPROTO_ICMPV6; + + /* Depending on the IPv6 address of the target, we'll need to determine + whether we use the assigned IPv6 address/RA or the link local address + */ + /* Use Link-local as source address */ + if (ipv6_is_it_our_link_local_address(context, &ipv6->ipv6_dst) == + TRUE) { + LOG_DEBUG("IPv6: NS using link local"); + memcpy((char *)&ipv6->ipv6_src, + (char *)&context->link_local_addr, + sizeof(struct ipv6_addr)); + } else { + longest_match_addr = + ipv6_find_longest_match(context, &ipv6->ipv6_dst); + if (longest_match_addr) { + LOG_DEBUG("IPv6: NS using longest match addr"); + memcpy((char *)&ipv6->ipv6_src, + (char *)longest_match_addr, + sizeof(struct ipv6_addr)); + } else { + LOG_DEBUG("IPv6: NS using link local instead"); + memcpy((char *)&ipv6->ipv6_src, + (char *)&context->link_local_addr, + sizeof(struct ipv6_addr)); + } + } + icmp = (struct icmpv6_hdr *)((u8_t *)ipv6 + sizeof(struct ipv6_hdr)); + + inet_ntop(AF_INET6, &ipv6->ipv6_src.addr8, addr_str, sizeof(addr_str)); + LOG_DEBUG("IPv6: NS host IP addr: %s", addr_str); + /* + * Destination IP address to be resolved is after the ICMPv6 + * header. + */ + memcpy((char *)((u8_t *)icmp + sizeof(struct icmpv6_hdr)), + (char *)&ipv6->ipv6_dst, sizeof(struct ipv6_addr)); + + /* + * Destination IP in the IPv6 header contains solicited-node multicast + * address corresponding to the target address. + * + * ff02::01:ffxx:yyzz. Where xyz are least + * significant of 24-bit MAC address. + */ + memset((char *)&ipv6->ipv6_dst, 0, sizeof(struct ipv6_addr) - 3); + ipv6->ipv6_dst.addr8[0] = 0xff; + ipv6->ipv6_dst.addr8[1] = 0x02; + ipv6->ipv6_dst.addr8[11] = 0x01; + ipv6->ipv6_dst.addr8[12] = 0xff; + ipv6_mc_init_dest_mac(eth, ipv6); + ipv6->ipv6_hop_limit = 255; + + icmp->icmpv6_type = ICMPV6_NEIGH_SOL; + icmp->icmpv6_code = 0; + icmp->icmpv6_data = 0; + icmp->icmpv6_cksum = 0; + ipv6_icmp_init_link_option(context, + (struct icmpv6_opt_link_addr *)((u8_t *)icmp + + sizeof(struct icmpv6_hdr) + + sizeof(struct ipv6_addr)), + IPV6_ICMP_OPTION_SRC_ADDR); + ipv6->ipv6_plen = HOST_TO_NET16((sizeof(struct icmpv6_hdr) + + sizeof(struct icmpv6_opt_link_addr) + + sizeof(struct ipv6_addr))); + /* Total packet size */ + pkt_len = (u8_t *) icmp - (u8_t *) eth + + sizeof(struct icmpv6_hdr) + + sizeof(struct icmpv6_opt_link_addr) + sizeof(struct ipv6_addr); + ipv6_setup_hdrs(context, eth, ipv6, pkt_len); + return pkt_len; +} + +static void ipv6_icmp_init_link_option(struct ipv6_context *context, + struct icmpv6_opt_link_addr *link_opt, + u8_t type) +{ + link_opt->hdr.type = type; + link_opt->hdr.len = sizeof(struct icmpv6_opt_link_addr) / 8; + memcpy((char *)&link_opt->link_addr, + (char *)&context->mac_addr, sizeof(struct mac_address)); +} + +static void ipv6_icmp_rx(struct ipv6_context *context) +{ + struct ipv6_hdr *ipv6 = + (struct ipv6_hdr *)context->ustack->network_layer; + struct icmpv6_hdr *icmp = (struct icmpv6_hdr *)((u8_t *)ipv6 + + sizeof(struct ipv6_hdr)); + + switch (icmp->icmpv6_type) { + case ICMPV6_RTR_ADV: + ipv6_icmp_handle_router_adv(context); + break; + + case ICMPV6_NEIGH_SOL: + ipv6_icmp_handle_nd_sol(context); + break; + + case ICMPV6_NEIGH_ADV: + ipv6_icmp_handle_nd_adv(context); + break; + + case ICMPV6_ECHO_REQUEST: + /* Response with ICMP reply */ + ipv6_icmp_handle_echo_request(context); + break; + + default: + break; + } +} + +static void ipv6_icmp_handle_router_adv(struct ipv6_context *context) +{ + struct ipv6_hdr *ipv6 = + (struct ipv6_hdr *)context->ustack->network_layer; + struct icmpv6_router_advert *icmp = + (struct icmpv6_router_advert *)((u8_t *)ipv6 + sizeof(struct ipv6_hdr)); + struct icmpv6_opt_hdr *icmp_opt; + u16_t opt_len; + u16_t len; + char addr_str[INET6_ADDRSTRLEN]; + + if (context->flags & IPV6_FLAGS_ROUTER_ADV_RECEIVED) + return; + + opt_len = HOST_TO_NET16(ipv6->ipv6_plen) - + sizeof(struct icmpv6_router_advert); + + icmp_opt = (struct icmpv6_opt_hdr *)((u8_t *)icmp + + sizeof(struct icmpv6_router_advert)); + len = 0; + while (len < opt_len) { + icmp_opt = (struct icmpv6_opt_hdr *)((u8_t *)icmp + + sizeof(struct icmpv6_router_advert) + + len); + + switch (icmp_opt->type) { + case IPV6_ICMP_OPTION_PREFIX: + ipv6_icmp_process_prefix(context, + (struct icmpv6_opt_prefix *)icmp_opt); + context->flags |= IPV6_FLAGS_ROUTER_ADV_RECEIVED; + break; + + default: + break; + } + + len += icmp_opt->len * 8; + } + + if (context->flags & IPV6_FLAGS_ROUTER_ADV_RECEIVED) { + LOG_DEBUG("IPv6: RTR ADV nd_ra_flags = 0x%x", + icmp->nd_ra_flags_reserved); + if (icmp->nd_ra_curhoplimit > 0) + context->hop_limit = icmp->nd_ra_curhoplimit; + + if (icmp->nd_ra_flags_reserved & IPV6_RA_MANAGED_FLAG) + context->flags |= IPV6_FLAGS_MANAGED_ADDR_CONFIG; + + if (icmp->nd_ra_flags_reserved & IPV6_RA_CONFIG_FLAG) + context->flags |= IPV6_FLAGS_OTHER_STATEFUL_CONFIG; + + if (icmp->nd_ra_router_lifetime != 0) { + /* There is a default router. */ + if (context->ustack->router_autocfg != + IPV6_RTR_AUTOCFG_OFF) + memcpy( + (char *)&context->default_router, + (char *)&ipv6->ipv6_src, + sizeof(struct ipv6_addr)); + inet_ntop(AF_INET6, &context->default_router, + addr_str, sizeof(addr_str)); + LOG_DEBUG("IPv6: Got default router IP addr: %s", + addr_str); + } + } +} + +static void ipv6_icmp_process_prefix(struct ipv6_context *context, + struct icmpv6_opt_prefix *icmp_prefix) +{ + struct ipv6_addr addr; + char addr_str[INET6_ADDRSTRLEN]; + + /* we only process on-link address info */ + if (!(icmp_prefix->flags & ICMPV6_OPT_PREFIX_FLAG_ON_LINK)) + return; + + /* + * We only process prefix length of 64 since our Identifier is 64-bit + */ + if (icmp_prefix->prefix_len == 64) { + /* Copy 64-bit from the local-link address to create + IPv6 address */ + memcpy((char *)&addr, + (char *)&icmp_prefix->prefix, 8); + memcpy((char *)&addr.addr8[8], + &context->link_local_addr.addr8[8], 8); + inet_ntop(AF_INET6, &addr, addr_str, sizeof(addr_str)); + LOG_DEBUG("IPv6: Got RA ICMP option IP addr: %s", addr_str); + ipv6_add_prefix_entry(context, &addr, 64); + } +} + +static void ipv6_icmp_handle_nd_adv(struct ipv6_context *context) +{ + struct eth_hdr *eth = + (struct eth_hdr *)context->ustack->data_link_layer; + struct ipv6_hdr *ipv6 = + (struct ipv6_hdr *)context->ustack->network_layer; + struct icmpv6_hdr *icmp = (struct icmpv6_hdr *)((u8_t *)ipv6 + + sizeof(struct ipv6_hdr)); + struct icmpv6_opt_link_addr *link_opt = + (struct icmpv6_opt_link_addr *)((u8_t *)icmp + + sizeof(struct icmpv6_hdr) + sizeof(struct ipv6_addr)); + struct ipv6_addr *tar_addr6; + char addr_str[INET6_ADDRSTRLEN]; + + /* Added the multicast check for ARP table update */ + /* Should we qualify for only our host's multicast and our + link_local_multicast?? */ + LOG_DEBUG("IPv6: Handle nd adv"); + if ((ipv6_is_it_our_address(context, &ipv6->ipv6_dst) == TRUE) || + (memcmp((char *)&context->link_local_multi, + (char *)&ipv6->ipv6_dst, sizeof(struct ipv6_addr)) == 0) || + (memcmp((char *)&context->multi, + (char *)&ipv6->ipv6_dst, sizeof(struct ipv6_addr)) == 0)) { + /* + * This is an ARP reply for our addresses. Let's update the + * ARP table. + */ + ipv6_update_arp_table(context, &ipv6->ipv6_src, + ð->src_mac); + + /* Now check for the target address option and update that as + well */ + if (link_opt->hdr.type == IPV6_ICMP_OPTION_TAR_ADDR) { + tar_addr6 = (struct ipv6_addr *)((u8_t *)icmp + + sizeof(struct icmpv6_hdr)); + LOG_DEBUG("IPV6: Target MAC " + "%02x:%02x:%02x:%02x:%02x:%02x", + link_opt->link_addr[0], link_opt->link_addr[1], + link_opt->link_addr[2], link_opt->link_addr[3], + link_opt->link_addr[4], link_opt->link_addr[5]); + inet_ntop(AF_INET6, &tar_addr6->addr8, addr_str, + sizeof(addr_str)); + LOG_DEBUG("IPv6: Target IP addr %s", addr_str); + ipv6_update_arp_table(context, tar_addr6, + (struct mac_address *)link_opt->link_addr); + } + + } +} + +static void ipv6_icmp_handle_nd_sol(struct ipv6_context *context) +{ + struct eth_hdr *eth = + (struct eth_hdr *)context->ustack->data_link_layer; + struct ipv6_hdr *ipv6 = + (struct ipv6_hdr *)context->ustack->network_layer; + struct icmpv6_hdr *icmp = (struct icmpv6_hdr *)((u8_t *)ipv6 + + sizeof(struct ipv6_hdr)); + struct icmpv6_opt_link_addr *link_opt = + (struct icmpv6_opt_link_addr *)((u8_t *)icmp + + sizeof(struct icmpv6_hdr) + sizeof(struct ipv6_addr)); + int icmpv6_opt_len = 0; + struct ipv6_addr tmp; + struct ipv6_addr *longest_match_addr, *tar_addr6; + + LOG_DEBUG("IPv6: Handle nd sol"); + + if ((memcmp((char *)&context->mac_addr, + (char *)ð->dest_mac, sizeof(struct mac_address)) != 0) && + (iscsiL2IsOurMcAddr(context, (struct mac_address *)ð->dest_mac) + == FALSE)) { + /* This packet is not for us to handle */ + LOG_DEBUG("IPv6: MAC not addressed to us " + "%02x:%02x:%02x:%02x:%02x:%02x", + eth->dest_mac.addr[0], eth->dest_mac.addr[1], + eth->dest_mac.addr[2], eth->dest_mac.addr[3], + eth->dest_mac.addr[4], eth->dest_mac.addr[5]); + return; + } + + /* Also check for the icmpv6_data before generating the reply */ + if (ipv6_is_it_our_address(context, + (struct ipv6_addr *) ((u8_t *) icmp + + sizeof(struct icmpv6_hdr))) + == FALSE) { + /* This packet is not for us to handle */ + LOG_DEBUG("IPv6: IP not addressed to us"); + return; + } + + /* Copy source MAC to Destination MAC */ + memcpy((char *)ð->dest_mac, + (char *)ð->src_mac, sizeof(struct mac_address)); + + /* Dest IP contains source IP */ + memcpy((char *)&tmp, + (char *)&ipv6->ipv6_dst, sizeof(struct ipv6_addr)); + memcpy((char *)&ipv6->ipv6_dst, + (char *)&ipv6->ipv6_src, sizeof(struct ipv6_addr)); + + /* Examine the Neighbor Solicitation ICMPv6 target address field. + If target address exist, use that to find best match src address + for the reply */ + if (link_opt->hdr.type == IPV6_ICMP_OPTION_SRC_ADDR) { + tar_addr6 = (struct ipv6_addr *)((u8_t *)icmp + + sizeof(struct icmpv6_hdr)); + if (ipv6_is_it_our_link_local_address(context, tar_addr6) + == TRUE) { + LOG_DEBUG("IPv6: NA using link local"); + memcpy((char *)&ipv6->ipv6_src, + (char *)&context->link_local_addr, + sizeof(struct ipv6_addr)); + } else { + longest_match_addr = + ipv6_find_longest_match(context, tar_addr6); + if (longest_match_addr) { + LOG_DEBUG("IPv6: NA using longest match addr"); + memcpy((char *)&ipv6->ipv6_src, + (char *)longest_match_addr, + sizeof(struct ipv6_addr)); + } else { + LOG_DEBUG("IPv6: NA using link local instead"); + memcpy((char *)&ipv6->ipv6_src, + (char *)&context->link_local_addr, + sizeof(struct ipv6_addr)); + } + } + } else { + /* No target link address, just use whatever it sent to us */ + LOG_DEBUG("IPv6: NA use dst addr"); + memcpy((char *)&ipv6->ipv6_src, + (char *)&tmp, + sizeof(struct ipv6_addr)); + } + ipv6->ipv6_hop_limit = 255; + icmp->icmpv6_type = ICMPV6_NEIGH_ADV; + icmp->icmpv6_code = 0; + icmp->icmpv6_data = 0; + icmp->icmpv6_cksum = 0; + icmp->data.icmpv6_un_data8[0] = + IPV6_NA_FLAG_SOLICITED | IPV6_NA_FLAG_OVERRIDE; + memcpy((char *)((u8_t *)icmp + sizeof(struct icmpv6_hdr)), + (char *)&ipv6->ipv6_src, + sizeof(struct ipv6_addr)); + + /* Add the target link address option only for all solicitation */ + ipv6_icmp_init_link_option(context, + (struct icmpv6_opt_link_addr *)((u8_t *)icmp + + sizeof(struct icmpv6_hdr) + + sizeof(struct ipv6_addr)), + IPV6_ICMP_OPTION_TAR_ADDR); + icmpv6_opt_len = sizeof(struct icmpv6_opt_link_addr); + ipv6->ipv6_plen = HOST_TO_NET16((sizeof(struct icmpv6_hdr) + + icmpv6_opt_len + sizeof(struct ipv6_addr))); + LOG_DEBUG("IPv6: Send nd adv"); + ipv6_send(context, + (u8_t *) icmp - (u8_t *) eth + + sizeof(struct icmpv6_hdr) + + sizeof(struct icmpv6_opt_link_addr) + + sizeof(struct ipv6_addr)); + return; +} + +static void ipv6_icmp_handle_echo_request(struct ipv6_context *context) +{ + struct eth_hdr *eth = + (struct eth_hdr *)context->ustack->data_link_layer; + struct ipv6_hdr *ipv6 = + (struct ipv6_hdr *)context->ustack->network_layer; + struct icmpv6_hdr *icmp = (struct icmpv6_hdr *)((u8_t *)ipv6 + + sizeof(struct ipv6_hdr)); + struct ipv6_addr temp; + + /* Copy source MAC to Destination MAC */ + memcpy((char *)ð->dest_mac, + (char *)ð->src_mac, sizeof(struct mac_address)); + + memcpy((char *)&temp, + (char *)&ipv6->ipv6_dst, sizeof(struct ipv6_addr)); + + /* Dest IP contains source IP */ + memcpy((char *)&ipv6->ipv6_dst, + (char *)&ipv6->ipv6_src, sizeof(struct ipv6_addr)); + /* Use Link-local as source address */ + memcpy((char *)&ipv6->ipv6_src, + (char *)&temp, sizeof(struct ipv6_addr)); + + ipv6->ipv6_hop_limit = context->hop_limit; + icmp->icmpv6_type = ICMPV6_ECHO_REPLY; + icmp->icmpv6_code = 0; + icmp->icmpv6_cksum = 0; + LOG_DEBUG("IPv6: Send echo reply"); + ipv6_send(context, (u8_t *) icmp - (u8_t *) eth + + sizeof(struct ipv6_hdr) + HOST_TO_NET16(ipv6->ipv6_plen)); + return; +} + +void ipv6_set_ip_params(struct ipv6_context *context, + struct ipv6_addr *src_ip, u8_t prefix_len, + struct ipv6_addr *default_gateway, + struct ipv6_addr *linklocal) +{ + if (!(IPV6_IS_ADDR_UNSPECIFIED(src_ip))) { + ipv6_add_prefix_entry(context, src_ip, prefix_len); + /* Create the multi_dest address */ + memset(&context->multi_dest, 0, sizeof(struct ipv6_addr)); + context->multi_dest.addr8[0] = 0xff; + context->multi_dest.addr8[1] = 0x02; + context->multi_dest.addr8[11] = 0x01; + context->multi_dest.addr8[12] = 0xff; + context->multi_dest.addr8[13] = src_ip->addr8[13]; + context->multi_dest.addr16[7] = src_ip->addr16[7]; + /* Create the multi address */ + memset(&context->multi, 0, sizeof(struct ipv6_addr)); + context->multi.addr8[0] = 0xfc; + context->multi.addr8[2] = 0x02; + context->multi.addr16[7] = src_ip->addr16[7]; + } + + if (!(IPV6_IS_ADDR_UNSPECIFIED(default_gateway))) { + /* Override the default gateway addr */ + memcpy((char *)&context->default_router, + (char *)default_gateway, sizeof(struct ipv6_addr)); + ipv6_add_prefix_entry(context, default_gateway, + prefix_len); + } + if (!(IPV6_IS_ADDR_UNSPECIFIED(linklocal))) { + /* Override the linklocal addr */ + memcpy((char *)&context->link_local_addr, + (char *)linklocal, sizeof(struct ipv6_addr)); + context->link_local_multi.addr8[0] = 0xff; + context->link_local_multi.addr8[1] = 0x02; + context->link_local_multi.addr8[11] = 0x01; + context->link_local_multi.addr8[12] = 0xff; + context->link_local_multi.addr8[13] |= + context->link_local_addr.addr8[13]; + context->link_local_multi.addr16[7] = + context->link_local_addr.addr16[7]; + + /* Default Prefix length is 64 */ + /* Add Link local address to the head of the ipv6 address + list */ + ipv6_add_prefix_entry(context, + &context->link_local_addr, 64); + } +} + +int ipv6_get_source_ip_addrs(struct ipv6_context *context, + struct ipv6_addr_entry *addr_list) +{ + struct ipv6_prefix_entry *ipv6_prefix; + int i; + + for (i = 0, ipv6_prefix = context->addr_list; ipv6_prefix != NULL; + ipv6_prefix = ipv6_prefix->next) { + memcpy((char *)&addr_list->ip_addr, + (char *)&ipv6_prefix->ip_addr, + sizeof(struct ipv6_addr)); + addr_list->prefix_len = ipv6_prefix->prefix_len * 8; + + i++; + addr_list++; + } + + return i; +} + +int ipv6_get_default_router_ip_addrs(struct ipv6_context *context, + struct ipv6_addr *ip_addr) +{ + /* This is a default router. */ + memcpy((char *)ip_addr, + (char *)&context->default_router, + sizeof(struct ipv6_addr)); + + return 1; +} + +static void ipv6_udp_rx(struct ipv6_context *context) +{ + struct eth_hdr *eth = + (struct eth_hdr *)context->ustack->data_link_layer; + struct ipv6_hdr *ipv6 = + (struct ipv6_hdr *)context->ustack->network_layer; + struct udp_hdr *udp = (struct udp_hdr *)((u8_t *)ipv6 + + sizeof(struct ipv6_hdr)); + struct dhcpv6_context *dhcpv6c; + + /* + * We only care about DHCPv6 packets from the DHCPv6 server. We drop + * all others. + */ + if (!(context->flags & IPV6_FLAGS_DISABLE_DHCPV6)) { + if ((udp->src_port == HOST_TO_NET16(DHCPV6_SERVER_PORT)) && + (udp->dest_port == HOST_TO_NET16(DHCPV6_CLIENT_PORT))) { + dhcpv6c = context->dhcpv6_context; + dhcpv6c->eth = eth; + dhcpv6c->ipv6 = ipv6; + dhcpv6c->udp = udp; + ipv6_udp_handle_dhcp(dhcpv6c); + } + } +} + +struct mac_address *ipv6_get_link_addr(struct ipv6_context *context) +{ + return &context->mac_addr; +} + +u16_t ipv6_do_stateful_dhcpv6(struct ipv6_context *context, u32_t flags) +{ + u16_t task = 0; + u16_t ra_flags; + + ra_flags = context->flags & + (IPV6_FLAGS_MANAGED_ADDR_CONFIG | IPV6_FLAGS_OTHER_STATEFUL_CONFIG); + + if (!(context->flags & IPV6_FLAGS_ROUTER_ADV_RECEIVED)) { + LOG_DEBUG("IPv6: There is no IPv6 router on the network"); + ra_flags |= + (IPV6_FLAGS_MANAGED_ADDR_CONFIG | + IPV6_FLAGS_OTHER_STATEFUL_CONFIG); + } + + if ((flags & ISCSI_FLAGS_DHCP_TCPIP_CONFIG) && + (ra_flags & IPV6_FLAGS_MANAGED_ADDR_CONFIG)) + task |= DHCPV6_TASK_GET_IP_ADDRESS; + + if ((flags & ISCSI_FLAGS_DHCP_ISCSI_CONFIG) && + (ra_flags & IPV6_FLAGS_OTHER_STATEFUL_CONFIG)) + task |= DHCPV6_TASK_GET_OTHER_PARAMS; + + LOG_DEBUG("IPv6: Stateful flags = 0x%x, ra_flags = 0x%x, task = 0x%x", + flags, ra_flags, task); + + return task; +} + +void ipv6_add_solit_node_address(struct ipv6_context *context, + struct ipv6_addr *ip_addr) +{ + struct mac_address mac_addr; + + /* + * Add Solicited Node Multicast Address for statically configured IPv6 + * address. + */ + mac_addr.addr[0] = 0x33; + mac_addr.addr[1] = 0x33; + mac_addr.addr[2] = 0xff; + mac_addr.addr[3] = ip_addr->addr8[13]; + mac_addr.addr[4] = ip_addr->addr8[14]; + mac_addr.addr[5] = ip_addr->addr8[15]; + iscsiL2AddMcAddr(context, (struct mac_address *)&mac_addr); +} + +void ipv6_cfg_link_local_addr(struct ipv6_context *context, + struct ipv6_addr *ip_addr) +{ + memcpy((char *)&context->link_local_addr, + (char *)ip_addr, sizeof(struct ipv6_addr)); +} + +void ipv6_disable_dhcpv6(struct ipv6_context *context) +{ + context->flags |= IPV6_FLAGS_DISABLE_DHCPV6; +} diff --git a/iscsiuio/src/uip/ipv6.h b/iscsiuio/src/uip/ipv6.h new file mode 100644 index 0000000..bc63762 --- /dev/null +++ b/iscsiuio/src/uip/ipv6.h @@ -0,0 +1,332 @@ +/* + * Copyright (c) 2011, Broadcom Corporation + * Copyright (c) 2014, QLogic Corporation + * + * Written by: Eddie Wai (eddie.wai@broadcom.com) + * Based on Kevin Tran's iSCSI boot code + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam Dunkels. + * 4. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ipv6.h - This file contains macro definitions pertaining to IPv6. + * + * RFC 2460 : IPv6 Specification. + * RFC 2373 : IPv6 Addressing Architecture. + * RFC 2462 : IPv6 Stateless Address Autoconfiguration. + * RFC 2464 : Transmission of IPv6 Packets over Ethernet Networks. + * + */ +#ifndef __IPV6_H__ +#define __IPV6_H__ + +#include "ipv6_ndpc.h" + +#define FALSE 0 +#define TRUE 1 + +#define LINK_LOCAL_PREFIX_LENGTH 2 +#define LAYER2_HEADER_LENGTH 14 +#define LAYER2_VLAN_HEADER_LENGTH 16 +#define LAYER2_TYPE_IPV6 0x86dd + +struct ipv6_addr { + union { + u8_t addr8[16]; + u16_t addr16[8]; + u32_t addr[4]; + }; +}; + +struct udp_hdr { + u16_t src_port; + u16_t dest_port; + u16_t length; + u16_t chksum; +}; + +struct mac_address { + union { + u8_t addr[6]; + struct { + u16_t first_2_bytes; + u32_t last_4_bytes; + } __attribute__ ((packed)); + }; +}; + +#define HOST_TO_NET16(a) htons(a) +#define HOST_TO_NET32(a) htonl(a) +#define NET_TO_HOST16(a) ntohs(a) +/* + * Local definition for masks + */ +#define IPV6_MASK0 { { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } } } +#define IPV6_MASK32 { { { 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } } +#define IPV6_MASK64 { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } } +#define IPV6_MASK96 { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } } } +#define IPV6_MASK128 { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } } } + +#ifdef BIG_ENDIAN +#define IPV6_ADDR_INT32_ONE 1 +#define IPV6_ADDR_INT32_TWO 2 +#define IPV6_ADDR_INT32_MNL 0xff010000 +#define IPV6_ADDR_INT32_MLL 0xff020000 +#define IPV6_ADDR_INT32_SMP 0x0000ffff +#define IPV6_ADDR_INT16_ULL 0xfe80 +#define IPV6_ADDR_INT16_USL 0xfec0 +#define IPV6_ADDR_INT16_MLL 0xff02 +#else /* LITTE ENDIAN */ +#define IPV6_ADDR_INT32_ONE 0x01000000 +#define IPV6_ADDR_INT32_TWO 0x02000000 +#define IPV6_ADDR_INT32_MNL 0x000001ff +#define IPV6_ADDR_INT32_MLL 0x000002ff +#define IPV6_ADDR_INT32_SMP 0xffff0000 +#define IPV6_ADDR_INT16_ULL 0x80fe +#define IPV6_ADDR_INT16_USL 0xc0fe +#define IPV6_ADDR_INT16_MLL 0x02ff +#endif + +/* + * Definition of some useful macros to handle IP6 addresses + */ +#define IPV6_ADDR_ANY_INIT \ + { { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } } +#define IPV6_ADDR_LOOPBACK_INIT \ + { { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } } +#define IPV6_ADDR_NODELOCAL_ALLNODES_INIT \ + { { { 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } } +#define IPV6_ADDR_INTFACELOCAL_ALLNODES_INIT \ + { { { 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } } +#define IPV6_ADDR_LINKLOCAL_ALLNODES_INIT \ + { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } } +#define IPV6_ADDR_LINKLOCAL_ALLROUTERS_INIT \ + { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 } } } + +#define IPV6_ARE_ADDR_EQUAL(a, b) \ + (memcmp((char *)a, (char *)b, sizeof(struct ipv6_addr)) == 0) + +/* Unspecified IPv6 address */ +#define IPV6_IS_ADDR_UNSPECIFIED(a) \ + ((((a)->addr[0]) == 0) && \ + (((a)->addr[1]) == 0) && \ + (((a)->addr[2]) == 0) && \ + (((a)->addr[3]) == 0)) + +/* IPv6 Scope Values */ +#define IPV6_ADDR_SCOPE_INTFACELOCAL 0x01 /* Node-local scope */ +#define IPV6_ADDR_SCOPE_LINKLOCAL 0x02 /* Link-local scope */ +#define IPV6_ADDR_SCOPE_SITELOCAL 0x05 /* Site-local scope */ +#define IPV6_ADDR_SCOPE_ORGLOCAL 0x08 /* Organization-local scope */ +#define IPV6_ADDR_SCOPE_GLOBAL 0x0e /* Global scope */ + +/* Link-local Unicast : 10-bits much be 1111111010b --> 0xfe80. */ +#define IPV6_IS_ADDR_LINKLOCAL(a) \ + (((a)->addr8[0] == 0xfe) && (((a)->addr8[1] & 0xc0) == 0x80)) + +/* Site-local Unicast : 10-bits much be 1111111011b --> 0xfec0. */ +#define IPV6_IS_ADDR_SITELOCAL(a) \ + (((a)->addr8[0] == 0xfe) && (((a)->addr8[1] & 0xc0) == 0xc0)) + +/* Multicast : 10bits much be 11111111b. Next 4 bits is flags | 4-bit scope */ +#define IPV6_IS_ADDR_MULTICAST(a) ((a)->addr8[0] == 0xff) + +#define IPV6_ADDR_MC_SCOPE(a) ((a)->addr8[1] & 0x0f) + +/* Multicast Scope */ + +struct eth_hdr { + struct mac_address dest_mac; + struct mac_address src_mac; + u16_t len_type; +}; + +struct ipv6_hdr { + union { + struct { + u32_t ipv6_flow; /* Version (4-bit) | + Traffic Class (8-bit) | + Flow ID (20-bit) */ + u16_t ipv6_plen; /* Payload length */ + u8_t ipv6_nxt_hdr; /* Next Header */ + u8_t ipv6_hop_limit; /* hop limit */ + } ipv6_dw1; + + u8_t ipv6_version_fc; /* 4 bits version, top 4 bits class */ + } ipv6_ctrl; + + struct ipv6_addr ipv6_src; /* Source address */ + struct ipv6_addr ipv6_dst; /* Destination address */ +}; + +#define ipv6_version_fc ipv6_ctrl.ipv6_version_fc +#define ipv6_flow ipv6_ctrl.ipv6_dw1.ipv6_flow +#define ipv6_plen ipv6_ctrl.ipv6_dw1.ipv6_plen +#define ipv6_nxt_hdr ipv6_ctrl.ipv6_dw1.ipv6_nxt_hdr +#define ipv6_hop_limit ipv6_ctrl.ipv6_dw1.ipv6_hop_limit + +#define IPV6_VERSION 0x60 +#define IPV6_VERSION_MASK 0xf0 +#define IPV6_HOP_LIMIT 64 + +/* Length of the IP header with no next header */ +#define IPV6_HEADER_LEN sizeof(struct ipv6_hdr) + +#ifdef BIG_ENDIAN +#define IPV6_FLOWINFO_MASK 0x0fffffff /* flow info (28 bits) */ +#define IPV6_FLOWLABEL_MASK 0x000fffff /* flow label (20 bits) */ +#else /* LITTLE_ENDIAN */ +#define IPV6_FLOWINFO_MASK 0xffffff0f /* flow info (28 bits) */ +#define IPV6_FLOWLABEL_MASK 0xffff0f00 /* flow label (20 bits) */ +#endif + +struct packet_ipv6 { + struct mac_address dest_mac; + struct mac_address src_mac; + u16_t len_type; + struct ipv6_hdr ipv6; + union { + struct udp_hdr udp; + } layer4_prot; +}; + +struct packet_ipv6_vlan { + struct mac_address dest_mac; + struct mac_address src_mac; + u16_t len_type; + u16_t vlan_id; + struct ipv6_hdr ipv6; + union { + struct udp_hdr udp; + } layer4_prot; +}; + +struct ipv6_arp_entry { + struct ipv6_addr ip_addr; + struct mac_address mac_addr; + u8_t time; +}; + +#define IPV6_NUM_OF_ADDRESS_ENTRY 4 + +struct ipv6_prefix_entry { + struct ipv6_prefix_entry *next; + struct ipv6_addr ip_addr; + u8_t prefix_len; +}; + +struct ipv6_addr_entry { + struct ipv6_addr ip_addr; + u8_t prefix_len; +}; + +struct ipv6_context { + u16_t flags; +#define IPV6_FLAGS_MANAGED_ADDR_CONFIG (1 << 0) +#define IPV6_FLAGS_OTHER_STATEFUL_CONFIG (1 << 1) +#define IPV6_FLAGS_ROUTER_ADV_RECEIVED (1 << 2) +#define IPV6_FLAGS_DISABLE_DHCPV6 (1 << 3) + + struct mac_address mac_addr; + struct ipv6_addr link_local_addr; + struct ipv6_addr link_local_multi; + struct ipv6_addr multi; /* For Static IPv6 only */ + struct ipv6_addr multi_dest; /* For Static IPv6 only */ + struct ipv6_addr default_router; + struct ipv6_prefix_entry *addr_list; + u8_t hop_limit; +#define UIP_ARPTAB_SIZE 8 + + struct uip_stack *ustack; +#define MAX_MCADDR_TABLE 5 + struct mac_address mc_addr[MAX_MCADDR_TABLE]; + u8_t arptime; + struct ipv6_arp_entry ipv6_arp_table[UIP_ARPTAB_SIZE]; + struct ipv6_prefix_entry ipv6_prefix_table[IPV6_NUM_OF_ADDRESS_ENTRY]; + + /* VLAN support */ + + void *dhcpv6_context; +}; + +#define ISCSI_FLAGS_DHCP_TCPIP_CONFIG (1<<0) +#define ISCSI_FLAGS_DHCP_ISCSI_CONFIG (1<<1) + +#define IPV6_MAX_ROUTER_SOL_DELAY 4 +#define IPV6_MAX_ROUTER_SOL_RETRY 3 + +#define DHCPV6_CLIENT_PORT 546 +#define DHCPV6_SERVER_PORT 547 + +/* Function prototype */ +void ipv6_init(struct ndpc_state *ndp, int cfg); +int ipv6_autoconfig(struct ipv6_context *context); +int ipv6_discover_address(struct ipv6_context *context); +struct ipv6_addr *ipv6_our_address(struct ipv6_context *context); +int ipv6_ip_in_arp_table(struct ipv6_context *context, + struct ipv6_addr *ipv6_addr, + struct mac_address *mac_addr); +void ipv6_arp_timer(struct ipv6_context *context); +void ipv6_arp_out(struct ipv6_context *context, int *uip_len); +int ipv6_add_prefix_entry(struct ipv6_context *context, + struct ipv6_addr *ip_addr, u8_t prefix_len); +void ipv6_set_ip_params(struct ipv6_context *context, + struct ipv6_addr *src_ip, u8_t prefix_len, + struct ipv6_addr *default_gateway, + struct ipv6_addr *linklocal); +void ipv6_set_host_addr(struct ipv6_context *context, struct ipv6_addr *src_ip); +int ipv6_get_default_router_ip_addrs(struct ipv6_context *context, + struct ipv6_addr *ip_addr); +struct mac_address *ipv6_get_link_addr(struct ipv6_context *context); +u16_t ipv6_do_stateful_dhcpv6(struct ipv6_context *context, u32_t flags); +void ipv6_add_solit_node_address(struct ipv6_context *context, + struct ipv6_addr *ip_addr); +int ipv6_get_source_ip_addrs(struct ipv6_context *context, + struct ipv6_addr_entry *addr_list); +void ipv6_cfg_link_local_addr(struct ipv6_context *context, + struct ipv6_addr *ip_addr); +void ipv6_disable_dhcpv6(struct ipv6_context *context); +int ipv6_send_nd_solicited_packet(struct ipv6_context *context, + struct eth_hdr *eth, struct ipv6_hdr *ipv6); +int ipv6_is_it_our_link_local_address(struct ipv6_context *context, + struct ipv6_addr *ip_addr); +void ipv6_mc_init_dest_mac(struct eth_hdr *eth, struct ipv6_hdr *ipv6); +struct ipv6_addr *ipv6_find_longest_match(struct ipv6_context *context, + struct ipv6_addr *ip_addr); + +#endif /* __IPV6_H__ */ diff --git a/iscsiuio/src/uip/ipv6_ndpc.c b/iscsiuio/src/uip/ipv6_ndpc.c new file mode 100644 index 0000000..a396cb7 --- /dev/null +++ b/iscsiuio/src/uip/ipv6_ndpc.c @@ -0,0 +1,427 @@ +/* + * Copyright (c) 2011, Broadcom Corporation + * Copyright (c) 2014, QLogic Corporation + * + * Written by: Eddie Wai (eddie.wai@broadcom.com) + * Based on the Swedish Institute of Computer Science's + * dhcpc.c code + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam Dunkels. + * 4. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ipv6_ndpc.c - Top level IPv6 Network Discovery Protocol Engine (RFC4861) + * + */ +#include +#include +#include +#include +#include +#include + +#include "uip.h" +#include "ipv6_ndpc.h" +#include "timer.h" +#include "pt.h" + +#include "debug.h" +#include "logger.h" +#include "nic.h" +#include "nic_utils.h" +#include "ipv6.h" +#include "ipv6_pkt.h" +#include "dhcpv6.h" + +const int dhcpv6_retry_timeout[DHCPV6_NUM_OF_RETRY] = { 1, 2, 4, 8 }; + +static PT_THREAD(handle_ndp(struct uip_stack *ustack, int force)) +{ + struct ndpc_state *s; + struct ipv6_context *ipv6c; + struct dhcpv6_context *dhcpv6c = NULL; + u16_t task = 0; + char buf[INET6_ADDRSTRLEN]; + + s = ustack->ndpc; + if (s == NULL) { + LOG_DEBUG("NDP: Could not find ndpc state"); + return PT_ENDED; + } + + ipv6c = s->ipv6_context; + if (!ipv6c) + goto ndpc_state_null; + + dhcpv6c = s->dhcpv6_context; + + PT_BEGIN(&s->pt); + + if (s->state == NDPC_STATE_BACKGROUND_LOOP) + goto ipv6_loop; + + if (s->state == NDPC_STATE_RTR_ADV) + goto rtr_adv; + + /* For AUTOCFG == DHCPv6, do all + For == ND, skip DHCP only and do RTR + For == UNUSED/UNSPEC, do all as according to DHCP or not */ + s->state = NDPC_STATE_RTR_SOL; + /* try_again: */ + s->ticks = CLOCK_SECOND * IPV6_MAX_ROUTER_SOL_DELAY; + s->retry_count = 0; + do { + /* Perform router solicitation and wait for + router advertisement */ + LOG_DEBUG("%s: ndpc_handle send rtr sol", s->nic->log_name); + ipv6_autoconfig(s->ipv6_context); + + timer_set(&s->timer, s->ticks); +wait_rtr: + s->ustack->uip_flags &= ~UIP_NEWDATA; + LOG_DEBUG("%s: ndpc_handle wait for rtr adv flags=0x%x", + s->nic->log_name, ipv6c->flags); + PT_WAIT_UNTIL(&s->pt, uip_newdata(s->ustack) + || timer_expired(&s->timer) || force); + + if (uip_newdata(s->ustack)) { + /* Validate incoming packets + Note that the uip_len is init from nic loop */ + ipv6_rx_packet(ipv6c, (u16_t) uip_datalen(s->ustack)); + if (ipv6c->flags & IPV6_FLAGS_ROUTER_ADV_RECEIVED) { + LOG_INFO("%s: ROUTER_ADV_RECEIVED", + s->nic->log_name); + /* Success */ + break; + } else if (!timer_expired(&s->timer)) { + /* Yes new data, but not what we want, + check for timer expiration before bumping + tick */ + goto wait_rtr; + } + } + s->retry_count++; + if (s->retry_count >= IPV6_MAX_ROUTER_SOL_RETRY) + /* Max router solicitation retry reached. Move to + IPv6 loop (no DHCPv6) */ + goto no_rtr_adv; + + } while (!(ipv6c->flags & IPV6_FLAGS_ROUTER_ADV_RECEIVED)); + + LOG_DEBUG("%s: ndpc_handle got rtr adv", s->nic->log_name); + +no_rtr_adv: + s->state = NDPC_STATE_RTR_ADV; + +rtr_adv: + if (!(ustack->ip_config & IPV6_CONFIG_DHCP)) + goto staticv6; + + /* Only DHCPv6 comes here */ + task = ipv6_do_stateful_dhcpv6(ipv6c, ISCSI_FLAGS_DHCP_TCPIP_CONFIG); + if (task) { + /* Run the DHCPv6 engine */ + + if (!dhcpv6c) + goto ipv6_loop; + + dhcpv6c->dhcpv6_task = task; + s->retry_count = 0; + s->state = NDPC_STATE_DHCPV6_DIS; + do { + /* Do dhcpv6 */ + dhcpv6c->timeout = dhcpv6_retry_timeout[s->retry_count]; + s->ticks = CLOCK_SECOND * dhcpv6c->timeout; + LOG_DEBUG("%s: ndpc_handle send dhcpv6 sol retry " + "cnt=%d", s->nic->log_name, s->retry_count); + dhcpv6_do_discovery(dhcpv6c); + + timer_set(&s->timer, s->ticks); +wait_dhcp: + s->ustack->uip_flags &= ~UIP_NEWDATA; + PT_WAIT_UNTIL(&s->pt, uip_newdata(s->ustack) + || timer_expired(&s->timer) || force); + + if (uip_newdata(s->ustack)) { + /* Validate incoming packets + Note that the uip_len is init from nic + loop */ + ipv6_rx_packet(ipv6c, + (u16_t) uip_datalen(s->ustack)); + if (dhcpv6c->dhcpv6_done == TRUE) + break; + else if (!timer_expired(&s->timer)) { + /* Yes new data, but not what we want, + check for timer expiration before + bumping tick */ + goto wait_dhcp; + } + } + s->retry_count++; + if (s->retry_count < DHCPV6_NUM_OF_RETRY) { + dhcpv6c->seconds += dhcpv6c->timeout; + } else { + LOG_DEBUG("%s: ndpc_handle DHCP failed", + s->nic->log_name); + /* Allow to goto background loop */ + goto ipv6_loop; + } + } while (dhcpv6c->dhcpv6_done == FALSE); + s->state = NDPC_STATE_DHCPV6_DONE; + + LOG_DEBUG("%s: ndpc_handle got dhcpv6", s->nic->log_name); + + /* End of DHCPv6 engine */ + } else { + /* Static IPv6 */ + if (ustack->ip_config == IPV6_CONFIG_DHCP) { + LOG_DEBUG("%s: ndpc_handle DHCP failed", + s->nic->log_name); + PT_RESTART(&s->pt); + } +staticv6: + ipv6_disable_dhcpv6(ipv6c); + } + /* Copy out the default_router_addr6 and ll */ + if (ustack->router_autocfg != IPV6_RTR_AUTOCFG_OFF) + memcpy(&ustack->default_route_addr6, + &ipv6c->default_router, sizeof(struct ipv6_addr)); + inet_ntop(AF_INET6, &ustack->default_route_addr6, + buf, sizeof(buf)); + LOG_INFO("%s: Default router IP: %s", s->nic->log_name, + buf); + + if (ustack->linklocal_autocfg != IPV6_LL_AUTOCFG_OFF) + memcpy(&ustack->linklocal6, &ipv6c->link_local_addr, + sizeof(struct ipv6_addr)); + inet_ntop(AF_INET6, &ustack->linklocal6, + buf, sizeof(buf)); + LOG_INFO("%s: Linklocal IP: %s", s->nic->log_name, + buf); + +ipv6_loop: + s->state = NDPC_STATE_BACKGROUND_LOOP; + LOG_DEBUG("%s: Loop", s->nic->log_name); + /* Background IPv6 loop */ + while (1) { + /* Handle all neightbor solicitation/advertisement here */ + s->ustack->uip_flags &= ~UIP_NEWDATA; + PT_WAIT_UNTIL(&s->pt, uip_newdata(s->ustack)); + + /* Validate incoming packets */ + ipv6_rx_packet(ipv6c, (u16_t) uip_datalen(s->ustack)); + } + +ndpc_state_null: + + while (1) + PT_YIELD(&s->pt); + + PT_END(&(s->pt)); +} + +/*---------------------------------------------------------------------------*/ +int ndpc_init(nic_t *nic, struct uip_stack *ustack, + const void *mac_addr, int mac_len) +{ + struct ipv6_context *ipv6c; + struct dhcpv6_context *dhcpv6c; + struct ndpc_state *s = ustack->ndpc; + struct ipv6_addr src, gw, ll; + char buf[INET6_ADDRSTRLEN]; + + if (s) { + LOG_DEBUG("NDP: NDP context already allocated"); + /* Already allocated, skip*/ + return -EALREADY; + } + s = malloc(sizeof(*s)); + if (s == NULL) { + LOG_ERR("%s: Couldn't allocate size for ndpc info", + nic->log_name); + goto error; + } + memset(s, 0, sizeof(*s)); + + if (s->ipv6_context) { + LOG_DEBUG("NDP: IPv6 context already allocated"); + ipv6c = s->ipv6_context; + goto init1; + } + ipv6c = malloc(sizeof(struct ipv6_context)); + if (ipv6c == NULL) { + LOG_ERR("%s: Couldn't allocate mem for IPv6 context info", + nic->log_name); + goto error1; + } +init1: + if (s->dhcpv6_context) { + LOG_DEBUG("NDP: DHCPv6 context already allocated"); + dhcpv6c = s->dhcpv6_context; + goto init2; + } + dhcpv6c = malloc(sizeof(struct dhcpv6_context)); + if (dhcpv6c == NULL) { + LOG_ERR("%s: Couldn't allocate mem for DHCPv6 context info", + nic->log_name); + goto error2; + } +init2: + memset(s, 0, sizeof(*s)); + memset(ipv6c, 0, sizeof(*ipv6c)); + memset(dhcpv6c, 0, sizeof(*dhcpv6c)); + + s->ipv6_context = ipv6c; + s->dhcpv6_context = dhcpv6c; + + s->nic = nic; + s->ustack = ustack; + s->mac_addr = (void *)mac_addr; + s->mac_len = mac_len; + s->state = NDPC_STATE_INIT; + + /* Init IPV6_CONTEXT */ + ipv6_init(s, ustack->ip_config); + + dhcpv6c->ipv6_context = ipv6c; + ipv6c->dhcpv6_context = dhcpv6c; + + /* Init DHCPV6_CONTEXT */ + dhcpv6_init(dhcpv6c); + + ustack->ndpc = s; + + PT_INIT(&s->pt); + + if (ustack->ip_config == IPV6_CONFIG_DHCP) { + /* DHCPv6 specific */ + memset(&src, 0, sizeof(src)); + } else { + /* Static v6 specific */ + memcpy(&src.addr8, &ustack->hostaddr6, + sizeof(struct ipv6_addr)); + ipv6_add_solit_node_address(ipv6c, &src); + + inet_ntop(AF_INET6, &src.addr8, buf, sizeof(buf)); + LOG_INFO("%s: Static hostaddr IP: %s", s->nic->log_name, + buf); + } + /* Copy out the default_router_addr6 and ll */ + if (ustack->router_autocfg == IPV6_RTR_AUTOCFG_OFF) + memcpy(&gw.addr8, &ustack->default_route_addr6, + sizeof(struct ipv6_addr)); + else + memset(&gw, 0, sizeof(gw)); + + if (ustack->linklocal_autocfg == IPV6_LL_AUTOCFG_OFF) + memcpy(&ll.addr8, &ustack->linklocal6, + sizeof(struct ipv6_addr)); + else + memset(&ll, 0, sizeof(ll)); + ipv6_set_ip_params(ipv6c, &src, + ustack->prefix_len, &gw, &ll); + + return 0; +error2: + free(ipv6c); + s->ipv6_context = NULL; +error1: + free(s); + ustack->ndpc = NULL; +error: + return -ENOMEM; +} + +/*---------------------------------------------------------------------------*/ +void ndpc_call(struct uip_stack *ustack) +{ + handle_ndp(ustack, 0); +} + +void ndpc_exit(struct ndpc_state *ndp) +{ + LOG_DEBUG("NDP - Exit ndpc_state = %p", ndp); + if (!ndp) + return; + if (ndp->ipv6_context) + free(ndp->ipv6_context); + if (ndp->dhcpv6_context) + free(ndp->dhcpv6_context); + free(ndp); +} + +int ndpc_request(struct uip_stack *ustack, void *in, void *out, int request) +{ + struct ndpc_state *s; + struct ipv6_context *ipv6c; + int ret = 0; + + if (!ustack) { + LOG_DEBUG("NDP: ustack == NULL"); + return -EINVAL; + } + s = ustack->ndpc; + if (s == NULL) { + LOG_DEBUG("NDP: Could not find ndpc state for request %d", + request); + return -EINVAL; + } + while (s->state != NDPC_STATE_BACKGROUND_LOOP) { + LOG_DEBUG("%s: ndpc state not in background loop, run handler " + "request = %d", s->nic->log_name, request); + handle_ndp(ustack, 1); + } + + ipv6c = s->ipv6_context; + switch (request) { + case NEIGHBOR_SOLICIT: + *(int *)out = ipv6_send_nd_solicited_packet(ipv6c, + (struct eth_hdr *)((struct ndpc_reqptr *)in)->eth, + (struct ipv6_hdr *)((struct ndpc_reqptr *)in)->ipv6); + break; + case CHECK_LINK_LOCAL_ADDR: + *(int *)out = ipv6_is_it_our_link_local_address(ipv6c, + (struct ipv6_addr *)in); + break; + case CHECK_ARP_TABLE: + *(int *)out = ipv6_ip_in_arp_table(ipv6c, + (struct ipv6_addr *)((struct ndpc_reqptr *)in)->ipv6, + (struct mac_address *)((struct ndpc_reqptr *)in)->eth); + break; + case GET_HOST_ADDR: + *(struct ipv6_addr **)out = ipv6_find_longest_match(ipv6c, + (struct ipv6_addr *)in); + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +/*---------------------------------------------------------------------------*/ diff --git a/iscsiuio/src/uip/ipv6_ndpc.h b/iscsiuio/src/uip/ipv6_ndpc.h new file mode 100644 index 0000000..709a050 --- /dev/null +++ b/iscsiuio/src/uip/ipv6_ndpc.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2011, Broadcom Corporation + * Copyright (c) 2014, QLogic Corporation + * + * Written by: Eddie Wai (eddie.wai@broadcom.com) + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam Dunkels. + * 4. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ipv6_ndpc.h - Top level IPv6 Network Discovery Protocol Engine (RFC4861) + * + */ +#ifndef __NDPC_H__ +#define __NDPC_H__ + +#include + +#include "nic.h" +#include "timer.h" +#include "pt.h" + +struct ndpc_reqptr { + void *eth; + void *ipv6; +}; + +struct ndpc_state { + struct pt pt; + + nic_t *nic; + struct uip_stack *ustack; + char state; + struct timer timer; + u16_t ticks; + void *mac_addr; + int mac_len; + int retry_count; + + time_t last_update; + + void *ipv6_context; + void *dhcpv6_context; +}; + +enum { + NDPC_STATE_INIT, + NDPC_STATE_RTR_SOL, + NDPC_STATE_RTR_ADV, + NDPC_STATE_DHCPV6_DIS, + NDPC_STATE_DHCPV6_DONE, + NDPC_STATE_BACKGROUND_LOOP +}; + +int ndpc_init(nic_t *nic, struct uip_stack *ustack, + const void *mac_addr, int mac_len); +void ndpc_call(struct uip_stack *ustack); +void ndpc_exit(struct ndpc_state *ndp); + +enum { + NEIGHBOR_SOLICIT, + CHECK_LINK_LOCAL_ADDR, + GET_LINK_LOCAL_ADDR, + GET_DEFAULT_ROUTER_ADDR, + CHECK_ARP_TABLE, + GET_HOST_ADDR +}; + +int ndpc_request(struct uip_stack *ustack, void *in, void *out, int request); + +#define UIP_NDP_CALL ndpc_call + +#endif /* __NDPC_H__ */ diff --git a/iscsiuio/src/uip/ipv6_pkt.h b/iscsiuio/src/uip/ipv6_pkt.h new file mode 100644 index 0000000..b42f1aa --- /dev/null +++ b/iscsiuio/src/uip/ipv6_pkt.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2011, Broadcom Corporation + * Copyright (c) 2014, QLogic Corporation + * + * Written by: Eddie Wai (eddie.wai@broadcom.com) + * Based on Kevin Tran's iSCSI boot code + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam Dunkels. + * 4. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ipv6_packet.h - IPv6 routine include file + * + */ +#ifndef __IPV6_PKT_H__ +#define __IPV6_PKT_H__ + +u16_t ipv6_process_rx(struct ipv6_hdr *ipv6); +void ipv6_rx_packet(struct ipv6_context *context, u16_t len); +void ipv6_setup_hdrs(struct ipv6_context *context, struct eth_hdr *eth, + struct ipv6_hdr *ipv6, u16_t packet_len); +int ipv6_send(struct ipv6_context *context, u16_t packet_len); +void ipv6_send_udp_packet(struct ipv6_context *context, u16_t packet_len); + +#endif /* __IPV6_PKT_H__ */ diff --git a/iscsiuio/src/uip/lc-addrlabels.h b/iscsiuio/src/uip/lc-addrlabels.h new file mode 100644 index 0000000..c394b22 --- /dev/null +++ b/iscsiuio/src/uip/lc-addrlabels.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2004-2005, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack + * + * Author: Adam Dunkels + * + */ + +/** + * \addtogroup lc + * @{ + */ + +/** + * \file + * Implementation of local continuations based on the "Labels as + * values" feature of gcc + * \author + * Adam Dunkels + * + * This implementation of local continuations is based on a special + * feature of the GCC C compiler called "labels as values". This + * feature allows assigning pointers with the address of the code + * corresponding to a particular C label. + * + * For more information, see the GCC documentation: + * http://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html + * + * Thanks to dividuum for finding the nice local scope label + * implementation. + */ + +#ifndef __LC_ADDRLABELS_H__ +#define __LC_ADDRLABELS_H__ + +/** \hideinitializer */ + +#define LC_INIT(s) (s = NULL) + +#define LC_RESUME(s) \ + do { \ + if (s != NULL) { \ + goto *s; \ + } \ + } while (0) + +#define LC_SET(s) \ + do { ({ __label__ resume; resume: (s) = &&resume; }); } while (0) + +#define LC_END(s) + +#endif /* __LC_ADDRLABELS_H__ */ + +/** @} */ diff --git a/iscsiuio/src/uip/lc-switch.h b/iscsiuio/src/uip/lc-switch.h new file mode 100644 index 0000000..1839b36 --- /dev/null +++ b/iscsiuio/src/uip/lc-switch.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2004-2005, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack + * + * Author: Adam Dunkels + * + */ + +/** + * \addtogroup lc + * @{ + */ + +/** + * \file + * Implementation of local continuations based on switch() statment + * \author Adam Dunkels + * + * This implementation of local continuations uses the C switch() + * statement to resume execution of a function somewhere inside the + * function's body. The implementation is based on the fact that + * switch() statements are able to jump directly into the bodies of + * control structures such as if() or while() statmenets. + * + * This implementation borrows heavily from Simon Tatham's coroutines + * implementation in C: + * http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html + */ + +#ifndef __LC_SWITCH_H__ +#define __LC_SWTICH_H__ + +/* WARNING! lc implementation using switch() does not work if an + LC_SET() is done within another switch() statement! */ + +/** \hideinitializer */ +#define LC_INIT(s) s = 0; + +#define LC_RESUME(s) switch (s) { case 0: + +#define LC_SET(s) s = __LINE__; case __LINE__: + +#define LC_END(s) } + +#endif /* __LC_SWITCH_H__ */ + +/** @} */ diff --git a/iscsiuio/src/uip/lc.h b/iscsiuio/src/uip/lc.h new file mode 100644 index 0000000..2e4a7bb --- /dev/null +++ b/iscsiuio/src/uip/lc.h @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2004-2005, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack + * + * Author: Adam Dunkels + * + */ + +/** + * \addtogroup pt + * @{ + */ + +/** + * \defgroup lc Local continuations + * @{ + * + * Local continuations form the basis for implementing protothreads. A + * local continuation can be set in a specific function to + * capture the state of the function. After a local continuation has + * been set can be resumed in order to restore the state of the + * function at the point where the local continuation was set. + * + * + */ + +/** + * \file lc.h + * Local continuations + * \author + * Adam Dunkels + * + */ + +#ifdef DOXYGEN +/** + * Initialize a local continuation. + * + * This operation initializes the local continuation, thereby + * unsetting any previously set continuation state. + * + * \hideinitializer + */ +#define LC_INIT(lc) + +/** + * Set a local continuation. + * + * The set operation saves the state of the function at the point + * where the operation is executed. As far as the set operation is + * concerned, the state of the function does not include the + * call-stack or local (automatic) variables, but only the program + * counter and such CPU registers that needs to be saved. + * + * \hideinitializer + */ +#define LC_SET(lc) + +/** + * Resume a local continuation. + * + * The resume operation resumes a previously set local continuation, thus + * restoring the state in which the function was when the local + * continuation was set. If the local continuation has not been + * previously set, the resume operation does nothing. + * + * \hideinitializer + */ +#define LC_RESUME(lc) + +/** + * Mark the end of local continuation usage. + * + * The end operation signifies that local continuations should not be + * used any more in the function. This operation is not needed for + * most implementations of local continuation, but is required by a + * few implementations. + * + * \hideinitializer + */ +#define LC_END(lc) + +/** + * \var typedef lc_t; + * + * The local continuation type. + * + * \hideinitializer + */ +#endif /* DOXYGEN */ + +#ifndef __LC_H__ +#define __LC_H__ + +#ifdef LC_CONF_INCLUDE +#include LC_CONF_INCLUDE +#else +#include "lc-switch.h" +#endif /* LC_CONF_INCLUDE */ + +#endif /* __LC_H__ */ + +/** @} */ +/** @} */ diff --git a/iscsiuio/src/uip/psock.c b/iscsiuio/src/uip/psock.c new file mode 100644 index 0000000..fcffbe7 --- /dev/null +++ b/iscsiuio/src/uip/psock.c @@ -0,0 +1,339 @@ +/* + * Copyright (c) 2004, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack + * + * Author: Adam Dunkels + * + */ + +#include +#include + +#include "uipopt.h" +#include "psock.h" +#include "uip.h" + +#define STATE_NONE 0 +#define STATE_ACKED 1 +#define STATE_READ 2 +#define STATE_BLOCKED_NEWDATA 3 +#define STATE_BLOCKED_CLOSE 4 +#define STATE_BLOCKED_SEND 5 +#define STATE_DATA_SENT 6 + +/* + * Return value of the buffering functions that indicates that a + * buffer was not filled by incoming data. + * + */ +#define BUF_NOT_FULL 0 +#define BUF_NOT_FOUND 0 + +/* + * Return value of the buffering functions that indicates that a + * buffer was completely filled by incoming data. + * + */ +#define BUF_FULL 1 + +/* + * Return value of the buffering functions that indicates that an + * end-marker byte was found. + * + */ +#define BUF_FOUND 2 + +/*---------------------------------------------------------------------------*/ +static void buf_setup(struct psock_buf *buf, u8_t *bufptr, u16_t bufsize) +{ + buf->ptr = bufptr; + buf->left = bufsize; +} + +/*---------------------------------------------------------------------------*/ +static u8_t +buf_bufdata(struct psock_buf *buf, u16_t len, u8_t **dataptr, u16_t *datalen) +{ + if (*datalen < buf->left) { + memcpy(buf->ptr, *dataptr, *datalen); + buf->ptr += *datalen; + buf->left -= *datalen; + *dataptr += *datalen; + *datalen = 0; + return BUF_NOT_FULL; + } else if (*datalen == buf->left) { + memcpy(buf->ptr, *dataptr, *datalen); + buf->ptr += *datalen; + buf->left = 0; + *dataptr += *datalen; + *datalen = 0; + return BUF_FULL; + } else { + memcpy(buf->ptr, *dataptr, buf->left); + buf->ptr += buf->left; + *datalen -= buf->left; + *dataptr += buf->left; + buf->left = 0; + return BUF_FULL; + } +} + +/*---------------------------------------------------------------------------*/ +static u8_t +buf_bufto(register struct psock_buf *buf, u8_t endmarker, + register u8_t **dataptr, register u16_t *datalen) +{ + u8_t c; + while (buf->left > 0 && *datalen > 0) { + c = *buf->ptr = **dataptr; + ++*dataptr; + ++buf->ptr; + --*datalen; + --buf->left; + + if (c == endmarker) + return BUF_FOUND; + } + + if (*datalen == 0) + return BUF_NOT_FOUND; + + while (*datalen > 0) { + c = **dataptr; + --*datalen; + ++*dataptr; + + if (c == endmarker) + return BUF_FOUND | BUF_FULL; + } + + return BUF_FULL; +} + +/*---------------------------------------------------------------------------*/ +static char send_data(register struct psock *s) +{ + if (s->state != STATE_DATA_SENT || uip_rexmit(s->ustack)) { + if (s->sendlen > uip_mss(s->ustack)) + uip_appsend(s->ustack, s->sendptr, uip_mss(s->ustack)); + else + uip_appsend(s->ustack, s->sendptr, s->sendlen); + s->state = STATE_DATA_SENT; + return 1; + } + return 0; +} + +/*---------------------------------------------------------------------------*/ +static char data_acked(struct psock *s) +{ + if (s->state == STATE_DATA_SENT && uip_acked(s->ustack)) { + if (s->sendlen > uip_mss(s->ustack)) { + s->sendlen -= uip_mss(s->ustack); + s->sendptr += uip_mss(s->ustack); + } else { + s->sendptr += s->sendlen; + s->sendlen = 0; + } + s->state = STATE_ACKED; + return 1; + } + return 0; +} + +/*---------------------------------------------------------------------------*/ +PT_THREAD(psock_send(struct uip_stack *ustack, + register struct psock *s, const u8_t *buf, + unsigned int len)) +{ + PT_BEGIN(&s->psockpt); + + /* If there is no data to send, we exit immediately. */ + if (len == 0) { + PT_EXIT(&s->psockpt); + } + + /* Save the length of and a pointer to the data that is to be + sent. */ + s->sendptr = buf; + s->sendlen = len; + + s->state = STATE_NONE; + + /* We loop here until all data is sent. The s->sendlen variable is + updated by the data_sent() function. */ + while (s->sendlen > 0) { + + /* + * The condition for this PT_WAIT_UNTIL is a little tricky: the + * protothread will wait here until all data has been acked + * (data_acked() returns true) and until all data has been sent + * (send_data() returns true). The two functions data_acked() + * and send_data() must be called in succession to ensure that + * all data is sent. Therefore the & operator is used instead of + * the && operator, which would cause only the data_acked() + * function to be called when it returns false. + */ + PT_WAIT_UNTIL(&s->psockpt, data_acked(s) & send_data(s)); + } + + s->state = STATE_NONE; + + PT_END(&s->psockpt); +} + +/*---------------------------------------------------------------------------*/ +PT_THREAD(psock_generator_send(register struct psock *s, + unsigned short (*generate) (void *), void *arg)) +{ + PT_BEGIN(&s->psockpt); + + /* Ensure that there is a generator function to call. */ + if (generate == NULL) { + PT_EXIT(&s->psockpt); + } + + /* Call the generator function to generate the data in the + uip_appdata buffer. */ + s->sendlen = generate(arg); + s->sendptr = s->ustack->uip_appdata; + + s->state = STATE_NONE; + do { + /* Call the generator function again if we are called to perform + a retransmission. */ + if (uip_rexmit(s->ustack)) + generate(arg); + /* Wait until all data is sent and acknowledged. */ + PT_WAIT_UNTIL(&s->psockpt, data_acked(s) & send_data(s)); + } while (s->sendlen > 0); + + s->state = STATE_NONE; + + PT_END(&s->psockpt); +} + +/*---------------------------------------------------------------------------*/ +u16_t psock_datalen(struct psock *psock) +{ + return psock->bufsize - psock->buf.left; +} + +/*---------------------------------------------------------------------------*/ +char psock_newdata(struct psock *s) +{ + if (s->readlen > 0) { + /* There is data in the uip_appdata buffer that has not yet been + read with the PSOCK_READ functions. */ + return 1; + } else if (s->state == STATE_READ) { + /* All data in uip_appdata buffer already consumed. */ + s->state = STATE_BLOCKED_NEWDATA; + return 0; + } else if (uip_newdata(s->ustack)) { + /* There is new data that has not been consumed. */ + return 1; + } else { + /* There is no new data. */ + return 0; + } +} + +/*---------------------------------------------------------------------------*/ +PT_THREAD(psock_readto(register struct psock *psock, u8_t c)) +{ + PT_BEGIN(&psock->psockpt); + + buf_setup(&psock->buf, psock->bufptr, psock->bufsize); + + /* XXX: Should add buf_checkmarker() before do{} loop, if + incoming data has been handled while waiting for a write. */ + + do { + if (psock->readlen == 0) { + PT_WAIT_UNTIL(&psock->psockpt, psock_newdata(psock)); + psock->state = STATE_READ; + psock->readptr = (u8_t *) psock->ustack->uip_appdata; + psock->readlen = uip_datalen(psock->ustack); + } + } while ((buf_bufto(&psock->buf, c, + &psock->readptr, + &psock->readlen) & BUF_FOUND) == 0); + + if (psock_datalen(psock) == 0) { + psock->state = STATE_NONE; + PT_RESTART(&psock->psockpt); + } + PT_END(&psock->psockpt); +} + +/*---------------------------------------------------------------------------*/ +PT_THREAD(psock_readbuf(register struct psock *psock)) +{ + PT_BEGIN(&psock->psockpt); + + buf_setup(&psock->buf, psock->bufptr, psock->bufsize); + + /* XXX: Should add buf_checkmarker() before do{} loop, if + incoming data has been handled while waiting for a write. */ + + do { + if (psock->readlen == 0) { + PT_WAIT_UNTIL(&psock->psockpt, psock_newdata(psock)); + printf("Waited for newdata\n"); + psock->state = STATE_READ; + psock->readptr = (u8_t *) psock->ustack->uip_appdata; + psock->readlen = uip_datalen(psock->ustack); + } + } while (buf_bufdata(&psock->buf, psock->bufsize, + &psock->readptr, &psock->readlen) != BUF_FULL); + + if (psock_datalen(psock) == 0) { + psock->state = STATE_NONE; + PT_RESTART(&psock->psockpt); + } + PT_END(&psock->psockpt); +} + +/*---------------------------------------------------------------------------*/ +void +psock_init(struct uip_stack *ustack, + register struct psock *psock, u8_t *buffer, unsigned int buffersize) +{ + psock->state = STATE_NONE; + psock->readlen = 0; + psock->bufptr = buffer; + psock->bufsize = buffersize; + psock->ustack = ustack; + buf_setup(&psock->buf, buffer, buffersize); + PT_INIT(&psock->pt); + PT_INIT(&psock->psockpt); +} + +/*---------------------------------------------------------------------------*/ diff --git a/iscsiuio/src/uip/psock.h b/iscsiuio/src/uip/psock.h new file mode 100644 index 0000000..ea86ef5 --- /dev/null +++ b/iscsiuio/src/uip/psock.h @@ -0,0 +1,383 @@ +/* + * Copyright (c) 2004, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack + * + * Author: Adam Dunkels + * + */ + +/** + * \defgroup psock Protosockets library + * @{ + * + * The protosocket library provides an interface to the uIP stack that is + * similar to the traditional BSD socket interface. Unlike programs + * written for the ordinary uIP event-driven interface, programs + * written with the protosocket library are executed in a sequential + * fashion and does not have to be implemented as explicit state + * machines. + * + * Protosockets only work with TCP connections. + * + * The protosocket library uses \ref pt protothreads to provide + * sequential control flow. This makes the protosockets lightweight in + * terms of memory, but also means that protosockets inherits the + * functional limitations of protothreads. Each protosocket lives only + * within a single function. Automatic variables (stack variables) are + * not retained across a protosocket library function call. + * + * \note Because the protosocket library uses protothreads, local + * variables will not always be saved across a call to a protosocket + * library function. It is therefore advised that local variables are + * used with extreme care. + * + * The protosocket library provides functions for sending data without + * having to deal with retransmissions and acknowledgements, as well + * as functions for reading data without having to deal with data + * being split across more than one TCP segment. + * + * Because each protosocket runs as a protothread, the protosocket has to be + * started with a call to PSOCK_BEGIN() at the start of the function + * in which the protosocket is used. Similarly, the protosocket protothread can + * be terminated by a call to PSOCK_EXIT(). + * + */ + +/** + * \file + * Protosocket library header file + * \author + * Adam Dunkels + * + */ + +#ifndef __PSOCK_H__ +#define __PSOCK_H__ + +#include "uip.h" +#include "uipopt.h" +#include "pt.h" + + /* + * The structure that holds the state of a buffer. + * + * This structure holds the state of a uIP buffer. The structure has + * no user-visible elements, but is used through the functions + * provided by the library. + * + */ +struct psock_buf { + u8_t *ptr; + unsigned short left; +}; + +/** + * The representation of a protosocket. + * + * The protosocket structrure is an opaque structure with no user-visible + * elements. + */ +struct psock { + struct pt pt, psockpt; /* Protothreads - one that's using the psock + functions, and one that runs inside the + psock functions. */ + const u8_t *sendptr; /* Pointer to the next data to be sent. */ + u8_t *readptr; /* Pointer to the next data to be read. */ + + u8_t *bufptr; /* Pointer to the buffer used for buffering + incoming data. */ + + u16_t sendlen; /* The number of bytes left to be sent. */ + u16_t readlen; /* The number of bytes left to be read. */ + + struct psock_buf buf; /* The structure holding the state of the + input buffer. */ + unsigned int bufsize; /* The size of the input buffer. */ + + unsigned char state; /* The state of the protosocket. */ + + struct uip_stack *ustack; +}; + +void psock_init(struct uip_stack *ustack, + struct psock *psock, u8_t *buffer, unsigned int buffersize); +/** + * Initialize a protosocket. + * + * This macro initializes a protosocket and must be called before the + * protosocket is used. The initialization also specifies the input buffer + * for the protosocket. + * + * \param psock (struct psock *) A pointer to the protosocket to be + * initialized + * + * \param buffer (char *) A pointer to the input buffer for the + * protosocket. + * + * \param buffersize (unsigned int) The size of the input buffer. + * + * \hideinitializer + */ +#define PSOCK_INIT(psock, buffer, buffersize) \ + psock_init(psock, buffer, buffersize) + +/** + * Start the protosocket protothread in a function. + * + * This macro starts the protothread associated with the protosocket and + * must come before other protosocket calls in the function it is used. + * + * \param psock (struct psock *) A pointer to the protosocket to be + * started. + * + * \hideinitializer + */ +#define PSOCK_BEGIN(psock) PT_BEGIN(&((psock)->pt)) + +PT_THREAD(psock_send(struct uip_stack *ustack, + struct psock *psock, const u8_t *buf, unsigned int len)); +/** + * Send data. + * + * This macro sends data over a protosocket. The protosocket protothread blocks + * until all data has been sent and is known to have been received by + * the remote end of the TCP connection. + * + * \param psock (struct psock *) A pointer to the protosocket over which + * data is to be sent. + * + * \param data (char *) A pointer to the data that is to be sent. + * + * \param datalen (unsigned int) The length of the data that is to be + * sent. + * + * \hideinitializer + */ +#define PSOCK_SEND(psock, data, datalen) \ + PT_WAIT_THREAD(&((psock)->pt), psock_send(psock, data, datalen)) + +/** + * \brief Send a null-terminated string. + * \param psock Pointer to the protosocket. + * \param str The string to be sent. + * + * This function sends a null-terminated string over the + * protosocket. + * + * \hideinitializer + */ +#define PSOCK_SEND_STR(psock, str) \ + PT_WAIT_THREAD(&((psock)->pt), psock_send(psock, str, strlen(str))) + +PT_THREAD(psock_generator_send(struct psock *psock, + unsigned short (*f) (void *), void *arg)); + +/** + * \brief Generate data with a function and send it + * \param psock Pointer to the protosocket. + * \param generator Pointer to the generator function + * \param arg Argument to the generator function + * + * This function generates data and sends it over the + * protosocket. This can be used to dynamically generate + * data for a transmission, instead of generating the data + * in a buffer beforehand. This function reduces the need for + * buffer memory. The generator function is implemented by + * the application, and a pointer to the function is given + * as an argument with the call to PSOCK_GENERATOR_SEND(). + * + * The generator function should place the generated data + * directly in the uip_appdata buffer, and return the + * length of the generated data. The generator function is + * called by the protosocket layer when the data first is + * sent, and once for every retransmission that is needed. + * + * \hideinitializer + */ +#define PSOCK_GENERATOR_SEND(psock, generator, arg) \ + PT_WAIT_THREAD(&((psock)->pt), \ + psock_generator_send(psock, generator, arg)) + +/** + * Close a protosocket. + * + * This macro closes a protosocket and can only be called from within the + * protothread in which the protosocket lives. + * + * \param psock (struct psock *) A pointer to the protosocket that is to + * be closed. + * + * \hideinitializer + */ +#define PSOCK_CLOSE(psock) uip_close() + +PT_THREAD(psock_readbuf(struct psock *psock)); +/** + * Read data until the buffer is full. + * + * This macro will block waiting for data and read the data into the + * input buffer specified with the call to PSOCK_INIT(). Data is read + * until the buffer is full.. + * + * \param psock (struct psock *) A pointer to the protosocket from which + * data should be read. + * + * \hideinitializer + */ +#define PSOCK_READBUF(psock) \ + PT_WAIT_THREAD(&((psock)->pt), psock_readbuf(psock)) + +PT_THREAD(psock_readto(struct psock *psock, unsigned char c)); +/** + * Read data up to a specified character. + * + * This macro will block waiting for data and read the data into the + * input buffer specified with the call to PSOCK_INIT(). Data is only + * read until the specifieed character appears in the data stream. + * + * \param psock (struct psock *) A pointer to the protosocket from which + * data should be read. + * + * \param c (char) The character at which to stop reading. + * + * \hideinitializer + */ +#define PSOCK_READTO(psock, c) \ + PT_WAIT_THREAD(&((psock)->pt), psock_readto(psock, c)) + +/** + * The length of the data that was previously read. + * + * This macro returns the length of the data that was previously read + * using PSOCK_READTO() or PSOCK_READ(). + * + * \param psock (struct psock *) A pointer to the protosocket holding the data. + * + * \hideinitializer + */ +#define PSOCK_DATALEN(psock) psock_datalen(psock) + +u16_t psock_datalen(struct psock *psock); + +/** + * Exit the protosocket's protothread. + * + * This macro terminates the protothread of the protosocket and should + * almost always be used in conjunction with PSOCK_CLOSE(). + * + * \sa PSOCK_CLOSE_EXIT() + * + * \param psock (struct psock *) A pointer to the protosocket. + * + * \hideinitializer + */ +#define PSOCK_EXIT(psock) PT_EXIT(&((psock)->pt)) + +/** + * Close a protosocket and exit the protosocket's protothread. + * + * This macro closes a protosocket and exits the protosocket's protothread. + * + * \param psock (struct psock *) A pointer to the protosocket. + * + * \hideinitializer + */ +#define PSOCK_CLOSE_EXIT(psock) \ + do { \ + PSOCK_CLOSE(psock); \ + PSOCK_EXIT(psock); \ + } while (0) + +/** + * Declare the end of a protosocket's protothread. + * + * This macro is used for declaring that the protosocket's protothread + * ends. It must always be used together with a matching PSOCK_BEGIN() + * macro. + * + * \param psock (struct psock *) A pointer to the protosocket. + * + * \hideinitializer + */ +#define PSOCK_END(psock) PT_END(&((psock)->pt)) + +char psock_newdata(struct psock *s); + +/** + * Check if new data has arrived on a protosocket. + * + * This macro is used in conjunction with the PSOCK_WAIT_UNTIL() + * macro to check if data has arrived on a protosocket. + * + * \param psock (struct psock *) A pointer to the protosocket. + * + * \hideinitializer + */ +#define PSOCK_NEWDATA(psock) psock_newdata(psock) + +/** + * Wait until a condition is true. + * + * This macro blocks the protothread until the specified condition is + * true. The macro PSOCK_NEWDATA() can be used to check if new data + * arrives when the protosocket is waiting. + * + * Typically, this macro is used as follows: + * + \code + PT_THREAD(thread(struct psock *s, struct timer *t)) + { + PSOCK_BEGIN(s); + + PSOCK_WAIT_UNTIL(s, PSOCK_NEWADATA(s) || timer_expired(t)); + + if(PSOCK_NEWDATA(s)) { + PSOCK_READTO(s, '\n'); + } else { + handle_timed_out(s); + } + + PSOCK_END(s); + } + \endcode + * + * \param psock (struct psock *) A pointer to the protosocket. + * \param condition The condition to wait for. + * + * \hideinitializer + */ +#define PSOCK_WAIT_UNTIL(psock, condition) \ + PT_WAIT_UNTIL(&((psock)->pt), (condition)); + +#define PSOCK_WAIT_THREAD(psock, condition) \ + PT_WAIT_THREAD(&((psock)->pt), (condition)) + +#endif /* __PSOCK_H__ */ + +/** @} */ diff --git a/iscsiuio/src/uip/pt.h b/iscsiuio/src/uip/pt.h new file mode 100644 index 0000000..ffb1d15 --- /dev/null +++ b/iscsiuio/src/uip/pt.h @@ -0,0 +1,322 @@ +/* + * Copyright (c) 2004-2005, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack + * + * Author: Adam Dunkels + * + */ + +/** + * \addtogroup pt + * @{ + */ + +/** + * \file + * Protothreads implementation. + * \author + * Adam Dunkels + * + */ + +#ifndef __PT_H__ +#define __PT_H__ + +#include "lc.h" + +struct pt { + unsigned short lc; +}; + +#define PT_WAITING 0 +#define PT_EXITED 1 +#define PT_ENDED 2 +#define PT_YIELDED 3 + +/** + * \name Initialization + * @{ + */ + +/** + * Initialize a protothread. + * + * Initializes a protothread. Initialization must be done prior to + * starting to execute the protothread. + * + * \param pt A pointer to the protothread control structure. + * + * \sa PT_SPAWN() + * + * \hideinitializer + */ +#define PT_INIT(pt) LC_INIT((pt)->lc) + +/** @} */ + +/** + * \name Declaration and definition + * @{ + */ + +/** + * Declaration of a protothread. + * + * This macro is used to declare a protothread. All protothreads must + * be declared with this macro. + * + * \param name_args The name and arguments of the C function + * implementing the protothread. + * + * \hideinitializer + */ +#define PT_THREAD(name_args) char name_args + +/** + * Declare the start of a protothread inside the C function + * implementing the protothread. + * + * This macro is used to declare the starting point of a + * protothread. It should be placed at the start of the function in + * which the protothread runs. All C statements above the PT_BEGIN() + * invokation will be executed each time the protothread is scheduled. + * + * \param pt A pointer to the protothread control structure. + * + * \hideinitializer + */ +#define PT_BEGIN(pt) { char PT_YIELD_FLAG __attribute__((__unused__)) = 1; LC_RESUME((pt)->lc) + +/** + * Declare the end of a protothread. + * + * This macro is used for declaring that a protothread ends. It must + * always be used together with a matching PT_BEGIN() macro. + * + * \param pt A pointer to the protothread control structure. + * + * \hideinitializer + */ +#define PT_END(pt) LC_END((pt)->lc); PT_YIELD_FLAG = 0; \ + PT_INIT(pt); return PT_ENDED; } + +/** @} */ + +/** + * \name Blocked wait + * @{ + */ + +/** + * Block and wait until condition is true. + * + * This macro blocks the protothread until the specified condition is + * true. + * + * \param pt A pointer to the protothread control structure. + * \param condition The condition. + * + * \hideinitializer + */ +#define PT_WAIT_UNTIL(pt, condition) \ + do { \ + LC_SET((pt)->lc); \ + if (!(condition)) { \ + return PT_WAITING; \ + } \ + } while (0) + +/** + * Block and wait while condition is true. + * + * This function blocks and waits while condition is true. See + * PT_WAIT_UNTIL(). + * + * \param pt A pointer to the protothread control structure. + * \param cond The condition. + * + * \hideinitializer + */ +#define PT_WAIT_WHILE(pt, cond) PT_WAIT_UNTIL((pt), !(cond)) + +/** @} */ + +/** + * \name Hierarchical protothreads + * @{ + */ + +/** + * Block and wait until a child protothread completes. + * + * This macro schedules a child protothread. The current protothread + * will block until the child protothread completes. + * + * \note The child protothread must be manually initialized with the + * PT_INIT() function before this function is used. + * + * \param pt A pointer to the protothread control structure. + * \param thread The child protothread with arguments + * + * \sa PT_SPAWN() + * + * \hideinitializer + */ +#define PT_WAIT_THREAD(pt, thread) PT_WAIT_WHILE((pt), PT_SCHEDULE(thread)) + +/** + * Spawn a child protothread and wait until it exits. + * + * This macro spawns a child protothread and waits until it exits. The + * macro can only be used within a protothread. + * + * \param pt A pointer to the protothread control structure. + * \param child A pointer to the child protothread's control structure. + * \param thread The child protothread with arguments + * + * \hideinitializer + */ +#define PT_SPAWN(pt, child, thread) \ + do { \ + PT_INIT((child)); \ + PT_WAIT_THREAD((pt), (thread)); \ + } while (0) + +/** @} */ + +/** + * \name Exiting and restarting + * @{ + */ + +/** + * Restart the protothread. + * + * This macro will block and cause the running protothread to restart + * its execution at the place of the PT_BEGIN() call. + * + * \param pt A pointer to the protothread control structure. + * + * \hideinitializer + */ +#define PT_RESTART(pt) \ + do { \ + PT_INIT(pt); \ + return PT_WAITING; \ + } while (0) + +/** + * Exit the protothread. + * + * This macro causes the protothread to exit. If the protothread was + * spawned by another protothread, the parent protothread will become + * unblocked and can continue to run. + * + * \param pt A pointer to the protothread control structure. + * + * \hideinitializer + */ +#define PT_EXIT(pt) \ + do { \ + PT_INIT(pt); \ + return PT_EXITED; \ + } while (0) + +/** @} */ + +/** + * \name Calling a protothread + * @{ + */ + +/** + * Schedule a protothread. + * + * This function shedules a protothread. The return value of the + * function is non-zero if the protothread is running or zero if the + * protothread has exited. + * + * \param f The call to the C function implementing the protothread to + * be scheduled + * + * \hideinitializer + */ +#define PT_SCHEDULE(f) ((f) == PT_WAITING) + +/** @} */ + +/** + * \name Yielding from a protothread + * @{ + */ + +/** + * Yield from the current protothread. + * + * This function will yield the protothread, thereby allowing other + * processing to take place in the system. + * + * \param pt A pointer to the protothread control structure. + * + * \hideinitializer + */ +#define PT_YIELD(pt) \ + do { \ + PT_YIELD_FLAG = 0; \ + LC_SET((pt)->lc); \ + if (PT_YIELD_FLAG == 0) { \ + return PT_YIELDED; \ + } \ + } while (0) + +/** + * \brief Yield from the protothread until a condition occurs. + * \param pt A pointer to the protothread control structure. + * \param cond The condition. + * + * This function will yield the protothread, until the + * specified condition evaluates to true. + * + * + * \hideinitializer + */ +#define PT_YIELD_UNTIL(pt, cond) \ + do { \ + PT_YIELD_FLAG = 0; \ + LC_SET((pt)->lc); \ + if ((PT_YIELD_FLAG == 0) || !(cond)) { \ + return PT_YIELDED; \ + } \ + } while (0) + +/** @} */ + +#endif /* __PT_H__ */ + +/** @} */ diff --git a/iscsiuio/src/uip/timer.c b/iscsiuio/src/uip/timer.c new file mode 100644 index 0000000..da77148 --- /dev/null +++ b/iscsiuio/src/uip/timer.c @@ -0,0 +1,127 @@ +/** + * \addtogroup timer + * @{ + */ + +/** + * \file + * Timer library implementation. + * \author + * Adam Dunkels + */ + +/* + * Copyright (c) 2004, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack + * + * Author: Adam Dunkels + * + */ + +#include "clock.h" +#include "timer.h" + +/*---------------------------------------------------------------------------*/ +/** + * Set a timer. + * + * This function is used to set a timer for a time sometime in the + * future. The function timer_expired() will evaluate to true after + * the timer has expired. + * + * \param t A pointer to the timer + * \param interval The interval before the timer expires. + * + */ +void timer_set(struct timer *t, clock_time_t interval) +{ + t->interval = interval; + t->start = clock_time(); +} + +/*---------------------------------------------------------------------------*/ +/** + * Reset the timer with the same interval. + * + * This function resets the timer with the same interval that was + * given to the timer_set() function. The start point of the interval + * is the exact time that the timer last expired. Therefore, this + * function will cause the timer to be stable over time, unlike the + * timer_rester() function. + * + * \param t A pointer to the timer. + * + * \sa timer_restart() + */ +void timer_reset(struct timer *t) +{ + t->start += t->interval; +} + +/*---------------------------------------------------------------------------*/ +/** + * Restart the timer from the current point in time + * + * This function restarts a timer with the same interval that was + * given to the timer_set() function. The timer will start at the + * current time. + * + * \note A periodic timer will drift if this function is used to reset + * it. For preioric timers, use the timer_reset() function instead. + * + * \param t A pointer to the timer. + * + * \sa timer_reset() + */ +void timer_restart(struct timer *t) +{ + t->start = clock_time(); +} + +/*---------------------------------------------------------------------------*/ +/** + * Check if a timer has expired. + * + * This function tests if a timer has expired and returns true or + * false depending on its status. + * + * \param t A pointer to the timer + * + * \return Non-zero if the timer has expired, zero otherwise. + * + */ +int timer_expired(struct timer *t) +{ + return (clock_time_t) (clock_time() - t->start) >= + (clock_time_t) t->interval; +} + +/*---------------------------------------------------------------------------*/ + +/** @} */ diff --git a/iscsiuio/src/uip/timer.h b/iscsiuio/src/uip/timer.h new file mode 100644 index 0000000..12739fd --- /dev/null +++ b/iscsiuio/src/uip/timer.h @@ -0,0 +1,84 @@ +/** + * \defgroup timer Timer library + * + * The timer library provides functions for setting, resetting and + * restarting timers, and for checking if a timer has expired. An + * application must "manually" check if its timers have expired; this + * is not done automatically. + * + * A timer is declared as a \c struct \c timer and all access to the + * timer is made by a pointer to the declared timer. + * + * \note The timer library uses the \ref clock "Clock library" to + * measure time. Intervals should be specified in the format used by + * the clock library. + * + * @{ + */ + +/** + * \file + * Timer library header file. + * \author + * Adam Dunkels + */ + +/* + * Copyright (c) 2004, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack + * + * Author: Adam Dunkels + * + */ +#ifndef __TIMER_H__ +#define __TIMER_H__ + +#include "clock.h" + +/** + * A timer. + * + * This structure is used for declaring a timer. The timer must be set + * with timer_set() before it can be used. + * + * \hideinitializer + */ +struct timer { + clock_time_t start; + clock_time_t interval; +}; + +void timer_set(struct timer *t, clock_time_t interval); +void timer_reset(struct timer *t); +void timer_restart(struct timer *t); +int timer_expired(struct timer *t); + +#endif /* __TIMER_H__ */ + +/** @} */ diff --git a/iscsiuio/src/uip/uip-neighbor.c b/iscsiuio/src/uip/uip-neighbor.c new file mode 100644 index 0000000..4c80c32 --- /dev/null +++ b/iscsiuio/src/uip/uip-neighbor.c @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2006, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack + * + */ + +/** + * \file + * Database of link-local neighbors, used by IPv6 code and + * to be used by a future ARP code rewrite. + * \author + * Adam Dunkels + */ + +#include "logger.h" +#include "uip.h" +#include "uip-neighbor.h" + +#include +#include +#include + +/******************************************************************************* + * Constants + ******************************************************************************/ +#define PFX "uip-neigh " + +#define MAX_TIME 128 + +/*---------------------------------------------------------------------------*/ +void uip_neighbor_init(struct uip_stack *ustack) +{ + int i; + + pthread_mutex_lock(&ustack->lock); + for (i = 0; i < UIP_NEIGHBOR_ENTRIES; ++i) { + memset(&(ustack->neighbor_entries[i].ipaddr), 0, + sizeof(ustack->neighbor_entries[i].ipaddr)); + memset(&(ustack->neighbor_entries[i].mac_addr), 0, + sizeof(ustack->neighbor_entries[i].mac_addr)); + ustack->neighbor_entries[i].time = MAX_TIME; + } + pthread_mutex_unlock(&ustack->lock); +} + +void uip_neighbor_add(struct uip_stack *ustack, + struct in6_addr *addr6, struct uip_eth_addr *addr) +{ + int i, oldest; + u8_t oldest_time; + char buf[INET6_ADDRSTRLEN]; + + inet_ntop(AF_INET6, addr6, buf, sizeof(buf)); + + pthread_mutex_lock(&ustack->lock); + + /* Find the first unused entry or the oldest used entry. */ + oldest_time = 0; + oldest = 0; + for (i = 0; i < UIP_NEIGHBOR_ENTRIES; ++i) { + if (ustack->neighbor_entries[i].time == MAX_TIME) { + oldest = i; + break; + } + if (uip_ip6addr_cmp + (ustack->neighbor_entries[i].ipaddr.s6_addr, addr6)) { + oldest = i; + break; + } + if (ustack->neighbor_entries[i].time > oldest_time) { + oldest = i; + oldest_time = ustack->neighbor_entries[i].time; + } + } + + /* Use the oldest or first free entry (either pointed to by the + "oldest" variable). */ + ustack->neighbor_entries[oldest].time = 0; + uip_ip6addr_copy(ustack->neighbor_entries[oldest].ipaddr.s6_addr, + addr6); + memcpy(&ustack->neighbor_entries[oldest].mac_addr, addr, + sizeof(struct uip_eth_addr)); + + LOG_DEBUG("Adding neighbor %s with " + "mac address %02x:%02x:%02x:%02x:%02x:%02x at %d", + buf, addr->addr[0], addr->addr[1], addr->addr[2], + addr->addr[3], addr->addr[4], addr->addr[5], oldest); + + pthread_mutex_unlock(&ustack->lock); +} + +/*---------------------------------------------------------------------------*/ +static struct neighbor_entry *find_entry(struct uip_stack *ustack, + struct in6_addr *addr6) +{ + int i; + + for (i = 0; i < UIP_NEIGHBOR_ENTRIES; ++i) { + if (uip_ip6addr_cmp + (ustack->neighbor_entries[i].ipaddr.s6_addr, + addr6->s6_addr)) { + return &ustack->neighbor_entries[i]; + } + } + + return NULL; +} + +/*---------------------------------------------------------------------------*/ +void uip_neighbor_update(struct uip_stack *ustack, struct in6_addr *addr6) +{ + struct neighbor_entry *e; + + pthread_mutex_lock(&ustack->lock); + + e = find_entry(ustack, addr6); + if (e != NULL) + e->time = 0; + + pthread_mutex_unlock(&ustack->lock); +} + +/*---------------------------------------------------------------------------*/ +int uip_neighbor_lookup(struct uip_stack *ustack, + struct in6_addr *addr6, uint8_t *mac_addr) +{ + struct neighbor_entry *e; + + pthread_mutex_lock(&ustack->lock); + e = find_entry(ustack, addr6); + if (e != NULL) { + char addr6_str[INET6_ADDRSTRLEN]; + uint8_t *entry_mac_addr; + + addr6_str[0] = '\0'; + inet_ntop(AF_INET6, addr6->s6_addr, addr6_str, + sizeof(addr6_str)); + entry_mac_addr = (uint8_t *)&e->mac_addr.addr; + + LOG_DEBUG(PFX + "Found %s at %02x:%02x:%02x:%02x:%02x:%02x", + addr6_str, + entry_mac_addr[0], entry_mac_addr[1], + entry_mac_addr[2], entry_mac_addr[3], + entry_mac_addr[4], entry_mac_addr[5]); + + memcpy(mac_addr, entry_mac_addr, sizeof(e->mac_addr)); + pthread_mutex_unlock(&ustack->lock); + return 0; + } + + pthread_mutex_unlock(&ustack->lock); + return -ENOENT; +} + +void uip_neighbor_out(struct uip_stack *ustack) +{ + struct neighbor_entry *e; + struct uip_eth_hdr *eth_hdr = + (struct uip_eth_hdr *)ustack->data_link_layer; + struct uip_ipv6_hdr *ipv6_hdr = + (struct uip_ipv6_hdr *)ustack->network_layer; + + pthread_mutex_lock(&ustack->lock); + + /* Find the destination IP address in the neighbor table and construct + the Ethernet header. If the destination IP addres isn't on the + local network, we use the default router's IP address instead. + + If not ARP table entry is found, we overwrite the original IP + packet with an ARP request for the IP address. */ + e = find_entry(ustack, (struct in6_addr *)ipv6_hdr->destipaddr); + if (e == NULL) { + struct uip_eth_addr eth_addr_tmp; + + memcpy(ð_addr_tmp, eth_hdr->src.addr, sizeof(eth_addr_tmp)); + memcpy(eth_hdr->src.addr, ustack->uip_ethaddr.addr, + sizeof(eth_hdr->src.addr)); + memcpy(eth_hdr->dest.addr, ð_addr_tmp, + sizeof(eth_hdr->dest.addr)); + + pthread_mutex_unlock(&ustack->lock); + return; + } + + memcpy(eth_hdr->dest.addr, &e->mac_addr, sizeof(eth_hdr->dest.addr)); + memcpy(eth_hdr->src.addr, ustack->uip_ethaddr.addr, + sizeof(eth_hdr->src.addr)); + + pthread_mutex_unlock(&ustack->lock); +} + +/*---------------------------------------------------------------------------*/ diff --git a/iscsiuio/src/uip/uip-neighbor.h b/iscsiuio/src/uip/uip-neighbor.h new file mode 100644 index 0000000..d10c57b --- /dev/null +++ b/iscsiuio/src/uip/uip-neighbor.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2006, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack + * + */ + +/** + * \file + * Header file for database of link-local neighbors, used by + * IPv6 code and to be used by future ARP code. + * \author + * Adam Dunkels + */ + +#ifndef __UIP_NEIGHBOR_H__ +#define __UIP_NEIGHBOR_H__ + +#include "uip.h" +#include "uip_eth.h" + +/* ICMP types */ +/* ICMPv6 error Messages */ +#define ICMPV6_DEST_UNREACH 1 +#define ICMPV6_PKT_TOOBIG 2 +#define ICMPV6_TIME_EXCEED 3 +#define ICMPV6_PARAMPROB 4 + +/* ICMPv6 Informational Messages */ +#define ICMPV6_ECHO_REQUEST 128 +#define ICMPV6_ECHO_REPLY 129 +#define ICMPV6_MGM_QUERY 130 +#define ICMPV6_MGM_REPORT 131 +#define ICMPV6_MGM_REDUCTION 132 + +/* Codes for Destination Unreachable */ +#define ICMPV6_NOROUTE 0 +#define ICMPV6_ADM_PROHIBITED 1 +#define ICMPV6_NOT_NEIGHBOUR 2 +#define ICMPV6_ADDR_UNREACH 3 +#define ICMPV6_PORT_UNREACH 4 + +/* Codes for Time Exceeded */ +#define ICMPV6_EXC_HOPLIMIT 0 +#define ICMPV6_EXC_FRAGTIME 1 + +/* Codes for Parameter Problem */ +#define ICMPV6_HDR_FIELD 0 +#define ICMPV6_UNK_NEXTHDR 1 +#define ICMPV6_UNK_OPTION 2 + +#if 0 +struct __attribute__ ((__packed__)) icmpv6_hdr { + u8_t type; + u8_t code; + u16_t checksum; + union { + struct { + u16_t id; + u16_t sequence; + } echo; + u32_t gateway; + struct { + u16_t unused; + u16_t mtu; + } frag; + } un; +}; +#endif + +void uip_neighbor_init(struct uip_stack *ustack); +void uip_neighbor_add(struct uip_stack *ustack, + struct in6_addr *addr6, struct uip_eth_addr *addr); +void uip_neighbor_update(struct uip_stack *ustack, struct in6_addr *addr6); +int uip_neighbor_lookup(struct uip_stack *ustack, struct in6_addr *ipaddr, + uint8_t *mac_addr); +void uip_neighbor_periodic(void); +void uip_neighbor_out(struct uip_stack *ustack); + +#endif /* __UIP-NEIGHBOR_H__ */ diff --git a/iscsiuio/src/uip/uip.c b/iscsiuio/src/uip/uip.c new file mode 100644 index 0000000..ec3d6ce --- /dev/null +++ b/iscsiuio/src/uip/uip.c @@ -0,0 +1,2405 @@ +#include +#include +#include +#include +#include +#include "uip.h" +#include "dhcpc.h" +#include "ipv6_ndpc.h" +#include "brcm_iscsi.h" + +/** + * \defgroup uip The uIP TCP/IP stack + * @{ + * + * uIP is an implementation of the TCP/IP protocol stack intended for + * small 8-bit and 16-bit microcontrollers. + * + * uIP provides the necessary protocols for Internet communication, + * with a very small code footprint and RAM requirements - the uIP + * code size is on the order of a few kilobytes and RAM usage is on + * the order of a few hundred bytes. + */ + +/** + * \file + * The uIP TCP/IP stack code. + * \author Adam Dunkels + */ + +/* + * Copyright (c) 2001-2003, Adam Dunkels. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack. + * + * + */ + +/* + * uIP is a small implementation of the IP, UDP and TCP protocols (as + * well as some basic ICMP stuff). The implementation couples the IP, + * UDP, TCP and the application layers very tightly. To keep the size + * of the compiled code down, this code frequently uses the goto + * statement. While it would be possible to break the uip_process() + * function into many smaller functions, this would increase the code + * size because of the overhead of parameter passing and the fact that + * the optimier would not be as efficient. + * + * The principle is that we have a small buffer, called the uip_buf, + * in which the device driver puts an incoming packet. The TCP/IP + * stack parses the headers in the packet, and calls the + * application. If the remote host has sent data to the application, + * this data is present in the uip_buf and the application read the + * data from there. It is up to the application to put this data into + * a byte stream if needed. The application will not be fed with data + * that is out of sequence. + * + * If the application whishes to send data to the peer, it should put + * its data into the uip_buf. The uip_appdata pointer points to the + * first available byte. The TCP/IP stack will calculate the + * checksums, and fill in the necessary header fields and finally send + * the packet back to the peer. +*/ + +#include "logger.h" + +#include "uip.h" +#include "uipopt.h" +#include "uip_arch.h" +#include "uip_eth.h" +#include "uip-neighbor.h" + +#include + +/******************************************************************************* + * Constants + ******************************************************************************/ +#define PFX "uip " + +static const uip_ip6addr_t all_ones_addr6 = { + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff +}; +static const uip_ip4addr_t all_ones_addr4 = { 0xffff, 0xffff }; + +const uip_ip6addr_t all_zeroes_addr6 = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 +}; +const uip_ip4addr_t all_zeroes_addr4 = { 0x0000, 0x0000 }; + +const uint8_t mutlicast_ipv6_prefix[16] = { + 0xfc, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +const uint8_t link_local_addres_prefix[16] = { + 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +const uint32_t link_local_address_prefix_length = 10; + +/* Structures and definitions. */ +#define TCP_FIN 0x01 +#define TCP_SYN 0x02 +#define TCP_RST 0x04 +#define TCP_PSH 0x08 +#define TCP_ACK 0x10 +#define TCP_URG 0x20 +#define TCP_CTL 0x3f + +#define TCP_OPT_END 0 /* End of TCP options list */ +#define TCP_OPT_NOOP 1 /* "No-operation" TCP option */ +#define TCP_OPT_MSS 2 /* Maximum segment size TCP option */ + +#define TCP_OPT_MSS_LEN 4 /* Length of TCP MSS option. */ + +#define ICMP_ECHO_REPLY 0 +#define ICMP_ECHO 8 + +#define ICMP6_ECHO_REPLY 129 +#define ICMP6_ECHO 128 +#define ICMP6_NEIGHBOR_SOLICITATION 135 +#define ICMP6_NEIGHBOR_ADVERTISEMENT 136 + +#define ICMP6_FLAG_S (1 << 6) + +#define ICMP6_OPTION_SOURCE_LINK_ADDRESS 1 +#define ICMP6_OPTION_TARGET_LINK_ADDRESS 2 + +/* Macros. */ +#define FBUF ((struct uip_tcpip_hdr *)&uip_reassbuf[0]) +#define UDPBUF(ustack) ((struct uip_udpip_hdr *)ustack->network_layer) + +/****************************************************************************** + * Utility Functions + *****************************************************************************/ +static int is_ipv6(struct uip_stack *ustack) +{ + u16_t type; + + type = ETH_BUF(ustack->uip_buf)->type; + type = ntohs(type); + if (type == UIP_ETHTYPE_8021Q) + type = ntohs(VLAN_ETH_BUF(ustack->uip_buf)->type); + else + type = ntohs(ETH_BUF(ustack->uip_buf)->type); + + return (type == UIP_ETHTYPE_IPv6); +} + +int is_ipv6_link_local_address(uip_ip6addr_t *addr) +{ + u8_t *test_adddr = (u8_t *) addr; + u8_t test_remainder; + + if (test_adddr[0] != link_local_addres_prefix[0]) + return 0; + + test_remainder = (test_adddr[1] & 0xC0) >> 6; + if (test_remainder != 2) + return 0; + + return 1; +} + +void uip_sethostaddr4(struct uip_stack *ustack, uip_ip4addr_t *addr) +{ + pthread_mutex_lock(&ustack->lock); + uip_ip4addr_copy(ustack->hostaddr, (addr)); + pthread_mutex_unlock(&ustack->lock); +} + +void uip_setdraddr4(struct uip_stack *ustack, uip_ip4addr_t *addr) +{ + pthread_mutex_lock(&ustack->lock); + uip_ip4addr_copy(ustack->default_route_addr, (addr)); + pthread_mutex_unlock(&ustack->lock); +} + +void uip_setnetmask4(struct uip_stack *ustack, uip_ip4addr_t *addr) +{ + pthread_mutex_lock(&ustack->lock); + uip_ip4addr_copy(ustack->netmask, (addr)); + pthread_mutex_unlock(&ustack->lock); +} + +void uip_setethernetmac(struct uip_stack *ustack, uint8_t *mac) +{ + pthread_mutex_lock(&ustack->lock); + memcpy(ustack->uip_ethaddr.addr, (mac), 6); + pthread_mutex_unlock(&ustack->lock); +} + +void set_uip_stack(struct uip_stack *ustack, + uip_ip4addr_t *ip, + uip_ip4addr_t *netmask, + uip_ip4addr_t *default_route, uint8_t *mac_addr) +{ + uip_sethostaddr4(ustack, ip); + uip_setnetmask4(ustack, netmask); + uip_setdraddr4(ustack, default_route); + uip_setethernetmac(ustack, mac_addr); +} + +#if !UIP_ARCH_ADD32 +void uip_add32(u8_t *op32, u16_t op16, u8_t *uip_acc32) +{ + uip_acc32[3] = op32[3] + (op16 & 0xff); + uip_acc32[2] = op32[2] + (op16 >> 8); + uip_acc32[1] = op32[1]; + uip_acc32[0] = op32[0]; + + if (uip_acc32[2] < (op16 >> 8)) { + ++uip_acc32[1]; + if (uip_acc32[1] == 0) + ++uip_acc32[0]; + } + + if (uip_acc32[3] < (op16 & 0xff)) { + ++uip_acc32[2]; + if (uip_acc32[2] == 0) { + ++uip_acc32[1]; + if (uip_acc32[1] == 0) + ++uip_acc32[0]; + } + } +} + +#endif /* UIP_ARCH_ADD32 */ + +#if !UIP_ARCH_CHKSUM +/*---------------------------------------------------------------------------*/ +static u16_t chksum(u16_t sum, const u8_t *data, u16_t len) +{ + u16_t t; + const u8_t *dataptr; + const u8_t *last_byte; + + dataptr = data; + last_byte = data + len - 1; + + while (dataptr < last_byte) { /* At least two more bytes */ + t = (dataptr[0] << 8) + dataptr[1]; + sum += t; + if (sum < t) + sum++; /* carry */ + dataptr += 2; + } + + if (dataptr == last_byte) { + t = (dataptr[0] << 8) + 0; + sum += t; + if (sum < t) + sum++; /* carry */ + } + + /* Return sum in host byte order. */ + return sum; +} + +/*---------------------------------------------------------------------------*/ +u16_t uip_chksum(u16_t *data, u16_t len) +{ + return htons(chksum(0, (u8_t *)data, len)); +} + +/*---------------------------------------------------------------------------*/ +#ifndef UIP_ARCH_IPCHKSUM +u16_t uip_ipchksum(struct uip_stack *ustack) +{ + u16_t sum; + u16_t uip_iph_len; + + if (is_ipv6(ustack)) + uip_iph_len = UIP_IPv6_H_LEN; + else + uip_iph_len = UIP_IPv4_H_LEN; + + sum = chksum(0, ustack->network_layer, uip_iph_len); + return (sum == 0) ? 0xffff : htons(sum); +} +#endif + +/*---------------------------------------------------------------------------*/ +static u16_t upper_layer_chksum_ipv4(struct uip_stack *ustack, u8_t proto) +{ + u16_t upper_layer_len; + u16_t sum; + struct uip_tcp_ipv4_hdr *tcp_ipv4_hdr = NULL; + + tcp_ipv4_hdr = (struct uip_tcp_ipv4_hdr *)ustack->network_layer; + + upper_layer_len = (((u16_t) (tcp_ipv4_hdr->len[0]) << 8) + + tcp_ipv4_hdr->len[1]) - UIP_IPv4_H_LEN; + + /* First sum pseudoheader. */ + /* IP protocol and length fields. This addition cannot carry. */ + sum = upper_layer_len + proto; + + sum = + chksum(sum, (u8_t *)&tcp_ipv4_hdr->srcipaddr[0], + 2 * sizeof(uip_ip4addr_t)); + /* Sum TCP header and data. */ + sum = chksum(sum, ustack->network_layer + UIP_IPv4_H_LEN, + upper_layer_len); + + return (sum == 0) ? 0xffff : htons(sum); +} + +/*---------------------------------------------------------------------------*/ +static uint16_t upper_layer_checksum_ipv6(uint8_t *data, uint8_t proto) +{ + uint16_t upper_layer_len; + uint16_t sum; + struct ip6_hdr *ipv6_hdr; + uint8_t *upper_layer; + uint32_t val; + + ipv6_hdr = (struct ip6_hdr *)data; + + upper_layer_len = ntohs(ipv6_hdr->ip6_plen); + + /* First sum pseudoheader. */ + sum = 0; + sum = chksum(sum, (const u8_t *)ipv6_hdr->ip6_src.s6_addr, + sizeof(ipv6_hdr->ip6_src)); + sum = chksum(sum, (const u8_t *)ipv6_hdr->ip6_dst.s6_addr, + sizeof(ipv6_hdr->ip6_dst)); + + val = htons(upper_layer_len); + sum = chksum(sum, (u8_t *)&val, sizeof(val)); + + val = htons(proto); + sum = chksum(sum, (u8_t *)&val, sizeof(val)); + + upper_layer = (uint8_t *)(ipv6_hdr + 1); + sum = chksum(sum, upper_layer, upper_layer_len); + + return (sum == 0) ? 0xffff : htons(sum); +} + +/*---------------------------------------------------------------------------*/ + +u16_t uip_icmp6chksum(struct uip_stack *ustack) +{ + uint8_t *data = ustack->network_layer; + + return upper_layer_checksum_ipv6(data, UIP_PROTO_ICMP6); +} + +uint16_t icmpv6_checksum(uint8_t *data) +{ + return upper_layer_checksum_ipv6(data, IPPROTO_ICMPV6); +} + +/*---------------------------------------------------------------------------*/ +u16_t uip_tcpchksum(struct uip_stack *ustack) +{ + return upper_layer_chksum_ipv4(ustack, UIP_PROTO_TCP); +} + +/*---------------------------------------------------------------------------*/ +#if UIP_UDP_CHECKSUMS +static u16_t uip_udpchksum_ipv4(struct uip_stack *ustack) +{ + return upper_layer_chksum_ipv4(ustack, UIP_PROTO_UDP); +} + +static u16_t uip_udpchksum_ipv6(struct uip_stack *ustack) +{ + uint8_t *data = ustack->network_layer; + + return upper_layer_checksum_ipv6(data, UIP_PROTO_UDP); +} + +u16_t uip_udpchksum(struct uip_stack *ustack) +{ + if (is_ipv6(ustack)) + return uip_udpchksum_ipv6(ustack); + else + return uip_udpchksum_ipv4(ustack); +} +#endif /* UIP_UDP_CHECKSUMS */ +#endif /* UIP_ARCH_CHKSUM */ +/*---------------------------------------------------------------------------*/ +void uip_init(struct uip_stack *ustack, uint8_t ipv6_enabled) +{ + u8_t c; + + for (c = 0; c < UIP_LISTENPORTS; ++c) + ustack->uip_listenports[c] = 0; + for (c = 0; c < UIP_CONNS; ++c) + ustack->uip_conns[c].tcpstateflags = UIP_CLOSED; +#if UIP_ACTIVE_OPEN + ustack->lastport = 1024; +#endif /* UIP_ACTIVE_OPEN */ + +#if UIP_UDP + for (c = 0; c < UIP_UDP_CONNS; ++c) + ustack->uip_udp_conns[c].lport = 0; +#endif /* UIP_UDP */ + + /* IPv4 initialization. */ +#if UIP_FIXEDADDR == 0 + /* uip_hostaddr[0] = uip_hostaddr[1] = 0; */ +#endif /* UIP_FIXEDADDR */ + + /* zero out the uIP statistics */ + memset(&ustack->stats, 0, sizeof(ustack->stats)); + + /* prepare the uIP lock */ + pthread_mutex_init(&ustack->lock, NULL); + + if (ipv6_enabled) + ustack->enable_IPv6 = UIP_SUPPORT_IPv6_ENABLED; + else + ustack->enable_IPv6 = UIP_SUPPORT_IPv6_DISABLED; + + ustack->dhcpc = NULL; + ustack->ndpc = NULL; +} +void uip_reset(struct uip_stack *ustack) +{ + /* There was an associated DHCP object, this memory needs to be + * freed */ + if (ustack->dhcpc) + free(ustack->dhcpc); + + ndpc_exit(ustack->ndpc); + + memset(ustack, 0, sizeof(*ustack)); +} + +/*---------------------------------------------------------------------------*/ +#if UIP_ACTIVE_OPEN +struct uip_conn *uip_connect(struct uip_stack *ustack, uip_ip4addr_t *ripaddr, + u16_t rport) +{ + u8_t c; + register struct uip_conn *conn, *cconn; + + /* Find an unused local port. */ +again: + ++ustack->lastport; + + if (ustack->lastport >= 32000) + ustack->lastport = 4096; + + /* Check if this port is already in use, and if so try to find + another one. */ + for (c = 0; c < UIP_CONNS; ++c) { + conn = &ustack->uip_conns[c]; + if (conn->tcpstateflags != UIP_CLOSED && + conn->lport == htons(ustack->lastport)) { + goto again; + } + } + + conn = 0; + for (c = 0; c < UIP_CONNS; ++c) { + cconn = &ustack->uip_conns[c]; + if (cconn->tcpstateflags == UIP_CLOSED) { + conn = cconn; + break; + } + if (cconn->tcpstateflags == UIP_TIME_WAIT) { + if (conn == 0 || cconn->timer > conn->timer) + conn = cconn; + } + } + + if (conn == 0) + return 0; + + conn->tcpstateflags = UIP_SYN_SENT; + + conn->snd_nxt[0] = ustack->iss[0]; + conn->snd_nxt[1] = ustack->iss[1]; + conn->snd_nxt[2] = ustack->iss[2]; + conn->snd_nxt[3] = ustack->iss[3]; + + conn->initialmss = conn->mss = UIP_TCP_MSS; + + conn->len = 1; /* TCP length of the SYN is one. */ + conn->nrtx = 0; + conn->timer = 1; /* Send the SYN next time around. */ + conn->rto = UIP_RTO; + conn->sa = 0; + conn->sv = 16; /* Initial value of the RTT variance. */ + conn->lport = htons(ustack->lastport); + conn->rport = rport; + uip_ip4addr_copy(&conn->ripaddr, ripaddr); + + return conn; +} +#endif /* UIP_ACTIVE_OPEN */ +/*---------------------------------------------------------------------------*/ +#if UIP_UDP +struct uip_udp_conn *uip_udp_new(struct uip_stack *ustack, + uip_ip4addr_t *ripaddr, u16_t rport) +{ + u8_t c; + register struct uip_udp_conn *conn; + + /* Find an unused local port. */ +again: + ++ustack->lastport; + + if (ustack->lastport >= 32000) + ustack->lastport = 4096; + + for (c = 0; c < UIP_UDP_CONNS; ++c) { + if (ustack->uip_udp_conns[c].lport == htons(ustack->lastport)) + goto again; + } + + conn = 0; + for (c = 0; c < UIP_UDP_CONNS; ++c) { + if (ustack->uip_udp_conns[c].lport == 0) { + conn = &ustack->uip_udp_conns[c]; + break; + } + } + + if (conn == 0) + return 0; + + conn->lport = htons(ustack->lastport); + conn->rport = rport; + if (ripaddr == NULL) + memset(conn->ripaddr, 0, sizeof(uip_ip4addr_t)); + else + uip_ip4addr_copy(&conn->ripaddr, ripaddr); + conn->ttl = UIP_TTL; + + return conn; +} +#endif /* UIP_UDP */ +/*---------------------------------------------------------------------------*/ +void uip_unlisten(struct uip_stack *ustack, u16_t port) +{ + u8_t c; + + for (c = 0; c < UIP_LISTENPORTS; ++c) { + if (ustack->uip_listenports[c] == port) { + ustack->uip_listenports[c] = 0; + return; + } + } +} + +/*---------------------------------------------------------------------------*/ +void uip_listen(struct uip_stack *ustack, u16_t port) +{ + u8_t c; + + for (c = 0; c < UIP_LISTENPORTS; ++c) { + if (ustack->uip_listenports[c] == 0) { + ustack->uip_listenports[c] = port; + return; + } + } +} + +/** + * Is new incoming data available? + * + * Will reduce to non-zero if there is new data for the application + * present at the uip_appdata pointer. The size of the data is + * avaliable through the uip_len variable. + * + * \hideinitializer + */ +int uip_newdata(struct uip_stack *ustack) +{ + return ustack->uip_flags & UIP_NEWDATA; +} + +/** + * Has previously sent data been acknowledged? + * + * Will reduce to non-zero if the previously sent data has been + * acknowledged by the remote host. This means that the application + * can send new data. + * + * \hideinitializer + */ +#define uip_acked() (uip_flags & UIP_ACKDATA) + +/** + * Has the connection just been connected? + * + * Reduces to non-zero if the current connection has been connected to + * a remote host. This will happen both if the connection has been + * actively opened (with uip_connect()) or passively opened (with + * uip_listen()). + * + * \hideinitializer + */ +int uip_connected(struct uip_stack *ustack) +{ + return ustack->uip_flags & UIP_CONNECTED; +} + +/** + * Has the connection been closed by the other end? + * + * Is non-zero if the connection has been closed by the remote + * host. The application may then do the necessary clean-ups. + * + * \hideinitializer + */ +int uip_closed(struct uip_stack *ustack) +{ + return ustack->uip_flags & UIP_CLOSE; +} + +/** + * Has the connection been aborted by the other end? + * + * Non-zero if the current connection has been aborted (reset) by the + * remote host. + * + * \hideinitializer + */ +int uip_aborted(struct uip_stack *ustack) +{ + return ustack->uip_flags & UIP_ABORT; +} + +/** + * Has the connection timed out? + * + * Non-zero if the current connection has been aborted due to too many + * retransmissions. + * + * \hideinitializer + */ +int uip_timedout(struct uip_stack *ustack) +{ + return ustack->uip_flags & UIP_TIMEDOUT; +} + +/** + * Do we need to retransmit previously data? + * + * Reduces to non-zero if the previously sent data has been lost in + * the network, and the application should retransmit it. The + * application should send the exact same data as it did the last + * time, using the uip_send() function. + * + * \hideinitializer + */ +int uip_rexmit(struct uip_stack *ustack) +{ + return ustack->uip_flags & UIP_REXMIT; +} + +/** + * Is the connection being polled by uIP? + * + * Is non-zero if the reason the application is invoked is that the + * current connection has been idle for a while and should be + * polled. + * + * The polling event can be used for sending data without having to + * wait for the remote host to send data. + * + * \hideinitializer + */ +int uip_poll(struct uip_stack *ustack) +{ + return ustack->uip_flags & UIP_POLL; +} + +int uip_initialmss(struct uip_stack *ustack) +{ + return ustack->uip_conn->initialmss; +} + +int uip_mss(struct uip_stack *ustack) +{ + return ustack->uip_conn->mss; +} + +/*---------------------------------------------------------------------------*/ +/* XXX: IP fragment reassembly: not well-tested. */ + +#if UIP_REASSEMBLY && !UIP_CONF_IPV6 +#define UIP_REASS_BUFSIZE (UIP_BUFSIZE - UIP_LLH_LEN) +static u8_t uip_reassbuf[UIP_REASS_BUFSIZE]; +static u8_t uip_reassbitmap[UIP_REASS_BUFSIZE / (8 * 8)]; +static const u8_t bitmap_bits[8] = { 0xff, 0x7f, 0x3f, 0x1f, + 0x0f, 0x07, 0x03, 0x01 +}; +static u16_t uip_reasslen; +static u8_t uip_reassflags; +#define UIP_REASS_FLAG_LASTFRAG 0x01 +static u8_t uip_reasstmr; + +#define IP_MF 0x20 + +static u8_t uip_reass(void) +{ + u16_t offset, len; + u16_t i; + + /* If ip_reasstmr is zero, no packet is present in the buffer, so we + write the IP header of the fragment into the reassembly + buffer. The timer is updated with the maximum age. */ + if (uip_reasstmr == 0) { + memcpy(uip_reassbuf, &BUF(ustack)->vhl, uip_iph_len); + uip_reasstmr = UIP_REASS_MAXAGE; + uip_reassflags = 0; + /* Clear the bitmap. */ + memset(uip_reassbitmap, 0, sizeof(uip_reassbitmap)); + } + + /* Check if the incoming fragment matches the one currently present + in the reasembly buffer. If so, we proceed with copying the + fragment into the buffer. */ + if (BUF(ustack)->srcipaddr[0] == FBUF(ustack)->srcipaddr[0] && + BUF(ustack)->srcipaddr[1] == FBUF(ustack)->srcipaddr[1] && + BUF(ustack)->destipaddr[0] == FBUF(ustack)->destipaddr[0] && + BUF(ustack)->destipaddr[1] == FBUF(ustack)->destipaddr[1] && + BUF(ustack)->ipid[0] == FBUF(ustack)->ipid[0] && + BUF(ustack)->ipid[1] == FBUF(ustack)->ipid[1]) { + + len = + (BUF(ustack)->len[0] << 8) + BUF(ustack)->len[1] - + (BUF(ustack)->vhl & 0x0f) * 4; + offset = + (((BUF(ustack)->ipoffset[0] & 0x3f) << 8) + + BUF(ustack)->ipoffset[1]) * 8; + + /* If the offset or the offset + fragment length overflows the + reassembly buffer, we discard the entire packet. */ + if (offset > UIP_REASS_BUFSIZE || + offset + len > UIP_REASS_BUFSIZE) { + uip_reasstmr = 0; + goto nullreturn; + } + + /* Copy the fragment into the reassembly buffer, at the right + offset. */ + memcpy(&uip_reassbuf[uip_iph_len + offset], + (char *)BUF + (int)((BUF(ustack)->vhl & 0x0f) * 4), len); + + /* Update the bitmap. */ + if (offset / (8 * 8) == (offset + len) / (8 * 8)) { + /* If the two endpoints are in the same byte, we only + update that byte. */ + + uip_reassbitmap[offset / (8 * 8)] |= + bitmap_bits[(offset / 8) & 7] & + ~bitmap_bits[((offset + len) / 8) & 7]; + } else { + /* If the two endpoints are in different bytes, we + update the bytes in the endpoints and fill the + stuff inbetween with 0xff. */ + uip_reassbitmap[offset / (8 * 8)] |= + bitmap_bits[(offset / 8) & 7]; + for (i = 1 + offset / (8 * 8); + i < (offset + len) / (8 * 8); ++i) { + uip_reassbitmap[i] = 0xff; + } + uip_reassbitmap[(offset + len) / (8 * 8)] |= + ~bitmap_bits[((offset + len) / 8) & 7]; + } + + /* If this fragment has the More Fragments flag set to zero, we + know that this is the last fragment, so we can calculate the + size of the entire packet. We also set the + IP_REASS_FLAG_LASTFRAG flag to indicate that we have received + the final fragment. */ + + if ((BUF(ustack)->ipoffset[0] & IP_MF) == 0) { + uip_reassflags |= UIP_REASS_FLAG_LASTFRAG; + uip_reasslen = offset + len; + } + + /* Finally, we check if we have a full packet in the buffer. + We do this by checking if we have the last fragment and if + all bits in the bitmap are set. */ + if (uip_reassflags & UIP_REASS_FLAG_LASTFRAG) { + /* Check all bytes up to and including all but the last + byte in the bitmap. */ + for (i = 0; i < uip_reasslen / (8 * 8) - 1; ++i) { + if (uip_reassbitmap[i] != 0xff) + goto nullreturn; + } + /* Check the last byte in the bitmap. It should contain + just the right amount of bits. */ + if (uip_reassbitmap[uip_reasslen / (8 * 8)] != + (u8_t) ~bitmap_bits[uip_reasslen / 8 & 7]) + goto nullreturn; + + /* If we have come this far, we have a full packet in + the buffer, so we allocate a pbuf and copy the + packet into it. We also reset the timer. */ + uip_reasstmr = 0; + memcpy(BUF, FBUF, uip_reasslen); + + /* Pretend to be a "normal" (i.e., not fragmented) IP + packet from now on. */ + BUF(ustack)->ipoffset[0] = BUF(ustack)->ipoffset[1] = 0; + BUF(ustack)->len[0] = uip_reasslen >> 8; + BUF(ustack)->len[1] = uip_reasslen & 0xff; + BUF(ustack)->ipchksum = 0; + BUF(ustack)->ipchksum = ~(uip_ipchksum()); + + return uip_reasslen; + } + } + +nullreturn: + return 0; +} +#endif /* UIP_REASSEMBLY */ +/*---------------------------------------------------------------------------*/ +static void uip_add_rcv_nxt(struct uip_stack *ustack, u16_t n) +{ + u8_t uip_acc32[4]; + + uip_add32(ustack->uip_conn->rcv_nxt, n, uip_acc32); + ustack->uip_conn->rcv_nxt[0] = uip_acc32[0]; + ustack->uip_conn->rcv_nxt[1] = uip_acc32[1]; + ustack->uip_conn->rcv_nxt[2] = uip_acc32[2]; + ustack->uip_conn->rcv_nxt[3] = uip_acc32[3]; +} + +/*---------------------------------------------------------------------------*/ + +/** @} */ + +/** + * \defgroup uipdevfunc uIP device driver functions + * @{ + * + * These functions are used by a network device driver for interacting + * with uIP. + */ + +/** + * Process an incoming packet. + * + * This function should be called when the device driver has received + * a packet from the network. The packet from the device driver must + * be present in the uip_buf buffer, and the length of the packet + * should be placed in the uip_len variable. + * + * When the function returns, there may be an outbound packet placed + * in the uip_buf packet buffer. If so, the uip_len variable is set to + * the length of the packet. If no packet is to be sent out, the + * uip_len variable is set to 0. + * + * The usual way of calling the function is presented by the source + * code below. + \code + uip_len = devicedriver_poll(); + if(uip_len > 0) { + uip_input(); + if(uip_len > 0) { + devicedriver_send(); + } + } + \endcode + * + * \note If you are writing a uIP device driver that needs ARP + * (Address Resolution Protocol), e.g., when running uIP over + * Ethernet, you will need to call the uIP ARP code before calling + * this function: + \code + #define BUF ((struct uip_eth_hdr *)&uip_buf[0]) + uip_len = ethernet_devicedrver_poll(); + if(uip_len > 0) { + if (BUF(ustack)->type == HTONS(UIP_ETHTYPE_IP)) { + uip_arp_ipin(); + uip_input(); + if (uip_len > 0) { + uip_arp_out(); + ethernet_devicedriver_send(); + } + } else if (BUF(ustack)->type == HTONS(UIP_ETHTYPE_ARP)) { + uip_arp_arpin(); + if (uip_len > 0) + ethernet_devicedriver_send(); + } + \endcode + * + * \hideinitializer + */ +void uip_input(struct uip_stack *ustack) +{ + uip_process(ustack, UIP_DATA); +} + +/** + * Periodic processing for a connection identified by its number. + * + * This function does the necessary periodic processing (timers, + * polling) for a uIP TCP conneciton, and should be called when the + * periodic uIP timer goes off. It should be called for every + * connection, regardless of whether they are open of closed. + * + * When the function returns, it may have an outbound packet waiting + * for service in the uIP packet buffer, and if so the uip_len + * variable is set to a value larger than zero. The device driver + * should be called to send out the packet. + * + * The ususal way of calling the function is through a for() loop like + * this: + \code + for(i = 0; i < UIP_CONNS; ++i) { + uip_periodic(i); + if(uip_len > 0) { + devicedriver_send(); + } + } + \endcode + * + * \note If you are writing a uIP device driver that needs ARP + * (Address Resolution Protocol), e.g., when running uIP over + * Ethernet, you will need to call the uip_arp_out() function before + * calling the device driver: + \code + for(i = 0; i < UIP_CONNS; ++i) { + uip_periodic(i); + if(uip_len > 0) { + uip_arp_out(); + ethernet_devicedriver_send(); + } + } + \endcode + * + * \param conn The number of the connection which is to be periodically polled. + * + * \hideinitializer + */ +void uip_periodic(struct uip_stack *ustack, int conn) +{ + ustack->uip_conn = &ustack->uip_conns[conn]; + uip_process(ustack, UIP_TIMER); +} + +#if UIP_UDP +/** + * Periodic processing for a UDP connection identified by its number. + * + * This function is essentially the same as uip_periodic(), but for + * UDP connections. It is called in a similar fashion as the + * uip_periodic() function: + \code + for(i = 0; i < UIP_UDP_CONNS; i++) { + uip_udp_periodic(i); + if(uip_len > 0) { + devicedriver_send(); + } + } + \endcode + * + * \note As for the uip_periodic() function, special care has to be + * taken when using uIP together with ARP and Ethernet: + \code + for(i = 0; i < UIP_UDP_CONNS; i++) { + uip_udp_periodic(i); + if(uip_len > 0) { + uip_arp_out(); + ethernet_devicedriver_send(); + } + } + \endcode + * + * \param conn The number of the UDP connection to be processed. + * + * \hideinitializer + */ +void uip_udp_periodic(struct uip_stack *ustack, int conn) +{ + ustack->uip_udp_conn = &ustack->uip_udp_conns[conn]; + uip_process(ustack, UIP_UDP_TIMER); +} +#endif + +void uip_ndp_periodic(struct uip_stack *ustack) +{ + uip_process(ustack, UIP_NDP_TIMER); +} + +void uip_process(struct uip_stack *ustack, u8_t flag) +{ + u8_t c; + u16_t tmp16; + register struct uip_conn *uip_connr = ustack->uip_conn; + + u16_t uip_iph_len = 0; + u16_t uip_ip_udph_len = 0; + u16_t uip_ip_tcph_len = 0; + struct ip6_hdr *ipv6_hdr = NULL; + struct uip_tcp_ipv4_hdr *tcp_ipv4_hdr = NULL; + struct uip_tcp_hdr *tcp_hdr = NULL; + struct uip_icmpv4_hdr *icmpv4_hdr = NULL; + struct uip_icmpv6_hdr *icmpv6_hdr __attribute__((__unused__)) = NULL; + struct uip_udp_hdr *udp_hdr = NULL; + + /* Drop invalid packets */ + if (ustack->uip_buf == NULL) { + LOG_ERR(PFX "ustack->uip_buf == NULL."); + return; + } + + if (is_ipv6(ustack)) { + uint8_t *buf; + uip_iph_len = UIP_IPv6_H_LEN; + uip_ip_udph_len = UIP_IPv6_UDPH_LEN; + uip_ip_tcph_len = UIP_IPv6_TCPH_LEN; + + ipv6_hdr = (struct ip6_hdr *)ustack->network_layer; + + buf = ustack->network_layer; + buf += sizeof(struct uip_ipv6_hdr); + tcp_hdr = (struct uip_tcp_hdr *)buf; + + buf = ustack->network_layer; + buf += sizeof(struct uip_ipv6_hdr); + udp_hdr = (struct uip_udp_hdr *)buf; + + buf = ustack->network_layer; + buf += sizeof(struct uip_ipv6_hdr); + icmpv6_hdr = (struct uip_icmpv6_hdr *)buf; + } else { + uint8_t *buf; + + uip_iph_len = UIP_IPv4_H_LEN; + uip_ip_udph_len = UIP_IPv4_UDPH_LEN; + uip_ip_tcph_len = UIP_IPv4_TCPH_LEN; + + tcp_ipv4_hdr = (struct uip_tcp_ipv4_hdr *)ustack->network_layer; + + buf = ustack->network_layer; + buf += sizeof(struct uip_ipv4_hdr); + tcp_hdr = (struct uip_tcp_hdr *)buf; + + buf = ustack->network_layer; + buf += sizeof(struct uip_ipv4_hdr); + icmpv4_hdr = (struct uip_icmpv4_hdr *)buf; + + buf = ustack->network_layer; + buf += sizeof(struct uip_ipv4_hdr); + udp_hdr = (struct uip_udp_hdr *)buf; + } /* End of ipv6 */ + +#if UIP_UDP + if (flag == UIP_UDP_SEND_CONN) + goto udp_send; +#endif /* UIP_UDP */ + ustack->uip_sappdata = ustack->uip_appdata = ustack->network_layer + + uip_ip_tcph_len; + + /* Check if we were invoked because of a poll request for a + particular connection. */ + if (flag == UIP_POLL_REQUEST) { + if ((uip_connr->tcpstateflags & UIP_TS_MASK) == UIP_ESTABLISHED + && !uip_outstanding(uip_connr)) { + ustack->uip_flags = UIP_POLL; + UIP_APPCALL(ustack); + goto appsend; + } + goto drop; + + /* Check if we were invoked because of the perodic timer + firing. */ + } else if (flag == UIP_TIMER) { +#if UIP_REASSEMBLY + if (uip_reasstmr != 0) + --uip_reasstmr; +#endif /* UIP_REASSEMBLY */ + /* Increase the initial sequence number. */ + if (++ustack->iss[3] == 0) { + if (++ustack->iss[2] == 0) { + if (++ustack->iss[1] == 0) + ++ustack->iss[0]; + } + } + + /* Reset the length variables. */ + ustack->uip_len = 0; + ustack->uip_slen = 0; + + /* Check if the connection is in a state in which we simply wait + for the connection to time out. If so, we increase the + connection's timer and remove the connection if it times + out. */ + if (uip_connr->tcpstateflags == UIP_TIME_WAIT || + uip_connr->tcpstateflags == UIP_FIN_WAIT_2) { + ++(uip_connr->timer); + if (uip_connr->timer == UIP_TIME_WAIT_TIMEOUT) + uip_connr->tcpstateflags = UIP_CLOSED; + } else if (uip_connr->tcpstateflags != UIP_CLOSED) { + /* If the connection has outstanding data, we increase + the connection's timer and see if it has reached the + RTO value in which case we retransmit. */ + if (uip_outstanding(uip_connr)) { + if (uip_connr->timer-- == 0) { + if (uip_connr->nrtx == UIP_MAXRTX || + ((uip_connr->tcpstateflags == + UIP_SYN_SENT + || uip_connr->tcpstateflags == + UIP_SYN_RCVD) + && uip_connr->nrtx == + UIP_MAXSYNRTX)) { + uip_connr->tcpstateflags = + UIP_CLOSED; + + /* We call UIP_APPCALL() with + uip_flags set to UIP_TIMEDOUT + to inform the application + that the connection has timed + out. */ + ustack->uip_flags = + UIP_TIMEDOUT; + UIP_APPCALL(ustack); + + /* We also send a reset packet + to the remote host. */ + tcp_hdr->flags = + TCP_RST | TCP_ACK; + goto tcp_send_nodata; + } + + /* Exponential backoff. */ + uip_connr->timer = + UIP_RTO << (uip_connr->nrtx > + 4 ? 4 : uip_connr-> + nrtx); + ++(uip_connr->nrtx); + + /* Ok, so we need to retransmit. + We do this differently depending on + which state we are in. + In ESTABLISHED, we call upon the + application so that it may prepare + the data for the retransmit. + In SYN_RCVD, we resend the SYNACK + that we sent earlier and in LAST_ACK + we have to retransmit our FINACK. */ + ++ustack->stats.tcp.rexmit; + switch (uip_connr-> + tcpstateflags & UIP_TS_MASK) { + case UIP_SYN_RCVD: + /* In the SYN_RCVD state, we + should retransmit our SYNACK + */ + goto tcp_send_synack; +#if UIP_ACTIVE_OPEN + case UIP_SYN_SENT: + /* In the SYN_SENT state, + we retransmit out SYN. */ + tcp_hdr->flags = 0; + goto tcp_send_syn; +#endif /* UIP_ACTIVE_OPEN */ + + case UIP_ESTABLISHED: + /* In the ESTABLISHED state, + we call upon the application + to do the actual retransmit + after which we jump into + the code for sending out the + packet (the apprexmit + label). */ + ustack->uip_flags = UIP_REXMIT; + UIP_APPCALL(ustack); + goto apprexmit; + + case UIP_FIN_WAIT_1: + case UIP_CLOSING: + case UIP_LAST_ACK: + /* In all these states we should + retransmit a FINACK. */ + goto tcp_send_finack; + + } + } + } else if ((uip_connr->tcpstateflags & UIP_TS_MASK) == + UIP_ESTABLISHED) { + /* If there was no need for a retransmission, + we poll the application for new data. */ + ustack->uip_flags = UIP_POLL; + UIP_APPCALL(ustack); + goto appsend; + } + } + goto drop; + } /* End of UIP_TIMER */ +#if UIP_UDP + if (flag == UIP_UDP_TIMER) { + /* This is for IPv4 DHCP only! */ + if (ustack->uip_udp_conn->lport != 0) { + ustack->uip_conn = NULL; + ustack->uip_sappdata = ustack->uip_appdata = + ustack->network_layer + uip_ip_udph_len; + ustack->uip_len = ustack->uip_slen = 0; + ustack->uip_flags = UIP_POLL; + UIP_UDP_APPCALL(ustack); + goto udp_send; + } else { + goto drop; + } + } +#endif + if (flag == UIP_NDP_TIMER) { + /* This is for IPv6 NDP Only! */ + if (1) { /* If NDP engine active */ + ustack->uip_len = ustack->uip_slen = 0; + ustack->uip_flags = UIP_POLL; + goto ndp_send; + } + } + + /* This is where the input processing starts. */ + ++ustack->stats.ip.recv; + + /* Start of IP input header processing code. */ + + if (is_ipv6(ustack)) { + u8_t version = ((ipv6_hdr->ip6_vfc) & 0xf0) >> 4; + + /* Check validity of the IP header. */ + if (version != 0x6) { /* IP version and header length. */ + ++ustack->stats.ip.drop; + ++ustack->stats.ip.vhlerr; + LOG_DEBUG(PFX "ipv6: invalid version(0x%x).", version); + goto drop; + } + } else { + /* Check validity of the IP header. */ + if (tcp_ipv4_hdr->vhl != 0x45) { + /* IP version and header length. */ + ++ustack->stats.ip.drop; + ++ustack->stats.ip.vhlerr; + LOG_DEBUG(PFX + "ipv4: invalid version or header length: " + "0x%x.", + tcp_ipv4_hdr->vhl); + goto drop; + } + } + + /* Check the size of the packet. If the size reported to us in + uip_len is smaller the size reported in the IP header, we assume + that the packet has been corrupted in transit. If the size of + uip_len is larger than the size reported in the IP packet header, + the packet has been padded and we set uip_len to the correct + value.. */ + + if (is_ipv6(ustack)) { + u16_t len = ntohs(ipv6_hdr->ip6_plen); + if (len > ustack->uip_len) { + LOG_DEBUG(PFX + "ip: packet shorter than reported in IP header" + ":IPv6_BUF(ustack)->len: %d ustack->uip_len: " + "%d", len, ustack->uip_len); + goto drop; + } + } else { + if ((tcp_ipv4_hdr->len[0] << 8) + + tcp_ipv4_hdr->len[1] <= ustack->uip_len) { + ustack->uip_len = (tcp_ipv4_hdr->len[0] << 8) + + tcp_ipv4_hdr->len[1]; + } else { + LOG_DEBUG(PFX + "ip: packet shorter than reported in IP header" + ":tcp_ipv4_hdr->len: %d ustack->uip_len:%d.", + (tcp_ipv4_hdr->len[0] << 8) + + tcp_ipv4_hdr->len[1], ustack->uip_len); + goto drop; + } + } + + if (!is_ipv6(ustack)) { + /* Check the fragment flag. */ + if ((tcp_ipv4_hdr->ipoffset[0] & 0x3f) != 0 || + tcp_ipv4_hdr->ipoffset[1] != 0) { +#if UIP_REASSEMBLY + uip_len = uip_reass(); + if (uip_len == 0) + goto drop; +#else /* UIP_REASSEMBLY */ + ++ustack->stats.ip.drop; + ++ustack->stats.ip.fragerr; + LOG_WARN(PFX "ip: fragment dropped."); + goto drop; +#endif /* UIP_REASSEMBLY */ + } + } + + if (!is_ipv6(ustack)) { + /* ipv4 */ + if (uip_ip4addr_cmp(ustack->hostaddr, all_zeroes_addr4)) { + /* If we are configured to use ping IP address + configuration and hasn't been assigned an IP + address yet, we accept all ICMP packets. */ +#if UIP_PINGADDRCONF && !UIP_CONF_IPV6 + if (tcp_ipv4_hdr->proto == UIP_PROTO_ICMP) { + LOG_WARN(PFX + "ip: possible ping config packet " + "received."); + goto icmp_input; + } else { + LOG_WARN(PFX + "ip: packet dropped since no " + "address assigned."); + goto drop; + } +#endif /* UIP_PINGADDRCONF */ + } else { + int broadcast_addr = 0xFFFFFFFF; + /* If IP broadcast support is configured, we check for + a broadcast UDP packet, which may be destined to us + */ + if ((tcp_ipv4_hdr->proto == UIP_PROTO_UDP) && + (uip_ip4addr_cmp + (tcp_ipv4_hdr->destipaddr, &broadcast_addr)) + /*&& + uip_ipchksum() == 0xffff */ + ) { + goto udp_input; + } + + /* Check if the packet is destined for our IP address + */ + if (!uip_ip4addr_cmp(tcp_ipv4_hdr->destipaddr, + ustack->hostaddr)) { + ++ustack->stats.ip.drop; + goto drop; + } + } + if (uip_ipchksum(ustack) != 0xffff) { + /* Compute and check the IP header checksum. */ + ++ustack->stats.ip.drop; + ++ustack->stats.ip.chkerr; + LOG_ERR(PFX "ip: bad checksum."); + goto drop; + } + } /* End of ipv4 */ + + if (is_ipv6(ustack)) { + if (ipv6_hdr->ip6_nxt == UIP_PROTO_TCP) { + /* Check for TCP packet. If so, proceed with TCP input + processing. */ + goto ndp_newdata; + } +#if UIP_UDP + if (ipv6_hdr->ip6_nxt == UIP_PROTO_UDP) + goto ndp_newdata; +#endif /* UIP_UDP */ + + /* This is IPv6 ICMPv6 processing code. */ + if (ipv6_hdr->ip6_nxt != UIP_PROTO_ICMP6) { + /* We only allow ICMPv6 packets from here. */ + ++ustack->stats.ip.drop; + ++ustack->stats.ip.protoerr; + goto drop; + } + + ++ustack->stats.icmp.recv; + +ndp_newdata: + /* This call is to handle the IPv6 Network Discovery Protocol */ + ustack->uip_flags = UIP_NEWDATA; + ustack->uip_slen = 0; +ndp_send: + UIP_NDP_CALL(ustack); + if (ustack->uip_slen != 0) { + ustack->uip_len = ustack->uip_slen; + goto send; + } else { + goto drop; + } + } else { + /* IPv4 Processing */ + if (tcp_ipv4_hdr->proto == UIP_PROTO_TCP) { + /* Check for TCP packet. If so, proceed with TCP input + processing. */ + goto tcp_input; + } +#if UIP_UDP + if (tcp_ipv4_hdr->proto == UIP_PROTO_UDP) + goto udp_input; +#endif /* UIP_UDP */ + + /* ICMPv4 processing code follows. */ + if (tcp_ipv4_hdr->proto != UIP_PROTO_ICMP) { + /* We only allow ICMP packets from here. */ + ++ustack->stats.ip.drop; + ++ustack->stats.ip.protoerr; + LOG_DEBUG(PFX "ip: neither tcp nor icmp."); + goto drop; + } +#if UIP_PINGADDRCONF +icmp_input: +#endif /* UIP_PINGADDRCONF */ + ++ustack->stats.icmp.recv; + + /* ICMP echo (i.e., ping) processing. This is simple, we only + change the ICMP type from ECHO to ECHO_REPLY and adjust the + ICMP checksum before we return the packet. */ + if (icmpv4_hdr->type != ICMP_ECHO) { + ++ustack->stats.icmp.drop; + ++ustack->stats.icmp.typeerr; + LOG_DEBUG(PFX "icmp: not icmp echo."); + goto drop; + } + + /* If we are configured to use ping IP address assignment, we + use the destination IP address of this ping packet and assign + it to ourself. */ +#if UIP_PINGADDRCONF + if ((ustack->hostaddr[0] | ustack->hostaddr[1]) == 0) { + ustack->hostaddr[0] = tcp_ipv4_hdr->destipaddr[0]; + ustack->hostaddr[1] = tcp_ipv4_hdr->destipaddr[1]; + } +#endif /* UIP_PINGADDRCONF */ + + icmpv4_hdr->type = ICMP_ECHO_REPLY; + + if (icmpv4_hdr->icmpchksum >= htons(0xffff - + (ICMP_ECHO << 8))) { + icmpv4_hdr->icmpchksum += htons(ICMP_ECHO << 8) + 1; + } else { + icmpv4_hdr->icmpchksum += htons(ICMP_ECHO << 8); + } + + /* Swap IP addresses. */ + uip_ip4addr_copy(tcp_ipv4_hdr->destipaddr, + tcp_ipv4_hdr->srcipaddr); + uip_ip4addr_copy(tcp_ipv4_hdr->srcipaddr, ustack->hostaddr); + + ++ustack->stats.icmp.sent; + goto send; + + /* End of IPv4 input header processing code. */ + } + +#if UIP_UDP + /* UDP input processing. */ +udp_input: + /* UDP processing is really just a hack. We don't do anything to the + UDP/IP headers, but let the UDP application do all the hard + work. If the application sets uip_slen, it has a packet to + send. */ +#if UIP_UDP_CHECKSUMS + ustack->uip_len = ustack->uip_len - uip_ip_udph_len; + ustack->uip_appdata = ustack->network_layer + uip_ip_udph_len; + if (UDPBUF(ustack)->udpchksum != 0 && uip_udpchksum(ustack) != 0xffff) { + ++ustack->stats.udp.drop; + ++ustack->stats.udp.chkerr; + LOG_DEBUG(PFX "udp: bad checksum."); + goto drop; + } +#else /* UIP_UDP_CHECKSUMS */ + uip_len = uip_len - uip_ip_udph_len; +#endif /* UIP_UDP_CHECKSUMS */ + + if (is_ipv6(ustack)) + goto udp_found; + + /* Demultiplex this UDP packet between the UDP "connections". */ + for (ustack->uip_udp_conn = &ustack->uip_udp_conns[0]; + ustack->uip_udp_conn < &ustack->uip_udp_conns[UIP_UDP_CONNS]; + ++ustack->uip_udp_conn) { + /* If the local UDP port is non-zero, the connection is + considered to be used. If so, the local port number is + checked against the destination port number in the + received packet. If the two port + numbers match, the remote port number is checked if the + connection is bound to a remote port. Finally, if the + connection is bound to a remote IP address, the source IP + address of the packet is checked. */ + + if (ustack->uip_udp_conn->lport != 0 && + UDPBUF(ustack)->destport == ustack->uip_udp_conn->lport && + (ustack->uip_udp_conn->rport == 0 || + UDPBUF(ustack)->srcport == ustack->uip_udp_conn->rport) && + (uip_ip4addr_cmp(ustack->uip_udp_conn->ripaddr, + all_zeroes_addr4) || + uip_ip4addr_cmp(ustack->uip_udp_conn->ripaddr, + all_ones_addr4) || + uip_ip4addr_cmp(tcp_ipv4_hdr->srcipaddr, + ustack->uip_udp_conn->ripaddr))) { + goto udp_found; + } + } + LOG_DEBUG(PFX + "udp: no matching connection found: dest port: %d src port: " + "%d", udp_hdr->destport, udp_hdr->srcport); + goto drop; + +udp_found: + ustack->uip_conn = NULL; + ustack->uip_flags = UIP_NEWDATA; + ustack->uip_sappdata = ustack->uip_appdata = ustack->network_layer + + uip_ip_udph_len; + ustack->uip_slen = 0; + if (is_ipv6(ustack)) + UIP_NDP_CALL(ustack); + else + UIP_UDP_APPCALL(ustack); +udp_send: + if (ustack->uip_slen == 0) + goto drop; + + ustack->uip_len = ustack->uip_slen + uip_ip_udph_len; + + if (is_ipv6(ustack)) { + goto ip_send_nolen; + } else { + tcp_ipv4_hdr->len[0] = (ustack->uip_len >> 8); + tcp_ipv4_hdr->len[1] = (ustack->uip_len & 0xff); + tcp_ipv4_hdr->ttl = ustack->uip_udp_conn->ttl; + tcp_ipv4_hdr->proto = UIP_PROTO_UDP; + } + + udp_hdr->udplen = htons(ustack->uip_slen + UIP_UDPH_LEN); + udp_hdr->udpchksum = 0; + + udp_hdr->srcport = ustack->uip_udp_conn->lport; + udp_hdr->destport = ustack->uip_udp_conn->rport; + + uip_ip4addr_copy(tcp_ipv4_hdr->srcipaddr, ustack->hostaddr); + uip_ip4addr_copy(tcp_ipv4_hdr->destipaddr, + ustack->uip_udp_conn->ripaddr); + + ustack->uip_appdata = ustack->network_layer + uip_ip_tcph_len; + + if (ustack->uip_buf == NULL) { + LOG_WARN(PFX "uip_buf == NULL on udp send"); + goto drop; + } +#if UIP_UDP_CHECKSUMS + /* Calculate UDP checksum. */ + udp_hdr->udpchksum = ~(uip_udpchksum(ustack)); + if (udp_hdr->udpchksum == 0) + udp_hdr->udpchksum = 0xffff; +#endif /* UIP_UDP_CHECKSUMS */ + + goto ip_send_nolen; +#endif /* UIP_UDP */ + + /* TCP input processing. */ +tcp_input: + ++ustack->stats.tcp.recv; + + /* Start of TCP input header processing code. */ + + if (uip_tcpchksum(ustack) != 0xffff) { /* Compute and check the TCP + checksum. */ + ++ustack->stats.tcp.drop; + ++ustack->stats.tcp.chkerr; + LOG_WARN(PFX "tcp: bad checksum."); + goto drop; + } + + if (is_ipv6(ustack)) { + /* Demultiplex this segment. */ + /* First check any active connections. */ + for (uip_connr = &ustack->uip_conns[0]; + uip_connr <= &ustack->uip_conns[UIP_CONNS - 1]; + ++uip_connr) { + if (uip_connr->tcpstateflags != UIP_CLOSED && + tcp_hdr->destport == uip_connr->lport && + tcp_hdr->srcport == uip_connr->rport && + uip_ip6addr_cmp(IPv6_BUF(ustack)->srcipaddr, + uip_connr->ripaddr)) { + goto found; + } + } + } else { + /* Demultiplex this segment. */ + /* First check any active connections. */ + for (uip_connr = &ustack->uip_conns[0]; + uip_connr <= &ustack->uip_conns[UIP_CONNS - 1]; + ++uip_connr) { + if (uip_connr->tcpstateflags != UIP_CLOSED && + tcp_hdr->destport == uip_connr->lport && + tcp_hdr->srcport == uip_connr->rport && + uip_ip4addr_cmp(tcp_ipv4_hdr->srcipaddr, + uip_connr->ripaddr)) { + goto found; + } + } + } + + /* If we didn't find and active connection that expected the packet, + either this packet is an old duplicate, or this is a SYN packet + destined for a connection in LISTEN. If the SYN flag isn't set, + it is an old packet and we send a RST. */ + if ((tcp_hdr->flags & TCP_CTL) != TCP_SYN) + goto reset; + + tmp16 = tcp_hdr->destport; + /* Next, check listening connections. */ + for (c = 0; c < UIP_LISTENPORTS; ++c) { + if (tmp16 == ustack->uip_listenports[c]) + goto found_listen; + } + + /* No matching connection found, so we send a RST packet. */ + ++ustack->stats.tcp.synrst; +reset: + + /* We do not send resets in response to resets. */ + if (tcp_hdr->flags & TCP_RST) + goto drop; + + ++ustack->stats.tcp.rst; + + tcp_hdr->flags = TCP_RST | TCP_ACK; + ustack->uip_len = uip_ip_tcph_len; + tcp_hdr->tcpoffset = 5 << 4; + + /* Flip the seqno and ackno fields in the TCP header. */ + c = tcp_hdr->seqno[3]; + tcp_hdr->seqno[3] = tcp_hdr->ackno[3]; + tcp_hdr->ackno[3] = c; + + c = tcp_hdr->seqno[2]; + tcp_hdr->seqno[2] = tcp_hdr->ackno[2]; + tcp_hdr->ackno[2] = c; + + c = tcp_hdr->seqno[1]; + tcp_hdr->seqno[1] = tcp_hdr->ackno[1]; + tcp_hdr->ackno[1] = c; + + c = tcp_hdr->seqno[0]; + tcp_hdr->seqno[0] = tcp_hdr->ackno[0]; + tcp_hdr->ackno[0] = c; + + /* We also have to increase the sequence number we are + acknowledging. If the least significant byte overflowed, we need + to propagate the carry to the other bytes as well. */ + if (++tcp_hdr->ackno[3] == 0) { + if (++tcp_hdr->ackno[2] == 0) { + if (++tcp_hdr->ackno[1] == 0) + ++tcp_hdr->ackno[0]; + } + } + + /* Swap port numbers. */ + tmp16 = tcp_hdr->srcport; + tcp_hdr->srcport = tcp_hdr->destport; + tcp_hdr->destport = tmp16; + + /* Swap IP addresses. */ + if (is_ipv6(ustack)) { + uip_ip6addr_copy(IPv6_BUF(ustack)->destipaddr, + IPv6_BUF(ustack)->srcipaddr); + uip_ip6addr_copy(IPv6_BUF(ustack)->srcipaddr, + ustack->hostaddr6); + } else { + uip_ip4addr_copy(tcp_ipv4_hdr->destipaddr, + tcp_ipv4_hdr->srcipaddr); + uip_ip4addr_copy(tcp_ipv4_hdr->srcipaddr, ustack->hostaddr); + } + + /* And send out the RST packet! */ + goto tcp_send_noconn; + + /* This label will be jumped to if we matched the incoming packet + with a connection in LISTEN. In that case, we should create a new + connection and send a SYNACK in return. */ +found_listen: + /* First we check if there are any connections avaliable. Unused + connections are kept in the same table as used connections, but + unused ones have the tcpstate set to CLOSED. Also, connections in + TIME_WAIT are kept track of and we'll use the oldest one if no + CLOSED connections are found. Thanks to Eddie C. Dost for a very + nice algorithm for the TIME_WAIT search. */ + uip_connr = 0; + for (c = 0; c < UIP_CONNS; ++c) { + if (ustack->uip_conns[c].tcpstateflags == UIP_CLOSED) { + uip_connr = &ustack->uip_conns[c]; + break; + } + if (ustack->uip_conns[c].tcpstateflags == UIP_TIME_WAIT) { + if (uip_connr == 0 || + ustack->uip_conns[c].timer > uip_connr->timer) { + uip_connr = &ustack->uip_conns[c]; + } + } + } + + if (uip_connr == 0) { + /* All connections are used already, we drop packet and hope + that the remote end will retransmit the packet at a time when + we have more spare connections. */ + ++ustack->stats.tcp.syndrop; + LOG_WARN(PFX "tcp: found no unused connections."); + goto drop; + } + ustack->uip_conn = uip_connr; + + /* Fill in the necessary fields for the new connection. */ + uip_connr->rto = uip_connr->timer = UIP_RTO; + uip_connr->sa = 0; + uip_connr->sv = 4; + uip_connr->nrtx = 0; + uip_connr->lport = tcp_hdr->destport; + uip_connr->rport = tcp_hdr->srcport; + if (is_ipv6(ustack)) { + uip_ip6addr_copy(uip_connr->ripaddr, + IPv6_BUF(ustack)->srcipaddr); + } else { + uip_ip4addr_copy(uip_connr->ripaddr, tcp_ipv4_hdr->srcipaddr); + } + uip_connr->tcpstateflags = UIP_SYN_RCVD; + + uip_connr->snd_nxt[0] = ustack->iss[0]; + uip_connr->snd_nxt[1] = ustack->iss[1]; + uip_connr->snd_nxt[2] = ustack->iss[2]; + uip_connr->snd_nxt[3] = ustack->iss[3]; + uip_connr->len = 1; + + /* rcv_nxt should be the seqno from the incoming packet + 1. */ + uip_connr->rcv_nxt[3] = tcp_hdr->seqno[3]; + uip_connr->rcv_nxt[2] = tcp_hdr->seqno[2]; + uip_connr->rcv_nxt[1] = tcp_hdr->seqno[1]; + uip_connr->rcv_nxt[0] = tcp_hdr->seqno[0]; + uip_add_rcv_nxt(ustack, 1); + + /* Parse the TCP MSS option, if present. */ + if ((tcp_hdr->tcpoffset & 0xf0) > 0x50) { + for (c = 0; c < ((tcp_hdr->tcpoffset >> 4) - 5) << 2;) { + ustack->opt = + ustack->uip_buf[uip_ip_tcph_len + UIP_LLH_LEN + c]; + if (ustack->opt == TCP_OPT_END) { + /* End of options. */ + break; + } else if (ustack->opt == TCP_OPT_NOOP) { + ++c; + /* NOP option. */ + } else if (ustack->opt == TCP_OPT_MSS && + ustack->uip_buf[uip_ip_tcph_len + + UIP_LLH_LEN + 1 + c] == + TCP_OPT_MSS_LEN) { + /* An MSS option with the right option length.*/ + tmp16 = + ((u16_t) ustack-> + uip_buf[uip_ip_tcph_len + UIP_LLH_LEN + 2 + + c] << 8) | (u16_t) ustack-> + uip_buf[uip_ip_tcph_len + UIP_LLH_LEN + 3 + + c]; + uip_connr->initialmss = uip_connr->mss = + tmp16 > UIP_TCP_MSS ? UIP_TCP_MSS : tmp16; + + /* And we are done processing options. */ + break; + } else { + /* All other options have a length field, so + that we easily can skip past them. */ + if (ustack-> + uip_buf[uip_ip_tcph_len + UIP_LLH_LEN + 1 + + c] == 0) { + /* If the length field is zero, the + options are malformed + and we don't process them further. */ + break; + } + c += ustack->uip_buf[uip_ip_tcph_len + + UIP_LLH_LEN + 1 + c]; + } + } + } + + /* Our response will be a SYNACK. */ +#if UIP_ACTIVE_OPEN +tcp_send_synack: + tcp_hdr->flags = TCP_ACK; + +tcp_send_syn: + tcp_hdr->flags |= TCP_SYN; +#else /* UIP_ACTIVE_OPEN */ +tcp_send_synack: + tcp_hdr->flags = TCP_SYN | TCP_ACK; +#endif /* UIP_ACTIVE_OPEN */ + + /* We send out the TCP Maximum Segment Size option with our + SYNACK. */ + tcp_hdr->optdata[0] = TCP_OPT_MSS; + tcp_hdr->optdata[1] = TCP_OPT_MSS_LEN; + tcp_hdr->optdata[2] = (UIP_TCP_MSS) / 256; + tcp_hdr->optdata[3] = (UIP_TCP_MSS) & 255; + ustack->uip_len = uip_ip_tcph_len + TCP_OPT_MSS_LEN; + tcp_hdr->tcpoffset = ((UIP_TCPH_LEN + TCP_OPT_MSS_LEN) / 4) << 4; + goto tcp_send; + + /* This label will be jumped to if we found an active connection. */ +found: + ustack->uip_conn = uip_connr; + ustack->uip_flags = 0; + /* We do a very naive form of TCP reset processing; we just accept + any RST and kill our connection. We should in fact check if the + sequence number of this reset is wihtin our advertised window + before we accept the reset. */ + if (tcp_hdr->flags & TCP_RST) { + uip_connr->tcpstateflags = UIP_CLOSED; + LOG_WARN(PFX "tcp: got reset, aborting connection."); + ustack->uip_flags = UIP_ABORT; + UIP_APPCALL(ustack); + goto drop; + } + /* Calculated the length of the data, if the application has sent + any data to us. */ + c = (tcp_hdr->tcpoffset >> 4) << 2; + /* uip_len will contain the length of the actual TCP data. This is + calculated by subtracing the length of the TCP header (in + c) and the length of the IP header (20 bytes). */ + ustack->uip_len = ustack->uip_len - c - uip_iph_len; + + /* First, check if the sequence number of the incoming packet is + what we're expecting next. If not, we send out an ACK with the + correct numbers in. */ + if (!(((uip_connr->tcpstateflags & UIP_TS_MASK) == UIP_SYN_SENT) && + ((tcp_hdr->flags & TCP_CTL) == (TCP_SYN | TCP_ACK)))) { + if ((ustack->uip_len > 0 + || ((tcp_hdr->flags & (TCP_SYN | TCP_FIN)) != 0)) + && (tcp_hdr->seqno[0] != uip_connr->rcv_nxt[0] + || tcp_hdr->seqno[1] != uip_connr->rcv_nxt[1] + || tcp_hdr->seqno[2] != uip_connr->rcv_nxt[2] + || tcp_hdr->seqno[3] != uip_connr->rcv_nxt[3])) { + goto tcp_send_ack; + } + } + + { + u8_t uip_acc32[4]; + + /* Next, check if the incoming segment acks any outstanding + data. If so, we update the sequence number, reset the len of + the outstanding data, calc RTT estimations, and reset the + retransmission timer. */ + if ((tcp_hdr->flags & TCP_ACK) && uip_outstanding(uip_connr)) { + uip_add32(uip_connr->snd_nxt, uip_connr->len, + uip_acc32); + + if (tcp_hdr->ackno[0] == uip_acc32[0] && + tcp_hdr->ackno[1] == uip_acc32[1] && + tcp_hdr->ackno[2] == uip_acc32[2] && + tcp_hdr->ackno[3] == uip_acc32[3]) { + /* Update sequence number. */ + uip_connr->snd_nxt[0] = uip_acc32[0]; + uip_connr->snd_nxt[1] = uip_acc32[1]; + uip_connr->snd_nxt[2] = uip_acc32[2]; + uip_connr->snd_nxt[3] = uip_acc32[3]; + + /* Do RTT estimation, unless we have done + retransmissions. */ + if (uip_connr->nrtx == 0) { + signed char m; + m = uip_connr->rto - uip_connr->timer; + /* This is taken directly from VJs + original code in his paper */ + m = m - (uip_connr->sa >> 3); + uip_connr->sa += m; + if (m < 0) + m = -m; + m = m - (uip_connr->sv >> 2); + uip_connr->sv += m; + uip_connr->rto = + (uip_connr->sa >> 3) + + uip_connr->sv; + + } + /* Set the acknowledged flag. */ + ustack->uip_flags = UIP_ACKDATA; + /* Reset the retransmission timer. */ + uip_connr->timer = uip_connr->rto; + + /* Reset length of outstanding data. */ + uip_connr->len = 0; + } + + } + + } + + /* Do different things depending on in what state the connection is. */ + switch (uip_connr->tcpstateflags & UIP_TS_MASK) { + /* CLOSED and LISTEN are not handled here. CLOSE_WAIT is not + implemented, since we force the application to close when the + peer sends a FIN (hence the application goes directly from + ESTABLISHED to LAST_ACK). */ + case UIP_SYN_RCVD: + /* In SYN_RCVD we have sent out a SYNACK in response to a SYN, + and we are waiting for an ACK that acknowledges the data we + sent out the last time. Therefore, we want to have the + UIP_ACKDATA flag set. + If so, we enter the ESTABLISHED state. */ + if (ustack->uip_flags & UIP_ACKDATA) { + uip_connr->tcpstateflags = UIP_ESTABLISHED; + ustack->uip_flags = UIP_CONNECTED; + uip_connr->len = 0; + if (ustack->uip_len > 0) { + ustack->uip_flags |= UIP_NEWDATA; + uip_add_rcv_nxt(ustack, ustack->uip_len); + } + ustack->uip_slen = 0; + UIP_APPCALL(ustack); + goto appsend; + } + goto drop; +#if UIP_ACTIVE_OPEN + case UIP_SYN_SENT: + /* In SYN_SENT, we wait for a SYNACK that is sent in response to + our SYN. The rcv_nxt is set to sequence number in the SYNACK + plus one, and we send an ACK. We move into the ESTABLISHED + state. */ + if ((ustack->uip_flags & UIP_ACKDATA) && + (tcp_hdr->flags & TCP_CTL) == (TCP_SYN | TCP_ACK)) { + + /* Parse the TCP MSS option, if present. */ + if ((tcp_hdr->tcpoffset & 0xf0) > 0x50) { + for (c = 0; + c < + ((tcp_hdr->tcpoffset >> 4) - 5) << 2;) { + ustack->opt = + ustack->uip_buf[uip_ip_tcph_len + + UIP_LLH_LEN + c]; + if (ustack->opt == TCP_OPT_END) { + /* End of options. */ + break; + } else if (ustack->opt == + TCP_OPT_NOOP) { + ++c; + /* NOP option. */ + } else if (ustack->opt == TCP_OPT_MSS && + ustack-> + uip_buf[uip_ip_tcph_len + + UIP_LLH_LEN + 1 + + c] == + TCP_OPT_MSS_LEN) { + /* An MSS option with the right + option length. */ + tmp16 = + (ustack-> + uip_buf[uip_ip_tcph_len + + UIP_LLH_LEN + 2 + + c] << 8) | ustack-> + uip_buf[uip_ip_tcph_len + + UIP_LLH_LEN + 3 + + c]; + uip_connr->initialmss = + uip_connr->mss = + tmp16 > + UIP_TCP_MSS ? UIP_TCP_MSS : + tmp16; + + /* And we are done processing + options. */ + break; + } else { + /* All other options have a + length field, so that we + easily can skip past them */ + if (ustack-> + uip_buf[uip_ip_tcph_len + + UIP_LLH_LEN + 1 + + c] == 0) { + /* If the length field + is zero, the options + are malformed and we + don't process them + further. */ + break; + } + c += ustack-> + uip_buf[uip_ip_tcph_len + + UIP_LLH_LEN + 1 + + c]; + } + } + } + uip_connr->tcpstateflags = UIP_ESTABLISHED; + uip_connr->rcv_nxt[0] = tcp_hdr->seqno[0]; + uip_connr->rcv_nxt[1] = tcp_hdr->seqno[1]; + uip_connr->rcv_nxt[2] = tcp_hdr->seqno[2]; + uip_connr->rcv_nxt[3] = tcp_hdr->seqno[3]; + uip_add_rcv_nxt(ustack, 1); + ustack->uip_flags = UIP_CONNECTED | UIP_NEWDATA; + uip_connr->len = 0; + ustack->uip_len = 0; + ustack->uip_slen = 0; + UIP_APPCALL(ustack); + goto appsend; + } + /* Inform the application that the connection failed */ + ustack->uip_flags = UIP_ABORT; + UIP_APPCALL(ustack); + /* The connection is closed after we send the RST */ + ustack->uip_conn->tcpstateflags = UIP_CLOSED; + goto reset; +#endif /* UIP_ACTIVE_OPEN */ + + case UIP_ESTABLISHED: + /* In the ESTABLISHED state, we call upon the application to + feed data into the uip_buf. If the UIP_ACKDATA flag is set, + the application should put new data into the buffer, + otherwise we are retransmitting an old segment, and the + application should put that data into the buffer. + + If the incoming packet is a FIN, we should close the + connection on this side as well, and we send out a FIN and + enter the LAST_ACK state. We require that there is no + outstanding data; otherwise the sequence numbers will be + screwed up. */ + + if (tcp_hdr->flags & TCP_FIN + && !(uip_connr->tcpstateflags & UIP_STOPPED)) { + if (uip_outstanding(uip_connr)) + goto drop; + uip_add_rcv_nxt(ustack, 1 + ustack->uip_len); + ustack->uip_flags |= UIP_CLOSE; + if (ustack->uip_len > 0) + ustack->uip_flags |= UIP_NEWDATA; + UIP_APPCALL(ustack); + uip_connr->len = 1; + uip_connr->tcpstateflags = UIP_LAST_ACK; + uip_connr->nrtx = 0; +tcp_send_finack: + tcp_hdr->flags = TCP_FIN | TCP_ACK; + goto tcp_send_nodata; + } + + /* Check the URG flag. If this is set, the segment carries + urgent data that we must pass to the application. */ + if ((tcp_hdr->flags & TCP_URG) != 0) { +#if UIP_URGDATA > 0 + uip_urglen = (tcp_hdr->urgp[0] << 8) | tcp_hdr->urgp[1]; + if (uip_urglen > uip_len) { + /* There is more urgent data in the next segment + to come. */ + uip_urglen = uip_len; + } + uip_add_rcv_nxt(uip_urglen); + uip_len -= uip_urglen; + uip_urgdata = uip_appdata; + uip_appdata += uip_urglen; + } else { + uip_urglen = 0; +#else /* UIP_URGDATA > 0 */ + ustack->uip_appdata = + ((char *)ustack->uip_appdata) + + ((tcp_hdr->urgp[0] << 8) | tcp_hdr->urgp[1]); + ustack->uip_len -= + (tcp_hdr->urgp[0] << 8) | tcp_hdr->urgp[1]; +#endif /* UIP_URGDATA > 0 */ + } + + /* If uip_len > 0 we have TCP data in the packet, and we flag + this by setting the UIP_NEWDATA flag and update the sequence + number we acknowledge. If the application has stopped the + dataflow using uip_stop(), we must not accept any data + packets from the remote host. */ + if (ustack->uip_len > 0 + && !(uip_connr->tcpstateflags & UIP_STOPPED)) { + ustack->uip_flags |= UIP_NEWDATA; + uip_add_rcv_nxt(ustack, ustack->uip_len); + } + + /* Check if the available buffer space advertised by the other + end is smaller than the initial MSS for this connection. + If so, we set the current MSS to the window size to ensure + that the application does not send more data than the other + end can handle. + + If the remote host advertises a zero window, we set the MSS + to the initial MSS so that the application will send an + entire MSS of data. This data will not be acknowledged by + the receiver, and the application will retransmit it. + This is called the "persistent timer" and uses the + retransmission mechanim. + */ + tmp16 = + ((u16_t) tcp_hdr->wnd[0] << 8) + (u16_t) tcp_hdr->wnd[1]; + if (tmp16 > uip_connr->initialmss || tmp16 == 0) + tmp16 = uip_connr->initialmss; + uip_connr->mss = tmp16; + + /* If this packet constitutes an ACK for outstanding data + (flagged by the UIP_ACKDATA flag, we should call the + application since it might want to send more data. + If the incoming packet had data from the peer + (as flagged by the UIP_NEWDATA flag), the application + must also be notified. + + When the application is called, the global variable uip_len + contains the length of the incoming data. The application can + access the incoming data through the global pointer + uip_appdata, which usually points uip_ip_tcph_len + + UIP_LLH_LEN bytes into the uip_buf array. + + If the application wishes to send any data, this data should + be put into the uip_appdata and the length of the data should + be put into uip_len. If the application don't have any data + to send, uip_len must be set to 0. */ + if (ustack->uip_flags & (UIP_NEWDATA | UIP_ACKDATA)) { + ustack->uip_slen = 0; + UIP_APPCALL(ustack); + +appsend: + + if (ustack->uip_flags & UIP_ABORT) { + ustack->uip_slen = 0; + uip_connr->tcpstateflags = UIP_CLOSED; + tcp_hdr->flags = TCP_RST | TCP_ACK; + goto tcp_send_nodata; + } + + if (ustack->uip_flags & UIP_CLOSE) { + ustack->uip_slen = 0; + uip_connr->len = 1; + uip_connr->tcpstateflags = UIP_FIN_WAIT_1; + uip_connr->nrtx = 0; + tcp_hdr->flags = TCP_FIN | TCP_ACK; + goto tcp_send_nodata; + } + + /* If uip_slen > 0, the application has data to be sent + */ + if (ustack->uip_slen > 0) { + + /* If the connection has acknowledged data, the + contents of the ->len variable should be + discarded. */ + if ((ustack->uip_flags & UIP_ACKDATA) != 0) + uip_connr->len = 0; + + /* If the ->len variable is non-zero the + connection has already data in transit and + cannot send anymore right now. */ + if (uip_connr->len == 0) { + + /* The application cannot send more than + what is allowed by the mss (the + minumum of the MSS and the available + window). */ + if (ustack->uip_slen > uip_connr->mss) { + ustack->uip_slen = + uip_connr->mss; + } + + /* Remember how much data we send out + now so that we know when everything + has been acknowledged. */ + uip_connr->len = ustack->uip_slen; + } else { + + /* If the application already had + unacknowledged data, we make sure + that the application does not send + (i.e., retransmit) out more than it + previously sent out. */ + ustack->uip_slen = uip_connr->len; + } + } + uip_connr->nrtx = 0; +apprexmit: + ustack->uip_appdata = ustack->uip_sappdata; + + /* If the application has data to be sent, or if the + incoming packet had new data in it, we must send + out a packet. */ + if (ustack->uip_slen > 0 && uip_connr->len > 0) { + /* Add the length of the IP and TCP headers. */ + ustack->uip_len = + uip_connr->len + uip_ip_tcph_len; + /* We always set the ACK flag in response + packets. */ + tcp_hdr->flags = TCP_ACK | TCP_PSH; + /* Send the packet. */ + goto tcp_send_noopts; + } + /* If there is no data to send, just send out a pure ACK + if there is newdata. */ + if (ustack->uip_flags & UIP_NEWDATA) { + ustack->uip_len = uip_ip_tcph_len; + tcp_hdr->flags = TCP_ACK; + goto tcp_send_noopts; + } + } + goto drop; + case UIP_LAST_ACK: + /* We can close this connection if the peer has acknowledged our + FIN. This is indicated by the UIP_ACKDATA flag. */ + if (ustack->uip_flags & UIP_ACKDATA) { + uip_connr->tcpstateflags = UIP_CLOSED; + ustack->uip_flags = UIP_CLOSE; + UIP_APPCALL(ustack); + } + break; + + case UIP_FIN_WAIT_1: + /* The application has closed the connection, but the remote + host hasn't closed its end yet. Thus we do nothing but wait + for a FIN from the other side. */ + if (ustack->uip_len > 0) + uip_add_rcv_nxt(ustack, ustack->uip_len); + if (tcp_hdr->flags & TCP_FIN) { + if (ustack->uip_flags & UIP_ACKDATA) { + uip_connr->tcpstateflags = UIP_TIME_WAIT; + uip_connr->timer = 0; + uip_connr->len = 0; + } else { + uip_connr->tcpstateflags = UIP_CLOSING; + } + uip_add_rcv_nxt(ustack, 1); + ustack->uip_flags = UIP_CLOSE; + UIP_APPCALL(ustack); + goto tcp_send_ack; + } else if (ustack->uip_flags & UIP_ACKDATA) { + uip_connr->tcpstateflags = UIP_FIN_WAIT_2; + uip_connr->len = 0; + goto drop; + } + if (ustack->uip_len > 0) + goto tcp_send_ack; + goto drop; + + case UIP_FIN_WAIT_2: + if (ustack->uip_len > 0) + uip_add_rcv_nxt(ustack, ustack->uip_len); + if (tcp_hdr->flags & TCP_FIN) { + uip_connr->tcpstateflags = UIP_TIME_WAIT; + uip_connr->timer = 0; + uip_add_rcv_nxt(ustack, 1); + ustack->uip_flags = UIP_CLOSE; + UIP_APPCALL(ustack); + goto tcp_send_ack; + } + if (ustack->uip_len > 0) + goto tcp_send_ack; + goto drop; + + case UIP_TIME_WAIT: + goto tcp_send_ack; + + case UIP_CLOSING: + if (ustack->uip_flags & UIP_ACKDATA) { + uip_connr->tcpstateflags = UIP_TIME_WAIT; + uip_connr->timer = 0; + } + } + goto drop; + + /* We jump here when we are ready to send the packet, and just want + to set the appropriate TCP sequence numbers in the TCP header. */ +tcp_send_ack: + tcp_hdr->flags = TCP_ACK; +tcp_send_nodata: + ustack->uip_len = uip_ip_tcph_len; +tcp_send_noopts: + tcp_hdr->tcpoffset = (UIP_TCPH_LEN / 4) << 4; +tcp_send: + /* We're done with the input processing. We are now ready to send a + reply. Our job is to fill in all the fields of the TCP and IP + headers before calculating the checksum and finally send the + packet. */ + tcp_hdr->ackno[0] = uip_connr->rcv_nxt[0]; + tcp_hdr->ackno[1] = uip_connr->rcv_nxt[1]; + tcp_hdr->ackno[2] = uip_connr->rcv_nxt[2]; + tcp_hdr->ackno[3] = uip_connr->rcv_nxt[3]; + + tcp_hdr->seqno[0] = uip_connr->snd_nxt[0]; + tcp_hdr->seqno[1] = uip_connr->snd_nxt[1]; + tcp_hdr->seqno[2] = uip_connr->snd_nxt[2]; + tcp_hdr->seqno[3] = uip_connr->snd_nxt[3]; + + if (is_ipv6(ustack)) { + IPv6_BUF(ustack)->proto = UIP_PROTO_TCP; + uip_ip6addr_copy(IPv6_BUF(ustack)->srcipaddr, + ustack->hostaddr6); + uip_ip6addr_copy(IPv6_BUF(ustack)->destipaddr, + uip_connr->ripaddr6); + } else { + tcp_ipv4_hdr->proto = UIP_PROTO_TCP; + uip_ip4addr_copy(tcp_ipv4_hdr->srcipaddr, ustack->hostaddr); + uip_ip4addr_copy(tcp_ipv4_hdr->destipaddr, uip_connr->ripaddr); + } + + tcp_hdr->srcport = uip_connr->lport; + tcp_hdr->destport = uip_connr->rport; + + if (uip_connr->tcpstateflags & UIP_STOPPED) { + /* If the connection has issued uip_stop(), we advertise a zero + window so that the remote host will stop sending data. */ + tcp_hdr->wnd[0] = tcp_hdr->wnd[1] = 0; + } else { + tcp_hdr->wnd[0] = ((UIP_RECEIVE_WINDOW) >> 8); + tcp_hdr->wnd[1] = ((UIP_RECEIVE_WINDOW) & 0xff); + } + +tcp_send_noconn: + if (is_ipv6(ustack)) { + IPv6_BUF(ustack)->ttl = UIP_TTL; + + /* For IPv6, the IP length field does not include the IPv6 IP + header length. */ + IPv6_BUF(ustack)->len[0] = + ((ustack->uip_len - uip_iph_len) >> 8); + IPv6_BUF(ustack)->len[1] = + ((ustack->uip_len - uip_iph_len) & 0xff); + } else { + tcp_ipv4_hdr->ttl = UIP_TTL; + tcp_ipv4_hdr->len[0] = (ustack->uip_len >> 8); + tcp_ipv4_hdr->len[1] = (ustack->uip_len & 0xff); + } + + tcp_hdr->urgp[0] = tcp_hdr->urgp[1] = 0; + + /* Calculate TCP checksum. */ + tcp_hdr->tcpchksum = 0; + tcp_hdr->tcpchksum = ~(uip_tcpchksum(ustack)); + +ip_send_nolen: + + if (!is_ipv6(ustack)) { + tcp_ipv4_hdr->vhl = 0x45; + tcp_ipv4_hdr->tos = 0; + tcp_ipv4_hdr->ipoffset[0] = tcp_ipv4_hdr->ipoffset[1] = 0; + ++ustack->ipid; + tcp_ipv4_hdr->ipid[0] = ustack->ipid >> 8; + tcp_ipv4_hdr->ipid[1] = ustack->ipid & 0xff; + /* Calculate IP checksum. */ + tcp_ipv4_hdr->ipchksum = 0; + tcp_ipv4_hdr->ipchksum = ~(uip_ipchksum(ustack)); + } + + ++ustack->stats.tcp.sent; +send: + if (is_ipv6(ustack)) { + LOG_DEBUG(PFX "Sending packet with length %d (%d)", + ustack->uip_len, ipv6_hdr ? ipv6_hdr->ip6_plen : 0); + } else { + LOG_DEBUG(PFX "Sending packet with length %d (%d)", + ustack->uip_len, + (tcp_ipv4_hdr->len[0] << 8) | tcp_ipv4_hdr->len[1]); + } + ++ustack->stats.ip.sent; + /* Return and let the caller do the actual transmission. */ + ustack->uip_flags = 0; + return; +drop: + ustack->uip_len = 0; + ustack->uip_flags = 0; + return; +} + +/*---------------------------------------------------------------------------*/ +void uip_send(struct uip_stack *ustack, const void *data, int len) +{ + if (len > 0) { + ustack->uip_slen = len; + if (data != ustack->uip_buf) + memcpy(ustack->uip_buf, (data), ustack->uip_slen); + } +} + +void uip_appsend(struct uip_stack *ustack, const void *data, int len) +{ + if (len > 0) { + ustack->uip_slen = len; + if (data != ustack->uip_sappdata) + memcpy(ustack->uip_sappdata, (data), ustack->uip_slen); + } +} + +u16_t uip_datalen(struct uip_stack *ustack) +{ + return ustack->uip_len; +} + +/** @} */ diff --git a/iscsiuio/src/uip/uip.h b/iscsiuio/src/uip/uip.h new file mode 100644 index 0000000..0225f6a --- /dev/null +++ b/iscsiuio/src/uip/uip.h @@ -0,0 +1,1569 @@ + +/** + * \addtogroup uip + * @{ + */ + +/** + * \file + * Header file for the uIP TCP/IP stack. + * \author Adam Dunkels + * + * The uIP TCP/IP stack header file contains definitions for a number + * of C macros that are used by uIP programs as well as internal uIP + * structures, TCP/IP header structures and function declarations. + * + */ + +/* + * Copyright (c) 2001-2003, Adam Dunkels. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack. + * + * + */ + +#ifndef __UIP_H__ +#define __UIP_H__ + +#include +#include + +#include "uipopt.h" + +#include "debug.h" + +#include "uip_eth.h" + +/* Forware declaration */ +struct uip_stack; + +/** + * Repressentation of an IP address. + * + */ +typedef u16_t uip_ip4addr_t[2]; +typedef u16_t uip_ip6addr_t[8]; + +const uip_ip6addr_t all_zeroes_addr6; +const uip_ip4addr_t all_zeroes_addr4; + +#define ETH_BUF(buf) ((struct uip_eth_hdr *)buf) +#define VLAN_ETH_BUF(buf) ((struct uip_vlan_eth_hdr *)buf) +#define IPv4_BUF(buf) ((struct uip_tcp_ipv4_hdr *)buf) +#define IPv6_BUF(buf) ((struct uip_tcp_ipv6_hdr *)buf) + +/*---------------------------------------------------------------------------*/ +/* First, the functions that should be called from the + * system. Initialization, the periodic timer and incoming packets are + * handled by the following three functions. + */ + +/** + * Set the IP address of this host. + * + * The IP address is represented as a 4-byte array where the first + * octet of the IP address is put in the first member of the 4-byte + * array. + * + * Example: + \code + + uip_ipaddr_t addr; + + uip_ipaddr(&addr, 192,168,1,2); + uip_sethostaddr(&addr); + + \endcode + * \param addr A pointer to an IP address of type uip_ipaddr_t; + * + * \sa uip_ipaddr() + * + * \hideinitializer + */ +void uip_sethostaddr4(struct uip_stack *ustack, uip_ip4addr_t *addr); + +/** + * Set the default router's IP address. + * + * \param addr A pointer to a uip_ipaddr_t variable containing the IP + * address of the default router. + * + * \sa uip_ipaddr() + * + * \hideinitializer + */ +void uip_setdraddr4(struct uip_stack *ustack, uip_ip4addr_t *addr); + +/** + * Set the netmask. + * + * \param addr A pointer to a uip_ipaddr_t variable containing the IP + * address of the netmask. + * + * \sa uip_ipaddr() + * + * \hideinitializer + */ +void uip_setnetmask4(struct uip_stack *ustack, uip_ip4addr_t *addr); + +/** + * Set the ethernet MAC address. + * + * \param addr A pointer to a uip_ipaddr_t variable containing the IP + * address of the netmask. + * + * \sa uip_ipaddr() + * + * \hideinitializer + */ +void uip_setethernetmac(struct uip_stack *ustack, uint8_t *mac); + +/** + * Get the default router's IP address. + * + * \param addr A pointer to a uip_ipaddr_t variable that will be + * filled in with the IP address of the default router. + * + * \hideinitializer + */ +#define uip_getdraddr(addr) uip_ipaddr_copy((addr), uip_draddr) + +/** + * Get the netmask. + * + * \param addr A pointer to a uip_ipaddr_t variable that will be + * filled in with the value of the netmask. + * + * \hideinitializer + */ +#define uip_getnetmask(addr) uip_ipaddr_copy((addr), uip_netmask) + +void set_uip_stack(struct uip_stack *ustack, + uip_ip4addr_t *ip, + uip_ip4addr_t *netmask, + uip_ip4addr_t *default_route, uint8_t *mac_addr); + +/** @} */ + +/** + * \defgroup uipinit uIP initialization functions + * @{ + * + * The uIP initialization functions are used for booting uIP. + */ + +/** + * uIP initialization function. + * + * This function should be called at boot up to initilize the uIP + * TCP/IP stack. + */ +void uip_init(struct uip_stack *ustack, uint8_t enable_ipv6); + +/** + * uIP reset function. + * + * This function should be called at to reset the uIP TCP/IP stack. + */ +void uip_reset(struct uip_stack *ustack); + +/** + * uIP initialization function. + * + * This function may be used at boot time to set the initial ip_id. + */ +void uip_setipid(u16_t id); + +/** + * + * + */ +#define uip_conn_active(conn) (uip_conns[conn].tcpstateflags != UIP_CLOSED) + +#if UIP_UDP +void uip_udp_periodic(struct uip_stack *ustack, int conn); +#endif /* UIP_UDP */ + +void uip_ndp_periodic(struct uip_stack *ustack); + +/** + * The uIP packet buffer. + * + * The uip_buf array is used to hold incoming and outgoing + * packets. The device driver should place incoming data into this + * buffer. When sending data, the device driver should read the link + * level headers and the TCP/IP headers from this buffer. The size of + * the link level headers is configured by the UIP_LLH_LEN define. + * + * \note The application data need not be placed in this buffer, so + * the device driver must read it from the place pointed to by the + * uip_appdata pointer as illustrated by the following example: + \code + void + devicedriver_send(void) + { + hwsend(&uip_buf[0], UIP_LLH_LEN); + if(uip_len <= UIP_LLH_LEN + UIP_TCPIP_HLEN) { + hwsend(&uip_buf[UIP_LLH_LEN], uip_len - UIP_LLH_LEN); + } else { + hwsend(&uip_buf[UIP_LLH_LEN], UIP_TCPIP_HLEN); + hwsend(uip_appdata, uip_len - UIP_TCPIP_HLEN - UIP_LLH_LEN); + } + } + \endcode + */ +/*extern u8_t uip_buf[UIP_BUFSIZE+2]; */ + +/** @} */ + +/*---------------------------------------------------------------------------*/ +/* Functions that are used by the uIP application program. Opening and + * closing connections, sending and receiving data, etc. is all + * handled by the functions below. +*/ +/** + * \defgroup uipappfunc uIP application functions + * @{ + * + * Functions used by an application running of top of uIP. + */ + +/** + * Start listening to the specified port. + * + * \note Since this function expects the port number in network byte + * order, a conversion using HTONS() or htons() is necessary. + * + \code + uip_listen(HTONS(80)); + \endcode + * + * \param port A 16-bit port number in network byte order. + */ +void uip_listen(struct uip_stack *ustack, u16_t port); + +/** + * Stop listening to the specified port. + * + * \note Since this function expects the port number in network byte + * order, a conversion using HTONS() or htons() is necessary. + * + \code + uip_unlisten(HTONS(80)); + \endcode + * + * \param port A 16-bit port number in network byte order. + */ +void uip_unlisten(struct uip_stack *ustack, u16_t port); + +/** + * Connect to a remote host using TCP. + * + * This function is used to start a new connection to the specified + * port on the specied host. It allocates a new connection identifier, + * sets the connection to the SYN_SENT state and sets the + * retransmission timer to 0. This will cause a TCP SYN segment to be + * sent out the next time this connection is periodically processed, + * which usually is done within 0.5 seconds after the call to + * uip_connect(). + * + * \note This function is avaliable only if support for active open + * has been configured by defining UIP_ACTIVE_OPEN to 1 in uipopt.h. + * + * \note Since this function requires the port number to be in network + * byte order, a conversion using HTONS() or htons() is necessary. + * + \code + uip_ipaddr_t ipaddr; + + uip_ipaddr(&ipaddr, 192,168,1,2); + uip_connect(&ipaddr, HTONS(80)); + \endcode + * + * \param ripaddr The IP address of the remote hot. + * + * \param port A 16-bit port number in network byte order. + * + * \return A pointer to the uIP connection identifier for the new connection, + * or NULL if no connection could be allocated. + * + */ +struct uip_conn *uip_connect(struct uip_stack *ustack, + uip_ip4addr_t *ripaddr, u16_t port); + +/** + * \internal + * + * Check if a connection has outstanding (i.e., unacknowledged) data. + * + * \param conn A pointer to the uip_conn structure for the connection. + * + * \hideinitializer + */ +#define uip_outstanding(conn) ((conn)->len) + +/** + * Send data on the current connection. + * + * This function is used to send out a single segment of TCP + * data. Only applications that have been invoked by uIP for event + * processing can send data. + * + * The amount of data that actually is sent out after a call to this + * funcion is determined by the maximum amount of data TCP allows. uIP + * will automatically crop the data so that only the appropriate + * amount of data is sent. The function uip_mss() can be used to query + * uIP for the amount of data that actually will be sent. + * + * \note This function does not guarantee that the sent data will + * arrive at the destination. If the data is lost in the network, the + * application will be invoked with the uip_rexmit() event being + * set. The application will then have to resend the data using this + * function. + * + * \param data A pointer to the data which is to be sent. + * + * \param len The maximum amount of data bytes to be sent. + * + * \hideinitializer + */ +void uip_send(struct uip_stack *ustack, const void *data, int len); +void uip_appsend(struct uip_stack *ustack, const void *data, int len); + +/** + * The length of any incoming data that is currently avaliable (if avaliable) + * in the uip_appdata buffer. + * + * The test function uip_data() must first be used to check if there + * is any data available at all. + * + * \hideinitializer + */ +/*void uip_datalen(void);*/ +u16_t uip_datalen(struct uip_stack *ustack); + +/** + * The length of any out-of-band data (urgent data) that has arrived + * on the connection. + * + * \note The configuration parameter UIP_URGDATA must be set for this + * function to be enabled. + * + * \hideinitializer + */ +#define uip_urgdatalen() uip_urglen + +/** + * Close the current connection. + * + * This function will close the current connection in a nice way. + * + * \hideinitializer + */ +#define uip_close() (uip_flags = UIP_CLOSE) + +/** + * Abort the current connection. + * + * This function will abort (reset) the current connection, and is + * usually used when an error has occured that prevents using the + * uip_close() function. + * + * \hideinitializer + */ +#define uip_abort() (uip_flags = UIP_ABORT) + +/** + * Tell the sending host to stop sending data. + * + * This function will close our receiver's window so that we stop + * receiving data for the current connection. + * + * \hideinitializer + */ +#define uip_stop() (uip_conn->tcpstateflags |= UIP_STOPPED) + +/** + * Find out if the current connection has been previously stopped with + * uip_stop(). + * + * \hideinitializer + */ +#define uip_stopped(conn) ((conn)->tcpstateflags & UIP_STOPPED) + +/** + * Restart the current connection, if is has previously been stopped + * with uip_stop(). + * + * This function will open the receiver's window again so that we + * start receiving data for the current connection. + * + * \hideinitializer + */ +#define uip_restart() do { uip_flags |= UIP_NEWDATA; \ + uip_conn->tcpstateflags &= ~UIP_STOPPED; \ + } while (0) + +/* uIP tests that can be made to determine in what state the current + connection is, and what the application function should do. */ + +/** + * Is the current connection a UDP connection? + * + * This function checks whether the current connection is a UDP connection. + * + * \hideinitializer + * + */ +#define uip_udpconnection() (uip_conn == NULL) + +/** + * Function declarations for hte uip_flags + */ +/** + * Is new incoming data available? + * + * Will reduce to non-zero if there is new data for the application + * present at the uip_appdata pointer. The size of the data is + * avaliable through the uip_len variable. + * + * \hideinitializer + */ +int uip_newdata(struct uip_stack *ustack); + +/** + * Has previously sent data been acknowledged? + * + * Will reduce to non-zero if the previously sent data has been + * acknowledged by the remote host. This means that the application + * can send new data. + * + * \hideinitializer + */ +int uip_acked(struct uip_stack *ustack); + +/** + * Has the connection just been connected? + * + * Reduces to non-zero if the current connection has been connected to + * a remote host. This will happen both if the connection has been + * actively opened (with uip_connect()) or passively opened (with + * uip_listen()). + * + * \hideinitializer + */ +int uip_connected(struct uip_stack *ustack); + +/** + * Has the connection been closed by the other end? + * + * Is non-zero if the connection has been closed by the remote + * host. The application may then do the necessary clean-ups. + * + * \hideinitializer + */ +int uip_closed(struct uip_stack *ustack); + +/** + * Has the connection been aborted by the other end? + * + * Non-zero if the current connection has been aborted (reset) by the + * remote host. + * + * \hideinitializer + */ +int uip_aborted(struct uip_stack *ustack); + +/** + * Has the connection timed out? + * + * Non-zero if the current connection has been aborted due to too many + * retransmissions. + * + * \hideinitializer + */ +int uip_timedout(struct uip_stack *ustack); + +/** + * Do we need to retransmit previously data? + * + * Reduces to non-zero if the previously sent data has been lost in + * the network, and the application should retransmit it. The + * application should send the exact same data as it did the last + * time, using the uip_send() function. + * + * \hideinitializer + */ +int uip_rexmit(struct uip_stack *ustack); + +/** + * Is the connection being polled by uIP? + * + * Is non-zero if the reason the application is invoked is that the + * current connection has been idle for a while and should be + * polled. + * + * The polling event can be used for sending data without having to + * wait for the remote host to send data. + * + * \hideinitializer + */ +int uip_poll(struct uip_stack *ustack); + +/** + * Get the initial maxium segment size (MSS) of the current + * connection. + * + * \hideinitializer + */ +int uip_initialmss(struct uip_stack *ustack); + +/** + * Get the current maxium segment size that can be sent on the current + * connection. + * + * The current maxiumum segment size that can be sent on the + * connection is computed from the receiver's window and the MSS of + * the connection (which also is available by calling + * uip_initialmss()). + * + * \hideinitializer + */ +int uip_mss(struct uip_stack *ustack); + +/** + * Set up a new UDP connection. + * + * This function sets up a new UDP connection. The function will + * automatically allocate an unused local port for the new + * connection. However, another port can be chosen by using the + * uip_udp_bind() call, after the uip_udp_new() function has been + * called. + * + * Example: + \code + uip_ipaddr_t addr; + struct uip_udp_conn *c; + + uip_ipaddr(&addr, 192,168,2,1); + c = uip_udp_new(&addr, HTONS(12345)); + if(c != NULL) { + uip_udp_bind(c, HTONS(12344)); + } + \endcode + * \param ripaddr The IP address of the remote host. + * + * \param rport The remote port number in network byte order. + * + * \return The uip_udp_conn structure for the new connection or NULL + * if no connection could be allocated. + */ +struct uip_udp_conn *uip_udp_new(struct uip_stack *ustack, + uip_ip4addr_t *ripaddr, u16_t rport); + +/** + * Removed a UDP connection. + * + * \param conn A pointer to the uip_udp_conn structure for the connection. + * + * \hideinitializer + */ +#define uip_udp_remove(conn) ((conn)->lport = 0) + +/** + * Bind a UDP connection to a local port. + * + * \param conn A pointer to the uip_udp_conn structure for the + * connection. + * + * \param port The local port number, in network byte order. + * + * \hideinitializer + */ +#define uip_udp_bind(conn, port) ((conn)->lport = port) + +/** + * Send a UDP datagram of length len on the current connection. + * + * This function can only be called in response to a UDP event (poll + * or newdata). The data must be present in the uip_buf buffer, at the + * place pointed to by the uip_appdata pointer. + * + * \param len The length of the data in the uip_buf buffer. + * + * \hideinitializer + */ +#define uip_udp_send(len) uip_appsend((char *)uip_appdata, len) + +/** @} */ + +/* uIP convenience and converting functions. */ + +/** + * \defgroup uipconvfunc uIP conversion functions + * @{ + * + * These functions can be used for converting between different data + * formats used by uIP. + */ + +/** + * Construct an IP address from four bytes. + * + * This function constructs an IP address of the type that uIP handles + * internally from four bytes. The function is handy for specifying IP + * addresses to use with e.g. the uip_connect() function. + * + * Example: + \code + uip_ipaddr_t ipaddr; + struct uip_conn *c; + + uip_ipaddr(&ipaddr, 192,168,1,2); + c = uip_connect(&ipaddr, HTONS(80)); + \endcode + * + * \param addr A pointer to a uip_ipaddr_t variable that will be + * filled in with the IP address. + * + * \param addr0 The first octet of the IP address. + * \param addr1 The second octet of the IP address. + * \param addr2 The third octet of the IP address. + * \param addr3 The forth octet of the IP address. + * + * \hideinitializer + */ +#define uip_ipaddr(addr, addr0, addr1, addr2, addr3) do { \ + ((u16_t *)(addr))[0] = const_htons(((addr0) << 8) | (addr1)); \ + ((u16_t *)(addr))[1] = const_htons(((addr2) << 8) | (addr3)); \ + } while (0) + +/** + * Construct an IPv6 address from eight 16-bit words. + * + * This function constructs an IPv6 address. + * + * \hideinitializer + */ +#define uip_ip6addr(addr, addr0, addr1, addr2, addr3, addr4, addr5, addr6, \ + addr7) \ + do { \ + ((u16_t *)(addr))[0] = HTONS((addr0)); \ + ((u16_t *)(addr))[1] = HTONS((addr1)); \ + ((u16_t *)(addr))[2] = HTONS((addr2)); \ + ((u16_t *)(addr))[3] = HTONS((addr3)); \ + ((u16_t *)(addr))[4] = HTONS((addr4)); \ + ((u16_t *)(addr))[5] = HTONS((addr5)); \ + ((u16_t *)(addr))[6] = HTONS((addr6)); \ + ((u16_t *)(addr))[7] = HTONS((addr7)); \ + } while (0) + +/** + * Copy an IP address to another IP address. + * + * Copies an IP address from one place to another. + * + * Example: + \code + uip_ipaddr_t ipaddr1, ipaddr2; + + uip_ipaddr(&ipaddr1, 192,16,1,2); + uip_ipaddr_copy(&ipaddr2, &ipaddr1); + \endcode + * + * \param dest The destination for the copy. + * \param src The source from where to copy. + * + * \hideinitializer + */ +#define uip_ip4addr_copy(dest, src) memcpy(dest, src, sizeof(uip_ip4addr_t)) +#define uip_ip6addr_copy(dest, src) memcpy(dest, src, sizeof(uip_ip6addr_t)) + +/** + * Compare two IP addresses + * + * Compares two IP addresses. + * + * Example: + \code + uip_ipaddr_t ipaddr1, ipaddr2; + + uip_ipaddr(&ipaddr1, 192,16,1,2); + if(uip_ipaddr_cmp(&ipaddr2, &ipaddr1)) { + printf("They are the same"); + } + \endcode + * + * \param addr1 The first IP address. + * \param addr2 The second IP address. + * + * \hideinitializer + */ +#define uip_ip4addr_cmp(addr1, addr2) (memcmp(addr1, addr2, \ + sizeof(uip_ip4addr_t)) == 0) +#define uip_ip6addr_cmp(addr1, addr2) (memcmp(addr1, addr2, \ + sizeof(uip_ip6addr_t)) == 0) + +/** + * Compare two IP addresses with netmasks + * + * Compares two IP addresses with netmasks. The masks are used to mask + * out the bits that are to be compared. + * + * Example: + \code + uip_ipaddr_t ipaddr1, ipaddr2, mask; + + uip_ipaddr(&mask, 255,255,255,0); + uip_ipaddr(&ipaddr1, 192,16,1,2); + uip_ipaddr(&ipaddr2, 192,16,1,3); + if(uip_ipaddr_maskcmp(&ipaddr1, &ipaddr2, &mask)) { + printf("They are the same"); + } + \endcode + * + * \param addr1 The first IP address. + * \param addr2 The second IP address. + * \param mask The netmask. + * + * \hideinitializer + */ +#define uip_ip4addr_maskcmp(addr1, addr2, mask) \ + (((((u16_t *)addr1)[0] & ((u16_t *)mask)[0]) == \ + (((u16_t *)addr2)[0] & ((u16_t *)mask)[0])) && \ + ((((u16_t *)addr1)[1] & ((u16_t *)mask)[1]) == \ + (((u16_t *)addr2)[1] & ((u16_t *)mask)[1]))) + +/** + * Mask out the network part of an IP address. + * + * Masks out the network part of an IP address, given the address and + * the netmask. + * + * Example: + \code + uip_ipaddr_t ipaddr1, ipaddr2, netmask; + + uip_ipaddr(&ipaddr1, 192,16,1,2); + uip_ipaddr(&netmask, 255,255,255,0); + uip_ipaddr_mask(&ipaddr2, &ipaddr1, &netmask); + \endcode + * + * In the example above, the variable "ipaddr2" will contain the IP + * address 192.168.1.0. + * + * \param dest Where the result is to be placed. + * \param src The IP address. + * \param mask The netmask. + * + * \hideinitializer + */ +#define uip_ip4addr_mask(dest, src, mask) do { \ + ((u16_t *)dest)[0] = ((u16_t *)src)[0] & ((u16_t *)mask)[0]; \ + ((u16_t *)dest)[1] = ((u16_t *)src)[1] & ((u16_t *)mask)[1]; \ + } while (0) + +/** + * Pick the first octet of an IP address. + * + * Picks out the first octet of an IP address. + * + * Example: + \code + uip_ipaddr_t ipaddr; + u8_t octet; + + uip_ipaddr(&ipaddr, 1,2,3,4); + octet = uip_ipaddr1(&ipaddr); + \endcode + * + * In the example above, the variable "octet" will contain the value 1. + * + * \hideinitializer + */ +#define uip_ipaddr1(addr) (htons(((u16_t *)(addr))[0]) >> 8) + +/** + * Pick the second octet of an IP address. + * + * Picks out the second octet of an IP address. + * + * Example: + \code + uip_ipaddr_t ipaddr; + u8_t octet; + + uip_ipaddr(&ipaddr, 1,2,3,4); + octet = uip_ipaddr2(&ipaddr); + \endcode + * + * In the example above, the variable "octet" will contain the value 2. + * + * \hideinitializer + */ +#define uip_ipaddr2(addr) (htons(((u16_t *)(addr))[0]) & 0xff) + +/** + * Pick the third octet of an IP address. + * + * Picks out the third octet of an IP address. + * + * Example: + \code + uip_ipaddr_t ipaddr; + u8_t octet; + + uip_ipaddr(&ipaddr, 1,2,3,4); + octet = uip_ipaddr3(&ipaddr); + \endcode + * + * In the example above, the variable "octet" will contain the value 3. + * + * \hideinitializer + */ +#define uip_ipaddr3(addr) (htons(((u16_t *)(addr))[1]) >> 8) + +/** + * Pick the fourth octet of an IP address. + * + * Picks out the fourth octet of an IP address. + * + * Example: + \code + uip_ipaddr_t ipaddr; + u8_t octet; + + uip_ipaddr(&ipaddr, 1,2,3,4); + octet = uip_ipaddr4(&ipaddr); + \endcode + * + * In the example above, the variable "octet" will contain the value 4. + * + * \hideinitializer + */ +#define uip_ipaddr4(addr) (htons(((u16_t *)(addr))[1]) & 0xff) + +/** + * Convert 16-bit quantity from host byte order to network byte order. + * + * This macro is primarily used for converting constants from host + * byte order to network byte order. For converting variables to + * network byte order, use the htons() function instead. + * + * \hideinitializer + */ +#if 0 +#ifndef HTONS +# if UIP_BYTE_ORDER == UIP_BIG_ENDIAN +# define HTONS(n) (n) +# else /* UIP_BYTE_ORDER == UIP_BIG_ENDIAN */ +# define HTONS(n) (u16_t)((((u16_t) (n)) << 8) | (((u16_t) (n)) >> 8)) +# endif /* UIP_BYTE_ORDER == UIP_BIG_ENDIAN */ +#else +#error "HTONS already defined!" +#endif /* HTONS */ +#endif + +#if UIP_BYTE_ORDER == UIP_BIG_ENDIAN +# error "Should not be here" +# define const_htons(n) (n) +# else /* UIP_BYTE_ORDER == UIP_BIG_ENDIAN */ +# define const_htons(n) (u16_t)((((u16_t) (n)) << 8) | (((u16_t) (n)) >> 8)) +# endif /* UIP_BYTE_ORDER == UIP_BIG_ENDIAN */ + +/* BWL */ +#if 0 +/** + * Convert 16-bit quantity from host byte order to network byte order. + * + * This function is primarily used for converting variables from host + * byte order to network byte order. For converting constants to + * network byte order, use the HTONS() macro instead. + */ +#ifndef htons +u16_t htons(u16_t val); +#endif /* htons */ +#ifndef ntohs +#define ntohs htons +#endif +#endif + +/** @} */ + +/** + * Pointer to the application data in the packet buffer. + * + * This pointer points to the application data when the application is + * called. If the application wishes to send data, the application may + * use this space to write the data into before calling uip_send(). + */ +/* extern void *uip_appdata; */ + +#if UIP_URGDATA > 0 +/* u8_t *uip_urgdata: + * + * This pointer points to any urgent data that has been received. Only + * present if compiled with support for urgent data (UIP_URGDATA). + */ +extern void *uip_urgdata; +#endif /* UIP_URGDATA > 0 */ + +/** + * \defgroup uipdrivervars Variables used in uIP device drivers + * @{ + * + * uIP has a few global variables that are used in device drivers for + * uIP. + */ + +/** + * The length of the packet in the uip_buf buffer. + * + * The global variable uip_len holds the length of the packet in the + * uip_buf buffer. + * + * When the network device driver calls the uIP input function, + * uip_len should be set to the length of the packet in the uip_buf + * buffer. + * + * When sending packets, the device driver should use the contents of + * the uip_len variable to determine the length of the outgoing + * packet. + * + */ +/* extern u16_t uip_len; */ + +/** @} */ + +#if UIP_URGDATA > 0 +extern u16_t uip_urglen, uip_surglen; +#endif /* UIP_URGDATA > 0 */ + +/** + * Representation of a uIP TCP connection. + * + * The uip_conn structure is used for identifying a connection. All + * but one field in the structure are to be considered read-only by an + * application. The only exception is the appstate field whos purpose + * is to let the application store application-specific state (e.g., + * file pointers) for the connection. The type of this field is + * configured in the "uipopt.h" header file. + */ +struct __attribute__ ((__packed__)) uip_conn { + uip_ip4addr_t ripaddr; + uip_ip6addr_t ripaddr6; + /**< The IP address of the remote host. */ + + u16_t lport; /**< The local TCP port, in network byte order. */ + u16_t rport; /**< The local remote TCP port, in network byte + order. */ + + u8_t rcv_nxt[4]; + /**< The sequence number that we expect to + receive next. */ + u8_t snd_nxt[4]; + /**< The sequence number that was last sent by + us. */ + u16_t len; /**< Length of the data that was previously sent. */ + u16_t mss; /**< Current maximum segment size for the + connection. */ + u16_t initialmss; + /**< Initial maximum segment size for the + connection. */ + u8_t sa; /**< Retransmission time-out calculation state + variable. */ + u8_t sv; /**< Retransmission time-out calculation state + variable. */ + u8_t rto; /**< Retransmission time-out. */ + u8_t tcpstateflags; + /**< TCP state and flags. */ + u8_t timer; /**< The retransmission timer. */ + u8_t nrtx; /**< The number of retransmissions for the last + segment sent. */ +}; + +/** + * \addtogroup uiparch + * @{ + */ + +/** + * 4-byte array used for the 32-bit sequence number calculations. + */ +extern u8_t uip_acc32[4]; + +/** @} */ + +#if UIP_UDP +/** + * Representation of a uIP UDP connection. + */ +struct uip_udp_conn { + uip_ip4addr_t ripaddr; + /**< The IP address of the remote peer. */ + u16_t lport; /**< The local port number in network byte order. */ + u16_t rport; /**< The remote port number in network byte order. */ + u8_t ttl; /**< Default time-to-live. */ + + /** The application state. */ +/* uip_udp_appstate_t appstate; */ +}; + +#endif /* UIP_UDP */ + +/** + * The structure holding the TCP/IP statistics that are gathered if + * UIP_STATISTICS is set to 1. + * + */ +struct uip_stats { + struct { + uip_stats_t drop; + /**< Number of dropped packets at the IP + layer. */ + uip_stats_t recv; + /**< Number of received packets at the IP + layer. */ + uip_stats_t sent; + /**< Number of sent packets at the IP + layer. */ + uip_stats_t vhlerr; + /**< Number of packets dropped due to wrong + IP version or header length. */ + uip_stats_t hblenerr; + /**< Number of packets dropped due to wrong + IP length, high byte. */ + uip_stats_t lblenerr; + /**< Number of packets dropped due to wrong + IP length, low byte. */ + uip_stats_t fragerr; + /**< Number of packets dropped since they + were IP fragments. */ + uip_stats_t chkerr; + /**< Number of packets dropped due to IP + checksum errors. */ + uip_stats_t protoerr; + /**< Number of packets dropped since they + were neither ICMP, UDP nor TCP. */ + } ip; /**< IP statistics. */ + struct { + uip_stats_t drop; + /**< Number of dropped ICMP packets. */ + uip_stats_t recv; + /**< Number of received ICMP packets. */ + uip_stats_t sent; + /**< Number of sent ICMP packets. */ + uip_stats_t typeerr; + /**< Number of ICMP packets with a wrong + type. */ + } icmp; /**< ICMP statistics. */ + struct { + uip_stats_t drop; + /**< Number of dropped TCP segments. */ + uip_stats_t recv; + /**< Number of recived TCP segments. */ + uip_stats_t sent; + /**< Number of sent TCP segments. */ + uip_stats_t chkerr; + /**< Number of TCP segments with a bad + checksum. */ + uip_stats_t ackerr; + /**< Number of TCP segments with a bad ACK + number. */ + uip_stats_t rst; + /**< Number of recevied TCP RST (reset) segments. */ + uip_stats_t rexmit; + /**< Number of retransmitted TCP segments. */ + uip_stats_t syndrop; + /**< Number of dropped SYNs due to too few + connections was avaliable. */ + uip_stats_t synrst; + /**< Number of SYNs for closed ports, + triggering a RST. */ + } tcp; /**< TCP statistics. */ +#if UIP_UDP + struct { + uip_stats_t drop; + /**< Number of dropped UDP segments. */ + uip_stats_t recv; + /**< Number of recived UDP segments. */ + uip_stats_t sent; + /**< Number of sent UDP segments. */ + uip_stats_t chkerr; + /**< Number of UDP segments with a bad + checksum. */ + } udp; /**< UDP statistics. */ +#endif /* UIP_UDP */ +}; + +/*---------------------------------------------------------------------------*/ +/* All the stuff below this point is internal to uIP and should not be + * used directly by an application or by a device driver. + */ +/*---------------------------------------------------------------------------*/ +/* u8_t uip_flags: + * + * When the application is called, uip_flags will contain the flags + * that are defined in this file. Please read below for more + * infomation. + */ +/* extern u8_t uip_flags; */ + +/* The following flags may be set in the global variable uip_flags + before calling the application callback. The UIP_ACKDATA, + UIP_NEWDATA, and UIP_CLOSE flags may both be set at the same time, + whereas the others are mutualy exclusive. Note that these flags + should *NOT* be accessed directly, but only through the uIP + functions/macros. */ + +#define UIP_ACKDATA 1 /* Signifies that the outstanding data was + acked and the application should send + out new data instead of retransmitting + the last data. */ +#define UIP_NEWDATA 2 /* Flags the fact that the peer has sent + us new data. */ +#define UIP_REXMIT 4 /* Tells the application to retransmit the + data that was last sent. */ +#define UIP_POLL 8 /* Used for polling the application, to + check if the application has data that + it wants to send. */ +#define UIP_CLOSE 16 /* The remote host has closed the + connection, thus the connection has + gone away. Or the application signals + that it wants to close the + connection. */ +#define UIP_ABORT 32 /* The remote host has aborted the + connection, thus the connection has + gone away. Or the application signals + that it wants to abort the + connection. */ +#define UIP_CONNECTED 64 /* We have got a connection from a remote + host and have set up a new connection + for it, or an active connection has + been successfully established. */ + +#define UIP_TIMEDOUT 128 /* The connection has been aborted due to + too many retransmissions. */ + +void uip_input(struct uip_stack *ustack); +void uip_periodic(struct uip_stack *ustack, int conn); + +/* uip_process(flag): + * + * The actual uIP function which does all the work. + */ +void uip_process(struct uip_stack *ustack, u8_t flag); + +/* The following flags are passed as an argument to the uip_process() + function. They are used to distinguish between the two cases where + uip_process() is called. It can be called either because we have + incoming data that should be processed, or because the periodic + timer has fired. These values are never used directly, but only in + the macrose defined in this file. */ + +#define UIP_DATA 1 /* Tells uIP that there is incoming + data in the uip_buf buffer. The + length of the data is stored in the + global variable uip_len. */ +#define UIP_TIMER 2 /* Tells uIP that the periodic timer + has fired. */ +#define UIP_POLL_REQUEST 3 /* Tells uIP that a connection should + be polled. */ +#define UIP_UDP_SEND_CONN 4 /* Tells uIP that a UDP datagram + should be constructed in the + uip_buf buffer. */ +#if UIP_UDP +#define UIP_UDP_TIMER 5 +#endif /* UIP_UDP */ + +#define UIP_NDP_TIMER 6 + +/* The TCP states used in the uip_conn->tcpstateflags. */ +#define UIP_CLOSED 0 +#define UIP_SYN_RCVD 1 +#define UIP_SYN_SENT 2 +#define UIP_ESTABLISHED 3 +#define UIP_FIN_WAIT_1 4 +#define UIP_FIN_WAIT_2 5 +#define UIP_CLOSING 6 +#define UIP_TIME_WAIT 7 +#define UIP_LAST_ACK 8 +#define UIP_TS_MASK 15 + +#define UIP_STOPPED 16 + +struct __attribute__ ((__packed__)) uip_tcp_hdr { + /* TCP header. */ + u16_t srcport, destport; + u8_t seqno[4], ackno[4], tcpoffset, flags, wnd[2]; + u16_t tcpchksum; + u8_t urgp[2]; + u8_t optdata[4]; +}; + +struct __attribute__ ((__packed__)) uip_ipv4_hdr { + /* IPv4 header. */ + u8_t vhl, tos, len[2], ipid[2], ipoffset[2], ttl, proto; + u16_t ipchksum; + u16_t srcipaddr[2], destipaddr[2]; +}; + +struct __attribute__ ((__packed__)) uip_ipv6_hdr { + /* IPv6 header. */ + u8_t vtc, tcflow; + u16_t flow; + u16_t len; + u8_t proto, ttl; + uip_ip6addr_t srcipaddr, destipaddr; +}; + +/* The TCP and IPv4 headers. */ +struct __attribute__ ((__packed__)) uip_tcp_ipv4_hdr { + /* IPv4 header. */ + u8_t vhl, tos, len[2], ipid[2], ipoffset[2], ttl, proto; + u16_t ipchksum; + u16_t srcipaddr[2], destipaddr[2]; + + /* TCP header. */ + u16_t srcport, destport; + u8_t seqno[4], ackno[4], tcpoffset, flags, wnd[2]; + u16_t tcpchksum; + u8_t urgp[2]; + u8_t optdata[4]; +}; + +/* The TCP and IP headers. */ +struct __attribute__ ((__packed__)) uip_tcp_ipv6_hdr { + /* IPv6 header. */ + u8_t vtc, tcflow; + u16_t flow; + u8_t len[2]; + u8_t proto, ttl; + uip_ip6addr_t srcipaddr, destipaddr; + + /* TCP header. */ + u16_t srcport, destport; + u8_t seqno[4], ackno[4], tcpoffset, flags, wnd[2]; + u16_t tcpchksum; + u8_t urgp[2]; + u8_t optdata[4]; +}; + +/* The ICMPv4 */ +struct __attribute__ ((__packed__)) uip_icmpv4_hdr { + /* ICMP (echo) header. */ + u8_t type, icode; + u16_t icmpchksum; + u16_t id, seqno; +}; + +/* The ICMPv6 */ +struct __attribute__ ((__packed__)) uip_icmpv6_hdr { + /* ICMP (echo) header. */ + u8_t type, icode; + u16_t icmpchksum; + u8_t flags, reserved1, reserved2, reserved3; + u8_t icmp6data[16]; + u8_t options[1]; +}; + +/* The ICMP and IP headers. */ +struct __attribute__ ((__packed__)) uip_icmpip_hdr { +#if UIP_CONF_IPV6 + /* IPv6 header. */ + u8_t vtc, tcf; + u16_t flow; + u8_t len[2]; + u8_t proto, ttl; + uip_ip6addr_t srcipaddr, destipaddr; +#else /* UIP_CONF_IPV6 */ + /* IPv4 header. */ + u8_t vhl, tos, len[2], ipid[2], ipoffset[2], ttl, proto; + u16_t ipchksum; + u16_t srcipaddr[2], destipaddr[2]; +#endif /* UIP_CONF_IPV6 */ + + /* ICMP (echo) header. */ + u8_t type, icode; + u16_t icmpchksum; +#if !UIP_CONF_IPV6 + u16_t id, seqno; +#else /* !UIP_CONF_IPV6 */ + u8_t flags, reserved1, reserved2, reserved3; + u8_t icmp6data[16]; + u8_t options[1]; +#endif /* !UIP_CONF_IPV6 */ +}; + +/* The UDP */ +struct __attribute__ ((__packed__)) uip_udp_hdr { + /* UDP header. */ + u16_t srcport, destport; + u16_t udplen; + u16_t udpchksum; +}; + +/* The UDP and IP headers. */ +struct __attribute__ ((__packed__)) uip_udpip_hdr { +#if UIP_CONF_IPV6 + /* IPv6 header. */ + u8_t vtc, tcf; + u16_t flow; + u8_t len[2]; + u8_t proto, ttl; + uip_ip6addr_t srcipaddr, destipaddr; +#else /* UIP_CONF_IPV6 */ + /* IP header. */ + u8_t vhl, tos, len[2], ipid[2], ipoffset[2], ttl, proto; + u16_t ipchksum; + u16_t srcipaddr[2], destipaddr[2]; +#endif /* UIP_CONF_IPV6 */ + + /* UDP header. */ + u16_t srcport, destport; + u16_t udplen; + u16_t udpchksum; +}; + +/** + * The buffer size available for user data in the \ref uip_buf buffer. + * + * This macro holds the available size for user data in the \ref + * uip_buf buffer. The macro is intended to be used for checking + * bounds of available user data. + * + * Example: + \code + snprintf(uip_appdata, UIP_APPDATA_SIZE, "%u\n", i); + \endcode + * + * \hideinitializer + */ +#define UIP_APPDATA_SIZE (UIP_BUFSIZE - UIP_LLH_LEN - UIP_TCPIP_HLEN) + +#define UIP_PROTO_ICMP 1 +#define UIP_PROTO_TCP 6 +#define UIP_PROTO_UDP 17 +#define UIP_PROTO_ICMP6 58 + +/* Header sizes. */ +#define UIP_IPv6_H_LEN 40 /* Size of IPv6 header */ +#define UIP_IPv4_H_LEN 20 /* Size of IPv4 header */ + +#define UIP_UDPH_LEN 8 /* Size of UDP header */ +#define UIP_TCPH_LEN 20 /* Size of TCP header */ + +#define UIP_IPv4_UDPH_LEN (UIP_UDPH_LEN + UIP_IPv4_H_LEN) /* Size of IPv4 + + UDP + header */ +#define UIP_IPv4_TCPH_LEN (UIP_TCPH_LEN + UIP_IPv4_H_LEN) /* Size of IPv4 + + TCP + header */ +#define UIP_TCP_IPv4_HLEN UIP_IPv4_TCPH_LEN + +#define UIP_IPv6_UDPH_LEN (UIP_UDPH_LEN + UIP_IPv6_H_LEN) /* Size of IPv6 + + UDP + header */ +#define UIP_IPv6_TCPH_LEN (UIP_TCPH_LEN + UIP_IPv6_H_LEN) /* Size of IPv6 + + TCP + header */ +#define UIP_TCP_IPv6_HLEN UIP_IPv6_TCPH_LEN + +/** + * Calculate the Internet checksum over a buffer. + * + * The Internet checksum is the one's complement of the one's + * complement sum of all 16-bit words in the buffer. + * + * See RFC1071. + * + * \param buf A pointer to the buffer over which the checksum is to be + * computed. + * + * \param len The length of the buffer over which the checksum is to + * be computed. + * + * \return The Internet checksum of the buffer. + */ +u16_t uip_chksum(u16_t *buf, u16_t len); + +/** + * Calculate the IP header checksum of the packet header in uip_buf. + * + * The IP header checksum is the Internet checksum of the 20 bytes of + * the IP header. + * + * \return The IP header checksum of the IP header in the uip_buf + * buffer. + */ +u16_t uip_ipchksum(struct uip_stack *ustack); + +/** + * Calculate the TCP checksum of the packet in uip_buf and uip_appdata. + * + * The TCP checksum is the Internet checksum of data contents of the + * TCP segment, and a pseudo-header as defined in RFC793. + * + * \return The TCP checksum of the TCP segment in uip_buf and pointed + * to by uip_appdata. + */ +u16_t uip_tcpchksum(struct uip_stack *ustack); + +/** + * Calculate the UDP checksum of the packet in uip_buf and uip_appdata. + * + * The UDP checksum is the Internet checksum of data contents of the + * UDP segment, and a pseudo-header as defined in RFC768. + * + * \return The UDP checksum of the UDP segment in uip_buf and pointed + * to by uip_appdata. + */ +u16_t uip_udpchksum(struct uip_stack *ustack); + +/* IPv6 checksum */ +uint16_t icmpv6_checksum(uint8_t *data); + +struct neighbor_entry { + struct in6_addr ipaddr; + struct uip_eth_addr mac_addr; + u8_t time; +}; + +struct uip_stack { + struct uip_eth_addr uip_ethaddr; + + u8_t *uip_buf; + + uint8_t *data_link_layer; /* Pointer to the data link layer */ + uint8_t *network_layer; /* Pointer to the network layer */ + void *uip_appdata; /* The uip_appdata pointer points to + application data. */ + void *uip_sappdata; /* The uip_appdata pointer points to + the application data which is to + be sent. */ +#if UIP_URGDATA > 0 + void *uip_urgdata; /* The uip_urgdata pointer points to + urgent data (out-of-band data), if + present. */ + u16_t uip_urglen, uip_surglen; +#endif /* UIP_URGDATA > 0 */ + + u16_t uip_len, uip_slen; /* The uip_len is either 8 or 16 bits, + depending on the maximum packet + size. */ + u8_t uip_flags; /* The uip_flags variable is used for + communication between the TCP/IP stack + and the application program. */ + struct uip_conn *uip_conn; /* uip_conn always points to the current + connection. */ + + struct uip_conn uip_conns[UIP_CONNS]; + /* The uip_conns array holds all TCP + connections. */ + u16_t uip_listenports[UIP_LISTENPORTS]; + /* The uip_listenports list all currently + listning ports. */ +#if UIP_UDP + struct uip_udp_conn *uip_udp_conn; + struct uip_udp_conn uip_udp_conns[UIP_UDP_CONNS]; +#endif /* UIP_UDP */ + + u16_t ipid; /* This ipid variable is an increasing + number that is used for the IP ID + field. */ + + u8_t iss[4]; /* The iss variable is used for the TCP + initial sequence number. */ + +#if UIP_ACTIVE_OPEN + u16_t lastport; /* Keeps track of the last port used for + a new connection. */ +#endif /* UIP_ACTIVE_OPEN */ + +#define IP_CONFIG_OFF 0x00 +#define IPV4_CONFIG_OFF 0x01 +#define IPV4_CONFIG_STATIC 0x02 +#define IPV4_CONFIG_DHCP 0x04 +#define IPV6_CONFIG_OFF 0x10 +#define IPV6_CONFIG_STATIC 0x20 +#define IPV6_CONFIG_DHCP 0x40 + u8_t ip_config; + + uip_ip4addr_t hostaddr, netmask, default_route_addr; + uip_ip6addr_t hostaddr6, netmask6, default_route_addr6, + linklocal6; + int prefix_len; + u8_t ipv6_autocfg; +#define IPV6_AUTOCFG_DHCPV6 (1<<0) +#define IPV6_AUTOCFG_ND (1<<1) +#define IPV6_AUTOCFG_NOTSPEC (1<<6) +#define IPV6_AUTOCFG_NOTUSED (1<<7) + u8_t linklocal_autocfg; +#define IPV6_LL_AUTOCFG_ON (1<<0) +#define IPV6_LL_AUTOCFG_OFF (1<<1) +#define IPV6_LL_AUTOCFG_NOTSPEC (1<<6) +#define IPV6_LL_AUTOCFG_NOTUSED (1<<7) + u8_t router_autocfg; +#define IPV6_RTR_AUTOCFG_ON (1<<0) +#define IPV6_RTR_AUTOCFG_OFF (1<<1) +#define IPV6_RTR_AUTOCFG_NOTSPEC (1<<6) +#define IPV6_RTR_AUTOCFG_NOTUSED (1<<7) + +#define UIP_NEIGHBOR_ENTRIES 8 + struct neighbor_entry neighbor_entries[UIP_NEIGHBOR_ENTRIES]; + + struct uip_stats stats; + + u8_t opt; + + pthread_mutex_t lock; + + /* IPv6 support */ +#define UIP_SUPPORT_IPv6_ENABLED 0x01 +#define UIP_SUPPORT_IPv6_DISABLED 0x02 + u8_t enable_IPv6; + + /* DHCPC client attached */ + void *dhcpc; + + /* NDP client */ + void *ndpc; +}; + +/******************************************************************************* + * IPv6 Support + ******************************************************************************/ +int set_ipv6_link_local_address(struct uip_stack *ustack); +int is_ipv6_link_local_address(uip_ip6addr_t *addr); + +void dump_uip_packet(struct uip_stack *ustack); + +#endif /* __UIP_H__ */ + +/** @} */ diff --git a/iscsiuio/src/uip/uip_arch.h b/iscsiuio/src/uip/uip_arch.h new file mode 100644 index 0000000..3ddacec --- /dev/null +++ b/iscsiuio/src/uip/uip_arch.h @@ -0,0 +1,137 @@ +/** + * \addtogroup uip + * {@ + */ + +/** + * \defgroup uiparch Architecture specific uIP functions + * @{ + * + * The functions in the architecture specific module implement the IP + * check sum and 32-bit additions. + * + * The IP checksum calculation is the most computationally expensive + * operation in the TCP/IP stack and it therefore pays off to + * implement this in efficient assembler. The purpose of the uip-arch + * module is to let the checksum functions to be implemented in + * architecture specific assembler. + * + */ + +/** + * \file + * Declarations of architecture specific functions. + * \author Adam Dunkels + */ + +/* + * Copyright (c) 2001, Adam Dunkels. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack. + * + * + */ + +#ifndef __UIP_ARCH_H__ +#define __UIP_ARCH_H__ + +#include "uip.h" + +/** + * Carry out a 32-bit addition. + * + * Because not all architectures for which uIP is intended has native + * 32-bit arithmetic, uIP uses an external C function for doing the + * required 32-bit additions in the TCP protocol processing. This + * function should add the two arguments and place the result in the + * global variable uip_acc32. + * + * \note The 32-bit integer pointed to by the op32 parameter and the + * result in the uip_acc32 variable are in network byte order (big + * endian). + * + * \param op32 A pointer to a 4-byte array representing a 32-bit + * integer in network byte order (big endian). + * + * \param op16 A 16-bit integer in host byte order. + */ +void uip_add32(u8_t *op32, u16_t op16, u8_t *uip_add32); + +/** + * Calculate the Internet checksum over a buffer. + * + * The Internet checksum is the one's complement of the one's + * complement sum of all 16-bit words in the buffer. + * + * See RFC1071. + * + * \note This function is not called in the current version of uIP, + * but future versions might make use of it. + * + * \param buf A pointer to the buffer over which the checksum is to be + * computed. + * + * \param len The length of the buffer over which the checksum is to + * be computed. + * + * \return The Internet checksum of the buffer. + */ +u16_t uip_chksum(u16_t *buf, u16_t len); + +/** + * Calculate the IP header checksum of the packet header in uip_buf. + * + * The IP header checksum is the Internet checksum of the 20 bytes of + * the IP header. + * + * \return The IP header checksum of the IP header in the uip_buf + * buffer. + */ +u16_t uip_ipchksum(struct uip_stack *ustack); + +/** + * Calculate the TCP checksum of the packet in uip_buf and uip_appdata. + * + * The TCP checksum is the Internet checksum of data contents of the + * TCP segment, and a pseudo-header as defined in RFC793. + * + * \note The uip_appdata pointer that points to the packet data may + * point anywhere in memory, so it is not possible to simply calculate + * the Internet checksum of the contents of the uip_buf buffer. + * + * \return The TCP checksum of the TCP segment in uip_buf and pointed + * to by uip_appdata. + */ +u16_t uip_tcpchksum(struct uip_stack *ustack); + +u16_t uip_udpchksum(struct uip_stack *ustack); + +/** @} */ +/** @} */ + +#endif /* __UIP_ARCH_H__ */ diff --git a/iscsiuio/src/uip/uip_arp.c b/iscsiuio/src/uip/uip_arp.c new file mode 100644 index 0000000..1b3761f --- /dev/null +++ b/iscsiuio/src/uip/uip_arp.c @@ -0,0 +1,479 @@ +#include +#include +#include + +#include "logger.h" +#include "packet.h" + +/** + * \addtogroup uip + * @{ + */ + +/** + * \defgroup uiparp uIP Address Resolution Protocol + * @{ + * + * The Address Resolution Protocol ARP is used for mapping between IP + * addresses and link level addresses such as the Ethernet MAC + * addresses. ARP uses broadcast queries to ask for the link level + * address of a known IP address and the host which is configured with + * the IP address for which the query was meant, will respond with its + * link level address. + * + * \note This ARP implementation only supports Ethernet. + */ + +/** + * \file + * Implementation of the ARP Address Resolution Protocol. + * \author Adam Dunkels + * + */ + +/* + * Copyright (c) 2001-2003, Adam Dunkels. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack. + * + * + */ + +#include "uip_arp.h" +#include "uip_eth.h" + +#include +#include + +static const struct uip_eth_addr broadcast_ethaddr = { + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff} }; +static const u16_t broadcast_ipaddr[2] = { 0xffff, 0xffff }; + +pthread_mutex_t arp_table_mutex = PTHREAD_MUTEX_INITIALIZER; +static struct arp_entry arp_table[UIP_ARPTAB_SIZE]; + +static u8_t arptime; + +/** + * Initialize the ARP module. + * + */ +/*----------------------------------------------------------------------------*/ +void uip_arp_init(void) +{ + u8_t i; + for (i = 0; i < UIP_ARPTAB_SIZE; ++i) + memset(&arp_table[i], 0, sizeof(arp_table[i])); + + pthread_mutex_init(&arp_table_mutex, NULL); +} + +/*----------------------------------------------------------------------------*/ +/** + * Periodic ARP processing function. + * + * This function performs periodic timer processing in the ARP module + * and should be called at regular intervals. The recommended interval + * is 10 seconds between the calls. + * + */ +/*----------------------------------------------------------------------------*/ +void uip_arp_timer(void) +{ + u8_t i; + struct arp_entry *tabptr; + + ++arptime; + for (i = 0; i < UIP_ARPTAB_SIZE; ++i) { + tabptr = &arp_table[i]; + if ((tabptr->ipaddr[0] | tabptr->ipaddr[1]) != 0 && + (u8_t)(arptime - tabptr->time) >= UIP_ARP_MAXAGE) + memset(tabptr->ipaddr, 0, 4); + } + +} + +/*----------------------------------------------------------------------------*/ +static void uip_arp_update(u16_t *ipaddr, struct uip_eth_addr *ethaddr) +{ + u8_t i; + struct arp_entry *tabptr; + + pthread_mutex_lock(&arp_table_mutex); + /* Walk through the ARP mapping table and try to find an entry to + update. If none is found, the IP -> MAC address mapping is + inserted in the ARP table. */ + for (i = 0; i < UIP_ARPTAB_SIZE; ++i) { + + tabptr = &arp_table[i]; + /* Only check those entries that are actually in use. */ + if (tabptr->ipaddr[0] != 0 && tabptr->ipaddr[1] != 0) { + + /* Check if the source IP address of the incoming packet + matches the IP address in this ARP table entry. */ + if (ipaddr[0] == tabptr->ipaddr[0] && + ipaddr[1] == tabptr->ipaddr[1]) { + + tabptr->time = arptime; + + pthread_mutex_unlock(&arp_table_mutex); + return; + } + } + } + + /* If we get here, no existing ARP table entry was found, so we + create one. */ + + /* First, we try to find an unused entry in the ARP table. */ + for (i = 0; i < UIP_ARPTAB_SIZE; ++i) { + tabptr = &arp_table[i]; + if (tabptr->ipaddr[0] == 0 && tabptr->ipaddr[1] == 0) + break; + } + + /* If no unused entry is found, we try to find the oldest entry and + throw it away. */ + if (i == UIP_ARPTAB_SIZE) { + u8_t c; + u8_t tmpage = 0; + c = 0; + for (i = 0; i < UIP_ARPTAB_SIZE; ++i) { + tabptr = &arp_table[i]; + if ((u8_t)(arptime - tabptr->time) > tmpage) { + tmpage = (u8_t)(arptime - tabptr->time); + c = i; + } + } + i = c; + tabptr = &arp_table[i]; + } + + /* Now, i is the ARP table entry which we will fill with the new + information. */ + memcpy(tabptr->ipaddr, ipaddr, 4); + memcpy(tabptr->ethaddr.addr, ethaddr->addr, 6); + tabptr->time = arptime; + + pthread_mutex_unlock(&arp_table_mutex); +} + +/** + * ARP processing for incoming ARP packets. + * + * This function should be called by the device driver when an ARP + * packet has been received. The function will act differently + * depending on the ARP packet type: if it is a reply for a request + * that we previously sent out, the ARP cache will be filled in with + * the values from the ARP reply. If the incoming ARP packet is an ARP + * request for our IP address, an ARP reply packet is created and put + * into the uip_buf[] buffer. + * + * When the function returns, the value of the global variable uip_len + * indicates whether the device driver should send out a packet or + * not. If uip_len is zero, no packet should be sent. If uip_len is + * non-zero, it contains the length of the outbound packet that is + * present in the uip_buf[] buffer. + * + * This function expects an ARP packet with a prepended Ethernet + * header in the uip_buf[] buffer, and the length of the packet in the + * global variable uip_len. + */ +void uip_arp_ipin(struct uip_stack *ustack, packet_t *pkt) +{ + struct ip_hdr *ip; + struct uip_eth_hdr *eth; + + eth = (struct uip_eth_hdr *)pkt->data_link_layer; + ip = (struct ip_hdr *)pkt->network_layer; + + if (uip_ip4addr_cmp(ip->destipaddr, ustack->hostaddr)) { + /* First, we register the one who made the request in our ARP + table, since it is likely that we will do more communication + with this host in the future. */ + uip_arp_update(ip->srcipaddr, ð->src); + } +} + +void +uip_arp_arpin(nic_interface_t *nic_iface, + struct uip_stack *ustack, packet_t *pkt) +{ + struct arp_hdr *arp; + struct uip_eth_hdr *eth; + + if (pkt->buf_size < sizeof(struct arp_hdr)) { + pkt->buf_size = 0; + return; + } + pkt->buf_size = 0; + + eth = (struct uip_eth_hdr *)pkt->data_link_layer; + arp = (struct arp_hdr *)pkt->network_layer; + + switch (arp->opcode) { + case const_htons(ARP_REQUEST): + /* ARP request. If it asked for our address, we send out a + reply. */ + if (uip_ip4addr_cmp(arp->dipaddr, ustack->hostaddr)) { + /* First, we register the one who made the request in + our ARP table, since it is likely that we will do + more communication with this host in the future. */ + uip_arp_update(arp->sipaddr, &arp->shwaddr); + + /* The reply opcode is 2. */ + arp->opcode = htons(2); + + memcpy(arp->dhwaddr.addr, arp->shwaddr.addr, 6); + memcpy(arp->shwaddr.addr, ustack->uip_ethaddr.addr, 6); + memcpy(eth->src.addr, ustack->uip_ethaddr.addr, 6); + memcpy(eth->dest.addr, arp->dhwaddr.addr, 6); + + arp->dipaddr[0] = arp->sipaddr[0]; + arp->dipaddr[1] = arp->sipaddr[1]; + arp->sipaddr[0] = ustack->hostaddr[0]; + arp->sipaddr[1] = ustack->hostaddr[1]; + + if (nic_iface->vlan_id == 0) { + eth->type = htons(UIP_ETHTYPE_ARP); + pkt->buf_size = sizeof(*arp) + sizeof(*eth); + } else { + eth->type = htons(UIP_ETHTYPE_8021Q); + pkt->buf_size = sizeof(*arp) + + sizeof(struct uip_vlan_eth_hdr); + } + } + break; + case const_htons(ARP_REPLY): + uip_arp_update(arp->sipaddr, &arp->shwaddr); + break; + default: + LOG_WARN("Unknown ARP opcode: %d", ntohs(arp->opcode)); + break; + } + + return; +} + +/** + * Prepend Ethernet header to an outbound IP packet and see if we need + * to send out an ARP request. + * + * This function should be called before sending out an IP packet. The + * function checks the destination IP address of the IP packet to see + * what Ethernet MAC address that should be used as a destination MAC + * address on the Ethernet. + * + * If the destination IP address is in the local network (determined + * by logical ANDing of netmask and our IP address), the function + * checks the ARP cache to see if an entry for the destination IP + * address is found. If so, an Ethernet header is prepended and the + * function returns. If no ARP cache entry is found for the + * destination IP address, the packet in the uip_buf[] is replaced by + * an ARP request packet for the IP address. The IP packet is dropped + * and it is assumed that they higher level protocols (e.g., TCP) + * eventually will retransmit the dropped packet. + * + * If the destination IP address is not on the local network, the IP + * address of the default router is used instead. + * + * When the function returns, a packet is present in the uip_buf[] + * buffer, and the length of the packet is in the global variable + * uip_len. + */ + +dest_ipv4_addr_t +uip_determine_dest_ipv4_addr(struct uip_stack *ustack, u16_t *ipaddr) +{ + struct uip_eth_hdr *eth; + struct ip_hdr *ip_buf; + + eth = (struct uip_eth_hdr *)ustack->data_link_layer; + ip_buf = (struct ip_hdr *)ustack->network_layer; + + /* Find the destination IP address in the ARP table and construct + the Ethernet header. If the destination IP addres isn't on the + local network, we use the default router's IP address instead. + + If not ARP table entry is found, we overwrite the original IP + packet with an ARP request for the IP address. */ + + /* First check if destination is a local broadcast. */ + if (uip_ip4addr_cmp(ip_buf->destipaddr, broadcast_ipaddr)) { + memcpy(ð->dest, broadcast_ethaddr.addr, 6); + + return LOCAL_BROADCAST; + } else { + /* Check if the destination address is on the local network. */ + if (!uip_ip4addr_maskcmp(ip_buf->destipaddr, + ustack->hostaddr, ustack->netmask)) { + /* Destination address was not on the local network, + so we need to use the default router's IP address + instead of the destination address when determining + the MAC address. */ + uip_ip4addr_copy(ipaddr, ustack->default_route_addr); + } else { + /* Else, we use the destination IP address. */ + uip_ip4addr_copy(ipaddr, ip_buf->destipaddr); + } + + return NONLOCAL_BROADCAST; + } +} + +arp_out_t is_in_arp_table(u16_t *ipaddr, struct arp_entry **tabptr) +{ + u8_t i; + + pthread_mutex_lock(&arp_table_mutex); + + for (i = 0; i < UIP_ARPTAB_SIZE; ++i) { + if (uip_ip4addr_cmp(ipaddr, arp_table[i].ipaddr)) { + *tabptr = &arp_table[i]; + break; + } + } + + pthread_mutex_unlock(&arp_table_mutex); + + if (i == UIP_ARPTAB_SIZE) + return NOT_IN_ARP_TABLE; + else + return IS_IN_ARP_TABLE; +} + +void uip_build_arp_request(struct uip_stack *ustack, u16_t *ipaddr) +{ + struct arp_hdr *arp; + struct uip_eth_hdr *eth; + + arp = (struct arp_hdr *)ustack->network_layer; + eth = (struct uip_eth_hdr *)ustack->data_link_layer; + + /* The destination address was not in our ARP table, so we + overwrite the IP packet with an ARP request. */ + + memset(eth->dest.addr, 0xff, 6); + memset(arp->dhwaddr.addr, 0x00, 6); + memcpy(eth->src.addr, ustack->uip_ethaddr.addr, 6); + memcpy(arp->shwaddr.addr, ustack->uip_ethaddr.addr, 6); + + uip_ip4addr_copy(arp->dipaddr, ipaddr); + uip_ip4addr_copy(arp->sipaddr, ustack->hostaddr); + arp->opcode = const_htons(ARP_REQUEST); /* ARP request. */ + arp->hwtype = const_htons(ARP_HWTYPE_ETH); + arp->protocol = const_htons(UIP_ETHTYPE_IPv4); + arp->hwlen = 6; + arp->protolen = 4; + eth->type = const_htons(UIP_ETHTYPE_ARP); + + ustack->uip_appdata = &ustack->uip_buf[UIP_TCP_IPv4_HLEN + UIP_LLH_LEN]; + + ustack->uip_len = sizeof(*arp) + sizeof(*eth); +} + +void +uip_build_eth_header(struct uip_stack *ustack, + u16_t *ipaddr, + struct arp_entry *tabptr, + struct packet *pkt, u16_t vlan_id) +{ + struct uip_ipv4_hdr *ip_buf; + struct uip_eth_hdr *eth; + struct uip_vlan_eth_hdr *eth_vlan; + + ip_buf = (struct uip_ipv4_hdr *)ustack->network_layer; + eth = (struct uip_eth_hdr *)ustack->data_link_layer; + eth_vlan = (struct uip_vlan_eth_hdr *)ustack->data_link_layer; + + /* First check if destination is a local broadcast. */ + if (uip_ip4addr_cmp(ip_buf->destipaddr, broadcast_ipaddr)) { + memcpy(eth->dest.addr, broadcast_ethaddr.addr, 6); + } else { + /* Build an ethernet header. */ + memcpy(eth->dest.addr, tabptr->ethaddr.addr, 6); + } + memcpy(eth->src.addr, ustack->uip_ethaddr.addr, 6); + + if (vlan_id == 0) { + eth->type = htons(UIP_ETHTYPE_IPv4); + + ustack->uip_len += sizeof(struct uip_eth_hdr); + pkt->buf_size += sizeof(struct uip_eth_hdr); + } else { + eth_vlan->tpid = htons(UIP_ETHTYPE_8021Q); + eth_vlan->vid = htons(vlan_id); + eth_vlan->type = htons(UIP_ETHTYPE_IPv4); + + ustack->uip_len += sizeof(struct uip_vlan_eth_hdr); + pkt->buf_size += sizeof(struct uip_vlan_eth_hdr); + } +} + +static struct arp_entry *uip_get_arp_entry(int index) +{ + return &arp_table[index]; +} + +int uip_lookup_arp_entry(uint32_t ip_addr, uint8_t *mac_addr) +{ + int i; + int rc = -EINVAL; + + pthread_mutex_lock(&arp_table_mutex); + + for (i = 0; i < UIP_ARPTAB_SIZE; ++i) { + struct arp_entry *entry = uip_get_arp_entry(i); + + if (((entry->ipaddr[1] << 16) == (ip_addr & 0xffff0000)) && + ((entry->ipaddr[0]) == (ip_addr & 0x0000ffff))) { + struct in_addr addr; + char *addr_str; + + addr.s_addr = ip_addr; + addr_str = inet_ntoa(addr); + + memcpy(mac_addr, entry->ethaddr.addr, 6); + + LOG_INFO("Found %s at %02x:%02x:%02x:%02x:%02x:%02x", + addr_str, + mac_addr[0], mac_addr[1], mac_addr[2], + mac_addr[3], mac_addr[4], mac_addr[5]); + rc = 0; + break; + } + } + + pthread_mutex_unlock(&arp_table_mutex); + return rc; +} + +/*----------------------------------------------------------------------------*/ + +/** @} */ +/** @} */ diff --git a/iscsiuio/src/uip/uip_arp.h b/iscsiuio/src/uip/uip_arp.h new file mode 100644 index 0000000..339d57d --- /dev/null +++ b/iscsiuio/src/uip/uip_arp.h @@ -0,0 +1,197 @@ +/** + * \addtogroup uip + * @{ + */ + +/** + * \addtogroup uiparp + * @{ + */ + +/** + * \file + * Macros and definitions for the ARP module. + * \author Adam Dunkels + */ + +/* + * Copyright (c) 2001-2003, Adam Dunkels. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack. + * + * + */ + +#ifndef __UIP_ARP_H__ +#define __UIP_ARP_H__ + +#include "packet.h" +#include "uip.h" +#include "uip_eth.h" + +#define ARP_REQUEST 1 +#define ARP_REPLY 2 + +#define ARP_HWTYPE_ETH 1 + +struct __attribute__ ((__packed__)) arp_hdr { + u16_t hwtype; + u16_t protocol; + u8_t hwlen; + u8_t protolen; + u16_t opcode; + struct uip_eth_addr shwaddr; + u16_t sipaddr[2]; + struct uip_eth_addr dhwaddr; + u16_t dipaddr[2]; +}; + +struct __attribute__ ((__packed__)) ip_hdr { + /* IP header. */ + u8_t vhl, tos, len[2], ipid[2], ipoffset[2], ttl, proto; + u16_t ipchksum; + u16_t srcipaddr[2], destipaddr[2]; +}; + +struct __attribute__ ((__packed__)) ethip_hdr { + struct uip_eth_hdr ethhdr; + /* IP header. */ + u8_t vhl, tos, len[2], ipid[2], ipoffset[2], ttl, proto; + u16_t ipchksum; + u16_t srcipaddr[2], destipaddr[2]; +}; + +struct arp_entry { + u16_t ipaddr[2]; + struct uip_eth_addr ethaddr; + u8_t time; +}; + +/* The uip_arp_init() function must be called before any of the other + ARP functions. */ +void uip_arp_init(void); + +/* The uip_arp_ipin() function should be called whenever an IP packet + arrives from the Ethernet. This function refreshes the ARP table or + inserts a new mapping if none exists. The function assumes that an + IP packet with an Ethernet header is present in the uip_buf buffer + and that the length of the packet is in the uip_len variable. */ +/*void uip_arp_ipin(void);*/ +/* #define uip_arp_ipin() */ +void uip_arp_ipin(struct uip_stack *ustack, struct packet *pkt); + +/* The uip_arp_arpin() should be called when an ARP packet is received + by the Ethernet driver. This function also assumes that the + Ethernet frame is present in the uip_buf buffer. When the + uip_arp_arpin() function returns, the contents of the uip_buf + buffer should be sent out on the Ethernet if the uip_len variable + is > 0. */ +void uip_arp_arpin(nic_interface_t *nic_iface, + struct uip_stack *ustack, struct packet *pkt); + +typedef enum { + ARP_SENT = 1, + ETH_HEADER_APPEDEND = 2, +} arp_out_t; + +typedef enum { + LOCAL_BROADCAST = 1, + NONLOCAL_BROADCAST = 2, +} dest_ipv4_addr_t; + +typedef enum { + IS_IN_ARP_TABLE = 1, + NOT_IN_ARP_TABLE = 2, +} arp_table_query_t; + +dest_ipv4_addr_t +uip_determine_dest_ipv4_addr(struct uip_stack *ustack, u16_t *ipaddr); +arp_out_t is_in_arp_table(u16_t *ipaddr, struct arp_entry **tabptr); + +void uip_build_arp_request(struct uip_stack *ustack, u16_t *ipaddr); + +void +uip_build_eth_header(struct uip_stack *ustack, + u16_t *ipaddr, + struct arp_entry *tabptr, + struct packet *pkt, u16_t vlan_id); + +/* The uip_arp_out() function should be called when an IP packet + should be sent out on the Ethernet. This function creates an + Ethernet header before the IP header in the uip_buf buffer. The + Ethernet header will have the correct Ethernet MAC destination + address filled in if an ARP table entry for the destination IP + address (or the IP address of the default router) is present. If no + such table entry is found, the IP packet is overwritten with an ARP + request and we rely on TCP to retransmit the packet that was + overwritten. In any case, the uip_len variable holds the length of + the Ethernet frame that should be transmitted. */ +arp_out_t uip_arp_out(struct uip_stack *ustack); + +/* The uip_arp_timer() function should be called every ten seconds. It + is responsible for flushing old entries in the ARP table. */ +void uip_arp_timer(void); + +int uip_lookup_arp_entry(uint32_t ip_addr, uint8_t *mac_addr); + +/** @} */ + +/** + * \addtogroup uipconffunc + * @{ + */ + +/** + * Specifiy the Ethernet MAC address. + * + * The ARP code needs to know the MAC address of the Ethernet card in + * order to be able to respond to ARP queries and to generate working + * Ethernet headers. + * + * \note This macro only specifies the Ethernet MAC address to the ARP + * code. It cannot be used to change the MAC address of the Ethernet + * card. + * + * \param eaddr A pointer to a struct uip_eth_addr containing the + * Ethernet MAC address of the Ethernet card. + * + * \hideinitializer + */ +#define uip_setethaddr(eaddr) do { \ + uip_ethaddr.addr[0] = eaddr.addr[0]; \ + uip_ethaddr.addr[1] = eaddr.addr[1]; \ + uip_ethaddr.addr[2] = eaddr.addr[2]; \ + uip_ethaddr.addr[3] = eaddr.addr[3]; \ + uip_ethaddr.addr[4] = eaddr.addr[4]; \ + uip_ethaddr.addr[5] = eaddr.addr[5]; \ + } while (0) + +/** @} */ +/** @} */ + +#endif /* __UIP_ARP_H__ */ diff --git a/iscsiuio/src/uip/uip_eth.c b/iscsiuio/src/uip/uip_eth.c new file mode 100644 index 0000000..9e1ea81 --- /dev/null +++ b/iscsiuio/src/uip/uip_eth.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2009-2011, Broadcom Corporation + * Copyright (c) 2014, QLogic Corporation + * + * Written by: Benjamin Li (benli@broadcom.com) + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam Dunkels. + * 4. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * uip_eth.c - CNIC UIO uIP user space stack + * + */ + +#include "uip.h" +#include "uip_eth.h" + +int is_vlan_packet(struct uip_vlan_eth_hdr *hdr) +{ + /* The TPID field in a 802.1Q Header must be 0x8100 */ + if (hdr->tpid == const_htons(UIP_ETHTYPE_8021Q)) + return 1; + + return 0; +} diff --git a/iscsiuio/src/uip/uip_eth.h b/iscsiuio/src/uip/uip_eth.h new file mode 100644 index 0000000..830c04c --- /dev/null +++ b/iscsiuio/src/uip/uip_eth.h @@ -0,0 +1,43 @@ +#ifndef __UIP_ETH_H__ +#define __UIP_ETH_H__ + +#include "uipopt.h" + +/******************************************************************************* + * Ether types + ******************************************************************************/ +#define UIP_ETHTYPE_ARP 0x0806 +#define UIP_ETHTYPE_IPv4 0x0800 +#define UIP_ETHTYPE_8021Q 0x8100 +#define UIP_ETHTYPE_IPv6 0x86dd + +/** + * Representation of a 48-bit Ethernet address. + */ +struct uip_eth_addr { + u8_t addr[6]; +}; + +/** + * The Ethernet header. + */ +struct __attribute__ ((__packed__)) uip_eth_hdr { + struct uip_eth_addr dest; + struct uip_eth_addr src; + u16_t type; +}; + +/** + * The 802.1Q Ethernet header (VLAN). + */ +struct __attribute__ ((__packed__)) uip_vlan_eth_hdr { + struct uip_eth_addr dest; + struct uip_eth_addr src; + u16_t tpid; + u16_t vid; + u16_t type; +}; + +int is_vlan_packet(struct uip_vlan_eth_hdr *hdr); + +#endif /* __UIP_ETH_H__ */ diff --git a/iscsiuio/src/uip/uipopt.h b/iscsiuio/src/uip/uipopt.h new file mode 100644 index 0000000..946fce2 --- /dev/null +++ b/iscsiuio/src/uip/uipopt.h @@ -0,0 +1,536 @@ +/** + * \defgroup uipopt Configuration options for uIP + * @{ + * + * uIP is configured using the per-project configuration file + * uipopt.h. This file contains all compile-time options for uIP and + * should be tweaked to match each specific project. The uIP + * distribution contains a documented example "uipopt.h" that can be + * copied and modified for each project. + * + * \note Most of the configuration options in the uipopt.h should not + * be changed, but rather the per-project uip-conf.h file. + */ + +/** + * \file + * Configuration options for uIP. + * \author Adam Dunkels + * + * This file is used for tweaking various configuration options for + * uIP. You should make a copy of this file into one of your project's + * directories instead of editing this example "uipopt.h" file that + * comes with the uIP distribution. + */ + +/* + * Copyright (c) 2001-2003, Adam Dunkels. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack. + * + * + */ + +#ifndef __UIPOPT_H__ +#define __UIPOPT_H__ + +#ifndef UIP_LITTLE_ENDIAN +#define UIP_LITTLE_ENDIAN 3412 +#endif /* UIP_LITTLE_ENDIAN */ +#ifndef UIP_BIG_ENDIAN +#define UIP_BIG_ENDIAN 1234 +#endif /* UIP_BIG_ENDIAN */ + +#include "uip-conf.h" + +/*----------------------------------------------------------------------------*/ + +/** + * \name Static configuration options + * @{ + * + * These configuration options can be used for setting the IP address + * settings statically, but only if UIP_FIXEDADDR is set to 1. The + * configuration options for a specific node includes IP address, + * netmask and default router as well as the Ethernet address. The + * netmask, default router and Ethernet address are appliciable only + * if uIP should be run over Ethernet. + * + * All of these should be changed to suit your project. +*/ + +/** + * Determines if uIP should use a fixed IP address or not. + * + * If uIP should use a fixed IP address, the settings are set in the + * uipopt.h file. If not, the macros uip_sethostaddr(), + * uip_setdraddr() and uip_setnetmask() should be used instead. + * + * \hideinitializer + */ +#define UIP_FIXEDADDR 0 + +/** + * Ping IP address asignment. + * + * uIP uses a "ping" packets for setting its own IP address if this + * option is set. If so, uIP will start with an empty IP address and + * the destination IP address of the first incoming "ping" (ICMP echo) + * packet will be used for setting the hosts IP address. + * + * \note This works only if UIP_FIXEDADDR is 0. + * + * \hideinitializer + */ +#ifdef UIP_CONF_PINGADDRCONF +#define UIP_PINGADDRCONF UIP_CONF_PINGADDRCONF +#else /* UIP_CONF_PINGADDRCONF */ +#define UIP_PINGADDRCONF 0 +#endif /* UIP_CONF_PINGADDRCONF */ + +/** + * Specifies if the uIP ARP module should be compiled with a fixed + * Ethernet MAC address or not. + * + * If this configuration option is 0, the macro uip_setethaddr() can + * be used to specify the Ethernet address at run-time. + * + * \hideinitializer + */ +#define UIP_FIXEDETHADDR 0 + +/** @} */ +/*------------------------------------------------------------------------------*/ +/** + * \name IP configuration options + * @{ + * + */ +/** + * The IP TTL (time to live) of IP packets sent by uIP. + * + * This should normally not be changed. + */ +#define UIP_TTL 64 + +/** + * Turn on support for IP packet reassembly. + * + * uIP supports reassembly of fragmented IP packets. This features + * requires an additonal amount of RAM to hold the reassembly buffer + * and the reassembly code size is approximately 700 bytes. The + * reassembly buffer is of the same size as the uip_buf buffer + * (configured by UIP_BUFSIZE). + * + * \note IP packet reassembly is not heavily tested. + * + * \hideinitializer + */ +#define UIP_REASSEMBLY 0 + +/** + * The maximum time an IP fragment should wait in the reassembly + * buffer before it is dropped. + * + */ +#define UIP_REASS_MAXAGE 40 + +/** @} */ + +/*----------------------------------------------------------------------------*/ +/** + * \name UDP configuration options + * @{ + */ + +/** + * Toggles wether UDP support should be compiled in or not. + * + * \hideinitializer + */ +#ifdef UIP_CONF_UDP +#define UIP_UDP UIP_CONF_UDP +#else /* UIP_CONF_UDP */ +#define UIP_UDP 0 +#endif /* UIP_CONF_UDP */ + +/** + * Toggles if UDP checksums should be used or not. + * + * \note Support for UDP checksums is currently not included in uIP, + * so this option has no function. + * + * \hideinitializer + */ +#ifdef UIP_CONF_UDP_CHECKSUMS +#define UIP_UDP_CHECKSUMS UIP_CONF_UDP_CHECKSUMS +#else +#define UIP_UDP_CHECKSUMS 0 +#endif + +/** + * The maximum amount of concurrent UDP connections. + * + * \hideinitializer + */ +#ifdef UIP_CONF_UDP_CONNS +#define UIP_UDP_CONNS UIP_CONF_UDP_CONNS +#else /* UIP_CONF_UDP_CONNS */ +#define UIP_UDP_CONNS 10 +#endif /* UIP_CONF_UDP_CONNS */ + +/** + * The name of the function that should be called when UDP datagrams arrive. + * + * \hideinitializer + */ + +/** @} */ +/*------------------------------------------------------------------------------*/ +/** + * \name TCP configuration options + * @{ + */ + +/** + * Determines if support for opening connections from uIP should be + * compiled in. + * + * If the applications that are running on top of uIP for this project + * do not need to open outgoing TCP connections, this configration + * option can be turned off to reduce the code size of uIP. + * + * \hideinitializer + */ +#define UIP_ACTIVE_OPEN 1 + +/** + * The maximum number of simultaneously open TCP connections. + * + * Since the TCP connections are statically allocated, turning this + * configuration knob down results in less RAM used. Each TCP + * connection requires approximatly 30 bytes of memory. + * + * \hideinitializer + */ +#ifndef UIP_CONF_MAX_CONNECTIONS +#define UIP_CONNS 10 +#else /* UIP_CONF_MAX_CONNECTIONS */ +#define UIP_CONNS UIP_CONF_MAX_CONNECTIONS +#endif /* UIP_CONF_MAX_CONNECTIONS */ + +/** + * The maximum number of simultaneously listening TCP ports. + * + * Each listening TCP port requires 2 bytes of memory. + * + * \hideinitializer + */ +#ifndef UIP_CONF_MAX_LISTENPORTS +#define UIP_LISTENPORTS 20 +#else /* UIP_CONF_MAX_LISTENPORTS */ +#define UIP_LISTENPORTS UIP_CONF_MAX_LISTENPORTS +#endif /* UIP_CONF_MAX_LISTENPORTS */ + +/** + * Determines if support for TCP urgent data notification should be + * compiled in. + * + * Urgent data (out-of-band data) is a rarely used TCP feature that + * very seldom would be required. + * + * \hideinitializer + */ +#define UIP_URGDATA 0 + +/** + * The initial retransmission timeout counted in timer pulses. + * + * This should not be changed. + */ +#define UIP_RTO 3 + +/** + * The maximum number of times a segment should be retransmitted + * before the connection should be aborted. + * + * This should not be changed. + */ +#define UIP_MAXRTX 8 + +/** + * The maximum number of times a SYN segment should be retransmitted + * before a connection request should be deemed to have been + * unsuccessful. + * + * This should not need to be changed. + */ +#define UIP_MAXSYNRTX 5 + +/** + * The TCP maximum segment size. + * + * This is should not be to set to more than + * UIP_BUFSIZE - UIP_LLH_LEN - UIP_TCPIP_HLEN. + */ +#define UIP_TCP_MSS (UIP_BUFSIZE - UIP_LLH_LEN - UIP_TCP_IPv4_HLEN) + +/** + * The size of the advertised receiver's window. + * + * Should be set low (i.e., to the size of the uip_buf buffer) is the + * application is slow to process incoming data, or high (32768 bytes) + * if the application processes data quickly. + * + * \hideinitializer + */ +#ifndef UIP_CONF_RECEIVE_WINDOW +#define UIP_RECEIVE_WINDOW UIP_TCP_MSS +#else +#define UIP_RECEIVE_WINDOW UIP_CONF_RECEIVE_WINDOW +#endif + +/** + * How long a connection should stay in the TIME_WAIT state. + * + * This configiration option has no real implication, and it should be + * left untouched. + */ +#define UIP_TIME_WAIT_TIMEOUT 120 + +/** @} */ +/*------------------------------------------------------------------------------*/ +/** + * \name ARP configuration options + * @{ + */ + +/** + * The size of the ARP table. + * + * This option should be set to a larger value if this uIP node will + * have many connections from the local network. + * + * \hideinitializer + */ +#ifdef UIP_CONF_ARPTAB_SIZE +#define UIP_ARPTAB_SIZE UIP_CONF_ARPTAB_SIZE +#else +#define UIP_ARPTAB_SIZE 8 +#endif + +/** + * The maxium age of ARP table entries measured in 10ths of seconds. + * + * An UIP_ARP_MAXAGE of 120 corresponds to 20 minutes (BSD + * default). + * Changed the default to 30 which corresponds to 5 minutes (Linux default) + */ +#define UIP_ARP_MAXAGE 30 + +/** @} */ + +/*----------------------------------------------------------------------------*/ + +/** + * \name General configuration options + * @{ + */ + +/** + * The size of the uIP packet buffer. + * + * The uIP packet buffer should not be smaller than 60 bytes, and does + * not need to be larger than 1500 bytes. Lower size results in lower + * TCP throughput, larger size results in higher TCP throughput. + * + * \hideinitializer + */ +#ifndef UIP_CONF_BUFFER_SIZE +#define UIP_BUFSIZE 400 +#else /* UIP_CONF_BUFFER_SIZE */ +#define UIP_BUFSIZE UIP_CONF_BUFFER_SIZE +#endif /* UIP_CONF_BUFFER_SIZE */ + +/** + * Determines if statistics support should be compiled in. + * + * The statistics is useful for debugging and to show the user. + * + * \hideinitializer + */ +#ifndef UIP_CONF_STATISTICS +#define UIP_STATISTICS 0 +#else /* UIP_CONF_STATISTICS */ +#define UIP_STATISTICS UIP_CONF_STATISTICS +#endif /* UIP_CONF_STATISTICS */ + +/** + * Determines if logging of certain events should be compiled in. + * + * This is useful mostly for debugging. The function uip_log() + * must be implemented to suit the architecture of the project, if + * logging is turned on. + * + * \hideinitializer + */ +#ifndef UIP_CONF_LOGGING +#define UIP_LOGGING 0 +#else /* UIP_CONF_LOGGING */ +#define UIP_LOGGING UIP_CONF_LOGGING +#endif /* UIP_CONF_LOGGING */ + +/** + * Broadcast support. + * + * This flag configures IP broadcast support. This is useful only + * together with UDP. + * + * \hideinitializer + * + */ +#ifndef UIP_CONF_BROADCAST +#define UIP_BROADCAST 0 +#else /* UIP_CONF_BROADCAST */ +#define UIP_BROADCAST UIP_CONF_BROADCAST +#endif /* UIP_CONF_BROADCAST */ + +/** + * Print out a uIP log message. + * + * This function must be implemented by the module that uses uIP, and + * is called by uIP whenever a log message is generated. + */ +void uip_log(char *msg); + +/** + * The link level header length. + * + * This is the offset into the uip_buf where the IP header can be + * found. For Ethernet, this should be set to 14. For SLIP, this + * should be set to 0. + * + * \hideinitializer + */ +#ifdef UIP_CONF_LLH_LEN +#define UIP_LLH_LEN UIP_CONF_LLH_LEN +#else /* UIP_CONF_LLH_LEN */ +#define UIP_LLH_LEN 14 +#endif /* UIP_CONF_LLH_LEN */ + +#if 0 +/** @} */ +/*------------------------------------------------------------------------------*/ +/** + * \name CPU architecture configuration + * @{ + * + * The CPU architecture configuration is where the endianess of the + * CPU on which uIP is to be run is specified. Most CPUs today are + * little endian, and the most notable exception are the Motorolas + * which are big endian. The BYTE_ORDER macro should be changed to + * reflect the CPU architecture on which uIP is to be run. + */ + +/** + * The byte order of the CPU architecture on which uIP is to be run. + * + * This option can be either BIG_ENDIAN (Motorola byte order) or + * LITTLE_ENDIAN (Intel byte order). + * + * \hideinitializer + */ +#ifdef UIP_CONF_BYTE_ORDER +#define UIP_BYTE_ORDER UIP_CONF_BYTE_ORDER +#else /* UIP_CONF_BYTE_ORDER */ +#define UIP_BYTE_ORDER UIP_LITTLE_ENDIAN +#endif /* UIP_CONF_BYTE_ORDER */ +#endif + +/** @} */ +/*------------------------------------------------------------------------------*/ + +/** + * \name Appication specific configurations + * @{ + * + * An uIP application is implemented using a single application + * function that is called by uIP whenever a TCP/IP event occurs. The + * name of this function must be registered with uIP at compile time + * using the UIP_APPCALL definition. + * + * uIP applications can store the application state within the + * uip_conn structure by specifying the type of the application + * structure by typedef:ing the type uip_tcp_appstate_t and uip_udp_appstate_t. + * + * The file containing the definitions must be included in the + * uipopt.h file. + * + * The following example illustrates how this can look. + \code + +void httpd_appcall(void); +#define UIP_APPCALL httpd_appcall + +struct httpd_state { + u8_t state; + u16_t count; + char *dataptr; + char *script; +}; +typedef struct httpd_state uip_tcp_appstate_t + \endcode + */ + +/** + * \var #define UIP_APPCALL + * + * The name of the application function that uIP should call in + * response to TCP/IP events. + * + */ + +/** + * \var typedef uip_tcp_appstate_t + * + * The type of the application state that is to be stored in the + * uip_conn structure. This usually is typedef:ed to a struct holding + * application state information. + */ + +/** + * \var typedef uip_udp_appstate_t + * + * The type of the application state that is to be stored in the + * uip_conn structure. This usually is typedef:ed to a struct holding + * application state information. + */ +/** @} */ +/** @} */ + +#endif /* __UIPOPT_H__ */ diff --git a/iscsiuio/src/unix/.gitignore b/iscsiuio/src/unix/.gitignore new file mode 100644 index 0000000..a2dca2d --- /dev/null +++ b/iscsiuio/src/unix/.gitignore @@ -0,0 +1,2 @@ +build_date.c +build_date.h diff --git a/iscsiuio/src/unix/Makefile.am b/iscsiuio/src/unix/Makefile.am new file mode 100644 index 0000000..898691d --- /dev/null +++ b/iscsiuio/src/unix/Makefile.am @@ -0,0 +1,39 @@ +SUBDIRS= libs + +AM_CFLAGS = -I${top_srcdir}/src/uip \ + -I${top_srcdir}/src/apps/brcm-iscsi \ + -I${top_srcdir}/src/apps/dhcpc \ + -I${top_srcdir}/src/unix/libs \ + -I${top_srcdir}/../include \ + -I${top_srcdir}/../usr + +sbin_PROGRAMS = iscsiuio + +iscsiuio_SOURCES = build_date.c \ + main.c \ + clock-arch.c \ + logger.c \ + nic.c \ + nic_id.c \ + nic_vlan.c \ + nic_nl.c \ + nic_utils.c \ + packet.c \ + iscsid_ipc.c + +iscsiuio_CFLAGS = $(AM_CFLAGS) \ + $(LIBNL_CFLAGS) \ + -DBYTE_ORDER=@ENDIAN@ + +iscsiuio_LDFLAGS= $(AM_LDADD) \ + -ldl \ + -rdynamic \ + $(LIBNL_LIBS) \ + -lpthread + +iscsiuio_LDADD = ${top_srcdir}/src/uip/lib_iscsi_uip.a \ + ${top_srcdir}/src/apps/dhcpc/lib_apps_dhcpc.a\ + ${top_srcdir}/src/apps/brcm-iscsi/lib_apps_brcm_iscsi.a \ + ${top_srcdir}/src/unix/libs/lib_iscsiuio_hw_cnic.a + +iscsiuio_YFLAGS = -d diff --git a/iscsiuio/src/unix/clock-arch.c b/iscsiuio/src/unix/clock-arch.c new file mode 100644 index 0000000..d853101 --- /dev/null +++ b/iscsiuio/src/unix/clock-arch.c @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2006, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack + * + */ + +/** + * \file + * Implementation of architecture-specific clock functionality + * \author + * Adam Dunkels + */ + +#include "clock-arch.h" +#include + +/*---------------------------------------------------------------------------*/ +clock_time_t clock_time(void) +{ + struct timeval tv; + struct timezone tz; + + gettimeofday(&tv, &tz); + + return tv.tv_sec * 1000 + tv.tv_usec / 1000; +} + +/*---------------------------------------------------------------------------*/ diff --git a/iscsiuio/src/unix/clock-arch.h b/iscsiuio/src/unix/clock-arch.h new file mode 100644 index 0000000..888933f --- /dev/null +++ b/iscsiuio/src/unix/clock-arch.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2006, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack + * + */ + +#ifndef __CLOCK_ARCH_H__ +#define __CLOCK_ARCH_H__ + +typedef int clock_time_t; +#define CLOCK_CONF_SECOND 1000 + +#endif /* __CLOCK_ARCH_H__ */ diff --git a/iscsiuio/src/unix/iscsid_ipc.c b/iscsiuio/src/unix/iscsid_ipc.c new file mode 100644 index 0000000..3e92d90 --- /dev/null +++ b/iscsiuio/src/unix/iscsid_ipc.c @@ -0,0 +1,1075 @@ +/* + * Copyright (c) 2009-2011, Broadcom Corporation + * Copyright (c) 2014, QLogic Corporation + * + * Written by: Benjamin Li (benli@broadcom.com) + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam Dunkels. + * 4. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * iscsi_ipc.c - Generic NIC management/utility functions + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PFX "iscsi_ipc " + +/* TODO fix me */ +#define IFNAMSIZ 15 + +#include "nic.h" +#include "nic_utils.h" +#include "nic_vlan.h" +#include "options.h" +#include "mgmt_ipc.h" +#include "iscsid_ipc.h" +#include "uip.h" +#include "uip_mgmt_ipc.h" + +#include "logger.h" +#include "uip.h" + +/* private iscsid options stucture */ +struct iscsid_options { + int fd; + pthread_t thread; +}; + +struct iface_rec_decode { + /* General */ + int32_t iface_num; + uint32_t ip_type; + + /* IPv4 */ + struct in_addr ipv4_addr; + struct in_addr ipv4_subnet_mask; + struct in_addr ipv4_gateway; + + /* IPv6 */ + struct in6_addr ipv6_addr; + struct in6_addr ipv6_subnet_mask; + uint32_t prefix_len; + struct in6_addr ipv6_linklocal; + struct in6_addr ipv6_router; + + uint8_t ipv6_autocfg; + uint8_t linklocal_autocfg; + uint8_t router_autocfg; + + uint8_t vlan_state; + uint8_t vlan_priority; + uint16_t vlan_id; + +#define MIN_MTU_SUPPORT 46 +#define MAX_MTU_SUPPORT 9000 + uint16_t mtu; +}; + + +/****************************************************************************** + * iscsid_ipc Constants + *****************************************************************************/ +static const char uio_udev_path_template[] = "/dev/uio%d"; + +/****************************************************************************** + * Globals + *****************************************************************************/ +static struct iscsid_options iscsid_opts = { + .fd = INVALID_FD, + .thread = INVALID_THREAD, +}; + +/****************************************************************************** + * iscsid Functions + *****************************************************************************/ + +static void *enable_nic_thread(void *data) +{ + nic_t *nic = (nic_t *) data; + + prepare_nic_thread(nic); + LOG_INFO(PFX "%s: started NIC enable thread state: 0x%x", + nic->log_name, nic->state) + + /* Enable the NIC */ + nic_enable(nic); + + nic->enable_thread = INVALID_THREAD; + + pthread_exit(NULL); +} + +static int decode_cidr(char *in_ipaddr_str, struct iface_rec_decode *ird) +{ + int rc = 0, i; + char *tmp, *tok; + char ipaddr_str[NI_MAXHOST]; + char str[INET6_ADDRSTRLEN]; + int keepbits = 0; + struct in_addr ia; + struct in6_addr ia6; + + if (strlen(in_ipaddr_str) > NI_MAXHOST) + strncpy(ipaddr_str, in_ipaddr_str, NI_MAXHOST); + else + strcpy(ipaddr_str, in_ipaddr_str); + + /* Find the CIDR if any */ + tmp = strchr(ipaddr_str, '/'); + if (tmp) { + /* CIDR found, now decode, tmpbuf = ip, tmp = netmask */ + tmp = ipaddr_str; + tok = strsep(&tmp, "/"); + LOG_INFO(PFX "in cidr: bitmask '%s' ip '%s'", tmp, tok); + keepbits = atoi(tmp); + strcpy(ipaddr_str, tok); + } + + /* Determine if the IP address passed from the iface file is + * an IPv4 or IPv6 address */ + rc = inet_pton(AF_INET, ipaddr_str, &ird->ipv6_addr); + if (rc == 0) { + /* Test to determine if the addres is an IPv6 address */ + rc = inet_pton(AF_INET6, ipaddr_str, &ird->ipv6_addr); + if (rc == 0) { + LOG_ERR(PFX "Could not parse IP address: '%s'", + ipaddr_str); + goto out; + } + ird->ip_type = AF_INET6; + if (keepbits > 128) { + LOG_ERR(PFX "CIDR netmask > 128 for IPv6: %d(%s)", + keepbits, tmp); + goto out; + } + if (!keepbits) { + /* Default prefix mask to 64 */ + memcpy(&ird->ipv6_subnet_mask.s6_addr, all_zeroes_addr6, + sizeof(struct in6_addr)); + ird->prefix_len = 64; + for (i = 0; i < 2; i++) + ird->ipv6_subnet_mask.s6_addr32[i] = 0xffffffff; + goto out; + } + ird->prefix_len = keepbits; + memcpy(&ia6.s6_addr, all_zeroes_addr6, sizeof(struct in6_addr)); + for (i = 0; i < 4; i++) { + if (keepbits < 32) { + ia6.s6_addr32[i] = keepbits > 0 ? + 0x00 - (1 << (32 - keepbits)) : 0; + ia6.s6_addr32[i] = htonl(ia6.s6_addr32[i]); + break; + } else + ia6.s6_addr32[i] = 0xFFFFFFFF; + keepbits -= 32; + } + ird->ipv6_subnet_mask = ia6; + if (inet_ntop(AF_INET6, &ia6, str, sizeof(str))) + LOG_INFO(PFX "Using netmask: %s", str); + } else { + ird->ip_type = AF_INET; + rc = inet_pton(AF_INET, ipaddr_str, &ird->ipv4_addr); + + if (keepbits > 32) { + LOG_ERR(PFX "CIDR netmask > 32 for IPv4: %d(%s)", + keepbits, tmp); + goto out; + } + ia.s_addr = keepbits > 0 ? 0x00 - (1 << (32 - keepbits)) : 0; + ird->ipv4_subnet_mask.s_addr = htonl(ia.s_addr); + LOG_INFO(PFX "Using netmask: %s", + inet_ntoa(ird->ipv4_subnet_mask)); + } +out: + return rc; +} + +static int decode_iface(struct iface_rec_decode *ird, struct iface_rec *rec) +{ + int rc = 0; + char ipaddr_str[NI_MAXHOST]; + + /* Decodes the rec contents */ + memset(ird, 0, sizeof(struct iface_rec_decode)); + + /* Detect for CIDR notation and strip off the netmask if present */ + rc = decode_cidr(rec->ipaddress, ird); + if (rc && !ird->ip_type) { + LOG_ERR(PFX "cidr decode err: rc=%d, ip_type=%d", + rc, ird->ip_type); + /* Can't decode address, just exit */ + return rc; + } + rc = 0; + ird->iface_num = rec->iface_num; + ird->vlan_id = rec->vlan_id; + if (rec->iface_num != IFACE_NUM_INVALID) { + ird->mtu = rec->mtu; + if (rec->vlan_id && strcmp(rec->vlan_state, "disable")) { + ird->vlan_state = 1; + ird->vlan_priority = rec->vlan_priority; + ird->vlan_id = rec->vlan_id; + } + if (ird->ip_type == AF_INET6) { + if (!strcmp(rec->ipv6_autocfg, "dhcpv6")) + ird->ipv6_autocfg = IPV6_AUTOCFG_DHCPV6; + else if (!strcmp(rec->ipv6_autocfg, "nd")) + ird->ipv6_autocfg = IPV6_AUTOCFG_ND; + else + ird->ipv6_autocfg = IPV6_AUTOCFG_NOTSPEC; + + if (!strcmp(rec->linklocal_autocfg, "auto")) + ird->linklocal_autocfg = IPV6_LL_AUTOCFG_ON; + else if (!strcmp(rec->linklocal_autocfg, "off")) + ird->linklocal_autocfg = IPV6_LL_AUTOCFG_OFF; + else /* default */ + ird->linklocal_autocfg = IPV6_LL_AUTOCFG_ON; + + if (!strcmp(rec->router_autocfg, "auto")) + ird->router_autocfg = IPV6_RTR_AUTOCFG_ON; + else if (!strcmp(rec->router_autocfg, "off")) + ird->router_autocfg = IPV6_RTR_AUTOCFG_OFF; + else /* default */ + ird->router_autocfg = IPV6_RTR_AUTOCFG_ON; + + /* Decode the addresses based on the control flags */ + /* For DHCP, ignore the IPv6 addr in the iface */ + if (ird->ipv6_autocfg == IPV6_AUTOCFG_DHCPV6) + memcpy(&ird->ipv6_addr, all_zeroes_addr6, + sizeof(struct in6_addr)); + /* Subnet mask priority: CIDR, then rec */ + if (!ird->ipv6_subnet_mask.s6_addr) + inet_pton(AF_INET6, rec->subnet_mask, + &ird->ipv6_subnet_mask); + + /* For LL on, ignore the IPv6 addr in the iface */ + if (ird->linklocal_autocfg == IPV6_LL_AUTOCFG_OFF) { + if (strlen(rec->ipv6_linklocal) > NI_MAXHOST) + strncpy(ipaddr_str, rec->ipv6_linklocal, + NI_MAXHOST); + else + strcpy(ipaddr_str, rec->ipv6_linklocal); + inet_pton(AF_INET6, ipaddr_str, + &ird->ipv6_linklocal); + } + + /* For RTR on, ignore the IPv6 addr in the iface */ + if (ird->router_autocfg == IPV6_RTR_AUTOCFG_OFF) { + if (strlen(rec->ipv6_router) > NI_MAXHOST) + strncpy(ipaddr_str, rec->ipv6_router, + NI_MAXHOST); + else + strcpy(ipaddr_str, rec->ipv6_router); + inet_pton(AF_INET6, ipaddr_str, + &ird->ipv6_router); + } + } else { + /* Subnet mask priority: CIDR, rec, default */ + if (!ird->ipv4_subnet_mask.s_addr) + inet_pton(AF_INET, rec->subnet_mask, + &ird->ipv4_subnet_mask); + if (!ird->ipv4_subnet_mask.s_addr) + ird->ipv4_subnet_mask.s_addr = + calculate_default_netmask( + ird->ipv4_addr.s_addr); + + if (strlen(rec->gateway) > NI_MAXHOST) + strncpy(ipaddr_str, rec->gateway, NI_MAXHOST); + else + strcpy(ipaddr_str, rec->gateway); + inet_pton(AF_INET, ipaddr_str, &ird->ipv4_gateway); + } + } else { + ird->ipv6_autocfg = IPV6_AUTOCFG_NOTUSED; + ird->linklocal_autocfg = IPV6_LL_AUTOCFG_NOTUSED; + ird->router_autocfg = IPV6_RTR_AUTOCFG_NOTUSED; + } + return rc; +} + +static int parse_iface(void *arg) +{ + int rc, i; + nic_t *nic = NULL; + nic_interface_t *nic_iface; + char *transport_name; + size_t transport_name_size; + nic_lib_handle_t *handle; + iscsid_uip_broadcast_t *data; + char ipv6_buf_str[INET6_ADDRSTRLEN]; + int request_type = 0; + struct iface_rec *rec; + struct iface_rec_decode ird; + struct in_addr src_match, dst_match; + pthread_attr_t attr; + + data = (iscsid_uip_broadcast_t *) arg; + rec = &data->u.iface_rec.rec; + LOG_INFO(PFX "Received request for '%s' to set IP address: '%s' " + "VLAN: '%d'", + rec->netdev, + rec->ipaddress, + rec->vlan_id); + + rc = decode_iface(&ird, rec); + if (ird.vlan_id && valid_vlan(ird.vlan_id) == 0) { + LOG_ERR(PFX "Invalid VLAN tag: %d", ird.vlan_id); + rc = -EIO; + goto early_exit; + } + if (rc && !ird.ip_type) { + LOG_ERR(PFX "iface err: rc=%d, ip_type=%d", rc, ird.ip_type); + goto early_exit; + } + + for (i = 0; i < 10; i++) { + struct timespec sleep_req, sleep_rem; + + if (pthread_mutex_trylock(&nic_list_mutex) == 0) + break; + + sleep_req.tv_sec = 0; + sleep_req.tv_nsec = 100000; + nanosleep(&sleep_req, &sleep_rem); + } + + if (i >= 10) { + LOG_WARN(PFX "Could not acquire nic_list_mutex lock"); + rc = -EIO; + goto early_exit; + } + + /* nic_list_mutex locked */ + + /* Check if we can find the NIC device using the netdev + * name */ + rc = from_netdev_name_find_nic(rec->netdev, &nic); + + if (rc != 0) { + LOG_WARN(PFX "Couldn't find NIC: %s, creating an instance", + rec->netdev); + + nic = nic_init(); + if (nic == NULL) { + LOG_ERR(PFX "Couldn't allocate space for NIC %s", + rec->netdev); + + rc = -ENOMEM; + goto done; + } + + strncpy(nic->eth_device_name, + rec->netdev, + sizeof(nic->eth_device_name)); + nic->config_device_name = nic->eth_device_name; + nic->log_name = nic->eth_device_name; + + if (nic_fill_name(nic) != 0) { + free(nic); + rc = -EIO; + goto done; + } + + nic_add(nic); + } else { + LOG_INFO(PFX " %s, using existing NIC", + rec->netdev); + } + + pthread_mutex_lock(&nic->nic_mutex); + if (nic->flags & NIC_GOING_DOWN) { + pthread_mutex_unlock(&nic->nic_mutex); + rc = -EIO; + LOG_INFO(PFX "nic->flags GOING DOWN"); + goto done; + } + + /* If we retry too many times allow iscsid to timeout */ + if (nic->pending_count > 1000) { + nic->pending_count = 0; + nic->flags &= ~NIC_ENABLED_PENDING; + pthread_mutex_unlock(&nic->nic_mutex); + + LOG_WARN(PFX "%s: pending count exceeded 1000", nic->log_name); + + rc = 0; + goto done; + } + + if (nic->flags & NIC_ENABLED_PENDING) { + struct timespec sleep_req, sleep_rem; + + nic->pending_count++; + pthread_mutex_unlock(&nic->nic_mutex); + + sleep_req.tv_sec = 0; + sleep_req.tv_nsec = 100000; + nanosleep(&sleep_req, &sleep_rem); + + LOG_INFO(PFX "%s: enabled pending", nic->log_name); + + rc = -EAGAIN; + goto done; + } + pthread_mutex_unlock(&nic->nic_mutex); + + prepare_library(nic); + + /* Sanity Check to ensure the transport names are the same */ + handle = nic->nic_library; + if (handle != NULL) { + (*handle->ops->lib_ops.get_transport_name) (&transport_name, + &transport_name_size); + + if (strncmp(transport_name, + rec->transport_name, + transport_name_size) != 0) { + LOG_ERR(PFX "%s Transport name is not equal " + "expected: %s got: %s", + nic->log_name, + rec->transport_name, + transport_name); + } + } else { + LOG_ERR(PFX "%s Couldn't find nic library ", nic->log_name); + rc = -EIO; + goto done; + } + + LOG_INFO(PFX "%s library set using transport_name %s", + nic->log_name, transport_name); + + /* Determine how to configure the IP address */ + if (ird.ip_type == AF_INET) { + if (memcmp(&ird.ipv4_addr, + all_zeroes_addr4, sizeof(uip_ip4addr_t)) == 0) { + LOG_INFO(PFX "%s: requesting configuration using DHCP", + nic->log_name); + request_type = IPV4_CONFIG_DHCP; + } else { + LOG_INFO(PFX "%s: requesting configuration using " + "static IP address", nic->log_name); + request_type = IPV4_CONFIG_STATIC; + } + } else if (ird.ip_type == AF_INET6) { + /* For the new 872_22, check ipv6_autocfg for DHCPv6 instead */ + switch (ird.ipv6_autocfg) { + case IPV6_AUTOCFG_DHCPV6: + request_type = IPV6_CONFIG_DHCP; + break; + case IPV6_AUTOCFG_ND: + request_type = IPV6_CONFIG_STATIC; + break; + case IPV6_AUTOCFG_NOTSPEC: + /* Treat NOTSPEC the same as NOTUSED for now */ + case IPV6_AUTOCFG_NOTUSED: + /* For 871 */ + default: + /* Just the IP address to determine */ + if (memcmp(&ird.ipv6_addr, + all_zeroes_addr6, + sizeof(struct in6_addr)) == 0) + request_type = IPV6_CONFIG_DHCP; + else + request_type = IPV6_CONFIG_STATIC; + } + } else { + LOG_ERR(PFX "%s: unknown ip_type to configure: 0x%x", + nic->log_name, ird.ip_type); + + rc = -EIO; + goto done; + } + + pthread_mutex_lock(&nic->nic_mutex); + + nic_iface = nic_find_nic_iface(nic, ird.ip_type, ird.vlan_id, + ird.iface_num, request_type); + + if (nic->flags & NIC_PATHREQ_WAIT) { + if (!nic_iface || + !(nic_iface->flags & NIC_IFACE_PATHREQ_WAIT)) { + int pathreq_wait; + + if (nic_iface && + (nic_iface->flags & NIC_IFACE_PATHREQ_WAIT2)) + pathreq_wait = 12; + else + pathreq_wait = 10; + + if (nic->pathreq_pending_count < pathreq_wait) { + struct timespec sleep_req, sleep_rem; + + pthread_mutex_unlock(&nic->nic_mutex); + + nic->pathreq_pending_count++; + sleep_req.tv_sec = 0; + sleep_req.tv_nsec = 100000; + nanosleep(&sleep_req, &sleep_rem); + /* Somebody else is waiting for PATH_REQ */ + LOG_INFO(PFX "%s: path req pending cnt=%d", + nic->log_name, + nic->pathreq_pending_count); + rc = -EAGAIN; + goto done; + } else { + nic->pathreq_pending_count = 0; + LOG_DEBUG(PFX "%s: path req pending cnt " + "exceeded!", nic->log_name); + /* Allow to fall thru */ + } + } + } + + nic->flags |= NIC_PATHREQ_WAIT; + + /* Create the network interface if it doesn't exist */ + if (nic_iface == NULL) { + LOG_DEBUG(PFX "%s couldn't find interface with " + "ip_type: 0x%x creating it", + nic->log_name, ird.ip_type); + nic_iface = nic_iface_init(); + + if (nic_iface == NULL) { + pthread_mutex_unlock(&nic->nic_mutex); + LOG_ERR(PFX "%s Couldn't allocate " + "interface with ip_type: 0x%x", + nic->log_name, ird.ip_type); + goto done; + } + nic_iface->protocol = ird.ip_type; + nic_iface->vlan_id = ird.vlan_id; + nic_iface->vlan_priority = ird.vlan_priority; + if (ird.mtu >= MIN_MTU_SUPPORT && ird.mtu <= MAX_MTU_SUPPORT) + nic_iface->mtu = ird.mtu; + nic_iface->iface_num = ird.iface_num; + nic_iface->request_type = request_type; + nic_add_nic_iface(nic, nic_iface); + + persist_all_nic_iface(nic); + + LOG_INFO(PFX "%s: created network interface", + nic->log_name); + } else { + /* Move the nic_iface to the front */ + set_nic_iface(nic, nic_iface); + LOG_INFO(PFX "%s: using existing network interface", + nic->log_name); + } + + nic_iface->flags |= NIC_IFACE_PATHREQ_WAIT1; + if (nic->nl_process_thread == INVALID_THREAD) { + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + rc = pthread_create(&nic->nl_process_thread, &attr, + nl_process_handle_thread, nic); + if (rc != 0) { + LOG_ERR(PFX "%s: Could not create NIC NL " + "processing thread [%s]", nic->log_name, + strerror(rc)); + nic->nl_process_thread = INVALID_THREAD; + /* Reset both WAIT flags */ + nic_iface->flags &= ~NIC_IFACE_PATHREQ_WAIT; + nic->flags &= ~NIC_PATHREQ_WAIT; + } + } + + pthread_mutex_unlock(&nic->nic_mutex); + + if (nic_iface->ustack.ip_config == request_type) { + /* Same request_type, check for STATIC address change */ + if (request_type == IPV4_CONFIG_STATIC) { + if (memcmp(nic_iface->ustack.hostaddr, &ird.ipv4_addr, + sizeof(struct in_addr))) + goto reacquire; + } else if (request_type == IPV6_CONFIG_STATIC) { + if (memcmp(nic_iface->ustack.hostaddr6, &ird.ipv6_addr, + sizeof(struct in6_addr))) + goto reacquire; + else + inet_ntop(AF_INET6, &ird.ipv6_addr, + ipv6_buf_str, + sizeof(ipv6_buf_str)); + } + LOG_INFO(PFX "%s: IP configuration didn't change using 0x%x", + nic->log_name, nic_iface->ustack.ip_config); + /* No need to acquire the IP address */ + inet_ntop(AF_INET6, &ird.ipv6_addr, ipv6_buf_str, + sizeof(ipv6_buf_str)); + + goto enable_nic; + } +reacquire: + /* Config needs to re-acquire for this nic_iface */ + pthread_mutex_lock(&nic->nic_mutex); + nic_iface->flags |= NIC_IFACE_ACQUIRE; + pthread_mutex_unlock(&nic->nic_mutex); + + /* Disable the nic loop from further processing, upon returned, + the nic_iface should be cleared */ + nic_disable(nic, 0); + + /* Check to see if this is using DHCP or if this is + * a static IPv4 address. This is done by checking + * if the IP address is equal to 0.0.0.0. If it is + * then the user has specified to use DHCP. If not + * then the user has spcicied to use a static IP address + * an the default netmask will be used */ + switch (request_type) { + case IPV4_CONFIG_DHCP: + memset(nic_iface->ustack.hostaddr, 0, sizeof(struct in_addr)); + LOG_INFO(PFX "%s: configuring using DHCP", nic->log_name); + nic_iface->ustack.ip_config = IPV4_CONFIG_DHCP; + break; + + case IPV4_CONFIG_STATIC: + memcpy(nic_iface->ustack.hostaddr, &ird.ipv4_addr, + sizeof(struct in_addr)); + LOG_INFO(PFX "%s: configuring using static IP " + "IPv4 address :%s ", + nic->log_name, inet_ntoa(ird.ipv4_addr)); + + if (ird.ipv4_subnet_mask.s_addr) + memcpy(nic_iface->ustack.netmask, + &ird.ipv4_subnet_mask, sizeof(struct in_addr)); + LOG_INFO(PFX " netmask: %s", inet_ntoa(ird.ipv4_subnet_mask)); + + /* Default route */ + if (ird.ipv4_gateway.s_addr) { + /* Check for validity */ + src_match.s_addr = ird.ipv4_addr.s_addr & + ird.ipv4_subnet_mask.s_addr; + dst_match.s_addr = ird.ipv4_gateway.s_addr & + ird.ipv4_subnet_mask.s_addr; + if (src_match.s_addr == dst_match.s_addr) + memcpy(nic_iface->ustack.default_route_addr, + &ird.ipv4_gateway, + sizeof(struct in_addr)); + } + nic_iface->ustack.ip_config = IPV4_CONFIG_STATIC; + break; + + case IPV6_CONFIG_DHCP: + memset(nic_iface->ustack.hostaddr6, 0, + sizeof(struct in6_addr)); + nic_iface->ustack.prefix_len = ird.prefix_len; + nic_iface->ustack.ipv6_autocfg = ird.ipv6_autocfg; + nic_iface->ustack.linklocal_autocfg = ird.linklocal_autocfg; + nic_iface->ustack.router_autocfg = ird.router_autocfg; + + if (memcmp(&ird.ipv6_subnet_mask, all_zeroes_addr6, + sizeof(struct in6_addr))) + memcpy(nic_iface->ustack.netmask6, + &ird.ipv6_subnet_mask, sizeof(struct in6_addr)); + if (ird.linklocal_autocfg == IPV6_LL_AUTOCFG_OFF) + memcpy(nic_iface->ustack.linklocal6, + &ird.ipv6_linklocal, sizeof(struct in6_addr)); + if (ird.router_autocfg == IPV6_RTR_AUTOCFG_OFF) + memcpy(nic_iface->ustack.default_route_addr6, + &ird.ipv6_router, sizeof(struct in6_addr)); + inet_ntop(AF_INET6, &ird.ipv6_addr, ipv6_buf_str, + sizeof(ipv6_buf_str)); + LOG_INFO(PFX "%s: configuring using DHCPv6", + nic->log_name); + nic_iface->ustack.ip_config = IPV6_CONFIG_DHCP; + break; + + case IPV6_CONFIG_STATIC: + memcpy(nic_iface->ustack.hostaddr6, &ird.ipv6_addr, + sizeof(struct in6_addr)); + nic_iface->ustack.prefix_len = ird.prefix_len; + nic_iface->ustack.ipv6_autocfg = ird.ipv6_autocfg; + nic_iface->ustack.linklocal_autocfg = ird.linklocal_autocfg; + nic_iface->ustack.router_autocfg = ird.router_autocfg; + + if (memcmp(&ird.ipv6_subnet_mask, all_zeroes_addr6, + sizeof(struct in6_addr))) + memcpy(nic_iface->ustack.netmask6, + &ird.ipv6_subnet_mask, sizeof(struct in6_addr)); + if (ird.linklocal_autocfg == IPV6_LL_AUTOCFG_OFF) + memcpy(nic_iface->ustack.linklocal6, + &ird.ipv6_linklocal, sizeof(struct in6_addr)); + if (ird.router_autocfg == IPV6_RTR_AUTOCFG_OFF) + memcpy(nic_iface->ustack.default_route_addr6, + &ird.ipv6_router, sizeof(struct in6_addr)); + + inet_ntop(AF_INET6, &ird.ipv6_addr, ipv6_buf_str, + sizeof(ipv6_buf_str)); + LOG_INFO(PFX "%s: configuring using static IP " + "IPv6 address: '%s'", nic->log_name, ipv6_buf_str); + + nic_iface->ustack.ip_config = IPV6_CONFIG_STATIC; + break; + + default: + LOG_INFO(PFX "%s: Unknown request type: 0x%x", + nic->log_name, request_type); + + } + +enable_nic: + switch (nic->state) { + case NIC_STOPPED: + /* This thread will be thrown away when completed */ + if (nic->enable_thread != INVALID_THREAD) { + rc = pthread_cancel(nic->enable_thread); + if (rc != 0) { + LOG_INFO(PFX "%s: failed to cancel enable NIC " + "thread\n", nic->log_name); + goto eagain; + } + } + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + rc = pthread_create(&nic->enable_thread, &attr, + enable_nic_thread, (void *)nic); + if (rc != 0) + LOG_WARN(PFX "%s: failed starting enable NIC thread\n", + nic->log_name); +eagain: + rc = -EAGAIN; + break; + + case NIC_RUNNING: + LOG_INFO(PFX "%s: NIC already enabled " + "flags: 0x%x state: 0x%x\n", + nic->log_name, nic->flags, nic->state); + rc = 0; + break; + default: + LOG_INFO(PFX "%s: NIC enable still in progress " + "flags: 0x%x state: 0x%x\n", + nic->log_name, nic->flags, nic->state); + rc = -EAGAIN; + } + + LOG_INFO(PFX "ISCSID_UIP_IPC_GET_IFACE: command: %x " + "name: %s, netdev: %s ipaddr: %s vlan: %d transport_name:%s", + data->header.command, rec->name, rec->netdev, + (ird.ip_type == AF_INET) ? inet_ntoa(ird.ipv4_addr) : + ipv6_buf_str, + ird.vlan_id, rec->transport_name); + +done: + pthread_mutex_unlock(&nic_list_mutex); + +early_exit: + return rc; +} + +/** + * process_iscsid_broadcast() - This function is used to process the + * broadcast messages from iscsid + */ +int process_iscsid_broadcast(int s2) +{ + int rc = 0; + iscsid_uip_broadcast_t *data; + iscsid_uip_rsp_t rsp; + FILE *fd; + size_t size; + iscsid_uip_cmd_e cmd; + uint32_t payload_len; + + fd = fdopen(s2, "r+"); + if (fd == NULL) { + LOG_ERR(PFX "Couldn't open file descriptor: %d(%s)", + errno, strerror(errno)); + return -EIO; + } + + /* This will be freed by parse_iface_thread() */ + data = (iscsid_uip_broadcast_t *) calloc(1, sizeof(*data)); + if (data == NULL) { + LOG_ERR(PFX "Couldn't allocate memory for iface data"); + rc = -ENOMEM; + goto error; + } + memset(data, 0, sizeof(*data)); + + size = fread(data, sizeof(iscsid_uip_broadcast_header_t), 1, fd); + if (!size) { + LOG_ERR(PFX "Could not read request: %d(%s)", + errno, strerror(errno)); + rc = ferror(fd); + goto error; + } + + cmd = data->header.command; + payload_len = data->header.payload_len; + + LOG_DEBUG(PFX "recv iscsid request: cmd: %d, payload_len: %d", + cmd, payload_len); + + size = fread(&data->u.iface_rec, payload_len, 1, fd); + if (!size) { + LOG_ERR(PFX "Could not read data: %d(%s)", + errno, strerror(errno)); + goto error; + } + + switch (cmd) { + case ISCSID_UIP_IPC_GET_IFACE: + rc = parse_iface(data); + switch (rc) { + case 0: + rsp.command = cmd; + rsp.err = ISCSID_UIP_MGMT_IPC_DEVICE_UP; + break; + case -EAGAIN: + rsp.command = cmd; + rsp.err = ISCSID_UIP_MGMT_IPC_DEVICE_INITIALIZING; + break; + default: + rsp.command = cmd; + rsp.err = ISCSID_UIP_MGMT_IPC_ERR; + } + + break; + default: + LOG_WARN(PFX "Unknown iscsid broadcast command: %x", + data->header.command); + + /* Send a response back to iscsid to tell it the + operation succeeded */ + rsp.command = cmd; + rsp.err = ISCSID_UIP_MGMT_IPC_OK; + break; + } + + size = fwrite(&rsp, sizeof(rsp), 1, fd); + if (size == -1) { + LOG_ERR(PFX "Could not send response: %d(%s)", + errno, strerror(errno)); + rc = ferror(fd); + } + +error: + free(data); + fclose(fd); + + return rc; +} + +static void iscsid_loop_close(void *arg) +{ + close(iscsid_opts.fd); + + LOG_INFO(PFX "iSCSI daemon socket closed"); +} + +/** + * iscsid_loop() - This is the function which will process the broadcast + * messages from iscsid + * + */ +static void *iscsid_loop(void *arg) +{ + int rc; + sigset_t set; + + pthread_cleanup_push(iscsid_loop_close, arg); + + sigfillset(&set); + rc = pthread_sigmask(SIG_BLOCK, &set, NULL); + if (rc != 0) { + LOG_ERR(PFX + "Couldn't set signal mask for the iscisd listening " + "thread"); + } + + LOG_DEBUG(PFX "Started iscsid listening thread"); + + while (1) { + struct sockaddr_un remote; + socklen_t sock_len; + int s2; + + LOG_DEBUG(PFX "Waiting for iscsid command"); + + sock_len = sizeof(remote); + s2 = accept(iscsid_opts.fd, + (struct sockaddr *)&remote, &sock_len); + if (s2 == -1) { + if (errno == EAGAIN) { + LOG_DEBUG("Got EAGAIN from accept"); + sleep(1); + continue; + } else if (errno == EINTR) { + LOG_DEBUG("Got EINTR from accept"); + /* The program is terminating, time to exit */ + break; + } + + LOG_ERR(PFX "Could not accept: %d(%s)", + s2, strerror(errno)); + continue; + } + + process_iscsid_broadcast(s2); + close(s2); + } + + pthread_cleanup_pop(0); + + LOG_ERR(PFX "exit iscsid listening thread"); + + pthread_exit(NULL); +} + +#define SD_SOCKET_FDS_START 3 + +static int ipc_systemd(void) +{ + char *env; + + env = getenv("LISTEN_PID"); + + if (!env || (strtoul(env, NULL, 10) != getpid())) + return -EINVAL; + + env = getenv("LISTEN_FDS"); + + if (!env) + return -EINVAL; + + if (strtoul(env, NULL, 10) != 1) { + LOG_ERR("Did not receive exactly one IPC socket from systemd"); + return -EINVAL; + } + + return SD_SOCKET_FDS_START; +} + +/****************************************************************************** + * Initialize/Cleanup routines + ******************************************************************************/ +/** + * iscsid_init() - This function will setup the thread used to listen for + * the iscsid broadcast messages + * @return 0 on success, <0 on failure + */ +int iscsid_init() +{ + int rc, addr_len; + struct sockaddr_un addr; + + iscsid_opts.fd = ipc_systemd(); + if (iscsid_opts.fd >= 0) + return 0; + + iscsid_opts.fd = socket(AF_LOCAL, SOCK_STREAM, 0); + if (iscsid_opts.fd < 0) { + LOG_ERR(PFX "Can not create IPC socket"); + return iscsid_opts.fd; + } + + addr_len = offsetof(struct sockaddr_un, sun_path) + strlen(ISCSID_UIP_NAMESPACE) + 1; + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_LOCAL; + memcpy((char *)&addr.sun_path + 1, ISCSID_UIP_NAMESPACE, + strlen(ISCSID_UIP_NAMESPACE)); + + rc = bind(iscsid_opts.fd, (struct sockaddr *)&addr, addr_len); + if (rc < 0) { + LOG_ERR(PFX "Can not bind IPC socket: %s", strerror(errno)); + goto error; + } + + rc = listen(iscsid_opts.fd, 32); + if (rc < 0) { + LOG_ERR(PFX "Can not listen IPC socket: %s", strerror(errno)); + goto error; + } + + return 0; +error: + close(iscsid_opts.fd); + iscsid_opts.fd = INVALID_FD; + + return rc; +} + +/** + * iscsid_start() - This function will start the thread used to listen for + * the iscsid broadcast messages + * @return 0 on success, <0 on failure + */ +int iscsid_start() +{ + pthread_attr_t attr; + int rc; + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + rc = pthread_create(&iscsid_opts.thread, &attr, iscsid_loop, NULL); + if (rc != 0) { + LOG_ERR(PFX "Could not start iscsid listening thread rc=%d", + rc); + goto error; + } + + return 0; + +error: + close(iscsid_opts.fd); + iscsid_opts.fd = INVALID_FD; + + return rc; +} + +/** + * iscsid_cleanup() - This is called when stoping the thread listening + * for the iscsid broadcast messages + */ +void iscsid_cleanup() +{ + int rc; + + if (iscsid_opts.fd != INVALID_FD) { + rc = pthread_cancel(iscsid_opts.thread); + if (rc != 0) { + LOG_ERR("Could not cancel iscsid listening thread: %s", + strerror(rc)); + } + } + + LOG_INFO(PFX "iscsid listening thread has shutdown"); +} diff --git a/iscsiuio/src/unix/iscsid_ipc.h b/iscsiuio/src/unix/iscsid_ipc.h new file mode 100644 index 0000000..60bcd71 --- /dev/null +++ b/iscsiuio/src/unix/iscsid_ipc.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2009-2011, Broadcom Corporation + * Copyright (c) 2014, QLogic Corporation + * + * Written by: Benjamin Li (benli@broadcom.com) + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam Dunkels. + * 4. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * iscsid_ipc.h: Generic NIC management/utility functions + * + */ +#ifndef __ISCSID_IPC_H__ +#define __ISCSID_IPC_H__ + +#include "uip.h" +#include "mgmt_ipc.h" + +enum mgmt_ipc_err iscsid_connect(int *fd); +int iscsid_get_ipaddr(int fd, uip_ip4addr_t *ipaddr); + +int iscsid_init(); +int iscsid_start(); +void iscsid_cleanup(); + +#endif /* __ISCSID_IPC_H__ */ diff --git a/iscsiuio/src/unix/libs/Makefile.am b/iscsiuio/src/unix/libs/Makefile.am new file mode 100644 index 0000000..890415f --- /dev/null +++ b/iscsiuio/src/unix/libs/Makefile.am @@ -0,0 +1,13 @@ +AM_CFLAGS = -I${top_srcdir}/src/uip \ + -I${top_srcdir}/src/unix \ + -I${top_srcdir}/src/unix/libs \ + -I${top_srcdir}/src/apps/dhcpc \ + -I${top_srcdir}/../include \ + -I${top_srcdir}/../usr + +noinst_LIBRARIES = lib_iscsiuio_hw_cnic.a + +lib_iscsiuio_hw_cnic_a_SOURCES = ../build_date.c \ + cnic.c \ + bnx2.c \ + bnx2x.c diff --git a/iscsiuio/src/unix/libs/bnx2.c b/iscsiuio/src/unix/libs/bnx2.c new file mode 100644 index 0000000..937336e --- /dev/null +++ b/iscsiuio/src/unix/libs/bnx2.c @@ -0,0 +1,1165 @@ +/* + * Copyright (c) 2009-2011, Broadcom Corporation + * Copyright (c) 2014, QLogic Corporation + * + * Written by: Benjamin Li (benli@broadcom.com) + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam Dunkels. + * 4. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * bnx2.c - bnx2 user space driver + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" + +#include "build_date.h" +#include "bnx2.h" +#include "cnic.h" +#include "logger.h" +#include "nic.h" +#include "nic_utils.h" +#include "options.h" + +#define PFX "bnx2 " + +/* Foward struct declarations */ +struct nic_ops bnx2_op; + +/******************************************************************************* + * NIC Library Strings + ******************************************************************************/ +static const char library_name[] = "bnx2"; +static const char library_version[] = PACKAGE_VERSION; +static const char library_uio_name[] = "bnx2_cnic"; + +/* The name that should be returned from /sys/class/uio/uio0/name */ +static const char cnic_uio_sysfs_name_tempate[] = "/sys/class/uio/uio%i/name"; +static const char cnic_uio_sysfs_name[] = "bnx2_cnic"; + +/******************************************************************************* + * String constants used to display human readable adapter name + ******************************************************************************/ +static const char brcm_5706C[] = "QLogic NetXtreme II BCM5706 1000Base-T"; +static const char hp_NC370T[] = + "HP NC370T Multifunction Gigabit Server Adapter"; +static const char hp_NC370I[] = + "HP NC370i Multifunction Gigabit Server Adapter"; +static const char brcm_5706S[] = "QLogic NetXtreme II BCM5706 1000Base-SX"; +static const char hp_NC370F[] = + "HP NC370F Multifunction Gigabit Server Adapter"; +static const char brcm_5708C[] = "QLogic NetXtreme II BCM5708 1000Base-T"; +static const char brcm_5708S[] = "QLogic NetXtreme II BCM5708 1000Base-SX"; +static const char brcm_5709C[] = "QLogic NetXtreme II BCM5709 1000Base-T"; +static const char brcm_5709S[] = "QLogic NetXtreme II BCM5709 1000Base-SX"; +static const char brcm_5716C[] = "QLogic NetXtreme II BCM5716 1000Base-T"; +static const char brcm_5716S[] = "QLogic NetXtreme II BCM5716 1000Base-SX"; + +/******************************************************************************* + * PCI ID constants + ******************************************************************************/ +#define PCI_VENDOR_ID_BROADCOM 0x14e4 +#define PCI_DEVICE_ID_NX2_5709 0x1639 +#define PCI_DEVICE_ID_NX2_5709S 0x163a +#define PCI_DEVICE_ID_NX2_5706 0x164a +#define PCI_DEVICE_ID_NX2_5708 0x164c +#define PCI_DEVICE_ID_NX2_5706S 0x16aa +#define PCI_DEVICE_ID_NX2_5708S 0x16ac + +#define PCI_VENDOR_ID_HP 0x103c + +#define PCI_ANY_ID (~0) + +/* This is the table used to match PCI vendor and device ID's to the + * human readable string names of the devices */ +static const struct pci_device_id bnx2_pci_tbl[] = { + {PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5706, + PCI_VENDOR_ID_HP, 0x3101, hp_NC370T}, + {PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5706, + PCI_VENDOR_ID_HP, 0x3106, hp_NC370I}, + {PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5706, + PCI_ANY_ID, PCI_ANY_ID, brcm_5706S}, + {PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5708, + PCI_ANY_ID, PCI_ANY_ID, brcm_5708C}, + {PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5706S, + PCI_VENDOR_ID_HP, 0x3102, hp_NC370F}, + {PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5706S, + PCI_ANY_ID, PCI_ANY_ID, brcm_5706S}, + {PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5708S, + PCI_ANY_ID, PCI_ANY_ID, brcm_5708S}, + {PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5709, + PCI_ANY_ID, PCI_ANY_ID, brcm_5709C}, + {PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5709S, + PCI_ANY_ID, PCI_ANY_ID, brcm_5709S}, + {PCI_VENDOR_ID_BROADCOM, 0x163b, + PCI_ANY_ID, PCI_ANY_ID, brcm_5716C}, + {PCI_VENDOR_ID_BROADCOM, 0x163c, + PCI_ANY_ID, PCI_ANY_ID, brcm_5716S}, +}; + +/******************************************************************************* + * bnx2 Library Functions + ******************************************************************************/ +/** + * bnx2_get_library_name() - Used to get the name of this NIC libary + * @param name - This function will return the pointer to this NIC + * library name + * @param name_size + */ +static void bnx2_get_library_name(char **name, size_t *name_size) +{ + *name = (char *)library_name; + *name_size = sizeof(library_name); +} + +/** + * bnx2_get_library_version() - Used to get the version string of this + * NIC libary + * @param version - This function will return the pointer to this NIC + * library version string + * @param version_size - This will be set with the version size + */ +static void bnx2_get_library_version(char **version, size_t *version_size) +{ + *version = (char *)library_version; + *version_size = sizeof(library_version); +} + +/** + * bnx2_get_build_date() - Used to get the build date string of this library + * @param version - This function will return the pointer to this NIC + * library build date string + * @param version_size - This will be set with the build date string size + */ +static void bnx2_get_build_date(char **build, size_t *build_size) +{ + *build = (char *)build_date; + *build_size = sizeof(build_date); +} + +/** + * bnx2_get_transport_name() - Used to get the transport name associated + * with this this NIC libary + * @param transport_name - This function will return the pointer to this NIC + * library's associated transport string + * @param transport_name_size - This will be set with the transport name size + */ +static void bnx2_get_transport_name(char **transport_name, + size_t *transport_name_size) +{ + *transport_name = (char *)bnx2i_library_transport_name; + *transport_name_size = bnx2i_library_transport_name_size; +} + +/** + * bnx2_get_uio_name() - Used to get the uio name associated with this this + * NIC libary + * @param uio_name - This function will return the pointer to this NIC + * library's associated uio string + * @param transport_name_size - This will be set with the uio name size + */ +static void bnx2_get_uio_name(char **uio_name, size_t *uio_name_size) +{ + *uio_name = (char *)library_uio_name; + *uio_name_size = sizeof(library_uio_name); +} + +/** + * bnx2_get_pci_table() - Used to get the PCI table for this NIC libary + * to determine which NIC's based off of PCI ID's + * are supported + * @param table - This function will return the pointer to the PCI table + * @param entries - This function will return the number of entries in the NIC + * library's PCI table + */ +static void bnx2_get_pci_table(struct pci_device_id **table, uint32_t *entries) +{ + *table = (struct pci_device_id *)bnx2_pci_tbl; + *entries = (uint32_t) (sizeof(bnx2_pci_tbl) / sizeof(bnx2_pci_tbl[0])); +} + +/** + * bnx2_get_ops() - Used to get the NIC library op table + * @param op - The op table of this NIC library + */ +struct nic_ops *bnx2_get_ops() +{ + return &bnx2_op; +} + +/******************************************************************************* + * bnx2 Utility Functions + ******************************************************************************/ +/******************************************************************************* + * Utility Functions Used to read register from the bnx2 device + ******************************************************************************/ +static void bnx2_wr32(bnx2_t *bp, __u32 off, __u32 val) +{ + *((volatile __u32 *)(bp->reg + off)) = val; +} + +static void bnx2_wr16(bnx2_t *bp, __u32 off, __u16 val) +{ + *((volatile __u16 *)(bp->reg + off)) = val; +} + +static __u32 bnx2_rd32(bnx2_t *bp, __u32 off) +{ + return *((volatile __u32 *)(bp->reg + off)); +} + +static int bnx2_reg_sync(bnx2_t *bp, __u32 off, __u16 length) +{ + return msync(bp->reg + off, length, MS_SYNC); +} + +/** + * bnx2_get_chip_id() - Used to retrive the chip ID from the nic + * @param dev - Device used to determin NIC type + * @return Chip ID read from the MISC ID register + */ +static int bnx2_get_chip_id(bnx2_t *bp) +{ + return bnx2_rd32(bp, BNX2_MISC_ID); +} + +/** + * bnx2_uio_verify() + * + */ +static int bnx2_uio_verify(nic_t *nic) +{ + char *raw = NULL, *raw_tmp; + uint32_t raw_size = 0; + char temp_path[sizeof(cnic_uio_sysfs_name_tempate) + 8]; + int rc = 0; + + /* Build the path to determine uio name */ + snprintf(temp_path, sizeof(temp_path), + cnic_uio_sysfs_name_tempate, nic->uio_minor); + + rc = capture_file(&raw, &raw_size, temp_path); + if (rc != 0) + goto error; + + /* sanitize name string by replacing newline with null termination */ + raw_tmp = raw; + while (*raw_tmp != '\n') + raw_tmp++; + *raw_tmp = '\0'; + + if (strncmp(raw, cnic_uio_sysfs_name, sizeof(cnic_uio_sysfs_name)) != + 0) { + LOG_ERR(PFX "%s: uio names not equal: " + "expecting %s got %s from %s", + nic->log_name, cnic_uio_sysfs_name, raw, temp_path); + rc = -EIO; + } + + free(raw); + + LOG_INFO(PFX "%s: Verified is a cnic_uio device", nic->log_name); + +error: + return rc; +} + +/******************************************************************************* + * bnx2 Utility Functions to get to the hardware consumer indexes + ******************************************************************************/ +static __u16 bnx2_get_rx_msix(bnx2_t *bp) +{ + struct status_block_msix *sblk = bp->status_blk.msix; + __u16 rx_cons; + + msync(sblk, sizeof(*sblk), MS_SYNC); + rx_cons = sblk->status_rx_quick_consumer_index; + barrier(); + if ((rx_cons & (MAX_RX_DESC_CNT)) == (MAX_RX_DESC_CNT)) + rx_cons++; + + return rx_cons; +} + +static __u16 bnx2_get_rx_msi(bnx2_t *bp) +{ + struct status_block *sblk = bp->status_blk.msi; + __u16 rx_cons; + + msync(sblk, sizeof(*sblk), MS_SYNC); + rx_cons = BNX2_SBLK_EVEN_IDX(sblk->rx2); + barrier(); + if ((rx_cons & (MAX_RX_DESC_CNT)) == (MAX_RX_DESC_CNT)) + rx_cons++; + + return rx_cons; +} + +static __u16 bnx2_get_tx_msix(bnx2_t *bp) +{ + struct status_block_msix *sblk = bp->status_blk.msix; + __u16 tx_cons; + + msync(sblk, sizeof(*sblk), MS_SYNC); + tx_cons = sblk->status_tx_quick_consumer_index; + barrier(); + if ((tx_cons & (MAX_TX_DESC_CNT)) == (MAX_TX_DESC_CNT)) + tx_cons++; + + return tx_cons; +} + +static __u16 bnx2_get_tx_msi(bnx2_t *bp) +{ + struct status_block *sblk = bp->status_blk.msi; + __u16 tx_cons; + + msync(sblk, sizeof(*sblk), MS_SYNC); + tx_cons = BNX2_SBLK_EVEN_IDX(sblk->tx2); + barrier(); + if ((tx_cons & (MAX_TX_DESC_CNT)) == (MAX_TX_DESC_CNT)) + tx_cons++; + + return tx_cons; +} + +typedef enum { + CNIC_VLAN_STRIPPING_ENABLED = 1, + CNIC_VLAN_STRIPPING_DISABLED = 2, +} CNIC_VLAN_STRIPPING_MODE; + +/** + * bnx2_strip_vlan_enabled() - This will query the device to determine whether + * VLAN tag stripping is enabled or not + * @param dev - device to check stripping or not + * @ return CNIC_VLAN_STRIPPING_ENABLED stripping is enabled + * CNIC_VLAN_STRIPPING_DISABLED stripping is not enabled + */ +static CNIC_VLAN_STRIPPING_MODE bnx2_strip_vlan_enabled(bnx2_t *bp) +{ + uint32_t val; + + val = bnx2_rd32(bp, BNX2_EMAC_RX_MODE); + + if (val & BNX2_EMAC_RX_MODE_KEEP_VLAN_TAG) + return CNIC_VLAN_STRIPPING_DISABLED; + else + return CNIC_VLAN_STRIPPING_ENABLED; +} + +/** + * bnx2_free() - Used to free a bnx2 structure + */ +static void bnx2_free(nic_t *nic) +{ + if (nic->priv) + free(nic->priv); + nic->priv = NULL; +} + + +/** + * bnx2_alloc() - Used to allocate a bnx2 structure + */ +static bnx2_t *bnx2_alloc(nic_t *nic) +{ + bnx2_t *bp = malloc(sizeof(*bp)); + if (bp == NULL) { + LOG_ERR(PFX "%s: Could not allocate bnx2 space", nic->log_name); + return NULL; + } + + /* Clear out the bnx2 contents */ + memset(bp, 0, sizeof(*bp)); + + bp->bar0_fd = INVALID_FD; + bp->flags = BNX2_UIO_TX_HAS_SENT; + + bp->parent = nic; + nic->priv = (void *)bp; + + return bp; +} + +/** + * bnx2_open() - This will initialize all the hardware resources + * @param dev - The struct nic device to open + * @return 0 on success, on failure a errno will be returned + */ +static int bnx2_open(nic_t *nic) +{ + bnx2_t *bp; + struct stat uio_stat; + int i, rc; + __u32 val; + uint32_t tx_cid; + __u32 msix_vector = 0; + char sysfs_resc_path[80]; + + /* Sanity Check: validate the parameters */ + if (nic == NULL) { + LOG_ERR(PFX "bnx2_open(): nic == NULL"); + return -EINVAL; + } + + if ((nic->priv) != NULL && + (((bnx2_t *) (nic->priv))->flags & BNX2_OPENED)) { + return 0; + } + + bp = bnx2_alloc(nic); + if (bp == NULL) { + LOG_ERR(PFX "bnx2_open(): Couldn't allocate bp priv struct", + nic->log_name); + return -ENOMEM; + } + + while (nic->fd < 0) { + nic->fd = open(nic->uio_device_name, O_RDWR | O_NONBLOCK); + if (nic->fd != INVALID_FD) { + LOG_ERR(PFX + "%s: uio device has been brought up via pid: " + "%d on fd: %d", + nic->uio_device_name, getpid(), nic->fd); + + rc = bnx2_uio_verify(nic); + if (rc != 0) + continue; + + break; + } else { + LOG_WARN(PFX "%s: Could not open device: %s, [%s]", + nic->log_name, nic->uio_device_name, + strerror(errno)); + manually_trigger_uio_event(nic, nic->uio_minor); + + /* udev might not have created the file yet */ + pthread_mutex_unlock(&nic->nic_mutex); + sleep(1); + pthread_mutex_lock(&nic->nic_mutex); + } + } + if (fstat(nic->fd, &uio_stat) < 0) { + LOG_ERR(PFX "%s: Could not fstat device", nic->log_name); + errno = -ENODEV; + goto error_alloc_rx_ring; + } + nic->uio_minor = minor(uio_stat.st_rdev); + + cnic_get_sysfs_pci_resource_path(nic, 0, sysfs_resc_path, 80); + bp->bar0_fd = open(sysfs_resc_path, O_RDWR | O_SYNC); + if (bp->bar0_fd < 0) { + LOG_ERR(PFX "%s: Could not open %s", nic->log_name, + sysfs_resc_path); + errno = -ENODEV; + goto error_alloc_rx_ring; + } + + /* TODO: hardcoded with the cnic driver */ + bp->rx_ring_size = 3; + bp->rx_buffer_size = 0x400; + + LOG_DEBUG(PFX "%s: using rx ring size: %d, rx buffer size: %d", + nic->log_name, bp->rx_ring_size, bp->rx_buffer_size); + + /* Determine the number of UIO events that have already occured */ + rc = detemine_initial_uio_events(nic, &nic->intr_count); + if (rc != 0) { + LOG_ERR("Could not determine the number ofinitial UIO events"); + nic->intr_count = 0; + } + + /* Allocate space for rx ring pointer */ + bp->rx_ring = malloc(sizeof(struct l2_fhdr *) * bp->rx_ring_size); + if (bp->rx_ring == NULL) { + LOG_ERR(PFX "%s: Could not allocate space for rx_ring", + nic->log_name); + errno = -ENOMEM; + goto error_alloc_rx_ring; + } + mlock(bp->rx_ring, sizeof(struct l2_fhdr *) * bp->rx_ring_size); + + /* Allocate space for rx pkt ring */ + bp->rx_pkt_ring = malloc(sizeof(void *) * bp->rx_ring_size); + if (bp->rx_pkt_ring == NULL) { + LOG_ERR(PFX "%s: Could not allocate space for rx_pkt_ring", + nic->log_name); + errno = -ENOMEM; + goto error_alloc_rx_pkt_ring; + } + mlock(bp->rx_pkt_ring, sizeof(void *) * bp->rx_ring_size); + + bp->reg = mmap(NULL, 0x12800, PROT_READ | PROT_WRITE, MAP_SHARED, + bp->bar0_fd, (off_t) 0); + if (bp->reg == MAP_FAILED) { + LOG_INFO(PFX "%s: Couldn't mmap registers: %s", + nic->log_name, strerror(errno)); + bp->reg = NULL; + goto error_regs; + } + + msync(bp->reg, 0x12800, MS_SYNC); + LOG_DEBUG(PFX "Chip ID: %x", bnx2_get_chip_id(bp)); + + /* on a 5709 when using MSI-X the status block is at an offset */ + if (BNX2_CHIP_NUM(bnx2_get_chip_id(bp)) == CHIP_NUM_5709) { + /* determine if we are using MSI-X */ + val = bnx2_rd32(bp, BNX2_TSCH_TSS_CFG); + if (val) { + /* We are in MSI-X mode */ + uint32_t base_cid = ((val >> 10) & 0x7ff) << 3; + msix_vector = (val >> 24) & 0xf; + + bp->status_blk_size = (128 * 9); + + tx_cid = base_cid + msix_vector - 1; + bp->flags |= BNX2_UIO_MSIX_ENABLED; + + bp->get_tx_cons = bnx2_get_tx_msix; + bp->get_rx_cons = bnx2_get_rx_msix; + + LOG_DEBUG(PFX "%s: tss_cfg: 0x%x tx cid: %d", + nic->log_name, val, tx_cid); + + LOG_INFO(PFX "%s: detected using MSI-X vector: %d", + nic->log_name, msix_vector); + } else { + /* We are not in MSI-X mode */ + bp->status_blk_size = 64; + tx_cid = 20; + + bp->get_tx_cons = bnx2_get_tx_msi; + bp->get_rx_cons = bnx2_get_rx_msi; + } + } else { + bp->status_blk_size = 64; + tx_cid = 20; + + bp->get_tx_cons = bnx2_get_tx_msi; + bp->get_rx_cons = bnx2_get_rx_msi; + } + + bp->sblk_map = mmap(NULL, bp->status_blk_size, + PROT_READ | PROT_WRITE, MAP_SHARED, + nic->fd, (off_t) nic->page_size); + if (bp->sblk_map == MAP_FAILED) { + LOG_INFO(PFX "%s: Could not mmap status block: %s", + nic->log_name, strerror(errno)); + goto error_sblk; + } + + if (bp->flags & BNX2_UIO_MSIX_ENABLED) { + uint8_t *status_blk = (uint8_t *) bp->sblk_map; + status_blk += (msix_vector * 128); + + bp->status_blk.msix = (struct status_block_msix *)status_blk; + + LOG_DEBUG(PFX "%s: msix initial cons: tx:%d rx:%d", + nic->log_name, + bp->status_blk.msix->status_tx_quick_consumer_index, + bp->status_blk.msix->status_rx_quick_consumer_index); + } else { + bp->status_blk.msi = (struct status_block *)bp->sblk_map; + + LOG_DEBUG(PFX "%s: msi initial tx:%d rx:%d", + nic->log_name, + BNX2_SBLK_EVEN_IDX(bp->status_blk.msi->tx2), + BNX2_SBLK_EVEN_IDX(bp->status_blk.msi->rx2)); + } + + bp->tx_ring = mmap(NULL, 2 * nic->page_size, + PROT_READ | PROT_WRITE, MAP_SHARED, nic->fd, + (off_t) 2 * nic->page_size); + if (bp->tx_ring == MAP_FAILED) { + LOG_INFO(PFX "%s: Could not mmap tx ring: %s", + nic->log_name, strerror(errno)); + bp->tx_ring = NULL; + goto error_tx_ring; + } + + bp->bufs = mmap(NULL, (bp->rx_ring_size + 1) * bp->rx_buffer_size, + PROT_READ | PROT_WRITE, + MAP_SHARED, nic->fd, (off_t) 3 * nic->page_size); + if (bp->bufs == MAP_FAILED) { + LOG_INFO(PFX "%s: Could not mmap buffers: %s", + nic->log_name, strerror(errno)); + bp->bufs = NULL; + goto error_bufs; + } + + bp->tx_bidx_io = MB_GET_CID_ADDR(tx_cid) + BNX2_L2CTX_TX_HOST_BIDX; + bp->tx_bseq_io = MB_GET_CID_ADDR(tx_cid) + BNX2_L2CTX_TX_HOST_BSEQ; + LOG_INFO(PFX "%s: tx_bidx_io: 0x%x tx_bseq_io: 0x%x", + nic->log_name, bp->tx_bidx_io, bp->tx_bseq_io); + + bp->rx_bidx_io = MB_GET_CID_ADDR(2) + BNX2_L2CTX_HOST_BDIDX; + bp->rx_bseq_io = MB_GET_CID_ADDR(2) + BNX2_L2CTX_HOST_BSEQ; + + bp->tx_cons = 0; + bp->tx_prod = 0; + bp->tx_pkt = bp->bufs; + + bp->rx_index = 0; + bp->rx_cons = 0; + bp->rx_prod = bp->rx_ring_size; + bp->rx_bseq = bp->rx_prod * bp->rx_buffer_size; + bnx2_wr16(bp, bp->rx_bidx_io, bp->rx_prod); + bnx2_wr32(bp, bp->rx_bseq_io, bp->rx_bseq); + + bnx2_reg_sync(bp, bp->rx_bidx_io, sizeof(__u16)); + bnx2_reg_sync(bp, bp->rx_bseq_io, sizeof(__u32)); + + for (i = 0; i < bp->rx_ring_size; i++) { + void *ptr = bp->bufs + (bp->rx_buffer_size * (i + 1)); + + bp->rx_ring[i] = (struct l2_fhdr *)ptr; + bp->rx_pkt_ring[i] = ptr + sizeof(struct l2_fhdr) + 2; + } + + /* Read the MAC address used for the iSCSI interface */ + val = bnx2_rd32(bp, BNX2_EMAC_MAC_MATCH4); + nic->mac_addr[0] = (__u8) (val >> 8); + nic->mac_addr[1] = (__u8) val; + + val = bnx2_rd32(bp, BNX2_EMAC_MAC_MATCH5); + nic->mac_addr[2] = (__u8) (val >> 24); + nic->mac_addr[3] = (__u8) (val >> 16); + nic->mac_addr[4] = (__u8) (val >> 8); + nic->mac_addr[5] = (__u8) val; + + LOG_INFO(PFX "%s: Using mac address: %2x:%2x:%2x:%2x:%2x:%2x", + nic->log_name, + nic->mac_addr[0], nic->mac_addr[1], nic->mac_addr[2], + nic->mac_addr[3], nic->mac_addr[4], nic->mac_addr[5]); + + /* Determine if Hardware VLAN tag stripping is enabled or not */ + if (CNIC_VLAN_STRIPPING_ENABLED == bnx2_strip_vlan_enabled(bp)) + nic->flags |= NIC_VLAN_STRIP_ENABLED; + + /* Prepare the multicast addresses */ + val = 4 | BNX2_RPM_SORT_USER2_BC_EN | BNX2_RPM_SORT_USER2_MC_EN; + if (BNX2_CHIP_NUM(bnx2_get_chip_id(bp)) != CHIP_NUM_5709) + val |= BNX2_RPM_SORT_USER2_PROM_VLAN; + + bnx2_wr32(bp, BNX2_RPM_SORT_USER2, 0x0); + bnx2_wr32(bp, BNX2_RPM_SORT_USER2, val); + bnx2_wr32(bp, BNX2_RPM_SORT_USER2, val | BNX2_RPM_SORT_USER2_ENA); + + rc = enable_multicast(nic); + if (rc != 0) { + errno = rc; + goto error_bufs; + } + msync(bp->reg, 0x12800, MS_SYNC); + LOG_INFO("%s: bnx2 uio initialized", nic->log_name); + + bp->flags |= BNX2_OPENED; + + return 0; + +error_bufs: + munmap(bp->tx_ring, 2 * nic->page_size); + +error_tx_ring: + munmap(bp->status_blk.msi, bp->status_blk_size); + +error_sblk: + munmap(bp->reg, 0x12800); + +error_regs: + munlock(bp->rx_pkt_ring, sizeof(void *) * bp->rx_ring_size); + free(bp->rx_pkt_ring); + bp->rx_pkt_ring = NULL; + +error_alloc_rx_pkt_ring: + munlock(bp->rx_ring, sizeof(struct l2_fhdr *) * bp->rx_ring_size); + free(bp->rx_ring); + bp->rx_ring = NULL; + +error_alloc_rx_ring: + if (nic->fd != INVALID_FD) { + close(nic->fd); + nic->fd = INVALID_FD; + } + bnx2_free(nic); + + return errno; +} + +/** + * bnx2_uio_close_resources() - Used to free resource for the bnx2 NIC + * @param nic - NIC device to free resource + * @param graceful - whether to wait to close gracefully + * @return 0 on success, <0 on failure + */ +static int bnx2_uio_close_resources(nic_t *nic, NIC_SHUTDOWN_T graceful) +{ + bnx2_t *bp = (bnx2_t *) nic->priv; + int rc = 0; + + /* Remove the multicast addresses if added */ + if ((nic->flags & NIC_ADDED_MULICAST) && + (graceful == ALLOW_GRACEFUL_SHUTDOWN)) + disable_multicast(nic); + + /* Check if there is an assoicated bnx2 device */ + if (bp == NULL) { + LOG_WARN(PFX "%s: when closing resources there is " + "no assoicated bnx2", nic->log_name); + return -EIO; + } + + /* Clean up allocated memory */ + if (bp->rx_ring != NULL) { + free(bp->rx_ring); + bp->rx_ring = NULL; + } + + if (bp->rx_pkt_ring != NULL) { + free(bp->rx_pkt_ring); + bp->rx_pkt_ring = NULL; + } + + /* Clean up mapped registers */ + if (bp->bufs != NULL) { + rc = munmap(bp->bufs, + (bp->rx_ring_size + 1) * bp->rx_buffer_size); + if (rc != 0) + LOG_WARN(PFX "%s: Couldn't unmap bufs", nic->log_name); + bp->bufs = NULL; + } + + if (bp->tx_ring != NULL) { + rc = munmap(bp->tx_ring, 2 * nic->page_size); + if (rc != 0) + LOG_WARN(PFX "%s: Couldn't unmap tx_rings", + nic->log_name); + bp->tx_ring = NULL; + } + + if (bp->status_blk.msix != NULL || bp->status_blk.msi != NULL) { + rc = munmap(bp->sblk_map, bp->status_blk_size); + if (rc != 0) + LOG_WARN(PFX "%s: Couldn't unmap status block", + nic->log_name); + bp->sblk_map = NULL; + + bp->status_blk.msix = NULL; + bp->status_blk.msi = NULL; + } + + if (bp->reg != NULL) { + rc = munmap(bp->reg, 0x12800); + if (rc != 0) + LOG_WARN(PFX "%s: Couldn't unmap regs", nic->log_name); + bp->reg = NULL; + } + + if (bp->bar0_fd != INVALID_FD) { + close(bp->bar0_fd); + bp->bar0_fd = INVALID_FD; + } + + if (nic->fd != INVALID_FD) { + rc = close(nic->fd); + if (rc != 0) { + LOG_WARN(PFX + "%s: Couldn't close uio file descriptor: %d", + nic->log_name, nic->fd); + } else { + LOG_DEBUG(PFX "%s: Closed uio file descriptor: %d", + nic->log_name, nic->fd); + } + + nic->fd = INVALID_FD; + } else { + LOG_WARN(PFX "%s: Invalid uio file descriptor: %d", + nic->log_name, nic->fd); + } + + LOG_INFO(PFX "%s: Closed all resources", nic->log_name); + + return 0; +} + +/** + * bnx2_close() - Used to close the NIC device + * @param nic - NIC device to close + * @param graceful - whether to wait to close gracefully + * @return 0 if successful, <0 if there is an error + */ +static int bnx2_close(nic_t *nic, NIC_SHUTDOWN_T graceful) +{ + /* Sanity Check: validate the parameters */ + if (nic == NULL) { + LOG_ERR(PFX "bnx2_close(): nic == NULL"); + return -EINVAL; + } + + LOG_INFO(PFX "Closing NIC device: %s", nic->log_name); + + bnx2_uio_close_resources(nic, graceful); + bnx2_free(nic); + + return 0; +} + +static void bnx2_prepare_xmit_packet(nic_t *nic, + nic_interface_t *nic_iface, + struct packet *pkt) +{ + bnx2_t *bp = (bnx2_t *) nic->priv; + struct uip_vlan_eth_hdr *eth_vlan = (struct uip_vlan_eth_hdr *)pkt->buf; + struct uip_eth_hdr *eth = (struct uip_eth_hdr *)bp->tx_pkt; + + if (eth_vlan->tpid == htons(UIP_ETHTYPE_8021Q)) { + memcpy(bp->tx_pkt, pkt->buf, sizeof(struct uip_eth_hdr)); + eth->type = eth_vlan->type; + pkt->buf_size -= (sizeof(struct uip_vlan_eth_hdr) - + sizeof(struct uip_eth_hdr)); + memcpy(bp->tx_pkt + sizeof(struct uip_eth_hdr), + pkt->buf + sizeof(struct uip_vlan_eth_hdr), + pkt->buf_size - sizeof(struct uip_eth_hdr)); + } else + memcpy(bp->tx_pkt, pkt->buf, pkt->buf_size); + + msync(bp->tx_pkt, pkt->buf_size, MS_SYNC); +} + +/** + * bnx2_get_tx_pkt() - This function is used to a TX packet from the NIC + * @param nic - The NIC device to send the packet + * + */ +void *bnx2_get_tx_pkt(nic_t *nic) +{ + bnx2_t *bp = (bnx2_t *) nic->priv; + return bp->tx_pkt; +} + +/** + * bnx2_start_xmit() - This function is used to send a packet of data + * @param nic - The NIC device to send the packet + * @param len - the length of the TX packet + * + */ +void bnx2_start_xmit(nic_t *nic, size_t len, u16_t vlan_id) +{ + bnx2_t *bp = (bnx2_t *) nic->priv; + uint16_t ring_prod; + struct tx_bd *txbd; + struct rx_bd *rxbd; + rxbd = (struct rx_bd *)(((__u8 *) bp->tx_ring) + nic->page_size); + + if ((rxbd->rx_bd_haddr_hi == 0) && (rxbd->rx_bd_haddr_lo == 0)) { + LOG_PACKET(PFX "%s: trying to transmit when device is closed", + nic->log_name); + pthread_mutex_unlock(&nic->xmit_mutex); + return; + } + + ring_prod = TX_RING_IDX(bp->tx_prod); + txbd = &bp->tx_ring[ring_prod]; + + txbd->tx_bd_mss_nbytes = len; + + if (vlan_id) { + txbd->tx_bd_vlan_tag_flags = (vlan_id << 16) | + TX_BD_FLAGS_VLAN_TAG | TX_BD_FLAGS_END | TX_BD_FLAGS_START; + } else + txbd->tx_bd_vlan_tag_flags = TX_BD_FLAGS_END | + TX_BD_FLAGS_START; + + bp->tx_bseq += len; + bp->tx_prod = NEXT_TX_BD(bp->tx_prod); + + bnx2_wr16(bp, bp->tx_bidx_io, bp->tx_prod); + bnx2_wr32(bp, bp->tx_bseq_io, bp->tx_bseq); + + bnx2_reg_sync(bp, bp->tx_bidx_io, sizeof(__u16)); + bnx2_reg_sync(bp, bp->tx_bseq_io, sizeof(__u32)); + + LOG_PACKET(PFX "%s: sent %d bytes using dev->tx_prod: %d", + nic->log_name, len, bp->tx_prod); +} + +/** + * bnx2_write() - Used to write the data to the hardware + * @param nic - NIC hardware to read from + * @param pkt - The packet which will hold the data to be sent on the wire + * @return 0 if successful, <0 if failed + */ +int bnx2_write(nic_t *nic, nic_interface_t *nic_iface, packet_t *pkt) +{ + bnx2_t *bp; + struct uip_stack *uip; + + /* Sanity Check: validate the parameters */ + if (nic == NULL || nic_iface == NULL || pkt == NULL) { + LOG_ERR(PFX "%s: bnx2_write() nic == 0x%p || " + " nic_iface == 0x%p || " + " pkt == 0x%x", nic, nic_iface, pkt); + return -EINVAL; + } + bp = (bnx2_t *)nic->priv; + uip = &nic_iface->ustack; + + if (pkt->buf_size == 0) { + LOG_ERR(PFX "%s: Trying to transmitted 0 sized packet", + nic->log_name); + return -EINVAL; + } + + if (pthread_mutex_trylock(&nic->xmit_mutex) != 0) { + LOG_PACKET(PFX "%s: Dropped previous transmitted packet", + nic->log_name); + return -EINVAL; + } + + bnx2_prepare_xmit_packet(nic, nic_iface, pkt); + bnx2_start_xmit(nic, pkt->buf_size, + (nic_iface->vlan_priority << 12) | + nic_iface->vlan_id); + + /* bump the bnx2 dev send statistics */ + nic->stats.tx.packets++; + nic->stats.tx.bytes += uip->uip_len; + + LOG_PACKET(PFX "%s: transmitted %d bytes " + "dev->tx_cons: %d, dev->tx_prod: %d, dev->tx_bseq:%d", + nic->log_name, pkt->buf_size, + bp->tx_cons, bp->tx_prod, bp->tx_bseq); + + return 0; +} + +/** + * bnx2_read() - Used to read the data from the hardware + * @param nic - NIC hardware to read from + * @param pkt - The packet which will hold the data + * @return 0 if successful, <0 if failed + */ +static int bnx2_read(nic_t *nic, packet_t *pkt) +{ + bnx2_t *bp; + int rc = 0; + uint16_t hw_cons, sw_cons; + + /* Sanity Check: validate the parameters */ + if (unlikely(nic == NULL || pkt == NULL)) { + LOG_ERR(PFX "%s: bnx2_write() nic == 0x%p || " + " pkt == 0x%x", nic, pkt); + return -EINVAL; + } + bp = (bnx2_t *)nic->priv; + + hw_cons = bp->get_rx_cons(bp); + sw_cons = bp->rx_cons; + + if (sw_cons != hw_cons) { + uint8_t rx_index = bp->rx_index % 3; + struct l2_fhdr *rx_hdr = bp->rx_ring[rx_index]; + void *rx_pkt = bp->rx_pkt_ring[rx_index]; + int len; + uint16_t errors; + + LOG_PACKET(PFX "%s: clearing rx interrupt: %d %d %d", + nic->log_name, sw_cons, hw_cons, rx_index); + + msync(rx_hdr, sizeof(struct l2_fhdr), MS_SYNC); + errors = ((rx_hdr->l2_fhdr_status & 0xffff0000) >> 16); + len = ((rx_hdr->l2_fhdr_vtag_len & 0xffff0000) >> 16) - 4; + + if (unlikely((errors & (L2_FHDR_ERRORS_BAD_CRC | + L2_FHDR_ERRORS_PHY_DECODE | + L2_FHDR_ERRORS_ALIGNMENT | + L2_FHDR_ERRORS_TOO_SHORT | + L2_FHDR_ERRORS_GIANT_FRAME)) || + (len <= 0) || + (len > (bp->rx_buffer_size - + (sizeof(struct l2_fhdr) + 2))) || + (len > pkt->max_buf_size))) { + /* One of the fields in the BD is bad */ + uint16_t status = ((rx_hdr->l2_fhdr_status & + 0x0000ffff)); + + LOG_ERR(PFX "%s: Recv error: 0x%x status: 0x%x " + "len: %d", nic->log_name, errors, status, len); + + if ((len < (bp->rx_buffer_size - + (sizeof(struct l2_fhdr) + 2))) && + (len < pkt->max_buf_size)) + dump_packet_to_log(pkt->nic_iface, rx_pkt, len); + } else { + if (len < (bp->rx_buffer_size - + (sizeof(struct l2_fhdr) + 2))) { + msync(rx_pkt, len, MS_SYNC); + /* Copy the data */ + memcpy(pkt->buf, rx_pkt, len); + pkt->buf_size = len; + + /* Properly set the packet flags */ + /* check if there is VLAN tagging on the + * packet */ + if (rx_hdr->l2_fhdr_status & + L2_FHDR_STATUS_VLAN_TAG) { + pkt->vlan_tag = + rx_hdr->l2_fhdr_vtag_len & 0x0FFF; + pkt->flags |= VLAN_TAGGED; + } else { + pkt->vlan_tag = 0; + } + + rc = 1; + + LOG_PACKET(PFX "%s: processing packet " + "length: %d", nic->log_name, len); + } else { + /* If the NIC passes up a packet bigger + * then the RX buffer, flag it */ + LOG_ERR(PFX "%s: invalid packet length %d " + "recieve ", nic->log_name, len); + } + } + + bp->rx_index++; + sw_cons = NEXT_RX_BD(sw_cons); + bp->rx_prod = NEXT_RX_BD(bp->rx_prod); + bp->rx_bseq += 0x400; + + bp->rx_cons = sw_cons; + bnx2_wr16(bp, bp->rx_bidx_io, bp->rx_prod); + bnx2_wr32(bp, bp->rx_bseq_io, bp->rx_bseq); + + bnx2_reg_sync(bp, bp->rx_bidx_io, sizeof(__u16)); + bnx2_reg_sync(bp, bp->rx_bseq_io, sizeof(__u32)); + + /* bump the bnx2 dev recv statistics */ + nic->stats.rx.packets++; + nic->stats.rx.bytes += pkt->buf_size; + } + + return rc; +} + +/******************************************************************************* + * Clearing TX interrupts + ******************************************************************************/ +/** + * bnx2_clear_tx_intr() - This routine is called when a TX interrupt occurs + * @param nic - the nic the interrupt occured on + * @return 0 on success + */ +static int bnx2_clear_tx_intr(nic_t *nic) +{ + bnx2_t *bp; + uint16_t hw_cons; + + /* Sanity check: ensure the parameters passed in are valid */ + if (unlikely(nic == NULL)) { + LOG_ERR(PFX "bnx2_read() nic == NULL"); + return -EINVAL; + } + bp = (bnx2_t *) nic->priv; + hw_cons = bp->get_tx_cons(bp); + + if (bp->flags & BNX2_UIO_TX_HAS_SENT) + bp->flags &= ~BNX2_UIO_TX_HAS_SENT; + + LOG_PACKET(PFX "%s: clearing tx interrupt [%d %d]", + nic->log_name, bp->tx_cons, hw_cons); + + bp->tx_cons = hw_cons; + + /* There is a queued TX packet that needs to be sent out. The usual + * case is when stack will send an ARP packet out before sending the + * intended packet */ + if (nic->tx_packet_queue != NULL) { + packet_t *pkt; + + LOG_PACKET(PFX "%s: sending queued tx packet", nic->log_name); + pkt = nic_dequeue_tx_packet(nic); + + /* Got a TX packet buffer of the TX queue and put it onto + * the hardware */ + if (pkt != NULL) { + bnx2_prepare_xmit_packet(nic, pkt->nic_iface, pkt); + + bnx2_start_xmit(nic, pkt->buf_size, + (pkt->nic_iface->vlan_priority << 12) | + pkt->nic_iface->vlan_id); + + LOG_PACKET(PFX "%s: transmitted queued packet %d bytes " + "dev->tx_cons: %d, dev->tx_prod: %d, " + "dev->tx_bseq:%d", + nic->log_name, pkt->buf_size, + bp->tx_cons, bp->tx_prod, bp->tx_bseq); + + return -EAGAIN; + } + } + + pthread_mutex_unlock(&nic->xmit_mutex); + + return 0; +} + +/******************************************************************************* + * bnx2 NIC op's table + ******************************************************************************/ +struct nic_ops bnx2_op = { + .description = "bnx2", + .open = bnx2_open, + .close = bnx2_close, + .write = bnx2_write, + .get_tx_pkt = bnx2_get_tx_pkt, + .start_xmit = bnx2_start_xmit, + .read = bnx2_read, + .clear_tx_intr = bnx2_clear_tx_intr, + .handle_iscsi_path_req = cnic_handle_iscsi_path_req, + + .lib_ops = { + .get_library_name = bnx2_get_library_name, + .get_pci_table = bnx2_get_pci_table, + .get_library_version = bnx2_get_library_version, + .get_build_date = bnx2_get_build_date, + .get_transport_name = bnx2_get_transport_name, + .get_uio_name = bnx2_get_uio_name, + }, +}; diff --git a/iscsiuio/src/unix/libs/bnx2.h b/iscsiuio/src/unix/libs/bnx2.h new file mode 100644 index 0000000..3ec9437 --- /dev/null +++ b/iscsiuio/src/unix/libs/bnx2.h @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2009-2011, Broadcom Corporation + * Copyright (c) 2014, QLogic Corporation + * + * Written by: Benjamin Li (benli@broadcom.com) + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam Dunkels. + * 4. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * bnx2.h - bnx2 user space driver + * + */ +#ifndef __BNX2_H__ +#define __BNX2_H__ + +#include "nic.h" + +/****************************************************************************** + * Default BNX2 values + ******************************************************************************/ +#define DEFAULT_NUM_RXBD 3 +#define DEFAULT_RX_LEN 0x400 + +/****************************************************************************** + * BNX2 Hardware structures + ******************************************************************************/ +/* status_block definition for MSI */ +struct status_block { + volatile __u32 status_attn_bits; + volatile __u32 status_attn_bits_ack; + volatile __u32 tx0; + volatile __u32 tx2; + volatile __u32 rx0; + volatile __u32 rx2; + volatile __u32 rx4; + volatile __u32 rx6; + volatile __u32 rx8; + volatile __u32 rx10; + volatile __u32 rx12; + volatile __u32 rx14; + volatile __u32 cmd; + volatile __u32 idx; +}; + +/* status_block definition for MSI-X */ +struct status_block_msix { +#if 0 +#if defined(__BIG_ENDIAN) + __u16 status_tx_quick_consumer_index; + __u16 status_rx_quick_consumer_index; + __u16 status_completion_producer_index; + __u16 status_cmd_consumer_index; + __u32 status_unused; + __u16 status_idx; + __u8 status_unused2; + __u8 status_blk_num; +#elif defined(__LITTLE_ENDIAN) + __u16 status_rx_quick_consumer_index; + __u16 status_tx_quick_consumer_index; + __u16 status_cmd_consumer_index; + __u16 status_completion_producer_index; + __u32 status_unused; + __u8 status_blk_num; + __u8 status_unused2; + __u16 status_idx; +#endif +#endif + __u16 status_rx_quick_consumer_index; + __u16 status_tx_quick_consumer_index; + __u16 status_cmd_consumer_index; + __u16 status_completion_producer_index; + __u32 status_unused; + __u8 status_blk_num; + __u8 status_unused2; + __u16 status_idx; +}; + +/* TX Buffer descriptor */ +struct tx_bd { + __u32 tx_bd_haddr_hi; + __u32 tx_bd_haddr_lo; + __u32 tx_bd_mss_nbytes; + __u32 tx_bd_vlan_tag_flags; +#define TX_BD_FLAGS_VLAN_TAG (1<<3) +#define TX_BD_FLAGS_END (1<<6) +#define TX_BD_FLAGS_START (1<<7) +}; + +/* RX Buffer descriptor */ +struct rx_bd { + __u32 rx_bd_haddr_hi; + __u32 rx_bd_haddr_lo; + + __u32 rx_bd_len; + __u32 rx_bd_flags; +#define RX_BD_FLAGS_END (1<<2) +#define RX_BD_FLAGS_START (1<<3) + +}; + +/* This is the RX L2 Frame header */ +struct l2_fhdr { + __u32 l2_fhdr_status; +#define L2_FHDR_ERRORS_BAD_CRC (1<<17) +#define L2_FHDR_ERRORS_PHY_DECODE (1<<18) +#define L2_FHDR_ERRORS_ALIGNMENT (1<<19) +#define L2_FHDR_ERRORS_TOO_SHORT (1<<20) +#define L2_FHDR_ERRORS_GIANT_FRAME (1<<21) +#define L2_FHDR_ERRORS_TCP_XSUM (1<<28) +#define L2_FHDR_ERRORS_UDP_XSUM (1<<31) + +#define L2_FHDR_STATUS_UDP_DATAGRAM (1<<15) +#define L2_FHDR_STATUS_TCP_DATAGRAM (1<<14) +#define L2_FHDR_STATUS_IP_DATAGRAM (1<<13) +#define L2_FHDR_STATUS_LLC_SNAP (1<<7) +#define L2_FHDR_STATUS_VLAN_TAG (1<<6) + + __u32 l2_fhdr_hash; + + __u32 l2_fhdr_vtag_len; + __u32 l2_fhdr_xsum; +}; + +/****************************************************************************** + * BNX2 Registers Defitions/Values + ******************************************************************************/ +#define BNX2_MISC_ID 0x00000808 +#define BNX2_EMAC_MAC_MATCH4 0x00001420 +#define BNX2_EMAC_MAC_MATCH5 0x00001424 + +#define BNX2_EMAC_RX_MODE 0x000014c8 +#define BNX2_EMAC_RX_MODE_RESET (1L<<0) +#define BNX2_EMAC_RX_MODE_FLOW_EN (1L<<2) +#define BNX2_EMAC_RX_MODE_KEEP_MAC_CONTROL (1L<<3) +#define BNX2_EMAC_RX_MODE_KEEP_PAUSE (1L<<4) +#define BNX2_EMAC_RX_MODE_ACCEPT_OVERSIZE (1L<<5) +#define BNX2_EMAC_RX_MODE_ACCEPT_RUNTS (1L<<6) +#define BNX2_EMAC_RX_MODE_LLC_CHK (1L<<7) +#define BNX2_EMAC_RX_MODE_PROMISCUOUS (1L<<8) +#define BNX2_EMAC_RX_MODE_NO_CRC_CHK (1L<<9) +#define BNX2_EMAC_RX_MODE_KEEP_VLAN_TAG (1L<<10) +#define BNX2_EMAC_RX_MODE_FILT_BROADCAST (1L<<11) +#define BNX2_EMAC_RX_MODE_SORT_MODE (1L<<12) + +#define BNX2_RPM_SORT_USER2 0x00001828 +#define BNX2_RPM_SORT_USER2_PM_EN (0xffffL<<0) +#define BNX2_RPM_SORT_USER2_BC_EN (1L<<16) +#define BNX2_RPM_SORT_USER2_MC_EN (1L<<17) +#define BNX2_RPM_SORT_USER2_MC_HSH_EN (1L<<18) +#define BNX2_RPM_SORT_USER2_PROM_EN (1L<<19) +#define BNX2_RPM_SORT_USER2_VLAN_EN (0xfL<<20) +#define BNX2_RPM_SORT_USER2_PROM_VLAN (1L<<24) +#define BNX2_RPM_SORT_USER2_ENA (1L<<31) + +/* + * tsch_reg definition + * offset: 0x4c00 + */ +#define BNX2_TSCH_TSS_CFG 0x00004c1c +#define BNX2_TSCH_TSS_CFG_TSS_START_CID (0x7ffL<<8) +#define BNX2_TSCH_TSS_CFG_NUM_OF_TSS_CON (0xfL<<24) +#define CNIC_UIO_INVALID_FD -1 + +#define BNX2_L2CTX_TX_HOST_BIDX 0x00000088 +#define BNX2_L2CTX_TX_HOST_BSEQ 0x00000090 + +#define BNX2_L2CTX_HOST_BDIDX 0x00000004 +#define BNX2_L2CTX_HOST_BSEQ 0x00000008 + +/* Used to determin the CHIP ID */ +/* chip num:16-31, rev:12-15, metal:4-11, bond_id:0-3 */ +#define BNX2_CHIP_NUM(bp) ((bp) & 0xffff0000) +#define CHIP_NUM_5706 0x57060000 +#define CHIP_NUM_5708 0x57080000 +#define CHIP_NUM_5709 0x57090000 + +#define CHIP_REV(bp) ((bp) & 0x0000f000) +#define CHIP_REV_Ax 0x00000000 +#define CHIP_REV_Bx 0x00001000 +#define CHIP_REV_Cx 0x00002000 + +#define CHIP_METAL(bp) ((bp) & 0x00000ff0) +#define CHIP_BONDING(bp) ((bp) & 0x0000000f) + +#define CHIP_ID(bp) ((bp) & 0xfffffff0) +#define CHIP_ID_5706_A0 0x57060000 +#define CHIP_ID_5706_A1 0x57060010 +#define CHIP_ID_5706_A2 0x57060020 +#define CHIP_ID_5708_A0 0x57080000 +#define CHIP_ID_5708_B0 0x57081000 +#define CHIP_ID_5708_B1 0x57081010 +#define CHIP_ID_5709_A0 0x57090000 +#define CHIP_ID_5709_A1 0x57090010 + +#define CHIP_BOND_ID(bp) ((bp) & 0xf) + +#define BNX2_SBLK_EVEN_IDX(x) (((x) & 0xffff0000) >> 16) + +#define TX_DESC_CNT (4096 / sizeof(struct tx_bd)) +#define MAX_TX_DESC_CNT (TX_DESC_CNT - 1) + +#define NEXT_TX_BD(x) ((((x) & (MAX_TX_DESC_CNT - 1)) == \ + (MAX_TX_DESC_CNT - 1)) ? \ + (x) + 2 : (x) + 1) + +#define TX_RING_IDX(x) ((x) & MAX_TX_DESC_CNT) + +#define RX_DESC_CNT (4096 / sizeof(struct rx_bd)) +#define MAX_RX_DESC_CNT (RX_DESC_CNT - 1) + +#define NEXT_RX_BD(x) ((((x) & (MAX_RX_DESC_CNT - 1)) == \ + (MAX_RX_DESC_CNT - 1)) ? \ + (x) + 2 : (x) + 1) + +#define MB_KERNEL_CTX_SHIFT 8 +#define MB_KERNEL_CTX_SIZE (1 << MB_KERNEL_CTX_SHIFT) +#define MB_KERNEL_CTX_MASK (MB_KERNEL_CTX_SIZE - 1) +#define MB_GET_CID_ADDR(_cid) (0x10000 + ((_cid) << MB_KERNEL_CTX_SHIFT)) + +typedef struct bnx2 { + nic_t *parent; + + uint16_t flags; +#define BNX2_UIO_MSIX_ENABLED 0x0001 +#define BNX2_UIO_TX_HAS_SENT 0x0002 +#define BNX2_OPENED 0x0004 + + int bar0_fd; + void *reg; /* Pointer to the mapped registers */ + + __u32 tx_bidx_io; + __u32 tx_bseq_io; + + __u16 tx_prod; + __u16 tx_cons; + __u32 tx_bseq; + + __u32 rx_bidx_io; + __u32 rx_bseq_io; + + __u16 rx_prod; + __u16 rx_cons; + __u32 rx_bseq; + + /* RX ring parameters */ + uint32_t rx_ring_size; + uint32_t rx_buffer_size; + + void *bufs; /* Pointer to the mapped buffer space */ + + /* Hardware Status Block locations */ + void *sblk_map; + union { + struct status_block *msi; + struct status_block_msix *msix; + } status_blk; + size_t status_blk_size; + + __u16(*get_rx_cons) (struct bnx2 *); + __u16(*get_tx_cons) (struct bnx2 *); + + uint16_t rx_index; + struct l2_fhdr **rx_ring; + void **rx_pkt_ring; + + struct tx_bd *tx_ring; + void *tx_pkt; + + struct l2_fhdr rcv_l2_fhdr; + __u8 rcv_buf[1500 + 2]; + __u32 rcv_size; +} bnx2_t; + +/****************************************************************************** + * bnx2 Function Declarations + ******************************************************************************/ +struct nic_ops *bnx2_get_ops(); +#endif /* __BNX2_H__ */ diff --git a/iscsiuio/src/unix/libs/bnx2x.c b/iscsiuio/src/unix/libs/bnx2x.c new file mode 100644 index 0000000..1495762 --- /dev/null +++ b/iscsiuio/src/unix/libs/bnx2x.c @@ -0,0 +1,1634 @@ +/* + * Copyright (c) 2009-2011, Broadcom Corporation + * Copyright (c) 2014, QLogic Corporation + * + * Written by: Benjamin Li (benli@broadcom.com) + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam Dunkels. + * 4. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * bnx2x.c - bnx2x user space driver + * + */ +#include +#include +#include +#include +#include /* Needed for linux/ethtool.h on RHEL 5.x */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" + +#include "build_date.h" +#include "bnx2x.h" +#include "cnic.h" +#include "logger.h" +#include "nic.h" +#include "nic_id.h" +#include "nic_utils.h" +#include "options.h" + +#define PFX "bnx2x " + +/* Foward struct declarations */ +struct nic_ops bnx2x_op; + +/******************************************************************************* + * NIC Library Strings + ******************************************************************************/ +static const char library_name[] = "bnx2x"; +static const char library_version[] = PACKAGE_VERSION; +static const char library_uio_name[] = "bnx2x_cnic"; + +/* The name that should be returned from /sys/class/uio/uio0/name */ +static const char cnic_uio_sysfs_name_tempate[] = "/sys/class/uio/uio%i/name"; +static const char bnx2x_uio_sysfs_name[] = "bnx2x_cnic"; + +/******************************************************************************* + * String constants used to display human readable adapter name + ******************************************************************************/ +static const char brcm_57710[] = "QLogic NetXtreme II BCM57710 10-Gigabit"; +static const char brcm_57711[] = "QLogic NetXtreme II BCM57711 10-Gigabit"; +static const char brcm_57711e[] = "QLogic NetXtreme II BCM57711E 10-Gigabit"; +static const char brcm_57712[] = "QLogic NetXtreme II BCM57712 10-Gigabit"; +static const char brcm_57712_MF[] = "QLogic NetXtreme II BCM57712 MF " + "10-Gigabit"; +static const char brcm_57712_VF[] = "QLogic NetXtreme II BCM57712 VF " + "10-Gigabit"; +static const char brcm_57713[] = "QLogic NetXtreme II BCM57713 10-Gigabit"; +static const char brcm_57713e[] = "QLogic NetXtreme II BCM57713E 10-Gigabit"; +static const char brcm_57800[] = "QLogic NetXtreme II BCM57800 10-Gigabit"; +static const char brcm_57800_MF[] = "QLogic NetXtreme II BCM57800 MF " + "10-Gigabit"; +static const char brcm_57800_VF[] = "QLogic NetXtreme II BCM57800 VF " + "10-Gigabit"; +static const char brcm_57810[] = "QLogic NetXtreme II BCM57810 10-Gigabit"; +static const char brcm_57810_MF[] = "QLogic NetXtreme II BCM57810 MF " + "10-Gigabit"; +static const char brcm_57810_VF[] = "QLogic NetXtreme II BCM57810 VF " + "10-Gigabit"; +static const char brcm_57811[] = "QLogic NetXtreme II BCM57811 10-Gigabit"; +static const char brcm_57811_MF[] = "QLogic NetXtreme II BCM57811 MF " + "10-Gigabit"; +static const char brcm_57811_VF[] = "QLogic NetXtreme II BCM57811 VF " + "10-Gigabit"; +static const char brcm_57840[] = "QLogic NetXtreme II BCM57840 10-Gigabit"; +static const char brcm_57840_MF[] = "QLogic NetXtreme II BCM57840 MF " + "10-Gigabit"; +static const char brcm_57840_VF[] = "QLogic NetXtreme II BCM57840 VF " + "10-Gigabit"; +static const char brcm_57840_4_10[] = "QLogic NetXtreme II BCM57840 4x" + "10-Gigabit"; +static const char brcm_57840_2_20[] = "QLogic NetXtreme II BCM57840 2x" + "20-Gigabit"; + +/******************************************************************************* + * PCI ID constants + ******************************************************************************/ +#define PCI_VENDOR_ID_BROADCOM 0x14e4 +#define PCI_DEVICE_ID_NX2_57710 0x164e +#define PCI_DEVICE_ID_NX2_57711 0x164f +#define PCI_DEVICE_ID_NX2_57711E 0x1650 +#define PCI_DEVICE_ID_NX2_57712 0x1662 +#define PCI_DEVICE_ID_NX2_57712_MF 0x1663 +#define PCI_DEVICE_ID_NX2_57712_VF 0x166f +#define PCI_DEVICE_ID_NX2_57713 0x1651 +#define PCI_DEVICE_ID_NX2_57713E 0x1652 +#define PCI_DEVICE_ID_NX2_57800 0x168a +#define PCI_DEVICE_ID_NX2_57800_MF 0x16a5 +#define PCI_DEVICE_ID_NX2_57800_VF 0x16a9 +#define PCI_DEVICE_ID_NX2_57810 0x168e +#define PCI_DEVICE_ID_NX2_57810_MF 0x16ae +#define PCI_DEVICE_ID_NX2_57810_VF 0x16af +#define PCI_DEVICE_ID_NX2_57811 0x163d +#define PCI_DEVICE_ID_NX2_57811_MF 0x163e +#define PCI_DEVICE_ID_NX2_57811_VF 0x163f +#define PCI_DEVICE_ID_NX2_57840_OBSOLETE 0x168d +#define PCI_DEVICE_ID_NX2_57840_MF_OBSOLETE 0x16ab +#define PCI_DEVICE_ID_NX2_57840_4_10 0x16a1 +#define PCI_DEVICE_ID_NX2_57840_2_20 0x16a2 +#define PCI_DEVICE_ID_NX2_57840_MF 0x16a4 +#define PCI_DEVICE_ID_NX2_57840_VF 0x16ad +#define PCI_ANY_ID (~0) + +/* This is the table used to match PCI vendor and device ID's to the + * human readable string names of the devices */ +static const struct pci_device_id bnx2x_pci_tbl[] = { + {PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_57710, + PCI_ANY_ID, PCI_ANY_ID, brcm_57710}, + {PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_57711, + PCI_ANY_ID, PCI_ANY_ID, brcm_57711}, + {PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_57711E, + PCI_ANY_ID, PCI_ANY_ID, brcm_57711e}, + {PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_57712, + PCI_ANY_ID, PCI_ANY_ID, brcm_57712}, + {PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_57712_MF, + PCI_ANY_ID, PCI_ANY_ID, brcm_57712_MF}, + {PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_57712_VF, + PCI_ANY_ID, PCI_ANY_ID, brcm_57712_VF}, + {PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_57713, + PCI_ANY_ID, PCI_ANY_ID, brcm_57713}, + {PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_57713E, + PCI_ANY_ID, PCI_ANY_ID, brcm_57713e}, + {PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_57800, + PCI_ANY_ID, PCI_ANY_ID, brcm_57800}, + {PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_57800_MF, + PCI_ANY_ID, PCI_ANY_ID, brcm_57800_MF}, + {PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_57800_VF, + PCI_ANY_ID, PCI_ANY_ID, brcm_57800_VF}, + {PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_57810, + PCI_ANY_ID, PCI_ANY_ID, brcm_57810}, + {PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_57810_MF, + PCI_ANY_ID, PCI_ANY_ID, brcm_57810_MF}, + {PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_57810_VF, + PCI_ANY_ID, PCI_ANY_ID, brcm_57810_VF}, + {PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_57811, + PCI_ANY_ID, PCI_ANY_ID, brcm_57811}, + {PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_57811_MF, + PCI_ANY_ID, PCI_ANY_ID, brcm_57811_MF}, + {PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_57811_VF, + PCI_ANY_ID, PCI_ANY_ID, brcm_57811_VF}, + {PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_57840_OBSOLETE, + PCI_ANY_ID, PCI_ANY_ID, brcm_57840}, + {PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_57840_MF_OBSOLETE, + PCI_ANY_ID, PCI_ANY_ID, brcm_57840_MF}, + {PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_57840_4_10, + PCI_ANY_ID, PCI_ANY_ID, brcm_57840_4_10}, + {PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_57840_2_20, + PCI_ANY_ID, PCI_ANY_ID, brcm_57840_2_20}, + {PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_57840_MF, + PCI_ANY_ID, PCI_ANY_ID, brcm_57840_MF}, + {PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_57840_VF, + PCI_ANY_ID, PCI_ANY_ID, brcm_57840_VF}, +}; + +static struct iro e1_iro[2] = { + {0x45a0, 0x90, 0x8, 0x0, 0x8}, /* T6.0 */ + {0x50c8, 0x90, 0x8, 0x0, 0x8}, /* T6.4 */ +}; + +static struct iro e1h_iro[2] = { + {0x1c40, 0xe0, 0x8, 0x0, 0x8}, /* T6.0 */ + {0x1e00, 0xe0, 0x8, 0x0, 0x8}, /* T6.4 */ +}; + +static struct iro e2_iro[2] = { + {0x6000, 0x20, 0x0, 0x0, 0x8}, /* T6.0 */ + {0x6000, 0x20, 0x0, 0x0, 0x8}, /* T6.4 */ +}; + +struct bnx2x_driver_version bnx2x_version = { + BNX2X_UNKNOWN_MAJOR_VERSION, + BNX2X_UNKNOWN_MINOR_VERSION, + BNX2X_UNKNOWN_SUB_MINOR_VERSION, +}; + +static int bnx2x_clear_tx_intr(nic_t *nic); + +/******************************************************************************* + * BNX2X Library Functions + ******************************************************************************/ +/** + * bnx2x_get_library_name() - Used to get the name of this NIC libary + * @param name - This function will return the pointer to this NIC + * library name + * @param name_size + */ +static void bnx2x_get_library_name(char **name, size_t *name_size) +{ + *name = (char *)library_name; + *name_size = sizeof(library_name); +} + +/** + * bnx2x_get_library_version() - Used to get the version string of this + * NIC libary + * @param version - This function will return the pointer to this NIC + * library version string + * @param version_size - This will be set with the version size + */ +static void bnx2x_get_library_version(char **version, size_t *version_size) +{ + *version = (char *)library_version; + *version_size = sizeof(library_version); +} + +/** + * bnx2x_get_build_date() - Used to get the build date string of this library + * @param version - This function will return the pointer to this NIC + * library build date string + * @param version_size - This will be set with the build date string size + */ +static void bnx2x_get_build_date(char **build, size_t *build_size) +{ + *build = (char *)build_date; + *build_size = sizeof(build_date); +} + +/** + * bnx2x_get_transport_name() - Used to get the transport name associated + * with this this NIC libary + * @param transport_name - This function will return the pointer to this NIC + * library's associated transport string + * @param transport_name_size - This will be set with the transport name size + */ +static void bnx2x_get_transport_name(char **transport_name, + size_t *transport_name_size) +{ + *transport_name = (char *)bnx2i_library_transport_name; + *transport_name_size = bnx2i_library_transport_name_size; +} + +/** + * bnx2x_get_uio_name() - Used to get the uio name associated with this this + * NIC libary + * @param uio_name - This function will return the pointer to this NIC + * library's associated uio string + * @param transport_name_size - This will be set with the uio name size + */ +static void bnx2x_get_uio_name(char **uio_name, size_t *uio_name_size) +{ + *uio_name = (char *)library_uio_name; + *uio_name_size = sizeof(library_uio_name); +} + +/** + * bnx2x_get_pci_table() - Used to get the PCI table for this NIC libary to + * determine which NIC's based off of PCI ID's are + * supported + * @param table - This function will return the pointer to the PCI table + * @param entries - This function will return the number of entries in the NIC + * library's PCI table + */ +static void bnx2x_get_pci_table(struct pci_device_id **table, + uint32_t *entries) +{ + *table = (struct pci_device_id *)bnx2x_pci_tbl; + *entries = + (uint32_t) (sizeof(bnx2x_pci_tbl) / sizeof(bnx2x_pci_tbl[0])); +} + +/** + * bnx2x_get_ops() - Used to get the NIC library op table + * @param op - The op table of this NIC library + */ +struct nic_ops *bnx2x_get_ops() +{ + return &bnx2x_op; +} + +/******************************************************************************* + * bnx2x Utility Functions + ******************************************************************************/ +/******************************************************************************* + * Utility Functions Used to read register from the bnx2x device + ******************************************************************************/ +static void bnx2x_set_drv_version_unknown(bnx2x_t *bp) +{ + bp->version.major = BNX2X_UNKNOWN_MAJOR_VERSION; + bp->version.minor = BNX2X_UNKNOWN_MINOR_VERSION; + bp->version.sub_minor = BNX2X_UNKNOWN_SUB_MINOR_VERSION; +} + +/* Return: 1 = Unknown, 0 = Known */ +static int bnx2x_is_drv_version_unknown(struct bnx2x_driver_version *version) +{ + if ((version->major == (uint16_t)BNX2X_UNKNOWN_MAJOR_VERSION) && + (version->minor == (uint16_t)BNX2X_UNKNOWN_MINOR_VERSION) && + (version->sub_minor == (uint16_t)BNX2X_UNKNOWN_SUB_MINOR_VERSION)) { + return 1; + } + + return 0; +} + +/** + * bnx2x_get_drv_version() - Used to determine the driver version + * @param bp - Device used to determine bnx2x driver version + */ +static int bnx2x_get_drv_version(bnx2x_t *bp) +{ + nic_t *nic = bp->parent; + int fd, rc; + struct ifreq ifr; + struct ethtool_drvinfo drvinfo; + char *tok, *save_ptr = NULL; + + /* Setup our control structures. */ + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, nic->eth_device_name); + + /* Open control socket. */ + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + LOG_ERR(PFX "%s: Cannot get socket to determine version " + "[0x%x %s]", nic->log_name, errno, strerror(errno)); + return -EIO; + } + + drvinfo.cmd = ETHTOOL_GDRVINFO; + ifr.ifr_data = (caddr_t) &drvinfo; + rc = ioctl(fd, SIOCETHTOOL, &ifr); + if (rc < 0) { + LOG_ERR(PFX "%s: call to ethool IOCTL failed [0x%x %s]", + nic->log_name, errno, strerror(errno)); + goto error; + } + + tok = strtok_r(drvinfo.version, ".", &save_ptr); + if (tok == NULL) { + rc = -EIO; + goto error; + } + bp->version.major = atoi(tok); + + tok = strtok_r(NULL, ".", &save_ptr); + if (tok == NULL) { + rc = -EIO; + goto error; + } + bp->version.minor = atoi(tok); + + tok = strtok_r(NULL, ".", &save_ptr); + if (tok == NULL) { + rc = -EIO; + goto error; + } + bp->version.sub_minor = atoi(tok); + + LOG_INFO(PFX "%s: bnx2x driver using version %d.%d.%d", + nic->log_name, + bp->version.major, bp->version.minor, bp->version.sub_minor); + + close(fd); + + return 0; + +error: + close(fd); + bnx2x_set_drv_version_unknown(bp); + + LOG_ERR(PFX "%s: error parsing driver string: '%s'", + nic->log_name, drvinfo.version); + + return rc; + +} + +static inline int bnx2x_is_ver70(bnx2x_t *bp) +{ + return (bp->version.major == 1 && bp->version.minor >= 70); +} + +static inline int bnx2x_is_ver60(bnx2x_t *bp) +{ + return (bp->version.major == 1 && (bp->version.minor == 60 || + bp->version.minor == 62 || + bp->version.minor == 64)); +} + +static inline int bnx2x_is_ver60_plus(bnx2x_t *bp) +{ + return bnx2x_is_ver60(bp) || bnx2x_is_ver70(bp); +} + +static inline int bnx2x_is_ver52(bnx2x_t *bp) +{ + return (bp->version.major == 1 && bp->version.minor == 52); +} + +static void bnx2x_wr32(bnx2x_t *bp, __u32 off, __u32 val) +{ + *((volatile __u32 *)(bp->reg + off)) = val; +} + +static void bnx2x_doorbell(bnx2x_t *bp, __u32 off, __u32 val) +{ + *((volatile __u32 *)(bp->reg2 + off)) = val; +} + +static void bnx2x_flush_doorbell(bnx2x_t *bp, __u32 off) +{ + volatile __u32 tmp __attribute__((__unused__)); + + barrier(); + tmp = *((volatile __u32 *)(bp->reg2 + off)); +} + +static __u32 bnx2x_rd32(bnx2x_t *bp, __u32 off) +{ + return *((volatile __u32 *)(bp->reg + off)); +} + +static int bnx2x_reg_sync(bnx2x_t *bp, __u32 off, __u16 length) +{ + return msync(bp->reg + off, length, MS_SYNC); +} + +static void bnx2x_update_rx_prod(bnx2x_t *bp) +{ + struct ustorm_eth_rx_producers rx_prods = { 0 }; + int i; + + rx_prods.bd_prod = bp->rx_bd_prod; + rx_prods.cqe_prod = bp->rx_prod; + + barrier(); + + for (i = 0; i < sizeof(struct ustorm_eth_rx_producers) / 4; i++) + bnx2x_wr32(bp, bp->rx_prod_io + i * 4, + ((__u32 *)&rx_prods)[i]); + + barrier(); + + bnx2x_reg_sync(bp, bp->rx_prod_io, + sizeof(struct ustorm_eth_rx_producers)); +} + +/** + * bnx2x_get_chip_id() - Used to retrive the chip ID from the nic + * @param dev - Device used to determin NIC type + * @return Chip ID read from the MISC ID register + */ +static int bnx2x_get_chip_id(bnx2x_t *bp) +{ + int val, id; + + /* Get the chip revision id and number. */ + /* chip num:16-31, rev:12-15, metal:4-11, bond_id:0-3 */ + val = bnx2x_rd32(bp, BNX2X_MISC_REG_CHIP_NUM); + id = ((val & 0xffff) << 16); + val = bnx2x_rd32(bp, BNX2X_MISC_REG_CHIP_REV); + id |= ((val & 0xf) << 12); + val = bnx2x_rd32(bp, BNX2X_MISC_REG_CHIP_METAL); + id |= ((val & 0xff) << 4); + val = bnx2x_rd32(bp, BNX2X_MISC_REG_BOND_ID); + id |= (val & 0xf); + + return id; +} + +/** + * bnx2x_uio_verify() + * + */ +static int bnx2x_uio_verify(nic_t *nic) +{ + char *raw = NULL, *raw_tmp; + uint32_t raw_size = 0; + char temp_path[sizeof(cnic_uio_sysfs_name_tempate) + 8]; + int rc = 0; + + /* Build the path to determine uio name */ + snprintf(temp_path, sizeof(temp_path), + cnic_uio_sysfs_name_tempate, nic->uio_minor); + + rc = capture_file(&raw, &raw_size, temp_path); + if (rc != 0) + goto error; + + /* sanitize name string by replacing newline with null termination */ + raw_tmp = raw; + while (*raw_tmp != '\n') + raw_tmp++; + *raw_tmp = '\0'; + + if (strncmp(raw, bnx2x_uio_sysfs_name, + sizeof(bnx2x_uio_sysfs_name)) != 0) { + LOG_ERR(PFX "%s: uio names not equal: " + "expecting %s got %s from %s", + nic->log_name, bnx2x_uio_sysfs_name, raw, temp_path); + rc = -EIO; + } + + free(raw); + + LOG_INFO(PFX "%s: Verified is a cnic_uio device", nic->log_name); + +error: + return rc; +} + +/******************************************************************************* + * bnx2x Utility Functions to get to the hardware consumer indexes + ******************************************************************************/ +static __u16 bnx2x_get_rx(bnx2x_t *bp) +{ + struct host_def_status_block *sblk = bp->status_blk.def; + __u16 rx_comp_cons; + + msync(sblk, sizeof(*sblk), MS_SYNC); + rx_comp_cons = + sblk->u_def_status_block. + index_values[HC_INDEX_DEF_U_ETH_ISCSI_RX_CQ_CONS]; + if ((rx_comp_cons & BNX2X_MAX_RCQ_DESC_CNT(bp)) == + BNX2X_MAX_RCQ_DESC_CNT(bp)) + rx_comp_cons++; + + return rx_comp_cons; +} + +static __u16 bnx2x_get_rx_60(bnx2x_t *bp) +{ + struct host_sp_status_block *sblk = bp->status_blk.sp; + __u16 rx_comp_cons; + + msync(sblk, sizeof(*sblk), MS_SYNC); + rx_comp_cons = + sblk->sp_sb.index_values[HC_SP_INDEX_ETH_ISCSI_RX_CQ_CONS]; + if ((rx_comp_cons & BNX2X_MAX_RCQ_DESC_CNT(bp)) == + BNX2X_MAX_RCQ_DESC_CNT(bp)) + rx_comp_cons++; + + return rx_comp_cons; +} + +static __u16 bnx2x_get_tx(bnx2x_t *bp) +{ + struct host_def_status_block *sblk = bp->status_blk.def; + __u16 tx_cons; + + msync(sblk, sizeof(*sblk), MS_SYNC); + tx_cons = + sblk->c_def_status_block. + index_values[HC_INDEX_DEF_C_ETH_ISCSI_CQ_CONS]; + + return tx_cons; +} + +static __u16 bnx2x_get_tx_60(bnx2x_t *bp) +{ + struct host_sp_status_block *sblk = bp->status_blk.sp; + __u16 tx_cons; + + msync(sblk, sizeof(*sblk), MS_SYNC); + tx_cons = sblk->sp_sb.index_values[HC_SP_INDEX_ETH_ISCSI_CQ_CONS]; + + return tx_cons; +} + +typedef enum { + CNIC_VLAN_STRIPPING_ENABLED = 1, + CNIC_VLAN_STRIPPING_DISABLED = 2, +} CNIC_VLAN_STRIPPING_MODE; + +/** + * bnx2x_strip_vlan_enabled() - This will query the device to determine whether + * VLAN tag stripping is enabled or not + * @param dev - device to check stripping or not + * @ return CNIC_VLAN_STRIPPING_ENABLED stripping is enabled + * CNIC_VLAN_STRIPPING_DISABLED stripping is not enabled + */ +static CNIC_VLAN_STRIPPING_MODE bnx2x_strip_vlan_enabled(bnx2x_t *bp) +{ + return CNIC_VLAN_STRIPPING_DISABLED; +} + +/** + * bnx2x_free() - Used to free a bnx2x structure + */ +static void bnx2x_free(nic_t *nic) +{ + if (nic->priv) + free(nic->priv); + nic->priv = NULL; +} + +/** + * bnx2x_alloc() - Used to allocate a bnx2x structure + */ +static bnx2x_t *bnx2x_alloc(nic_t *nic) +{ + bnx2x_t *bp = malloc(sizeof(*bp)); + + if (bp == NULL) { + LOG_ERR(PFX "%s: Could not allocate BNX2X space", + nic->log_name); + return NULL; + } + + /* Clear out the CNIC contents */ + memset(bp, 0, sizeof(*bp)); + + bp->bar0_fd = INVALID_FD; + bp->bar2_fd = INVALID_FD; + + bp->parent = nic; + nic->priv = (void *)bp; + + bnx2x_set_drv_version_unknown(bp); + + return bp; +} + +/** + * bnx2x_open() - This will initialize all the hardware resources underneath + * a struct cnic_uio device + * @param dev - The struct cnic_uio device to attach the hardware with + * @return 0 on success, on failure a errno will be returned + */ +static int bnx2x_open(nic_t *nic) +{ + bnx2x_t *bp; + struct stat uio_stat; + int i, rc; + __u32 val; + int count; + char sysfs_resc_path[80]; + uint32_t bus; + uint32_t slot; + uint32_t func; + + /* Sanity Check: validate the parameters */ + if (nic == NULL) { + LOG_ERR(PFX "nic == NULL"); + return -EINVAL; + } + + if ((nic->priv) != NULL && + (((bnx2x_t *) (nic->priv))->flags & BNX2X_OPENED)) { + return 0; + } + + bp = bnx2x_alloc(nic); + if (bp == NULL) + return -ENOMEM; + + if (bnx2x_is_drv_version_unknown(&bnx2x_version)) { + /* If version is unknown, go read from ethtool */ + rc = bnx2x_get_drv_version(bp); + if (rc) + goto open_error; + } else { + /* Version is not unknown, just use it */ + bnx2x_version.major = bp->version.major; + bnx2x_version.minor = bp->version.minor; + bnx2x_version.sub_minor = bp->version.sub_minor; + } + + count = 0; + while ((nic->fd < 0) && count < 15) { + /* udev might not have created the file yet */ + pthread_mutex_unlock(&nic->nic_mutex); + sleep(1); + pthread_mutex_lock(&nic->nic_mutex); + + nic->fd = open(nic->uio_device_name, O_RDWR | O_NONBLOCK); + if (nic->fd != INVALID_FD) { + LOG_ERR(PFX "%s: uio device has been brought up " + "via pid: %d on fd: %d", + nic->uio_device_name, getpid(), nic->fd); + + rc = bnx2x_uio_verify(nic); + if (rc != 0) + continue; + + break; + } else { + LOG_WARN(PFX "%s: Could not open device: %s, [%s]", + nic->log_name, nic->uio_device_name, + strerror(errno)); + + manually_trigger_uio_event(nic, nic->uio_minor); + + /* udev might not have created the file yet */ + pthread_mutex_unlock(&nic->nic_mutex); + sleep(1); + pthread_mutex_lock(&nic->nic_mutex); + + count++; + } + } + if (fstat(nic->fd, &uio_stat) < 0) { + LOG_ERR(PFX "%s: Could not fstat device", nic->log_name); + rc = -ENODEV; + goto open_error; + } + nic->uio_minor = minor(uio_stat.st_rdev); + + cnic_get_sysfs_pci_resource_path(nic, 0, sysfs_resc_path, 80); + bp->bar0_fd = open(sysfs_resc_path, O_RDWR | O_SYNC); + if (bp->bar0_fd < 0) { + LOG_ERR(PFX "%s: Could not open %s", nic->log_name, + sysfs_resc_path); + rc = -ENODEV; + goto open_error; + } + + bp->reg = mmap(NULL, BNX2X_BAR_SIZE, PROT_READ | PROT_WRITE, + MAP_SHARED, bp->bar0_fd, (off_t) 0); + + if (bp->reg == MAP_FAILED) { + LOG_INFO(PFX "%s: Couldn't mmap BAR registers: %s", + nic->log_name, strerror(errno)); + bp->reg = NULL; + rc = errno; + goto open_error; + } + + msync(bp->reg, BNX2X_BAR_SIZE, MS_SYNC); + + cnic_get_sysfs_pci_resource_path(nic, 2, sysfs_resc_path, 80); + bp->bar2_fd = open(sysfs_resc_path, O_RDWR | O_SYNC); + if (bp->bar2_fd < 0) { + LOG_ERR(PFX "%s: Could not open %s", nic->log_name, + sysfs_resc_path); + rc = -ENODEV; + goto open_error; + } + + bp->reg2 = mmap(NULL, BNX2X_BAR2_SIZE, PROT_READ | PROT_WRITE, + MAP_SHARED, bp->bar2_fd, (off_t) 0); + + if (bp->reg2 == MAP_FAILED) { + LOG_INFO(PFX "%s: Couldn't mmap BAR2 registers: %s", + nic->log_name, strerror(errno)); + bp->reg2 = NULL; + rc = errno; + goto open_error; + } + + /* TODO: hardcoded with the cnic driver */ + bp->rx_ring_size = 15; + bp->rx_buffer_size = 0x400; + + LOG_DEBUG(PFX "%s: using rx ring size: %d, rx buffer size: %d", + nic->log_name, bp->rx_ring_size, bp->rx_buffer_size); + + /* Determine the number of UIO events that have already occured */ + rc = detemine_initial_uio_events(nic, &nic->intr_count); + if (rc != 0) { + LOG_ERR("Could not determine the number ofinitial UIO events"); + nic->intr_count = 0; + } + + /* Allocate space for rx pkt ring */ + bp->rx_pkt_ring = malloc(sizeof(void *) * bp->rx_ring_size); + if (bp->rx_pkt_ring == NULL) { + LOG_ERR(PFX "%s: Could not allocate space for rx_pkt_ring", + nic->log_name); + rc = errno; + goto open_error; + } + + if (bnx2x_is_ver60_plus(bp)) + bp->status_blk_size = sizeof(struct host_sp_status_block); + else if (bnx2x_is_ver52(bp)) + bp->status_blk_size = sizeof(struct host_def_status_block); + else { + LOG_INFO(PFX "%s: Unsupported bnx2x driver [%d.%d]", + nic->log_name, bp->version.major, bp->version.minor); + + rc = -ENOTSUP; + goto open_error; + } + + bp->status_blk.def = mmap(NULL, bp->status_blk_size, + PROT_READ | PROT_WRITE, MAP_SHARED, + nic->fd, (off_t) nic->page_size); + if (bp->status_blk.def == MAP_FAILED) { + LOG_INFO(PFX "%s: Could not mmap status block: %s", + nic->log_name, strerror(errno)); + bp->status_blk.def = NULL; + rc = errno; + goto open_error; + } + + bp->tx_ring = mmap(NULL, 4 * nic->page_size, + PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_LOCKED, + nic->fd, (off_t) 2 * nic->page_size); + if (bp->tx_ring == MAP_FAILED) { + LOG_INFO(PFX "%s: Could not mmap tx ring: %s", + nic->log_name, strerror(errno)); + bp->tx_ring = NULL; + rc = errno; + goto open_error; + } + + bp->rx_comp_ring.cqe = (union eth_rx_cqe *) + (((__u8 *) bp->tx_ring) + 2 * nic->page_size); + + bp->bufs = mmap(NULL, (bp->rx_ring_size + 1) * bp->rx_buffer_size, + PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_LOCKED, + nic->fd, (off_t) 3 * nic->page_size); + if (bp->bufs == MAP_FAILED) { + LOG_INFO(PFX "%s: Could not mmap buffers: %s", + nic->log_name, strerror(errno)); + bp->bufs = NULL; + rc = errno; + goto open_error; + } + + bp->chip_id = bnx2x_get_chip_id(bp); + LOG_DEBUG(PFX "Chip ID: %x", bp->chip_id); + + rc = get_bus_slot_func_num(nic, &bus, &slot, &func); + if (rc != 0) { + LOG_INFO(PFX "%s: Couldn't determine bus:slot.func", + nic->log_name); + goto open_error; + } + /* In E1/E1H use pci device function as read from sysfs. + * In E2/E3 read physical function from ME register since these chips + * support Physical Device Assignment where kernel BDF maybe arbitrary + * (depending on hypervisor). + */ + if (CHIP_IS_E2_PLUS(bp)) { + func = (bnx2x_rd32(bp, BAR_ME_REGISTER) & ME_REG_ABS_PF_NUM) >> + ME_REG_ABS_PF_NUM_SHIFT; + } + bp->func = func; + bp->port = bp->func % PORT_MAX; + + if (CHIP_IS_E2_PLUS(bp)) { + __u32 val = bnx2x_rd32(bp, MISC_REG_PORT4MODE_EN_OVWR); + if (!(val & 1)) + val = bnx2x_rd32(bp, MISC_REG_PORT4MODE_EN); + else + val = (val >> 1) & 1; + + if (val) + bp->pfid = func >> 1; + else + bp->pfid = func & 0x6; + } else { + bp->pfid = func; + } + + if (bnx2x_is_ver60_plus(bp)) + bp->port = bp->pfid & 1; + + bp->cid = 17; + bp->client_id = 17; + + if (bnx2x_is_ver60_plus(bp)) { + struct client_init_general_data *data = bp->bufs; + + bp->client_id = data->client_id; + if (data->uid.cid) + bp->cid = data->uid.cid; + if (bp->version.minor >= 78 && bp->version.sub_minor >= 55 && + data->uid.cid_override_key == UIO_USE_TX_DOORBELL) { + bp->tx_doorbell = data->uid.tx_db_off; + LOG_INFO(PFX "%s: tx doorbell override offset = 0x%x", + nic->log_name, bp->tx_doorbell); + } + } + + LOG_INFO(PFX "%s: func 0x%x, pfid 0x%x, client_id 0x%x, cid 0x%x", + nic->log_name, bp->func, bp->pfid, bp->client_id, bp->cid); + + if (CHIP_IS_E1(bp)) + bp->iro = e1_iro; + else if (CHIP_IS_E1H(bp)) + bp->iro = e1h_iro; + else if (CHIP_IS_E2_PLUS(bp)) + bp->iro = e2_iro; + + if (bnx2x_is_ver60_plus(bp)) { + __u32 cl_qzone_id = BNX2X_CL_QZONE_ID(bp, bp->client_id); + + bp->iro_idx = 0; + if (bp->version.minor >= 64) { + bp->iro_idx = 1; + cl_qzone_id = BNX2X_CL_QZONE_ID_64(bp, bp->client_id); + } + + bp->rx_prod_io = BAR_USTRORM_INTMEM + + (CHIP_IS_E2_PLUS(bp) ? + USTORM_RX_PRODS_E2_OFFSET(cl_qzone_id) : + USTORM_RX_PRODS_E1X_OFFSET(bp->port, bp->client_id)); + + if (!bp->tx_doorbell) + bp->tx_doorbell = bp->cid * 0x80 + 0x40; + + bp->get_rx_cons = bnx2x_get_rx_60; + bp->get_tx_cons = bnx2x_get_tx_60; + bp->tx_vlan_tag_bit = ETH_TX_BD_FLAGS_VLAN_TAG_T6X; + } else { + bp->rx_prod_io = BAR_USTRORM_INTMEM + + USTORM_RX_PRODS_OFFSET(bp->port, bp->client_id); + + bp->tx_doorbell = bp->cid * nic->page_size + 0x40; + + bp->get_rx_cons = bnx2x_get_rx; + bp->get_tx_cons = bnx2x_get_tx; + bp->tx_vlan_tag_bit = ETH_TX_BD_FLAGS_VLAN_TAG_T5X; + } + + bp->tx_cons = 0; + bp->tx_prod = 0; + bp->tx_bd_prod = 0; + bp->tx_pkt = bp->bufs; + + bp->rx_index = 0; + bp->rx_cons = 0; + bp->rx_bd_cons = 0; + bp->rx_prod = 127; + bp->rx_bd_prod = bp->rx_ring_size; + + for (i = 0; i < bp->rx_ring_size; i++) { + void *ptr = bp->bufs + (bp->rx_buffer_size * (i + 1)); + + bp->rx_pkt_ring[i] = ptr; + } + + val = bnx2x_rd32(bp, MISC_REG_SHARED_MEM_ADDR); + + bp->shmem_base = val; + val = bnx2x_rd32(bp, bp->shmem_base + SHMEM_ISCSI_MAC_UPPER(bp)); + nic->mac_addr[0] = (__u8) (val >> 8); + nic->mac_addr[1] = (__u8) val; + val = bnx2x_rd32(bp, bp->shmem_base + SHMEM_ISCSI_MAC_LOWER(bp)); + nic->mac_addr[2] = (__u8) (val >> 24); + nic->mac_addr[3] = (__u8) (val >> 16); + nic->mac_addr[4] = (__u8) (val >> 8); + nic->mac_addr[5] = (__u8) val; + + if (bnx2x_is_ver60_plus(bp) && CHIP_IS_E2_PLUS(bp)) { + __u32 mf_cfg_addr = 0; + __u32 mac_offset; + __u8 mac[6]; + + val = bnx2x_rd32(bp, (BNX2X_PATH(bp) ? MISC_REG_GENERIC_CR_1 : + MISC_REG_GENERIC_CR_0)); + bp->shmem_base2 = val; + if (bp->shmem_base2) { + /* size */ + val = bnx2x_rd32(bp, bp->shmem_base2); + + if (val > 0x10) + mf_cfg_addr = + bnx2x_rd32(bp, bp->shmem_base2 + 0x10); + } + + if (!mf_cfg_addr) + mf_cfg_addr = bp->shmem_base + 0x7e4; + + /* shared_feat_cfg.config */ + val = bnx2x_rd32(bp, bp->shmem_base + 0x354); + /* SI mode */ + if ((val & 0x700) == 0x300) { + mac_offset = 0xe4 + (bp->func * 0x28) + 4; + val = bnx2x_rd32(bp, mf_cfg_addr + mac_offset); + mac[0] = (__u8) (val >> 8); + mac[1] = (__u8) val; + mac_offset += 4; + val = bnx2x_rd32(bp, mf_cfg_addr + mac_offset); + mac[2] = (__u8) (val >> 24); + mac[3] = (__u8) (val >> 16); + mac[4] = (__u8) (val >> 8); + mac[5] = (__u8) val; + + if (mac[0] != 0xff) { + memcpy(nic->mac_addr, mac, 6); + } else if (bp->func > 1) { + LOG_INFO(PFX "%s: Invalid mac address: " + "%02x:%02x:%02x:%02x:%02x:%02x, abort", + nic->log_name, + mac[0], mac[1], mac[2], + mac[3], mac[4], mac[5]); + rc = -ENOTSUP; + goto open_error; + } + } else if ((val & 0x700) == 0) { + __u32 proto_offset = 0x24 + (bp->func * 0x18); + __u32 ovtag_offset = proto_offset + 0xc; + + rc = -ENOTSUP; + val = bnx2x_rd32(bp, mf_cfg_addr + ovtag_offset); + val &= 0xffff; + /* SD mode, check for valid outer VLAN */ + if (val == 0xffff) { + LOG_ERR(PFX "%s: Invalid OV detected for SD, " + " fallback to SF mode!\n", + nic->log_name); + goto SF; + } + /* Check for iSCSI protocol */ + val = bnx2x_rd32(bp, mf_cfg_addr + proto_offset); + if ((val & 6) != 6) + goto open_error; + + mac_offset = proto_offset + 0x4; + val = bnx2x_rd32(bp, mf_cfg_addr + mac_offset); + mac[0] = (__u8) (val >> 8); + mac[1] = (__u8) val; + mac_offset += 4; + val = bnx2x_rd32(bp, mf_cfg_addr + mac_offset); + mac[2] = (__u8) (val >> 24); + mac[3] = (__u8) (val >> 16); + mac[4] = (__u8) (val >> 8); + mac[5] = (__u8) val; + memcpy(nic->mac_addr, mac, 6); + + } + } +SF: + LOG_INFO(PFX "%s: Using mac address: %02x:%02x:%02x:%02x:%02x:%02x", + nic->log_name, + nic->mac_addr[0], nic->mac_addr[1], nic->mac_addr[2], + nic->mac_addr[3], nic->mac_addr[4], nic->mac_addr[5]); + + /* Determine if Hardware VLAN tag stripping is enabled or not */ + if (CNIC_VLAN_STRIPPING_ENABLED == bnx2x_strip_vlan_enabled(bp)) + nic->flags |= NIC_VLAN_STRIP_ENABLED; + + msync(bp->reg, BNX2X_BAR_SIZE, MS_SYNC); + + LOG_INFO("%s: bnx2x initialized", nic->log_name); + + bnx2x_update_rx_prod(bp); + bp->flags |= BNX2X_OPENED; + + return 0; + +open_error: + if (bp->tx_ring) { + munmap(bp->tx_ring, 4 * nic->page_size); + bp->tx_ring = NULL; + } + + if (bp->status_blk.def) { + munmap(bp->status_blk.def, bp->status_blk_size); + bp->status_blk.def = NULL; + } + + if (bp->reg) { + munmap(bp->reg, BNX2X_BAR_SIZE); + bp->reg = NULL; + } + + if (bp->reg2) { + munmap(bp->reg2, BNX2X_BAR2_SIZE); + bp->reg2 = NULL; + } + + if (bp->rx_pkt_ring) { + free(bp->rx_pkt_ring); + bp->rx_pkt_ring = NULL; + } + + if (bp->bar2_fd != INVALID_FD) { + close(bp->bar2_fd); + bp->bar2_fd = INVALID_FD; + } + + if (bp->bar0_fd != INVALID_FD) { + close(bp->bar0_fd); + bp->bar0_fd = INVALID_FD; + } + if (nic->fd != INVALID_FD) { + close(nic->fd); + nic->fd = INVALID_FD; + } + bnx2x_free(nic); + + return rc; +} + +/** + * bnx2x_uio_close_resources() - Used to free resource for the NIC/CNIC + * @param nic - NIC device to free resource + * @param graceful - whether to wait to close gracefully + * @return 0 on success, <0 on failure + */ +static int bnx2x_uio_close_resources(nic_t *nic, NIC_SHUTDOWN_T graceful) +{ + bnx2x_t *bp = (bnx2x_t *) nic->priv; + int rc = 0; + + /* Check if there is an assoicated bnx2x device */ + if (bp == NULL) { + LOG_WARN(PFX "%s: when closing resources there is " + "no assoicated bnx2x", nic->log_name); + return -EIO; + } + + /* Clean up allocated memory */ + + if (bp->rx_pkt_ring != NULL) { + free(bp->rx_pkt_ring); + bp->rx_pkt_ring = NULL; + } + + /* Clean up mapped registers */ + if (bp->bufs != NULL) { + rc = munmap(bp->bufs, + (bp->rx_ring_size + 1) * bp->rx_buffer_size); + if (rc != 0) + LOG_WARN(PFX "%s: Couldn't unmap bufs", nic->log_name); + bp->bufs = NULL; + } + + if (bp->tx_ring != NULL) { + rc = munmap(bp->tx_ring, 4 * nic->page_size); + if (rc != 0) + LOG_WARN(PFX "%s: Couldn't unmap tx_rings", + nic->log_name); + bp->tx_ring = NULL; + } + + if (bp->status_blk.def != NULL) { + rc = munmap(bp->status_blk.def, bp->status_blk_size); + if (rc != 0) + LOG_WARN(PFX "%s: Couldn't unmap status block", + nic->log_name); + bp->status_blk.def = NULL; + } + + if (bp->reg != NULL) { + rc = munmap(bp->reg, BNX2X_BAR_SIZE); + if (rc != 0) + LOG_WARN(PFX "%s: Couldn't unmap regs", nic->log_name); + bp->reg = NULL; + } + + if (bp->reg2 != NULL) { + rc = munmap(bp->reg2, BNX2X_BAR2_SIZE); + if (rc != 0) + LOG_WARN(PFX "%s: Couldn't unmap regs", nic->log_name); + bp->reg2 = NULL; + } + + if (bp->bar2_fd != INVALID_FD) { + close(bp->bar2_fd); + bp->bar2_fd = INVALID_FD; + } + + if (bp->bar0_fd != INVALID_FD) { + close(bp->bar0_fd); + bp->bar0_fd = INVALID_FD; + } + + if (nic->fd != INVALID_FD) { + rc = close(nic->fd); + if (rc != 0) { + LOG_WARN(PFX + "%s: Couldn't close uio file descriptor: %d", + nic->log_name, nic->fd); + } else { + LOG_DEBUG(PFX "%s: Closed uio file descriptor: %d", + nic->log_name, nic->fd); + } + + nic->fd = INVALID_FD; + } else { + LOG_WARN(PFX "%s: Invalid uio file descriptor: %d", + nic->log_name, nic->fd); + } + + bnx2x_set_drv_version_unknown(bp); + + LOG_INFO(PFX "%s: Closed all resources", nic->log_name); + + return 0; +} + +/** + * bnx2x_close() - Used to close the NIC device + * @param nic - NIC device to close + * @param graceful - whether to wait to close gracefully + * @return 0 if successful, <0 if there is an error + */ +static int bnx2x_close(nic_t *nic, NIC_SHUTDOWN_T graceful) +{ + /* Sanity Check: validate the parameters */ + if (nic == NULL) { + LOG_ERR(PFX "bnx2x_close(): nic == NULL"); + return -EINVAL; + } + if (nic->priv == NULL) { + LOG_ERR(PFX "bnx2x_close(): nic->priv == NULL"); + return -EINVAL; + } + + LOG_INFO(PFX "Closing NIC device: %s", nic->log_name); + + bnx2x_uio_close_resources(nic, graceful); + bnx2x_free(nic); + + return 0; +} + +static void bnx2x_prepare_xmit_packet(nic_t *nic, + nic_interface_t *nic_iface, + struct packet *pkt) +{ + bnx2x_t *bp = (bnx2x_t *) nic->priv; + struct uip_vlan_eth_hdr *eth_vlan = (struct uip_vlan_eth_hdr *)pkt->buf; + struct uip_eth_hdr *eth = (struct uip_eth_hdr *)bp->tx_pkt; + + if (eth_vlan->tpid == htons(UIP_ETHTYPE_8021Q)) { + memcpy(bp->tx_pkt, pkt->buf, sizeof(struct uip_eth_hdr)); + eth->type = eth_vlan->type; + pkt->buf_size -= (sizeof(struct uip_vlan_eth_hdr) - + sizeof(struct uip_eth_hdr)); + memcpy(bp->tx_pkt + sizeof(struct uip_eth_hdr), + pkt->buf + sizeof(struct uip_vlan_eth_hdr), + pkt->buf_size - sizeof(struct uip_eth_hdr)); + } else + memcpy(bp->tx_pkt, pkt->buf, pkt->buf_size); + + msync(bp->tx_pkt, pkt->buf_size, MS_SYNC); +} + +/** + * bnx2x_get_tx_pkt() - This function is used to a TX packet from the NIC + * @param nic - The NIC device to send the packet + */ +void *bnx2x_get_tx_pkt(nic_t *nic) +{ + bnx2x_t *bp = (bnx2x_t *) nic->priv; + return bp->tx_pkt; +} + +/** + * bnx2x_start_xmit() - This function is used to send a packet of data + * @param nic - The NIC device to send the packet + * @param len - the length of the TX packet + * + */ +void bnx2x_start_xmit(nic_t *nic, size_t len, u16_t vlan_id) +{ + bnx2x_t *bp = (bnx2x_t *) nic->priv; + uint16_t ring_prod; + struct eth_tx_start_bd *txbd; + struct eth_tx_bd *txbd2; + struct eth_rx_bd *rx_bd; + rx_bd = (struct eth_rx_bd *)(((__u8 *) bp->tx_ring) + nic->page_size); + + if ((rx_bd->addr_hi == 0) && (rx_bd->addr_lo == 0)) { + LOG_PACKET(PFX "%s: trying to transmit when device is closed", + nic->log_name); + pthread_mutex_unlock(&nic->xmit_mutex); + return; + } + + ring_prod = BNX2X_TX_RING_IDX(bp->tx_bd_prod); + txbd = &bp->tx_ring[ring_prod]; + + BNX2X_SET_TX_VLAN(bp, txbd, vlan_id); + + bp->tx_prod++; + bp->tx_bd_prod = BNX2X_NEXT_TX_BD(bp->tx_bd_prod); + bp->tx_bd_prod = BNX2X_NEXT_TX_BD(bp->tx_bd_prod); + + ring_prod = BNX2X_TX_RING_IDX(bp->tx_bd_prod); + txbd2 = (struct eth_tx_bd *)&bp->tx_ring[ring_prod]; + + txbd2->nbytes = len - 0x10; + txbd2->total_pkt_bytes = len; + + bp->tx_bd_prod = BNX2X_NEXT_TX_BD(bp->tx_bd_prod); + + barrier(); + if (nic->nl_process_if_down == 0) { + bnx2x_doorbell(bp, bp->tx_doorbell, 0x02 | + (bp->tx_bd_prod << 16)); + bnx2x_flush_doorbell(bp, bp->tx_doorbell); + } else { + /* If the doorbell is not rung, the packet will not + get sent. Hence, the xmit_mutex lock will not + get freed. + */ + pthread_mutex_unlock(&nic->xmit_mutex); + } + LOG_PACKET(PFX "%s: sent %d bytes using bp->tx_prod: %d", + nic->log_name, len, bp->tx_prod); +} + +/** + * bnx2x_write() - Used to write the data to the hardware + * @param nic - NIC hardware to read from + * @param pkt - The packet which will hold the data to be sent on the wire + * @return 0 if successful, <0 if failed + */ +int bnx2x_write(nic_t *nic, nic_interface_t *nic_iface, packet_t *pkt) +{ + bnx2x_t *bp; + struct uip_stack *uip; + int i = 0; + + /* Sanity Check: validate the parameters */ + if (nic == NULL || nic_iface == NULL || pkt == NULL) { + LOG_ERR(PFX "%s: bnx2x_write() nic == 0x%p || " + " nic_iface == 0x%p || " + " pkt == 0x%x", nic, nic_iface, pkt); + return -EINVAL; + } + bp = (bnx2x_t *) nic->priv; + uip = &nic_iface->ustack; + + if (pkt->buf_size == 0) { + LOG_ERR(PFX "%s: Trying to transmitted 0 sized packet", + nic->log_name); + return -EINVAL; + } + + /* Try to wait for a TX completion */ + for (i = 0; i < 15; i++) { + struct timespec sleep_req = {.tv_sec = 0, .tv_nsec = 5000000 }, + sleep_rem; + + if (bnx2x_clear_tx_intr(nic) == 0) + break; + + nanosleep(&sleep_req, &sleep_rem); + } + + if (pthread_mutex_trylock(&nic->xmit_mutex) != 0) { + LOG_PACKET(PFX "%s: Dropped previous transmitted packet", + nic->log_name); + return -EINVAL; + } + + bnx2x_prepare_xmit_packet(nic, nic_iface, pkt); + bnx2x_start_xmit(nic, pkt->buf_size, + (nic_iface->vlan_priority << 12) | + nic_iface->vlan_id); + + /* bump the cnic dev send statistics */ + nic->stats.tx.packets++; + nic->stats.tx.bytes += uip->uip_len; + + LOG_PACKET(PFX "%s: transmitted %d bytes " + "dev->tx_cons: %d, dev->tx_prod: %d, dev->tx_bd_prod:%d", + nic->log_name, pkt->buf_size, + bp->tx_cons, bp->tx_prod, bp->tx_bd_prod); + + return 0; +} + +static inline int bnx2x_get_rx_pad(bnx2x_t *bp, union eth_rx_cqe *cqe) +{ + int pad = 0; + + if (bnx2x_is_ver70(bp)) + pad = ((union eth_rx_cqe_70 *)cqe)->fast_path_cqe_70. \ + placement_offset; + else if (bnx2x_is_ver60(bp)) { + if (bp->version.minor >= 64) + pad = cqe->fast_path_cqe_64.placement_offset; + else + pad = cqe->fast_path_cqe.placement_offset; + } + return pad; +} + +/** + * bnx2x_read() - Used to read the data from the hardware + * @param nic - NIC hardware to read from + * @param pkt - The packet which will hold the data + * @return 0 if successful, <0 if failed + */ +static int bnx2x_read(nic_t *nic, packet_t *pkt) +{ + bnx2x_t *bp; + int rc = 0; + uint16_t hw_cons, sw_cons, bd_cons, bd_prod; + + /* Sanity Check: validate the parameters */ + if (nic == NULL || pkt == NULL) { + LOG_ERR(PFX "%s: bnx2x_read() nic == 0x%p || " + " pkt == 0x%x", nic, pkt); + return -EINVAL; + } + bp = (bnx2x_t *) nic->priv; + + hw_cons = bp->get_rx_cons(bp); + sw_cons = bp->rx_cons; + bd_cons = BNX2X_RX_BD(bp->rx_bd_cons); + bd_prod = BNX2X_RX_BD(bp->rx_bd_prod); + + if (sw_cons != hw_cons) { + uint16_t comp_ring_index = sw_cons & BNX2X_MAX_RCQ_DESC_CNT(bp); + uint8_t ring_index; + union eth_rx_cqe *cqe; + __u8 cqe_fp_flags; + void *rx_pkt; + int len, pad, cqe_size, max_len; + rc = 1; + + if (bnx2x_is_ver70(bp)) { + cqe = (union eth_rx_cqe *) + &bp->rx_comp_ring.cqe70[comp_ring_index]; + cqe_size = sizeof(union eth_rx_cqe_70); + } else { + cqe = &bp->rx_comp_ring.cqe[comp_ring_index]; + cqe_size = sizeof(union eth_rx_cqe); + } + cqe_fp_flags = cqe->fast_path_cqe.type_error_flags; + + LOG_PACKET(PFX "%s: clearing rx interrupt: %d %d", + nic->log_name, sw_cons, hw_cons); + + msync(cqe, cqe_size, MS_SYNC); + + if (!(cqe_fp_flags & ETH_FAST_PATH_RX_CQE_TYPE)) { + ring_index = bd_cons % 15; + len = cqe->fast_path_cqe.pkt_len; + pad = bnx2x_get_rx_pad(bp, cqe); + rx_pkt = bp->rx_pkt_ring[ring_index] + pad; + + /* Doto query MTU size of physical device */ + /* Ensure len is valid */ + max_len = pkt->max_buf_size < bp->rx_buffer_size ? + pkt->max_buf_size : bp->rx_buffer_size; + if (len + pad > max_len) { + LOG_DEBUG(PFX "%s: bad BD length: %d", + nic->log_name, len); + len = max_len - pad; + } + if (len > 0) { + msync(rx_pkt, len, MS_SYNC); + /* Copy the data */ + memcpy(pkt->buf, rx_pkt, len); + pkt->buf_size = len; + + /* Properly set the packet flags */ + /* check if there is VLAN tagging */ + if (cqe->fast_path_cqe.vlan_tag != 0) { + pkt->vlan_tag = + cqe->fast_path_cqe.vlan_tag; + pkt->flags |= VLAN_TAGGED; + } else { + pkt->vlan_tag = 0; + } + + LOG_PACKET(PFX + "%s: processing packet length: %d", + nic->log_name, len); + + /* bump the cnic dev recv statistics */ + nic->stats.rx.packets++; + nic->stats.rx.bytes += pkt->buf_size; + } + + bd_cons = BNX2X_NEXT_RX_IDX(bd_cons); + bd_prod = BNX2X_NEXT_RX_IDX(bd_prod); + + } + sw_cons = BNX2X_NEXT_RCQ_IDX(bp, sw_cons); + bp->rx_prod = BNX2X_NEXT_RCQ_IDX(bp, bp->rx_prod); + } + bp->rx_cons = sw_cons; + bp->rx_bd_cons = bd_cons; + bp->rx_bd_prod = bd_prod; + bp->rx_hw_prod = hw_cons; + + if (rc) + bnx2x_update_rx_prod(bp); + + return rc; +} + +/******************************************************************************* + * Clearing TX interrupts + ******************************************************************************/ +/** + * bnx2x_clear_tx_intr() - This routine is called when a TX interrupt occurs + * @param nic - the nic the interrupt occured on + * @return 0 on success + */ +static int bnx2x_clear_tx_intr(nic_t *nic) +{ + bnx2x_t *bp; + uint16_t hw_cons; + + /* Sanity check: ensure the parameters passed in are valid */ + if (unlikely(nic == NULL)) { + LOG_ERR(PFX "bnx2x_read() nic == NULL"); + return -EINVAL; + } + bp = (bnx2x_t *) nic->priv; + hw_cons = bp->get_tx_cons(bp); + + if (bp->tx_cons == hw_cons) { + if (bp->tx_cons == bp->tx_prod) { + /* Make sure the xmit_mutex lock is unlock */ + if (pthread_mutex_trylock(&nic->xmit_mutex)) + LOG_ERR(PFX "bnx2x tx lock with prod == cons"); + + pthread_mutex_unlock(&nic->xmit_mutex); + return 0; + } + return -EAGAIN; + } + + LOG_PACKET(PFX "%s: clearing tx interrupt [%d %d]", + nic->log_name, bp->tx_cons, hw_cons); + bp->tx_cons = hw_cons; + + /* There is a queued TX packet that needs to be sent out. The usual + * case is when stack will send an ARP packet out before sending the + * intended packet */ + if (nic->tx_packet_queue != NULL) { + packet_t *pkt; + int i; + + LOG_PACKET(PFX "%s: sending queued tx packet", nic->log_name); + pkt = nic_dequeue_tx_packet(nic); + + /* Got a TX packet buffer of the TX queue and put it onto + * the hardware */ + if (pkt != NULL) { + bnx2x_prepare_xmit_packet(nic, pkt->nic_iface, pkt); + + bnx2x_start_xmit(nic, pkt->buf_size, + (pkt->nic_iface->vlan_priority << 12) | + pkt->nic_iface->vlan_id); + + LOG_PACKET(PFX "%s: transmitted queued packet %d bytes " + "dev->tx_cons: %d, dev->tx_prod: %d, " + "dev->tx_bd_prod:%d", + nic->log_name, pkt->buf_size, + bp->tx_cons, bp->tx_prod, bp->tx_bd_prod); + + return 0; + } + + /* Try to wait for a TX completion */ + for (i = 0; i < 15; i++) { + struct timespec sleep_req = {.tv_sec = 0, + .tv_nsec = 5000000 + }, sleep_rem; + + hw_cons = bp->get_tx_cons(bp); + if (bp->tx_cons != hw_cons) { + LOG_PACKET(PFX + "%s: clearing tx interrupt [%d %d]", + nic->log_name, bp->tx_cons, hw_cons); + bp->tx_cons = hw_cons; + + break; + } + + nanosleep(&sleep_req, &sleep_rem); + } + } + + pthread_mutex_unlock(&nic->xmit_mutex); + + return 0; +} + +/******************************************************************************* + * bnx2x NIC op's table + ******************************************************************************/ +struct nic_ops bnx2x_op = { + .description = "bnx2x", + .open = bnx2x_open, + .close = bnx2x_close, + .write = bnx2x_write, + .get_tx_pkt = bnx2x_get_tx_pkt, + .start_xmit = bnx2x_start_xmit, + .read = bnx2x_read, + .clear_tx_intr = bnx2x_clear_tx_intr, + .handle_iscsi_path_req = cnic_handle_iscsi_path_req, + + .lib_ops = { + .get_library_name = bnx2x_get_library_name, + .get_pci_table = bnx2x_get_pci_table, + .get_library_version = bnx2x_get_library_version, + .get_build_date = bnx2x_get_build_date, + .get_transport_name = bnx2x_get_transport_name, + .get_uio_name = bnx2x_get_uio_name, + }, +}; diff --git a/iscsiuio/src/unix/libs/bnx2x.h b/iscsiuio/src/unix/libs/bnx2x.h new file mode 100644 index 0000000..ce55cfc --- /dev/null +++ b/iscsiuio/src/unix/libs/bnx2x.h @@ -0,0 +1,712 @@ +/* + * Copyright (c) 2009-2011, Broadcom Corporation + * + * Written by: Benjamin Li (benli@broadcom.com) + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam Dunkels. + * 4. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * bnx2x.h - bnx2x user space driver + * + */ +#ifndef __BNX2X_H__ +#define __BNX2X_H__ + +#include "nic.h" + +/****************************************************************************** + * Default CNIC values + ******************************************************************************/ +#define DEFAULT_BNX2X_NUM_RXBD 15 +#define DEFAULT_BNX2X_RX_LEN 0x400 + +/****************************************************************************** + * BNX2X Hardware structures + ******************************************************************************/ +#define HC_USTORM_DEF_SB_NUM_INDICES 8 +#define HC_CSTORM_DEF_SB_NUM_INDICES 8 +#define HC_XSTORM_DEF_SB_NUM_INDICES 4 +#define HC_TSTORM_DEF_SB_NUM_INDICES 4 + +struct atten_def_status_block { + volatile __u32 attn_bits; + volatile __u32 attn_bits_ack; + volatile __u8 status_block_id; + volatile __u8 reserved0; + volatile __u16 attn_bits_index; + volatile __u32 reserved1; +}; + +struct cstorm_def_status_block_u { + volatile __u16 index_values[HC_USTORM_DEF_SB_NUM_INDICES]; + volatile __u16 status_block_index; + volatile __u8 func; + volatile __u8 status_block_id; + volatile __u32 __flags; +}; + +struct cstorm_def_status_block_c { + volatile __u16 index_values[HC_CSTORM_DEF_SB_NUM_INDICES]; + volatile __u16 status_block_index; + volatile __u8 func; + volatile __u8 status_block_id; + volatile __u32 __flags; +}; + +struct xstorm_def_status_block { + volatile __u16 index_values[HC_XSTORM_DEF_SB_NUM_INDICES]; + volatile __u16 status_block_index; + volatile __u8 func; + volatile __u8 status_block_id; + volatile __u32 __flags; +}; + +struct tstorm_def_status_block { + volatile __u16 index_values[HC_TSTORM_DEF_SB_NUM_INDICES]; + volatile __u16 status_block_index; + volatile __u8 func; + volatile __u8 status_block_id; + volatile __u32 __flags; +}; + +struct host_def_status_block { + struct atten_def_status_block atten_status_block; + struct cstorm_def_status_block_u u_def_status_block; + struct cstorm_def_status_block_c c_def_status_block; + struct xstorm_def_status_block x_def_status_block; + struct tstorm_def_status_block t_def_status_block; +}; + +#define HC_INDEX_DEF_U_ETH_ISCSI_RX_CQ_CONS 1 +#define HC_INDEX_DEF_U_ETH_ISCSI_RX_BD_CONS 3 +#define HC_INDEX_DEF_C_ETH_ISCSI_CQ_CONS 5 + +struct atten_sp_status_block { + __u32 attn_bits; + __u32 attn_bits_ack; + __u8 status_block_id; + __u8 reserved0; + __u16 attn_bits_index; + __u32 reserved1; +}; + +#define HC_SP_SB_MAX_INDICES 16 + +struct hc_sp_status_block { + __u16 index_values[HC_SP_SB_MAX_INDICES]; + __u16 running_index; + __u16 rsrv; + __u32 rsrv1; +}; + +struct host_sp_status_block { + struct atten_sp_status_block atten_status_block; + struct hc_sp_status_block sp_sb; +}; + +#define HC_SP_INDEX_ETH_ISCSI_CQ_CONS 5 +#define HC_SP_INDEX_ETH_ISCSI_RX_CQ_CONS 1 + +/* + * VLAN mode on TX BDs + */ +enum eth_tx_vlan_type { + X_ETH_NO_VLAN = 0, + X_ETH_OUTBAND_VLAN = 1, + X_ETH_INBAND_VLAN = 2, + X_ETH_FW_ADDED_VLAN = 3, + MAX_ETH_TX_VLAN_TYPE +}; + +/* TX Buffer descriptor */ +struct eth_tx_bd_flags { + __u8 as_bitfield; +/* t6.X HSI */ +#define ETH_TX_BD_FLAGS_IP_CSUM_T6X (0x1<<0) +#define ETH_TX_BD_FLAGS_IP_CSUM_SHIFT_T6X 0 +#define ETH_TX_BD_FLAGS_L4_CSUM_T6X (0x1<<1) +#define ETH_TX_BD_FLAGS_L4_CSUM_SHIFT_T6X 1 +#define ETH_TX_BD_FLAGS_VLAN_MODE_T6X (0x3<<2) +#define ETH_TX_BD_FLAGS_VLAN_MODE_SHIFT_T6X 2 +#define ETH_TX_BD_FLAGS_START_BD_T6X (0x1<<4) +#define ETH_TX_BD_FLAGS_START_BD_SHIFT_T6X 4 +#define ETH_TX_BD_FLAGS_IS_UDP_T6X (0x1<<5) +#define ETH_TX_BD_FLAGS_IS_UDP_SHIFT_T6X 5 +#define ETH_TX_BD_FLAGS_SW_LSO_T6X (0x1<<6) +#define ETH_TX_BD_FLAGS_SW_LSO_SHIFT_T6X 6 +#define ETH_TX_BD_FLAGS_IPV6_T6X (0x1<<7) +#define ETH_TX_BD_FLAGS_IPV6_SHIFT_T6X 7 + +/* Legacy t5.2 HSI defines */ +#define ETH_TX_BD_FLAGS_VLAN_TAG_T5X (0x1<<0) +#define ETH_TX_BD_FLAGS_VLAN_TAG_SHIFT_T5X 0 +#define ETH_TX_BD_FLAGS_IP_CSUM_T5X (0x1<<1) +#define ETH_TX_BD_FLAGS_IP_CSUM_SHIFT_T5X 1 +#define ETH_TX_BD_FLAGS_L4_CSUM_T5X (0x1<<2) +#define ETH_TX_BD_FLAGS_L4_CSUM_SHIFT_T5X 2 +#define ETH_TX_BD_FLAGS_END_BD_T5X (0x1<<3) +#define ETH_TX_BD_FLAGS_END_BD_SHIFT_T5X 3 +#define ETH_TX_BD_FLAGS_START_BD_T5X (0x1<<4) +#define ETH_TX_BD_FLAGS_START_BD_SHIFT_T5X 4 +#define ETH_TX_BD_FLAGS_HDR_POOL_T5X (0x1<<5) +#define ETH_TX_BD_FLAGS_HDR_POOL_SHIFT_T5X 5 +#define ETH_TX_BD_FLAGS_SW_LSO_T5X (0x1<<6) +#define ETH_TX_BD_FLAGS_SW_LSO_SHIFT_T5X 6 +#define ETH_TX_BD_FLAGS_IPV6_T5X (0x1<<7) +#define ETH_TX_BD_FLAGS_IPV6_SHIFT_T5X 7 +}; + +#define ETH_TX_BD_FLAGS_VLAN_TAG_T6X \ + (X_ETH_OUTBAND_VLAN << ETH_TX_BD_FLAGS_VLAN_MODE_SHIFT_T6X) + +#define BNX2X_SET_TX_VLAN(bp, txbd, vlan_id) \ + do { \ + if (vlan_id) { \ + (txbd)->vlan = vlan_id; \ + (txbd)->bd_flags.as_bitfield |= \ + (bp)->tx_vlan_tag_bit; \ + } else { \ + (txbd)->vlan = (bp)->tx_prod; \ + (txbd)->bd_flags.as_bitfield &= \ + ~(bp)->tx_vlan_tag_bit; \ + } \ + } while (0) + +struct eth_tx_start_bd { + __u32 addr_lo; + __u32 addr_hi; + __u16 nbd; + __u16 nbytes; + __u16 vlan; + struct eth_tx_bd_flags bd_flags; + __u8 general_data; +#define ETH_TX_START_BD_HDR_NBDS (0x3F<<0) +#define ETH_TX_START_BD_HDR_NBDS_SHIFT 0 +#define ETH_TX_START_BD_ETH_ADDR_TYPE (0x3<<6) +#define ETH_TX_START_BD_ETH_ADDR_TYPE_SHIFT 6 +}; + +struct eth_tx_bd { + __u32 addr_lo; + __u32 addr_hi; + __u16 total_pkt_bytes; + __u16 nbytes; + __u8 reserved[4]; +}; + +/* RX Buffer descriptor */ +struct eth_rx_bd { + __u32 addr_lo; + __u32 addr_hi; +}; + +struct ramrod_data { + volatile __u32 data_lo; + volatile __u32 data_hi; +}; + +struct common_ramrod_eth_rx_cqe { + volatile __u8 ramrod_type; +#define COMMON_RAMROD_ETH_RX_CQE_TYPE (0x1<<0) +#define COMMON_RAMROD_ETH_RX_CQE_TYPE_SHIFT 0 +#define COMMON_RAMROD_ETH_RX_CQE_RESERVED0 (0x7F<<1) +#define COMMON_RAMROD_ETH_RX_CQE_RESERVED0_SHIFT 1 + volatile __u8 conn_type; + volatile __u16 reserved1; + volatile __u32 conn_and_cmd_data; +#define COMMON_RAMROD_ETH_RX_CQE_CID (0xFFFFFF<<0) +#define COMMON_RAMROD_ETH_RX_CQE_CID_SHIFT 0 +#define COMMON_RAMROD_ETH_RX_CQE_CMD_ID (0xFF<<24) +#define COMMON_RAMROD_ETH_RX_CQE_CMD_ID_SHIFT 24 + struct ramrod_data protocol_data; + __u32 reserved2[4]; +}; + +struct common_ramrod_eth_rx_cqe_70 { + volatile __u8 ramrod_type; + volatile __u8 conn_type; + volatile __u16 reserved1; + volatile __u32 conn_and_cmd_data; + struct ramrod_data protocol_data; + __u32 echo; + __u32 reserved2[11]; +}; + +struct parsing_flags { + volatile __u16 flags; +}; + +struct eth_fast_path_rx_cqe { + volatile __u8 type_error_flags; +#define ETH_FAST_PATH_RX_CQE_TYPE (0x1<<0) +#define ETH_FAST_PATH_RX_CQE_TYPE_SHIFT 0 +#define ETH_FAST_PATH_RX_CQE_PHY_DECODE_ERR_FLG (0x1<<1) +#define ETH_FAST_PATH_RX_CQE_PHY_DECODE_ERR_FLG_SHIFT 1 +#define ETH_FAST_PATH_RX_CQE_IP_BAD_XSUM_FLG (0x1<<2) +#define ETH_FAST_PATH_RX_CQE_IP_BAD_XSUM_FLG_SHIFT 2 +#define ETH_FAST_PATH_RX_CQE_L4_BAD_XSUM_FLG (0x1<<3) +#define ETH_FAST_PATH_RX_CQE_L4_BAD_XSUM_FLG_SHIFT 3 +#define ETH_FAST_PATH_RX_CQE_START_FLG (0x1<<4) +#define ETH_FAST_PATH_RX_CQE_START_FLG_SHIFT 4 +#define ETH_FAST_PATH_RX_CQE_END_FLG (0x1<<5) +#define ETH_FAST_PATH_RX_CQE_END_FLG_SHIFT 5 +#define ETH_FAST_PATH_RX_CQE_RESERVED0 (0x3<<6) +#define ETH_FAST_PATH_RX_CQE_RESERVED0_SHIFT 6 + volatile __u8 status_flags; +#define ETH_FAST_PATH_RX_CQE_RSS_HASH_TYPE (0x7<<0) +#define ETH_FAST_PATH_RX_CQE_RSS_HASH_TYPE_SHIFT 0 +#define ETH_FAST_PATH_RX_CQE_RSS_HASH_FLG (0x1<<3) +#define ETH_FAST_PATH_RX_CQE_RSS_HASH_FLG_SHIFT 3 +#define ETH_FAST_PATH_RX_CQE_BROADCAST_FLG (0x1<<4) +#define ETH_FAST_PATH_RX_CQE_BROADCAST_FLG_SHIFT 4 +#define ETH_FAST_PATH_RX_CQE_MAC_MATCH_FLG (0x1<<5) +#define ETH_FAST_PATH_RX_CQE_MAC_MATCH_FLG_SHIFT 5 +#define ETH_FAST_PATH_RX_CQE_IP_XSUM_NO_VALIDATION_FLG (0x1<<6) +#define ETH_FAST_PATH_RX_CQE_IP_XSUM_NO_VALIDATION_FLG_SHIFT 6 +#define ETH_FAST_PATH_RX_CQE_L4_XSUM_NO_VALIDATION_FLG (0x1<<7) +#define ETH_FAST_PATH_RX_CQE_L4_XSUM_NO_VALIDATION_FLG_SHIFT 7 + volatile __u8 placement_offset; + volatile __u8 queue_index; + volatile __u32 rss_hash_result; + volatile __u16 vlan_tag; + volatile __u16 pkt_len; + volatile __u16 len_on_bd; + struct parsing_flags pars_flags; + volatile __u16 sgl[8]; +}; + +union eth_sgl_or_raw_data { + volatile __u16 sgl[8]; + volatile __u32 raw_data[4]; +}; + +struct eth_fast_path_rx_cqe_64 { + volatile __u8 type_error_flags; +#define ETH_FAST_PATH_RX_CQE_TYPE_64 (0x3<<0) +#define ETH_FAST_PATH_RX_CQE_TYPE_SHIFT_64 0 +#define ETH_FAST_PATH_RX_CQE_SGL_RAW_SEL (0x1<<2) +#define ETH_FAST_PATH_RX_CQE_SGL_RAW_SEL_SHIFT 2 +#define ETH_FAST_PATH_RX_CQE_PHY_DECODE_ERR_FLG_64 (0x1<<3) +#define ETH_FAST_PATH_RX_CQE_PHY_DECODE_ERR_FLG_SHIFT_64 3 +#define ETH_FAST_PATH_RX_CQE_IP_BAD_XSUM_FLG_64 (0x1<<4) +#define ETH_FAST_PATH_RX_CQE_IP_BAD_XSUM_FLG_SHIFT_64 4 +#define ETH_FAST_PATH_RX_CQE_L4_BAD_XSUM_FLG_64 (0x1<<5) +#define ETH_FAST_PATH_RX_CQE_L4_BAD_XSUM_FLG_SHIFT_64 5 +#define ETH_FAST_PATH_RX_CQE_RESERVED0_64 (0x3<<6) +#define ETH_FAST_PATH_RX_CQE_RESERVED0_SHIFT_64 6 + volatile __u8 status_flags; +#define ETH_FAST_PATH_RX_CQE_RSS_HASH_TYPE (0x7<<0) +#define ETH_FAST_PATH_RX_CQE_RSS_HASH_TYPE_SHIFT 0 +#define ETH_FAST_PATH_RX_CQE_RSS_HASH_FLG (0x1<<3) +#define ETH_FAST_PATH_RX_CQE_RSS_HASH_FLG_SHIFT 3 +#define ETH_FAST_PATH_RX_CQE_BROADCAST_FLG (0x1<<4) +#define ETH_FAST_PATH_RX_CQE_BROADCAST_FLG_SHIFT 4 +#define ETH_FAST_PATH_RX_CQE_MAC_MATCH_FLG (0x1<<5) +#define ETH_FAST_PATH_RX_CQE_MAC_MATCH_FLG_SHIFT 5 +#define ETH_FAST_PATH_RX_CQE_IP_XSUM_NO_VALIDATION_FLG (0x1<<6) +#define ETH_FAST_PATH_RX_CQE_IP_XSUM_NO_VALIDATION_FLG_SHIFT 6 +#define ETH_FAST_PATH_RX_CQE_L4_XSUM_NO_VALIDATION_FLG (0x1<<7) +#define ETH_FAST_PATH_RX_CQE_L4_XSUM_NO_VALIDATION_FLG_SHIFT 7 + volatile __u8 queue_index; + volatile __u8 placement_offset; + volatile __u32 rss_hash_result; + volatile __u16 vlan_tag; + volatile __u16 pkt_len; + volatile __u16 len_on_bd; + struct parsing_flags pars_flags; + union eth_sgl_or_raw_data sgl_or_raw_data; +}; + +struct eth_fast_path_rx_cqe_70 { + volatile __u8 type_error_flags; + volatile __u8 status_flags; + volatile __u8 queue_index; + volatile __u8 placement_offset; + volatile __u32 rss_hash_result; + volatile __u16 vlan_tag; + volatile __u16 pkt_len; + volatile __u16 len_on_bd; + struct parsing_flags pars_flags; + union eth_sgl_or_raw_data sgl_or_raw_data; + __u32 reserved1[8]; +}; + +struct eth_rx_cqe_next_page { + __u32 addr_lo; + __u32 addr_hi; + __u32 reserved[6]; +}; + +struct eth_rx_cqe_next_page_70 { + __u32 addr_lo; + __u32 addr_hi; + __u32 reserved[14]; +}; + +union eth_rx_cqe { + struct eth_fast_path_rx_cqe fast_path_cqe; + struct eth_fast_path_rx_cqe_64 fast_path_cqe_64; + struct common_ramrod_eth_rx_cqe ramrod_cqe; + struct eth_rx_cqe_next_page next_page_cqe; +}; + +union eth_rx_cqe_70 { + struct eth_fast_path_rx_cqe_70 fast_path_cqe_70; + struct common_ramrod_eth_rx_cqe_70 ramrod_cqe_70; + struct eth_rx_cqe_next_page_70 next_page_cqe_70; +}; + +struct uio_init_data { + __u32 cid; + __u32 tx_db_off; + __u32 cid_override_key; +#define UIO_USE_TX_DOORBELL 0x017855DB +}; + +struct client_init_general_data { + __u8 client_id; + __u8 statistics_counter_id; + __u8 statistics_en_flg; + __u8 is_fcoe_flg; + __u8 activate_flg; + __u8 sp_client_id; + __u16 mtu; + __u8 statistics_zero_flg; + __u8 func_id; + __u8 cos; + __u8 traffic_type; + struct uio_init_data uid; +}; + +/****************************************************************************** + * BNX2X Registers and HSI + ******************************************************************************/ +#define BNX2X_BAR_SIZE 0x500000 +#define BNX2X_BAR2_SIZE 0x12000 + +#define BNX2X_CHIP_ID(bp) (bp->chip_id & 0xfffffff0) + +#define PORT_MAX 2 + +/* [R 4] This field indicates the type of the device. '0' - 2 Ports; '1' - 1 + * Port. */ +#define BNX2X_MISC_REG_BOND_ID 0xa400 +/* [R 8] These bits indicate the metal revision of the chip. This value + * starts at 0x00 for each all-layer tape-out and increments by one for each + * tape-out. */ +#define BNX2X_MISC_REG_CHIP_METAL 0xa404 +/* [R 16] These bits indicate the part number for the chip. */ +#define BNX2X_MISC_REG_CHIP_NUM 0xa408 +/* [R 4] These bits indicate the base revision of the chip. This value + * starts at 0x0 for the A0 tape-out and increments by one for each + * all-layer tape-out. */ +#define BNX2X_MISC_REG_CHIP_REV 0xa40c + +/* From the bnx2x driver */ +#define CHIP_NUM(bp) (bp->chip_id >> 16) +#define CHIP_NUM_57710 0x164e +#define CHIP_NUM_57711 0x164f +#define CHIP_NUM_57711E 0x1650 +#define CHIP_NUM_57712 0x1662 +#define CHIP_NUM_57712_MF 0x1663 +#define CHIP_NUM_57712_VF 0x166f +#define CHIP_NUM_57713 0x1651 +#define CHIP_NUM_57713E 0x1652 +#define CHIP_NUM_57800 0x168a +#define CHIP_NUM_57800_MF 0x16a5 +#define CHIP_NUM_57800_VF 0x16a9 +#define CHIP_NUM_57810 0x168e +#define CHIP_NUM_57810_MF 0x16ae +#define CHIP_NUM_57810_VF 0x16af +#define CHIP_NUM_57811 0x163d +#define CHIP_NUM_57811_MF 0x163e +#define CHIP_NUM_57811_VF 0x163f +#define CHIP_NUM_57840_OBSOLETE 0x168d +#define CHIP_NUM_57840_MF_OBSOLETE 0x16ab +#define CHIP_NUM_57840_4_10 0x16a1 +#define CHIP_NUM_57840_2_20 0x16a2 +#define CHIP_NUM_57840_MF 0x16a4 +#define CHIP_NUM_57840_VF 0x16ad + +#define CHIP_IS_E1(bp) (CHIP_NUM(bp) == CHIP_NUM_57710) +#define CHIP_IS_57711(bp) (CHIP_NUM(bp) == CHIP_NUM_57711) +#define CHIP_IS_57711E(bp) (CHIP_NUM(bp) == CHIP_NUM_57711E) +#define CHIP_IS_57712(bp) (CHIP_NUM(bp) == CHIP_NUM_57712) +#define CHIP_IS_57712_VF(bp) (CHIP_NUM(bp) == CHIP_NUM_57712_VF) +#define CHIP_IS_57712_MF(bp) (CHIP_NUM(bp) == CHIP_NUM_57712_MF) +#define CHIP_IS_57800(bp) (CHIP_NUM(bp) == CHIP_NUM_57800) +#define CHIP_IS_57800_VF(bp) (CHIP_NUM(bp) == CHIP_NUM_57800_MF) +#define CHIP_IS_57800_MF(bp) (CHIP_NUM(bp) == CHIP_NUM_57800_VF) +#define CHIP_IS_57810(bp) (CHIP_NUM(bp) == CHIP_NUM_57810) +#define CHIP_IS_57810_VF(bp) (CHIP_NUM(bp) == CHIP_NUM_57810_MF) +#define CHIP_IS_57810_MF(bp) (CHIP_NUM(bp) == CHIP_NUM_57810_VF) +#define CHIP_IS_57811(bp) (CHIP_NUM(bp) == CHIP_NUM_57811) +#define CHIP_IS_57811_VF(bp) (CHIP_NUM(bp) == CHIP_NUM_57811_MF) +#define CHIP_IS_57811_MF(bp) (CHIP_NUM(bp) == CHIP_NUM_57811_VF) + +#define CHIP_IS_57840(bp) \ + ((CHIP_NUM(bp) == CHIP_NUM_57840_4_10) || \ + (CHIP_NUM(bp) == CHIP_NUM_57840_2_20) || \ + (CHIP_NUM(bp) == CHIP_NUM_57840_OBSOLETE)) +#define CHIP_IS_57840_MF(bp) ((CHIP_NUM(bp) == CHIP_NUM_57840_MF) || \ + (CHIP_NUM(bp) == CHIP_NUM_57840_MF_OBSOLETE)) +#define CHIP_IS_57840_VF(bp) (CHIP_NUM(bp) == CHIP_NUM_57840_VF) +#define CHIP_IS_E1H(bp) (CHIP_IS_57711(bp) || \ + CHIP_IS_57711E(bp)) + +#define CHIP_IS_E2(bp) (CHIP_IS_57712(bp) || \ + CHIP_IS_57712_MF(bp) || \ + CHIP_IS_57712_VF(bp)) +#define CHIP_IS_E3(bp) (CHIP_IS_57800(bp) || \ + CHIP_IS_57800_MF(bp) || \ + CHIP_IS_57800_VF(bp) || \ + CHIP_IS_57810(bp) || \ + CHIP_IS_57810_MF(bp) || \ + CHIP_IS_57810_VF(bp) || \ + CHIP_IS_57840(bp) || \ + CHIP_IS_57840_MF(bp) || \ + CHIP_IS_57840_VF(bp) || \ + CHIP_IS_57811(bp) || \ + CHIP_IS_57811_MF(bp) || \ + CHIP_IS_57811_VF(bp)) + +#define CHIP_IS_E1x(bp) (CHIP_IS_E1((bp)) || CHIP_IS_E1H((bp))) +#define USES_WARPCORE(bp) (CHIP_IS_E3(bp)) +#define IS_E1H_OFFSET (!CHIP_IS_E1H(bp)) +/* End of From the bnx2x driver */ + +#define CHIP_IS_E2_PLUS(bp) (CHIP_IS_E2(bp) || CHIP_IS_E3(bp)) + +#define MISC_REG_SHARED_MEM_ADDR 0xa2b4 + +#define MISC_REG_BOND_ID 0xa400 +#define MISC_REG_CHIP_METAL 0xa404 +#define MISC_REG_CHIP_NUM 0xa408 +#define MISC_REG_CHIP_REV 0xa40c + +#define MISC_REG_PORT4MODE_EN 0xa750 +#define MISC_REG_PORT4MODE_EN_OVWR 0xa720 + +#define MISC_REG_GENERIC_CR_0 0xa460 +#define MISC_REG_GENERIC_CR_1 0xa464 + +#define BAR_USTRORM_INTMEM 0x400000 +#define BAR_CSTRORM_INTMEM 0x410000 +#define BAR_XSTRORM_INTMEM 0x420000 +#define BAR_TSTRORM_INTMEM 0x430000 + +#define BAR_ME_REGISTER 0x450000 +#define ME_REG_PF_NUM_SHIFT 0 +#define ME_REG_PF_NUM\ + (7L<iro[bp->iro_idx]) + +#define USTORM_RX_PRODS_E1X_OFFSET(port, client_id) \ + (IRO_ENT.base + ((port) * IRO_ENT.m1) + ((client_id) * IRO_ENT.m2)) + +#define USTORM_RX_PRODS_E2_OFFSET(qzone_id) \ + (IRO_ENT.base + ((qzone_id) * IRO_ENT.m1)) + +#define ETH_MAX_RX_CLIENTS_E1H 28 +#define ETH_MAX_RX_CLIENTS_E2 28 + +#define BNX2X_CL_QZONE_ID(bp, cli) \ + (cli + (bp->port * (CHIP_IS_E2(bp) ? \ + ETH_MAX_RX_CLIENTS_E2 : \ + ETH_MAX_RX_CLIENTS_E1H))) + +#define BNX2X_CL_QZONE_ID_64(bp, cli) \ + (CHIP_IS_E2_PLUS(bp) ? (cli) : \ + (cli + (bp->port * ETH_MAX_RX_CLIENTS_E1H))) + +#define BNX2X_PATH(bp) (!CHIP_IS_E2_PLUS(bp) ? 0 : (bp)->func & 1) + +#define SHMEM_P0_ISCSI_MAC_UPPER 0x4c +#define SHMEM_P0_ISCSI_MAC_LOWER 0x50 +#define SHMEM_P1_ISCSI_MAC_UPPER 0x1dc +#define SHMEM_P1_ISCSI_MAC_LOWER 0x1e0 + +#define SHMEM_ISCSI_MAC_UPPER(bp) \ + (((bp)->port == 0) ? \ + SHMEM_P0_ISCSI_MAC_UPPER : SHMEM_P1_ISCSI_MAC_UPPER) + +#define SHMEM_ISCSI_MAC_LOWER(bp) \ + (((bp)->port == 0) ? \ + SHMEM_P0_ISCSI_MAC_LOWER : SHMEM_P1_ISCSI_MAC_LOWER) + +#define BNX2X_RCQ_DESC_CNT (4096 / sizeof(union eth_rx_cqe)) +#define BNX2X_RCQ_DESC_CNT_70 (4096 / sizeof(union eth_rx_cqe_70)) +#define BNX2X_MAX_RCQ_DESC_CNT(bp) \ + ((bnx2x_is_ver70(bp) ? BNX2X_RCQ_DESC_CNT_70 : BNX2X_RCQ_DESC_CNT) - 1) + +#define BNX2X_RX_DESC_CNT (4096 / sizeof(struct eth_rx_bd)) +#define BNX2X_MAX_RX_DESC_CNT (BNX2X_RX_DESC_CNT - 2) +#define BNX2X_NUM_RX_BD (BNX2X_RX_DESC_CNT * 1) +#define BNX2X_MAX_RX_BD (BNX2X_NUM_RX_BD - 1) + +#define BNX2X_TX_DESC_CNT (4096 / sizeof(struct eth_tx_start_bd)) +#define BNX2X_MAX_TX_DESC_CNT (BNX2X_TX_DESC_CNT - 1) + +#define BNX2X_NEXT_RX_IDX(x) ((((x) & (BNX2X_RX_DESC_CNT - 1)) == \ + (BNX2X_MAX_RX_DESC_CNT - 1)) ? \ + (x) + 3 : (x) + 1) + +#define BNX2X_NEXT_RCQ_IDX(bp, x) \ + ((((x) & BNX2X_MAX_RCQ_DESC_CNT(bp)) == \ + (BNX2X_MAX_RCQ_DESC_CNT(bp) - 1)) ? (x) + 2 : (x) + 1) +#define BNX2X_RX_BD(x) ((x) & BNX2X_MAX_RX_BD) + +#define BNX2X_NEXT_TX_BD(x) ((((x) & (BNX2X_MAX_TX_DESC_CNT - 1)) == \ + (BNX2X_MAX_TX_DESC_CNT - 1)) ? \ + (x) + 2 : (x) + 1) + +#define BNX2X_TX_RING_IDX(x) ((x) & BNX2X_MAX_TX_DESC_CNT) + +struct ustorm_eth_rx_producers { + __u16 cqe_prod; + __u16 bd_prod; + __u16 sge_prod; + __u16 reserved; +}; + +#define BNX2X_UNKNOWN_MAJOR_VERSION -1 +#define BNX2X_UNKNOWN_MINOR_VERSION -1 +#define BNX2X_UNKNOWN_SUB_MINOR_VERSION -1 +struct bnx2x_driver_version { + uint16_t major; + uint16_t minor; + uint16_t sub_minor; +}; + +typedef struct bnx2x { + nic_t *parent; + + struct bnx2x_driver_version version; + + uint16_t flags; +#define CNIC_UIO_UNITIALIZED 0x0001 +#define CNIC_UIO_INITIALIZED 0x0002 +#define CNIC_UIO_ENABLED 0x0004 +#define CNIC_UIO_DISABLED 0x0008 +#define CNIC_UIO_IPv6_ENABLED 0x0010 +#define CNIC_UIO_ADDED_MULICAST 0x0020 +#define CNIC_UIO_MSIX_ENABLED 0x0200 +#define CNIC_UIO_TX_HAS_SENT 0x0400 +#define BNX2X_OPENED 0x0800 + + void *reg; /* Pointer to the BAR1 mapped registers */ + void *reg2; /* Pointer to the BAR2 mapped registers */ + + int bar0_fd; + int bar2_fd; + + __u32 chip_id; + __u32 shmem_base; + __u32 shmem_base2; + int func; + int port; + int pfid; + __u32 cid; + __u32 client_id; + + struct iro *iro; + int iro_idx; + + __u32 tx_doorbell; + + __u16 tx_prod; + __u16 tx_bd_prod; + __u16 tx_cons; + __u8 tx_vlan_tag_bit; + + __u32 rx_prod_io; + + __u16 rx_prod; + __u16 rx_bd_prod; + __u16 rx_cons; + __u16 rx_bd_cons; + __u16 rx_hw_prod; + + __u16(*get_rx_cons) (struct bnx2x *); + __u16(*get_tx_cons) (struct bnx2x *); + + /* RX ring parameters */ + uint32_t rx_ring_size; + uint32_t rx_buffer_size; + + void *bufs; /* Pointer to the mapped buffer space */ + + /* Hardware Status Block locations */ + void *sblk_map; + union { + struct host_def_status_block *def; + struct host_sp_status_block *sp; + } status_blk; + + int status_blk_size; + + uint16_t rx_index; + union { + union eth_rx_cqe *cqe; + union eth_rx_cqe_70 *cqe70; + } rx_comp_ring; + void **rx_pkt_ring; + + struct eth_tx_start_bd *tx_ring; + void *tx_pkt; + +} bnx2x_t; + +/****************************************************************************** + * bnx2x Function Declarations + ******************************************************************************/ +void bnx2x_start_xmit(nic_t *nic, size_t len, u16_t vlan_id); + +struct nic_ops *bnx2x_get_ops(); +#endif /* __BNX2X_H__ */ diff --git a/iscsiuio/src/unix/libs/cnic.c b/iscsiuio/src/unix/libs/cnic.c new file mode 100644 index 0000000..7f473c4 --- /dev/null +++ b/iscsiuio/src/unix/libs/cnic.c @@ -0,0 +1,662 @@ +/* + * Copyright (c) 2009-2011, Broadcom Corporation + * Copyright (c) 2014, QLogic Corporation + * + * Written by: Benjamin Li (benli@broadcom.com) + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam Dunkels. + * 4. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * cnic.c - CNIC UIO uIP user space stack + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "uip_arp.h" +#include "nic.h" +#include "nic_utils.h" +#include "logger.h" +#include "options.h" + +#include "cnic.h" +#include "iscsi_if.h" +#include "ipv6_ndpc.h" + +/******************************************************************************* + * Constants + ******************************************************************************/ +#define PFX "CNIC " + +static const uip_ip6addr_t all_ones_addr6 = { + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff }; + +/******************************************************************************* + * Constants shared between the bnx2 and bnx2x modules + ******************************************************************************/ +const char bnx2i_library_transport_name[] = "bnx2i"; +const size_t bnx2i_library_transport_name_size = + sizeof(bnx2i_library_transport_name); + +/****************************************************************************** + * Netlink Functions + ******************************************************************************/ + +static int cnic_arp_send(nic_t *nic, nic_interface_t *nic_iface, int fd, + __u8 *mac_addr, __u32 ip_addr, char *addr_str) +{ + struct ether_header *eth; + struct ether_arp *arp; + __u32 dst_ip = ip_addr; + int pkt_size = sizeof(*eth) + sizeof(*arp); + int rc; + struct in_addr addr; + static const uint8_t multicast_mac[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + rc = pthread_mutex_trylock(&nic->xmit_mutex); + if (rc != 0) { + LOG_DEBUG(PFX "%s: could not get xmit_mutex", nic->log_name); + return -EAGAIN; + } + + eth = (*nic->ops->get_tx_pkt) (nic); + if (eth == NULL) { + LOG_WARN(PFX "%s: couldn't get tx packet", nic->log_name); + return -EAGAIN; + } + + nic_fill_ethernet_header(nic_iface, eth, + nic->mac_addr, (void *)multicast_mac, + &pkt_size, (void *)&arp, ETHERTYPE_ARP); + + arp->arp_hrd = htons(ARPHRD_ETHER); + arp->arp_pro = htons(ETHERTYPE_IP); + arp->arp_hln = ETH_ALEN; + arp->arp_pln = 4; + arp->arp_op = htons(ARPOP_REQUEST); + memcpy(arp->arp_sha, nic->mac_addr, ETH_ALEN); + memset(arp->arp_tha, 0, ETH_ALEN); + + /* Copy the IP address's into the ARP response */ + memcpy(arp->arp_spa, nic_iface->ustack.hostaddr, 4); + memcpy(arp->arp_tpa, &dst_ip, 4); + + (*nic->nic_library->ops->start_xmit) (nic, pkt_size, + (nic_iface->vlan_priority << 12) | + nic_iface->vlan_id); + + memcpy(&addr.s_addr, &dst_ip, sizeof(addr.s_addr)); + LOG_DEBUG(PFX "%s: Sent cnic arp request for IP: %s", + nic->log_name, addr_str); + + return 0; +} + +static int cnic_neigh_soliciation_send(nic_t *nic, + nic_interface_t *nic_iface, int fd, + __u8 *mac_addr, + struct in6_addr *addr6_dst, + char *addr_str) +{ + struct ether_header *eth; + struct ip6_hdr *ipv6_hdr; + int rc, pkt_size; + char buf[INET6_ADDRSTRLEN]; + struct ndpc_reqptr req_ptr; + + rc = pthread_mutex_trylock(&nic->xmit_mutex); + if (rc != 0) { + LOG_WARN(PFX "%s: could not get xmit_mutex", nic->log_name); + return -EAGAIN; + } + + /* Build the ethernet header */ + eth = (*nic->ops->get_tx_pkt) (nic); + if (eth == NULL) { + LOG_WARN(PFX "%s: couldn't get tx packet", nic->log_name); + return -EAGAIN; + } + + /* Copy the requested target address to the ipv6.dst */ + ipv6_hdr = + (struct ip6_hdr *)((u8_t *) eth + sizeof(struct ether_header)); + + memcpy(ipv6_hdr->ip6_dst.s6_addr, addr6_dst->s6_addr, + sizeof(struct in6_addr)); + + nic_fill_ethernet_header(nic_iface, eth, nic->mac_addr, nic->mac_addr, + &pkt_size, (void *)&ipv6_hdr, ETHERTYPE_IPV6); + req_ptr.eth = (void *)eth; + req_ptr.ipv6 = (void *)ipv6_hdr; + if (ndpc_request(&nic_iface->ustack, &req_ptr, &pkt_size, + NEIGHBOR_SOLICIT)) + return -EAGAIN; + + /* Debug to print out the pkt context */ + inet_ntop(AF_INET6, ipv6_hdr->ip6_dst.s6_addr, buf, sizeof(buf)); + LOG_DEBUG(PFX "%s: ipv6 dst addr: %s", nic->log_name, buf); + LOG_DEBUG(PFX "neighbor sol content " + "dst mac %02x:%02x:%02x:%02x:%02x:%02x", + eth->ether_dhost[0], eth->ether_dhost[1], + eth->ether_dhost[2], eth->ether_dhost[3], + eth->ether_dhost[4], eth->ether_dhost[5]); + LOG_DEBUG(PFX "src mac %02x:%02x:%02x:%02x:%02x:%02x", + eth->ether_shost[0], eth->ether_shost[1], + eth->ether_shost[2], eth->ether_shost[3], + eth->ether_shost[4], eth->ether_shost[5]); + (*nic->nic_library->ops->start_xmit) (nic, pkt_size, + (nic_iface->vlan_priority << 12) | + nic_iface->vlan_id); + + LOG_DEBUG(PFX "%s: Sent cnic ICMPv6 neighbor request %s", + nic->log_name, addr_str); + + return 0; +} + +static int cnic_nl_neigh_rsp(nic_t *nic, int fd, + struct iscsi_uevent *ev, + struct iscsi_path *path_req, + __u8 *mac_addr, + nic_interface_t *nic_iface, int status, int type) +{ + int rc; + uint8_t *ret_buf; + struct iscsi_uevent *ret_ev; + struct iscsi_path *path_rsp; + struct sockaddr_nl dest_addr; + char addr_dst_str[INET6_ADDRSTRLEN]; + + memset(&dest_addr, 0, sizeof(dest_addr)); + dest_addr.nl_family = AF_NETLINK; + dest_addr.nl_pid = 0; + dest_addr.nl_groups = 0; /* unicast */ + + ret_buf = calloc(1, NLMSG_SPACE(sizeof(struct iscsi_uevent) + 256)); + if (ret_buf == NULL) { + LOG_ERR(PFX "Could not allocate memory for path req resposne"); + return -ENOMEM; + } + + memset(ret_buf, 0, NLMSG_SPACE(sizeof(struct iscsi_uevent) + 256)); + + /* prepare the iscsi_uevent buffer */ + ret_ev = (struct iscsi_uevent *)ret_buf; + ret_ev->type = ISCSI_UEVENT_PATH_UPDATE; + ret_ev->transport_handle = ev->transport_handle; + ret_ev->u.set_path.host_no = ev->r.req_path.host_no; + + /* Prepare the iscsi_path buffer */ + path_rsp = (struct iscsi_path *)(ret_buf + sizeof(*ret_ev)); + path_rsp->handle = path_req->handle; + if (type == AF_INET) { + path_rsp->ip_addr_len = 4; + memcpy(&path_rsp->src.v4_addr, nic_iface->ustack.hostaddr, + sizeof(nic_iface->ustack.hostaddr)); + + inet_ntop(AF_INET, &path_rsp->src.v4_addr, + addr_dst_str, sizeof(addr_dst_str)); + } else { + u8_t *src_ipv6; + int ret; + + /* Depending on the IPv6 address of the target we will need to + * determine whether we use the assigned IPv6 address or the + * link local IPv6 address */ + if (ndpc_request(&nic_iface->ustack, &path_req->dst.v6_addr, + &ret, CHECK_LINK_LOCAL_ADDR)) { + src_ipv6 = (u8_t *)all_zeroes_addr6; + LOG_DEBUG(PFX "RSP Check LL failed"); + goto src_done; + } + if (ret) { + /* Get link local IPv6 address */ + src_ipv6 = (u8_t *)&nic_iface->ustack.linklocal6; + } else { + if (ndpc_request(&nic_iface->ustack, + &path_req->dst.v6_addr, + &src_ipv6, GET_HOST_ADDR)) { + src_ipv6 = (u8_t *)all_zeroes_addr6; + LOG_DEBUG(PFX "RSP Get host addr failed"); + } + if (src_ipv6 == NULL) { + src_ipv6 = (u8_t *)all_zeroes_addr6; + LOG_DEBUG(PFX "RSP no Best matched addr found"); + } + } +src_done: + path_rsp->ip_addr_len = 16; + memcpy(&path_rsp->src.v6_addr, src_ipv6, + sizeof(nic_iface->ustack.hostaddr6)); + + inet_ntop(AF_INET6, &path_rsp->src.v6_addr, + addr_dst_str, sizeof(addr_dst_str)); + } + memcpy(path_rsp->mac_addr, mac_addr, 6); + path_rsp->vlan_id = (nic_iface->vlan_priority << 12) | + nic_iface->vlan_id; + path_rsp->pmtu = nic_iface->mtu ? nic_iface->mtu : path_req->pmtu; + + rc = __kipc_call(fd, ret_ev, sizeof(*ret_ev) + sizeof(*path_rsp)); + if (rc > 0) { + LOG_DEBUG(PFX "neighbor reply sent back to kernel " + "%s at %02x:%02x:%02x:%02x:%02x:%02x with vlan %d", + addr_dst_str, + mac_addr[0], mac_addr[1], + mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5], + nic_iface->vlan_id); + + } else { + LOG_ERR(PFX "send neighbor reply failed: %d", rc); + } + + free(ret_buf); + + return rc; +} + +static const struct timeval tp_wait = { + .tv_sec = 0, + .tv_usec = 250000, +}; + +/** + * cnic_handle_ipv4_iscsi_path_req() - This function will handle the IPv4 + * path req calls the bnx2i kernel module + * @param nic - The nic the message is directed towards + * @param fd - The file descriptor to be used to extract the private data + * @param ev - The iscsi_uevent + * @param buf - The private message buffer + */ +int cnic_handle_ipv4_iscsi_path_req(nic_t *nic, int fd, + struct iscsi_uevent *ev, + struct iscsi_path *path, + nic_interface_t *nic_iface) +{ + struct in_addr src_addr, dst_addr, + src_matching_addr, dst_matching_addr, netmask; + __u8 mac_addr[6]; + int rc; + uint16_t arp_retry; + int status = 0; +#define MAX_ARP_RETRY 4 + + memset(mac_addr, 0, sizeof(mac_addr)); + memcpy(&dst_addr, &path->dst.v4_addr, sizeof(dst_addr)); + memcpy(&src_addr, nic_iface->ustack.hostaddr, sizeof(src_addr)); + + if (nic_iface->ustack.netmask[0] | nic_iface->ustack.netmask[1]) + memcpy(&netmask.s_addr, nic_iface->ustack.netmask, + sizeof(src_addr)); + else + netmask.s_addr = calculate_default_netmask(dst_addr.s_addr); + + src_matching_addr.s_addr = src_addr.s_addr & netmask.s_addr; + dst_matching_addr.s_addr = dst_addr.s_addr & netmask.s_addr; + + LOG_DEBUG(PFX "%s: src=%s", nic->log_name, inet_ntoa(src_addr)); + LOG_DEBUG(PFX "%s: dst=%s", nic->log_name, inet_ntoa(dst_addr)); + LOG_DEBUG(PFX "%s: nm=%s", nic->log_name, inet_ntoa(netmask)); + if (src_matching_addr.s_addr != dst_matching_addr.s_addr) { + /* If there is an assigned gateway address then use it + * if the source address doesn't match */ + if (nic_iface->ustack.default_route_addr[0] | + nic_iface->ustack.default_route_addr[1]) { + memcpy(&dst_addr, + &nic_iface->ustack.default_route_addr, + sizeof(dst_addr)); + } else { + arp_retry = MAX_ARP_RETRY; + LOG_DEBUG(PFX "%s: no default", nic->log_name); + goto done; + } + } + arp_retry = 0; + + rc = uip_lookup_arp_entry(dst_addr.s_addr, mac_addr); + if (rc != 0) { + while ((arp_retry < MAX_ARP_RETRY) && (event_loop_stop == 0)) { + char *dst_addr_str; + int count; + struct timespec ts; + struct timeval tp; + struct timeval tp_abs; + + dst_addr_str = inet_ntoa(dst_addr); + + LOG_INFO(PFX "%s: Didn't find IPv4: '%s' in ARP table", + nic->log_name, dst_addr_str); + rc = cnic_arp_send(nic, nic_iface, fd, + mac_addr, + dst_addr.s_addr, dst_addr_str); + if (rc != 0) { + status = -EIO; + goto done; + } + + for (count = 0; count < 8; count++) { + /* Convert from timeval to timespec */ + rc = gettimeofday(&tp, NULL); + + timeradd(&tp, &tp_wait, &tp_abs); + + ts.tv_sec = tp_abs.tv_sec; + ts.tv_nsec = tp_abs.tv_usec * 1000; + + /* Wait 1s for if_down */ + pthread_mutex_lock(&nic->nl_process_mutex); + rc = pthread_cond_timedwait + (&nic->nl_process_if_down_cond, + &nic->nl_process_mutex, &ts); + + if (rc == ETIMEDOUT) { + pthread_mutex_unlock + (&nic->nl_process_mutex); + + rc = uip_lookup_arp_entry(dst_addr. + s_addr, + mac_addr); + if (rc == 0) + goto done; + } else { + nic->nl_process_if_down = 0; + pthread_mutex_unlock + (&nic->nl_process_mutex); + + arp_retry = MAX_ARP_RETRY; + goto done; + + } + } + + arp_retry++; + } + } + +done: + + if (arp_retry >= MAX_ARP_RETRY) { + status = -EIO; + rc = -EIO; + } + + if (status != 0 || rc != 0) + pthread_mutex_unlock(&nic->xmit_mutex); + + cnic_nl_neigh_rsp(nic, fd, ev, path, mac_addr, + nic_iface, status, AF_INET); + + return rc; +} + +/** + * cnic_handle_ipv6_iscsi_path_req() - This function will handle the IPv4 + * path req calls the bnx2i kernel module + * @param nic - The nic the message is directed towards + * @param fd - The file descriptor to be used to extract the private data + * @param ev - The iscsi_uevent + * @param buf - The private message buffer + */ +int cnic_handle_ipv6_iscsi_path_req(nic_t *nic, int fd, + struct iscsi_uevent *ev, + struct iscsi_path *path, + nic_interface_t *nic_iface) +{ + __u8 mac_addr[6]; + int rc, i; + uint16_t neighbor_retry; + int status = 0; + char addr_dst_str[INET6_ADDRSTRLEN]; + struct in6_addr src_addr, dst_addr, + src_matching_addr, dst_matching_addr, netmask; + struct in6_addr *addr; + struct ndpc_reqptr req_ptr; + + memset(mac_addr, 0, sizeof(mac_addr)); + + inet_ntop(AF_INET6, &path->dst.v6_addr, + addr_dst_str, sizeof(addr_dst_str)); + + /* Depending on the IPv6 address of the target we will need to + * determine whether we use the assigned IPv6 address or the + * link local IPv6 address */ + memcpy(&dst_addr, &path->dst.v6_addr, sizeof(struct in6_addr)); + if (ndpc_request(&nic_iface->ustack, &dst_addr, + &rc, CHECK_LINK_LOCAL_ADDR)) { + neighbor_retry = MAX_ARP_RETRY; + LOG_DEBUG(PFX "Check LL failed"); + goto done; + } + if (rc) { + LOG_DEBUG(PFX "Use LL"); + /* Get link local IPv6 address */ + addr = (struct in6_addr *)&nic_iface->ustack.linklocal6; + } else { + LOG_DEBUG(PFX "Use Best matched"); + if (ndpc_request(&nic_iface->ustack, + &dst_addr, + &addr, GET_HOST_ADDR)) { + neighbor_retry = MAX_ARP_RETRY; + LOG_DEBUG(PFX "Use Best matched failed"); + goto done; + } + if (addr == NULL) { + neighbor_retry = MAX_ARP_RETRY; + LOG_DEBUG(PFX "No Best matched found"); + goto done; + } + } + /* Got the best matched src IP address */ + memcpy(&src_addr, addr, sizeof(struct in6_addr)); + + if (nic_iface->ustack.netmask6[0] | nic_iface->ustack.netmask6[1] | + nic_iface->ustack.netmask6[2] | nic_iface->ustack.netmask6[3] | + nic_iface->ustack.netmask6[4] | nic_iface->ustack.netmask6[5] | + nic_iface->ustack.netmask6[6] | nic_iface->ustack.netmask6[7]) + memcpy(&netmask.s6_addr, nic_iface->ustack.netmask6, + sizeof(struct in6_addr)); + else + memcpy(&netmask.s6_addr, all_zeroes_addr6, + sizeof(struct in6_addr)); + + inet_ntop(AF_INET6, &src_addr.s6_addr16, addr_dst_str, + sizeof(addr_dst_str)); + LOG_DEBUG(PFX "src IP addr %s", addr_dst_str); + inet_ntop(AF_INET6, &dst_addr.s6_addr16, addr_dst_str, + sizeof(addr_dst_str)); + LOG_DEBUG(PFX "dst IP addr %s", addr_dst_str); + inet_ntop(AF_INET6, &netmask.s6_addr16, addr_dst_str, + sizeof(addr_dst_str)); + LOG_DEBUG(PFX "prefix mask %s", addr_dst_str); + + for (i = 0; i < 4; i++) { + src_matching_addr.s6_addr32[i] = src_addr.s6_addr32[i] & + netmask.s6_addr32[i]; + dst_matching_addr.s6_addr32[i] = dst_addr.s6_addr32[i] & + netmask.s6_addr32[i]; + if (src_matching_addr.s6_addr32[i] != + dst_matching_addr.s6_addr32[i]) { + /* No match with the prefix mask, use default route */ + if (memcmp(nic_iface->ustack.default_route_addr6, + all_zeroes_addr6, sizeof(*addr))) { + memcpy(&dst_addr, + nic_iface->ustack.default_route_addr6, + sizeof(dst_addr)); + inet_ntop(AF_INET6, &dst_addr.s6_addr16, + addr_dst_str, sizeof(addr_dst_str)); + LOG_DEBUG(PFX "Use default router IP addr %s", + addr_dst_str); + break; + } else { + neighbor_retry = MAX_ARP_RETRY; + goto done; + } + } + } + +#define MAX_ARP_RETRY 4 + neighbor_retry = 0; + + req_ptr.eth = (void *)mac_addr; + req_ptr.ipv6 = (void *)&dst_addr; + if (ndpc_request(&nic_iface->ustack, &req_ptr, &rc, CHECK_ARP_TABLE)) { + /* ndpc request failed, skip neighbor solicit send */ + neighbor_retry = MAX_ARP_RETRY; + goto done; + } + if (!rc) { + inet_ntop(AF_INET6, &dst_addr.s6_addr16, + addr_dst_str, sizeof(addr_dst_str)); + LOG_DEBUG(PFX + "%s: Preparing to send IPv6 neighbor solicitation " + "to dst: '%s'", nic->log_name, addr_dst_str); + while ((neighbor_retry < MAX_ARP_RETRY) + && (event_loop_stop == 0)) { + int count; + struct timespec ts; + struct timeval tp; + struct timeval tp_abs; + + LOG_INFO(PFX "%s: Didn't find IPv6: '%s'\n", + nic->log_name, addr_dst_str); + + rc = cnic_neigh_soliciation_send(nic, nic_iface, fd, + mac_addr, + &dst_addr, + addr_dst_str); + if (rc != 0) { + status = -EIO; + goto done; + } + + for (count = 0; count < 8; count++) { + /* Convert from timeval to timespec */ + rc = gettimeofday(&tp, NULL); + + timeradd(&tp, &tp_wait, &tp_abs); + + ts.tv_sec = tp_abs.tv_sec; + ts.tv_nsec = tp_abs.tv_usec * 1000; + + pthread_mutex_lock(&nic->nl_process_mutex); + rc = pthread_cond_timedwait + (&nic->nl_process_if_down_cond, + &nic->nl_process_mutex, &ts); + + if (rc == ETIMEDOUT) { + pthread_mutex_unlock + (&nic->nl_process_mutex); + + req_ptr.eth = (void *)mac_addr; + req_ptr.ipv6 = (void *)&dst_addr; + if (ndpc_request + (&nic_iface->ustack, &req_ptr, &rc, + CHECK_ARP_TABLE)) { + /* ndpc request failed, + force retry */ + rc = 0; + } + if (rc) + goto done; + } else { + nic->nl_process_if_down = 0; + pthread_mutex_unlock + (&nic->nl_process_mutex); + + neighbor_retry = MAX_ARP_RETRY; + goto done; + } + } + neighbor_retry++; + } + } + +done: + if (neighbor_retry >= MAX_ARP_RETRY) { + status = -EIO; + rc = -EIO; + } + + if (status != 0 || rc != 0) + pthread_mutex_unlock(&nic->xmit_mutex); + + cnic_nl_neigh_rsp(nic, fd, ev, path, mac_addr, + nic_iface, status, AF_INET6); + return rc; +} + +/** + * cnic_handle_iscsi_path_req() - This function will handle the path req calls + * the bnx2i kernel module + * @param nic - The nic the message is directed towards + * @param fd - The file descriptor to be used to extract the private data + * @param ev - The iscsi_uevent + * @param path - The private message buffer + * @param nic_iface - The nic_iface to use for this connection request + */ +int cnic_handle_iscsi_path_req(nic_t *nic, int fd, struct iscsi_uevent *ev, + struct iscsi_path *path, + nic_interface_t *nic_iface) +{ + + LOG_DEBUG(PFX "%s: Netlink message with VLAN ID: %d, path MTU: %d " + "minor: %d ip_addr_len: %d", + nic->log_name, path->vlan_id, path->pmtu, 0 /* TODO FIX */ , + path->ip_addr_len); + + if (path->ip_addr_len == 4) + return cnic_handle_ipv4_iscsi_path_req(nic, fd, ev, path, + nic_iface); + else if (path->ip_addr_len == 16) + return cnic_handle_ipv6_iscsi_path_req(nic, fd, ev, path, + nic_iface); + else { + LOG_DEBUG(PFX "%s: unknown ip_addr_len: %d size dropping ", + nic->log_name, path->ip_addr_len); + return -EIO; + } +} diff --git a/iscsiuio/src/unix/libs/cnic.h b/iscsiuio/src/unix/libs/cnic.h new file mode 100644 index 0000000..6244a94 --- /dev/null +++ b/iscsiuio/src/unix/libs/cnic.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2009-2011, Broadcom Corporation + * Copyright (c) 2014, QLogic Corporation + * + * Written by: Benjamin Li (benli@broadcom.com) + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam Dunkels. + * 4. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * cnic.h - CNIC UIO uIP user space stack + * + */ +#ifndef __CNIC_NL_H__ +#define __CNIC_NL_H__ + +/******************************************************************************* + * Constants shared between the bnx2 and bnx2x modules + ******************************************************************************/ +extern const char bnx2i_library_transport_name[]; +extern const size_t bnx2i_library_transport_name_size; + +int cnic_nl_open(); +void cnic_nl_close(); + +int cnic_handle_iscsi_path_req(nic_t *nic, int, struct iscsi_uevent *, + struct iscsi_path *path, + nic_interface_t *nic_iface); + +#endif /* __CNIC_NL_H__ */ diff --git a/iscsiuio/src/unix/logger.c b/iscsiuio/src/unix/logger.c new file mode 100644 index 0000000..d41f9e8 --- /dev/null +++ b/iscsiuio/src/unix/logger.c @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2009-2011, Broadcom Corporation + * Copyright (c) 2014, QLogic Corporation + * + * Written by: Benjamin Li (benli@broadcom.com) + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam Dunkels. + * 4. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * logger.c - Logging Utilities + * + */ +#include +#include +#include +#include +#include + +#include "options.h" +#include "logger.h" + +/****************************************************************************** + * Default logger values + ******************************************************************************/ +static const char default_logger_filename[] = "/var/log/iscsiuio.log"; + +struct logger main_log = { + .enabled = LOGGER_ENABLED, + .fp = NULL, + .log_file = (char *)default_logger_filename, + .level = LOG_LEVEL_INFO, + .lock = PTHREAD_MUTEX_INITIALIZER, + + .stats = { + .debug = 0, + .info = 0, + .warn = 0, + .error = 0, + + .last_log_time = 0, + }, +}; + +/****************************************************************************** + * Logger Functions + ******************************************************************************/ +/** + * log_uip() - Main logging function + * @param level_str - log level string + * @param fmt - log format + */ +void log_uip(char *level_str, char *fmt, ...) +{ + char time_buf[32]; + va_list ap, ap2; + + pthread_mutex_lock(&main_log.lock); + va_start(ap, fmt); + + if (main_log.fp == NULL) + goto end; + + main_log.stats.last_log_time = time(NULL); + strftime(time_buf, 26, "%a %b %d %T %Y", + localtime(&main_log.stats.last_log_time)); + va_copy(ap2, ap); + + if (main_log.enabled == LOGGER_ENABLED) { + fprintf(main_log.fp, "%s [%s]", level_str, time_buf); + vfprintf(main_log.fp, fmt, ap); + fprintf(main_log.fp, "\n"); + } + + if (opt.debug == DEBUG_ON) { + fprintf(stdout, "%s [%s]", level_str, time_buf); + vfprintf(stdout, fmt, ap2); + fprintf(stdout, "\n"); + + /* Force the printing of the log file */ + fflush(main_log.fp); + + /* Force the printing of the log out to standard output */ + fflush(stdout); + } + +end: + va_end(ap2); + va_end(ap); + pthread_mutex_unlock(&main_log.lock); +} + +/****************************************************************************** + * Initialize/Clean up routines + ******************************************************************************/ +/** + * init_logger() - Prepare the logger + * @param filename - path to where the log will be written to + * @return 0 on success, <0 on failure + */ +int init_logger(char *filename) +{ + int rc = 0; + + pthread_mutex_lock(&main_log.lock); + + if (opt.debug != DEBUG_ON) { + rc = -EIO; + goto disable; + } + main_log.fp = fopen(filename, "a"); + if (main_log.fp == NULL) { + printf("Could not create log file: %s <%s>\n", + filename, strerror(errno)); + rc = -EIO; + } +disable: + if (rc) + main_log.enabled = LOGGER_DISABLED; + else + main_log.enabled = LOGGER_ENABLED; + + pthread_mutex_unlock(&main_log.lock); + + if (!rc) + LOG_INFO("Initialize logger using log file: %s", filename); + + return rc; +} + +void fini_logger(int type) +{ + pthread_mutex_lock(&main_log.lock); + + if (main_log.fp != NULL) { + fclose(main_log.fp); + main_log.fp = NULL; + + if (opt.debug == DEBUG_ON) { + printf("Closed logger\n"); + fflush(stdout); + } + } + + if (type == SHUTDOWN_LOGGER) { + if ((main_log.log_file != NULL) && + (main_log.log_file != default_logger_filename)) { + free(main_log.log_file); + main_log.log_file = NULL; + } + } + + main_log.enabled = LOGGER_DISABLED; + + pthread_mutex_unlock(&main_log.lock); +} diff --git a/iscsiuio/src/unix/logger.h b/iscsiuio/src/unix/logger.h new file mode 100644 index 0000000..06e2084 --- /dev/null +++ b/iscsiuio/src/unix/logger.h @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2009-2011, Broadcom Corporation + * Copyright (c) 2014, QLogic Corporation + * + * Written by: Benjamin Li (benli@broadcom.com) + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam Dunkels. + * 4. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * logger.h - Logging Utilities + * + */ +#ifndef __LOGGER_H__ +#define __LOGGER_H__ + +#include +#include +#include +#include +#include + +/******************************************************************************* + * Logger Levels + ******************************************************************************/ +#define LOG_LEVEL_PACKET 5 +#define LOG_LEVEL_DEBUG 4 +#define LOG_LEVEL_INFO 3 +#define LOG_LEVEL_WARN 2 +#define LOG_LEVEL_ERR 1 +#define LOG_LEVEL_UNKNOWN 0 + +#define LOG_LEVEL_PACKET_STR "PKT " +#define LOG_LEVEL_DEBUG_STR "DBG " +#define LOG_LEVEL_INFO_STR "INFO " +#define LOG_LEVEL_WARN_STR "WARN " +#define LOG_LEVEL_ERR_STR "ERR " +#define LOG_LEVEL_UNKNOWN_STR "? " + +/******************************************************************************* + * Logging Macro's + ******************************************************************************/ +#define LOG_PACKET(fmt, args...) { if (LOG_LEVEL_PACKET <= \ + main_log.level) { \ + log_uip(LOG_LEVEL_PACKET_STR, fmt,\ + ##args);\ + } } +#define LOG_DEBUG(fmt, args...) { if (LOG_LEVEL_DEBUG <= main_log.level) { \ + log_uip(LOG_LEVEL_DEBUG_STR, fmt,\ + ##args);\ + } } + +#define LOG_INFO(fmt, args...) { if (LOG_LEVEL_INFO <= main_log.level) { \ + log_uip(LOG_LEVEL_INFO_STR, fmt,\ + ##args); \ + } } +#define LOG_WARN(fmt, args...) { if (LOG_LEVEL_WARN <= main_log.level) { \ + log_uip(LOG_LEVEL_WARN_STR, fmt,\ + ##args); \ + } } +#define LOG_ERR(fmt, args...) { if (LOG_LEVEL_ERR <= main_log.level) { \ + log_uip(LOG_LEVEL_ERR_STR, fmt,\ + ##args); \ + } } + +/******************************************************************************* + * Logging Statistics + ******************************************************************************/ +struct logger_stats { + uint64_t debug; + uint64_t info; + uint64_t warn; + uint64_t error; + + time_t last_log_time; +}; + +/******************************************************************************* + * Logger Structure + ******************************************************************************/ +struct logger { + FILE *fp; + char *log_file; + int8_t level; + +#define LOGGER_ENABLED 0x01 +#define LOGGER_DISABLED 0x02 + int8_t enabled; + + pthread_mutex_t lock; + + struct logger_stats stats; +}; + +extern struct logger main_log; + +int init_logger(char *); +void log_uip(char *level_str, char *fmt, ...); +void fini_logger(int); + +#define CLOSE_LOGGER 0x01 +#define SHUTDOWN_LOGGER 0x02 + +#endif diff --git a/iscsiuio/src/unix/main.c b/iscsiuio/src/unix/main.c new file mode 100644 index 0000000..c1a72d8 --- /dev/null +++ b/iscsiuio/src/unix/main.c @@ -0,0 +1,399 @@ +/* + * Copyright (c) 2001, Adam Dunkels. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam Dunkels. + * 4. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack. + * + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "uip.h" +#include "uip_arp.h" +#include "uip_eth.h" + +#include "timer.h" + +#include "build_date.h" +#include "config.h" +#include "iscsid_ipc.h" +#include "logger.h" +#include "nic.h" +#include "nic_id.h" +#include "nic_nl.h" +#include "nic_utils.h" +#include "options.h" +#include "packet.h" + +#include "dhcpc.h" + +#include "iscsid_ipc.h" +#include "brcm_iscsi.h" + +/******************************************************************************* + * Constants + ******************************************************************************/ +#define PFX "main " + +static const char default_pid_filepath[] = "/var/run/iscsiuio.pid"; + +/******************************************************************************* + * Global Variables + ******************************************************************************/ +static const struct option long_options[] = { + {"debug", 0, 0, 0}, + {"version", 0, 0, 0}, + {"help", 0, 0, 0}, + {0, 0, 0, 0} +}; + +struct options opt = { + .debug = DEBUG_OFF, +}; + +int event_loop_stop; +extern nic_t *nic_list; + +struct utsname cur_utsname; + +/** + * cleanup() - This function is called when this program is to be closed + * This function will clean up all the cnic uio interfaces and + * flush/close the logger + */ +static void cleanup() +{ + iscsid_cleanup(); + + nic_remove_all(); + + unload_all_nic_libraries(); + + LOG_INFO("Done waiting for cnic's/stacks to gracefully close"); + + fini_logger(SHUTDOWN_LOGGER); +} + +/** + * signal_handle_thread() - This is the signal handling thread of this program + * This is the only thread which will handle signals. + * All signals are routed here and handled here to + * provide consistant handling. + */ +static pthread_t signal_thread; +static void *signal_handle_thread(void *arg) +{ + sigset_t set; + int rc; + int signal; + + sigfillset(&set); + + LOG_INFO("signal handling thread ready"); + +signal_wait: + rc = sigwait(&set, &signal); + + switch (signal) { + case SIGINT: + LOG_INFO("Caught SIGINT signal"); + break; + case SIGUSR1: + LOG_INFO("Caught SIGUSR1 signal, rotate log"); + fini_logger(SHUTDOWN_LOGGER); + rc = init_logger(main_log.log_file); + if (rc != 0) + printf("Could not initialize the logger in " + "signal!\n"); + goto signal_wait; + default: + break; + } + event_loop_stop = 1; + + LOG_INFO("terminating..."); + + cleanup(); + exit(EXIT_SUCCESS); +} + +static void show_version() +{ + printf("%s: Version '%s', Build Date: '%s'\n", + APP_NAME, PACKAGE_VERSION, build_date); +} + +static void main_usage() +{ + show_version(); + + printf("\nUsage: %s [OPTION]\n", APP_NAME); + printf("iscsiuio daemon.\n" + "-f, --foreground make the program run in the foreground\n" + "-d, --debug debuglevel print debugging information\n" + "-p, --pid=pidfile use pid file (default %s).\n" + "-h, --help display this help and exit\n" + "-v, --version display version and exit\n", + default_pid_filepath); +} + +static void daemon_init() +{ + int fd; + + fd = open("/dev/null", O_RDWR); + if (fd == -1) + exit(-1); + + dup2(fd, 0); + dup2(fd, 1); + dup2(fd, 2); + setsid(); + chdir("/"); +} + +#define ISCSI_OOM_PATH_LEN 48 + +int oom_adjust(void) +{ + int fd; + char path[ISCSI_OOM_PATH_LEN]; + struct stat statb; + + if (nice(-10) < 0) + LOG_DEBUG("Could not increase process priority: %s", + strerror(errno)); + + snprintf(path, ISCSI_OOM_PATH_LEN, "/proc/%d/oom_score_adj", getpid()); + if (stat(path, &statb)) { + /* older kernel so use old oom_adj file */ + snprintf(path, ISCSI_OOM_PATH_LEN, "/proc/%d/oom_adj", + getpid()); + } + fd = open(path, O_WRONLY); + if (fd < 0) + return -1; + if (write(fd, "-16", 3) < 0) /* for 2.6.11 */ + LOG_DEBUG("Could not set oom score to -16: %s", + strerror(errno)); + if (write(fd, "-17", 3) < 0) /* for Andrea's patch */ + LOG_DEBUG("Could not set oom score to -17: %s", + strerror(errno)); + close(fd); + return 0; +} + + +/******************************************************************************* + * Main routine + ******************************************************************************/ +int main(int argc, char *argv[]) +{ + int rc; + sigset_t set; + const char *pid_file = default_pid_filepath; + int fd; + int foreground = 0; + pid_t pid; + pthread_attr_t attr; + + /* Record the start time for the user space daemon */ + opt.start_time = time(NULL); + + /* parse the parameters */ + while (1) { + int c, option_index; + + c = getopt_long(argc, argv, "fd:p:vh", + long_options, &option_index); + + if (c == -1) + break; + + switch (c) { + + case 'f': + foreground = 1; + break; + + /* Enable debugging mode */ + case 'd': + main_log.level = atoi(optarg); + opt.debug = DEBUG_ON; + break; + case 'p': + pid_file = optarg; + break; + case 'v': + show_version(); + exit(EXIT_SUCCESS); + case 'h': + default: + main_usage(); + exit(EXIT_SUCCESS); + } + } + + if (main_log.enabled == LOGGER_ENABLED) { + /* initialize the logger */ + rc = init_logger(main_log.log_file); + if (rc != 0 && opt.debug == DEBUG_ON) + printf("WARN: Could not initialize the logger\n"); + } + + LOG_INFO("Started iSCSI uio stack: Ver " PACKAGE_VERSION); + LOG_INFO("Build date: %s", build_date); + + if (opt.debug == DEBUG_ON) + LOG_INFO("Debug mode enabled"); + + event_loop_stop = 0; + nic_list = NULL; + + /* Determine the current kernel version */ + memset(&cur_utsname, 0, sizeof(cur_utsname)); + + rc = uname(&cur_utsname); + if (rc == 0) { + LOG_INFO("Running on sysname: '%s', release: '%s', " + "version '%s' machine: '%s'", + cur_utsname.sysname, cur_utsname.release, + cur_utsname.version, cur_utsname.machine); + } else + LOG_WARN("Could not determine kernel version"); + + /* Initialze the iscsid listener */ + rc = iscsid_init(); + if (rc != 0) + goto error; + + if (!foreground) { + char buf[64]; + ssize_t written_bytes; + + fd = open(pid_file, O_WRONLY | O_CREAT, 0644); + if (fd < 0) { + printf("Unable to create pid file: %s", pid_file); + exit(1); + } + + pid = fork(); + if (pid < 0) { + printf("Starting daemon failed"); + exit(1); + } else if (pid) { + exit(0); + } + + rc = chdir("/"); + if (rc == -1) + printf("Unable to chdir(\") [%s]", strerror(errno)); + + if (lockf(fd, F_TLOCK, 0) < 0) { + printf("Unable to lock pid file: %s [%s]", + pid_file, strerror(errno)); + exit(1); + } + + rc = ftruncate(fd, 0); + if (rc == -1) + printf("ftruncate(%d, 0) failed [%s]", + fd, strerror(errno)); + + sprintf(buf, "%d\n", getpid()); + written_bytes = write(fd, buf, strlen(buf)); + if (written_bytes == -1) + printf("Could not write lock file [%s]", + strerror(errno)); + + daemon_init(); + } + + /* Load the NIC libraries */ + rc = load_all_nic_libraries(); + if (rc != 0) + goto error; + + brcm_iscsi_init(); + + /* ensure we don't see any signals */ + sigemptyset(&set); + sigaddset(&set, SIGINT); + sigaddset(&set, SIGQUIT); + sigaddset(&set, SIGTERM); + sigaddset(&set, SIGUSR1); + rc = pthread_sigmask(SIG_SETMASK, &set, NULL); + + /* Spin off the signal handling thread */ + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + rc = pthread_create(&signal_thread, &attr, signal_handle_thread, NULL); + if (rc != 0) + LOG_ERR("Could not create signal handling thread"); + + /* Using sysfs to discover iSCSI hosts */ + nic_discover_iscsi_hosts(); + + /* oom-killer will not kill us at the night... */ + if (oom_adjust()) + LOG_DEBUG("Can not adjust oom-killer's pardon"); + + /* we don't want our active sessions to be paged out... */ + if (mlockall(MCL_CURRENT | MCL_FUTURE)) { + LOG_ERR("failed to mlockall, exiting..."); + goto error; + } + + /* Start the iscsid listener */ + rc = iscsid_start(); + if (rc != 0) + goto error; + + /* NetLink connection to listen to NETLINK_ISCSI private messages */ + nic_nl_open(); + +error: + cleanup(); + exit(EXIT_FAILURE); +} diff --git a/iscsiuio/src/unix/nic.c b/iscsiuio/src/unix/nic.c new file mode 100644 index 0000000..38a5776 --- /dev/null +++ b/iscsiuio/src/unix/nic.c @@ -0,0 +1,1533 @@ +/* + * Copyright (c) 2009-2011, Broadcom Corporation + * Copyright (c) 2014, QLogic Corporation + * + * Written by: Benjamin Li (benli@broadcom.com) + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam Dunkels. + * 4. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * nic.c - Generic NIC management/utility functions + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dhcpc.h" +#include "ipv6_ndpc.h" + +#include "logger.h" +#include "nic.h" +#include "nic_utils.h" +#include "options.h" + +#include "uip.h" +#include "uip_arp.h" +#include "uip_eth.h" +#include "uip-neighbor.h" + +#include "bnx2.h" +#include "bnx2x.h" +#include "ipv6.h" + +/****************************************************************************** + * Constants + *****************************************************************************/ +#define PFX "nic " +#define PCI_ANY_ID (~0) + +/****************************************************************************** + * Global variables + *****************************************************************************/ +/* Used to store a list of NIC libraries */ +pthread_mutex_t nic_lib_list_mutex = PTHREAD_MUTEX_INITIALIZER; +nic_lib_handle_t *nic_lib_list; + +/* Used to store a list of active cnic devices */ +pthread_mutex_t nic_list_mutex = PTHREAD_MUTEX_INITIALIZER; +nic_t *nic_list; + +/****************************************************************************** + * Functions to handle NIC libraries + *****************************************************************************/ +/** + * alloc_nic_library_handle() - Used to allocate a NIC library handle + * @return NULL if memory couldn't be allocated, pointer to the handle + * to the NIC library handle + */ +static nic_lib_handle_t *alloc_nic_library_handle() +{ + nic_lib_handle_t *handle; + + handle = malloc(sizeof(*handle)); + if (handle == NULL) { + LOG_ERR("Could not allocate memory for library handle"); + return NULL; + } + + memset(handle, 0, sizeof(*handle)); + handle->ops = NULL; + + pthread_mutex_init(&handle->mutex, NULL); + + return handle; +} + +static void free_nic_library_handle(nic_lib_handle_t *handle) +{ + free(handle); +} + +/** + * load_nic_library() - This function is used to load a NIC library + * @param handle - This is the library handle to load + * @return 0 = Success; <0 = failure + */ +static int load_nic_library(nic_lib_handle_t *handle) +{ + int rc; + char *library_name; + size_t library_name_size; + char *library_version; + size_t library_version_size; + char *build_date_str; + size_t build_date_str_size; + + pthread_mutex_lock(&handle->mutex); + + /* Validate the NIC ops table ensure that all the fields are not NULL */ + if ((handle->ops->open) == NULL || + (handle->ops->close) == NULL || + (handle->ops->read) == NULL || + (handle->ops->write) == NULL || + (handle->ops->clear_tx_intr == NULL)) { + LOG_ERR("Invalid NIC ops table: open: 0x%x, close: 0x%x," + "read: 0x%x, write: 0x%x clear_tx_intr: 0x%x " + "lib_ops: 0x%x", + handle->ops->open, handle->ops->close, + handle->ops->read, handle->ops->write, + handle->ops->clear_tx_intr, handle->ops->lib_ops); + rc = -EINVAL; + handle->ops = NULL; + goto error; + } + + /* Validate the NIC library ops table to ensure that all the proper + * fields are filled */ + if ((handle->ops->lib_ops.get_library_name == NULL) || + (handle->ops->lib_ops.get_pci_table == NULL) || + (handle->ops->lib_ops.get_library_version == NULL) || + (handle->ops->lib_ops.get_build_date == NULL) || + (handle->ops->lib_ops.get_transport_name == NULL)) { + rc = -EINVAL; + goto error; + } + + (*handle->ops->lib_ops.get_library_name) (&library_name, + &library_name_size); + (*handle->ops->lib_ops.get_library_version) (&library_version, + &library_version_size); + (*handle->ops->lib_ops.get_build_date) (&build_date_str, + &build_date_str_size); + + LOG_DEBUG("Loaded nic library '%s' Version: '%s' build on %s'", + library_name, library_version, build_date_str); + + pthread_mutex_unlock(&handle->mutex); + + return 0; + +error: + pthread_mutex_unlock(&handle->mutex); + + return rc; +} + +static struct nic_ops *(*nic_get_ops[]) () = { +bnx2_get_ops, bnx2x_get_ops,}; + +int load_all_nic_libraries() +{ + int rc, i = 0; + nic_lib_handle_t *handle; + + for (i = 0; i < sizeof(nic_get_ops) / sizeof(nic_get_ops[0]); i++) { + /* Add the CNIC library */ + handle = alloc_nic_library_handle(); + if (handle == NULL) { + LOG_ERR("Could not allocate memory for CNIC nic lib"); + return -ENOMEM; + } + + handle->ops = (*nic_get_ops[i]) (); + + rc = load_nic_library(handle); + if (rc != 0) { + free_nic_library_handle(handle); + return rc; + } + /* Add the CNIC library to the list of library handles */ + pthread_mutex_lock(&nic_lib_list_mutex); + + /* Add this library to the list of nic libraries we + * know about */ + if (nic_lib_list == NULL) { + nic_lib_list = handle; + } else { + nic_lib_handle_t *current = nic_lib_list; + + while (current->next != NULL) + current = current->next; + + current->next = handle; + } + pthread_mutex_unlock(&nic_lib_list_mutex); + + LOG_DEBUG("Added '%s' nic library", handle->ops->description); + } + + return rc; +} + +int unload_all_nic_libraries() +{ + nic_lib_handle_t *current, *next; + + pthread_mutex_lock(&nic_lib_list_mutex); + current = nic_lib_list; + + while (current != NULL) { + next = current->next; + free_nic_library_handle(current); + + current = next; + } + + pthread_mutex_unlock(&nic_lib_list_mutex); + + nic_lib_list = NULL; + + return 0; +} + +NIC_LIBRARY_EXIST_T does_nic_uio_name_exist(char *name) +{ + NIC_LIBRARY_EXIST_T rc; + nic_lib_handle_t *current; + + pthread_mutex_lock(&nic_lib_list_mutex); + current = nic_lib_list; + + while (current != NULL) { + char *uio_name; + size_t uio_name_size; + + (*current->ops->lib_ops.get_uio_name) (&uio_name, + &uio_name_size); + + if (strncmp(name, uio_name, uio_name_size) == 0) { + rc = NIC_LIBRARY_EXSITS; + goto done; + } + + current = current->next; + } + + rc = NIC_LIBRARY_DOESNT_EXIST; + +done: + pthread_mutex_unlock(&nic_lib_list_mutex); + return rc; +} + +NIC_LIBRARY_EXIST_T does_nic_library_exist(char *name) +{ + NIC_LIBRARY_EXIST_T rc; + nic_lib_handle_t *current; + + pthread_mutex_lock(&nic_lib_list_mutex); + current = nic_lib_list; + + while (current != NULL) { + char *library_name; + size_t library_name_size; + + (*current->ops->lib_ops.get_library_name) (&library_name, + &library_name_size); + + if (strncmp(name, library_name, library_name_size) == 0) { + rc = NIC_LIBRARY_EXSITS; + goto done; + } + + current = current->next; + } + + rc = NIC_LIBRARY_DOESNT_EXIST; + +done: + pthread_mutex_unlock(&nic_lib_list_mutex); + return rc; +} + +/** + * find_nic_lib_using_pci_id() - Find the proper NIC library using the + * PCI ID's + * @param vendor - PCI vendor ID to search on + * @param device - PCI device ID to search on + * @param subvendor - PCI subvendor ID to search on + * @param subdevice - PCI subdevice ID to search on + * @param handle - This function will return the nic lib handle if found + * @return 0 if found, <0 not found + */ +int find_nic_lib_using_pci_id(uint32_t vendor, uint32_t device, + uint32_t subvendor, uint32_t subdevice, + nic_lib_handle_t **handle, + struct pci_device_id **pci_entry) +{ + int rc; + nic_lib_handle_t *current; + + pthread_mutex_lock(&nic_lib_list_mutex); + current = nic_lib_list; + + while (current != NULL) { + struct pci_device_id *pci_table; + uint32_t entries; + int i; + + current->ops->lib_ops.get_pci_table(&pci_table, &entries); + + /* Sanity check the the pci table coming from the + * hardware library */ + if (entries > MAX_PCI_DEVICE_ENTRIES) { + LOG_WARN(PFX "Too many pci_table entries(%d) skipping", + entries); + continue; + } + + for (i = 0; i < entries; i++) { + LOG_DEBUG(PFX "Checking against: " + "vendor: 0x%x device:0x%x " + "subvendor:0x%x subdevice:0x%x", + pci_table[i].vendor, pci_table[i].device, + pci_table[i].subvendor, + pci_table[i].subdevice); + + if ((pci_table[i].vendor == vendor) && + (pci_table[i].device == device) && + (pci_table[i].subvendor == PCI_ANY_ID || + pci_table[i].subvendor == subvendor) && + (pci_table[i].subdevice == PCI_ANY_ID || + pci_table[i].subdevice == subdevice)) { + *handle = current; + *pci_entry = &pci_table[i]; + rc = 0; + goto done; + } + } + + current = current->next; + } + rc = -EINVAL; + +done: + pthread_mutex_unlock(&nic_lib_list_mutex); + + return rc; +} + +/** + * nic_init() - This will properly initialize a struct cnic_uio device + * @return NULL is there is a failure and pointer to an allocated/initialized + * struct cnic_uio on success + */ +nic_t *nic_init() +{ + nic_t *nic; + + nic = malloc(sizeof(*nic)); + if (nic == NULL) { + LOG_ERR("Couldn't malloc space for nic"); + return NULL; + } + + memset(nic, 0, sizeof(*nic)); + nic->uio_minor = -1; + nic->fd = INVALID_FD; + nic->next = NULL; + nic->thread = INVALID_THREAD; + nic->enable_thread = INVALID_THREAD; + nic->flags |= NIC_DISABLED; + nic->state = NIC_STOPPED; + nic->free_packet_queue = NULL; + nic->tx_packet_queue = NULL; + nic->nic_library = NULL; + nic->pci_id = NULL; + nic->page_size = getpagesize(); + + /* nic_mutex is used to protect nic ops */ + pthread_mutex_init(&nic->nic_mutex, NULL); + pthread_mutex_init(&nic->xmit_mutex, NULL); + pthread_mutex_init(&nic->free_packet_queue_mutex, NULL); + + pthread_cond_init(&nic->enable_wait_cond, NULL); + pthread_cond_init(&nic->enable_done_cond, NULL); + pthread_cond_init(&nic->nic_loop_started_cond, NULL); + pthread_cond_init(&nic->disable_wait_cond, NULL); + + nic->rx_poll_usec = DEFAULT_RX_POLL_USEC; + + pthread_mutex_init(&nic->nl_process_mutex, NULL); + pthread_cond_init(&nic->nl_process_if_down_cond, NULL); + pthread_cond_init(&nic->nl_process_cond, NULL); + nic->nl_process_thread = INVALID_THREAD; + nic->nl_process_if_down = 0; + nic->nl_process_head = 0; + nic->nl_process_tail = 0; + memset(&nic->nl_process_ring, 0, sizeof(nic->nl_process_ring)); + + return nic; +} + +void nic_add(nic_t *nic) +{ + /* Add this device to our list of nics */ + if (nic_list == NULL) { + nic_list = nic; + } else { + nic_t *current = nic_list; + + while (current->next != NULL) + current = current->next; + + current->next = nic; + } +} + +/** + * nic_remove() - Used to remove the NIC for the nic list + * @param nic - the nic to remove + */ +int nic_remove(nic_t *nic) +{ + int rc; + nic_t *prev, *current; + struct stat file_stat; + nic_interface_t *nic_iface, *next_nic_iface, *vlan_iface; + + pthread_mutex_lock(&nic->nic_mutex); + + /* Check if the file node exists before closing */ + rc = stat(nic->uio_device_name, &file_stat); + if ((rc == 0) && (nic->ops)) + nic->ops->close(nic, 0); + pthread_mutex_unlock(&nic->nic_mutex); + + nic->state = NIC_EXIT; + + if (nic->enable_thread != INVALID_THREAD) { + LOG_DEBUG(PFX "%s: Canceling nic enable thread", nic->log_name); + + rc = pthread_cancel(nic->enable_thread); + if (rc != 0) + LOG_DEBUG(PFX "%s: Couldn't send cancel to nic enable " + "thread", nic->log_name); + + nic->enable_thread = INVALID_THREAD; + LOG_DEBUG(PFX "%s: nic enable thread cleaned", nic->log_name); + } else { + LOG_DEBUG(PFX "%s: NIC enable thread already canceled", + nic->log_name); + } + + if (nic->thread != INVALID_THREAD) { + LOG_DEBUG(PFX "%s: Canceling nic thread", nic->log_name); + + rc = pthread_cancel(nic->thread); + if (rc != 0) + LOG_DEBUG(PFX "%s: Couldn't send cancel to nic", + nic->log_name); + + nic->thread = INVALID_THREAD; + LOG_DEBUG(PFX "%s: nic thread cleaned", nic->log_name); + } else { + LOG_DEBUG(PFX "%s: NIC thread already canceled", nic->log_name); + } + + if (nic->nl_process_thread != INVALID_THREAD) { + LOG_DEBUG(PFX "%s: Canceling nic nl thread", nic->log_name); + + rc = pthread_cancel(nic->nl_process_thread); + if (rc != 0) + LOG_DEBUG(PFX "%s: Couldn't send cancel to nic nl " + "thread", nic->log_name); + + nic->nl_process_thread = INVALID_THREAD; + LOG_DEBUG(PFX "%s: nic nl thread cleaned", nic->log_name); + } else { + LOG_DEBUG(PFX "%s: NIC nl thread already canceled", + nic->log_name); + } + + current = prev = nic_list; + while (current != NULL) { + if (current == nic) + break; + + prev = current; + current = current->next; + } + + if (current != NULL) { + if (current == nic_list) + nic_list = current->next; + else + prev->next = current->next; + + /* Before freeing the nic, must free all the associated + nic_iface */ + nic_iface = current->nic_iface; + while (nic_iface != NULL) { + vlan_iface = nic_iface->vlan_next; + while (vlan_iface != NULL) { + next_nic_iface = vlan_iface->vlan_next; + free(vlan_iface); + vlan_iface = next_nic_iface; + } + next_nic_iface = nic_iface->next; + free(nic_iface); + nic_iface = next_nic_iface; + } + free(nic); + } else { + LOG_ERR(PFX "%s: Couldn't find nic to remove", nic->log_name); + } + + return 0; +} + +/** + * nic_close() - Used to indicate to a NIC that it should close + * Must be called with nic->nic_mutex + * @param nic - the nic to close + * @param graceful - ALLOW_GRACEFUL_SHUTDOWN will check the nic state + * before proceeding to close() + * FORCE_SHUTDOWN will force the nic to close() + * reguardless of the state + * @param clean - this will free the proper strings assoicated + * with the NIC + * + */ +void nic_close(nic_t *nic, NIC_SHUTDOWN_T graceful, int clean) +{ + int rc; + nic_interface_t *nic_iface, *vlan_iface; + struct stat file_stat; + + /* The NIC could be configured by the uIP config file + * but not assoicated with a hardware library just yet + * we will need to check for this */ + if (nic->ops == NULL) { + LOG_WARN(PFX "%s: when closing nic->ops == NULL", + nic->log_name); + goto error; + } + + /* Check if the file node exists */ + rc = stat(nic->uio_device_name, &file_stat); + if ((rc == 0) && (nic->ops)) + rc = (*nic->ops->close) (nic, graceful); + if (rc != 0) { + LOG_ERR(PFX "%s: Could not close nic", nic->log_name); + } else { + nic->state = NIC_STOPPED; + nic->flags &= ~NIC_ENABLED; + nic->flags |= NIC_DISABLED; + } + + nic_iface = nic->nic_iface; + while (nic_iface != NULL) { + if (!((nic_iface->flags & NIC_IFACE_PERSIST) == + NIC_IFACE_PERSIST)) { + uip_reset(&nic_iface->ustack); + vlan_iface = nic_iface->vlan_next; + while (vlan_iface != NULL) { + uip_reset(&vlan_iface->ustack); + vlan_iface = vlan_iface->vlan_next; + } + } + nic_iface = nic_iface->next; + } + + /* The NIC must be destroyed and init'ed once again, + * POSIX defines that the mutex will be undefined it + * init'ed twice without a destroy */ + pthread_mutex_destroy(&nic->xmit_mutex); + pthread_mutex_init(&nic->xmit_mutex, NULL); + + if (clean & FREE_CONFIG_NAME) { + /* Free any named strings we might be holding onto */ + if (nic->flags & NIC_CONFIG_NAME_MALLOC) { + free(nic->config_device_name); + nic->flags &= ~NIC_CONFIG_NAME_MALLOC; + } + nic->config_device_name = NULL; + } + + if (clean & FREE_UIO_NAME) { + if (nic->flags & NIC_UIO_NAME_MALLOC) { + free(nic->uio_device_name); + nic->uio_device_name = NULL; + + nic->flags &= ~NIC_UIO_NAME_MALLOC; + } + } + + LOG_ERR(PFX "%s: nic closed", nic->log_name); +error: + return; +} + +/** + * nic_iface_init() - This function is used to add an interface to the + * structure cnic_uio + * @return 0 on success, <0 on failure + */ +nic_interface_t *nic_iface_init() +{ + nic_interface_t *nic_iface = malloc(sizeof(*nic_iface)); + if (nic_iface == NULL) { + LOG_ERR("Could not allocate space for nic iface"); + return NULL; + } + + memset(nic_iface, 0, sizeof(*nic_iface)); + nic_iface->next = NULL; + nic_iface->vlan_next = NULL; + nic_iface->iface_num = IFACE_NUM_INVALID; + nic_iface->request_type = IP_CONFIG_OFF; + + return nic_iface; +} + +/** + * nic_add_nic_iface() - This function is used to add an interface to the + * nic structure + * Called with nic_mutex held + * @param nic - struct nic device to add the interface to + * @param nic_iface - network interface used to add to the nic + * @return 0 on success, <0 on failure + */ +int nic_add_nic_iface(nic_t *nic, nic_interface_t *nic_iface) +{ + nic_interface_t *current, *prev; + + /* Make sure it doesn't already exist */ + current = nic_find_nic_iface(nic, nic_iface->protocol, + nic_iface->vlan_id, nic_iface->iface_num, + nic_iface->request_type); + if (current) { + LOG_DEBUG(PFX "%s: nic interface for VLAN: %d, protocol: %d" + " already exist", nic->log_name, nic_iface->vlan_id, + nic_iface->protocol); + return 0; + } + + prev = NULL; + current = nic->nic_iface; + while (current != NULL) { + if (current->protocol == nic_iface->protocol) { + /* Replace parent */ + nic_iface->vlan_next = current; + nic_iface->next = current->next; + current->next = NULL; + if (prev) + prev->next = nic_iface; + else + nic->nic_iface = nic_iface; + goto done; + } + prev = current; + current = current->next; + } + nic_iface->next = nic->nic_iface; + nic->nic_iface = nic_iface; +done: + /* Set nic_interface common fields */ + nic_iface->parent = nic; + memcpy(&nic_iface->ustack.uip_ethaddr.addr, nic->mac_addr, ETH_ALEN); + nic->num_of_nic_iface++; + + LOG_INFO(PFX "%s: Added nic interface for VLAN: %d, protocol: %d", + nic->log_name, nic_iface->vlan_id, nic_iface->protocol); + + return 0; +} + +/****************************************************************************** + * Routine to process interrupts from the NIC device + ******************************************************************************/ +/** + * nic_process_intr() - Routine used to process interrupts from the hardware + * @param nic - NIC hardware to process the interrupt on + * @return 0 on success, <0 on failure + */ +int nic_process_intr(nic_t *nic, int discard_check) +{ + fd_set fdset; + int ret; + int count; + struct timeval tv; + + /* Simple sanity checks */ + if (discard_check != 1 && nic->state != NIC_RUNNING) { + LOG_ERR(PFX "%s: Couldn't process interupt NIC not running", + nic->log_name); + return -EBUSY; + } + + if (discard_check != 1 && nic->fd == INVALID_FD) { + LOG_ERR(PFX "%s: NIC fd not valid", nic->log_name); + return -EIO; + } + + FD_ZERO(&fdset); + FD_SET(nic->fd, &fdset); + + tv.tv_sec = 0; + pthread_mutex_lock(&nic->nic_mutex); + if (nic->flags & NIC_LONG_SLEEP) + tv.tv_usec = 1000; + else + tv.tv_usec = nic->rx_poll_usec; + pthread_mutex_unlock(&nic->nic_mutex); + + /* Wait for an interrupt to come in or timeout */ + ret = select(nic->fd + 1, &fdset, NULL, NULL, &tv); + switch (ret) { + case 1: + /* Usually there should only be one file descriptor ready + * to read */ + break; + case 0: + return ret; + case -1: + LOG_ERR(PFX "%s: error waiting for interrupt: %s", + nic->log_name, strerror(errno)); + return 0; + default: + LOG_ERR(PFX "%s: unknown number of FD's, ignoring: %d ret", + nic->log_name, ret); + return 0; + } + + ret = read(nic->fd, &count, sizeof(count)); + pthread_mutex_lock(&nic->nic_mutex); + if (ret > 0) { + nic->stats.interrupts++; + LOG_PACKET(PFX "%s: interrupt count: %d prev: %d", + nic->log_name, count, nic->intr_count); + + if (count == nic->intr_count) { + LOG_PACKET(PFX "%s: got interrupt but count still the " + "same", nic->log_name, count); + } + + /* Check if we missed an interrupt. With UIO, + * the count should be incremental */ + if (count != nic->intr_count + 1) { + nic->stats.missed_interrupts++; + LOG_PACKET(PFX "%s: Missed interrupt! on %d not %d", + nic->log_name, count, nic->intr_count); + } + + nic->intr_count = count; + + (*nic->ops->clear_tx_intr) (nic); + ret = 1; + } + pthread_mutex_unlock(&nic->nic_mutex); + + return ret; +} + +static void prepare_ipv4_packet(nic_t *nic, + nic_interface_t *nic_iface, + struct uip_stack *ustack, packet_t *pkt) +{ + u16_t ipaddr[2]; + arp_table_query_t arp_query; + dest_ipv4_addr_t dest_ipv4_addr; + struct arp_entry *tabptr; + int queue_rc; + int vlan_id = 0; + + /* If the rx vlan tag is not stripped and vlan is present in the pkt, + manual stripping is required because tx is using hw vlan tag! */ + if (pkt->network_layer == pkt->data_link_layer + + sizeof(struct uip_vlan_eth_hdr)) { + /* VLAN is detected in the pkt buf */ + memcpy(pkt->data_link_layer + 12, pkt->network_layer - 2, + pkt->buf_size - sizeof(struct uip_vlan_eth_hdr) + 2); + } + dest_ipv4_addr = uip_determine_dest_ipv4_addr(ustack, ipaddr); + if (dest_ipv4_addr == LOCAL_BROADCAST) { + uip_build_eth_header(ustack, ipaddr, NULL, pkt, vlan_id); + return; + } + + arp_query = is_in_arp_table(ipaddr, &tabptr); + + switch (arp_query) { + case IS_IN_ARP_TABLE: + uip_build_eth_header(ustack, + ipaddr, tabptr, pkt, vlan_id); + break; + case NOT_IN_ARP_TABLE: + queue_rc = nic_queue_tx_packet(nic, nic_iface, pkt); + if (queue_rc) { + LOG_ERR("could not queue TX packet: %d", queue_rc); + } else { + uip_build_arp_request(ustack, ipaddr); + } + break; + default: + LOG_ERR("Unknown arp state"); + break; + } +} + +static void prepare_ipv6_packet(nic_t *nic, + nic_interface_t *nic_iface, + struct uip_stack *ustack, packet_t *pkt) +{ + struct uip_eth_hdr *eth; + struct uip_vlan_eth_hdr *eth_vlan; + int vlan_id = 0; + + if (pkt->network_layer == pkt->data_link_layer + + sizeof(struct uip_vlan_eth_hdr)) { + /* VLAN is detected in the pkt buf */ + memcpy(pkt->data_link_layer + 12, pkt->network_layer - 2, + pkt->buf_size - sizeof(struct uip_vlan_eth_hdr) + 2); + } + eth = (struct uip_eth_hdr *)ustack->data_link_layer; + eth_vlan = (struct uip_vlan_eth_hdr *)ustack->data_link_layer; + if (vlan_id == 0) { + eth->type = htons(UIP_ETHTYPE_IPv6); + } else { + eth_vlan->tpid = htons(UIP_ETHTYPE_8021Q); + eth_vlan->vid = htons(vlan_id); + eth_vlan->type = htons(UIP_ETHTYPE_IPv6); + } +} + +static void prepare_ustack(nic_t *nic, + nic_interface_t *nic_iface, + struct uip_stack *ustack, struct packet *pkt) +{ + struct ether_header *eth = NULL; + ustack->uip_buf = pkt->buf; + ustack->uip_len = pkt->buf_size; + + pkt->nic = nic; + pkt->nic_iface = nic_iface; + + ustack->data_link_layer = pkt->buf; + /* Adjust the network layer pointer depending if + * there is a VLAN tag or not, or if the hardware + * has stripped out the + * VLAN tag */ + ustack->network_layer = ustack->data_link_layer + + sizeof(struct uip_eth_hdr); + /* Init buffer to be IPv6 */ + if (nic_iface->ustack.ip_config == IPV6_CONFIG_DHCP || + nic_iface->ustack.ip_config == IPV6_CONFIG_STATIC) { + eth = (struct ether_header *)ustack->data_link_layer; + eth->ether_type = htons(UIP_ETHTYPE_IPv6); + } +} + +int do_timers_per_nic_iface(nic_t *nic, nic_interface_t *nic_iface, + struct timer *arp_timer) +{ + packet_t *pkt; + struct uip_stack *ustack = &nic_iface->ustack; + int i; + + pkt = get_next_free_packet(nic); + if (pkt == NULL) + return -EIO; + + if (nic_iface->protocol == AF_INET) { + for (i = 0; i < UIP_UDP_CONNS; i++) { + prepare_ustack(nic, nic_iface, ustack, pkt); + + uip_udp_periodic(ustack, i); + /* If the above function invocation resulted + * in data that should be sent out on the + * network, the global variable uip_len is + * set to a value > 0. */ + if (ustack->uip_len > 0) { + pkt->buf_size = ustack->uip_len; + + prepare_ipv4_packet(nic, nic_iface, ustack, + pkt); + + (*nic->ops->write) (nic, nic_iface, pkt); + ustack->uip_len = 0; + } + } + } else { + /* Added periodic poll for IPv6 NDP engine */ + if (ustack->ndpc != NULL) { /* If engine is active */ + prepare_ustack(nic, nic_iface, ustack, pkt); + + uip_ndp_periodic(ustack); + /* If the above function invocation resulted + * in data that should be sent out on the + * network, the global variable uip_len is + * set to a value > 0. */ + if (ustack->uip_len > 0) { + pkt->buf_size = ustack->uip_len; + prepare_ipv6_packet(nic, nic_iface, ustack, + pkt); + (*nic->ops->write) (nic, nic_iface, pkt); + ustack->uip_len = 0; + } + } + } + /* Call the ARP timer function every 10 seconds. */ + if (timer_expired(arp_timer)) { + timer_reset(arp_timer); + uip_arp_timer(); + } + put_packet_in_free_queue(pkt, nic); + return 0; +} + +static int check_timers(nic_t *nic, + struct timer *periodic_timer, struct timer *arp_timer) +{ + if (timer_expired(periodic_timer)) { + nic_interface_t *nic_iface, *vlan_iface; + + timer_reset(periodic_timer); + + pthread_mutex_lock(&nic->nic_mutex); + + nic_iface = nic->nic_iface; + while (nic_iface != NULL) { + do_timers_per_nic_iface(nic, nic_iface, arp_timer); + vlan_iface = nic_iface->vlan_next; + while (vlan_iface != NULL) { + do_timers_per_nic_iface(nic, vlan_iface, + arp_timer); + vlan_iface = vlan_iface->vlan_next; + } + nic_iface = nic_iface->next; + } + + pthread_mutex_unlock(&nic->nic_mutex); + } + return 0; +} + +int process_packets(nic_t *nic, + struct timer *periodic_timer, + struct timer *arp_timer, nic_interface_t *nic_iface) +{ + int rc; + packet_t *pkt; + + pkt = get_next_free_packet(nic); + if (pkt == NULL) { + LOG_DEBUG(PFX "%s: Couldn't get buffer for processing packet", + nic->log_name); + return -ENOMEM; + } + + pthread_mutex_lock(&nic->nic_mutex); + rc = (*nic->ops->read) (nic, pkt); + pthread_mutex_unlock(&nic->nic_mutex); + + if ((rc != 0) && (pkt->buf_size > 0)) { + uint16_t type = 0; + int af_type = 0; + struct uip_stack *ustack; + uint16_t vlan_id; + + pkt->data_link_layer = pkt->buf; + + vlan_id = pkt->vlan_tag & 0xFFF; + if ((vlan_id == 0) || + (NIC_VLAN_STRIP_ENABLED & nic->flags)) { + struct uip_eth_hdr *hdr = ETH_BUF(pkt->buf); + type = ntohs(hdr->type); + pkt->network_layer = pkt->data_link_layer + + sizeof(struct uip_eth_hdr); + } else { + struct uip_vlan_eth_hdr *hdr = VLAN_ETH_BUF(pkt->buf); + type = ntohs(hdr->type); + pkt->network_layer = pkt->data_link_layer + + sizeof(struct uip_vlan_eth_hdr); + } + + switch (type) { + case UIP_ETHTYPE_IPv6: + af_type = AF_INET6; + break; + case UIP_ETHTYPE_IPv4: + case UIP_ETHTYPE_ARP: + af_type = AF_INET; + break; + default: + LOG_PACKET(PFX "%s: Ignoring vlan:0x%x ethertype:0x%x", + nic->log_name, vlan_id, type); + goto done; + } + + pthread_mutex_lock(&nic->nic_mutex); + + /* check if we have the given VLAN interface */ + if (nic_iface != NULL) { + if (vlan_id != nic_iface->vlan_id) { + /* Matching nic_iface not found, drop */ + pthread_mutex_unlock(&nic->nic_mutex); + rc = EINVAL; /* Return the +error code */ + goto done; + } + goto nic_iface_present; + } + + /* Best effort to find the correct instance + Input: protocol and vlan_tag */ + nic_iface = nic_find_nic_iface(nic, af_type, vlan_id, + IFACE_NUM_INVALID, + IP_CONFIG_OFF); + if (nic_iface == NULL) { + /* Matching nic_iface not found */ + pthread_mutex_unlock(&nic->nic_mutex); + LOG_PACKET(PFX "%s: Couldn't find interface for " + "VLAN: %d af_type %d", + nic->log_name, vlan_id, af_type); + rc = EINVAL; /* Return the +error code */ + goto done; + } +nic_iface_present: + pkt->nic_iface = nic_iface; + + ustack = &nic_iface->ustack; + + ustack->uip_buf = pkt->buf; + ustack->uip_len = pkt->buf_size; + ustack->data_link_layer = pkt->buf; + + /* Adjust the network layer pointer depending if there is a + * VLAN tag or not, or if the hardware has stripped out the + * VLAN tag */ + if ((vlan_id == 0) || + (NIC_VLAN_STRIP_ENABLED & nic->flags)) + ustack->network_layer = ustack->data_link_layer + + sizeof(struct uip_eth_hdr); + else + ustack->network_layer = ustack->data_link_layer + + sizeof(struct uip_vlan_eth_hdr); + + /* determine how we should process this packet based on the + * ethernet type */ + switch (type) { + case UIP_ETHTYPE_IPv6: + uip_input(ustack); + if (ustack->uip_len > 0) { + /* The pkt generated has already consulted + the IPv6 ARP table */ + pkt->buf_size = ustack->uip_len; + prepare_ipv6_packet(nic, nic_iface, + ustack, pkt); + + (*nic->ops->write) (nic, nic_iface, pkt); + } + break; + case UIP_ETHTYPE_IPv4: + uip_arp_ipin(ustack, pkt); + uip_input(ustack); + /* If the above function invocation resulted + * in data that should be sent out on the + * network, the global variable uip_len is + * set to a value > 0. */ + if (ustack->uip_len > 0) { + prepare_ipv4_packet(nic, nic_iface, + ustack, pkt); + + (*nic->ops->write) (nic, nic_iface, pkt); + } + + break; + case UIP_ETHTYPE_ARP: + uip_arp_arpin(nic_iface, ustack, pkt); + + /* If the above function invocation resulted + * in data that should be sent out on the + * network, the global variable uip_len + * is set to a value > 0. */ + if (pkt->buf_size > 0) + (*nic->ops->write) (nic, nic_iface, pkt); + break; + } + ustack->uip_len = 0; + pthread_mutex_unlock(&nic->nic_mutex); + } + +done: + put_packet_in_free_queue(pkt, nic); + + return rc; +} + +static int process_dhcp_loop(nic_t *nic, + nic_interface_t *nic_iface, + struct timer *periodic_timer, + struct timer *arp_timer) +{ + struct dhcpc_state *s; + struct ndpc_state *n; + int rc; + struct timeval start_time; + struct timeval current_time; + struct timeval wait_time; + struct timeval total_time; + + /* 10s loop time to wait for DHCP */ + switch (nic_iface->ustack.ip_config) { + case IPV4_CONFIG_DHCP: + wait_time.tv_sec = 10; + break; + case IPV6_CONFIG_DHCP: + wait_time.tv_sec = 15; + break; + case IPV6_CONFIG_STATIC: + wait_time.tv_sec = 4; + break; + default: + wait_time.tv_sec = 2; + } + wait_time.tv_usec = 0; + + s = nic_iface->ustack.dhcpc; + n = nic_iface->ustack.ndpc; + + if (gettimeofday(&start_time, NULL)) { + LOG_ERR(PFX "%s: Couldn't get time of day to start DHCP timer", + nic->log_name); + return -EIO; + } + + timeradd(&start_time, &wait_time, &total_time); + + periodic_timer->start = periodic_timer->start - + periodic_timer->interval; + + while ((event_loop_stop == 0) && + (nic->flags & NIC_ENABLED) && !(nic->flags & NIC_GOING_DOWN)) { + + if (nic_iface->ustack.ip_config == IPV4_CONFIG_DHCP) { + if (s->state == STATE_CONFIG_RECEIVED) + break; + } + if (nic_iface->ustack.ip_config == IPV6_CONFIG_DHCP || + nic_iface->ustack.ip_config == IPV6_CONFIG_STATIC) { + if (n->state == NDPC_STATE_BACKGROUND_LOOP) + break; + } + + /* Check the periodic and ARP timer */ + check_timers(nic, periodic_timer, arp_timer); + + rc = nic_process_intr(nic, 1); + + while ((rc > 0) && (!(nic->flags & NIC_GOING_DOWN))) { + rc = process_packets(nic, + periodic_timer, + arp_timer, nic_iface); + } + + if (gettimeofday(¤t_time, NULL)) { + LOG_ERR(PFX "%s: Couldn't get current time for " + "DHCP start", nic->log_name); + return -EIO; + } + + if (timercmp(&total_time, ¤t_time, <)) { + LOG_ERR(PFX "%s: timeout waiting for DHCP/NDP", + nic->log_name); + if (nic_iface->ustack.ip_config == IPV6_CONFIG_DHCP || + nic_iface->ustack.ip_config == IPV6_CONFIG_STATIC) + n->retry_count = IPV6_MAX_ROUTER_SOL_RETRY; + return -EIO; + } + } + + if (nic->flags & NIC_GOING_DOWN) + return -EIO; + else if (nic->flags & NIC_DISABLED) + return -EINVAL; + else + return 0; +} + +/* Called with nic_mutex locked */ +static int do_acquisition(nic_t *nic, nic_interface_t *nic_iface, + struct timer *periodic_timer, struct timer *arp_timer) +{ + struct in_addr addr; + struct in6_addr addr6; + char buf[INET6_ADDRSTRLEN]; + int rc = -1; + + /* New acquisition */ + uip_init(&nic_iface->ustack, nic->flags & NIC_IPv6_ENABLED); + memcpy(&nic_iface->ustack.uip_ethaddr.addr, nic->mac_addr, ETH_ALEN); + + LOG_INFO(PFX "%s: Initialized ip stack: VLAN: %d", + nic->log_name, nic_iface->vlan_id); + + LOG_INFO(PFX "%s: mac: %02x:%02x:%02x:%02x:%02x:%02x", + nic->log_name, + nic_iface->mac_addr[0], + nic_iface->mac_addr[1], + nic_iface->mac_addr[2], + nic_iface->mac_addr[3], + nic_iface->mac_addr[4], + nic_iface->mac_addr[5]); + + switch (nic_iface->ustack.ip_config) { + case IPV4_CONFIG_STATIC: + memcpy(&addr.s_addr, nic_iface->ustack.hostaddr, + sizeof(addr.s_addr)); + + LOG_INFO(PFX "%s: Using IP address: %s", + nic->log_name, inet_ntoa(addr)); + + memcpy(&addr.s_addr, nic_iface->ustack.netmask, + sizeof(addr.s_addr)); + + LOG_INFO(PFX "%s: Using netmask: %s", + nic->log_name, inet_ntoa(addr)); + + set_uip_stack(&nic_iface->ustack, + &nic_iface->ustack.hostaddr, + &nic_iface->ustack.netmask, + &nic_iface->ustack.default_route_addr, + nic_iface->mac_addr); + break; + + case IPV4_CONFIG_DHCP: + set_uip_stack(&nic_iface->ustack, + &nic_iface->ustack.hostaddr, + &nic_iface->ustack.netmask, + &nic_iface->ustack.default_route_addr, + nic_iface->mac_addr); + if (dhcpc_init(nic, &nic_iface->ustack, + nic_iface->mac_addr, ETH_ALEN)) { + if (nic_iface->ustack.dhcpc) { + LOG_DEBUG(PFX "%s: DHCPv4 engine already " + "initialized!", nic->log_name); + goto skip; + } else { + LOG_DEBUG(PFX "%s: DHCPv4 engine failed " + "initialization!", nic->log_name); + goto error; + } + } + pthread_mutex_unlock(&nic->nic_mutex); + rc = process_dhcp_loop(nic, nic_iface, periodic_timer, + arp_timer); + pthread_mutex_lock(&nic->nic_mutex); + + if (rc) { + LOG_ERR(PFX "%s: DHCP failed", nic->log_name); + /* For DHCPv4 failure, the ustack must be cleaned so + it can re-acquire on the next iscsid request */ + uip_reset(&nic_iface->ustack); + + /* Signal that the device enable is + done */ + pthread_cond_broadcast(&nic->enable_done_cond); + pthread_mutex_unlock(&nic->nic_mutex); + + if (nic->enable_thread == INVALID_THREAD) + goto dhcp_err; + + rc = pthread_cancel(nic->enable_thread); + if (rc != 0) + LOG_ERR(PFX "%s: Couldn't cancel " + "enable nic thread", nic->log_name); +dhcp_err: + pthread_mutex_lock(&nic->nic_mutex); + goto error; + } + + if (nic->flags & NIC_DISABLED) { + /* Break out of this loop */ + break; + } + + LOG_INFO(PFX "%s: Initialized dhcp client", nic->log_name); + break; + + case IPV6_CONFIG_DHCP: + case IPV6_CONFIG_STATIC: + if (ndpc_init(nic, &nic_iface->ustack, nic_iface->mac_addr, + ETH_ALEN)) { + LOG_DEBUG(PFX "%s: IPv6 engine already initialized!", + nic->log_name); + goto skip; + } + pthread_mutex_unlock(&nic->nic_mutex); + rc = process_dhcp_loop(nic, nic_iface, periodic_timer, + arp_timer); + pthread_mutex_lock(&nic->nic_mutex); + if (rc) { + /* Don't reset and allow to use RA and LL */ + LOG_ERR(PFX "%s: IPv6 DHCP/NDP failed", nic->log_name); + } + if (nic_iface->ustack.ip_config == IPV6_CONFIG_STATIC) { + memcpy(&addr6.s6_addr, nic_iface->ustack.hostaddr6, + sizeof(addr6.s6_addr)); + inet_ntop(AF_INET6, addr6.s6_addr, buf, sizeof(buf)); + LOG_INFO(PFX "%s: hostaddr IP: %s", nic->log_name, buf); + memcpy(&addr6.s6_addr, nic_iface->ustack.netmask6, + sizeof(addr6.s6_addr)); + inet_ntop(AF_INET6, addr6.s6_addr, buf, sizeof(buf)); + LOG_INFO(PFX "%s: netmask IP: %s", nic->log_name, buf); + } + break; + + default: + LOG_INFO(PFX "%s: ipconfig = %d?", nic->log_name, + nic_iface->ustack.ip_config); + } +skip: + /* Mark acquisition done for this nic iface */ + nic_iface->flags &= ~NIC_IFACE_ACQUIRE; + + LOG_INFO(PFX "%s: enabled vlan %d protocol: %d", nic->log_name, + nic_iface->vlan_id, nic_iface->protocol); + return 0; + +error: + return -EIO; +} + + +void *nic_loop(void *arg) +{ + nic_t *nic = (nic_t *) arg; + int rc = -1; + sigset_t set; + struct timer periodic_timer, arp_timer; + + sigfillset(&set); + rc = pthread_sigmask(SIG_BLOCK, &set, NULL); + if (rc != 0) { + /* TODO: determine if we need to exit this thread if we fail + * to set the signal mask */ + LOG_ERR(PFX "%s: Couldn't set signal mask", nic->log_name); + } + + /* Signal the device to enable itself */ + pthread_mutex_lock(&nic->nic_mutex); + pthread_cond_signal(&nic->nic_loop_started_cond); + + /* nic_mutex must be locked */ + while ((event_loop_stop == 0) && + !(nic->flags & NIC_EXIT_MAIN_LOOP) && + !(nic->flags & NIC_GOING_DOWN)) { + nic_interface_t *nic_iface, *vlan_iface; + + if (nic->flags & NIC_DISABLED) { + LOG_DEBUG(PFX "%s: Waiting to be enabled", + nic->log_name); + + /* Wait for the device to be enabled */ + /* nic_mutex is already locked */ + pthread_cond_wait(&nic->enable_wait_cond, + &nic->nic_mutex); + + if (nic->state == NIC_EXIT) { + pthread_mutex_unlock(&nic->nic_mutex); + pthread_exit(NULL); + } + LOG_DEBUG(PFX "%s: is now enabled", nic->log_name); + } + /* initialize the device to send/rec data */ + rc = (*nic->ops->open) (nic); + if (rc != 0) { + LOG_ERR(PFX "%s: Could not initialize CNIC UIO device", + nic->log_name); + + if (rc == -ENOTSUP) + nic->flags |= NIC_EXIT_MAIN_LOOP; + else + nic->flags &= ~NIC_ENABLED; + + /* Signal that the device enable is done */ + pthread_cond_broadcast(&nic->enable_done_cond); + pthread_mutex_unlock(&nic->nic_mutex); + goto dev_close; + } + nic_set_all_nic_iface_mac_to_parent(nic); + pthread_mutex_unlock(&nic->nic_mutex); + + rc = alloc_free_queue(nic, 5); + if (rc != 5) { + if (rc != 0) { + LOG_WARN(PFX "%s: Allocated %d packets " + "instead of %d", nic->log_name, rc, 5); + } else { + LOG_ERR(PFX "%s: No packets allocated " + "instead of %d", nic->log_name, 5); + /* Signal that the device enable is done */ + pthread_cond_broadcast(&nic->enable_done_cond); + goto dev_close; + } + } + /* Indication for the nic_disable routine that the nic + has started running */ + nic->state = NIC_STARTED_RUNNING; + + /* Initialize the system clocks */ + timer_set(&periodic_timer, CLOCK_SECOND / 2); + timer_set(&arp_timer, CLOCK_SECOND * 10); + + /* Prepare the stack for each of the VLAN interfaces */ + pthread_mutex_lock(&nic->nic_mutex); + + /* If DHCP fails, exit loop and restart the engine */ + nic_iface = nic->nic_iface; + while (nic_iface != NULL) { + if (nic_iface->flags & NIC_IFACE_ACQUIRE) { + do_acquisition(nic, nic_iface, + &periodic_timer, + &arp_timer); + } + vlan_iface = nic_iface->vlan_next; + while (vlan_iface != NULL) { + if (vlan_iface->flags & NIC_IFACE_ACQUIRE) { + do_acquisition(nic, vlan_iface, + &periodic_timer, + &arp_timer); + } + vlan_iface = vlan_iface->next; + } + nic_iface = nic_iface->next; + } + if (nic->flags & NIC_DISABLED) { + LOG_WARN(PFX "%s: nic was disabled during nic loop, " + "closing flag 0x%x", + nic->log_name, nic->flags); + /* Signal that the device enable is done */ + pthread_cond_broadcast(&nic->enable_done_cond); + pthread_mutex_unlock(&nic->nic_mutex); + goto dev_close_free; + } + + /* This is when we start the processing of packets */ + nic->start_time = time(NULL); + nic->state = NIC_RUNNING; + + nic->flags &= ~NIC_ENABLED_PENDING; + + /* Signal that the device enable is done */ + pthread_cond_broadcast(&nic->enable_done_cond); + pthread_mutex_unlock(&nic->nic_mutex); + + LOG_INFO(PFX "%s: entering main nic loop", nic->log_name); + + while ((nic->state == NIC_RUNNING) && + (event_loop_stop == 0) && + !(nic->flags & NIC_GOING_DOWN)) { + /* Check the periodic and ARP timer */ + check_timers(nic, &periodic_timer, &arp_timer); + rc = nic_process_intr(nic, 0); + while ((rc > 0) && + (nic->state == NIC_RUNNING) && + !(nic->flags & NIC_GOING_DOWN)) { + rc = process_packets(nic, + &periodic_timer, + &arp_timer, NULL); + } + } + + LOG_INFO(PFX "%s: exited main processing loop", nic->log_name); + +dev_close_free: + free_free_queue(nic); +dev_close: + pthread_mutex_lock(&nic->nic_mutex); + + if (nic->flags & NIC_GOING_DOWN) { + nic_close(nic, 1, FREE_NO_STRINGS); + + nic->flags &= ~NIC_GOING_DOWN; + } else { + pthread_mutex_destroy(&nic->xmit_mutex); + pthread_mutex_init(&nic->xmit_mutex, NULL); + } + nic->pending_count = 0; + + if (!(nic->flags & NIC_EXIT_MAIN_LOOP)) { + /* Signal we are done closing CNIC/UIO device */ + pthread_cond_broadcast(&nic->disable_wait_cond); + } + } + /* clean up the nic flags */ + nic->flags &= ~NIC_ENABLED_PENDING; + + pthread_mutex_unlock(&nic->nic_mutex); + + LOG_INFO(PFX "%s: nic loop thread exited", nic->log_name); + + nic->thread = INVALID_THREAD; + + pthread_exit(NULL); +} diff --git a/iscsiuio/src/unix/nic.h b/iscsiuio/src/unix/nic.h new file mode 100644 index 0000000..8484032 --- /dev/null +++ b/iscsiuio/src/unix/nic.h @@ -0,0 +1,384 @@ +/* + * Copyright (c) 2009-2011, Broadcom Corporation + * Copyright (c) 2014, QLogic Corporation + * + * Written by: Benjamin Li (benli@broadcom.com) + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam Dunkels. + * 4. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * nic.h - NIC header file + * + */ + +#include + +#ifndef __NIC_H__ +#define __NIC_H__ + +#include +#include +#include +#include +#include +#include + +#include "nic_nl.h" +#include "packet.h" +#include "uip.h" + +#include "iscsi_if.h" + +/* Foward declarations */ +struct nic_ops; +struct nic_lib_handle; +struct packet; +struct nic_op; + +extern pthread_mutex_t nic_lib_list_mutex; +extern struct nic_lib_handle *nic_lib_list; + +/* Used to store a list of active cnic devices */ +extern pthread_mutex_t nic_list_mutex; +extern struct nic *nic_list; + +extern void *nl_process_handle_thread(void *arg); + +/******************************************************************************* + * Constants + ******************************************************************************/ +#define MAX_PCI_DEVICE_ENTRIES 64 /* Maxium number of pci_device_id + entries a hw library may contain */ + +#define FREE_CONFIG_NAME 0x0001 +#define FREE_UIO_NAME 0x0002 +#define FREE_ALL_STRINGS (FREE_CONFIG_NAME | FREE_UIO_NAME) +#define FREE_NO_STRINGS 0x0000 + +/****************************************************************************** + * Enumerations + ******************************************************************************/ +typedef enum { + ALLOW_GRACEFUL_SHUTDOWN = 1, + FORCE_SHUTDOWN = 2, +} NIC_SHUTDOWN_T; + +/******************************************************************************* + * Structure used to hold PCI vendor, device, subvendor and subdevice ID's + ******************************************************************************/ +struct pci_device_id { + const uint32_t vendor, device; /* Vendor and device ID or PCI_ANY_ID */ + const uint32_t subvendor, subdevice; /* Subsystem ID's/PCI_ANY_ID */ + const char *device_name; /* Data private to the driver */ +}; + +/****************************************************************************** + * NIC statistics structure + ******************************************************************************/ +struct nic_stats { + uint64_t interrupts; + uint64_t missed_interrupts; + + struct { + uint64_t packets; + uint64_t bytes; + } tx; + + struct { + uint64_t packets; + uint64_t bytes; + } rx; +}; + +/****************************************************************************** + * NIC interface structure + ******************************************************************************/ +typedef struct nic_interface { + struct nic_interface *vlan_next; + struct nic_interface *next; + struct nic *parent; + + uint16_t protocol; + uint16_t flags; +#define NIC_IFACE_PERSIST (1<<0) +#define NIC_IFACE_ACQUIRE (1<<1) +#define NIC_IFACE_PATHREQ_WAIT1 (1<<2) +#define NIC_IFACE_PATHREQ_WAIT2 (1<<3) +#define NIC_IFACE_PATHREQ_WAIT (NIC_IFACE_PATHREQ_WAIT1 | \ + NIC_IFACE_PATHREQ_WAIT2) + uint8_t mac_addr[ETH_ALEN]; + uint8_t vlan_priority; + uint16_t vlan_id; +#define NO_VLAN 0x8000 + + uint16_t mtu; + time_t start_time; + + struct uip_stack ustack; + +#define IFACE_NUM_PRESENT (1<<0) +#define IFACE_NUM_INVALID -1 + int iface_num; + int request_type; +} nic_interface_t; + +/****************************************************************************** + * NIC lib operations structure + ******************************************************************************/ +struct nic_lib_ops { + /* Used to get the NIC library name */ + void (*get_library_name) (char **library_name, + size_t *library_name_size); + + /* Used to get to the PCI table supported by the NIC library */ + void (*get_pci_table) (struct pci_device_id **table, + uint32_t *entries); + + /* Used to get the version of this NIC library */ + void (*get_library_version) (char **version_string, + size_t *version_string_size); + + /* Used to get the NIC library build date */ + void (*get_build_date) (char **build_date_string, + size_t *build_date_string_size); + + /* Used to get the transport name assoicated with this library */ + void (*get_transport_name) (char **transport_name, + size_t *transport_name_size); + + /* Used to get the uio name assoicated with this library */ + void (*get_uio_name) (char **uio_name, size_t *uio_name_size); + +}; + +/******************************************************************************* + * NIC op table definition + ******************************************************************************/ +typedef struct nic_ops { + struct nic_lib_ops lib_ops; + + char *description; + int (*open) (struct nic *); + int (*close) (struct nic *, NIC_SHUTDOWN_T); + int (*read) (struct nic *, struct packet *); + int (*write) (struct nic *, nic_interface_t *, struct packet *); + void *(*get_tx_pkt) (struct nic *); + void (*start_xmit) (struct nic *, size_t, u16_t vlan_id); + int (*clear_tx_intr) (struct nic *); + int (*handle_iscsi_path_req) (struct nic *, + int, + struct iscsi_uevent *ev, + struct iscsi_path *path, + nic_interface_t *nic_iface); +} net_ops_t; + +typedef struct nic_lib_handle { + struct nic_lib_handle *next; + + pthread_mutex_t mutex; + struct nic_ops *ops; +} nic_lib_handle_t; + +typedef struct nic { + struct nic *next; + + uint32_t flags; +#define NIC_UNITIALIZED 0x0001 +#define NIC_INITIALIZED 0x0002 +#define NIC_ENABLED 0x0004 +#define NIC_DISABLED 0x0008 +#define NIC_IPv6_ENABLED 0x0010 +#define NIC_ADDED_MULICAST 0x0020 +#define NIC_LONG_SLEEP 0x0040 +#define NIC_PATHREQ_WAIT 0x0080 + +#define NIC_VLAN_STRIP_ENABLED 0x0100 +#define NIC_MSIX_ENABLED 0x0200 +#define NIC_TX_HAS_SENT 0x0400 +#define NIC_ENABLED_PENDING 0x0800 + +#define NIC_UIO_NAME_MALLOC 0x1000 +#define NIC_CONFIG_NAME_MALLOC 0x2000 +#define NIC_EXIT_MAIN_LOOP 0x4000 +#define NIC_GOING_DOWN 0x8000 +#define NIC_RESET_UIP 0x10000 + + uint16_t state; +#define NIC_STOPPED 0x0001 +#define NIC_STARTED_RUNNING 0x0002 +#define NIC_RUNNING 0x0004 +#define NIC_EXIT 0x0010 + + int fd; /* Holds the file descriptor to UIO */ + uint16_t uio_minor; /* Holds the UIO minor number */ + + uint32_t host_no; /* Holds the associated host number */ + + char *library_name; /* Name of the library to assoicate with */ + char *log_name; /* Human friendly name used in the log + file */ + char *config_device_name; /* Name read from the XML configuration + file */ + char eth_device_name[IFNAMSIZ]; /* Network interface name */ + char *uio_device_name; /* UIO device name */ + + uint32_t intr_count; /* Total UIO interrupt count */ + + int page_size; + + /* Held for nic ops manipulation */ + pthread_mutex_t nic_mutex; + + /* iSCSI ring ethernet MAC address */ + __u8 mac_addr[ETH_ALEN]; + + /* Used to manage the network interfaces of this device */ + __u32 num_of_nic_iface; + nic_interface_t *nic_iface; + + /* Wait for the device to be enabled */ + pthread_cond_t enable_wait_cond; + + /* Wait for the device to be finished enabled */ + pthread_cond_t enable_done_cond; + + /* Wait for the nic loop to start */ + pthread_cond_t nic_loop_started_cond; + + /* Wait for the device to be disabled */ + pthread_cond_t disable_wait_cond; + + /* Held when transmitting */ + pthread_mutex_t xmit_mutex; + + /* The thread this device is running on */ + pthread_t thread; + + /* The thread used to enable the device */ + pthread_t enable_thread; + + /* Statistical Information on this device */ + time_t start_time; + struct nic_stats stats; + + /* Number of retrys from iscsid */ + uint32_t pending_count; + uint32_t pathreq_pending_count; + +#define DEFAULT_RX_POLL_USEC 100 /* usec */ + /* options enabled by the user */ + uint32_t rx_poll_usec; + + /* Used to hold hardware specific data */ + void *priv; + + /* Used to hold the TX packets that are needed to be sent */ + struct packet *tx_packet_queue; + + /* Mutex to protect the list of free packets */ + pthread_mutex_t free_packet_queue_mutex; + + /* Used to hold the free packets that are needed to be sent */ + struct packet *free_packet_queue; + + /* Points to the NIC library */ + nic_lib_handle_t *nic_library; + + /* Points to the PCI table entry */ + struct pci_device_id *pci_id; + + /* Used to process the interrupt */ + int (*process_intr) (struct nic *nic); + + struct nic_ops *ops; + + /* NL processing parameters */ + pthread_t nl_process_thread; + pthread_cond_t nl_process_cond; + pthread_cond_t nl_process_if_down_cond; + pthread_mutex_t nl_process_mutex; + int nl_process_if_down; + int nl_process_head; + int nl_process_tail; +#define NIC_NL_PROCESS_MAX_RING_SIZE 128 +#define NIC_NL_PROCESS_LAST_ENTRY (NIC_NL_PROCESS_MAX_RING_SIZE - 1) +#define NIC_NL_PROCESS_NEXT_ENTRY(x) ((x + 1) & NIC_NL_PROCESS_MAX_RING_SIZE) + void *nl_process_ring[NIC_NL_PROCESS_MAX_RING_SIZE]; +} nic_t; + +/****************************************************************************** + * Function Prototypes + *****************************************************************************/ +int load_all_nic_libraries(); + +nic_t *nic_init(); +void nic_add(nic_t *nic); +int nic_remove(nic_t *nic); + +int nic_add_nic_iface(nic_t *nic, nic_interface_t *nic_iface); +int nic_process_intr(nic_t *nic, int discard_check); + +nic_interface_t *nic_iface_init(); + +typedef enum { + NIC_LIBRARY_EXSITS = 1, + NIC_LIBRARY_DOESNT_EXIST = 2, +} NIC_LIBRARY_EXIST_T; + +NIC_LIBRARY_EXIST_T does_nic_uio_name_exist(char *name); +NIC_LIBRARY_EXIST_T does_nic_library_exist(char *name); + +/******************************************************************************* + * Packet management utility functions + ******************************************************************************/ +struct packet *get_next_tx_packet(nic_t *nic); +struct packet *get_next_free_packet(nic_t *nic); +void put_packet_in_tx_queue(struct packet *pkt, nic_t *nic); +void put_packet_in_free_queue(struct packet *pkt, nic_t *nic); + +int unload_all_nic_libraries(); +void nic_close(nic_t *nic, NIC_SHUTDOWN_T graceful, int clean); + +/* Use this function to fill in minor number and uio, and eth names */ +int nic_fill_name(nic_t *nic); + +int enable_multicast(nic_t *nic); +int disable_multicast(nic_t *nic); + +void nic_set_all_nic_iface_mac_to_parent(nic_t *nic); +int find_nic_lib_using_pci_id(uint32_t vendor, uint32_t device, + uint32_t subvendor, uint32_t subdevice, + nic_lib_handle_t **handle, + struct pci_device_id **pci_entry); + +void *nic_loop(void *arg); + +int nic_packet_capture(struct nic *, struct packet *pkt); + +#endif /* __NIC_H__ */ diff --git a/iscsiuio/src/unix/nic_id.c b/iscsiuio/src/unix/nic_id.c new file mode 100644 index 0000000..6b2467c --- /dev/null +++ b/iscsiuio/src/unix/nic_id.c @@ -0,0 +1,362 @@ +/* + * Copyright (c) 2009-2011, Broadcom Corporation + * Copyright (c) 2014, QLogic Corporation + * + * Written by: Benjamin Li (benli@broadcom.com) + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam Dunkels. + * 4. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * nic_id.c - Using sysfs to determine the PCI vendor, device, subvendor and + * subdevice ID's + * + */ +#include +#include +#include +#include +#include +#include + +#include "logger.h" +#include "nic.h" + +#define PFX "nic_id " + +/******************************************************************************* + * Sysfs constant strings used to get PCI vendor, and device ID's + ******************************************************************************/ +const char uio_vendor_id_template[] = "/sys/class/uio/uio%d/device/vendor"; +const char uio_subvendor_id_template[] = + "/sys/class/uio/uio%d/device/subsystem_vendor"; +const char uio_device_id_template[] = "/sys/class/uio/uio%d/device/device"; +const char uio_subdevice_id_template[] = + "/sys/class/uio/uio%d/device/subsystem_device"; +const char uio_device_symlink_template[] = "/sys/class/uio/uio%d/device"; + +/** + * get_id() - Utility function to read hex values from sysfs + * @param nic - NIC device to use + * @param sysfs_template - sysfs path template to use + * @param sysfs_template_size - sysfs path template size in bytes + * @parm id - this is the value returned from the sysfs entry + * @return 0 on success <0 on failure + */ +static int get_id(nic_t *nic, + const char *sysfs_template, + const size_t sysfs_template_size, uint32_t *id) +{ + int rc = 0; + FILE *fp; + size_t chars_read; + char buf[7]; + char *path; + size_t path_size; + + path_size = sysfs_template_size + 4; + path = malloc(path_size); + if (path == NULL) { + LOG_ERR("Could not allocate memory for %s", sysfs_template); + return -ENOMEM; + } + + snprintf(path, path_size, sysfs_template, nic->uio_minor); + + fp = fopen(path, "r"); + if (fp == NULL) { + LOG_ERR(PFX "%s: Could not open path: %s [%s]", + nic->log_name, path, strerror(errno)); + rc = -EIO; + goto error_fopen; + } + + chars_read = fread(buf, sizeof(buf), 1, fp); + if (chars_read != 1) { + LOG_ERR(PFX "%s: Could not read from: %s [%s]", + nic->log_name, path, strerror(ferror(fp))); + rc = -EIO; + goto error; + } + + chars_read = sscanf(buf, "%x", id); + if (chars_read != 1) { + LOG_ERR(PFX "%s: Could interpret value: %s from: %s [%s]", + nic->log_name, buf, path, strerror(errno)); + rc = -EIO; + goto error; + } + +error: + fclose(fp); + +error_fopen: + free(path); + + return rc; +} + +static int get_vendor(nic_t *nic, uint32_t *id) +{ + return get_id(nic, + uio_vendor_id_template, sizeof(uio_vendor_id_template), + id); +} + +static int get_subvendor(nic_t *nic, uint32_t *id) +{ + return get_id(nic, + uio_subvendor_id_template, + sizeof(uio_subvendor_id_template), id); +} + +static int get_device(nic_t *nic, uint32_t *id) +{ + return get_id(nic, + uio_device_id_template, + sizeof(uio_device_id_template), id); +} + +static int get_subdevice(nic_t *nic, uint32_t *id) +{ + return get_id(nic, + uio_subdevice_id_template, + sizeof(uio_subdevice_id_template), id); +} + +int get_bus_slot_func_num(nic_t *nic, + uint32_t *bus, uint32_t *slot, uint32_t *func) +{ + size_t size; + char *path, *tok, *tok2; + int path_tokens, i; + size_t path_size; + char *read_pci_bus_slot_func_str; + char pci_bus_slot_func_str[32]; + int rc; + char *saveptr; + + path_size = sizeof(uio_device_symlink_template) + 4; + path = malloc(path_size); + if (path == NULL) { + LOG_ERR(PFX "%s: Could not allocate path memory for %s", + nic->log_name, uio_device_symlink_template); + rc = -ENOMEM; + goto error_alloc_path; + } + + read_pci_bus_slot_func_str = malloc(128); + if (read_pci_bus_slot_func_str == NULL) { + LOG_ERR(PFX "%s: Could not allocate read pci bus memory for %s", + nic->log_name, uio_device_symlink_template); + rc = -ENOMEM; + goto error_alloc_read_pci_bus; + } + + snprintf(path, path_size, uio_device_symlink_template, nic->uio_minor); + + size = readlink(path, read_pci_bus_slot_func_str, 128); + if (size == -1) { + LOG_ERR(PFX "%s: Error with %s: %s", + nic->log_name, path, strerror(errno)); + rc = errno; + goto error; + } + + if (size > ((128) - 1)) { + read_pci_bus_slot_func_str[128 - 1] = '\0'; + LOG_ERR(PFX "%s: not enough space (%d) for reading PCI " + "slot:bus.func %s: %s", + nic->log_name, size, path, strerror(errno)); + rc = -EIO; + goto error; + } + + /* readlink() doesn't NULL terminate the string */ + read_pci_bus_slot_func_str[size] = '\0'; + + path_tokens = 0; + tok = strtok_r(read_pci_bus_slot_func_str, "/", &saveptr); + while (tok != NULL) { + path_tokens++; + tok = strtok_r(NULL, "/", &saveptr); + } + + size = readlink(path, read_pci_bus_slot_func_str, 128); + if (size == -1) { + LOG_ERR(PFX "%s: Error with %s: %s", + nic->log_name, path, strerror(errno)); + rc = errno; + goto error; + } + + if (size > ((128) - 1)) { + read_pci_bus_slot_func_str[128 - 1] = '\0'; + LOG_ERR(PFX "%s: not enough space for reading PCI " + "slot:bus.func %s: %s", + nic->log_name, path, strerror(errno)); + rc = -EIO; + goto error; + } + + /* readlink() doesn't NULL terminate the string */ + read_pci_bus_slot_func_str[size] = '\0'; + + tok = strtok_r(read_pci_bus_slot_func_str, "/", &saveptr); + for (i = 0; i < path_tokens - 1; i++) + tok = strtok_r(NULL, "/", &saveptr); + strcpy(pci_bus_slot_func_str, tok); + + tok = strtok_r(pci_bus_slot_func_str, ":", &saveptr); + if (tok == NULL) { + LOG_ERR(PFX "%s: Error with slot string: %s", + nic->log_name, pci_bus_slot_func_str); + rc = -EIO; + goto error; + } + + tok = strtok_r(NULL, ":", &saveptr); + if (tok == NULL) { + LOG_ERR(PFX "%s: Error parsing slot: %s", + nic->log_name, pci_bus_slot_func_str); + rc = -EIO; + goto error; + } + + sscanf(tok, "%x", bus); + + /* Need to extract the next token "xx.x" */ + tok = strtok_r(NULL, ":", &saveptr); + if (tok == NULL) { + LOG_ERR(PFX "%s: Error extracing bus.func: %s", + nic->log_name, pci_bus_slot_func_str); + rc = -EIO; + goto error; + } + + tok2 = strtok_r(tok, ".", &saveptr); + if (tok2 == NULL) { + LOG_ERR(PFX "%s: Error parsing bus: %s", + nic->log_name, pci_bus_slot_func_str); + rc = -EIO; + goto error; + } + + sscanf(tok2, "%x", slot); + + tok2 = strtok_r(NULL, ".", &saveptr); + if (tok2 == NULL) { + LOG_ERR(PFX "%s: Error parsing func: %s", + nic->log_name, pci_bus_slot_func_str); + rc = -EIO; + goto error; + } + + sscanf(tok2, "%x", func); + LOG_INFO(PFX "%s: is found at %02x:%02x.%02x", nic->log_name, + *bus, *slot, *func); + rc = 0; +error: + free(read_pci_bus_slot_func_str); +error_alloc_read_pci_bus: + free(path); +error_alloc_path: + return rc; +} + +/** + * find_set_nic_lib() - Match the NIC library to the NIC + * @param nic - NIC device to determine which NIC library to use + * @return 0 on success <0 on failure + */ +int find_set_nic_lib(nic_t *nic) +{ + uint32_t vendor; + uint32_t subvendor; + uint32_t device; + uint32_t subdevice; + + uint32_t pci_bus; + uint32_t pci_slot; + uint32_t pci_func; + int rc = 0; + + nic_lib_handle_t *handle; + struct pci_device_id *pci_entry; + + rc = get_vendor(nic, &vendor); + if (rc != 0) { + LOG_ERR(PFX "%s: Could not get vendor id [0x%x]", + nic->log_name, rc); + return rc; + } + + rc = get_subvendor(nic, &subvendor); + if (rc != 0) { + LOG_ERR(PFX "%s: Could not get subvendor id [0x%x]", + nic->log_name, rc); + return rc; + } + + rc = get_device(nic, &device); + if (rc != 0) { + LOG_ERR(PFX "%s: Could not get device id [0x%x]", + nic->log_name, rc); + return rc; + } + + rc = get_subdevice(nic, &subdevice); + if (rc != 0) { + LOG_ERR(PFX "%s: Could not get subdevice id [0x%x]", + nic->log_name, rc); + return rc; + } + + get_bus_slot_func_num(nic, &pci_bus, &pci_slot, &pci_func); + + LOG_DEBUG(PFX "%s: Looking for device vendor: " + "0x%x subvendor: 0x%x device: 0x%x subdevice: 0x%x", + nic->log_name, vendor, subvendor, device, subdevice); + + rc = find_nic_lib_using_pci_id(vendor, device, subvendor, subdevice, + &handle, &pci_entry); + + if (rc != 0) { + LOG_WARN(PFX "%s: Couldn't find proper NIC library", + nic->log_name); + return rc; + } + + nic->nic_library = handle; + nic->pci_id = pci_entry; + + /* Prepare the NIC library op table */ + nic->ops = handle->ops; + + return 0; +} diff --git a/iscsiuio/src/unix/nic_id.h b/iscsiuio/src/unix/nic_id.h new file mode 100644 index 0000000..340580f --- /dev/null +++ b/iscsiuio/src/unix/nic_id.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2009-2011, Broadcom Corporation + * Copyright (c) 2014, QLogic Corporation + * + * Written by: Benjamin Li (benli@broadcom.com) + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam Dunkels. + * 4. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * nic_id.h - NIC uIP NetLink user space stack + * + */ +#ifndef __NIC_ID_H__ +#define __NIC_ID_H__ + +int find_set_nic_lib(nic_t *nic); + +int get_bus_slot_func_num(nic_t *nic, + uint32_t *bus, uint32_t *slot, uint32_t *func); + +#endif /* __NIC_ID_H__ */ diff --git a/iscsiuio/src/unix/nic_nl.c b/iscsiuio/src/unix/nic_nl.c new file mode 100644 index 0000000..391003f --- /dev/null +++ b/iscsiuio/src/unix/nic_nl.c @@ -0,0 +1,678 @@ +/* + * Copyright (c) 2009-2011, Broadcom Corporation + * Copyright (c) 2014, QLogic Corporation + * + * Written by: Benjamin Li (benli@broadcom.com) + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam Dunkels. + * 4. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * nic_nl.c - NIC uIP NetLink user space stack + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "uip_arp.h" +#include "logger.h" +#include "options.h" + +#include "nic.h" +#include "nic_nl.h" +#include "nic_utils.h" + +/******************************************************************************* + * Constants + ******************************************************************************/ +#define PFX "NIC_NL " + +static u8_t nlm_sendbuf[NLM_BUF_DEFAULT_MAX]; + +static struct sockaddr_nl src_addr; + +static const struct sockaddr_nl dest_addr = { + .nl_family = AF_NETLINK, + .nl_pid = 0, /* kernel */ + .nl_groups = 0, /* unicast */ +}; + +#define POLL_NL 0 +#define POLL_MAX 1 + +/* Netlink */ +int nl_sock = INVALID_FD; + +static int nl_read(int ctrl_fd, char *data, int size, int flags) +{ + int rc; + struct iovec iov; + struct msghdr msg; + + iov.iov_base = data; + iov.iov_len = size; + + memset(&src_addr, 0, sizeof(src_addr)); + src_addr.nl_family = AF_NETLINK; + src_addr.nl_pid = getpid(); + src_addr.nl_groups = 1; + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = (void *)&src_addr; + msg.msg_namelen = sizeof(src_addr); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + rc = recvmsg(ctrl_fd, &msg, flags); + + return rc; +} + +static int +kwritev(int fd, enum iscsi_uevent_e type, struct iovec *iovp, int count) +{ + int i, rc; + struct nlmsghdr *nlh; + struct msghdr msg; + struct iovec iov; + int datalen = 0; + + for (i = 0; i < count; i++) + datalen += iovp[i].iov_len; + + nlh = (struct nlmsghdr *)nlm_sendbuf; + memset(nlh, 0, NLMSG_SPACE(datalen)); + + nlh->nlmsg_len = NLMSG_SPACE(datalen); + nlh->nlmsg_pid = getpid(); + nlh->nlmsg_flags = 0; + nlh->nlmsg_type = type; + + datalen = 0; + for (i = 0; i < count; i++) { + memcpy(NLMSG_DATA(nlh) + datalen, iovp[i].iov_base, + iovp[i].iov_len); + datalen += iovp[i].iov_len; + } + iov.iov_base = (void *)nlh; + iov.iov_len = nlh->nlmsg_len; + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = (void *)&dest_addr; + msg.msg_namelen = sizeof(dest_addr); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + do { + rc = sendmsg(fd, &msg, 0); + if (rc == -ENOMEM) { + LOG_ERR(PFX "sendmsg: alloc_skb() failed"); + sleep(1); + } else if (rc < 0) { + LOG_ERR(PFX "sendmsg: bug?: on %d %s[0x%x]", + fd, strerror(errno), errno); + sleep(1); + } + } while ((rc < 0) && (event_loop_stop == 0)); + + return rc; +} + +/* + * __kipc_call() should never block. Therefore + * Netlink's xmit logic is serialized. This means we do not allocate on + * xmit path. Instead we reuse nlm_sendbuf buffer. + * + * Transport must assure non-blocking operations for: + * + * - session_create() + * - conn_create() + * - conn_bind() + * _ set_param() + * - conn_start() + * - conn_stop() + * + * Its OK to block for cleanup for short period of time in operatations for: + * + * - conn_destroy() + * - session_destroy() + * + * FIXME: interface needs to be extended to allow longer blocking on + * cleanup. (Dima) + */ +int __kipc_call(int fd, void *iov_base, int iov_len) +{ + int rc; + struct iovec iov; + struct iscsi_uevent *ev = iov_base; + enum iscsi_uevent_e type = ev->type; + + /* Sanity check */ + if (iov_base == NULL) + return -EINVAL; + + iov.iov_base = iov_base; + iov.iov_len = iov_len; + + rc = kwritev(fd, type, &iov, 1); + + return rc; +} + +static int pull_from_nl(char **buf) +{ + int rc; + size_t ev_size, payload_size, alloc_size; + char nlm_ev[NLMSG_SPACE(sizeof(struct iscsi_uevent))]; + struct nlmsghdr *nlh; + char *data = NULL; + struct iscsi_uevent *ev; + + /* Take a quick peek at what how much uIP will need to read */ + rc = nl_read(nl_sock, nlm_ev, + NLMSG_SPACE(sizeof(struct iscsi_uevent)), + MSG_PEEK | MSG_WAITALL); + if (rc <= 0) { + LOG_ERR("can not read nlm_ev, error %s[%d]", + strerror(errno), rc); + if (rc == 0) + return -EIO; + else + return errno; + } + nlh = (struct nlmsghdr *)nlm_ev; + + if (unlikely(nlh->nlmsg_len < NLMSG_ALIGN(sizeof(struct nlmsghdr)))) { + LOG_ERR(PFX "Invalid nlh->nlmsg_len length: " + "nlh->nlmsg_len(%d) < " + "NLMSG_ALIGN(sizeof(struct nlmsghdr))(%d)", + nlh->nlmsg_len, NLMSG_ALIGN(sizeof(struct nlmsghdr))); + return -EINVAL; + } + + ev = (struct iscsi_uevent *)NLMSG_DATA(nlh); + if (ev->type == ISCSI_KEVENT_PATH_REQ) { + ev_size = nlh->nlmsg_len - NLMSG_ALIGN(sizeof(struct nlmsghdr)); + payload_size = ev_size - sizeof(struct iscsi_uevent); + if (payload_size < sizeof(struct iscsi_path)) + alloc_size = nlh->nlmsg_len + (payload_size - + sizeof(struct iscsi_path)); + else + alloc_size = nlh->nlmsg_len; + } else { + alloc_size = nlh->nlmsg_len; + } + data = (char *)malloc(alloc_size); + if (unlikely(data == NULL)) { + LOG_ERR(PFX "Couldn't allocate %d bytes for Netlink " + "iSCSI message", alloc_size); + return -ENOMEM; + } + + memset(data, 0, alloc_size); + rc = nl_read(nl_sock, data, (int)nlh->nlmsg_len, MSG_WAITALL); + if (rc <= 0) { + LOG_ERR("can not read nlm_ev, error %s[%d]", + strerror(errno), rc); + if (rc == 0) + rc = -EIO; + else + rc = errno; + + goto error; + } + *buf = data; + return 0; +error: + if (data != NULL) + free(data); + + return rc; +} + +static const struct timespec ctldev_sleep_req = { + .tv_sec = 0, + .tv_nsec = 250000000, +}; + +static int ctldev_handle(char *data, nic_t *nic) +{ + int rc; + struct iscsi_uevent *ev; + uint8_t *payload; + struct iscsi_path *path; + char *msg_type_str; + int i; + nic_interface_t *nic_iface = NULL; + + ev = (struct iscsi_uevent *)NLMSG_DATA(data); + switch (ev->type) { + case ISCSI_KEVENT_PATH_REQ: + msg_type_str = "path_req"; + break; + default: + /* We don't care about other iSCSI Netlink messages */ + LOG_DEBUG(PFX "Received ev->type: 0x%x", ev->type); + rc = 0; + goto error; + } + + /* This is a message that drivers should be interested in */ + LOG_INFO(PFX "%s: Processing '%s'", nic->log_name, msg_type_str); + + payload = (uint8_t *) ((uint8_t *) ev) + sizeof(*ev); + path = (struct iscsi_path *)payload; + + if (ev->type == ISCSI_KEVENT_PATH_REQ) { + struct timespec sleep_rem; + nic_interface_t *vlan_iface; + uint16_t ip_type; + int iface_num, vlan_id; + + if (path->ip_addr_len == 4) + ip_type = AF_INET; + else if (path->ip_addr_len == 16) + ip_type = AF_INET6; + else + ip_type = 0; +#ifdef REQ_PATH_IFACE_NUM + /* Find the nic_iface to use */ + iface_num = ev->r.req_path.iface_num ? + ev->r.req_path.iface_num : IFACE_NUM_INVALID; +#else + iface_num = IFACE_NUM_INVALID; +#endif + vlan_id = path->vlan_id ? path->vlan_id : NO_VLAN; + + LOG_DEBUG(PFX "%s: PATH_REQ with iface_num %d VLAN %d", + nic->log_name, iface_num, vlan_id); + + pthread_mutex_lock(&nic->nic_mutex); + + nic_iface = nic_find_nic_iface(nic, ip_type, vlan_id, + iface_num, IP_CONFIG_OFF); + if (nic_iface == NULL) { + nic_iface = nic_find_nic_iface(nic, ip_type, + NO_VLAN, + IFACE_NUM_INVALID, + IP_CONFIG_OFF); + if (nic_iface == NULL) { + pthread_mutex_unlock(&nic->nic_mutex); + LOG_ERR(PFX "%s: Couldn't find nic iface parent" + " vlan: %d ip_type: %d " + "ip_addr_len: %d to clone", + nic->log_name, path->vlan_id, ip_type, + path->ip_addr_len); + goto error; + } + if (nic_iface->iface_num != IFACE_NUM_INVALID) { + /* New VLAN support: + Use the nic_iface found from the top + of the protocol family and ignore + the VLAN id from the path_req */ + if (!(nic_iface->iface_num == 0 && + nic_iface->vlan_id == 0 && + path->vlan_id)) { + pthread_mutex_unlock(&nic->nic_mutex); + goto nic_iface_done; + } + /* If iface_num == 0 and vlan_id == 0 but + the vlan_id from path_req is > 0, + then fallthru to the legacy support since + this is most likely from an older iscsid + (RHEL6.2/6.3 but has iface_num support) + */ + } + /* Legacy VLAN support: + This newly created nic_iface must inherit the + network parameters from the parent nic_iface + */ + LOG_DEBUG(PFX "%s: Created the nic_iface for vlan: %d " + "ip_type: %d", nic->log_name, path->vlan_id, + ip_type); + vlan_iface = nic_iface_init(); + if (vlan_iface == NULL) { + pthread_mutex_unlock(&nic->nic_mutex); + LOG_ERR(PFX "%s: Couldn't allocate " + "space for vlan: %d ip_type: " + "%d", nic->log_name, path->vlan_id, + ip_type); + goto error; + } + vlan_iface->protocol = ip_type; + vlan_iface->vlan_id = path->vlan_id; + nic_add_nic_iface(nic, vlan_iface); + + vlan_iface->ustack.ip_config = + nic_iface->ustack.ip_config; + memcpy(vlan_iface->ustack.hostaddr, + nic_iface->ustack.hostaddr, + sizeof(nic_iface->ustack.hostaddr)); + memcpy(vlan_iface->ustack.netmask, + nic_iface->ustack.netmask, + sizeof(nic_iface->ustack.netmask)); + memcpy(vlan_iface->ustack.netmask6, + nic_iface->ustack.netmask6, + sizeof(nic_iface->ustack.netmask6)); + memcpy(vlan_iface->ustack.hostaddr6, + nic_iface->ustack.hostaddr6, + sizeof(nic_iface->ustack.hostaddr6)); + + /* Persist so when nic_close won't call uip_reset + to nullify nic_iface->ustack */ + persist_all_nic_iface(nic); + + nic_iface = vlan_iface; + nic_iface->flags |= NIC_IFACE_ACQUIRE; + + pthread_mutex_unlock(&nic->nic_mutex); + + /* nic_disable but not going down */ + nic_disable(nic, 0); + } else { + pthread_mutex_unlock(&nic->nic_mutex); + } +nic_iface_done: + /* Force enable the NIC */ + if (nic->state == NIC_STOPPED) + nic_enable(nic); + + /* Ensure that the NIC is RUNNING */ + rc = -EIO; + for (i = 0; i < 10; i++) { + if (nic->state == NIC_RUNNING) { + rc = 0; + break; + } + + nanosleep(&ctldev_sleep_req, &sleep_rem); + } + + if (rc != 0) { + LOG_WARN(PFX "%s[vlan: %d protocol: %d]: not running, " + "cmd: 0x%x nic state: 0x%x flags: 0x%x", + nic->log_name, + nic_iface->vlan_id, nic_iface->protocol, + ev->type, nic->state, nic->flags); + goto error; + } + } + + if (nic->ops) { + switch (ev->type) { + case ISCSI_KEVENT_PATH_REQ: + /* pass the request up to the user space + * library driver */ + nic_iface->flags |= NIC_IFACE_PATHREQ_WAIT2; + nic_iface->flags &= ~NIC_IFACE_PATHREQ_WAIT1; + if (nic->ops->handle_iscsi_path_req) + nic->ops->handle_iscsi_path_req(nic, + nl_sock, ev, + path, + nic_iface); + nic_iface->flags &= ~NIC_IFACE_PATHREQ_WAIT; + pthread_mutex_lock(&nic->nic_mutex); + nic->flags &= ~NIC_PATHREQ_WAIT; + pthread_mutex_unlock(&nic->nic_mutex); + LOG_INFO(PFX "%s: 'path_req' operation finished", + nic->log_name); + + rc = 0; + break; + default: + rc = -EAGAIN; + break; + } + } + +error: + + return rc; +} + +/* NIC specific nl processing thread */ +void *nl_process_handle_thread(void *arg) +{ + int rc; + nic_t *nic = (nic_t *)arg; + + if (nic == NULL) + goto error; + + while (!event_loop_stop) { + char *data = NULL; + + rc = pthread_cond_wait(&nic->nl_process_cond, + &nic->nl_process_mutex); + if (rc != 0) { + LOG_ERR("Fatal error in NL processing thread " + "during wait[%s]", strerror(rc)); + break; + } + + data = nic->nl_process_ring[nic->nl_process_head]; + nic->nl_process_ring[nic->nl_process_head] = NULL; + nic->nl_process_tail = + NIC_NL_PROCESS_NEXT_ENTRY(nic->nl_process_tail); + + pthread_mutex_unlock(&nic->nl_process_mutex); + + if (data) { + ctldev_handle(data, nic); + free(data); + } + } +error: + return NULL; +} + +static void flush_nic_nl_process_ring(nic_t *nic) +{ + int i; + + for (i = 0; i < NIC_NL_PROCESS_MAX_RING_SIZE; i++) { + if (nic->nl_process_ring[i] != NULL) { + free(nic->nl_process_ring[i]); + nic->nl_process_ring[i] = NULL; + } + } + + nic->nl_process_head = 0; + nic->nl_process_tail = 0; + + LOG_DEBUG(PFX "%s: Flushed NIC NL ring", nic->log_name); +} + +/** + * nic_nl_open() - This is called when opening/creating the Netlink listening + * thread + * @param dev - CNIC UIO device to create a NetLink listener on + * @return 0 on success, <0 on failure + */ +int nic_nl_open() +{ + int rc; + char *msg_type_str; + + /* Prepare the thread to issue the ARP's */ + nl_sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ISCSI); + if (nl_sock < 0) { + LOG_ERR(PFX "can not create NETLINK_ISCSI socket [%s]", + strerror(errno)); + rc = -ENOMEM; + goto error; + } + + memset(&src_addr, 0, sizeof(src_addr)); + src_addr.nl_family = AF_NETLINK; + src_addr.nl_pid = getpid(); + src_addr.nl_groups = ISCSI_NL_GRP_UIP; + + while ((!event_loop_stop)) { + rc = bind(nl_sock, + (struct sockaddr *)&src_addr, sizeof(src_addr)); + if (rc == 0) + break; + + LOG_ERR(PFX "waiting binding to NETLINK_ISCSI socket"); + + sleep(1); + } + + if (event_loop_stop) { + rc = -EINVAL; + goto error; + } + + LOG_INFO(PFX "Netlink to CNIC on pid %d is ready", src_addr.nl_pid); + + while (!event_loop_stop) { + struct iscsi_uevent *ev; + char *buf = NULL; + uint32_t host_no; + nic_t *nic; + + rc = pull_from_nl(&buf); + if (rc != 0) + continue; + + /* Try to abort ARP'ing if a if_down was received */ + ev = (struct iscsi_uevent *)NLMSG_DATA(buf); + switch (ev->type) { + case ISCSI_KEVENT_IF_DOWN: + host_no = ev->r.notify_if_down.host_no; + msg_type_str = "if_down"; + break; + case ISCSI_KEVENT_PATH_REQ: + host_no = ev->r.req_path.host_no; + msg_type_str = "path_req"; + break; + default: + /* We don't care about other iSCSI Netlink messages */ + continue; + } + LOG_INFO(PFX "Received %s for host %d", msg_type_str, host_no); + + /* Make sure the nic list doesn't get yanked */ + pthread_mutex_lock(&nic_list_mutex); + + rc = from_host_no_find_associated_eth_device(host_no, &nic); + if (rc != 0) { + pthread_mutex_unlock(&nic_list_mutex); + LOG_ERR(PFX "Dropping msg, couldn't find nic with host " + "no: %d", host_no); + continue; + } + + /* Found the nic */ + if (nic->nl_process_thread == INVALID_THREAD) { + /* If thread is not valid, just drop it */ + pthread_mutex_unlock(&nic_list_mutex); + LOG_ERR(PFX "Dropping msg, nic nl process thread " + "not ready for host no: %d", host_no); + continue; + } + + if (ev->type == ISCSI_KEVENT_IF_DOWN) { + char eth_device_name[IFNAMSIZ]; + + pthread_mutex_lock(&nic->nl_process_mutex); + nic->nl_process_if_down = 1; + flush_nic_nl_process_ring(nic); + pthread_cond_broadcast(&nic->nl_process_if_down_cond); + pthread_mutex_unlock(&nic->nl_process_mutex); + + memcpy(eth_device_name, nic->eth_device_name, + sizeof(eth_device_name)); + + pthread_mutex_lock(&nic->nic_mutex); + nic->flags &= ~NIC_PATHREQ_WAIT; + nic->flags |= NIC_EXIT_MAIN_LOOP; + pthread_cond_broadcast(&nic->enable_done_cond); + pthread_mutex_unlock(&nic->nic_mutex); + + pthread_mutex_lock(&nic->nl_process_mutex); + nic->nl_process_if_down = 0; + pthread_mutex_unlock(&nic->nl_process_mutex); + + nic_disable(nic, 1); + + nic_remove(nic); + pthread_mutex_unlock(&nic_list_mutex); + + LOG_INFO(PFX "%s: 'if_down' operation finished", + eth_device_name); + continue; + } + + /* Place msg into the nic specific queue */ + pthread_mutex_lock(&nic->nl_process_mutex); + if ((nic->nl_process_head + 1 == nic->nl_process_tail) || + (nic->nl_process_tail == 0 && + nic->nl_process_head == NIC_NL_PROCESS_LAST_ENTRY)) { + pthread_mutex_unlock(&nic->nl_process_mutex); + pthread_mutex_unlock(&nic_list_mutex); + LOG_WARN(PFX "%s: No space on Netlink ring", + nic->log_name); + continue; + } + + nic->nl_process_ring[nic->nl_process_head] = buf; + nic->nl_process_head = + NIC_NL_PROCESS_NEXT_ENTRY(nic->nl_process_head); + pthread_cond_signal(&nic->nl_process_cond); + + pthread_mutex_unlock(&nic->nl_process_mutex); + + pthread_mutex_unlock(&nic_list_mutex); + + LOG_DEBUG(PFX "Pulled nl event"); + } + + LOG_INFO(PFX "Netlink thread exit'ing"); + rc = 0; + +error: + return 0; +} diff --git a/iscsiuio/src/unix/nic_nl.h b/iscsiuio/src/unix/nic_nl.h new file mode 100644 index 0000000..c68d81c --- /dev/null +++ b/iscsiuio/src/unix/nic_nl.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2009-2011, Broadcom Corporation + * Copyright (c) 2014, QLogic Corporation + * + * Written by: Benjamin Li (benli@broadcom.com) + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam Dunkels. + * 4. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * nic_nl.h - NIC uIP NetLink user space stack + * + */ + +#ifndef __NIC_NL_H__ +#define __NIC_NL_H__ + +#include + +int nic_nl_open(); +void nic_nl_close(); + +int __kipc_call(int fd, void *iov_base, int iov_len); + +extern pthread_cond_t nl_process_if_down_cond; +extern pthread_mutex_t nl_process_mutex; +extern int nl_process_if_down; + +#endif /* __NIC_NL_H__ */ diff --git a/iscsiuio/src/unix/nic_utils.c b/iscsiuio/src/unix/nic_utils.c new file mode 100644 index 0000000..d57cc4f --- /dev/null +++ b/iscsiuio/src/unix/nic_utils.c @@ -0,0 +1,1640 @@ +/* + * Copyright (c) 2009-2011, Broadcom Corporation + * Copyright (c) 2014, QLogic Corporation + * + * Written by: Benjamin Li (benli@broadcom.com) + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam Dunkels. + * 4. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * nic_util.c - shared NIC utility functions + * + */ +#include +#include +#include +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "logger.h" +#include "nic.h" +#include "nic_id.h" +#include "nic_vlan.h" +#include "nic_utils.h" +#include "options.h" + +#define PFX "nic_utils " + +/****************************************************************************** + * String constants + *****************************************************************************/ +static const char nic_uio_sysfs_name_tempate[] = "/sys/class/uio/uio%i/name"; +static const char cnic_sysfs_uio_event_template[] = + "/sys/class/uio/uio%d/event"; +static const char base_uio_sysfs_name[] = "/sys/class/uio/"; +static const char uio_name[] = "uio"; + +static const char uio_base_dir[] = "/dev/uio"; +static const char uio_udev_path_template[] = "/dev/uio%hd"; +static const char uio_uevent_path_template[] = "/sys/class/uio/uio%d/uevent"; + +static const char base_iscsi_host_name[] = "/sys/class/iscsi_host/"; +static const char host_template[] = "host%d"; +static const char iscsi_host_path_template[] = "/sys/class/iscsi_host/host%d"; +static const char iscsi_host_path_netdev_template[] = + "/sys/class/iscsi_host/host%d/netdev"; +static const char cnic_uio_sysfs_resc_template[] = + "/sys/class/uio/uio%i/device/resource%i"; + +/** + * manually_trigger_uio_event() - If the uio file node doesn't exist then + * try to retrigger udev to create the file + * node by touch the uevent file in sysfs + * @param nic - the nic to trigger on + * @param uio_minor - UIO the minor number to use + * @return 0 on success + */ +int manually_trigger_uio_event(nic_t *nic, int uio_minor) +{ + int fd; + char uio_uevent_path[sizeof(uio_uevent_path_template) + 10]; + char enable_str[] = "online"; + int rc; + size_t bytes_wrote; + + rc = sprintf(uio_uevent_path, uio_uevent_path_template, uio_minor); + if (rc < 0) { + LOG_ERR(PFX "%s: Could not build uio uevent path", + nic->log_name); + return -EIO; + } + + LOG_DEBUG(PFX "%s: triggering UIO uevent path: %s", + nic->log_name, uio_uevent_path); + + fd = open(uio_uevent_path, O_WRONLY); + if (fd == -1) { + LOG_ERR(PFX "%s: Could not open uio uevent path: %s [%s]", + nic->log_name, uio_uevent_path, strerror(errno)); + return -EIO; + } + + bytes_wrote = write(fd, enable_str, sizeof(enable_str)); + if (bytes_wrote != sizeof(enable_str)) { + LOG_ERR(PFX "%s: Could write to uio uevent path: %s [%s]", + nic->log_name, uio_uevent_path, strerror(errno)); + rc = -EIO; + } else + rc = 0; + + close(fd); + return rc; +} + +static int wait_for_file_node_timed(nic_t *nic, char *filepath, int seconds) +{ + struct timeval start_time; + struct timeval wait_time; + struct timeval total_time; + struct timespec sleep_req, sleep_rem; + + sleep_req.tv_sec = 0; + sleep_req.tv_nsec = 250000000; + + wait_time.tv_sec = seconds; + wait_time.tv_usec = 0; + + if (gettimeofday(&start_time, NULL)) { + LOG_ERR(PFX "%s: Couldn't gettimeofday() during watch file: %s" + "[%s]", nic->log_name, filepath, strerror(errno)); + return -EIO; + } + + timeradd(&start_time, &wait_time, &total_time); + + while (1) { + struct timeval current_time; + struct stat file_stat; + + /* Check if the file node exists */ + if (stat(filepath, &file_stat) == 0) + return 0; + + if (gettimeofday(¤t_time, NULL)) { + LOG_ERR(PFX "%s: Couldn't get current time for " + "watching file: %s [%s]", + nic->log_name, filepath, strerror(errno)); + return -EIO; + } + + /* Timeout has excceded return -ETIME */ + if (timercmp(&total_time, ¤t_time, <)) { + LOG_ERR(PFX "%s: timeout waiting %d secs for file: %s", + nic->log_name, seconds, filepath); + return -ETIME; + } + + nanosleep(&sleep_req, &sleep_rem); + } +} + +/****************************************************************************** + * Autodiscovery of iscsi_hosts + *****************************************************************************/ +static int filter_host_name(const struct dirent *entry) +{ + if ((memcmp(entry->d_name, "host", 4) == 0)) + return 1; + else + return 0; +} + +int nic_discover_iscsi_hosts() +{ + struct dirent **files; + int count; + int i; + int rc; + + count = scandir(base_iscsi_host_name, &files, filter_host_name, + alphasort); + + switch (count) { + case 0: + /* Currently there are no iSCSI hosts */ + rc = 0; + break; + + case -1: + LOG_WARN(PFX "Error when scanning path: %s[%s]", + base_iscsi_host_name, strerror(errno)); + rc = -EINVAL; + break; + + default: + /* There are iSCSI hosts */ + pthread_mutex_lock(&nic_list_mutex); + for (i = 0; i < count; i++) { + int host_no; + char *raw = NULL; + uint32_t raw_size = 0; + char temp_path[sizeof(iscsi_host_path_netdev_template) + + 8]; + rc = sscanf(files[i]->d_name, host_template, &host_no); + nic_t *nic; + + LOG_INFO(PFX "Found host[%d]: %s", + host_no, files[i]->d_name); + + /* Build the path to determine netdev name */ + snprintf(temp_path, sizeof(temp_path), + iscsi_host_path_netdev_template, host_no); + + rc = capture_file(&raw, &raw_size, temp_path); + if (rc != 0) + continue; + + rc = from_host_no_find_associated_eth_device(host_no, + &nic); + if (rc != 0) { + /* Normalize the string */ + if (raw[raw_size - 1] == '\n') + raw[raw_size - 1] = '\0'; + + nic = nic_init(); + if (nic == NULL) { + LOG_ERR(PFX "Couldn't allocate " + "space for NIC %s " + "during scan", raw); + + rc = -ENOMEM; + break; + } + + strncpy(nic->eth_device_name, raw, raw_size); + nic->config_device_name = nic->eth_device_name; + nic->log_name = nic->eth_device_name; + + if (nic_fill_name(nic) != 0) { + free(nic); + free(raw); + rc = -EIO; + continue; + } + + nic_add(nic); + + LOG_INFO(PFX "NIC not found creating an " + "instance for host_no: %d %s", + host_no, nic->eth_device_name); + } else + LOG_INFO(PFX "%s: NIC found host_no: %d", + nic->log_name, host_no); + + free(raw); + } + pthread_mutex_unlock(&nic_list_mutex); + + /* Cleanup the scandir() call */ + for (i = 0; i < count; i++) + free(files[i]); + free(files); + + rc = 0; + break; + } + + return rc; +} + +/****************************************************************************** + * Enable/Disable Multicast on physical interface + *****************************************************************************/ +static int nic_util_enable_disable_multicast(nic_t *nic, uint32_t cmd) +{ + int rc = 0; + struct uip_eth_addr multicast_addr; + int fd; + struct ifreq ifr; + + /* adding ethernet multicast address for IPv6 */ + memcpy(&multicast_addr, nic->mac_addr, ETH_ALEN); + multicast_addr.addr[0] = 0x33; + multicast_addr.addr[1] = 0x33; + multicast_addr.addr[2] = 0xff; + + /* Prepare the request */ + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, nic->eth_device_name, + sizeof(nic->eth_device_name)); + memcpy(ifr.ifr_hwaddr.sa_data, multicast_addr.addr, ETH_ALEN); + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + LOG_ERR(PFX "%s: Couldn't create socket to %s " + "multicast address: %s", + nic->log_name, + cmd == SIOCADDMULTI ? "added" : "delete", + strerror(errno)); + return errno; + } + + rc = fcntl(fd, F_SETFL, O_NONBLOCK); + if (rc != 0) { + LOG_WARN("%s: Couldn't set to ethtool IOCTL to " + "non-blocking [%s]", nic->log_name, strerror(errno)); + } + + if (ioctl(fd, cmd, (char *)&ifr) != 0) { + LOG_ERR("%s: Couldn't issue ioctl socket to %s " + "multicast address: %s", + nic->log_name, + cmd == SIOCADDMULTI ? "add" : "delete", + strerror(errno)); + rc = errno; + goto error; + } + + LOG_INFO(PFX "%s: %s address %02x:%02x:%02x:%02x:%02x:%02x " + "to multicast list", + nic->log_name, + cmd == SIOCADDMULTI ? "Added" : "Deleted", + multicast_addr.addr[0], multicast_addr.addr[1], + multicast_addr.addr[2], multicast_addr.addr[3], + multicast_addr.addr[4], multicast_addr.addr[5]); + + if (cmd == SIOCADDMULTI) + nic->flags |= NIC_ADDED_MULICAST; + else + nic->flags &= ~NIC_ADDED_MULICAST; + +error: + close(fd); + + return rc; +} + +/** + * enable_multicast() - This fuction is used to enable + * the listening of multicast addresses for a given network interface + * @param nic - NIC device to enable multicast on + * @return 0 for success or <0 for failure + */ +int enable_multicast(nic_t *nic) +{ + return nic_util_enable_disable_multicast(nic, SIOCADDMULTI); +} + +/** + * disable_multicast() - This fuction is used to disable + * the listening of multicast addresses for a given network interface + * @param dev - NIC device to disable multicast on + * @return 0 for success or <0 for failure + */ +int disable_multicast(nic_t *nic) +{ + return nic_util_enable_disable_multicast(nic, SIOCDELMULTI); +} + +/******************************************************************************* + * Finding associated UIO/physical network interfaces + ******************************************************************************/ +static int filter_net_name(const struct dirent *entry) +{ + if ((memcmp(entry->d_name, "net:", 4) == 0)) + return 1; + else + return 0; +} + +static char *extract_net_name(struct dirent **files) +{ + return strstr(files[0]->d_name, ":"); +} + +static int filter_dot_out(const struct dirent *entry) +{ + if ((memcmp(entry->d_name, ".", 1) == 0)) + return 0; + else + return 1; +} + +static char *extract_none(struct dirent **files) +{ + return files[0]->d_name; +} + +/** + * from_host_no_find_nic() - Given the host number + * this function will try to find the assoicated nic interface + * Must be called with nic_list_mutex lock + * @param host_no - minor number of the UIO device + * @param nic - pointer to the NIC will set if successful + * @return 0 on success, <0 on error + */ +int from_host_no_find_associated_eth_device(int host_no, nic_t **nic) +{ + nic_t *current_nic = nic_list; + char *raw = NULL, *raw_tmp; + uint32_t raw_size = 0; + + char temp_path[sizeof(iscsi_host_path_netdev_template) + 8]; + int rc = -EIO; + + /* Build the path to determine uio name */ + snprintf(temp_path, sizeof(temp_path), + iscsi_host_path_netdev_template, host_no); + + rc = capture_file(&raw, &raw_size, temp_path); + if (rc != 0) + goto error; + + /* sanitize name string by replacing newline with null termination */ + raw_tmp = raw; + while (*raw_tmp != '\n' && raw_size--) + raw_tmp++; + *raw_tmp = '\0'; + + rc = -EIO; + + current_nic = nic_list; + while (current_nic != NULL) { + if (strcmp(raw, current_nic->eth_device_name) == 0) { + *nic = current_nic; + rc = 0; + break; + } + + current_nic = current_nic->next; + } + + free(raw); + +error: + return rc; +} + +/******************************************************************************* + * NIC packet handling functions + ******************************************************************************/ +/** + * from_uio_find_associated_eth_device() - Given the uio minor number + * this function will try to find the assoicated phyisical network + * interface + * @param uio_minor - minor number of the UIO device + * @param name - char buffer which will be filled if successful + * @param name_size - size of the name buffer + * @return >0 minor number <0 an error + */ +static int from_uio_find_associated_eth_device(nic_t *nic, + int uio_minor, + char *name, size_t name_size) +{ + char *path; + int rc; + int count; + struct dirent **files; + char *parsed_name; + int i; + int path_iterator; + char *search_paths[] = { "/sys/class/uio/uio%i/device/", + "/sys/class/uio/uio%i/device/net" + }; + int path_to[] = { 5, 1 }; + int (*search_filters[]) (const struct dirent *) = { + filter_net_name, filter_dot_out,}; + char *(*extract_name[]) (struct dirent **files) = { + extract_net_name, extract_none,}; + int extract_name_offset[] = { 1, 0 }; + + path = malloc(PATH_MAX); + if (path == NULL) { + LOG_ERR(PFX "Could not allocate memory for path"); + rc = -ENOMEM; + goto error; + } + + for (path_iterator = 0; + path_iterator < sizeof(search_paths) / sizeof(search_paths[0]); + path_iterator++) { + /* Build the path to determine uio name */ + rc = sprintf(path, search_paths[path_iterator], uio_minor); + + wait_for_file_node_timed(nic, path, path_to[path_iterator]); + + count = scandir(path, &files, + search_filters[path_iterator], alphasort); + + switch (count) { + case 1: + parsed_name = (*extract_name[path_iterator]) (files); + if (parsed_name == NULL) { + LOG_WARN(PFX "Couldn't find delimiter in: %s", + files[0]->d_name); + + break; + } + + strncpy(name, + parsed_name + + extract_name_offset[path_iterator], name_size); + + free(files[0]); + free(files); + + rc = 0; + break; + + case 0: + rc = -EINVAL; + break; + + case -1: + LOG_WARN(PFX "Error when scanning path: %s[%s]", + path, strerror(errno)); + rc = -EINVAL; + break; + + default: + LOG_WARN(PFX + "Too many entries when looking for device: %s", + path); + + /* Cleanup the scandir() call */ + for (i = 0; i < count; i++) + free(files[i]); + free(files); + + rc = -EINVAL; + break; + } + + if (rc == 0) + break; + } + +error: + free(path); + + return rc; +} + +/** + * filter_uio_name() - This is the callback used by scandir when looking for + * the number of uio entries + */ +static int filter_uio_name(const struct dirent *entry) +{ + /* Only return if the name of the file begins with 'uio' */ + if ((memcmp(entry->d_name, uio_name, sizeof(uio_name) - 1) == 0)) + return 1; + else + return 0; +} + +/** + * from_netdev_name_find_nic() - This is used to find the NIC device given + * the netdev name + * @param interface_name - name of the interface to search on + * @param nic - pointer of the pointer to the NIC + * @return 0 on success, <0 on failure + */ +int from_netdev_name_find_nic(char *interface_name, nic_t **nic) +{ + nic_t *current_nic; + + current_nic = nic_list; + while (current_nic != NULL) { + if (strcmp(interface_name, current_nic->eth_device_name) == 0) + break; + + current_nic = current_nic->next; + } + + if (current_nic == NULL) + return -EINVAL; + + *nic = current_nic; + return 0; +} + +/** + * from_phys_name_find_assoicated_uio_device() - This is used to find the + * uio minor + * when given a network interface name + * @param interface_name - network interface name to search for + * @return >0 minor number <0 an error + */ +int from_phys_name_find_assoicated_uio_device(nic_t *nic) +{ + char *path = NULL; + int count; + struct dirent **files; + int i; + int rc; + char *interface_name = nic->config_device_name; + + if (interface_name == NULL) + interface_name = nic->eth_device_name; + + /* Wait at least 10 seconds for uio sysfs entries to appear */ + rc = wait_for_file_node_timed(nic, (char *)base_uio_sysfs_name, 10); + if (rc != 0) + return rc; + + count = scandir(base_uio_sysfs_name, + &files, filter_uio_name, alphasort); + + switch (count) { + case 0: + LOG_WARN(PFX "Couldn't find %s to determine uio minor", + interface_name); + return -EINVAL; + + case -1: + LOG_WARN(PFX "Error when scanning for %s in path: %s [%s]", + interface_name, base_uio_sysfs_name, strerror(errno)); + return -EINVAL; + } + + path = malloc(PATH_MAX); + if (path == NULL) { + LOG_ERR(PFX "Could not allocate memory for path"); + return -ENOMEM; + } + + /* Run through the contents of the filtered files to see if the + * network interface name matches that of the uio device */ + for (i = 0; i < count; i++) { + int uio_minor; + char eth_name[IFNAMSIZ]; + + rc = sscanf(files[i]->d_name, "uio%d", &uio_minor); + if (rc != 1) { + LOG_WARN("Could not parse: %s", files[i]->d_name); + continue; + } + + rc = from_uio_find_associated_eth_device(nic, + uio_minor, + eth_name, + sizeof(eth_name)); + if (rc != 0) { + LOG_WARN("uio minor: %d not valid [%D]", uio_minor, rc); + continue; + } + + if (strncmp(eth_name, interface_name, sizeof(eth_name)) == 0) { + memcpy(nic->eth_device_name, + eth_name, sizeof(nic->eth_device_name)); + + LOG_INFO(PFX "%s associated with uio%d", + nic->eth_device_name, uio_minor); + + rc = uio_minor; + goto done; + } + } + + LOG_WARN("Could not find assoicate uio device with %s", interface_name); + + rc = -EINVAL; +done: + if (path != NULL) + free(path); + + for (i = 0; i < count; i++) + free(files[i]); + free(files); + + return rc; + +} + +/** + * nic_verify_uio_sysfs_name() - Using the name entry in sysfs it will try to + * match the NIC library name + * @param nic - The NIC hardware to check + * + */ +int nic_verify_uio_sysfs_name(nic_t *nic) +{ + char *raw = NULL, *raw_tmp; + uint32_t raw_size = 0; + char temp_path[sizeof(nic_uio_sysfs_name_tempate) + 8]; + int rc = 0; + + /* Build the path to determine uio name */ + snprintf(temp_path, sizeof(temp_path), + nic_uio_sysfs_name_tempate, nic->uio_minor); + + rc = capture_file(&raw, &raw_size, temp_path); + if (rc != 0) + goto error; + + /* sanitize name string by replacing newline with null termination */ + raw_tmp = raw; + while (*raw_tmp != '\n' && raw_size--) + raw_tmp++; + *raw_tmp = '\0'; + + /* If the nic library is not set then check if there is a library + * which matches the library name */ + if (nic->nic_library == NULL) { + NIC_LIBRARY_EXIST_T exist; + + exist = does_nic_uio_name_exist(raw); + if (exist == NIC_LIBRARY_DOESNT_EXIST) { + LOG_ERR(PFX "%s: could not find library: %s ", + nic->log_name, raw); + rc = -EIO; + } + } else { + char *library_name; + size_t library_name_size; + + /* Get the string name from the NIC library */ + (*nic->ops->lib_ops.get_library_name) (&library_name, + &library_name_size); + + if (strcmp(raw, library_name) != 0) { + LOG_ERR(PFX "%s: uio names not equal: " + "expecting %s got %s from %s", + nic->log_name, library_name, raw, temp_path); + rc = -EIO; + } + } + + free(raw); + + LOG_INFO(PFX "%s: Verified is a cnic_uio device", nic->log_name); + +error: + return rc; +} + +/** + * nic_fill_name() - This will initialize all the hardware resources underneath + * a struct cnic_uio device + * @param nic - The nic device to attach the hardware with + * @return 0 on success, on failure a errno will be returned + */ +int nic_fill_name(nic_t *nic) +{ + int rc; + + if ((nic->config_device_name != NULL) && + (memcmp(uio_base_dir, nic->config_device_name, + sizeof(uio_base_dir) - 1) == 0)) { + uint16_t uio_minor; + char eth_name[sizeof(nic->eth_device_name)]; + + wait_for_file_node_timed(nic, nic->config_device_name, 5); + + /* Determine the minor number for the UIO device */ + rc = sscanf(nic->config_device_name, uio_udev_path_template, + &uio_minor); + if (rc != 1) { + LOG_WARN(PFX "%s: Could not parse for minor number", + nic->uio_device_name); + return -EINVAL; + } else + nic->uio_minor = uio_minor; + + nic->uio_device_name = nic->config_device_name; + + /* Determine the assoicated physical network interface */ + rc = from_uio_find_associated_eth_device(nic, + nic->uio_minor, + eth_name, + sizeof(eth_name)); + if (rc != 0) { + LOG_WARN(PFX "%s: Couldn't find associated eth device", + nic->uio_device_name); + } else { + memcpy(nic->eth_device_name, + eth_name, sizeof(eth_name)); + } + + LOG_INFO(PFX "%s: configured for uio device for %s", + nic->log_name, nic->uio_device_name); + + } else { + LOG_INFO(PFX "looking for uio device for %s", + nic->config_device_name); + + rc = from_phys_name_find_assoicated_uio_device(nic); + if (rc < 0) { + LOG_ERR(PFX "Could not determine UIO name for %s", + nic->config_device_name); + + return -rc; + } + + nic->uio_minor = rc; + + if (nic->flags & NIC_UIO_NAME_MALLOC) + free(nic->uio_device_name); + + nic->uio_device_name = + malloc(sizeof(uio_udev_path_template) + 8); + if (nic->uio_device_name == NULL) { + LOG_INFO(PFX "%s: Couldn't malloc space for uio name", + nic->log_name); + return -ENOMEM; + } + + snprintf(nic->uio_device_name, + sizeof(uio_udev_path_template) + 8, + uio_udev_path_template, nic->uio_minor); + + nic->flags |= NIC_UIO_NAME_MALLOC; + } + + return 0; +} + +void cnic_get_sysfs_pci_resource_path(nic_t *nic, int resc_no, + char *sys_path, size_t size) +{ + /* Build the path to sysfs pci resource */ + snprintf(sys_path, size, + cnic_uio_sysfs_resc_template, nic->uio_minor, resc_no); + +} + +void prepare_library(nic_t *nic) +{ + int rc; + NIC_LIBRARY_EXIST_T exist; + + nic_fill_name(nic); + + /* No assoicated library, we can skip it */ + if (nic->library_name != NULL) { + /* Check that we have the proper NIC library loaded */ + exist = does_nic_library_exist(nic->library_name); + if (exist == NIC_LIBRARY_DOESNT_EXIST) { + LOG_ERR(PFX "NIC library doesn't exists: %s", + nic->library_name); + goto error; + } + } + + /* Determine the NIC library to use based on the PCI Id */ + rc = find_set_nic_lib(nic); + if (rc != 0) { + LOG_ERR(PFX "%s: Couldn't find NIC library", nic->log_name); + goto error; + } + + LOG_INFO("%s: found NIC '%s'", nic->log_name, nic->pci_id->device_name); +error: + return; +} + +void prepare_nic_thread(nic_t *nic) +{ + pthread_attr_t attr; + int rc; + + pthread_mutex_lock(&nic->nic_mutex); + if (nic->thread == INVALID_THREAD) { + struct timespec ts; + struct timeval tp; + + LOG_INFO(PFX "%s: spinning up thread for nic", nic->log_name); + + /* Try to spin up the nic thread */ + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + rc = pthread_create(&nic->thread, &attr, nic_loop, nic); + if (rc != 0) { + LOG_ERR(PFX "%s: Couldn't create thread for nic", + nic->log_name); + goto error; + } + + /* Convert from timeval to timespec */ + rc = gettimeofday(&tp, NULL); + ts.tv_sec = tp.tv_sec; + ts.tv_nsec = tp.tv_usec * 1000; + ts.tv_sec += 5; /* TODO: hardcoded wait for 5 seconds */ + + /* Wait for the nic loop thread to to running */ + rc = pthread_cond_timedwait(&nic->nic_loop_started_cond, + &nic->nic_mutex, &ts); + + LOG_INFO("Created nic thread: %s", nic->log_name); + } + + pthread_mutex_unlock(&nic->nic_mutex); + +error: + return; +} + +/******************************************************************************* + * Functions used to enable/disable the NIC + ******************************************************************************/ +/** + * nic_enable() - Function used to enable the NIC + * @param nic - NIC to enable + * @return 0 on success, <0 on failure + */ +int nic_enable(nic_t *nic) +{ + if (nic->flags & NIC_GOING_DOWN) { + LOG_INFO(PFX "%s: NIC device is going down, " + "flag: 0x%x state: 0x%x", + nic->log_name, nic->flags, nic->state); + return -EINVAL; + } + if (nic->state == NIC_STOPPED) { + struct timespec ts; + struct timeval tp; + int rc; + + pthread_mutex_lock(&nic->nic_mutex); + /* Signal the device to enable itself */ + pthread_cond_broadcast(&nic->enable_wait_cond); + + nic->flags &= ~NIC_DISABLED; + nic->flags |= NIC_ENABLED; + nic->flags |= NIC_ENABLED_PENDING; + + /* Convert from timeval to timespec */ + rc = gettimeofday(&tp, NULL); + ts.tv_sec = tp.tv_sec; + ts.tv_nsec = tp.tv_usec * 1000; + ts.tv_sec += 100; + + /* Wait for the device to be enabled */ + rc = pthread_cond_timedwait(&nic->enable_done_cond, + &nic->nic_mutex, &ts); + if (rc == 0 && nic->flags & NIC_ENABLED) { + LOG_DEBUG(PFX "%s: device enabled", nic->log_name); + } else { + nic->flags &= ~NIC_ENABLED; + nic->flags |= NIC_DISABLED; + nic->flags &= ~NIC_ENABLED_PENDING; + + LOG_ERR(PFX "%s: waiting to finish nic_enable err: %s", + nic->log_name, strerror(rc)); + } + pthread_mutex_unlock(&nic->nic_mutex); + + return rc; + } else { + LOG_INFO(PFX "%s: device already enabled: " + "flag: 0x%x state: 0x%x", + nic->log_name, nic->flags, nic->state); + return -EALREADY; + } +} + +/** + * nic_disable() - Function used to disable the NIC + * @param nic - NIC to disble + * @return void + */ +void nic_disable(nic_t *nic, int going_down) +{ + if (nic->state == NIC_STARTED_RUNNING || + nic->state == NIC_RUNNING) { + struct timespec ts; + struct timeval tp; + int rc; + + /* Wait for the device to be disabled */ + pthread_mutex_lock(&nic->nic_mutex); + + nic->flags &= ~NIC_ENABLED; + nic->flags |= NIC_DISABLED; + nic->flags &= ~NIC_STARTED_RUNNING; + nic->state = NIC_STOPPED; + + if (going_down) + nic->flags |= NIC_GOING_DOWN; + + /* Convert from timeval to timespec */ + rc = gettimeofday(&tp, NULL); + if (rc) { + LOG_ERR("gettimeofday failed, should never happen: %d\n", errno); + pthread_mutex_unlock(&nic->nic_mutex); + return; + } + + ts.tv_sec = tp.tv_sec; + ts.tv_nsec = tp.tv_usec * 1000; + ts.tv_sec += 5; /* TODO: hardcoded wait for 5 seconds */ + + /* Wait for the device to be disabled */ + rc = pthread_cond_timedwait(&nic->disable_wait_cond, + &nic->nic_mutex, &ts); + if (rc) { + LOG_ERR("cond_timedwait failed, should never happen: %d\n", errno); + } + + pthread_mutex_unlock(&nic->nic_mutex); + + LOG_DEBUG(PFX "%s: device disabled", nic->log_name); + + } else { + LOG_WARN(PFX "%s: device already disabled: " + "flag: 0x%x state: 0x%x", + nic->log_name, nic->flags, nic->state); + } +} + +void nic_close_all() +{ + nic_t *nic; + + pthread_mutex_lock(&nic_list_mutex); + + /* Start the shutdown process */ + nic = nic_list; + while (nic != NULL) { + pthread_mutex_lock(&nic->nic_mutex); + nic_close(nic, 1, FREE_ALL_STRINGS); + pthread_mutex_unlock(&nic->nic_mutex); + + nic = nic->next; + } + pthread_mutex_unlock(&nic_list_mutex); + + LOG_INFO(PFX "All NICs closed"); +} + +void nic_remove_all() +{ + nic_t *nic, *nic_next; + + pthread_mutex_lock(&nic_list_mutex); + + /* Start the shutdown process */ + nic = nic_list; + while (nic != NULL) { + nic_next = nic->next; + pthread_mutex_lock(&nic->nic_mutex); + nic_close(nic, 1, FREE_ALL_STRINGS); + pthread_mutex_unlock(&nic->nic_mutex); + nic_remove(nic); + nic = nic_next; + } + pthread_mutex_unlock(&nic_list_mutex); + + LOG_INFO(PFX "All NICs removed"); +} + + +/****************************************************************************** + * Routines to read initialized UIO values from sysfs + *****************************************************************************/ +/** + * determine_initial_uio_events() - This utility function will + * determine the number of uio events that have occured on the + * given device. This value is read from the UIO sysfs entry + * @param dev - device to read from + * @param num_of_event - number of UIO events + * @return 0 is success, <0 failure + */ +int detemine_initial_uio_events(nic_t *nic, uint32_t *num_of_events) +{ + char *raw = NULL; + uint32_t raw_size = 0; + ssize_t elements_read; + char temp_path[sizeof(cnic_sysfs_uio_event_template) + 8]; + int rc; + + /* Capture RX buffer size */ + snprintf(temp_path, sizeof(temp_path), + cnic_sysfs_uio_event_template, nic->uio_minor); + + rc = capture_file(&raw, &raw_size, temp_path); + if (rc != 0) + goto error; + + elements_read = sscanf(raw, "%d", num_of_events); + if (elements_read != 1) { + LOG_ERR(PFX "%s: Couldn't parse UIO events size from %s", + nic->log_name, temp_path); + rc = -EIO; + goto error; + } + + rc = 0; +error: + if (raw != NULL) + free(raw); + + return rc; +} + +/** + * nic_set_all_nic_iface_mac_to_parent() - This is a utility function used to + * intialize all the MAC addresses of the network interfaces for a given + * CNIC UIO device + * Call with nic mutex held + * @param dev - CNIC UIO device to initialize + */ +void nic_set_all_nic_iface_mac_to_parent(nic_t *nic) +{ + nic_interface_t *current, *vlan_current; + + current = nic->nic_iface; + while (current != NULL) { + /* Set the initial MAC address of this interface to the parent + * adapter */ + memcpy(current->mac_addr, nic->mac_addr, 6); + + vlan_current = current->vlan_next; + while (vlan_current != NULL) { + memcpy(vlan_current->mac_addr, nic->mac_addr, 6); + vlan_current = vlan_current->vlan_next; + } + current = current->next; + } +} + +/******************************************************************************* + * NIC packet handling functions + ******************************************************************************/ +/** + * nic_alloc_packet_buffer() - Used to allocate a packet buffer used to + * send a TX packet later + * @param nic - nic device to send the packet on + * @param nic_iface - nic interface to send out on + * @param buf - pointer to the buffer to send + * @param buf_size - size in bytes of the buffer to send + * @return pointer to the allocated packet buffer + * NULL if memory could not be allocated + */ +static packet_t *nic_alloc_packet_buffer(nic_t *nic, + nic_interface_t *nic_iface, + uint8_t *buf, size_t buf_size) +{ + packet_t *pkt; + + pkt = malloc(sizeof(*pkt) + buf_size); + if (pkt == NULL) { + LOG_ERR(PFX "%s: Couldn't allocate space for packet buffer", + nic->log_name); + return NULL; + } + + pkt->next = NULL; + pkt->nic = nic; + pkt->nic_iface = nic_iface; + pkt->buf_size = buf_size; + memcpy(pkt->buf, buf, buf_size); + + return pkt; +} + +/** + * nic_queue_tx_packet() - Used to queue a TX packet buffer to send later + * @param nic - NIC device to send the packet on + * @param nic_iface - NIC interface to send on the packet on + * @param pkt - packet to queue + * @return 0 if successful or <0 if unsuccessful + */ +int nic_queue_tx_packet(nic_t *nic, + nic_interface_t *nic_iface, packet_t *pkt) +{ + packet_t *queued_pkt; + + queued_pkt = nic_alloc_packet_buffer(nic, nic_iface, + pkt->buf, pkt->buf_size); + if (queued_pkt == NULL) { + LOG_ERR(PFX "%s: Couldn't allocate tx packet to queue", + nic->log_name); + return -ENOMEM; + } + + if (nic->tx_packet_queue == NULL) { + nic->tx_packet_queue = queued_pkt; + } else { + packet_t *current_pkt; + + current_pkt = nic->tx_packet_queue; + while (current_pkt->next != NULL) + current_pkt = current_pkt->next; + + current_pkt->next = queued_pkt; + } + + LOG_DEBUG(PFX "%s: tx packet queued", nic->log_name); + + return 0; +} + +/** + * nic_dequeue_tx_packet() - Used pop a TX packet buffer of the TX + * @param dev - cnic_uio device to send the packet on + * @param buf - pointer to the buffer to send + * @param buf_size - size in bytes of the buffer to send + * @return NULL if there are no more TX packet buffers to send + * pointer to the packet buffer which is detached from the device + */ +packet_t *nic_dequeue_tx_packet(nic_t *nic) +{ + packet_t *pkt; + + pkt = nic->tx_packet_queue; + + /* There is a packet buffer to send, time to detach it from the + * cnic_uio device */ + if (pkt != NULL) { + nic->tx_packet_queue = pkt->next; + pkt->next = NULL; + } + + return pkt; +} + +void nic_fill_ethernet_header(nic_interface_t *nic_iface, + void *data, + void *src_addr, void *dest_addr, + int *pkt_size, void **start_addr, + uint16_t ether_type) +{ + struct ether_header *eth; + uint16_t *vlan_hdr; + + eth = data; + + memcpy(eth->ether_shost, src_addr, ETH_ALEN); + memcpy(eth->ether_dhost, dest_addr, ETH_ALEN); + + vlan_hdr = (uint16_t *) (eth + 1); + eth->ether_type = htons(ether_type); + + *start_addr = vlan_hdr; +} + +/******************************************************************************* + * NIC interface management utility functions + ******************************************************************************/ +/** + * nic_find_nic_iface() - This function is used to find an interface + * from the NIC + * @param nic - NIC to look for network interfaces + * @param vlan_id - VLAN id to look for + * @param protocol - either AF_INET or AF_INET6 + * @param iface_num - iface num to use if present + * @param request_type - IPV4/6 DHCP/STATIC + * @return nic_iface - if found network interface with the given VLAN ID + * if not found a NULL is returned + */ +nic_interface_t *nic_find_nic_iface(nic_t *nic, + uint16_t protocol, + uint16_t vlan_id, + int iface_num, + int request_type) +{ + nic_interface_t *current = nic->nic_iface; + nic_interface_t *current_vlan = NULL; + + while (current != NULL) { + if (current->protocol != protocol) + goto next; + + /* Check for iface_num first */ + if (iface_num != IFACE_NUM_INVALID) { + if (current->iface_num == iface_num) { + /* Exception is when iface_num == 0, need to + check for request_type also if != + IP_CONFIG_OFF */ + if (!iface_num && request_type != + IP_CONFIG_OFF) { + if (current->request_type == + request_type) + goto found; + } else { + goto found; + } + } + } else if (vlan_id == NO_VLAN) { + /* Just return the top of the family */ + goto found; + } else { + if ((current->vlan_id == vlan_id) && + ((request_type == IP_CONFIG_OFF) || + (current->request_type == request_type))) + goto found; + } + /* vlan_next loop */ + current_vlan = current->vlan_next; + while (current_vlan != NULL) { + if (iface_num != IFACE_NUM_INVALID) { + if (current_vlan->iface_num == iface_num) { + if (!iface_num && request_type != + IP_CONFIG_OFF) { + if (current_vlan->request_type + == request_type) + goto vlan_found; + } else { + goto vlan_found; + } + } + } + if ((current_vlan->vlan_id == vlan_id) && + ((request_type == IP_CONFIG_OFF) || + (current_vlan->request_type == request_type))) + goto vlan_found; + + current_vlan = current_vlan->vlan_next; + } +next: + current = current->next; + } +vlan_found: + current = current_vlan; +found: + return current; +} + +/* Called with nic mutex held */ +void persist_all_nic_iface(nic_t *nic) +{ + nic_interface_t *current_vlan, *current; + + current = nic->nic_iface; + while (current != NULL) { + current->flags |= NIC_IFACE_PERSIST; + current_vlan = current->vlan_next; + while (current_vlan != NULL) { + current_vlan->flags |= NIC_IFACE_PERSIST; + current_vlan = current_vlan->vlan_next; + } + current = current->next; + } +} + +/* Sets the nic_iface to the front of the AF */ +void set_nic_iface(nic_t *nic, nic_interface_t *nic_iface) +{ + nic_interface_t *current, *prev; + nic_interface_t *current_vlan, *prev_vlan; + + prev = NULL; + current = nic->nic_iface; + while (current != NULL) { + if (current->protocol != nic_iface->protocol) + goto next; + /* If its already on top of the list, exit */ + if (current == nic_iface) + goto done; + + prev_vlan = current; + current_vlan = current->vlan_next; + + while (current_vlan != NULL) { + if (current_vlan == nic_iface) { + /* Found inside the vlan list */ + /* For vlan == 0, place on top of + the AF list */ + prev_vlan->vlan_next = + current_vlan->vlan_next; + current_vlan->vlan_next = current; + if (prev) + prev->next = current_vlan; + else + nic->nic_iface = current_vlan; + goto done; + } + prev_vlan = current_vlan; + current_vlan = current_vlan->vlan_next; + } +next: + prev = current; + current = current->next; + } +done: + return; +} + +/******************************************************************************* + * Packet management utility functions + ******************************************************************************/ +/** + * get_next_packet_in_queue() - This function will return the next packet in + * the queue + * @param queue - the queue to pull the packet from + * @return the packet in the queue + */ +static packet_t *get_next_packet_in_queue(packet_t **queue) +{ + packet_t *pkt; + + if (*queue == NULL) + return NULL; + + pkt = *queue; + *queue = pkt->next; + + return pkt; +} + +/** + * get_next_tx_packet() - This function will return the next packet in + * the TX queue + * @param nic - NIC to pull the TX packet from + * @return the packet in hte queue + */ +packet_t *get_next_tx_packet(nic_t *nic) +{ + return get_next_packet_in_queue(&nic->tx_packet_queue); +} + +/** + * get_next_free_packet() - This function will return the next packet in + * the free queue + * @param nic - NIC to pull the RX packet from + * @return the packet in hte queue + */ +packet_t *get_next_free_packet(nic_t *nic) +{ + packet_t *pkt; + pthread_mutex_lock(&nic->free_packet_queue_mutex); + pkt = get_next_packet_in_queue(&nic->free_packet_queue); + pthread_mutex_unlock(&nic->free_packet_queue_mutex); + + if (pkt != NULL) + reset_packet(pkt); + + return pkt; +} + +/** + * put_packet_in_queue() - This function will place the packet in the given + * queue + * @param pkt - the packet to place + * @param queue - the queue to place the packet + * @return the packet in the queue + */ +static void put_packet_in_queue(packet_t *pkt, packet_t **queue) +{ + if (*queue == NULL) + *queue = pkt; + else { + pkt->next = *queue; + *queue = pkt; + } +} + +/** + * put_packet_in_tx_queue() - This function will place the packet in + * the TX queue + * @param pkt - packet to place + * @param nic - NIC to pull the TX packet from + * @return the packet in hte queue + */ +void put_packet_in_tx_queue(packet_t *pkt, nic_t *nic) +{ + return put_packet_in_queue(pkt, &nic->tx_packet_queue); +} + +/** + * put_packet_in_free_queue() - This function will place the packet in + * the RX queue + * @param pkt - packet to place + * @param nic - NIC to pull the RX packet from + * @return the packet in hte queue + */ +void put_packet_in_free_queue(packet_t *pkt, nic_t *nic) +{ + pthread_mutex_lock(&nic->free_packet_queue_mutex); + put_packet_in_queue(pkt, &nic->free_packet_queue); + pthread_mutex_unlock(&nic->free_packet_queue_mutex); +} + +uint32_t calculate_default_netmask(uint32_t ip_addr) +{ + uint32_t netmask; + + if (IN_CLASSA(ntohl(ip_addr))) + netmask = htonl(IN_CLASSA_NET); + else if (IN_CLASSB(ntohl(ip_addr))) + netmask = htonl(IN_CLASSB_NET); + else if (IN_CLASSC(ntohl(ip_addr))) + netmask = htonl(IN_CLASSC_NET); + else { + LOG_ERR("Unable to guess netmask for address %x\n", &ip_addr); + return -1; + } + + return netmask; +} + +void dump_packet_to_log(struct nic_interface *iface, + uint8_t *buf, uint16_t buf_len) +{ + + FILE *file; + char str[80]; + int i, count; + + file = fmemopen(str, sizeof(str), "w+"); + if (file == NULL) { + LOG_ERR(PFX "Could not create logging file stream for packet " + "logging: [%d: %s]", errno, strerror(errno)); + return; + } + + LOG_PACKET(PFX "%s: Start packet dump len: %d", iface->parent->log_name, + buf_len); + + for (i = 0; i < buf_len; i++) { + rewind(file); + fprintf(file, "%03x: ", i); + + for (count = 0; (count < 8) && i < buf_len; count++, i++) + fprintf(file, " %02x", buf[i]); + fflush(file); + + LOG_PACKET(PFX "%s: %s", iface->parent->log_name, str); + } + + LOG_PACKET(PFX "%s: end packet dump", iface->parent->log_name); + + fclose(file); +} + +/******************************************************************************* + * File Management + ******************************************************************************/ + /** + * determine_file_size_read() - when fstat doesn't work on filepath + * within the /proc filesytem, we need to read/count the size of the file + * until we hit a EOF + * @parm filepath - path of the file in which to determine the filesize in + * bytes + * @return file size in bytes, <0 on failure + */ +int determine_file_size_read(const char *filepath) +{ + size_t total_size = 0; + ssize_t size = 1; + int fd; + char buf[1024]; + + fd = open(filepath, O_RDONLY); + if (fd == -1) { + LOG_ERR("Could not open file: %s [%s]", + filepath, strerror(errno)); + return -1; + } + + while (size > 0) { + size = read(fd, buf, sizeof(buf)); + + switch (size) { + case 0: + break; + case -1: + LOG_ERR("Error reading file: %s [%s]", + filepath, strerror(errno)); + total_size = -1; + break; + default: + total_size += size; + break; + } + } + + close(fd); + + return total_size; +} + +/** + * capture_file() - Used to capture a file into a buffer + * @param raw - This pointer will be set to the buffer which will hold the + * file contents + * @param raw_size - This is the size of the buffer returned + * @param path - The file path to capture the data from + * @return 0 is returned on success, <0 is returned on failure + */ +int capture_file(char **raw, uint32_t *raw_size, const char *path) +{ + FILE *fp; + size_t read_size; + int rc = 0; + int file_size; + + file_size = determine_file_size_read(path); + if (file_size < 0) { + LOG_ERR("Could not determine size %s", path); + return -EIO; + } + + fp = fopen(path, "r"); + if (fp == NULL) { + LOG_ERR("Could not open path %s [%s]", path, strerror(errno)); + return -EIO; + } + + *raw = malloc(file_size); + if (*raw == NULL) { + LOG_ERR("Could not malloc space for capture %s", path); + rc = -ENOMEM; + goto error; + } + + read_size = fread(*raw, file_size, 1, fp); + if (!read_size) { + LOG_ERR("Could not read capture, path: %s len: %d [%s]", + path, file_size, strerror(ferror(fp))); + free(*raw); + *raw = NULL; + rc = errno; + } else + *raw_size = file_size; + +error: + fclose(fp); + + LOG_INFO("Done capturing %s", path); + + return rc; +} diff --git a/iscsiuio/src/unix/nic_utils.h b/iscsiuio/src/unix/nic_utils.h new file mode 100644 index 0000000..d5c1b58 --- /dev/null +++ b/iscsiuio/src/unix/nic_utils.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2009-2011, Broadcom Corporation + * Copyright (c) 2014, QLogic Corporation + * + * Written by: Benjamin Li (benli@broadcom.com) + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam Dunkels. + * 4. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * nic_util.h - NIC utility functions + * + */ +#ifndef __NIC_UTILS_H__ +#define __NIC_UTILS_H__ + +#include "nic.h" + +/****************************************************************************** + * Function Prototype + ******************************************************************************/ +int manually_trigger_uio_event(nic_t *nic, int uio_minor); + +int nic_discover_iscsi_hosts(); + +int enable_mutlicast(nic_t *nic); +int disable_mutlicast(nic_t *nic); + +int from_netdev_name_find_nic(char *interface_name, nic_t **nic); + +int from_host_no_find_associated_eth_device(int host_no, nic_t **nic); + +int from_phys_name_find_assoicated_uio_device(nic_t *nic); + +int nic_queue_tx_packet(nic_t *nic, + nic_interface_t *nic_iface, packet_t *pkt); + +packet_t *nic_dequeue_tx_packet(nic_t *nic); + +void nic_fill_ethernet_header(nic_interface_t *nic_iface, + void *data, + void *src_addr, void *dest_addr, + int *pkt_size, void **start_addr, + uint16_t ether_type); + +struct nic_interface *nic_find_nic_iface(nic_t *nic, uint16_t protocol, + uint16_t vlan_id, int iface_num, + int request_type); +void set_nic_iface(nic_t *nic, nic_interface_t *nic_iface); + +void persist_all_nic_iface(nic_t *nic); + +int add_vlan_interfaces(nic_t *nic); + +int nic_verify_uio_sysfs_name(nic_t *nic); +void cnic_get_sysfs_pci_resource_path(nic_t *nic, int resc_no, + char *sys_path, size_t size); +void nic_close_all(); +void nic_remove_all(); + +int detemine_initial_uio_events(nic_t *nic, uint32_t *num_of_events); + +uint32_t calculate_default_netmask(uint32_t ip_addr); + +void prepare_nic_thread(nic_t *nic); +void prepare_library(nic_t *nic); + +int nic_enable(nic_t *nic); +void nic_disable(nic_t *nic, int going_down); + +void dump_packet_to_log(struct nic_interface *iface, + uint8_t *buf, uint16_t buf_len); + +int determine_file_size_read(const char *filepath); +int capture_file(char **raw, uint32_t *raw_size, const char *path); + +#endif /* __NIC_UTILS_H__ */ diff --git a/iscsiuio/src/unix/nic_vlan.c b/iscsiuio/src/unix/nic_vlan.c new file mode 100644 index 0000000..eb33381 --- /dev/null +++ b/iscsiuio/src/unix/nic_vlan.c @@ -0,0 +1,337 @@ +/* + * Copyright (c) 2009-2011, Broadcom Corporation + * Copyright (c) 2014, QLogic Corporation + * + * Written by: Benjamin Li (benli@broadcom.com) + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam Dunkels. + * 4. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * nic_vlan.c - uIP user space stack VLAN utilities + * + */ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "logger.h" +#include "nic.h" +#include "nic_utils.h" +#include "nic_vlan.h" + +/******************************************************************************* + * Constants + ******************************************************************************/ +#define PFX "vlan" + +static const char proc_vlan_config_path[] = "/proc/net/vlan/config"; + +/******************************************************************************* + * Resolving Found VLAN's for CNIC + ******************************************************************************/ +int init_vlan_found_handle(struct vlan_found_handle *found_handle, + struct vlan_handle *handle) +{ + memset(found_handle, 0, sizeof(*found_handle)); + + found_handle->entries = malloc(found_handle->num_of_entries * + sizeof(struct vlan_found_entry)); + if (found_handle->entries == NULL) { + LOG_ERR("Could not allocate space for found entries"); + return -ENOMEM; + } + + found_handle->handle = handle; + found_handle->num_of_entries = handle->num_of_entries; + + memset(found_handle->entries, 0, found_handle->num_of_entries * + sizeof(struct vlan_found_entry)); + + handle->outstanding_found_handles++; + + return 0; +} + +void release_vlan_found_handle(struct vlan_found_handle *found_handle) +{ + if (found_handle->entries != NULL) { + free(found_handle->entries); + found_handle->entries = NULL; + } + + found_handle->num_of_entries = 0; + + found_handle->handle->outstanding_found_handles--; + + found_handle->handle = NULL; + +} + +/******************************************************************************* + * Resolving VLAN's for CNIC + ******************************************************************************/ +/** + * init_vlan_handle() - Used to initialize struct ipv4_route_handle so + * that is can be used + * @param handle - Pointer to struct ipv4_route_handle to initialize + * @return 0 on success and <0 on failure + */ +void init_vlan_table(struct vlan_handle *handle) +{ + handle->entries = NULL; + handle->num_of_entries = 0; +} + +/** + * parse_vlan_table() - Given the raw dump of a Linux vlan table, this + * function will parse the into entries held by + * struct vlan_handle + * @param handle - struct vlan_handle used to hold the parsed contents + * @param raw - buffer to parse the contents from + * @param raw_size - size of the buffer in bytes + * @return 0 on success, <0 on failure + */ +int parse_vlan_table(struct vlan_handle *handle, char *raw, uint32_t raw_size) +{ + FILE *fp; + int i; + char *token; + size_t size; + int rc; + + token = raw; + + /* determine the number of entries */ + while (*token != '\0') { + if (*token == '\n') + handle->num_of_entries++; + + token++; + } + + /* There are 2 lines which describe the vlan table + * This lines need to be skipped with counting */ + handle->num_of_entries -= 2; + + LOG_INFO("Number of vlan entries: %d", handle->num_of_entries); + + size = handle->num_of_entries * sizeof(struct vlan_entry); + handle->entries = malloc(size); + if (handle->entries == NULL) { + LOG_ERR + ("Couldn't malloc space to parse vlan table. entires: %d " + "size: %d", + handle->num_of_entries, size); + return -ENOMEM; + } + + fp = fmemopen(raw, raw_size, "r"); + if (fp == NULL) { + LOG_ERR("Could not open raw dump of vlan table"); + rc = errno; + goto fmemopen_error; + } + + if (fscanf(fp, "%*[^\n]\n") < 0) { /* Skip the first line. */ + LOG_ERR("Empty or missing line, or read error"); + rc = -EIO; + goto error; + } + + if (fscanf(fp, "%*[^\n]\n") < 0) { /* Skip the second line. */ + LOG_ERR("Empty or missing line, or read error"); + rc = -EIO; + goto error; + } + + i = 0; + /* Time to parse the routing table */ + while (1) { + struct vlan_entry *entry = &handle->entries[i]; + int r; + + r = fscanf(fp, "%15s |%hu |%15s", + entry->vlan_iface_name, + &entry->vlan_id, entry->phy_iface_name); + if (r != 3) { + if (feof(fp)) { /* EOF with no (nonspace) chars read. */ + break; + } + + LOG_WARN("Parsing error: parsed %d elements", r); + break; + } + + i++; + + LOG_DEBUG("Vlan %d: vlan iface:%s vlan id:%d phys iface:%s", + i, + entry->vlan_iface_name, + entry->vlan_id, entry->phy_iface_name); + } + + fclose(fp); + + return 0; + +error: + fclose(fp); + +fmemopen_error: + if (handle->entries != NULL) + free(handle->entries); + + return rc; +} + +/** + * capture_vlan_table() - This function will snapshot the Linux vlan + * routing table for further processing + * @param handle - struct vlan_handle used to hold the routing context + * @return 0 on success, <0 on failure + */ +int capture_vlan_table(struct vlan_handle *handle) +{ + char *raw = NULL; + uint32_t raw_size = 0; + int rc; + + rc = capture_file(&raw, &raw_size, proc_vlan_config_path); + if (rc != 0) + goto error; + + rc = parse_vlan_table(handle, raw, raw_size); + if (rc != 0) + goto error; + +error: + if (raw != NULL) + free(raw); + + return rc; +} + +/** + * release_vlan_table() - This function will free all resources used by + * the handle + * @param handle - struct vlan_handle used to hold the routing context + */ +void release_vlan_table(struct vlan_handle *handle) +{ + if (handle->entries != NULL) { + free(handle->entries); + handle->entries = NULL; + } + + handle->num_of_entries = 0; +} + +/** + * find_phy_using_vlan_interface() - Given the interface name determine VLAN + * tag ID to match either the physical or VLAN interface name + * @param vlan_iface_name - VLAN interface used to find the physical + * interface + * @param phy_iface_name - returned value is the physical interface name + * @param vlan_id - returned value is the VLAN id + * @return 1 is returned if the interface is a VLAN, 0 if the interface is not + * <0 is returned if there is an error + */ +int find_phy_using_vlan_interface(struct vlan_handle *handle, + char *vlan_iface_name, + char **phy_iface_name, uint16_t *vlan_id) +{ + int i, rc = 0; + + for (i = 0; i < handle->num_of_entries; i++) { + struct vlan_entry *entry = &handle->entries[i]; + + /* Compare VLAN interface names to find a match */ + if (strcmp(entry->vlan_iface_name, vlan_iface_name) == 0) { + *phy_iface_name = entry->phy_iface_name; + *vlan_id = entry->vlan_id; + rc = 1; + break; + } + } + + return rc; +} + +/** + * find_vlans_using_phy_interface() - Given the physical interface name this + * function will determine the VLAN interface name and VLAN ID + * @param iface_name - physical interface used to find the vlan interface + * @param vlan_iface_name - returned value is the VLAN interface name + * @return The number of VLAN interfaces found + */ +int find_vlans_using_phy_interface(struct vlan_handle *handle, + struct vlan_found_handle *found_handle, + char *phy_iface_name) +{ + int i, num_found = 0; + + for (i = 0; i < handle->num_of_entries; i++) { + struct vlan_entry *entry = &handle->entries[i]; + + /* Compare interface names to find a match */ + if (strcmp(entry->phy_iface_name, phy_iface_name) == 0) { + found_handle->entries[i].found = VLAN_ENTRY_FOUND; + num_found++; + } + } + + return num_found; +} + +/** + * valid_vlan() - determine if the vlan value which is passed is valid + * @param vlan - vlan value to test + * @return 0 - not valid, 1 - valid + */ +int valid_vlan(short int vlan) +{ + /* Allow vlan 1 to connect */ + if (vlan > 0 && vlan < 4095) + return 1; + + return 0; +} diff --git a/iscsiuio/src/unix/nic_vlan.h b/iscsiuio/src/unix/nic_vlan.h new file mode 100644 index 0000000..610f721 --- /dev/null +++ b/iscsiuio/src/unix/nic_vlan.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2009-2011, Broadcom Corporation + * Copyright (c) 2014, QLogic Corporation + * + * Written by: Benjamin Li (benli@broadcom.com) + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam Dunkels. + * 4. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * nic_vlan.h - uIP user space stack VLAN utilities + * + */ +#ifndef __NIC_VLAN_H__ +#define __NIC_VLAN_H__ + +#include + +/* Used to hold entries in the vlan table */ +struct vlan_entry { + char vlan_iface_name[16]; + char phy_iface_name[16]; + uint16_t vlan_id; +}; + +struct vlan_handle { + struct vlan_entry *entries; + uint32_t num_of_entries; + + uint32_t outstanding_found_handles; +}; + +struct vlan_found_entry { +#define VLAN_ENTRY_FOUND 1 +#define VLAN_ENTRY_NOT_FOUND 0 + uint8_t found; +}; + +struct vlan_found_handle { + struct vlan_handle *handle; + uint32_t num_of_entries; + struct vlan_found_entry *entries; +}; + +/******************************************************************************* + * Function Prototypes + ******************************************************************************/ +void init_vlan_table(struct vlan_handle *handle); +int capture_vlan_table(struct vlan_handle *handle); +void release_vlan_table(struct vlan_handle *handle); + +int find_phy_using_vlan_interface(struct vlan_handle *handle, + char *vlan_iface_name, + char **phy_iface_name, uint16_t *vlan_id); +int find_vlans_using_phy_interface(struct vlan_handle *handle, + struct vlan_found_handle *found_handle, + char *phy_iface_name); + +int init_vlan_found_handle(struct vlan_found_handle *found_handle, + struct vlan_handle *handle); +void release_vlan_found_handle(struct vlan_found_handle *found_handle); + +int valid_vlan(short int vlan); +#endif /* __NIC_VLAN_H__ */ diff --git a/iscsiuio/src/unix/options.h b/iscsiuio/src/unix/options.h new file mode 100644 index 0000000..df03255 --- /dev/null +++ b/iscsiuio/src/unix/options.h @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2009-2011, Broadcom Corporation + * Copyright (c) 2014, QLogic Corporation + * + * Written by: Benjamin Li (benli@broadcom.com) + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam Dunkels. + * 4. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * options.h - CNIC UIO uIP user space stack + * + */ +#ifndef __OPTIONS_H__ +#define __OPTIONS_H__ + +#include +#include +#include + +/****************************************************************************** + * Constants which are tuned at compile time by the user + *****************************************************************************/ + +/** + * MAX_COUNT_NIC_NL_RESP - This is the maximum number of polls uIP will + * try for a kernel response after a PATH_REQ + */ +#define MAX_COUNT_NIC_NL_RESP 128 + +/** + * NLM_BUF_DEFAULT_MAX - This is the buffer size allocated for the send/receive + * buffers used by the uIP Netlink subsystem. This + * value is in bytes. + */ +#define NLM_BUF_DEFAULT_MAX 8192 /* bytes */ + +/****************************************************************************** + * Non adjustable constants + *****************************************************************************/ +#ifndef ETHERTYPE_IP +#define ETHERTYPE_IP 0x0800 /* IP */ +#endif /* ETHERTYPE_IP */ + +#ifndef ETHERTYPE_IPV6 +#define ETHERTYPE_IPV6 0x86dd /* IP protocol version 6 */ +#endif /* ETHERTYPE_IPV6 */ + +#ifndef ETHERTYPE_ARP +#define ETHERTYPE_ARP 0x0806 /* Address resolution */ +#endif /* ETHERTYPE_ARP */ + +#ifndef ETHERTYPE_VLAN +#define ETHERTYPE_VLAN 0x8100 /* IEEE 802.1Q VLAN tagging */ +#endif /* ETHERTYPE_VLAN */ + +#define APP_NAME "iscsiuio" +/* BUILD_DATE is automatically generated from the Makefile */ + +#define DEBUG_OFF 0x1 +#define DEBUG_ON 0x2 + +#define INVALID_FD -1 +#define INVALID_THREAD -1 + +struct options { + char debug; + + /* Time the userspace daemon was started */ + time_t start_time; +}; + +extern int event_loop_stop; +extern struct options opt; + +#ifdef WORDS_BIGENDIAN +#define ntohll(x) (x) +#define htonll(x) (x) +#else +#define ntohll(x) bswap_64(x) +#define htonll(x) bswap_64(x) +#endif + +# define likely(x) __builtin_expect(!!(x), 1) +# define unlikely(x) __builtin_expect(!!(x), 0) + +/* taken from Linux kernel, include/linux/compiler-gcc.h */ +/* Optimization barrier */ +/* The "volatile" is due to gcc bugs */ +#define barrier() __asm__ __volatile__("": : :"memory") + +#endif diff --git a/iscsiuio/src/unix/packet.c b/iscsiuio/src/unix/packet.c new file mode 100644 index 0000000..c0eeb1f --- /dev/null +++ b/iscsiuio/src/unix/packet.c @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2009-2011, Broadcom Corporation + * Copyright (c) 2014, QLogic Corporation + * + * Written by: Benjamin Li (benli@broadcom.com) + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam Dunkels. + * 4. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * packet.c - packet management + * + */ +#include +#include + +#include "logger.h" +#include "packet.h" +#include "nic.h" + +/** + * alloc_packet() - Function used to allocate memory for a packet + * @param max_buf_size - max packet size + * @param priv_size - size of the assoicated private data + * @return NULL if failed, on success return a pointer to the packet + */ +struct packet *alloc_packet(size_t max_buf_size, size_t priv_size) +{ + struct packet *pkt; + void *priv; + + pkt = malloc(max_buf_size + sizeof(struct packet)); + if (pkt == NULL) { + LOG_ERR("Could not allocate any memory for packet"); + return NULL; + } + + priv = malloc(priv_size); + if (priv == NULL) { + LOG_ERR("Could not allocate any memory for private structure"); + goto free_pkt; + } + + pkt->max_buf_size = max_buf_size; + pkt->priv = priv; + + return pkt; + +free_pkt: + free(pkt); + + return NULL; +} + +void free_packet(struct packet *pkt) +{ + if (pkt->priv != NULL) + free(pkt->priv); + + free(pkt); +} + +/** + * reset_packet() - This will reset the packet fields to default values + * @param pkt - the packet to reset + */ +void reset_packet(packet_t *pkt) +{ + pkt->next = NULL; + + pkt->flags = 0; + pkt->vlan_tag = 0; + + pkt->buf_size = 0; + + pkt->data_link_layer = NULL; + pkt->network_layer = NULL; +} + +int alloc_free_queue(nic_t *nic, size_t num_of_packets) +{ + int i; + + pthread_mutex_lock(&nic->free_packet_queue_mutex); + for (i = 0; i < num_of_packets; i++) { + packet_t *pkt; + + pkt = alloc_packet(1500, 1500); + if (pkt == NULL) { + goto done; + } + + reset_packet(pkt); + + pkt->next = nic->free_packet_queue; + nic->free_packet_queue = pkt; + } + +done: + pthread_mutex_unlock(&nic->free_packet_queue_mutex); + + return i; +} + +void free_free_queue(nic_t *nic) +{ + packet_t *pkt, *pkt_next; + + pthread_mutex_lock(&nic->free_packet_queue_mutex); + pkt = nic->free_packet_queue; + while (pkt) { + pkt_next = pkt->next; + free_packet(pkt); + pkt = pkt_next; + } + nic->free_packet_queue = NULL; + pthread_mutex_unlock(&nic->free_packet_queue_mutex); +} diff --git a/iscsiuio/src/unix/packet.h b/iscsiuio/src/unix/packet.h new file mode 100644 index 0000000..b63d688 --- /dev/null +++ b/iscsiuio/src/unix/packet.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2009-2011, Broadcom Corporation + * Copyright (c) 2014, QLogic Corporation + * + * Written by: Benjamin Li (benli@broadcom.com) + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam Dunkels. + * 4. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * packet.h - packet definitions + * + */ +#include + +#ifndef __PACKET_H__ +#define __PACKET_H__ + +#include "nic.h" + +struct nic; +struct nic_interface; + +typedef struct packet { + struct packet *next; + + uint32_t flags; +#define VLAN_TAGGED 0x0001 + uint16_t vlan_tag; + + size_t max_buf_size; + size_t buf_size; + + uint8_t *data_link_layer; + uint8_t *network_layer; + + struct nic *nic; + struct nic_interface *nic_iface; + + void *priv; + uint8_t buf[]; +} packet_t; + +/****************************************************************************** + * Packet Function Declarations + *****************************************************************************/ +int alloc_free_queue(struct nic *, size_t num_of_packets); +void free_free_queue(struct nic *); +void reset_packet(packet_t *pkt); + +#endif /* __PACKET_H__ */ diff --git a/iscsiuio/src/unix/uip-conf.h b/iscsiuio/src/unix/uip-conf.h new file mode 100644 index 0000000..e6e11a5 --- /dev/null +++ b/iscsiuio/src/unix/uip-conf.h @@ -0,0 +1,160 @@ +/** + * \addtogroup uipopt + * @{ + */ + +/** + * \name Project-specific configuration options + * @{ + * + * uIP has a number of configuration options that can be overridden + * for each project. These are kept in a project-specific uip-conf.h + * file and all configuration names have the prefix UIP_CONF. + */ + +/* + * Copyright (c) 2006, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack + * + */ + +/** + * \file + * An example uIP configuration file + * \author + * Adam Dunkels + */ + +#ifndef __UIP_CONF_H__ +#define __UIP_CONF_H__ + +#include + +/** + * 8 bit datatype + * + * This typedef defines the 8-bit type used throughout uIP. + * + * \hideinitializer + */ +typedef uint8_t u8_t; + +/** + * 16 bit datatype + * + * This typedef defines the 16-bit type used throughout uIP. + * + * \hideinitializer + */ +typedef uint16_t u16_t; + +/** + * 32 bit datatype + * + * This typedef defines the 16-bit type used throughout uIP. + * + * \hideinitializer + */ +typedef uint32_t u32_t; + +/** + * Statistics datatype + * + * This typedef defines the dataype used for keeping statistics in + * uIP. + * + * \hideinitializer + */ +typedef uint64_t uip_stats_t; + +/** + * Maximum number of TCP connections. + * + * \hideinitializer + */ +#define UIP_CONF_MAX_CONNECTIONS 40 + +/** + * Maximum number of listening TCP ports. + * + * \hideinitializer + */ +#define UIP_CONF_MAX_LISTENPORTS 40 + +/** + * uIP buffer size. + * + * \hideinitializer + */ +#define UIP_CONF_BUFFER_SIZE 420 + +/** + * CPU byte order. + * + * \hideinitializer + */ +#define UIP_CONF_BYTE_ORDER LITTLE_ENDIAN + +/** + * Logging on or off + * + * \hideinitializer + */ +#define UIP_CONF_LOGGING 1 + +/** + * UDP support on or off + * + * \hideinitializer + */ +#define UIP_CONF_UDP 1 + +/** + * UDP checksums on or off + * + * \hideinitializer + */ +#define UIP_CONF_UDP_CHECKSUMS 1 + +/** + * uIP statistics on or off + * + * \hideinitializer + */ +#define UIP_CONF_STATISTICS 1 + +#define UIP_CONF_IPV6 0 + +#define INET_ADDRSTRLEN 16 +#define INET6_ADDRSTRLEN 46 + +#endif /* __UIP_CONF_H__ */ + +/** @} */ +/** @} */ diff --git a/sysfs-documentation b/sysfs-documentation new file mode 100644 index 0000000..54fc497 --- /dev/null +++ b/sysfs-documentation @@ -0,0 +1,514 @@ +Description of iface attributes and their valid values +====================================================== + +== IPv4 attributes == + +ipaddress +--------- +IP address in format XXX.XXX.XXX.XXX + +gateway +------- +IP address of the network router or gateway device in format XXX.XXX.XXX.XXX + +subnet +------ +Broadcast address in format XXX.XXX.XXX.XXX + +bootproto +--------- +The protocol type used to initialize interface + +Valid values: "dhcp" or "static" + +dhcp_dns_address_en +------------------- +Request DNS Server IP Addresses and Domain Name + +If bootproto is set to dhcp and dhcp_dns_address_en is enable, +requests DNS addresses (option 6) and domain name (option 15) in its +DHCP parameter request list. + +Valid values: "enable" or "disable" + +dhcp_slp_da_info_en +------------------- +Request SLP DA Information and SLP Scope +If bootproto is set to dhcp and dhcp_slp_da_info_en is enable, +requests SLP DA information (option 78) and SLP scope (option 79) +in its DHCP parameter request list. + +Valid values: "enable" or "disable" + +tos_en +------ +Enable IPv4 type of service (ToS) + +When tos_en is set to enable, use value set in tos when transmitting IPv4 TCP +packets on iSCSI connections. + +Valid values: "enable" or "disable" + +tos +--- +IPv4 Type of service (ToS) + +When tos_en is set to enable, use value set in tos when transmitting IPv4 TCP +packets on iSCSI connections. + +Valid range: 8-bit value. [0-255] + +grat_arp_en +----------- +Enable Gratuitous ARP Requests + +Valid values: "enable" or "disable" + +dhcp_alt_client_id_en +--------------------- +DHCP Use Alternate Client ID + +When dhcp_alt_client_id_en is set to enable, use the Client ID configured in +dhcp_alt_client_id as its Client ID (DHCP option 61) in outgoing DHCP messages. + +Valid values: "enable" or "disable" + +dhcp_alt_client_id +------------------ +DHCP Alternate Client ID + +When dhcp_alt_client_id_en is set to enable, use value set in dhcp_alt_client_id +for Client ID in DHCP messages. + +Valid values: 11-byte Client ID + +dhcp_req_vendor_id_en +--------------------- +DHCP Require Vendor ID + +When dhcp_req_vendor_id_en is set to enable, use value set in dhcp_vendor_id as +its vendor ID (DHCP option 60) in outgoing DHCP messages. + +Valid values: "enable" or "disable" + +dhcp_use_vendor_id_en +--------------------- +DHCP Use Vendor ID + +When dhcp_use_vendor_id_en is set to enable, use value set in dhcp_vendor_id as +its vendor ID (DHCP option 60) in outgoing DHCP messages. + +Valid values: "enable" or "disable" + +dhcp_vendor_id +-------------- +DHCP Vendor ID + +When dhcp_req_vendor_id_en or dhcp_use_vendor_id_en is set to enable, +use value set in dhcp_vendor_id for Vendor ID in DHCP messages. + +Valid values: 11-byte Client ID + +dhcp_learn_iqn_en +----------------- +DHCP Learn IQN + +When dhcp_learn_iqn_en is set to enable, iSCSI initiator attempts to use DHCP +to learn its (IQN) iSCSI name. + +Valid values: "enable" or "disable" + +fragment_disable +---------------- +Fragmentation Disable. + +When fragment_disable is set to disable, iSCSI initiator cannot fragment IP +datagrams. + +Valid values: "enable" or "disable" + +incoming_forwarding_en +---------------------- +When incoming_forwarding_en is set to enable, iSCSI initiator forwards all +incoming network traffic to the network driver, except for iSCSI TCP packets +destined to the iSCSI initiator. + +Valid values: "enable" or "disable" + +ttl +--- +IPv4 Time to Live (TTL) + +This attribute contain TTL value sent in IPv4 TCP packets transmitted on +iSCSI connections. + +Valid range: 8-bit value. [0-255] + +== IPv6 attributes == + +ipaddress +--------- +IP address in IPv6 format. + +link_local_addr +--------------- +Link local address in IPv6 format. + +router_addr +----------- +Router address in IPv6 format. + +ipaddr_autocfg +-------------- +Autoconfigure IPv6 Address. + +Valid values: nd, dhcpv6 or disable +qla4xxx don't support dhcpv6. + +link_local_autocfg +------------------ +Autoconfigure IPv6 Link Local Address. + +IPv6 neighbor discovery protocol to discover Link Local Address. + +Valid values: auto or disable + + +router_autocfg +-------------- +Autoconfigure IPv6 Router address. + +IPv6 neighbor discovery protocol to discover a default router address. + +Valid values: auto or disable + +link_local_state +---------------- +This Read-only attribute show Link Local IP address state in sysfs. + +Valid values: Unconfigured, Acquiring, Tentative, Valid, Disabling, Invalid, + Deprecated. + + +router_state +------------ +This Read-only attribute shows router state. + +Valid values: Unknown, Advertised, Manual, Stale. + + +grat_neighbor_adv_en +-------------------- +Enable Gratuitious Neighbor Advertisement + +Valid values: "enable" or "disable" + +mld_en +------ +Enable IPv6 Multicast Listener Discovery + +Valid values: "enable" or "disable" + +flow_label +---------- +This attribute specifies the default value of the Flow Label field in the +IPv6 header of TCP packets transmitted on iSCSI connections + +Valid range: 20-bit value. [0-1048575] +Value zero indicates that the traffic is not assigned to a labelled flow. + +traffic_class +------------- +This attribute specifies the IPv6 traffic class value to be used in IPv6 +TCP packets transmitted from the firmware on iSCSI connections. + +Valid range: 8-bit value. [0-255] + +hop_limit +--------- +This attribute specifies the IPv6 hop limit value to be used in IPv6 TCP +packets transmitted from the firmware on iSCSI connections + +Valid range: 8-bit value. [0-255] + +nd_reachable_tmo +---------------- +This attribute specifies the time (in milliseconds) that a node assumes +that the neighbor is reachable after confirmation. + +Valid range: 4-byte value. [0-4294967295] + +nd_rexmit_time +-------------- +This attribute specifies the time (in milliseconds) between retransmitted +neighbor solicitation messages. + +Valid range: 4-byte value. [0-4294967295] + +nd_stale_tmo +------------ +This attribute specifies the time (in milliseconds) after which a stale +neighbor or destination cache entry is discarded. + +Valid range: 4-byte value. [0-4294967295] + +dup_addr_detect_cnt +------------------- +This attribute specifies the IPv6 duplicate address detection count + +Valid range: 8-bit value. [0-255] + 0 - Disable + 1 - TryOnce + 2 - TryTwice, and so on + +router_adv_link_mtu +------------------- +IPv6 Router Advertised Link MTU Size. + +Valid range: 1280 bytes to 1500 bytes + +== Common == +enabled +------- +This attribute is used to enable or disable IPv4 or IPv6 protocol. + +Valid values: "enable" or "disable" + +vlan_id +------- +This attribute specifies 12-bit VLAN identifier (VID) + +Valid range: 12-bit value. [1-4094] + +vlan_priority +------------- +This attribute specifies Priority to outbound packets containing the +specified VLAN-ID (VID) + +Valid range: 3-bit value. [0-7] + +vlan_enabled +------------ +VLAN Tagging Enable. + +When this attribute is set to enable, use value set in vlan_id and +vlan_priority to transmit IP packets, and discards IP packets that were +received without a matching VLAN ID + +Valid values: "enable" or "disable" + +mtu +--- +Ethernet MTU Size. + +This field specifies the maximum payload length in byte of an +Ethernet frame supported by iSCSI initiator. + +Valid values: 576 bytes to 9000 bytes + +port +---- +This attribute shows the initiator iSCSI port number. + +ipaddress_state +--------------- +This Read-only attribute show IP address state. + +Valid values: Unconfigured, Acquiring, Tentative, Valid, Disabling, Invalid, + Deprecated. + +delayed_ack_en +-------------- +When this attribute is set to enable, TCP delayed ACK is enabled. + +Valid values: "enable" or "disable" + +tcp_nagle_disable +----------------- +When this attribute is set to disable, TCP Nagle algorithm is disabled. + +Valid values: "enable" or "disable" + +tcp_wsf_disable +--------------- +When this attribute is set to disable, TCP window scale is disabled. + +Valid values: "enable" or "disable" + +tcp_wsf +------- +This attribute specifies the TCP window scale factor to be negotiated +on TCP connections. + +Valid range: 8-bit value. [0-255] + +tcp_timer_scale +--------------- +The TCP Timer Scale is scale factor that adjusts the time interval between +timer ticks on a TCP connection. The scale factor allows for faster time-outs +for connections running on a very small network, versus connections running +on a very large network. + +Valid range: 3-bit value. [0-7] + +tcp_timestamp_en +---------------- +When this attribute is set to enable, iSCSI initiator negotiates to use time +stamps in TCP headers + +Valid values: "enable" or "disable" + +cache_id +-------- +This Read-only attribute is used to find the valid cache entries for the +interface. + +For IPv4, ARP cache entry +For IPv6, Neighbor cache entry + +redirect_en +----------- +For IPv4: +When this attribute is set to enable, an ARP redirect can modify the address +resolution protocol (ARP) table and any active connections. + +For IPv6: +When this attribute is set to enable and neighbor advertisements are received, +the connection table is examined and updated if any active connections match +the IP address on the neighbor advertisement. This action is required for +failover and redirect. + +Valid values: "enable" or "disable" + +def_taskmgmt_tmo +---------------- +This attribute specifies timeout interval in seconds that iSCSI uses for +timing out task-management commands. + +Valid range: 16-bit value [0-65535]. + +header_digest +------------- +When this attribute is set to enable iSCSI initiator negotiates for +HeaderDigest=CRC32 and when set to disable negotiates HeaderDigest=none. + +Valid values: "enable" or "disable" + +data_digest +----------- +When this attribute is set to enable iSCSI initiator negotiates for +DataDigest=CRC32 and when set to disable negotiates DataDigest=none. + +Valid values: "enable" or "disable" + +immediate_data +-------------- +When this attribute is set to enable iSCSI initiator negotiates for +ImmediateData=yes and When set to disable negotiates ImmediateData=none + +Valid values: "enable" or "disable" + +initial_r2t +----------- +When this attribute is set to enable iSCSI initiator negotiates for +InitialR2T=yes. When set to disable negotiates InitialR2T=no. + +Valid values: "enable" or "disable" + +data_seq_in_order +----------------- +When this attribute is set to enable iSCSI initiator set data sequences +in order + +Valid values: "enable" or "disable" +qla4xxx does not support out-of-order data sequences + +data_pdu_in_order +----------------- +When this attribute is set to enable iSCSI initiator set Data PDU +in order + +Valid values: "enable" or "disable" +qla4xxx does not support out-of-order Data PDUs. + +erl +--- +Error Recovery Level + +This attribute specifies error recovery level (ERL) supported by the +connection. + +Valid values: 2-bit value [0-2] + +max_recv_dlength +---------------- +iSCSI Maximum Receive Data Segment Length. + +This attribute specifies Maximum data segment length in bytes, that receive +in an iSCSI PDU. + +first_burst_len +--------------- +iSCSI First Burst Length + +This attribute Specifies the maximum amount of unsolicited data an iSCSI +initiator can send to the target during the execution of a single SCSI command, +in bytes. + +max_outstanding_r2t +------------------- +iSCSI Maximum Outstanding R2T + +This attribute Specifies how many R2T PDUs per command can be outstanding +during an iSCSI session. + +max_burst_len +------------- +This attribute Specifies the maximum length for unsolicited or immediate data +iSCSI session can send or receive. + +chap_auth +--------- +When this attribute is set to enable iSCSI session performs authentication +during the security state of login phase. + +Valid values: "enable" or "disable" + +bidi_chap +--------- +When this attribute is set to enable iSCSI session generates a CHAP challenge +to any target that has issued a CHAP challenge to the iSCSI session. +iSCSI session issues the challenge to the target after responding to the +targets challenge. This attribute is ignored if chap_auth is set to disable. + +Valid values: "enable" or "disable" + +discovery_auth_optional +----------------------- +When this attribute is set to enable and the chap_auth is set to enable, +iSCSI session does not require authentication on discovery sessions unless +requested by the peer. When this attribute is set to disable iSCSI session +requires CHAP authentication for a discovery session. + +Valid values: "enable" or "disable" + +discovery_logout +---------------- +When this attribute is set to enable, iSCSI initiator initiates an iSCSI logout +on a discovery session when discovery is complete (before closing the connection). +When this attribute is set to disable, iSCSI initiator closes the connection when +discovery is complete. + +Valid values: "enable" or "disable" + +strict_login_comp_en +-------------------- +When this attribute is set to enable, iSCSI initiator enforces the iSCSI login +negotiation rules. When this attribute is set to disable, iSCSI initiator does +not enforce iSCSI login negotiation. + +Valid values: "enable" or "disable" + +initiator_name +-------------- +This Read-only attribute contains the iSCSI Name string used by the firmware. diff --git a/usr/Makefile b/usr/Makefile index 673b7f1..e23fee1 100644 --- a/usr/Makefile +++ b/usr/Makefile @@ -28,9 +28,9 @@ IPC_OBJ=ioctl.o endif endif -OPTFLAGS ?= -O2 -g +CFLAGS ?= -O2 -g WARNFLAGS ?= -Wall -Wstrict-prototypes -CFLAGS += $(OPTFLAGS) $(WARNFLAGS) -I../include -I. -I../utils/open-isns \ +CFLAGS += $(WARNFLAGS) -I../include -I. -I../utils/open-isns \ -D$(OSNAME) $(IPC_CFLAGS) PROGRAMS = iscsid iscsiadm iscsistart @@ -40,7 +40,8 @@ SYSDEPS_SRCS = $(wildcard ../utils/sysdeps/*.o) ISCSI_LIB_SRCS = iscsi_util.o io.o auth.o iscsi_timer.o login.o log.o md5.o \ sha1.o iface.o idbm.o sysfs.o host.o session_info.o iscsi_sysfs.o \ iscsi_net_util.o iscsid_req.o transport.o iser.o cxgbi.o be2iscsi.o \ - initiator_common.o iscsi_err.o $(IPC_OBJ) $(SYSDEPS_SRCS) + initiator_common.o iscsi_err.o flashnode.o uip_mgmt_ipc.o \ + $(IPC_OBJ) $(SYSDEPS_SRCS) # core initiator files INITIATOR_SRCS = initiator.o scsi.o actor.o event_poll.o mgmt_ipc.o kern_err_table.o @@ -54,14 +55,14 @@ all: $(PROGRAMS) iscsid: $(ISCSI_LIB_SRCS) $(INITIATOR_SRCS) $(DISCOVERY_SRCS) \ iscsid.o session_mgmt.o discoveryd.o - $(CC) $(CFLAGS) $^ -o $@ -L../utils/open-isns -lisns + $(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ -L../utils/open-isns -lisns -lrt -lmount iscsiadm: $(ISCSI_LIB_SRCS) $(DISCOVERY_SRCS) iscsiadm.o session_mgmt.o - $(CC) $(CFLAGS) $^ -o $@ -L../utils/open-isns -lisns + $(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ -L../utils/open-isns -lisns iscsistart: $(ISCSI_LIB_SRCS) $(INITIATOR_SRCS) $(FW_BOOT_SRCS) \ iscsistart.o statics.o - $(CC) $(CFLAGS) -static $^ -o $@ + $(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ -lrt -lmount clean: rm -f *.o $(PROGRAMS) .depend $(LIBSYS) diff --git a/usr/actor.c b/usr/actor.c index b8f8e61..b4f9e95 100644 --- a/usr/actor.c +++ b/usr/actor.c @@ -1,7 +1,8 @@ /* - * iSCSI usermode single-threaded scheduler + * iSCSI timeout & deferred work handling * * Copyright (C) 2004 Dmitry Yusupov, Alex Aizman + * Copyright (C) 2014 Red Hat Inc. * maintained by open-iscsi@googlegroups.com * * This program is free software; you can redistribute it and/or modify @@ -17,53 +18,32 @@ * See the file COPYING included with this distribution for more details. */ #include +#include +#include +#include +#include #include "actor.h" #include "log.h" #include "list.h" static LIST_HEAD(pend_list); -static LIST_HEAD(poll_list); -static LIST_HEAD(actor_list); -static volatile uint64_t previous_time; -static volatile uint32_t scheduler_loops; +static LIST_HEAD(ready_list); static volatile int poll_in_progress; -static volatile uint64_t actor_jiffies = 0; - -#define actor_diff(_time1, _time2) ({ \ - uint64_t __ret; \ - if ((_time2) >= (_time1)) \ - __ret = (_time2) - (_time1); \ - else \ - __ret = ((~0ULL) - (_time1)) + (_time2); \ - __ret; \ -}) - -#define ACTOR_TICKS actor_jiffies -#define ACTOR_TICKS_10MS(_a) (_a) -#define ACTOR_MS_TO_TICKS(_a) ((_a)/ACTOR_RESOLUTION) static uint64_t -actor_diff_time(actor_t *thread, uint64_t current_time) +actor_time_left(actor_t *thread, uint64_t current_time) { - uint64_t diff_time = actor_diff(thread->scheduled_at, current_time); - if(diff_time >= thread->ttschedule) + if (current_time > thread->ttschedule) return 0; - return (thread->ttschedule - diff_time); + else + return (thread->ttschedule - current_time); } #define time_after(a,b) \ ((int64_t)(b) - (int64_t)(a) < 0) void -actor_init(void) -{ - poll_in_progress = 0; - previous_time = 0; - scheduler_loops = 0; -} - -void -actor_new(actor_t *thread, void (*callback)(void *), void *data) +actor_init(actor_t *thread, void (*callback)(void *), void *data) { INIT_LIST_HEAD(&thread->list); thread->state = ACTOR_NOTSCHEDULED; @@ -77,11 +57,18 @@ actor_delete(actor_t *thread) log_debug(7, "thread %08lx delete: state %d", (long)thread, thread->state); switch(thread->state) { - case ACTOR_SCHEDULED: case ACTOR_WAITING: - case ACTOR_POLL_WAITING: + /* TODO: remove/reset alarm if we were 1st entry in pend_list */ + /* priority: low */ + /* fallthrough */ + case ACTOR_SCHEDULED: log_debug(1, "deleting a scheduled/waiting thread!"); list_del_init(&thread->list); + if (list_empty(&pend_list)) { + log_debug(7, "nothing left on pend_list, deactivating alarm"); + alarm(0); + } + break; default: break; @@ -89,73 +76,94 @@ actor_delete(actor_t *thread) thread->state = ACTOR_NOTSCHEDULED; } +/* + * Inserts actor on pend list and sets alarm if new item is + * sooner than previous entries. + */ static void -actor_schedule_private(actor_t *thread, uint32_t ttschedule, int head) +actor_insert_on_pend_list(actor_t *thread, uint32_t delay_secs) { - uint64_t delay_time, current_time; - actor_t *next_thread; + struct actor *orig_head; + struct actor *new_head; + struct actor *next_thread; - delay_time = ACTOR_MS_TO_TICKS(ttschedule); - current_time = ACTOR_TICKS; + orig_head = list_first_entry_or_null(&pend_list, + struct actor, list); - log_debug(7, "thread %p schedule: delay %" PRIu64 " state %d", - thread, delay_time, thread->state); + /* insert new entry in sort order */ + list_for_each_entry(next_thread, &pend_list, list) { + if (time_after(next_thread->ttschedule, thread->ttschedule)) { + log_debug(7, "next thread %p due %lld", next_thread, + (long long)next_thread->ttschedule); + log_debug(7, "new thread %p is before (%lld), inserting", thread, + (long long)thread->ttschedule); + + /* insert new thread before the next thread */ + __list_add(&thread->list, next_thread->list.prev, &next_thread->list); + goto inserted; + } + } + + if (orig_head) { + log_debug(7, "last thread %p due %lld", next_thread, + (long long)next_thread->ttschedule); + log_debug(7, "new thread %p is after (%lld), inserting at tail", thread, + (long long)thread->ttschedule); + } + else + log_debug(7, "new thread %p due %lld is first item on pend_list", thread, + (long long)thread->ttschedule); + + /* Not before any existing entries */ + list_add_tail(&thread->list, &pend_list); + +inserted: + new_head = list_first_entry(&pend_list, struct actor, list); + if (orig_head != new_head) { + int result = alarm(delay_secs); + log_debug(7, "new alarm set for %d seconds, old alarm %d", + delay_secs, result); + } +} + +static void +actor_schedule_private(actor_t *thread, uint32_t delay_secs, int head) +{ + time_t current_time; + + struct timespec tv; + + if (clock_gettime(CLOCK_MONOTONIC_COARSE, &tv)) { + log_error("clock_getime failed, can't schedule!"); + return; + } + + current_time = tv.tv_sec; + + log_debug(7, "thread %p schedule: delay %u state %d", + thread, delay_secs, thread->state); - /* convert ttscheduled msecs in 10s of msecs by dividing for now. - * later we will change param to 10s of msecs */ switch(thread->state) { case ACTOR_WAITING: log_error("rescheduling a waiting thread!"); list_del(&thread->list); + /* fall-through */ case ACTOR_NOTSCHEDULED: INIT_LIST_HEAD(&thread->list); - /* if ttschedule is 0, put in scheduled queue and change - * state to scheduled, else add current time to ttschedule and - * insert in the queue at the correct point */ - if (delay_time == 0) { - /* For head addition, it must go onto the head of the - actor_list regardless if poll is in progress or not - */ - if (poll_in_progress && !head) { - thread->state = ACTOR_POLL_WAITING; - list_add_tail(&thread->list, - &poll_list); - } else { - thread->state = ACTOR_SCHEDULED; - if (head) - list_add(&thread->list, - &actor_list); - else - list_add_tail(&thread->list, - &actor_list); - } + + if (delay_secs == 0) { + thread->state = ACTOR_SCHEDULED; + if (head) + list_add(&thread->list, &ready_list); + else + list_add_tail(&thread->list, &ready_list); } else { thread->state = ACTOR_WAITING; - thread->ttschedule = delay_time; - thread->scheduled_at = current_time; + thread->ttschedule = current_time + delay_secs; - /* insert new entry in sort order */ - list_for_each_entry(next_thread, &pend_list, list) { - log_debug(7, "thread %p %" PRIu64 " %"PRIu64, - next_thread, - next_thread->scheduled_at + - next_thread->ttschedule, - current_time + delay_time); - - if (time_after(next_thread->scheduled_at + - next_thread->ttschedule, - current_time + delay_time)) { - list_add(&thread->list, - &next_thread->list); - goto done; - } - } - - list_add_tail(&thread->list, &pend_list); + actor_insert_on_pend_list(thread, delay_secs); } -done: break; - case ACTOR_POLL_WAITING: case ACTOR_SCHEDULED: // don't do anything break; @@ -180,117 +188,90 @@ actor_schedule(actor_t *thread) } void -actor_timer(actor_t *thread, uint32_t timeout, void (*callback)(void *), +actor_timer(actor_t *thread, uint32_t timeout_secs, void (*callback)(void *), void *data) { - actor_new(thread, callback, data); - actor_schedule_private(thread, timeout, 0); -} - -int -actor_timer_mod(actor_t *thread, uint32_t timeout, void *data) -{ - if (thread->state == ACTOR_WAITING) { - list_del_init(&thread->list); - thread->data = data; - actor_schedule_private(thread, timeout, 0); - return 1; - } - return 0; -} - -void -actor_check(uint64_t current_time) -{ - struct actor *thread, *tmp; - - list_for_each_entry_safe(thread, tmp, &pend_list, list) { - if (actor_diff_time(thread, current_time)) { - log_debug(7, "thread %08lx wait some more", - (long)thread); - /* wait some more */ - break; - } - - /* it is time to schedule this entry */ - list_del_init(&thread->list); - - log_debug(2, "thread %08lx was scheduled at %" PRIu64 ":" - "%" PRIu64 ", curtime %" PRIu64 " q_forw %p " - "&pend_list %p", - (long)thread, thread->scheduled_at, thread->ttschedule, - current_time, pend_list.next, &pend_list); - - if (poll_in_progress) { - thread->state = ACTOR_POLL_WAITING; - list_add_tail(&thread->list, &poll_list); - log_debug(7, "thread %08lx now in poll_list", - (long)thread); - } else { - thread->state = ACTOR_SCHEDULED; - list_add_tail(&thread->list, &actor_list); - log_debug(7, "thread %08lx now in actor_list", - (long)thread); - } - } + actor_init(thread, callback, data); + actor_schedule_private(thread, timeout_secs, 0); } +/* + * Execute all items that have expired. + * + * Set an alarm if items remain. Caller must catch SIGALRM and + * then re-invoke this function. + */ void actor_poll(void) { + struct actor *thread, *tmp; uint64_t current_time; - struct actor *thread; + struct timespec tv; - /* check that there are no any concurrency */ if (poll_in_progress) { - log_error("concurrent actor_poll() is not allowed"); + log_error("recursive actor_poll() is not allowed"); + return; } - /* don't check wait list every single poll. - * get new time. Shift it to make 10s of msecs approx - * if new time is not same as old time */ - if (scheduler_loops++ > ACTOR_MAX_LOOPS) { - /* try coming in about every 100 msecs */ - current_time = ACTOR_TICKS; - scheduler_loops = 0; - /* checking whether we are in the same tick... */ - if ( ACTOR_TICKS_10MS(current_time) != - ACTOR_TICKS_10MS(previous_time)) { - previous_time = current_time; - actor_check(current_time); + if (clock_gettime(CLOCK_MONOTONIC_COARSE, &tv)) { + log_error("clock_gettime failed, can't schedule!"); + return; + } + + current_time = tv.tv_sec; + + /* + * Move items that are ripe from pend_list to ready_list. + * Actors are in sorted order of ascending run time, so + * stop at the first unripe entry. + */ + log_debug(7, "current time %" PRIu64, current_time); + + list_for_each_entry_safe(thread, tmp, &pend_list, list) { + uint64_t time_left = actor_time_left(thread, current_time); + if (time_left) { + log_debug(7, "thread %08lx due %" PRIu64 ", wait %" PRIu64 " more", + (long)thread, thread->ttschedule, time_left); + + alarm(time_left); + break; } + + /* This entry can be run now */ + list_del_init(&thread->list); + + log_debug(2, "thread %08lx was scheduled for " + "%" PRIu64 ", curtime %" PRIu64 " q_forw %p " + "&pend_list %p", + (long)thread, thread->ttschedule, + current_time, pend_list.next, &pend_list); + + list_add_tail(&thread->list, &ready_list); + assert(thread->state == ACTOR_WAITING); + thread->state = ACTOR_SCHEDULED; + log_debug(7, "thread %08lx now in ready_list", + (long)thread); + } + + /* Disable alarm if nothing else pending */ + if (list_empty(&pend_list)) { + log_debug(7, "nothing on pend_list, deactivating alarm"); + alarm(0); } - /* the following code to check in the main data path */ poll_in_progress = 1; - while (!list_empty(&actor_list)) { - thread = list_entry(actor_list.next, struct actor, list); + while (!list_empty(&ready_list)) { + thread = list_first_entry(&ready_list, struct actor, list); list_del_init(&thread->list); if (thread->state != ACTOR_SCHEDULED) - log_error("actor_list: thread state corrupted! " + log_error("ready_list: thread state corrupted! " "Thread with state %d in actor list.", thread->state); thread->state = ACTOR_NOTSCHEDULED; log_debug(7, "exec thread %08lx callback", (long)thread); thread->callback(thread->data); - log_debug(7, "thread removed\n"); + log_debug(7, "thread %08lx done", (long)thread); } poll_in_progress = 0; - - while (!list_empty(&poll_list)) { - thread = list_entry(poll_list.next, struct actor, list); - list_del_init(&thread->list); - - if (thread->state != ACTOR_POLL_WAITING) - log_error("poll_list: thread state corrupted!" - "Thread with state %d in poll list.", - thread->state); - thread->state = ACTOR_SCHEDULED; - list_add_tail(&thread->list, &actor_list); - log_debug(7, "thread %08lx removed from poll_list", - (long)thread); - } - - ACTOR_TICKS++; } diff --git a/usr/actor.h b/usr/actor.h index 704224d..7283dce 100644 --- a/usr/actor.h +++ b/usr/actor.h @@ -22,15 +22,11 @@ #include "types.h" #include "list.h" -#define ACTOR_RESOLUTION 250 /* in millis */ -#define ACTOR_MAX_LOOPS 1 - typedef enum actor_state_e { ACTOR_INVALID, ACTOR_WAITING, ACTOR_SCHEDULED, ACTOR_NOTSCHEDULED, - ACTOR_POLL_WAITING } actor_state_e; typedef struct actor { @@ -38,18 +34,16 @@ typedef struct actor { actor_state_e state; void *data; void (*callback)(void * ); - uint64_t scheduled_at; - uint64_t ttschedule; + time_t ttschedule; } actor_t; -extern void actor_new(actor_t *thread, void (*callback)(void *), void * data); +extern void actor_init(actor_t *thread, void (*callback)(void *), void * data); extern void actor_delete(actor_t *thread); extern void actor_schedule_head(actor_t *thread); extern void actor_schedule(actor_t *thread); -extern void actor_timer(actor_t *thread, uint32_t timeout, +extern void actor_timer(actor_t *thread, uint32_t delay_secs, void (*callback)(void *), void *data); -extern int actor_timer_mod(actor_t *thread, uint32_t new_timeout, void *data); +extern int actor_timer_mod(actor_t *thread, uint32_t new_delay_secs, void *data); extern void actor_poll(void); -extern void actor_init(void); #endif /* ACTOR_H */ diff --git a/usr/auth.c b/usr/auth.c index c924545..00b4388 100644 --- a/usr/auth.c +++ b/usr/auth.c @@ -109,13 +109,13 @@ acl_chap_auth_request(struct iscsi_acl *client, char *username, unsigned int id, /* the expected credentials are in the session */ if (session->username_in == NULL) { log_error("failing authentication, no incoming username " - "configured to authenticate target %s\n", + "configured to authenticate target %s", session->target_name); return AUTH_STATUS_FAIL; } if (strcmp(username, session->username_in) != 0) { log_error("failing authentication, received incorrect " - "username from target %s\n", session->target_name); + "username from target %s", session->target_name); return AUTH_STATUS_FAIL; } @@ -123,7 +123,7 @@ acl_chap_auth_request(struct iscsi_acl *client, char *username, unsigned int id, (session->password_in == NULL) || (session->password_in[0] == '\0')) { log_error("failing authentication, no incoming password " - "configured to authenticate target %s\n", + "configured to authenticate target %s", session->target_name); return AUTH_STATUS_FAIL; } @@ -132,7 +132,7 @@ acl_chap_auth_request(struct iscsi_acl *client, char *username, unsigned int id, if (rsp_length != sizeof(verify_data)) { log_error("failing authentication, received incorrect " - "CHAP response length %u from target %s\n", + "CHAP response length %u from target %s", rsp_length, session->target_name); return AUTH_STATUS_FAIL; } @@ -154,13 +154,13 @@ acl_chap_auth_request(struct iscsi_acl *client, char *username, unsigned int id, auth_md5_final(verify_data, &context); if (memcmp(response_data, verify_data, sizeof(verify_data)) == 0) { - log_debug(1, "initiator authenticated target %s\n", + log_debug(1, "initiator authenticated target %s", session->target_name); return AUTH_STATUS_PASS; } log_error("failing authentication, received incorrect CHAP " - "response from target %s\n", session->target_name); + "response from target %s", session->target_name); return AUTH_STATUS_FAIL; } @@ -189,24 +189,24 @@ get_random_bytes(unsigned char *data, unsigned int length) long r; unsigned n; - int fd; + int fd, r_size = sizeof(r); fd = open("/dev/urandom", O_RDONLY); while (length > 0) { - if (!fd || read(fd, &r, sizeof(long)) != -1) + if (fd == -1 || read(fd, &r, r_size) != r_size) r = rand(); r = r ^ (r >> 8); r = r ^ (r >> 4); n = r & 0x7; - if (!fd || read(fd, &r, sizeof(long)) != -1) + if (fd == -1 || read(fd, &r, r_size) != r_size) r = rand(); r = r ^ (r >> 8); r = r ^ (r >> 5); n = (n << 3) | (r & 0x7); - if (!fd || read(fd, &r, sizeof(long)) != -1) + if (fd == -1 || read(fd, &r, r_size) != r_size) r = rand(); r = r ^ (r >> 8); r = r ^ (r >> 5); @@ -2002,7 +2002,7 @@ acl_dbg_status_to_text(int dbg_status) "AuthMethod negotiation failed", "AuthMethod negotiated to none", "CHAP algorithm negotiation failed", - "CHAP challange reflected", + "CHAP challenge reflected", "Local password same as remote", "Local password not set", "CHAP identifier bad", diff --git a/usr/be2iscsi.c b/usr/be2iscsi.c index ce8b719..8a346a5 100644 --- a/usr/be2iscsi.c +++ b/usr/be2iscsi.c @@ -19,7 +19,6 @@ void be2iscsi_create_conn(struct iscsi_conn *conn) { struct iscsi_session *session = conn->session; - conn_rec_t *conn_rec = &session->nrec.conn[conn->id]; if (conn->max_recv_dlength > 65536) conn->max_recv_dlength = 65536; @@ -33,10 +32,6 @@ void be2iscsi_create_conn(struct iscsi_conn *conn) if (conn->max_xmit_dlength > 65536) conn->max_xmit_dlength = 65536; - if (!conn_rec->iscsi.MaxXmitDataSegmentLength || - conn_rec->iscsi.MaxXmitDataSegmentLength > 65536) - conn_rec->iscsi.MaxXmitDataSegmentLength = 65536; - session->erl = 0; session->initial_r2t_en = 1; } diff --git a/usr/config.h b/usr/config.h index 998caff..fd31a54 100644 --- a/usr/config.h +++ b/usr/config.h @@ -201,6 +201,9 @@ typedef struct session_rec { * allowed to be initiated on this record */ unsigned char multiple; + char boot_root[BOOT_NAME_MAXLEN]; + char boot_nic[BOOT_NAME_MAXLEN]; + char boot_target[BOOT_NAME_MAXLEN]; } session_rec_t; #define ISCSI_TRANSPORT_NAME_MAXLEN 16 @@ -229,11 +232,59 @@ typedef struct iface_rec { * 1 = enable */ uint16_t mtu; uint16_t port; + char delayed_ack[ISCSI_MAX_STR_LEN]; + char nagle[ISCSI_MAX_STR_LEN]; + char tcp_wsf_state[ISCSI_MAX_STR_LEN]; + uint8_t tcp_wsf; + uint8_t tcp_timer_scale; + char tcp_timestamp[ISCSI_MAX_STR_LEN]; + char dhcp_dns[ISCSI_MAX_STR_LEN]; + char dhcp_slp_da[ISCSI_MAX_STR_LEN]; + char tos_state[ISCSI_MAX_STR_LEN]; + uint8_t tos; + char gratuitous_arp[ISCSI_MAX_STR_LEN]; + char dhcp_alt_client_id_state[ISCSI_MAX_STR_LEN]; + char dhcp_alt_client_id[ISCSI_MAX_STR_LEN]; + char dhcp_req_vendor_id_state[ISCSI_MAX_STR_LEN]; + char dhcp_vendor_id_state[ISCSI_MAX_STR_LEN]; + char dhcp_vendor_id[ISCSI_MAX_STR_LEN]; + char dhcp_learn_iqn[ISCSI_MAX_STR_LEN]; + char fragmentation[ISCSI_MAX_STR_LEN]; + char incoming_forwarding[ISCSI_MAX_STR_LEN]; + uint8_t ttl; + char gratuitous_neighbor_adv[ISCSI_MAX_STR_LEN]; + char redirect[ISCSI_MAX_STR_LEN]; + char mld[ISCSI_MAX_STR_LEN]; + uint32_t flow_label; + uint32_t traffic_class; + uint8_t hop_limit; + uint32_t nd_reachable_tmo; + uint32_t nd_rexmit_time; + uint32_t nd_stale_tmo; + uint8_t dup_addr_detect_cnt; + uint32_t router_adv_link_mtu; + uint16_t def_task_mgmt_tmo; + char header_digest[ISCSI_MAX_STR_LEN]; + char data_digest[ISCSI_MAX_STR_LEN]; + char immediate_data[ISCSI_MAX_STR_LEN]; + char initial_r2t[ISCSI_MAX_STR_LEN]; + char data_seq_inorder[ISCSI_MAX_STR_LEN]; + char data_pdu_inorder[ISCSI_MAX_STR_LEN]; + uint8_t erl; + uint32_t max_recv_dlength; + uint32_t first_burst_len; + uint16_t max_out_r2t; + uint32_t max_burst_len; + char chap_auth[ISCSI_MAX_STR_LEN]; + char bidi_chap[ISCSI_MAX_STR_LEN]; + char strict_login_comp[ISCSI_MAX_STR_LEN]; + char discovery_auth[ISCSI_MAX_STR_LEN]; + char discovery_logout[ISCSI_MAX_STR_LEN]; char port_state[ISCSI_MAX_STR_LEN]; char port_speed[ISCSI_MAX_STR_LEN]; /* * TODO: we may have to make this bigger and interconnect - * specific for infinniband + * specific for infiniband */ char hwaddress[ISCSI_HWADDRESS_BUF_SIZE]; char transport_name[ISCSI_TRANSPORT_NAME_MAXLEN]; diff --git a/usr/discovery.c b/usr/discovery.c index afce6c0..38968ca 100644 --- a/usr/discovery.c +++ b/usr/discovery.c @@ -111,7 +111,7 @@ int discovery_isns_set_servername(char *address, int port) int len; if (port > USHRT_MAX) { - log_error("Invalid port %d\n", port); + log_error("Invalid port %d", port); return ISCSI_ERR_INVAL; } @@ -193,7 +193,7 @@ int discovery_isns_query(struct discovery_rec *drec, const char *iname, status = isns_query_response_get_objects(qry, &objects); if (status) { log_error("Unable to extract object list from query " - "response: %s\n", isns_strerror(status)); + "response: %s", isns_strerror(status)); rc = ISCSI_ERR; goto free_query; } @@ -391,7 +391,7 @@ int discovery_fw(void *data, struct iface_rec *iface, rc = fw_get_targets(&targets); if (rc) { log_error("Could not get list of targets from firmware. " - "(err %d)\n", rc); + "(err %d)", rc); return rc; } if (list_empty(&targets)) @@ -406,7 +406,7 @@ int discovery_fw(void *data, struct iface_rec *iface, rec = idbm_create_rec_from_boot_context(bcontext); if (!rec) { log_error("Could not convert firmware info to " - "node record.\n"); + "node record."); rc = ISCSI_ERR_NOMEM; goto free_targets; } @@ -456,7 +456,7 @@ int discovery_offload_sendtargets(int host_no, int do_login, */ rc = iscsid_exec_req(&req, &rsp, 1); if (rc) { - log_error("Could not offload sendtargets to %s.\n", + log_error("Could not offload sendtargets to %s.", drec->address); iscsi_err_print_msg(rc); return rc; @@ -817,7 +817,7 @@ iscsi_alloc_session(struct iscsi_sendtargets_config *config, session->t = iscsi_sysfs_get_transport_by_name(iface->transport_name); if (!session->t) { log_error("iSCSI driver %s is not loaded. Load the module " - "then retry the command.\n", iface->transport_name); + "then retry the command.", iface->transport_name); *rc = ISCSI_ERR_TRANS_NOT_FOUND; goto fail; } @@ -1036,7 +1036,7 @@ static void iscsi_destroy_session(struct iscsi_session *session) rc = ipc->stop_conn(session->t->handle, session->id, conn->id, STOP_CONN_TERM); if (rc) { - log_error("Could not stop conn %d:%d cleanly (err %d)\n", + log_error("Could not stop conn %d:%d cleanly (err %d)", session->id, conn->id, rc); goto done; } @@ -1091,7 +1091,7 @@ static int iscsi_create_leading_conn(struct iscsi_session *session) */ conn->socket_fd = ipc->ctldev_open(); if (conn->socket_fd < 0) { - log_error("Could not open netlink interface (err %d)\n", + log_error("Could not open netlink interface (err %d)", errno); return ISCSI_ERR_INTERNAL; } @@ -1109,14 +1109,15 @@ static int iscsi_create_leading_conn(struct iscsi_session *session) rc = iscsi_host_set_net_params(iface, session); if (rc) { - log_error("Could not set host net params (err %d)\n", + log_error("Could not set host net params (err %d)", rc); - rc = ISCSI_ERR_INTERNAL; + if (rc != ISCSI_ERR_AGAIN) + rc = ISCSI_ERR_INTERNAL; goto close_ipc; } /* create interconnect endpoint */ - log_debug(2, "%s discovery ep connect\n", __FUNCTION__); + log_debug(2, "%s discovery ep connect", __FUNCTION__); rc = t->template->ep_connect(conn, 1); if (rc < 0) { rc = ISCSI_ERR_TRANS; @@ -1139,21 +1140,21 @@ static int iscsi_create_leading_conn(struct iscsi_session *session) break; } while (1); - log_debug(2, "%s discovery create session\n", __FUNCTION__); + log_debug(2, "%s discovery create session", __FUNCTION__); /* create kernel structs */ rc = ipc->create_session(session->t->handle, conn->transport_ep_handle, 1, 32, 1, &session->id, &host_no); if (rc) { - log_error("Could not create kernel session (err %d).\n", rc); + log_error("Could not create kernel session (err %d).", rc); rc = ISCSI_ERR_INTERNAL; goto disconnect; } - log_debug(2, "%s discovery created session %u\n", __FUNCTION__, + log_debug(2, "%s discovery created session %u", __FUNCTION__, session->id); session->isid[3] = session->id; - log_debug(2, "%s discovery create conn\n", __FUNCTION__); + log_debug(2, "%s discovery create conn", __FUNCTION__); rc = ipc->create_conn(t->handle, session->id, conn->id, &conn->id); if (rc) { log_error("Could not create connection (err %d)", rc); @@ -1161,7 +1162,7 @@ static int iscsi_create_leading_conn(struct iscsi_session *session) goto disconnect; } - log_debug(2, "%s discovery bind conn\n", __FUNCTION__); + log_debug(2, "%s discovery bind conn", __FUNCTION__); if (ipc->bind_conn(t->handle, session->id, conn->id, conn->transport_ep_handle, (conn->id == 0), &rc) || rc) { @@ -1207,7 +1208,7 @@ close_ipc: static struct iscsi_ev_context * iscsi_ev_context_get(struct iscsi_conn *conn, int ev_size) { - log_debug(2, "%s: ev_size %d\n", __FUNCTION__, ev_size); + log_debug(2, "%s: ev_size %d", __FUNCTION__, ev_size); ipc_ev_context.data = calloc(1, ev_size); if (!ipc_ev_context.data) @@ -1403,6 +1404,17 @@ redirect_reconnect: iscsi_copy_operational_params(&session->conn[0], &config->session_conf, &config->conn_conf); + if (t->caps & CAP_TEXT_NEGO) { + log_debug(2, "%s discovery set params", __FUNCTION__); + rc = iscsi_session_set_params(conn); + if (rc) { + log_error("Could not set iscsi params for conn %d:%d " + "(err %d)", session->id, conn->id, rc); + rc = ISCSI_ERR_INTERNAL; + goto login_failed; + } + } + if ((session->t->caps & CAP_LOGIN_OFFLOAD)) goto start_conn; @@ -1477,7 +1489,7 @@ redirect_reconnect: case ISCSI_LOGIN_STATUS_AUTH_FAILED: case ISCSI_LOGIN_STATUS_TGT_FORBIDDEN: log_error("discovery login to %s rejected: " - "initiator failed authorization\n", + "initiator failed authorization", conn->host); rc = ISCSI_ERR_LOGIN_AUTH_FAILED; goto login_failed; @@ -1509,16 +1521,16 @@ redirect_reconnect: return 0; start_conn: - log_debug(2, "%s discovery set params\n", __FUNCTION__); - rc = iscsi_session_set_params(conn); + log_debug(2, "%s discovery set neg params", __FUNCTION__); + rc = iscsi_session_set_neg_params(conn); if (rc) { log_error("Could not set iscsi params for conn %d:%d (err " - "%d)\n", session->id, conn->id, rc); + "%d)", session->id, conn->id, rc); rc = ISCSI_ERR_INTERNAL; goto login_failed; } - log_debug(2, "%s discovery start conn\n", __FUNCTION__); + log_debug(2, "%s discovery start conn", __FUNCTION__); if (ipc->start_conn(t->handle, session->id, conn->id, &rc) || rc) { log_error("Cannot start conn %d:%d (err %d)", session->id, conn->id, rc); diff --git a/usr/discoveryd.c b/usr/discoveryd.c index de080ea..1e14977 100644 --- a/usr/discoveryd.c +++ b/usr/discoveryd.c @@ -211,7 +211,7 @@ static void fork_disc(const char *def_iname, struct discovery_rec *drec, exit(0); } else if (pid < 0) log_error("Fork failed (err %d - %s). Will not be able " - "to perform discovery to %s.\n", + "to perform discovery to %s.", errno, strerror(errno), drec->address); else { shutdown_callback(pid); @@ -254,7 +254,7 @@ static int isns_build_objs(isns_portal_info_t *portal_info, nportals = isns_enumerate_portals(iflist, nportals); if (nportals == 0) { log_error("Unable to enumerate portals - " - "no usable interfaces found\n"); + "no usable interfaces found"); free(iflist); return ISCSI_ERR_NO_OBJS_FOUND; } @@ -557,7 +557,7 @@ static int isns_setup_registration_refresh(isns_simple_t *rsp, int poll_inval) status = isns_query_response_get_objects(rsp, &objs); if (status) { log_error("Unable to extract object list from " - "registration response: %s\n", + "registration response: %s", isns_strerror(status)); return ISCSI_ERR; } @@ -693,7 +693,7 @@ static int isns_register_objs(isns_client_t *clnt, isns_object_list_t *objs, status = isns_simple_call(clnt->ic_socket, ®); if (status != ISNS_SUCCESS) { - log_error("SCN registration for node %s failed: %s\n", + log_error("SCN registration for node %s failed: %s", isns_source_name(node->source), isns_strerror(status)); /* @@ -907,7 +907,7 @@ static int isns_scn_recv(isns_server_t *svr, isns_socket_t *svr_sock, function = isns_message_function(msg); if (function != ISNS_STATE_CHANGE_NOTIFICATION) { - log_warning("Discarding unexpected %s message\n", + log_warning("Discarding unexpected %s message", isns_function_name(function)); isns_message_release(msg); continue; diff --git a/usr/event_poll.c b/usr/event_poll.c index f36fec1..209ee02 100644 --- a/usr/event_poll.c +++ b/usr/event_poll.c @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include "mgmt_ipc.h" #include "iscsi_ipc.h" @@ -37,7 +39,9 @@ #include "initiator.h" #include "iscsi_err.h" -static int reap_count; +static unsigned int reap_count; + +#define REAP_WAKEUP 1000 /* in millisecs */ void reap_inc(void) { @@ -50,7 +54,7 @@ void reap_proc(void) /* * We don't really need reap_count, but calling wait() all the - * time seems execessive. + * time seems excessive. */ max_reaps = reap_count; for (i = 0; i < max_reaps; i++) { @@ -80,7 +84,7 @@ int shutdown_callback(pid_t pid) INIT_LIST_HEAD(&cb->list); cb->pid = pid; - log_debug(1, "adding %d for shutdown cb\n", pid); + log_debug(1, "adding %d for shutdown cb", pid); list_add_tail(&cb->list, &shutdown_callbacks); return 0; } @@ -90,7 +94,7 @@ static void shutdown_notify_pids(void) struct shutdown_callback *cb; list_for_each_entry(cb, &shutdown_callbacks, list) { - log_debug(1, "Killing %d\n", cb->pid); + log_debug(1, "Killing %d", cb->pid); kill(cb->pid, SIGTERM); } } @@ -105,7 +109,7 @@ static int shutdown_wait_pids(void) * sign that it is gone. */ if (waitpid(cb->pid, NULL, WNOHANG)) { - log_debug(1, "%d done\n", cb->pid); + log_debug(1, "%d done", cb->pid); list_del(&cb->list); free(cb); } @@ -116,12 +120,12 @@ static int shutdown_wait_pids(void) #define POLL_CTRL 0 #define POLL_IPC 1 -#define POLL_MAX 2 +#define POLL_ALARM 2 +#define POLL_MAX 3 static int event_loop_stop; static queue_task_t *shutdown_qtask; - void event_loop_exit(queue_task_t *qtask) { shutdown_qtask = qtask; @@ -132,11 +136,26 @@ void event_loop(struct iscsi_ipc *ipc, int control_fd, int mgmt_ipc_fd) { struct pollfd poll_array[POLL_MAX]; int res, has_shutdown_children = 0; + sigset_t sigset; + int sig_fd; + + /* Mask off SIGALRM so we can recv it via signalfd */ + sigemptyset(&sigset); + sigaddset(&sigset, SIGALRM); + sigprocmask(SIG_SETMASK, &sigset, NULL); + + sig_fd = signalfd(-1, &sigset, SFD_NONBLOCK); + if (sig_fd == -1) { + log_error("signalfd failed: %m"); + return; + } poll_array[POLL_CTRL].fd = control_fd; poll_array[POLL_CTRL].events = POLLIN; poll_array[POLL_IPC].fd = mgmt_ipc_fd; poll_array[POLL_IPC].events = POLLIN; + poll_array[POLL_ALARM].fd = sig_fd; + poll_array[POLL_ALARM].events = POLLIN; event_loop_stop = 0; while (1) { @@ -149,7 +168,11 @@ void event_loop(struct iscsi_ipc *ipc, int control_fd, int mgmt_ipc_fd) break; } - res = poll(poll_array, POLL_MAX, ACTOR_RESOLUTION); + /* Runs actors and may set alarm for future actors */ + actor_poll(); + + res = poll(poll_array, POLL_MAX, reap_count ? REAP_WAKEUP : -1); + if (res > 0) { log_debug(6, "poll result %d", res); if (poll_array[POLL_CTRL].revents) @@ -157,6 +180,18 @@ void event_loop(struct iscsi_ipc *ipc, int control_fd, int mgmt_ipc_fd) if (poll_array[POLL_IPC].revents) mgmt_ipc_handle(mgmt_ipc_fd); + + if (poll_array[POLL_ALARM].revents) { + struct signalfd_siginfo si; + + if (read(sig_fd, &si, sizeof(si)) == -1) { + log_error("got sigfd read() error, errno (%d), " + "exiting", errno); + break; + } else { + log_debug(1, "Poll was woken by an alarm"); + } + } } else if (res < 0) { if (errno == EINTR) { log_debug(1, "event_loop interrupted"); @@ -165,15 +200,20 @@ void event_loop(struct iscsi_ipc *ipc, int control_fd, int mgmt_ipc_fd) "exiting", res, errno); break; } - } else - actor_poll(); + } + reap_proc(); + /* * flush sysfs cache since kernel objs may * have changed as a result of handling op */ sysfs_cleanup(); } + if (shutdown_qtask) mgmt_ipc_write_rsp(shutdown_qtask, ISCSI_SUCCESS); + + close(sig_fd); + sigprocmask(SIG_UNBLOCK, &sigset, NULL); } diff --git a/usr/flashnode.c b/usr/flashnode.c new file mode 100644 index 0000000..fe5ab57 --- /dev/null +++ b/usr/flashnode.c @@ -0,0 +1,615 @@ +/* + * iSCSI flashnode helpers + * + * Copyright (C) 2013 QLogic Corporation. + * Maintained by open-iscsi@googlegroups.com + * + * 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. + * + * See the file COPYING included with this distribution for more details. + */ +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "idbm.h" +#include "iscsi_util.h" +#include "transport.h" +#include "iscsi_sysfs.h" +#include "list.h" +#include "sysdeps.h" +#include "idbm_fields.h" +#include "iscsi_err.h" +#include "iscsi_ipc.h" +#include "iscsi_netlink.h" +#include "flashnode.h" +#include "iscsi_settings.h" + +char key[NAME_MAXVAL]; + +char *to_key(const char *fmt) +{ + int i = 0; + memset(key, 0, sizeof(key)); + sprintf(key, fmt, i); + return key; +} + +int flashnode_info_print_flat(void *data, struct flashnode_rec *fnode, + uint32_t host_no, uint32_t flashnode_idx) +{ + printf("%s: [%d] ", fnode->transport_name, flashnode_idx); + if (!strlen((char *)fnode->conn[0].ipaddress)) + printf("%s:", UNKNOWN_VALUE); + else if (strchr((char *)fnode->conn[0].ipaddress, '.')) + printf("%s:", fnode->conn[0].ipaddress); + else + printf("[%s]:", fnode->conn[0].ipaddress); + + if (!fnode->conn[0].port) + printf("%s,", UNKNOWN_VALUE); + else + printf("%u,", fnode->conn[0].port); + + printf("%u ", fnode->sess.tpgt); + + if (!strlen(fnode->sess.targetname)) + printf("%s\n", UNKNOWN_VALUE); + else + printf("%s\n", fnode->sess.targetname); + + return 0; +} + +static int flashnode_fill_isid(struct flashnode_rec *fnode, struct iovec *iov) +{ + struct iscsi_flashnode_param_info *fnode_param; + struct nlattr *attr; + int len; + uint8_t isid[6]; + + len = sizeof(struct iscsi_flashnode_param_info) + 6; + iov->iov_base = iscsi_nla_alloc(ISCSI_FLASHNODE_ISID, len); + if (!iov->iov_base) + return 1; + + attr = iov->iov_base; + iov->iov_len = NLA_ALIGN(attr->nla_len); + + fnode_param = (struct iscsi_flashnode_param_info *)ISCSI_NLA_DATA(attr); + fnode_param->param = ISCSI_FLASHNODE_ISID; + fnode_param->len = 6; + + sscanf(fnode->sess.isid, "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx", + &isid[0], &isid[1], &isid[2], &isid[3], &isid[4], &isid[5]); + + memcpy(fnode_param->value, isid, fnode_param->len); + return 0; +} + +static int flashnode_fill_ipv4_addr(struct flashnode_rec *fnode, + struct iovec *iov, int param_type) +{ + struct iscsi_flashnode_param_info *fnode_param; + struct nlattr *attr; + int len; + int rc; + + len = sizeof(struct iscsi_flashnode_param_info) + 4; + iov->iov_base = iscsi_nla_alloc(param_type, len); + if (!iov->iov_base) + return 1; + + attr = iov->iov_base; + iov->iov_len = NLA_ALIGN(attr->nla_len); + + fnode_param = (struct iscsi_flashnode_param_info *)ISCSI_NLA_DATA(attr); + fnode_param->param = param_type; + fnode_param->len = 4; + + switch (param_type) { + case ISCSI_FLASHNODE_IPADDR: + rc = inet_pton(AF_INET, (char *)fnode->conn[0].ipaddress, + fnode_param->value); + break; + case ISCSI_FLASHNODE_REDIRECT_IPADDR: + rc = inet_pton(AF_INET, (char *)fnode->conn[0].redirect_ipaddr, + fnode_param->value); + break; + default: + goto free; + } + + if (rc <= 0) + goto free; + + return 0; + +free: + free(iov->iov_base); + iov->iov_base = NULL; + iov->iov_len = 0; + return 1; +} + +static int flashnode_fill_ipv6_addr(struct flashnode_rec *fnode, + struct iovec *iov, int param_type) +{ + struct iscsi_flashnode_param_info *fnode_param; + struct nlattr *attr; + int len; + int rc; + + len = sizeof(struct iscsi_flashnode_param_info) + 16; + iov->iov_base = iscsi_nla_alloc(param_type, len); + if (!iov->iov_base) + return 1; + + attr = iov->iov_base; + iov->iov_len = NLA_ALIGN(attr->nla_len); + + fnode_param = (struct iscsi_flashnode_param_info *)ISCSI_NLA_DATA(attr); + fnode_param->param = param_type; + fnode_param->len = 16; + + switch (param_type) { + case ISCSI_FLASHNODE_IPADDR: + rc = inet_pton(AF_INET6, (char *)fnode->conn[0].ipaddress, + fnode_param->value); + break; + case ISCSI_FLASHNODE_REDIRECT_IPADDR: + rc = inet_pton(AF_INET6, (char *)fnode->conn[0].redirect_ipaddr, + fnode_param->value); + break; + case ISCSI_FLASHNODE_LINK_LOCAL_IPV6: + rc = inet_pton(AF_INET6, (char *)fnode->conn[0].link_local_ipv6, + fnode_param->value); + break; + default: + goto free; + } + + if (rc <= 0) + goto free; + + return 0; + +free: + free(iov->iov_base); + iov->iov_base = NULL; + iov->iov_len = 0; + return 1; +} + +static int flashnode_fill_ipaddr(struct flashnode_rec *fnode, struct iovec *iov, + int param_type) +{ + int rc = 0; + + if (!strncmp(fnode->sess.portal_type, "ipv4", 4)) + rc = flashnode_fill_ipv4_addr(fnode, iov, param_type); + else + rc = flashnode_fill_ipv6_addr(fnode, iov, param_type); + + return rc; +} + +static int flashnode_fill_uint8(struct flashnode_rec *fnode, struct iovec *iov, + int param_type, uint8_t val) +{ + struct iscsi_flashnode_param_info *fnode_param; + struct nlattr *attr; + int len; + + len = sizeof(struct iscsi_flashnode_param_info) + 1; + iov->iov_base = iscsi_nla_alloc(param_type, len); + if (!iov->iov_base) + return 1; + + attr = iov->iov_base; + iov->iov_len = NLA_ALIGN(attr->nla_len); + + fnode_param = (struct iscsi_flashnode_param_info *)ISCSI_NLA_DATA(attr); + fnode_param->param = param_type; + fnode_param->len = 1; + fnode_param->value[0] = val; + return 0; +} + +static int flashnode_fill_uint16(struct flashnode_rec *fnode, struct iovec *iov, + int param_type, uint16_t val) +{ + struct iscsi_flashnode_param_info *fnode_param; + struct nlattr *attr; + int len; + + len = sizeof(struct iscsi_flashnode_param_info) + 2; + iov->iov_base = iscsi_nla_alloc(param_type, len); + if (!iov->iov_base) + return 1; + + attr = iov->iov_base; + iov->iov_len = NLA_ALIGN(attr->nla_len); + + fnode_param = (struct iscsi_flashnode_param_info *)ISCSI_NLA_DATA(attr); + fnode_param->param = param_type; + fnode_param->len = 2; + memcpy(fnode_param->value, &val, fnode_param->len); + return 0; +} + +static int flashnode_fill_uint32(struct flashnode_rec *fnode, struct iovec *iov, + int param_type, uint32_t val) +{ + struct iscsi_flashnode_param_info *fnode_param; + struct nlattr *attr; + int len; + + len = sizeof(struct iscsi_flashnode_param_info) + 4; + iov->iov_base = iscsi_nla_alloc(param_type, len); + if (!iov->iov_base) + return 1; + + attr = iov->iov_base; + iov->iov_len = NLA_ALIGN(attr->nla_len); + + fnode_param = (struct iscsi_flashnode_param_info *)ISCSI_NLA_DATA(attr); + fnode_param->param = param_type; + fnode_param->len = 4; + memcpy(fnode_param->value, &val, fnode_param->len); + return 0; +} + +static int flashnode_fill_str(struct flashnode_rec *fnode, struct iovec *iov, + int param_type, char *buf, int buflen) +{ + struct iscsi_flashnode_param_info *fnode_param; + struct nlattr *attr; + int len; + + len = sizeof(struct iscsi_flashnode_param_info) + buflen; + iov->iov_base = iscsi_nla_alloc(param_type, len); + if (!iov->iov_base) + return 1; + + attr = iov->iov_base; + iov->iov_len = NLA_ALIGN(attr->nla_len); + + fnode_param = (struct iscsi_flashnode_param_info *)ISCSI_NLA_DATA(attr); + fnode_param->param = param_type; + fnode_param->len = buflen; + memcpy(fnode_param->value, buf, fnode_param->len); + return 0; +} + +int flashnode_build_config(struct list_head *params, + struct flashnode_rec *fnode, struct iovec *iovs) +{ + struct user_param *param; + struct iovec *iov = NULL; + int count = 0; + int port = 3260; + + /* start at 2, because 0 is for nlmsghdr and 1 for event */ + iov = iovs + 2; + + list_for_each_entry(param, params, list) { + if (!strcmp(param->name, FLASHNODE_SESS_AUTO_SND_TGT_DISABLE)) { + if (!flashnode_fill_uint8(fnode, &iov[count], + ISCSI_FLASHNODE_AUTO_SND_TGT_DISABLE, + fnode->sess.auto_snd_tgt_disable)) + count++; + } else if (!strcmp(param->name, + FLASHNODE_SESS_DISCOVERY_SESS)) { + if (!flashnode_fill_uint8(fnode, &iov[count], + ISCSI_FLASHNODE_DISCOVERY_SESS, + fnode->sess.discovery_session)) + count++; + } else if (!strcmp(param->name, FLASHNODE_SESS_ENTRY_EN)) { + if (!flashnode_fill_uint8(fnode, &iov[count], + ISCSI_FLASHNODE_ENTRY_EN, + fnode->sess.entry_enable)) + count++; + } else if (!strcmp(param->name, FLASHNODE_SESS_IMM_DATA_EN)) { + if (!flashnode_fill_uint8(fnode, &iov[count], + ISCSI_FLASHNODE_IMM_DATA_EN, + fnode->sess.immediate_data)) + count++; + } else if (!strcmp(param->name, + FLASHNODE_SESS_INITIAL_R2T_EN)) { + if (!flashnode_fill_uint8(fnode, &iov[count], + ISCSI_FLASHNODE_INITIAL_R2T_EN, + fnode->sess.initial_r2t)) + count++; + } else if (!strcmp(param->name, + FLASHNODE_SESS_DATASEQ_INORDER)) { + if (!flashnode_fill_uint8(fnode, &iov[count], + ISCSI_FLASHNODE_DATASEQ_INORDER, + fnode->sess.data_seq_in_order)) + count++; + } else if (!strcmp(param->name, FLASHNODE_SESS_PDU_INORDER)) { + if (!flashnode_fill_uint8(fnode, &iov[count], + ISCSI_FLASHNODE_PDU_INORDER, + fnode->sess.data_pdu_in_order)) + count++; + } else if (!strcmp(param->name, FLASHNODE_SESS_CHAP_AUTH_EN)) { + if (!flashnode_fill_uint8(fnode, &iov[count], + ISCSI_FLASHNODE_CHAP_AUTH_EN, + fnode->sess.chap_auth_en)) + count++; + } else if (!strcmp(param->name, + FLASHNODE_SESS_DISCOVERY_LOGOUT_EN)) { + if (!flashnode_fill_uint8(fnode, &iov[count], + ISCSI_FLASHNODE_DISCOVERY_LOGOUT_EN, + fnode->sess.discovery_logout_en)) + count++; + } else if (!strcmp(param->name, FLASHNODE_SESS_BIDI_CHAP_EN )) { + if (!flashnode_fill_uint8(fnode, &iov[count], + ISCSI_FLASHNODE_BIDI_CHAP_EN, + fnode->sess.bidi_chap_en)) + count++; + } else if (!strcmp(param->name, + FLASHNODE_SESS_DISCOVERY_AUTH_OPTIONAL)) { + if (!flashnode_fill_uint8(fnode, &iov[count], + ISCSI_FLASHNODE_DISCOVERY_AUTH_OPTIONAL, + fnode->sess.discovery_auth_optional)) + count++; + } else if (!strcmp(param->name, FLASHNODE_SESS_ERL)) { + if (!flashnode_fill_uint8(fnode, &iov[count], + ISCSI_FLASHNODE_ERL, + fnode->sess.erl)) + count++; + } else if (!strcmp(param->name, + FLASHNODE_SESS_DEF_TIME2WAIT)) { + if (!flashnode_fill_uint16(fnode, &iov[count], + ISCSI_FLASHNODE_DEF_TIME2WAIT, + fnode->sess.def_time2wait)) + count++; + } else if (!strcmp(param->name, + FLASHNODE_SESS_DEF_TIME2RETAIN)) { + if (!flashnode_fill_uint16(fnode, &iov[count], + ISCSI_FLASHNODE_DEF_TIME2RETAIN, + fnode->sess.def_time2retain)) + count++; + } else if (!strcmp(param->name, FLASHNODE_SESS_MAX_R2T)) { + if (!flashnode_fill_uint16(fnode, &iov[count], + ISCSI_FLASHNODE_MAX_R2T, + fnode->sess.max_outstanding_r2t)) + count++; + } else if (!strcmp(param->name, FLASHNODE_SESS_TSID)) { + if (!flashnode_fill_uint16(fnode, &iov[count], + ISCSI_FLASHNODE_TSID, + fnode->sess.tsid)) + count++; + } else if (!strcmp(param->name, FLASHNODE_SESS_MAX_BURST)) { + if (!flashnode_fill_uint32(fnode, &iov[count], + ISCSI_FLASHNODE_MAX_BURST, + fnode->sess.max_burst_len)) + count++; + } else if (!strcmp(param->name, + FLASHNODE_SESS_DEF_TASKMGMT_TMO)) { + if (!flashnode_fill_uint16(fnode, &iov[count], + ISCSI_FLASHNODE_DEF_TASKMGMT_TMO, + fnode->sess.def_taskmgmt_tmo)) + count++; + } else if (!strcmp(param->name, FLASHNODE_SESS_NAME)) { + if (!flashnode_fill_str(fnode, &iov[count], + ISCSI_FLASHNODE_NAME, + fnode->sess.targetname, + sizeof(fnode->sess.targetname))) + count++; + } else if (!strcmp(param->name, FLASHNODE_SESS_FIRST_BURST)) { + if (!flashnode_fill_uint32(fnode, &iov[count], + ISCSI_FLASHNODE_FIRST_BURST, + fnode->sess.first_burst_len)) + count++; + } else if (!strcmp(param->name, FLASHNODE_SESS_ISID)) { + if (!flashnode_fill_isid(fnode, &iov[count])) + count++; + } else if (!strcmp(param->name, FLASHNODE_SESS_ALIAS)) { + if (!flashnode_fill_str(fnode, &iov[count], + ISCSI_FLASHNODE_ALIAS, + fnode->sess.targetalias, + sizeof(fnode->sess.targetalias))) + count++; + } else if (!strcmp(param->name, FLASHNODE_SESS_TPGT)) { + if (!flashnode_fill_uint16(fnode, &iov[count], + ISCSI_FLASHNODE_TPGT, + fnode->sess.tpgt)) + count++; + } else if (!strcmp(param->name, + FLASHNODE_SESS_DISCOVERY_PARENT_IDX)) { + if (!flashnode_fill_uint16(fnode, &iov[count], + ISCSI_FLASHNODE_DISCOVERY_PARENT_IDX, + fnode->sess.discovery_parent_idx)) + count++; + } else if (!strcmp(param->name, + FLASHNODE_SESS_DISCOVERY_PARENT_TYPE)) { + if (!flashnode_fill_str(fnode, &iov[count], + ISCSI_FLASHNODE_DISCOVERY_PARENT_TYPE, + fnode->sess.discovery_parent_type, + sizeof(fnode->sess.discovery_parent_type))) + count++; + } else if (!strcmp(param->name, FLASHNODE_SESS_PORTAL_TYPE)) { + if (!flashnode_fill_str(fnode, &iov[count], + ISCSI_FLASHNODE_PORTAL_TYPE, + fnode->sess.portal_type, + sizeof(fnode->sess.portal_type))) + count++; + } else if (!strcmp(param->name, + to_key(FLASHNODE_SESS_CHAP_OUT_IDX))) { + if (!flashnode_fill_uint32(fnode, &iov[count], + ISCSI_FLASHNODE_CHAP_OUT_IDX, + fnode->sess.chap_out_idx)) + count++; + } else if (!strcmp(param->name, to_key(FLASHNODE_CONN_PORT))) { + if (fnode->conn[0].port) + port = fnode->conn[0].port; + if (!flashnode_fill_uint16(fnode, &iov[count], + ISCSI_FLASHNODE_PORT, port)) + count++; + } else if (!strcmp(param->name, + to_key(FLASHNODE_CONN_IPADDR))) { + if (!flashnode_fill_ipaddr(fnode, &iov[count], + ISCSI_FLASHNODE_IPADDR)) + count++; + } else if (!strcmp(param->name, + to_key(FLASHNODE_CONN_MAX_RECV_DLENGTH))) { + if (!flashnode_fill_uint32(fnode, &iov[count], + ISCSI_FLASHNODE_MAX_RECV_DLENGTH, + fnode->conn[0].max_recv_dlength)) + count++; + } else if (!strcmp(param->name, + to_key(FLASHNODE_CONN_IS_FW_ASSIGNED_IPV6))) { + if (!flashnode_fill_uint8(fnode, &iov[count], + ISCSI_FLASHNODE_IS_FW_ASSIGNED_IPV6, + fnode->conn[0].is_fw_assigned_ipv6)) + count++; + } else if (!strcmp(param->name, + to_key(FLASHNODE_CONN_HDR_DGST_EN))) { + if (!flashnode_fill_uint8(fnode, &iov[count], + ISCSI_FLASHNODE_HDR_DGST_EN, + fnode->conn[0].header_digest_en)) + count++; + } else if (!strcmp(param->name, + to_key(FLASHNODE_CONN_DATA_DGST_EN))) { + if (!flashnode_fill_uint8(fnode, &iov[count], + ISCSI_FLASHNODE_DATA_DGST_EN, + fnode->conn[0].data_digest_en)) + count++; + } else if (!strcmp(param->name, + to_key(FLASHNODE_CONN_SNACK_REQ_EN))) { + if (!flashnode_fill_uint8(fnode, &iov[count], + ISCSI_FLASHNODE_SNACK_REQ_EN, + fnode->conn[0].snack_req_en)) + count++; + } else if (!strcmp(param->name, + to_key(FLASHNODE_CONN_TCP_TIMESTAMP_STAT))) { + if (!flashnode_fill_uint8(fnode, &iov[count], + ISCSI_FLASHNODE_TCP_TIMESTAMP_STAT, + fnode->conn[0].tcp_timestamp_stat)) + count++; + } else if (!strcmp(param->name, + to_key(FLASHNODE_CONN_TCP_NAGLE_DISABLE))) { + if (!flashnode_fill_uint8(fnode, &iov[count], + ISCSI_FLASHNODE_TCP_NAGLE_DISABLE, + fnode->conn[0].tcp_nagle_disable)) + count++; + } else if (!strcmp(param->name, + to_key(FLASHNODE_CONN_TCP_WSF_DISABLE))) { + if (!flashnode_fill_uint8(fnode, &iov[count], + ISCSI_FLASHNODE_TCP_WSF_DISABLE, + fnode->conn[0].tcp_wsf_disable)) + count++; + } else if (!strcmp(param->name, + to_key(FLASHNODE_CONN_TCP_TIMER_SCALE))) { + if (!flashnode_fill_uint8(fnode, &iov[count], + ISCSI_FLASHNODE_TCP_TIMER_SCALE, + fnode->conn[0].tcp_timer_scale)) + count++; + } else if (!strcmp(param->name, + to_key(FLASHNODE_CONN_TCP_TIMESTAMP_EN))) { + if (!flashnode_fill_uint8(fnode, &iov[count], + ISCSI_FLASHNODE_TCP_TIMESTAMP_EN, + fnode->conn[0].tcp_timestamp_en)) + count++; + } else if (!strcmp(param->name, + to_key(FLASHNODE_CONN_IP_FRAG_DISABLE))) { + if (!flashnode_fill_uint8(fnode, &iov[count], + ISCSI_FLASHNODE_IP_FRAG_DISABLE, + fnode->conn[0].fragment_disable)) + count++; + } else if (!strcmp(param->name, + to_key(FLASHNODE_CONN_MAX_XMIT_DLENGTH))) { + if (!flashnode_fill_uint32(fnode, &iov[count], + ISCSI_FLASHNODE_MAX_XMIT_DLENGTH, + fnode->conn[0].max_xmit_dlength)) + count++; + } else if (!strcmp(param->name, + to_key(FLASHNODE_CONN_KEEPALIVE_TMO))) { + if (!flashnode_fill_uint16(fnode, &iov[count], + ISCSI_FLASHNODE_KEEPALIVE_TMO, + fnode->conn[0].keepalive_tmo)) + count++; + } else if (!strcmp(param->name, + to_key(FLASHNODE_CONN_REDIRECT_IPADDR))) { + if (!flashnode_fill_ipaddr(fnode, &iov[count], + ISCSI_FLASHNODE_REDIRECT_IPADDR)) + count++; + } else if (!strcmp(param->name, + to_key(FLASHNODE_CONN_MAX_SEGMENT_SIZE))) { + if (!flashnode_fill_uint32(fnode, &iov[count], + ISCSI_FLASHNODE_MAX_SEGMENT_SIZE, + fnode->conn[0].max_segment_size)) + count++; + } else if (!strcmp(param->name, + to_key(FLASHNODE_CONN_LOCAL_PORT))) { + if (!flashnode_fill_uint16(fnode, &iov[count], + ISCSI_FLASHNODE_LOCAL_PORT, + fnode->conn[0].local_port)) + count++; + } else if (!strcmp(param->name, + to_key(FLASHNODE_CONN_IPV4_TOS))) { + if (!flashnode_fill_uint8(fnode, &iov[count], + ISCSI_FLASHNODE_IPV4_TOS, + fnode->conn[0].ipv4_tos)) + count++; + } else if (!strcmp(param->name, + to_key(FLASHNODE_CONN_IPV6_TC))) { + if (!flashnode_fill_uint8(fnode, &iov[count], + ISCSI_FLASHNODE_IPV6_TC, + fnode->conn[0].ipv6_traffic_class)) + count++; + } else if (!strcmp(param->name, + to_key(FLASHNODE_CONN_IPV6_FLOW_LABEL))) { + if (!flashnode_fill_uint16(fnode, &iov[count], + ISCSI_FLASHNODE_IPV6_FLOW_LABEL, + fnode->conn[0].ipv6_flow_lbl)) + count++; + } else if (!strcmp(param->name, + to_key(FLASHNODE_CONN_LINK_LOCAL_IPV6))) { + if (!flashnode_fill_ipv6_addr(fnode, &iov[count], + ISCSI_FLASHNODE_LINK_LOCAL_IPV6)) + count++; + } else if (!strcmp(param->name, + to_key(FLASHNODE_CONN_TCP_XMIT_WSF))) { + if (!flashnode_fill_uint32(fnode, &iov[count], + ISCSI_FLASHNODE_TCP_XMIT_WSF, + fnode->conn[0].tcp_xmit_wsf)) + count++; + } else if (!strcmp(param->name, + to_key(FLASHNODE_CONN_TCP_RECV_WSF))) { + if (!flashnode_fill_uint32(fnode, &iov[count], + ISCSI_FLASHNODE_TCP_RECV_WSF, + fnode->conn[0].tcp_recv_wsf)) + count++; + } else if (!strcmp(param->name, + to_key(FLASHNODE_CONN_STATSN))) { + if (!flashnode_fill_uint32(fnode, &iov[count], + ISCSI_FLASHNODE_STATSN, + fnode->conn[0].stat_sn)) + count++; + } else if (!strcmp(param->name, + to_key(FLASHNODE_CONN_EXP_STATSN))) { + if (!flashnode_fill_uint32(fnode, &iov[count], + ISCSI_FLASHNODE_EXP_STATSN, + fnode->conn[0].exp_stat_sn)) + count++; + } + } + + return count; +} diff --git a/usr/flashnode.h b/usr/flashnode.h new file mode 100644 index 0000000..2950fb5 --- /dev/null +++ b/usr/flashnode.h @@ -0,0 +1,129 @@ +/* + * iSCSI flashnode helpers + * + * Copyright (C) 2013 QLogic Corporation. + * Maintained by open-iscsi@googlegroups.com + * + * 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. + * + * See the file COPYING included with this distribution for more details. + */ +#ifndef FLASHNODE_H +#define FLASHNODE_H +#include +#include +#include + +#include "types.h" +#include "config.h" +#include "auth.h" + +#define MAX_FLASHNODE_IDX UINT_MAX + +typedef enum portal_type { + IPV4, + IPV6, +} portal_type_e; + +typedef struct flashnode_sess_rec { + char targetname[TARGET_NAME_MAXLEN]; + char targetalias[TARGET_NAME_MAXLEN]; + char username[AUTH_STR_MAX_LEN]; + char username_in[AUTH_STR_MAX_LEN]; + char password[AUTH_STR_MAX_LEN]; + char password_in[AUTH_STR_MAX_LEN]; + /* indicates if discovery was done through iSNS discovery service + * or through sendTarget */ + char discovery_parent_type[ISCSI_MAX_STR_LEN]; + char isid[16]; + char portal_type[5]; /* ipv4 or ipv6 */ + unsigned first_burst_len; + unsigned max_burst_len; + uint16_t def_time2wait; + uint16_t def_time2retain; + uint16_t max_outstanding_r2t; + uint16_t tsid; + uint16_t def_taskmgmt_tmo; + uint16_t tpgt; + uint16_t chap_out_idx; + uint16_t chap_in_idx; + /* index of iSCSI discovery session if the entry is + * discovered by iSCSI discovery session + */ + uint16_t discovery_parent_idx; + /* Firmware auto sendtarget discovery disable */ + uint8_t auto_snd_tgt_disable; + uint8_t discovery_session; + /* indicates if this flashnode entry is enabled or disabled */ + uint8_t entry_enable; + uint8_t immediate_data; + uint8_t initial_r2t; + uint8_t data_seq_in_order; + uint8_t data_pdu_in_order; + uint8_t chap_auth_en; + /* enables firmware to auto logout the discovery session on discovery + * completion + */ + uint8_t discovery_logout_en; + uint8_t bidi_chap_en; + /* makes authentication for discovery session optional */ + uint8_t discovery_auth_optional; + uint8_t erl; + uint8_t is_boot_target; +} flashnode_sess_rec_t; + +typedef struct flashnode_conn_rec { + char ipaddress[NI_MAXHOST]; + char redirect_ipaddr[NI_MAXHOST]; + char link_local_ipv6[NI_MAXHOST]; + unsigned max_recv_dlength; + unsigned max_xmit_dlength; + unsigned max_segment_size; + unsigned tcp_xmit_wsf; + unsigned tcp_recv_wsf; + uint32_t stat_sn; + uint32_t exp_stat_sn; + uint16_t keepalive_tmo; + uint16_t port; + uint16_t local_port; + uint16_t ipv6_flow_lbl; + /* Link local IPv6 address is assigned by firmware or driver */ + uint8_t is_fw_assigned_ipv6; + uint8_t header_digest_en; + uint8_t data_digest_en; + uint8_t snack_req_en; + /* tcp timestamp negotiation status */ + uint8_t tcp_timestamp_stat; + uint8_t tcp_nagle_disable; + /* tcp window scale factor */ + uint8_t tcp_wsf_disable; + uint8_t tcp_timer_scale; + uint8_t tcp_timestamp_en; + uint8_t fragment_disable; + uint8_t ipv4_tos; + uint8_t ipv6_traffic_class; +} flashnode_conn_rec_t; + +struct flashnode_rec { + struct list_head list; + char transport_name[ISCSI_TRANSPORT_NAME_MAXLEN]; + flashnode_sess_rec_t sess; + flashnode_conn_rec_t conn[ISCSI_CONN_MAX]; +}; + +extern int flashnode_info_print_flat(void *data, struct flashnode_rec *tgt, + uint32_t host_no, uint32_t flashnode_idx); +extern int iscsi_logout_flashnode_sid(struct iscsi_transport *t, + uint32_t host_no, uint32_t sid); +extern int flashnode_build_config(struct list_head *params, + struct flashnode_rec *flashnode, + struct iovec *iovs); +#endif diff --git a/usr/host.c b/usr/host.c index b03e50f..f2052d3 100644 --- a/usr/host.c +++ b/usr/host.c @@ -34,6 +34,7 @@ #include "initiator.h" #include "iface.h" #include "iscsi_err.h" +#include "iscsi_netlink.h" static int match_host_to_session(void *data, struct session_info *info) { @@ -242,7 +243,7 @@ static int host_info_print_tree(void *data, struct host_info *hinfo) link_info.data = &hinfo->host_no; err = iscsi_sysfs_for_each_session(&link_info, &num_found, - session_info_create_list); + session_info_create_list, 0); if (err || !num_found) return 0; @@ -314,3 +315,112 @@ int host_info_print(int info_level, uint32_t host_no) } return 0; } + +static int chap_fill_param_uint(struct iovec *iov, int param, + uint32_t param_val, int param_len) +{ + struct iscsi_param_info *param_info; + struct nlattr *attr; + int len; + uint8_t val8 = 0; + uint16_t val16 = 0; + uint32_t val32 = 0; + char *val = NULL; + + len = sizeof(struct iscsi_param_info) + param_len; + iov->iov_base = iscsi_nla_alloc(param, len); + if (!iov->iov_base) + return 1; + + attr = iov->iov_base; + iov->iov_len = NLA_ALIGN(attr->nla_len); + + param_info = (struct iscsi_param_info *)ISCSI_NLA_DATA(attr); + param_info->param = param; + param_info->len = param_len; + + switch (param_len) { + case 1: + val8 = (uint8_t)param_val; + val = (char *)&val8; + break; + + case 2: + val16 = (uint16_t)param_val; + val = (char *)&val16; + break; + + case 4: + val32 = (uint32_t)param_val; + val = (char *)&val32; + break; + + default: + goto free; + } + memcpy(param_info->value, val, param_len); + + return 0; + +free: + free(iov->iov_base); + iov->iov_base = NULL; + iov->iov_len = 0; + return 1; +} + +static int chap_fill_param_str(struct iovec *iov, int param, char *param_val, + int param_len) +{ + struct iscsi_param_info *param_info; + struct nlattr *attr; + int len; + + len = sizeof(struct iscsi_param_info) + param_len; + iov->iov_base = iscsi_nla_alloc(param, len); + if (!iov->iov_base) + return 1; + + attr = iov->iov_base; + iov->iov_len = NLA_ALIGN(attr->nla_len); + + param_info = (struct iscsi_param_info *)ISCSI_NLA_DATA(attr); + param_info->param = param; + param_info->len = param_len; + memcpy(param_info->value, param_val, param_len); + return 0; +} + +int chap_build_config(struct iscsi_chap_rec *crec, struct iovec *iovs) +{ + struct iovec *iov = NULL; + int count = 0; + + /* start at 2, because 0 is for nlmsghdr and 1 for event */ + iov = iovs + 2; + + if (!chap_fill_param_uint(&iov[count], ISCSI_CHAP_PARAM_INDEX, + crec->chap_tbl_idx, + sizeof(crec->chap_tbl_idx))) + count++; + + if (!chap_fill_param_uint(&iov[count], ISCSI_CHAP_PARAM_CHAP_TYPE, + crec->chap_type, sizeof(crec->chap_type))) + count++; + + if (!chap_fill_param_str(&iov[count], ISCSI_CHAP_PARAM_USERNAME, + crec->username, strlen(crec->username))) + count++; + + if (!chap_fill_param_str(&iov[count], ISCSI_CHAP_PARAM_PASSWORD, + (char *)crec->password, + strlen((char *)crec->password))) + count++; + + if (!chap_fill_param_uint(&iov[count], ISCSI_CHAP_PARAM_PASSWORD_LEN, + crec->password_length, + sizeof(crec->password_length))) + count++; + + return count; +} diff --git a/usr/host.h b/usr/host.h index 894ab91..149aa0d 100644 --- a/usr/host.h +++ b/usr/host.h @@ -5,6 +5,9 @@ #include "types.h" #include "config.h" +#define MAX_HOST_NO UINT_MAX + +#define MAX_CHAP_ENTRIES 2048 #define MAX_CHAP_BUF_SZ 4096 #define REQ_CHAP_BUF_SZ (MAX_CHAP_BUF_SZ + sizeof(struct iscsi_uevent)) @@ -14,5 +17,6 @@ struct host_info { }; extern int host_info_print(int info_level, uint32_t host_no); +extern int chap_build_config(struct iscsi_chap_rec *crec, struct iovec *iovs); #endif diff --git a/usr/idbm.c b/usr/idbm.c index 4d30aa9..198a5ef 100644 --- a/usr/idbm.c +++ b/usr/idbm.c @@ -94,6 +94,17 @@ static struct idbm *db; _n++; \ } while (0) +#define __recinfo_uint32(_key, _info, _rec, _name, _show, _n, _mod) do { \ + _info[_n].type = TYPE_UINT32; \ + strlcpy(_info[_n].name, _key, NAME_MAXVAL); \ + snprintf(_info[_n].value, VALUE_MAXVAL, "%d", _rec->_name); \ + _info[_n].data = &_rec->_name; \ + _info[_n].data_len = sizeof(_rec->_name); \ + _info[_n].visible = _show; \ + _info[_n].can_modify = _mod; \ + _n++; \ +} while (0) + #define __recinfo_int_o2(_key,_info,_rec,_name,_show,_op0,_op1,_n, _mod) do { \ _info[_n].type = TYPE_INT_O; \ strlcpy(_info[_n].name, _key, NAME_MAXVAL); \ @@ -226,6 +237,9 @@ void idbm_recinfo_node(node_rec_t *r, recinfo_t *ri) { int num = 0, i; + int iface_type; + + iface_type = iface_get_iptype(&r->iface); __recinfo_str(NODE_NAME, ri, r, name, IDBM_SHOW, num, 0); __recinfo_int(NODE_TPGT, ri, r, tpgt, IDBM_SHOW, num, 0); @@ -248,6 +262,8 @@ idbm_recinfo_node(node_rec_t *r, recinfo_t *ri) __recinfo_str(IFACE_IPADDR, ri, r, iface.ipaddress, IDBM_SHOW, num, 1); __recinfo_str(IFACE_ISCSINAME, ri, r, iface.name, IDBM_SHOW, num, 1); __recinfo_str(IFACE_NETNAME, ri, r, iface.netdev, IDBM_SHOW, num, 1); + __recinfo_str(IFACE_GATEWAY, ri, r, iface.gateway, IDBM_SHOW, num, 1); + __recinfo_str(IFACE_SUBNET_MASK, ri, r, iface.subnet_mask, IDBM_SHOW, num, 1); /* * svn 780 compat: older versions used node.transport_name and * rec->transport_name @@ -255,21 +271,6 @@ idbm_recinfo_node(node_rec_t *r, recinfo_t *ri) __recinfo_str(IFACE_TRANSPORTNAME, ri, r, iface.transport_name, IDBM_SHOW, num, 1); __recinfo_str(IFACE_INAME, ri, r, iface.iname, IDBM_SHOW, num, 1); - __recinfo_str(IFACE_BOOT_PROTO, ri, r, iface.bootproto, IDBM_SHOW, - num, 1); - __recinfo_str(IFACE_SUBNET_MASK, ri, r, iface.subnet_mask, - IDBM_SHOW, num, 1); - __recinfo_str(IFACE_GATEWAY, ri, r, iface.gateway, IDBM_SHOW, num, 1); - __recinfo_str(IFACE_IPV6_AUTOCFG, ri, r, iface.ipv6_autocfg, - IDBM_SHOW, num, 1); - __recinfo_str(IFACE_LINKLOCAL_AUTOCFG, ri, r, iface.linklocal_autocfg, - IDBM_SHOW, num, 1); - __recinfo_str(IFACE_ROUTER_AUTOCFG, ri, r, iface.router_autocfg, - IDBM_SHOW, num, 1); - __recinfo_str(IFACE_LINKLOCAL, ri, r, iface.ipv6_linklocal, - IDBM_SHOW, num, 1); - __recinfo_str(IFACE_ROUTER, ri, r, iface.ipv6_router, IDBM_SHOW, num, - 1); __recinfo_str(IFACE_STATE, ri, r, iface.state, IDBM_SHOW, num, 1); __recinfo_uint16(IFACE_VLAN_ID, ri, r, iface.vlan_id, IDBM_SHOW, num, 1); @@ -281,6 +282,115 @@ idbm_recinfo_node(node_rec_t *r, recinfo_t *ri) __recinfo_uint16(IFACE_MTU, ri, r, iface.mtu, IDBM_SHOW, num, 1); __recinfo_uint16(IFACE_PORT, ri, r, iface.port, IDBM_SHOW, num, 1); + if (iface_type == ISCSI_IFACE_TYPE_IPV4) { + __recinfo_str(IFACE_BOOT_PROTO, ri, r, iface.bootproto, + IDBM_SHOW, num, 1); + __recinfo_str(IFACE_DHCP_ALT_CID, ri, r, + iface.dhcp_alt_client_id_state, IDBM_SHOW, + num, 1); + __recinfo_str(IFACE_DHCP_ALT_CID_STR, ri, r, + iface.dhcp_alt_client_id, IDBM_SHOW, num, 1); + __recinfo_str(IFACE_DHCP_DNS, ri, r, iface.dhcp_dns, IDBM_SHOW, + num, 1); + __recinfo_str(IFACE_DHCP_LEARN_IQN, ri, r, + iface.dhcp_learn_iqn, IDBM_SHOW, num, 1); + __recinfo_str(IFACE_DHCP_REQ_VID, ri, r, + iface.dhcp_req_vendor_id_state, IDBM_SHOW, + num, 1); + __recinfo_str(IFACE_DHCP_VID, ri, r, iface.dhcp_vendor_id_state, + IDBM_SHOW, num, 1); + __recinfo_str(IFACE_DHCP_VID_STR, ri, r, iface.dhcp_vendor_id, + IDBM_SHOW, num, 1); + __recinfo_str(IFACE_DHCP_SLP_DA, ri, r, iface.dhcp_slp_da, + IDBM_SHOW, num, 1); + __recinfo_str(IFACE_FRAGMENTATION, ri, r, iface.fragmentation, + IDBM_SHOW, num, 1); + __recinfo_str(IFACE_GRAT_ARP, ri, r, iface.gratuitous_arp, + IDBM_SHOW, num, 1); + __recinfo_str(IFACE_IN_FORWARD, ri, r, + iface.incoming_forwarding, IDBM_SHOW, num, 1); + __recinfo_str(IFACE_TOS_STATE, ri, r, iface.tos_state, + IDBM_SHOW, num, 1); + __recinfo_uint8(IFACE_TOS, ri, r, iface.tos, IDBM_SHOW, num, 1); + __recinfo_uint8(IFACE_TTL, ri, r, iface.ttl, IDBM_SHOW, num, 1); + } else if (iface_type == ISCSI_IFACE_TYPE_IPV6) { + __recinfo_str(IFACE_IPV6_AUTOCFG, ri, r, iface.ipv6_autocfg, + IDBM_SHOW, num, 1); + __recinfo_str(IFACE_LINKLOCAL_AUTOCFG, ri, r, + iface.linklocal_autocfg, IDBM_SHOW, num, 1); + __recinfo_str(IFACE_ROUTER_AUTOCFG, ri, r, iface.router_autocfg, + IDBM_SHOW, num, 1); + __recinfo_str(IFACE_LINKLOCAL, ri, r, iface.ipv6_linklocal, + IDBM_SHOW, num, 1); + __recinfo_str(IFACE_ROUTER, ri, r, iface.ipv6_router, + IDBM_SHOW, num, 1); + __recinfo_uint8(IFACE_DUP_ADDR_DETECT_CNT, ri, r, + iface.dup_addr_detect_cnt, IDBM_SHOW, num, 1); + __recinfo_uint32(IFACE_FLOW_LABEL, ri, r, iface.flow_label, + IDBM_SHOW, num, 1); + __recinfo_str(IFACE_GRAT_NEIGHBOR_ADV, ri, r, + iface.gratuitous_neighbor_adv, IDBM_SHOW, num, 1); + __recinfo_uint8(IFACE_HOP_LIMIT, ri, r, iface.hop_limit, + IDBM_SHOW, num, 1); + __recinfo_str(IFACE_MLD, ri, r, iface.mld, IDBM_SHOW, num, 1); + __recinfo_uint32(IFACE_ND_REACHABLE_TMO, ri, r, + iface.nd_reachable_tmo, IDBM_SHOW, num, 1); + __recinfo_uint32(IFACE_ND_REXMIT_TIME, ri, r, + iface.nd_rexmit_time, IDBM_SHOW, num, 1); + __recinfo_uint32(IFACE_ND_STALE_TMO, ri, r, iface.nd_stale_tmo, + IDBM_SHOW, num, 1); + __recinfo_uint32(IFACE_RTR_ADV_LINK_MTU, ri, r, + iface.router_adv_link_mtu, IDBM_SHOW, num, 1); + __recinfo_uint8(IFACE_TRAFFIC_CLASS, ri, r, iface.traffic_class, + IDBM_SHOW, num, 1); + } + + __recinfo_str(IFACE_DELAYED_ACK, ri, r, iface.delayed_ack, IDBM_SHOW, + num, 1); + __recinfo_str(IFACE_TCP_NAGLE, ri, r, iface.nagle, IDBM_SHOW, num, 1); + __recinfo_str(IFACE_TCP_WSF_STATE, ri, r, iface.tcp_wsf_state, + IDBM_SHOW, num, 1); + __recinfo_uint8(IFACE_TCP_WSF, ri, r, iface.tcp_wsf, IDBM_SHOW, num, 1); + __recinfo_uint8(IFACE_TCP_TIMER_SCALE, ri, r, iface.tcp_timer_scale, + IDBM_SHOW, num, 1); + __recinfo_str(IFACE_TCP_TIMESTAMP, ri, r, iface.tcp_timestamp, + IDBM_SHOW, num, 1); + __recinfo_str(IFACE_REDIRECT, ri, r, iface.redirect, IDBM_SHOW, num, 1); + __recinfo_uint16(IFACE_DEF_TMF_TMO, ri, r, iface.def_task_mgmt_tmo, + IDBM_SHOW, num, 1); + __recinfo_str(IFACE_HDRDGST, ri, r, iface.header_digest, IDBM_SHOW, + num, 1); + __recinfo_str(IFACE_DATADGST, ri, r, iface.data_digest, IDBM_SHOW, + num, 1); + __recinfo_str(IFACE_IMM_DATA, ri, r, iface.immediate_data, IDBM_SHOW, + num, 1); + __recinfo_str(IFACE_INITIAL_R2T, ri, r, iface.initial_r2t, IDBM_SHOW, + num, 1); + __recinfo_str(IFACE_DSEQ_INORDER, ri, r, iface.data_seq_inorder, + IDBM_SHOW, num, 1); + __recinfo_str(IFACE_DPDU_INORDER, ri, r, iface.data_pdu_inorder, + IDBM_SHOW, num, 1); + __recinfo_uint8(IFACE_ERL, ri, r, iface.erl, IDBM_SHOW, num, 1); + __recinfo_uint32(IFACE_MAX_RECV_DLEN, ri, r, iface.max_recv_dlength, + IDBM_SHOW, num, 1); + __recinfo_uint32(IFACE_FIRST_BURST, ri, r, iface.first_burst_len, + IDBM_SHOW, num, 1); + __recinfo_uint16(IFACE_MAX_R2T, ri, r, iface.max_out_r2t, IDBM_SHOW, + num, 1); + __recinfo_uint32(IFACE_MAX_BURST, ri, r, iface.max_burst_len, IDBM_SHOW, + num, 1); + __recinfo_str(IFACE_CHAP_AUTH, ri, r, iface.chap_auth, IDBM_SHOW, + num, 1); + __recinfo_str(IFACE_BIDI_CHAP, ri, r, iface.bidi_chap, IDBM_SHOW, + num, 1); + __recinfo_str(IFACE_STRICT_LOGIN_COMP, ri, r, iface.strict_login_comp, + IDBM_SHOW, num, 1); + __recinfo_str(IFACE_DISCOVERY_AUTH, ri, r, iface.discovery_auth, + IDBM_SHOW, num, 1); + __recinfo_str(IFACE_DISCOVERY_LOGOUT, ri, r, iface.discovery_logout, + IDBM_SHOW, num, 1); + + __recinfo_str(NODE_DISC_ADDR, ri, r, disc_address, IDBM_SHOW, num, 0); __recinfo_int(NODE_DISC_PORT, ri, r, disc_port, IDBM_SHOW, @@ -414,6 +524,9 @@ idbm_recinfo_node(node_rec_t *r, recinfo_t *ri) void idbm_recinfo_iface(iface_rec_t *r, recinfo_t *ri) { int num = 0; + int iface_type; + + iface_type = iface_get_iptype(r); __recinfo_str(IFACE_ISCSINAME, ri, r, name, IDBM_SHOW, num, 0); __recinfo_str(IFACE_NETNAME, ri, r, netdev, IDBM_SHOW, num, 1); @@ -422,19 +535,6 @@ void idbm_recinfo_iface(iface_rec_t *r, recinfo_t *ri) __recinfo_str(IFACE_TRANSPORTNAME, ri, r, transport_name, IDBM_SHOW, num, 1); __recinfo_str(IFACE_INAME, ri, r, iname, IDBM_SHOW, num, 1); - __recinfo_str(IFACE_BOOT_PROTO, ri, r, bootproto, IDBM_SHOW, num, 1); - __recinfo_str(IFACE_SUBNET_MASK, ri, r, subnet_mask, - IDBM_SHOW, num, 1); - __recinfo_str(IFACE_GATEWAY, ri, r, gateway, IDBM_SHOW, num, 1); - __recinfo_str(IFACE_IPV6_AUTOCFG, ri, r, ipv6_autocfg, - IDBM_SHOW, num, 1); - __recinfo_str(IFACE_LINKLOCAL_AUTOCFG, ri, r, linklocal_autocfg, - IDBM_SHOW, num, 1); - __recinfo_str(IFACE_ROUTER_AUTOCFG, ri, r, router_autocfg, - IDBM_SHOW, num, 1); - __recinfo_str(IFACE_LINKLOCAL, ri, r, ipv6_linklocal, - IDBM_SHOW, num, 1); - __recinfo_str(IFACE_ROUTER, ri, r, ipv6_router, IDBM_SHOW, num, 1); __recinfo_str(IFACE_STATE, ri, r, state, IDBM_SHOW, num, 1); __recinfo_uint16(IFACE_VLAN_ID, ri, r, vlan_id, IDBM_SHOW, num, 1); __recinfo_uint8(IFACE_VLAN_PRIORITY, ri, r, vlan_priority, @@ -443,9 +543,110 @@ void idbm_recinfo_iface(iface_rec_t *r, recinfo_t *ri) __recinfo_int(IFACE_NUM, ri, r, iface_num, IDBM_SHOW, num, 1); __recinfo_uint16(IFACE_MTU, ri, r, mtu, IDBM_SHOW, num, 1); __recinfo_uint16(IFACE_PORT, ri, r, port, IDBM_SHOW, num, 1); + + if (iface_type == ISCSI_IFACE_TYPE_IPV4) { + __recinfo_str(IFACE_BOOT_PROTO, ri, r, bootproto, IDBM_SHOW, + num, 1); + __recinfo_str(IFACE_SUBNET_MASK, ri, r, subnet_mask, IDBM_SHOW, + num, 1); + __recinfo_str(IFACE_GATEWAY, ri, r, gateway, IDBM_SHOW, num, 1); + __recinfo_str(IFACE_DHCP_ALT_CID, ri, r, + dhcp_alt_client_id_state, IDBM_SHOW, num, 1); + __recinfo_str(IFACE_DHCP_ALT_CID_STR, ri, r, dhcp_alt_client_id, + IDBM_SHOW, num, 1); + __recinfo_str(IFACE_DHCP_DNS, ri, r, dhcp_dns, IDBM_SHOW, + num, 1); + __recinfo_str(IFACE_DHCP_LEARN_IQN, ri, r, dhcp_learn_iqn, + IDBM_SHOW, num, 1); + __recinfo_str(IFACE_DHCP_REQ_VID, ri, r, + dhcp_req_vendor_id_state, IDBM_SHOW, num, 1); + __recinfo_str(IFACE_DHCP_VID, ri, r, dhcp_vendor_id_state, + IDBM_SHOW, num, 1); + __recinfo_str(IFACE_DHCP_VID_STR, ri, r, dhcp_vendor_id, + IDBM_SHOW, num, 1); + __recinfo_str(IFACE_DHCP_SLP_DA, ri, r, dhcp_slp_da, IDBM_SHOW, + num, 1); + __recinfo_str(IFACE_FRAGMENTATION, ri, r, fragmentation, + IDBM_SHOW, num, 1); + __recinfo_str(IFACE_GRAT_ARP, ri, r, gratuitous_arp, IDBM_SHOW, + num, 1); + __recinfo_str(IFACE_IN_FORWARD, ri, r, incoming_forwarding, + IDBM_SHOW, num, 1); + __recinfo_str(IFACE_TOS_STATE, ri, r, tos_state, IDBM_SHOW, + num, 1); + __recinfo_uint8(IFACE_TOS, ri, r, tos, IDBM_SHOW, num, 1); + __recinfo_uint8(IFACE_TTL, ri, r, ttl, IDBM_SHOW, num, 1); + } else if (iface_type == ISCSI_IFACE_TYPE_IPV6) { + __recinfo_str(IFACE_IPV6_AUTOCFG, ri, r, ipv6_autocfg, + IDBM_SHOW, num, 1); + __recinfo_str(IFACE_LINKLOCAL_AUTOCFG, ri, r, linklocal_autocfg, + IDBM_SHOW, num, 1); + __recinfo_str(IFACE_ROUTER_AUTOCFG, ri, r, router_autocfg, + IDBM_SHOW, num, 1); + __recinfo_str(IFACE_LINKLOCAL, ri, r, ipv6_linklocal, IDBM_SHOW, + num, 1); + __recinfo_str(IFACE_ROUTER, ri, r, ipv6_router, IDBM_SHOW, + num, 1); + __recinfo_uint8(IFACE_DUP_ADDR_DETECT_CNT, ri, r, + dup_addr_detect_cnt, IDBM_SHOW, num, 1); + __recinfo_uint32(IFACE_FLOW_LABEL, ri, r, flow_label, IDBM_SHOW, + num, 1); + __recinfo_str(IFACE_GRAT_NEIGHBOR_ADV, ri, r, + gratuitous_neighbor_adv, IDBM_SHOW, num, 1); + __recinfo_uint8(IFACE_HOP_LIMIT, ri, r, hop_limit, IDBM_SHOW, + num, 1); + __recinfo_str(IFACE_MLD, ri, r, mld, IDBM_SHOW, num, 1); + __recinfo_uint32(IFACE_ND_REACHABLE_TMO, ri, r, + nd_reachable_tmo, IDBM_SHOW, num, 1); + __recinfo_uint32(IFACE_ND_REXMIT_TIME, ri, r, nd_rexmit_time, + IDBM_SHOW, num, 1); + __recinfo_uint32(IFACE_ND_STALE_TMO, ri, r, nd_stale_tmo, + IDBM_SHOW, num, 1); + __recinfo_uint32(IFACE_RTR_ADV_LINK_MTU, ri, r, + router_adv_link_mtu, IDBM_SHOW, num, 1); + __recinfo_uint8(IFACE_TRAFFIC_CLASS, ri, r, traffic_class, + IDBM_SHOW, num, 1); + } + + __recinfo_str(IFACE_DELAYED_ACK, ri, r, delayed_ack, IDBM_SHOW, num, 1); + __recinfo_str(IFACE_TCP_NAGLE, ri, r, nagle, IDBM_SHOW, num, 1); + __recinfo_str(IFACE_TCP_WSF_STATE, ri, r, tcp_wsf_state, IDBM_SHOW, + num, 1); + __recinfo_uint8(IFACE_TCP_WSF, ri, r, tcp_wsf, IDBM_SHOW, num, 1); + __recinfo_uint8(IFACE_TCP_TIMER_SCALE, ri, r, tcp_timer_scale, + IDBM_SHOW, num, 1); + __recinfo_str(IFACE_TCP_TIMESTAMP, ri, r, tcp_timestamp, IDBM_SHOW, + num, 1); + __recinfo_str(IFACE_REDIRECT, ri, r, redirect, IDBM_SHOW, num, 1); + __recinfo_uint16(IFACE_DEF_TMF_TMO, ri, r, def_task_mgmt_tmo, IDBM_SHOW, + num, 1); + __recinfo_str(IFACE_HDRDGST, ri, r, header_digest, IDBM_SHOW, num, 1); + __recinfo_str(IFACE_DATADGST, ri, r, data_digest, IDBM_SHOW, num, 1); + __recinfo_str(IFACE_IMM_DATA, ri, r, immediate_data, IDBM_SHOW, num, 1); + __recinfo_str(IFACE_INITIAL_R2T, ri, r, initial_r2t, IDBM_SHOW, num, 1); + __recinfo_str(IFACE_DSEQ_INORDER, ri, r, data_seq_inorder, IDBM_SHOW, + num, 1); + __recinfo_str(IFACE_DPDU_INORDER, ri, r, data_pdu_inorder, IDBM_SHOW, + num, 1); + __recinfo_uint8(IFACE_ERL, ri, r, erl, IDBM_SHOW, num, 1); + __recinfo_uint32(IFACE_MAX_RECV_DLEN, ri, r, max_recv_dlength, + IDBM_SHOW, num, 1); + __recinfo_uint32(IFACE_FIRST_BURST, ri, r, first_burst_len, IDBM_SHOW, + num, 1); + __recinfo_uint16(IFACE_MAX_R2T, ri, r, max_out_r2t, IDBM_SHOW, num, 1); + __recinfo_uint32(IFACE_MAX_BURST, ri, r, max_burst_len, IDBM_SHOW, + num, 1); + __recinfo_str(IFACE_CHAP_AUTH, ri, r, chap_auth, IDBM_SHOW, num, 1); + __recinfo_str(IFACE_BIDI_CHAP, ri, r, bidi_chap, IDBM_SHOW, num, 1); + __recinfo_str(IFACE_STRICT_LOGIN_COMP, ri, r, strict_login_comp, + IDBM_SHOW, num, 1); + __recinfo_str(IFACE_DISCOVERY_AUTH, ri, r, discovery_auth, IDBM_SHOW, + num, 1); + __recinfo_str(IFACE_DISCOVERY_LOGOUT, ri, r, discovery_logout, + IDBM_SHOW, num, 1); } -static void idbm_recinfo_host_chap(struct iscsi_chap_rec *r, recinfo_t *ri) +void idbm_recinfo_host_chap(struct iscsi_chap_rec *r, recinfo_t *ri) { int num = 0; @@ -454,14 +655,14 @@ static void idbm_recinfo_host_chap(struct iscsi_chap_rec *r, recinfo_t *ri) if (r->chap_type == CHAP_TYPE_OUT) { __recinfo_str(HOST_AUTH_USERNAME, ri, r, username, IDBM_SHOW, - num, 0); + num, 1); __recinfo_str(HOST_AUTH_PASSWORD, ri, r, password, IDBM_MASKED, num, 1); __recinfo_int(HOST_AUTH_PASSWORD_LEN, ri, r, password_length, IDBM_HIDE, num, 1); } else { __recinfo_str(HOST_AUTH_USERNAME_IN, ri, r, username, IDBM_SHOW, - num, 0); + num, 1); __recinfo_str(HOST_AUTH_PASSWORD_IN, ri, r, password, IDBM_MASKED, num, 1); __recinfo_int(HOST_AUTH_PASSWORD_IN_LEN, ri, r, password_length, @@ -469,6 +670,158 @@ static void idbm_recinfo_host_chap(struct iscsi_chap_rec *r, recinfo_t *ri) } } +void idbm_recinfo_flashnode(struct flashnode_rec *r, recinfo_t *ri) +{ + int num = 0; + int i; + + __recinfo_uint8(FLASHNODE_SESS_AUTO_SND_TGT_DISABLE, ri, r, + sess.auto_snd_tgt_disable, IDBM_SHOW, num, 1); + __recinfo_uint8(FLASHNODE_SESS_DISCOVERY_SESS, ri, r, + sess.discovery_session, IDBM_SHOW, num, 1); + __recinfo_str(FLASHNODE_SESS_PORTAL_TYPE, ri, r, sess.portal_type, + IDBM_SHOW, num, 1); + __recinfo_uint8(FLASHNODE_SESS_ENTRY_EN, ri, r, + sess.entry_enable, IDBM_SHOW, num, 1); + __recinfo_uint8(FLASHNODE_SESS_IMM_DATA_EN, ri, r, sess.immediate_data, + IDBM_SHOW, num, 1); + __recinfo_uint8(FLASHNODE_SESS_INITIAL_R2T_EN, ri, r, sess.initial_r2t, + IDBM_SHOW, num, 1); + __recinfo_uint8(FLASHNODE_SESS_DATASEQ_INORDER, ri, r, + sess.data_seq_in_order, IDBM_SHOW, num, 1); + __recinfo_uint8(FLASHNODE_SESS_PDU_INORDER, ri, r, + sess.data_pdu_in_order, IDBM_SHOW, num, 1); + __recinfo_uint8(FLASHNODE_SESS_CHAP_AUTH_EN, ri, r, sess.chap_auth_en, + IDBM_SHOW, num, 1); + __recinfo_uint8(FLASHNODE_SESS_DISCOVERY_LOGOUT_EN, ri, r, + sess.discovery_logout_en, IDBM_SHOW, num, 1); + __recinfo_uint8(FLASHNODE_SESS_BIDI_CHAP_EN, ri, r, sess.bidi_chap_en, + IDBM_SHOW, num, 1); + __recinfo_uint8(FLASHNODE_SESS_DISCOVERY_AUTH_OPTIONAL, ri, r, + sess.discovery_auth_optional, IDBM_SHOW, num, 1); + __recinfo_uint8(FLASHNODE_SESS_ERL, ri, r, sess.erl, IDBM_SHOW, num, 1); + __recinfo_uint32(FLASHNODE_SESS_FIRST_BURST, ri, r, + sess.first_burst_len, IDBM_SHOW, num, 1); + __recinfo_uint16(FLASHNODE_SESS_DEF_TIME2WAIT, ri, r, + sess.def_time2wait, IDBM_SHOW, num, 1); + __recinfo_uint16(FLASHNODE_SESS_DEF_TIME2RETAIN, ri, r, + sess.def_time2retain, IDBM_SHOW, num, 1); + __recinfo_uint16(FLASHNODE_SESS_MAX_R2T, ri, r, + sess.max_outstanding_r2t, IDBM_SHOW, num, 1); + __recinfo_str(FLASHNODE_SESS_ISID, ri, r, sess.isid, IDBM_SHOW, num, 1); + __recinfo_uint16(FLASHNODE_SESS_TSID, ri, r, sess.tsid, IDBM_SHOW, + num, 1); + __recinfo_uint32(FLASHNODE_SESS_MAX_BURST, ri, r, sess.max_burst_len, + IDBM_SHOW, num, 1); + __recinfo_uint16(FLASHNODE_SESS_DEF_TASKMGMT_TMO, ri, r, + sess.def_taskmgmt_tmo, IDBM_SHOW, num, 1); + __recinfo_str(FLASHNODE_SESS_ALIAS, ri, r, sess.targetalias, IDBM_SHOW, + num, 1); + __recinfo_str(FLASHNODE_SESS_NAME, ri, r, sess.targetname, IDBM_SHOW, + num, 1); + __recinfo_uint16(FLASHNODE_SESS_DISCOVERY_PARENT_IDX, ri, r, + sess.discovery_parent_idx, IDBM_SHOW, num, 1); + __recinfo_str(FLASHNODE_SESS_DISCOVERY_PARENT_TYPE, ri, r, + sess.discovery_parent_type, IDBM_SHOW, num, 1); + __recinfo_uint16(FLASHNODE_SESS_TPGT, ri, r, sess.tpgt, IDBM_SHOW, + num, 1); + __recinfo_uint16(FLASHNODE_SESS_CHAP_OUT_IDX, ri, r, sess.chap_out_idx, + IDBM_SHOW, num, 1); + __recinfo_uint16(FLASHNODE_SESS_CHAP_IN_IDX, ri, r, sess.chap_in_idx, + IDBM_SHOW, num, 1); + __recinfo_str(FLASHNODE_SESS_USERNAME, ri, r, sess.username, IDBM_SHOW, + num, 1); + __recinfo_str(FLASHNODE_SESS_USERNAME_IN, ri, r, sess.username_in, + IDBM_SHOW, num, 1); + __recinfo_str(FLASHNODE_SESS_PASSWORD, ri, r, sess.password, IDBM_SHOW, + num, 1); + __recinfo_str(FLASHNODE_SESS_PASSWORD_IN, ri, r, sess.password_in, + IDBM_SHOW, num, 1); + __recinfo_uint8(FLASHNODE_SESS_IS_BOOT_TGT, ri, r, sess.is_boot_target, + IDBM_SHOW, num, 1); + + for (i = 0; i < ISCSI_CONN_MAX; i++) { + char key[NAME_MAXVAL]; + + sprintf(key, FLASHNODE_CONN_IS_FW_ASSIGNED_IPV6, i); + __recinfo_uint8(key, ri, r, conn[i].is_fw_assigned_ipv6, + IDBM_SHOW, num, 1); + sprintf(key, FLASHNODE_CONN_HDR_DGST_EN, i); + __recinfo_uint8(key, ri, r, conn[i].header_digest_en, IDBM_SHOW, + num, 1); + sprintf(key, FLASHNODE_CONN_DATA_DGST_EN, i); + __recinfo_uint8(key, ri, r, conn[i].data_digest_en, IDBM_SHOW, + num, 1); + sprintf(key, FLASHNODE_CONN_SNACK_REQ_EN, i); + __recinfo_uint8(key, ri, r, conn[i].snack_req_en, IDBM_SHOW, + num, 1); + sprintf(key, FLASHNODE_CONN_TCP_TIMESTAMP_STAT, i); + __recinfo_uint8(key, ri, r, conn[i].tcp_timestamp_stat, + IDBM_SHOW, num, 1); + sprintf(key, FLASHNODE_CONN_TCP_NAGLE_DISABLE, i); + __recinfo_uint8(key, ri, r, conn[i].tcp_nagle_disable, + IDBM_SHOW, num, 1); + sprintf(key, FLASHNODE_CONN_TCP_WSF_DISABLE, i); + __recinfo_uint8(key, ri, r, conn[i].tcp_wsf_disable, IDBM_SHOW, + num, 1); + sprintf(key, FLASHNODE_CONN_TCP_TIMER_SCALE, i); + __recinfo_uint8(key, ri, r, conn[i].tcp_timer_scale, IDBM_SHOW, + num, 1); + sprintf(key, FLASHNODE_CONN_TCP_TIMESTAMP_EN, i); + __recinfo_uint8(key, ri, r, conn[i].tcp_timestamp_en, + IDBM_SHOW, num, 1); + sprintf(key, FLASHNODE_CONN_IP_FRAG_DISABLE, i); + __recinfo_uint8(key, ri, r, conn[i].fragment_disable, IDBM_SHOW, + num, 1); + sprintf(key, FLASHNODE_CONN_MAX_XMIT_DLENGTH, i); + __recinfo_uint32(key, ri, r, conn[i].max_xmit_dlength, + IDBM_SHOW, num, 1); + sprintf(key, FLASHNODE_CONN_MAX_RECV_DLENGTH, i); + __recinfo_uint32(key, ri, r, conn[i].max_recv_dlength, + IDBM_SHOW, num, 1); + sprintf(key, FLASHNODE_CONN_KEEPALIVE_TMO, i); + __recinfo_uint16(key, ri, r, conn[i].keepalive_tmo, IDBM_SHOW, + num, 1); + sprintf(key, FLASHNODE_CONN_PORT, i); + __recinfo_uint16(key, ri, r, conn[i].port, IDBM_SHOW, num, 1); + sprintf(key, FLASHNODE_CONN_IPADDR, i); + __recinfo_str(key, ri, r, conn[i].ipaddress, IDBM_SHOW, num, 1); + sprintf(key, FLASHNODE_CONN_REDIRECT_IPADDR, i); + __recinfo_str(key, ri, r, conn[i].redirect_ipaddr, IDBM_SHOW, + num, 1); + sprintf(key, FLASHNODE_CONN_MAX_SEGMENT_SIZE, i); + __recinfo_uint32(key, ri, r, conn[i].max_segment_size, + IDBM_SHOW, num, 1); + sprintf(key, FLASHNODE_CONN_LOCAL_PORT, i); + __recinfo_uint16(key, ri, r, conn[i].local_port, IDBM_SHOW, + num, 1); + sprintf(key, FLASHNODE_CONN_IPV4_TOS, i); + __recinfo_uint8(key, ri, r, conn[i].ipv4_tos, IDBM_SHOW, + num, 1); + sprintf(key, FLASHNODE_CONN_IPV6_TC, i); + __recinfo_uint8(key, ri, r, conn[i].ipv6_traffic_class, + IDBM_SHOW, num, 1); + sprintf(key, FLASHNODE_CONN_IPV6_FLOW_LABEL, i); + __recinfo_uint16(key, ri, r, conn[i].ipv6_flow_lbl, IDBM_SHOW, + num, 1); + sprintf(key, FLASHNODE_CONN_LINK_LOCAL_IPV6, i); + __recinfo_str(key, ri, r, conn[i].link_local_ipv6, IDBM_SHOW, + num, 1); + sprintf(key, FLASHNODE_CONN_TCP_XMIT_WSF, i); + __recinfo_uint32(key, ri, r, conn[i].tcp_xmit_wsf, IDBM_SHOW, + num, 1); + sprintf(key, FLASHNODE_CONN_TCP_RECV_WSF, i); + __recinfo_uint32(key, ri, r, conn[i].tcp_recv_wsf, IDBM_SHOW, + num, 1); + sprintf(key, FLASHNODE_CONN_STATSN, i); + __recinfo_uint32(key, ri, r, conn[i].stat_sn, IDBM_SHOW, + num, 1); + sprintf(key, FLASHNODE_CONN_EXP_STATSN, i); + __recinfo_uint32(key, ri, r, conn[i].exp_stat_sn, IDBM_SHOW, + num, 1); + } +} + recinfo_t *idbm_recinfo_alloc(int max_keys) { recinfo_t *info; @@ -502,6 +855,9 @@ void idbm_print(int type, void *rec, int show, FILE *f) case IDBM_PRINT_TYPE_HOST_CHAP: idbm_recinfo_host_chap((struct iscsi_chap_rec *)rec, info); break; + case IDBM_PRINT_TYPE_FLASHNODE: + idbm_recinfo_flashnode((struct flashnode_rec *)rec, info); + break; } fprintf(f, "%s\n", ISCSI_BEGIN_REC); @@ -629,6 +985,13 @@ setup_passwd_len: *(uint16_t *)info[i].data = strtoul(value, NULL, 10); goto updated; + } else if (info[i].type == TYPE_UINT32) { + if (!info[i].data) + continue; + + *(uint32_t *)info[i].data = + strtoul(value, NULL, 10); + goto updated; } else if (info[i].type == TYPE_STR) { if (!info[i].data) continue; @@ -679,6 +1042,8 @@ updated: check_password_param(discovery.sendtargets.auth.password_in); check_password_param(discovery.slp.auth.password); check_password_param(discovery.slp.auth.password_in); + check_password_param(host.auth.password); + check_password_param(host.auth.password_in); return 0; } @@ -694,7 +1059,7 @@ int idbm_verify_param(recinfo_t *info, char *name) if (strcmp(name, info[i].name)) continue; - log_debug(7, "verify %s %d\n", name, info[i].can_modify); + log_debug(7, "verify %s %d", name, info[i].can_modify); if (info[i].can_modify) return 0; else { @@ -797,20 +1162,20 @@ static void idbm_sync_config(void) idbm_recinfo_node(&db->nrec, db->ninfo); if (!db->get_config_file) { - log_debug(1, "Could not get config file. No config file fn\n"); + log_debug(1, "Could not get config file. No config file fn"); return; } config_file = db->get_config_file(); if (!config_file) { - log_debug(1, "Could not get config file for sync config\n"); + log_debug(1, "Could not get config file for sync config"); return; } f = fopen(config_file, "r"); if (!f) { log_debug(1, "cannot open configuration file %s. " - "Default location is %s.\n", + "Default location is %s.", config_file, CONFIG_FILE); return; } @@ -880,6 +1245,12 @@ int idbm_print_host_chap_info(struct iscsi_chap_rec *chap) return 0; } +int idbm_print_flashnode_info(struct flashnode_rec *fnode) +{ + idbm_print(IDBM_PRINT_TYPE_FLASHNODE, fnode, 1, stdout); + return 0; +} + int idbm_print_node_flat(void *data, node_rec_t *rec) { if (strchr(rec->conn[0].address, '.')) @@ -960,7 +1331,7 @@ int idbm_lock(void) if (access(LOCK_DIR, F_OK) != 0) { if (mkdir(LOCK_DIR, 0660) != 0) { - log_error("Could not open %s: %s\n", LOCK_DIR, + log_error("Could not open %s: %s", LOCK_DIR, strerror(errno)); return ISCSI_ERR_IDBM; } @@ -1040,7 +1411,7 @@ static int __idbm_rec_read(node_rec_t *out_rec, char *conf) f = fopen(conf, "r"); if (!f) { - log_debug(5, "Could not open %s err %s\n", conf, + log_debug(5, "Could not open %s err %s", conf, strerror(errno)); rc = ISCSI_ERR_IDBM; goto unlock; @@ -1182,7 +1553,7 @@ static int idbm_for_each_drec(int type, char *config_root, void *data, !strcmp(entity_dent->d_name, "..")) continue; - log_debug(5, "found %s\n", entity_dent->d_name); + log_debug(5, "found %s", entity_dent->d_name); tmp_port = strchr(entity_dent->d_name, ','); if (!tmp_port) @@ -1439,7 +1810,7 @@ int idbm_for_each_portal(int *found, void *data, idbm_portal_op_fn *fn, !strcmp(portal_dent->d_name, "..")) continue; - log_debug(5, "found %s\n", portal_dent->d_name); + log_debug(5, "found %s", portal_dent->d_name); tmp_port = strchr(portal_dent->d_name, ','); if (!tmp_port) continue; @@ -1481,7 +1852,7 @@ int idbm_for_each_node(int *found, void *data, idbm_node_op_fn *fn) !strcmp(node_dent->d_name, "..")) continue; - log_debug(5, "searching %s\n", node_dent->d_name); + log_debug(5, "searching %s", node_dent->d_name); curr_rc = fn(found, data, node_dent->d_name); /* less than zero means it was not a match */ if (curr_rc > 0 && !rc) @@ -1557,7 +1928,7 @@ idbm_discovery_read(discovery_rec_t *out_rec, int drec_type, snprintf(portal, PATH_MAX, "%s/%s,%d", disc_type_to_config_vals[drec_type].config_root, addr, port); - log_debug(5, "Looking for config file %s\n", portal); + log_debug(5, "Looking for config file %s", portal); rc = idbm_lock(); if (rc) @@ -1566,7 +1937,7 @@ idbm_discovery_read(discovery_rec_t *out_rec, int drec_type, f = idbm_open_rec_r(portal, disc_type_to_config_vals[drec_type].config_name); if (!f) { - log_debug(1, "Could not open %s: %s\n", portal, + log_debug(1, "Could not open %s: %s", portal, strerror(errno)); rc = ISCSI_ERR_IDBM; goto unlock; @@ -1596,7 +1967,7 @@ static FILE *idbm_open_rec_w(char *portal, char *config) FILE *f; int err; - log_debug(5, "Looking for config file %s\n", portal); + log_debug(5, "Looking for config file %s", portal); err = stat(portal, &statb); if (err) @@ -1608,14 +1979,14 @@ static FILE *idbm_open_rec_w(char *portal, char *config) */ if (unlink(portal)) { log_error("Could not convert %s to %s/%s. " - "err %d\n", portal, portal, + "err %d", portal, portal, config, errno); return NULL; } mkdir_portal: if (mkdir(portal, 0660) != 0) { - log_error("Could not make dir %s err %d\n", + log_error("Could not make dir %s err %d", portal, errno); return NULL; } @@ -1625,7 +1996,7 @@ mkdir_portal: strlcat(portal, config, PATH_MAX); f = fopen(portal, "w"); if (!f) - log_error("Could not open %s err %d\n", portal, errno); + log_error("Could not open %s err %d", portal, errno); return f; } @@ -1638,14 +2009,14 @@ static int idbm_rec_write(node_rec_t *rec) portal = malloc(PATH_MAX); if (!portal) { - log_error("Could not alloc portal\n"); + log_error("Could not alloc portal"); return ISCSI_ERR_NOMEM; } snprintf(portal, PATH_MAX, "%s", NODE_CONFIG_DIR); if (access(portal, F_OK) != 0) { if (mkdir(portal, 0660) != 0) { - log_error("Could not make %s: %s\n", portal, + log_error("Could not make %s: %s", portal, strerror(errno)); rc = ISCSI_ERR_IDBM; goto free_portal; @@ -1655,7 +2026,7 @@ static int idbm_rec_write(node_rec_t *rec) snprintf(portal, PATH_MAX, "%s/%s", NODE_CONFIG_DIR, rec->name); if (access(portal, F_OK) != 0) { if (mkdir(portal, 0660) != 0) { - log_error("Could not make %s: %s\n", portal, + log_error("Could not make %s: %s", portal, strerror(errno)); rc = ISCSI_ERR_IDBM; goto free_portal; @@ -1698,7 +2069,7 @@ static int idbm_rec_write(node_rec_t *rec) * Old style portal as a file, but with tpgt. Let's update it. */ if (unlink(portal)) { - log_error("Could not convert %s: %s\n", portal, + log_error("Could not convert %s: %s", portal, strerror(errno)); rc = ISCSI_ERR_IDBM; goto unlock; @@ -1713,7 +2084,7 @@ mkdir_portal: rec->name, rec->conn[0].address, rec->conn[0].port, rec->tpgt); if (stat(portal, &statb)) { if (mkdir(portal, 0660) != 0) { - log_error("Could not make dir %s: %s\n", + log_error("Could not make dir %s: %s", portal, strerror(errno)); rc = ISCSI_ERR_IDBM; goto unlock; @@ -1726,7 +2097,7 @@ mkdir_portal: open_conf: f = fopen(portal, "w"); if (!f) { - log_error("Could not open %s: %sd\n", portal, strerror(errno)); + log_error("Could not open %s: %s", portal, strerror(errno)); rc = ISCSI_ERR_IDBM; goto unlock; } @@ -1752,7 +2123,7 @@ idbm_discovery_write(discovery_rec_t *rec) portal = malloc(PATH_MAX); if (!portal) { - log_error("Could not alloc portal\n"); + log_error("Could not alloc portal"); return ISCSI_ERR_NOMEM; } @@ -1764,7 +2135,7 @@ idbm_discovery_write(discovery_rec_t *rec) disc_type_to_config_vals[rec->type].config_root); if (access(portal, F_OK) != 0) { if (mkdir(portal, 0660) != 0) { - log_error("Could not make %s: %s\n", portal, + log_error("Could not make %s: %s", portal, strerror(errno)); rc = ISCSI_ERR_IDBM; goto unlock; @@ -1778,7 +2149,7 @@ idbm_discovery_write(discovery_rec_t *rec) f = idbm_open_rec_w(portal, disc_type_to_config_vals[rec->type].config_name); if (!f) { - log_error("Could not open %s: %s\n", portal, strerror(errno)); + log_error("Could not open %s: %s", portal, strerror(errno)); rc = ISCSI_ERR_IDBM; goto unlock; } @@ -2150,10 +2521,10 @@ int idbm_delete_discovery(discovery_rec_t *drec) snprintf(portal, PATH_MAX, "%s/%s,%d", disc_type_to_config_vals[drec->type].config_root, drec->address, drec->port); - log_debug(5, "Removing config file %s\n", portal); + log_debug(5, "Removing config file %s", portal); if (stat(portal, &statb)) { - log_debug(5, "Could not stat %s to delete disc err %d\n", + log_debug(5, "Could not stat %s to delete disc err %d", portal, errno); goto free_portal; } @@ -2166,7 +2537,7 @@ int idbm_delete_discovery(discovery_rec_t *drec) } if (unlink(portal)) - log_debug(5, "Could not remove %s err %d\n", portal, errno); + log_debug(5, "Could not remove %s err %d", portal, errno); memset(portal, 0, PATH_MAX); snprintf(portal, PATH_MAX, "%s/%s,%d", @@ -2216,7 +2587,7 @@ static int idbm_remove_disc_to_node_link(node_rec_t *rec, goto done; } - log_debug(7, "found drec %s %d\n", + log_debug(7, "found drec %s %d", tmprec->disc_address, tmprec->disc_port); /* rm link from discovery source to node */ memset(portal, 0, PATH_MAX); @@ -2230,7 +2601,7 @@ static int idbm_remove_disc_to_node_link(node_rec_t *rec, if (!stat(portal, &statb)) { if (unlink(portal)) { - log_error("Could not remove link %s: %s\n", + log_error("Could not remove link %s: %s", portal, strerror(errno)); rc = ISCSI_ERR_IDBM; } else @@ -2267,7 +2638,7 @@ int idbm_delete_node(node_rec_t *rec) memset(portal, 0, PATH_MAX); snprintf(portal, PATH_MAX, "%s/%s/%s,%d", NODE_CONFIG_DIR, rec->name, rec->conn[0].address, rec->conn[0].port); - log_debug(5, "Removing config file %s iface id %s\n", + log_debug(5, "Removing config file %s iface id %s", portal, rec->iface.name); rc = idbm_lock(); @@ -2285,14 +2656,14 @@ int idbm_delete_node(node_rec_t *rec) if (!stat(portal, &statb)) goto rm_conf; - log_error("Could not stat %s to delete node: %s\n", + log_error("Could not stat %s to delete node: %s", portal, strerror(errno)); rc = ISCSI_ERR_IDBM; goto unlock; rm_conf: if (unlink(portal)) { - log_error("Could not remove %s: %s\n", portal, strerror(errno)); + log_error("Could not remove %s: %s", portal, strerror(errno)); rc = ISCSI_ERR_IDBM; goto unlock; } @@ -2470,7 +2841,7 @@ int idbm_init(idbm_get_config_file_fn *fn) /* make sure root db dir is there */ if (access(ISCSI_CONFIG_ROOT, F_OK) != 0) { if (mkdir(ISCSI_CONFIG_ROOT, 0660) != 0) { - log_error("Could not make %s %d\n", ISCSI_CONFIG_ROOT, + log_error("Could not make %s %d", ISCSI_CONFIG_ROOT, errno); return errno; } @@ -2569,6 +2940,12 @@ struct node_rec *idbm_create_rec_from_boot_context(struct boot_context *context) strlen((char *)context->chap_password); rec->session.auth.password_in_length = strlen((char *)context->chap_password_in); + strlcpy(rec->session.boot_root, context->boot_root, + sizeof(context->boot_root)); + strlcpy(rec->session.boot_nic, context->boot_nic, + sizeof(context->boot_nic)); + strlcpy(rec->session.boot_target, context->boot_target, + sizeof(context->boot_target)); iface_setup_from_boot_context(&rec->iface, context); diff --git a/usr/idbm.h b/usr/idbm.h index 245f046..b9020fe 100644 --- a/usr/idbm.h +++ b/usr/idbm.h @@ -27,6 +27,7 @@ #include "initiator.h" #include "config.h" #include "list.h" +#include "flashnode.h" #define NODE_CONFIG_DIR ISCSI_CONFIG_ROOT"nodes" #define SLP_CONFIG_DIR ISCSI_CONFIG_ROOT"slp" @@ -42,6 +43,7 @@ #define TYPE_STR 2 #define TYPE_UINT8 3 #define TYPE_UINT16 4 +#define TYPE_UINT32 5 #define MAX_KEYS 256 /* number of keys total(including CNX_MAX) */ #define NAME_MAXVAL 128 /* the maximum length of key name */ #define VALUE_MAXVAL 256 /* the maximum length of 223 bytes in the RFC. */ @@ -85,6 +87,7 @@ struct user_param { struct list_head list; char *name; char *value; + int param; }; typedef int (idbm_iface_op_fn)(void *data, node_rec_t *rec); @@ -168,6 +171,7 @@ enum { IDBM_PRINT_TYPE_NODE, IDBM_PRINT_TYPE_IFACE, IDBM_PRINT_TYPE_HOST_CHAP, + IDBM_PRINT_TYPE_FLASHNODE }; extern void idbm_print(int type, void *rec, int show, FILE *f); @@ -181,5 +185,9 @@ extern struct node_rec * idbm_create_rec_from_boot_context(struct boot_context *context); extern int idbm_print_host_chap_info(struct iscsi_chap_rec *chap); +extern void idbm_recinfo_host_chap(struct iscsi_chap_rec *r, recinfo_t *ri); + +extern int idbm_print_flashnode_info(struct flashnode_rec *target); +extern void idbm_recinfo_flashnode(struct flashnode_rec *r, recinfo_t *ri); #endif /* IDBM_H */ diff --git a/usr/idbm_fields.h b/usr/idbm_fields.h index 358d014..5790a03 100644 --- a/usr/idbm_fields.h +++ b/usr/idbm_fields.h @@ -89,6 +89,55 @@ #define IFACE_NUM "iface.iface_num" #define IFACE_MTU "iface.mtu" #define IFACE_PORT "iface.port" +#define IFACE_DELAYED_ACK "iface.delayed_ack" +#define IFACE_TCP_NAGLE "iface.tcp_nagle" +#define IFACE_TCP_WSF_STATE "iface.tcp_wsf_state" +#define IFACE_TCP_WSF "iface.tcp_wsf" +#define IFACE_TCP_TIMER_SCALE "iface.tcp_timer_scale" +#define IFACE_TCP_TIMESTAMP "iface.tcp_timestamp" +#define IFACE_DHCP_DNS "iface.dhcp_dns" +#define IFACE_DHCP_SLP_DA "iface.dhcp_slp_da" +#define IFACE_TOS_STATE "iface.tos_state" +#define IFACE_TOS "iface.tos" +#define IFACE_GRAT_ARP "iface.gratuitous_arp" +#define IFACE_DHCP_ALT_CID "iface.dhcp_alt_client_id_state" +#define IFACE_DHCP_ALT_CID_STR "iface.dhcp_alt_client_id" +#define IFACE_DHCP_REQ_VID "iface.dhcp_req_vendor_id_state" +#define IFACE_DHCP_VID "iface.dhcp_vendor_id_state" +#define IFACE_DHCP_VID_STR "iface.dhcp_vendor_id" +#define IFACE_DHCP_LEARN_IQN "iface.dhcp_learn_iqn" +#define IFACE_FRAGMENTATION "iface.fragmentation" +#define IFACE_IN_FORWARD "iface.incoming_forwarding" +#define IFACE_TTL "iface.ttl" +#define IFACE_GRAT_NEIGHBOR_ADV "iface.gratuitous_neighbor_adv" +#define IFACE_REDIRECT "iface.redirect" +#define IFACE_IGNORE_ICMP_ECHO_REQ "iface.ignore_icmp_echo_request" +#define IFACE_MLD "iface.mld" +#define IFACE_FLOW_LABEL "iface.flow_label" +#define IFACE_TRAFFIC_CLASS "iface.traffic_class" +#define IFACE_HOP_LIMIT "iface.hop_limit" +#define IFACE_ND_REACHABLE_TMO "iface.nd_reachable_tmo" +#define IFACE_ND_REXMIT_TIME "iface.nd_rexmit_time" +#define IFACE_ND_STALE_TMO "iface.nd_stale_tmo" +#define IFACE_DUP_ADDR_DETECT_CNT "iface.dup_addr_detect_cnt" +#define IFACE_RTR_ADV_LINK_MTU "iface.router_adv_link_mtu" +#define IFACE_DEF_TMF_TMO "iface.def_task_mgmt_timeout" +#define IFACE_HDRDGST "iface.header_digest" +#define IFACE_DATADGST "iface.data_digest" +#define IFACE_IMM_DATA "iface.immediate_data" +#define IFACE_INITIAL_R2T "iface.initial_r2t" +#define IFACE_DSEQ_INORDER "iface.data_seq_inorder" +#define IFACE_DPDU_INORDER "iface.data_pdu_inorder" +#define IFACE_ERL "iface.erl" +#define IFACE_MAX_RECV_DLEN "iface.max_receive_data_len" +#define IFACE_FIRST_BURST "iface.first_burst_len" +#define IFACE_MAX_R2T "iface.max_outstanding_r2t" +#define IFACE_MAX_BURST "iface.max_burst_len" +#define IFACE_CHAP_AUTH "iface.chap_auth" +#define IFACE_BIDI_CHAP "iface.bidi_chap" +#define IFACE_STRICT_LOGIN_COMP "iface.strict_login_compliance" +#define IFACE_DISCOVERY_AUTH "iface.discovery_auth" +#define IFACE_DISCOVERY_LOGOUT "iface.discovery_logout" /* discovery fields */ #define DISC_STARTUP "discovery.startup" @@ -126,4 +175,67 @@ #define HOST_AUTH_PASSWORD_IN "host.auth.password_in" #define HOST_AUTH_PASSWORD_IN_LEN "host.auth.password_in_length" +/* flash target session fields */ +#define FLASHNODE_SESS_AUTO_SND_TGT_DISABLE "flashnode.session.auto_snd_tgt_disable" +#define FLASHNODE_SESS_DISCOVERY_SESS "flashnode.session.discovery_session" +#define FLASHNODE_SESS_PORTAL_TYPE "flashnode.session.portal_type" +#define FLASHNODE_SESS_ENTRY_EN "flashnode.session.entry_enable" +#define FLASHNODE_SESS_IMM_DATA_EN "flashnode.session.immediate_data" +#define FLASHNODE_SESS_INITIAL_R2T_EN "flashnode.session.initial_r2t" +#define FLASHNODE_SESS_DATASEQ_INORDER "flashnode.session.data_seq_in_order" +#define FLASHNODE_SESS_PDU_INORDER "flashnode.session.data_pdu_in_order" +#define FLASHNODE_SESS_CHAP_AUTH_EN "flashnode.session.chap_auth_en" +#define FLASHNODE_SESS_DISCOVERY_LOGOUT_EN "flashnode.session.discovery_logout_en" +#define FLASHNODE_SESS_BIDI_CHAP_EN "flashnode.session.bidi_chap_en" +#define FLASHNODE_SESS_DISCOVERY_AUTH_OPTIONAL "flashnode.session.discovery_auth_optional" +#define FLASHNODE_SESS_ERL "flashnode.session.erl" +#define FLASHNODE_SESS_FIRST_BURST "flashnode.session.first_burst_len" +#define FLASHNODE_SESS_DEF_TIME2WAIT "flashnode.session.def_time2wait" +#define FLASHNODE_SESS_DEF_TIME2RETAIN "flashnode.session.def_time2retain" +#define FLASHNODE_SESS_MAX_R2T "flashnode.session.max_outstanding_r2t" +#define FLASHNODE_SESS_ISID "flashnode.session.isid" +#define FLASHNODE_SESS_TSID "flashnode.session.tsid" +#define FLASHNODE_SESS_MAX_BURST "flashnode.session.max_burst_len" +#define FLASHNODE_SESS_DEF_TASKMGMT_TMO "flashnode.session.def_taskmgmt_tmo" +#define FLASHNODE_SESS_ALIAS "flashnode.session.targetalias" +#define FLASHNODE_SESS_NAME "flashnode.session.targetname" +#define FLASHNODE_SESS_TPGT "flashnode.session.tpgt" +#define FLASHNODE_SESS_DISCOVERY_PARENT_IDX "flashnode.session.discovery_parent_idx" +#define FLASHNODE_SESS_DISCOVERY_PARENT_TYPE "flashnode.session.discovery_parent_type" +#define FLASHNODE_SESS_CHAP_OUT_IDX "flashnode.session.chap_out_idx" +#define FLASHNODE_SESS_CHAP_IN_IDX "flashnode.session.chap_in_idx" +#define FLASHNODE_SESS_USERNAME "flashnode.session.username" +#define FLASHNODE_SESS_USERNAME_IN "flashnode.session.username_in" +#define FLASHNODE_SESS_PASSWORD "flashnode.session.password" +#define FLASHNODE_SESS_PASSWORD_IN "flashnode.session.password_in" +#define FLASHNODE_SESS_IS_BOOT_TGT "flashnode.session.is_boot_target" + +/* flash target connection fields */ +#define FLASHNODE_CONN_IS_FW_ASSIGNED_IPV6 "flashnode.conn[%d].is_fw_assigned_ipv6" +#define FLASHNODE_CONN_HDR_DGST_EN "flashnode.conn[%d].header_digest_en" +#define FLASHNODE_CONN_DATA_DGST_EN "flashnode.conn[%d].data_digest_en" +#define FLASHNODE_CONN_SNACK_REQ_EN "flashnode.conn[%d].snack_req_en" +#define FLASHNODE_CONN_TCP_TIMESTAMP_STAT "flashnode.conn[%d].tcp_timestamp_stat" +#define FLASHNODE_CONN_TCP_NAGLE_DISABLE "flashnode.conn[%d].tcp_nagle_disable" +#define FLASHNODE_CONN_TCP_WSF_DISABLE "flashnode.conn[%d].tcp_wsf_disable" +#define FLASHNODE_CONN_TCP_TIMER_SCALE "flashnode.conn[%d].tcp_timer_scale" +#define FLASHNODE_CONN_TCP_TIMESTAMP_EN "flashnode.conn[%d].tcp_timestamp_en" +#define FLASHNODE_CONN_IP_FRAG_DISABLE "flashnode.conn[%d].fragment_disable" +#define FLASHNODE_CONN_MAX_RECV_DLENGTH "flashnode.conn[%d].max_recv_dlength" +#define FLASHNODE_CONN_MAX_XMIT_DLENGTH "flashnode.conn[%d].max_xmit_dlength" +#define FLASHNODE_CONN_KEEPALIVE_TMO "flashnode.conn[%d].keepalive_tmo" +#define FLASHNODE_CONN_PORT "flashnode.conn[%d].port" +#define FLASHNODE_CONN_IPADDR "flashnode.conn[%d].ipaddress" +#define FLASHNODE_CONN_REDIRECT_IPADDR "flashnode.conn[%d].redirect_ipaddr" +#define FLASHNODE_CONN_MAX_SEGMENT_SIZE "flashnode.conn[%d].max_segment_size" +#define FLASHNODE_CONN_LOCAL_PORT "flashnode.conn[%d].local_port" +#define FLASHNODE_CONN_IPV4_TOS "flashnode.conn[%d].ipv4_tos" +#define FLASHNODE_CONN_IPV6_TC "flashnode.conn[%d].ipv6_traffic_class" +#define FLASHNODE_CONN_IPV6_FLOW_LABEL "flashnode.conn[%d].ipv6_flow_label" +#define FLASHNODE_CONN_LINK_LOCAL_IPV6 "flashnode.conn[%d].link_local_ipv6" +#define FLASHNODE_CONN_TCP_XMIT_WSF "flashnode.conn[%d].tcp_xmit_wsf" +#define FLASHNODE_CONN_TCP_RECV_WSF "flashnode.conn[%d].tcp_recv_wsf" +#define FLASHNODE_CONN_STATSN "flashnode.conn[%d].statsn" +#define FLASHNODE_CONN_EXP_STATSN "flashnode.conn[%d].exp_statsn" + #endif diff --git a/usr/iface.c b/usr/iface.c index 3a9582e..0a7f0bb 100644 --- a/usr/iface.c +++ b/usr/iface.c @@ -227,7 +227,7 @@ int iface_conf_delete(struct iface_rec *iface) def_iface = iface_match_default(iface); if (def_iface) { log_error("iface %s is a special interface and " - "cannot be deleted.\n", iface->name); + "cannot be deleted.", iface->name); return ISCSI_ERR_INVAL; } @@ -259,7 +259,7 @@ int iface_conf_write(struct iface_rec *iface) def_iface = iface_match_default(iface); if (def_iface) { log_error("iface %s is a special interface and " - "is not stored in %s.\n", iface->name, + "is not stored in %s.", iface->name, IFACE_CONFIG_DIR); return ISCSI_ERR_INVAL; } @@ -299,7 +299,7 @@ int iface_conf_update(struct list_head *params, struct iface_rec *iface) def_iface = iface_match_default(iface); if (def_iface) { log_error("iface %s is a special interface and " - "cannot be modified.\n", iface->name); + "cannot be modified.", iface->name); return ISCSI_ERR_INVAL; } @@ -450,8 +450,10 @@ int iface_get_iptype(struct iface_rec *iface) /* try to figure out by name */ if (strstr(iface->name, "ipv4")) return ISCSI_IFACE_TYPE_IPV4; - else + else if (strstr(iface->name, "ipv6")) return ISCSI_IFACE_TYPE_IPV6; + else /* assume ipv4 by default */ + return ISCSI_IFACE_TYPE_IPV4; } else { if (strcmp(iface->bootproto, "dhcp") && !strstr(iface->ipaddress, ".")) @@ -470,17 +472,14 @@ static int iface_setup_binding_from_kern_iface(void *data, if (!strlen(hinfo->iface.hwaddress)) { log_error("Invalid offload iSCSI host %u. Missing " - "hwaddress. Try upgrading %s driver.\n", + "hwaddress. Try upgrading %s driver.", hinfo->host_no, hinfo->iface.transport_name); return 0; } memset(&iface, 0, sizeof(struct iface_rec)); - strcpy(iface.hwaddress, hinfo->iface.hwaddress); - strcpy(iface.transport_name, hinfo->iface.transport_name); - if (kern_iface) { - iface.iface_num = kern_iface->iface_num; + memcpy(&iface, kern_iface, sizeof(iface)); snprintf(iface.name, sizeof(iface.name), "%s.%s.%s.%u", kern_iface->transport_name, @@ -492,6 +491,9 @@ static int iface_setup_binding_from_kern_iface(void *data, hinfo->iface.transport_name, hinfo->iface.hwaddress); } + strcpy(iface.hwaddress, hinfo->iface.hwaddress); + strcpy(iface.transport_name, hinfo->iface.transport_name); + memset(iface_path, 0, sizeof(iface_path)); snprintf(iface_path, PATH_MAX, "%s/%s", IFACE_CONFIG_DIR, iface.name); @@ -601,6 +603,105 @@ void iface_copy(struct iface_rec *dst, struct iface_rec *src) dst->mtu = src->mtu; if (src->port) dst->port = src->port; + if (strlen(src->delayed_ack)) + strcpy(dst->delayed_ack, src->delayed_ack); + if (strlen(src->nagle)) + strcpy(dst->nagle, src->nagle); + if (strlen(src->tcp_wsf_state)) + strcpy(dst->tcp_wsf_state, src->tcp_wsf_state); + if (src->tcp_wsf) + dst->tcp_wsf = src->tcp_wsf; + if (src->tcp_timer_scale) + dst->tcp_timer_scale = src->tcp_timer_scale; + if (strlen(src->tcp_timestamp)) + strcpy(dst->tcp_timestamp, src->tcp_timestamp); + if (strlen(src->dhcp_dns)) + strcpy(dst->dhcp_dns, src->dhcp_dns); + if (strlen(src->dhcp_slp_da)) + strcpy(dst->dhcp_slp_da, src->dhcp_slp_da); + if (strlen(src->tos_state)) + strcpy(dst->tos_state, src->tos_state); + if (src->tos) + dst->tos = src->tos; + if (strlen(src->gratuitous_arp)) + strcpy(dst->gratuitous_arp, src->gratuitous_arp); + if (strlen(src->dhcp_alt_client_id_state)) + strcpy(dst->dhcp_alt_client_id_state, + src->dhcp_alt_client_id_state); + if (strlen(src->dhcp_alt_client_id)) + strcpy(dst->dhcp_alt_client_id, src->dhcp_alt_client_id); + if (strlen(src->dhcp_req_vendor_id_state)) + strcpy(dst->dhcp_req_vendor_id_state, + src->dhcp_req_vendor_id_state); + if (strlen(src->dhcp_vendor_id_state)) + strcpy(dst->dhcp_vendor_id_state, src->dhcp_vendor_id_state); + if (strlen(src->dhcp_vendor_id)) + strcpy(dst->dhcp_vendor_id, src->dhcp_vendor_id); + if (strlen(src->dhcp_learn_iqn)) + strcpy(dst->dhcp_learn_iqn, src->dhcp_learn_iqn); + if (strlen(src->fragmentation)) + strcpy(dst->fragmentation, src->fragmentation); + if (strlen(src->incoming_forwarding)) + strcpy(dst->incoming_forwarding, src->incoming_forwarding); + if (src->ttl) + dst->ttl = src->ttl; + if (strlen(src->gratuitous_neighbor_adv)) + strcpy(dst->gratuitous_neighbor_adv, + src->gratuitous_neighbor_adv); + if (strlen(src->redirect)) + strcpy(dst->redirect, src->redirect); + if (strlen(src->mld)) + strcpy(dst->mld, src->mld); + if (src->flow_label) + dst->flow_label = src->flow_label; + if (src->traffic_class) + dst->traffic_class = src->traffic_class; + if (src->hop_limit) + dst->hop_limit = src->hop_limit; + if (src->nd_reachable_tmo) + dst->nd_reachable_tmo = src->nd_reachable_tmo; + if (src->nd_rexmit_time) + dst->nd_rexmit_time = src->nd_rexmit_time; + if (src->nd_stale_tmo) + dst->nd_stale_tmo = src->nd_stale_tmo; + if (src->dup_addr_detect_cnt) + dst->dup_addr_detect_cnt = src->dup_addr_detect_cnt; + if (src->router_adv_link_mtu) + dst->router_adv_link_mtu = src->router_adv_link_mtu; + if (src->def_task_mgmt_tmo) + dst->def_task_mgmt_tmo = src->def_task_mgmt_tmo; + if (strlen(src->header_digest)) + strcpy(dst->header_digest, src->header_digest); + if (strlen(src->data_digest)) + strcpy(dst->data_digest, src->data_digest); + if (strlen(src->immediate_data)) + strcpy(dst->immediate_data, src->immediate_data); + if (strlen(src->initial_r2t)) + strcpy(dst->initial_r2t, src->initial_r2t); + if (strlen(src->data_seq_inorder)) + strcpy(dst->data_seq_inorder, src->data_seq_inorder); + if (strlen(src->data_pdu_inorder)) + strcpy(dst->data_pdu_inorder, src->data_pdu_inorder); + if (src->erl) + dst->erl = src->erl; + if (src->max_recv_dlength) + dst->max_recv_dlength = src->max_recv_dlength; + if (src->first_burst_len) + dst->first_burst_len = src->first_burst_len; + if (src->max_out_r2t) + dst->max_out_r2t = src->max_out_r2t; + if (src->max_burst_len) + dst->max_burst_len = src->max_burst_len; + if (strlen(src->chap_auth)) + strcpy(dst->chap_auth, src->chap_auth); + if (strlen(src->bidi_chap)) + strcpy(dst->bidi_chap, src->bidi_chap); + if (strlen(src->strict_login_comp)) + strcpy(dst->strict_login_comp, src->strict_login_comp); + if (strlen(src->discovery_auth)) + strcpy(dst->discovery_auth, src->discovery_auth); + if (strlen(src->discovery_logout)) + strcpy(dst->discovery_logout, src->discovery_logout); if (strlen(src->hwaddress)) strcpy(dst->hwaddress, src->hwaddress); if (strlen(src->transport_name)) @@ -894,7 +995,6 @@ int iface_setup_from_boot_context(struct iface_rec *iface, struct boot_context *context) { struct iscsi_transport *t = NULL; - char transport_name[ISCSI_TRANSPORT_NAME_MAXLEN]; uint32_t hostno; if (strlen(context->initiatorname)) @@ -902,7 +1002,8 @@ int iface_setup_from_boot_context(struct iface_rec *iface, sizeof(iface->iname)); if (strlen(context->scsi_host_name)) { - if (sscanf(context->scsi_host_name, "iscsi_boot%u", &hostno) != 1) { + if (sscanf(context->scsi_host_name, + "iscsi_boot%u", &hostno) != 1) { log_error("Could not parse %s's host no.", context->scsi_host_name); return 0; @@ -910,6 +1011,8 @@ int iface_setup_from_boot_context(struct iface_rec *iface, } else if (strlen(context->iface)) { /* this ifdef is only temp until distros and firmwares are updated */ #ifdef OFFLOAD_BOOT_SUPPORTED + char transport_name[ISCSI_TRANSPORT_NAME_MAXLEN]; + int rc; memset(transport_name, 0, ISCSI_TRANSPORT_NAME_MAXLEN); /* make sure offload driver is loaded */ @@ -917,6 +1020,10 @@ int iface_setup_from_boot_context(struct iface_rec *iface, transport_name)) t = iscsi_sysfs_get_transport_by_name(transport_name); + if (net_ifup_netdev(context->iface)) + log_warning("Could not bring up netdev %s for boot", + context->iface); + hostno = iscsi_sysfs_get_host_no_from_hwaddress(context->mac, &rc); if (rc) { @@ -925,7 +1032,7 @@ int iface_setup_from_boot_context(struct iface_rec *iface, * host then the MAC must be for network card, so boot * is not going to be offloaded. */ - log_debug(3, "Could not match %s to host\n", + log_debug(3, "Could not match %s to host", context->mac); return 0; } @@ -957,7 +1064,12 @@ int iface_setup_from_boot_context(struct iface_rec *iface, sizeof(iface->hwaddress)); strlcpy(iface->ipaddress, context->ipaddr, sizeof(iface->ipaddress)); - log_debug(1, "iface " iface_fmt "\n", iface_str(iface)); + iface->vlan_id = atoi(context->vlan); + strlcpy(iface->subnet_mask, context->mask, + sizeof(iface->subnet_mask)); + strlcpy(iface->gateway, context->gateway, + sizeof(iface->gateway)); + log_debug(1, "iface " iface_fmt "", iface_str(iface)); return 1; } @@ -982,7 +1094,7 @@ int iface_create_ifaces_from_boot_contexts(struct list_head *ifaces, /* use dummy name. If valid it will get overwritten below */ iface = iface_alloc(DEFAULT_IFACENAME, &rc); if (!iface) { - log_error("Could not setup iface %s for boot\n", + log_error("Could not setup iface %s for boot", context->iface); goto fail; } @@ -1016,6 +1128,79 @@ struct iface_param_count { int count; }; +#define IFACE_NET_PARAM_EN_CNT(param_val, cnt) { \ + if (!strcmp(param_val, "disable") || \ + !strcmp(param_val, "enable")) \ + (*cnt)++; \ +} + +/** + * iface_get_common_param_count - Gets common parameters count for given iface + * @iface: iface to setup + * @count: number of parameters to set + */ +static void iface_get_common_param_count(struct iface_rec *iface, int *count) +{ + if (strcmp(iface->vlan_state, "disable")) { + /* vlan_state enabled */ + (*count)++; + + if (iface->vlan_id) + /* For vlan value */ + (*count)++; + } else { + /* vlan_state disabled */ + (*count)++; + } + + if (iface->mtu) + (*count)++; + + if (iface->port) + (*count)++; + + IFACE_NET_PARAM_EN_CNT(iface->delayed_ack, count); + IFACE_NET_PARAM_EN_CNT(iface->nagle, count); + IFACE_NET_PARAM_EN_CNT(iface->tcp_wsf_state, count); + IFACE_NET_PARAM_EN_CNT(iface->tcp_timestamp, count); + IFACE_NET_PARAM_EN_CNT(iface->redirect, count); + IFACE_NET_PARAM_EN_CNT(iface->header_digest, count); + IFACE_NET_PARAM_EN_CNT(iface->data_digest, count); + IFACE_NET_PARAM_EN_CNT(iface->immediate_data, count); + IFACE_NET_PARAM_EN_CNT(iface->initial_r2t, count); + IFACE_NET_PARAM_EN_CNT(iface->data_seq_inorder, count); + IFACE_NET_PARAM_EN_CNT(iface->data_pdu_inorder, count); + IFACE_NET_PARAM_EN_CNT(iface->chap_auth, count); + IFACE_NET_PARAM_EN_CNT(iface->bidi_chap, count); + IFACE_NET_PARAM_EN_CNT(iface->strict_login_comp, count); + IFACE_NET_PARAM_EN_CNT(iface->discovery_auth, count); + IFACE_NET_PARAM_EN_CNT(iface->discovery_logout, count); + + if (iface->tcp_wsf) + (*count)++; + + if (iface->tcp_timer_scale) + (*count)++; + + if (iface->def_task_mgmt_tmo) + (*count)++; + + if (iface->erl) + (*count)++; + + if (iface->max_recv_dlength) + (*count)++; + + if (iface->first_burst_len) + (*count)++; + + if (iface->max_burst_len) + (*count)++; + + if (iface->max_out_r2t) + (*count)++; +} + /** * __iface_get_param_count - Gets netconfig parameter count for given iface * @data: iface_param_count structure @@ -1034,10 +1219,10 @@ static int __iface_get_param_count(void *data, struct iface_rec *iface) if (iptype == ISCSI_IFACE_TYPE_IPV4) { if (strcmp(iface->state, "disable")) { - if (strstr(iface->bootproto, "dhcp")) + if (strstr(iface->bootproto, "dhcp")) { /* DHCP enabled */ count++; - else { + } else { /* DHCP disabled */ count++; @@ -1052,12 +1237,13 @@ static int __iface_get_param_count(void *data, struct iface_rec *iface) if (strstr(iface->gateway, ".")) /* User configured Gateway */ count++; - } else + } else { /* * IPv4 Address not valid, decrement * count of DHCP */ count--; + } } /* @@ -1068,37 +1254,68 @@ static int __iface_get_param_count(void *data, struct iface_rec *iface) /* iface state */ count++; - if (strcmp(iface->vlan_state, "disable")) { - /* vlan_state enabled */ + IFACE_NET_PARAM_EN_CNT(iface->dhcp_dns, + &count); + + IFACE_NET_PARAM_EN_CNT(iface->dhcp_slp_da, + &count); + + IFACE_NET_PARAM_EN_CNT(iface->tos_state, + &count); + + IFACE_NET_PARAM_EN_CNT(iface->gratuitous_arp, + &count); + + IFACE_NET_PARAM_EN_CNT( + iface->dhcp_alt_client_id_state, + &count); + + if (iface->dhcp_alt_client_id[0]) count++; - if (iface->vlan_id) - /* For vlan value */ - count++; - } else - /* vlan_state disabled */ + IFACE_NET_PARAM_EN_CNT( + iface->dhcp_req_vendor_id_state, + &count); + + IFACE_NET_PARAM_EN_CNT( + iface->dhcp_vendor_id_state, + &count); + + if (iface->dhcp_vendor_id[0]) count++; - if (iface->mtu) + IFACE_NET_PARAM_EN_CNT(iface->dhcp_learn_iqn, + &count); + + IFACE_NET_PARAM_EN_CNT(iface->fragmentation, + &count); + + IFACE_NET_PARAM_EN_CNT( + iface->incoming_forwarding, + &count); + + if (iface->tos) count++; - if (iface->port) + if (iface->ttl) count++; + + iface_get_common_param_count(iface, &count); } - } else + } else { /* IPv4 is disabled, iface state */ count++; - + } } else if (iptype == ISCSI_IFACE_TYPE_IPV6) { if (strcmp(iface->state, "disable")) { /* IPv6 Address */ if (strstr(iface->ipv6_autocfg, "nd") || - strstr(iface->ipv6_autocfg, "dhcpv6")) + strstr(iface->ipv6_autocfg, "dhcpv6")) { /* Autocfg enabled */ count++; - else { + } else { /* Autocfg disabled */ count++; @@ -1159,26 +1376,42 @@ static int __iface_get_param_count(void *data, struct iface_rec *iface) /* iface state */ count++; - if (strcmp(iface->vlan_state, "disable")) { - /* vlan_state enabled */ + IFACE_NET_PARAM_EN_CNT( + iface->gratuitous_neighbor_adv, + &count); + + IFACE_NET_PARAM_EN_CNT(iface->mld, &count); + + if (iface->flow_label) + count++; + + if (iface->traffic_class) + count++; + + if (iface->hop_limit) count++; - if (iface->vlan_id) - /* For vlan value */ - count++; - } else - /* vlan_state disabled */ + if (iface->nd_reachable_tmo) count++; - if (iface->mtu) + if (iface->nd_rexmit_time) count++; - if (iface->port) + if (iface->nd_stale_tmo) count++; + + if (iface->dup_addr_detect_cnt) + count++; + + if (iface->router_adv_link_mtu) + count++; + + iface_get_common_param_count(iface, &count); } - } else + } else { /* IPv6 is disabled, iface state */ count++; + } } iface_params->count += count; @@ -1197,7 +1430,7 @@ int iface_get_param_count(struct iface_rec *iface, int iface_all) int num_found = 0, rc; struct iface_param_count iface_params; - log_debug(8, "In iface_get_param_count\n"); + log_debug(8, "In iface_get_param_count"); iface_params.primary = iface; iface_params.count = 0; @@ -1208,63 +1441,73 @@ int iface_get_param_count(struct iface_rec *iface, int iface_all) else rc = __iface_get_param_count(&iface_params, iface); - log_debug(8, "iface_get_param_count: rc = %d, count = %d\n", + log_debug(8, "iface_get_param_count: rc = %d, count = %d", rc, iface_params.count); return iface_params.count; } -/* IPv4/IPv6 Port: 3260 or User defined */ -static int iface_fill_port(struct iovec *iov, struct iface_rec *iface, - uint32_t iface_type) +/* write integer parameter value */ +static int iface_fill_int_param_val(struct iovec *iov, uint32_t iface_num, + uint8_t iface_type, uint16_t param, + uint8_t param_type, uint32_t param_len, + uint32_t param_val) { int len; struct iscsi_iface_param_info *net_param; - uint16_t port = 3260; struct nlattr *attr; + uint8_t val8 = 0; + uint16_t val16 = 0; + uint32_t val32 = 0; + char *val = NULL; - len = sizeof(struct iscsi_iface_param_info) + sizeof(port); - iov->iov_base = iscsi_nla_alloc(ISCSI_NET_PARAM_PORT, len); - if (!iov->iov_base) - return 1; - attr = iov->iov_base; - iov->iov_len = NLA_ALIGN(attr->nla_len); - - net_param = (struct iscsi_iface_param_info *)ISCSI_NLA_DATA(attr); - net_param->param = ISCSI_NET_PARAM_PORT; - net_param->iface_type = iface_type; - net_param->iface_num = iface->iface_num; - net_param->param_type = ISCSI_NET_PARAM; - net_param->len = 2; - if (iface->port) - port = iface->port; - memcpy(net_param->value, &port, net_param->len); - return 0; -} - -static int iface_fill_mtu(struct iovec *iov, struct iface_rec *iface, - uint32_t iface_type) -{ - int len; - struct iscsi_iface_param_info *net_param; - uint16_t mtu = 0; - struct nlattr *attr; - - len = sizeof(struct iscsi_iface_param_info) + 2; - iov->iov_base = iscsi_nla_alloc(ISCSI_NET_PARAM_MTU, len); + len = sizeof(struct iscsi_iface_param_info) + param_len; + iov->iov_base = iscsi_nla_alloc(param, len); if (!(iov->iov_base)) return 1; + attr = iov->iov_base; iov->iov_len = NLA_ALIGN(attr->nla_len); - net_param = (struct iscsi_iface_param_info *)ISCSI_NLA_DATA(attr); - net_param->param = ISCSI_NET_PARAM_MTU; + net_param->iface_num = iface_num; + net_param->len = param_len; + net_param->param = param; net_param->iface_type = iface_type; - net_param->iface_num = iface->iface_num; - net_param->param_type = ISCSI_NET_PARAM; - net_param->len = 2; - mtu = iface->mtu; - memcpy(net_param->value, &mtu, net_param->len); + net_param->param_type = param_type; + switch (param_len) { + case 1: + val8 = (uint8_t)param_val; + val = (char *)&val8; + break; + + case 2: + val16 = (uint16_t)param_val; + val = (char *)&val16; + break; + + case 4: + val32 = (uint32_t)param_val; + val = (char *)&val32; + break; + + default: + goto free; + } + memcpy(net_param->value, val, param_len); return 0; +free: + free(iov->iov_base); + iov->iov_base = NULL; + iov->iov_len = 0; + return 1; +} + +#define IFACE_SET_PARAM_INTVAL(iov, inum, itype, param, ptype, plen, \ + ival, gcnt, lcnt) { \ + if (ival && !iface_fill_int_param_val(iov, inum, itype, param, \ + ptype, plen, ival)) { \ + (*gcnt)++; \ + (*lcnt)++; \ + } \ } /* IPv4/IPv6 VLAN_ID: decimal value <= 4095 */ @@ -1301,60 +1544,52 @@ static int iface_fill_vlan_id(struct iovec *iov, struct iface_rec *iface, return 0; } -/* IPv4/IPv6 VLAN state: disable/enable */ -static int iface_fill_vlan_state(struct iovec *iov, struct iface_rec *iface, - uint32_t iface_type) +/* disable/enable parameters */ +static int iface_fill_param_state(struct iovec *iov, uint32_t iface_num, + uint8_t iface_type, uint16_t param, + uint8_t param_type, char *param_val) { int len; struct iscsi_iface_param_info *net_param; struct nlattr *attr; + if (!param_val[0]) + return 1; + len = sizeof(struct iscsi_iface_param_info) + 1; - iov->iov_base = iscsi_nla_alloc(ISCSI_NET_PARAM_VLAN_ENABLED, len); + iov->iov_base = iscsi_nla_alloc(param, len); if (!(iov->iov_base)) return 1; attr = iov->iov_base; iov->iov_len = NLA_ALIGN(attr->nla_len); net_param = (struct iscsi_iface_param_info *)ISCSI_NLA_DATA(attr); - net_param->param = ISCSI_NET_PARAM_VLAN_ENABLED; - net_param->iface_type = iface_type; - net_param->iface_num = iface->iface_num; - net_param->param_type = ISCSI_NET_PARAM; + net_param->iface_num = iface_num; net_param->len = 1; - if (strcmp(iface->vlan_state, "disable") && iface->vlan_id) - net_param->value[0] = ISCSI_VLAN_ENABLE; - else /* Assume disabled */ - net_param->value[0] = ISCSI_VLAN_DISABLE; + net_param->param = param; + net_param->iface_type = iface_type; + net_param->param_type = param_type; + if (!strcmp(param_val, "disable")) + net_param->value[0] = ISCSI_NET_PARAM_DISABLE; + else if (!strcmp(param_val, "enable")) + net_param->value[0] = ISCSI_NET_PARAM_ENABLE; + else + goto free; return 0; +free: + free(iov->iov_base); + iov->iov_base = NULL; + iov->iov_len = 0; + return 1; } -/* IPv4/IPv6 Network state: disable/enable */ -static int iface_fill_net_state(struct iovec *iov, struct iface_rec *iface, - uint32_t iface_type) -{ - int len; - struct iscsi_iface_param_info *net_param; - struct nlattr *attr; - - len = sizeof(struct iscsi_iface_param_info) + 1; - iov->iov_base = iscsi_nla_alloc(ISCSI_NET_PARAM_IFACE_ENABLE, len); - if (!(iov->iov_base)) - return 1; - - attr = iov->iov_base; - iov->iov_len = NLA_ALIGN(attr->nla_len); - net_param = (struct iscsi_iface_param_info *)ISCSI_NLA_DATA(attr); - net_param->param = ISCSI_NET_PARAM_IFACE_ENABLE; - net_param->iface_type = iface_type; - net_param->iface_num = iface->iface_num; - net_param->param_type = ISCSI_NET_PARAM; - net_param->len = 1; - if (!strcmp(iface->state, "disable")) - net_param->value[0] = ISCSI_IFACE_DISABLE; - else /* Assume enabled */ - net_param->value[0] = ISCSI_IFACE_ENABLE; - return 0; +#define IFACE_SET_PARAM_STATE(iov, inum, itype, param, ptype, ival, \ + gcnt, lcnt) { \ + if (!iface_fill_param_state(iov, inum, itype, param, ptype, \ + ival)) { \ + (*gcnt)++; \ + (*lcnt)++; \ + } \ } /* IPv4 Bootproto: DHCP/static */ @@ -1474,8 +1709,8 @@ static int iface_fill_router_autocfg(struct iovec *iov, struct iface_rec *iface) } /* IPv4 IPAddress/Subnet Mask/Gateway: 4 bytes */ -static int iface_fill_net_ipv4_addr(struct iovec *iov, struct iface_rec *iface, - uint32_t param) +static int iface_fill_net_ipv4_addr(struct iovec *iov, uint32_t iface_num, + uint16_t param, char *param_val) { int rc = 1; int len; @@ -1492,29 +1727,12 @@ static int iface_fill_net_ipv4_addr(struct iovec *iov, struct iface_rec *iface, net_param = (struct iscsi_iface_param_info *)ISCSI_NLA_DATA(attr); net_param->param = param; net_param->iface_type = ISCSI_IFACE_TYPE_IPV4; - net_param->iface_num = iface->iface_num; + net_param->iface_num = iface_num; net_param->len = 4; net_param->param_type = ISCSI_NET_PARAM; - - switch (param) { - case ISCSI_NET_PARAM_IPV4_ADDR: - rc = inet_pton(AF_INET, iface->ipaddress, net_param->value); - if (rc <= 0) - goto free; - break; - case ISCSI_NET_PARAM_IPV4_SUBNET: - rc = inet_pton(AF_INET, iface->subnet_mask, net_param->value); - if (rc <= 0) - goto free; - break; - case ISCSI_NET_PARAM_IPV4_GW: - rc = inet_pton(AF_INET, iface->gateway, net_param->value); - if (rc <= 0) - goto free; - break; - default: + rc = inet_pton(AF_INET, param_val, net_param->value); + if (rc <= 0) goto free; - } /* validate */ if (!net_param->value[0] && !net_param->value[1] && @@ -1529,9 +1747,19 @@ free: return 1; } +#define IFACE_SET_NET_PARAM_IPV4_ADDR(iov, inum, param, ival, gcnt, \ + lcnt) { \ + if (strstr(ival, ".")) { \ + if (!iface_fill_net_ipv4_addr(iov, inum, param, ival)) {\ + (*gcnt)++; \ + (*lcnt)++; \ + } \ + } \ +} + /* IPv6 IPAddress/LinkLocal/Router: 16 bytes */ -static int iface_fill_net_ipv6_addr(struct iovec *iov, struct iface_rec *iface, - uint32_t param) +static int iface_fill_net_ipv6_addr(struct iovec *iov, uint32_t iface_num, + uint16_t param, char *param_val) { int rc; int len; @@ -1548,30 +1776,12 @@ static int iface_fill_net_ipv6_addr(struct iovec *iov, struct iface_rec *iface, net_param = (struct iscsi_iface_param_info *)ISCSI_NLA_DATA(attr); net_param->param = param; net_param->iface_type = ISCSI_IFACE_TYPE_IPV6; - net_param->iface_num = iface->iface_num; + net_param->iface_num = iface_num; net_param->param_type = ISCSI_NET_PARAM; net_param->len = 16; - - switch (param) { - case ISCSI_NET_PARAM_IPV6_ADDR: - rc = inet_pton(AF_INET6, iface->ipaddress, net_param->value); - if (rc <= 0) - goto free; - break; - case ISCSI_NET_PARAM_IPV6_LINKLOCAL: - rc = inet_pton(AF_INET6, iface->ipv6_linklocal, - net_param->value); - if (rc <= 0) - goto free; - break; - case ISCSI_NET_PARAM_IPV6_ROUTER: - rc = inet_pton(AF_INET6, iface->ipv6_router, net_param->value); - if (rc <= 0) - goto free; - break; - default: + rc = inet_pton(AF_INET6, param_val, net_param->value); + if (rc <= 0) goto free; - } return 0; free: @@ -1581,6 +1791,54 @@ free: return 1; } +#define IFACE_SET_NET_PARAM_IPV6_ADDR(iov, inum, param, ival, gcnt, \ + lcnt) { \ + if (strstr(ival, ":")) { \ + if (!iface_fill_net_ipv6_addr(iov, inum, param, ival)) {\ + (*gcnt)++; \ + (*lcnt)++; \ + } \ + } \ +} + +/* write string parameter value */ +static int iface_fill_str_param_val(struct iovec *iov, uint32_t iface_num, + uint8_t iface_type, uint16_t param, + uint32_t param_len, char *param_val) +{ + int len; + struct iscsi_iface_param_info *net_param; + struct nlattr *attr; + + if (!param_val[0]) + return 1; + + len = sizeof(struct iscsi_iface_param_info) + param_len; + iov->iov_base = iscsi_nla_alloc(param, len); + if (!(iov->iov_base)) + return 1; + + attr = iov->iov_base; + iov->iov_len = NLA_ALIGN(attr->nla_len); + net_param = (struct iscsi_iface_param_info *)ISCSI_NLA_DATA(attr); + net_param->iface_num = iface_num; + net_param->len = param_len; + net_param->param = param; + net_param->iface_type = iface_type; + net_param->param_type = ISCSI_NET_PARAM; + memcpy(net_param->value, param_val, param_len); + return 0; +} + +#define IFACE_SET_NET_PARAM_STRVAL(iov, inum, itype, param, plen, \ + ival, gcnt, lcnt) { \ + if (!iface_fill_str_param_val(iov, inum, itype, param, plen, \ + ival)) { \ + (*gcnt)++; \ + (*lcnt)++; \ + } \ +} + struct iface_net_config { struct iface_rec *primary; struct iovec *iovs; @@ -1600,16 +1858,21 @@ static int __iface_build_net_config(void *data, struct iface_rec *iface) /* start at 2, because 0 is for nlmsghdr and 1 for event */ iov = net_config->iovs + 2; + if (!iface->port) + iface->port = 3260; + iptype = iface_get_iptype(iface); - if (iptype == ISCSI_IFACE_TYPE_IPV4) { + switch (iptype) { + case ISCSI_IFACE_TYPE_IPV4: if (!strcmp(iface->state, "disable")) { - if (!iface_fill_net_state(&iov[net_config->count], - iface, - ISCSI_IFACE_TYPE_IPV4)) { - net_config->count++; - count++; - } - + IFACE_SET_PARAM_STATE(&iov[net_config->count], + iface->iface_num, + ISCSI_IFACE_TYPE_IPV4, + ISCSI_NET_PARAM_IFACE_ENABLE, + ISCSI_NET_PARAM, + iface->state, + &net_config->count, + &count); return 0; } @@ -1625,28 +1888,27 @@ static int __iface_build_net_config(void *data, struct iface_rec *iface) net_config->count++; count++; } - if (!iface_fill_net_ipv4_addr(&iov[net_config->count], - iface, - ISCSI_NET_PARAM_IPV4_ADDR)) { - net_config->count++; - count++; - } - if (strstr(iface->subnet_mask, ".")) { - if (!iface_fill_net_ipv4_addr( - &iov[net_config->count], iface, - ISCSI_NET_PARAM_IPV4_SUBNET)) { - net_config->count++; - count++; - } - } - if (strstr(iface->gateway, ".")) { - if (!iface_fill_net_ipv4_addr( - &iov[net_config->count], iface, - ISCSI_NET_PARAM_IPV4_GW)) { - net_config->count++; - count++; - } - } + + IFACE_SET_NET_PARAM_IPV4_ADDR(&iov[net_config->count], + iface->iface_num, + ISCSI_NET_PARAM_IPV4_ADDR, + iface->ipaddress, + &net_config->count, + &count); + + IFACE_SET_NET_PARAM_IPV4_ADDR(&iov[net_config->count], + iface->iface_num, + ISCSI_NET_PARAM_IPV4_SUBNET, + iface->subnet_mask, + &net_config->count, + &count); + + IFACE_SET_NET_PARAM_IPV4_ADDR(&iov[net_config->count], + iface->iface_num, + ISCSI_NET_PARAM_IPV4_GW, + iface->gateway, + &net_config->count, + &count); } /* @@ -1654,51 +1916,146 @@ static int __iface_build_net_config(void *data, struct iface_rec *iface) * fill state and other parameters (if any) */ if (count) { - if (!iface_fill_net_state(&iov[net_config->count], - iface, - ISCSI_IFACE_TYPE_IPV4)) { - net_config->count++; - count++; - } - if (!iface_fill_vlan_state(&iov[net_config->count], - iface, - ISCSI_IFACE_TYPE_IPV4)) { - net_config->count++; - count++; - } - if (strcmp(iface->vlan_state, "disable") && - iface->vlan_id) { - if (!iface_fill_vlan_id(&iov[net_config->count], - iface, ISCSI_IFACE_TYPE_IPV4)) { - net_config->count++; - count++; - } - } - if (iface->mtu) { - if (!iface_fill_mtu(&iov[net_config->count], - iface, - ISCSI_IFACE_TYPE_IPV4)) { - net_config->count++; - count++; - } - } - if (iface->port) { - if (!iface_fill_port(&iov[net_config->count], - iface, - ISCSI_IFACE_TYPE_IPV4)) { - net_config->count++; - count++; - } - } + IFACE_SET_PARAM_STATE(&iov[net_config->count], + iface->iface_num, + ISCSI_IFACE_TYPE_IPV4, + ISCSI_NET_PARAM_IPV4_DHCP_DNS_ADDR_EN, + ISCSI_NET_PARAM, + iface->dhcp_dns, + &net_config->count, + &count); + + IFACE_SET_PARAM_STATE(&iov[net_config->count], + iface->iface_num, + ISCSI_IFACE_TYPE_IPV4, + ISCSI_NET_PARAM_IPV4_DHCP_SLP_DA_EN, + ISCSI_NET_PARAM, + iface->dhcp_slp_da, + &net_config->count, + &count); + + IFACE_SET_PARAM_STATE(&iov[net_config->count], + iface->iface_num, + ISCSI_IFACE_TYPE_IPV4, + ISCSI_NET_PARAM_IPV4_TOS_EN, + ISCSI_NET_PARAM, + iface->tos_state, + &net_config->count, + &count); + + IFACE_SET_PARAM_INTVAL(&iov[net_config->count], + iface->iface_num, + ISCSI_IFACE_TYPE_IPV4, + ISCSI_NET_PARAM_IPV4_TOS, + ISCSI_NET_PARAM, + 1, + iface->tos, + &net_config->count, + &count); + + IFACE_SET_PARAM_STATE(&iov[net_config->count], + iface->iface_num, + ISCSI_IFACE_TYPE_IPV4, + ISCSI_NET_PARAM_IPV4_GRAT_ARP_EN, + ISCSI_NET_PARAM, + iface->gratuitous_arp, + &net_config->count, + &count); + + IFACE_SET_PARAM_STATE(&iov[net_config->count], + iface->iface_num, + ISCSI_IFACE_TYPE_IPV4, + ISCSI_NET_PARAM_IPV4_DHCP_ALT_CLIENT_ID_EN, + ISCSI_NET_PARAM, + iface->dhcp_alt_client_id_state, + &net_config->count, + &count); + + IFACE_SET_NET_PARAM_STRVAL(&iov[net_config->count], + iface->iface_num, + ISCSI_IFACE_TYPE_IPV4, + ISCSI_NET_PARAM_IPV4_DHCP_ALT_CLIENT_ID, + strlen(iface->dhcp_alt_client_id), + iface->dhcp_alt_client_id, + &net_config->count, + &count); + + IFACE_SET_PARAM_STATE(&iov[net_config->count], + iface->iface_num, + ISCSI_IFACE_TYPE_IPV4, + ISCSI_NET_PARAM_IPV4_DHCP_REQ_VENDOR_ID_EN, + ISCSI_NET_PARAM, + iface->dhcp_req_vendor_id_state, + &net_config->count, + &count); + + IFACE_SET_PARAM_STATE(&iov[net_config->count], + iface->iface_num, + ISCSI_IFACE_TYPE_IPV4, + ISCSI_NET_PARAM_IPV4_DHCP_USE_VENDOR_ID_EN, + ISCSI_NET_PARAM, + iface->dhcp_vendor_id_state, + &net_config->count, + &count); + + IFACE_SET_NET_PARAM_STRVAL(&iov[net_config->count], + iface->iface_num, + ISCSI_IFACE_TYPE_IPV4, + ISCSI_NET_PARAM_IPV4_DHCP_VENDOR_ID, + strlen(iface->dhcp_vendor_id), + iface->dhcp_vendor_id, + &net_config->count, + &count); + + IFACE_SET_PARAM_STATE(&iov[net_config->count], + iface->iface_num, + ISCSI_IFACE_TYPE_IPV4, + ISCSI_NET_PARAM_IPV4_DHCP_LEARN_IQN_EN, + ISCSI_NET_PARAM, + iface->dhcp_learn_iqn, + &net_config->count, + &count); + + IFACE_SET_PARAM_STATE(&iov[net_config->count], + iface->iface_num, + ISCSI_IFACE_TYPE_IPV4, + ISCSI_NET_PARAM_IPV4_FRAGMENT_DISABLE, + ISCSI_NET_PARAM, + iface->fragmentation, + &net_config->count, + &count); + + IFACE_SET_PARAM_STATE(&iov[net_config->count], + iface->iface_num, + ISCSI_IFACE_TYPE_IPV4, + ISCSI_NET_PARAM_IPV4_IN_FORWARD_EN, + ISCSI_NET_PARAM, + iface->incoming_forwarding, + &net_config->count, + &count); + + IFACE_SET_PARAM_INTVAL(&iov[net_config->count], + iface->iface_num, + ISCSI_IFACE_TYPE_IPV4, + ISCSI_NET_PARAM_IPV4_TTL, + ISCSI_NET_PARAM, + 1, + iface->ttl, + &net_config->count, + &count); } - } else if (iptype == ISCSI_IFACE_TYPE_IPV6) { + break; + + case ISCSI_IFACE_TYPE_IPV6: if (!strcmp(iface->state, "disable")) { - if (!iface_fill_net_state(&iov[net_config->count], - iface, - ISCSI_IFACE_TYPE_IPV6)) { - net_config->count++; - count++; - } + IFACE_SET_PARAM_STATE(&iov[net_config->count], + iface->iface_num, + ISCSI_IFACE_TYPE_IPV6, + ISCSI_NET_PARAM_IFACE_ENABLE, + ISCSI_NET_PARAM, + iface->state, + &net_config->count, + &count); return 0; } @@ -1717,12 +2074,12 @@ static int __iface_build_net_config(void *data, struct iface_rec *iface) count++; } /* User provided IPv6 Address */ - if (!iface_fill_net_ipv6_addr(&iov[net_config->count], - iface, - ISCSI_NET_PARAM_IPV6_ADDR)) { - net_config->count++; - count++; - } + IFACE_SET_NET_PARAM_IPV6_ADDR(&iov[net_config->count], + iface->iface_num, + ISCSI_NET_PARAM_IPV6_ADDR, + iface->ipaddress, + &net_config->count, + &count); } /* For LinkLocal Address */ @@ -1741,12 +2098,12 @@ static int __iface_build_net_config(void *data, struct iface_rec *iface) count++; } /* User provided Link Local Address */ - if (!iface_fill_net_ipv6_addr(&iov[net_config->count], - iface, - ISCSI_NET_PARAM_IPV6_LINKLOCAL)) { - net_config->count++; - count++; - } + IFACE_SET_NET_PARAM_IPV6_ADDR(&iov[net_config->count], + iface->iface_num, + ISCSI_NET_PARAM_IPV6_LINKLOCAL, + iface->ipv6_linklocal, + &net_config->count, + &count); } /* For Router Address */ @@ -1763,12 +2120,12 @@ static int __iface_build_net_config(void *data, struct iface_rec *iface) count++; } /* User provided Router Address */ - if (!iface_fill_net_ipv6_addr(&iov[net_config->count], - iface, - ISCSI_NET_PARAM_IPV6_ROUTER)) { - net_config->count++; - count++; - } + IFACE_SET_NET_PARAM_IPV6_ADDR(&iov[net_config->count], + iface->iface_num, + ISCSI_NET_PARAM_IPV6_ROUTER, + iface->ipv6_router, + &net_config->count, + &count); } /* @@ -1776,44 +2133,378 @@ static int __iface_build_net_config(void *data, struct iface_rec *iface) * fill state and other parameters */ if (count) { - if (!iface_fill_net_state(&iov[net_config->count], - iface, - ISCSI_IFACE_TYPE_IPV6)) { - net_config->count++; - count++; - } - if (!iface_fill_vlan_state(&iov[net_config->count], - iface, - ISCSI_IFACE_TYPE_IPV6)) { + IFACE_SET_PARAM_STATE(&iov[net_config->count], + iface->iface_num, + ISCSI_IFACE_TYPE_IPV6, + ISCSI_NET_PARAM_IPV6_GRAT_NEIGHBOR_ADV_EN, + ISCSI_NET_PARAM, + iface->gratuitous_neighbor_adv, + &net_config->count, + &count); + + IFACE_SET_PARAM_STATE(&iov[net_config->count], + iface->iface_num, + ISCSI_IFACE_TYPE_IPV6, + ISCSI_NET_PARAM_IPV6_MLD_EN, + ISCSI_NET_PARAM, + iface->mld, + &net_config->count, + &count); + + IFACE_SET_PARAM_INTVAL(&iov[net_config->count], + iface->iface_num, + ISCSI_IFACE_TYPE_IPV6, + ISCSI_NET_PARAM_IPV6_FLOW_LABEL, + ISCSI_NET_PARAM, + 4, + iface->flow_label, + &net_config->count, + &count); + + IFACE_SET_PARAM_INTVAL(&iov[net_config->count], + iface->iface_num, + ISCSI_IFACE_TYPE_IPV6, + ISCSI_NET_PARAM_IPV6_TRAFFIC_CLASS, + ISCSI_NET_PARAM, + 1, + iface->traffic_class, + &net_config->count, + &count); + + IFACE_SET_PARAM_INTVAL(&iov[net_config->count], + iface->iface_num, + ISCSI_IFACE_TYPE_IPV6, + ISCSI_NET_PARAM_IPV6_HOP_LIMIT, + ISCSI_NET_PARAM, + 1, + iface->hop_limit, + &net_config->count, + &count); + + IFACE_SET_PARAM_INTVAL(&iov[net_config->count], + iface->iface_num, + ISCSI_IFACE_TYPE_IPV6, + ISCSI_NET_PARAM_IPV6_ND_REACHABLE_TMO, + ISCSI_NET_PARAM, + 4, + iface->nd_reachable_tmo, + &net_config->count, + &count); + + IFACE_SET_PARAM_INTVAL(&iov[net_config->count], + iface->iface_num, + ISCSI_IFACE_TYPE_IPV6, + ISCSI_NET_PARAM_IPV6_ND_REXMIT_TIME, + ISCSI_NET_PARAM, + 4, + iface->nd_rexmit_time, + &net_config->count, + &count); + + IFACE_SET_PARAM_INTVAL(&iov[net_config->count], + iface->iface_num, + ISCSI_IFACE_TYPE_IPV6, + ISCSI_NET_PARAM_IPV6_ND_STALE_TMO, + ISCSI_NET_PARAM, + 4, + iface->nd_stale_tmo, + &net_config->count, + &count); + + IFACE_SET_PARAM_INTVAL(&iov[net_config->count], + iface->iface_num, + ISCSI_IFACE_TYPE_IPV6, + ISCSI_NET_PARAM_IPV6_DUP_ADDR_DETECT_CNT, + ISCSI_NET_PARAM, + 1, + iface->dup_addr_detect_cnt, + &net_config->count, + &count); + + IFACE_SET_PARAM_INTVAL(&iov[net_config->count], + iface->iface_num, + ISCSI_IFACE_TYPE_IPV6, + ISCSI_NET_PARAM_IPV6_RTR_ADV_LINK_MTU, + ISCSI_NET_PARAM, + 4, + iface->router_adv_link_mtu, + &net_config->count, + &count); + } + break; + } + + /* Fill parameters common to IPv4 and IPv6 ifaces */ + if (count) { + IFACE_SET_PARAM_STATE(&iov[net_config->count], + iface->iface_num, + iptype, + ISCSI_NET_PARAM_IFACE_ENABLE, + ISCSI_NET_PARAM, + iface->state, + &net_config->count, + &count); + + IFACE_SET_PARAM_STATE(&iov[net_config->count], + iface->iface_num, + iptype, + ISCSI_NET_PARAM_VLAN_ENABLED, + ISCSI_NET_PARAM, + iface->vlan_state, + &net_config->count, + &count); + + if (strcmp(iface->vlan_state, "disable") && iface->vlan_id) { + if (!iface_fill_vlan_id(&iov[net_config->count], iface, + iptype)) { net_config->count++; count++; } - if (strcmp(iface->vlan_state, "disable") && - iface->vlan_id) { - if (!iface_fill_vlan_id(&iov[net_config->count], - iface, - ISCSI_IFACE_TYPE_IPV6)) { - net_config->count++; - count++; - } - } - if (iface->mtu) { - if (!iface_fill_mtu(&iov[net_config->count], - iface, - ISCSI_IFACE_TYPE_IPV6)) { - net_config->count++; - count++; - } - } - if (iface->port) { - if (!iface_fill_port(&iov[net_config->count], - iface, - ISCSI_IFACE_TYPE_IPV6)) { - net_config->count++; - count++; - } - } } + + IFACE_SET_PARAM_INTVAL(&iov[net_config->count], + iface->iface_num, + iptype, + ISCSI_NET_PARAM_MTU, + ISCSI_NET_PARAM, + 2, + iface->mtu, + &net_config->count, + &count); + + IFACE_SET_PARAM_INTVAL(&iov[net_config->count], + iface->iface_num, + iptype, + ISCSI_NET_PARAM_PORT, + ISCSI_NET_PARAM, + 2, + iface->port, + &net_config->count, + &count); + + IFACE_SET_PARAM_STATE(&iov[net_config->count], + iface->iface_num, + iptype, + ISCSI_NET_PARAM_DELAYED_ACK_EN, + ISCSI_NET_PARAM, + iface->delayed_ack, + &net_config->count, + &count); + + IFACE_SET_PARAM_STATE(&iov[net_config->count], + iface->iface_num, + iptype, + ISCSI_NET_PARAM_TCP_NAGLE_DISABLE, + ISCSI_NET_PARAM, + iface->nagle, + &net_config->count, + &count); + + IFACE_SET_PARAM_STATE(&iov[net_config->count], + iface->iface_num, + iptype, + ISCSI_NET_PARAM_TCP_WSF_DISABLE, + ISCSI_NET_PARAM, + iface->tcp_wsf_state, + &net_config->count, + &count); + + IFACE_SET_PARAM_INTVAL(&iov[net_config->count], + iface->iface_num, + iptype, + ISCSI_NET_PARAM_TCP_WSF, + ISCSI_NET_PARAM, + 1, + iface->tcp_wsf, + &net_config->count, + &count); + + IFACE_SET_PARAM_INTVAL(&iov[net_config->count], + iface->iface_num, + iptype, + ISCSI_NET_PARAM_TCP_TIMER_SCALE, + ISCSI_NET_PARAM, + 1, + iface->tcp_timer_scale, + &net_config->count, + &count); + + IFACE_SET_PARAM_STATE(&iov[net_config->count], + iface->iface_num, + iptype, + ISCSI_NET_PARAM_TCP_TIMESTAMP_EN, + ISCSI_NET_PARAM, + iface->tcp_timestamp, + &net_config->count, + &count); + + IFACE_SET_PARAM_STATE(&iov[net_config->count], + iface->iface_num, + iptype, + ISCSI_NET_PARAM_REDIRECT_EN, + ISCSI_NET_PARAM, + iface->redirect, + &net_config->count, + &count); + + IFACE_SET_PARAM_INTVAL(&iov[net_config->count], + iface->iface_num, + iptype, + ISCSI_IFACE_PARAM_DEF_TASKMGMT_TMO, + ISCSI_IFACE_PARAM, + 2, + iface->def_task_mgmt_tmo, + &net_config->count, + &count); + + IFACE_SET_PARAM_STATE(&iov[net_config->count], + iface->iface_num, + iptype, + ISCSI_IFACE_PARAM_HDRDGST_EN, + ISCSI_IFACE_PARAM, + iface->header_digest, + &net_config->count, + &count); + + IFACE_SET_PARAM_STATE(&iov[net_config->count], + iface->iface_num, + iptype, + ISCSI_IFACE_PARAM_DATADGST_EN, + ISCSI_IFACE_PARAM, + iface->data_digest, + &net_config->count, + &count); + + IFACE_SET_PARAM_STATE(&iov[net_config->count], + iface->iface_num, + iptype, + ISCSI_IFACE_PARAM_IMM_DATA_EN, + ISCSI_IFACE_PARAM, + iface->immediate_data, + &net_config->count, + &count); + + IFACE_SET_PARAM_STATE(&iov[net_config->count], + iface->iface_num, + iptype, + ISCSI_IFACE_PARAM_INITIAL_R2T_EN, + ISCSI_IFACE_PARAM, + iface->initial_r2t, + &net_config->count, + &count); + + IFACE_SET_PARAM_STATE(&iov[net_config->count], + iface->iface_num, + iptype, + ISCSI_IFACE_PARAM_DATASEQ_INORDER_EN, + ISCSI_IFACE_PARAM, + iface->data_seq_inorder, + &net_config->count, + &count); + + IFACE_SET_PARAM_STATE(&iov[net_config->count], + iface->iface_num, + iptype, + ISCSI_IFACE_PARAM_PDU_INORDER_EN, + ISCSI_IFACE_PARAM, + iface->data_pdu_inorder, + &net_config->count, + &count); + + IFACE_SET_PARAM_INTVAL(&iov[net_config->count], + iface->iface_num, + iptype, + ISCSI_IFACE_PARAM_ERL, + ISCSI_IFACE_PARAM, + 1, + iface->erl, + &net_config->count, + &count); + + IFACE_SET_PARAM_INTVAL(&iov[net_config->count], + iface->iface_num, + iptype, + ISCSI_IFACE_PARAM_MAX_RECV_DLENGTH, + ISCSI_IFACE_PARAM, + 4, + iface->max_recv_dlength, + &net_config->count, + &count); + + IFACE_SET_PARAM_INTVAL(&iov[net_config->count], + iface->iface_num, + iptype, + ISCSI_IFACE_PARAM_FIRST_BURST, + ISCSI_IFACE_PARAM, + 4, + iface->first_burst_len, + &net_config->count, + &count); + + IFACE_SET_PARAM_INTVAL(&iov[net_config->count], + iface->iface_num, + iptype, + ISCSI_IFACE_PARAM_MAX_R2T, + ISCSI_IFACE_PARAM, + 2, + iface->max_out_r2t, + &net_config->count, + &count); + + IFACE_SET_PARAM_INTVAL(&iov[net_config->count], + iface->iface_num, + iptype, + ISCSI_IFACE_PARAM_MAX_BURST, + ISCSI_IFACE_PARAM, + 4, + iface->max_burst_len, + &net_config->count, + &count); + + IFACE_SET_PARAM_STATE(&iov[net_config->count], + iface->iface_num, + iptype, + ISCSI_IFACE_PARAM_CHAP_AUTH_EN, + ISCSI_IFACE_PARAM, + iface->chap_auth, + &net_config->count, + &count); + + IFACE_SET_PARAM_STATE(&iov[net_config->count], + iface->iface_num, + iptype, + ISCSI_IFACE_PARAM_BIDI_CHAP_EN, + ISCSI_IFACE_PARAM, + iface->bidi_chap, + &net_config->count, + &count); + + IFACE_SET_PARAM_STATE(&iov[net_config->count], + iface->iface_num, + iptype, + ISCSI_IFACE_PARAM_STRICT_LOGIN_COMP_EN, + ISCSI_IFACE_PARAM, + iface->strict_login_comp, + &net_config->count, + &count); + + IFACE_SET_PARAM_STATE(&iov[net_config->count], + iface->iface_num, + iptype, + ISCSI_IFACE_PARAM_DISCOVERY_AUTH_OPTIONAL, + ISCSI_IFACE_PARAM, + iface->discovery_auth, + &net_config->count, + &count); + + IFACE_SET_PARAM_STATE(&iov[net_config->count], + iface->iface_num, + iptype, + ISCSI_IFACE_PARAM_DISCOVERY_LOGOUT_EN, + ISCSI_IFACE_PARAM, + iface->discovery_logout, + &net_config->count, + &count); } return 0; } @@ -1832,7 +2523,7 @@ int iface_build_net_config(struct iface_rec *iface, int iface_all, int num_found = 0, rc; struct iface_net_config net_config; - log_debug(8, "In iface_build_net_config\n"); + log_debug(8, "In iface_build_net_config"); net_config.primary = iface; net_config.iovs = iovs; @@ -1844,7 +2535,7 @@ int iface_build_net_config(struct iface_rec *iface, int iface_all, else rc = __iface_build_net_config(&net_config, iface); - log_debug(8, "iface_build_net_config: rc = %d, count = %d\n", + log_debug(8, "iface_build_net_config: rc = %d, count = %d", rc, net_config.count); return net_config.count; } diff --git a/usr/initiator.c b/usr/initiator.c index 79ca32c..3b39c5d 100644 --- a/usr/initiator.c +++ b/usr/initiator.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "initiator.h" #include "transport.h" @@ -45,6 +46,7 @@ #include "iscsi_sysfs.h" #include "iscsi_settings.h" #include "iface.h" +#include "host.h" #include "sysdeps.h" #include "iscsi_err.h" #include "kern_err_table.h" @@ -54,10 +56,19 @@ #define PROC_DIR "/proc" +struct login_task_retry_info { + actor_t retry_actor; + queue_task_t *qtask; + node_rec_t *rec; + int retry_count; +}; + static void iscsi_login_timedout(void *data); static int iscsi_sched_ev_context(struct iscsi_ev_context *ev_context, struct iscsi_conn *conn, unsigned long tmo, int event); +static int queue_session_login_task_retry(struct login_task_retry_info *info, + node_rec_t *rec, queue_task_t *qtask); static int iscsi_ev_context_alloc(iscsi_conn_t *conn) { @@ -222,7 +233,7 @@ __check_iscsi_status_class(iscsi_session_t *session, int cid, } case ISCSI_STATUS_CLS_TARGET_ERR: log_error("conn %d login rejected: target error " - "(%02x/%02x)\n", conn->id, status_class, status_detail); + "(%02x/%02x)", conn->id, status_class, status_detail); /* * We have no idea what the problem is. But spec says initiator * may retry later. @@ -230,7 +241,7 @@ __check_iscsi_status_class(iscsi_session_t *session, int cid, return CONN_LOGIN_RETRY; default: log_error("conn %d login response with unknown status " - "class 0x%x, detail 0x%x\n", conn->id, status_class, + "class 0x%x, detail 0x%x", conn->id, status_class, status_detail); break; } @@ -265,7 +276,7 @@ __session_conn_create(iscsi_session_t *session, int cid) conn->logout_timeout = conn_rec->timeo.logout_timeout; if (!conn->logout_timeout) { log_error("Invalid timeo.logout_timeout. Must be greater " - "than zero. Using default %d.\n", + "than zero. Using default %d.", DEF_LOGOUT_TIMEO); conn->logout_timeout = DEF_LOGOUT_TIMEO; } @@ -273,7 +284,7 @@ __session_conn_create(iscsi_session_t *session, int cid) conn->login_timeout = conn_rec->timeo.login_timeout; if (!conn->login_timeout) { log_error("Invalid timeo.login_timeout. Must be greater " - "than zero. Using default %d.\n", + "than zero. Using default %d.", DEF_LOGIN_TIMEO); conn->login_timeout = DEF_LOGIN_TIMEO; } @@ -285,14 +296,14 @@ __session_conn_create(iscsi_session_t *session, int cid) conn->noop_out_timeout = conn_rec->timeo.noop_out_timeout; if (conn->noop_out_interval && !conn->noop_out_timeout) { log_error("Invalid timeo.noop_out_timeout. Must be greater " - "than zero. Using default %d.\n", + "than zero. Using default %d.", DEF_NOOP_OUT_TIMEO); conn->noop_out_timeout = DEF_NOOP_OUT_TIMEO; } if (conn->noop_out_timeout && !conn->noop_out_interval) { log_error("Invalid timeo.noop_out_interval. Must be greater " - "than zero. Using default %d.\n", + "than zero. Using default %d.", DEF_NOOP_OUT_INTERVAL); conn->noop_out_interval = DEF_NOOP_OUT_INTERVAL; } @@ -323,14 +334,17 @@ session_release(iscsi_session_t *session) } static iscsi_session_t* -__session_create(node_rec_t *rec, struct iscsi_transport *t) +__session_create(node_rec_t *rec, struct iscsi_transport *t, int *rc) { iscsi_session_t *session; - int hostno, rc = 0; + int hostno; + + *rc = 0; session = calloc(1, sizeof (*session)); if (session == NULL) { log_debug(1, "can not allocate memory for session"); + *rc = ISCSI_ERR_NOMEM; return NULL; } log_debug(2, "Allocted session %p", session); @@ -355,8 +369,8 @@ __session_create(node_rec_t *rec, struct iscsi_transport *t) session->initiator_name = dconfig->initiator_name; else { log_error("No initiator name set. Cannot create session."); - free(session); - return NULL; + *rc = ISCSI_ERR_INVAL; + goto free_session; } if (strlen(session->nrec.iface.alias)) @@ -383,31 +397,31 @@ __session_create(node_rec_t *rec, struct iscsi_transport *t) /* setup authentication variables for the session*/ iscsi_setup_authentication(session, &rec->session.auth); - session->param_mask = ~0ULL; - if (!(t->caps & CAP_MULTI_R2T)) - session->param_mask &= ~ISCSI_MAX_R2T; - if (!(t->caps & CAP_HDRDGST)) - session->param_mask &= ~ISCSI_HDRDGST_EN; - if (!(t->caps & CAP_DATADGST)) - session->param_mask &= ~ISCSI_DATADGST_EN; - if (!(t->caps & CAP_MARKERS)) { - session->param_mask &= ~ISCSI_IFMARKER_EN; - session->param_mask &= ~ISCSI_OFMARKER_EN; - } + iscsi_session_init_params(session); - hostno = iscsi_sysfs_get_host_no_from_hwinfo(&rec->iface, &rc); - if (!rc) { - /* - * if the netdev or mac was set, then we are going to want - * to want to bind the all the conns/eps to a specific host - * if offload is used. - */ - session->conn[0].bind_ep = 1; - session->hostno = hostno; - } + if (t->template->bind_ep_required) { + hostno = iscsi_sysfs_get_host_no_from_hwinfo(&rec->iface, rc); + if (!*rc) { + /* + * if the netdev or mac was set, then we are going to want + * to want to bind the all the conns/eps to a specific host + * if offload is used. + */ + session->conn[0].bind_ep = 1; + session->hostno = hostno; + } else if (*rc == ISCSI_ERR_HOST_NOT_FOUND) { + goto free_session; + } else { + *rc = 0; + } + } list_add_tail(&session->list, &t->sessions); return session; + +free_session: + free(session); + return NULL; } static void iscsi_flush_context_pool(struct iscsi_session *session) @@ -431,7 +445,7 @@ static void iscsi_flush_context_pool(struct iscsi_session *session) static void __session_destroy(iscsi_session_t *session) { - log_debug(1, "destroying session\n"); + log_debug(1, "destroying session"); list_del(&session->list); iscsi_flush_context_pool(session); session_release(session); @@ -509,14 +523,14 @@ queue_delayed_reopen(queue_task_t *qtask, int delay) { iscsi_conn_t *conn = qtask->conn; - log_debug(4, "Requeue reopen attempt in %d secs\n", delay); + log_debug(4, "Requeue reopen attempt in %d secs", delay); /* * iscsi_login_eh can handle the login resched as * if it were login time out */ actor_delete(&conn->login_timer); - actor_timer(&conn->login_timer, delay * 1000, + actor_timer(&conn->login_timer, delay, iscsi_login_timedout, qtask); } @@ -552,11 +566,53 @@ static int iscsi_conn_connect(struct iscsi_conn *conn, queue_task_t *qtask) iscsi_sched_ev_context(ev_context, conn, 0, EV_CONN_POLL); log_debug(3, "Setting login timer %p timeout %d", &conn->login_timer, conn->login_timeout); - actor_timer(&conn->login_timer, conn->login_timeout * 1000, + actor_timer(&conn->login_timer, conn->login_timeout, iscsi_login_timedout, qtask); return 0; } +static void iscsi_uio_poll_login_timedout(void *data) +{ + struct queue_task *qtask = data; + struct iscsi_conn *conn = qtask->conn; + iscsi_session_t *session = conn->session; + + log_debug(3, "timeout waiting for UIO ..."); + mgmt_ipc_write_rsp(qtask, ISCSI_ERR_TRANS_TIMEOUT); + conn_delete_timers(conn); + __session_destroy(session); +} + +static int iscsi_sched_uio_poll(queue_task_t *qtask) +{ + struct iscsi_conn *conn = qtask->conn; + struct iscsi_session *session = conn->session; + struct iscsi_transport *t = session->t; + struct iscsi_ev_context *ev_context; + + if (!t->template->set_net_config) + return 0; + + ev_context = iscsi_ev_context_get(conn, 0); + if (!ev_context) { + /* while reopening the recv pool should be full */ + log_error("BUG: __session_conn_reopen could " + "not get conn context for recv."); + return -ENOMEM; + } + + ev_context->data = qtask; + conn->state = ISCSI_CONN_STATE_XPT_WAIT; + + iscsi_sched_ev_context(ev_context, conn, 0, EV_UIO_POLL); + + log_debug(3, "Setting login UIO poll timer %p timeout %d", + &conn->login_timer, conn->login_timeout); + actor_timer(&conn->login_timer, conn->login_timeout, + iscsi_uio_poll_login_timedout, qtask); + return -EAGAIN; +} + static void __session_conn_reopen(iscsi_conn_t *conn, queue_task_t *qtask, int do_stop, int redirected) @@ -598,6 +654,11 @@ __session_conn_reopen(iscsi_conn_t *conn, queue_task_t *qtask, int do_stop, if (!redirected) session->reopen_cnt++; + /* uIP will needs to be re-triggered on the connection re-open */ + if (iscsi_set_net_config(conn->session->t, conn->session, + &conn->session->nrec.iface) != 0) + goto queue_reopen; + if (iscsi_conn_connect(conn, qtask)) { delay = ISCSI_CONN_ERR_REOPEN_DELAY; goto queue_reopen; @@ -605,7 +666,7 @@ __session_conn_reopen(iscsi_conn_t *conn, queue_task_t *qtask, int do_stop, return; queue_reopen: - log_debug(4, "Waiting %u seconds before trying to reconnect.\n", delay); + log_debug(4, "Waiting %u seconds before trying to reconnect.", delay); queue_delayed_reopen(qtask, delay); } @@ -638,7 +699,7 @@ static int iscsi_retry_initial_login(struct iscsi_conn *conn) timeout.tv_sec = initial_login_retry_max * conn->login_timeout; if (gettimeofday(&now, NULL)) { log_error("Could not get time of day. Dropping down to " - "max retry check.\n"); + "max retry check."); return initial_login_retry_max > conn->session->reopen_cnt; } timeradd(&conn->initial_connect_time, &timeout, &fail_time); @@ -649,7 +710,7 @@ static int iscsi_retry_initial_login(struct iscsi_conn *conn) */ if (timercmp(&now, &fail_time, >)) { log_debug(1, "Giving up on initial login attempt after " - "%u seconds.\n", + "%u seconds.", initial_login_retry_max * conn->login_timeout); return 0; } @@ -756,7 +817,7 @@ static void iscsi_login_eh(struct iscsi_conn *conn, struct queue_task *qtask, break; default: - log_error("Ignoring login error %d in conn state %d.\n", + log_error("Ignoring login error %d in conn state %d.", err, conn->state); break; } @@ -830,7 +891,7 @@ __conn_error_handle(iscsi_session_t *session, iscsi_conn_t *conn) "let connection stop"); return; default: - log_debug(8, "invalid state %d\n", conn->state); + log_debug(8, "invalid state %d", conn->state); return; } @@ -887,7 +948,7 @@ static void iscsi_login_redirect(iscsi_conn_t *conn) iscsi_session_t *session = conn->session; iscsi_login_context_t *c = &conn->login_context; - log_debug(3, "login redirect ...\n"); + log_debug(3, "login redirect ..."); if (session->r_stage == R_STAGE_NO_CHANGE) session->r_stage = R_STAGE_SESSION_REDIRECT; @@ -954,9 +1015,9 @@ static void conn_send_nop_out(void *data) __send_nopout(conn); - actor_timer(&conn->nop_out_timer, conn->noop_out_timeout*1000, + actor_timer(&conn->nop_out_timer, conn->noop_out_timeout, conn_nop_out_timeout, conn); - log_debug(3, "noop out timeout timer %p start, timeout %d\n", + log_debug(3, "noop out timeout timer %p start, timeout %d", &conn->nop_out_timer, conn->noop_out_timeout); } @@ -993,7 +1054,7 @@ static void session_scan_host(struct iscsi_session *session, int hostno, exit(0); } else if (pid > 0) { reap_inc(); - if (qtask) { + if (qtask && qtask->mgmt_ipc_fd >= 0) { close(qtask->mgmt_ipc_fd); free(qtask); } @@ -1010,12 +1071,7 @@ setup_full_feature_phase(iscsi_conn_t *conn) actor_delete(&conn->login_timer); - if (iscsi_session_set_params(conn)) { - iscsi_login_eh(conn, c->qtask, ISCSI_ERR_LOGIN); - return; - } - - if (iscsi_host_set_params(session)) { + if (iscsi_session_set_neg_params(conn)) { iscsi_login_eh(conn, c->qtask, ISCSI_ERR_LOGIN); return; } @@ -1062,9 +1118,9 @@ setup_full_feature_phase(iscsi_conn_t *conn) /* noop_out */ if (conn->userspace_nop && conn->noop_out_interval) { - actor_timer(&conn->nop_out_timer, conn->noop_out_interval*1000, + actor_timer(&conn->nop_out_timer, conn->noop_out_interval, conn_send_nop_out, conn); - log_debug(3, "noop out timer %p start\n", + log_debug(3, "noop out timer %p start", &conn->nop_out_timer); } } @@ -1079,7 +1135,7 @@ static void iscsi_logout_timedout(void *data) * assume we were in ISCSI_CONN_STATE_IN_LOGOUT or there * was some nasty error */ - log_debug(3, "logout timeout, dropping conn...\n"); + log_debug(3, "logout timeout, dropping conn..."); __conn_error_handle(conn->session, conn); } @@ -1110,7 +1166,7 @@ static int iscsi_send_logout(iscsi_conn_t *conn) iscsi_sched_ev_context(ev_context, conn, conn->logout_timeout, EV_CONN_LOGOUT_TIMER); - log_debug(3, "logout timeout timer %u\n", + log_debug(3, "logout timeout timer %u", conn->logout_timeout * 1000); } @@ -1146,7 +1202,7 @@ static void iscsi_recv_nop_in(iscsi_conn_t *conn, struct iscsi_hdr *hdr) /* noop out rsp */ actor_delete(&conn->nop_out_timer); /* schedule a new ping */ - actor_timer(&conn->nop_out_timer, conn->noop_out_interval*1000, + actor_timer(&conn->nop_out_timer, conn->noop_out_interval, conn_send_nop_out, conn); } else /* noop in req */ if (!__send_nopin_rsp(conn, (struct iscsi_nopin*)hdr, @@ -1159,7 +1215,7 @@ static void iscsi_recv_logout_rsp(iscsi_conn_t *conn, struct iscsi_hdr *hdr) { struct iscsi_logout_rsp *logout_rsp = (struct iscsi_logout_rsp *)hdr; - log_debug(3, "Recv: logout response %d\n", logout_rsp->response); + log_debug(3, "Recv: logout response %d", logout_rsp->response); if (logout_rsp->response == 2 || logout_rsp->response == 3) { conn->session->def_time2wait = ntohs(logout_rsp->t2wait); log_debug(4, "logout rsp returned time2wait %u", @@ -1177,7 +1233,7 @@ static void iscsi_recv_async_msg(iscsi_conn_t *conn, struct iscsi_hdr *hdr) unsigned int senselen; struct scsi_sense_hdr sshdr; - log_debug(3, "Read AEN %d\n", async_hdr->async_event); + log_debug(3, "Read AEN %d", async_hdr->async_event); switch (async_hdr->async_event) { case ISCSI_ASYNC_MSG_SCSI_EVENT: @@ -1195,33 +1251,33 @@ static void iscsi_recv_async_msg(iscsi_conn_t *conn, struct iscsi_hdr *hdr) break; case ISCSI_ASYNC_MSG_REQUEST_LOGOUT: log_warning("Target requests logout within %u seconds for " - "connection\n", ntohs(async_hdr->param3)); + "connection", ntohs(async_hdr->param3)); if (iscsi_send_logout(conn)) log_error("Could not send logout in response to" - "logout request aen\n"); + "logout request aen"); break; case ISCSI_ASYNC_MSG_DROPPING_CONNECTION: log_warning("Target dropping connection %u, reconnect min %u " - "max %u\n", ntohs(async_hdr->param1), + "max %u", ntohs(async_hdr->param1), ntohs(async_hdr->param2), ntohs(async_hdr->param3)); session->def_time2wait = (uint32_t)ntohs(async_hdr->param2) & 0x0000FFFFFL; break; case ISCSI_ASYNC_MSG_DROPPING_ALL_CONNECTIONS: log_warning("Target dropping all connections, reconnect min %u " - "max %u\n", ntohs(async_hdr->param2), + "max %u", ntohs(async_hdr->param2), ntohs(async_hdr->param3)); session->def_time2wait = (uint32_t)ntohs(async_hdr->param2) & 0x0000FFFFFL; break; case ISCSI_ASYNC_MSG_PARAM_NEGOTIATION: log_warning("Received async event param negotiation, " - "dropping session\n"); + "dropping session"); __conn_error_handle(session, conn); break; case ISCSI_ASYNC_MSG_VENDOR_SPECIFIC: default: - log_warning("AEN not supported\n"); + log_warning("AEN not supported"); } } @@ -1336,7 +1392,7 @@ static void session_conn_recv_pdu(void *data) break; default: iscsi_ev_context_put(ev_context); - log_error("Invalid state. Dropping PDU.\n"); + log_error("Invalid state. Dropping PDU."); } } @@ -1420,7 +1476,7 @@ static void session_increase_wq_priority(struct iscsi_session *session) fail: log_error("Could not set session%d priority. " "READ/WRITE throughout and latency could be " - "affected.\n", session->id); + "affected.", session->id); } static int session_ipc_create(struct iscsi_session *session) @@ -1469,6 +1525,11 @@ static void setup_offload_login_phase(iscsi_conn_t *conn) return; } + if (iscsi_session_set_neg_params(conn)) { + iscsi_login_eh(conn, c->qtask, ISCSI_ERR_LOGIN); + return; + } + if (iscsi_host_set_params(session)) { iscsi_login_eh(conn, c->qtask, ISCSI_ERR_LOGIN); return; @@ -1511,7 +1572,6 @@ static void session_conn_poll(void *data) rc = session->t->template->ep_poll(conn, 1); if (rc == 0) { log_debug(4, "poll not connected %d", rc); - /* timedout: Poll again. */ ev_context = iscsi_ev_context_get(conn, 0); if (!ev_context) { /* while polling the recv pool should be full */ @@ -1521,7 +1581,8 @@ static void session_conn_poll(void *data) return; } ev_context->data = qtask; - iscsi_sched_ev_context(ev_context, conn, 0, EV_CONN_POLL); + /* not connected yet, check later */ + iscsi_sched_ev_context(ev_context, conn, 1, EV_CONN_POLL); } else if (rc > 0) { /* connected! */ memset(c, 0, sizeof(iscsi_login_context_t)); @@ -1580,6 +1641,16 @@ static void session_conn_poll(void *data) return; } + if (iscsi_session_set_params(conn)) { + iscsi_login_eh(conn, qtask, ISCSI_ERR_LOGIN); + return; + } + + if (iscsi_host_set_params(session)) { + iscsi_login_eh(conn, qtask, ISCSI_ERR_LOGIN); + return; + } + if (iscsi_login_begin(session, c)) { iscsi_login_eh(conn, qtask, ISCSI_ERR_LOGIN); return; @@ -1618,6 +1689,9 @@ static void session_conn_process_login(void *data) if (state == ISCSI_CONN_STATE_FREE) goto failed_login; + if (conn->state == ISCSI_CONN_STATE_LOGGED_IN) + return; + conn->state = ISCSI_CONN_STATE_LOGGED_IN; /* * ok we were in_login and now we got the notification that we are @@ -1667,6 +1741,53 @@ failed_login: } +static void session_conn_uio_poll(void *data) +{ + struct iscsi_ev_context *ev_context = data; + iscsi_conn_t *conn = ev_context->conn; + struct iscsi_session *session = conn->session; + queue_task_t *qtask = ev_context->data; + int rc; + + log_debug(4, "retrying uio poll"); + rc = iscsi_set_net_config(session->t, session, + &conn->session->nrec.iface); + if (rc != 0) { + if (rc == ISCSI_ERR_AGAIN) { + ev_context->data = qtask; + iscsi_sched_ev_context(ev_context, conn, 2, + EV_UIO_POLL); + return; + } else { + log_error("session_conn_uio_poll() " + "connection failure [0x%x]", rc); + actor_delete(&conn->login_timer); + iscsi_login_eh(conn, qtask, ISCSI_ERR_INTERNAL); + iscsi_ev_context_put(ev_context); + return; + } + } + + iscsi_ev_context_put(ev_context); + actor_delete(&conn->login_timer); + log_debug(4, "UIO ready trying connect"); + + /* uIP is ready try to connect */ + if (gettimeofday(&conn->initial_connect_time, NULL)) + log_error("Could not get initial connect time. If " + "login errors iscsid may give up the initial " + "login early. You should manually login."); + + conn->state = ISCSI_CONN_STATE_XPT_WAIT; + if (iscsi_conn_connect(conn, qtask)) { + int delay = ISCSI_CONN_ERR_REOPEN_DELAY; + + log_debug(4, "Waiting %u seconds before trying to reconnect.", + delay); + queue_delayed_reopen(qtask, delay); + } +} + static int iscsi_sched_ev_context(struct iscsi_ev_context *ev_context, struct iscsi_conn *conn, unsigned long tmo, int event) @@ -1679,14 +1800,14 @@ static int iscsi_sched_ev_context(struct iscsi_ev_context *ev_context, ev_context->conn = conn; switch (event) { case EV_CONN_RECV_PDU: - actor_new(&ev_context->actor, session_conn_recv_pdu, + actor_init(&ev_context->actor, session_conn_recv_pdu, ev_context); actor_schedule(&ev_context->actor); break; case EV_CONN_ERROR: error = *(enum iscsi_err *)ev_context->data; - actor_new(&ev_context->actor, session_conn_error, + actor_init(&ev_context->actor, session_conn_error, ev_context); /* * We handle invalid host, by killing the session. @@ -1699,21 +1820,25 @@ static int iscsi_sched_ev_context(struct iscsi_ev_context *ev_context, actor_schedule(&ev_context->actor); break; case EV_CONN_LOGIN: - actor_new(&ev_context->actor, session_conn_process_login, + actor_init(&ev_context->actor, session_conn_process_login, ev_context); actor_schedule(&ev_context->actor); break; case EV_CONN_POLL: - actor_new(&ev_context->actor, session_conn_poll, + actor_timer(&ev_context->actor, tmo, + session_conn_poll, ev_context); + break; + case EV_UIO_POLL: + actor_init(&ev_context->actor, session_conn_uio_poll, ev_context); actor_schedule(&ev_context->actor); break; case EV_CONN_LOGOUT_TIMER: - actor_timer(&ev_context->actor, tmo * 1000, + actor_timer(&ev_context->actor, tmo, iscsi_logout_timedout, ev_context); break; case EV_CONN_STOP: - actor_new(&ev_context->actor, iscsi_stop, + actor_init(&ev_context->actor, iscsi_stop, ev_context); actor_schedule(&ev_context->actor); break; @@ -1752,14 +1877,14 @@ static int session_is_running(node_rec_t *rec) if (session_find_by_rec(rec)) return 1; - if (iscsi_sysfs_for_each_session(rec, &nr_found, iscsi_match_session)) + if (iscsi_sysfs_for_each_session(rec, &nr_found, iscsi_match_session, + 0)) return 1; return 0; } -int -session_login_task(node_rec_t *rec, queue_task_t *qtask) +static int __session_login_task(node_rec_t *rec, queue_task_t *qtask) { iscsi_session_t *session; iscsi_conn_t *conn; @@ -1782,7 +1907,7 @@ session_login_task(node_rec_t *rec, queue_task_t *qtask) (!(t->caps & CAP_RECOVERY_L1) && rec->session.iscsi.ERL > 1)) { log_error("Transport '%s' does not support ERL %d." - "Setting ERL to ERL0.\n", + "Setting ERL to ERL0.", t->name, rec->session.iscsi.ERL); rec->session.iscsi.ERL = 0; } @@ -1815,19 +1940,21 @@ session_login_task(node_rec_t *rec, queue_task_t *qtask) if (!(t->caps & CAP_MARKERS) && rec->conn[0].iscsi.IFMarker) { log_error("Transport '%s' does not support IFMarker. " - "Disabling IFMarkers.\n", t->name); + "Disabling IFMarkers.", t->name); rec->conn[0].iscsi.IFMarker = 0; } if (!(t->caps & CAP_MARKERS) && rec->conn[0].iscsi.OFMarker) { log_error("Transport '%s' does not support OFMarker." - "Disabling OFMarkers.\n", t->name); + "Disabling OFMarkers.", t->name); rec->conn[0].iscsi.OFMarker = 0; } - session = __session_create(rec, t); - if (!session) + session = __session_create(rec, t, &rc); + if (rc == ISCSI_ERR_HOST_NOT_FOUND) + return rc; + else if (!session) return ISCSI_ERR_LOGIN; /* FIXME: login all connections! marked as "automatic" */ @@ -1841,7 +1968,17 @@ session_login_task(node_rec_t *rec, queue_task_t *qtask) conn = &session->conn[0]; qtask->conn = conn; - if (iscsi_host_set_net_params(&rec->iface, session)) { + rc = iscsi_host_set_net_params(&rec->iface, session); + if (rc == ISCSI_ERR_AGAIN) { + iscsi_sched_uio_poll(qtask); + /* + * Cannot block iscsid, so caller is going to internally + * retry the operation. + */ + qtask->rsp.command = MGMT_IPC_SESSION_LOGIN; + qtask->rsp.err = ISCSI_SUCCESS; + return ISCSI_SUCCESS; + } else if (rc) { __session_destroy(session); return ISCSI_ERR_LOGIN; } @@ -1857,7 +1994,7 @@ session_login_task(node_rec_t *rec, queue_task_t *qtask) if (iscsi_conn_connect(conn, qtask)) { log_debug(4, "Initial connect failed. Waiting %u seconds " - "before trying to reconnect.\n", + "before trying to reconnect.", ISCSI_CONN_ERR_REOPEN_DELAY); queue_delayed_reopen(qtask, ISCSI_CONN_ERR_REOPEN_DELAY); } @@ -1865,6 +2002,74 @@ session_login_task(node_rec_t *rec, queue_task_t *qtask) return ISCSI_SUCCESS; } +int +session_login_task(node_rec_t *rec, queue_task_t *qtask) +{ + int rc; + + rc = __session_login_task(rec, qtask); + if (rc == ISCSI_ERR_HOST_NOT_FOUND) { + rc = queue_session_login_task_retry(NULL, rec, qtask); + if (rc) + return rc; + /* + * we are going to internally retry. Will return final rc + * when completed + */ + return ISCSI_SUCCESS; + } + return rc; +} + +static void session_login_task_retry(void *data) +{ + struct login_task_retry_info *info = data; + int rc; + + rc = __session_login_task(info->rec, info->qtask); + if (rc == ISCSI_ERR_HOST_NOT_FOUND) { + if (info->retry_count == 5) { + /* give up */ + goto write_rsp; + } + + rc = queue_session_login_task_retry(info, info->rec, + info->qtask); + if (rc) + goto write_rsp; + /* we are going to internally retry */ + return; + } else if (rc) { + /* hard error - no retry */ + goto write_rsp; + } else + /* successfully started login operation */ + goto free; +write_rsp: + mgmt_ipc_write_rsp(info->qtask, rc); +free: + free(info); +} + +static int queue_session_login_task_retry(struct login_task_retry_info *info, + node_rec_t *rec, queue_task_t *qtask) +{ + if (!info) { + info = malloc(sizeof(*info)); + if (!info) + return ISCSI_ERR_NOMEM; + memset(info, 0, sizeof(*info)); + info->qtask = qtask; + info->rec = rec; + } + + info->retry_count++; + log_debug(4, "queue session setup attempt in %d secs, retries %d", + 3, info->retry_count); + actor_timer(&info->retry_actor, 3, session_login_task_retry, info); + return 0; +} + static int sync_conn(iscsi_session_t *session, uint32_t cid) { @@ -1892,14 +2097,14 @@ iscsi_sync_session(node_rec_t *rec, queue_task_t *qtask, uint32_t sid) if (!t) return ISCSI_ERR_TRANS_NOT_FOUND; - session = __session_create(rec, t); + session = __session_create(rec, t, &err); if (!session) return ISCSI_ERR_LOGIN; session->id = sid; session->hostno = iscsi_sysfs_get_host_no_from_sid(sid, &err); if (err) { - log_error("Could not get hostno for session %d\n", sid); + log_error("Could not get hostno for session %d", sid); goto destroy_session; } @@ -1920,7 +2125,7 @@ iscsi_sync_session(node_rec_t *rec, queue_task_t *qtask, uint32_t sid) destroy_session: __session_destroy(session); - log_error("Could not sync session%d err %d\n", sid, err); + log_error("Could not sync session%d err %d", sid, err); return err; } @@ -1931,10 +2136,199 @@ static int session_unbind(struct iscsi_session *session) err = ipc->unbind_session(session->t->handle, session->id); if (err) /* older kernels did not support unbind */ - log_debug(2, "Could not unbind session %d.\n", err); + log_debug(2, "Could not unbind session %d.", err); return err; } +static struct libmnt_table *mtab, *swaps; + +static void libmount_cleanup(void) +{ + mnt_free_table(mtab); + mnt_free_table(swaps); + mtab = swaps = NULL; +} + +static int libmount_init(void) +{ + mnt_init_debug(0); + mtab = mnt_new_table(); + swaps = mnt_new_table(); + if (!mtab || !swaps) { + libmount_cleanup(); + return -ENOMEM; + } + mnt_table_parse_mtab(mtab, NULL); + mnt_table_parse_swaps(swaps, NULL); + return 0; +} + +static int trans_filter(const struct dirent *d) +{ + if (!strcmp(".", d->d_name) || !strcmp("..", d->d_name)) + return 0; + return 1; +} + +static int subdir_filter(const struct dirent *d) +{ + if (!(d->d_type & DT_DIR)) + return 0; + return trans_filter(d); +} + +static int is_partition(const char *path) +{ + char *devtype; + int rc = 0; + + devtype = sysfs_get_uevent_devtype(path); + if (!devtype) + return 0; + if (strcmp(devtype, "partition") == 0) + rc = 1; + free(devtype); + return rc; +} + +static int blockdev_check_mnts(char *syspath) +{ + struct libmnt_fs *fs; + char *devname = NULL; + char *_devname = NULL; + int rc = 0; + + devname = sysfs_get_uevent_devname(syspath); + if (!devname) + goto out; + + _devname = calloc(1, PATH_MAX); + if (!_devname) + goto out; + snprintf(_devname, PATH_MAX, "/dev/%s", devname); + + fs = mnt_table_find_source(mtab, _devname, MNT_ITER_FORWARD); + if (fs) { + rc = 1; + goto out; + } + fs = mnt_table_find_source(swaps, _devname, MNT_ITER_FORWARD); + if (fs) + rc = 1; +out: + free(devname); + free(_devname); + return rc; +} + +static int count_device_users(char *syspath); + +static int blockdev_get_partitions(char *syspath) +{ + struct dirent **parts = NULL; + int n, i; + int count = 0; + + n = scandir(syspath, &parts, subdir_filter, alphasort); + for (i = 0; i < n; i++) { + char *newpath; + + newpath = calloc(1, PATH_MAX); + if (!newpath) + continue; + snprintf(newpath, PATH_MAX, "%s/%s", syspath, parts[i]->d_name); + free(parts[i]); + if (is_partition(newpath)) { + count += count_device_users(newpath); + } + free(newpath); + } + free(parts); + return count; +} + +static int blockdev_get_holders(char *syspath) +{ + char *path = NULL; + struct dirent **holds = NULL; + int n, i; + int count = 0; + + path = calloc(1, PATH_MAX); + if (!path) + return 0; + snprintf(path, PATH_MAX, "%s/holders", syspath); + + n = scandir(path, &holds, trans_filter, alphasort); + for (i = 0; i < n; i++) { + char *newpath; + char *rp; + + newpath = calloc(1, PATH_MAX); + if (!newpath) + continue; + snprintf(newpath, PATH_MAX, "%s/%s", path, holds[i]->d_name); + + free(holds[i]); + rp = realpath(newpath, NULL); + if (rp) + count += count_device_users(rp); + free(newpath); + free(rp); + } + free(path); + free(holds); + return count; +} + +static int count_device_users(char *syspath) +{ + int count = 0; + count += blockdev_check_mnts(syspath); + count += blockdev_get_partitions(syspath); + count += blockdev_get_holders(syspath); + return count; +}; + +static void device_in_use(void *data, int host_no, int target, int lun) +{ + char *syspath = NULL; + char *devname = NULL; + int *count = data; + + devname = iscsi_sysfs_get_blockdev_from_lun(host_no, target, lun); + if (!devname) + goto out; + syspath = calloc(1, PATH_MAX); + if (!syspath) + goto out; + snprintf(syspath, PATH_MAX, "/sys/class/block/%s", devname); + *count += count_device_users(syspath); +out: + free(syspath); + free(devname); +} + +static int session_in_use(int sid) +{ + int host_no = -1, err = 0; + int count = 0; + + if (libmount_init()) { + log_error("Failed to initialize libmount, " + "not checking for active mounts on session [%d].", + sid); + return 0; + } + + host_no = iscsi_sysfs_get_host_no_from_sid(sid, &err); + if (!err) + iscsi_sysfs_for_each_device(&count, host_no, sid, device_in_use); + + libmount_cleanup(); + return count; +} + int session_logout_task(int sid, queue_task_t *qtask) { iscsi_session_t *session; @@ -1943,7 +2337,7 @@ int session_logout_task(int sid, queue_task_t *qtask) session = session_find_by_sid(sid); if (!session) { - log_debug(1, "session sid %d not found.\n", sid); + log_debug(1, "session sid %d not found.", sid); return ISCSI_ERR_SESS_NOT_FOUND; } conn = &session->conn[0]; @@ -1958,10 +2352,16 @@ int session_logout_task(int sid, queue_task_t *qtask) session->r_stage == R_STAGE_SESSION_REDIRECT))) { invalid_state: log_error("session in invalid state for logout. " - "Try again later\n"); + "Try again later"); return ISCSI_ERR_INTERNAL; } + if (dconfig->safe_logout && session_in_use(sid)) { + log_error("Session is actively in use for mounted storage, " + "and iscsid.safe_logout is configured."); + return ISCSI_ERR_BUSY; + } + /* FIXME: logout all active connections */ conn = &session->conn[0]; if (conn->logout_qtask) @@ -1983,7 +2383,7 @@ invalid_state: return ISCSI_SUCCESS; } - log_error("Could not send logout pdu. Dropping session\n"); + log_error("Could not send logout pdu. Dropping session"); /* fallthrough */ default: rc = session_conn_shutdown(conn, qtask, ISCSI_SUCCESS); @@ -2001,7 +2401,7 @@ iscsi_host_send_targets(queue_task_t *qtask, int host_no, int do_login, t = iscsi_sysfs_get_transport_by_hba(host_no); if (!t) { - log_error("Invalid host no %d for sendtargets\n", host_no); + log_error("Invalid host no %d for sendtargets", host_no); return ISCSI_ERR_TRANS_NOT_FOUND; } if (!(t->caps & CAP_SENDTARGETS_OFFLOAD)) diff --git a/usr/initiator.h b/usr/initiator.h index b45caab..c34625b 100644 --- a/usr/initiator.h +++ b/usr/initiator.h @@ -83,6 +83,7 @@ typedef enum iscsi_event_e { EV_CONN_LOGOUT_TIMER, EV_CONN_STOP, EV_CONN_LOGIN, + EV_UIO_POLL, } iscsi_event_e; struct queue_task; @@ -343,6 +344,7 @@ extern void free_initiator(void); extern void iscsi_initiator_init(void); /* initiator code common to discovery and normal sessions */ +extern int iscsi_session_set_neg_params(struct iscsi_conn *conn); extern int iscsi_session_set_params(struct iscsi_conn *conn); extern int iscsi_host_set_params(struct iscsi_session *session); extern int iscsi_host_set_net_params(struct iface_rec *iface, @@ -353,5 +355,9 @@ extern void iscsi_copy_operational_params(struct iscsi_conn *conn, extern int iscsi_setup_authentication(struct iscsi_session *session, struct iscsi_auth_config *auth_cfg); extern int iscsi_setup_portal(struct iscsi_conn *conn, char *address, int port); +extern int iscsi_set_net_config(struct iscsi_transport *t, + iscsi_session_t *session, + struct iface_rec *iface); +extern void iscsi_session_init_params(struct iscsi_session *session); #endif /* INITIATOR_H */ diff --git a/usr/initiator_common.c b/usr/initiator_common.c index ef6820c..ee856b3 100644 --- a/usr/initiator_common.c +++ b/usr/initiator_common.c @@ -51,21 +51,9 @@ struct iscsi_session *session_find_by_sid(uint32_t sid) return NULL; } -/* - * calculate parameter's padding - */ -static unsigned int -__padding(unsigned int param) +const static unsigned int align_32_down(unsigned int param) { - int pad; - - pad = param & 3; - if (pad) { - pad = 4 - pad; - log_debug(1, "parameter's value %d padded to %d bytes\n", - param, param + pad); - } - return param + pad; + return param & ~0x3; } int iscsi_setup_authentication(struct iscsi_session *session, @@ -77,7 +65,7 @@ int iscsi_setup_authentication(struct iscsi_session *session, if (auth_cfg->username_in[0] || auth_cfg->password_in_length) { /* sanity check the config */ if (auth_cfg->password_length == 0) { - log_warning("CHAP configuratoin has incoming " + log_warning("CHAP configuration has incoming " "authentication credentials but has no " "outgoing credentials configured."); return EINVAL; @@ -151,11 +139,11 @@ iscsi_copy_operational_params(struct iscsi_conn *conn, conn->datadgst_en = conn_conf->DataDigest; conn->max_recv_dlength = - __padding(conn_conf->MaxRecvDataSegmentLength); + align_32_down(conn_conf->MaxRecvDataSegmentLength); if (conn->max_recv_dlength < ISCSI_MIN_MAX_RECV_SEG_LEN || conn->max_recv_dlength > ISCSI_MAX_MAX_RECV_SEG_LEN) { log_error("Invalid iscsi.MaxRecvDataSegmentLength. Must be " - "within %u and %u. Setting to %u\n", + "within %u and %u. Setting to %u", ISCSI_MIN_MAX_RECV_SEG_LEN, ISCSI_MAX_MAX_RECV_SEG_LEN, DEF_INI_MAX_RECV_SEG_LEN); @@ -166,13 +154,13 @@ iscsi_copy_operational_params(struct iscsi_conn *conn, /* zero indicates to use the target's value */ conn->max_xmit_dlength = - __padding(conn_conf->MaxXmitDataSegmentLength); + align_32_down(conn_conf->MaxXmitDataSegmentLength); if (conn->max_xmit_dlength == 0) conn->max_xmit_dlength = ISCSI_DEF_MAX_RECV_SEG_LEN; if (conn->max_xmit_dlength < ISCSI_MIN_MAX_RECV_SEG_LEN || conn->max_xmit_dlength > ISCSI_MAX_MAX_RECV_SEG_LEN) { log_error("Invalid iscsi.MaxXmitDataSegmentLength. Must be " - "within %u and %u. Setting to %u\n", + "within %u and %u. Setting to %u", ISCSI_MIN_MAX_RECV_SEG_LEN, ISCSI_MAX_MAX_RECV_SEG_LEN, DEF_INI_MAX_RECV_SEG_LEN); @@ -184,7 +172,7 @@ iscsi_copy_operational_params(struct iscsi_conn *conn, /* session's operational parameters */ session->initial_r2t_en = session_conf->InitialR2T; session->imm_data_en = session_conf->ImmediateData; - session->first_burst = __padding(session_conf->FirstBurstLength); + session->first_burst = align_32_down(session_conf->FirstBurstLength); /* * some targets like netapp fail the login if sent bad first_burst * and max_burst lens, even when immediate data=no and @@ -193,7 +181,7 @@ iscsi_copy_operational_params(struct iscsi_conn *conn, if (session->first_burst < ISCSI_MIN_FIRST_BURST_LEN || session->first_burst > ISCSI_MAX_FIRST_BURST_LEN) { log_error("Invalid iscsi.FirstBurstLength of %u. Must be " - "within %u and %u. Setting to %u\n", + "within %u and %u. Setting to %u", session->first_burst, ISCSI_MIN_FIRST_BURST_LEN, ISCSI_MAX_FIRST_BURST_LEN, @@ -202,11 +190,11 @@ iscsi_copy_operational_params(struct iscsi_conn *conn, session->first_burst = DEF_INI_FIRST_BURST_LEN; } - session->max_burst = __padding(session_conf->MaxBurstLength); + session->max_burst = align_32_down(session_conf->MaxBurstLength); if (session->max_burst < ISCSI_MIN_MAX_BURST_LEN || session->max_burst > ISCSI_MAX_MAX_BURST_LEN) { log_error("Invalid iscsi.MaxBurstLength of %u. Must be " - "within %u and %u. Setting to %u\n", + "within %u and %u. Setting to %u", session->max_burst, ISCSI_MIN_MAX_BURST_LEN, ISCSI_MAX_MAX_BURST_LEN, DEF_INI_MAX_BURST_LEN); session_conf->MaxBurstLength = DEF_INI_MAX_BURST_LEN; @@ -215,7 +203,7 @@ iscsi_copy_operational_params(struct iscsi_conn *conn, if (session->first_burst > session->max_burst) { log_error("Invalid iscsi.FirstBurstLength of %u. Must be " - "less than iscsi.MaxBurstLength. Setting to %u\n", + "less than iscsi.MaxBurstLength. Setting to %u", session->first_burst, session->max_burst); session_conf->FirstBurstLength = session->max_burst; session->first_burst = session->max_burst; @@ -324,12 +312,32 @@ int iscsi_host_set_params(struct iscsi_session *session) return 0; } -#define MAX_SESSION_PARAMS 32 +static inline void iscsi_session_clear_param(struct iscsi_session *session, + int param) +{ + session->param_mask &= ~(1ULL << param); +} -int iscsi_session_set_params(struct iscsi_conn *conn) +void iscsi_session_init_params(struct iscsi_session *session) +{ + session->param_mask = ~0ULL; + if (!(session->t->caps & CAP_MULTI_R2T)) + iscsi_session_clear_param(session, ISCSI_PARAM_MAX_R2T); + if (!(session->t->caps & CAP_HDRDGST)) + iscsi_session_clear_param(session, ISCSI_PARAM_HDRDGST_EN); + if (!(session->t->caps & CAP_DATADGST)) + iscsi_session_clear_param(session, ISCSI_PARAM_DATADGST_EN); + if (!(session->t->caps & CAP_MARKERS)) { + iscsi_session_clear_param(session, ISCSI_PARAM_IFMARKER_EN); + iscsi_session_clear_param(session, ISCSI_PARAM_OFMARKER_EN); + } +} + +#define MAX_SESSION_NEG_PARAMS 16 + +int iscsi_session_set_neg_params(struct iscsi_conn *conn) { struct iscsi_session *session = conn->session; - struct iscsi_transport *t = session->t; int i, rc; uint32_t one = 1, zero = 0; struct connparam { @@ -337,7 +345,7 @@ int iscsi_session_set_params(struct iscsi_conn *conn) int type; void *value; int conn_only; - } conntbl[MAX_SESSION_PARAMS] = { + } conntbl[MAX_SESSION_NEG_PARAMS] = { { .param = ISCSI_PARAM_MAX_RECV_DLENGTH, .value = &conn->max_recv_dlength, @@ -411,18 +419,61 @@ int iscsi_session_set_params(struct iscsi_conn *conn) }, { .param = ISCSI_PARAM_EXP_STATSN, .value = &conn->exp_statsn, - .type = ISCSI_INT, + .type = ISCSI_UINT, .conn_only = 1, }, { - .param = ISCSI_PARAM_TARGET_NAME, - .conn_only = 0, - .type = ISCSI_STRING, - .value = session->target_name, - }, { .param = ISCSI_PARAM_TPGT, .value = &session->portal_group_tag, .type = ISCSI_INT, .conn_only = 0, + }, + }; + + iscsi_session_init_params(session); + + /* Entered full-feature phase! */ + for (i = 0; i < MAX_SESSION_NEG_PARAMS; i++) { + if (conn->id != 0 && !conntbl[i].conn_only) + continue; + + if (!(session->param_mask & (1ULL << conntbl[i].param))) + continue; + + rc = ipc->set_param(session->t->handle, session->id, + conn->id, conntbl[i].param, conntbl[i].value, + conntbl[i].type); + if (rc && rc != -ENOSYS) { + log_error("can't set operational parameter %d for " + "connection %d:%d, retcode %d (%d)", + conntbl[i].param, session->id, conn->id, + rc, errno); + return EPERM; + } + + print_param_value(conntbl[i].param, conntbl[i].value, + conntbl[i].type); + } + + return 0; +} + +#define MAX_SESSION_PARAMS 20 + +int iscsi_session_set_params(struct iscsi_conn *conn) +{ + struct iscsi_session *session = conn->session; + int i, rc; + struct connparam { + int param; + int type; + void *value; + int conn_only; + } conntbl[MAX_SESSION_PARAMS] = { + { + .param = ISCSI_PARAM_TARGET_NAME, + .conn_only = 0, + .type = ISCSI_STRING, + .value = session->target_name, }, { .param = ISCSI_PARAM_PERSISTENT_ADDRESS, .value = session->nrec.conn[conn->id].address, @@ -492,29 +543,41 @@ int iscsi_session_set_params(struct iscsi_conn *conn) .param = ISCSI_PARAM_IFACE_NAME, .value = session->nrec.iface.name, .type = ISCSI_STRING, + .conn_only = 0, }, { .param = ISCSI_PARAM_INITIATOR_NAME, .value = session->initiator_name, .type = ISCSI_STRING, + .conn_only = 0, + }, { + .param = ISCSI_PARAM_BOOT_ROOT, + .value = session->nrec.session.boot_root, + .type = ISCSI_STRING, + .conn_only = 0, + }, { + .param = ISCSI_PARAM_BOOT_NIC, + .value = session->nrec.session.boot_nic, + .type = ISCSI_STRING, + .conn_only = 0, + }, { + .param = ISCSI_PARAM_BOOT_TARGET, + .value = session->nrec.session.boot_target, + .type = ISCSI_STRING, + .conn_only = 0, + }, { + .param = ISCSI_PARAM_DISCOVERY_SESS, + .value = &session->type, + .type = ISCSI_INT, + .conn_only = 0, }, }; - session->param_mask = ~0ULL; - if (!(t->caps & CAP_MULTI_R2T)) - session->param_mask &= ~ISCSI_MAX_R2T; - if (!(t->caps & CAP_HDRDGST)) - session->param_mask &= ~ISCSI_HDRDGST_EN; - if (!(t->caps & CAP_DATADGST)) - session->param_mask &= ~ISCSI_DATADGST_EN; - if (!(t->caps & CAP_MARKERS)) { - session->param_mask &= ~ISCSI_IFMARKER_EN; - session->param_mask &= ~ISCSI_OFMARKER_EN; - } + iscsi_session_init_params(session); /* some llds will send nops internally */ if (!iscsi_sysfs_session_supports_nop(session->id)) { - session->param_mask &= ~ISCSI_PING_TMO; - session->param_mask &= ~ISCSI_RECV_TMO; + iscsi_session_clear_param(session, ISCSI_PARAM_PING_TMO); + iscsi_session_clear_param(session, ISCSI_PARAM_RECV_TMO); } /* Entered full-feature phase! */ @@ -562,6 +625,36 @@ TODO handle this return 0; } +int iscsi_set_net_config(struct iscsi_transport *t, iscsi_session_t *session, + struct iface_rec *iface) +{ + if (t->template->set_net_config) { + /* uip needs the netdev name */ + struct host_info hinfo; + int hostno, rc; + + /* this assumes that the netdev or hw address is going to be + set */ + hostno = iscsi_sysfs_get_host_no_from_hwinfo(iface, &rc); + if (rc) { + log_debug(4, "Couldn't get host no."); + return rc; + } + + /* uip needs the netdev name */ + if (!strlen(iface->netdev)) { + memset(&hinfo, 0, sizeof(hinfo)); + hinfo.host_no = hostno; + iscsi_sysfs_get_hostinfo_by_host_no(&hinfo); + strcpy(iface->netdev, hinfo.iface.netdev); + } + + return t->template->set_net_config(t, iface, session); + } + + return 0; +} + int iscsi_host_set_net_params(struct iface_rec *iface, struct iscsi_session *session) { @@ -571,7 +664,7 @@ int iscsi_host_set_net_params(struct iface_rec *iface, struct host_info hinfo; log_debug(3, "setting iface %s, dev %s, set ip %s, hw %s, " - "transport %s.\n", + "transport %s.", iface->name, iface->netdev, iface->ipaddress, iface->hwaddress, iface->transport_name); @@ -580,9 +673,18 @@ int iscsi_host_set_net_params(struct iface_rec *iface, /* if we need to set the ip addr then set all the iface net settings */ if (!iface_is_bound_by_ipaddr(iface)) { - log_warning("Please set the iface.ipaddress for iface %s, " - "then retry the login command.\n", iface->name); - return EINVAL; + if (t->template->set_host_ip == SET_HOST_IP_REQ) { + log_warning("Please set the iface.ipaddress for iface " + "%s, then retry the login command.", + iface->name); + return EINVAL; + } else if (t->template->set_host_ip == SET_HOST_IP_OPT) { + log_info("Optional iface.ipaddress for iface %s " + "not set.", iface->name); + return 0; + } else { + return EINVAL; + } } /* these type of drivers need the netdev upd */ @@ -600,6 +702,10 @@ int iscsi_host_set_net_params(struct iface_rec *iface, log_warning("Could not brining up netdev %s. Try running " "'ifup %s' first if login fails.", netdev, netdev); + rc = iscsi_set_net_config(t, session, iface); + if (rc != 0) + return rc; + rc = host_set_param(t, session->hostno, ISCSI_HOST_PARAM_IPADDRESS, iface->ipaddress, ISCSI_STRING); diff --git a/usr/io.c b/usr/io.c index 45adea5..f552e1e 100644 --- a/usr/io.c +++ b/usr/io.c @@ -113,14 +113,14 @@ static int get_hwaddress_from_netdev(char *netdev, char *hwaddress) (void *)&(s4->sin_addr), buf, INET_ADDRSTRLEN)) continue; - log_debug(4, "name %s addr %s\n", ifa->ifa_name, buf); + log_debug(4, "name %s addr %s", ifa->ifa_name, buf); break; case AF_INET6: s6 = (struct sockaddr_in6 *)(ifa->ifa_addr); if (!inet_ntop(ifa->ifa_addr->sa_family, (void *)&(s6->sin6_addr), buf, INET6_ADDRSTRLEN)) continue; - log_debug(4, "name %s addr %s\n", ifa->ifa_name, buf); + log_debug(4, "name %s addr %s", ifa->ifa_name, buf); break; default: continue; @@ -239,7 +239,7 @@ static int bind_conn_to_iface(iscsi_conn_t *conn, struct iface_rec *iface) if (setsockopt(conn->socket_fd, SOL_SOCKET, SO_BINDTODEVICE, session->netdev, strlen(session->netdev) + 1) < 0) { - log_error("Could not bind connection %d to %s\n", + log_error("Could not bind connection %d to %s", conn->id, session->netdev); return -1; } @@ -357,7 +357,7 @@ iscsi_io_tcp_poll(iscsi_conn_t *conn, int timeout_ms) len = sizeof(int); if (getsockopt(conn->socket_fd, SOL_SOCKET, SO_ERROR, (char *) &rc, &len) < 0) { - log_error("getsockopt for connect poll failed\n"); + log_error("getsockopt for connect poll failed"); return -1; } if (rc) { @@ -366,7 +366,7 @@ iscsi_io_tcp_poll(iscsi_conn_t *conn, int timeout_ms) conn->host, sizeof(conn->host), serv, sizeof(serv), NI_NUMERICHOST|NI_NUMERICSERV); - log_error("connect to %s:%s failed (%s)\n", + log_error("connect to %s:%s failed (%s)", conn->host, serv, strerror(rc)); return -rc; } diff --git a/usr/iscsi_err.c b/usr/iscsi_err.c index 4fe1c53..11e0348 100644 --- a/usr/iscsi_err.c +++ b/usr/iscsi_err.c @@ -51,6 +51,8 @@ static char *iscsi_err_msgs[] = { /* 26 */ "iSNS registration failed", /* 27 */ "operation not supported", /* 28 */ "device or resource in use", + /* 29 */ "operation failed but retry may succeed", + /* 30 */ "unknown discovery type", }; char *iscsi_err_to_str(int err) diff --git a/usr/iscsi_ipc.h b/usr/iscsi_ipc.h index db5f1f0..5087b5c 100644 --- a/usr/iscsi_ipc.h +++ b/usr/iscsi_ipc.h @@ -30,6 +30,7 @@ enum { ISCSI_INT, + ISCSI_UINT, ISCSI_STRING, }; @@ -143,8 +144,26 @@ struct iscsi_ipc { uint16_t chap_tbl_idx, uint32_t num_entries, char *chap_buf, uint32_t *valid_chap_entries); + int (*set_chap) (uint64_t transport_handle, uint32_t host_no, + struct iovec *iovs, uint32_t param_count); + int (*delete_chap) (uint64_t transport_handle, uint32_t host_no, uint16_t chap_tbl_idx); + int (*set_flash_node_params) (uint64_t transport_handle, + uint32_t host_no, uint32_t flashnode_idx, + struct iovec *iovs, uint32_t param_count); + int (*new_flash_node) (uint64_t transport_handle, uint32_t host_no, + void *value, uint32_t *flashnode_idx); + int (*del_flash_node) (uint64_t transport_handle, uint32_t host_no, + uint32_t flashnode_idx); + int (*login_flash_node) (uint64_t transport_handle, uint32_t host_no, + uint32_t flashnode_idx); + int (*logout_flash_node) (uint64_t transport_handle, uint32_t host_no, + uint32_t flashnode_idx); + int (*logout_flash_node_sid) (uint64_t transport_handle, + uint32_t host_no, uint32_t sid); + int (*get_host_stats) (uint64_t transport_handle, uint32_t host_no, + char *host_stats); }; #endif /* ISCSI_IPC_H */ diff --git a/usr/iscsi_net_util.c b/usr/iscsi_net_util.c index 6d0ebf9..848b4c6 100644 --- a/usr/iscsi_net_util.c +++ b/usr/iscsi_net_util.c @@ -188,7 +188,7 @@ int net_setup_netdev(char *netdev, char *local_ip, char *mask, char *gateway, int ret; if (!strlen(netdev)) { - log_error("No netdev name in fw entry.\n"); + log_error("No netdev name in fw entry."); return EINVAL; } @@ -203,13 +203,13 @@ int net_setup_netdev(char *netdev, char *local_ip, char *mask, char *gateway, * has already been handled (2 targets in IBFT may share one NIC) */ if (!inet_aton(local_ip, &sk_ipaddr.sin_addr)) { - log_error("Invalid or missing ipaddr in fw entry\n"); + log_error("Invalid or missing ipaddr in fw entry"); ret = EINVAL; goto done; } if (!inet_aton(mask, &sk_netmask.sin_addr)) { - log_error("Invalid or missing netmask in fw entry\n"); + log_error("Invalid or missing netmask in fw entry"); ret = EINVAL; goto done; } @@ -217,7 +217,7 @@ int net_setup_netdev(char *netdev, char *local_ip, char *mask, char *gateway, inet_aton("255.255.255.255", &sk_hostmask.sin_addr); if (!inet_aton(remote_ip, &sk_tgt_ipaddr.sin_addr)) { - log_error("Invalid or missing target ipaddr in fw entry\n"); + log_error("Invalid or missing target ipaddr in fw entry"); ret = EINVAL; goto done; } @@ -317,7 +317,7 @@ int net_ifup_netdev(char *netdev) int ret = 0; if (!strlen(netdev)) { - log_error("No netdev name in fw entry.\n"); + log_error("No netdev name in fw entry."); return EINVAL; } @@ -338,11 +338,11 @@ int net_ifup_netdev(char *netdev) } if (ifr.ifr_flags & IFF_UP) { - log_debug(3, "%s up\n", netdev); + log_debug(3, "%s up", netdev); goto done; } - log_debug(3, "bringing %s up\n", netdev); + log_debug(3, "bringing %s up", netdev); /* Bring up interface */ memset(&ifr, 0, sizeof(ifr)); diff --git a/usr/iscsi_sysfs.c b/usr/iscsi_sysfs.c index 123dde3..3a37a48 100644 --- a/usr/iscsi_sysfs.c +++ b/usr/iscsi_sysfs.c @@ -24,11 +24,13 @@ #include #include #include +#include #include "log.h" #include "initiator.h" #include "transport.h" #include "idbm.h" +#include "idbm_fields.h" #include "version.h" #include "iscsi_sysfs.h" #include "sysdeps.h" @@ -37,6 +39,7 @@ #include "session_info.h" #include "host.h" #include "iscsi_err.h" +#include "flashnode.h" /* * TODO: remove the _DIR defines and search for subsys dirs like @@ -45,18 +48,22 @@ #define ISCSI_TRANSPORT_DIR "/sys/class/iscsi_transport" #define ISCSI_SESSION_DIR "/sys/class/iscsi_session" #define ISCSI_HOST_DIR "/sys/class/iscsi_host" +#define ISCSI_FLASHNODE_DIR "/sys/bus/iscsi_flashnode/devices" #define ISCSI_SESSION_SUBSYS "iscsi_session" #define ISCSI_CONN_SUBSYS "iscsi_connection" #define ISCSI_HOST_SUBSYS "iscsi_host" #define ISCSI_TRANSPORT_SUBSYS "iscsi_transport" #define ISCSI_IFACE_SUBSYS "iscsi_iface" +#define ISCSI_FLASHNODE_SUBSYS "iscsi_flashnode" #define SCSI_HOST_SUBSYS "scsi_host" #define SCSI_SUBSYS "scsi" #define ISCSI_SESSION_ID "session%d" #define ISCSI_CONN_ID "connection%d:0" #define ISCSI_HOST_ID "host%d" +#define ISCSI_FLASHNODE_SESS "flashnode_sess-%d:%d" +#define ISCSI_FLASHNODE_CONN "flashnode_conn-%d:%d:0" /* * TODO: make this into a real API and check inputs better and add doc. @@ -130,7 +137,7 @@ static int read_transports(void) if (list_empty(&t->list)) free(t); else - log_error("Could not update %s.\n", + log_error("Could not update %s.", t->name); continue; } @@ -140,7 +147,7 @@ static int read_transports(void) if (list_empty(&t->list)) free(t); else - log_error("Could not update %s.\n", + log_error("Could not update %s.", t->name); continue; } @@ -265,7 +272,7 @@ uint32_t iscsi_sysfs_get_host_no_from_sid(uint32_t sid, int *err) if (!sysfs_lookup_devpath_by_subsys_id(devpath, sizeof(devpath), ISCSI_SESSION_SUBSYS, id)) { log_error("Could not lookup devpath for %s. Possible sysfs " - "incompatibility.\n", id); + "incompatibility.", id); *err = ISCSI_ERR_SYSFS_LOOKUP; return 0; } @@ -273,7 +280,7 @@ uint32_t iscsi_sysfs_get_host_no_from_sid(uint32_t sid, int *err) session_dev = sysfs_device_get(devpath); if (!session_dev) { log_error("Could not get dev for %s. Possible sysfs " - "incompatibility.\n", id); + "incompatibility.", id); *err = ISCSI_ERR_SYSFS_LOOKUP; return 0; } @@ -298,7 +305,7 @@ uint32_t iscsi_sysfs_get_host_no_from_sid(uint32_t sid, int *err) if (!host_dev) { log_error("Could not get host dev for %s. Possible " - "sysfs incompatibility.\n", id); + "sysfs incompatibility.", id); *err = ISCSI_ERR_SYSFS_LOOKUP; return 0; } @@ -440,6 +447,271 @@ uint32_t iscsi_sysfs_get_host_no_from_hwinfo(struct iface_rec *iface, int *rc) } /* + * Read the flash node attributes based on host and flash node index. + */ +int iscsi_sysfs_get_flashnode_info(struct flashnode_rec *fnode, + uint32_t host_no, + uint32_t flashnode_idx) +{ + char sess_id[NAME_SIZE] = {'\0'}; + char conn_id[NAME_SIZE] = {'\0'}; + char fnode_path[PATH_SIZE] = {'\0'}; + struct iscsi_transport *t; + int ret = 0; + + t = iscsi_sysfs_get_transport_by_hba(host_no); + if (!t) + log_debug(7, "could not get transport name for host%d", + host_no); + else + strncpy(fnode->transport_name, t->name, + ISCSI_TRANSPORT_NAME_MAXLEN); + + snprintf(sess_id, sizeof(sess_id), ISCSI_FLASHNODE_SESS, host_no, + flashnode_idx); + + snprintf(fnode_path, sizeof(fnode_path), ISCSI_FLASHNODE_DIR"/%s", + sess_id); + if (access(fnode_path, F_OK) != 0) + return errno; + + snprintf(conn_id, sizeof(conn_id), ISCSI_FLASHNODE_CONN, host_no, + flashnode_idx); + + snprintf(fnode_path, sizeof(fnode_path), ISCSI_FLASHNODE_DIR"/%s", + conn_id); + if (access(fnode_path, F_OK) != 0) + return errno; + + + sysfs_get_uint8(conn_id, ISCSI_FLASHNODE_SUBSYS, "is_fw_assigned_ipv6", + &((fnode->conn[0]).is_fw_assigned_ipv6)); + sysfs_get_str(sess_id, ISCSI_FLASHNODE_SUBSYS, "portal_type", + (fnode->sess).portal_type, + sizeof((fnode->sess).portal_type)); + sysfs_get_uint8(sess_id, ISCSI_FLASHNODE_SUBSYS, "auto_snd_tgt_disable", + &((fnode->sess).auto_snd_tgt_disable)); + sysfs_get_uint8(sess_id, ISCSI_FLASHNODE_SUBSYS, "discovery_session", + &((fnode->sess).discovery_session)); + sysfs_get_uint8(sess_id, ISCSI_FLASHNODE_SUBSYS, "entry_enable", + &((fnode->sess).entry_enable)); + sysfs_get_uint8(conn_id, ISCSI_FLASHNODE_SUBSYS, "header_digest", + &((fnode->conn[0]).header_digest_en)); + sysfs_get_uint8(conn_id, ISCSI_FLASHNODE_SUBSYS, "data_digest", + &((fnode->conn[0]).data_digest_en)); + sysfs_get_uint8(sess_id, ISCSI_FLASHNODE_SUBSYS, "immediate_data", + &((fnode->sess).immediate_data)); + sysfs_get_uint8(sess_id, ISCSI_FLASHNODE_SUBSYS, "initial_r2t", + &((fnode->sess).initial_r2t)); + sysfs_get_uint8(sess_id, ISCSI_FLASHNODE_SUBSYS, "data_seq_in_order", + &((fnode->sess).data_seq_in_order)); + sysfs_get_uint8(sess_id, ISCSI_FLASHNODE_SUBSYS, "data_pdu_in_order", + &((fnode->sess).data_pdu_in_order)); + sysfs_get_uint8(sess_id, ISCSI_FLASHNODE_SUBSYS, "chap_auth", + &((fnode->sess).chap_auth_en)); + sysfs_get_uint8(conn_id, ISCSI_FLASHNODE_SUBSYS, "snack_req", + &((fnode->conn[0]).snack_req_en)); + sysfs_get_uint8(sess_id, ISCSI_FLASHNODE_SUBSYS, "discovery_logout", + &((fnode->sess).discovery_logout_en)); + sysfs_get_uint8(sess_id, ISCSI_FLASHNODE_SUBSYS, "bidi_chap", + &((fnode->sess).bidi_chap_en)); + sysfs_get_uint8(sess_id, ISCSI_FLASHNODE_SUBSYS, + "discovery_auth_optional", + &((fnode->sess).discovery_auth_optional)); + sysfs_get_uint8(sess_id, ISCSI_FLASHNODE_SUBSYS, "erl", + &((fnode->sess).erl)); + sysfs_get_uint8(conn_id, ISCSI_FLASHNODE_SUBSYS, "tcp_timestamp_stat", + &((fnode->conn[0]).tcp_timestamp_stat)); + sysfs_get_uint8(conn_id, ISCSI_FLASHNODE_SUBSYS, "tcp_nagle_disable", + &((fnode->conn[0]).tcp_nagle_disable)); + sysfs_get_uint8(conn_id, ISCSI_FLASHNODE_SUBSYS, "tcp_wsf_disable", + &((fnode->conn[0]).tcp_wsf_disable)); + sysfs_get_uint8(conn_id, ISCSI_FLASHNODE_SUBSYS, "tcp_timer_scale", + &((fnode->conn[0]).tcp_timer_scale)); + sysfs_get_uint8(conn_id, ISCSI_FLASHNODE_SUBSYS, "tcp_timestamp_enable", + &((fnode->conn[0]).tcp_timestamp_en)); + sysfs_get_uint8(conn_id, ISCSI_FLASHNODE_SUBSYS, "fragment_disable", + &((fnode->conn[0]).fragment_disable)); + sysfs_get_uint(conn_id, ISCSI_FLASHNODE_SUBSYS, "max_recv_dlength", + &((fnode->conn[0]).max_recv_dlength)); + sysfs_get_uint(conn_id, ISCSI_FLASHNODE_SUBSYS, "max_xmit_dlength", + &((fnode->conn[0]).max_xmit_dlength)); + sysfs_get_uint(sess_id, ISCSI_FLASHNODE_SUBSYS, "first_burst_len", + &((fnode->sess).first_burst_len)); + sysfs_get_uint16(sess_id, ISCSI_FLASHNODE_SUBSYS, "def_time2wait", + &((fnode->sess).def_time2wait)); + sysfs_get_uint16(sess_id, ISCSI_FLASHNODE_SUBSYS, "def_time2retain", + &((fnode->sess).def_time2retain)); + sysfs_get_uint16(sess_id, ISCSI_FLASHNODE_SUBSYS, "max_outstanding_r2t", + &((fnode->sess).max_outstanding_r2t)); + sysfs_get_uint16(conn_id, ISCSI_FLASHNODE_SUBSYS, "keepalive_tmo", + &((fnode->conn[0]).keepalive_tmo)); + sysfs_get_str(sess_id, ISCSI_FLASHNODE_SUBSYS, "isid", + (fnode->sess).isid, sizeof((fnode->sess).isid)); + sysfs_get_uint16(sess_id, ISCSI_FLASHNODE_SUBSYS, "tsid", + &((fnode->sess).tsid)); + sysfs_get_uint16(conn_id, ISCSI_FLASHNODE_SUBSYS, "port", + &((fnode->conn[0]).port)); + sysfs_get_uint(sess_id, ISCSI_FLASHNODE_SUBSYS, "max_burst_len", + &((fnode->sess).max_burst_len)); + sysfs_get_uint16(sess_id, ISCSI_FLASHNODE_SUBSYS, "def_taskmgmt_tmo", + &((fnode->sess).def_taskmgmt_tmo)); + sysfs_get_str(sess_id, ISCSI_FLASHNODE_SUBSYS, "targetalias", + (fnode->sess).targetalias, + sizeof((fnode->sess).targetalias)); + sysfs_get_str(conn_id, ISCSI_FLASHNODE_SUBSYS, "ipaddress", + (fnode->conn[0]).ipaddress, + sizeof((fnode->conn[0]).ipaddress)); + sysfs_get_str(conn_id, ISCSI_FLASHNODE_SUBSYS, "redirect_ipaddr", + (fnode->conn[0]).redirect_ipaddr, + sizeof((fnode->conn[0]).redirect_ipaddr)); + sysfs_get_uint(conn_id, ISCSI_FLASHNODE_SUBSYS, "max_segment_size", + &((fnode->conn[0]).max_segment_size)); + sysfs_get_uint16(conn_id, ISCSI_FLASHNODE_SUBSYS, "local_port", + &((fnode->conn[0]).local_port)); + sysfs_get_uint8(conn_id, ISCSI_FLASHNODE_SUBSYS, "ipv4_tos", + &((fnode->conn[0]).ipv4_tos)); + sysfs_get_uint8(conn_id, ISCSI_FLASHNODE_SUBSYS, "ipv6_traffic_class", + &((fnode->conn[0]).ipv6_traffic_class)); + sysfs_get_uint16(conn_id, ISCSI_FLASHNODE_SUBSYS, "ipv6_flow_label", + &((fnode->conn[0]).ipv6_flow_lbl)); + sysfs_get_str(sess_id, ISCSI_FLASHNODE_SUBSYS, "targetname", + (fnode->sess).targetname, + sizeof((fnode->sess).targetname)); + sysfs_get_str(conn_id, ISCSI_FLASHNODE_SUBSYS, "link_local_ipv6", + (fnode->conn[0]).link_local_ipv6, + sizeof((fnode->conn[0]).link_local_ipv6)); + sysfs_get_uint16(sess_id, ISCSI_FLASHNODE_SUBSYS, + "discovery_parent_idx", + &((fnode->sess).discovery_parent_idx)); + sysfs_get_str(sess_id, ISCSI_FLASHNODE_SUBSYS, + "discovery_parent_type", + (fnode->sess).discovery_parent_type, + sizeof((fnode->sess).discovery_parent_type)); + sysfs_get_uint16(sess_id, ISCSI_FLASHNODE_SUBSYS, "tpgt", + &((fnode->sess).tpgt)); + sysfs_get_uint(conn_id, ISCSI_FLASHNODE_SUBSYS, "tcp_xmit_wsf", + &((fnode->conn[0]).tcp_xmit_wsf)); + sysfs_get_uint(conn_id, ISCSI_FLASHNODE_SUBSYS, "tcp_recv_wsf", + &((fnode->conn[0]).tcp_recv_wsf)); + sysfs_get_uint16(sess_id, ISCSI_FLASHNODE_SUBSYS, "chap_out_idx", + &((fnode->sess).chap_out_idx)); + sysfs_get_uint16(sess_id, ISCSI_FLASHNODE_SUBSYS, "chap_in_idx", + &((fnode->sess).chap_in_idx)); + sysfs_get_str(sess_id, ISCSI_FLASHNODE_SUBSYS, "username", + (fnode->sess).username, sizeof((fnode->sess).username)); + sysfs_get_str(sess_id, ISCSI_FLASHNODE_SUBSYS, "username_in", + (fnode->sess).username_in, + sizeof((fnode->sess).username_in)); + sysfs_get_str(sess_id, ISCSI_FLASHNODE_SUBSYS, "password", + (fnode->sess).password, sizeof((fnode->sess).password)); + sysfs_get_str(sess_id, ISCSI_FLASHNODE_SUBSYS, "password_in", + (fnode->sess).password_in, + sizeof((fnode->sess).password_in)); + sysfs_get_uint(conn_id, ISCSI_FLASHNODE_SUBSYS, "statsn", + &((fnode->conn[0]).stat_sn)); + sysfs_get_uint(conn_id, ISCSI_FLASHNODE_SUBSYS, "exp_statsn", + &((fnode->conn[0]).exp_stat_sn)); + sysfs_get_uint8(sess_id, ISCSI_FLASHNODE_SUBSYS, "is_boot_target", + &((fnode->sess).is_boot_target)); + return ret; +} + +/* + * For each flash node of the given host, perform operation specified in fn. + */ +int iscsi_sysfs_for_each_flashnode(void *data, uint32_t host_no, int *nr_found, + iscsi_sysfs_flashnode_op_fn *fn) +{ + struct dirent **namelist; + int rc = 0, i, n; + struct flashnode_rec *fnode; + uint32_t flashnode_idx; + uint32_t hostno; + + fnode = malloc(sizeof(*fnode)); + if (!fnode) + return ISCSI_ERR_NOMEM; + + n = scandir(ISCSI_FLASHNODE_DIR, &namelist, trans_filter, alphasort); + if (n <= 0) + goto free_fnode; + + for (i = 0; i < n; i++) { + memset(fnode, 0, sizeof(*fnode)); + + if (!strncmp(namelist[i]->d_name, "flashnode_conn", + strlen("flashnode_conn"))) + continue; + + if (sscanf(namelist[i]->d_name, ISCSI_FLASHNODE_SESS, + &hostno, &flashnode_idx) != 2) { + log_error("Invalid iscsi target dir: %s", + namelist[i]->d_name); + break; + } + + if (host_no != hostno) + continue; + + rc = iscsi_sysfs_get_flashnode_info(fnode, host_no, + flashnode_idx); + if (rc) + break; + + rc = fn(data, fnode, host_no, flashnode_idx); + if (rc != 0) + break; + (*nr_found)++; + } + + for (i = 0; i < n; i++) + free(namelist[i]); + free(namelist); + +free_fnode: + free(fnode); + return rc; +} + +static int iscsi_sysfs_read_boot(struct iface_rec *iface, char *session) +{ + char boot_root[BOOT_NAME_MAXLEN], boot_nic[BOOT_NAME_MAXLEN]; + char boot_name[BOOT_NAME_MAXLEN], boot_content[BOOT_NAME_MAXLEN]; + + /* Extract boot info */ + strlcpy(boot_name, "boot_target", sizeof(boot_name)); + if (sysfs_get_str(session, ISCSI_SESSION_SUBSYS, boot_name, + boot_content, BOOT_NAME_MAXLEN)) + return -1; + strlcpy(boot_name, "boot_nic", sizeof(boot_name)); + if (sysfs_get_str(session, ISCSI_SESSION_SUBSYS, boot_name, boot_nic, + BOOT_NAME_MAXLEN)) + return -1; + strlcpy(boot_name, "boot_root", sizeof(boot_name)); + if (sysfs_get_str(session, ISCSI_SESSION_SUBSYS, boot_name, boot_root, + BOOT_NAME_MAXLEN)) + return -1; + + /* If all boot_root/boot_target/boot_nic exist, then extract the + info from the boot nic */ + if (sysfs_get_str(boot_nic, boot_root, "vlan", boot_content, + BOOT_NAME_MAXLEN)) + log_debug(5, "could not read %s/%s/vlan", boot_root, boot_nic); + else + iface->vlan_id = atoi(boot_content); + + if (sysfs_get_str(boot_nic, boot_root, "subnet-mask", + iface->subnet_mask, NI_MAXHOST)) + log_debug(5, "could not read %s/%s/subnet", boot_root, + boot_nic); + + log_debug(5, "sysfs read boot returns %s/%s/ vlan = %d subnet = %s", + boot_root, boot_nic, iface->vlan_id, iface->subnet_mask); + return 0; +} + +/* * Read in iface settings based on host and session values. If * session is not passed in, then the ifacename will not be set. And * if the session is not passed in then iname will only be set for @@ -469,7 +741,7 @@ static int iscsi_sysfs_read_iface(struct iface_rec *iface, int host_no, ret = sysfs_get_str(host_id, ISCSI_HOST_SUBSYS, "hwaddress", iface->hwaddress, sizeof(iface->hwaddress)); if (ret) - log_debug(7, "could not read hwaddress for host%d\n", host_no); + log_debug(7, "could not read hwaddress for host%d", host_no); if (iface_kern_id) ret = sysfs_get_str(iface_kern_id, ISCSI_IFACE_SUBSYS, @@ -480,14 +752,14 @@ static int iscsi_sysfs_read_iface(struct iface_rec *iface, int host_no, ret = sysfs_get_str(host_id, ISCSI_HOST_SUBSYS, "ipaddress", iface->ipaddress, sizeof(iface->ipaddress)); if (ret) - log_debug(7, "could not read local address for host%d\n", + log_debug(7, "could not read local address for host%d", host_no); /* if not found just print out default */ ret = sysfs_get_str(host_id, ISCSI_HOST_SUBSYS, "netdev", iface->netdev, sizeof(iface->netdev)); if (ret) - log_debug(7, "could not read netdev for host%d\n", host_no); + log_debug(7, "could not read netdev for host%d", host_no); /* * For drivers like qla4xxx we can only set the iname at the @@ -527,7 +799,7 @@ static int iscsi_sysfs_read_iface(struct iface_rec *iface, int host_no, * global iname so we can just fill it in here. */ log_debug(7, "Could not read initiatorname for " - "host%d\n", host_no); + "host%d", host_no); /* optional so do not return error */ ret = 0; } @@ -553,7 +825,7 @@ static int iscsi_sysfs_read_iface(struct iface_rec *iface, int host_no, iface->name, sizeof(iface->name)); if (ret) { log_debug(7, "could not read iface name for " - "session %s\n", session); + "session %s", session); /* * if the ifacename file is not there then we are * using a older kernel and can try to find the @@ -562,11 +834,14 @@ static int iscsi_sysfs_read_iface(struct iface_rec *iface, int host_no, */ if (iface_get_by_net_binding(iface, iface)) log_debug(7, "Could not find iface for session " - "bound to:" iface_fmt "\n", + "bound to:" iface_fmt "", iface_str(iface)); } } + if (session && t->template->use_boot_info) + iscsi_sysfs_read_boot(iface, session); + if (!iface_kern_id) goto done; @@ -581,6 +856,71 @@ static int iscsi_sysfs_read_iface(struct iface_rec *iface, int host_no, sysfs_get_str(iface_kern_id, ISCSI_IFACE_SUBSYS, "subnet", iface->subnet_mask, sizeof(iface->subnet_mask)); + + sysfs_get_str(iface_kern_id, ISCSI_IFACE_SUBSYS, + "dhcp_alt_client_id_en", + iface->dhcp_alt_client_id_state, + sizeof(iface->dhcp_alt_client_id_state)); + + sysfs_get_str(iface_kern_id, ISCSI_IFACE_SUBSYS, + "dhcp_alt_client_id", + iface->dhcp_alt_client_id, + sizeof(iface->dhcp_alt_client_id)); + + sysfs_get_str(iface_kern_id, ISCSI_IFACE_SUBSYS, + "dhcp_dns_address_en", + iface->dhcp_dns, sizeof(iface->dhcp_dns)); + + sysfs_get_str(iface_kern_id, ISCSI_IFACE_SUBSYS, + "dhcp_learn_iqn_en", + iface->dhcp_learn_iqn, + sizeof(iface->dhcp_learn_iqn)); + + sysfs_get_str(iface_kern_id, ISCSI_IFACE_SUBSYS, + "dhcp_req_vendor_id_en", + iface->dhcp_req_vendor_id_state, + sizeof(iface->dhcp_req_vendor_id_state)); + + sysfs_get_str(iface_kern_id, ISCSI_IFACE_SUBSYS, + "dhcp_use_vendor_id_en", + iface->dhcp_vendor_id_state, + sizeof(iface->dhcp_vendor_id_state)); + + sysfs_get_str(iface_kern_id, ISCSI_IFACE_SUBSYS, + "dhcp_vendor_id", + iface->dhcp_vendor_id, + sizeof(iface->dhcp_vendor_id)); + + sysfs_get_str(iface_kern_id, ISCSI_IFACE_SUBSYS, + "dhcp_slp_da_info_en", + iface->dhcp_slp_da, sizeof(iface->dhcp_slp_da)); + + sysfs_get_str(iface_kern_id, ISCSI_IFACE_SUBSYS, + "fragment_disable", + iface->fragmentation, + sizeof(iface->fragmentation)); + + sysfs_get_str(iface_kern_id, ISCSI_IFACE_SUBSYS, + "grat_arp_en", + iface->gratuitous_arp, + sizeof(iface->gratuitous_arp)); + + sysfs_get_str(iface_kern_id, ISCSI_IFACE_SUBSYS, + "incoming_forwarding_en", + iface->incoming_forwarding, + sizeof(iface->incoming_forwarding)); + + sysfs_get_str(iface_kern_id, ISCSI_IFACE_SUBSYS, + "tos_en", + iface->tos_state, sizeof(iface->tos_state)); + + if (sysfs_get_uint8(iface_kern_id, ISCSI_IFACE_SUBSYS, + "tos", &iface->tos)) + iface->tos = 0; + + if (sysfs_get_uint8(iface_kern_id, ISCSI_IFACE_SUBSYS, + "ttl", &iface->ttl)) + iface->ttl = 0; } else { sysfs_get_str(iface_kern_id, ISCSI_IFACE_SUBSYS, "ipaddr_autocfg", @@ -597,6 +937,53 @@ static int iscsi_sysfs_read_iface(struct iface_rec *iface, int host_no, sysfs_get_str(iface_kern_id, ISCSI_IFACE_SUBSYS, "router_addr", iface->ipv6_router, sizeof(iface->ipv6_router)); + + sysfs_get_str(iface_kern_id, ISCSI_IFACE_SUBSYS, "router_state", + iface->router_autocfg, + sizeof(iface->router_autocfg)); + + if (sysfs_get_uint8(iface_kern_id, ISCSI_IFACE_SUBSYS, + "dup_addr_detect_cnt", + &iface->dup_addr_detect_cnt)) + iface->dup_addr_detect_cnt = 0; + + if (sysfs_get_uint(iface_kern_id, ISCSI_IFACE_SUBSYS, + "flow_label", &iface->flow_label)) + iface->flow_label = 0; + + sysfs_get_str(iface_kern_id, ISCSI_IFACE_SUBSYS, + "grat_neighbor_adv_en", + iface->gratuitous_neighbor_adv, + sizeof(iface->gratuitous_neighbor_adv)); + + if (sysfs_get_uint8(iface_kern_id, ISCSI_IFACE_SUBSYS, + "hop_limit", &iface->hop_limit)) + iface->hop_limit = 0; + + sysfs_get_str(iface_kern_id, ISCSI_IFACE_SUBSYS, "mld_en", + iface->mld, sizeof(iface->mld)); + + if (sysfs_get_uint(iface_kern_id, ISCSI_IFACE_SUBSYS, + "nd_reachable_tmo", + &iface->nd_reachable_tmo)) + iface->nd_reachable_tmo = 0; + + if (sysfs_get_uint(iface_kern_id, ISCSI_IFACE_SUBSYS, + "nd_rexmit_time", &iface->nd_rexmit_time)) + iface->nd_rexmit_time = 0; + + if (sysfs_get_uint(iface_kern_id, ISCSI_IFACE_SUBSYS, + "nd_stale_tmo", &iface->nd_stale_tmo)) + iface->nd_stale_tmo = 0; + + if (sysfs_get_uint(iface_kern_id, ISCSI_IFACE_SUBSYS, + "router_adv_link_mtu", + &iface->router_adv_link_mtu)) + iface->router_adv_link_mtu = 0; + + if (sysfs_get_uint(iface_kern_id, ISCSI_IFACE_SUBSYS, + "traffic_class", &iface->traffic_class)) + iface->traffic_class = 0; } if (sysfs_get_uint16(iface_kern_id, ISCSI_IFACE_SUBSYS, "port", @@ -613,6 +1000,94 @@ static int iscsi_sysfs_read_iface(struct iface_rec *iface, int host_no, &iface->vlan_priority)) iface->vlan_priority = UINT8_MAX; + sysfs_get_str(iface_kern_id, ISCSI_IFACE_SUBSYS, "vlan_enabled", + iface->vlan_state, sizeof(iface->vlan_state)); + + sysfs_get_str(iface_kern_id, ISCSI_IFACE_SUBSYS, "enabled", + iface->state, sizeof(iface->state)); + + sysfs_get_str(iface_kern_id, ISCSI_IFACE_SUBSYS, "delayed_ack_en", + iface->delayed_ack, sizeof(iface->delayed_ack)); + + sysfs_get_str(iface_kern_id, ISCSI_IFACE_SUBSYS, "tcp_nagle_disable", + iface->nagle, sizeof(iface->nagle)); + + sysfs_get_str(iface_kern_id, ISCSI_IFACE_SUBSYS, "tcp_wsf_disable", + iface->tcp_wsf_state, sizeof(iface->tcp_wsf_state)); + + if (sysfs_get_uint8(iface_kern_id, ISCSI_IFACE_SUBSYS, "tcp_wsf", + &iface->tcp_wsf)) + iface->tcp_wsf = 0; + + if (sysfs_get_uint8(iface_kern_id, ISCSI_IFACE_SUBSYS, + "tcp_timer_scale", &iface->tcp_timer_scale)) + iface->tcp_timer_scale = 0; + + sysfs_get_str(iface_kern_id, ISCSI_IFACE_SUBSYS, "tcp_timestamp_en", + iface->tcp_timestamp, sizeof(iface->tcp_timestamp)); + + sysfs_get_str(iface_kern_id, ISCSI_IFACE_SUBSYS, "redirect_en", + iface->redirect, sizeof(iface->redirect)); + + if (sysfs_get_uint16(iface_kern_id, ISCSI_IFACE_SUBSYS, + "def_taskmgmt_tmo", &iface->def_task_mgmt_tmo)) + iface->def_task_mgmt_tmo = 0; + + sysfs_get_str(iface_kern_id, ISCSI_IFACE_SUBSYS, "header_digest", + iface->header_digest, sizeof(iface->header_digest)); + + sysfs_get_str(iface_kern_id, ISCSI_IFACE_SUBSYS, "data_digest", + iface->data_digest, sizeof(iface->data_digest)); + + sysfs_get_str(iface_kern_id, ISCSI_IFACE_SUBSYS, "immediate_data", + iface->immediate_data, sizeof(iface->immediate_data)); + + sysfs_get_str(iface_kern_id, ISCSI_IFACE_SUBSYS, "initial_r2t", + iface->initial_r2t, sizeof(iface->initial_r2t)); + + sysfs_get_str(iface_kern_id, ISCSI_IFACE_SUBSYS, "data_seq_in_order", + iface->data_seq_inorder, sizeof(iface->data_seq_inorder)); + + sysfs_get_str(iface_kern_id, ISCSI_IFACE_SUBSYS, "data_pdu_in_order", + iface->data_pdu_inorder, sizeof(iface->data_pdu_inorder)); + + if (sysfs_get_uint8(iface_kern_id, ISCSI_IFACE_SUBSYS, "erl", + &iface->erl)) + iface->erl = 0; + + if (sysfs_get_uint(iface_kern_id, ISCSI_IFACE_SUBSYS, + "max_recv_dlength", &iface->max_recv_dlength)) + iface->max_recv_dlength = 0; + + if (sysfs_get_uint(iface_kern_id, ISCSI_IFACE_SUBSYS, + "first_burst_len", &iface->first_burst_len)) + iface->first_burst_len = 0; + + if (sysfs_get_uint16(iface_kern_id, ISCSI_IFACE_SUBSYS, + "max_outstanding_r2t", &iface->max_out_r2t)) + iface->max_out_r2t = 0; + + if (sysfs_get_uint(iface_kern_id, ISCSI_IFACE_SUBSYS, + "max_burst_len", &iface->max_burst_len)) + iface->max_burst_len = 0; + + sysfs_get_str(iface_kern_id, ISCSI_IFACE_SUBSYS, "chap_auth", + iface->chap_auth, sizeof(iface->chap_auth)); + + sysfs_get_str(iface_kern_id, ISCSI_IFACE_SUBSYS, "bidi_chap", + iface->bidi_chap, sizeof(iface->bidi_chap)); + + sysfs_get_str(iface_kern_id, ISCSI_IFACE_SUBSYS, "strict_login_comp_en", + iface->strict_login_comp, + sizeof(iface->strict_login_comp)); + + sysfs_get_str(iface_kern_id, ISCSI_IFACE_SUBSYS, + "discovery_auth_optional", + iface->discovery_auth, sizeof(iface->discovery_auth)); + + sysfs_get_str(iface_kern_id, ISCSI_IFACE_SUBSYS, "discovery_logout", + iface->discovery_logout, sizeof(iface->discovery_logout)); + if (sscanf(iface_kern_id, "ipv%d-iface-%u-%u", &iface_type, &tmp_host_no, &iface_num) == 3) iface->iface_num = iface_num; @@ -740,7 +1215,7 @@ int iscsi_sysfs_session_has_leadconn(uint32_t sid) * /sys/devices/platform/hostH/sessionS/targetH:B:I * /sys/devices/platform/hostH/sessionS * - * return the sid S. If just the sid is passed in it will be covnerted + * return the sid S. If just the sid is passed in it will be converted * to a int. */ int iscsi_sysfs_get_sid_from_path(char *session) @@ -748,15 +1223,16 @@ int iscsi_sysfs_get_sid_from_path(char *session) struct sysfs_device *dev_parent, *dev; struct stat statb; char devpath[PATH_SIZE]; + char *end; + int sid; + + sid = strtol(session, &end, 10); + if (sid > 0 && *session != '\0' && *end == '\0') + return sid; if (lstat(session, &statb)) { - log_debug(1, "Could not stat %s failed with %d", - session, errno); - if (index(session, '/')) { - log_error("%s is an invalid session path\n", session); - exit(1); - } - return atoi(session); + log_error("%s is an invalid session ID or path", session); + exit(1); } if (!S_ISDIR(statb.st_mode) && !S_ISLNK(statb.st_mode)) { @@ -772,7 +1248,7 @@ int iscsi_sysfs_get_sid_from_path(char *session) dev = sysfs_device_get(devpath); if (!dev) { log_error("Could not get dev for %s. Possible sysfs " - "incompatibility.\n", devpath); + "incompatibility.", devpath); return -1; } @@ -932,11 +1408,13 @@ int iscsi_sysfs_get_sessioninfo_by_id(struct session_info *info, char *session) } int iscsi_sysfs_for_each_session(void *data, int *nr_found, - iscsi_sysfs_session_op_fn *fn) + iscsi_sysfs_session_op_fn *fn, + int in_parallel) { struct dirent **namelist; - int rc = 0, n, i; + int rc = 0, n, i, chldrc = 0; struct session_info *info; + pid_t pid = 0; info = calloc(1, sizeof(*info)); if (!info) @@ -958,14 +1436,52 @@ int iscsi_sysfs_for_each_session(void *data, int *nr_found, continue; } - rc = fn(data, info); - if (rc > 0) - break; - else if (rc == 0) - (*nr_found)++; - else - /* if less than zero it means it was not a match */ - rc = 0; + if (in_parallel) { + pid = fork(); + } + if (pid == 0) { + rc = fn(data, info); + if (in_parallel) { + exit(rc); + } else { + if (rc > 0) { + break; + } else if (rc == 0) { + (*nr_found)++; + } else { + /* if less than zero it means it was not a match */ + rc = 0; + } + } + } else if (pid < 0) { + log_error("could not fork() for session %s, err %d", + namelist[i]->d_name, errno); + } + } + + if (in_parallel) { + while (1) { + if (wait(&chldrc) < 0) { + /* + * ECHILD means no more children which is + * expected to happen sooner or later. + */ + if (errno != ECHILD) { + rc = errno; + } + break; + } + + if ((chldrc > 0) && (rc == 0)) { + /* + * The non-parallel code path returns the first + * error so this keeps the same semantics. + */ + rc = chldrc; + } else if (chldrc == 0) { + (*nr_found)++; + } + } } for (i = 0; i < n; i++) @@ -1006,7 +1522,7 @@ int iscsi_sysfs_get_device_state(char *state, int host_no, int target, int lun) snprintf(id, sizeof(id), "%d:0:%d:%d", host_no, target, lun); if (sysfs_get_str(id, SCSI_SUBSYS, "state", state, SCSI_MAX_STATE_VALUE)) { - log_debug(3, "Could not read attr state for %s\n", id); + log_debug(3, "Could not read attr state for %s", id); return ISCSI_ERR_SYSFS_LOOKUP; } @@ -1027,7 +1543,7 @@ char *iscsi_sysfs_get_blockdev_from_lun(int host_no, int target, int lun) snprintf(id, sizeof(id), "%d:0:%d:%d", host_no, target, lun); if (!sysfs_lookup_devpath_by_subsys_id(devpath, sizeof(devpath), SCSI_SUBSYS, id)) { - log_debug(3, "Could not lookup devpath for %s %s\n", + log_debug(3, "Could not lookup devpath for %s %s", SCSI_SUBSYS, id); return NULL; } @@ -1055,7 +1571,7 @@ char *iscsi_sysfs_get_blockdev_from_lun(int host_no, int target, int lun) * 2.6.25 dropped the symlink and now block is a dir. */ if (lstat(path_full, &statbuf)) { - log_error("Could not stat block path %s err %d\n", + log_error("Could not stat block path %s err %d", path_full, errno); break; } @@ -1074,7 +1590,7 @@ char *iscsi_sysfs_get_blockdev_from_lun(int host_no, int target, int lun) /* it should not be this hard should it? :) */ blk_dirfd = opendir(path_full); if (!blk_dirfd) { - log_debug(3, "Could not open blk path %s\n", + log_debug(3, "Could not open blk path %s", path_full); break; } @@ -1110,7 +1626,7 @@ static uint32_t get_target_no_from_sid(uint32_t sid, int *err) snprintf(id, sizeof(id), "session%u", sid); if (!sysfs_lookup_devpath_by_subsys_id(devpath, sizeof(devpath), ISCSI_SESSION_SUBSYS, id)) { - log_debug(3, "Could not lookup devpath for %s %s\n", + log_debug(3, "Could not lookup devpath for %s %s", ISCSI_SESSION_SUBSYS, id); return 0; } @@ -1283,7 +1799,7 @@ int iscsi_sysfs_for_each_device(void *data, int host_no, uint32_t sid, snprintf(id, sizeof(id), "session%u", sid); if (!sysfs_lookup_devpath_by_subsys_id(devpath, sizeof(devpath), ISCSI_SESSION_SUBSYS, id)) { - log_debug(3, "Could not lookup devpath for %s %s\n", + log_debug(3, "Could not lookup devpath for %s %s", ISCSI_SESSION_SUBSYS, id); return ISCSI_ERR_SYSFS_LOOKUP; } @@ -1367,7 +1883,7 @@ pid_t iscsi_sysfs_scan_host(int hostno, int async) snprintf(id, sizeof(id), ISCSI_HOST_ID, hostno); sysfs_set_param(id, SCSI_HOST_SUBSYS, "scan", write_buf, strlen(write_buf)); - log_debug(4, "scanning host%d completed\n", hostno); + log_debug(4, "scanning host%d completed", hostno); } else if (pid > 0) { log_debug(4, "scanning host%d from pid %d", hostno, pid); } else diff --git a/usr/iscsi_sysfs.h b/usr/iscsi_sysfs.h index 2b15d78..9a56105 100644 --- a/usr/iscsi_sysfs.h +++ b/usr/iscsi_sysfs.h @@ -31,6 +31,7 @@ struct iscsi_conn; struct iscsi_session_operational_config; struct iscsi_conn_operational_config; struct iscsi_auth_config; +struct flashnode_rec; #define SCSI_MAX_STATE_VALUE 32 @@ -42,13 +43,16 @@ extern int iscsi_sysfs_session_has_leadconn(uint32_t sid); typedef int (iscsi_sysfs_session_op_fn)(void *, struct session_info *); typedef int (iscsi_sysfs_host_op_fn)(void *, struct host_info *); +typedef int (iscsi_sysfs_flashnode_op_fn)(void *, struct flashnode_rec *, + uint32_t, uint32_t); typedef int (iscsi_sysfs_iface_op_fn)(void *, struct iface_rec *); extern int iscsi_sysfs_for_each_iface_on_host(void *data, uint32_t host_no, int *nr_found, iscsi_sysfs_iface_op_fn *fn); extern int iscsi_sysfs_for_each_session(void *data, int *nr_found, - iscsi_sysfs_session_op_fn *fn); + iscsi_sysfs_session_op_fn *fn, + int in_parallel); extern int iscsi_sysfs_for_each_host(void *data, int *nr_found, iscsi_sysfs_host_op_fn *fn); extern uint32_t iscsi_sysfs_get_host_no_from_sid(uint32_t sid, int *err); @@ -56,6 +60,20 @@ extern uint32_t iscsi_sysfs_get_host_no_from_hwinfo(struct iface_rec *iface, int *rc); extern uint32_t iscsi_sysfs_get_host_no_from_hwaddress(char *hwaddress, int *rc); extern int iscsi_sysfs_get_hostinfo_by_host_no(struct host_info *hinfo); +extern int iscsi_sysfs_for_each_flashnode(void *data, uint32_t host_no, + int *nr_found, + iscsi_sysfs_flashnode_op_fn *fn); +extern int iscsi_sysfs_get_flashnode_info(struct flashnode_rec *fnode, + uint32_t host_no, + uint32_t flashnode_id); +extern int iscsi_sysfs_update_flashnode_param(uint32_t host_no, + uint32_t flashnode_id, + char *name, char *val); +extern int iscsi_sysfs_create_flashnode(uint32_t host_no, char *ipver); +extern int iscsi_sysfs_del_flashnode(uint32_t host_no, uint32_t flashnode_id); +extern int iscsi_sysfs_login_flashnode(uint32_t host_no, uint32_t flashnode_id); +extern int iscsi_sysfs_logout_flashnode(uint32_t host_no, + uint32_t flashnode_id); extern int iscsi_sysfs_get_sid_from_path(char *session); extern char *iscsi_sysfs_get_blockdev_from_lun(int hostno, int target, int sid); diff --git a/usr/iscsi_util.c b/usr/iscsi_util.c index 5e3420e..a4f33cf 100644 --- a/usr/iscsi_util.c +++ b/usr/iscsi_util.c @@ -25,16 +25,28 @@ #include #include #include +#include +#include #include #include #include +#include "sysdeps.h" #include "log.h" #include "iscsi_settings.h" #include "iface.h" #include "session_info.h" #include "iscsi_util.h" +int setup_abstract_addr(struct sockaddr_un *addr, char *unix_sock_name) +{ + memset(addr, 0, sizeof(*addr)); + addr->sun_family = AF_LOCAL; + strlcpy(addr->sun_path + 1, unix_sock_name, sizeof(addr->sun_path) - 1); + return offsetof(struct sockaddr_un, sun_path) + + strlen(addr->sun_path + 1) + 1; +} + void daemon_init(void) { int fd; @@ -60,7 +72,8 @@ int oom_adjust(void) char path[ISCSI_OOM_PATH_LEN]; struct stat statb; - if (nice(-10) < 0) + errno = 0; + if (nice(-10) == -1 && errno != 0) log_debug(1, "Could not increase process priority: %s", strerror(errno)); @@ -135,10 +148,10 @@ int increase_max_files(void) err = getrlimit(RLIMIT_NOFILE, &rl); if (err) { - log_debug(1, "Could not get file limit (err %d)\n", errno); + log_debug(1, "Could not get file limit (err %d)", errno); return errno; } - log_debug(1, "Max file limits %lu %lu\n", rl.rlim_cur, rl.rlim_max); + log_debug(1, "Max file limits %lu %lu", rl.rlim_cur, rl.rlim_max); if (rl.rlim_cur < ISCSI_MAX_FILES) rl.rlim_cur = ISCSI_MAX_FILES; @@ -147,7 +160,7 @@ int increase_max_files(void) err = setrlimit(RLIMIT_NOFILE, &rl); if (err) { - log_debug(1, "Could not set file limit to %lu/%lu (err %d)\n", + log_debug(1, "Could not set file limit to %lu/%lu (err %d)", rl.rlim_cur, rl.rlim_max, errno); return errno; } @@ -306,7 +319,7 @@ int __iscsi_match_session(node_rec_t *rec, char *targetname, unsigned sid) { if (!rec) { - log_debug(6, "no rec info to match\n"); + log_debug(6, "no rec info to match"); return 1; } diff --git a/usr/iscsi_util.h b/usr/iscsi_util.h index 110dfa8..ff725eb 100644 --- a/usr/iscsi_util.h +++ b/usr/iscsi_util.h @@ -26,4 +26,7 @@ extern int __iscsi_match_session(struct node_rec *rec, char *targetname, extern char *strstrip(char *s); extern char *cfg_get_string_param(char *pathname, const char *key); +struct sockaddr_un; +extern int setup_abstract_addr(struct sockaddr_un *addr, char *unix_sock_name); + #endif diff --git a/usr/iscsiadm.c b/usr/iscsiadm.c index 8f9de05..aa7cf07 100644 --- a/usr/iscsiadm.c +++ b/usr/iscsiadm.c @@ -53,6 +53,7 @@ #include "iscsi_err.h" #include "iscsi_ipc.h" #include "iscsi_timer.h" +#include "flashnode.h" static char program_name[] = "iscsiadm"; static char config_file[TARGET_NAME_MAXLEN]; @@ -67,7 +68,9 @@ enum iscsiadm_mode { MODE_IFACE, MODE_FW, MODE_PING, - MODE_CHAP + MODE_CHAP, + MODE_FLASHNODE, + MODE_HOST_STATS }; enum iscsiadm_op { @@ -78,7 +81,9 @@ enum iscsiadm_op { OP_SHOW = 0x8, OP_NONPERSISTENT = 0x10, OP_APPLY = 0x20, - OP_APPLY_ALL = 0x40 + OP_APPLY_ALL = 0x40, + OP_LOGIN = 0x80, + OP_LOGOUT = 0x100 }; static struct option const long_options[] = @@ -111,9 +116,11 @@ static struct option const long_options[] = {"packetsize", required_argument, NULL, 'b'}, {"count", required_argument, NULL, 'c'}, {"interval", required_argument, NULL, 'i'}, + {"index", required_argument, NULL, 'x'}, + {"portal_type", optional_argument, NULL, 'A'}, {NULL, 0, NULL, 0}, }; -static char *short_options = "RlDVhm:a:b:c:C:p:P:T:H:i:I:U:k:L:d:r:n:v:o:sSt:u"; +static char *short_options = "RlDVhm:a:b:c:C:p:P:T:H:i:I:U:k:L:d:r:n:v:o:sSt:ux:A:"; static void usage(int status) { @@ -129,8 +136,8 @@ iscsiadm -m node [ -hV ] [ -d debug_level ] [ -P printlevel ] [ -L all,manual,au [ [ -o operation ] [ -n name ] [ -v value ] ]\n\ iscsiadm -m session [ -hV ] [ -d debug_level ] [ -P printlevel] [ -r sessionid | sysfsdir [ -R | -u | -s ] [ -o operation ] [ -n name ] [ -v value ] ]\n\ iscsiadm -m iface [ -hV ] [ -d debug_level ] [ -P printlevel ] [ -I ifacename | -H hostno|MAC ] [ [ -o operation ] [ -n name ] [ -v value ] ] [ -C ping [ -a ip ] [ -b packetsize ] [ -c count ] [ -i interval ] ]\n\ -iscsiadm -m fw [ -l ]\n\ -iscsiadm -m host [ -P printlevel ] [ -H hostno|MAC ] [ -C chap [ -o operation ] [ -v chap_tbl_idx ] ]\n\ +iscsiadm -m fw [ -d debug_level ] [ -l ]\n\ +iscsiadm -m host [ -P printlevel ] [ -H hostno|MAC ] [ [ -C chap [ -x chap_tbl_idx ] ] | [ -C flashnode [ -A portal_type ] [ -x flashnode_idx ] ] | [ -C stats ] ] [ [ -o operation ] [ -n name ] [ -v value ] ] \n\ iscsiadm -k priority\n"); } exit(status); @@ -155,6 +162,10 @@ str_to_op(char *str) op = OP_APPLY; else if (!strcmp("applyall", str)) op = OP_APPLY_ALL; + else if (!strcmp("login", str)) + op = OP_LOGIN; + else if (!strcmp("logout", str)) + op = OP_LOGOUT; else op = OP_NOOP; @@ -195,6 +206,11 @@ str_to_submode(char *str) sub_mode = MODE_PING; else if (!strcmp("chap", str)) sub_mode = MODE_CHAP; + else if (!strcmp("flashnode", str)) + sub_mode = MODE_FLASHNODE; + else if (!strcmp("stats", str)) + sub_mode = MODE_HOST_STATS; + else sub_mode = -1; @@ -221,6 +237,21 @@ str_to_type(char *str) return type; } +static int +str_to_portal_type(char *str) +{ + int ptype; + + if (!strcmp("ipv4", str)) + ptype = IPV4; + else if (!strcmp("ipv6", str)) + ptype = IPV6; + else + ptype = -1; + + return ptype; +} + static void kill_iscsid(int priority) { iscsiadm_req_t req; @@ -247,7 +278,7 @@ static void kill_iscsid(int priority) if (rc) { iscsi_err_print_msg(rc); log_error("Could not stop iscsid. Trying sending iscsid " - "SIGTERM or SIGKILL signals manually\n"); + "SIGTERM or SIGKILL signals manually"); } } @@ -272,7 +303,7 @@ static int print_ifaces(struct iface_rec *iface, int info_level) if (iface) { err = iface_conf_read(iface); if (err) { - log_error("Could not read iface %s.\n", + log_error("Could not read iface %s.", iface->name); return err; } @@ -320,7 +351,8 @@ match_startup_mode(node_rec_t *rec, char *mode) } static int -for_each_session(struct node_rec *rec, iscsi_sysfs_session_op_fn *fn) +for_each_session(struct node_rec *rec, iscsi_sysfs_session_op_fn *fn, + int in_parallel) { int err, num_found = 0; @@ -328,7 +360,8 @@ for_each_session(struct node_rec *rec, iscsi_sysfs_session_op_fn *fn) num_found = 1; err = fn(rec, rec->session.info); } else { - err = iscsi_sysfs_for_each_session(rec, &num_found, fn); + err = iscsi_sysfs_for_each_session(rec, &num_found, fn, + in_parallel); } if (err) log_error("Could not execute operation on all sessions: %s", @@ -370,7 +403,7 @@ __logout_by_startup(void *data, struct list_head *list, * this is due to a HW driver or some other driver * not hooked in */ - log_debug(7, "could not read data for [%s,%s.%d]\n", + log_debug(7, "could not read data for [%s,%s.%d]", info->targetname, info->persistent_address, info->persistent_port); return -1; @@ -408,7 +441,7 @@ logout_by_startup(char *mode) rc = iscsi_logout_portals(mode, &nr_found, 1, __logout_by_startup); if (rc == ISCSI_ERR_NO_OBJS_FOUND) log_error("No matching sessions found"); - return rc; + return rc; } struct startup_data { @@ -452,7 +485,7 @@ __do_leading_login(void *data, struct list_head *list, struct node_rec *rec) * If there is an existing session that matcthes the target, * the leading login is complete. */ - if (iscsi_sysfs_for_each_session(rec, &nr_found, iscsi_match_target)) { + if (iscsi_sysfs_for_each_session(rec, &nr_found, iscsi_match_target, 0)) { log_debug(1, "Skipping %s: Already a session for that target", rec->name); return -1; @@ -552,7 +585,7 @@ login_by_startup(char *mode) list_for_each_entry_safe(rec, tmp_rec, &startup.leading_logins, list) { if (!iscsi_sysfs_for_each_session(rec, &nr_found, - iscsi_match_target)) + iscsi_match_target, 0)) missed_leading_login++; /* * Cleanup the list, since 'iscsi_login_portals_safe' @@ -582,6 +615,8 @@ static int iscsi_logout_matched_portal(void *data, struct list_head *list, { struct node_rec *pattern_rec = data; struct iscsi_transport *t; + uint32_t host_no; + int rc = 0; t = iscsi_sysfs_get_transport_by_sid(info->sid); if (!t) @@ -590,7 +625,19 @@ static int iscsi_logout_matched_portal(void *data, struct list_head *list, if (!iscsi_match_session(pattern_rec, info)) return -1; - return iscsi_logout_portal(info, list); + host_no = iscsi_sysfs_get_host_no_from_sid(info->sid, &rc); + if (rc) { + log_error("could not get host_no for session%d: %s.", + info->sid, iscsi_err_to_str(rc)); + return -1; + } + + if (!iscsi_sysfs_session_user_created(info->sid)) + rc = iscsi_logout_flashnode_sid(t, host_no, info->sid); + else + rc = iscsi_logout_portal(info, list); + + return rc; } static int rec_match_fn(void *data, node_rec_t *rec) @@ -1092,17 +1139,55 @@ do_software_sendtargets(discovery_rec_t *drec, struct list_head *ifaces, return rc; } +static int do_isns(discovery_rec_t *drec, struct list_head *ifaces, + int info_level, int do_login, int op) +{ + struct list_head rec_list; + struct node_rec *rec, *tmp; + int rc; + + INIT_LIST_HEAD(&rec_list); + /* + * compat: if the user did not pass any op then we do all + * ops for them + */ + if (!op) + op = OP_NEW | OP_DELETE | OP_UPDATE; + + + rc = idbm_bind_ifaces_to_nodes(discovery_isns, drec, ifaces, + &rec_list); + if (rc) { + log_error("Could not perform iSNS discovery: %s", + iscsi_err_to_str(rc)); + return rc; + } else if (list_empty(&rec_list)) { + log_error("No portals found"); + return ISCSI_ERR_NO_OBJS_FOUND; + } + + rc = exec_disc_op_on_recs(drec, &rec_list, info_level, do_login, op); + + list_for_each_entry_safe(rec, tmp, &rec_list, list) { + list_del(&rec->list); + free(rec); + } + + return rc; +} + static int -do_sendtargets(discovery_rec_t *drec, struct list_head *ifaces, - int info_level, int do_login, int op, int sync_drec) +do_target_discovery(discovery_rec_t *drec, struct list_head *ifaces, + int info_level, int do_login, int op, int sync_drec) { + struct iface_rec *tmp, *iface; int rc, host_no; struct iscsi_transport *t; if (list_empty(ifaces)) { ifaces = NULL; - goto sw_st; + goto sw_discovery; } /* we allow users to mix hw and sw iscsi so we have to sort it out */ @@ -1131,64 +1216,36 @@ do_sendtargets(discovery_rec_t *drec, struct list_head *ifaces, host_no = iscsi_sysfs_get_host_no_from_hwinfo(iface, &rc); if (rc || host_no == -1) { log_debug(1, "Could not match iface" iface_fmt " to " - "host.", iface_str(iface)); + "host.", iface_str(iface)); /* try software iscsi */ continue; } - if (t->caps & CAP_SENDTARGETS_OFFLOAD) { - do_offload_sendtargets(drec, host_no, do_login); - list_del(&iface->list); - free(iface); - } + if (drec->type == DISCOVERY_TYPE_SENDTARGETS) + if (t->caps & CAP_SENDTARGETS_OFFLOAD) { + do_offload_sendtargets(drec, host_no, do_login); + list_del(&iface->list); + free(iface); + } } if (list_empty(ifaces)) return ISCSI_ERR_NO_OBJS_FOUND; -sw_st: - return do_software_sendtargets(drec, ifaces, info_level, do_login, - op, sync_drec); -} - -static int do_isns(discovery_rec_t *drec, struct list_head *ifaces, - int info_level, int do_login, int op) -{ - struct list_head rec_list; - struct node_rec *rec, *tmp; - int rc; - - INIT_LIST_HEAD(&rec_list); - /* - * compat: if the user did not pass any op then we do all - * ops for them - */ - if (!op) - op = OP_NEW | OP_DELETE | OP_UPDATE; - - drec->type = DISCOVERY_TYPE_ISNS; - - rc = idbm_bind_ifaces_to_nodes(discovery_isns, drec, ifaces, - &rec_list); - if (rc) { - log_error("Could not perform iSNS discovery: %s", - iscsi_err_to_str(rc)); - return rc; - } else if (list_empty(&rec_list)) { - log_error("No portals found"); - return ISCSI_ERR_NO_OBJS_FOUND; +sw_discovery: + switch (drec->type) { + case DISCOVERY_TYPE_SENDTARGETS: + return do_software_sendtargets(drec, ifaces, info_level, + do_login, op, sync_drec); + case DISCOVERY_TYPE_ISNS: + return do_isns(drec, ifaces, info_level, do_login, op); + default: + log_debug(1, "Unknown Discovery Type : %d", drec->type); + return ISCSI_ERR_UNKNOWN_DISCOVERY_TYPE; } - - rc = exec_disc_op_on_recs(drec, &rec_list, info_level, do_login, op); - - list_for_each_entry_safe(rec, tmp, &rec_list, list) { - list_del(&rec->list); - free(rec); - } - - return rc; } + static int verify_mode_params(int argc, char **argv, char *allowed, int skip_m) { @@ -1229,7 +1286,7 @@ static int iface_apply_net_config(struct iface_rec *iface, int op) int fd; log_debug(8, "Calling iscsid, to apply net config for" - "iface.name = %s\n", iface->name); + "iface.name = %s", iface->name); if (op == OP_APPLY_ALL) iface_all = 1; @@ -1373,19 +1430,193 @@ exit_chap_info: return rc; } -static int delete_host_chap_info(uint32_t host_no, char *value) +static int fill_host_chap_rec(struct list_head *params, + struct iscsi_chap_rec *crec, recinfo_t *cinfo, + uint16_t chap_tbl_idx, int type, int *param_count) +{ + struct user_param *param; + int rc = 0; + + crec->chap_tbl_idx = chap_tbl_idx; + crec->chap_type = type; + + idbm_recinfo_host_chap(crec, cinfo); + + list_for_each_entry(param, params, list) { + rc = idbm_rec_update_param(cinfo, param->name, param->value, 0); + if (rc) + break; + } + + if (!rc) + *param_count += 3; /* index, type and password_length */ + + return rc; +} + +static int verify_host_chap_params(struct list_head *params, int *type, + int *param_count) +{ + struct user_param *param; + int username = -1; + int password = -1; + int rc = 0; + + list_for_each_entry(param, params, list) { + *param_count += 1; + + if (!strcmp(param->name, HOST_AUTH_USERNAME)) + username = CHAP_TYPE_OUT; + else if (!strcmp(param->name, HOST_AUTH_PASSWORD)) + password = CHAP_TYPE_OUT; + else if (!strcmp(param->name, HOST_AUTH_USERNAME_IN)) + username = CHAP_TYPE_IN; + else if (!strcmp(param->name, HOST_AUTH_PASSWORD_IN)) + password = CHAP_TYPE_IN; + else + continue; + } + + if ((username == CHAP_TYPE_OUT) && (password == CHAP_TYPE_OUT)) { + if (type) + *type = CHAP_TYPE_OUT; + + rc = ISCSI_SUCCESS; + } else if ((username == CHAP_TYPE_IN) && (password == CHAP_TYPE_IN)) { + if (type) + *type = CHAP_TYPE_IN; + + rc = ISCSI_SUCCESS; + } else { + rc = ISCSI_ERR; + } + + return rc; +} + +static int set_host_chap_info(uint32_t host_no, uint64_t chap_index, + struct list_head *params) +{ + struct iscsi_transport *t = NULL; + struct iscsi_chap_rec crec; + recinfo_t *chap_info = NULL; + struct iovec *iovs = NULL; + struct iovec *iov = NULL; + int type; + int param_count = 0; + int param_used; + int rc = 0; + int fd, i = 0; + + if (list_empty(params)) { + log_error("Chap username/password not provided."); + goto exit_set_chap; + } + + chap_info = idbm_recinfo_alloc(MAX_KEYS); + if (!chap_info) { + log_error("Out of Memory."); + rc = ISCSI_ERR_NOMEM; + goto exit_set_chap; + } + + t = iscsi_sysfs_get_transport_by_hba(host_no); + if (!t) { + log_error("Could not match hostno %d to transport.", host_no); + rc = ISCSI_ERR_TRANS_NOT_FOUND; + goto free_info_rec; + } + + rc = verify_host_chap_params(params, &type, ¶m_count); + if (rc) { + log_error("Invalid username/password pair passed. Unable to determine the type of chap entry"); + rc = ISCSI_ERR_INVAL; + goto free_info_rec; + } + + if (param_count > 2) { + log_error("Only one pair of username/password can be passed."); + rc = ISCSI_ERR; + goto free_info_rec; + } + + memset(&crec, 0, sizeof(crec)); + rc = fill_host_chap_rec(params, &crec, chap_info, chap_index, type, + ¶m_count); + if (rc) { + log_error("Unable to fill CHAP record"); + goto free_info_rec; + } + + /* +2 for event and nlmsghdr */ + param_count += 2; + iovs = calloc((param_count * sizeof(struct iovec)), + sizeof(char)); + if (!iovs) { + log_error("Out of Memory."); + rc = ISCSI_ERR_NOMEM; + goto free_info_rec; + } + + /* param_used gives actual number of iovecs used for chap */ + param_used = chap_build_config(&crec, iovs); + if (!param_used) { + log_error("Build chap config failed."); + rc = ISCSI_ERR; + goto free_iovec; + } + + fd = ipc->ctldev_open(); + if (fd < 0) { + rc = ISCSI_ERR_INTERNAL; + log_error("Netlink open failed."); + goto free_iovec; + } + + rc = ipc->set_chap(t->handle, host_no, iovs, param_count); + if (rc < 0) { + log_error("CHAP setting failed"); + if (rc == -EBUSY) { + rc = ISCSI_ERR_BUSY; + log_error("CHAP index %d is in use.", + crec.chap_tbl_idx); + } else { + rc = ISCSI_ERR; + } + + goto exit_set_chap; + } + + ipc->ctldev_close(); + +free_iovec: + /* start at 2, because 0 is for nlmsghdr and 1 for event */ + iov = iovs + 2; + for (i = 0; i < param_used; i++, iov++) { + if (iov->iov_base) + free(iov->iov_base); + } + + free(iovs); + +free_info_rec: + if (chap_info) + free(chap_info); + +exit_set_chap: + return rc; +} + +static int delete_host_chap_info(uint32_t host_no, uint16_t chap_tbl_idx) { struct iscsi_transport *t = NULL; int fd, rc = 0; - uint16_t chap_tbl_idx; - if (!value) { - log_error("CHAP deletion requires --value=table_index."); - return ISCSI_ERR_INVAL; + if (chap_tbl_idx > MAX_CHAP_ENTRIES) { + log_error("Invalid chap table index."); + goto exit_delete_chap; } - chap_tbl_idx = (uint16_t)atoi(value); - t = iscsi_sysfs_get_transport_by_hba(host_no); if (!t) { log_error("Could not match hostno %d to " @@ -1401,7 +1632,7 @@ static int delete_host_chap_info(uint32_t host_no, char *value) goto exit_delete_chap; } - log_info("Deleteing CHAP index: %d\n", chap_tbl_idx); + log_info("Deleteing CHAP index: %d", chap_tbl_idx); rc = ipc->delete_chap(t->handle, host_no, chap_tbl_idx); if (rc < 0) { log_error("CHAP Delete failed."); @@ -1419,7 +1650,7 @@ exit_delete_chap: } static int exec_host_chap_op(int op, int info_level, uint32_t host_no, - char *value) + uint64_t chap_index, struct list_head *params) { int rc = ISCSI_ERR_INVAL; @@ -1427,8 +1658,12 @@ static int exec_host_chap_op(int op, int info_level, uint32_t host_no, case OP_SHOW: rc = get_host_chap_info(host_no); break; + case OP_NEW: + case OP_UPDATE: + rc = set_host_chap_info(host_no, chap_index, params); + break; case OP_DELETE: - rc = delete_host_chap_info(host_no, value); + rc = delete_host_chap_info(host_no, chap_index); break; default: log_error("Invalid operation."); @@ -1438,6 +1673,588 @@ static int exec_host_chap_op(int op, int info_level, uint32_t host_no, return rc; } +static int get_flashnode_info(uint32_t host_no, uint32_t flashnode_idx) +{ + struct flashnode_rec fnode; + int rc = 0; + + memset(&fnode, 0, sizeof(fnode)); + rc = iscsi_sysfs_get_flashnode_info(&fnode, host_no, flashnode_idx); + if (rc) { + log_error("Could not read info for flashnode %u of host %u, %s", + flashnode_idx, host_no, strerror(rc)); + return rc; + } + + idbm_print_flashnode_info(&fnode); + return rc; +} + +static int list_flashnodes(int info_level, uint32_t host_no) +{ + int rc = 0; + int num_found = 0; + + rc = iscsi_sysfs_for_each_flashnode(NULL, host_no, &num_found, + flashnode_info_print_flat); + + if (!num_found) { + log_error("No flashnodes attached to host %u.", host_no); + rc = ISCSI_ERR_NO_OBJS_FOUND; + } + + return rc; +} + +int iscsi_set_flashnode_params(struct iscsi_transport *t, uint32_t host_no, + uint32_t flashnode_idx, struct list_head *params) +{ + struct flashnode_rec fnode; + recinfo_t *flashnode_info; + struct user_param *param; + struct iovec *iovs = NULL; + struct iovec *iov = NULL; + int fd, rc = 0; + int param_count = 0; + int param_used = 0; + int i; + + flashnode_info = idbm_recinfo_alloc(MAX_KEYS); + if (!flashnode_info) { + log_error("Out of Memory."); + rc = ISCSI_ERR_NOMEM; + goto free_info_rec; + } + + memset(&fnode, 0, sizeof(fnode)); + rc = iscsi_sysfs_get_flashnode_info(&fnode, host_no, flashnode_idx); + if (rc) { + log_error("Could not read info for flashnode %u, %s", + flashnode_idx, strerror(rc)); + goto free_info_rec; + } + + idbm_recinfo_flashnode(&fnode, flashnode_info); + + i = 0; + list_for_each_entry(param, params, list) { + param_count++; + rc = idbm_verify_param(flashnode_info, param->name); + if (rc) + goto free_info_rec; + } + + list_for_each_entry(param, params, list) { + rc = idbm_rec_update_param(flashnode_info, param->name, + param->value, 0); + if (rc) + goto free_info_rec; + } + + /* +2 for event and nlmsghdr */ + param_count += 2; + iovs = calloc((param_count * sizeof(struct iovec)), + sizeof(char)); + if (!iovs) { + log_error("Out of Memory."); + rc = ISCSI_ERR_NOMEM; + goto free_info_rec; + } + + /* param_used gives actual number of iovecs used for flashnode */ + param_used = flashnode_build_config(params, &fnode, iovs); + if (!param_used) { + log_error("Build flashnode config failed."); + rc = ISCSI_ERR; + goto free_iovec; + } + + fd = ipc->ctldev_open(); + if (fd < 0) { + log_error("Netlink open failed."); + rc = ISCSI_ERR_INTERNAL; + goto free_iovec; + } + + log_info("Update flashnode %u.", flashnode_idx); + rc = ipc->set_flash_node_params(t->handle, host_no, flashnode_idx, + iovs, param_count); + if (rc < 0) + rc = ISCSI_ERR; + + + ipc->ctldev_close(); + +free_iovec: + /* start at 2, because 0 is for nlmsghdr and 1 for event */ + iov = iovs + 2; + for (i = 0; i < param_used; i++, iov++) { + if (iov->iov_base) + free(iov->iov_base); + } + + free(iovs); + +free_info_rec: + if (flashnode_info) + free(flashnode_info); + + return rc; +} + +int iscsi_new_flashnode(struct iscsi_transport *t, uint32_t host_no, char *val, + uint32_t *flashnode_idx) +{ + int fd, rc = 0; + + fd = ipc->ctldev_open(); + if (fd < 0) { + log_error("Netlink open failed."); + rc = ISCSI_ERR_INTERNAL; + goto exit_new_flashnode; + } + + log_info("Create new flashnode for host %u.", host_no); + rc = ipc->new_flash_node(t->handle, host_no, val, flashnode_idx); + if (rc < 0) + rc = ISCSI_ERR; + + ipc->ctldev_close(); + +exit_new_flashnode: + return rc; +} + +int iscsi_del_flashnode(struct iscsi_transport *t, uint32_t host_no, + uint32_t flashnode_idx) +{ + int fd, rc = 0; + + fd = ipc->ctldev_open(); + if (fd < 0) { + log_error("Netlink open failed."); + rc = ISCSI_ERR_INTERNAL; + goto exit_del_flashnode; + } + + log_info("Delete flashnode %u.", flashnode_idx); + rc = ipc->del_flash_node(t->handle, host_no, flashnode_idx); + if (rc < 0) + rc = ISCSI_ERR; + + ipc->ctldev_close(); + +exit_del_flashnode: + return rc; +} + +int iscsi_login_flashnode(struct iscsi_transport *t, uint32_t host_no, + uint32_t flashnode_idx) +{ + int fd, rc = 0; + + fd = ipc->ctldev_open(); + if (fd < 0) { + log_error("Netlink open failed."); + rc = ISCSI_ERR_INTERNAL; + goto exit_login_flashnode; + } + + log_info("Login to flashnode %u.", flashnode_idx); + rc = ipc->login_flash_node(t->handle, host_no, flashnode_idx); + if (rc == -EPERM) + rc = ISCSI_ERR_SESS_EXISTS; + else if (rc < 0) + rc = ISCSI_ERR_LOGIN; + + ipc->ctldev_close(); + +exit_login_flashnode: + return rc; +} + +int iscsi_logout_flashnode(struct iscsi_transport *t, uint32_t host_no, + uint32_t flashnode_idx) +{ + int fd, rc = 0; + + fd = ipc->ctldev_open(); + if (fd < 0) { + log_error("Netlink open failed."); + rc = ISCSI_ERR_INTERNAL; + goto exit_logout; + } + + log_info("Logout flashnode %u.", flashnode_idx); + rc = ipc->logout_flash_node(t->handle, host_no, flashnode_idx); + if (rc == -ESRCH) + rc = ISCSI_ERR_SESS_NOT_FOUND; + else if (rc < 0) + rc = ISCSI_ERR_LOGOUT; + + ipc->ctldev_close(); + +exit_logout: + return rc; +} + +int iscsi_logout_flashnode_sid(struct iscsi_transport *t, uint32_t host_no, + uint32_t sid) +{ + int fd, rc = 0; + + fd = ipc->ctldev_open(); + if (fd < 0) { + log_error("Netlink open failed."); + rc = ISCSI_ERR_INTERNAL; + goto exit_logout_sid; + } + + log_info("Logout sid %u.", sid); + rc = ipc->logout_flash_node_sid(t->handle, host_no, sid); + if (rc < 0) { + log_error("Logout of sid %u failed.", sid); + rc = ISCSI_ERR_LOGOUT; + } else { + log_info("Logout of sid %u successful.", sid); + } + + ipc->ctldev_close(); + +exit_logout_sid: + return rc; +} + +static int exec_flashnode_op(int op, int info_level, uint32_t host_no, + uint64_t fnode_idx, int type, + struct list_head *params) +{ + struct iscsi_transport *t = NULL; + int rc = ISCSI_SUCCESS; + char *portal_type; + uint32_t flashnode_idx; + + if (op != OP_SHOW && op != OP_NOOP && op != OP_NEW && + fnode_idx > MAX_FLASHNODE_IDX) { + log_error("Invalid flashnode index"); + rc = ISCSI_ERR_INVAL; + goto exit_flashnode_op; + } + + flashnode_idx = (uint32_t)fnode_idx; + t = iscsi_sysfs_get_transport_by_hba(host_no); + if (!t) { + log_error("Could not match hostno %u to transport.", host_no); + rc = ISCSI_ERR_TRANS_NOT_FOUND; + goto exit_flashnode_op; + } + + switch (op) { + case OP_NOOP: + case OP_SHOW: + if (fnode_idx > MAX_FLASHNODE_IDX) + rc = list_flashnodes(info_level, host_no); + else + rc = get_flashnode_info(host_no, flashnode_idx); + break; + case OP_NEW: + if (type == IPV4) { + portal_type = "ipv4"; + } else if (type == IPV6) { + portal_type = "ipv6"; + } else { + log_error("Invalid type mentioned for flashnode"); + rc = ISCSI_ERR_INVAL; + goto exit_flashnode_op; + } + rc = iscsi_new_flashnode(t, host_no, portal_type, + &flashnode_idx); + if (!rc) + log_info("New flashnode for host %u added at index %u.", + host_no, flashnode_idx); + else + log_error("Creation of flashnode for host %u failed.", + host_no); + break; + case OP_DELETE: + rc = iscsi_del_flashnode(t, host_no, flashnode_idx); + if (!rc) + log_info("Flashnode %u of host %u deleted.", + flashnode_idx, host_no); + else + log_error("Deletion of flashnode %u of host %u failed.", + flashnode_idx, host_no); + break; + case OP_UPDATE: + rc = iscsi_set_flashnode_params(t, host_no, flashnode_idx, + params); + if (!rc) + log_info("Update for flashnode %u of host %u successful.", + flashnode_idx, host_no); + else + log_error("Update for flashnode %u of host %u failed.", + flashnode_idx, host_no); + break; + case OP_LOGIN: + rc = iscsi_login_flashnode(t, host_no, flashnode_idx); + if (!rc) + log_info("Login to flashnode %u of host %u successful.", + flashnode_idx, host_no); + else if (rc == ISCSI_ERR_SESS_EXISTS) + log_info("Flashnode %u of host %u already logged in.", + flashnode_idx, host_no); + else + log_error("Login to flashnode %u of host %u failed.", + flashnode_idx, host_no); + break; + case OP_LOGOUT: + rc = iscsi_logout_flashnode(t, host_no, flashnode_idx); + if (!rc) + log_info("Logout of flashnode %u of host %u successful.", + flashnode_idx, host_no); + else if (rc == ISCSI_ERR_SESS_NOT_FOUND) + log_info("Flashnode %u of host %u not logged in.", + flashnode_idx, host_no); + else + log_error("Logout of flashnode %u of host %u failed.", + flashnode_idx, host_no); + break; + default: + log_error("Invalid operation"); + rc = ISCSI_ERR_INVAL; + break; + } + +exit_flashnode_op: + return rc; +} + +static void print_host_stats(struct iscsi_offload_host_stats *host_stats) +{ + /* MAC */ + printf("Host Statistics:\n" + "\tmactx_frames: %lld\n" + "\tmactx_bytes: %lld\n" + "\tmactx_multicast_frames: %lld\n" + "\tmactx_broadcast_frames: %lld\n" + "\tmactx_pause_frames: %lld\n" + "\tmactx_control_frames: %lld\n" + "\tmactx_deferral: %lld\n" + "\tmactx_excess_deferral: %lld\n" + "\tmactx_late_collision: %lld\n" + "\tmactx_abort: %lld\n" + "\tmactx_single_collision: %lld\n" + "\tmactx_multiple_collision: %lld\n" + "\tmactx_collision: %lld\n" + "\tmactx_frames_dropped: %lld\n" + "\tmactx_jumbo_frames: %lld\n" + "\tmacrx_frames: %lld\n" + "\tmacrx_bytes: %lld\n" + "\tmacrx_unknown_control_frames: %lld\n" + "\tmacrx_pause_frames: %lld\n" + "\tmacrx_control_frames: %lld\n" + "\tmacrx_dribble: %lld\n" + "\tmacrx_frame_length_error: %lld\n" + "\tmacrx_jabber: %lld\n" + "\tmacrx_carrier_sense_error: %lld\n" + "\tmacrx_frame_discarded: %lld\n" + "\tmacrx_frames_dropped: %lld\n" + "\tmac_crc_error: %lld\n" + "\tmac_encoding_error: %lld\n" + "\tmacrx_length_error_large: %lld\n" + "\tmacrx_length_error_small: %lld\n" + "\tmacrx_multicast_frames: %lld\n" + "\tmacrx_broadcast_frames: %lld\n" + /* IP */ + "\tiptx_packets: %lld\n" + "\tiptx_bytes: %lld\n" + "\tiptx_fragments: %lld\n" + "\tiprx_packets: %lld\n" + "\tiprx_bytes: %lld\n" + "\tiprx_fragments: %lld\n" + "\tip_datagram_reassembly: %lld\n" + "\tip_invalid_address_error: %lld\n" + "\tip_error_packets: %lld\n" + "\tip_fragrx_overlap: %lld\n" + "\tip_fragrx_outoforder: %lld\n" + "\tip_datagram_reassembly_timeout: %lld\n" + "\tipv6tx_packets: %lld\n" + "\tipv6tx_bytes: %lld\n" + "\tipv6tx_fragments: %lld\n" + "\tipv6rx_packets: %lld\n" + "\tipv6rx_bytes: %lld\n" + "\tipv6rx_fragments: %lld\n" + "\tipv6_datagram_reassembly: %lld\n" + "\tipv6_invalid_address_error: %lld\n" + "\tipv6_error_packets: %lld\n" + "\tipv6_fragrx_overlap: %lld\n" + "\tipv6_fragrx_outoforder: %lld\n" + "\tipv6_datagram_reassembly_timeout: %lld\n" + /* TCP */ + "\ttcptx_segments: %lld\n" + "\ttcptx_bytes: %lld\n" + "\ttcprx_segments: %lld\n" + "\ttcprx_byte: %lld\n" + "\ttcp_duplicate_ack_retx: %lld\n" + "\ttcp_retx_timer_expired: %lld\n" + "\ttcprx_duplicate_ack: %lld\n" + "\ttcprx_pure_ackr: %lld\n" + "\ttcptx_delayed_ack: %lld\n" + "\ttcptx_pure_ack: %lld\n" + "\ttcprx_segment_error: %lld\n" + "\ttcprx_segment_outoforder: %lld\n" + "\ttcprx_window_probe: %lld\n" + "\ttcprx_window_update: %lld\n" + "\ttcptx_window_probe_persist: %lld\n" + /* ECC */ + "\tecc_error_correction: %lld\n" + /* iSCSI */ + "\tiscsi_pdu_tx: %lld\n" + "\tiscsi_data_bytes_tx: %lld\n" + "\tiscsi_pdu_rx: %lld\n" + "\tiscsi_data_bytes_rx: %lld\n" + "\tiscsi_io_completed: %lld\n" + "\tiscsi_unexpected_io_rx: %lld\n" + "\tiscsi_format_error: %lld\n" + "\tiscsi_hdr_digest_error: %lld\n" + "\tiscsi_data_digest_error: %lld\n" + "\tiscsi_sequence_error: %lld\n", + /* MAC */ + (unsigned long long)host_stats->mactx_frames, + (unsigned long long)host_stats->mactx_bytes, + (unsigned long long)host_stats->mactx_multicast_frames, + (unsigned long long)host_stats->mactx_broadcast_frames, + (unsigned long long)host_stats->mactx_pause_frames, + (unsigned long long)host_stats->mactx_control_frames, + (unsigned long long)host_stats->mactx_deferral, + (unsigned long long)host_stats->mactx_excess_deferral, + (unsigned long long)host_stats->mactx_late_collision, + (unsigned long long)host_stats->mactx_abort, + (unsigned long long)host_stats->mactx_single_collision, + (unsigned long long)host_stats->mactx_multiple_collision, + (unsigned long long)host_stats->mactx_collision, + (unsigned long long)host_stats->mactx_frames_dropped, + (unsigned long long)host_stats->mactx_jumbo_frames, + (unsigned long long)host_stats->macrx_frames, + (unsigned long long)host_stats->macrx_bytes, + (unsigned long long)host_stats->macrx_unknown_control_frames, + (unsigned long long)host_stats->macrx_pause_frames, + (unsigned long long)host_stats->macrx_control_frames, + (unsigned long long)host_stats->macrx_dribble, + (unsigned long long)host_stats->macrx_frame_length_error, + (unsigned long long)host_stats->macrx_jabber, + (unsigned long long)host_stats->macrx_carrier_sense_error, + (unsigned long long)host_stats->macrx_frame_discarded, + (unsigned long long)host_stats->macrx_frames_dropped, + (unsigned long long)host_stats->mac_crc_error, + (unsigned long long)host_stats->mac_encoding_error, + (unsigned long long)host_stats->macrx_length_error_large, + (unsigned long long)host_stats->macrx_length_error_small, + (unsigned long long)host_stats->macrx_multicast_frames, + (unsigned long long)host_stats->macrx_broadcast_frames, + /* IP */ + (unsigned long long)host_stats->iptx_packets, + (unsigned long long)host_stats->iptx_bytes, + (unsigned long long)host_stats->iptx_fragments, + (unsigned long long)host_stats->iprx_packets, + (unsigned long long)host_stats->iprx_bytes, + (unsigned long long)host_stats->iprx_fragments, + (unsigned long long)host_stats->ip_datagram_reassembly, + (unsigned long long)host_stats->ip_invalid_address_error, + (unsigned long long)host_stats->ip_error_packets, + (unsigned long long)host_stats->ip_fragrx_overlap, + (unsigned long long)host_stats->ip_fragrx_outoforder, + (unsigned long long)host_stats->ip_datagram_reassembly_timeout, + (unsigned long long)host_stats->ipv6tx_packets, + (unsigned long long)host_stats->ipv6tx_bytes, + (unsigned long long)host_stats->ipv6tx_fragments, + (unsigned long long)host_stats->ipv6rx_packets, + (unsigned long long)host_stats->ipv6rx_bytes, + (unsigned long long)host_stats->ipv6rx_fragments, + (unsigned long long)host_stats->ipv6_datagram_reassembly, + (unsigned long long)host_stats->ipv6_invalid_address_error, + (unsigned long long)host_stats->ipv6_error_packets, + (unsigned long long)host_stats->ipv6_fragrx_overlap, + (unsigned long long)host_stats->ipv6_fragrx_outoforder, + (unsigned long long)host_stats->ipv6_datagram_reassembly_timeout, + /* TCP */ + (unsigned long long)host_stats->tcptx_segments, + (unsigned long long)host_stats->tcptx_bytes, + (unsigned long long)host_stats->tcprx_segments, + (unsigned long long)host_stats->tcprx_byte, + (unsigned long long)host_stats->tcp_duplicate_ack_retx, + (unsigned long long)host_stats->tcp_retx_timer_expired, + (unsigned long long)host_stats->tcprx_duplicate_ack, + (unsigned long long)host_stats->tcprx_pure_ackr, + (unsigned long long)host_stats->tcptx_delayed_ack, + (unsigned long long)host_stats->tcptx_pure_ack, + (unsigned long long)host_stats->tcprx_segment_error, + (unsigned long long)host_stats->tcprx_segment_outoforder, + (unsigned long long)host_stats->tcprx_window_probe, + (unsigned long long)host_stats->tcprx_window_update, + (unsigned long long)host_stats->tcptx_window_probe_persist, + /* ECC */ + (unsigned long long)host_stats->ecc_error_correction, + /* iSCSI */ + (unsigned long long)host_stats->iscsi_pdu_tx, + (unsigned long long)host_stats->iscsi_data_bytes_tx, + (unsigned long long)host_stats->iscsi_pdu_rx, + (unsigned long long)host_stats->iscsi_data_bytes_rx, + (unsigned long long)host_stats->iscsi_io_completed, + (unsigned long long)host_stats->iscsi_unexpected_io_rx, + (unsigned long long)host_stats->iscsi_format_error, + (unsigned long long)host_stats->iscsi_hdr_digest_error, + (unsigned long long)host_stats->iscsi_data_digest_error, + (unsigned long long)host_stats->iscsi_sequence_error); +} + +static int exec_host_stats_op(int op, int info_level, uint32_t host_no) +{ + struct iscsi_transport *t = NULL; + char *req_buf = NULL; + int rc = ISCSI_SUCCESS; + int fd = 0, buf_size = 0; + + t = iscsi_sysfs_get_transport_by_hba(host_no); + if (!t) { + log_error("Could not match hostno %u to transport.", host_no); + rc = ISCSI_ERR_TRANS_NOT_FOUND; + goto exit_host_stats; + } + + buf_size = sizeof(struct iscsi_offload_host_stats) + + sizeof(struct iscsi_uevent); + req_buf = calloc(1, buf_size); + if (!req_buf) { + log_error("Could not allocate memory for host stats request."); + rc = ISCSI_ERR_NOMEM; + goto exit_host_stats; + } + + fd = ipc->ctldev_open(); + if (fd < 0) { + rc = ISCSI_ERR_INTERNAL; + log_error("Netlink open failed."); + goto exit_host_stats; + } + + rc = ipc->get_host_stats(t->handle, host_no, req_buf); + if (rc < 0) { + log_error("get_host_stats failed. errno=%d", errno); + rc = ISCSI_ERR; + goto exit_host_stats; + } + + print_host_stats((struct iscsi_offload_host_stats *)(req_buf + + sizeof(struct iscsi_uevent))); + + ipc->ctldev_close(); + +exit_host_stats: + free(req_buf); + return rc; +} + static int verify_iface_params(struct list_head *params, struct node_rec *rec) { struct user_param *param; @@ -1473,7 +2290,7 @@ static int verify_iface_params(struct list_head *params, struct node_rec *rec) /* TODO: merge iter helpers and clean them up, so we can use them here */ static int exec_iface_op(int op, int do_show, int info_level, - struct iface_rec *iface, uint32_t host_no, + struct iface_rec *iface, uint64_t host_no, struct list_head *params) { struct host_info hinfo; @@ -1594,9 +2411,9 @@ update_fail: printf("%s applied.\n", iface->name); break; case OP_APPLY_ALL: - if (host_no == -1) { - log_error("Applyall requires a host number or MAC " - "passed in with the --host argument."); + if (host_no > MAX_HOST_NO) { + log_error("Applyall requires a valid host number or MAC" + " passed in with the --host argument."); rc = ISCSI_ERR_INVAL; break; } @@ -1607,7 +2424,7 @@ update_fail: memset(&hinfo, 0, sizeof(struct host_info)); hinfo.host_no = host_no; if (iscsi_sysfs_get_hostinfo_by_host_no(&hinfo)) { - log_error("Could not match host%u to ifaces.", host_no); + log_error("Could not match host%lu to ifaces.", host_no); rc = ISCSI_ERR_INVAL; break; } @@ -1618,7 +2435,7 @@ update_fail: break; } - printf("Applied settings to ifaces attached to host%u.\n", + printf("Applied settings to ifaces attached to host%lu.\n", host_no); break; default: @@ -1711,12 +2528,12 @@ static int exec_node_op(int op, int do_login, int do_logout, } if (do_rescan) { - rc = for_each_session(rec, rescan_portal); + rc = for_each_session(rec, rescan_portal, 1); goto out; } if (do_stats) { - rc = for_each_session(rec, session_stats); + rc = for_each_session(rec, session_stats, 0); goto out; } @@ -1832,7 +2649,7 @@ static int exec_fw_disc_op(discovery_rec_t *drec, struct list_head *ifaces, rc = fw_get_targets(&targets); if (rc) { log_error("Could not get list of targets from firmware. " - "(err %d)\n", rc); + "(err %d)", rc); return rc; } rc = iface_create_ifaces_from_boot_contexts(&new_ifaces, &targets); @@ -1845,7 +2662,7 @@ discover_fw_tgts: rc = idbm_bind_ifaces_to_nodes(discovery_fw, drec, ifaces, &rec_list); if (rc) - log_error("Could not perform fw discovery.\n"); + log_error("Could not perform fw discovery."); else rc = exec_disc_op_on_recs(drec, &rec_list, info_level, do_login, op); @@ -1883,7 +2700,7 @@ static int exec_fw_op(discovery_rec_t *drec, struct list_head *ifaces, rc = fw_get_targets(&targets); if (rc) { log_error("Could not get list of targets from firmware. " - "(err %d)\n", rc); + "(err %d)", rc); return rc; } @@ -1892,7 +2709,7 @@ static int exec_fw_op(discovery_rec_t *drec, struct list_head *ifaces, rec = idbm_create_rec_from_boot_context(context); if (!rec) { log_error("Could not convert firmware info to " - "node record.\n"); + "node record."); rc = ISCSI_ERR_NOMEM; break; } @@ -1999,16 +2816,10 @@ static int exec_discover(int disc_type, char *ip, int port, rc = 0; switch (disc_type) { case DISCOVERY_TYPE_SENDTARGETS: - /* - * idbm_add_discovery call above handles drec syncing so - * we always pass in 0 here. - */ - rc = do_sendtargets(drec, ifaces, info_level, do_login, op, + case DISCOVERY_TYPE_ISNS: + rc = do_target_discovery(drec, ifaces, info_level, do_login, op, 0); break; - case DISCOVERY_TYPE_ISNS: - rc = do_isns(drec, ifaces, info_level, do_login, op); - break; default: log_error("Unsupported discovery type."); break; @@ -2140,8 +2951,7 @@ static int exec_disc_op(int disc_type, char *ip, int port, idbm_sendtargets_defaults(&drec.u.sendtargets); strlcpy(drec.address, ip, sizeof(drec.address)); drec.port = port; - - rc = do_sendtargets(&drec, ifaces, info_level, + rc = do_target_discovery(&drec, ifaces, info_level, do_login, op, 1); if (rc) goto done; @@ -2164,7 +2974,9 @@ static int exec_disc_op(int disc_type, char *ip, int port, else drec.port = port; - rc = do_isns(&drec, ifaces, info_level, do_login, op); + drec.type = DISCOVERY_TYPE_ISNS; + rc = do_target_discovery(&drec, ifaces, info_level, + do_login, op, 0); if (rc) goto done; break; @@ -2195,8 +3007,9 @@ static int exec_disc_op(int disc_type, char *ip, int port, } if ((do_discover || do_login) && drec.type == DISCOVERY_TYPE_SENDTARGETS) { - rc = do_sendtargets(&drec, ifaces, info_level, - do_login, op, 0); + rc = do_target_discovery(&drec, ifaces, + info_level, do_login, + op, 0); } else if (op == OP_NOOP || op == OP_SHOW) { if (!idbm_print_discovery_info(&drec, do_show)) { @@ -2234,10 +3047,10 @@ done: return rc; } -static uint32_t parse_host_info(char *optarg, int *rc) +static uint64_t parse_host_info(char *optarg, int *rc) { int err = 0; - uint32_t host_no = -1; + uint64_t host_no; *rc = 0; if (strstr(optarg, ":")) { @@ -2250,8 +3063,11 @@ static uint32_t parse_host_info(char *optarg, int *rc) *rc = ISCSI_ERR_INVAL; } } else { - host_no = strtoul(optarg, NULL, 10); - if (errno) { + host_no = strtoull(optarg, NULL, 10); + if (errno || (host_no > MAX_HOST_NO)) { + if (host_no > MAX_HOST_NO) + errno = ERANGE; + log_error("Invalid host no %s. %s.", optarg, strerror(errno)); *rc = ISCSI_ERR_INVAL; @@ -2286,7 +3102,7 @@ static char *iscsi_ping_stat_strs[] = { static char *iscsi_ping_stat_to_str(uint32_t status) { if (status < 0 || status > ISCSI_PING_NO_ARP_RECEIVED) { - log_error("Invalid ping status %u\n", status); + log_error("Invalid ping status %u", status); return NULL; } @@ -2403,12 +3219,14 @@ main(int argc, char **argv) int tpgt = PORTAL_GROUP_TAG_UNKNOWN, killiscsid=-1, do_show=0; int packet_size=32, ping_count=1, ping_interval=0; int do_discover = 0, sub_mode = -1; + int portal_type = -1; struct sigaction sa_old; struct sigaction sa_new; struct list_head ifaces; struct iface_rec *iface = NULL, *tmp; struct node_rec *rec = NULL; - uint32_t host_no = -1; + uint64_t host_no = (uint64_t)MAX_HOST_NO + 1; + uint64_t index = ULLONG_MAX; struct user_param *param; struct list_head params; @@ -2551,6 +3369,18 @@ main(int argc, char **argv) printf("%s version %s\n", program_name, ISCSI_VERSION_STR); return 0; + case 'x': + index = strtoull(optarg, NULL, 10); + if (errno) { + log_error("Invalid index %s. %s.", + optarg, strerror(errno)); + rc = ISCSI_ERR_INVAL; + goto free_ifaces; + } + break; + case 'A': + portal_type = str_to_portal_type(optarg); + break; case 'h': usage(0); } @@ -2583,7 +3413,7 @@ main(int argc, char **argv) usage(ISCSI_ERR_INVAL); if (mode == MODE_FW) { - if ((rc = verify_mode_params(argc, argv, "ml", 0))) { + if ((rc = verify_mode_params(argc, argv, "dml", 0))) { log_error("fw mode: option '-%c' is not " "allowed/supported", rc); rc = ISCSI_ERR_INVAL; @@ -2603,7 +3433,7 @@ main(int argc, char **argv) switch (mode) { case MODE_HOST: - if ((rc = verify_mode_params(argc, argv, "CHdmPov", 0))) { + if ((rc = verify_mode_params(argc, argv, "CHdmPotnvxA", 0))) { log_error("host mode: option '-%c' is not " "allowed/supported", rc); rc = ISCSI_ERR_INVAL; @@ -2612,15 +3442,44 @@ main(int argc, char **argv) if (sub_mode != -1) { switch (sub_mode) { case MODE_CHAP: - if (!op || !host_no) { + if (!op || (host_no > MAX_HOST_NO)) { log_error("CHAP mode requires host " "no and valid operation"); rc = ISCSI_ERR_INVAL; break; } + + if (index == ULLONG_MAX) + index = (uint64_t)MAX_CHAP_ENTRIES + 1; + rc = exec_host_chap_op(op, info_level, host_no, - value); + index, ¶ms); break; + case MODE_FLASHNODE: + if (host_no > MAX_HOST_NO) { + log_error("FLASHNODE mode requires host no"); + rc = ISCSI_ERR_INVAL; + break; + } + + if (index == ULLONG_MAX) + index = (uint64_t)MAX_FLASHNODE_IDX + 1; + + rc = exec_flashnode_op(op, info_level, host_no, + index, portal_type, + ¶ms); + break; + case MODE_HOST_STATS: + if (host_no > MAX_HOST_NO) { + log_error("STATS mode requires host no"); + rc = ISCSI_ERR_INVAL; + break; + } + + rc = exec_host_stats_op(op, info_level, + host_no); + break; + default: log_error("Invalid Sub Mode"); break; diff --git a/usr/iscsid.c b/usr/iscsid.c index b4bb65b..f8ffd23 100644 --- a/usr/iscsid.c +++ b/usr/iscsid.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -111,9 +112,7 @@ setup_rec_from_negotiated_values(node_rec_t *rec, struct session_info *info) strlcpy(rec->name, info->targetname, TARGET_NAME_MAXLEN); rec->conn[0].port = info->persistent_port; strlcpy(rec->conn[0].address, info->persistent_address, NI_MAXHOST); - memcpy(&rec->iface, &info->iface, sizeof(struct iface_rec)); rec->tpgt = info->tpgt; - iface_copy(&rec->iface, &info->iface); iscsi_sysfs_get_negotiated_session_conf(info->sid, &session_conf); iscsi_sysfs_get_negotiated_conn_conf(info->sid, &conn_conf); @@ -194,7 +193,7 @@ static int sync_session(void *data, struct session_info *info) struct iscsi_transport *t; int rc, retries = 0; - log_debug(7, "sync session [%d][%s,%s.%d][%s]\n", info->sid, + log_debug(7, "sync session [%d][%s,%s.%d][%s]", info->sid, info->targetname, info->persistent_address, info->port, info->iface.hwaddress); @@ -236,8 +235,9 @@ static int sync_session(void *data, struct session_info *info) info->persistent_address, info->persistent_port, &info->iface)) { log_warning("Could not read data from db. Using default and " - "currently negotiated values\n"); + "currently negotiated values"); setup_rec_from_negotiated_values(&rec, info); + iface_copy(&rec.iface, &info->iface); } else { /* * we have a valid record and iface so lets merge @@ -251,13 +251,12 @@ static int sync_session(void *data, struct session_info *info) memset(&sysfsrec, 0, sizeof(node_rec_t)); setup_rec_from_negotiated_values(&sysfsrec, info); /* - * target, portal and iface name values have to be the same + * target, portal and iface values have to be the same * or we would not have found the record, so just copy - * CHAP and iface settings. + * CHAP settings. */ memcpy(&rec.session.auth, &sysfsrec.session.auth, sizeof(struct iscsi_auth_config)); - memcpy(&rec.iface, &info->iface, sizeof(rec.iface)); } /* multiple drivers could be connected to the same portal */ @@ -333,7 +332,7 @@ static void missing_iname_warn(char *initiatorname_file) "iqn.yyyy-mm.[:identifier].\n\n" "Example: InitiatorName=iqn.2001-04.com.redhat:fc6.\n" "If using hardware iscsi like qla4xxx this message can be " - "ignored.\n", initiatorname_file, initiatorname_file); + "ignored.", initiatorname_file, initiatorname_file); } int main(int argc, char *argv[]) @@ -342,6 +341,7 @@ int main(int argc, char *argv[]) char *config_file = CONFIG_FILE; char *initiatorname_file = INITIATOR_NAME_FILE; char *pid_file = PID_FILE; + char *safe_logout; int ch, longindex; uid_t uid = 0; struct sigaction sa_old; @@ -477,11 +477,25 @@ int main(int argc, char *argv[]) } } - if (uid && setuid(uid) < 0) - perror("setuid\n"); + if (gid && setgid(gid) < 0) { + log_error("Unable to setgid to %d", gid); + log_close(log_pid); + exit(ISCSI_ERR); + } - if (gid && setgid(gid) < 0) - perror("setgid\n"); + if ((geteuid() == 0) && (getgroups(0, NULL))) { + if (setgroups(0, NULL) != 0) { + log_error("Unable to drop supplementary group ids"); + log_close(log_pid); + exit(ISCSI_ERR); + } + } + + if (uid && setuid(uid) < 0) { + log_error("Unable to setuid to %d", uid); + log_close(log_pid); + exit(ISCSI_ERR); + } memset(&daemon_config, 0, sizeof (daemon_config)); daemon_config.pid_file = pid_file; @@ -507,11 +521,17 @@ int main(int argc, char *argv[]) daemon_config.initiator_name : "NOT SET"); log_debug(1, "InitiatorAlias=%s", daemon_config.initiator_alias); + safe_logout = cfg_get_string_param(config_file, "iscsid.safe_logout"); + if (safe_logout && !strcmp(safe_logout, "Yes")) + daemon_config.safe_logout = 1; + free(safe_logout); + pid = fork(); if (pid == 0) { int nr_found = 0; /* child */ - iscsi_sysfs_for_each_session(NULL, &nr_found, sync_session); + /* TODO - test with async support enabled */ + iscsi_sysfs_for_each_session(NULL, &nr_found, sync_session, 0); exit(0); } else if (pid < 0) { log_error("Fork failed error %d: existing sessions" @@ -534,7 +554,6 @@ int main(int argc, char *argv[]) exit(ISCSI_ERR); } - actor_init(); event_loop(ipc, control_fd, mgmt_ipc_fd); idbm_terminate(); diff --git a/usr/iscsid.h b/usr/iscsid.h index 15f264f..b9f3d54 100644 --- a/usr/iscsid.h +++ b/usr/iscsid.h @@ -29,6 +29,7 @@ struct iscsi_daemon_config { char *pid_file; char *initiator_name; char *initiator_alias; + int safe_logout; }; extern struct iscsi_daemon_config *dconfig; diff --git a/usr/iscsid_req.c b/usr/iscsid_req.c index 0902011..75bcf22 100644 --- a/usr/iscsid_req.c +++ b/usr/iscsid_req.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,7 @@ #include "iscsi_util.h" #include "config.h" #include "iscsi_err.h" +#include "uip_mgmt_ipc.h" static void iscsid_startup(void) { @@ -54,9 +56,9 @@ static void iscsid_startup(void) #define MAXSLEEP 128 -static int iscsid_connect(int *fd, int start_iscsid) +static int ipc_connect(int *fd, char *unix_sock_name, int start_iscsid) { - int nsec; + int nsec, addr_len; struct sockaddr_un addr; *fd = socket(AF_LOCAL, SOCK_STREAM, 0); @@ -65,15 +67,13 @@ static int iscsid_connect(int *fd, int start_iscsid) return ISCSI_ERR_ISCSID_NOTCONN; } - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_LOCAL; - memcpy((char *) &addr.sun_path + 1, ISCSIADM_NAMESPACE, - strlen(ISCSIADM_NAMESPACE)); + addr_len = setup_abstract_addr(&addr, unix_sock_name); + /* * Trying to connect with exponential backoff */ for (nsec = 1; nsec <= MAXSLEEP; nsec <<= 1) { - if (connect(*fd, (struct sockaddr *) &addr, sizeof(addr)) == 0) + if (connect(*fd, (struct sockaddr *) &addr, addr_len) == 0) /* Connection established */ return ISCSI_SUCCESS; @@ -96,6 +96,11 @@ static int iscsid_connect(int *fd, int start_iscsid) return ISCSI_ERR_ISCSID_NOTCONN; } +static int iscsid_connect(int *fd, int start_iscsid) +{ + return ipc_connect(fd, ISCSIADM_NAMESPACE, start_iscsid); +} + int iscsid_request(int *fd, iscsiadm_req_t *req, int start_iscsid) { int err; @@ -192,3 +197,82 @@ int iscsid_req_by_sid(iscsiadm_cmd_e cmd, int sid) return err; return iscsid_req_wait(cmd, fd); } + +static int uip_connect(int *fd) +{ + return ipc_connect(fd, ISCSID_UIP_NAMESPACE, 0); +} + +int uip_broadcast(void *buf, size_t buf_len) +{ + int err; + int fd; + iscsid_uip_rsp_t rsp; + int flags; + int count; + + err = uip_connect(&fd); + if (err) { + log_warning("uIP daemon is not up"); + return err; + } + + log_debug(3, "connected to uIP daemon"); + + /* Send the data to uIP */ + err = write(fd, buf, buf_len); + if (err != buf_len) { + log_error("got write error (%d/%d), daemon died?", + err, errno); + close(fd); + return ISCSI_ERR_ISCSID_COMM_ERR; + } + + log_debug(3, "send iface config to uIP daemon"); + + /* Set the socket to a non-blocking read, this way if there are + * problems waiting for uIP, iscsid can bailout early */ + flags = fcntl(fd, F_GETFL, 0); + if (flags == -1) + flags = 0; + err = fcntl(fd, F_SETFL, flags | O_NONBLOCK); + if (err) { + log_error("could not set uip broadcast to non-blocking: %d", + errno); + close(fd); + return ISCSI_ERR; + } + +#define MAX_UIP_BROADCAST_READ_TRIES 3 + for (count = 0; count < MAX_UIP_BROADCAST_READ_TRIES; count++) { + /* Wait for the response */ + err = read(fd, &rsp, sizeof(rsp)); + if (err == sizeof(rsp)) { + log_debug(3, "Broadcasted to uIP with length: %ld " + "cmd: 0x%x rsp: 0x%x", buf_len, + rsp.command, rsp.err); + err = 0; + break; + } else if ((err == -1) && (errno == EAGAIN)) { + usleep(250000); + continue; + } else { + log_error("Could not read response (%d/%d), daemon " + "died?", err, errno); + err = ISCSI_ERR; + break; + } + } + + if (count == MAX_UIP_BROADCAST_READ_TRIES) { + log_error("Could not broadcast to uIP after %d tries", + count); + err = ISCSI_ERR_AGAIN; + } else if (rsp.err != ISCSID_UIP_MGMT_IPC_DEVICE_UP) { + log_debug(3, "Device is not ready"); + err = ISCSI_ERR_AGAIN; + } + + close(fd); + return err; +} diff --git a/usr/iscsid_req.h b/usr/iscsid_req.h index 68f5256..4fff43d 100644 --- a/usr/iscsid_req.h +++ b/usr/iscsid_req.h @@ -33,4 +33,6 @@ extern int iscsid_req_by_rec(int cmd, struct node_rec *rec); extern int iscsid_req_by_sid_async(int cmd, int sid, int *fd); extern int iscsid_req_by_sid(int cmd, int sid); +extern int uip_broadcast(void *buf, size_t buf_len); + #endif diff --git a/usr/iscsistart.c b/usr/iscsistart.c index 6924d49..7ff2236 100644 --- a/usr/iscsistart.c +++ b/usr/iscsistart.c @@ -126,7 +126,7 @@ static int stop_event_loop(void) rc = iscsid_exec_req(&req, &rsp, 0); if (rc) { iscsi_err_print_msg(rc); - log_error("Could not stop event_loop\n"); + log_error("Could not stop event_loop"); } return rc; } @@ -287,22 +287,22 @@ static void catch_signal(int signo) static int check_params(char *initiatorname) { if (!initiatorname) { - log_error("InitiatorName not set. Exiting %s\n", program_name); + log_error("InitiatorName not set. Exiting %s", program_name); return EINVAL; } if (config_rec.tpgt == PORTAL_GROUP_TAG_UNKNOWN) { - log_error("Portal Group not set. Exiting %s\n", program_name); + log_error("Portal Group not set. Exiting %s", program_name); return EINVAL; } if (!strlen(config_rec.name)) { - log_error("TargetName not set. Exiting %s\n", program_name); + log_error("TargetName not set. Exiting %s", program_name); return EINVAL; } if (!strlen(config_rec.conn[0].address)) { - log_error("IP Address not set. Exiting %s\n", program_name); + log_error("IP Address not set. Exiting %s", program_name); return EINVAL; } @@ -472,7 +472,7 @@ int main(int argc, char *argv[]) mgmt_ipc_fd = mgmt_ipc_listen(); if (mgmt_ipc_fd < 0) { - log_error("Could not setup mgmt ipc\n"); + log_error("Could not setup mgmt ipc"); exit(ISCSI_ERR_NOMEM); } @@ -509,7 +509,6 @@ int main(int argc, char *argv[]) * Start Main Event Loop */ iscsi_initiator_init(); - actor_init(); event_loop(ipc, control_fd, mgmt_ipc_fd); ipc->ctldev_close(); mgmt_ipc_close(mgmt_ipc_fd); diff --git a/usr/login.c b/usr/login.c index db76c80..8289b03 100644 --- a/usr/login.c +++ b/usr/login.c @@ -50,16 +50,14 @@ iscsi_add_text(struct iscsi_hdr *pdu, char *data, int max_data_length, int pdu_length = ntoh24(pdu->dlength); char *text = data; char *end = data + max_data_length; - char *pdu_text; /* find the end of the current text */ text += pdu_length; - pdu_text = text; pdu_length += length; if (text + length >= end) { log_warning("Failed to add login text " - "'%s=%s'\n", param, value); + "'%s=%s'", param, value); return 0; } @@ -170,7 +168,7 @@ resolve_address(char *host, char *port, struct sockaddr_storage *ss) if ((rc = getaddrinfo(host, port, &hints, &res))) { log_error("Cannot resolve host %s. getaddrinfo error: " - "[%s]\n", host, gai_strerror(rc)); + "[%s]", host, gai_strerror(rc)); return rc; } @@ -1558,10 +1556,10 @@ iscsi_login(iscsi_session_t *session, int cid, char *buffer, size_t bufsize, repoll: pfd.revents = 0; ret = poll(&pfd, 1, timeout); - log_debug(7, "%s: Poll return %d\n", __FUNCTION__, ret); + log_debug(7, "%s: Poll return %d", __FUNCTION__, ret); if (iscsi_timer_expired(&connection_timer)) { log_warning("Login response timeout. Waited %d " - "seconds and did not get reponse PDU.\n", + "seconds and did not get reponse PDU.", session->conn[0].active_timeout); c->ret = LOGIN_FAILED; return c->ret; @@ -1595,7 +1593,7 @@ repoll: } } else if (ret < 0) { - log_error("Login poll error.\n"); + log_error("Login poll error."); c->ret = LOGIN_FAILED; return c->ret; } diff --git a/usr/md5.c b/usr/md5.c index 4ef1cb7..ba6c86d 100644 --- a/usr/md5.c +++ b/usr/md5.c @@ -127,7 +127,7 @@ MD5Final(md5byte digest[16], struct MD5Context *ctx) byteSwap(ctx->buf, 4); memcpy(digest, ctx->buf, 16); - memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ + memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ } #ifndef ASM_MD5 diff --git a/usr/mgmt_ipc.c b/usr/mgmt_ipc.c index 5c39c2e..c16bce9 100644 --- a/usr/mgmt_ipc.c +++ b/usr/mgmt_ipc.c @@ -36,28 +36,33 @@ #include "sysdeps.h" #include "iscsi_ipc.h" #include "iscsi_err.h" +#include "iscsi_util.h" #define PEERUSER_MAX 64 #define EXTMSG_MAX (64 * 1024) +#define SD_SOCKET_FDS_START 3 int mgmt_ipc_listen(void) { - int fd, err; + int fd, err, addr_len; struct sockaddr_un addr; + /* first check if we have fd handled by systemd */ + fd = mgmt_ipc_systemd(); + if (fd >= 0) + return fd; + + /* manually establish a socket */ fd = socket(AF_LOCAL, SOCK_STREAM, 0); if (fd < 0) { log_error("Can not create IPC socket"); return fd; } - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_LOCAL; - memcpy((char *) &addr.sun_path + 1, ISCSIADM_NAMESPACE, - strlen(ISCSIADM_NAMESPACE)); + addr_len = setup_abstract_addr(&addr, ISCSIADM_NAMESPACE); - if ((err = bind(fd, (struct sockaddr *) &addr, sizeof(addr))) < 0) { + if ((err = bind(fd, (struct sockaddr *) &addr, addr_len)) < 0 ) { log_error("Can not bind IPC socket"); close(fd); return err; @@ -72,6 +77,28 @@ mgmt_ipc_listen(void) return fd; } +int mgmt_ipc_systemd(void) +{ + const char *env; + + env = getenv("LISTEN_PID"); + + if (!env || (strtoul(env, NULL, 10) != getpid())) + return -EINVAL; + + env = getenv("LISTEN_FDS"); + + if (!env) + return -EINVAL; + + if (strtoul(env, NULL, 10) != 1) { + log_error("Did not receive exactly one IPC socket from systemd"); + return -EINVAL; + } + + return SD_SOCKET_FDS_START; +} + void mgmt_ipc_close(int fd) { @@ -437,7 +464,6 @@ mgmt_ipc_write_rsp(queue_task_t *qtask, int err) qtask->rsp.err = err; if (write(qtask->mgmt_ipc_fd, &qtask->rsp, sizeof(qtask->rsp)) < 0) log_error("IPC qtask write failed: %s", strerror(errno)); - close(qtask->mgmt_ipc_fd); mgmt_ipc_destroy_queue_task(qtask); } diff --git a/usr/mgmt_ipc.h b/usr/mgmt_ipc.h index 7d8ce72..55972ed 100644 --- a/usr/mgmt_ipc.h +++ b/usr/mgmt_ipc.h @@ -112,6 +112,7 @@ typedef int mgmt_ipc_fn_t(struct queue_task *); struct queue_task; void mgmt_ipc_write_rsp(struct queue_task *qtask, int err); int mgmt_ipc_listen(void); +int mgmt_ipc_systemd(void); void mgmt_ipc_close(int fd); void mgmt_ipc_handle(int accept_fd); diff --git a/usr/netlink.c b/usr/netlink.c index c43f686..2b85efe 100644 --- a/usr/netlink.c +++ b/usr/netlink.c @@ -339,6 +339,10 @@ __kipc_call(struct iovec *iovp, int count) } else if (ev->type == ISCSI_UEVENT_GET_CHAP) { /* kget_chap() will read */ return 0; + } else if (ev->type == ISCSI_UEVENT_GET_HOST_STATS) { + /* kget_host_stats() will read */ + return 0; + } else { if ((rc = nlpayload_read(ctrl_fd, (void*)ev, sizeof(*ev), 0)) < 0) { @@ -372,7 +376,7 @@ ksendtargets(uint64_t transport_handle, uint32_t host_no, struct sockaddr *addr) else if (addr->sa_family == PF_INET6) addrlen = sizeof(struct sockaddr_in6); else { - log_error("%s unknown addr family %d\n", + log_error("%s unknown addr family %d", __FUNCTION__, addr->sa_family); return -EINVAL; } @@ -382,7 +386,7 @@ ksendtargets(uint64_t transport_handle, uint32_t host_no, struct sockaddr *addr) iov[1].iov_len = sizeof(*ev) + addrlen; rc = __kipc_call(iov, 2); if (rc < 0) { - log_error("sendtargets failed rc%d\n", rc); + log_error("sendtargets failed rc%d", rc); return rc; } return 0; @@ -674,7 +678,7 @@ kset_host_param(uint64_t transport_handle, uint32_t host_no, sprintf(param_str, "%s", (char *)value); break; default: - log_error("invalid type %d\n", type); + log_error("invalid type %d", type); return -EINVAL; } ev->u.set_host_param.len = len = strlen(param_str) + 1; @@ -712,13 +716,16 @@ kset_param(uint64_t transport_handle, uint32_t sid, uint32_t cid, case ISCSI_INT: sprintf(param_str, "%d", *((int *)value)); break; + case ISCSI_UINT: + sprintf(param_str, "%u", *((unsigned int *)value)); + break; case ISCSI_STRING: if (!strlen(value)) return 0; sprintf(param_str, "%s", (char *)value); break; default: - log_error("invalid type %d\n", type); + log_error("invalid type %d", type); return -EINVAL; } ev->u.set_param.len = len = strlen(param_str) + 1; @@ -866,7 +873,7 @@ ktransport_ep_connect(iscsi_conn_t *conn, int non_blocking) else if (dst_addr->sa_family == PF_INET6) addrlen = sizeof(struct sockaddr_in6); else { - log_error("%s unknown addr family %d\n", + log_error("%s unknown addr family %d", __FUNCTION__, dst_addr->sa_family); return -EINVAL; } @@ -935,7 +942,7 @@ ktransport_ep_disconnect(iscsi_conn_t *conn) iov[1].iov_len = sizeof(ev); rc = __kipc_call(iov, 2); if (rc < 0) { - log_error("connnection %d:%d transport disconnect failed for " + log_error("connection %d:%d transport disconnect failed for " "ep %" PRIu64 " with error %d.", conn->session->id, conn->id, conn->transport_ep_handle, rc); } else @@ -1031,6 +1038,10 @@ static int krecv_conn_state(struct iscsi_conn *conn, uint32_t *state) /* fatal handling error or conn error */ goto exit; + /* unexpected event without a receive context */ + if (!conn->recv_context) + return -EAGAIN; + *state = *(enum iscsi_conn_state *)conn->recv_context->data; ipc_ev_clbk->put_ev_context(conn->recv_context); @@ -1068,7 +1079,7 @@ ksend_ping(uint64_t transport_handle, uint32_t host_no, struct sockaddr *addr, else if (addr->sa_family == PF_INET6) addrlen = sizeof(struct sockaddr_in6); else { - log_error("%s unknown addr family %d\n", + log_error("%s unknown addr family %d", __FUNCTION__, addr->sa_family); return -EINVAL; } @@ -1228,6 +1239,29 @@ static int kget_chap(uint64_t transport_handle, uint32_t host_no, return rc; } +static int kset_chap(uint64_t transport_handle, uint32_t host_no, + struct iovec *iovs, uint32_t param_count) +{ + int rc; + struct iscsi_uevent ev; + struct iovec *iov = iovs + 1; + + log_debug(8, "in %s", __func__); + + ev.type = ISCSI_UEVENT_SET_CHAP; + ev.transport_handle = transport_handle; + ev.u.set_path.host_no = host_no; + + iov->iov_base = &ev; + iov->iov_len = sizeof(ev); + + rc = __kipc_call(iovs, param_count); + if (rc < 0) + return rc; + + return 0; +} + static int kdelete_chap(uint64_t transport_handle, uint32_t host_no, uint16_t chap_tbl_idx) { @@ -1252,6 +1286,211 @@ static int kdelete_chap(uint64_t transport_handle, uint32_t host_no, return rc; } +static int +kset_flashnode_params(uint64_t transport_handle, uint32_t host_no, + uint32_t flashnode_idx, struct iovec *iovs, + uint32_t param_count) +{ + struct iscsi_uevent ev; + int rc, ev_len; + struct iovec *iov = iovs + 1; + + log_debug(8, "in %s", __FUNCTION__); + + ev_len = sizeof(ev); + ev.type = ISCSI_UEVENT_SET_FLASHNODE_PARAMS; + ev.transport_handle = transport_handle; + ev.u.set_flashnode.host_no = host_no; + ev.u.set_flashnode.flashnode_idx = flashnode_idx; + /* first two iovs for nlmsg hdr and ev */ + ev.u.set_flashnode.count = param_count - 2; + + iov->iov_base = &ev; + iov->iov_len = ev_len; + rc = __kipc_call(iovs, param_count); + if (rc < 0) + return rc; + + return 0; +} + +static int +knew_flashnode(uint64_t transport_handle, uint32_t host_no, void *value, + uint32_t *flashnode_idx) +{ + struct iscsi_uevent *ev; + char *param_str; + int rc, len; + struct iovec iov[2]; + + log_debug(7, "in %s", __FUNCTION__); + + memset(setparam_buf, 0, NLM_SETPARAM_DEFAULT_MAX); + ev = (struct iscsi_uevent *)setparam_buf; + ev->type = ISCSI_UEVENT_NEW_FLASHNODE; + ev->transport_handle = transport_handle; + ev->u.new_flashnode.host_no = host_no; + + param_str = setparam_buf + sizeof(*ev); + if (!strlen(value)) + return 0; + sprintf(param_str, "%s", (char *)value); + len = strlen(param_str) + 1; + ev->u.new_flashnode.len = len; + + + iov[1].iov_base = ev; + iov[1].iov_len = sizeof(*ev) + len; + rc = __kipc_call(iov, 2); + if (rc < 0) + return rc; + + *flashnode_idx = ev->r.new_flashnode_ret.flashnode_idx; + return 0; +} + +static int +kdel_flashnode(uint64_t transport_handle, uint32_t host_no, + uint32_t flashnode_idx) +{ + struct iscsi_uevent ev; + int rc; + struct iovec iov[2]; + + log_debug(7, "in %s", __FUNCTION__); + + memset(&ev, 0, sizeof(struct iscsi_uevent)); + ev.type = ISCSI_UEVENT_DEL_FLASHNODE; + ev.transport_handle = transport_handle; + ev.u.del_flashnode.host_no = host_no; + ev.u.del_flashnode.flashnode_idx = flashnode_idx; + + iov[1].iov_base = &ev; + iov[1].iov_len = sizeof(ev); + rc = __kipc_call(iov, 2); + if (rc < 0) + return rc; + + return 0; +} + +static int +klogin_flashnode(uint64_t transport_handle, uint32_t host_no, + uint32_t flashnode_idx) +{ + struct iscsi_uevent ev; + int rc; + struct iovec iov[2]; + + log_debug(7, "in %s", __FUNCTION__); + + memset(&ev, 0, sizeof(struct iscsi_uevent)); + ev.type = ISCSI_UEVENT_LOGIN_FLASHNODE; + ev.transport_handle = transport_handle; + ev.u.login_flashnode.host_no = host_no; + ev.u.login_flashnode.flashnode_idx = flashnode_idx; + + iov[1].iov_base = &ev; + iov[1].iov_len = sizeof(ev); + rc = __kipc_call(iov, 2); + if (rc < 0) + return rc; + + return 0; +} + +static int +klogout_flashnode(uint64_t transport_handle, uint32_t host_no, + uint32_t flashnode_idx) +{ + struct iscsi_uevent ev; + int rc; + struct iovec iov[2]; + + log_debug(7, "in %s", __FUNCTION__); + + memset(&ev, 0, sizeof(struct iscsi_uevent)); + ev.type = ISCSI_UEVENT_LOGOUT_FLASHNODE; + ev.transport_handle = transport_handle; + ev.u.logout_flashnode.host_no = host_no; + ev.u.logout_flashnode.flashnode_idx = flashnode_idx; + + iov[1].iov_base = &ev; + iov[1].iov_len = sizeof(ev); + rc = __kipc_call(iov, 2); + if (rc < 0) + return rc; + + return 0; +} + +static int +klogout_flashnode_sid(uint64_t transport_handle, uint32_t host_no, + uint32_t sid) +{ + struct iscsi_uevent ev; + int rc; + struct iovec iov[2]; + + log_debug(7, "in %s", __FUNCTION__); + + memset(&ev, 0, sizeof(struct iscsi_uevent)); + ev.type = ISCSI_UEVENT_LOGOUT_FLASHNODE_SID; + ev.transport_handle = transport_handle; + ev.u.logout_flashnode_sid.host_no = host_no; + ev.u.logout_flashnode_sid.sid = sid; + + iov[1].iov_base = &ev; + iov[1].iov_len = sizeof(ev); + rc = __kipc_call(iov, 2); + if (rc < 0) + return rc; + + return 0; +} + +static int kget_host_stats(uint64_t transport_handle, uint32_t host_no, + char *host_stats) +{ + int rc = 0; + int ev_size; + struct iscsi_uevent ev; + struct iovec iov[2]; + char nlm_ev[NLMSG_SPACE(sizeof(struct iscsi_uevent))]; + struct nlmsghdr *nlh; + + memset(&ev, 0, sizeof(struct iscsi_uevent)); + + ev.type = ISCSI_UEVENT_GET_HOST_STATS; + ev.transport_handle = transport_handle; + ev.u.get_host_stats.host_no = host_no; + + iov[1].iov_base = &ev; + iov[1].iov_len = sizeof(ev); + rc = __kipc_call(iov, 2); + if (rc < 0) + return rc; + + if ((rc = nl_read(ctrl_fd, nlm_ev, + NLMSG_SPACE(sizeof(struct iscsi_uevent)), + MSG_PEEK)) < 0) { + log_error("can not read nlm_ev, error %d", rc); + return rc; + } + + nlh = (struct nlmsghdr *)nlm_ev; + ev_size = nlh->nlmsg_len - NLMSG_ALIGN(sizeof(struct nlmsghdr)); + + if ((rc = nlpayload_read(ctrl_fd, (void *)host_stats, + ev_size, 0)) < 0) { + log_error("can not read from NL socket, error %d", rc); + return rc; + } + + return rc; +} + + static void drop_data(struct nlmsghdr *nlh) { int ev_size; @@ -1281,7 +1520,7 @@ static int ctldev_handle(void) nlh = (struct nlmsghdr *)nlm_ev; ev = (struct iscsi_uevent *)NLMSG_DATA(nlm_ev); - log_debug(7, "%s got event type %u\n", __FUNCTION__, ev->type); + log_debug(7, "%s got event type %u", __FUNCTION__, ev->type); /* drivers like qla4xxx can be inserted after iscsid is started */ switch (ev->type) { case ISCSI_KEVENT_CREATE_SESSION: @@ -1296,10 +1535,10 @@ static int ctldev_handle(void) ev->r.c_session_ret.sid); return 0; case ISCSI_KEVENT_DESTROY_SESSION: + drop_data(nlh); if (!ipc_ev_clbk) return 0; - drop_data(nlh); if (ipc_ev_clbk->destroy_session) ipc_ev_clbk->destroy_session(ev->r.d_session.host_no, ev->r.d_session.sid); @@ -1324,15 +1563,15 @@ static int ctldev_handle(void) case ISCSI_KEVENT_HOST_EVENT: switch (ev->r.host_event.code) { case ISCSI_EVENT_LINKUP: - log_warning("Host%u: Link Up.\n", + log_warning("Host%u: Link Up.", ev->r.host_event.host_no); break; case ISCSI_EVENT_LINKDOWN: - log_warning("Host%u: Link Down.\n", + log_warning("Host%u: Link Down.", ev->r.host_event.host_no); break; default: - log_debug(7, "Host%u: Unknwon host event: %u.\n", + log_debug(7, "Host%u: Unknwon host event: %u.", ev->r.host_event.host_no, ev->r.host_event.code); } @@ -1372,7 +1611,7 @@ static int ctldev_handle(void) * nl interface. */ log_debug(1, "Could not verify connection %d:%d. Dropping " - "event.\n", sid, cid); + "event.", sid, cid); drop_data(nlh); return -ENXIO; } @@ -1382,8 +1621,8 @@ static int ctldev_handle(void) ev_context = ipc_ev_clbk->get_ev_context(conn, ev_size); if (!ev_context) { - /* retry later */ log_error("Can not allocate memory for receive context."); + drop_data(nlh); return -ENOMEM; } @@ -1542,7 +1781,15 @@ struct iscsi_ipc nl_ipc = { .recv_conn_state = krecv_conn_state, .exec_ping = kexec_ping, .get_chap = kget_chap, + .set_chap = kset_chap, .delete_chap = kdelete_chap, + .set_flash_node_params = kset_flashnode_params, + .new_flash_node = knew_flashnode, + .del_flash_node = kdel_flashnode, + .login_flash_node = klogin_flashnode, + .logout_flash_node = klogout_flashnode, + .logout_flash_node_sid = klogout_flashnode_sid, + .get_host_stats = kget_host_stats, }; struct iscsi_ipc *ipc = &nl_ipc; diff --git a/usr/session_info.c b/usr/session_info.c index 1f84c49..2f48e65 100644 --- a/usr/session_info.c +++ b/usr/session_info.c @@ -64,20 +64,32 @@ void session_info_free_list(struct list_head *list) } } +static char *get_iscsi_node_type(struct session_info *info) +{ + int pid = iscsi_sysfs_session_user_created(info->sid); + + if (!pid) + return "flash"; + else + return "non-flash"; +} + static int session_info_print_flat(void *data, struct session_info *info) { struct iscsi_transport *t = iscsi_sysfs_get_transport_by_sid(info->sid); if (strchr(info->persistent_address, '.')) - printf("%s: [%d] %s:%d,%d %s\n", + printf("%s: [%d] %s:%d,%d %s (%s)\n", t ? t->name : UNKNOWN_VALUE, info->sid, info->persistent_address, - info->persistent_port, info->tpgt, info->targetname); + info->persistent_port, info->tpgt, info->targetname, + get_iscsi_node_type(info)); else - printf("%s: [%d] [%s]:%d,%d %s\n", + printf("%s: [%d] [%s]:%d,%d %s (%s)\n", t ? t->name : UNKNOWN_VALUE, info->sid, info->persistent_address, - info->persistent_port, info->tpgt, info->targetname); + info->persistent_port, info->tpgt, info->targetname, + get_iscsi_node_type(info)); return 0; } @@ -230,7 +242,8 @@ void session_info_print_tree(struct list_head *list, char *prefix, list_for_each_entry(curr, list, list) { if (!prev || strcmp(prev->targetname, curr->targetname)) { - printf("%sTarget: %s\n", prefix, curr->targetname); + printf("%sTarget: %s (%s)\n", prefix, curr->targetname, + get_iscsi_node_type(curr)); prev = NULL; } @@ -278,6 +291,7 @@ void session_info_print_tree(struct list_head *list, char *prefix, printf("%s\t\tSID: %d\n", prefix, curr->sid); print_iscsi_state(curr->sid, prefix); } + if (flags & SESSION_INFO_ISCSI_TIM) { printf("%s\t\t*********\n", prefix); printf("%s\t\tTimeouts:\n", prefix); @@ -368,7 +382,7 @@ int session_info_print(int info_level, struct session_info *info, int do_show) num_found = 1; } else err = iscsi_sysfs_for_each_session(info, &num_found, - session_info_print_flat); + session_info_print_flat, 0); break; case 3: version = iscsi_sysfs_get_iscsi_kernel_version(); @@ -403,7 +417,7 @@ int session_info_print(int info_level, struct session_info *info, int do_show) link_info.match_fn = NULL; err = iscsi_sysfs_for_each_session(&link_info, &num_found, - session_info_create_list); + session_info_create_list, 0); if (err || !num_found) break; diff --git a/usr/session_mgmt.c b/usr/session_mgmt.c index ec1f43a..87b8e00 100644 --- a/usr/session_mgmt.c +++ b/usr/session_mgmt.c @@ -172,18 +172,18 @@ int iscsi_login_portal(void *data, struct list_head *list, struct node_rec *rec) * that are missing. */ rc = iscsi_sysfs_for_each_session(rec, &session_count, - iscsi_match_session_count); + iscsi_match_session_count, 0); if (rc) { log_error("Could not count current number of sessions"); goto done; } if (session_count >= rec->session.nr_sessions) { - log_debug(1, "%s: %d session%s requested, but %d " + log_warning("%s: %d session%s requested, but %d " "already present.", rec->iface.name, rec->session.nr_sessions, rec->session.nr_sessions == 1 ? "" : "s", session_count); - rc = 0; + rc = ISCSI_ERR_SESS_EXISTS; goto done; } @@ -421,7 +421,7 @@ int iscsi_logout_portals(void *data, int *nr_found, int wait, *nr_found = 0; err = iscsi_sysfs_for_each_session(&link_info, nr_found, - session_info_create_list); + session_info_create_list, 0); if (err && !list_empty(&session_list)) log_error("Could not read in all sessions: %s", iscsi_err_to_str(err)); @@ -466,7 +466,8 @@ free_list: int iscsi_check_for_running_session(struct node_rec *rec) { int nr_found = 0; - if (iscsi_sysfs_for_each_session(rec, &nr_found, iscsi_match_session)) + if (iscsi_sysfs_for_each_session(rec, &nr_found, iscsi_match_session, + 0)) return 1; return 0; } diff --git a/usr/strings.c b/usr/strings.c index 638cb4d..da5df28 100644 --- a/usr/strings.c +++ b/usr/strings.c @@ -74,7 +74,7 @@ int str_enlarge_data(struct str_buffer *s, int length) if (s) { s->data_length += length; if (s->data_length > s->allocated_length) { - log_debug(7, "enlarge buffer from %lu to %lu\n", + log_debug(7, "enlarge buffer from %lu to %lu", s->allocated_length, s->data_length); new_buf = realloc(s->buffer, s->data_length); if (!new_buf) { diff --git a/usr/sysfs.c b/usr/sysfs.c index 7f31c1a..6520bf6 100644 --- a/usr/sysfs.c +++ b/usr/sysfs.c @@ -82,7 +82,7 @@ int sysfs_init(void) remove_trailing_chars(sysfs_path, '/'); } else strlcpy(sysfs_path, "/sys", sizeof(sysfs_path)); - dbg("sysfs_path='%s'\n", sysfs_path); + dbg("sysfs_path='%s'", sysfs_path); INIT_LIST_HEAD(&dev_list); INIT_LIST_HEAD(&attr_list); @@ -123,7 +123,7 @@ void sysfs_device_set_values(struct sysfs_device *dev, const char *devpath, if (pos == NULL) return; strlcpy(dev->kernel, &pos[1], sizeof(dev->kernel)); - dbg("kernel='%s'\n", dev->kernel); + dbg("kernel='%s'", dev->kernel); /* some devices have '!' in their name, change that to '/' */ pos = dev->kernel; @@ -138,7 +138,7 @@ void sysfs_device_set_values(struct sysfs_device *dev, const char *devpath, while (isdigit(pos[-1])) pos--; strlcpy(dev->kernel_number, pos, sizeof(dev->kernel_number)); - dbg("kernel_number='%s'\n", dev->kernel_number); + dbg("kernel_number='%s'", dev->kernel_number); } int sysfs_resolve_link(char *devpath, size_t size) @@ -155,11 +155,11 @@ int sysfs_resolve_link(char *devpath, size_t size) if (len <= 0) return -1; link_target[len] = '\0'; - dbg("path link '%s' points to '%s'\n", devpath, link_target); + dbg("path link '%s' points to '%s'", devpath, link_target); for (back = 0; strncmp(&link_target[back * 3], "../", 3) == 0; back++) ; - dbg("base '%s', tail '%s', back %i\n", devpath, &link_target[back * 3], back); + dbg("base '%s', tail '%s', back %i", devpath, &link_target[back * 3], back); for (i = 0; i <= back; i++) { char *pos = strrchr(devpath, '/'); @@ -167,7 +167,7 @@ int sysfs_resolve_link(char *devpath, size_t size) return -1; pos[0] = '\0'; } - dbg("after moving back '%s'\n", devpath); + dbg("after moving back '%s'", devpath); strlcat(devpath, "/", size); strlcat(devpath, &link_target[back * 3], size); return 0; @@ -195,7 +195,7 @@ struct sysfs_device *sysfs_device_get(const char *devpath) strncmp(devpath, "/block/", 7) != 0) return NULL; - dbg("open '%s'\n", devpath); + dbg("open '%s'", devpath); strlcpy(devpath_real, devpath, sizeof(devpath_real)); remove_trailing_chars(devpath_real, '/'); if (devpath[0] == '\0' ) @@ -204,7 +204,7 @@ struct sysfs_device *sysfs_device_get(const char *devpath) /* look for device already in cache (we never put an untranslated path in the cache) */ list_for_each_entry(dev_loop, &dev_list, node) { if (strcmp(dev_loop->devpath, devpath_real) == 0) { - dbg("found in cache '%s'\n", dev_loop->devpath); + dbg("found in cache '%s'", dev_loop->devpath); return dev_loop; } } @@ -213,7 +213,7 @@ struct sysfs_device *sysfs_device_get(const char *devpath) strlcpy(path, sysfs_path, sizeof(path)); strlcat(path, devpath_real, sizeof(path)); if (lstat(path, &statbuf) != 0) { - dbg("stat '%s' failed: %s\n", path, strerror(errno)); + dbg("stat '%s' failed: %s", path, strerror(errno)); return NULL; } if (S_ISLNK(statbuf.st_mode)) { @@ -223,14 +223,14 @@ struct sysfs_device *sysfs_device_get(const char *devpath) /* now look for device in cache after path translation */ list_for_each_entry(dev_loop, &dev_list, node) { if (strcmp(dev_loop->devpath, devpath_real) == 0) { - dbg("found in cache '%s'\n", dev_loop->devpath); + dbg("found in cache '%s'", dev_loop->devpath); return dev_loop; } } } /* it is a new device */ - dbg("new uncached device '%s'\n", devpath_real); + dbg("new uncached device '%s'", devpath_real); dev = malloc(sizeof(struct sysfs_device)); if (dev == NULL) return NULL; @@ -246,7 +246,7 @@ struct sysfs_device *sysfs_device_get(const char *devpath) if (len > 0) { /* get subsystem from "subsystem" link */ link_target[len] = '\0'; - dbg("subsystem link '%s' points to '%s'\n", link_path, link_target); + dbg("subsystem link '%s' points to '%s'", link_path, link_target); pos = strrchr(link_target, '/'); if (pos != NULL) strlcpy(dev->subsystem, &pos[1], sizeof(dev->subsystem)); @@ -275,13 +275,13 @@ struct sysfs_device *sysfs_device_get(const char *devpath) len = readlink(link_path, link_target, sizeof(link_target)); if (len > 0) { link_target[len] = '\0'; - dbg("driver link '%s' points to '%s'\n", link_path, link_target); + dbg("driver link '%s' points to '%s'", link_path, link_target); pos = strrchr(link_target, '/'); if (pos != NULL) strlcpy(dev->driver, &pos[1], sizeof(dev->driver)); } - dbg("add to cache 'devpath=%s', subsystem='%s', driver='%s'\n", dev->devpath, dev->subsystem, dev->driver); + dbg("add to cache 'devpath=%s', subsystem='%s', driver='%s'", dev->devpath, dev->subsystem, dev->driver); list_add(&dev->node, &dev_list); return dev; @@ -292,14 +292,14 @@ struct sysfs_device *sysfs_device_get_parent(struct sysfs_device *dev) char parent_devpath[PATH_SIZE]; char *pos; - dbg("open '%s'\n", dev->devpath); + dbg("open '%s'", dev->devpath); /* look if we already know the parent */ if (dev->parent != NULL) return dev->parent; strlcpy(parent_devpath, dev->devpath, sizeof(parent_devpath)); - dbg("'%s'\n", parent_devpath); + dbg("'%s'", parent_devpath); /* strip last element */ pos = strrchr(parent_devpath, '/'); @@ -310,12 +310,12 @@ struct sysfs_device *sysfs_device_get_parent(struct sysfs_device *dev) if (strncmp(parent_devpath, "/class", 6) == 0) { pos = strrchr(parent_devpath, '/'); if (pos == &parent_devpath[6] || pos == parent_devpath) { - dbg("/class top level, look for device link\n"); + dbg("/class top level, look for device link"); goto device_link; } } if (strcmp(parent_devpath, "/block") == 0) { - dbg("/block top level, look for device link\n"); + dbg("/block top level, look for device link"); goto device_link; } @@ -364,7 +364,7 @@ char *sysfs_attr_get_value(const char *devpath, const char *attr_name) ssize_t size; size_t sysfs_len; - dbg("open '%s'/'%s'\n", devpath, attr_name); + dbg("open '%s'/'%s'", devpath, attr_name); sysfs_len = strlcpy(path_full, sysfs_path, sizeof(path_full)); if(sysfs_len >= sizeof(path_full)) sysfs_len = sizeof(path_full) - 1; @@ -376,23 +376,23 @@ char *sysfs_attr_get_value(const char *devpath, const char *attr_name) /* look for attribute in cache */ list_for_each_entry(attr_loop, &attr_list, node) { if (strcmp(attr_loop->path, path) == 0) { - dbg("found in cache '%s'\n", attr_loop->path); + dbg("found in cache '%s'", attr_loop->path); return attr_loop->value; } } /* store attribute in cache (also negatives are kept in cache) */ - dbg("new uncached attribute '%s'\n", path_full); + dbg("new uncached attribute '%s'", path_full); attr = malloc(sizeof(struct sysfs_attr)); if (attr == NULL) return NULL; memset(attr, 0x00, sizeof(struct sysfs_attr)); strlcpy(attr->path, path, sizeof(attr->path)); - dbg("add to cache '%s'\n", path_full); + dbg("add to cache '%s'", path_full); list_add(&attr->node, &attr_list); if (lstat(path_full, &statbuf) != 0) { - dbg("stat '%s' failed: %s\n", path_full, strerror(errno)); + dbg("stat '%s' failed: %s", path_full, strerror(errno)); goto out; } @@ -407,7 +407,7 @@ char *sysfs_attr_get_value(const char *devpath, const char *attr_name) link_target[len] = '\0'; pos = strrchr(link_target, '/'); if (pos != NULL) { - dbg("cache '%s' with link value '%s'\n", path_full, value); + dbg("cache '%s' with link value '%s'", path_full, value); strlcpy(attr->value_local, &pos[1], sizeof(attr->value_local)); attr->value = attr->value_local; } @@ -426,7 +426,7 @@ char *sysfs_attr_get_value(const char *devpath, const char *attr_name) /* read attribute value */ fd = open(path_full, O_RDONLY); if (fd < 0) { - dbg("attribute '%s' can not be opened\n", path_full); + dbg("attribute '%s' can not be opened", path_full); goto out; } size = read(fd, value, sizeof(value)); @@ -439,7 +439,7 @@ char *sysfs_attr_get_value(const char *devpath, const char *attr_name) /* got a valid value, store and return it */ value[size] = '\0'; remove_trailing_chars(value, '\n'); - dbg("cache '%s' with attribute value '%s'\n", path_full, value); + dbg("cache '%s' with attribute value '%s'", path_full, value); strlcpy(attr->value_local, value, sizeof(attr->value_local)); attr->value = attr->value_local; @@ -554,14 +554,14 @@ char *sysfs_get_value(const char *id, char *subsys, char *param) if (!sysfs_lookup_devpath_by_subsys_id(devpath, sizeof(devpath), subsys, id)) { - log_debug(3, "Could not lookup devpath for %s %s\n", + log_debug(3, "Could not lookup devpath for %s %s", subsys, id); return NULL; } sysfs_value = sysfs_attr_get_value(devpath, param); if (!sysfs_value) { - log_debug(3, "Could not read attr %s on path %s\n", + log_debug(3, "Could not read attr %s on path %s", param, devpath); return NULL; } @@ -671,12 +671,11 @@ int sysfs_set_param(char *id, char *subsys, char *attr_name, char devpath[PATH_SIZE]; size_t sysfs_len; char path_full[PATH_SIZE]; - const char *path; int rc = 0, fd; if (!sysfs_lookup_devpath_by_subsys_id(devpath, sizeof(devpath), subsys, id)) { - log_debug(3, "Could not lookup devpath for %s %s\n", + log_debug(3, "Could not lookup devpath for %s %s", subsys, id); return EIO; } @@ -684,25 +683,24 @@ int sysfs_set_param(char *id, char *subsys, char *attr_name, sysfs_len = strlcpy(path_full, sysfs_path, sizeof(path_full)); if(sysfs_len >= sizeof(path_full)) sysfs_len = sizeof(path_full) - 1; - path = &path_full[sysfs_len]; strlcat(path_full, devpath, sizeof(path_full)); strlcat(path_full, "/", sizeof(path_full)); strlcat(path_full, attr_name, sizeof(path_full)); if (lstat(path_full, &statbuf)) { - log_debug(3, "Could not stat %s\n", path_full); + log_debug(3, "Could not stat %s", path_full); return errno; } if ((statbuf.st_mode & S_IWUSR) == 0) { - log_error("Could not write to %s. Invalid permissions.\n", + log_error("Could not write to %s. Invalid permissions.", path_full); return EACCES; } fd = open(path_full, O_WRONLY); if (fd < 0) { - log_error("Could not open %s err %d\n", path_full, errno); + log_error("Could not open %s err %d", path_full, errno); return errno; } @@ -711,3 +709,43 @@ int sysfs_set_param(char *id, char *subsys, char *attr_name, close(fd); return rc; } + +char *sysfs_get_uevent_field(const char *path, const char *field) +{ + char *uevent_path = NULL; + FILE *f = NULL; + char *line, buffer[1024]; + char *ff, *d; + char *out = NULL; + + uevent_path = calloc(1, PATH_MAX); + if (!uevent_path) + return NULL; + snprintf(uevent_path, PATH_MAX, "%s/uevent", path); + + f = fopen(uevent_path, "r"); + if (!f) + goto out; + while ((line = fgets(buffer, sizeof (buffer), f))) { + ff = strtok(line, "="); + d = strtok(NULL, "\n"); + if (strcmp(ff, field)) + continue; + out = strdup(d); + break; + } + fclose(f); +out: + free(uevent_path); + return out; +} + +char *sysfs_get_uevent_devtype(const char *path) +{ + return sysfs_get_uevent_field(path, "DEVTYPE"); +} + +char *sysfs_get_uevent_devname(const char *path) +{ + return sysfs_get_uevent_field(path, "DEVNAME"); +} diff --git a/usr/sysfs.h b/usr/sysfs.h index 304dbbf..462060e 100644 --- a/usr/sysfs.h +++ b/usr/sysfs.h @@ -66,4 +66,8 @@ extern int sysfs_get_uint16(char *id, char *subsys, char *param, extern int sysfs_set_param(char *id, char *subsys, char *attr_name, char *write_buf, ssize_t buf_size); +extern char *sysfs_get_uevent_field(const char *path, const char *field); +extern char *sysfs_get_uevent_devtype(const char *path); +extern char *sysfs_get_uevent_devname(const char *path); + #endif diff --git a/usr/transport.c b/usr/transport.c index e6e3dfc..c96d9c6 100644 --- a/usr/transport.c +++ b/usr/transport.c @@ -35,6 +35,7 @@ #include "log.h" #include "iscsi_util.h" #include "iscsi_sysfs.h" +#include "uip_mgmt_ipc.h" #include "cxgbi.h" #include "be2iscsi.h" #include "iser.h" @@ -57,7 +58,8 @@ struct iscsi_transport_template iscsi_iser = { struct iscsi_transport_template cxgb3i = { .name = "cxgb3i", - .set_host_ip = 1, + .set_host_ip = SET_HOST_IP_OPT, + .bind_ep_required = 1, .ep_connect = ktransport_ep_connect, .ep_poll = ktransport_ep_poll, .ep_disconnect = ktransport_ep_disconnect, @@ -66,7 +68,8 @@ struct iscsi_transport_template cxgb3i = { struct iscsi_transport_template cxgb4i = { .name = "cxgb4i", - .set_host_ip = 1, + .set_host_ip = SET_HOST_IP_NOT_REQ, + .bind_ep_required = 1, .ep_connect = ktransport_ep_connect, .ep_poll = ktransport_ep_poll, .ep_disconnect = ktransport_ep_disconnect, @@ -75,14 +78,18 @@ struct iscsi_transport_template cxgb4i = { struct iscsi_transport_template bnx2i = { .name = "bnx2i", - .set_host_ip = 1, + .set_host_ip = SET_HOST_IP_REQ, + .use_boot_info = 1, + .bind_ep_required = 1, .ep_connect = ktransport_ep_connect, .ep_poll = ktransport_ep_poll, .ep_disconnect = ktransport_ep_disconnect, + .set_net_config = uip_broadcast_params, }; struct iscsi_transport_template be2iscsi = { .name = "be2iscsi", + .bind_ep_required = 1, .create_conn = be2iscsi_create_conn, .ep_connect = ktransport_ep_connect, .ep_poll = ktransport_ep_poll, @@ -91,7 +98,16 @@ struct iscsi_transport_template be2iscsi = { struct iscsi_transport_template qla4xxx = { .name = "qla4xxx", - .set_host_ip = 0, + .set_host_ip = SET_HOST_IP_NOT_REQ, + .bind_ep_required = 1, + .ep_connect = ktransport_ep_connect, + .ep_poll = ktransport_ep_poll, + .ep_disconnect = ktransport_ep_disconnect, +}; + +struct iscsi_transport_template ocs = { + .name = "ocs", + .bind_ep_required = 1, .ep_connect = ktransport_ep_connect, .ep_poll = ktransport_ep_poll, .ep_disconnect = ktransport_ep_disconnect, @@ -105,6 +121,7 @@ static struct iscsi_transport_template *iscsi_transport_templates[] = { &bnx2i, &qla4xxx, &be2iscsi, + &ocs, NULL }; @@ -132,7 +149,7 @@ int transport_probe_for_offload(void) for (i = 0; ifni[i].if_index && ifni[i].if_name; i++) { struct if_nameindex *n = &ifni[i]; - log_debug(6, "kmod probe found %s\n", n->if_name); + log_debug(6, "kmod probe found %s", n->if_name); strlcpy(if_hwaddr.ifr_name, n->if_name, IFNAMSIZ); if (ioctl(sockfd, SIOCGIFHWADDR, &if_hwaddr) < 0) @@ -264,12 +281,12 @@ int set_transport_template(struct iscsi_transport *t) if (!strcmp(tmpl->name, t->name)) { t->template = tmpl; - log_debug(3, "Matched transport %s\n", t->name); + log_debug(3, "Matched transport %s", t->name); return 0; } } log_error("Could not find template for %s. An updated iscsiadm " - "is probably needed.\n", t->name); + "is probably needed.", t->name); return ENOSYS; } diff --git a/usr/transport.h b/usr/transport.h index 672561b..831403b 100644 --- a/usr/transport.h +++ b/usr/transport.h @@ -20,6 +20,12 @@ #include "types.h" #include "config.h" +enum set_host_ip_opts { + SET_HOST_IP_NOT_REQ, /* iface.ipaddress is not supported */ + SET_HOST_IP_REQ, /* iface.ipaddress must be specified */ + SET_HOST_IP_OPT, /* iface.ipaddress is not required */ +}; + struct iscsi_transport; struct iscsi_conn; @@ -31,10 +37,15 @@ struct iscsi_transport_template { * the host's ip address. */ uint8_t set_host_ip; + uint8_t use_boot_info; + uint8_t bind_ep_required; int (*ep_connect) (struct iscsi_conn *conn, int non_blocking); int (*ep_poll) (struct iscsi_conn *conn, int timeout_ms); void (*ep_disconnect) (struct iscsi_conn *conn); void (*create_conn) (struct iscsi_conn *conn); + int (*set_net_config) (struct iscsi_transport *t, + struct iface_rec *iface, + struct iscsi_session *session); }; /* represents data path provider */ diff --git a/usr/types.h b/usr/types.h index 77e3f97..9d9ba86 100644 --- a/usr/types.h +++ b/usr/types.h @@ -10,6 +10,7 @@ #include #include #include +#include /* * using the __be types allows stricter static diff --git a/usr/uip_mgmt_ipc.c b/usr/uip_mgmt_ipc.c new file mode 100644 index 0000000..1dfc6d0 --- /dev/null +++ b/usr/uip_mgmt_ipc.c @@ -0,0 +1,41 @@ +/* + * uIP iSCSI Daemon/Admin Management IPC + * + * 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. + * + * See the file COPYING included with this distribution for more details. + */ + +#include + +#include "log.h" +#include "uip_mgmt_ipc.h" +#include "iscsid_req.h" + +int uip_broadcast_params(struct iscsi_transport *t, + struct iface_rec *iface, + struct iscsi_session *session) +{ + struct iscsid_uip_broadcast broadcast; + + log_debug(3, "broadcasting to uip"); + + memset(&broadcast, 0, sizeof(broadcast)); + + broadcast.header.command = ISCSID_UIP_IPC_GET_IFACE; + broadcast.header.payload_len = sizeof(*iface); + + memcpy(&broadcast.u.iface_rec, iface, sizeof(*iface)); + + return uip_broadcast(&broadcast, + sizeof(iscsid_uip_broadcast_header_t) + + sizeof(*iface)); +} diff --git a/usr/uip_mgmt_ipc.h b/usr/uip_mgmt_ipc.h new file mode 100644 index 0000000..29a4769 --- /dev/null +++ b/usr/uip_mgmt_ipc.h @@ -0,0 +1,73 @@ +/* + * uIP iSCSI Daemon/Admin Management IPC + * + * 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. + * + * See the file COPYING included with this distribution for more details. + */ +#ifndef UIP_MGMT_IPC_H +#define UIP_MGMT_IPC_H + +#include "types.h" +#include "iscsi_if.h" +#include "config.h" +#include "mgmt_ipc.h" + +#include "initiator.h" +#include "transport.h" + +#define ISCSID_UIP_NAMESPACE "ISCSID_UIP_ABSTRACT_NAMESPACE" + +typedef enum iscsid_uip_cmd { + ISCSID_UIP_IPC_UNKNOWN = 0, + ISCSID_UIP_IPC_GET_IFACE = 1, + + __ISCSID_UIP_IPC_MAX_COMMAND +} iscsid_uip_cmd_e; + +typedef struct iscsid_uip_broadcast_header { + iscsid_uip_cmd_e command; + uint32_t payload_len; +} iscsid_uip_broadcast_header_t; + +/* IPC Request */ +typedef struct iscsid_uip_broadcast { + struct iscsid_uip_broadcast_header header; + + union { + /* messages */ + struct ipc_broadcast_iface_rec { + struct iface_rec rec; + } iface_rec; + } u; +} iscsid_uip_broadcast_t; + +typedef enum iscsid_uip_mgmt_ipc_err { + ISCSID_UIP_MGMT_IPC_OK = 0, + ISCSID_UIP_MGMT_IPC_ERR = 1, + ISCSID_UIP_MGMT_IPC_ERR_NOT_FOUND = 2, + ISCSID_UIP_MGMT_IPC_ERR_NOMEM = 3, + ISCSID_UIP_MGMT_IPC_DEVICE_UP = 4, + ISCSID_UIP_MGMT_IPC_DEVICE_INITIALIZING = 5, +} iscsid_uip_mgmt_ipc_err_e; + +/* IPC Response */ +typedef struct iscsid_uip_mgmt_rsp { + iscsid_uip_cmd_e command; + iscsid_uip_mgmt_ipc_err_e err; +} iscsid_uip_rsp_t; + +extern int uip_broadcast_params(struct iscsi_transport *t, + struct iface_rec *iface, + struct iscsi_session *session); + + +#endif /* UIP_MGMT_IPC_H */ diff --git a/utils/Makefile b/utils/Makefile index 2c7e891..f65f1e7 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -1,12 +1,13 @@ # This Makefile will work only with GNU make. -CFLAGS += $(OPTFLAGS) -O2 -fno-inline -Wall -Wstrict-prototypes -g +CFLAGS ?= -O2 -fno-inline -g +CFLAGS += -Wall -Wstrict-prototypes PROGRAMS = iscsi-iname all: $(PROGRAMS) iscsi-iname: md5.o iscsi-iname.o - $(CC) $(CFLAGS) $^ $(DBM_LIB) -o $@ + $(CC) $(CFLAGS) $(LDFLAGS) $^ $(DBM_LIB) -o $@ clean: rm -f *.o $(PROGRAMS) .depend diff --git a/utils/fwparam_ibft/Makefile b/utils/fwparam_ibft/Makefile index c72bb7f..773d8eb 100644 --- a/utils/fwparam_ibft/Makefile +++ b/utils/fwparam_ibft/Makefile @@ -26,9 +26,9 @@ OBJS := fw_entry.o fwparam_sysfs.o $(SYSDEPS_OBJS) ../../usr/iscsi_net_util.o OBJS += prom_lex.o prom_parse.tab.o fwparam_ppc.o CLEANFILES = $(OBJS) *.output *~ -OPTFLAGS ?= -O2 -g -fPIC +CFLAGS ?= -O2 -g WARNFLAGS ?= -Wall -Wstrict-prototypes -CFLAGS += $(OPTFLAGS) $(WARNFLAGS) -I../../include -I../../usr -D_GNU_SOURCE +CFLAGS += -fPIC $(WARNFLAGS) -I../../include -I../../usr -D_GNU_SOURCE all: $(OBJS) diff --git a/utils/fwparam_ibft/fw_entry.c b/utils/fwparam_ibft/fw_entry.c index b6f05c1..f94a035 100644 --- a/utils/fwparam_ibft/fw_entry.c +++ b/utils/fwparam_ibft/fw_entry.c @@ -67,15 +67,15 @@ int fw_setup_nics(void) * to force iSCSI traffic through correct NIC */ list_for_each_entry(context, &targets, list) { - /* if it is a offload nic ignore it */ - if (!net_get_transport_name_from_netdev(context->iface, + /* if it is a offload nic ignore it */ + if (!net_get_transport_name_from_netdev(context->iface, transport)) continue; if (iface_prev == NULL || strcmp(context->iface, iface_prev)) { /* Note: test above works because there is a - * maximum of two targets in the iBFT - */ + * maximum of two targets in the iBFT + */ iface_prev = context->iface; needs_bringup = 1; } @@ -192,10 +192,13 @@ static void dump_network(struct boot_context *context) if (strlen(context->mac)) printf("%s = %s\n", IFACE_HWADDR, context->mac); /* - * If this has a valid address then DHCP was used (broadcom sends - * 0.0.0.0). + * If the 'origin' field is 3 (IBFT_IP_PREFIX_ORIGIN_DHCP), + * then DHCP is used. + * Otherwise evaluate the 'dhcp' field, if this has a valid + * address then DHCP was used (broadcom sends 0.0.0.0). */ - if (strlen(context->dhcp) && strcmp(context->dhcp, "0.0.0.0")) + if ((context->origin == IBFT_IP_PREFIX_ORIGIN_DHCP) || + (strlen(context->dhcp) && strcmp(context->dhcp, "0.0.0.0"))) printf("%s = DHCP\n", IFACE_BOOT_PROTO); else printf("%s = STATIC\n", IFACE_BOOT_PROTO); diff --git a/utils/fwparam_ibft/fwparam_ibft_sysfs.c b/utils/fwparam_ibft/fwparam_ibft_sysfs.c index 9185c85..2dc6f6d 100644 --- a/utils/fwparam_ibft/fwparam_ibft_sysfs.c +++ b/utils/fwparam_ibft/fwparam_ibft_sysfs.c @@ -201,6 +201,8 @@ static int fill_nic_context(char *id, struct boot_context *context) sizeof(context->secondary_dns)); sysfs_get_str(id, IBFT_SUBSYS, "dhcp", context->dhcp, sizeof(context->dhcp)); + sysfs_get_str(id, IBFT_SUBSYS, "origin", context->origin, + sizeof(context->origin)); return 0; } diff --git a/utils/fwparam_ibft/fwparam_sysfs.c b/utils/fwparam_ibft/fwparam_sysfs.c index 3997363..23eb913 100644 --- a/utils/fwparam_ibft/fwparam_sysfs.c +++ b/utils/fwparam_ibft/fwparam_sysfs.c @@ -170,6 +170,18 @@ static int fill_nic_context(char *subsys, char *id, { int rc; + rc = sysfs_get_int(id, subsys, "flags", &context->nic_flags); + /* + * Per spec we would need to check against Bit 0 + * (Block Valid Flag), but some firmware only + * sets Bit 1 (Firmware Booting Selected). + * So any setting is deemed okay. + */ + if (!rc && (context->nic_flags == 0)) + rc = ENODEV; + if (rc) + return rc; + rc = sysfs_get_str(id, subsys, "mac", context->mac, sizeof(context->mac)); if (rc) @@ -200,6 +212,9 @@ static int fill_nic_context(char *subsys, char *id, strlcpy(context->scsi_host_name, subsys, sizeof(context->scsi_host_name)); + memset(&context->boot_nic, 0, sizeof(context->boot_nic)); + snprintf(context->boot_nic, sizeof(context->boot_nic), "%s", id); + sysfs_get_str(id, subsys, "ip-addr", context->ipaddr, sizeof(context->ipaddr)); sysfs_get_str(id, subsys, "vlan", context->vlan, @@ -214,6 +229,7 @@ static int fill_nic_context(char *subsys, char *id, sizeof(context->secondary_dns)); sysfs_get_str(id, subsys, "dhcp", context->dhcp, sizeof(context->dhcp)); + sysfs_get_int(id, subsys, "origin", (int *)&context->origin); return 0; } @@ -224,12 +240,26 @@ static void fill_initiator_context(char *subsys, struct boot_context *context) sizeof(context->initiatorname)); sysfs_get_str("initiator", subsys, "isid", context->isid, sizeof(context->isid)); + + strlcpy(context->boot_root, subsys, sizeof(context->boot_root)); } static int fill_tgt_context(char *subsys, char *id, struct boot_context *context) { int rc; + rc = sysfs_get_int(id, subsys, "flags", &context->target_flags); + /* + * Per spec we would need to check against Bit 0 + * (Block Valid Flag), but some firmware only + * sets Bit 1 (Firmware Booting Selected). + * So any setting is deemed okay. + */ + if (!rc && (context->target_flags == 0)) + rc = ENODEV; + if (rc) + return rc; + rc = sysfs_get_str(id, subsys, "target-name", context->targetname, sizeof(context->targetname)); if (rc) @@ -240,6 +270,9 @@ static int fill_tgt_context(char *subsys, char *id, if (rc) return rc; + memset(&context->boot_target, 0, sizeof(context->boot_target)); + snprintf(context->boot_target, sizeof(context->boot_target), "%s", id); + /* * We can live without the rest of they do not exist. If we * failed to get them we will figure it out when we login. @@ -315,7 +348,7 @@ static int get_boot_info(struct boot_context *context, char *rootdir, nic_cnt = 0; tgt_cnt = 0; if (file_exist(initiator_dir)) { - /* Find the target's and the ethernet's */ + /* Find the targets and the ethernets */ rc = nftw(rootdir, find_sysfs_dirs, 20, 1); /* Find wihch target and which ethernet have @@ -391,7 +424,7 @@ static int get_targets(struct list_head *list, char *rootdir, char *subsys) nic_cnt = 0; tgt_cnt = 0; - /* Find the target's and the ethernet's */ + /* Find the targets and the ethernets */ nftw(rootdir, find_sysfs_dirs, 20, 1); for (i = 0; i < tgt_cnt; i++) { context = calloc(1, sizeof(*context)); diff --git a/utils/iscsi-gen-initiatorname b/utils/iscsi-gen-initiatorname new file mode 100755 index 0000000..88bd43b --- /dev/null +++ b/utils/iscsi-gen-initiatorname @@ -0,0 +1,73 @@ +#!/bin/bash +# +# /sbin/iscsi-gen-initiatorname +# +# Generate a default iSCSI Initiatorname for SUSE installations. +# +# Copyright (c) 2011 Hannes Reinecke, SUSE Labs +# This script is licensed under the GPL. +# + +if [ "$1" ] ; then + if [ "$1" = "-f" ] ; then + FORCE=1 + else + echo "Invalid option $1" + echo "Usage: $0 [-f]" + exit 1 + fi +fi + +if [ -d /sys/firmware/ibft/initiator ] ; then + read iSCSI_INITIATOR_NAME < /sys/firmware/ibft/initiator/initiator-name +fi + +if [ -f /etc/iscsi/initiatorname.iscsi -a -z "$FORCE" ] ; then + if [ "$iSCSI_INITIATOR_NAME" ] ; then + eval $(cat /etc/iscsi/initiatorname.iscsi | sed -e '/^#/d') + if [ "$iSCSI_INITIATOR_NAME" != "$InitiatorName" ] ; then + echo "iSCSI Initiatorname from iBFT is different from the current setting." + echo "Please call '/sbin/iscsi-gen-initiatorname -f' to update the iSCSI Initiatorname." + exit 1 + fi + fi +fi + +if [ "$iSCSI_INITIATOR_NAME" ] ; then + cat << EOF >> /etc/iscsi/initiatorname.iscsi +## +## /etc/iscsi/iscsi.initiatorname +## +## iSCSI Initiatorname taken from iBFT BIOS tables. +## +## DO NOT EDIT OR REMOVE THIS FILE! +## If you remove this file, the iSCSI daemon will not start. +## Any change here will not be reflected to the iBFT BIOS tables. +## If a different initiatorname is required please change the +## initiatorname in the BIOS setup and call +## /sbin/iscsi-gen-initiatorname -f +## to recreate an updated version of this file. +## +InitiatorName=$iSCSI_INITIATOR_NAME +EOF +fi + +if [ ! -f /etc/iscsi/initiatorname.iscsi ] ; then + cat << EOF >> /etc/iscsi/initiatorname.iscsi +## +## /etc/iscsi/iscsi.initiatorname +## +## Default iSCSI Initiatorname. +## +## DO NOT EDIT OR REMOVE THIS FILE! +## If you remove this file, the iSCSI daemon will not start. +## If you change the InitiatorName, existing access control lists +## may reject this initiator. The InitiatorName must be unique +## for each iSCSI initiator. Do NOT duplicate iSCSI InitiatorNames. +EOF + ISSUEDATE="1996-04" + INAME=$(/sbin/iscsi-iname -p iqn.$ISSUEDATE.de.suse:01) + printf "InitiatorName=$INAME\n" >>/etc/iscsi/initiatorname.iscsi + chmod 0600 /etc/iscsi/initiatorname.iscsi +fi + diff --git a/utils/iscsi_offload b/utils/iscsi_offload new file mode 100755 index 0000000..7cd1dad --- /dev/null +++ b/utils/iscsi_offload @@ -0,0 +1,378 @@ +#!/bin/bash +# +# iscsi_offload +# +# Configure iSCSI offload engines for use with open-iscsi +# Usage: +# iscsi_offload [-d | -f | -i | -t ] +# +# Copyright (c) 2011 Hannes Reinecke, SUSE Labs +# This script is licensed under the GPL. +# +# The script creates an open-iscsi interface definition +# in the style -, where matches the +# network interface passed on the commandline. +# If '-t' (test mode) is passed as an option, the script +# will not create nor modify any setting but just print +# the currently active ones. +# +# Currently the script works with Broadcom (bnx2i) and +# Chelsio T3 (cxgbi) iSCSI offload engines. +# Should work with Chelsio T4, but has not been tested. +# ServerEngines (be2iscsi) and QLogic (qla4xxx) can only +# be configured via BIOS, open-iscsi support is still in +# development. +# + +# +# Return codes: +# 0: Success +# 1: Invalid command line parameter +# 2: iSCSI offloading not supported +# 3: Error during module loading +# 4: Cannot configure interface via iscsiadm, use BIOS setup +# 5: internal error running iscsiadm +# +# Output: +# [none|dhcp|ip |ibft] +# where +# : MAC Address of the iSCSI offload engine +# none: No IP configuration set for the iSCSI offload engine +# dhcp: iSCSI offload engine configured for DHCP +# ip: iSCSI offload engine configured with static IP address +# ibft: iSCSI offload engine configured from iBFT values +# + +# +# Figure out the MAC address of the iSCSI offload engine +# corresponding to a NIC from a given PCI device. +# bnx2 is using one PCI device per port for both network and iSCSI offloading +# cxgb3 is using one PCI device for everything. +# +iscsi_macaddress_from_pcidevice() +{ + local path=$1 + local if=$2 + local h + local host + + for h in $path/host* ; do + if [ -d "$h" ] ; then + host=${h##*/} + read netdev < /sys/class/iscsi_host/$host/netdev + if [ "$netdev" = "$IFNAME" ] ; then + read mac < /sys/class/iscsi_host/$host/hwaddress + if [ "$mac" != "00:00:00:00:00:00" ] ; then + echo "$mac" + fi + break; + fi + fi + done +} + +# +# Figure out the MAC address of the iSCSI offload engine +# corresponding to a NIC from a given PCI function. +# It is assumed that the MAC address of the iSCSI offload +# engine is equal of the MAC address of the NIC plus one. +# Suitable for be2iscsi and qla4xxx +# +iscsi_macaddress_from_pcifn() +{ + local path=$1 + local if=$2 + local h + local host + local ifmac + + ifmac=$(ip addr show dev $if | sed -n 's/ *link\/ether \(.*\) brd.*/\1/p') + m5=$(( 0x${ifmac##*:} )) + m5=$(( $m5 + 1 )) + ifmac=$(printf "%s:%02x" ${ifmac%:*} $m5) + for host in /sys/class/iscsi_host/host* ; do + if [ -L "$host" ] ; then + read mac < $host/hwaddress + if [ "$mac" = "$ifmac" ] ; then + echo "$mac" + break; + fi + fi + done +} + +update_iface_setting() { + local iface="$1" + local name="$2" + local value="$3" + + iface_value=$(iscsiadm -m iface -I $iface | sed -n "s/$name = \(.*\)/\1/p") + if [ "$iface_value" = "" ] ; then + iface_value= + fi + if [ "$iface_value" != "$value" ] ; then + if ! iscsiadm -m iface -I $iface -o update -n "$name" -v "$value" ; then + return 1 + fi + fi + return 0 +} + +while getopts di:t options ; do + case $options in + d ) mode=dhcp;; + i ) mode=static + optaddr=$OPTARG + ;; + f ) mode=firmware;; + t ) dry_run=1;; + ?) printf "Usage: %s [-d|-t|-i ipaddr|-f] ifname\n" $0 + exit 1;; + esac +done +shift $(($OPTIND - 1)) + +IFNAME=$1 +ibft_mode="none" + +if [ -z "$IFNAME" ] ; then + echo "No interface specified" + exit 1 +fi + +if [ "$dry_run" ] ; then + if [ "$mode" = "dhcp" ] ; then + echo "'-t' specified, ignoring '-d'" + mode= + elif [ "$mode" = "static" ] ; then + echo "'-t' specified, ignoring '-s'" + mode= + fi +fi + +if [ ! -L /sys/class/net/$IFNAME ] ; then + echo "Interface $IFNAME not found" + exit 1 +fi + +if [ "$optaddr" ] && ! ip route get $optaddr ; then + echo "Invalid IP address $optaddr" + exit 1 +fi +if [ "$dry_run" ] ; then + mode= +fi + + +ifpath=$(cd -P /sys/class/net/$IFNAME; echo $PWD) +pcipath=$(cd -P $ifpath/device; echo $PWD) + +if [ -d $pcipath ] ; then + drvlink=$(readlink $pcipath/driver) + driver=${drvlink##*/} +fi + +if [ -z "$driver" ] ; then + echo "No driver found for interface $IFNAME" + exit 1 +fi + +case "$driver" in + bnx2*) + mod=bnx2i + ;; + cxgb*) + mod=cxgb3i + ;; + be2*) + mod=be2iscsi + ;; + qla*) + mod=qla4xxx + ;; +esac + +if [ -z "$mod" ] ; then + echo "iSCSI offloading not supported on interface $IFNAME" + exit 2 +fi + +# Check if the required modules are already loaded +loaded=$(sed -n "/^$mod/p" /proc/modules) +if [ -z "$loaded" ] ; then + modprobe $mod +fi + +loaded=$(sed -n "/^$mod/p" /proc/modules) +if [ -z "$loaded" ] ; then + echo "Loading of $mod.ko failed, please check dmesg" + exit 3 +fi + +# Get the correct MAC address for the various devices +if [ "$mod" = "bnx2i" ] ; then + mac=$(iscsi_macaddress_from_pcidevice $pcipath $IFNAME) +elif [ "$mod" = "cxgb3i" ] ; then + mac=$(iscsi_macaddress_from_pcidevice $pcipath $IFNAME) +elif [ "$mod" = "be2iscsi" ] ; then + mac=$(iscsi_macaddress_from_pcifn $pcipath $IFNAME) +elif [ "$mod" = "qla4xxx" ] ; then + mac=$(iscsi_macaddress_from_pcifn $pcipath $IFNAME) +fi + +if [ -z "$mac" ] ; then + echo "iSCSI offloading not supported on interface $IFNAME" + exit 2 +fi + +gen_iface="$mod.$mac" +ioe_iface="${IFNAME}-${mod}" + +# Get existing settings +if iscsiadm -m iface -I $ioe_iface > /dev/null 2>&1 ; then + ioe_mac=$(iscsiadm -m iface -I $ioe_iface 2> /dev/null| sed -n "s/iface\.hwaddress = \(.*\)/\1/p") + ioe_mod=$(iscsiadm -m iface -I $ioe_iface 2> /dev/null| sed -n "s/iface\.transport_name = \(.*\)/\1/p") + ipaddr=$(iscsiadm -m iface -I $ioe_iface 2> /dev/null| sed -n "s/iface\.ipaddress = \(.*\)/\1/p") + if [ "$ipaddr" == "" ] ; then + ipaddr= + fi +elif [ "$mod" = "be2iscsi" ] ; then + ioe_mac=$mac + ioe_mod=$mod +else + # Create new interface + iscsiadm -m iface -I $ioe_iface --op=new 2> /dev/null + ioe_mac= + ioe_mod= + ipaddr= +fi + +if [ -z "$dry_run" ] ; then + if [ "$ioe_mac" != "$mac" ] ; then + if [ -n "$ioe_mac" ] ; then + echo "Warning: Updating MAC address on iface $ioe_iface" + fi + update_iface_setting $ioe_iface iface.hwaddress "$mac" + fi + + if [ "$ioe_mod" != "$mod" ] ; then + if [ -n "$ioe_mod" ] ; then + echo "Warning: Update transport on iface $ioe_iface" + fi + update_iface_setting $ioe_iface iface.transport_name "$mod" + fi +elif [ -z "$ipaddr" ] ; then + ipaddr=$(iscsiadm -m iface -I $gen_iface 2> /dev/null| sed -n "s/iface\.ipaddress = \(.*\)/\1/p") + if [ "$ipaddr" = "" ] ; then + ipaddr= + fi +elif [ "$ioe_mod" != "$mod" ] ; then + echo "Warning: Transport mismatch on iface $ioe_iface: $ioe_mod should be $mod" +fi + +# Check iBFT setting +for d in /sys/firmware/* ; do + [ -d $d ] || continue + [ -d $d/ethernet0 ] || continue + iboot_dir=$d +done +if [ -n "$iboot_dir" ] && [ -d "$iboot_dir" ] ; then + for if in ${iboot_dir}/ethernet* ; do + read ibft_mac < $if/mac + [ "$ibft_mac" = "$mac" ] || continue + ibft_origin=0 + [ -f ${if}/origin ] && read ibft_origin < $if/origin + if [ "$ibft_origin" -eq 1 ] ; then + ibft_mode="static" + elif [ "$ibft_origin" -eq 3 ] ; then + ibft_mode="dhcp" + fi + [ -f $if/dhcp ] && read ibft_dhcp < $if/dhcp + if [ -n "$ibft_dhcp" -a "$ibft_mode" != "dhcp" ] ; then + ibft_mode=dhcp + fi + if [ "$ibft_mode" = "dhcp" ] ; then + ibft_ipaddr="0.0.0.0" + ibft_gateway= + ibft_mask= + break + fi + [ -f $if/ip-addr ] && read ibft_ipaddr < $if/ip-addr + [ -f $if/gateway ] && read ibft_gateway < $if/gateway + [ -f $if/subnet-mask ] && read ibft_mask < $if/subnet-mask + break + done +fi + +if [ -z "$optaddr" ] && [ "$ibft_ipaddr" ] ; then + optaddr=$ibft_ipaddr +fi + +# Check if the interface needs to be configured +if [ -z "$mode" ] ; then + if [ "$ibft_mode" != "none" ] ; then + echo "$mac ibft" + mode="ibft" + elif [ -z "$ipaddr" ] ; then + echo "$mac none" + mode="none" + elif [ "$ipaddr" = "0.0.0.0" ] ; then + echo "$mac dhcp" + ipaddr= + mode="dhcp" + else + echo "$mac ip $ipaddr" + mode="static" + fi + [ "$dry_run" ] && exit 0 +elif [ "$mode" = "dhcp" ] ; then + if [ "$ipaddr" = "0.0.0.0" ] ; then + echo "$mac dhcp" + exit 0 + fi + optaddr="0.0.0.0" +elif [ "$mode" = "static" ] && [ "$ipaddr" = "$optaddr" ] ; then + echo "$mac ip $ipaddr" + exit 0 +fi + +if [ "$mod" = "be2iscsi" ] ; then + exit 4 +fi + +if ! update_iface_setting $ioe_iface iface.ipaddress "$optaddr" ; then + echo "Failed to set IP address: $?" + exit 1 +fi +if ! update_iface_setting $gen_iface iface.ipaddress "$optaddr" ; then + echo "Failed to set IP address for generic interface: $?" + exit 1 +fi + +if ! update_iface_setting $ioe_iface iface.gateway "$ibft_gateway" ; then + echo "Failed to set gateway address: $?" + exit 1 +fi + +if ! update_iface_setting $gen_iface iface.gateway "$ibft_gateway" ; then + echo "Failed to set gateway address for generic interface: $?" + exit 1 +fi + +if ! update_iface_setting $ioe_iface iface.subnet_mask "$ibft_mask" ; then + echo "Failed to set subnet mask: $?" + exit 1 +fi + +if ! update_iface_setting $gen_iface iface.subnet_mask "$ibft_mask" ; then + echo "Failed to set subnet mask for generic interface: $?" + exit 1 +fi + +if [ "$mod" = "qla4xxx" ] ; then + iscsiadm -m iface -H $mac -o applyall +fi +ip link set dev $IFNAME up + +exit 0 + diff --git a/utils/md5.c b/utils/md5.c index 53956c6..cbe8d08 100644 --- a/utils/md5.c +++ b/utils/md5.c @@ -133,7 +133,7 @@ MD5Final(md5byte digest[16], struct MD5Context *ctx) byteSwap(ctx->buf, 4); memcpy(digest, ctx->buf, 16); - memset(ctx, 0, sizeof (ctx)); /* In case it's sensitive */ + memset(ctx, 0, sizeof (*ctx)); /* In case it's sensitive */ } #ifndef ASM_MD5 diff --git a/utils/open-isns/bitvector.c b/utils/open-isns/bitvector.c index 3e23f26..9d66276 100644 --- a/utils/open-isns/bitvector.c +++ b/utils/open-isns/bitvector.c @@ -327,10 +327,10 @@ isns_bitvector_is_empty(const isns_bitvector_t *bv) wp = bv->ib_words; end = wp + bv->ib_count; while (wp < end) { - unsigned int base, rlen; + unsigned int rlen; - base = *wp++; - rlen = *wp++; + rlen = wp[1]; + wp += 2; while (rlen--) { if (*wp++) @@ -462,11 +462,10 @@ isns_bitvector_foreach(const isns_bitvector_t *bv, wp = bv->ib_words; end = wp + bv->ib_count; while (wp < end) { - unsigned int base, rlen, bits; + unsigned int base, rlen; base = wp[0]; rlen = wp[1]; - bits = rlen * 32; wp += 2; while (rlen--) { @@ -492,11 +491,10 @@ isns_bitvector_dump(const isns_bitvector_t *bv, isns_print_fn_t *fn) wp = bv->ib_words; end = wp + bv->ib_count; while (wp < end) { - unsigned int base, rlen, bits; + unsigned int base, rlen; base = wp[0]; rlen = wp[1]; - bits = rlen * 32; wp += 2; fn(" <%u:", base); @@ -538,11 +536,10 @@ isns_bitvector_print(const isns_bitvector_t *bv, wp = bv->ib_words; end = wp + bv->ib_count; while (wp < end) { - unsigned int base, rlen, bits; + unsigned int base, rlen; base = wp[0]; rlen = wp[1]; - bits = rlen * 32; wp += 2; while (rlen--) { diff --git a/utils/open-isns/dd.c b/utils/open-isns/dd.c index b392036..c2dcd10 100644 --- a/utils/open-isns/dd.c +++ b/utils/open-isns/dd.c @@ -599,10 +599,9 @@ isns_dd_insert(isns_dd_t *dd) void isns_dd_list_resize(isns_dd_list_t *list, unsigned int last_index) { - unsigned int new_size, cur_size; + unsigned int new_size; isns_dd_t **new_data; - cur_size = LIST_SIZE(list->ddl_count); new_size = LIST_SIZE(last_index + 1); if (new_size < list->ddl_count) return; diff --git a/utils/open-isns/doc/isnsadm.8 b/utils/open-isns/doc/isnsadm.8 index c3e2b83..88ec4cf 100644 --- a/utils/open-isns/doc/isnsadm.8 +++ b/utils/open-isns/doc/isnsadm.8 @@ -25,6 +25,10 @@ isnsadm \- iSNS client utility .PP .B isnsadm .RB [ ... ] +.RI --dd-deregister " dd-id attr=value +.PP +.B isnsadm +.RB [ ... ] .RI --enroll " client-name attr=value .PP .B isnsadm @@ -452,6 +456,18 @@ Note, in order to add members to an existing domain, you must specify the domain's numeric ID. The domain's symbolic name is not a valid handle when referring to a discovery domain. .\"--------------------------- +.SS Discovery Domain Deregistration mode +In this mode, you can deregister a discoery domain previously registered. +Only the node which registered a discovery domain in the first place is +permitted to remove it, or any of its members. (Control +nodes are not bound by this restriction). +.PP +In Discovery Domain deregistration mode, the argument list consists of +the Discovery Domain ID, followed by a list of +.IB attr = value +pairs. Discovery Domain Deregistration supports the same set of attributes as +query mode. +.\"--------------------------- .SS Client Enrollment This mode only works when the server recognizes the client as having control node capabilities, which is possible in diff --git a/utils/open-isns/isnsadm.c b/utils/open-isns/isnsadm.c index fadd87d..db34f8f 100644 --- a/utils/open-isns/isnsadm.c +++ b/utils/open-isns/isnsadm.c @@ -272,6 +272,8 @@ usage(int exval, const char *msg) "\nThe following actions are supported:\n" " --register Register one or more objects\n" " --deregister Deregister an object (and children)\n" + " --dd-register Register a Discovery Domain (and members)\n" + " --dd-deregister Deregister a Discovery Domain (and members)\n" " --query Query iSNS server for objects\n" " --list List all objects of a given type\n" " --enroll Create a new policy object for a client\n" diff --git a/utils/sysdeps/Makefile b/utils/sysdeps/Makefile index 53c10e5..4299164 100644 --- a/utils/sysdeps/Makefile +++ b/utils/sysdeps/Makefile @@ -1,6 +1,7 @@ # This Makefile will work only with GNU make. -CFLAGS += $(OPTFLAGS) $(WARNFLAGS) -O2 -fno-inline -Wall -Wstrict-prototypes -g +CFLAGS ?= -O2 -fno-inline -g +CFLAGS += $(WARNFLAGS) -Wall -Wstrict-prototypes SYSDEPS_OBJS=sysdeps.o -- 2.1.0