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/24
VPC38
(VLAN 20):ip 192.168.20.5/24
VPC39
(VLAN 30):ip 192.168.30.6/24
R3 측 VPC:
VPC40
(VLAN 10):ip 192.168.10.10/24
VPC41
(VLAN 20):ip 192.168.20.11/24
VPC42
(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