CIDR — /24가 무엇을 뜻하고, 무엇을 뜻하지 않는가
방화벽 규칙이나 라우팅 설정에는 이런 표기가 나온다.
10.0.0.0/24
192.168.1.0/16
0.0.0.0/0
슬래시 뒤의 숫자가 무엇을 정하는지, 그리고 이 표기가 정하지 않는 것이 무엇인지 정리한다. 마지막 항목이 실무에서 가장 자주 문제가 된다.
슬래시 뒤 숫자는 “앞에서 몇 비트가 고정인가”
IP 주소는 32비트다. 10.0.0.5는 사람이 읽기 쉽게 8비트씩 끊어 적은 것이다.
10.0.0.5 → 00001010 00000000 00000000 00000101
└─10─┘ └──0──┘ └──0──┘ └──5──┘
/24는 앞에서 24비트가 고정이라는 뜻이다. 나머지 8비트는 자유다.
10.0.0.0/24 → 00001010 00000000 00000000 ........
└────── 고정 24비트 ──────┘ └자유 8비트┘
자유 비트가 8개면 경우의 수는 2의 8제곱, 즉 256개다. 그래서 /24는 주소
256개를 가리킨다.
| 표기 | 고정 비트 | 자유 비트 | 주소 개수 |
|---|---|---|---|
/32 | 32 | 0 | 1개 (주소 하나) |
/24 | 24 | 8 | 256 |
/16 | 16 | 16 | 65,536 |
/8 | 8 | 24 | 16,777,216 |
/0 | 0 | 32 | 전부 |
숫자가 클수록 범위가 좁다. /32는 주소 하나를 딱 집는 것이고, /0은 모든
주소다.
뒷자리는 지워야 한다
10.1.2.3/8이라고 쓰면 어떻게 될까. /8이니 앞 8비트만 고정이고 나머지는 자유다.
즉 1.2.3 부분은 아무 의미가 없다.
10.1.2.3/8 이 실제로 가리키는 것 = 10.0.0.0/8
같은 범위를 두 가지로 적을 수 있다는 뜻이다. 사람에게는 같아 보여도 프로그램에는 다른 값이 될 수 있다. 두 표기를 그대로 비교하면 다르다고 나오고, 목록에 넣으면 중복으로 쌓인다.
그래서 CIDR을 다루는 코드는 대개 자유 비트를 0으로 지운 형태로 정규화한다. 이 작업을 하지 않으면 “같은 범위를 두 번 등록했는데 하나만 지워지는” 종류의 버그가 생긴다.
범위가 겹치면 좁은 쪽이 이긴다
규칙 두 개가 있다고 하자.
10.0.0.0/8 → 허용
10.1.2.0/24 → 차단
10.1.2.5는 둘 다에 속한다. /8은 10.으로 시작하는 모든 주소를 포함하고,
/24는 그보다 좁게 포함한다.
이때는 더 좁은 쪽이 이긴다. 고정 비트가 많은 쪽, 즉 더 구체적인 쪽이다. 이 방식을 최장 접두사 매칭(longest prefix match)이라고 부른다.
라우터가 경로를 고를 때 쓰는 방식이고, 커널의 주소 자료구조도 같은 규칙을 쓴다. 그래서 넓은 규칙을 깔아두고 그 안의 일부를 좁은 규칙으로 예외 처리하는 것이 가능하다.
주의할 점은 순서가 아니라 좁기로 정해진다는 것이다. 방화벽 규칙 중에는 위에서 아래로 읽으며 처음 맞는 것을 쓰는 방식도 있으므로, 쓰고 있는 도구가 어느 쪽인지 확인해야 한다.
비트로 비교한다
접두사 매칭은 문자열 비교가 아니다. 비트를 앞에서부터 훑는다.
찾는 주소: 10.1.2.5 → 00001010 00000001 ...
후보 /8: 10.0.0.0/8 → 00001010 ........ ← 앞 8비트 일치
후보 /24: 10.1.2.0/24→ 00001010 00000001 00000010 .... ← 앞 24비트 일치
앞에서부터 비트를 따라가며 가장 깊이 일치하는 것을 고른다. 그래서 앞쪽 비트가 먼저 와야 한다. 바이트 순서를 잘못 넣으면 엉뚱한 범위에 맞는다.
이 때문에 CIDR을 커널에 넘기는 코드는 주소를 네트워크 바이트 순서(큰 자리가 앞)로 맞춘다. 리틀엔디언 정수를 그대로 넘기면 비트가 뒤집혀 실려서, 테스트에서는 멀쩡해 보이다가 실제 매칭이 어긋난다.
CIDR은 포트를 모른다
여기가 실무에서 가장 자주 놓치는 부분이다.
CIDR은 주소 범위만 정한다. 포트도, 프로토콜도 담지 않는다. 그래서 방화벽 규칙에
10.0.0.0/24를 허용으로 넣으면 그 뜻은 이렇게 된다.
이 256개 주소의 모든 포트에, TCP든 UDP든 허용
의도가 “저 대역의 웹 서버에 접근”이었더라도 규칙은 SSH도, 데이터베이스도, 열려 있는 모든 것을 함께 연다.
| 표기 | 여는 범위 |
|---|---|
10.0.0.5:8080/tcp | 주소 하나, 포트 하나, 프로토콜 하나 |
10.0.0.5/32 | 주소 하나, 모든 포트, 모든 프로토콜 |
10.0.0.0/24 | 주소 256개, 모든 포트, 모든 프로토콜 |
/32조차 포트를 좁히지 않는다. 주소를 하나로 좁힌 것뿐이다.
그래서 정책을 쓸 때는 주소:포트 단위가 기본이고, CIDR은 예외로 두는 편이 안전 하다. 대역 전체를 열어야 하는 경우가 실제로 있지만, 그때는 그것이 의도라는 것을 분명히 하고 쓰는 것이 낫다.
자주 보는 대역
| 대역 | 무엇 |
|---|---|
10.0.0.0/8 | 사설 주소 |
172.16.0.0/12 | 사설 주소 |
192.168.0.0/16 | 사설 주소 (가정용 공유기) |
127.0.0.0/8 | 루프백 |
100.64.0.0/10 | 통신사 NAT 용으로 예약된 대역 |
0.0.0.0/0 | 전부 |
마지막 것을 방화벽 허용 규칙에 넣으면 아무것도 막지 않는 것과 같다.
정리
- 슬래시 뒤 숫자는 앞에서 고정된 비트 수다. 클수록 좁다.
- 자유 비트에 값이 들어 있으면 의미가 없다. 비교하기 전에 0으로 지운다.
- 범위가 겹치면 좁은 쪽이 이긴다. 순서가 아니라 고정 비트 수로 정해진다.
- 매칭은 앞에서부터 비트를 훑는다. 바이트 순서를 맞춰야 한다.
- CIDR은 포트와 프로토콜을 담지 않는다.
/32도 그 주소의 모든 포트를 연다.