• 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

  • 相关阅读:
    前台隐藏或者看不见
    关于线程安全
    JAVA中的length属性和length()方法和size()方法的区别
    内存泄露和内存溢出
    配置tomcat,只需要启动一次
    ThickBox关闭本页,刷新父页
    Myeclipse断点问题
    解决windows远程(Telnet)最大连接数的问题
    父页刷新的方法
    Jsp页面大小写转换
  • 原文地址:https://www.cnblogs.com/myblesh/p/6292090.html
Copyright © 2020-2023  润新知