OSDN Git Service

update libMiMic
[mimic/MiMicSDK.git] / lib / src / netif / dhcp / NyLPC_cDhcpClient.c
diff --git a/lib/src/netif/dhcp/NyLPC_cDhcpClient.c b/lib/src/netif/dhcp/NyLPC_cDhcpClient.c
new file mode 100644 (file)
index 0000000..ef627b4
--- /dev/null
@@ -0,0 +1,354 @@
+/*********************************************************************************
+ * PROJECT: MiMic
+ * --------------------------------------------------------------------------------
+ *
+ * This file is part of MiMic
+ * Copyright (C)2011-2013 Ryo Iizuka
+ *
+ * MiMic is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * For further information please contact.
+ *  http://nyatla.jp/
+ *  <airmail(at)ebony.plala.or.jp> or <nyatla(at)nyatla.jp>
+ *
+ *********************************************************************************/
+#include "NyLPC_cDhcpClient.h"
+#include "../NyLPC_cNet.h"
+#include <stdio.h>
+#include <string.h>
+
+struct NyLPC_TDhcpHeader
+{
+    NyLPC_TUInt8 op;
+    NyLPC_TUInt8 htype;
+    NyLPC_TUInt8 hlen;
+    NyLPC_TUInt8 hops;
+    NyLPC_TUInt32 xid;
+    NyLPC_TUInt16 secs;
+    NyLPC_TUInt16 flags;
+    NyLPC_TUInt32 ciaddr;
+    NyLPC_TUInt32 yiaddr;
+    NyLPC_TUInt32 siaddr;
+    NyLPC_TUInt32 giaddr;
+    struct{
+        struct NyLPC_TEthAddr emac;
+        NyLPC_TChar padding[10];
+    }chaddr;
+    NyLPC_TChar sname[64];
+    NyLPC_TChar file[128];
+}PACK_STRUCT_END;
+
+#define NyLPC_TDhcpHeader_BOOTREQUEST 1
+#define NyLPC_TDhcpHeader_BOOTREPLY   2
+
+#define DHCP_OPT_ID_ROUTER 3
+#define DHCP_OPT_ID_SERVER_ID 54
+#define DHCP_OPT_ID_NETMASK 1
+#define DHCP_OPT_ID_MESSAGETYPE 53
+
+
+
+/**
+ * DHCPパケットから32bit値を読み出す。
+ * @return
+ * ネットワークオーダー
+ */
+static NyLPC_TBool getUInt32Option(const NyLPC_TUInt8* i_buf,NyLPC_TUInt16 len,NyLPC_TUInt8 i_id,NyLPC_TUInt32* o_v)
+{
+    const NyLPC_TUInt8* p=i_buf+sizeof(struct NyLPC_TDhcpHeader)+4;
+    while(*p!=0x00 && p<(i_buf+len-5)){
+        if(*p==i_id){
+            if(*(p+1)==4){
+                *o_v=*((NyLPC_TUInt32*)(p+2));
+                return NyLPC_TBool_TRUE;
+            }
+        }else{
+            p+=(*(p+1))+2;
+        }
+    }
+    return NyLPC_TBool_FALSE;
+}
+static NyLPC_TBool getUInt8Option(const NyLPC_TUInt8* i_buf,NyLPC_TUInt16 len,NyLPC_TUInt8 i_id,NyLPC_TUInt8* o_v)
+{
+    const NyLPC_TUInt8* p=i_buf+sizeof(struct NyLPC_TDhcpHeader)+4;
+    while(*p!=0x00 && p<(i_buf+len-5)){
+        if(*p==i_id){
+            if(*(p+1)==1){
+                *o_v=*(p+2);
+                return NyLPC_TBool_TRUE;
+            }
+        }else{
+            p+=(*(p+1))+2;
+        }
+    }
+    return NyLPC_TBool_FALSE;
+}
+static NyLPC_TBool NyLPC_TDhcpHeader_parseDHCPOFFER(const NyLPC_TUInt8* i_buf,NyLPC_TUInt16 i_len,NyLPC_TUInt32 i_xid,NyLPC_TcDhcpClient_t* i_inst)
+{
+    struct NyLPC_TDhcpHeader* p=(struct NyLPC_TDhcpHeader*)i_buf;
+    //XIDのチェック
+    if(p->xid!=NyLPC_HTONL(i_xid)){
+        return NyLPC_TBool_FALSE;
+    }
+    //OFFERのclient IPアドレスをresultへ保存情報の保存
+    i_inst->_result->ip_addr.v=p->yiaddr;
+    //SERVER IDを保存
+    if(!getUInt32Option(i_buf,i_len,DHCP_OPT_ID_SERVER_ID,&i_inst->_offerserver.v)){
+        return NyLPC_TBool_FALSE;
+    }
+    return NyLPC_TBool_TRUE;
+}
+
+static NyLPC_TBool NyLPC_TDhcpHeader_parseDHCPACK(const NyLPC_TUInt8* i_buf,NyLPC_TUInt16 i_len,NyLPC_TUInt32 i_xid,NyLPC_TcIPv4Config_t* result)
+{
+    struct NyLPC_TDhcpHeader* p=(struct NyLPC_TDhcpHeader*)i_buf;
+    //XIDのチェック
+    if(p->xid!=NyLPC_HTONL(i_xid)){
+        return NyLPC_TBool_FALSE;
+    }
+    if(!getUInt32Option(i_buf,i_len,DHCP_OPT_ID_ROUTER,&result->dr_addr.v)){
+        result->dr_addr=NyLPC_TIPv4Addr_ZERO;
+    }
+    if(!getUInt32Option(i_buf,i_len,DHCP_OPT_ID_NETMASK,&result->netmask.v)){
+        result->netmask=NyLPC_TIPv4Addr_ZERO;
+    }
+    result->ip_addr.v=p->yiaddr;
+    return NyLPC_TBool_TRUE;
+}
+
+static void NyLPC_TDhcpHeader_setDHCPDISCOVER(char* i_buf,NyLPC_TUInt32 i_xid,const struct NyLPC_TEthAddr* emac,NyLPC_TUInt16* o_len)
+{
+    struct NyLPC_TDhcpHeader* p=(struct NyLPC_TDhcpHeader*)i_buf;
+    memset(i_buf,0,sizeof(struct NyLPC_TDhcpHeader));
+    p->op=NyLPC_TDhcpHeader_BOOTREQUEST;
+    p->htype=1;
+    p->hlen=6;
+    p->xid=NyLPC_HTONL(i_xid);
+    p->chaddr.emac=*emac;
+    p->flags=NyLPC_HTONS(0x8000);
+    memcpy(i_buf+sizeof(struct NyLPC_TDhcpHeader),
+        "\x63\x82\x53\x63"      //4
+        "\x35\x01\x01"          //3 MESSAGE TYPE
+        "\x37\x03\x01\x03\x06"  //5 REQUEST LIST(1,3,6)
+        "\x3d\x07\x01\x00\x00\x00\x00\x00\x00" //9 CLIENT INDIFIRE
+        "\xff",4+3+5+9+1);
+    //emacの上書き
+    memcpy((i_buf+sizeof(struct NyLPC_TDhcpHeader)+4+3+5+3),emac->addr,6);
+    //送信するパケットの長さ
+    *o_len=sizeof(struct NyLPC_TDhcpHeader)+4+3+5+9+1;
+    return;
+}
+static void NyLPC_TDhcpHeader_setDHCPREQUEST(char* i_buf,NyLPC_TUInt32 i_xid,const struct NyLPC_TIPv4Addr* i_sid,const struct NyLPC_TIPv4Addr* i_reqid,const struct NyLPC_TEthAddr* emac,NyLPC_TUInt16* o_len)
+{
+    struct NyLPC_TDhcpHeader* p=(struct NyLPC_TDhcpHeader*)i_buf;
+    memset(i_buf,0,sizeof(struct NyLPC_TDhcpHeader));
+    p->op=NyLPC_TDhcpHeader_BOOTREQUEST;
+    p->htype=1;
+    p->hlen=6;
+    p->xid=NyLPC_HTONL(i_xid);
+    p->chaddr.emac=*emac;
+    p->flags=NyLPC_HTONS(0x8000);
+    memcpy(i_buf+sizeof(struct NyLPC_TDhcpHeader),
+        "\x63\x82\x53\x63"      //4
+        "\x35\x01\x03"          //3 MESSAGE TYPE
+        "\x37\x03\x01\x03\x06"  //5 REQUEST LIST(1,3,6)
+        "\x3d\x07\x01\x00\x00\x00\x00\x00\x00" //9 CLIENT INDIFIRE
+        "\x36\x04\x00\x00\x00\x00" // 6 SERVER ID
+        "\x32\x04\x00\x00\x00\x00" // 6 Reqested IP
+        "\xff",4+3+5+9+6+6+1);
+    //emacの上書き
+    memcpy((i_buf+sizeof(struct NyLPC_TDhcpHeader)+4+3+5+3),emac->addr,6);
+    //sidの上書き
+    memcpy((i_buf+sizeof(struct NyLPC_TDhcpHeader)+4+3+5+9+2),i_sid,4);
+    //reqidの上書き
+    memcpy((i_buf+sizeof(struct NyLPC_TDhcpHeader)+4+3+5+9+6+2),i_reqid,4);
+    //送信するパケットの長さ
+    *o_len=sizeof(struct NyLPC_TDhcpHeader)+4+3+5+9+6+6+1;
+    return;
+}
+
+
+
+#define TcDhcpSock_ST_WAIT_OFFER    1
+#define TcDhcpSock_ST_WAIT_OFFER_OK 2
+#define TcDhcpSock_ST_WAIT_ACK 3
+#define TcDhcpSock_ST_WAIT_ACK_OK 4
+#define TcDhcpSock_ST_DONE_NG 3
+#define TcDhcpSock_ST_DONE_OK 4
+
+
+
+
+#define DHCP_OPT_ID_MESSAGETYPE_ACK   5
+#define DHCP_OPT_ID_MESSAGETYPE_OFFER 2
+
+static NyLPC_TBool onPacket(NyLPC_TiUdpSocket_t* i_inst,const void* i_buf,const struct NyLPC_TIPv4RxInfo* i_info);
+
+/**
+ * DHCPソケットを作成します。
+ */
+NyLPC_TBool NyLPC_cDhcpClient_initialize(NyLPC_TcDhcpClient_t* i_inst)
+{
+    i_inst->_socket=NyLPC_cNet_createUdpSocketEx(68,NyLPC_TSocketType_UDP_NOBUF);
+    if(i_inst->_socket==NULL){
+        return NyLPC_TBool_FALSE;
+    }
+    i_inst->_socket->_tag=i_inst;
+    NyLPC_iUdpSocket_setBroadcast(i_inst->_socket);
+    NyLPC_iUdpSocket_setOnRxHandler(i_inst->_socket,onPacket);
+    return NyLPC_TBool_TRUE;
+}
+void NyLPC_cDhcpClient_finalize(NyLPC_TcDhcpClient_t* i_inst)
+{
+    NyLPC_iUdpSocket_finalize(i_inst->_socket);
+}
+#define TIMEOUT_SOCKAPI_MS 1000
+#define TIMEOUT_RECVMSG_MS 3000
+
+/**
+ * ネットワークを更新します。
+ * emac/default_mssを設定したネットワークが必要です。
+ */
+static NyLPC_TBool NyLPC_cDhcpClient_dhcpRequest(NyLPC_TcDhcpClient_t* i_inst,NyLPC_TcIPv4Config_t* i_result)
+{
+    char* buf;
+    NyLPC_TcStopwatch_t sw;
+    NyLPC_TUInt16 s;
+    NyLPC_TInt16 hint=sizeof(struct NyLPC_TDhcpHeader)+128;
+    i_inst->txid+=(*(NyLPC_TUInt16*)(&(i_result->eth_mac.addr[2])))+(*(NyLPC_TUInt16*)(&(i_result->eth_mac.addr[4])));
+    i_inst->_result=i_result;
+    buf=NyLPC_iUdpSocket_allocSendBuf(i_inst->_socket,hint,&s,TIMEOUT_SOCKAPI_MS);
+    if(buf==NULL || s<hint){
+        return NyLPC_TBool_FALSE;
+    }
+    NyLPC_TDhcpHeader_setDHCPDISCOVER(buf,i_inst->txid,&i_inst->_result->eth_mac,&s);
+    i_inst->_status=TcDhcpSock_ST_WAIT_OFFER;
+    if(!NyLPC_iUdpSocket_psend(i_inst->_socket,&NyLPC_TIPv4Addr_BROADCAST,67,buf,s)){
+        NyLPC_iUdpSocket_releaseSendBuf(i_inst->_socket,buf);
+        return NyLPC_TBool_FALSE;
+    }
+    NyLPC_cStopwatch_initialize(&sw);
+    NyLPC_cStopwatch_startExpire(&sw,TIMEOUT_RECVMSG_MS);
+    while(i_inst->_status==TcDhcpSock_ST_WAIT_OFFER){
+        if(NyLPC_cStopwatch_isExpired(&sw)){
+            return NyLPC_TBool_FALSE;
+        }
+    }
+    //レスポンスのチェック
+    if(i_inst->_status!=TcDhcpSock_ST_WAIT_OFFER_OK)
+    {
+        return NyLPC_TBool_FALSE;
+    }
+    buf=NyLPC_iUdpSocket_allocSendBuf(i_inst->_socket,hint,&s,TIMEOUT_SOCKAPI_MS);
+    if(buf==NULL || s<hint){
+        return NyLPC_TBool_FALSE;
+    }
+    NyLPC_TDhcpHeader_setDHCPREQUEST(buf,i_inst->txid,&(i_inst->_offerserver),&(i_inst->_result->ip_addr),&i_inst->_result->eth_mac,&s);
+    i_inst->_status=TcDhcpSock_ST_WAIT_ACK;
+    if(!NyLPC_iUdpSocket_psend(i_inst->_socket,&NyLPC_TIPv4Addr_BROADCAST,67,buf,s)){
+        NyLPC_iUdpSocket_releaseSendBuf(i_inst->_socket,buf);
+        return NyLPC_TBool_FALSE;
+    }
+    NyLPC_cStopwatch_startExpire(&sw,TIMEOUT_RECVMSG_MS);
+    while(i_inst->_status==TcDhcpSock_ST_WAIT_ACK){
+        if(NyLPC_cStopwatch_isExpired(&sw)){
+            return NyLPC_TBool_FALSE;
+        }
+    }
+    //レスポンスのチェック
+    if(i_inst->_status!=TcDhcpSock_ST_WAIT_ACK_OK)
+    {
+        return NyLPC_TBool_FALSE;
+    }
+    return NyLPC_TBool_TRUE;
+}
+
+/**
+ * NyLPC_TcIPv4Config_tをDHCPで更新します。
+ * この関数をコールする時は、サービスは停止中でなければなりません。
+ * @param i_cfg
+ * 更新するi_cfg構造体。
+ * emac,default_mssは設定済である必要があります。他のフィールド値は不定で構いません。
+ * 更新されるフィールドは、ip,netmast,default_rootの3つです。
+ * @return
+ * 更新に成功した場合TRUE
+ */
+NyLPC_TBool NyLPC_cDhcpClient_requestAddr(NyLPC_TcDhcpClient_t* i_inst,NyLPC_TcIPv4Config_t* i_cfg,NyLPC_TInt16 i_repeat)
+{
+    NyLPC_TInt16 i;
+    NyLPC_TBool ret=NyLPC_TBool_FALSE;
+    NyLPC_TcIPv4Config_t c2;
+    //工場出荷時設定でリセットしてIPを0に
+    NyLPC_cIPv4Config_initialzeCopy(&c2,i_cfg);
+    NyLPC_cIPv4Config_setIp(&c2,&NyLPC_TIPv4Addr_ZERO,&NyLPC_TIPv4Addr_ZERO);
+    NyLPC_cIPv4Config_setDefaultRoute(&c2,&NyLPC_TIPv4Addr_ZERO);
+    //netを開始
+    NyLPC_cNet_start(&c2);
+    for(i=i_repeat-1;i>=0;i--){
+        ret=NyLPC_cDhcpClient_dhcpRequest(i_inst,i_cfg);
+        if(ret){
+            break;
+        }
+    }
+    NyLPC_cNet_stop();
+    NyLPC_cIPv4Config_finalize(&c2);
+    return ret;
+}
+
+
+
+static NyLPC_TBool onPacket(NyLPC_TiUdpSocket_t* i_inst,const void* i_buf,const struct NyLPC_TIPv4RxInfo* i_info)
+{
+    NyLPC_TUInt8 mt;//message type
+    NyLPC_TcDhcpClient_t* inst=(NyLPC_TcDhcpClient_t*)i_inst->_tag;
+    struct NyLPC_TDhcpHeader* dnsh=(struct NyLPC_TDhcpHeader*)i_buf;
+    if(i_info->size<sizeof(struct NyLPC_TDhcpHeader)+1){
+        return NyLPC_TBool_FALSE;//DROP
+    }
+    switch(inst->_status)
+    {
+    case TcDhcpSock_ST_WAIT_ACK:
+        if(dnsh->op!=NyLPC_TDhcpHeader_BOOTREPLY){
+            return NyLPC_TBool_FALSE;
+            }
+        if(!getUInt8Option(i_buf,i_info->size,DHCP_OPT_ID_MESSAGETYPE,&mt)){
+            return NyLPC_TBool_FALSE;
+        }
+        if(mt!=DHCP_OPT_ID_MESSAGETYPE_ACK){
+            return NyLPC_TBool_FALSE;
+        }
+        if(!NyLPC_TDhcpHeader_parseDHCPACK(i_buf,i_info->size,inst->txid,inst->_result)){
+            return NyLPC_TBool_FALSE;
+        }
+        inst->_status=TcDhcpSock_ST_WAIT_ACK_OK;
+        break;
+    case TcDhcpSock_ST_WAIT_OFFER:
+        if(dnsh->op!=NyLPC_TDhcpHeader_BOOTREPLY){
+            return NyLPC_TBool_FALSE;
+            }
+        if(!getUInt8Option(i_buf,i_info->size,DHCP_OPT_ID_MESSAGETYPE,&mt)){
+            return NyLPC_TBool_FALSE;
+        }
+        if(mt!=DHCP_OPT_ID_MESSAGETYPE_OFFER){
+            return NyLPC_TBool_FALSE;
+        }
+        if(!NyLPC_TDhcpHeader_parseDHCPOFFER(i_buf,i_info->size,inst->txid,inst)){
+            return NyLPC_TBool_FALSE;
+        }
+        inst->_status=TcDhcpSock_ST_WAIT_OFFER_OK;
+        break;
+    }
+    return NyLPC_TBool_FALSE;
+
+}