IETF-OPSAWG-WG / lxnm

L3 VPN Yang Model
8 stars 11 forks source link

Tom Petch Comments #343

Closed sbarguil closed 3 years ago

sbarguil commented 3 years ago

On 16/07/2021 17:05, The IESG wrote:

The IESG has received a request from the Operations and Management Area Working Group WG (opsawg) to consider the following document: - 'A Layer 3 VPN Network YANG Model'

as Proposed Standard The IESG plans to make a decision in the next few weeks, and solicits final comments on this action. Please send substantive comments to the last-call@ietf.org mailing lists by 2021-08-06. Exceptionally, comments may be sent to iesg@ietf.org instead. In either case, please retain the beginning of the Subject line to allow automated sorting.

This I-D includes a YANG module which provides configuration for BGP, OSPF, IS-IS, PIM, IGMP, VRRP, RIP, BFD, MLD, NTP, DHCP and such like and, as such, overlaps substantially with existing RFC and I-D. It does not build on that work, rather it takes a different direction, which seems likely to induce misunderstandings. At WG LC, the shepherd suggested that with so much coverage of VPN, then IETF LC should be flagged to the BESS WG so that they could review it; I see no sign of that having happened. By the same token, I think that that suggestion could apply to several other WG. Thus the coverage of BGP here seems at variance with much if not most of bgp-model.

There is often a need in YANG to differentiate between this version, that version or both, which is modelled as a base YANG identity from which more specific ones are derived, using YANG's function 'derived-from-or-self' when a combination is wanted. Not here. Here there are three identity for e.g. ipv4; one for ipv4 alone, one for ipv6 alone and 'dualstack' for when both versions are present. Thus 'ipv6' has a different meaning to any other YANG module I know, meaning 'ipv6-without-ipv4', and a test for ipv6 becomes

     when "../address-family = 'vpn-common:ipv6' or "
               + "'vpn-common:dual-stack'" {
while a test for ipv4 is
     when "../protocol-type = 'host' and "
              + "../address-family = 'vpn-common:ipv4' or "
              + "'vpn-common:dual-stack'";

The concept of dualstack will be familiar to those implementing hosts with IPv6 support but is not something I have ever seen elsewhere and may be unfamiliar to most, at least with this meaning as opposed to support of other related protocols perhaps outside ther remit of the IETF.

Likewise, this use of address family, with different semantics, is inconsistent with its use in such as BGP and RFC8349 (inter alia). Unwise IMHO.

I see this use of dualstack and address family as major flaws, show stoppers.

For OSPF, other documents derive ospfv2 and ospfv3 from a base ospf identity and use that to specify ospfv2 or ospfv3, the natural way to model the OSPF protocols; here, OSPF is differentiated by 'address-family' i.e. by ipv4 or ipv6 or dualstack, not something I have ever seen with OSPF. What does it mean to activate IPv4, as specified here? OSPFv2 or OPSFv3 or both? If OSPFv3 is used to convey a IPv4 prefix, is that ospf/ipv4 or ospf/ipv6 or ospf/dualstack? The security options, in 'container keying-material' are at variance with those in draft-ietf-ospf-yang in 'container authentication', similar but different. Therein, 'case ipsec' is OPSFv3 only but there is no way to specify that here with its concept of an OSPF address family.

The I-D defines a type for 'area-address' but this is not the one used by OSPF protocols.

RIP, likewise, is diffentiated by address-family rather than by version. 'leaf flush-interval' here defaults to 180, which is invalid as it is not greater than the 'invalid-interval'; it is 240 in RFC8695.

With BGP it seems more accurate to say that most objects here differ from their potential equivalents in bgp-yang, either in identifier, in description or in semantics. BGP, RFC4271, defines holdtime and keepalive - not here, where it is hold-time (with different semantics to RFC4271) and keepalive while 'address-family' has a narrower meaning than would be expected for BGP.

leaf hold-time {
         type uint16 {
           range "0 | 3..65535";
         }
         units "seconds";
         default "90";
         description
           "Time interval (in seconds) for the HoldTimer established
            with the peer.  When read as operational data (ro), the
            value of this object is calculated by this BGP speaker,
            using the smaller of the values in hold-time that was
            configured (rw) in the running datastore and the Hold Time
            received in the OPEN message.

            This value must be at least three seconds
            if it is not zero (0).

            If the Hold Timer has not been established
            with the peer this object MUST have a value
            of zero (0).

            If the configured value of hold-time object was
            a value of (0), then when read this object MUST have a
            value of (0) also.";
         reference
           "RFC 4271, Section 4.2.
            RFC 4271, Section 10.";
       }
       leaf keepalive {
         type uint16 {
           range "0..21845";
         }
         units "seconds";
         description
           "When used as a configuration (rw) value, this Time interval
           (in seconds) for the KeepAlive timer configured for this BGP
            speaker with this peer.  A reasonable maximum value for this
            timer would be one-third of the configured hold-time.

            In the absence of explicit configuration of the keepalive
            value, operationally it SHOULD have a value of one-third of
            the negotiated hold-time.

            If the value of this object is zero (0), no periodic
            KEEPALIVE messages are sent to the peer after the BGP
            connection has been established.

            The actual time interval for the KEEPALIVE messages is
            indicated by operational value of keepalive.";
         reference
           "RFC 4271, Section 4.4.
            RFC 4271, Section 10.";
       }

There appears to be no way to configure the BGP identifier (for me, the first step).

leaf identifier {
             type yang:dotted-quad;
             description
               "BGP Identifier of the router - an unsigned 32-bit,
                non-zero integer that should be unique within an AS.
                The value of the BGP Identifier for a BGP speaker is
                determined upon startup and is the same for every local
                interface and BGP peer.";
             reference
               "RFC 6286: AS-Wide Unique BGP ID for BGP-4. Section 2.1";
           }

The local address does not allow configuration of the port. 'neighbor' configuration does not allow for identifier or port.

             leaf local-address {
               type inet:ip-address;
               config false;
               description
                 "The local IP address of this entry's BGP connection.";
             }

             leaf local-port {
               type inet:port-number;
               config false;
               description
                 "The local port for the TCP connection between
                  the BGP peers.";
             }

             leaf remote-address {
               type inet:ip-address;
               description
                 "The remote IP address of this entry's BGP peer.";
             }

             leaf remote-port {
               type inet:port-number;
               config false;
               description
                 "The remote port for the TCP connection
                  between the BGP peers.  Note that the
                  objects local-addr, local-port, remote-addr, and
                  reemote-port provide the appropriate
                  reference to the standard MIB TCP
                  connection table.";
             }

Here 'multihop' is configured as

     leaf multihop {
       type uint8;
          description
            "Describes the number of IP hops allowed
            between a given BGP neighbor and the PE.";
where draft-idr-bgp model has
    leaf multihop-ttl {
       type uint8;
          description
               "Time-to-live value to use when packets are sent to the
                 referenced group or neighbors and ebgp-multihop is
enabled";

which I find clearer.

The limit on prefixes here is specified in container bgp-max-prefix as "It allows to control how many prefixes can be received from a neighbor." while draft-idr-bgp-model has "Maximum number of prefixes that will be accepted from the neighbour" which I find more precise. The point where the limit is applied matters; currently, there is a discussion in the IDR WG about specifying two different limits, one for inbound and one for outbound, a change which seems likely to be needed.

More significantly, here restart-interval is

    leaf restart-interval {
      type uint16;
       units "minutes";
while bgp-model has
          leaf restart-timer {
            type uint32;
            units "seconds";
a difference in scale. Here the action is
     leaf warning-threshold
         type decimal64 {
            fraction-digits 5;
              range "0..100";    }
                           units "percent";

whereas the bgp model has

          leaf shutdown-threshold-pct {
            type rt-types:percentage;

a difference in approach

leaf prepend-global-as controls the prepend of a second AS, the one for the node in addition to the one for VPN network access level.

bgp-model has

       container set-as-path-prepend {
         description "Action to prepend local AS number to the AS-path a
            specified number of times";

which is, well, different.

Here the specification of the holdtime only caters for its value once the session is established; as RFC4271 says, it may be set to a larger value during session establishment

Compared to bgp-model there is an additional security option,

      case explicit {

which I do not understand but would appear to have a key of type string which sounds like an unmentioned security vulnerability.

Away from BGP, 'router-id' is imported from 'module ietf-routing-types' with the appropriate semantics and there is a definition of router-id as a 32 bit number in s.7.5 but then a second router-id leaf is created with the wrong semantics. The need for a second concept, an addressible one (which 'router-id' is not), has arisen before and draft-ietf-ospf-yang defines such an object and gave it a different name, 'te-rid', TE being one, but not the only, use thereof. Different semantics call for a different name; that name seems suitable for all uses if that is what is wanted here. (The I S I S yang module uses te-rid as well as ipv4-router-id, ipv6-router-id).

When a range is specified, in YANG, which may be a single address, that case is commonly modeled by specifying 'start' = 'end'. Here it is modelled with only the start-address with the presence of the end-address implying a range but with no YANG constraint on the value of end-address vis-a-vis start address; that seems unnecessarily flexible.

MD5 is part of the security options but is not mentioned in the Security Considerations; for the NTP yang data model, it was flagged as deprecated while the TLS WG is seeking to eliminate its use.

Other minor comments

typedef area-address { ... description "This type defines the area address format."; Well, worth adding this is not for OSPF

Where 'start' and 'end' are specified, it is customary to impose a YANG constraint of 'start>end' or 'start>=end'

     leaf dr-priority {

... Numerically larger DR priority allows a node to be elected as a DR."; Well, any value allows election; perhaps, as draft-ietf-pim-yang, puts it "A larger value has a higher priority over a smaller value.";

     leaf update-interval {

... "Indicates the RIP update time. That is, the amount of time for which routing updates are sent."; Perhaps "Interval at which RIPv2 or RIPng updates are sent."; as per rtgwg-yang-rip

                 leaf signalling-type {
                     enum ldp {

... In this case, an IGP routing protocol must also be activated."; Probably 'MUST' and likewise for 'enum bgp'

             container service {
               leaf mtu {

... If CsC is enabled, the requested MTU will refer to the MPLS MTU and not to the IP MTU."; MPLS does not really do MTU; mpls-base-yang uses 'leaf maximum-labeled-packet' after a discussion about this issue.

CsC should appear in terminology.

boucadair commented 3 years ago

Hi Tom,

Thank you for the review.

You did already raised the comment about ipv4/ipv6/dual-stack identities during the WGLC. We clarified that at that time. I don't see any new element in your comment on this particular point, but I understand that you want to increase awareness so that others may look at this. This is appreciated.

As mentioned in the IETF LC of the vpn-common, this identity is used to characterize the connectivity type provided by the VPN service. We are not manipulating AFIs/SAFIs as those will be derived when translating into specific device modules (which we aren’t as the L3NM is a network model (as per RFC8969)) from the VPN service type and the address family. We will add a note about this.

Selecting the routing protocol version or whether two sessions or one single session (BGP case, for example) is deployment-specific. For example, when dual-stack is indicated in the L3NM, whether both OSPFv2 and OPSFv3 instances will be activated or only OSPFv3 is used (i.e., also announce IPv4 routes as per RFC5838) is a function of the underlying device capabilities and local guidelines.

Also, "dual-stack" identity was added to rationalize the module and factorize items that are applicable for both IPv4 and IPv6. With this value, we can have more compact network service definition. For example, with the current specification we can have something such as (only an excerpt from the full body):

          "address-family": [
            {
              "address-family": "vpn-common:dual-stack",
              "vpn-targets": {
                "vpn-target": [
                  {
                    "id": "1",
                    "route-targets": [
                      "0:65550:1"
                    ],
                    "route-target-type": "both"
                  }
                ]
              }
            }
          ]

While the following will be needed to convey the same configuration if dual-stack identity is not supported.

          "address-family": [
            {
              "address-family": "vpn-common:ipv4",
              "vpn-targets": {
                "vpn-target": [
                  {
                    "id": "1",
                    "route-targets": [
                      "0:65550:1"
                    ],
                    "route-target-type": "both"
                  }
                ]
              }
            },
            {
              "address-family": "vpn-common:ipv6",
              "vpn-targets": {
                "vpn-target": [
                  {
                    "id": "1",
                    "route-targets": [
                      "0:65550:1"
                    ],
                    "route-target-type": "both"
                  }
                ]
              }
            }
          ]

The dual-stack identity is also useful when a policy is to be set for both IPv4 and IPv6 as a set. For example, the current module can control the maximum number of routes that will apply independently of the address family. We don't require a 50%-50% split between the two as soon as the sum does not exceed the indicated value. As such, we can indicate something such as.

          "address-family": [
            {
              "address-family": "vpn-common:dual-stack",
              "vpn-targets": {
                "vpn-target": [
                  {
                    "id": "1",
                    "route-targets": [
                      "0:65550:1"
                    ],
                    "route-target-type": "both"
                  }
                ]
              },
              "maximum-routes": [
                {
                  "protocol": "vpn-common:any",
                  "maximum-routes": 100
                }
              ]
            }
          ]

Absent that identity, only strict maximums are possible such as in this example:

          "address-family": [
            {
              "address-family": "vpn-common:ipv4",
              "vpn-targets": {
                "vpn-target": [
                  {
                    "id": "1",
                    "route-targets": [
                      "0:65550:1"
                    ],
                    "route-target-type": "both"
                  }
                ]
              },
              "maximum-routes": [
                {
                  "protocol": "vpn-common:any",
                  "maximum-routes": 50
                }
              ]
            },
            {
              "address-family": "vpn-common:ipv6",
              "vpn-targets": {
                "vpn-target": [
                  {
                    "id": "1",
                    "route-targets": [
                      "0:65550:1"
                    ],
                    "route-target-type": "both"
                  }
                ]
              },
              "maximum-routes": [
                {
                  "protocol": "vpn-common:any",
                  "maximum-routes": 50
                }
              ]
            }
          ]

The same reasoning applies for various data nodes in the module for which address family is indicated.

Otherwise, please see inline.

Cheers, Med

-----Message d'origine----- De : tom petch [mailto:daedulus@btconnect.com] Envoyé : jeudi 29 juillet 2021 13:13 À : last-call@ietf.org Cc : adrian@olddog.co.uk; opsawg-chairs@ietf.org; draft-ietf-opsawg-l3sm-l3nm@ietf.org Objet : Re: Last Call: (A Layer 3 VPN Network YANG Model) to Proposed Standard

On 16/07/2021 17:05, The IESG wrote:

The IESG has received a request from the Operations and Management Area Working Group WG (opsawg) to consider the following document: - 'A Layer 3 VPN Network YANG Model'

as Proposed Standard The IESG plans to make a decision in the next few weeks, and solicits final comments on this action. Please send substantive comments to the last-call@ietf.org mailing lists by 2021-08-06. Exceptionally, comments may be sent to iesg@ietf.org instead. In either case, please retain the beginning of the Subject line to allow automated sorting.

This I-D includes a YANG module which provides configuration for BGP, OSPF, IS-IS, PIM, IGMP, VRRP, RIP, BFD, MLD, NTP, DHCP and such like and, as such, overlaps substantially with existing RFC and I-D. It does not build on that work, rather it takes a different direction, which seems likely to induce misunderstandings. At WG LC, the shepherd suggested that with so much coverage of VPN, then IETF LC should be flagged to the BESS WG so that they could review it; I see no sign of that having happened. By the same token, I think that that suggestion could apply to several other WG. Thus the coverage of BGP here seems at variance with much if not most of bgp-model.

There is often a need in YANG to differentiate between this version, that version or both, which is modelled as a base YANG identity from which more specific ones are derived, using YANG's function 'derived-from-or-self' when a combination is wanted. Not here. Here there are three identity for e.g. ipv4; one for ipv4 alone, one for ipv6 alone and 'dualstack' for when both versions are present. Thus 'ipv6' has a different meaning to any other YANG module I know, meaning 'ipv6-without-ipv4', and a test for ipv6 becomes when "../address-family = 'vpn-common:ipv6' or "

The concept of dualstack will be familiar to those implementing hosts with IPv6 support but is not something I have ever seen elsewhere and may be unfamiliar to most, at least with this meaning as opposed to support of other related protocols perhaps outside ther remit of the IETF.

Likewise, this use of address family, with different semantics, is inconsistent with its use in such as BGP and RFC8349 (inter alia). Unwise IMHO.

I see this use of dualstack and address family as major flaws, show stoppers.

For OSPF, other documents derive ospfv2 and ospfv3 from a base ospf identity and use that to specify ospfv2 or ospfv3, the natural way to model the OSPF protocols; here, OSPF is differentiated by 'address-family' i.e. by ipv4 or ipv6 or dualstack, not something I have ever seen with OSPF. What does it mean to activate IPv4, as specified here? OSPFv2 or OPSFv3 or both? If OSPFv3 is used to convey a IPv4 prefix, is that ospf/ipv4 or ospf/ipv6 or ospf/dualstack? The security options, in 'container keying-material' are at variance with those in draft-ietf-ospf-yang in 'container authentication', similar but different. Therein, 'case ipsec' is OPSFv3 only but there is no way to specify that here with its concept of an OSPF address family.

The I-D defines a type for 'area-address' but this is not the one used by OSPF protocols.

**[[Med]] Sure, the document does not say that. FWIW, here is what we have for OSPF:

    |     +--rw ospf {vpn-common:rtg-ospf}?
    |     |  +--rw address-family?   identityref
    |     |  +--rw area-id           yang:dotted-quad

That type is used for isis:

    |     +--rw isis {vpn-common:rtg-isis}?
    |     |  +--rw address-family?   identityref
    |     |  +--rw area-address      area-address**

RIP, likewise, is diffentiated by address-family rather than by version. 'leaf flush-interval' here defaults to 180, which is invalid as it is not greater than the 'invalid-interval'; it is 240 in RFC8695.

[[Med]] Good catch. Fixed. Thanks.

With BGP it seems more accurate to say that most objects here differ from their potential equivalents in bgp-yang, either in identifier, in description or in semantics. BGP, RFC4271, defines holdtime and keepalive - not here, where it is hold-time (with different semantics to RFC4271)

[[Med]] This is the same name used in draft-ietf-idr-bgp-model:

         |  |  +--rw timers
         |  |  |  +--rw connect-retry-interval?             uint16
         |  |  |  +--rw hold-time?                          uint16

and keepalive while 'address-family' has a narrower meaning than would be expected for BGP. There appears to be no way to configure the BGP identifier (for me, the first step).

[[Med]] Why would such identifier be required in a network model? Of course, it is completely fine to have in the device module.

The local address does not allow configuration of the port.

[[Med]] We are assuming that the default port is used. We didn't receive any request to control that as part of the service/network module. Please note that even the current BGP device module does not allow for such as you can see in this subtree:

         +--rw neighbors
         |  +--rw neighbor* [remote-address]
         |  |  +--ro local-address?               inet:ip-address
         |  |  +--ro local-port?                  inet:port-number
         |  |  +--rw remote-address               inet:ip-address
         |  |  +--ro remote-port?                 inet:port-number
         |  |  +--ro peer-type?                   bt:peer-type 

'neighbor' configuration does not allow for identifier or port.

[[Med]] Idem as above.

Here 'multihop' is configured as leaf multihop { type uint8; description "Describes the number of IP hops allowed between a given BGP neighbor and the PE."; where draft-idr-bgp model has leaf multihop-ttl { type uint8; description "Time-to-live value to use when packets are sent to the referenced group or neighbors and ebgp-multihop is enabled"; which I find clearer.

[[Med]] We are reasoning in term of IP hops, not decremented "TIME to leave", as indicated in this part from RFC4271

      3) When sending a message to an external peer X, and the peer is
`         multiple IP hops away from the speaker (aka "multihop EBGP"):    `  

The limit on prefixes here is specified in container bgp-max-prefix as "It allows to control how many prefixes can be received from a neighbor." while draft-idr-bgp-model has "Maximum number of prefixes that will be accepted from the neighbour" which I find more precise.

[[Med]] The description also says " Indicates the maximum number of BGP prefixes allowed in the BGP session." That's said, will see how to tweak the text for better clarity.

The point where the limit is applied matters; currently, there is a discussion in the IDR WG about specifying two different limits, one for inbound and one for outbound, a change which seems likely to be needed.

More significantly, here restart-interval is leaf restart-interval { type uint16; units "minutes"; while bgp-model has leaf restart-timer { type uint32; units "seconds"; a difference in scale. Here the action is leaf warning-threshold type decimal64 { fraction-digits 5; range "0..100"; } units "percent"; whereas the bgp model has leaf shutdown-threshold-pct { type rt-types:percentage; a difference in approach

[[Med]] The requirement we had is to customize the behaviour as agreed/set with the customer. I prefer to keep what we have in the current spec.

leaf prepend-global-as controls the prepend of a second AS, the one for the node in addition to the one for VPN network access level.

bgp-model has container set-as-path-prepend { description "Action to prepend local AS number to the AS-path a specified number of times"; which is, well, different.

[[Med]] Sure, but these are not the same features.

Here the specification of the holdtime only caters for its value once the session is established; as RFC4271 says, it may be set to a larger value during session establishment

Compared to bgp-model there is an additional security option, case explicit { which I do not understand but would appear to have a key of type string which sounds like an unmentioned security vulnerability.

**[[Med]] Hmm. This is covered as we do have the following:

Several data nodes ('bgp', 'ospf', 'isis', 'rip', and 'bfd') rely upon [RFC8177] for authentication purposes. Therefore, this module inherits the security considerations discussed in Section 5 of [RFC8177].

And RFC8177 says:

When configured, the key strings can be encrypted using the AES Key Wrap algorithm [AES-KEY-WRAP]. The AES key-encryption key (KEK) is not included in the YANG model and must be set or derived independent of key chain configuration. When AES key encryption is used, the hex-key-string feature is also required since the encrypted keys will contain characters that are not representable in the YANG string built-in type [YANG-1.1]. It is RECOMMENDED that key strings be encrypted using AES key encryption to prevent key chains from being retrieved and stored with the key strings in cleartext. This recommendation is independent of the access protection that is availed from the NETCONF access control model (NACM) [NETCONF-ACM].**

Away from BGP, 'router-id' is imported from 'module ietf-routing-types' with the appropriate semantics and there is a definition of router-id as a 32 bit number in s.7.5 but then a second router-id leaf is created with the wrong semantics.

[[Med]] This is to cover when/if router-ids are provided as IPv6 addresses.

The need for a second concept, an addressible one (which 'router-id' is not), has arisen before and draft-ietf-ospf-yang defines such an object and gave it a different name, 'te-rid', TE being one, but not the only, use thereof. Different semantics call for a different name; that name seems suitable for all uses if that is what is wanted here. (The I S I S yang module uses te-rid as well as ipv4-router-id, ipv6-router-id).

When a range is specified, in YANG, which may be a single address, that case is commonly modeled by specifying 'start' = 'end'. Here it is modelled with only the start-address with the presence of the end-address implying a range but with no YANG constraint on the value of end-address vis-a-vis start address; that seems unnecessarily flexible.

[[Med]] We are inhering that from RFC8299.

MD5 is part of the security options but is not mentioned in the Security Considerations; for the NTP yang data model, it was flagged as deprecated while the TLS WG is seeking to eliminate its use.

[[Med]] Already added a new sentence to cover. Please refer to the secdir list.

Other minor comments

typedef area-address { ... description "This type defines the area address format."; Well, worth adding this is not for OSPF

Where 'start' and 'end' are specified, it is customary to impose a YANG constraint of 'start>end' or 'start>=end'

[[Med]] Noted. Will check and fix if it makes sense. Thanks.

     leaf dr-priority {

... Numerically larger DR priority allows a node to be elected as a DR."; Well, any value allows election; perhaps, as draft-ietf-pim-yang, puts it "A larger value has a higher priority over a smaller value.";

[[Med]] That's better, indeed. Will fix.

     leaf update-interval {

... "Indicates the RIP update time. That is, the amount of time for which routing updates are sent."; Perhaps "Interval at which RIPv2 or RIPng updates are sent."; as per rtgwg-yang-rip

[[Med]] The OLD text is OK, but can make this change s/routing updates/RIP updates

                 leaf signalling-type {
                     enum ldp {

... In this case, an IGP routing protocol must also be activated."; Probably 'MUST' and likewise for 'enum bgp'

             container service {
               leaf mtu {

... If CsC is enabled, the requested MTU will refer to the MPLS MTU and not to the IP MTU."; MPLS does not really do MTU; mpls-base-yang uses 'leaf maximum-labeled-packet' after a discussion about this issue.

[[Med]] Thanks for pointing to this. Adjusted accordingly.

CsC should appear in terminology.

[[Med]] OK. Fixed.

Tom Petch