インターネット/トランスポート層 – TCP/IPスタック

jp
tcpip_layer5

インターネット・プロトコル・スイート

インターネット/トランスポート層として、リンク層上にTCP/IPを実装します。今回の実装では、uIP*1を利用しました。

uIP

uIPは、Swedish Institute of Computer ScienceのAdam Dunkels氏が開発したオープンソースのTCP/IPスタックです。商用、非商用どちらでもフリーで利用できます。uIPはC言語で記述された、8bit CPUでも実装可能な小コードサイズとRAM使用サイズを特徴としたTCP/IPスタックであり、豊富なアプリケーションサンプル(Webサーバ、Webクライアント、SMTPクライアント、Telnetサーバ等)が付属しています。

uIPは以下のディレクトリで構成されています。

uip-1.0
         uip/
         apps/
             webserver/
         doc/
         lib/
         unix/

uipディレクトリにTCP/IPスタックコードが、apps/webserverディレクトリにWebサーバのアプリケーションサンプルが格納されています。今回は、主にこの2つのディレクトリのデータを利用して、TCP/IP及びHTTPの実装を行います。

uIPの移植

ターゲットとするシステムに組み込む為に、uIPコンフィグレーション・ファイルの設定とタイマー機能の実装を行います。

コンフィグレーションファイルの設定

uip-conf.hを書き換えて、各パラメータの設定を行います。主に、uIPで使用されるデータタイプの定義と、バッファサイズの指定を行います。

#ifndef __UIP_CONF_H__
 #define __UIP_CONF_H__

 // データタイプの定義
 typedef unsigned char u8_t;
 typedef unsigned short u16_t;
 typedef unsigned short uip_stats_t;
 // 
 #define UIP_CONF_MAX_CONNECTIONS 40 // 最大Connection数
 #define UIP_CONF_MAX_LISTENPORTS 40 // 最大 Listening TCPポート数
 #define UIP_CONF_BUFFER_SIZE 840 // パケットバッファサイズ

 #define UIP_CONF_BYTE_ORDER LITTLE_ENDIAN
 #define UIP_CONF_UDP 0
 #define UIP_CONF_UDP_CHECKSUMS  1
 #define UIP_CONF_STATISTICS 1
 // http用ヘッダファイル
 #include "webserver.h"
 #endif // __UIP_CONF_H__
タイマーの実装

タイマーは、TCPの状態遷移をTIME_WAITからCLOSEDに遷移させる為や、パケットの再送タイミングとして使用されます。
今回は、SH-4Aの32bitタイマユニット(TMU2)を利用してタイマーを実装しています。以下の設定では、TMU2に対して1秒ごとにアンダーフローインタラプトが発生するように指定を行っています。(clock_init())

(clock-arch.h)

#ifndef __CLOCK_ARCH_H__
 #define __CLOCK_ARCH_H__

 typedef unsigned int clock_time_t;
 #define CLOCK_CONF_SECOND 1000

 int get_timer_count_ether(int ms_count);

 #endif // __CLOCK_ARCH_H__

(clock-arch.c)

#include "iodefine.h"
 #include "clock-arch.h"

 clock_time_t
 clock_time(void) {
     // return TMU2 current counter
     unsigned int x = TMU2.TCNT;
     return x;
 }

 int get_timer_count_ether(int ms_count) {
     return (int)((float)ms_count/0.030720); // 30ns x 1024 (PCK/1024)
 }

 void clock_init(void) {
     // TMU2 is used for Networking
     // Configured to count ARP timeout
     // pck = 33MHz = 30ns, pck/1024 = 130KHz = 30us
     // set interrupt priority for TMU0(bit28:24),TMU1(bit20:16), TMU2(bit12:8)
     INTC.INT2PRI0.LONG = 0x0c0c0c00; // bit 28-24 (0,1 = mask)
     INTC.INT2MSKCR.LONG = 1; // TMU0-2
     TMU2.TCOR = get_timer_count_ether( 1000); // 1Sec
     TMU2.TCNT = get_timer_count_ether( 1000);
     TMU2.TCR.WORD = 0x0024; // timer control UNF=0,UNIE=1(int),CKEG=00 TPSC=100
 }

uIPの組み込み

TCP/IP処理のメインルーチンは、Ethernet Frameの受信インタラプトをトリガとして処理を開始します。Ethernet Frameを受信すると、typeフィールドの値を確認し、データがIP(Internet Protocol)の場合とARP(Address Resolution Protocol)の場合で処理を分岐します。

FrameデータがARPパケットの場合、uip_arp_arpin()をコールして応答用のARPパケットを生成します。応答用ARPには送信元のMACアドレスがセットされます。ARPパケットの生成後、ether_write_frame()をコールしてEthernet Frameを送信します。

FrameデータがIPの場合、次の順に処理を行います。

  1. uip_arp_ipin()をコールして、送信元のIPアドレスとMACアドレスの対応を確認し、内部ARPテーブルのアップデートを行います。
  2. uip_input()をコールしてTCPパケットの処理を行います。現在のTCPの遷移状態に従って、処理を行うイベントを判別し、UIP_APP_CALL()をコールします。HTTPは、この関数内で処理されます。
  3. UIP_APP_CALL()の結果、送信用のIPパケットが存在すれば、uip_arp_out()をコールしてEthernet Headerの作成を行います。
  4. ether_write_frame()をコールして、Ethernet Frameを送信します。
ether_pic5

uIP動作フロー

実際のコードは以下のようになります。コード中のuip_lenとuip_bufは、uIPで定義されているグローバル変数です。uIPとリンク層は、uip_lenとuip_bufを用いてデータの受け渡しを行います。uip_bufは送受信用のデータを格納する為に使用され、uip_lenは、送受信用データの長さを格納する為に使用されます。
(実装コード:SH4インタラプトハンドラ INTR IRQ2)

 void INT_IRL_LEVEL9() {
     // INTR IRQ2
     struct st_intsrc int_src;
     int i;
     int_src.LONG = ETHER_INT_SOURCE.LONG;

     if (int_src.TXB) {
         ETHER_INT_SOURCE.LONG = 1; // Clear Int
     }
     if (int_src.RXB) {
         // Ethernet Frameの受信
         ETHER_INT_SOURCE.LONG = 4; // インタラプトのクリア

         uip_len = ether_read_frame(uip_buf); // Frameデータと長さを獲得
         if(uip_len > 0) {
             if(BUF->type == htons(UIP_ETHTYPE_IP)) {
                 // IP Packet
                 uip_arp_ipin(); // ARPテーブルアップデート
                 uip_input(); // TCPパケット処理、(UIP_APP_CALL()呼び出し)
                 if(uip_len > 0) {
                     uip_arp_out(); // Ethernet Headerの作成
                     ether_write_frame(uip_len, uip_buf); // Ethernet Frameの送信
                 }
             } else if(BUF->type == htons(UIP_ETHTYPE_ARP)) {
                 // ARP
                 uip_arp_arpin(); // 応答用ARPパケットの生成
                 if(uip_len > 0) {
                     ether_write_frame(uip_len,uip_buf); // ARPパケットの送信
                 }
             }
         } 
     }
 }

タイトルとURLをコピーしました