+TCP used as L3 bridge

glenenglish wrote on Tuesday, April 18, 2017:

Hi all
I want to use my FreeRTOS+TCP device as a L3 bridge (living behind a router)
That is to say, there are two boxes which are radio modems.
Each end runs freertos+TCP. I know the source code pretty well
Now, the implementation is trivial but has anyone done this ?

Does anyone have any suggestions on WHERE to hook in the bridge logic and keep all the other stuff happy?
Essentially I have to pick it off
some in

prvProcessEthernetPacket() and check if the dst addr is our addr or ‘the other end’
and or prvProcessIPPacket()

on the send side, I need to do the reverse of course, inject up the stream…

In the usage case, the device (modem) will be connected to a router. .The router will route traffic to the device for traffic destined to the other subnets. IE its not actually used for bridging two networks, but for routing between networks. I find the best way to do this sort of thing using microwave links is to put routers are each end of the link (like, the other end , and run the link itself as a bridge (with its own subnet ) etc etc

anyway, requirements are pretty much the same, I use the term bridge because that’s what it is doing. something else will do the routing and handle the routing tables etc


heinbali01 wrote on Wednesday, April 19, 2017:

Hi Glen,

First some general information:

The IP-task sends all packets to Ethernet by calling this driver function:

    BaseType_t xNetworkInterfaceOutput( pxDescriptor, t bReleaseAfterSend );

The IP-task receives Ethernet packets by receiving eNetworkRxEvent messages in its queue ( xNetworkEventQueue ). A driver can send network buffers to that queue by calling xSendEventStructToIPTask().

Those are the two paths between +TCP and the Ethernet.

About ‘internal’ routing: the current implementation is simple:

  • Every target that is within the netmask is contacted directly (using ARP look-up)
  • Packets to other IP-addresses will be sent to the gateway, if any.

But every packet will finally be sent by the same xNetworkInterfaceOutput().

Now you want to route packets that come from your LAN, and forward them to a radio-connection and v.v., true?

prvProcessEthernetPacket() and check if the dst addr is our addr or ‘the other end’

I think it’s a good place to forward packets!
prvProcessEthernetPacket() will handle every packet that has been received by NetworkInterface.c (i.e. from the LAN).

Something like the following ?

     void prvProcessEthernetPacket( NetworkBufferDescriptor_t * const pxNetworkBuffer )
     EthernetHeader_t *pxEthernetHeader;
     volatile eFrameProcessingResult_t eReturned;
         configASSERT( pxNetworkBuffer );
+        if( xToMyRouter( pxNetworkBuffer ) )
+        {
+            /* The packet has bee forwarded to a router. */
+            eReturned = eFrameConsumed;
+        }
+        else
             /* Interpret the Ethernet frame. */
             eReturned = ipCONSIDER_FRAME_FOR_PROCESSING( pxNetworkBuffer->pucEthernetBuffer );
             pxEthernetHeader = ( EthernetHeader_t * ) ( pxNetworkBuffer->pucEthernetBuffer );
         if( eReturned == eProcessBuffer )

So if the function xToMyRouter() handles / forwards the packet, it gets ownership of the network buffer ( call vReleaseNetworkBufferAndDescriptor() after use ), and it must return a non-zero value. Do not block, just queue the network buffer, in order not to hold-up the IP-task.
If this approach turns out to be useful, we could make some permanent application hook here.

I suppose that you will also create a task that monitors / receives packets from the radio-connection.
We miss something here. You would like to call xNetworkInterfaceOutput() directly to forward the packet to the LAN, true? But the code in NetworkInterface.c assumes that the xNetworkInterfaceOutput() function is non re-entrant.
The IP-task does not have a message type to forward a packet to the LAN. We could add that type of messages:

     eNetworkDownEvent,      /* 0: The network interface has been lost and/or needs [re]connecting. */
     eNetworkRxEvent,        /* 1: The network interface has queued a received Ethernet frame. */
     eARPTimerEvent,         /* 2: The ARP timer expired. */
     eStackTxEvent,          /* 3: The software stack has queued a packet to transmit. */
+    eForwardTxPacketEvent,  /* 4: Blindly forward a packet by passing it to xNetworkInterfaceOutput(). */
     eDHCPEvent,             /* 5: Process the DHCP state machine. */

/* And in prvIPTask() in FreeRTOS_IP.c */

     switch( xReceivedEvent.eEventType )
         case eForwardTxPacketEvent:
             /* Set the `bReleaseAfterSend` parameter to true, to pass the ownership to the driver. */
             xNetworkInterfaceOutput( ( NetworkBufferDescriptor_t * ) xReceivedEvent.pvData, pdTRUE );

eStackTxEvent is a bit misleading, it will only handle UDP-packets. It was added at the time when the library was still called +UDP :slight_smile:

Or you could change the implementation of xNetworkInterfaceOutput() to become re-entrant.

I hope that I answered your question. Regards.

glenenglish wrote on Wednesday, April 19, 2017:

Hi Hein
thank you for the time to write that up.

Essentially it is a layer 3 switch with only one other destination (IE all packets != me send to radio)

Yes, on receiving data packets from the demodulator (the RX side of the MODEM) , I will write some code to push it into a queue just like a UDP packet would be, except of course, it is the ‘whole packet’. (IE I wont just push out the network, they’ll join a queue just like other protocols.

So I will need to add some functionality to the stack , which will be pretty easy.
Whether it becomes mainline is another thing I guess, its a corner case…

I will need to learn a bit more about routers and the fine print, also (IE special behviour that the router connected to my modem-bridge expects from me) .

cheers - glen