메인 라우터는 RouterOS로 유지하고, mangle 마크와 별도 라우팅 테이블로 일부 기기만 Surge를 거치게 합니다. 서브넷은 나누지 않습니다.
처음 네트워크를 이것저것 만지던 시절에는 집에서 OpenWrt를 메인 라우터로 썼습니다. WAN 연결부터 인터넷 접속, 프록시까지 전부 그 장비에 맡겼습니다. 하지만 안정성은 좋지 않았습니다. 제가 뭔가를 시험할 때마다 집 전체의 인터넷이 끊어졌고, 아무것도 건드리지 않아도 프록시 연결에 문제가 생기면 원래는 직접 접속할 수 있는 사이트까지 열리지 않았습니다. 게임 관련 서비스처럼 프록시가 필요 없는 통신도 있었고, 프록시를 쓸 필요가 없는 기기도 많았습니다. 이런 기기들이 프록시를 거치지 않도록 따로 우회 규칙까지 관리해야 했습니다.
이후 Surge의 Apple TV 버전이 나왔고, Surge를 보조 게이트웨이로 써서 메인 라우터에서 프록시 역할을 분리하기로 했습니다.
처음 썼던 방식
보조 게이트웨이를 쓰는 방법은 몇 가지가 있습니다. 프록시가 필요한 기기의 게이트웨이와 DNS를 보조 게이트웨이 주소로 수동 설정하거나, VLAN을 나누거나, 메인 라우터가 DHCP로 보조 게이트웨이의 주소를 알려주게 할 수 있습니다. 수동 설정을 쓰면 가족이 쓰는 기기까지 제가 하나하나 설정을 관리하고 문제가 생길 때마다 해결해야 합니다. 그래서 처음에는 DHCP로 설정을 배포하는 방식을 택했습니다.
DHCP는 번호가 붙은 옵션으로 여러 네트워크 설정을 전달할 수 있습니다. 게이트웨이는 option 3, DNS는 option 6으로 지정하며, 평소 배포하는 설정을 덮어쓰는 방식입니다. 프록시가 필요한 기기에 이 두 옵션을 지정하면 해당 기기의 트래픽이 보조 게이트웨이로 향합니다.
하지만 한동안 써 보니 가족이 스스로 전환할 수 없다는 문제가 남았습니다. 기기가 받는 설정은 라우터가 정하므로, 사용자가 기기에서 간단히 바꿀 방법이 없었습니다. 프록시를 거치지 않는 일반 연결로 돌아가려면 여전히 저를 불러야 했습니다.
보조 게이트웨이의 연결 상태를 확인하다가 끊어지면 해당 설정의 배포를 중단하는 방법도 생각했습니다. 하지만 DHCP에는 임대 시간이 있고, 기기는 다음 임대 갱신 때 설정을 다시 받습니다. 변경 사항이 바로 반영되지 않아 복구가 느렸고, 이 방식도 만족스럽지 않았습니다.
두 개의 Wi-Fi
당시 집에는 AP로 쓰는 Xiaomi 라우터가 두 대 있었고, 각각 다른 SSID를 설정할 수 있었습니다. 그래서 프록시를 거치는 Wi-Fi와 거치지 않는 Wi-Fi를 따로 만들면 어떨까 생각했습니다. 가족은 원하는 쪽에 연결하면 됩니다. 일반 연결로 돌아가고 싶을 때도 Wi-Fi만 바꾸면 되니 임대 갱신을 기다릴 필요가 없습니다.
가장 간단한 방법은 Wi-Fi 하나를 보조 게이트웨이에 연결하고, 다른 하나는 메인 게이트웨이에 연결하는 것입니다. 다만 저는 집 안의 기기를 모두 같은 서브넷에 두고 싶었습니다. 그래야 기기 관리나 스마트 홈, LAN 안의 여러 서비스를 그대로 사용할 수 있습니다. 서로 통신할 수 없거나 중간에 NAT가 필요한 별도 네트워크로 나누고 싶지는 않았습니다.
마침 메인 라우터는 이미 MikroTik RB5009U로 바꾼 상태였습니다. 일반적인 라우터처럼 라우팅 테이블을 다룰 수 있으니, BGP와 ASN을 만지면서 얻은 경험을 활용할 수 있겠다고 생각했습니다.
구성 아이디어
Surge를 실행하는 Apple TV를 추가해 네트워크의 출구를 하나 더 만듭니다. 다만 모든 기기의 기본 게이트웨이는 메인 라우터로 유지합니다. 방화벽에 규칙을 추가해 특정 AP가 연결된 포트로 들어오는 트래픽이나, 지정한 IP 주소 또는 MAC 주소에서 오는 트래픽만 Surge로 보내면 됩니다.
다른 게이트웨이로 보낸다는 것은 다른 경로 집합을 사용하게 한다는 뜻입니다. 별도의 라우팅 테이블을 만들고, Surge로 보낼 패킷에 마크를 붙입니다. 마크가 붙은 패킷은 기본 main 테이블 대신 새 테이블을 조회합니다. 새 테이블에는 Surge를 다음 홉으로 하는 기본 경로 하나만 넣습니다.
BGP에서 정책에 따라 다음 홉을 고르는 것과 같은 원리입니다. 여기서는 패킷의 출발지를 기준으로 선택한다는 점이 다릅니다.
RouterOS는 패킷을 단계별로 처리하며, 각 규칙이 적용되는 단계도 정해져 있습니다. 먼저 패킷이 어떤 단계를 어떤 순서로 통과하는지 알아야 합니다.
RouterOS의 패킷 흐름
RouterOS 방화벽에는 여러 체인이 있습니다. 들어온 IP 패킷은 대략 다음 순서로 처리됩니다.
공식 문서의 Packet Flow in RouterOS를 바탕으로 간략하게 정리한 그림입니다. 공식 문서에는 브리지, 하드웨어 오프로드, IPsec 등을 포함한 전체 흐름도가 있습니다. 라우팅을 거쳐 전달되는 경로는 다음과 같이 나와 있습니다.
라우팅된 패킷의 포워딩 체인 순서(MikroTik 공식 문서)
이 구성에서는 특히 몇 가지 처리의 순서가 중요합니다.
라우팅 결정은 prerouting과 dstnat 뒤에 이뤄집니다. 경로가 정해지기 전에 mangle prerouting에서 마크를 붙이고, dstnat에서 목적지 주소를 바꿀 수 있습니다. 라우팅 테이블을 조회할 때 쓰는 주소는 이미 바뀐 목적지 주소입니다.
srcnat은 라우팅 결정 뒤, 패킷이 나가기 직전의 단계입니다. 경로가 이미 정해졌으므로 여기서는 출발지 주소만 바꿀 수 있습니다.
따라서 여기서 다루는 수신 패킷의 라우팅 마크는 경로 결정 전인 mangle prerouting에서 붙여야 합니다. forward에서는 이미 경로가 정해진 뒤라 마크를 붙여도 늦습니다.
라우터 자체에서 생성한 트래픽은 output 체인을 통과합니다. 이 경우에는 먼저 라우팅 결정이 이뤄진 다음 mangle output을 거칩니다. 공식 문서는 그 뒤에 routing adjustment라는 처리도 보여 줍니다. output 체인의 mangle에서도 정책 라우팅을 할 수 있게 하는 우회 방법입니다. 다만 이 글에서 다루는 것은 기기가 보내고 라우터가 전달하는 트래픽이므로, 이 부분은 사용하지 않습니다.
Note
아래 주소와 규칙은 IPv4 전용이며 IPv6 트래픽은 처리하지 않습니다. 클라이언트가 IPv6로도 외부에 연결할 수 있다면 별도의 정책이 필요합니다. 이 설정만으로 IPv4와 IPv6 트래픽이 모두 프록시를 거치게 되는 것은 아닙니다.
라우팅 테이블
먼저 라우팅 테이블을 만듭니다.
BASH
/routing/table/add name=rsurge fib
테이블을 만들 때 fib를 지정합니다. 공식 정책 라우팅 예제에서도 이 옵션을 사용하며, 이렇게 해야 테이블의 경로가 포워딩에 쓰이는 FIB에 반영됩니다. 또한 RouterOS v7의 라우팅 마크는 미리 만들어 둔 라우팅 테이블에 대응해야 합니다. 테이블이 없으면 mangle에 지정한 마크가 동작하지 않습니다.
아래에서 물리 브리지 포트를 조건으로 사용하는 in-bridge-port를 쓰려면 브리지의 use-ip-firewall을 먼저 활성화해야 합니다. 기본값은 no입니다. 이 옵션을 켜면 브리지 트래픽이 IP 방화벽을 통과하며 Bridge FastPath와 CPU 부하에도 영향을 줍니다. 활성화하기 전에 장비의 처리 성능을 확인하세요. 뒤에 나오는 출발지 IP 주소 목록 방식으로 바꾸면 이 브리지 포트 조건은 필요하지 않습니다. 자세한 내용은 MikroTik의 Common Firewall Matchers and Actions와 Bridging and Switching을 참고하세요.
in-bridge-port=ether8은 어떤 기기가 프록시를 거칠지 정합니다. 나중에 이 포트 아래에 스위치를 추가하고, 프록시가 필요한 AP와 기기를 모두 여기에 연결했습니다. 그러면 이 포트로 들어오는 트래픽을 한꺼번에 마크할 수 있어 기기마다 규칙을 만들 필요가 없습니다. mangle은 브리지 안의 특정 물리 포트를 직접 조건으로 쓸 수 있어 편리합니다.
dst-address-list=!lan-ip는 내부 네트워크로 향하는 트래픽을 제외합니다. lan-ip는 내부 서브넷을 담은 주소 목록입니다. 내부로 향하는 트래픽은 Surge로 보내지 않고 일반 경로를 따라 직접 전달해야 합니다. 그래야 스마트 홈과 LAN의 여러 서비스를 계속 사용할 수 있습니다.
src-address=!10.10.77.4는 Surge 자체를 제외합니다. Surge가 요청을 처리한 뒤 외부로 보낼 트래픽은 메인 라우터로 돌아옵니다. 이를 제외하지 않으면 다시 마크가 붙어 Surge로 되돌아가므로 루프가 생깁니다.
대상 기기가 한 포트에 모여 있지 않다면, IP 주소를 주소 목록에 넣고 src-address-list를 조건으로 사용할 수도 있습니다.
RouterOS의 기본 설정에는 FastTrack 규칙이 있습니다. 연결이 성립된 뒤 대부분의 후속 처리를 건너뛰어 포워딩 속도를 높이는 기능입니다. 공식 문서에 따르면 FastTrack은 라우팅 마크를 붙이기 전에 경로를 조회하므로 main 테이블에서만 동작합니다. 즉, mark-routing을 사용하는 연결이 FastTrack의 대상이 되면 패킷은 Surge가 아니라 main의 경로를 따릅니다.
이 규칙이 있다면 Surge를 거치는 연결을 FastTrack에서 제외해야 합니다. 같은 조건의 mangle 규칙을 하나 더 추가해 해당 연결에 연결 마크를 붙입니다. 그다음 FastTrack 규칙에 connection-mark=no-mark를 추가하면 마크가 없는 연결만 FastTrack을 사용하게 됩니다.
이 mark-connection 규칙은 앞의 mark-routing 규칙보다 앞에 둬도 뒤에 둬도 됩니다. 두 규칙 모두 passthrough가 yes이므로 이후 규칙도 계속 검사합니다.
DNS
이제 트래픽을 Surge로 보낼 수 있지만, DNS 설정이 남아 있습니다.
Surge의 Enhanced Mode는 Fake IP를 사용합니다. 클라이언트가 도메인 이름을 조회하면 Surge는 198.18.0.0/15 범위의 가상 IP 주소를 반환합니다. 클라이언트가 그 주소로 연결하면 Surge는 어떤 도메인인지 알아내고 규칙에 따라 처리합니다. 따라서 DNS 요청도 Surge에 도달해야 합니다. 그렇지 않으면 실제 IP 주소가 반환되어 Fake IP 방식을 사용할 수 없습니다.
dstnat으로 대상 기기의 DNS 요청을 Surge의 Fake IP DNS 주소인 198.18.0.2로 보냅니다.
DNS 규칙 두 개에도 src-address=!10.10.77.4 조건을 넣어 Surge 자체를 제외합니다. Surge가 같은 브리지 포트에 연결되어 있어도 외부 DNS 서버로 보내는 요청이 자기 자신에게 돌아오지 않도록 하기 위해서입니다. DNS는 TCP도 쓰므로 같은 조건으로 TCP 53번 포트 규칙을 추가합니다. DoH나 DoT 같은 암호화 DNS는 이 두 규칙으로 처리할 수 없으므로 클라이언트나 네트워크 정책에서 별도로 관리해야 합니다. 앞에서 기기를 고르는 조건을 출발지 주소 목록으로 바꿨다면 DNS 규칙 두 개도 같은 조건으로 바꾸세요.
여기서도 순서가 중요합니다. DNS 요청은 mangle prerouting, dstnat, 라우팅 결정 순서로 진행됩니다.
클라이언트가 외부 DNS 서버를 지정했다면 목적지가 lan-ip에 없으므로 mangle에서 이미 마크가 붙습니다. dstnat은 목적지를 198.18.0.2로 바꾸고, 이후 rsurge의 기본 경로를 통해 Surge로 전달합니다.
반면 DNS 서버로 라우터 자체를 지정했다면 목적지가 내부 네트워크이므로 mangle의 lan-ip 조건에 의해 제외되어 마크가 붙지 않습니다. dstnat에서 목적지가 198.18.0.2로 바뀌어도 라우팅 결정 시점에는 마크가 없으므로 main을 조회합니다. 따라서 main에도 198.18.0.0/16을 Surge로 보내는 경로가 필요합니다.
클라이언트가 DNS 요청을 보낸 곳은 라우터의 10.10.77.1이므로 응답도 같은 주소에서 오기를 기대합니다. 그런데 요청만 Surge로 넘기면 Surge가 클라이언트에게 직접 응답하고, 그 출발지 주소는 198.18.0.2가 됩니다. 클라이언트는 지정한 DNS 서버에서 온 응답이 아니므로 이를 버립니다. srcnat으로 요청의 출발지 주소를 라우터 자체의 주소로 바꾸면 Surge의 응답이 먼저 라우터로 돌아옵니다. 라우터는 연결 추적을 이용해 주소 변환을 되돌린 뒤 클라이언트에게 전달합니다.
사용해 보니
이제 집에는 인터넷에 직접 연결하는 Wi-Fi와 Surge를 거치는 Wi-Fi가 있습니다. 둘 다 같은 서브넷이므로 기기끼리 그대로 통신할 수 있습니다. 연결에 문제가 생겨도 가족은 Wi-Fi만 바꾸면 되고 저를 부를 필요가 없습니다. Surge에 문제가 생기더라도 메인 라우터나 프록시가 필요 없는 기기에는 영향을 주지 않습니다.
앞으로 서비스에 따라 경로를 달리 쓰고 싶다면 rsurge에 경로를 추가하면 됩니다. 예를 들어 특정 서브넷만 Surge를 거치지 않고 실제 WAN 다음 홉으로 보낼 수 있습니다. 이때 게이트웨이에 메인 라우터 자체의 LAN 주소인 10.10.77.1을 넣으면 안 됩니다. 유효한 업스트림 다음 홉이 아니기 때문입니다. WAN 게이트웨이가 고정되어 있다면 아래 형식으로 지정할 수 있습니다. @main은 이 다음 홉을 main 테이블에서 찾으라는 뜻입니다. PPPoE를 사용한다면 실제 구성에 맞는 PPPoE 인터페이스를 지정하세요.
처음 네트워크를 이것저것 만지던 시절에는 집에서 OpenWrt를 메인 라우터로 썼습니다. WAN 연결부터 인터넷 접속, 프록시까지 전부 그 장비에 맡겼습니다. 하지만 안정성은 좋지 않았습니다. 제가 뭔가를 시험할 때마다 집 전체의 인터넷이 끊어졌고, 아무것도 건드리지 않아도 프록시 연결에 문제가 생기면 원래는 직접 접속할 수 있는 사이트까지 열리지 않았습니다. 게임 관련 서비스처럼 프록시가 필요 없는 통신도 있었고, 프록시를 쓸 필요가 없는 기기도 많았습니다. 이런 기기들이 프록시를 거치지 않도록 따로 우회 규칙까지 관리해야 했습니다.
이후 Surge의 Apple TV 버전이 나왔고, Surge를 보조 게이트웨이로 써서 메인 라우터에서 프록시 역할을 분리하기로 했습니다.
처음 썼던 방식
보조 게이트웨이를 쓰는 방법은 몇 가지가 있습니다. 프록시가 필요한 기기의 게이트웨이와 DNS를 보조 게이트웨이 주소로 수동 설정하거나, VLAN을 나누거나, 메인 라우터가 DHCP로 보조 게이트웨이의 주소를 알려주게 할 수 있습니다. 수동 설정을 쓰면 가족이 쓰는 기기까지 제가 하나하나 설정을 관리하고 문제가 생길 때마다 해결해야 합니다. 그래서 처음에는 DHCP로 설정을 배포하는 방식을 택했습니다.
DHCP는 번호가 붙은 옵션으로 여러 네트워크 설정을 전달할 수 있습니다. 게이트웨이는 option 3, DNS는 option 6으로 지정하며, 평소 배포하는 설정을 덮어쓰는 방식입니다. 프록시가 필요한 기기에 이 두 옵션을 지정하면 해당 기기의 트래픽이 보조 게이트웨이로 향합니다.
하지만 한동안 써 보니 가족이 스스로 전환할 수 없다는 문제가 남았습니다. 기기가 받는 설정은 라우터가 정하므로, 사용자가 기기에서 간단히 바꿀 방법이 없었습니다. 프록시를 거치지 않는 일반 연결로 돌아가려면 여전히 저를 불러야 했습니다.
보조 게이트웨이의 연결 상태를 확인하다가 끊어지면 해당 설정의 배포를 중단하는 방법도 생각했습니다. 하지만 DHCP에는 임대 시간이 있고, 기기는 다음 임대 갱신 때 설정을 다시 받습니다. 변경 사항이 바로 반영되지 않아 복구가 느렸고, 이 방식도 만족스럽지 않았습니다.
두 개의 Wi-Fi
당시 집에는 AP로 쓰는 Xiaomi 라우터가 두 대 있었고, 각각 다른 SSID를 설정할 수 있었습니다. 그래서 프록시를 거치는 Wi-Fi와 거치지 않는 Wi-Fi를 따로 만들면 어떨까 생각했습니다. 가족은 원하는 쪽에 연결하면 됩니다. 일반 연결로 돌아가고 싶을 때도 Wi-Fi만 바꾸면 되니 임대 갱신을 기다릴 필요가 없습니다.
가장 간단한 방법은 Wi-Fi 하나를 보조 게이트웨이에 연결하고, 다른 하나는 메인 게이트웨이에 연결하는 것입니다. 다만 저는 집 안의 기기를 모두 같은 서브넷에 두고 싶었습니다. 그래야 기기 관리나 스마트 홈, LAN 안의 여러 서비스를 그대로 사용할 수 있습니다. 서로 통신할 수 없거나 중간에 NAT가 필요한 별도 네트워크로 나누고 싶지는 않았습니다.
마침 메인 라우터는 이미 MikroTik RB5009U로 바꾼 상태였습니다. 일반적인 라우터처럼 라우팅 테이블을 다룰 수 있으니, BGP와 ASN을 만지면서 얻은 경험을 활용할 수 있겠다고 생각했습니다.
구성 아이디어
Surge를 실행하는 Apple TV를 추가해 네트워크의 출구를 하나 더 만듭니다. 다만 모든 기기의 기본 게이트웨이는 메인 라우터로 유지합니다. 방화벽에 규칙을 추가해 특정 AP가 연결된 포트로 들어오는 트래픽이나, 지정한 IP 주소 또는 MAC 주소에서 오는 트래픽만 Surge로 보내면 됩니다.
다른 게이트웨이로 보낸다는 것은 다른 경로 집합을 사용하게 한다는 뜻입니다. 별도의 라우팅 테이블을 만들고, Surge로 보낼 패킷에 마크를 붙입니다. 마크가 붙은 패킷은 기본 main 테이블 대신 새 테이블을 조회합니다. 새 테이블에는 Surge를 다음 홉으로 하는 기본 경로 하나만 넣습니다.
BGP에서 정책에 따라 다음 홉을 고르는 것과 같은 원리입니다. 여기서는 패킷의 출발지를 기준으로 선택한다는 점이 다릅니다.
RouterOS는 패킷을 단계별로 처리하며, 각 규칙이 적용되는 단계도 정해져 있습니다. 먼저 패킷이 어떤 단계를 어떤 순서로 통과하는지 알아야 합니다.
RouterOS의 패킷 흐름
RouterOS 방화벽에는 여러 체인이 있습니다. 들어온 IP 패킷은 대략 다음 순서로 처리됩니다.
공식 문서의 Packet Flow in RouterOS를 바탕으로 간략하게 정리한 그림입니다. 공식 문서에는 브리지, 하드웨어 오프로드, IPsec 등을 포함한 전체 흐름도가 있습니다. 라우팅을 거쳐 전달되는 경로는 다음과 같이 나와 있습니다.
이 구성에서는 특히 몇 가지 처리의 순서가 중요합니다.
라우팅 결정은 prerouting과 dstnat 뒤에 이뤄집니다. 경로가 정해지기 전에 mangle prerouting에서 마크를 붙이고, dstnat에서 목적지 주소를 바꿀 수 있습니다. 라우팅 테이블을 조회할 때 쓰는 주소는 이미 바뀐 목적지 주소입니다.
srcnat은 라우팅 결정 뒤, 패킷이 나가기 직전의 단계입니다. 경로가 이미 정해졌으므로 여기서는 출발지 주소만 바꿀 수 있습니다.
따라서 여기서 다루는 수신 패킷의 라우팅 마크는 경로 결정 전인 mangle prerouting에서 붙여야 합니다. forward에서는 이미 경로가 정해진 뒤라 마크를 붙여도 늦습니다.
라우터 자체에서 생성한 트래픽은 output 체인을 통과합니다. 이 경우에는 먼저 라우팅 결정이 이뤄진 다음 mangle output을 거칩니다. 공식 문서는 그 뒤에 routing adjustment라는 처리도 보여 줍니다. output 체인의 mangle에서도 정책 라우팅을 할 수 있게 하는 우회 방법입니다. 다만 이 글에서 다루는 것은 기기가 보내고 라우터가 전달하는 트래픽이므로, 이 부분은 사용하지 않습니다.
아래 주소와 규칙은 IPv4 전용이며 IPv6 트래픽은 처리하지 않습니다. 클라이언트가 IPv6로도 외부에 연결할 수 있다면 별도의 정책이 필요합니다. 이 설정만으로 IPv4와 IPv6 트래픽이 모두 프록시를 거치게 되는 것은 아닙니다.
라우팅 테이블
먼저 라우팅 테이블을 만듭니다.
테이블을 만들 때 fib를 지정합니다. 공식 정책 라우팅 예제에서도 이 옵션을 사용하며, 이렇게 해야 테이블의 경로가 포워딩에 쓰이는 FIB에 반영됩니다. 또한 RouterOS v7의 라우팅 마크는 미리 만들어 둔 라우팅 테이블에 대응해야 합니다. 테이블이 없으면 mangle에 지정한 마크가 동작하지 않습니다.
그다음 Surge를 가리키는 기본 경로를 이 테이블에 추가합니다.
메인 라우터의 주소는 10.10.77.1, Surge의 주소는 10.10.77.4이며, 서브넷은 10.10.77.0/24입니다.
이제 라우팅 테이블이 두 개입니다. main의 기본 경로는 기존 WAN 연결용 인터페이스를 사용하고, rsurge의 기본 경로는 Surge를 가리킵니다.
뒤의 규칙에서는 lan-ip 주소 목록을 사용하므로 먼저 LAN 서브넷을 등록합니다. 직접 통신해야 할 다른 내부 서브넷이 있다면 이 목록에 함께 추가하세요.
마크 붙이기
다음으로 어떤 기기의 트래픽에 rsurge를 사용할지 mangle 규칙으로 정합니다.
아래에서 물리 브리지 포트를 조건으로 사용하는 in-bridge-port를 쓰려면 브리지의 use-ip-firewall을 먼저 활성화해야 합니다. 기본값은 no입니다. 이 옵션을 켜면 브리지 트래픽이 IP 방화벽을 통과하며 Bridge FastPath와 CPU 부하에도 영향을 줍니다. 활성화하기 전에 장비의 처리 성능을 확인하세요. 뒤에 나오는 출발지 IP 주소 목록 방식으로 바꾸면 이 브리지 포트 조건은 필요하지 않습니다. 자세한 내용은 MikroTik의 Common Firewall Matchers and Actions와 Bridging and Switching을 참고하세요.
이 규칙에는 세 가지 조건이 있습니다.
in-bridge-port=ether8은 어떤 기기가 프록시를 거칠지 정합니다. 나중에 이 포트 아래에 스위치를 추가하고, 프록시가 필요한 AP와 기기를 모두 여기에 연결했습니다. 그러면 이 포트로 들어오는 트래픽을 한꺼번에 마크할 수 있어 기기마다 규칙을 만들 필요가 없습니다. mangle은 브리지 안의 특정 물리 포트를 직접 조건으로 쓸 수 있어 편리합니다.
dst-address-list=!lan-ip는 내부 네트워크로 향하는 트래픽을 제외합니다. lan-ip는 내부 서브넷을 담은 주소 목록입니다. 내부로 향하는 트래픽은 Surge로 보내지 않고 일반 경로를 따라 직접 전달해야 합니다. 그래야 스마트 홈과 LAN의 여러 서비스를 계속 사용할 수 있습니다.
src-address=!10.10.77.4는 Surge 자체를 제외합니다. Surge가 요청을 처리한 뒤 외부로 보낼 트래픽은 메인 라우터로 돌아옵니다. 이를 제외하지 않으면 다시 마크가 붙어 Surge로 되돌아가므로 루프가 생깁니다.
대상 기기가 한 포트에 모여 있지 않다면, IP 주소를 주소 목록에 넣고 src-address-list를 조건으로 사용할 수도 있습니다.
FastTrack
RouterOS의 기본 설정에는 FastTrack 규칙이 있습니다. 연결이 성립된 뒤 대부분의 후속 처리를 건너뛰어 포워딩 속도를 높이는 기능입니다. 공식 문서에 따르면 FastTrack은 라우팅 마크를 붙이기 전에 경로를 조회하므로 main 테이블에서만 동작합니다. 즉, mark-routing을 사용하는 연결이 FastTrack의 대상이 되면 패킷은 Surge가 아니라 main의 경로를 따릅니다.
이 규칙이 있다면 Surge를 거치는 연결을 FastTrack에서 제외해야 합니다. 같은 조건의 mangle 규칙을 하나 더 추가해 해당 연결에 연결 마크를 붙입니다. 그다음 FastTrack 규칙에 connection-mark=no-mark를 추가하면 마크가 없는 연결만 FastTrack을 사용하게 됩니다.
이 mark-connection 규칙은 앞의 mark-routing 규칙보다 앞에 둬도 뒤에 둬도 됩니다. 두 규칙 모두 passthrough가 yes이므로 이후 규칙도 계속 검사합니다.
DNS
이제 트래픽을 Surge로 보낼 수 있지만, DNS 설정이 남아 있습니다.
Surge의 Enhanced Mode는 Fake IP를 사용합니다. 클라이언트가 도메인 이름을 조회하면 Surge는 198.18.0.0/15 범위의 가상 IP 주소를 반환합니다. 클라이언트가 그 주소로 연결하면 Surge는 어떤 도메인인지 알아내고 규칙에 따라 처리합니다. 따라서 DNS 요청도 Surge에 도달해야 합니다. 그렇지 않으면 실제 IP 주소가 반환되어 Fake IP 방식을 사용할 수 없습니다.
dstnat으로 대상 기기의 DNS 요청을 Surge의 Fake IP DNS 주소인 198.18.0.2로 보냅니다.
DNS 규칙 두 개에도 src-address=!10.10.77.4 조건을 넣어 Surge 자체를 제외합니다. Surge가 같은 브리지 포트에 연결되어 있어도 외부 DNS 서버로 보내는 요청이 자기 자신에게 돌아오지 않도록 하기 위해서입니다. DNS는 TCP도 쓰므로 같은 조건으로 TCP 53번 포트 규칙을 추가합니다. DoH나 DoT 같은 암호화 DNS는 이 두 규칙으로 처리할 수 없으므로 클라이언트나 네트워크 정책에서 별도로 관리해야 합니다. 앞에서 기기를 고르는 조건을 출발지 주소 목록으로 바꿨다면 DNS 규칙 두 개도 같은 조건으로 바꾸세요.
여기서도 순서가 중요합니다. DNS 요청은 mangle prerouting, dstnat, 라우팅 결정 순서로 진행됩니다.
클라이언트가 외부 DNS 서버를 지정했다면 목적지가 lan-ip에 없으므로 mangle에서 이미 마크가 붙습니다. dstnat은 목적지를 198.18.0.2로 바꾸고, 이후 rsurge의 기본 경로를 통해 Surge로 전달합니다.
반면 DNS 서버로 라우터 자체를 지정했다면 목적지가 내부 네트워크이므로 mangle의 lan-ip 조건에 의해 제외되어 마크가 붙지 않습니다. dstnat에서 목적지가 198.18.0.2로 바뀌어도 라우팅 결정 시점에는 마크가 없으므로 main을 조회합니다. 따라서 main에도 198.18.0.0/16을 Surge로 보내는 경로가 필요합니다.
마지막으로 srcnat 규칙을 추가합니다.
클라이언트가 DNS 요청을 보낸 곳은 라우터의 10.10.77.1이므로 응답도 같은 주소에서 오기를 기대합니다. 그런데 요청만 Surge로 넘기면 Surge가 클라이언트에게 직접 응답하고, 그 출발지 주소는 198.18.0.2가 됩니다. 클라이언트는 지정한 DNS 서버에서 온 응답이 아니므로 이를 버립니다. srcnat으로 요청의 출발지 주소를 라우터 자체의 주소로 바꾸면 Surge의 응답이 먼저 라우터로 돌아옵니다. 라우터는 연결 추적을 이용해 주소 변환을 되돌린 뒤 클라이언트에게 전달합니다.
사용해 보니
이제 집에는 인터넷에 직접 연결하는 Wi-Fi와 Surge를 거치는 Wi-Fi가 있습니다. 둘 다 같은 서브넷이므로 기기끼리 그대로 통신할 수 있습니다. 연결에 문제가 생겨도 가족은 Wi-Fi만 바꾸면 되고 저를 부를 필요가 없습니다. Surge에 문제가 생기더라도 메인 라우터나 프록시가 필요 없는 기기에는 영향을 주지 않습니다.
앞으로 서비스에 따라 경로를 달리 쓰고 싶다면 rsurge에 경로를 추가하면 됩니다. 예를 들어 특정 서브넷만 Surge를 거치지 않고 실제 WAN 다음 홉으로 보낼 수 있습니다. 이때 게이트웨이에 메인 라우터 자체의 LAN 주소인 10.10.77.1을 넣으면 안 됩니다. 유효한 업스트림 다음 홉이 아니기 때문입니다. WAN 게이트웨이가 고정되어 있다면 아래 형식으로 지정할 수 있습니다. @main은 이 다음 홉을 main 테이블에서 찾으라는 뜻입니다. PPPoE를 사용한다면 실제 구성에 맞는 PPPoE 인터페이스를 지정하세요.
여기서 <网段>(대상 서브넷)과 <实际WAN网关>(실제 WAN 게이트웨이)는 자리표시자입니다. 자신의 네트워크에 맞는 값으로 바꾸세요.
OpenWrt에서도 ip rule과 ip route table을 쓰면 같은 구성을 만들 수 있습니다. 패킷에 먼저 마크를 붙인 뒤 다른 라우팅 테이블을 조회하게 한다는 원리는 같습니다.