Configuring and Running RNDIS

This document explains how to configure the Symbian platform to support RNDIS.

It describes configuring the Internet Access Point (IAP) in the Central Repository, configuring the IAP settings for IP connectivity and setting up the IP forwarding to allow data to be forwarded between the interfaces.

Introduction

RNDIS uses the Comms Framework to provide IP communications over USB. It is necessary to configure an Internet Access Point (IAP) for use by RNDIS. Comms Framework uses a layered architecture and so it is necessary to configure RNDIS for each layer as well as for LAN and PAN. The IAP ID for RNDIS must be placed in the Central Repository. IP forwarding must be configured to enable TCP/IP to transfer data between the two interfaces.

In addition it is necessary to define the RNDIS Personality for USB so RNDIS can be started and stopped when the USB cable is attached or detached.

Configuring the IAP ID in the Central Repository

The IAP ID to be used by the RNDIS service is saved in the Central Repository. Device creators need to configure the IAP ID in the Central Repository.

If more than one IAP is provided, the first record in the table has a higher priority than the later IAP. If the first IAP fails to start, the second IAP tries to start and so on.

The following code shows how to configure an IAP ID to the Central Repository. For simplicity, it does not show the transaction. It places the IAP in the first row, giving it a higher priority than any other IAP IDs.

Note: Processes that write to the Central Repository must have NetworkControl capability.

 
const TUid KRndisIapIdRepositoryUID = {0x10286a43};
TInt iapPrefId = 2;
TUint firstRow = 0x01010000; // The first row of IAP repository table
TInt err = KErrNone;

CRepository* repository = NULL;
repository = CRepository::NewLC(KRndisIapIdRepositoryUID);

err = repository->Create(firstRow, iapPrefId);
if (KErrAlreadyExists == err)
    {
    err = repository->Set(firstRow, iapPrefId);
    }
User::LeaveIfError(err);
User::PopAndDestroy(repository);

Configuring the IAP Settings

Device creators must configure the IAP to be used as the RNDIS network interface in the phone.

When the RNDIS Service is started, a RNDIS network interface will be started. The DHCP server starts automatically and works in DHCP server mode. It configures the PC with the IP address, DNS server IP address and the default gateway. A number of tables need to be updated to configure the IAP.

The following code shows how the LAN Service table can be configured:

[LANService]
ADD_SECTION
# COMMDB_ID = 1
    Name=RNDIS Ethernet Static IP
    IfNetworks=ip
    IpNetMask=255.255.255.0
    IpGateway=0.0.0.0
    IpAddrFromServer=FALSE
#N.B. The DHCP Server functionality is limited 
#to assigning only one IP address, and hence RNDIS can not work with PAN 
#(if there is active connection) at the same time. The IP address the DHCP 
#Server assigns is based on a statically configured IP address in the 
#CommsDB settings for the IAP that the DHCP Server is associated with.
    IpAddr=192.168.2.100
#N.B. 192.168.2.101 will be the IP address of the PC allocated by the DHCP 
#server in the phone.
    IpDNSAddrFromServer=FALSE
    IpNameServer1=0.0.0.0
    IpNameServer2=0.0.0.0
    ConfigDaemonManagerName=NetCfgExtnDhcp
    ConfigDaemonName=!DhcpServ
    LanServiceExtensionTableName=PANServiceExtensions
    LanServiceExtensionTableRecordId=1
    FIELD_COUNT=13
END_ADD

The following example shows how the PANServiceExtensions table can be configured:

[PANServiceExtensions]
ADD_SECTION
# COMMDB_ID = 1
    Name=PANServiceExtensionsTable1
    LocalRole=PANUNKNOWN
    PeerRole=PANUNKNOWN
    PromptForRemoteDevices=FALSE
    DisableSdpQuery=FALSE
    AllowIncomingConnections=TRUE
    PromptIfMACListFails=FALSE
    NapServiceEnabled=TRUE
    FIELD_COUNT=8
END_ADD

The following example shows how the LANBearer Table can be configured:

[LANBearer]
ADD_SECTION
# COMMDB_ID = 1
    Name=RNDIS
    IfName=ethint
    LDDFilename=eusbc
    PacketDriverName=rndispkt.drv
    LastSocketActivityTimeout=-1
    LastSessionClosedTimeout=-1
    LastSocketClosedTimeout=-1
    Agent=rndisagt.agt
    FIELD_COUNT=8
END_ADD

The following example shows how the AccessPoint table can be configured:

[AccessPointTable]
ADD_SECTION
# COMMDB_ID = 249
    Id=1
    Name=ethintMappedFromIAP1
    AccessPointSelectionPolicy=0
    Tier=0x11800300 // This should map to record COMMDB_ID = 3 in TierTable
    MCpr=0x12000E00 // This should map to record COMMDB_ID = 14 in McprTable
    Cpr=0x12800300 // This should map to record COMMDB_ID = 3 in CprTable
    SCpr=0x13000300 // This should map to record COMMDB_ID = 3 in SCprTable
    Protocol=0x13800B00 // This should map to record COMMDB_ID = 11 in ProtocolTable
    CprConfig=0
    AppSID=0
    FIELD_COUNT=10
END_ADD

The following example shows how the Tier Table can be configured:

[TierTable]
ADD_SECTION
# COMMDB_ID = 3
    Id=271064565
    Name=Tier3
    TierThreadName=ESock_IP
    TierManagerName=LinkTierManager
    DefaultAccessPoint=0x0E00FE00
    PromptUser=0
    FIELD_COUNT=6
END_ADD

The following example shows how the MCPR Table can be configured:

[MCprTable]
ADD_SECTION
# COMMDB_ID = 14
    Name=rndisethermcpr
    MCprUid=271084097 
    FIELD_COUNT=2
END_ADD

The following example shows how the CPR Table can be configured:

[CprTable]
ADD_SECTION
# COMMDB_ID = 3 
    Name=agentcpr
    CprUid=271064552
    FIELD_COUNT=2
END_ADD

The following example shows how the SCPR Table can be configured:

[SCprTable]
ADD_SECTION
# COMMDB_ID = 3
    Name=agentscpr
    SCprUid=271064554
    FIELD_COUNT=2
END_ADD

The following example shows how the Protocol Table can be configured:

[ProtocolTable]
ADD_SECTION
# COMMDB_ID = 11
    Name=rndisether
    ProtocolUid=271084098
    FIELD_COUNT=2
END_ADD

IP forwarding

To allow the TCP/IP stack to forward traffic between two interfaces, IP forwarding must be enabled. To do this the appropriate tcpip.ini file needs to be modified to include forward= 1 under the [ip] section, for example:

[ip]
maxholdtime= 90
ipv4linklocal= 2
forward= 1

Specifying the RNDIS personality

The RNDIS personality is defined in usbmanrndis.rss and usbmanmtprndis.rss. Device creators need to provide the fields that are indicated by comments below.

PERSONALITY
{
    bDeviceClass = 0x02;
    bDeviceSubClass = 0x00;
    protocol = 0x00;
    numConfigurations = 1;
    vendorId = per_vendoridx; // please specify
    productId = per_productidx; // please specify
    bcdDevice = per_bcddevicex; // please specify
    manufacturer = per_manufacturerx; // please specify, see note below
    product =  per_productx; // please specify, see note below
    id = x; // please specify                
    class_uids = "20013d2f";            
    description = per_descriptionx; // please specify, see note below
}

Note: The field manufacturer, product and description can be specified in a local language, for example, in French or in German. To do this, define them in the local language in usbman_01.rls, usbman_02.rls and so on.

Starting the RNDIS service

To start the RNDIS service, the USB Control Application needs to iterate through the USB Personalities and find the one that supports RNDIS.

Note: There is a USB Control Application in the source code that detects when a USB cable attaches or detaches. Device creators can use that for reference and add the starting and stopping of the RNDIS Control Application.

const TUid KUsbRndisClassControllerUID={0x20013d2f};

     RUsb usb;
     User::LeaveIfError( usb.Connect() ); 
     //Iterate through the USB Personalities and find the one that supports RNDIS.
      TInt personalityId = KErrNotFound;
      TBool supported    = EFalse;
      RArray<TInt> personalityIds; 
      if (KErrNone == usb.GetPersonalityIds(personalityIds))
            {
            const TInt size = personalityIds.Count();
            for (TInt index = 0; index < size; index++)
                {
                 TInt err = usb.ClassSupported(personalityIds[index], 
                                            KUsbRndisClassControllerUID, 
                                             supported);
                 if(supported && KErrNone == err)
                  {
                  personalityId = personalityIds[index];
                  break;
                  }
                }
            personalityIds.Close();
            }
       if (KErrNotFound != personalityId) 
          {
TRequestStatus status;
           usb.TryStart(personalityId, status);
           User::WaitForRequest(status);
           usb.Close();
           LEAVEIFERROR(status.Int());
        }

Related information