FreeRTOSを動かしてみる(14)

[前回](https://qiita.com/t-yano-tryhard/items/36130ed771d42f63991a)の続き

# FreeRTOSのIPスタック
arp応答をLinuxと同じように返しても動かないということは完全にFreeRTOSののIPスタックの問題

arp発行している場所を突き止めて、そこから追っていく。

## ARPの発行元
```
(gdb) bt 
#0  pfOutput (pxDescriptor=0x200067a0 <xInterfaces.5>, pxNetworkBuffer=0x20010990 <xNetworkBufferDescriptors.0>, xReleaseAfterSend=1) at /home/voyager/workspace/freertos/FreeRTOS-Plus-TCP/source/portable/NetworkInterface/virtether/NetworkInterface.c:173
#1  0x00003db8 in FreeRTOS_OutputARPRequest (ulIPAddress=23374016) at /home/voyager/workspace/freertos/FreeRTOS-Plus-TCP/source/FreeRTOS_ARP.c:1316
#2  0x0000bdb8 in prvTCPPrepareConnect_IPV4 (pxSocket=0x20113808) at /home/voyager/workspace/freertos/FreeRTOS-Plus-TCP/source/FreeRTOS_TCP_Transmission_IPv4.c:367
#3  0x0000b57e in prvTCPPrepareConnect (pxSocket=<optimized out>) at /home/voyager/workspace/freertos/FreeRTOS-Plus-TCP/source/FreeRTOS_TCP_Transmission.c:496
#4  0x0000b59a in prvTCPMakeSurePrepared (pxSocket=<optimized out>) at /home/voyager/workspace/freertos/FreeRTOS-Plus-TCP/source/FreeRTOS_TCP_Transmission.c:87
#5  0x0000b9fa in prvTCPSendPacket (pxSocket=pxSocket@entry=0x20113808) at /home/voyager/workspace/freertos/FreeRTOS-Plus-TCP/source/FreeRTOS_TCP_Transmission.c:139
#6  0x0000a436 in xTCPSocketCheck (pxSocket=pxSocket@entry=0x20113808) at /home/voyager/workspace/freertos/FreeRTOS-Plus-TCP/source/FreeRTOS_TCP_IP.c:226
#7  0x000091b8 in xTCPTimerCheck (xWillSleep=xWillSleep@entry=1) at /home/voyager/workspace/freertos/FreeRTOS-Plus-TCP/source/FreeRTOS_Sockets.c:4828
```

`prvTCPPrepareConnect_IPV4`の中で接続する前にarpを送っている
```gdscript
(gdb) f 2
#2  0x0000bdd6 in prvTCPPrepareConnect_IPV4 (pxSocket=0x20113808) at /home/voyager/workspace/freertos/FreeRTOS-Plus-TCP/source/FreeRTOS_TCP_Transmission_IPv4.c:368
368	            FreeRTOS_OutputARPRequest( ulRemoteIP );
(gdb) l 350
345	    switch( eReturned )
346	    {
347	        case eARPCacheHit:    /* An ARP table lookup found a valid entry. */
348	            break;            /* We can now prepare the SYN packet. */

(gdb) p eReturned 
$1 = eARPCacheMiss
```
TCPで送ろうとしたけど送り先がわからんという話
なのでここでeARPCacheHitが返るようにする必要がある。

```
(gdb) l 343
338	        }
339	    #endif /* ipconfigHAS_PRINTF != 0 */
340	
341	    ulRemoteIP = FreeRTOS_htonl( pxSocket->u.xTCP.xRemoteIP.ulIP_IPv4 );
342	    /* Determine the ARP cache status for the requested IP address. */
343	    eReturned = eARPGetCacheEntry( &( ulRemoteIP ), &( xEthAddress ), &( pxSocket->pxEndPoint ) );
```

`eARPGetCacheEntry`を見ていく
マルチキャストでもブロードキャストでも無い場合は`eARPGetCacheEntryGateWay`に行くらしい
```c
 971         if( xIsIPv4Multicast( ulAddressToLookup ) != 0 )
 ...
 992         else if( ( FreeRTOS_htonl( ulAddressToLookup ) & 0xffU ) == 0xffU ) /* Is this a broadcast address li     ke x.x.x.255 ? */
...
1005         else1006         {
1007             eReturn = eARPGetCacheEntryGateWay( pulIPAddress, pxMACAddress, ppxEndPoint );
1008         }
```
でここが本命
```c
1085                 eReturn = prvCacheLookup( ulAddressToLookup, pxMACAddress, ppxEndPoint );
```

ふむ
```gdscript
(gdb) p/x xARPCache 
$12 = {{ulIPAddress = 0x0, xMACAddress = {ucBytes = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, ucAge = 0x0, ucValid = 0x0, pxEndPoint = 0x0}, {ulIPAddress = 0x0, xMACAddress = {ucBytes = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, ucAge = 0x0, ucValid = 0x0, pxEndPoint = 0x0}, {ulIPAddress = 0x0, xMACAddress = {ucBytes = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, ucAge = 0x0, ucValid = 0x0, pxEndPoint = 0x0}, {ulIPAddress = 0x0, xMACAddress = {ucBytes = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, ucAge = 0x0, ucValid = 0x0, pxEndPoint = 0x0}, {ulIPAddress = 0x0, xMACAddress = {ucBytes = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, ucAge = 0x0, ucValid = 0x0, pxEndPoint = 0x0}, {ulIPAddress = 0x0, xMACAddress = {ucBytes = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, ucAge = 0x0, ucValid = 0x0, pxEndPoint = 0x0}, {ulIPAddress = 0x0, xMACAddress = {ucBytes = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, ucAge = 0x0, ucValid = 0x0, pxEndPoint = 0x0}, {ulIPAddress = 0x0, xMACAddress = {ucBytes = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, ucAge = 0x0, ucValid = 0x0, pxEndPoint = 0x0}, {ulIPAddress = 0x0, xMACAddress = {ucBytes = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, ucAge = 0x0, ucValid = 0x0, pxEndPoint = 0x0}, {ulIPAddress = 0x0, xMACAddress = {ucBytes = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, ucAge = 0x0, ucValid = 0x0, pxEndPoint = 0x0}}
```

ここに値を入れる必要がある

```shell
FreeRTOS-Plus-TCP$ grep xARPCache . -wnrI | fgrep -v "/test/" | grep " = "
./source/FreeRTOS_ARP.c:635:                lResult = xARPCache[ x ].ulIPAddress;
./source/FreeRTOS_ARP.c:672:                    xARPCache[ x ].ucAge = ( uint8_t ) ipconfigMAX_ARP_AGE;
./source/FreeRTOS_ARP.c:742:            xARPCache[ xLocation.xUseEntry ].ulIPAddress = ulIPAddress;
./source/FreeRTOS_ARP.c:750:                xARPCache[ xLocation.xUseEntry ].ucAge = ( uint8_t ) ipconfigMAX_ARP_AGE;
./source/FreeRTOS_ARP.c:751:                xARPCache[ xLocation.xUseEntry ].ucValid = ( uint8_t ) pdTRUE;
./source/FreeRTOS_ARP.c:752:                xARPCache[ xLocation.xUseEntry ].pxEndPoint = pxEndPoint;
./source/FreeRTOS_ARP.c:756:                xARPCache[ xLocation.xUseEntry ].ucAge = ( uint8_t ) ipconfigMAX_ARP_RETRANSMISSIONS;
./source/FreeRTOS_ARP.c:757:                xARPCache[ xLocation.xUseEntry ].ucValid = ( uint8_t ) pdFALSE;
./source/FreeRTOS_ARP.c:827:                xARPCache[ x ].ucAge = ( uint8_t ) ipconfigMAX_ARP_AGE;
./source/FreeRTOS_ARP.c:828:                xARPCache[ x ].ucValid = ( uint8_t ) pdTRUE;
./source/FreeRTOS_ARP.c:829:                xARPCache[ x ].pxEndPoint = pxEndPoint;
./source/FreeRTOS_ARP.c:852:                    BaseType_t xOtherIsLocal = ( FreeRTOS_FindEndPointOnNetMask( xARPCache[ x ].ulIPAddress, 3 ) != NULL ) ? 1 : 0; /* ARP remote address. */
./source/FreeRTOS_ARP.c:874:            ucMinAgeFound = xARPCache[ x ].ucAge;
./source/FreeRTOS_ARP.c:919:                *pulIPAddress = xARPCache[ x ].ulIPAddress;
./source/FreeRTOS_ARP.c:924:                    *( ppxInterface ) = xARPCache[ x ].pxEndPoint->pxNetworkInterface;
./source/FreeRTOS_ARP.c:1142:                    *( ppxEndPoint ) = xARPCache[ x ].pxEndPoint;
./source/FreeRTOS_ARP.c:1201:                xARPCache[ x ].ulIPAddress = 0U;
```
751Lか828Lあたりだろうな
```c
 690 void vARPRefreshCacheEntry( const MACAddress_t * pxMACAddress,
 691                             const uint32_t ulIPAddress,
 692                             struct xNetworkEndPoint * pxEndPoint )
...
 776 static BaseType_t prvFindCacheEntry( const MACAddress_t * pxMACAddress,
 777                                      const uint32_t ulIPAddress,
 778                                      struct xNetworkEndPoint * pxEndPoint,
 779                                      CacheLocation_t * pxLocation )
```

これあれか、ドライバがarpテーブル更新する必要があるのか？

試しに更新してみたら一応ARPはHITするようになった
```gdscript
Breakpoint 1, prvTCPPrepareConnect_IPV4 (pxSocket=0x20113808) at /home/voyager/workspace/freertos/FreeRTOS-Plus-TCP/source/FreeRTOS_TCP_Transmission_IPv4.c:344
344	    switch( eReturned )
$1 = eARPCacheMiss

Breakpoint 1, prvTCPPrepareConnect_IPV4 (pxSocket=0x20113808) at /home/voyager/workspace/freertos/FreeRTOS-Plus-TCP/source/FreeRTOS_TCP_Transmission_IPv4.c:344
344	    switch( eReturned )
$2 = eARPCacheHit
```

なのにいつまでたってもSYNパケットがドライバにやってこない

EndPointがnullだとダメらしい
```gdscript
prvTCPReturnPacket_IPV4 (pxSocket=pxSocket@entry=0x20113808, pxDescriptor=pxDescriptor@entry=0x0 <isr_vector>, ulLen=ulLen@entry=52, xReleaseAfterSend=xReleaseAfterSend@entry=0) at /home/voyager/workspace/freertos/FreeRTOS-Plus-TCP/source/FreeRTOS_TCP_Transmission_IPv4.c:88
163	            if( pxNetworkBuffer->pxEndPoint == NULL )
164	            {
165	                prvTCPReturn_SetEndPoint( pxSocket, pxNetworkBuffer, uxIPHeaderSize );
166	
167	                if( pxNetworkBuffer->pxEndPoint == NULL )
168	                {
169	                    if( xDoRelease != pdFALSE )
170	                    {
171	                        vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );
(gdb) 
```

EndPointを枠だけ作ってみた
```c
NetworkEndPoint_t opposit_ep = {0};
```

絶対おかしいやろこれ
```gdscript
^C
Program received signal SIGINT, Interrupt.
vAssertCalled () at main.c:139
139	        while( looping == 0LU )
(gdb) bt 
#0  vAssertCalled () at main.c:139
#1  0x0000bd4a in prvTCPReturnPacket_IPV4 (pxSocket=pxSocket@entry=0x201138f8, pxDescriptor=pxDescriptor@entry=0x0 <isr_vector>, ulLen=ulLen@entry=52, xReleaseAfterSend=xReleaseAfterSend@entry=0) at /home/voyager/workspace/freertos/FreeRTOS-Plus-TCP/source/FreeRTOS_TCP_Transmission_IPv4.c:283
#2  0x0000b5c4 in prvTCPReturnPacket (pxSocket=pxSocket@entry=0x201138f8, pxDescriptor=pxDescriptor@entry=0x0 <isr_vector>, ulLen=ulLen@entry=52, xReleaseAfterSend=xReleaseAfterSend@entry=0) at /home/voyager/workspace/freertos/FreeRTOS-Plus-TCP/source/FreeRTOS_TCP_Transmission.c:288
#3  0x0000ba44 in prvTCPSendPacket (pxSocket=pxSocket@entry=0x201138f8) at /home/voyager/workspace/freertos/FreeRTOS-Plus-TCP/source/FreeRTOS_TCP_Transmission.c:175
#4  0x0000a436 in xTCPSocketCheck (pxSocket=pxSocket@entry=0x201138f8) at /home/voyager/workspace/freertos/FreeRTOS-Plus-TCP/source/FreeRTOS_TCP_IP.c:226
#5  0x000091b8 in xTCPTimerCheck (xWillSleep=xWillSleep@entry=1) at /home/voyager/workspace/freertos/FreeRTOS-Plus-TCP/source/FreeRTOS_Sockets.c:4828
#6  0x00005edc in vCheckNetworkTimers () at /home/voyager/workspace/freertos/FreeRTOS-Plus-TCP/source/FreeRTOS_IP_Timers.c:302
#7  0x00005b84 in prvProcessIPEventsAndTimers () at /home/voyager/workspace/freertos/FreeRTOS-Plus-TCP/source/FreeRTOS_IP.c:266
#8  0x00005cae in prvIPTask (pvParameters=<optimized out>) at /home/voyager/workspace/freertos/FreeRTOS-Plus-TCP/source/FreeRTOS_IP.c:247
#9  0x00000290 in prvPortStartFirstTask () at /home/voyager/workspace/freertos/freertos_cm3/FreeRTOS/Source/portable/GCC/ARM_CM3/port.c:247
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
(gdb) f 1
#1  0x0000bd4a in prvTCPReturnPacket_IPV4 (pxSocket=pxSocket@entry=0x201138f8, pxDescriptor=pxDescriptor@entry=0x0 <isr_vector>, ulLen=ulLen@entry=52, xReleaseAfterSend=xReleaseAfterSend@entry=0) at /home/voyager/workspace/freertos/FreeRTOS-Plus-TCP/source/FreeRTOS_TCP_Transmission_IPv4.c:283
283	            configASSERT( pxNetworkBuffer->pxEndPoint->pxNetworkInterface != NULL );
(gdb) p pxEndPoint->pxNetworkInterface
No symbol "pxEndPoint" in current context.
(gdb) l
278	            #endif /* if( ipconfigETHERNET_MINIMUM_PACKET_BYTES > 0 ) */
279	
280	            /* Send! */
281	            iptraceNETWORK_INTERFACE_OUTPUT( pxNetworkBuffer->xDataLength, pxNetworkBuffer->pucEthernetBuffer );
282	
283	            configASSERT( pxNetworkBuffer->pxEndPoint->pxNetworkInterface != NULL );
284	            configASSERT( pxNetworkBuffer->pxEndPoint->pxNetworkInterface->pfOutput != NULL );
285	
286	            NetworkInterface_t * pxInterface = pxNetworkBuffer->pxEndPoint->pxNetworkInterface;
287	            ( void ) pxInterface->pfOutput( pxInterface, pxNetworkBuffer, xDoRelease );
(gdb) p pxNetworkBuffer 
$4 = (NetworkBufferDescriptor_t *) 0x20010408 <xIPTaskStack.1+39756>
(gdb) p pxNetworkBuffer->pxEndPoint 
$5 = (struct xNetworkEndPoint *) 0x20011374 <opposit_ep>
(gdb) p pxNetworkBuffer->pxEndPoint->pxNetworkInterface->pfOutput 
$6 = (NetworkInterfaceOutputFunction_t) 0xf5 <Default_Handler2>
(gdb) 

```

とりあえず通すためと割り切る
```c
NetworkInterface_t opposit_if = {
    .pfOutput = pfOutput,
    .pxEndPoint = &opposit_ep,
};
```

一応データは飛んだが未接続と判断された
```
[6768] NetworkInterface.c(195) pfOutput ip:0 po:0 bp:0 len:66 r:0 data:
aa 00 02 11 22 33 00 00 00 00 00 00 08 00 45 00
00 34 00 02 00 00 80 06 16 19 00 00 00 00 c0 a8
64 01 05 01 1b ee 02 02 02 02 00 00 00 00 80 02
0b 68 1a 14 00 00 02 04 05 b4 01 03 03 00 01 01
04 02
[3678] FreeRTOS_Sockets.c(3854) FreeRTOS_connect uxEvents:20 xResut:0 fr:0xdaa1 
[3678] app_client.c(98) vTCPSend not connected. -128 fr:0xdb7d 
[3678] FreeRTOS_Sockets.c(4747) FreeRTOS_shutdown pdFREERTOS_ERRNO_ENOTCONN fr:0xdafd 
```

逆も同じくどこかへ消えていった
```
[2608] NetworkInterface.c(262) ether_task rx data to stack:1 sz:74 data:
aa 00 02 aa bb cc aa 00 02 11 22 33 08 00 45 00
00 3c 05 16 40 00 40 06 ec 51 c0 a8 64 01 c0 a8
64 02 de 24 1b ef d6 03 72 f8 00 00 00 00 a0 02
fe 2e d7 19 00 00 02 04 05 a6 04 02 08 0a 19 30
cd 30 00 00 00 00 01 03 03 07 
 fr:0x291 
```

3行目末尾が `02`になっているからこれがSYNパケットで問題ないが
LinuxにしろFreeRTOSにしろACKパケットが返ってこない

EndPoint設定したら受信したデータがポートフォワードされてるっぽい
```
[6768] NetworkInterface.c(195) pfOutput ip:0 po:0 bp:0 len:98 r:1 data:
aa 00 02 11 22 33 00 00 00 00 00 00 08 00 45 00
00 54 2e 03 00 00 40 01 03 52 c0 a8 64 02 c0 a8
64 01 00 00 37 7f 00 28 00 01 12 c6 8b 65 00 00
00 00 61 59 0a 00 00 00 00 00 10 11 12 13 14 15
16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25
 fr:0x5837 
[2608] NetworkInterface.c(262) ether_task rx data to stack:1 sz:98 data:
aa 00 02 11 22 33 00 00 00 00 00 00 08 00 45 00
00 54 2e 03 00 00 40 01 03 52 c0 a8 64 02 c0 a8
64 01 00 00 37 7f 00 28 00 01 12 c6 8b 65 00 00
00 00 61 59 0a 00 00 00 00 00 10 11 12 13 14 15
16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25
 fr:0x291 
```

どう考えてもARPテーブルの内容保持にEndPointとかInterFaceとかの情報が必要とは思えないし
この解決方法はなしだな。

だとすると本来意図されている動きを見つける必要がある