L2VPN EVPL 테스트 Version 2
1. 시나리오 개요
본 문서는 NSO(Network Services Orchestrator)를 사용하여 두 개의 사이트를 L2VPN으로 연결하고, 각 사이트의 VLAN 10, 20, 30을 서로 통신시키는 것을 목표
물리적 구성
PE 라우터: R1, R3
액세스 스위치: Switch1 (R1 측), Switch2 (R3 측)
엔드포인트: VPC37/38/39 (R1 측), VPC40/41/42 (R3 측)
논리적 연결 목표
VLAN 10 (
VPC37) <==> VLAN 10 (VPC40)VLAN 20 (
VPC38) <==> VLAN 20 (VPC41)VLAN 30 (
VPC39) <==> VLAN 30 (VPC42)
- 토폴로지
2. NSO 서비스 패키지 최종 코드
2.1. l2vpn.yang (서비스 모델)
module l2vpn {
namespace "http://example.com/l2vpn";
prefix l2vpn;
import ietf-inet-types {
prefix inet;
}
import tailf-ncs {
prefix ncs;
}
description
"L2VPN service using direct interface rewrite for EVPL.";
revision 2025-07-29 {
description "Final version using direct rewrite for EVPL+dot1q scenario.";
}
list l2vpn {
key name;
description "List of L2VPN service instances.";
uses ncs:service-data;
ncs:servicepoint "l2vpn-servicepoint";
leaf name {
type string;
description "Unique name for this L2VPN service instance.";
}
leaf device {
type leafref {
path "/ncs:devices/ncs:device/ncs:name";
}
mandatory true;
description "The target device on which to configure the L2VPN.";
}
container local-l2transport-interface {
description "Configuration for the local L2 transport interface.";
leaf interface-type {
type enumeration {
enum "GigabitEthernet";
enum "TenGigE";
enum "HundredGigE";
}
mandatory true;
description "Type of the L2 transport interface.";
}
leaf interface-main-id {
type string;
mandatory true;
description "Main part of the interface ID (e.g., '0/0/0/0').";
}
leaf subinterface-id {
type uint32;
mandatory true;
description "Subinterface ID, which will be appended after a dot (e.g., 100).";
}
leaf mtu {
type uint16;
default 1522;
description "MTU for the L2 transport interface.";
}
leaf encapsulation-type {
type enumeration {
enum "dot1q";
enum "untagged";
enum "default";
}
mandatory true;
description "Encapsulation type for the L2 transport interface.";
}
leaf vlan-id {
when "../encapsulation-type = 'dot1q'";
type uint16 {
range "1..4094";
}
mandatory true;
description "VLAN ID for dot1q encapsulation.";
}
leaf service-type {
type enumeration {
enum "epl";
enum "evpl";
}
mandatory true;
description "Select the L2VPN service type.";
}
container rewrite-policy {
when "../service-type = 'evpl' and ../encapsulation-type = 'dot1q'";
description "Rewrite policy settings for EVPL.";
leaf rewrite-type {
type enumeration {
enum "none";
enum "pop1";
enum "translate1to1";
}
mandatory true;
description "Type of ingress rewrite operation.";
}
leaf rewrite-vlan-id {
when "../rewrite-type = 'translate1to1'";
type uint16 {
range "1..4094";
}
mandatory true;
description "VLAN ID for 1-to-1 translate rewrite.";
}
}
}
container pw-class {
description "Pseudowire class configuration for the L2VPN.";
leaf pw-class-name {
type string {
pattern "[a-zA-Z0-9_-]+";
}
mandatory true;
description "Name of the pseudowire class.";
}
leaf control-word {
type boolean;
default "false";
description "Enable or disable control-word for the pseudowire.";
}
}
container xconnect {
description "Xconnect group and point-to-point connection configuration.";
leaf group-name {
type string {
pattern "[a-zA-Z0-9_-]+";
}
mandatory true;
description "Name of the xconnect group (e.g., Provisioned).";
}
leaf p2p-name {
type string {
pattern "[a-zA-Z0-9_-]+";
}
mandatory true;
description "Name of the point-to-point connection (e.g., epl1).";
}
leaf neighbor-ip {
type inet:ipv4-address;
mandatory true;
description "Neighbor IPv4 address for the pseudowire (e.g., 10.10.10.10).";
}
leaf pw-id {
type uint32;
mandatory true;
description "Pseudowire ID (e.g., 13).";
}
leaf pw-class-ref {
type leafref {
path "../../pw-class/pw-class-name";
}
mandatory true;
description "Reference to the pseudowire class to be used for this xconnect.";
}
}
}
}
2.2. main.py
import ncs
from ncs.application import Service
class ServiceCallbacks(Service):
@Service.create
def cb_create(self, tctx, root, service, proplist):
self.log.info(f"Service create for '{service.name}' using direct rewrite logic.")
tvars = ncs.template.Variables()
iface = service.local_l2transport_interface
# rewrite 관련 변수 초기화
l2_serv_ingress_rewrite_type = 'none'
l2_serv_ingress_rewrite_vlan = 0
# rewrite_policy 컨테이너가 있는지 확인
if hasattr(iface, 'rewrite_policy'):
rewrite_policy = iface.rewrite_policy
l2_serv_ingress_rewrite_type = rewrite_policy.rewrite_type
if l2_serv_ingress_rewrite_type == 'translate1to1' and hasattr(rewrite_policy, 'rewrite_vlan_id'):
l2_serv_ingress_rewrite_vlan = rewrite_policy.rewrite_vlan_id
# 템플릿에 rewrite 변수 전달
tvars.add('L2_SERV_INGRESS_REWRITE_TYPE', l2_serv_ingress_rewrite_type)
tvars.add('L2_SERV_INGRESS_REWRITE_VLAN', l2_serv_ingress_rewrite_vlan)
# 나머지 모든 변수 전달
interface_id_combined = f"{iface.interface_main_id}.{iface.subinterface_id}"
full_interface_name = f"{iface.interface_type}{interface_id_combined}"
tvars.add('DEVICE', service.device)
tvars.add('INTERFACE_TYPE', iface.interface_type)
tvars.add('INTERFACE_ID', interface_id_combined)
tvars.add('FULL_INTERFACE_NAME', full_interface_name)
tvars.add('MTU', iface.mtu)
tvars.add('L2_SERV_ENCAP', iface.encapsulation_type)
tvars.add('L2_SERV_VLAN_ID', iface.vlan_id if hasattr(iface, 'vlan_id') else 0)
pw = service.pw_class
tvars.add('PW_CLASS_NAME', pw.pw_class_name)
tvars.add('CONTROL_WORD_ENABLED', 'true' if pw.control_word else 'false')
xc = service.xconnect
tvars.add('XCONNECT_GROUP_NAME', xc.group_name)
tvars.add('P2P_NAME', xc.p2p_name)
tvars.add('NEIGHBOR_IP', xc.neighbor_ip)
tvars.add('PW_ID', xc.pw_id)
tvars.add('PW_CLASS_REF', xc.pw_class_ref)
template = ncs.template.Template(service)
template.apply('l2vpn-template', tvars)
class L2vpn(ncs.application.Application):
def setup(self):
self.log.info('L2vpn RUNNING')
self.register_service('l2vpn-servicepoint', ServiceCallbacks)
def teardown(self):
self.log.info('L2vpn FINISHED')
2.3. l2vpn-template.xml (디바이스 템플릿)
<config xmlns="http://tail-f.com/ns/config/1.0"
xmlns:ncs="http://tail-f.com/ns/ncs">
<devices xmlns="http://tail-f.com/ns/ncs">
<device>
<name>{$DEVICE}</name>
<config>
<interface xmlns="http://tail-f.com/ned/cisco-ios-xr">
<GigabitEthernet-subinterface ncs:when="{$INTERFACE_TYPE = 'GigabitEthernet'}">
<GigabitEthernet>
<id>{$INTERFACE_ID}</id>
<mode>l2transport</mode>
<mtu>{$MTU}</mtu>
<encapsulation>
<default ncs:when="{$L2_SERV_ENCAP = 'default'}"/>
<dot1q ncs:when="{$L2_SERV_ENCAP = 'dot1q'}">
<vlan-id ncs:when="{$L2_SERV_VLAN_ID != 0}">{$L2_SERV_VLAN_ID}</vlan-id>
</dot1q>
<untagged ncs:when="{$L2_SERV_ENCAP = 'untagged'}"/>
</encapsulation>
<rewrite ncs:when="{$L2_SERV_INGRESS_REWRITE_TYPE != 'none'}">
<ingress>
<tag>
<pop ncs:when="{$L2_SERV_INGRESS_REWRITE_TYPE = 'pop1'}">1</pop>
<translate ncs:when="{$L2_SERV_INGRESS_REWRITE_TYPE = 'translate1to1'}">1-to-1</translate>
<dot1q ncs:when="{$L2_SERV_INGRESS_REWRITE_TYPE = 'translate1to1' and $L2_SERV_INGRESS_REWRITE_VLAN != 0}">{$L2_SERV_INGRESS_REWRITE_VLAN}</dot1q>
<mode ncs:when="{$L2_SERV_INGRESS_REWRITE_TYPE = 'translate1to1'}">symmetric</mode>
</tag>
</ingress>
</rewrite>
</GigabitEthernet>
</GigabitEthernet-subinterface>
<TenGigE-subinterface ncs:when="{$INTERFACE_TYPE = 'TenGigE'}">
<TenGigE>
<id>{$INTERFACE_ID}</id>
<mode>l2transport</mode>
<mtu>{$MTU}</mtu>
<encapsulation>
<default ncs:when="{$L2_SERV_ENCAP = 'default'}"/>
<dot1q ncs:when="{$L2_SERV_ENCAP = 'dot1q'}">
<vlan-id ncs:when="{$L2_SERV_VLAN_ID != 0}">{$L2_SERV_VLAN_ID}</vlan-id>
</dot1q>
<untagged ncs:when="{$L2_SERV_ENCAP = 'untagged'}"/>
</encapsulation>
<rewrite ncs:when="{$L2_SERV_INGRESS_REWRITE_TYPE != 'none'}">
<ingress>
<tag>
<pop ncs:when="{$L2_SERV_INGRESS_REWRITE_TYPE = 'pop1'}">1</pop>
<translate ncs:when="{$L2_SERV_INGRESS_REWRITE_TYPE = 'translate1to1'}">1-to-1</translate>
<dot1q ncs:when="{$L2_SERV_INGRESS_REWRITE_TYPE = 'translate1to1' and $L2_SERV_INGRESS_REWRITE_VLAN != 0}">{$L2_SERV_INGRESS_REWRITE_VLAN}</dot1q>
<mode ncs:when="{$L2_SERV_INGRESS_REWRITE_TYPE = 'translate1to1'}">symmetric</mode>
</tag>
</ingress>
</rewrite>
</TenGigE>
</TenGigE-subinterface>
<HundredGigE-subinterface ncs:when="{$INTERFACE_TYPE = 'HundredGigE'}">
<HundredGigE>
<id>{$INTERFACE_ID}</id>
<mode>l2transport</mode>
<mtu>{$MTU}</mtu>
<encapsulation>
<default ncs:when="{$L2_SERV_ENCAP = 'default'}"/>
<dot1q ncs:when="{$L2_SERV_ENCAP = 'dot1q'}">
<vlan-id ncs:when="{$L2_SERV_VLAN_ID != 0}">{$L2_SERV_VLAN_ID}</vlan-id>
</dot1q>
<untagged ncs:when="{$L2_SERV_ENCAP = 'untagged'}"/>
</encapsulation>
<rewrite ncs:when="{$L2_SERV_INGRESS_REWRITE_TYPE != 'none'}">
<ingress>
<tag>
<pop ncs:when="{$L2_SERV_INGRESS_REWRITE_TYPE = 'pop1'}">1</pop>
<translate ncs:when="{$L2_SERV_INGRESS_REWRITE_TYPE = 'translate1to1'}">1-to-1</translate>
<dot1q ncs:when="{$L2_SERV_INGRESS_REWRITE_TYPE = 'translate1to1' and $L2_SERV_INGRESS_REWRITE_VLAN != 0}">{$L2_SERV_INGRESS_REWRITE_VLAN}</dot1q>
<mode ncs:when="{$L2_SERV_INGRESS_REWRITE_TYPE = 'translate1to1'}">symmetric</mode>
</tag>
</ingress>
</rewrite>
</HundredGigE>
</HundredGigE-subinterface>
</interface>
<l2vpn xmlns="http://tail-f.com/ned/cisco-ios-xr">
<pw-class>
<name>{$PW_CLASS_NAME}</name>
<encapsulation>
<mpls>
<control-word ncs:when="{$CONTROL_WORD_ENABLED = 'true'}"/>
</mpls>
</encapsulation>
</pw-class>
<xconnect>
<group>
<name>{$XCONNECT_GROUP_NAME}</name>
<p2p>
<name>{$P2P_NAME}</name>
<interface>
<name>{$FULL_INTERFACE_NAME}</name>
</interface>
<neighbor>
<address>{$NEIGHBOR_IP}</address>
<pw-id>{$PW_ID}</pw-id>
<ip-version>ipv4</ip-version>
<pw-class>{$PW_CLASS_REF}</pw-class>
</neighbor>
</p2p>
</group>
</xconnect>
</l2vpn>
</config>
</device>
</devices>
</config>
3. 전체 구성 단계
3.1. 1단계: VPC IP 주소 설정
L2VPN으로 연결된 VPC들은 같은 IP 서브넷에 있어야 하며, 게이트웨이가 필요 없다.
R1 측 VPC:
VPC37(VLAN 10):ip 192.168.10.4/24VPC38(VLAN 20):ip 192.168.20.5/24VPC39(VLAN 30):ip 192.168.30.6/24
R3 측 VPC:
VPC40(VLAN 10):ip 192.168.10.10/24VPC41(VLAN 20):ip 192.168.20.11/24VPC42(VLAN 30):ip 192.168.30.12/24
3.2. 2단계: L2 스위치 설정
양쪽 스위치는 VPC로부터 받은 트래픽에 올바른 VLAN 태그를 붙여 PE 라우터로 전달하는 역할을 합니다.
Switch1 설정 (
R1측)configure terminal vlan 10,20,30 ! interface Ethernet0/1 description To_VPC37 switchport mode access switchport access vlan 10 ! interface Ethernet0/2 description To_VPC38 switchport mode access switchport access vlan 20 ! interface Ethernet0/3 description To_VPC39 switchport mode access switchport access vlan 30 ! interface Ethernet0/0 description To_R1 switchport trunk encapsulation dot1q switchport mode trunk ! end
Switch2 설정 (
R3측)configure terminal vlan 10,20,30 ! interface Ethernet0/1 description To_VPC40 switchport mode access switchport access vlan 10 ! interface Ethernet0/2 description To_VPC41 switchport mode access switchport access vlan 20 ! interface Ethernet0/3 description To_VPC42 switchport mode access switchport access vlan 30 ! interface Ethernet0/0 description To_R3 switchport trunk encapsulation dot1q switchport mode trunk ! end
3.3. 3단계: NSO L2VPN 서비스 생성
! =================================================== ! 연결 1: VLAN 10 ! =================================================== l2vpn R1_VLAN10 device R1 local-l2transport-interface interface-type GigabitEthernet local-l2transport-interface interface-main-id 0/0/0/9 local-l2transport-interface subinterface-id 10 local-l2transport-interface encapsulation-type dot1q local-l2transport-interface vlan-id 10 local-l2transport-interface service-type evpl xconnect group-name L2VPN_Group xconnect p2p-name P2P_VLAN10 xconnect neighbor-ip 30.30.30.30 xconnect pw-id 1310 xconnect pw-class-ref L2VPN-CLASS exit l2vpn R3_VLAN10 device R3 local-l2transport-interface interface-type GigabitEthernet local-l2transport-interface interface-main-id 0/0/0/9 local-l2transport-interface subinterface-id 10 local-l2transport-interface encapsulation-type dot1q local-l2transport-interface vlan-id 10 local-l2transport-interface service-type evpl xconnect group-name L2VPN_Group xconnect p2p-name P2P_VLAN10 xconnect neighbor-ip 10.10.10.10 xconnect pw-id 1310 xconnect pw-class-ref L2VPN-CLASS exit ! =================================================== ! 연결 2: VLAN 20 ! =================================================== l2vpn R1_VLAN20 device R1 local-l2transport-interface interface-type GigabitEthernet local-l2transport-interface interface-main-id 0/0/0/9 local-l2transport-interface subinterface-id 20 local-l2transport-interface encapsulation-type dot1q local-l2transport-interface vlan-id 20 local-l2transport-interface service-type evpl xconnect group-name L2VPN_Group xconnect p2p-name P2P_VLAN20 xconnect neighbor-ip 30.30.30.30 xconnect pw-id 1320 xconnect pw-class-ref L2VPN-CLASS exit l2vpn R3_VLAN20 device R3 local-l2transport-interface interface-type GigabitEthernet local-l2transport-interface interface-main-id 0/0/0/9 local-l2transport-interface subinterface-id 20 local-l2transport-interface encapsulation-type dot1q local-l2transport-interface vlan-id 20 local-l2transport-interface service-type evpl xconnect group-name L2VPN_Group xconnect p2p-name P2P_VLAN20 xconnect neighbor-ip 10.10.10.10 xconnect pw-id 1320 xconnect pw-class-ref L2VPN-CLASS exit ! =================================================== ! 연결 3: VLAN 30 ! =================================================== l2vpn R1_VLAN30 device R1 local-l2transport-interface interface-type GigabitEthernet local-l2transport-interface interface-main-id 0/0/0/9 local-l2transport-interface subinterface-id 30 local-l2transport-interface encapsulation-type dot1q local-l2transport-interface vlan-id 30 local-l2transport-interface service-type evpl xconnect group-name L2VPN_Group xconnect p2p-name P2P_VLAN30 xconnect neighbor-ip 30.30.30.30 xconnect pw-id 1330 xconnect pw-class-ref L2VPN-CLASS exit l2vpn R3_VLAN30 device R3 local-l2transport-interface interface-type GigabitEthernet local-l2transport-interface interface-main-id 0/0/0/9 local-l2transport-interface subinterface-id 30 local-l2transport-interface encapsulation-type dot1q local-l2transport-interface vlan-id 30 local-l2transport-interface service-type evpl xconnect group-name L2VPN_Group xconnect p2p-name P2P_VLAN30 xconnect neighbor-ip 10.10.10.10 xconnect pw-id 1330 xconnect pw-class-ref L2VPN-CLASS exit commit
4. 검증 및 확인
모든 설정이 적용된 후, 아래 방법으로 통신을 확인합니다.
PE 라우터에서 L2VPN 상태 확인
R1 RP/0/RP0/CPU0:ios#show l2vpn xconnect Fri Aug 1 00:38:03.986 UTC Legend: ST = State, UP = Up, DN = Down, AD = Admin Down, UR = Unresolved, SB = Standby, SR = Standby Ready, (PP) = Partially Programmed XConnect Segment 1 Segment 2 Group Name ST Description ST Description ST ------------------------ ----------------------------- ----------------------------- L2VPN_Group P2P_R1_R3 UP Gi0/0/0/9.10 UP 30.30.30.30 1310 UP ---------------------------------------------------------------------------------------- L2VPN_Group P2P_R1_R3_1 UP Gi0/0/0/9.20 UP 30.30.30.30 1320 UP ---------------------------------------------------------------------------------------- L2VPN_Group P2P_R1_R3_2 UP Gi0/0/0/9.30 UP 30.30.30.30 1330 UP ---------------------------------------------------------------------------------------- R3 RP/0/RP0/CPU0:ios#show l2vpn xconnect Fri Aug 1 00:38:57.996 UTC Legend: ST = State, UP = Up, DN = Down, AD = Admin Down, UR = Unresolved, SB = Standby, SR = Standby Ready, (PP) = Partially Programmed XConnect Segment 1 Segment 2 Group Name ST Description ST Description ST ------------------------ ----------------------------- ----------------------------- L2VPN_Group P2P_R3_R1 UP Gi0/0/0/9.10 UP 10.10.10.10 1310 UP ---------------------------------------------------------------------------------------- L2VPN_Group P2P_R3_R1_1 UP Gi0/0/0/9.20 UP 10.10.10.10 1320 UP ---------------------------------------------------------------------------------------- L2VPN_Group P2P_R3_R1_2 UP Gi0/0/0/9.30 UP 10.10.10.10 1330 UP ----------------------------------------------------------------------------------------VPC 간 Ping 테스트:
VPC37에서ping 192.168.10.10(VPC40)VPCS> ping 192.168.10.10 84 bytes from 192.168.10.10 icmp_seq=1 ttl=64 time=1.541 ms 84 bytes from 192.168.10.10 icmp_seq=2 ttl=64 time=1.875 ms 84 bytes from 192.168.10.10 icmp_seq=3 ttl=64 time=1.774 ms 84 bytes from 192.168.10.10 icmp_seq=4 ttl=64 time=1.796 ms 84 bytes from 192.168.10.10 icmp_seq=5 ttl=64 time=1.867 ms
VPC38에서ping 192.168.20.11(VPC41)VPCS> ping 192.168.20.11 84 bytes from 192.168.20.11 icmp_seq=1 ttl=64 time=1.572 ms 84 bytes from 192.168.20.11 icmp_seq=2 ttl=64 time=1.950 ms 84 bytes from 192.168.20.11 icmp_seq=3 ttl=64 time=1.812 ms 84 bytes from 192.168.20.11 icmp_seq=4 ttl=64 time=1.932 ms 84 bytes from 192.168.20.11 icmp_seq=5 ttl=64 time=1.791 ms
VPC39에서ping 192.168.30.12(VPC42)VPCS> ping 192.168.30.12 84 bytes from 192.168.30.12 icmp_seq=1 ttl=64 time=1.620 ms 84 bytes from 192.168.30.12 icmp_seq=2 ttl=64 time=1.789 ms 84 bytes from 192.168.30.12 icmp_seq=3 ttl=64 time=1.875 ms 84 bytes from 192.168.30.12 icmp_seq=4 ttl=64 time=1.867 ms 84 bytes from 192.168.30.12 icmp_seq=5 ttl=64 time=1.989 ms
