AutoIPの実装サンプル

返信する
アバター
eForce技術担当
記事: 193
登録日時: 2014年4月24日(木) 14:18

AutoIPの実装サンプル

投稿記事 by eForce技術担当 » 2015年5月29日(金) 18:33

対象:
μNet3/Compact、μNet3/Standard

概要:
本項では、μNet3でAutoIP機能を使用したい場合の実装例を記載します。
AutoIPは下記のAPIを使用する事で実現する事ができます。

・IPアドレスの設定およびIPアドレス競合を検知した場合のコールバック関数設定を行う
ER net_cfg(UH num, UH opt, VP val)

・IPアドレスの競合探知を行う
ER net_acd(UH dev_num, T_NET_ACD *acd)

実装詳細:
・AutoIPを設定するタイミングについて
静的IPやDHCPサーバなどを介した動的IPの設定が出来なかった場合に
AutoIPの設定が実行されます。

・AutoIPの設定アドレスの範囲について
設定されるアドレスの範囲は「169.254.1.0?169.254.254.255」となります。
※169.254.0.0?169.254.0.255と169.254.255.0?169.254.255.255は予約済で使用できません。

・アドレス取得時の注意点について
まずアドレス取得は下記のような工程を辿ります。
 1.ランダムに選択されたリンクローカルアドレスを生成。
 2.アドレス競合チェックのためにARP通知などを行う。
 3.競合していた場合は「1.」の工程に戻る。

上記の過程でアドレス取得を行っているホスト側は、アドレス競合の回数を記憶する必要があり、
もしその回数がMAX_CONFLICTS(※1)で定義されている値以上になった場合、ホスト側はアドレス取得工程の
速度をRATE_LIMIT_INTERVAL(※2)で定義されている値に制限しなければなりません。
※1:MAX_CONFLICTSの値はデフォルトで「10(回)」と既定されています。
※2:RATE_LIMIT_INTERVALの値はデフォルトで「60(秒)」と既定されています。

サンプルコード:
上記を踏まえて下記にμNet3でのAutoIP機能の実装コードを記載します。

コード: 全て選択

/*---------------------------------------------------------------------------*/
/* プロトタイプ定義                                                          */
/*---------------------------------------------------------------------------*/
extern ER set_apipa( UH dev_num );
static ER apipa_acd_cbk( T_NET_ACD * acd );

/*---------------------------------------------------------------------------*/
/* 定数定義                                                                  */
/*---------------------------------------------------------------------------*/
#define AIP_CFL_CNT     10          /* アドレス競合の速度制限適用までの回数  */
#define AIP_RATE_INT    60*1000     /* アドレス取得の速度制限のインターバル  */


static ER apipa_acd_cbk(T_NET_ACD * acd)
{
    // アドレス取得後に競合が発生した場合にコールバックされる(処理内容は任意)

    // E_OK 以外を返した場合、ARP Announceを通知しない(つまりIPアドレスを破棄する)
    // E_OK を返した場合、ARP Announceを通知する(つまりIPアドレスを保持する)
}

ER set_apipa( UH dev_num )
{
                                        /* AutoIPのベース情報               */
    T_NET_ADR adr = { 0x00, 0x00, 0xA9FE0000, 0x00000000, 0xFFFF0000 };
    T_NET_ACD acd;                      /* アドレス競合情報                  */
    SYSTIM sysTim;                      /* システム時間                      */
    UW conflict_cnt;                    /* アドレス競合回数                  */
    ER ercd;                            /* 戻り値                            */

    /*-----------------------------------------------------------------------*/
    /* 乱数初期化                                                            */
    /*-----------------------------------------------------------------------*/
    get_tim( &sysTim );
    net_rand_seed( sysTim.ltime );

    /*-----------------------------------------------------------------------*/
    /* AutoIP取得処理                                                        */
    /*-----------------------------------------------------------------------*/
    for ( conflict_cnt = 1; ; conflict_cnt++ ) {
        /* ランダムなリンクローカルアドレス生成(169.254.XXX.XXX)           */
        adr.ipaddr &= 0xFFFF0000;
        adr.ipaddr |= (net_rand() % 253 + 1) << 8 | (net_rand() % 253 + 1);

        /* ホストにリンクローカルアドレスを設定                              */
        ercd = net_cfg( dev_num, NET_IP4_CFG, (VP)&adr );
        if ( ercd != E_OK ) {
            return ercd;
        }

        /* リンクローカルアドレスの競合探知を行う                            */
        ercd = net_acd( dev_num, &acd );
        if ( ercd == E_OK ) {
            /* 競合無しなので、後々競合検知した場合のコールバック関数設定    */
            ercd = net_cfg( dev_num, NET_ACD_CBK, (VP)apipa_acd_cbk );
            break;
        } else if ( ercd != E_SYS ) {
            /* E_OKでもE_SYSでもない場合、net_acd関数で異常発生              */
            break;
        }

        /* アドレス競合の回数が既定以上の場合、速度制限を行う                */
        if ( conflict_cnt >= AIP_CFL_CNT ) {
            /* ARPストーム防止                                               */
            tslp_tsk( AIP_RATE_INT );
        }
    }

    return ercd;
}
実装サンプルAPIの説明:
【書式】
ER set_apipa( UH dev_num )

【パラメータ】
UH dev_num : デバイス番号

【戻り値】
ER ercd 正常終了(E_OK)またはエラーコード

【解説】
dev_numで指定されるデバイスに対して、AutoIPの設定を行います。
このサンプルAPIはリンクローカルアドレスが取得できるまで制御を戻しません。
※RFC3927の仕様ではAutoIPの取得要求の回数上限は得に設けられていません。
 これはAutoIPが最大6万台以上のアドレス空間を保持している事と、そのアドレスが
 ローカルネットワーク内でしか使われないからであると考えられます。

競合の検出と対処方法:
以降の内容は競合検出のコールバックの処理実装に絡む内容である為、
必ずしも下記に準じる必要はありません。(コールバックの実装はユーザに委ねられます)
従って、あくまで参考資料として参照ください。

AutoIP取得後にアドレスの競合が検出された場合の対処法として
RFC3927では下記が既定されています。

a) 競合を検出した場合、ホストはアドレスの使用を即刻中止しホスト側へエラーを通知しても良い。(MAY)
b) 何らかの理由でアドレスを保持したい場合、ホストは競合通知の時刻を記録した上で、
  アドレスの継続保持を宣言(ARP Announce)を一度通知しても良い。(MAY)
  但し、競合検出がDEFEND_INTERVAL(※1)秒以内に再び現れた場合、ホストは「(a)」の処理を実施する。(MUST)

ホストは競合通知を無視してはいけない。従って上記(a)または(b)の処理を実行しなければならない。(MUST)
※1:DEFEND_INTERVALの値はデフォルトで「10(秒)」と既定されています。

これらに対処するため、一番シンプルな方法は下記の様に(a)の処理のみを実装します。

コード: 全て選択

ER g_aip_detect_err = E_OK;   /* AutoIPでの競合エラー                        */

static ER apipa_acd_cbk(T_NET_ACD * acd)
{
    /* 競合を検出した場合、即座にアドレスを破棄する                          */
    g_aip_detect_err = E_SYS;
    return E_SYS;
}


ホスト側のタスク()
{
    for ( ; ; ) {
        ・
        ・

        // AutoIPエラーの検出
        if ( g_aip_detect_err != E_OK ) {
            // エラーを検出した時の処理
        }

        ・
        ・
    }
}
可能な限りIPアドレスの保持を試みたい場合は下記の様に(a)(b)の処理を実装します。

コード: 全て選択

ER g_aip_detect_err = E_OK;             /* AutoIPでの競合エラー              */

#define AIP_DFD_INT     10*1000         /* アドレス破棄判定用インターバル    */

static ER apipa_acd_cbk(T_NET_ACD * acd)
{
    static UD last_time = 0;            /* 最後に競合検出した時間            */
    UD now_time;                        /* 競合検出した時間                  */
    SYSTIM sysTim;                      /* システム時間                      */

    /* 現在のシステム時刻を取得                                              */
    get_tim( &sysTim );
    now_time = (UD)sysTim.utime << 32 | (UD)sysTim.ltime;

    /* 最後に競合した時間を計算してアドレス破棄を判定する                    */
    if ( last_time != 0 && (now_time - last_time) < AIP_DFD_INT ) {
        /* E_SYSを返す場合、アドレス保持の継続宣言を行わない                 */
        g_aip_detect_err = E_SYS;
        return E_SYS;
    }

    /* 最後に競合検出した時間を更新                                          */
    last_time = now_time;

    /* E_OKを返す場合、アドレス保持の継続宣言を行う                          */
    return E_OK;
}

ホスト側のタスク()
{
    for ( ; ; ) {
        ・
        ・

        // AutoIPエラーの検出
        if ( g_aip_detect_err != E_OK ) {
            // エラーを検出した時の処理
        }

        ・
        ・
    }
}
参考文献:
http://tools.ietf.org/html/rfc3927

返信する