FIND Protocol
Locate embedded devices in the LAN
FIND is a free to use protocol for Embedded devices which makes it easy and fast to locate them in the LAN. FIND locates all embedded devices supporting discovery in fractions of a second. With less than 300 bytes of ROM and no RAM for the implementation, it can be easily implemented on any device with any TCP/IP stack. You are free to use FIND for anything, even commercial and closed source software.
FIND - Fast Interoperable Network Discovery Protocol
In today's networks, even simple questions regarding the reachability of a device are nearly impossible to answer. Especially headless devices like sensors, network printers and network attached storages are often not responsive.
New devices which have to request an IP address from a DHCP server to get reachable are an everyday scenario. In home networks the IP address will normally automatically assigned to each device which sends a request. Thereafter, the device is available on the network, but still not automatically reachable for the user. To connect to the new device you need at least its IP address or the device name if local name resolution protocols like NetBios or mDNS are supported. If the IP address and the device name are unknown, the simplest solution is often to check the administration panel of the router and search the list of distributed IP addresses. This is doable, but not user-friendly.
How easy could it be, if every device would simply send a response, if the user asks for the name and the IP address of each device in his network? Sounds simple and reasonable, but nevertheless it is not normal in today's networks.
SEGGER's FIND (Fast Interoperable Network Discovery) protocol is designed to fill this gap. It helps to collect the most important information of each target on the network, works on top of each TCP/IP stack and helps to satisfy the needs of the normal user.
FIND Discover - The FIND response collector
The simplest way to display the collected information is our free tool FIND Discover. It sends queries and lists all valid responses of the devices in the network.
FIND command-line server
To test FIND in your network, you can download a simple command line implementation of a FIND server, which generates and sends valid FIND responses with the information of your host. The application opens the UDP port 50022 and waits until it receives a valid FIND query. A FIND response will be send back after the receipt of a FIND query.
Download Sample command-line FIND server for Microsoft Windows hosts.
How to add FIND to your application
The protocol can be used on every hardware and with every TCP/IP stack.
If your device can send UDP packets, it can also add support for the FIND protocol.
Client side:
- Open UDP port 50022 in listening mode.
- Send a response to UDP port 50022 back to the query sender (unicast)
Host side:
- Open UDP port 50022 in listening mode.
- Send a FIND query (UDP broadcast to port 50022) with payload
FINDReq=1;
. - Check for responses sent by the devices with FIND support in the network.
emNet client side implementation
The code is for the client side is really simple. The sample implementation uses the emNet UDP zero-copy API.
/*********************************************************************
* (c) SEGGER Microcontroller GmbH *
* The Embedded Experts *
**********************************************************************
* *
* Copyright 2018 - SEGGER Microcontroller GmbH - www.segger.com *
* *
* Redistribution and use in source form, with or without *
* modification, are permitted provided that the following *
* condition is met: *
* *
* 1. Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. *
* *
**********************************************************************
----------------------------------------------------------------------
File : IP_FIND.c
Purpose : Sample implementation of SEGGER's FIND protocol.
-------- END-OF-HEADER ---------------------------------------------
*/
#include "IP_FIND.h"
#include "IP.h"
#include "SEGGER.h"
/*********************************************************************
*
* Defines, configurable
*
**********************************************************************
*/
#define TARGET_NAME "MyTarget" // Target Name. Mandatory, usually a fixed string
#define TARGET_SERIAL_NUMBER 12345678 // Serial number. Mandatory, the define is a number, the value transmitted
// is an ASCII string. Typically, a function call will be used to get the
// serial number.
#define FIND_RESPONSE_BUFFER_SIZE 128 // Maximum FIND packet size is 512 bytes.
// It usually makes sense to limit this since the work buffer is located
// on the stack, and a bigger buffer would increase stack requirements.
#define ADD_IP_ADDRESSES 0
/*********************************************************************
*
* Defines, fixed
*
**********************************************************************
*/
#define FIND_RECEIVE_IDENTIFIER "FINDReq=1;"
#define FIND_RECEIVE_IDENTIFIER_LEN 11 // FIND_RECEIVE_IDENTIFIER length including the zero-termination
#define FIND_SEND_IDENTIFIER "FIND=1;"
#define FIND_DEF_PORT 50022
/*********************************************************************
*
* Local functions
*
**********************************************************************
*/
/*********************************************************************
*
* _OnRx()
*
* Function descrition
* Default FIND protocol client callback. Called from stack whenever
* we get a FIND request.
*
* Parameters
* pInPacket: Pointer to incoming packet.
* pContext : Context set during callback registration.
*
* Return value
* IP_OK : Packet processed. pInPacket will be freed.
*
* Notes
* (1) Freeing pInPacket:
* With either return value, the IN-packet is freed by the stack
* and therefore can not be re-used nor has it to be freed by
* this routine.
*
* Incoming packet example:
* "FINDReq=1;"
*
* Outgoing packet example:
* Normal packet:
* "FIND=1;HWADDR=00:22:C7:89:87:45;DeviceName=MyTarget;SN=12345678;"
*
* With included IPv4/IPv6 addresses
* "FIND=1;IP=192.168.2.124,FE80:0000:0000:0000:0222:C7FF:FE89:8745;HWADDR=00:22:C7:89:87:45;DeviceName=MyTarget;SN=12345678;"
*/
static int _OnRx(IP_PACKET* pInPacket, void* pContext) {
unsigned IFaceId;
U32 TargetAddr;
int Pos;
char* pOutData;
char* pInData;
IP_PACKET* pOutPacket;
unsigned char aMacData[6];
int SerialNumber;
char aFINDResponse[FIND_RESPONSE_BUFFER_SIZE];
#if ADD_IP_ADDRESSES
U32 IPAddr;
#if IP_SUPPORT_IPV6
U8 NumAddr;
U8 AddrIndex;
IPV6_ADDR IPv6Addr;
#endif
#endif
IP_USE_PARA(pContext);
pInData = (char*)IP_UDP_GetDataPtr(pInPacket); // Get the pointer to the UDP payload.
//
// Check if this is a valid FIND packet.
//
if (memcmp(pInData, FIND_RECEIVE_IDENTIFIER, FIND_RECEIVE_IDENTIFIER_LEN) != 0) {
return IP_OK; // Discard it, since the request is not valid .
}
//
// Build response
//
IFaceId = IP_UDP_GetIFIndex(pInPacket);
//
// Fill packet with data, containing IPAddr, HWAddr, S/N and name.
//
memset(aFINDResponse, 0, FIND_RESPONSE_BUFFER_SIZE); // Make sure all fields are cleared.
Pos = SEGGER_snprintf(aFINDResponse, FIND_RESPONSE_BUFFER_SIZE, FIND_SEND_IDENTIFIER); // Answer string for a discover response.
#if ADD_IP_ADDRESSES
//
// Add IPv4 information.
//
IPAddr = IP_GetIPAddr(IFaceId);
IPAddr = htonl(IPAddr);
Pos += SEGGER_snprintf(&aFINDResponse[Pos], FIND_RESPONSE_BUFFER_SIZE - Pos, "IP=%i", IPAddr);
#if IP_SUPPORT_IPV6
//
// Add IPv6 information, in systems without IPv6 support this should be eliminated.
//
AddrIndex = 0;
IP_IPV6_GetIPv6Addr(IFaceId, AddrIndex, NULL, &NumAddr); // Get number of configured IPv6 adresses
if (NumAddr > 0) {
//
// Add IPv6 addresses
// If your target uses more as one IPv6 address, please check if the size of the working buffer is sufficient to store them all.
//
do {
IP_IPV6_GetIPv6Addr(IFaceId, AddrIndex, &IPv6Addr, &NumAddr);
Pos += SEGGER_snprintf(&aFINDResponse[Pos], FIND_RESPONSE_BUFFER_SIZE - Pos, ",%n", &IPv6Addr.Union.aU8[0]);
AddrIndex++;
} while(NumAddr > AddrIndex);
Pos += SEGGER_snprintf(&aFINDResponse[Pos], FIND_RESPONSE_BUFFER_SIZE - Pos, ";");
}
#endif // IP_SUPPORT_IPV6
#endif // ADD_IP_ADDRESSES
//
// Add MAC information.
//
IP_GetHWAddr(IFaceId, aMacData, 6);
Pos += SEGGER_snprintf(&aFINDResponse[Pos], FIND_RESPONSE_BUFFER_SIZE - Pos, "HWADDR=%_h;", &aMacData[0]);
//
// Add device name.
//
Pos += SEGGER_snprintf(&aFINDResponse[Pos], FIND_RESPONSE_BUFFER_SIZE - Pos, "DeviceName=%s;", TARGET_NAME);
//
// Add serial number.
//
SerialNumber = TARGET_SERIAL_NUMBER;
Pos += SEGGER_snprintf(&aFINDResponse[Pos], FIND_RESPONSE_BUFFER_SIZE - Pos, "SN=%d;", SerialNumber);
//
// Allocate and send response packet.
//
pOutPacket = IP_UDP_AllocEx(IFaceId, Pos + 1); // Allocate packet on the same interface
if (pOutPacket) {
pOutData = (char*)IP_UDP_GetDataPtr(pOutPacket); // Get data pointer
SEGGER_memcpy(pOutData, aFINDResponse, Pos + 1); // Copy response into packet buffer
IP_UDP_GetSrcAddr(pInPacket, &TargetAddr, sizeof(TargetAddr)); // Get the src address of the IN-packet
Pos = IP_UDP_SendAndFree(IFaceId, TargetAddr, FIND_DEF_PORT, FIND_DEF_PORT, pOutPacket); // Send packet
}
return IP_OK;
}
/*********************************************************************
*
* Global functions
*
**********************************************************************
*/
/*********************************************************************
*
* IP_FIND_Init()
*
* Function description
* Adds the FIND protocol to your application.
*
* Additional information
* Opens a UDP socket listening on port xxxxx
*
* Return value
* == 0: O.K.
* == -1: Error.
*/
int IP_FIND_Init(void) {
IP_UDP_CONNECTION* pCon;
int r;
r = 0;
pCon = IP_UDP_Open(0uL /* any foreign host */, FIND_DEF_PORT, FIND_DEF_PORT, _OnRx, NULL); // Use default sample implementation
if (pCon == NULL) {
r = -1;
}
return r;
}
/****** End Of File *************************************************/
Add it to your emNet application: Download the archive and add the included files to your project. To enable FIND support on your device, IP_FIND_Init()
has to be called in your application. That's it. Thereafter your device will answer FIND queries and can be found in your network.
Download Sample implementation of the client side for emNet
//
// Add FIND protocol to the stack.
//
IP_FIND_Init();
Resource usage
Compiler settings: | Size optimized |
---|---|
CPU: | Cortex-M4 |
ROM usage: | 224 bytes of CODE memory |
Protocol specification
The protocol is simple and efficient. A host sends a query via UDP broadcast to port 50022 and all clients which are listening on that port send a UDP unicast response with the used address configuration to port 50022.
- The maximum size of the payload of FIND packets (query/response) is 512 bytes.
- The payload of FIND queries and FIND responses is a zero-terminated UTF-8 string.
- All printable characters except = , ; can be used in the string. The characters = , ; are used as delimiters.
Query payload: The payload of the query is a zero-terminated UTF-8 string, which will be checked by the listening devices. The string consists of the keyword FINDReq followed by the delimiter (=), the protocol version (1) and the delimiter (;). Optional type/value pairs (e.g. filter rules) can be added to the query. The payload ends with the zero-termination.
FINDReq=1;
Response structure: The payload of the response is a zero-terminated UTF-8 string. The string starts with a marker which has to be checked by the device which receives the responses. The marker consists of the keyword FIND followed by the delimiter (=) and the protocol version (1). The marker ends with the delimiter (;). The marker is followed by type/value pairs. The sequence of the type/value pairs in the response string is not relevant.
FIND=1;Type=Value;Type=Value;[...]Type=Value;
Mandatory fields: The protocol specifies the following type/value pairs as mandatory:
- Device name (e.g.
DeviceName=MyTarget;
) - Serial number (e.g.
SN=12345678;
) - Hardware address of the interface (e.g.
HWADDR=00:22:CC:AA:BB:CC;
)
Our sample implementation sends also the IP of the device as additional information.
- IP address(es) of the interface (e.g.
IP=192.168.123.123;
)
The IP address of the device is always part of the IP header of response. If the IP address is not part of the response string, the client should extract the IP address out of the IP header of the packet.
Additional information can be added to the response (Type=Value;
). Clients should ignore and skip unknown type/value pairs.
FIND=1;IP=127.0.0.1;HWADDR=AA:BB:CC:DD:EE:FF;DeviceName=MyTarget;SN=12345678;
The protocol on the wire
Devices with FIND support are listening on UDP port 50022. To get the information from each device with FIND support the host sends a UDP broadcast query with the query marker FINDReq=1
as payload to port 50022.
The devices with FIND support in the same network segment will receive the broadcast and check the payload for the query marker. If the marker is valid, each device will send an unicast response with the most important configuration settings as payload back to the host.
J-Link support
The industry leading SEGGER J-Link supports FIND since software version V6.34f. Use the free FIND Discover tool to detect the J-Link PROs in your network.