Chapter 195. Debug and Test

195.1. Debugging

195.1.1. Asserts

If the target platform resources allow, the first step in debugging should be to enable ASSERTs. The inclusion of assert checking will increase the code footprint and lower the performance, but does allow the code to catch internal errors from unexpected data values. e.g. when the application/client is not able to guarantee the validity of data passed into the mDNS Responder.

The mDNS Responder asserts are controlled via the standard eCos Infrastructure CYGPKG_INFRA package CYGDBG_USE_ASSERTS option. If enabled, then run-time assertion checks are performed by the mDNS Responder.

If assertions are enabled, and a debugger is being used it is normally worthwhile setting a breakpoint on the cyg_assert_fail symbol so that the debugger will stop prior to entering the default busy-loop processing.

195.1.2. Diagnostic Output

In conjunction with the CYGDBG_MDNS_DEBUG CDL configuration setting, the header-file src/mdns_debug.h implements the mDNS specific debug control.

When CYGDBG_MDNS_DEBUG is enabled a set of individually selectable sub-systems are available to control the diagnostic output generated.

However, when developing or debugging the mDNS Responder implementation, it may be simpler (with fewer build side-effects) to control the debugging output via uncommenting the necessary manifests at the head of the src/mdns_debug.h source file than re-configuring the complete eCos configuration via the CDL. That way only the mDNS package will be re-built.

[Note]Note

Some diagnostic output, if enabled, may adversely affect the operation of the mDNS Responder as seen by 3rd-party code. For example, “slow” serial diagnostic output of the packet parsing and response generation could mean that a significant amount of time passes, such that the mDNS Responder no longer adheres to the timings as specified by the mDNS/DNS-SD standards.

195.2. Testing

If the configuration option CYGPKG_NET_MDNS_TESTS is enabled then a set of simple tests are built.

[Note]Note

If the target platform has limited memory and is unable to execute the tests then the eCos synthetic Linux target can be used to execute tests and verify the behaviour of the mDNS implementation when debugging, assertions or large test executables are required.

195.2.1. mdns_example

The mdns_example provides a very basic HTTP daemon, with enough functionality to pass the Bonjour Conformance Test (see Section 195.2.5, “Bonjour Conformance Test”).

On startup the application initialises a HTTP daemon listening on port 80, and registers and enables the mDNS announcing that service. A HTTP GET request for the default root page will return an HTML page listing the sub-pages provided. The application is manually terminated by requesting the exit page. The config page has specific support for the features needed for the BCT.

The mdns_example is, however, a simple example of announcing a DNS-SD service, as well as a simple lwIP HTTP daemon demonstration.

195.2.2. dnssd_example

The dnssd_example application demonstrates the use of the DNS-SD API to discover network services (see Section 195.2.6, “DNS-SD Example” for more detail). The application will enumerate local services being announced to the network, before performing specific queries for “_http” and “_ipp” services to ascertain server and TXT information.

195.2.3. mdns_testp

The mdns_testp uses dummy network interfaces to monitor the responses generated by the mDNS Responder in a known clean environment. The physical network connection is not used after the startup initialisation. The test application controls the packets injected into the networking stack and provides its own artificial driver layer for packet transmission. This allows for specific mDNS Responder features to be tested without the noise and interference of a real network setup, and without specifying a stand-alone, closed, network like the 3rd-party Bonjour conformance testing.

195.2.4. mdns_farm

The mdns_farm test is primarily for use in the eCosCentric® automated testfarm. It can however be manually executed by starting the application, and then manually executing the simple bash test script misc/mdnstest1.sh on a suitable host, passing the network address of the target executing the mdns_farm executable. For example:

$ ./misc/mdnstest1.sh 192.168.7.165
[Note]Note

The host used to execute the script should have a suitable avahi configuration.

195.2.5. Bonjour Conformance Test

If the configuration option CYGPKG_NET_MDNS_TESTS_STANDALONE is enabled then the mdns_example is built. This implements a simple mDNS application providing, by default, a limited functionality HTTP daemon on port 80. This test program, when used with a suitable eCos system configuration, can be used for execution against the Bonjour Conformance Test (BCT).

[Note]Note

For the full BCT test, which includes link-local address verification, the eCos configuration should have the lwIP IPv4 AutoIP (CYGFUN_LWIP_AUTOIP) support enabled and the network interface being used for the Ethernet connection configured to obtain its network address using AutoIP (CYGOPT_LWIP_ETH_DEV_ADDR_AUTOIP#).

For the BCT to function, a very specific closed network setup is required. For the mDNS Responder package testing the following equipment was used:

  • Unit-Under-Test (UUT)

    The target platform being tested, executing a suitable eCosPro configuration.

  • MacBookPro (OS: 10.8.2)

    Executing the BonjourConformanceTest v1.2.8 in a Terminal shell window, and the Safari v6.0.1 web-browser.

  • Airport Extreme (HW: A1408 FW: 7.6.1)

    10base-T (wired) Ethernet connections to the UUT and the MacBookPro.

The HTTP daemon provided by the mdns_example application provides support for the necessary BCT interaction using specific CGI parameters to the /config page.

The BCT initially tests the IPv4 link-local address support before starting the mDNS Responder interaction.

The following example URLs are based on the BCT v1.2.8 interaction with a default mDNS configuration. If the application being tested uses a different starting hostname, or later BCT executables change the processing, then the target address will need to be modified to suit the state reached at the specific point in the BCT sequence.

For the MANUAL NAME CHANGE component the unit-under-test (UUT) needs to be supplied with a specific service label. The config?service= option allows a new service-label to be specified for the registered HTTP daemon.

ecospro-21.local./config?service=New - Bonjour Service Name
[Note]Note

The mdns_example provides the HTTP daemon as the first registered service, and the simple config?service= option interacts with that specific service in the knowledge that the BCT uses the first announced service as its test SRV.

For the Mixed-Network Interoperability Test ROUTABLE TO LINK-LOCAL COMMUNICATION component the UUT needs to be configured with a routable IP address. The config?ip= option allows an IPv4 address to be specified. This will re-configure the default network interface to the specified IPv4 address with an explicit netmask of 255.255.255.0. For example:

ecospro-42.local./config?ip=17.1.1.1

For the final CHATTINESS component of the BCT the UUT needs to be re-configured with a link-local address. The config?ll= option allows an IPv4 link-local address to be supplied, explicitly setting the netmask 255.255.0.0. For example:

ecospro-42.local./config?ll=169.254.1.0
[Note]Note

For the final CHATTINESS component of the BCT, after changing the UUT IPv4 address to a link-local address described above it is important to exit the Safari web-browser being executed on the test host to avoid its Bonjour support interfering with the BCT execution.

When the BCT execution finishes it prompts the operator regarding the generation of an execution report. When applying for Bonjour Conformance the report should be created, and in conjunction with the generated debug.log file, sent to Apple for validation as documented in their procedures.

The results for an actual BCT run using the mdns_example, executing on a STM32F207xx (Cortex-M3) based platform, are available in the Example 195.1, “ doc/bct_stm32f207_result.txt and doc/bct_stm32f207_debug.log files. These files are original, as produced by the BCT application. For reference, the doc/bct_stm32f207_terminal.txt file contains the execution output captured from the MacOS Terminal.

The following is a listing of the doc/bct_result.txt file as provided in the package.

[Note]Note

The BCT actually finished its testing at 18:05 on Tue Nov 20th, but the Completed timestamp reflects when the report was written and does not reflect the time taken for the actual BCT run.

Example 195.1.  doc/bct_stm32f207_result.txt

            
Bonjour Conformance Test Version 1.2.8
Started   Tue Nov 20 12:57:22 2012
Completed Wed Nov 21 09:00:08 2012


Link-Local Address Allocation
-----------------------------
	PASSED: INITIAL PROBING
	PASSED: PROBING: RATE LIMITING
	PASSED: PROBING: CONFLICTING SIMULTANEOUS PROBES
	PASSED: PROBING: PROBE DENIALS
	PASSED: PROBING COMPLETION
	WARNING: SUBSEQUENT CONFLICTS: RE-PROBE AFTER FIRST CONFLICT
	WARNING: SUBSEQUENT CONFLICTS: RE-PROBE AFTER FIRST CONFLICT
	WARNING: SUBSEQUENT CONFLICTS: RE-PROBE AFTER FIRST CONFLICT
	WARNING: SUBSEQUENT CONFLICTS: RE-PROBE AFTER FIRST CONFLICT
	WARNING: SUBSEQUENT CONFLICTS: RE-PROBE AFTER FIRST CONFLICT
	WARNING: SUBSEQUENT CONFLICTS: RE-PROBE AFTER FIRST CONFLICT
	WARNING: SUBSEQUENT CONFLICTS: RE-PROBE AFTER FIRST CONFLICT
	WARNING: SUBSEQUENT CONFLICTS: RE-PROBE AFTER FIRST CONFLICT
	WARNING: SUBSEQUENT CONFLICTS: RE-PROBE AFTER FIRST CONFLICT

	PASSED: SUBSEQUENT CONFLICTS
	PASSED: HOT-PLUG: USE OF PREVIOUS ADDRESS AS FIRST PROBE CANDIDATE
	PASSED: CABLE CHANGE HANDLING
	PASSED: PREMATURE MDNS PROBING
PASSED with 9 warning(s).

Multicast DNS
-----------------------------
	PASSED: INITIAL PROBING
	PASSED: PROBING: SIMULTANEOUS PROBE CONFLICT
	PASSED: PROBING: RATE LIMITING
	PASSED: PROBING: PROBE DENIALS
	PASSED: WINNING SIMULTANEOUS PROBES - ANNOUNCEMENTS
	PASSED: WINNING SIMULTANEOUS PROBES: WINNING SIMULTANEOUS PROBES
	PASSED: SRV PROBING/ANNOUNCEMENTS
	PASSED: SUBSEQUENT CONFLICT - ANNOUNCEMENTS
	PASSED: SUBSEQUENT CONFLICT - A
	PASSED: SUBSEQUENT CONFLICT - ANNOUNCEMENTS
	PASSED: SUBSEQUENT CONFLICT - SRV
	PASSED: SIMPLE REPLY VERIFICATION
	PASSED: SHARED REPLY TIMING - UNIFORM RANDOM REPLY TIME DISTRIBUTION
	PASSED: SHARED REPLY TIMING
	PASSED: MULTIPLE QUESTIONS - SHARED REPLY TIMING - UNIFORM RANDOM REPLY TIME DISTRIBUTION
	PASSED: MULTIPLE QUESTIONS - SHARED REPLY TIMING
	PASSED: REPLY AGGREGATION
	PASSED: MANUAL NAME CHANGE - ANNOUNCEMENTS
	PASSED: HOT-PLUGGING: INITIAL PROBING
	PASSED: HOT-PLUGGING: PROBING: SIMULTANEOUS PROBE CONFLICT
	PASSED: HOT-PLUGGING: PROBING: RATE LIMITING
	PASSED: HOT-PLUGGING: PROBING: PROBE DENIALS
	PASSED: HOT-PLUGGING: WINNING SIMULTANEOUS PROBES - ANNOUNCEMENTS
	PASSED: HOT-PLUGGING: WINNING SIMULTANEOUS PROBES: WINNING SIMULTANEOUS PROBES
	PASSED: HOT-PLUGGING: SRV PROBING/ANNOUNCEMENTS
	PASSED: HOT-PLUGGING: SUBSEQUENT CONFLICT - ANNOUNCEMENTS
	PASSED: HOT-PLUGGING: SUBSEQUENT CONFLICT - A
	PASSED: HOT-PLUGGING: SUBSEQUENT CONFLICT - ANNOUNCEMENTS
	PASSED: HOT-PLUGGING: SUBSEQUENT CONFLICT - SRV
	PASSED: HOT-PLUGGING
	PASSED: NO DUPLICATE RECORDS IN PACKETS
	PASSED: REQUIRED ADDITIONAL RECORDS IN ANSWERS
	PASSED: LEGAL CHARACTERS IN ADDRESS RECORD NAMES
	PASSED: CACHE FLUSH BIT SET IN NON-SHARED RESPONSES
	PASSED: CACHE FLUSH BIT NOT SET IN PROPOSED ANSWER OF PROBES
PASSED with 0 warning(s).

Mixed-Network Interoperability
-----------------------------
	PASSED: LINK-LOCAL TO ROUTABLE COMMUNICATION
	PASSED: ROUTABLE TO LINK-LOCAL COMMUNICATION
	PASSED: CACHE FLUSH BIT NOT SET IN UNICAST RESPONSE
	PASSED: UNICAST INTEROPERABILITY
	PASSED: CHATTINESS
	PASSED: mDNS IP TTL CHECK
	PASSED: DUPLICATE RECORDS CHECK
	PASSED: ADDITIONAL RECORDS IN ANSWER CHECK
PASSED with 0 warning(s).


******************************************************************************
	CONGRATULATIONS: You successfully passed the Bonjour Conformance test
******************************************************************************

195.2.6. DNS-SD Example

If required the dnssd_example application can be used as a starting point for a real-world application. The test starts a thread which performs some DNS-SD operations, as likely to be needed by an application that needs to search for mDNS announced network services. The test is contained within the function:

void thread_dnssd(cyg_addrword_t arg);

Since the callback function (as described in cyg_mdns_discovery_callback_register()) must NEVER block, this example uses the eCos mailbox and fixed-size-memory-allocator functionality as the mechanism to communicate between the callback (which was registered with the mDNS world with the aforementioned function) and the application code. It uses the non-blocking functionality of these core eCos features to ensure the callback will not block.

The core of the foreground functionality is performed in the function:

void example_browse(const char *tag, const cyg_uint8 * const *services, cyg_uint8 cflags, cyg_uint32 waitsecs, dump_hostport_callback_fn cb_hostport);

which performs a PTR service browse operation, and then waits for callback events to the function registered against the specific services name. As documented, a query can result in multiple registered callbacks being called since the queries are common, and not specific to a registered services name. The waitsecs just specifies how long (in seconds) the example code will wait for responses before returning. The code currently waits a relatively long time after issuing the single query, whereas a real-world application would more likely have its own control loop to re-issue periodic queries if needed; and to maintain whatever data structures for discovered services it needs to track. However, whilst the callback is active, any matching mDNS response will be passed through the callback, irrespective of whether that response is the result of a query generated by this “host”, or another network device. Another example in the function:

service_table_entry *example_query(const cyg_uint8 * const *query, cyg_uint16 type, callback_context *ctx);

implements functionality where the query is re-issued every second, but the function completes as soon as a valid result is received (which would normally be a very short time after the initial query is transmitted to the network).

The example callback is provided by the cb_service() function, and is coded to support both the basic service browsing, but also the individual specific queries. Simpler applications could implement a simpler function just responding to the information they need. Fundamentally the callback implements two phases: Processing records, and marking the end of a “set” of responses:

void cb_service(void *priv, cyg_uint32 state, const cyg_mdns_resource *phrr,
                struct pbuf *p, u16_t offname, u16_t offdata)
{
  if (state & MDNS_DISCOVERY_CB_NOMORE) { // No record for this callback
    /* If required notify foreground.

       This could trigger a U/I update with recently acquired data, or
       just be the point at which captured data is passed through. */
  } else { // Process record
    switch (phrr->type) {
      …
    }
  }

  return;
}

A fundamental task of the callback is to extract the encoded information from the referenced lwIP packet buffer. The following example fragment shows how the referenced response name can be copied into a local buffer. This example uses raw=true so that the result is an uncompressed version in the mDNS encoded string format (length byte followed by the field data, with the set of fields terminated by a 0x00 byte indicating a zero-length field):

cyg_uint16 nlen = cyg_mdns_strlen(p,offname,true);
cyg_uint8 name[nlen];
u16_t endindex;

endindex = cyg_mdns_name_uncompress((char *)name,sizeof(name),p,offname,true);
CYG_ASSERT((endindex <= offdata),"Name length mismatch");
[Note]Note

The callback should NEVER modify the passed lwIP packet buffer. A mDNS response may contain multiple records that are relevant to more than one callback as well as to the internal mDNS Responder operation. Also mDNS responses may be compressed, where data is shared between records. This is why a DNS-SD query client that needs to preserve information needs to make its own uncompressed copies without changing the referenced mDNS response data.

Back at the application foreground level, the first substantial operation performed within thread_dnssd() is to use the specific DNS-SD “wildcard” query to ascertain what service types are being announced to the network. This uses the cyg_mdns_name_services_dnssd_udp_local variable (built-in to the mDNS package) to reference the standard “_services._dns-sd._udp.local” name. The function outputs each service type discovered, for example:

INFO:<Browsing for “Local services” (wait 4-seconds)>
Service: _http._tcp.local
Service: _pdl-datastream._tcp.local
Service: _ipp._tcp.local
Service: _printer._tcp.local
Service: _udisks-ssh._tcp.local
Service: _ssh._tcp.local
Service: _sftp-ssh._tcp.local
Service: _workstation._tcp.local
Service: _http-alt._tcp.local
Service: _scanner._tcp.local
PASS:<Service Browse>

The example then performs three passes each against the “_http” and “_ipp” local service names, with each pass allowing more records to be passed to the registered callback for processing.

The first pass uses the default control flags, which will pass only the explicit matching ANSWER records for any active PTR answers. The application will output a line for each specific service type that provides a response:

INFO:<Browsing for “_http” (wait 4-seconds)>
Service: Officejet Pro 8500 A910 [D0220F]._http._tcp.local
Service: Xerox Phaser 6280DN (ac:a8:0d)._http._tcp.local
PASS:<Service Browse>

The second pass explicitly adds the MDNS_DISCOVERY_ADDITIONAL flag to have the callback supplied with matching ADDITIONAL records, and not just the matching ANSWER record. For example, this now results in the server name, port and TXT records being recorded if they are supplied:

INFO:<Browsing for “_http” ADDITIONAL (wait 4-seconds)>
INFO:<Callback flags now 03>
Service: Officejet Pro 8500 A910 [D0220F]._http._tcp.local
  http://hp8500.local:80/
  TXT-0 contains 0 entries
Service: Xerox Phaser 6280DN (ac:a8:0d)._http._tcp.local
  http://XRX0000AAACA80D.local:80/
  TXT-0 contains 9 entries
    [0]=“qtotal=5”
    [1]=“ty=Xerox Phaser 6280DN”
    [2]=“product=(Phaser 6280DN)”
    [3]=“pdl=application/postscript”
    [4]=“adminurl=http://.local./”
    [5]=“usb_MFG=Xerox”
    [6]=“usb_MDL=Xerox Phaser 6280DN”
    [7]=“Binary=T”
    [8]=“TBCP=T”
PASS:<Service Browse>

The third pass adds the MDNS_DISCOVERY_ALL flag requesting that the callback is supplied all ADDITIONAL records in the response if an ANSWER was previously passed for the response being processed. This can be used to allow the callback to gather network address information (from A or AAAA records as appropriate) so that the callback can supply complete service information in a single result. As can be seen from the following example output, the IPv4 and IPv6 addresses are now provided in the result passed through the mailbox from the callback processing to the foreground client code:

INFO:<Browsing for “_http” ALL ADDITIONAL (wait 4-seconds)>
INFO:<Callback flags now 07>
Service: Officejet Pro 8500 A910 [D0220F]._http._tcp.local
  http://hp8500.local:80/
  IPv4 192.168.7.6
  IPv6 2001:4D48:AD00:5A17:6AB5:99FF:FED0:220F
  IPv6 FE80::6AB5:99FF:FED0:220F
  TXT-0 contains 0 entries
Service: Xerox Phaser 6280DN (ac:a8:0d)._http._tcp.local
  http://XRX0000AAACA80D.local:80/
  IPv4 192.168.7.25
  TXT-0 contains 9 entries
    [0]=“qtotal=5”
    [1]=“ty=Xerox Phaser 6280DN”
    [2]=“product=(Phaser 6280DN)”
    [3]=“pdl=application/postscript”
    [4]=“adminurl=http://.local./”
    [5]=“usb_MFG=Xerox”
    [6]=“usb_MDL=Xerox Phaser 6280DN”
    [7]=“Binary=T”
    [8]=“TBCP=T”
PASS:<Service Browse>

As mentioned, the example will then perform the same queries for the “_ipp” service name outputting suitable results for the discovered service (if any are actually present on the connected network).

Finally the example performs some explicit queries as would be performed by a simple application that needs to enumerate a (previously discovered, or hardwired) service. The code uses the first valid “_http” service response, and directs specific queries to this service name (unlike the “wildcard” PTR queries used to discover services). It uses the example_query() to request the SRV, and then the A/AAAA address records as appropriate. The output would be similar to the following.

INFO:<Request specific records>
Instance: Officejet Pro 8500 A910 [D0220F]
Service: Officejet Pro 8500 A910 [D0220F]._http._tcp.local
  Host: hp8500.local (Port 80)
  IPv6 2001:4D48:AD00:5A17:6AB5:99FF:FED0:220F
  IPv6 FE80::6AB5:99FF:FED0:220F
  IPv4 192.168.7.6