NETCONF In-Depth

As mentioned in the previous article “Intro to YANG/NETCONF,” NETCONF is a transport protocol for YANG data models. NETCONF uses SSH over port 830 and operates in a stateful client/server model, in which the network device is the NETCONF server.

NETCONF uses multiple data stores (running, startup, and candidate) but a NETCONF device only has to support a running data store.

NETCONF exclusively uses XML as message encoding. YANG defines the model that the data must follow. NETCONF is the protocol used to transmit YANG data and defines the client/server interaction and RPC messages. XML is used to encode the data in a machine-readable format.

In the following article we will lab NETCONF and explore its interactions. NETCONF isn’t meant to be used via a manual CLI, but this method will allow us to better learn how NETCONF works “under the hood.”

Enabling NETCONF in IOS-XE

To enable NETCONF, you use the following command:

netconf-yang

This will turn on NETCONF and accept SSH connections to port 830.

You can change the SSH port using the following command:

Router(config)#netconf-yang ssh port ?
  <1-65535>  Port number range (default port number is 830)

You can also restrict NETCONF SSH connections to only permit source IPs matching a given ACL:

netconf-yang ssh ipv4 access-list <acl name>

Finally, it is important to note that the candidate datastore is not supported by default on IOS-XE. This makes sense when considering that the IOS-XE CLI has no candidate datastore - changes you make in the CLI are immediately present in the running config. In order to use a candidate datastore with NETCONF you must issue the following command:

netconf-yang feature candidate-datastore

You can verify NETCONF is enabled using the following show commands:

Router#show netconf-yang status     
netconf-yang: enabled
netconf-yang ssh port: 830
netconf-yang candidate-datastore: enabled

Router#show netconf-yang datastores 
Datastore Name             : running
Datastore Name             : candidate

Enabling NETCONF in IOS-XR

To enable NETCONF in IOS-XR use the following commands:

ssh server v2
ssh server netconf
netconf agent tty
netconf-yang agent ssh

! In enable mode
crypto key generate rsa

Just like IOS-XE you can change the SSH port to something other than the default (830).

RP/0/0/CPU0:ios(config)#ssh server netconf port ?
  <1-65535>  port number on which ssh service to be started for netconf

You can also apply an ACL:

RP/0/0/CPU0:ios(config)#ssh server netconf ipv4 access-list ?
  WORD  Name of IPv4 access list - Max 32 characters

The candidate-datastore feature is not configurable like in IOS-XE, I believe because IOS-XR natively supports a candidate database by default.

In IOS-XR I don’t see a way to verify whether NETCONF is configured. You can only see currently active client sessions:

RP/0/0/CPU0:ios#show netconf-yang clients 
Sun Oct 23 15:01:27.935 UTC
Netconf clients
client session ID|     NC version|    client connect time|        last OP time|        last OP type|    <lock>|
       3593234057|        unknown|         0d  0h  4m 12s|                    |                    |        No|

Manually executing a NETCONF session

For the remainder of the article I will use a CSR1000v and Ubuntu host directly connected to the router.

Now that we have enabeld NETCONF on our device, we can manually SSH to port 830 to verify that it is working. On a linux machine we can run the following command:

ssh -p 830 username@10.0.0.1 -s netconf

The -s option specifies an SSH subsystem, and -p specifies the port. If you are using a lab device, make sure a local username/password is configured with privilege level 15.

When you run this, you will get back a flood of capabilities from the network device. This is part of the NETCONF Hello process.

cisco@ubuntu:~$ ssh -p 830 cisco@10.0.0.1 -s netconf
cisco@10.0.0.1's password:
<?xml version="1.0" encoding="UTF-8"?>
<hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<capabilities>
<capability>urn:ietf:params:netconf:base:1.0</capability>
...snip...
<capability>http://tail-f.com/ns/netconf/actions/1.0</capability>
<capability>http://cisco.com/ns/cisco-xe-ietf-ip-deviation?module=cisco-xe-ietf-ip-deviation&amp;revision=2016-08-10</capability>
...snip...
<capability>http://cisco.com/ns/yang/Cisco-IOS-XE-aaa?module=Cisco-IOS-XE-aaa&amp;revision=2020-07-02</capability>
...snip...
<capability>http://openconfig.net/yang/aaa?module=openconfig-aaa&amp;revision=2017-09-18</capability>
...snip...
<capability>urn:ietf:params:xml:ns:netconf:base:1.0?module=ietf-netconf&amp;revision=2011-06-01&amp;features=writable-running,rollback-on-error,validate,xpath</capability>
...snip...
</capabilities>
<session-id>29</session-id></hello>]]>]]>

We can see that this CSR1000v supports IETF, OpenConfig, and Native (Cisco) YANG data models. Notice there is a session ID given to us. Because NETCONF is stateful, state is kept for each session and each session is marked with a session ID. The ]]>]]> characers mark the end of message. This is kind of like an “OVER” signal in NETCONF.

In response the client needs to send a Hello with its own supported capabilities. We can manually paste the following XML:

<?xml version="1.0" encoding="UTF-8"?>
   <hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
     <capabilities>
       <capability>urn:ietf:params:netconf:base:1.0</capability>
     </capabilities>
   </hello>]]>]]>

The server (CSR1000v) will not respond, but the session is still up. Next we can run any RPC request, such as getting information about an interface.

<?xml version="1.0" encoding="UTF-8"?>
<rpc message-id="1111" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
  <get>
   <filter>
    <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
     <interface>
       <name>GigabitEthernet1</name>
     </interface>
    </interfaces>
   </filter>
  </get>
</rpc>]]>]]>

The server sends a response which includes the message-id of 1111. This was a random number that I picked. By having the server echo the message-id back, a client can keep track of multiple RPC requests.

<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="1111">
 <data>
  <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
   <interface>
    <name>GigabitEthernet1</name>
    <type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:ethernetCsmacd</type>
    <enabled>true</enabled>
    <ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip">
     <address>
      <ip>10.0.0.1</ip>
      <netmask>255.255.255.252</netmask>
     </address>
    </ipv4>
    <ipv6 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"></ipv6>
   </interface>
  </interfaces>
 </data></rpc-reply>]]>]]>

Here we can see the IPv4 and IPv6 information for the interface returned from the router.

Locking the configuration

Let’s see what happens when we lock the running config. This is common practice when using NETCONF. You normally want to lock the running and candidate config stores at the beginning of the session, make the changes, commit, and then unlock the config stores. This prevents other changes from happening while you make your own changes.

In the CLI session we can paste the following RPC call to lock the running config:

<rpc message-id="101"
          xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
       <lock>
         <target>
           <running/>
         </target>
       </lock>
</rpc>

The server (CSR1000v) responds with an OK:

<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="101"><ok/></rpc-reply>]]>]]>

In the CSR1000v console, if we try to go into global config mode we are rejected because a NETCONF user has locked the running config.

Router#conf t
Configuration mode is locked by process '446' user 'NETCONF' from terminal '64'. Please try later.

Additionally, if we open a second SSH window to our linux host and open a second NETCONF session to the CSR1000v, we will be prevented from making any changes. Notice that when I send an edit-config RPC call I get back an error that the datastore is locked:

<?xml version="1.0" encoding="UTF-8"?>
<rpc message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
 <edit-config>
  <target>
   <running/>
  </target>
  <config>
   <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
    <interface>
     <name>GigabitEthernet2</name>
     <type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:ethernetCsmacd</type>
     <enabled>true</enabled>
     <ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip">
      <address>
       <ip>10.2.2.1</ip>
       <netmask>255.255.255.252</netmask>
      </address>
     </ipv4>
     <ipv6 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"></ipv6>
    </interface>
   </interfaces>
  </config>
 </edit-config>
</rpc>]]>]]>

<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="101"><rpc-error>
<error-type>application</error-type>
<error-tag>in-use</error-tag>
<error-severity>error</error-severity>
<error-app-tag>config-locked</error-app-tag>
<error-info><session-id>34</session-id>
</error-info>
</rpc-error>
</rpc-reply>]]>]]>

Above I tried to configure Gi2 with 10.2.2.1/30, however the CSR1000v rejected by edit-config attempt, telling me that the running-config datastore is currently locked by session ID 34.

If I hit ctrl+C in the initial SSH window, this releases the lock and my configuration attempt on my second window succeeds when I attempt it again:

<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="101"><ok/></rpc-reply>]]>]]>

Router#show run int gi2
Building configuration...

Current configuration : 119 bytes
!
interface GigabitEthernet2
 ip address 10.2.2.1 255.255.255.252
 negotiation auto
 no mop enabled
 no mop sysid
end

Other Erorr Messages

What happens if I try to edit an interface like above, but for an interface that does not exist on the device, such as Gi10?

The CSR1000v will send back an “invalid-value” error.

<?xml version="1.0" encoding="UTF-8"?>
<rpc message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
 <edit-config>
  <target>
   <running/>
  </target>
  <config>
   <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
    <interface>
     <name>GigabitEthernet10</name>
     <type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:ethernetCsmacd</type>
     <enabled>true</enabled>
     <ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip">
      <address>
       <ip>10.10.10.1</ip>
       <netmask>255.255.255.252</netmask>
      </address>
     </ipv4>
     <ipv6 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"></ipv6>
    </interface>
   </interfaces>
  </config>
 </edit-config>
</rpc>]]>]]>

<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="101"><rpc-error>
<error-type>application</error-type>
<error-tag>invalid-value</error-tag>
<error-severity>error</error-severity>
<error-message xml:lang="en">inconsistent value: Device refused one or more commands</error-message><error-info><severity xmlns='http://cisco.com/yang/cisco-ia'>error_cli</severity>
<detail xmlns='http://cisco.com/yang/cisco-ia'>
  <bad-cli>
    <bad-command>interface GigabitEthernet10</bad-command>
    <error-location>11</error-location>
    <parser-response></parser-response>
    <parser-context>interface GigabitEthernet10</parser-context>
  </bad-cli>
</detail>
</error-info>
</rpc-error>
</rpc-reply>]]>]]>

Above we see a an error tag of “invalid-value” and in the detail we see a “bad-command” because int Gi10 is rejected. In general “invalid-value” means that the content is completely valid based on the YANG model itself, but the device couldn’t successfully apply the configuration. Interestingly, in the CLI we see a CLI rollback warning because the RPC edit-config attempt failed and had to be rolled back.

Router#
*Oct 23 14:25:25.716: %SYS-5-CONFIG_P: Configured programmatically by process iosp_vty_100001_dmiauthd_fd_185 from console as NETCONF on vty63
*Oct 23 14:25:25.718: %DMI-4-CLI_ROLLBACK_WARN: R0/0: dmiauthd: CLI rollback warning: tid (83): interface GigabitEthernet10.

What happens if we try to send an RPC request for a data model that isn’t supported by the device?

Normally a software NETCONF client can know what models are supported by the initial NETCONF handshake. But we as a human might attempt to access a data model that doesn’t exist.

<?xml version="1.0" encoding="UTF-8"?>
<rpc message-id="1111" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
  <get>
   <filter>
    <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces-new">
     <interface>
      <name>GigabitEthernet2</name>
      </interface>
     </interfaces>
    </filter>
  </get>
</rpc>]]>]]>


<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="1111">
<data></data>
</rpc-reply>]]>]]>

Above, the RPC request is completely valid except I changed the model new to ietf-interfaces-new which does not exist. You would expect an error message such as “data model not supported” but the device simply replies with empty data, as denoted by <data></data>.

Likewise, an RPC call that uses the correct model name, but uses a leaf that does not adhere to the model returns the same empty data response. Below, I use “GigabitEthernet” as a leaf instead of as the name of the interface, so the data does not meet the YANG model definition.

<?xml version="1.0" encoding="UTF-8"?>
<rpc message-id="1111" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
  <get>
   <filter>
    <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
     <interface>
      <GigabitEthernet>
       <name>2</name>
      </GigabitEthernet>
      </interface>
     </interfaces>
    </filter>
  </get>
</rpc>]]>]]>

<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="1111">
<data></data>
</rpc-reply>]]>]]>

I believe the idea behind the NETCONF server not explaining that the data model is invalid or not supported in the error message, is that this should be handled by the client. The client should know all data models supported by the server, and should also be able to form valid data based on these models without verification from the server itself.

Further Reading/Practice

https://developer.cisco.com/learning/tracks/netprog-eng/intro-device-level-interfaces/

https://www.cisco.com/c/en/us/support/docs/storage-networking/management/200933-YANG-NETCONF-Configuration-Validation.html

https://www.cisco.com/c/en/us/td/docs/ios-xml/ios/prog/configuration/175/b_175_programmability_cg/m_175_prog_yang_netconf.html

https://www.cisco.com/c/en/us/td/docs/ios-xml/ios/cns/configuration/xe-16-7/cns-xe-16-7-book/cns-netconf.pdf

https://null.53bits.co.uk/index.php?page=netconf-on-ios-xr

Last updated