CWMP開源代碼研究番外篇——博通方案


聲明:本篇文章來自於某公司Cable Modem產品的文檔資料,源碼來自於博通公司,只提供參考(為保護產權,本人沒有源碼)。

前文曾提到會寫一篇關於博通的tr069,那么福利來了。福利,福利,福利,重要的事情說三遍!

如果你正在閱讀博通的相關產品代碼而又苦於沒有文檔參閱,那么我相信本文將會非常適合你。

一. TR069的Makefile和源碼
1. 編譯:

在編譯選項中添加“tr69”, 對應的makefile為:   REV/rbb_cm_src/Bfc/make/BfcTR69.mak  

2. 相關源碼

主要有3部分代碼:tr069 client agent代碼,用c實現;TR069Thread和CLI配置代碼,c++實現;client agent與系統間的接口代碼,c++實現。(具體實現在后文講解)

1 //tr069 client agent代碼
2 REV/rbb_cm_src/Bfc/IpHelpers/TR069/tr69c/
3 inc/  #放置與數據模型相關的宏定義,數據結構定義頭文件
4 main/ #agent實現文件,main,informer,event
5 nanoxml/ #
6 SOAPParser/ #soap解析,其中RPCState.c為RPC命令處理接口
7 standard/ #節點數據模型定義文件
8 webproto/ #
 1 //tr069thread與CLI參數配置
 2 REV/rbb_cm_src/Bfc/IpHelpers/TR069/
 3 BcmBfcTr69CommandTable.cpp #tr69 CLI實現
 4 BcmBfcTr69SnmpApi.cpp  #提供了通過snmp進行對系統參數的get/set接口,tr69 agent利用這些接口進行大部分參數的get/set
 5 BcmBfcTr69ThreadIpStackACT.cpp #ip_stack ip變化時,通知TR069Thread啟動或停止agent
 6 BcmBfcTr69SocketApi.cpp  #提供系統與agent間有關socket操作的接口,供agent調用
 7 BcmBfcTr69Entry.cpp  #
 8 BfcTr69NonVolSettings.cpp
 9 BfcTr69NonVolSettingsCommandTable.cpp  #tr69 non-vol參數配置CLI
10 BcmBfcTr69NonVolApi.cpp  #提供non-vol存取的接口,供agent調用
11 BcmBfcTr69Thread.cpp  #tr069進程實現,控制agent的運行/停止/重啟
1 //client agent系統調用接口
2 REV/rbb_cm_src/Bfc/IpHelpers/TR069/tr69c/bcmBfcIf/
3 其中的文件定義了與standard/目錄下數據模型結構中對應的object的add/del,parameters的get/set api

二. 配置參數  

1. CLI

1 CM>cd non-vol/tr69
2 CD>ls
3 acs_password acs_url acs_username conn_req_password conn_req_url conn_req_username
4 ip_stack_num periodic_inform_enable periodic_inform_interval
5 stun_enable stun_max_keepalive stun_mix_keepalive stun_password stun_server stun_username  

其中,ip_stack_num為tr69 agent關聯的wan interface對應的ipstack number,默認指定為3

此外,可使用的命令有如下:

1 CM>cd tr69
2 CM>ls
3 acs_url log send_inform show start_client stop_client test walk_tree

其中: client可理解為tr069 client agent
acs_url 修改acsurl
send_inform agent運行前提下,立即發送inform
start_client 如ip_stack_num對應的wan interface處於可用狀態,則會運行agent
walk_tree 查看節點結構

2. CM ConfigFile

參數配置 CM Config有定義TR-069參數

1 eRouter Configuration Encodings (202)
2   eRouter TR-069 Management Server (2)
3     EnableCWMP (1)
4     URL (2)
5     Username (3)
6     Password (4)
7     ConnectionRequestUserName (5)
8     ConnectionRequestPassword (6)
9     ACSOverride (7)

其中,EnableSWMP和ACSOverride會影響tr69 agent的行為。 下文詳述

3. DHCP option

利用DHCPv6 Option17或DHCPv4 Option125來配置ACSUrl參數

三. 多種配置參數的優先順序

1. 啟動方式  

  • 通過CLI(start_client)啟動,此時使用的為通過CLI配置的參數,即在NonVol中的參數
  • 自動啟動,條件為CM Config中的EnableCWMP為true,或者得到有效的DHCPv6 Option17或DHCPv4 Option125。此時的參數選擇如下

2. 自動啟動時的參數選擇  

1 // ACSUrl的優先順序:
2 if(ACSOverride==true || (ACSOverride==false && ACSUrl in NonVol=="http://10.10.10.10:8080/acs"))
3   DHCPv6 Option17 > DHCPv4 Option125 > CM Config > NonVol
4 else if(ACSOverride==false and ACSUrl in NonVol!="http://10.10.10.10:8080/acs")
5   DHCPv6 Option17 > DHCPv4 Option125 > NonVol > CM Config
// 其他參數(acsusername,acspassword,connrequsername,connreqpassword)的選擇:
if(ACSOverride==true || (ACSOverride==false && ACSUrl in NonVol=="http://10.10.10.10:8080/acs"))
  CM Config > NonVol
else if(ACSOverride==false && ACSUrl in NonVol!="http://10.10.10.10:8080/acs")
    !NonVol > CM Config

3.ipv4 or ipv6

如果ACSUrl中使用"[]"包含IP地址或者由DHCPv6 Option17得到的ACSUrl,則嘗試首先使用IPv6發起連接.

四. 具體實現

1. TR069 Thread狀態控制    

 1 // tr069thread有以下狀態和消息類型:
 2     enum
 3     {
 4         kStartThread = 0, //目前此msg僅自CLI
 5         kStopThread, //同上
 6         kStartClient,//同上
 7         kStopClient, //同上
 8         kSendInform, //同上
 9         kIpAddressChanged //當相關的ip_stack ip發生變化時,會收到此消息類型      
10     }QCommands;
11 
12     typedef enum
13     {
14         kClientStarting = 0,
15         kClientReady,       // Thread initialized and ready for Core
16         kClientRunning,
17         kClientStopping,
18         kClientStopped,     // Client (core) is stopped, thread running
19         kExitingThread      // Going away completely.
20     } ThreadState;

 進程根據進程狀態和收到的消息類型來控制tr69 client agent,start/stop agent的過程用下圖簡單描述:

2. Socket建立

建立2個socket,acsconnection socket用於連接ACS;acslisten socket用於接收acs的RPC.

 1 // acs connection socket建立
 2 wget.c wget_Connect()
 3              \_www.c www_EstablishConnection()
 4                           \_BcmBfcTr69SocketApi.cpp BfcTr69Api_SocketAcsConnection()
 5                                   \_BcmBfcTr69Thread::SocketAcsConnection()
 6 
 7 // acs listen socket建立
 8 tr69c_main() -> initTask()
 9                   \_informer.c initInformer() -> startACSComm() ->
10                      startACSListener() -> startACScallback()
11                                               \_BfcTr69Api_SocketACSListenSocket()
12                                                    \_BcmBfcTr69Thread::SocketACSListenSocket()

3. 數據模型的建立

數據模型標准:TR-181_Issue-2.pdf,非TR-98 Gataway模型

1)模型對應數據結構

 1 //節點數據結構定義:
 2 REV/rbb_cm_src/Bfc/IpHelpers/TR069/tr69c/inc/tr69cdef.h
 3  typedef union TRxPAttrib {
 4     struct Attrib {
 5     eTRxType    etype:8;
 6     unsigned    slength:16;
 7     unsigned    inhibitActiveNotify:1; /* set to always inhibit change notification: use on counters */
 8     } attrib;
 9     InstanceDesc    *instance;
10  } TRxPAttrib;
11 
12  typedef struct TRxObjNode {
13     const char  *name;//節點或參數名稱
14     TRxPAttrib  paramAttrib;//主要指示參數類型,object/string/bool...
15     TRxSETFUNC  setTRxParam;//object的del,parameter的set    
16     TRxGETFUNC  getTRxParam;//object的add,parameter的get
17     void        *objDetail; //節點類型為object時,對應object的詳細內容(包含parameter和下級object)
18     InstanceDope *instanceDope;
19  } TRxObjNode;

 用以上結構表示以下節點:

 Device.InterfaceStackNumberOfEntries //unsigned類型parameter,readonly
 Device.DeviceSummary //string類型parameter,readonly                                          
 Device.IP. //不可動態添加的object類型,readonly

 Device.IP.IPv4Capable
 Device.IP.IPv4Enable
 ...
 Device.IP.InterfaceNumberOfEntries
 Device.IP.Interface.{i}. //可動態添加的object類型,read-write

 Device.IP.Interface.i.Enable //read-write
 Device.IP.Interface.i.IPv4Enable //read-write
 ...                               

的實例對應為

 1 tr181i2DeviceParams.c
 2   TRxObjNode  tr181i2DeviceDesc[] = {
 3     {InterfaceStackNumberOfEntries,{{tUnsigned,0,0}}, NULL, getInterfaceStackNumEntries,NULL,NULL},
 4     {DeviceSummary,{{tString}}, NULL, getDeviceSummary,NULL,NULL},
 5     {IP,{{tObject,0,0}}, NULL,NULL, ipDesc,NULL},
 6     {NULL}
 7   };
 8 
 9 tr181i2DeviceParams.c  
10   TRxObjNode  ipDesc[] = {
11     {IPv4Capable,{{tBool,0}}, NULL,getIPv4Capable,NULL,NULL},
12     {IPv4Enable,{{tBool,0}}, NULL,getIPv4Enable,NULL,NULL},
13     {InterfaceNumberOfEntries,{{tUnsigned,0,16}}, NULL,getIPInterfaceNumberOfEntries,NULL,NULL},
14     {ActivePortNumberOfEntries,{{tUnsigned,0,16}}, NULL,getIPActivePortNumberOfEntries,NULL,NULL},
15     {Interface,{{tObject,0,0}}, NULL,NULL, ipInterfaceDesc,NULL},
16     {NULL}
17   };
18 
19 tr181i2IPInterfaceParams.c
20   TRxObjNode  ipInterfaceDesc[] = {
21     {instanceIDMASK,{{0}}, deleteIPInterfaceInstance, addIPInterfaceInstance, ipInterfaceInstanceDesc},
22   };//其中,instanceIDMASK為新節點標識,deleteIPInterfaceInstance為delete Interface節點的API名稱
23 
24   TRxObjNode  ipInterfaceInstanceDesc[] = {
25     {Enable,{{tBool,0}},setIPInterfaceEnable,getIPInterfaceEnable,NULL,NULL},
26     {IPv4Enable,{{tBool,0}},setIPInterfaceIPv4Enable,getIPInterfaceIPv4Enable,NULL,NULL},
27     {NULL}//其中,setIPInterfaceIPv4Enable/getIPInterfaceIPv4Enable為Device.IP.Interface.i.IPv4Enable參數的set/get API名稱
28 };

以上,根據  

tr181i2DeviceDesc[]
    \_ipDesc[]
        \_ipInterfaceDesc[]

連接成了

Device.
   \_IP.
      \_Interface.

節點樹形結構 

4. RPC處理過程

1) 入口函數

   對應處理函數入戶為: REV/rbb_cm_src/Bfc/IpHelpers/TR069/tr69c/SOAPParser/RPCState.c runRPC()

目前,支持如下方法:

 1       switch (rpcAction->rpcMethod) {
 2         case rpcGetRPCMethods:
 3             ...
 4         case rpcSetParameterValues:
 5             ...
 6         case rpcGetParameterValues:
 7             ...
 8         case rpcGetParameterNames:
 9             ...
10         case rpcGetParameterAttributes:
11             ...
12         case rpcSetParameterAttributes:
13             ...
14         case rpcAddObject:
15             ...
16         case rpcDeleteObject:
17             ...
18         case rpcReboot:
19             ...
20         case rpcFactoryReset:
21             ...
22 #if DOWNLOAD_SUPPORTED
23         case rpcDownload:
24             ...
25 #endif
26         case rpcInformResponse:
27             ...
28         case rpcTransferCompleteResponse:
29             ...
30         case rpcGetRPCMethodsResponse:
31             ...
32         case rpcFault:
33             ...
View Code

2) parameters對應的get/set api  

仍以上述Device.IP.Interface.節點為例,此節點對應的api位於:

1 REV/rbb_cm_src/Bfc/IpHelpers/TR069/tr69c/bcmBfcIf/BcmBfcTr181i2IPInterfaceHandlers.cpp

注意到實際上這里大部分API沒有實現實際功能。
實際上,TR069的get/set是通過SNMP實現的
,比如WiFi.Radio.{i}.Enable這個參數的get/set:

View Code

3)notify機制

passive notify:(1)在agent啟動或者收到inform response后,遍歷rootDevice的所有節點參數,將attribute為非none的參數的值更新為從對應mib中獲取的值。  change parameter value to mib in some where  (2) 在發送inform之前,再遍歷所有attribute為非none的參數,將其值與mib中獲取的值比較,如有變化,則將其加入到inform發送出去

active notify: 未分析

5. 如何添加object/parameter

以添加如下節點和參數為例:

<object ref="Device.NAT.PortMapping.{i}." requirement="createDelete">
...
<parameter ref="RemoteHost" requirement="readWrite"/>
<parameter ref="ExternalPort" requirement="readWrite"/>
...
</object>

1)添加參數對應數據結構定義

添加tr181i2NATPortMappingParams.c/h,定義節點參數結構:

 1 --- /dev/null
 2 +++ b/REV/rbb_cm_src/Bfc/IpHelpers/TR069/tr69c/standard/tr181i2NATPortMappingParams.c
 3 @@ -0,0 +1,35 @@
 4 +//  Filename:       tr181i2NATPortMappingParams.c
 5 +
 6 +#include "sharedparams.h"
 7 +#include "tr181i2NATPortMappingParams.h"
 8 +
 9 +/* Device.NAT.PortMapping.{i} */
10 +TRXGFUNC(getPortMappingRemoteHost);
11 +TRXSFUNC(setPortMappingRemoteHost);
12 +TRXGFUNC(getPortMappingExternalPort);
13 +TRXSFUNC(setPortMappingExternalPort);
14 +
15 +TRxObjNode  natPortMappingInstanceDesc[] = {
16 +    {RemoteHost,{{tString,0,64}}, setPortMappingRemoteHost, getPortMappingRemoteHost, NULL,NULL},
17 +    {ExternalPort,{{tUnsigned,0,16}}, setPortMappingExternalPort,getPortMappingExternalPort,NULL,NULL},
18 +    {NULL}
19 +};
20 +
21 +/* Device.NAT.PortMapping. */
22 +TRXGFUNC(addPortMappingInstance);
23 +TRXSFUNC(deletePortMappingInstance);
24 +
25 +TRxObjNode  natPortMappingDesc[] = {
26 +    {instanceIDMASK,{{0}}, deletePortMappingInstance, addPortMappingInstance, natPortMappingInstanceDesc},
27 +};
28 diff --git a/REV/rbb_cm_src/Bfc/IpHelpers/TR069/tr69c/standard/tr181i2NATPortMappingParams.h b/REV/rbb_cm_src/Bf
29 new file mode 100755
30 index 0000000..f45b208
31 --- /dev/null
32 +++ b/REV/rbb_cm_src/Bfc/IpHelpers/TR069/tr69c/standard/tr181i2NATPortMappingParams.h
33 @@ -0,0 +1,24 @@
34 +//  Filename:       tr181i2NATPortMappingParams.h
35 +
36 +#ifndef TR181I2_NAT_PORTMAPPING_PARAMS_H
37 +#define TR181I2_NAT_PORTMAPPING_PARAMS_H
38 +
39 +#include "../inc/tr69cdefs.h"
40 +
41 +/* Device.NAT.PortMapping.{i}*/
42 +SVAR(RemoteHost);
43 +SVAR(ExternalPort);
44 +
45 +#endif   // TR181I2_NAT_PARAMS_H
View Code

將增加的PortMapping節點加入Device.NAT.節點下:

 1 +++ b/REV/rbb_cm_src/Bfc/IpHelpers/TR069/tr69c/standard/standardparams.c
 2    #include "../standard/tr181i2NATParams.h"
 3 +  #include "../standard/tr181i2NATPortMappingParams.h"
 4    #include "../standard/tr181i2UPnPParams.h"
 5 
 6 +++ b/REV/rbb_cm_src/Bfc/IpHelpers/TR069/tr69c/standard/tr181i2NATParams.c
 7  #include "tr181i2NATParams.h"
 8  
 9 +extern TRxObjNode natPortMappingDesc[];
10  
11  TRxObjNode  natDesc[] = {
12      {InterfaceSettingNumberOfEntries,{{tUnsigned,0,16}}, NULL,getNATInterfaceSettingNumberOfEntries,NULL,NULL},
13      {PortMappingNumberOfEntries,{{tUnsigned,0,16}}, NULL,getNATPortMappingNumberOfEntries,NULL,NULL},
14 +    {PortMapping,{{tObject,0,0}},NULL,NULL,natPortMappingDesc,NULL},
15      {NULL}
16  };
17  
18 +++ b/REV/rbb_cm_src/Bfc/IpHelpers/TR069/tr69c/standard/tr181i2NATParams.h
19 @@ -60,5 +60,6 @@
20  
21  SVAR(InterfaceSettingNumberOfEntries);
22  SVAR(PortMappingNumberOfEntries);
23 +SVAR(PortMapping);
View Code

2) 添加對應Add/Del object,Get/Set parameter API

NAT.PortMapping.對應的api添加在BcmBfcTr181i2NATPortMappingHandlers.cpp:

 1 +++ b/REV/rbb_cm_src/Bfc/IpHelpers/TR069/tr69c/bcmBfcIf/BcmBfcTr181i2NATPortMappingHandlers.cpp
 2 
 3 + * File Name  : BcmBfcTr181i2NATPortMappingHandlers.c
 4 +
 5 +#include <stdio.h>
 6 +#include <stdlib.h>
 7 +#include <string.h>
 8 +
 9 +#include "bcmtypes.h"
10 +#include "BcmBfcTr69SnmpApi.h"
11 +
12 +// Give these CPP functions C linkage
13 +extern "C" 
14 +{
15 +
16 +#include "../inc/tr69cdefs.h"
17 +#include "BcmBfcTr69Log.h"
18 +
19 +extern char* strdup (char * str);
20 +
21 +TRX_STATUS getPortMappingRemoteHost(char **value)
22 +{
23 +    InstanceDesc *idp;
24 +
25 +    if ((idp = getCurrentInstanceDesc()) == NULL)
26 +        return TRX_ERR;
27 +
28 +    BfcTr69Api_GetFromSnmp(kOID_clabNATPortMappingRemoteHost, idp->hwUserData, value);
29 +
30 +    return TRX_OK;
31 +}
32 +
33 +TRX_STATUS setPortMappingRemoteHost(char *value)
34 +{ 
35 +    InstanceDesc *idp;
36 +    
37 +    if ((idp = getCurrentInstanceDesc()) == NULL)
38 +        return TRX_ERR;
39 +    if(value == NULL)
40 +        return TRX_ERR;
41 +
42 +    if(BfcTr69Api_SnmpSetString(kOID_clabNATPortMappingRemoteHost, idp->hwUserData, value))
43 +        return TRX_OK;
44 +
45 +    return TRX_ERR;
46 +}
47 
48 +TRX_STATUS addPortMappingInstance(char **value)
49 +{
50 +    InstanceDesc *idp;
51 +
52 +    if ((idp = getNewInstanceDesc(getCurrentNode(), getCurrentInstanceDesc(), 0)))//添加新節點
53 +    {
54 +        idp->hwUserData = *value;
55 +        idp->instanceID = BfcTr69Api_SnmpToTr69Index(idp->hwUserData);//此api僅從snmp獲取相應節點id,作用不詳
56 +        return TRX_OK;
57 +    }
58 +    return TRX_ERR;
59 +}
60 +
61 +TRX_STATUS deletePortMappingInstance(char *value)
62 +{
63 +    TRxObjNode *n;
64 +    InstanceDesc *idp;
65 +    int id = atoi(value);
66 +    
67 +    if ((idp = findInstanceDesc(n=getCurrentNode(), id)))
68 +    {
69 +        if (!deleteInstanceDesc(n, id))//刪除節點,但不會通知snmp刪除相應節點
70 +        {
71 +            return TRX_OK;
72 +        }
73 +    }
74 +    return TRX_ERR;
75 +}
76 +
77 +} // extern "C"
View Code

3) 修改BfcTR69.mak

因新增加了tr181i2NATPortMappingParams.c和BcmBfcTr181i2NATPortMappingHandlers.cpp文件,需要修改Makefile:

1 +++ b/REV/rbb_cm_src/Bfc/make/BfcTR69.mak
2 
3  BFC_TR69C_OBJECTS += BcmBfcTr181i2NATHandlers.o
4 +BFC_TR69C_OBJECTS += BcmBfcTr181i2NATPortMappingHandlers.o
5  BFC_TR69C_OBJECTS += BcmBfcTr181i2UPnPHandlers.o
6 
7  BFC_TR69C_OBJECTS += tr181i2NATParams.o
8 +BFC_TR69C_OBJECTS += tr181i2NATPortMappingParams.o
9  BFC_TR69C_OBJECTS += tr181i2UPnPParams.o
View Code

4) 增加SNMP對節點的實現

依照目前的實現,TR069對節點/參數的操作,最終是通過SNMP Agent來達成。如果要新添加的節點/參數還未包含在SNMP Agent中,

需先增加SNMP Agent對此節點/參數的實現,包括mib定義,節點的Add/Del。

. 改善或問題

1. set parameter時的數據有效性檢測

目前的實現中,tr69c/SOAPParser/RPCState.c char *doSetParameterValues(RPCAction *a)

有支持對參數名稱/參數是否可寫做檢查,但對設置值的有效性檢查只有簡單的“對非字符型參數不可設置為空”做了檢查,而未對數據范圍等做檢查。 改進的方法可擴展參數設置SetFunc()的返回類型,在SetFunc中檢查數據有效性,如數據無效,返回9007.

2. 增加/刪除節點與SNMP同步  

從目前已有實現節點中來看,當TR069 Add/Del一個節點時,並沒有通知SNMP Add/Del相應節點。而TR069的Get/Set又依賴於SNMP,理論上,TR069和SNMP的Add/Del節點操作應該需要同步。

3. 節點模板  

構建節點數據結構的形式相對固定,設想可用模板程序根據節點信息自動生成。如從xml -> *.c


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM