Author Topic: Multi-homed network and multiple interfaces  (Read 7826 times)

Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3236
    • View Profile
    • uTasker
Multi-homed network and multiple interfaces
« on: July 19, 2012, 11:50:22 PM »
Hi All

Furure versions uTasker project will support multi-homed networking and multiple interfaces and the following describes how to modify existing projects for compatibility with the newer versions with these features. This description is linked to in the release notes where "multiple interface and multiple network support" has been added.

Regards

Mark


**************************************************************************************************

The uTasker project has new support for mult-homed networking. This means that the TCP/IP stack can handle multiple networks and also multiple interface (including multiple interfaces in each network)
This new support required a few adjustments that affect the original definitions.
The following is a step by step explanation about making existing projects compatible with the new use if multi-network/interface operation is not required:


1)
Oringinally NETWORK_PARAMETERS network; (and network_flash) was a single network struct holding the IP defauls of the single network
New: NETWORK_PARAMETERS network[IP_NETWORK_COUNT]; this should now be defined as an array of network structs, whereby IP_NETWORK_COUNT defaults to 1

2)
All uses of network (and network_flash) need to be changed to network[DEFAULT_NETWORK], whereby DEFAULT_NETWORK defaults to 0

3) The interface fnGetARPentry() has been changed to be more flexible

Rather than returning either the IP or MAC address in an ARP cache entry it returns a pointer to the netry so that more infomration can be displayed.
extern unsigned char *fnGetARPentry(unsigned char ucEntry, int iIP_MAC);
has been changed to
extern ARP_TAB *fnGetARPentry(unsigned char ucEntry, int iIP_MAC);

This means that routines no longer pass the requested information but instead extract the required part from the ARP_TAB pointer. For example:

cPtr = (CHAR *)fnGetARPentry((unsigned char)(*ptrBuffer - 'a'), GET_IP); // get pointer to IP address

becomes

ARP_TAB *PtrEntry = fnGetARPentry((unsigned char)(*ptrBuffer - 'a')); // get pointer to ARP entry
cPtr = PtrEntry->ucIP;




The three changes above are adequate to make existing projects fully compatible.





To use the new features the following steps can be followed:

1) Change the number of networks by adding the following define with the number required (example 2)
#define IP_NETWORK_COUNT 2

2) Add a define for the second network (remembering that the default network defaults to 0)
#define SECOND_NETWORK   1    // next network number

3) Add the configuration of the second network (the first one is already configured). For example add a second default set:

static const NETWORK_PARAMETERS network_default_2 = {
    (AUTO_NEGOTIATE /*| FULL_DUPLEX*/ | RX_FLOW_CONTROL),                // usNetworkOptions - see driver.h for other possibilities
    {0x00, 0x00, 0x00, 0x00, 0x00, 0x01},                                // ucOurMAC - when no other value can be read from parameters this will be used
    { 192, 168, 2, 2 },                                                  // ucOurIP - our default IP address
    { 255, 255, 255, 0 },                                                // ucNetMask - Our default network mask
    { 192, 168, 2, 1 },                                                  // ucDefGW - Our default gateway
    { 192, 168, 2, 1 },                                                  // ucDNS_server - Our default DNS server
#ifdef USE_IPV6
    { _IP6_ADD_DIGIT(0x2001), _IP6_ADD_DIGIT(0x0470), _IP6_ADD_DIGIT(0x0026), _IP6_ADD_DIGIT(0x0105), _IP6_ADD_DIGIT(0x0000), _IP6_ADD_DIGIT(0x0000), _IP6_ADD_DIGIT(0x0000), _IP6_ADD_DIGIT(0x0020) }, // default global IPV6 address
    #if defined USE_IPV6INV4
    { 216, 66, 80, 98 },                                                 // IPv6 in IPv4 tunnelling enabled when the tunnel address is not 0.0.0.0
    #endif
#endif
};

and then initialise the second set with it if no defaults are already available:

            fnSetDefaultNetwork(&network[DEFAULT_NETWORK]);              // if no parameters are available, load the default set
            fnSetDefaultNetwork2(&network[SECOND_NETWORK]);              // load defaults for second network

where:
extern void fnSetDefaultNetwork2(NETWORK_PARAMETERS *ptrNetPars)
{
    uMemcpy(ptrNetPars, &network_default_2, sizeof(NETWORK_PARAMETERS));
}


Now the second network has its own configuration.

4) If there is a second network it needs to be attached to another interface. That means that a second interface must also be defined.
#define IP_NETWORK_COUNT 2   // two interfaces in the system

Assuming that there are two Ethernet interfaces controlled by the processor each must be opened and their handle entered as follows:

Ethernet_handle = fnOpen(TYPE_ETHERNET, FOR_I_O, &ethernet);
fnEnterInterfaceHandle(DEFAULT_IP_INTERFACE, Ethernet_handle);
Ethernet_handle_2 = fnOpen(TYPE_ETHERNET, FOR_I_O, &ethernet);
fnEnterInterfaceHandle(SECOND_IP_INTERFACE, Ethernet_handle_2);

Where the additional interface has its own number (DEFAULT_IP_INTERFACE default is 0)
#define SECOND_IP_INTERFACE        1        // next interfacenumber


5) Receiving on a new interface depends on its type (it could be Ethernet or it could be another adapter like Wifi, serial etc. but with Ethernet conform content).
The important this is that all received data passed to the TCP/IP stack are pass with the following fields set to math the interface and its network:

ETHERNET_FRAME rx_frame;
.. set up frame content
rx_frame.ucNetworkID = SECOND_NETWORK;                                // mark that frame arriving on this interface belong to the second network
rx_frame.Tx_handle = Ethernet_handle_2;                               // mark the interface handle to be used when responding
..
fnHandleIP_protocol(rx_frame.ptEth->ucData[IPV4_PROTOCOL_OFFSET], &rx_frame); // pass frame to IP

The TCP/IP stack now known which network the data belongs to and also which interface to send any responses to.

6) When the user establishes connections or sends data this will default take place on the default interface on the default network.
To define that this should be on a different network and using a corresponding interface the user controls it with its socket number. All UDP and TCP 'connection' use a socket so their basic operation. Also ICMP can use a 'virtual' socket for identification purposes during its use.

The basic socket definition (in types.h) is as follows:
typedef signed char        USOCKET;
This allows up to 127 sockets (127 UDP and 127 TCP) to be maintained.
The following shows how the socket with is made 15 bit and then the network and interface(s) belonging to each socket are coded into it. The example shows a configuration supporting 2 networks and 3 interfaces but many more could be coded into the socket without restricting the number of sockets possible.

// TCP/IP support
//
typedef signed char        USOCKET;                                      // socket support from 0..32767 (negative values are errors) - some bits used to identify network and interface


// Multi-network/interface socket control
//
#define NETWORK_SHIFT        14                                          // valid for just 2 networks and USOCKET as signed short
#define NETWORK_MASK         0x01
#define INTERFACE_SHIFT      11                                          // valid for up to 3 interfaces and USOCKET as signed short
#define INTERFACE_MASK       0x07
#define SOCKET_NUMBER_MASK   0x7ff

/*------------------------------------------------------------------*/
/* V | N | I2 | I1 | I0 | S | S | S | S | S | S | S | S | S | S | S */
/*------------------------------------------------------------------*/

// V = valid socket
// N = Network (0 / 1) that the socket can use - there are two networks available
// Ix = flags for each interface that the socket can use I0, I1, I2 means that there are 3 physical sockets available (2 in one network and 1 in the other)
// S = socket number from 0..0x7ff
// note that USOCKET has been chosen as signed short to give adequate width


7) The networks and interfaces can now be defines accoring to project use with help from the following macros (this can be added in config.h after the inclusion of types.h):

// Multi-network/interface socket control - see types.h for the configuration
//
#define PRIMARY_NETWORK_SOCKET      defineNetwork(DEFAULT_NETWORK)
#define SECONDARY_NETWORK_SOCKET    defineNetwork(SECOND_NETWORK)

#define PRIMARY_INTERFACE           defineInterface(DEFAULT_IP_INTERFACE)
#define SECONDARY_INTERFACE         defineInterface(SECOND_IP_INTERFACE)

8 ) The following sends a ping to the first network (default)
fnSendPing(ping_address, MAX_TTL, OWN_TASK, dummy_socket);
which is equivalent to
fnSendPing(ping_address, MAX_TTL, OWN_TASK, (dummy_socket | PRIMARY_NETWORK_SOCKET | PRIMARY_INTERFACE));

Therefore to direct it to the second interface the socket is simply set with the specific socket details
fnSendPing(ping_address, MAX_TTL, OWN_TASK, (dummy_socket | SECONDARY_NETWORK_SOCKET | SECONDARY_INTERFACE));


Note that, if there are multiple interface on the second network only the defined interface will be used for discovering the IP address if it is not already resolved.
To cause address resolution to take place on all (or a defined sub-set of) the interfaces each interface can be individually specified - eg.
fnSendPing(ping_address, MAX_TTL, OWN_TASK, (dummy_socket | SECONDARY_NETWORK_SOCKET | SECONDARY_INTERFACE | TERTIARY_INTERFACE));


*****************************************************************