只是在工作中的一些总结,必然有不准确的地方,想到啥就写啥了,没什么逻辑。
简介
根据rfc6241 Network Configuration Protocol (NETCONF)介绍,netconf协议提供一种简单的机制,通过该机制网络设备进行管理。协议可以是设备对外提供一套完整并且规范的配置接口,管理者可以通过这些标准接口对设备进行配置操作。
netconf也是采用的服务端、客户端模式,与其他管理协议稍有区别的是,被管理设备是netconf服务端,管理者是netconf客户端(通常叫控制器),控制器与被管理设备建立的是长连接,实际上连接是由ssh、tls、soap或https等建立,当控制器想通过netconf协议对设备进行配置时,首先要建立ssh、tls、soap或https安全连接,这些安全连接建立后,netconf在这些安全连接基础上再建立netconf会话,所以netconf协议的连接是灵活的、安全的。
Netconf协议由yang语言建立配置模型,由xml语言封装数据,通过ssh、tls、soap或者https等通道传递数据。也就是说,设备将yang模型定义好并且在设备上实现,控制器根据设备定义的yang模型,按照yang模型中定义的配置格式,将要下发的数据用xml语言封装,发送给设备,设备对xml格式数据进行解析,更改设备配置。
Netconf基于yang模型进行建模,yang模型限定了格式,但是不限定内容,格式的限定保证控制器与网络设备配置模型的同步,内容的不限定保证了上层控制工具可以对业务的“编程式”下发。
yang模型
yang语言的定义主要在rfc6020和rfc7950中,其中rfc6020是第一版yang模型定义,版本是1,rfc7950解决了rfc6020的一些模糊和缺陷,版本是1.1,它新增加和修改了一些语法的定义,而且其中有一小部分修改是rfc6020向后不兼容的。
rfc对一些通用的网络配置定义了一些标准yang模型,比如ietf-interface.yang、ietf-vxlan.yang、ietf-ip.yang,这些yang模型是持续更新的,他们依赖的yang模型也是持续更新的,不一定最新版本的yang模型就一定是最好的,要根据自己设备的支持情况以及后续可能的基本扩展选择yang模型,像华三、华为厉害的就自己设计和定义。
yang也是一门语言,有自己的语法,具体的语法还是需要详细阅读rfc的定义好好研究,可以将设备按模块分别定义成一个yang文件,将这些yang文件汇聚在一起,就构成了一个设备的配置模型树,每一个yang模型都是这个配置树的一个树枝,每个树枝下有具体的配置项,yang语法相对简单,理解起来比较容易,以下是个人对yang模型中的关键字结合c语言的一些理解,仅供参考。
container
container student {
leaf name{
type string;
}
Leaf age{
type uint32;
}
}
类似于:
struct student {
char *name;
int age;
};
leaf
最底层节点,具体的配置项。
list
可以理解成c语言中实现的一个链表。
list student {
key "name";
leaf name{
type string;
}
leaf age{
type uint32;
}
}
类似于:
list相当于:
struct list {
struct list *next;
};
student相当于:
struct student {
char *name;
int age;
};
list相当于c语言中定义的一个结构体链表,与c语言不同,list关键字两步并成一步,在声明链表的同时直接将链表中每个个体的数据结构也直接定义出来,实际上这个list可以说是抽象的一个list,在控制器中获取到的数据是这样的:
<name>张三<name/>
<age>18<age/>
<name>李四<name/>
<age>22<age/>
这两条数据就抽象成了一个list student,student包含name和age两个属性,也就是说每个student都有name和age。
list下定义了很多leaf,将其中的一个或多个leaf指定为key,key是检索条件,用来确定你要操作list中的哪个节点,key是必须下发的,不然设备不知道下发的这些属性是要配置哪个节点。就好比给某个接口配置ip地址,那么一定要下发接口名,不然不知道给哪个接口配置ip地址。
leaf-list
相当于c预言的字符串数组或者int数组。
leaf-list subjetc {
type string;
}
类似于:
char *subject[SIZE];
但是它与list的区别是leaf-list是同一类型的leaf的一个集合,它是最底层的leaf,没有子节点,而list不是最底层的,它下面是可以继续定义leaf子节点的,举例来说,list就好比一个学生,它有年龄、姓名、学习哪些科目,leaf-list就是“学习哪些科目”,将这些科目统一定义成一个leaf-list,用xml表示就是:
<name>张三<name/>
<age>22<age/>
<subject>语文<subject/>
<subject>数学<subject/>
<name>李四<name/>
<age>22<age/>
<subject>政治<subject/>
<subject>历史<subject/>
<subject>地理<subject/>
简单的说student是一个list,它有name、age、subject三个属性,subject是一个leaf-list,每一个数据都是一个leaf。
augment
扩展是将augment定义的内容扩展到另外一个yang文件的某个结构下。也就是说你可以在a.yang中定义一些新的container、list、leaf,将这些新的定义通过augment关键字扩展到另外一个已经定义好并且开发完不想再修改yang文件的b.yang中。
比如a.yang定义如下,并且代码层面已经开发完成与控制器联调也已经完成:
container student {
leaf name{
type string;
}
leaf age {
type uint32;
}
leaf class {
type string;
}
}
但是,在二期项目中发现不够用,还需要增加一个新的属性,但是由于各种原因不想修改a.yang这个文件,那么可以在b.yang中使用augment(其中std是a的prefix,见rfc中对import的说明):
augment "/std: student " {
leaf evening_study {
type boolean;
}
}
设备加载好a.yang和b.yang后数据模型如下:
container student {
leaf name {
type string;
}
leaf age {
type uint32;
}
leaf class {
type string;
}
leaf evening_study {
type boolean;
}
}
但是,需要注意的是,在配置下发的时候,需要对扩展的属性添加命名空间。比如a.yang的命名空间是“a”,b.yang的命名空间是“b”,那么下发时需要指定命名空间:
<name>张三<name/>
<class>高三< class />
<evening_study nc:namespace=”b”>true< evening_study />
< student />
when
条件定义
augment "/std: student " {
when "class=’高三’“;
leaf evening_study {
type boolean;
}
}
when是对上一层的限定,也就是说当下发的配置中class属性的值只有是“高三”时,student这个大结构体中才有evening_study这条属性,才可以对这条属性进行配置。
mandatory true
限定某个属性必须下发,限定范围同when一样,在哪个属性的大括号内,就限定哪个属性。
config false
限定属性是只能查询不可配置的,限定范围同when一样,在哪个属性的大括号内就限定哪个属性。
xml
xml语言是非常通用的一种语言格式,w3school中有很好的教学示例。rfc6241中定义了netconf用到的xml相关定义,比如editOperationType:
-->
控制器将要下发的配置,根据yang模型的定义,封装成xml格式,下发到netconf设备,具体的格式,随便去华为官网上,搜索netconf,把那款产品的配置手册下载下来,应有尽有!
基于ssh的netconf会话
Netconf会话实际上比较简单,但是在建立netconf会话之前需要为netconf会话准备一条安全的通道,可以是ssh、tls、https、soap。下面以ssh为例简单介绍下netconf会话的建立过程。
主动连接
Netconf服务端主要分为两个部分,一般是两个进程:sshd进程和netconfd进程,sshd进程监听830端口,控制器是ssh客户端,就像使用ssh客户端登录到主机一样,控制器向sshd的830端口发起连接,输入用户名和密码认证成功后,建立ssh tunnel,sshd启动netconf-subsystem进程,subsystem进程是netconf模块提供给sshd进程的,它的内部实现是将ssh tunnel读到的消息直接写给netconf进程,从netconf读到的消息直接写到ssh tunnel,发给控制器,如图所示:
上面这一堆,仅仅是建立了一条安全通道,这时候可以说控制器可以将消息发到netconfd进程,netconfd进程也可以将消息发送到控制器了,将中间的ssh这一堆抽象成一个socket连接即可(所以说这个连接是安全的,因为是ssh安全协议),这时候控制器和netconfd进程互发netconf协议的hello消息,开始正式建立netconf会话,这就是之所以说netconf会话是建立在ssh会话之上的。
这种连接方式,都是由控制器主动向netconf设备发起连接,要求netconf设备必须在公网上,控制器能够找的到被管理设备才行,但是一些终端设备必然是在nat后的,所以netconf还有另外一种连接方式,解决终端在nat时控制器无法找到终端的问题,那就是callhome连接。
callhome连接
rfc8071对callhome有着详细的描述,这里不再赘述,callhome总体流程图如rfc8071所示:
rfc8071的描述很清晰, netconf服务端和客户端必须同时支持callhome,以ssh为例,简单说下callhome方式建立会话过程:
1, 首先设备向控制器发起一个tcp连接,即设备侧创建一个socket,向控制器的callhome服务监听发起connect。这里要分清客户端和服务端,对于callhome协议,设备侧是客户端,控制器是服务端,所以设备向控制器主动先发起tcp连接,当connect成功后,设备是ssh服务端,控制器是ssh客户端。
2, tcp连接建立成功后,控制器利用并且必须只用这个连接,向设备侧发送ssh报文,发起ssh会话。
3, ssh会话建立后,与上面的主动连接就完全相同了,开始建立netconf会话。
以上三步可以看出,其实callhome方式上线与主动建立连接方式上线的唯一区别就是,tcp连接是由谁率先发起的,主动建立连接是控制器主动向netconf设备发起的tcp连接,这时设备要在公网上,不能在nat后,而callhome上线,是netconf设备主动向控制器先发起tcp连接,连接建立后,后面的流程与主动建立连接是一样的,但是,callhome的上线时,netconf设备可以在nat后,只要保证控制器在公网就行了。