LoginSignup
0
0

STM32で始めるFreeRTOSとLwIP(3) -DHCP Client

Last updated at Posted at 2024-05-24

この記事の続きになります。前回はFreeRTOSとLwIPを使ったサンプルの初期化部分のソースコードが理解できたので、DHCP Clientのソースコードの中身を見ていきたいと思います。
なお、FreeRTOSの詳しい説明に関しては割愛します。FreeRTOSの内容については下記の記事にもまとめているので、こちらを参照していただければと思います。
https://qiita.com/tom_S/items/9d81f876fa83c66a6065

この記事ではDHCPの機能とDHCP Clientについて軽く説明した後、サンプルで実装されているDHCP Clientの実装を見ていきます。
前回の記事でも少々説明しましたが、DHCP Clientの実装は「DHCP_Thread」にて行っているので、こちらを見ていきます。

DHCPの機能

DHCP(Dynamic Host Configuration Protocol)は、IPアドレスの設定を自動で行う仕組みです。また、設定するIPアドレスが他のネットワークインターフェースと重複しないようにIPアドレスを管理します。
DHCPを使用する際は、DHCP ClientとDHCP Serverが存在していないと動作しません。
IPアドレスの設定フローとしては下記となります。

  1. DHCP ClientがDHCP Serverへ、IPアドレスの設定を要求するパケット(DHCP Discover)を送信
  2. DHCPサーバーからDHCP Clientへ割り振られたIPアドレスを送信(DHCP Offer)
  3. DHCP Clientは割り振られたIPアドレスを設定したことを示すパケット(DHCP Request)をDHCP Serverへ送信
  4. DHCP Serverは、送ったIPアドレスが設定されたことを承認するパケット(DHCP ACK)をDHCP Clientへ送信

DHCP.png

DHCP_Thread

上記で説明したDHCPのフローを基にサンプルのソースコード(DHCP_Thread)を確認していきます。
DHCP_Thread()はapp_ethernet.cに実装されています。下記の通りです。

void DHCP_Thread(void const * argument)
{
 struct netif *netif = (struct netif *) argument;
 ip_addr_t ipaddr;
 ip_addr_t netmask;
 ip_addr_t gw;
 struct dhcp *dhcp;
#ifdef USE_LCD
 uint8_t iptxt[20];
#endif
 for (;;)
 {
   switch (DHCP_state)
   {
   case DHCP_START:
     {
       ip_addr_set_zero_ip4(&netif->ip_addr);
       ip_addr_set_zero_ip4(&netif->netmask);
       ip_addr_set_zero_ip4(&netif->gw);
       DHCP_state = DHCP_WAIT_ADDRESS;
#ifdef USE_LCD
       LCD_UsrLog ("  State: Looking for DHCP server ...\n");
#else
       BSP_LED_Off(LED1);
       BSP_LED_Off(LED2);
#endif
       /* DHCP Discoverパケットを送信(受信処理はLwIPの内部処理で行う) */
       dhcp_start(netif);
     }
     break;
   case DHCP_WAIT_ADDRESS:
     {
       /* DHCPでIPアドレスが取得できていない */
       if (dhcp_supplied_address(netif))
       {
         DHCP_state = DHCP_ADDRESS_ASSIGNED;
#ifdef USE_LCD
         sprintf((char *)iptxt, "%s", ip4addr_ntoa(netif_ip4_addr(netif)));
         LCD_UsrLog ("IP address assigned by a DHCP server: %s\n", iptxt);
#else
         BSP_LED_On(LED1);
         BSP_LED_Off(LED2);
#endif
       }
       else
       {
         dhcp = (struct dhcp *)netif_get_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP);
         /* DHCP timeout */
         if (dhcp->tries > MAX_DHCP_TRIES)
         {
           DHCP_state = DHCP_TIMEOUT;
           /* Static address used */
           IP_ADDR4(&ipaddr, IP_ADDR0 ,IP_ADDR1 , IP_ADDR2 , IP_ADDR3 );
           IP_ADDR4(&netmask, NETMASK_ADDR0, NETMASK_ADDR1, NETMASK_ADDR2, NETMASK_ADDR3);
           IP_ADDR4(&gw, GW_ADDR0, GW_ADDR1, GW_ADDR2, GW_ADDR3);
           netif_set_addr(netif, ip_2_ip4(&ipaddr), ip_2_ip4(&netmask), ip_2_ip4(&gw));
#ifdef USE_LCD
           sprintf((char *)iptxt, "%s", ip4addr_ntoa(netif_ip4_addr(netif)));
           LCD_UsrLog ("DHCP Timeout !! \n");
           LCD_UsrLog ("Static IP address: %s\n", iptxt);
#else
           BSP_LED_On(LED1);
           BSP_LED_Off(LED2);
#endif
         }
       }
     }
     break;
 case DHCP_LINK_DOWN:
   {
     DHCP_state = DHCP_OFF;
#ifdef USE_LCD
     LCD_UsrLog ("The network cable is not connected \n");
#else
     BSP_LED_Off(LED1);
     BSP_LED_On(LED2);
#endif
   }
   break;
   default: break;
   }
   /* wait 500 ms */
   osDelay(500);
 }
}

上記のスレッドの機能としては下記となります。

  • Ethernetが接続された際にDHCP Clientとして動作し、Ethernetが切断されたら状態を「DHCP_state」の値が「DHCP_START」なら、dhcp_start()を呼び出し、「DHCP_state」の値を「DHCP_WAIT_ADDRESS」に変更
    • dhcp_start()はLwIPのAPIで、DHCP Client制御用のインスタンスを作成し、IPアドレスの設定を要求するパケット(DHCP Discover)を送信する
  • 「DHCP_state」の値が「DHCP_WAIT_ADDRESS」なら、dhcp_supplied_address()を呼び出してIPアドレスを取得できたかを確認。所得できた状態ならIPアドレスを設定し、「DHCP_state」の値を「DHCP_ADDRESS_ASSIGNED」に変更。タイムアウトしており、IPアドレスを取得できていない場合は、固定のIPアドレスを設定して「DHCP_state」の値を「DHCP_TIMEOUT」に変更
  • 「DHCP_state」の値が「DHCP_LINK_DOWN」なら、「DHCP_state」の値を「DHCP_OFF」に変更

DHCPのステート「DHCP_state」は、このスレッド「DHCP_Thread()」とEthernetの接続状況が変わった際に呼ばれるコールバック「ethernet_link_status_updated()」で値が変わります。

ethernet_link_status_updated

「ethernet_link_status_updated()」はこちらの記事で説明した初期化時のサンプルコードで「netif_set_link_callback()」を使って、コールバックとして登録されています。実装は下記のとおりです。

void ethernet_link_status_updated(struct netif *netif)
{
 if (netif_is_up(netif))
{
#if LWIP_DHCP
   /* Update DHCP state machine */
   DHCP_state = DHCP_START;
#elif defined(USE_LCD)
   uint8_t iptxt[20];
   sprintf((char *)iptxt, "%s", ip4addr_ntoa(netif_ip4_addr(netif)));
   LCD_UsrLog ("Static IP address: %s\n", iptxt);
#else
   BSP_LED_On(LED1);
   BSP_LED_Off(LED2);
#endif /* LWIP_DHCP */
 }
 else
 {
#if LWIP_DHCP
   /* Update DHCP state machine */
   DHCP_state = DHCP_LINK_DOWN;
#elif defined(USE_LCD)
   LCD_UsrLog ("The network cable is not connected \n");
#else
   BSP_LED_Off(LED1);
   BSP_LED_On(LED2);
#endif /* LWIP_DHCP */
 }
}

「ethernet_link_status_updated()」の機能としては、下記のようになります。LEDの制御も含まれているのですが、DHCPと関係ないので割愛します。

  • Ethernetが接続状態になったら、「DHCP_state」の値を「DHCP_START」に変更する
  • Ethernetが切断状態になったら、「DHCP_state」の値を「DHCP_LINK_DOWN」に変更する

DHCP Clientの実装のまとめ

上記で示したDHCP Clientのサンプルソースコードにおける振る舞いをまとめると、下記のようになります。

  • Ethernetケーブルが接続された際に、IPアドレスの設定を要求するパケット(DHCP Discover)を送信する
    • DHCP Offer、DHCP Request、DHCP ACKの処理はLwIP内で行っている
  • Ethernetケーブルが切断された際は、IPアドレスは設定されていない状態に戻る
  • Ethernetケーブルを切断→接続を行うことで、再度DHCPのIPアドレス取得フローが始まる
  • DHCPでIPを取得できたかどうかは、スレッド内でポーリングして確認している

シーケンスを図にまとめると下記のようになります。

DHCPサンプル.png

サンプルソースのためこのような実装になっているかと思うのですが、本来ならDHCPスレッドはメッセージボックスやイベントグループなどの通知で動作させる形にするのが妥当かと思います。
このあたりも、全てのサンプルソースコードが読み終わった際に修正してみたいと思います。

次の記事ではHTTP Serverのサンプルソースを見ていきます。

参考資料

DHCP
https://milestone-of-se.nesuke.com/nw-basic/grasp-nw/dhcp/
https://www.challenge-cf.jp/post/wireshark%E3%81%A7%E3%82%AF%E3%83%A9%E3%82%A4%E3%82%A2%E3%83%B3%E3%83%88%E3%81%8Cdhcp%E3%82%B5%E3%83%BC%E3%83%90%E3%81%8B%E3%82%89ip%E3%82%A2%E3%83%89%E3%83%AC%E3%82%B9%E3%82%92%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B%E3%81%BE%E3%81%A7%E3%81%AE%E5%90%84%E3%83%A1%E3%83%83%E3%82%BB%E3%83%BC%E3%82%B8%E3%82%92%E8%A6%8B%E3%81%A6%E3%81%BF%E3%81%9F

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0