Service discovery is the ability to find the services in a network that are necessary for operation. For example, if you install a new desktop system on a network, how do you configure it for the mail server, the default gateway (where non-local connections should be routed), or the available printers? Typically, this information is communicated by a systems administrator and then manually configured at each machine -- a time-consuming process that, given the changes that inevitably occur in networks, requires recurring maintenance.
Therefore, you can see the value in being able to automatically discover services on a network in addition to rediscovering them if they are removed or if they change location. In this article, I'll introduce you to a protocol designed to simplify this process -- the Service Location Protocol (SLP) -- and delve into the protocol's architecture for service advertisement and discovery.
Service discovery is an interesting aspect of modern networks; if you look around, you'll find it in use today. The Dynamic Host Configuration Protocol (DHCP) offers a first level of service discovery that provides not only IP address leases but Domain Name Service (DNS) identification so that URLs, such as domain names, can be successfully resolved.
DNS was extended by RFC 2782 to allow queries of services or protocols that return address records (where the service can be found). This RFC is commonly referred to as the DNS SRV (or DNS for Service Discovery -- DNS SD). Much like a traditional DNS that resolves fully qualified domain names to IP addresses, DNS SRV allows the resolution of services/protocols to IP addresses. For example, the code
_mail._tcp.mtjones.com
_ntp._udp.mtjones.com
requests the IP addresses for the TCP-based SMTP mail server and the User Datagram Protocol-based NTP time server in the domain mtjones.com.
You can find several protocols that provide service discovery in some form or another. Table 1 lists some of the competing technologies. As you can see, there are a number of competing standards. Some are complementary (such as DHCP and DNS which are commonplace today), but others are competitive (such as SLP and Universal Plug and Play, or UPnP).
Table 1. Protocols that provide some form of service discovery
| Protocol | Description |
|---|---|
| DHCP | Dynamic Host Configuration Protocol (IETF Standard) |
| DNS | Domain Name Service (IETF Standard) |
| ZeroConf | Zero Configuration IP Networking (IETF Standard) |
| SSDP | Simple Service Discovery Protocol (IETF Standard, used in Microsoft® UPnP) |
| LDAP | Lightweight Directory Access Protocol (IETF Standard) |
| NIS | Network Information Service |
| Bonjour | Apple® Computer's name for ZeroConf (previously Rendezvous) |
| Jini | Java™ open architecture that includes dynamic discovery (Sun Microsystems) |
| Salutation | Service discovery protocol (Salutation Consortium) |
| SLP | Service Location Protocol (IETF Standard) |
But enough about the competing standards. Now, let's dig in to SLP to see what it does, how it works, and how you can apply it to your applications.
SLP is a dynamic configuration protocol for applications operating in local area networks (LANs) or wide area networks (WANs). As a client, SLP can be used to find services using service types and attributes (such as type printer and attribute color). As a service provider, SLP can be used to advertise a service as well as specific attributes of the available service.
SLP can also operate in a number of architectures, including those with a central server caching service advertisements and a decentralized architecture in which the advertisers respond to queries themselves.
Let's first look at the actors in an SLP-enabled network. SLP has three types of actors:
- User-Agent (UA)
- Service-Agent (SA)
- The optional Directory-Agent (DA)
Although I define these distinctions, it's important to note that an application can both request and advertise services and thus be a UA and an SA at the same time.
The UA is the client in SLP. The UA operates on behalf of an application to identify a given service on the network. The UA does this either by communicating with the DA (if available) or directly to the SA. I explain this concept more when I explore SLP organization.
The SA operates on behalf of applications that provide services on a network. If a DA is present, the SA communicates the services that are available and their location to the DA. Otherwise, the SA responds directly to the UA when a request is made.
Finally, the DA is an optional actor in SLP that serves as a central point for advertisements and queries. If a DA is present, service advertisements that SAs send are cached in the DA. Then, when UAs query for services, the DA responds directly. Having a DA means that it's not necessary for every service provider to advertise services. A single entity can register services on behalf of those services, minimizing their overhead.
Because the DA is an optional element of SLP, the methods for which SLP actors communicate can differ greatly. But first, how do SAs and UAs determine whether a DA is actually present?
Discovering a DA in a network is done two ways in SLP:
- The first, called Active DA Discovery, uses SLP itself to find a DA service. The UA sends out a multicast request for a
service:directory-agent. Any DAs that receive this request respond directly to the UA with their addresses. - The second method for DA discovery is called Passive DA Discovery. In this mode, the DA periodically sends out DA advertisements so that UAs and SAs know that it's there. In the event the UDP response to an Active DA Discovery is lost, the periodic advertisement from Passive DA Discovery automatically resolves the issue.
This architecture gives SLP quite a bit of flexibility, but when the DA is absent, it can be a burden to the SAs.
Discovering services without a DA
Let's start with the decentralized organization of SLP. In this topology, a DA is not present on the network; therefore, UAs communicate their requests indirectly to SAs. See Figure 1 for a graphical description. Because no DA is present, SAs are not able to cache their services and therefore must respond to UA requests directly.
Figure 1. Service discovery without a DA

From Figure 1, the UA sends out a service request identifying the service that is needed. Because no DA is present, the request is sent as a multicast. Any SAs that provide the requested service respond to the User-Agent in a unicast response. The response identifies the service and its location (address and port).
Discovering services with a DA
When a DA is available on the network, UAs and SAs communicate with the DA directly for the their service requests and advertisements. The DA is discovered through either passive or active DA discovery. In the example shown in Figure 2, both the User and Service Agent find the DA through passive discovery (via a multicast from the DA). This discovery is communicated in a DAAdvert message from the DA.
Figure 2. Service discovery with a DA

The SA advertises its service to the DA through an SAAdvert message to which the DA responds with a SrvAck to acknowledge receipt of the advertisement. As the DA is known to the SA, its request is unicast to the DA in a UDP frame.
The UA requests the location of a service from the DA in a SrvRqst message. The DA responds with a unicast UDP SrvRply containing the address and port for the service.
In large networks, having a DA is desirable because it cuts down on the number of multicast messages that are sent on the network. Without a DA, all SAs and UAs receive all multicast messages; most are ignored (because they're not the target of the message), so the host processor is wasted.
A service in SLP is defined by a service URL. The URL has the form
service:<service-type>://<addrspec> |
where <service-type> is the type of service being advertised and <addrspec> is the service's location (domain name or IP address and port number). For example, the following example advertises a mail server at mail.com on port 25:
service:mail://mail.com:25
If the UA requested the location of "service:mail", it would be returned with the service URL defining its location. Note that multiple services may be returned and it's up to the UA to decide which to use. For services in which you expect only one to be returned, SLP provides a way to limit your search. It's called a scope and it allows you to segment services for specific users.
When an SA advertises a new service, it can optionally advertise it within a scope. A scope is just a string to identify a particular zone. For example, if you had two printers, one on the first floor and another on the second, you could define the scopes as first_floor and second_floor. When your UA made a request for service:printer, the scope for the particular floor would be applied, limiting the search to the assets on that floor (in the scope).
You can use scopes to define the proximity to the service (such as on the same floor) or the administrative ownership of the service (such as accounting or sales). A service may also be advertised multiple times with different scopes if the service is shared.
SLP implements a number of messages, some of which you saw in Figures 1 and 2. In addition to registering and finding services, messages are provided to deregister services that are no longer available, search for all services based upon their type, and request the attributes of a particular service.
Table 2 defines the messages in SLP. I provide these only for completeness; I won't dig into them any further. If you use the Ethereal protocol analyzer with the SLP plug-in, you'll see these messages and their parsed data.
Table 2. Messages communicated in SLP
| Message | From | To | Description |
|---|---|---|---|
SrvReg | SA | DA | Register (advertise) a service |
SrvDeReg | SA | DA | Deregister a service |
SrvAck | DA | SA | Response to SrvReg and SrvDereg messages |
SrvRqst | UA | SA/DA | Request the location of a service |
SrvRply | SA/DA | UA | Response to SrvRqst of a desired service location |
SrvTypeRqst | UA | SA/DA | Request the location of a service based upon the defined type |
SrvTypeReply | SA/DA | UA | Response to SrvTypeRqst of a desired service location |
AttrRqst | UA | SA/DA | Request attributes for a given service |
AttrRply | SA/DA | UA | Response from AttrRqst with attributes for a service |
DAAdvert | DA | UA/SA | Sent by DAs to notify UAs and SAs of their existence and location |
SAAdvert | SA | UA | Sent by SAs to notify UAs of their existence and location |
An SLP implementation: OpenSLP
OpenSLP is an open source implementation of SLP. It consists of an API library from which you can write SAs and UAs. It also comes with a DA called slpd that you can configure using its configuration file at /etc/slp.conf.
The OpenSLP API uses a callback architecture. You make a request using one of the API functions; and, in the context of that API function, a callback function is called with the resulting response data or status. Table 3 lists the API functions in OpenSLP and their purpose. The SLPReg, SLPDereg, and SLPFind* functions all use a callback to return their status and optional data.
Table 3. The primary functions of the OpenSLP library
| Function | Description |
|---|---|
SLPOpen | Open an OpenSLP API instance |
SLPClose | Close an OpenSLP API instance |
SLPReg | Register a service and URL |
SLPDereg | Deregister a URL for a service |
SLPFindSrvs | Find a registered service given a service type |
SLPFindSrvTypes | Find all service types registered for the scope |
In addition to the API library, OpenSLP comes with slptool which you can use to interactively perform most of the SLP functions. This tool can be useful for registering a service or finding a service. For example, the following snippet shows how you can find a daytime service:
$ slptool findsrvs service:daytime
service:daytime://www.mtjones.com:45667,65535
$
The Resources section provides links for more information on the OpenSLP API and its associated tools.
Now, let's look at an example of server and client applications that use the OpenSLP API. In this example, I create a daytime socket server: when a client connects to it, it emits an ASCII string of the current time. First I discuss the source of the applications, and then you'll see both applications in action.
Listing 1 shows the SLP-enabled daytime server. I've split the code into three sections to more easily describe its functionality:
- Section 1 is a simple socket setup.
- Section 2 performs the service registration.
- Section 3 implements the daytime service.
The first section is socket setup for the daytime server. I create a socket, enable address reuse for it with setsockopt, bind an address and port to the socket, and finally allow incoming connections by calling listen (which places the TCP server socket into the listening state).
Section 2 performs the service registration with the OpenSLP API. First, I create a new SLP handle with SLPOpen and then register my service with SLPReg. Recall that the service URL contains the service name (daytime), the location of the service (www.mtjones.com), and the TCP port number at which the service can be accessed (45667). After the SLPReg callback is called (slpRegCallback), the SLPReg function finishes and I close the SLP handle with SLPClose. My service is now registered with the DA.
With service registration complete, I move on to Section 3, which implements the daytime server. I await a new connection with accept, and, upon receiving a new client connection, I grab the current time, convert it to a string, and then write it to the client socket with the write API function. The client closes the socket and awaits a new client connection.
Listing 1. SLP-enabled daytime server
#include <stdio.h>
#include <slp.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#define MAX_BUFFER 80
void slpRegCallback( SLPHandle hslp, SLPError errcode, void *cookie )
{
if (errcode == SLP_OK) {
printf("Service registered\n");
}
*(SLPError *)cookie = errcode;
return;
}
int main()
{
SLPError err, callback_err;
SLPHandle hslp;
int servsock, clisock, on = 1;
struct sockaddr_in sa;
time_t t;
char timeBuffer[MAX_BUFFER+1];
/* --------------------------------------
* Section 1 -- Daytime Server Setup
* -------------------------------------*/
/* Create a new socket */
if ((servsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket");
return servsock;
}
/* Enable address reuse */
if ((setsockopt( servsock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) < 0) {
perror("setsockopt");
return -1;
}
/* Bind our socket to port 45667 and all interfaces */
memset( &sa, 0, sizeof(sa) );
sa.sin_family = AF_INET;
sa.sin_port = htons(45667);
sa.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(servsock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
perror("bind");
return -1;
}
/* Place the socket into the listening state (able to accept new
* connections).
*/
listen( servsock, 5 );
/* -------------------------------------------
* Section 2 -- SLP Service Registration
* ------------------------------------------*/
/* Open an SLP API instance */
err = SLPOpen( "en", SLP_FALSE, &hslp );
if (err != SLP_OK) {
printf("SLPOpen failed %d\n", err);
return err;
}
/* Register the service with SLP */
err = SLPReg( hslp, "service:daytime://www.mtjones.com:45667",
SLP_LIFETIME_MAXIMUM, 0, "", SLP_TRUE,
slpRegCallback, &callback_err );
if ((err != SLP_OK) || (callback_err != SLP_OK)) {
printf("SLPReg failed %d/%d\n", err, callback_err);
return err;
}
/* Registration is complete, close the SLP instance */
SLPClose( hslp );
/* -------------------------------------
* Section 3 -- Daytime Server Loop
* ------------------------------------*/
/* Service was successfully registered, start the daytime server. */
while (1) {
/* Await a client connection */
if ((clisock = accept( servsock, (struct sockaddr *)NULL, NULL)) < 0) {
perror("accept");
close(servsock);
return clisock;
}
/* Get the time and send it to the client */
t = time(NULL);
snprintf( timeBuffer, MAX_BUFFER, "%s\n", ctime(&t) );
write( clisock, timeBuffer, strlen(timeBuffer) );
close( clisock );
}
close( servsock );
return 0;
}
|
I've split the SLP-enabled daytime client into two functions (see Listing 2). The first is the SLP callback function (for the SLPFindSrvs function), and the second is the main function which implements the daytime client.
Rather than describe these two functions independently, I discuss them in terms of their flow. The main function is split into two sections. Section 1 of main performs the SLP service location. After getting a new SLP handle from SLPOpen, I call SLPFindSrvs looking for service service:daytime (with the scope default). The callback for this SLP call is slpSrvURLCallback. This function is called for every service that's returned. Inside the callback, I parse the service URL returned with SLPParseSrvURL and store the IP address of the service and its port number for later use. Note the use of the resolve_name function here: this function is available in the accompanying .ZIP file, but I avoid it here for brevity. Its sole purpose is to resolve the string domain name to a 32-bit IP address.
After all the services have been returned, control is returned from SLPFindSrvs to main and the SLP handle closed with SLPClose. I then check to see if a service was found (I use the last one that I find); if no service was found, I exit.
The final section, Section 2, is the daytime socket client. Given the IP address and port number found through SLP, I create a new socket and connect to the server. Upon connect, I read from the socket and then print the response (the current time).
That's it! The SLP portions of both the client and the server were small, even in this tiny demonstration.
Listing 2. SLP-enabled daytime client
#include <stdio.h>
#include <slp.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>
#include <unistd.h>
#define MAX_BUFFER 80
int found = 0;
struct sockaddr_in sa;
int port;
SLPBoolean slpSrvURLCallback( SLPHandle hslp, const char *srvurl,
unsigned short lifetime, SLPError errcode,
void *cookie )
{
SLPSrvURL *parsedURL;
SLPError err;
/* Check the callback for success */
if (errcode == SLP_OK) {
printf("srvurl = %s\n", srvurl);
/* Parse the service string into its basic elements */
err = SLPParseSrvURL( srvurl, &parsedURL );
/* If the parse was successful, grab the necessary elements and
* cache them for the application. */
if (err == SLP_OK) {
printf("Found %s\n", parsedURL->s_pcSrvType);
printf("at host %s\n", parsedURL->s_pcHost);
printf("port number %d\n", parsedURL->s_iPort);
found = 1;
/* Resolve the fully qualified domain name to an IP address */
printf("Resolving host to IP address\n");
if (resolve_name( &sa, parsedURL->s_pcHost ) == 0) {
printf("Resolved to %s\n", inet_ntoa(sa.sin_addr));
port = parsedURL->s_iPort;
}
SLPFree( (void *)parsedURL );
}
*(SLPError *)cookie = SLP_OK;
} else if (errcode == SLP_LAST_CALL) {
/* no action */
printf("Final call -- slp find done.\n");
} else {
*(SLPError *)cookie = errcode;
}
return SLP_TRUE;
}
int main()
{
SLPError err, callback_err;
SLPHandle hslp;
char timeBuffer[MAX_BUFFER+1];
int sock, in;
/* -------------------------------------
* Section 1 -- SLP Service Request
* ------------------------------------*/
/* Open a new SLP API instance */
err = SLPOpen( "en", SLP_FALSE, &hslp );
if (err != SLP_OK) {
printf("SLPOpen failed %d\n", err);
return err;
}
/* Try to find the desired service */
err = SLPFindSrvs( hslp, "service:daytime", "default", 0,
slpSrvURLCallback, &callback_err );
if ((err != SLP_OK) || (callback_err != SLP_OK)) {
printf("SLPFind failed %d/%d\n", err, callback_err);
return err;
}
/* Close this SLP API instance */
SLPClose( hslp );
/* If the service wasn't found, exit now */
if (!found) {
close(sock);
printf("Service not found.\n");
return -1;
}
/* --------------------------------------
* Section 2 -- Daytime Client Setup
* -------------------------------------*/
/* Create a new socket */
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket");
return sock;
}
/* Bind the socket to the discovered service (address and port) */
memset( &sa, 0, sizeof(sa) );
sa.sin_family = AF_INET;
sa.sin_port = htons(port);
printf("Connecting to service\n");
if (connect( sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
close(sock);
perror("connect");
return -1;
}
/* Read the time from the daytime server */
in = read(sock, timeBuffer, MAX_BUFFER);
timeBuffer[in] = 0;
printf("Client received: %s\n", timeBuffer);
close(sock);
return 0;
}
|
Demonstrating the server and client
Listing 3 shows the server and client in action. I build the SLP server and client with the make utility. I start the slpd utility (the SLP DA) and then start the server (slpreg). Now that the server is running and presumably registered, I use the slptool as an optional step to see if the DA now knows about the service. From the return of slptool, my registration worked, so I try out my client slpfind. This application looks for the service and then, after parsing the resulting service URL, connects to the server and gets back the current time.
You can download slpreg, slpfind, and the makefile from the Download section, below.
Listing 3. Demonstrating the SLP server and client
# make gcc -Wall -o slpreg slpreg.c -lslp gcc -Wall -o slpfind slpfind.c -lslp # slpd # ./slpreg & [1] 9275 Service registered # slptool findsrvs service:daytime service:daytime://www.mtjones.com:45667,65535 # ./slpfind srvurl = service:daytime://www.mtjones.com:45667 Found service:daytime at host www.mtjones.com port number 45667 Resolving host to IP address Resolved to 66.54.202.174 Final call -- slp find done. Connecting to service Client received: Sat Apr 16 14:04:26 2005 # |
SLP has been released in a variety of products from a number of companies. Some of the companies and products are listed in Table 4.
Table 4. Products with integrated SLP
| Company | Product |
|---|---|
| Axis Communication | Network printers and cameras |
| GroupLogic | ExtremeZ-IP file- and printer-sharing product |
| IBM | Communications Server; TotalStorage SAN Volume Controller; TotalStorage Multiple Device Manager (MDM); TN3270 Terminal |
| Open Door Networks | Shareway IP file-sharing product |
| Symantec | Norton Personal Firewall for Macintosh |
| WBEM Solutions | J WBEM Server |
A number of modern operating systems also include support for SLP, including GNU/Linux®, IBM AIX, HP-UX, NetBSD, Sun Solaris 8, Mac OS X, and Novell SuSE Enterprise Server 9. You can also write your application for SLP in more than just C. Several languages support bindings for SLP, including C/C++, Java™, and Python.
Finally, SLP can also be found in numerous RFCs recommending its use (for example, iSCSI and FCIP). Despite its integration into numerous operating systems, products, and language support, there is dissent over SLP's more broad acceptance. The number of protocols occupying the service discovery space is large, but none has yet achieved a de facto standard status.
| Description | Name | Size | Download method |
|---|---|---|---|
| Demonstration SLP client and server | l-slp-demo.zip | 3KB | HTTP |
Information about download methods
Learn
-
See a lightweight implementation of SLP in TCP/IP Application Layer Protocols for Embedded Systems by M. Tim Jones.
- UPnP is a technology to enable simple and robust connectivity among PCs and stand alone devices.
- Zero Configuration Networking is an official IETF charter focusing on zero-administration environments.
- Service Location Protocol Overview (IBM Redbooks Technote, August 2003) provides an overview of SLP.
-
This SLP help file explains the protocol.
-
Find more resources for Linux developers in the developerWorks Linux zone.
Get products and technologies
-
Explore the OpenSLP implementation of SLP.
- Bonjour is Apple's trade name for the IETF ZeroConf protocol and enables service discovery in IP networks.
- SLP language bindings for the Python language are available at SourceForge.
-
The GNU/Linux printer daemon (CUPS, or Common UNIX Printing System) can use SLP for printer discovery. An interesting paper on the topic was presented at MICON 2000.
-
Build your next development project on Linux with IBM trial software, available for download directly from developerWorks.
Discuss
-
Get involved in the developerWorks community by participating in developerWorks blogs.

M. Tim Jones is an embedded software engineer and the author of GNU/Linux Application Programming, AI Application Programming, and BSD Sockets Programming from a Multilanguage Perspective. His engineering background ranges from the development of kernels for geosynchronous spacecraft to embedded systems architecture and networking protocols development. Tim is a senior principal engineer at Emulex Corp.




