Tag: networking

Configuring ISCSI Targets and Initiators on Fedora 16


Configuring ISCSI is simple, but the configuration options can be complex.  This is a simple illustration of how you can configure an ISCSI target (storage space on a server) and connect to it using and initiator (client that will use the space).  Everything will be as trim as possible, with basic IP restriction and simple CHAP security.  If you want to get a bit fancier just look up the options for the various commands that are used most things that I’ve wanted to do have been simple modifications to this basic pattern.

For this example I will be using a virtual machine dev01 as the target that hosts the storage and a virtual machine dev02 as the initiator that connects to and uses the storage on dev01.  Everything done in this example is done while logged in as root on the respective system.


Configuring the Target

Setting up the local firewall

If you are using netfilter (note the change to the firewalld service in fedora 16 from the iptables service in fedora 15) then you will need to allow incoming TCP connections to the port configured for ISCSI.  The default port is 3260 and used throughout this post.  You can change it, but if you do you will need to specify the port in all connection commands and change the port opened in netfilter.  I used the following commands to log inbound connections, open the port and save it so that it will persist through reboots.

[root@dev01 ~]# iptables -I INPUT 3 -p tcp --dport 3260 -m state --state NEW,ESTABLISHED -j ACCEPT
[root@dev01 ~]# iptables -I INPUT 3 -p tcp --dport 3260 -m limit --limit 5/m --limit-burst 7 -j LOG --log-prefix="iptables-iscsi: "
[root@dev01 ~]# iptables-save > /etc/sysconfig/iptables

Using SELinux

If you are using SELinux then you may have to configure SELinux to allow tgtd access to the file.  To do this the simplest way is to set SELinux to permissive complete the installation and testing then run audit2allow to create a new SELinux module based on the tgtd alerts in the audit file.  So start by setting your SELinux status to permissive.

[root@dev01 ~]# setenforce 0

Once you have completed all of the configuration items in the following sections you should then run the audit2allow script for the tgtd service entries. If you are using files as storage rather than devices then you could just set the labels on the files to ‘system_u:object_r:tgtd_var_lib_t:s0′. However if you still have trouble then I suggest that you perform the following anyway.

[root@dev01 ~]# cat /var/log/audit/audit.log | audit2allow -M iscsi-disk1.pol
[root@dev01 ~]# semodule -i iscsi-disk1.pol.pp

Creating the space

In ISCSI terms the target is the server on which the storage space actually resides.  The storage space can be anything from a file on a disk system, a dedicated physical device, a RAM disk or even another network mounted storage device.  In this case to prepare the device I will be creating a file for the storage space.

[root@dev01 ~]# mkdir /iscsi-disks
[root@dev01 ~]# dd if=/dev/zero of=/iscsi-disks/iscsi-disk1 bs=1M count=2000

Installing the SCSI tools and services

First you need to install the tools and services required with their associated dependencies.  Once the service has been installed it needs to be turned on and set to turn on at start up.

[root@dev01 ~]# yum -y install scsi-target-utils
[root@dev01 ~]# service tgtd start
[root@dev01 ~]# chkconfig tgtd on

Creating the ISCSI device

Next we get to actually configure the resource itself.  This involves creating the ISCSI device and adding logical units (LUNs) to it.  In this case we only add 1LUN but you could add more. The details for the ISCSI device are held in a database in ‘/var/lib/iscsi/nodes/’ and are manipulated and viewed using the ‘tgtadm’ tool. In the listing below the last command illustrates how to view the changes that you have just made.

We also create a name for the ISCSI device.  The name could be anything but there is a standard to be applied that keeps us out of name collision difficulties.  The ISCSI standard specifies ‘iqn.yyyy-mm.<reversed domain name>[:identifier]‘ as the standard naming convention.  So in this case our device is named ‘iqn.2012-01.nz.net.chesterproductions:dev01′.

[root@dev01 ~]# tgtadm --lld iscsi --mode target --op new --tid=1 --targetname iqn.2012-01.nz.net.chesterproductions:dev01
[root@dev01 ~]# tgtadm --lld iscsi --mode logicalunit --op new --tid 1 --lun 1 -b /iscsi-disks/iscsi-disk1
[root@dev01 ~]# tgtadm --lld iscsi --mode target --op show

So that was the way to do it manually. If you want to have tgtd automatically load your device when it starts then you need to add an entry in to the ‘/etc/tgt/targets.conf’ file. The file installed by default is full of examples that are commented out. Given that there is so much fine information I’ll be brief and just provide an example that works for me. The one warning that I would give though is that this will be executed when tgtd starts, if you are like me and prefer to control when this appears then you may like to script the manual solution above somewhere more convenient for you.

<target iqn.2012-01.nz.net.chesterproductions:dev01>
    backing-store /iscsi-disks/iscsi-disk1
    incominguser auser secret
#    outgoinguser anotheruser anothersecret
</target>

 Configuring access to the device

Finally we configure the user details (CHAP target authentication in this case) and allow connections from any IP address opening it up to be accessed by the initiator (client that uses the storage space).  Once this is done we are ready to configure and hook up our initiator. Note that this configuration will not persist between machine restarts but can be scripted to do so.

[root@dev01 ~]# tgtadm --lld iscsi --mode account --op new --user auser --password secret
[root@dev01 ~]# tgtadm --lld iscsi --mode account --op bind --tid 1 --user auser
[root@dev01 ~]# tgtadm --lld iscsi --mode target --op bind --tid 1 -I ALL
[root@dev01 ~]# tgtadm --lld iscsi --mode account --op show


Configuring the Initiator (client)

Installing the ISCSI tools and services

First you need to install the tools and services required with their associated dependencies.  Once the service has been installed it needs to be turned on and set to turn on at start up.

[root@dev02 ~]# yum -y install iscsi-initiator-utils
[root@dev02 ~]# service iscsid start
[root@dev02 ~]# chkconfig iscsid on

Creating the local ISCSI device

Once the services and tools have been installed you can create the ISCSI local device.  This is all done through the ‘iscsiadm’ tool and like the ‘tgtadm’ tool on the target the ‘iscsiadm’ tool manipulates the database in ‘/var/lib/iscsi/nodes/’.  You can use the command ‘iscsiadm -m node’ to view the discovered nodes in the database. There are a number of steps to the configuration, but basically you just need to discover the target – which puts its details in the database, configure the authentication details and then log in.  At the end of this process ‘sfdisk’ should recognise a new ‘/dev/sdX’ device provided by ISCSI that it can format and use like any other.

[root@dev02 ~]# iscsiadm -m discovery -t st -p dev01
Starting iscsid:                                           [  OK  ]
192.168.122.11:3260,1 iqn.2012-01.nz.net.chesterproductions:dev01
[root@dev02 ~]# iscsiadm -m node
192.168.122.11:3260,1 iqn.2012-01.nz.net.chesterproductions:dev01
[root@dev02 ~]# iscsiadm -m node --targetname "iqn.2012-01.nz.net.chesterproductions:dev01" --portal dev01 --op update --name node.session.auth.authmethod --value CHAP
[root@dev02 ~]# iscsiadm -m node --targetname "iqn.2012-01.nz.net.chesterproductions:dev01" --portal dev01 --op update --name node.session.auth.username --value auser
[root@dev02 ~]# iscsiadm -m node --targetname "iqn.2012-01.nz.net.chesterproductions:dev01" --portal dev01 --op update --name node.session.auth.password --value password
[root@dev02 ~]# iscsiadm -m node --targetname "iqn.2012-01.nz.net.chesterproductions:dev01" --portal dev01 --op update --name node.session.auth.password --value secret
[root@dev02 ~]# iscsiadm -m node --targetname "iqn.2012-01.nz.net.chesterproductions:dev01" --portal dev01 --login

Format and mount the new disk

Next we make the device usable in just the same way as you would any other device.  There’s no rocket science here.

[root@dev02 ~]# fdisk /dev/sda
[root@dev02 ~]# mkfs.ext4 /dev/sda1
[root@dev02 ~]# mkdir /data
[root@dev02 ~]# mount /dev/sda1 /data

Configure the disk to auto mount at boot

To make the device auto mount at boot all you need to do is add a standard entry to ‘/etc/fstab’ with the parameters of ‘defaults,auto,_netdev’.  That’s it you can now use your ISCSI storage just like the on board disk physically in your machine.

If you have more than one disk then the ‘/dev/sdX/’ link is not guaranteed to be assigned to the same disk each time. Each disk takes the first available device name as it is connected. To mount specific disks to specific mount points you need to use the UUID. The UUID is a unique identifier assigned tot he disk whent he file system is created. To locate the UUID you can use the ‘blkid’ command as seen below.

Alternatively you can use the logical volume manager and it will take care of all of this for you.

[root@dev02 ~]# blkid | grep sda1
/dev/sda1: UUID="d894a751-93a4-4739-806b-b1f5fd30b4e1" TYPE="ext4"
[root@dev02 ~]# vim /etc/fstab
...
UUID=d894a751-93a4-4739-806b-b1f5fd30b4e1 /data ext4 defaults,auto,_netdev 0 0


Other Places to Look for Help

There are several other places that I used to look for help.  These include the local man pages and the help pages that were installed with the software.  The locally installed help pages have similar useful examples but go way beyond the scope of this simple introductory example.  The following references were especially useful.

http://fedoraproject.org/wiki/Scsi-target-utils_Quickstart_Guide

http://www.howtoforge.com/using-iscsi-on-fedora-10-initiator-and-target

http://linux.die.net/man/8/tgtadm

http://linux.die.net/man/8/iscsiadm

http://www.open-iscsi.org/

http://iscsitarget.sourceforge.net/


A Brief Linux Sockets Connect Illustration

For a little while I’ve been playing with sockets in C now and have come up with the following succinct example of connecting.  Note that the connection is fairly flexible with regards to protocol and transport type.  It really is simply there to make the connection to somewhere else with as few questions asked.  It will involve DNS and service lookups if you provide names instead of the network address and port.  If you want to catch exceptions you need to clear the standard error variable and check it yourself after receiving a fail return value.  It won’t, or at least shouldn’t, report any exceptions because for my current purposes failures are not really exceptions just dead ends on some of many paths.  I tried to keep it simple too, have fun.


/* connect_to_socket
 *
 * connect to a socket using an initialised addrinfo structure,
 *
 * info is an initialised addrinfo structure
 * sock is a pointer to the location to store the socket descriptor when opened
 *
 * returns 0 if successful
 */
int connect_to_socket(const struct addrinfo *info, int* sock) {

    /* open socket */
    *sock = socket(info->ai_family, info->ai_socktype, info->ai_protocol);
    if (*sock < 1) return -1;

    /* connect to the server*/
    if (connect(*sock, info->ai_addr, info->ai_addrlen) == 0)
        return 0;

    /* clean up on failure */
    close(*sock);
    return -1;
}

/* connect_to_server
 *
 * connect to a server using the server name or adress and port or service name
 *
 * server is a string containing either the name or address of the server
 * port is a string containing either the service name or port number to use
 * sock is a pointer to the location to store the socket descriptor when opened
 *
 *  returns 0 if successful
 */
int connect_to_server(const char* server, const char* port, int* sock) {

    struct addrinfo hints, *info = NULL, *list = NULL;
    int e = 0;

    /* initialise the hints for retrieving the address details */
    memset(&hints, 0, sizeof (hints));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE;
    hints.ai_protocol = 0;

    /* get the address if the server to connect to */
    e = getaddrinfo(server, port, &hints, &list);
    if (e != 0) return -1;

    /* connect to the first socket in the list */
    for (info = list; info != NULL; info = info->ai_next)
        if (connect_to_socket(info, sock) == 0)
            break;

    /* clean up and return */
    if (list != NULL) freeaddrinfo(list);
    if (sock > 0) return 0;
    else return -1;
}

Size Matters – the Quest for the ideal MTU

One of the fundamental aspects of communication is the amount of information that can be communicated in one chunk before allowing others to communicate through the same channel; the maximum transmission unit (MTU).

In computer communications the MTU is the amount of data transportable across the lowest layer of the communication stack; Ethernet, FDDI, etc. From the base MTU each layer above in the networking stack breaks the data that it has to send into packets that will fit in the next layer down. For TCP/IP over Ethernet the base Ethernet specification includes a MTU of 1500 bytes (octets in the IETF RFC) not including the Ethernet header and the TCP/IP layers expand their default packet size from 536 and 576 bytes respectively to 1460 and 1500. Effectively the MTU sets the packet size of higher protocols in the OSI model.

Networks are often unable to transport a maximum sized packet. In which case the packets are fragmented either by the sender and receiver or some device(s) in the network in between. There are many reasons for this, for example:

  • Change in base network media i.e. Ethernet to FDDI conversion.
  • Poor line quality.
  • Protocol tunnelling consuming part of the transmission with embedded headers etc.
  • Network device capacity.
  • Data transmission streams with conflicting efficient packet size efficiencies i.e. FTP and VOIP.

Using the standard MTU dynamic shaping protocols normally gets around the these transmission problems. If a device can’t handle a delivered MTU it responds with an ICMP message that informs the sender and advertises its acceptable MTU. However it doesn’t always work and can produce various effects from black hole routing, to delayed responses and network application transmission jitter (very annoying in media streaming applications). Some of the reasons for this are hardend or unsophisticated systems that do not support dynamic MTU discovery or IPsec bridges or other tunnels improperly configured.

To remedy issues it is best to replace the networking infrastructure that is causing the issue with components that are well behaved and can support dynamic MTU. Some times however this is not possible so you have two options, configure a gateway to the device to clamp the MTU to an acceptable size (which you have already found out) or manually configure the sending devices to have an appropriate MTU for the affected route. If you have Windows devices then your best option is to use an existing or introduce a new Unix/Linux based gateway to clamp for you.

Linux (specifically Redhat/Fedora distributions) provides many facilities for the manipulation of MTU. The tools include the ‘/proc’ system components and various applications from the nettools package as well as our old friend ifconfig. So the facilities that I generally use at the start are:

  • ifconfig – to view the MTU and other network information.
  • tcpdump – to look for MTU dynamic shaping ICMP responses.
  • ethtool – to set the MTU and other network card options.
  • route – for fixed MTU setting based on route.
  • ‘/proc/sys/net/ipv4/ip_no_pmtu_disc’ – for fixing the MTU for testing.

ifconfig is the first place to start, it will tell you without any fuss what your network card is set to. A simple ifconfig on the command line will give you something that looks like this:

[root@bluetop ipv4]# ifconfig
 eth0      Link encap:Ethernet  HWaddr 00:0B:DB:19:46:4D
 inet addr:10.0.0.176  Bcast:10.0.0.255  Mask:255.255.255.0
 UP BROADCAST MULTICAST  MTU:1500  Metric:1
 RX packets:0 errors:0 dropped:0 overruns:0 frame:0
 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
 collisions:0 txqueuelen:1000
 RX bytes:0 (0.0 b)  TX bytes:0 (0.0 b)
 Interrupt:10
lo        Link encap:Local Loopback
 inet addr:127.0.0.1  Mask:255.0.0.0
 inet6 addr: ::1/128 Scope:Host
 UP LOOPBACK RUNNING  MTU:16436  Metric:1
 RX packets:6178 errors:0 dropped:0 overruns:0 frame:0
 TX packets:6178 errors:0 dropped:0 overruns:0 carrier:0
 collisions:0 txqueuelen:0
 RX bytes:309060 (301.8 KiB)  TX bytes:309060 (301.8 KiB)
wlan0     Link encap:Ethernet  HWaddr 00:11:50:FD:7B:65
 inet addr:192.168.2.98  Bcast:192.168.2.255  Mask:255.255.255.0
 inet6 addr: fe80::211:50ff:fefd:7b65/64 Scope:Link
 UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
 RX packets:4126 errors:0 dropped:0 overruns:0 frame:0
 TX packets:2447 errors:0 dropped:0 overruns:0 carrier:0
 collisions:0 txqueuelen:1000
 RX bytes:4505043 (4.2 MiB)  TX bytes:393742 (384.5 KiB)

From this you can see that I’ve three interfaces running and eth0 is not being used. However note that the MTU value of each interface is shown here. This is the MTU for the device but not the path. Note also that the MTU for a particular path may be much smaller. In Fedora this value can be set a number of ways but the two simplest are the GUI network tool, which is very simple just look at it as it doesn’t need to be discussed here discussed here, and once again ifconfig:

[root@bluetop ipv4]# ifconfig lo
lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:16430  Metric:1
          RX packets:6417 errors:0 dropped:0 overruns:0 frame:0
          TX packets:6417 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:336104 (328.2 KiB)  TX bytes:336104 (328.2 KiB)
[root@bluetop ipv4]# ifconfig lo mtu 16436
[root@bluetop ipv4]# ifconfig lo
lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:6417 errors:0 dropped:0 overruns:0 frame:0
          TX packets:6417 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:336104 (328.2 KiB)  TX bytes:336104 (328.2 KiB)

In the example above the loopback device has had its MTU set to 16436 from 16430. If we echo 1 to ‘/proc/sys/net/ipv4/ip_no_pmtu_disc’ then MTU dynamic shaping will be turned off and the machine will not send data packets less than the maximum if it has data to fill it. This is some times useful when trying to simulate devices on a network that do not have dynamic MTU built in or disabled.

ip is another tool that can be used for manipulating the MTU. But instead of altering the MTU for the whole device it can be targeted to a specific network. This is especially useful when a network device is shared between different networks. This tool can do all sorts of things so its a good place to start when considering things like tunnelling and complex routing. But here we’ll stick to setting MTU’s on specific routes. For example lets say that we’ve discovered that the network 10.0.0.0 really needs to have an MTU of 1400 but its behind our default gateway and its just not going to work out well for all of the other networks if we clamp to an MTU of 1400 for everyone. So here’s the answer:

[root@bluetop ipv4]# route -ee
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface    MSS   Window irtt
192.168.2.0     *               255.255.255.0   U     0      0        0 wlan0    0     0      0
default         192.168.2.1     0.0.0.0         UG    0      0        0 wlan0    0     0      0
[root@bluetop ipv4]# ip route add 10.0.0.0/24 via 192.168.2.1 mtu 1400
[root@bluetop ipv4]# ip route show
10.0.0.0/24 via 192.168.2.1 dev wlan0  mtu 1400
192.168.2.0/24 dev wlan0  proto kernel  scope link  src 192.168.2.98
default via 192.168.2.1 dev wlan0  proto static
[root@bluetop ipv4]# route -ee
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface    MSS   Window irtt
10.0.0.0        192.168.2.1     255.255.255.0   UG    0      0        0 wlan0    0     0      0
192.168.2.0     *               255.255.255.0   U     0      0        0 wlan0    0     0      0
default         192.168.2.1     0.0.0.0         UG    0      0        0 wlan0    0     0      0

Neat? Well this can be surprisingly useful in many places. In fact the inverse of the previous example is uncommonly useful when dealing with an artificially clamped default path. Consider your ADSL connection which probably uses PPoE (Point to Point over Ethernet) tunnelling. Your local machine undoubtedly uses an Ethernet MTU of 1500 and the ADSL device probably does on the connection to your network provider (which is invariably different from your ISP). However PPoE consumes a little of that 1500 bytes to pack in its tunnelling protocol. This means that on large data transfers your machine is pumping out 1500 bytes of TCP/IP to your ADSL connection and that device is quite likely fragmenting those packets into 2 so that it can get its tunnelling protocol messages into the same packets. Effectively you are adding a small but significant overhead to the whole data transfer process increasing the consumption of your ISP data cap and causing your ADSL device to work harder than required. Yes you are in fact solely responsible for global warming. You crim!

Often in professional networks where PPoE tunnels are used you will see various counter measures to avoid the side effects of the tunnelling overhead. One way is to configure jumbo (oversized MTU) packets on the tunnelling devices. However that really requires you to configure both ends and its not really normal for you to be able to configure your network supplier’s routers. At least not legally. However you can clamp your default routes from your devices and set your local LAN MTU to, well, whatever you want. That way you don’t affect your local LAN performance and you can improve your gateway throughput.

Naturally this is reasonably dangerous so I’m just going to give you the command and let you break your network yourself. I guarantee that you will stuff this up so have a play when you don’t mind spending the time on it; and by the way this sort of stupidity can raise the temperature on your physical devices: so you can actually physically damage your equipment or alter its life expectancy this way. BE WARNED you own ALL of the risk on this one. But do have fun!

ip route add default via 10.0.0.1 mtu 1470

Copyright © 1996-2010 Code Snips. All rights reserved.
iDream theme by Templates Next | Powered by WordPress