DNAT / SNAT
- 가상 라우터가 수행하는 핵심 기능인 NAT(Network Address Translation) : 네트워크 패킷의 IP 헤더에 있는 출발지 또는 목적지 IP 주소를 다시 쓰는 기술
- DNAT (Destination NAT, 목적지 ANT)
- Ingress 트래픽의 목적지 IP 주소를 변경한다.
- 윈도우 PC가 유동 IP 10.10.10.203으로 ping을 보내면, 가상 라우터가 이 패킷을 받고, 패킷의 목적지를 vm1의 사설 IP 192.168.100.199로 변경하여 private 네트워크로 전달한다.
- SNAT (Source NAT, 출발지 NAT)
- Egress 트래픽의 출발지 IP 주소를 변경한다.
- vm1이 ping 8.8.8.8로 패킷을 보내면, vm1은 패킷을 게이트웨이인 route1으로 보낸다. 라우터는 이 패킷을 외부로 내보내기 전에, 패킷의 출발지를 192.168.100.199에서 유동 IP인 10.10.10.203으로 변경한다.
br-ex (External Bridge, 외부 브리지) / br-int
- L2 스위치 : L2(데이터 링크 계층)에서 작동하는 물리적 장치로, 이 장치의 유일한 목적은 연결된 장치들의 MAC 주소를 학습하여, A가 B에게 보낸 패킷을 B에게만 정확히 전달하는 패킷 교환이다.
- 가상 브리지 :
Open vSwitch(OVS)나 Linux Bridge는 L2 스위치의 기능을 소프트웨어로 구현한 것이다. 물리적 스위치가 여러 개의 랜 포트를 연결하듯, 가상 브리지는 여러 개의 가상 인터페이스(가상 머신의 NIC, 가상 라우터의 포트)를 논리적으로 연결한다.
- OpenStack에서 br-ex
- 외부 세계와 OpenStack 내부 세계를 연결하는 역할.
- 물리적 연결 (Uplink) : IP를 할당하지 않은 ens34 인터페이스가 연결된다. 이를 통해 br-ex는 ens34가 연결된 VMnet8 (NAT) 네트워크와 통신할 수 있다.
- 가상 연결 (Internal) : br-ex는 가상 라우터인 router1의 외부 게이트웨이 포트가 연결된다.
- 즉, br-ex는 router1이 외부 인터넷(VMnet8)으로 나갈 수 있도록 연결해주는 가상 스위치이다.
- vm1에서 나가는 패킷은 private 네트워크 -> router1 -> br-ex -> ens34 -> VMnet8 게이트웨이(10.10.10.2) 순서로 이동한다.
- br-int (Integration Bridge) : 모든 VM, 모든 가상 라우터가 연결되는 메인 스위치.
iptables와 체인의 개념
- 가상 라우터의 모든 L3 기능은 리눅스 커널의 Netfilter 프레임워크와 이를 제어하는 iptables 유틸리티를 통해 구현된다.
- iptables는 패킷을 처리하기 위해 여러 개의 테이블을 사용하며, 각 테이블은 특정 체인이라는 규칙 목록을 가진다
- Table : 작업의 종류를 정의한다.
filter 테이블 : 패킷을 필터링(방화벽) 할 때 사용한다. (허용/차단)
nat 테이블 : 패킷의 IP 주소를 변경할 때 사용한다. (NAT), 패킷을 차단하지 않는다.
- Chain : 작업이 적용되는 시점을 정의한다.
PREROUTING : 패킷이 머신에 들어오자마자, 라우팅 결정을 하기 전에 적용
INPUT : PREROUTING을 통과한 패킷 중, 이 머신 자체가 목적인 패킷에 적용
FORWARD : PREROUTING을 통과한 패킷 중, 이 머신이 목적이 아니라 단순히 통과(라우팅) 시키는 패킷에 적용
OUTPUT : 이 머신 자체에서 생성되어 나가는 패킷에 적용
POSTROUTING : 패킷이 모든 결정을 마치고 밖으로 나가기 직전에 적용
인스턴스 간 통신
- 두 개의 인스턴스, vm1(192.168.100.5)과 vm2(192.168.100.6)를 생성했다고 가정.
- vm1과 vm2가 동일한 private 네트워크에 있을 때 (L2 통신)
- vm1이 ping 192.168.100.6을 실행한다.
- vm1은 vm2가 동일 L2 네트워크에 있음을 확인하고, vm2의 MAC 주소를 찾기 위해 ARP 요청을 브로드캐스트한다.
- 이 ARP 요청은 vm1의 가상 NIC(tap-xxx)를 통해 br-int에 도달한다.
- br-int는 L2 스위치처럼 작동하여, 이 ARP 요청을 자신에게 연결된 모든 포트로 전달한다.
- vm2가 ARP 요청을 받고, 자신의 MAC 주소를 담아 ARP 응답을 vm1에게 보낸다.
- vm1은 vm2의 MAC 주소를 학습하고, ICMP(ping) 패킷을 br-int를 통해 vm2의 MAC 주소로 직접 유니캐스트한다.
- => 가상 라우터를 거치지 않는다. L3(IP) 라우팅이 필요 없으며, 오직 br-int 가상 스위치 내에서 L2(MAC) 통신으로 완료된다.
- vm1과 vm3이 다른 네트워크에 있을 때 (L3 통신)
- vm1 (192.168.100.5)이 vm3 (192.168.200.5)에 ping을 시도한다.
- 두 VM은 router1에 함께 연결되어 있다. (router1은 192.168.100.1과 192.168.200.1 두 개의 내부 IP를 가짐)
- vm1은 vm3가 다른 네트워크 대역에 있음을 인지하고, 패킷을 자신의 게이트웨이(192.168.100.1)로 보낸다.
- 이 게이트웨이 주소는 router1 의 내부 인터페이스(qr-xxx)이다.
- 패킷은 br-int를 통해 router1의 qr-xxx 포트로 들어간다.
- router1은 자신의 라우팅 테이블을 확인한다. "목적지가 192.168.200.5이므로, 192.168.200.1 인터페이스(qr-yyy)로 내보내야 한다."고 결정한다.
- 패킷은 router1의 qr-yyy 포트를 통해 다시 br-int로 나온다.
- br-int는 이 패킷을 vm3로 전달한다.
- => AWS VPC의 라우팅 테이블 처럼, 서로 다른 네트워크 간의 통신은 가상 라우터를 경유하여 L3 라우팅된다.
- vm1에서 ping 8.8.8.8 실행
- VM -> 라우터 : vm1은 패킷(Src: 192.168.100.199, Dst: 8.8.8.8)을 게이트웨이(192.168.100.1, router1의 내부 포트)로 보낸다.
- 라우터 진입 : 패킷이 br-int를 거쳐 router1 (네임스페이스)로 진입한다.
- 라우팅 결정 : router1은 자신의 라우팅 테이블을 본다.
- default via 10.10.10.2 dev qg-xxx (외부 게이트웨이 포트)
- "이 패킷은 qg-xxx 포트로 내보내야 한다"고 결정한다.
- 방화벽 통과 (Filter 테이블 - FORWARD 체인)
- 패킷이 FORWARD 체인을 통과한다.
- vm1의 보안 그룹 Egress 규칙이 ICMP를 ACCEPT하는지 검사한다. (성공했다고 가정)
- 주소 변환 (NAT 테이블 - POSTROUTING 체인)
- 패킷이 모든 결정을 마치고 qg-xxx 포트로 나가기 직전, POSTROUTING 체인을 만난다.
- router1은 vm1이 유동 IP (10.10.10.203)를 가지고 있음을 알고 있다. neutron-l3-agent는 이 체인에 SNAT 규칙을 설정해 두었다.
- SNAT ... source 192.168.100.199 ... to:10.10.10.203
- vm1의 출발지 IP가 192.168.100.199 (사설)에서 10.10.10.203 (유동 IP)으로 변경(SNAT)된다.
- 인터넷으로 전송
- 변환된 패킷(Src: 10.10.10.203, Dst: 8.8.8.8)이 router1을 떠나 br-ex (외부 브리지)에 도착한다.
- br-ex는 이 패킷을 ens34 (외부망 NIC)로 전달한다.
- 패킷은 VMnet8의 게이트웨이(10.10.10.2)를 통해 실제 인터넷으로 나간다.
Neutron
- Neutron은 패킷을 직접 전달하지 않는다. 사용자의 요청(네트워크 생성, 유동 IP 연결 등)을 받아서, openvswitch나 iptables 등이 어떻게 동작해야 하는지 설정값을 계산하고 명령을 내린다.
- Neutron은 하나의 프로그램이 아니라, 여러 서비스(에이전트)가 협력하여 작동하는 시스템이다.
- 사용자의 요청을 받아 실제 네트워크를 구성하는 저수준 기술(OVS,Namespaces,iptables)에게 구체적인 설정 명령을 내리는 에이전트들의 집합체.
- neutron-server (API 서버)
- Horizon 대시보드나 CLI에서 오는 모든 요청을 가장 먼저 받는 서비스이다. 사용자의 요청이 유효한지 검사하고, 작업 요청을 OpenStack의 MariaDB에 기록한다.
- Message Queue (ex. RabbitMQ)
- neutron-server와 에이전트들 간의 작업 지시 전달 시스템
- neutron-server는 neutron-l3-agent에 직접 명령을 내리지 않는다. 특정 작업을 처리하라는 메세지를 큐에 남긴다.
- neutron-l3-agent (L3 에이전트)
- 가상 라우터 관리자
- 메시지 큐를 항상 감시하다가, neutron-server가 남긴 메시지를 발견하면 즉시 가져간다.
ip netns add qrouter-xxx 명령을 실행하여 네트워크 네임스페이스(가상 라우터)를 생성.
iptables -t nat ... 명령을 실행하여 SNAT/DNAT (유동 IP) 규칙을 적용.
iptables -t filter ... 명령을 실행하여 보안 그룹(방화벽) 규칙을 적용.
- neutron-openvswitch-agent (L2 에이전트)
- 가상 스위치 관리자 (br-int, br-ex)
- 메시지 큐를 감시하다가 L2 관련 작업을 수행한다.
ovs-vsctl add-br br-ex 명령을 실행하여 OVS 브리지를 생성
ovs-vsctl add-port br-ex ens34 명령을 실행하여 물리적 NIC를 브리지에 연결
- vm1이 생성되면, vm1의 가상 NIC(tap-xxx)를 br-int (통합 브리지)에 연결합니다.
- neutron-dhcp-agent (DHCP 에이전트)
- 사설 IP 주소 자동 할당
- Horizon 대시보드에서 네트워크를 만들 때 Enable DHCP를 체크하면, 이 에이전트가 요청을 받고 qdhcp-xxx라는 별도의 네임스페이스를 만든다.
- qdhcp 네임스페이스 안에서 dnsmasq라는 경량 DHCP 서버를 실행시켜, vm을 부팅할 때 특정 사설 IP를 자동으로 할당한다.
- vm1에 유동 IP 10.10.10.203을 연결했을 때, Neutron 내부에서 순차적으로 일어나는 이벤트
- 사용자가 Horizon 대시보드에서 vm1에 유동 IP 연결을 클릭
- neutron-server : 요청을 받는다.
- MariaDB에 "작업 요청: 10.10.10.203과 192.168.100.199를 router1을 통해 연결하라"고 기록한다.
- neutron-server -> RabbitMQ : neutron-server가 RabbitMQ에 작업 지시를 보낸다. (Topic: l3_agent)
- neutron-l3-agent : RabbitMQ를 감시하다가 작업 지시를 받는다.
- neutron-l3-agent -> Host OS : L3 에이전트가 호스트 OS(VM)의 커널에
ip netns exec qrouter-xxx ... 명령을 실행하여, router1의 nat 테이블과 filter 테이블에 DNAT, SNAT 규칙을 추가한다.
- 결과 : iptables 규칙이 생성된다.