stm32plus::net, UDP module

The User Datagram Protocol (UDP) is a very simple and efficient protocol for exchanging single datagrams between two hosts, many hosts via multicast or all available hosts using broadcast.

UDP makes no attempt to be reliable and so the responsibility for the detection of packet loss and retransmission is borne by the application programmer. Many of the most efficient low latency, high throughput networking systems used by enterprises in the world today use UDP as the basis for their transports.

stm32plus::net offers support for synchronous and asynchronous UDP transmission and reception. Broadcast send and receive are supported. Multicast send is supported. Multicast receive to the ‘all hosts’ group is supported but multicast subscription to locally defined groups via IGMP is not yet supported.

UDP implementation

The stm32plus::net UDP implementation presents a simple send/receive interface for client code as well as a high-performance subscription interface for receiving UDP datagrams within the same IRQ that notified us of the ethernet frame.

UDP has no compile-time parameters, you can include it by adding its module name to your transport layer configuration like this:

typedef TransportLayer<Udp> MyTransportLayer;

Note that this code fragment only shows Udp in the transport layer. In practice a useful transport layer will include more than just UDP.

Configuration Parameters

The following parameters are made available in the stack’s configuration object for customisation:

// datagrams sent to ports with no handler will get an ICMP error message (if ICMP is configured in). default is true
bool udp_sendPortUnreachable;      

The udp_sendPortUnreachable parameter allows you to control whether the stack responds to datagrams sent to a port that you are not listening on with an ICMP destination unreachable message if ICMP is configured into your stack. These messages can be useful for debugging and testing but in recent years they have become an attack vector for criminals attempting to find programs with vulnerabilities to attack. If you have not configured ICMP into your stack then this flag does nothing.

Methods exposed

bool udpSend(const IpAddress& ipaddress,
             uint16_t sourcePortNumber,
             uint16_t destinationPortNumber,
             const void *data,
             uint16_t dataSize,
             bool async,
             uint32_t transmitTimeout);

bool udpReceive(uint16_t portNumber,
                void *buffer,
                uint16_t& size,
                uint32_t receiveTimeout=0);

udpSend sends a datagram to a remote computer. You must specify the remote address with the ipaddress parameter. sourcePortNumber is the port number that you would like to send from and destinationPortNumber is the port number on the remote host that you would like to send to.

data is a pointer to the data you want to send and dataSize is the number of bytes to send. UDP lets you send datagrams that are larger than the underlying MTU size, leaving it up to the IP module to fragment and reassemble the datagram into packets small enough to be transmitted on the wire. If you need to send such large packets then you must ensure that you have configured your IP module in the network layer to do fragmentation and/or reassembly.

If you set the async flag then you should be aware that dynamic memory is allocated to hold the datagram until transmission is complete so you must have enough SRAM to hold it and any other datagrams pending transmission. If you do not set the async flag then udpSend blocks until the datagram has been transmitted or the timeout expires. Datagrams transmitted synchronously only use dynamic memory for the datagram and frame header information because the datagram data is transmitted directly from the memory pointer that you supply. Synchronous mode trades network throughput for reduced CPU load and SRAM usage.

transmitTimeout is only used in synchronous mode and it holds the number of milliseconds that you want to wait for the datagram to be transmitted. The UDP module will wait this long for notification from the link layer that the frame(s) containing the datagram have been put on to the wire.

udpReceive is used to wait for a datagram to arrive on the port you specify. This method blocks until data is available or the receiveTimeout number of milliseconds is hit. Data from the received datagram is stored in buffer up to a maximum of size bytes. The actual amount received is returned in size. If more data is available in the datagram then the method returns false and the error provider code will be E_MSG_SIZE. true is returned if all the data in the datagram could be copied into your buffer.

If a datagram arrives and you are not blocking in this method then the datagram is dropped. If you need to be absolutely sure that you will receive any datagram sent to your port then you must use the asynchronous subscription described below.

Events raised

sender UdpReceiveEventSender
event UdpDatagramEvent
identifier NetEventType::UDP_DATAGRAM
context IRQ
purpose UDP datagram received
		/**
		 * Event descriptor for a UDP datagram arrival
		 */

		struct UdpDatagramEvent : NetEventDescriptor {

			UdpDatagram& udpDatagram;							///< the UDP datagram
			bool handled;													///< set to true if this datagram was handled

			/**
			 * Constructor
			 * @param udp The UDP packet structure
			 * @param ip The IP packet structure
			 */

			UdpDatagramEvent(UdpDatagram& udp)
				: NetEventDescriptor(NetEventType::UDP_DATAGRAM),
					udpDatagram(udp),
					handled(false) {
			}
		};

If you subscribe to this event then you are hooking in to the network stack within the context of the IRQ that notified us of the arrival of a new frame of data.

The UdpDatagram structure is mapped directly into the original frame data. It is not a copy. Using this method is the most efficient way to receive UDP datagrams and guarantees that you cannot miss a packet for any other reason than all the receive buffers being full. However, you must be aware of the limitations of programming within the context of an IRQ.

If your subscription handler accepts the datagram then you should set handled to true. If no subscription handlers set the handled flag then the UDP module will attempt to send back an ICMP port unreachable message to the sender. This will of course not happen unless ICMP is configured into your stack.

Related examples

net_udp_send is a UDP datagram sender example. net_udp_receive is a synchronous UDP datagram receiver and net_udp_receive_async is an asynchronous, event-based receiver.