1# TCP Echo Client Demo for MPS2 Cortex-M3 AN385 emulated using QEMU with TAP Networking 2 3## Setup Description 4The demo requires 2 components - 51. Echo Client - The demo in this repository. 61. Echo Server - An external echo server. 7 8We need a Virtual Machine (VM) running Linux OS to run this demo. Echo Client 9runs in the Virtual Machine (VM) and Echo Server runs on the host machine. 10``` 11+--------------------------------------------------------+ 12| Host Machine | 13| OS - Any | 14| Runs - Echo Server | 15| +--------------------------+ | 16| | Virtual Machine (VM) | | 17| | OS - Linux | | 18| | Runs - Echo Client | | 19| | | | 20| +----------------+ | +----------------+ | | 21| | | | | | | | 22| | | | | | | | 23| | Echo Server | <-------> | Echo Client | | | 24| | | | | | | | 25| | | | | | | | 26| | | | | | | | 27| +----------------+ | +----------------+ | | 28| | | | 29| +--------------------------+ | 30+--------------------------------------------------------+ 31``` 32 33## Setting up VM 341. Install a Virtual Machine software on your machine. On Windows you can use 35[Oracle VirtualBox](https://www.virtualbox.org/) and on Mac you can use 36[Parallels](https://www.parallels.com/products/desktop/). 372. Launch a Linux VM. We tested using Ubuntu 22.04. 383. Install the following tools in the VM: 39 * [GNU Arm Embedded Toolchain](https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads). 40 * [qemu-arm-system](https://www.qemu.org/download). 41 * Make (Version 4.3): 42 ``` 43 sudo apt install make 44 ``` 45 * ipcalc: 46 ``` 47 sudo apt install ipcalc 48 ``` 49 * brctl: 50 ``` 51 sudo apt install bridge-utils 52 ``` 534. Clone the source code in the VM: 54 ```shell 55 git clone https://github.com/FreeRTOS/FreeRTOS.git --recurse-submodules --depth 1 56 ``` 57 58## Launch Echo Server 59Launch Echo Server on the host machine. 60### Host OS is Linux 61* Install `netcat`: 62 ``` 63 sudo apt install netcat 64 ``` 65* Start an Echo Server on port 7: 66 ```shell 67 sudo nc -l 7 68 ``` 69 70### Host OS is Windows 71* Install [Npcap/Nmap](https://nmap.org/download.html#windows). 72* Start an Echo Server on port 7: 73 ```shell 74 ncat -l 7 75 ``` 76 77### Host OS is Mac 78* Install `netcat`: 79 ```shell 80 brew install netcat 81 ``` 82* Start an Echo Server on port 7: 83 ```shell 84 nc -l -p 7 85 ``` 86 87## Enable Tap Networking in QEMU 88 89The Tap Networking backend makes use of a tap networking device in the host. It offers very good performance and can be configured to create virtually any type of network topology. 90 91The Echo Client in this demo runs in QEMU inside the VM. We need to enable 92tap networking in QEMU to enable the Echo Client to be able to reach the Echo 93Server. Do the following steps in the VM: 94 95 961. Run the `ifconfig` command to find the VM's network interface details: 97``` 98enp0s3: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 9001 99 inet 192.168.1.81 netmask 255.255.255.0 broadcast 192.168.15.255 100 inet6 fe80::89c:55ff:fe3d:18ad prefixlen 64 scopeid 0x20<link> 101 ether 0a:9c:55:3d:18:ad txqueuelen 1000 (Ethernet) 102 RX packets 15001255 bytes 11443805826 (11.4 GB) 103 RX errors 0 dropped 0 overruns 0 frame 0 104 TX packets 9248218 bytes 2080385000 (2.0 GB) 105 TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 106 107``` 108 1092. Define a shell variable `VM_NETWORK_INTERFACE` and set its value to the 110name of the network interface of the VM. For example, in the above output 111of the `ifconfig` command, name of the the network interface is `enp0s3`: 112```shell 113export VM_NETWORK_INTERFACE=enp0s3 114``` 115 1163. Define a shell variable `VM_IP_ADDRESS` and set its value to the IP address 117of the VM. For example, in the above output of the `ifconfig` command, IP 118address of the VM is `192.168.1.81`: 119```shell 120export VM_IP_ADDRESS=192.168.1.81 121``` 122 1234. Define a shell variable `VM_NETMASK` and set its value to the netmask of 124the VM. For example, in the above output of the `ifconfig` command, netmask 125of the VM is `255.255.255.0`: 126```shell 127export VM_NETMASK=255.255.255.0 128``` 129 1305. Calculate the CIDR of the VM from the netmask: 131```shell 132$ ipcalc -b 1.1.1.1 $VM_NETMASK | grep Netmask 133Netmask: 255.255.255.0 = 24 134``` 135CIDR is `24` in the above output. 136 1376. Define a shell variable `VM_CIDR` and set its value to the CIDR of the VM 138found in the above step. 139```shell 140export VM_CIDR=24 141``` 142 1437. Find the Default Gateway for the VM: 144```shell 145$ ip route show 146default via 192.168.1.254 dev enp0s3 proto dhcp src 192.168.1.81 metric 100 147``` 148Default Gateway is `192.168.1.254` in the above output. 149 1508. Define a shell variable `VM_DEFAULT_GATEWAY` and set its value to the 151Default Gateway of the VM found in the above step. 152```shell 153export VM_DEFAULT_GATEWAY=192.168.1.254 154``` 155 1569. Find the DNS Server of the VM: 157```shell 158$ grep "nameserver" /etc/resolv.conf 159nameserver 192.168.1.254 160``` 161 16210. Define a shell variable `VM_DNS_SERVER` and set its value to the 163DNS Server of the host machine found in the above step. 164```shell 165export VM_DNS_SERVER=192.168.1.254 166``` 167 16811. Pick an IP address for the QEMU which is in the same network as the VM. 169This IP address must not be in-use by any other machine on the same network. 170Define a shell variable `QEMU_IP_ADDRESS` and set its value to the 171picked IP Address. For example, run the following command if you picked 172`192.168.1.80`: 173```shell 174export QEMU_IP_ADDRESS=192.168.1.80 175``` 176 17712. Pick a MAC address for the QEMU. Define a shell variable `QEMU_MAC_ADDRESS` 178and set its value to the picked MAC Address. For example, run the following 179command if you picked `52:54:00:12:34:AD`: 180```shell 181export QEMU_MAC_ADDRESS=52:54:00:12:34:AD 182``` 183 18413. Define a shell variable `ECHO_SERVER_IP_ADDRESS` and set its value to the 185IP address of the Echo Server which is running on the host. For example, 186run the following command if the IP address of the Echo Server is 187`192.168.1.204`: 188```shell 189export ECHO_SERVER_IP_ADDRESS=192.168.1.204 190``` 191 19214. Turn off firewall on the VM. 193On Ubuntu run: 194```shell 195sudo ufw disable 196sudo ufw status 197``` 198On RedHat/Fedora system run: 199```shell 200sudo systemctl status firewalld 201sudo systemctl stop firewalld 202``` 203 20415. Create virtual bridge (virbr0) and virtual NIC (virbr0-nic) to enable 205networking in QEMU. 206```shell 207sudo ip link add virbr0 type bridge 208sudo ip tuntap add dev virbr0-nic mode tap 209 210sudo ip addr add $VM_IP_ADDRESS/$VM_CIDR dev virbr0 211 212sudo brctl addif virbr0 $VM_NETWORK_INTERFACE 213sudo brctl addif virbr0 virbr0-nic 214 215sudo ip link set virbr0 up 216sudo ip link set virbr0-nic up 217 218sudo ip route add default via $VM_DEFAULT_GATEWAY dev virbr0 219``` 220 221The following diagram shows the setup: 222``` 223+-------------------------------------------------------------------------+ 224| Virtual Machine (VM) | 225| | 226| +-------------------------+ | VM NIC (enp0s3) 227| | | Virtual NIC (virbr0-nic) +--+ 228| | QEMU +--+ | | 229| | | | +--------------+ | | 230| | | +--------->| virbr0 | ---------->| +--------> Internet 231| | | | +--------------+ | | 232| | +--+ Virtual Bridge | | 233| | | +--+ 234| +-------------------------+ | 235| | 236| | 237+-------------------------------------------------------------------------+ 238``` 239 240## Build and Run 241Do the following steps in the VM where you cloned the code: 242 2431. Set `configIP_ADDR0`-`configIP_ADDR3` in `FreeRTOSConfig.h` to the value 244of `QEMU_IP_ADDRESS`: 245```shell 246echo $QEMU_IP_ADDRESS 247``` 248```c 249#define configIP_ADDR0 192 250#define configIP_ADDR1 168 251#define configIP_ADDR2 1 252#define configIP_ADDR3 80 253``` 254 2552. Set `configNET_MASK0`-`configNET_MASK3` in `FreeRTOSConfig.h` to the value 256of `VM_NETMASK`: 257```shell 258echo $VM_NETMASK 259``` 260```c 261#define configNET_MASK0 255 262#define configNET_MASK1 255 263#define configNET_MASK2 255 264#define configNET_MASK3 0 265``` 266 2673. Set `configGATEWAY_ADDR0`-`configGATEWAY_ADDR3` in `FreeRTOSConfig.h` to 268the value of `VM_DEFAULT_GATEWAY`: 269```shell 270echo $VM_DEFAULT_GATEWAY 271``` 272```c 273#define configGATEWAY_ADDR0 192 274#define configGATEWAY_ADDR1 168 275#define configGATEWAY_ADDR2 1 276#define configGATEWAY_ADDR3 254 277``` 278 2794. Set `configDNS_SERVER_ADDR0`-`configDNS_SERVER_ADDR3` in `FreeRTOSConfig.h` 280to the value of `VM_DNS_SERVER`: 281```shell 282echo $VM_DNS_SERVER 283``` 284```c 285#define configDNS_SERVER_ADDR0 192 286#define configDNS_SERVER_ADDR1 168 287#define configDNS_SERVER_ADDR2 1 288#define configDNS_SERVER_ADDR3 254 289``` 290 2915. Set `configMAC_ADDR0`-`configMAC_ADDR5` in `FreeRTOSConfig.h` to the value 292of `QEMU_MAC_ADDRESS`: 293```shell 294echo $QEMU_MAC_ADDRESS 295``` 296```c 297#define configMAC_ADDR0 0x52 298#define configMAC_ADDR1 0x54 299#define configMAC_ADDR2 0x00 300#define configMAC_ADDR3 0x12 301#define configMAC_ADDR4 0x34 302#define configMAC_ADDR5 0xAD 303``` 304 3056. Set `configECHO_SERVER_ADDR0`-`configECHO_SERVER_ADDR3` in `FreeRTOSConfig.h` 306to the value of `ECHO_SERVER_IP_ADDRESS`: 307```shell 308echo $ECHO_SERVER_IP_ADDRESS 309``` 310```c 311#define configECHO_SERVER_ADDR0 192 312#define configECHO_SERVER_ADDR1 168 313#define configECHO_SERVER_ADDR2 1 314#define configECHO_SERVER_ADDR3 204 315``` 316 3177. The echo server is assumed to be on port 7, which is the standard echo 318protocol port. You can change the port to any other listening port (e.g. 3682 ). 319Set `echoECHO_PORT` to the value of this port. 320 321```c 322#define echoECHO_PORT ( 7 ) 323``` 324 3258. Build: 326```shell 327make 328``` 329 3309. Run: 331```shell 332sudo qemu-system-arm -machine mps2-an385 -cpu cortex-m3 \ 333 -kernel ./build/freertos_tcp_mps2_demo.axf \ 334 -netdev tap,id=mynet0,ifname=virbr0-nic,script=no \ 335 -net nic,macaddr=$QEMU_MAC_ADDRESS,model=lan9118,netdev=mynet0 \ 336 -object filter-dump,id=tap_dump,netdev=mynet0,file=/tmp/qemu_tap_dump\ 337 -display gtk -m 16M -nographic -serial stdio \ 338 -monitor null -semihosting -semihosting-config enable=on,target=native 339``` 340 34110. You should see that following output on the terminal of the Echo Server (which 342is running `sudo nc -l 7` or `netcat -l 7` depending on your OS): 343``` 3440FGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~0123456789:;<=> ? 345@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~0123456789:;<=>? 346@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~0123456789:;<=>? 347@ABCDEFGHIJKLM 348``` 349 350## Debug 3511. Build with debugging symbols: 352``` 353make DEBUG=1 354``` 355 3562. Start QEMU in the paused state waiting for GDB connection: 357```shell 358sudo qemu-system-arm -machine mps2-an385 -cpu cortex-m3 -s -S \ 359 -kernel ./build/freertos_tcp_mps2_demo.axf \ 360 -netdev tap,id=mynet0,ifname=virbr0-nic,script=no \ 361 -net nic,macaddr=$QEMU_MAC_ADDRESS,model=lan9118,netdev=mynet0 \ 362 -object filter-dump,id=tap_dump,netdev=mynet0,file=/tmp/qemu_tap_dump\ 363 -display gtk -m 16M -nographic -serial stdio \ 364 -monitor null -semihosting -semihosting-config enable=on,target=native 365``` 366 3673. Run GDB: 368```shell 369$ arm-none-eabi-gdb -q ./build/freertos_tcp_mps2_demo.axf 370 371(gdb) target remote :1234 372(gdb) break main 373(gdb) c 374``` 375 3764. The above QEMU command creates a network packet dump in the file 377`/tmp/qemu_tap_dump` which you can examine using `tcpdump` or WireShark: 378```shell 379sudo tcpdump -r /tmp/qemu_tap_dump | less 380``` 381