聲明:本篇文章來自於某公司Cable Modem產品的文檔資料,源碼來自於博通公司,只提供參考(爲保護產權,本人沒有源碼)。c++
前文曾提到會寫一篇關於博通的tr069,那麼福利來了。福利,福利,福利,重要的事情說三遍!git
若是你正在閱讀博通的相關產品代碼而又苦於沒有文檔參閱,那麼我相信本文將會很是適合你。web
一. TR069的Makefile和源碼
1. 編譯:api
在編譯選項中添加「tr69」, 對應的makefile爲: REV/rbb_cm_src/Bfc/make/BfcTR69.mak 數據結構
2. 相關源碼app
主要有3部分代碼:tr069 client agent代碼,用c實現;TR069Thread和CLI配置代碼,c++實現;client agent與系統間的接口代碼,c++實現。(具體實如今後文講解)socket
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
二. 配置參數
ide
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,默認指定爲3ui
此外,可以使用的命令有以下:
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. 啓動方式
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 ...
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:
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
將增長的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);
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"
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
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