Sunday, October 5, 2025

How to create an iSCSI target on FreeBSD

Creating an iSCSI target on FreeBSD, particularly with ZFS, is typically done by exporting a ZFS Volume (ZVOL), which is a block-level device, not a ZFS filesystem/dataset. iSCSI targets present themselves as raw block devices to the initiator (client), which is the intended use for a ZVOL.

Here is a step-by-step guide to create an iSCSI target on FreeBSD 14.3 using a ZFS Volume and the CAM Target Layer (CTL) daemon, ctld. 

Create a ZFS Volume (ZVOL)

A ZFS Volume acts as a raw block device and is the correct backing store for an iSCSI Logical Unit Number (LUN). 

Check my ZFS Pools

 root@bhyve01:/STORAGE-DATA # zpool list  
 NAME           SIZE  ALLOC   FREE  CKPOINT  EXPANDSZ   FRAG    CAP  DEDUP   HEALTH  ALTROOT  
 OS-DATA        136G  87.6M   136G        -         -     0%     0%  1.00x   ONLINE  -  
 STORAGE-DATA  2.72T  29.3G  2.69T        -         -     8%     1%  1.00x   ONLINE  -  
 root@bhyve01:/STORAGE-DATA #   

I will use ZFS POOL STORAGE-DATA for iSCSI volumes. Let's list main (top level) ZFS datasets in ZSF POOL STORAGE-DATA

 root@bhyve01:/STORAGE-DATA # zfs list -d 1 STORAGE-DATA   
 NAME                           USED  AVAIL  REFER  MOUNTPOINT  
 STORAGE-DATA                  19.5G  1.74T  44.0K  /STORAGE-DATA  
 STORAGE-DATA/bhyve-datastore  19.5G  1.74T  9.43G  /STORAGE-DATA/bhyve-datastore  
 root@bhyve01:/STORAGE-DATA #   

Create ZFS Volume

Now we can create ZFS Volume (aka ZVOL).

zfs create -V 50G -o volmode=dev STORAGE-DATA/iscsi_lun_bhyve_01 

The -V flag specifies the volume size. It's recommended to set volmode=dev for iSCSI. 

 root@bhyve01:/STORAGE-DATA # zfs create -V 50G -o volmode=dev STORAGE-DATA/iscsi_lun_bhyve_01   
 root@bhyve01:/STORAGE-DATA #   

Verify created Volume

 root@bhyve01:/STORAGE-DATA # zfs list -d 1 STORAGE-DATA  
 NAME                              USED  AVAIL  REFER  MOUNTPOINT  
 STORAGE-DATA                     70.3G  1.69T  44.0K  /STORAGE-DATA  
 STORAGE-DATA/bhyve-datastore     19.5G  1.69T  9.43G  /STORAGE-DATA/bhyve-datastore  
 STORAGE-DATA/iscsi_lun_bhyve_01  50.8G  1.74T  24.0K  -  
 root@bhyve01:/STORAGE-DATA #  

The new ZVOL will be accessible as a block device under /dev/zvol/.

 root@bhyve01:/STORAGE-DATA # ls -la /dev/zvol/STORAGE-DATA/*  
 crw-r----- 1 root operator 0xc0 Oct 5 18:31 /dev/zvol/STORAGE-DATA/iscsi_lun_bhyve_01  
 root@bhyve01:/STORAGE-DATA #  

The path is what we'll use in the ctl.conf file.

Configure the CTL iSCSI Target

The iSCSI target configuration is managed by the ctld daemon and is defined in the /etc/ctl.conf file.

Create or edit the configuration file

Use your preferred text editor (like vi or ee) to create /etc/ctl.conf.

vi /etc/ctl.conf 

The configuration file should be readable only by root, as it may contain secrets (though this example uses no authentication). 

chmod 600 /etc/ctl.conf 

Paste the following content, replacing the placeholder values as necessary

Portal Group (pg0) - defines the network interfaces and port on which the target listens. 0.0.0.0 listens on all IPv4 addresses.

Target - defines the iSCSI Qualified Name (IQN). The IQN must be unique and follow the format: iqn.YYYY-MM.reverse.domain:unique_name.

LUN - The Logical Unit Number (LUN 0 is standard) that maps to the ZVOL block device path.

Below is my configuration file ...

# /etc/ctl.conf
# Define a Portal Group (where to listen for connections)
portal-group pg0 {
  # Listen on two IPv4 addresses on the default iSCSI port (3260)
  listen 192.168.24.11:3260
  listen 192.168.25.11:3260
  # Listen on IPv6 addresses on the default iSCSI port (3260) is disabled
  #listen [::]:3260
  # Allow discovery without CHAP authentication
  discovery-auth-group no-authentication
}
 
# Define the iSCSI Target (the server-side identifier)
target iqn.2025-10.cz.uw:home.zfs01 {
  # Alias is a friendly name
  alias "ZFS01 ZVOL iSCSI Target"

  # Use the defined portal group
  portal-group pg0

  # No authentication for all initiators (for simplicity, generally not recommended)
  auth-group no-authentication

  # Define the LUN (Logical Unit Number)
  lun 0 {
    # Path to the ZFS Volume device
    path /dev/zvol/STORAGE-DATA/iscsi_lun_bhyve_01
  }
}

It is best practice to set file permissions to be readable just by root, because the file can contain secrets (if using CHAP).

chmod 600 /etc/ctl.conf 

Enable, Start, and Manage the iSCSI Target Service

Use the sysrc command to enable the ctld service and then start it.

Enable the service to start on boot

sysrc ctld_enable="YES"

Start the service

service ctld start

Reload service config

If you make changes to /etc/ctl.conf later, you can apply them without disconnecting clients using

service ctld reload 

Verification

Check the service status

service ctld status

Check for listeners

Confirm that the iSCSI port is open.

netstat -na | grep 3260

You should see output similar to this:

 root@bhyve01:~ # netstat -na | grep 3260  
 tcp4    0   0 192.168.25.11.3260   *.*          LISTEN     
 tcp4    0   0 192.168.24.11.3260   *.*          LISTEN     
 root@bhyve01:~ #   

Check the exported LUNs

Use the ctladm utility to view the targets and LUNs.

ctladm devlist 

 root@bhyve01:~ # ctladm devlist   
 LUN Backend    Size (Blocks)   BS Serial Number  Device ID      
  0  block          104857600  512 MYSERIAL0000   MYDEVID0000     
 root@bhyve01:~ #  

FreeBSD server is now configured as an iSCSI target, exporting the ZFS Volume as a raw block device that can be connected to by iSCSI initiators on your network

No comments:

Post a Comment