Netduino home hardware projects downloads community

Jump to content


The Netduino forums have been replaced by new forums at community.wildernesslabs.co. This site has been preserved for archival purposes only and the ability to make new accounts or posts has been turned off.
Photo

Netbios


  • Please log in to reply
37 replies to this topic

#1 pascal06

pascal06

    Advanced Member

  • Members
  • PipPipPip
  • 95 posts
  • LocationFrance

Posted 04 October 2010 - 09:30 PM

Hello,

Netbios name resolution can be a easy way to communicate with a NetduinoPlus. Especialy in case of using DHCP. With this feature, you can use (only on local subnet) the name of your Netduino instead of using IP address. But, it seems that NetduinoPlus doesn't support local broadcast UDP. My sample works on Emulator, but not on NetduinoPlus.

using System;
using System.Threading;
using Microsoft.SPOT;
using System.Net.Sockets;
using System.Net;
using Microsoft.SPOT.Net.NetworkInformation;

namespace Netbios
{
    public class Program
    {
        private const int UDP_PORT_NETBIOS_NS = 137;

        public static Byte[] EncodeNetbiosName(string Name)
        {
            byte[] result = new byte[32];
            char c;
            for (int i = 0; i < 15; i++)
            {
                c = i < Name.Length ? Name[i] : ' ';
                result[i * 2] = (byte)(((byte)(c) >> 4) + 65);
                result[(i * 2) + 1] = (byte)(((byte)(c) & 0x0f) + 65);
            }
            result[30] = 0x41;
            result[31] = 0x41;
            return result;
        }

        public static string DecodeNetbiosName(byte[] NbName)
        {
            string result = "";
            for (int i = 0; i < 15; i++)
            {
                byte b1 = NbName[i * 2];
                byte b2 = NbName[(i * 2) + 1];
                char c = (char)( ( (b1 - 65) << 4 ) | ( b2 - 65  ) );
                result += c;
            }
            return result;
        }

        public static bool BytesEqual(byte[] Array1, int Start1, byte[] Array2, int Start2, int Count)
        {
            bool result = true;
            for (int i = 0; i < Count - 1; i++)
            {
                if (Array1[i + Start1] != Array2[i + Start2])
                {
                    result = false;
                    break;
                }
            }
            return result;
        }

        public static void Main()
        {
            byte[] myNbName = EncodeNetbiosName("NETDUINO");

            NetworkInterface[] networkInterfaces = NetworkInterface.GetAllNetworkInterfaces();

            using (Socket serverSocket = new Socket(AddressFamily.InterNetwork,
                                                    SocketType.Dgram,
                                                    ProtocolType.Udp))
            {
                EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, UDP_PORT_NETBIOS_NS);
                byte[] IP = IPAddress.Parse(networkInterfaces[0].IPAddress).GetAddressBytes();
                serverSocket.Bind(remoteEndPoint);
                while (true)
                {
                    if (serverSocket.Poll(1000, //timeout in micro seconds
                                               SelectMode.SelectRead))
                    {
                        byte[] inBuffer = new byte[serverSocket.Available];
                        int count = serverSocket.ReceiveFrom(inBuffer, ref remoteEndPoint);
                        if ((inBuffer[2] >> 3) == 0) // opcode == 0
                        {
                            byte[] nbName = new byte[32];
                            Array.Copy(inBuffer, 13, nbName, 0, 32);
                            Debug.Print("NETBIOS NAME QUERY: "+DecodeNetbiosName(nbName));
                            if (BytesEqual(inBuffer, 13, myNbName, 0, 32))
                            {
                                byte[] outBuffer = new byte[62];
                                outBuffer[0] = inBuffer[0]; // trnid
                                outBuffer[1] = inBuffer[1]; // trnid
                                outBuffer[2] = 0x85;

                                outBuffer[3] = 0x00;
                                outBuffer[4] = 0x00;
                                outBuffer[5] = 0x00;
                                outBuffer[6] = 0x00;

                                outBuffer[7] = 0x01;

                                outBuffer[8] = 0x00;
                                outBuffer[9] = 0x00;
                                outBuffer[10] = 0x00;
                                outBuffer[11] = 0x00;

                                outBuffer[12] = 0x20;
                                for (int i = 0; i < 32; i++)
                                {
                                    outBuffer[i + 13] = myNbName[i];
                                }

                                outBuffer[45] = 0x00;

                                outBuffer[46] = 0x00; outBuffer[47] = 0x20; // RR_TYPE: NB
                                outBuffer[48] = 0x00; outBuffer[49] = 0x01; // RR_CLASS: IN

                                outBuffer[50] = 0x00; // TTL
                                outBuffer[51] = 0x0f;
                                outBuffer[52] = 0x0f;
                                outBuffer[53] = 0x0f;

                                outBuffer[54] = 0x00; outBuffer[55] = 0x06; // RDLENGTH

                                outBuffer[56] = 0x60; outBuffer[57] = 0x00; // NB_FLAGS

                                outBuffer[58] = IP[0];
                                outBuffer[59] = IP[1];
                                outBuffer[60] = IP[2];
                                outBuffer[61] = IP[3];

                                serverSocket.SendTo(outBuffer, remoteEndPoint);
                            }
                        }

                    }
                    Thread.Sleep(100);
                }
            }
        }

    }
}

To test this program, run it on emulator and try to ping NETDUINO with a computer connected on the same subnet.

Is it possible to change the configuration of lwIP to support udp local broadcast ?

Pascal

#2 Chris Walker

Chris Walker

    Secret Labs Staff

  • Moderators
  • 7767 posts
  • LocationNew York, NY

Posted 04 October 2010 - 10:31 PM

Is it possible to change the configuration of lwIP to support udp local broadcast ?


Quite possibly. lwIP is a bit of a black box of sorts, but we can look into it.

Please note that the emulator on Windows probably uses RTIP (the commercial stack) or Microsoft's IP stack instead of lwIP--so you may different behavior in the emulator than on an open-source .NET MF device.

Chris

#3 pascal06

pascal06

    Advanced Member

  • Members
  • PipPipPip
  • 95 posts
  • LocationFrance

Posted 05 October 2010 - 09:39 AM

Quite possibly. lwIP is a bit of a black box of sorts, but we can look into it.

Please note that the emulator on Windows probably uses RTIP (the commercial stack) or Microsoft's IP stack instead of lwIP--so you may different behavior in the emulator than on an open-source .NET MF device.

Chris


Thanks Chris,

For me, the emulator use the host windows stack because the emulator is 100% written with managed code.

I have tested my program on a TAOHE-II which use RTIP, and it works.

So, my conclusion is the lwIP doesn't catch broadcast packets. I will also take a look on it.

A little question, how to see firmware debug message like LWIP_DEBUGF ....

Pascal

#4 pascal06

pascal06

    Advanced Member

  • Members
  • PipPipPip
  • 95 posts
  • LocationFrance

Posted 05 October 2010 - 10:07 AM

Quite possibly. lwIP is a bit of a black box of sorts, but we can look into it.


Chris,

To be able to receive a udp local broadcast, we need to be able to receive ethernet broadcast.

Look at line 45 in AT91_EMAC_lwip.h:
static const UINT32 EMAC_NBC = (0x1ul << 5); // (EMAC) No broadcast.

It seems that MAC layer is configured to not catch any broadcast packet.

Thought ?

Pascal

#5 pascal06

pascal06

    Advanced Member

  • Members
  • PipPipPip
  • 95 posts
  • LocationFrance

Posted 07 October 2010 - 09:33 PM

Look at line 45 in AT91_EMAC_lwip.h:
static const UINT32 EMAC_NBC = (0x1ul << 5); // (EMAC) No broadcast.


That's not the right direction. I'm able now, with some firmware modifications, to send debug messages to COM1. And after putting messages on ip.c and udp.c, I can see that my NetduinoPlus receive UDP even if this is a local broadcast and the EMAC_NBC register is enabled. So, I will continue to search why these broadcast are not catched by my application.

DM9161_AutoNegotiate
Valid PHY Found: 31
PHY: Vendor Number Model = 0xA
PHY: Model Revision Number = 0x0
AutoNegotiate complete
DM9161_GetLinkSpeed passed
Link detected 0x0
ip address from interface info: 192.168.5.100
.NetMF v4.1.2821.0
NetduinoPlus, Build Date:Oct  7 2010 23:03:52
ARM Compiler version 410462

TinyCLR (Build 4.1.2821.0)

Starting...
Created EE.Started 
Hardware.
No debugger!
//
Extra lines removed
//   
Ready.
IP Received
UDP Received
IP Received
UDP Received
IP Received
UDP Received
IP Received
UDP Received
IP Received
UDP Received
IP Received
IP Received
UDP Received
IP Received
IP Received 

Pascal

#6 Charles

Charles

    Advanced Member

  • Members
  • PipPipPip
  • 192 posts

Posted 09 October 2010 - 02:21 AM

Could you post your current code? If I get a chance I will load it on my N+ this weekend and have a look.

#7 pascal06

pascal06

    Advanced Member

  • Members
  • PipPipPip
  • 95 posts
  • LocationFrance

Posted 09 October 2010 - 09:49 AM

Could you post your current code? If I get a chance I will load it on my N+ this weekend and have a look.


Thanks Charles,

But I found the way to solve the issue :rolleyes:

We need to disable IP_SOF_BROADCAST_RECV define in opt.h. This a filter at pcb level to drop any broadcast.

Instead of changing the firmware, I also see that we can leave this define as it is, and enable the pcb options SOF_BROADCAST to also solve this issue. I just need to find the way to enable it at C# level.

Pascal

#8 pascal06

pascal06

    Advanced Member

  • Members
  • PipPipPip
  • 95 posts
  • LocationFrance

Posted 09 October 2010 - 12:33 PM

Hello,

It seems that we have a issue on both AT91 EMAC & ENC28J60 driver.

Usualy, we need to enable some flags to define interface capability.

And that's not the case on both driver, here my proposal modification for Netduino :

err_t AT91_EMAC_ethhw_init(struct netif *myNetIf) 
{ 
    myNetIf->mtu = AT91_EMAC_MAX_FRAME_SIZE;

    /* ethhw_init() is user-defined */
    /* use ip_input instead of ethernet_input for non-ethernet hardware */
    /* (this function is assigned to netif.input and should be called by the hardware driver) */

    /* Assign the xmit routine to the stack's netif and call the driver's Open */

    myNetIf->output = etharp_output;
    myNetIf->linkoutput = AT91_EMAC_LWIP_xmit;
    myNetIf->status_callback = AT91_EMAC__status_callback;
    
    // [DP CHANGE]
    myNetIf->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;

    AT91_EMAC_LWIP_open( myNetIf );

    return 0; 
}

And with this modification, I see another good side effect, now my Netduino send a gratuitus ARP when it start. Which is a usualy behavior.

So, we also need to modify my Netbios program to enable Broadcast receive at socket level (which is not necessary with RTIP). Here the final code for Netbios :

using System;
using System.Threading;
using Microsoft.SPOT;
using System.Net.Sockets;
using System.Net;
using Microsoft.SPOT.Net.NetworkInformation;

namespace Netbios
{
    public class Program
    {
        private const int UDP_PORT_NETBIOS_NS = 137;

        public static Byte[] EncodeNetbiosName(string Name)
        {
            byte[] result = new byte[32];
            char c;
            for (int i = 0; i < 15; i++)
            {
                c = i < Name.Length ? Name[i] : ' ';
                result[i * 2] = (byte)(((byte)(c) >> 4) + 65);
                result[(i * 2) + 1] = (byte)(((byte)(c) & 0x0f) + 65);
            }
            result[30] = 0x41;
            result[31] = 0x41;
            return result;
        }

        public static string DecodeNetbiosName(byte[] NbName)
        {
            string result = "";
            for (int i = 0; i < 15; i++)
            {
                byte b1 = NbName[i * 2];
                byte b2 = NbName[(i * 2) + 1];
                char c = (char)(((b1 - 65) << 4) | (b2 - 65));
                result += c;
            }
            return result;
        }

        public static bool BytesEqual(byte[] Array1, int Start1, byte[] Array2, int Start2, int Count)
        {
            bool result = true;
            for (int i = 0; i < Count - 1; i++)
            {
                if (Array1[i + Start1] != Array2[i + Start2])
                {
                    result = false;
                    break;
                }
            }
            return result;
        }

        public static void Main()
        {
            Thread.Sleep(10000);
            byte[] myNbName = EncodeNetbiosName("NETDUINO");

            NetworkInterface[] networkInterfaces = NetworkInterface.GetAllNetworkInterfaces();

            using (Socket serverSocket = new Socket(AddressFamily.InterNetwork,
                                                    SocketType.Dgram,
                                                    ProtocolType.Udp))
            {
                serverSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true); // Enable broadcast
                EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, UDP_PORT_NETBIOS_NS);
                byte[] IP = IPAddress.Parse(networkInterfaces[0].IPAddress).GetAddressBytes();
                serverSocket.Bind(remoteEndPoint);
                while (true)
                {
                    if (serverSocket.Poll(1000, //timeout in micro seconds 
                                               SelectMode.SelectRead))
                    {
                        byte[] inBuffer = new byte[serverSocket.Available];
                        int count = serverSocket.ReceiveFrom(inBuffer, ref remoteEndPoint);
                        if ((inBuffer[2] >> 3) == 0) // opcode == 0 
                        {
                            byte[] nbName = new byte[32];
                            Array.Copy(inBuffer, 13, nbName, 0, 32);
                            Debug.Print("NETBIOS NAME QUERY: " + DecodeNetbiosName(nbName));
                            if (BytesEqual(inBuffer, 13, myNbName, 0, 32))
                            {
                                byte[] outBuffer = new byte[62];
                                outBuffer[0] = inBuffer[0]; // trnid 
                                outBuffer[1] = inBuffer[1]; // trnid 
                                outBuffer[2] = 0x85;

                                outBuffer[3] = 0x00;
                                outBuffer[4] = 0x00;
                                outBuffer[5] = 0x00;
                                outBuffer[6] = 0x00;

                                outBuffer[7] = 0x01;

                                outBuffer[8] = 0x00;
                                outBuffer[9] = 0x00;
                                outBuffer[10] = 0x00;
                                outBuffer[11] = 0x00;

                                outBuffer[12] = 0x20;
                                for (int i = 0; i < 32; i++)
                                {
                                    outBuffer[i + 13] = myNbName[i];
                                }

                                outBuffer[45] = 0x00;

                                outBuffer[46] = 0x00; outBuffer[47] = 0x20; // RR_TYPE: NB 
                                outBuffer[48] = 0x00; outBuffer[49] = 0x01; // RR_CLASS: IN 

                                outBuffer[50] = 0x00; // TTL 
                                outBuffer[51] = 0x0f;
                                outBuffer[52] = 0x0f;
                                outBuffer[53] = 0x0f;

                                outBuffer[54] = 0x00; outBuffer[55] = 0x06; // RDLENGTH 

                                outBuffer[56] = 0x60; outBuffer[57] = 0x00; // NB_FLAGS 

                                outBuffer[58] = IP[0];
                                outBuffer[59] = IP[1];
                                outBuffer[60] = IP[2];
                                outBuffer[61] = IP[3];

                                serverSocket.SendTo(outBuffer, remoteEndPoint);
                            }
                        }

                    }
                    Thread.Sleep(100);
                }
            }
        }

    }
}

Now, I will test if this modification have any effect regarding unknow IP commuication. When you try to communcate to a IP which is not respond to a ARP request.

Pascal

#9 pascal06

pascal06

    Advanced Member

  • Members
  • PipPipPip
  • 95 posts
  • LocationFrance

Posted 09 October 2010 - 03:26 PM

Hello,

Regarding ARP request issue, I found this (funny) etharp.c :

/**
 * Clears expired entries in the ARP table.
 *
 * This function should be called every ETHARP_TMR_INTERVAL microseconds (5 seconds),
 * in order to expire entries in the ARP table.
 */
void
etharp_tmr(void)
{
  u8_t i;

  LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer\n"));
  /* remove expired entries from the ARP table */
  for (i = 0; i < ARP_TABLE_SIZE; ++i) {
    arp_table[i].ctime++;
    if (((arp_table[i].state == ETHARP_STATE_STABLE) &&
         (arp_table[i].ctime >= ARP_MAXAGE)) ||
        ((arp_table[i].state == ETHARP_STATE_PENDING)  &&
         (arp_table[i].ctime >= ARP_MAXPENDING))) {
         /* pending or stable entry has become old! */
      LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: expired %s entry %"U16_F".\n",
           arp_table[i].state == ETHARP_STATE_STABLE ? "stable" : "pending", (u16_t)i));
      /* clean up entries that have just been expired */
      /* remove from SNMP ARP index tree */
      snmp_delete_arpidx_tree(arp_table[i].netif, &arp_table[i].ipaddr);
#if ARP_QUEUEING
      /* and empty packet queue */
      if (arp_table[i].q != NULL) {
        /* remove all queued packets */
        LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: freeing entry %"U16_F", packet queue %p.\n", (u16_t)i, (void *)(arp_table[i].q)));
        free_etharp_q(arp_table[i].q);
        arp_table[i].q = NULL;
      }
#endif
      /* recycle entry for re-use */      
      arp_table[i].state = ETHARP_STATE_EMPTY;
    }
#if ARP_QUEUEING
    /* still pending entry? (not expired) */
    if (arp_table[i].state == ETHARP_STATE_PENDING) {
        /* resend an ARP query here? */
    }
#endif
  }
}

It seems that lwip need to be improved ... Strange,

For me, yes we need to resend ARP and after a (configurable) number of time, we need to generate a exception,

I will try to populate this part of code ...

Pascal

#10 Chris Walker

Chris Walker

    Secret Labs Staff

  • Moderators
  • 7767 posts
  • LocationNew York, NY

Posted 09 October 2010 - 05:01 PM

Hmm, very interesting. Are you using RVDS to compile your code? Would you like us to compile in a set of changes that you can test?

#11 pascal06

pascal06

    Advanced Member

  • Members
  • PipPipPip
  • 95 posts
  • LocationFrance

Posted 09 October 2010 - 05:11 PM

Hmm, very interesting. Are you using RVDS to compile your code? Would you like us to compile in a set of changes that you can test?


I use RVDS 4.1 Pro Evaluation, expire in 26 days. I have some time to fix it :)

Pascal

#12 Chris Walker

Chris Walker

    Secret Labs Staff

  • Moderators
  • 7767 posts
  • LocationNew York, NY

Posted 09 October 2010 - 05:48 PM

I use RVDS 4.1 Pro Evaluation, expire in 26 days. I have some time to fix it :)


Okay, cool. Thanks for your help and contributions on this!

Chris

#13 pascal06

pascal06

    Advanced Member

  • Members
  • PipPipPip
  • 95 posts
  • LocationFrance

Posted 09 October 2010 - 06:01 PM

Okay, cool. Thanks for your help and contributions on this!

Chris


In lwip, we have multiple timers, to timeout when a event not occurs in certain time, or others proposes.

Timer logic is on sys.c, and inititialisation of all timers is on tcpip.c. But it seems that this timer doesn't work. I have put some message and no timer event occurs, never !!!

I cannot debug, because the firmware cannot be compiled with debug flavor (to big).

Do you have a way in your side to check if these timers works ?

As a example, arp_timer in tcpip.c should be fire every 5 seconds, and it's not the case.

Pascal

#14 pascal06

pascal06

    Advanced Member

  • Members
  • PipPipPip
  • 95 posts
  • LocationFrance

Posted 14 October 2010 - 07:09 PM

Hello, Now, I'm able to compile version 4.1 with lwip in debug mode on my TAHOEII. I can see all debug messages on UART2 of my TAHOEII. It's usefull to compare behavior. And I see that lwip send a ARP 10 times before raising a exception in case of no response. But it is not the case with Netduino. My feeling is that related to timer. Pascal

#15 Chris Walker

Chris Walker

    Secret Labs Staff

  • Moderators
  • 7767 posts
  • LocationNew York, NY

Posted 14 October 2010 - 07:43 PM

Hello,

Now, I'm able to compile version 4.1 with lwip in debug mode on my TAHOEII. I can see all debug messages on UART2 of my TAHOEII.
It's usefull to compare behavior. And I see that lwip send a ARP 10 times before raising a exception in case of no response.
But it is not the case with Netduino. My feeling is that related to timer.

Pascal


Great find, Pascal. Very interesting.

You're using the ENC28J60 on the Tahoe II board--right? I wonder if it's a difference between the lwIP drivers for the integrated AT91 EMAC and the lwIP drivers for the ENC28J60. Based on historical .NET MF use, the ENC28J60 drivers are probably well tested.

Chris

#16 pascal06

pascal06

    Advanced Member

  • Members
  • PipPipPip
  • 95 posts
  • LocationFrance

Posted 14 October 2010 - 07:51 PM

Great find, Pascal. Very interesting.

You're using the ENC28J60 on the Tahoe II board--right? I wonder if it's a difference between the lwIP drivers for the integrated AT91 EMAC and the lwIP drivers for the ENC28J60. Based on historical .NET MF use, the ENC28J60 drivers are probably well tested.

Chris


Yes I use ENC28J60 on Tahoe II board. I will search about differences ...

Pascal

#17 Chris Walker

Chris Walker

    Secret Labs Staff

  • Moderators
  • 7767 posts
  • LocationNew York, NY

Posted 14 October 2010 - 07:52 PM

Yes I use ENC28J60 on Tahoe II board. I will search about differences ...


Pascal,

Have you tried to use an ENC28J60 on the Netduino--and compile for the ENC28J60 drivers instead of the AT91_EMAC drivers? It would be interesting to see if that behaved similarly...

Chris

#18 pascal06

pascal06

    Advanced Member

  • Members
  • PipPipPip
  • 95 posts
  • LocationFrance

Posted 14 October 2010 - 07:53 PM

Yes I use ENC28J60 on Tahoe II board. I will search about differences ...

Pascal


But, if you are able to use a JTAG to debug, may you make a little test, just to ensure that all timer works ?

In tcpip.c, arp_timer() should be fire every 5 seconds ...

Pascal

#19 Chris Walker

Chris Walker

    Secret Labs Staff

  • Moderators
  • 7767 posts
  • LocationNew York, NY

Posted 17 October 2010 - 08:22 PM

But, if you are able to use a JTAG to debug, may you make a little test, just to ensure that all timer works ?

In tcpip.c, arp_timer() should be fire every 5 seconds ...


Pascal,

Could you try the following?

Comment out lines 132 and 140 in:
C:\MicroFrameworkPK_v4_1\DeviceCode\Targets\Native\AT91\DeviceCode\AT91_EMAC_lwip\AT91_EMAC_lwip_adapter.cpp
// LwipUpTimeCompletion.EnqueueDelta64( 2000000 );
// LwipUpTimeCompletion.EnqueueDelta64( 2000000 );

If you do that, do your ARP timers start firing properly?

Chris

#20 Valkyrie-MT

Valkyrie-MT

    Advanced Member

  • Members
  • PipPipPip
  • 315 posts
  • LocationIndiana, USA

Posted 21 October 2010 - 01:48 AM

I tried Pascal06's code without the driver change on my N+, but it no workie. It doesn't seem to do anything. Bummer. Is there an N+ firmware pack that includes this driver patch that could resolve this? I think I am too much of a Newbie to try compiling my own version... -Valkyrie-MT




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users

home    hardware    projects    downloads    community    where to buy    contact Copyright © 2016 Wilderness Labs Inc.  |  Legal   |   CC BY-SA
This webpage is licensed under a Creative Commons Attribution-ShareAlike License.