Integrating Citrix ICA Client into LTSP

Paul Whittaker (Paul.Whittaker@iname.com)
Director, Cloud Network Technologies Pty Ltd, Australia
Last Update: 11 February 2002

Index

Introduction

This HowTo and the files provided with it will enable you to modify LTSP 3.0 to allow thin clients to connect to a Windows NT4 or Windows 2000 terminal server using the ICA protocol. It has been tested with ICA Client for Linux 6.20.973 against servers running Windows NT4 Terminal Server Edition (SP6) with Metaframe 1.8 (SP2, FR1) and Windows 2000 Server (hotfixes but no SPs) with Metaframe XP 1.0 (SP1, FR1).

Out of the box, Windows NT4 Terminal Server Edition and Windows 2000 Server provide an RDP (Remote Desktop Protocol) service; client workstations must connect to the terminal server using this protocol. RDP is somewhat like X11 combined with XDMCP, but features built-in encryption and compression and has some inherent limitations such as a maximum of 256 colors. Windows NT4TSE implements RDP version 4, and Windows 2000 Server implements RDP version 5. RDP 5 has some minor improvements such as rudimentary load balancing.

Citrix have developed a product known as MetaFrame (formerly known as WinFrame) which integrates smoothly with Windows NT4TSE/2000 Server and implements an alternative protocol called ICA (Independent Computing Architecture), which is essentially a value-added vendor derivative of RDP. ICA is a much more complex protocol than RDP; its principal aim is to increase the granularity of remote access such that individual applications can be "exported" from a terminal server and "imported" seamlessly into the desktop of a remote client, in addition to the traditional "desktop-in-a-window" approach. It also provides many additional features such as client resource sharing, 16 and 24-bit color depth, rudimentary sound support and automatic load balancing, although some of these features cannot be used until additional licenses are purchased.

(Side note: A now-defunct Citrix Metaframe companion product called UNIX Integration Services can be used as an X11-ICA and NAS-ICA bridge. Using this product, you can query your Metaframe server using the X11/XDMCP protocol and get full sound support via the LTSP sound server with absolutely no modification to standard LTSP whatsoever!)

Citrix have adopted the cruel-but-effective marketing strategy of charging like a wounded bull for the Citrix Metaframe server product but making the client product freely available for multiple platforms in order to promote widespread use of the ICA protocol. Hence we can make use of the freely available ICA client for Linux on diskless LTSP clients. However, the licensing agreement for the Citrix ICA Client expressly forbids redistribution of the client, so I cannot provide a "shrink-wrapped" LTSP bolt-on - you must fetch the ICA client yourself (for use at your own site only), and integrate it using these intructions. If you want to bundle ICA Client as part of any kind of distribution you must pay Citrix $$$ for the privilege.

Modes of Access

I have antipicated the need for two modes of operation for the ICA Client:

  1. ICA at Boot-Up

    In this mode of operation the thin client workstation boots directly into a full-screen Windows desktop or full-screen standalone Windows application. The X11 protocol is only used locally on the workstation and is all but invisible to the end-user; all graphical data exchanges with the server are made using the ICA protocol. The thin client looks and feels like a Windows box.

    A possible variation of this is a "dual mode" whereby the LTSP workstation starts up a second X11 display, or second X11 server instance on another virtual terminal, and runs ICA full-screen in this. You then have X11 and ICA displays side-by-side or switchable using VT hot keys. This is not covered in this HowTo.

  2. ICA on Demand

    In this mode of operation the end-user invokes specific ICA services via rsh, which are displayed on the desktop (whether X11, RDP or ICA) in subwindows, just like Local Apps under X11. ICA Client can do this is two different ways; the "Windows-in-a-Box" approach, using a fixed-size window with independent internal subwindow management, or using a "seamless" window (in practice not quite seamless on an X11 desktop, due to irreconcilable differences between X11 and Windows window management) whereby the window appears to be under the control of the local window manager (ie. can be resized, iconified etc).

My Solution

My solution caters to either or both access methods. It consists of a shell wrapper for the main ICA Client binary (wfica) which dynamically generates throwaway ICA config files based on command-line arguments, lts.conf defaults and internal defaults (in order of decreasing priority), and launches wfica against them. The wrapper can be invoked from xinit for ICA at Boot-Up mode, or via rsh (either directly or as a user's shell) for ICA on Demand mode.

Citrix ICA Client is highly configurable, and I cannot hope to cater to every possible permutation of its use in this HowTo. I have made a best effort to cater to most common client- and server-specific configurables. For some options I use deductive logic or autodetection rather than user-defined settings in order to reduce option complexity.

I have not attempted to cache or preset anything to do with authentication (ie. username, password, and domain) because it's too difficult to do this in a sufficiently flexible and secure manner. I want to allow for anonymous access to a stateless thin client as well as the Local Apps scenario. In any case, ICA Client has a connection re-use feature that will prevent the need for an explicit logon under some circumstances.

The wrapper explicitly sets only a bare minimum of client options. Where possible, rather than modify the wrapper to set more options, you should edit the global config files in /opt/ltsp/i386/usr/lib/ICAClient/config to meet any site-specific needs. Unfortunately due to some strange quirks in wfica's config file parsing, certain options (eg. keyboard settings) must be set in the wfclient.ini file created by the wrapper.

Drive, Printer and Com Port Mapping

LSTP lacks the client-side facilities needed to make effective use of ICA Client's drive and printer mapping capabilities, so I have disabled them and do not recommend their use. The desired effect can be achieved through use of the Removable Media contrib for LTSP 3.0 and lp_server respectively, using standard FTP and JetDirect/AppSocket protocols that are supported by native Windows drivers. This is perhaps not as elegant as the ICA Client methods, but it's a lot more thin-client friendly.

If serial ports are detected (ie. if the serial.o kernel module is preloaded), my script will automatically map up to two of them onto COM1: and COM2: on the server using the ICA com port mapping feature. If you want to use serial expansion cards, you're on your own.

Audio Mapping

To enable audio in ICA you must preload the relevant sound modules using MODULE_nn options, and then either set ICA_AUDIO to Y or invoke the wrapper using the +a switch. Note that ICA audio does not require the LTSP Sound package and does not require that you set SOUND to Y in lts.conf. The LTSP Sound package provides a network audio protocol abstraction (NAS) that duplicates a built-in capability of the ICA protocol in an incompatible fashion. It's safer not to install it, to avoid possible audio device contention.

Unfortunately ICA Client doesn't implement a software mixer (or synthesizer) under Windows, even if your hardware is capable of it, so you'll have to resort to hardware volume controls. If your default software mixer setting is too quiet, you may need to install aumix on your LTSP workstation so that you can increase the volume at boot-time.

ICA audio has three bandwidth-greediness settings, with worsening sound quality at lower bandwidth usage. My wrapper uses the maximum and minimum settings only, based upon a user-defined high or low available bandwidth setting. Note, however, that a client request for high audio bandwidth doesn't guarantee that it will get it. There is also a server-side setting (medium by default), and the lower of the two values is used. If you are getting poor sound quality even on a high-bandwidth network, check your server setting.

Don't expect miracles from ICA audio. The best you can hope for is laggy WAV file playback without too much white noise. No network audio protocol can ever hope to match local audio playback for quality, and the ICA implentation isn't very good as network audio protocols go. Unless you really need it, it's better to leave it off to reduce server load and bandwidth consumption.

Theory of Operation

ICA at Boot-Up

  1. In accordance with the /etc/lts.conf RCFILE_nn setting, /etc/rc.local invokes /etc/rc.d/rc.ica. rc.ica rewrites the /tmp/start_ws file created by rc.local such that it launches the X server via /usr/X11R6/bin/xinit rather than directly, and omits the XDMCP query argument. rc.ica then creates a /tmp/xinitrc script that repeatedly launches the ICA Client wrapper /usr/bin/ica against the ICA_SERVICE specified in lts.conf, in a never-ending loop.
  2. rc.local exits and init enters runlevel 5, executing /tmp/start_ws.
  3. xinit starts and spawns the X server as specified by its command line parameters, and then executes /.xinitrc, which is a symbolic link to /tmp/xinitrc.
  4. .xinitrc runs the wrapper script ica with the -F (fullscreen) and -quiet options and an ICA service name. ica constructs files in /tmp according to command line arguments, lts.conf defaults and internal defaults, and then launches /usr/lib/ICAClient/wfica against these files using command-line arguments.
  5. wfica runs fullscreen and initiates an ICA connection to the specified service.
  6. When the ICA session terminates, .xinitrc restarts it as per step 3 above. This is somewhat tidier and more efficient than exiting and allowing init to respawn xinit and thereby restart the X server.

ICA on Demand

  1. The user, or a program initiated by the user, runs an rsh command on the server to invoke the ICA Client wrapper with the required options. For example, using Local Apps from a Linux server, you might use something like "rsh -n ws1 ica Word +a &" to invoke a published application called Word, with audio enabled, from your workstation ws1. Without Local Apps from a Windows server, you might use "rsh ws1 -l ica -n Word +a" to achieve the same result by means of a dummy account ica whose shell is the ICA wrapper.
  2. xinetd on the LTSP workstation spawns /usr/sbin/in.rshd to handle the request.
  3. in.rshd uses PAM libraries to check whether the command is allowed, according to rules in /etc/pam.d/rsh (which in turn consult /etc/hosts.equiv and ~/.rhosts as required). If allowed, in.rshd invokes the command with the arguments provided as the calling user or the user specified with -l. The actual command issued is "user-shell -c program 'arguments'", so using the above examples you would have something like "/bin/bash -c ica 'Word +a'" and "/usr/bin/ica -c Word '+a'" respectively.
  4. ica constructs files in /tmp according to arguments (ignoring a leading "-c" if present), lts.conf defaults and internal defaults, and then launches /usr/lib/ICAClient/wfica against these files using command-line arguments. The display used is implictly ":0" unless overridden via the command line; if not using Local Apps the display must allow access from any user on the workstation (either by DISABLE_ACCESS_CONTROL=Y in lts.conf or by creation of an /etc/X0.hosts file, which is a bit more secure).

Installation and Setup

Requirements: ltsp_core, ltsp_kernel, and ltsp_x_core LTSP 3.0.x packages. If using ICA on Demand mode and not using ICA at Boot-Up mode you will need ltsp_x_fonts also. For ICA on Demand mode you will normally need ltsp_local_apps installed and enabled, but it is possible to set up anonymous access without enabling Local Apps (see below).

Installation Steps Common to All Modes

Installation Steps for ICA at Boot-Up

Installation Steps for ICA on Demand

Installation Steps for ICA on Demand without Local Apps

lts.conf Options

The ICA wrapper script makes use of several new lts.conf options:

ICA_AUDIO
One of: Y or N (default N). Y enables ICA audio (if an audio device is available), N disables.

ICA_BANDWIDTH
One of: high or low (default high). high tells the wrapper to optimize for LAN bandwidths, and low for WAN/modem bandwidths, through use or disuse of compression and an audio bandwidth cap (if applicable).

ICA_BROWSER_ADDR
An IP address or hostname, optionally appending :port where port is the port number for the HTTP/HTTPS service, at which the ICA browser service can be found. The default is the local broadcast address if the protocol is UDP, ica:80 if HTTP and ica:443 if HTTPS.

ICA_BROWSER_PROTO
One of: udp, http or https (default http). This is the protocol that ICA Client will use to discover addresses for published applications.

ICA_COLOR
One of: max or min (default max). max makes the wrapper try to match the color depth of the X11 display in the ICA window. min tells the wrapper to set a pessimistic safe value, to avoid colormap conflicts in an 8-bit display or unlicensed feature warnings at greater depths (specifically 16 or 256 colors respectively). The wrapper always tells wfica to use private colormaps where necessary for 8-bit displays, because the "approximate colors" setting doesn't work in an unmanaged display (ie. ICA at Boot-up mode).

ICA_ENCRYPTION
One of: basic, 40, 56, 128 and 128login (default: basic).

ICA_WINSIZE
A string of the form NxN specifying the X and Y dimensions, in pixels, of the ICA window (default 800x600). This value is only meaningful for ICA on Demand connections to hosts - not published applications - and when not using fullscreen mode. The wrapper will use "seamless" mode for published applications in preference to a fixed geometry.

The wrapper will also use the RAMDISK_SIZE to determine the maximum amount of space the ICA Client bitmap cache should be allowed to use (one quarter of RAMDISK_SIZE). The cache is shared by all users of the LTSP thin client and persists for the run-time of the client, so it is probably in your interest to increase this value if you can spare the RAM.

There is one additional option that is not used by the wrapper itself, but rather by the rc.d script that invokes it (via xinit) at boot-time, when using the ICA at Boot-up mode of operation:

ICA_SERVICE
A hostname, IP address or published application name (default is the value of SERVER or 192.168.0.254 if unset). If you have two or more ICA servers and you wish to load balance, you should publish the desktop (ie. explorer.exe) and specify it by name here rather than use hostnames.

Wrapper Syntax

The wrapper requires a mandatory service name, which may be either a hostname, IP address or published application name. The wrapper will invoke a custom C binary to distinguish between the two by using a gethostbyname(2) system call to attempt to resolve the identifier into an IP address - if it can't, it will assume it is an application. It is therefore vital that no application has the same name as a known host or host alias. It is also important for parsing reasons that applications do not contain shell metacharacters or words beginning with minus (-) or plus (+). Applications may contain spaces, and need not be double- or single-quoted.

Counter-conventionally, the mandatory service argument must appear before any option switches. This is deliberate, to avoid parsing problems that might otherwise occur with some implementations of rsh (eg. Windows rsh) if you use the wrapper as the shell of a dummy user. The sense of the audio options is also somewhat counter-conventional, for consistency with bandwidth and color options; think of "-" as "down" and "+" as "up".

All wrapper options have a long (human-readable) form and a short form; you may use either. After the wrapper options you may append one or more options for the wfica binary itself. One such useful option is the -param switch, supported by ICA Client 6.20 and above. If you have the necessary server-side feature release and license installed, this allows you to pass parameters (such as a filename) through to a published application. This means that you can potentially set up an rsh command as a file-type association, and have your LTSP thin client automagically fire up a Windows application and load the document as a browser or mailer detach action - how cool is that!

Wrapper syntax is briefly as follows:

Usage: ica service [options] [wfica arguments...]

Options:
        --browseraddr,-A hostname[:port]        ICA browser address
	--noaudio,-a				Disable audio
	--audio,+a				Enable audio
        --lowbandwidth,-b                       Optimize for low bandwidth
        --highbandwidth,+b                      Optimize for high bandwidth
        --mincolor,-c                           Limit color usage to avoid
                                                colormap conflicts or
                                                unsupported feature warnings
        --maxcolor,+c                           Maximize available colors
        --encryption,-e basic|40|56|128[login]  Encryption level
        --fullscreen,-F                         Display using the entire screen
        --browserproto,-p http|https|udp        ICA browser protocol
        --winsize,-s widthxheight               Window dimensions
        --help,-?                               Display this message

Defaults are derived from the relevant lts.conf variables where applicable, and internal defaults otherwise (see lts.conf section above). --fullscreen is not the default, and if specified overrides any --winsize or ICA_WINSIZE option.