W5500EVB with NetBIOs Name Service (NBNS)

The Main difference of DNS and NBNS is that the prior is generally for Internet, while the later is generally for local network. It would be an advantage for your W5500EVB to perform NBNS in terms of IoT development. To elaborate, NBNS basically works as a name displayed, regardless of wireless or wired, within the local network. For instance, the name of a printer displayed on your screen is based on NBNS. This is a sample code for using W5500 EVB as an SMTP (Simple Mail Transfer Protocol) Client.

In the following example, UDP and port 137, which is a port usually used by NBNS, would be used. When a query is received, W5500EVB checks the packet and send response back to the sender if the request matches the preset NetBIOs name of W5500.


Before burning the bin file into your EVB:

      1. Connect W5500 EVB to the router/NAT that you are using, or any other device that allows you to connect to internet
      2. Rebuild the sample code and burn it into W5500EVB
      3. Turn off the firewall if necessary


Code Explanation

There are two functions in the main loop: do_dns() and do_smtp(). For do_dns(), it is a function to find the IP address of SMTP server, which is not necessarily need in terms of SMTP or sending email; for do_smtp(), it aims at doing the whole process of email sending, which world be talked about in detail.

int main(void)
{


After boot or reboot W5500EVB, all necessary configurations are initialized.

systick_init(72);
USART1_Config();
i2c_CfgGpio();
	
printf(" NetBIOS Name Server Demo V1.0 \r\n");

gpio_for_w5500_config();
reset_w5500();
set_w5500_mac();
socket_buf_init(txsize, rxsize);
	
while(1)
{
  do_dhcp();
  if(dhcp_ok==1)
  {
    do_https();
    do_netbios();
  } 
 }
} 


In the main loop, there are two functions: do_http() and do_netbios(). After all the necessary declaration of variables, it does the do_dhcp() and get a set of IP address from the local DHCP server.



do_https()

The https here works as a medium to show the NetBIOS name. The function do_https() and do)netbios() works separately and it is not necessary to set a webpage for displaying the name. Here I will explain the function a bit, for more details please refer to (this tutorial).

HTTPs requires TCP connection, therefore W5500EVB opens a TCP socket and waits until a connection is established. Once it is established, W5500EVB will check the request packet and send packet with html code back to the client. The client can then surf the web page with his browser.

The difference between this do_https() and the one in the tutorial mentioned is that this do_https() only has one set of html code, while the other one has three sets.

void do_https(void)
{
	uint8 ch=SOCK_HTTPS;
	uint16 len;
	
	st_http_request *http_request;
	memset(rx_buf,0x00,MAX_URI_SIZE);
	http_request = (st_http_request*)rx_buf;					 

	switch(getSn_SR(ch))
	{
		case SOCK_INIT:
			listen(ch);
			break;
		
		case SOCK_LISTEN:
			break;
		
		case SOCK_ESTABLISHED:
			if(getSn_IR(ch) & Sn_IR_CON)
			{
				setSn_IR(ch, Sn_IR_CON);
			}
			if ((len = getSn_RX_RSR(ch)) > 0)		
			{
				len = recv(ch, (uint8*)http_request, len);
				*(((uint8*)http_request)+len) = 0;
				proc_http(ch, (uint8*)http_request);
				disconnect(ch);
			}
			break;
			
		case SOCK_CLOSE_WAIT:
			if ((len = getSn_RX_RSR(ch)) > 0)
			{
				len = recv(ch, (uint8*)http_request, len);     
				*(((uint8*)http_request)+len) = 0;
				proc_http(ch, (uint8*)http_request);
			}
			disconnect(ch);
			break;
			
		case SOCK_CLOSED:
			socket(ch, Sn_MR_TCP, 80, 0x00);
			break;
		
		default:
			break;
	}
}



do_netbios()

 After declaring necessary variables, W5500EVB starts switch statement:

void do_netbios(void)
{
  unsigned char state;
  unsigned int len,i;
  state = getSn_SR(NETBIOS_SOCK);
  switch(state)
  {


Once the UDP socket is opened, it does nothing until a request is received, in other words, data is received and detected by checking the free space of the register using getSn_RX_RSR(NETBIOS_SOCK).

    case SOCK_UDP:
      if((len=getSn_RX_RSR(NETBIOS_SOCK))>0)
      {
        unsigned char rem_ip_addr[4];
        uint16 rem_udp_port;
        char netbios_name[NETBIOS_NAME_LEN+1];
        NETBIOS_HDR* netbios_hdr;
        NETBIOS_NAME_HDR* netbios_name_hdr;


      // Get the data from register and store it into a RX_buffer.
        len=recvfrom(NETBIOS_SOCK,(unsigned char*)&netbios_rx_buf,len,rem_ip_addr,&rem_udp_port);
        printf("get_data: len=%d\r\n",len);

      // Print it out for debugging
        for(i=0;i<len;i++)
        {
          printf("%2x ",netbios_rx_buf[i]);
        }
        printf("\r\n");
        printf("rem_ip_addr=%d.%d.%d.%d:%d\r\n",rem_ip_addr[0],rem_ip_addr[1],rem_ip_addr[2],rem_ip_addr[3],rem_udp_port);

      // Analyse the data by first classifying the data, by putting them into two structs.
      // The format of the packet will be discussed afterwards.
        netbios_hdr = (NETBIOS_HDR*)netbios_rx_buf;
        netbios_name_hdr = (NETBIOS_NAME_HDR*)(netbios_hdr+1);


Check to see if it is a valid NBNS packet and it is a request packet. If it is, decode the data into a human readable name. Else the function ends and waits for the next packet.

        if (((netbios_hdr->flags & ntohs(NETB_HFLAG_OPCODE)) == ntohs(NETB_HFLAG_OPCODE_NAME_QUERY)) && 
            ((netbios_hdr->flags & ntohs(NETB_HFLAG_RESPONSE)) == 0) &&
             (netbios_hdr->questions == ntohs(1))) 
        {
          printf("netbios name query question\r\n");

          netbios_name_decoding( (char*)(netbios_name_hdr->encname), netbios_name, sizeof(netbios_name));
          printf("name is %s\r\n",netbios_name);


Check if the name matches the pre set NetBIOS name of W5500EVB. If it matches, construct a response packet according to the request packet, then send a response back to the sender .

          if (strcmp(netbios_name, NETBIOS_W5500_NAME) == 0) 
          {
            uint8 ip_addr[4];
            NETBIOS_RESP *resp = (NETBIOS_RESP*)netbios_tx_buf;
            printf("name is matched!\r\n");

                resp->resp_hdr.trans_id      = netbios_hdr->trans_id;
                resp->resp_hdr.flags         = htons(NETB_HFLAG_RESPONSE |
                                               NETB_HFLAG_OPCODE_NAME_QUERY |
                                               NETB_HFLAG_AUTHORATIVE |
                                               NETB_HFLAG_RECURS_DESIRED);
                resp->resp_hdr.questions     = 0;
                resp->resp_hdr.answerRRs     = htons(1);
                resp->resp_hdr.authorityRRs  = 0;
                resp->resp_hdr.additionalRRs = 0;

            memcpy( resp->resp_name.encname, netbios_name_hdr->encname, sizeof(netbios_name_hdr->encname));

                resp->resp_name.nametype     = netbios_name_hdr->nametype;
                resp->resp_name.type         = netbios_name_hdr->type;
                resp->resp_name.cls          = netbios_name_hdr->cls;
                resp->resp_name.ttl          = htonl(NETBIOS_NAME_TTL);
                resp->resp_name.datalen      = htons(sizeof(resp->resp_name.flags)+sizeof(resp->resp_name.addr));
                resp->resp_name.flags        = htons(NETB_NFLAG_NODETYPE_BNODE);

            getSIPR(ip_addr);
            memcpy(resp->resp_name.addr,ip_addr,4);
            sendto(NETBIOS_SOCK, (unsigned char*)resp, sizeof(NETBIOS_RESP), rem_ip_addr, rem_udp_port);
            printf("send response\r\n");
          }


Else ends the functions, loops and does nothing until another packet is received.

          else 
            printf("name is not matched!\r\n");
        }
      }

      break;

    case SOCK_CLOSED:
      close(NETBIOS_SOCK);
      socket(NETBIOS_SOCK,Sn_MR_UDP,NETBIOS_PORT,0);
      break;

    default:
      break;
  }
}


Format of Received Packet

Since NetBIOS is not often seen on the Internet, it would be a bit difficult for learners to follow. Thus, the format of the packet received by W5500EVB will be briefly discussed below.

The format of the packet received would be like the following:

NetBIOs Name Service Packets. Photo from RFC1002

The general format, which is called the General Format of Name Service Packets, would be a little bit more complex. For more details please reference to (the protocol standard) 

The header structure includes the following elements:

Structure of HEADER. Photo from RFC1002
  • QDCOUNT, ANCOUNT, NSCOUNT, and ARCOUNT are generally not useful to analyse the packet,please look it up yourself if you are interested.
  • NAME_TRN_ID, Transaction ID for Name Service Transaction, is a unique ID placed by the requester for better identification.
  • OPCODE shows the purpose of the packet, such as whether it is a response packet or a request packet; whether it is a query or a registration.
  • NM_FLAGS shows the characteristic, or special request of the packet. For instance, requester may desire a Recursion or the packet. It may also indicate that the datagram is too large that there would be follow-up packet coming.
  • RCODE shows error if there is any

While the following is the structure of Question Entries:

QUESTION ENTRIES. Photo from RFC1002
  • QUESTION_NAME is the compressed name representation of the NetBIOS name for the request.
  • QUESTION_TYPE shows the type of request. The values for this field are specified for each request.
  • QUESTION_CLASS shows the class of the request. The values for this field are specified for each request.



In the sample code, there are two structs storing and classifying the received packet, which are “NETBIOS_HDR” for Header and NETBIOS_NAME_HDR for Question Entries respectively. The following are the structs:

/** NetBIOS message header */
#pragma pack(1)
typedef struct _NETBIOS_HDR {
  uint16 trans_id;
  uint16 flags;
  uint16 questions;
  uint16 answerRRs;
  uint16 authorityRRs;
  uint16 additionalRRs;
}NETBIOS_HDR;


/** NetBIOS message name part */
typedef struct _NETBIOS_NAME_HDR {
  uint8  nametype;
  uint8  encname[(NETBIOS_NAME_LEN*2)+1];
  uint16 type;
  uint16 cls;
  uint32 ttl;
  uint16 datalen;
  uint16 flags;
  //ip_addr_p_t addr;
  uint8 addr[4];
}NETBIOS_NAME_HDR;



To have a thorough look of what the packet received would look like:

Output of the Sample Code with Analysis of Received Data Packet



Remarks

  • Broadcast is sent from PC(upper computer) to find whose the NetBIOs name is, similar to the case of doing DHCP.   
  • To test the function, you may simply type “W5500” in the search engine bar of your web browser. Please be noted that the netBIOs name would be restored in your computer for 10 minutes that there would be no response from your W5500EVB to serial if you type it again within that period of time.
  • Other than “W5500”, you may also type anything without any “.com” or “.org” to test the function. For example:
    • “iamagoodguy” can be used to test the function
    • “google.com” cannot be used to test the function