[CS300 #144] IP 주소와 서브넷 — /24 가 정확히 무슨 뜻인가
컴퓨터공학 300 주제 시리즈의 144번째 글이다. 전체 지도는 여기.
한 줄 요약
IP 주소는 “네트워크 부분 + 호스트 부분”으로 나뉘고, /24 같은 프리픽스 길이가 그 경계를 정한다. 같은 네트워크 부분을 공유하는 주소 묶음이 서브넷이다.
왜 필요한가
서브넷을 이해하지 못하면 다음 질문에 답할 수 없다.
- 이 두 서버는 라우터를 거쳐야 하는가, 바로 통신하는가?
- 쿠버네티스 파드 대역과 회사 VPN 대역이 겹치면 왜 일부 서비스만 안 되는가?
- 방화벽 규칙에
198.51.100.0/22라고 쓰면 어디부터 어디까지 열리는가?
클라우드에서 VPC 를 만들 때, 홈랩에 클러스터 대역을 정할 때, 보안 그룹을 쓸 때 모두 이 계산을 한다. 틀리면 나중에 대역을 바꾸는 비용이 크다.
핵심 개념
IPv4 주소
IPv4 주소는 32비트다(RFC 791). 8비트씩 끊어 10진수로 쓴다. 192.0.2.10 은 2진수로 다음과 같다.
192 .0 .2 .10
11000000 00000000 00000010 00001010
프리픽스와 서브넷 마스크
192.0.2.10/24 는 “앞 24비트가 네트워크 부분”이라는 뜻이다. 같은 내용을 서브넷 마스크로 쓰면 255.255.255.0 이다.
주소 11000000 00000000 00000010 00001010 192.0.2.10
마스크 11111111 11111111 11111111 00000000 255.255.255.0 (/24)
AND 11000000 00000000 00000010 00000000 192.0.2.0 ← 네트워크 주소
- 네트워크 주소: 호스트 비트가 모두 0. 서브넷 자체를 가리킨다.
- 브로드캐스트 주소: 호스트 비트가 모두 1.
/24면192.0.2.255. - 호스트에 줄 수 있는 주소: 그 사이.
/24면 2^8 − 2 = 254개. - 예외로
/31은 점대점 링크에서 두 주소를 모두 호스트에 쓰도록 허용된다(RFC 3021)./32는 주소 하나를 뜻한다.
| 프리픽스 | 마스크 | 전체 주소 수 | 일반 호스트 수 |
|---|---|---|---|
| /16 | 255.255.0.0 | 65,536 | 65,534 |
| /22 | 255.255.252.0 | 1,024 | 1,022 |
| /24 | 255.255.255.0 | 256 | 254 |
| /26 | 255.255.255.192 | 64 | 62 |
| /30 | 255.255.255.252 | 4 | 2 |
같은 서브넷인지 판단하기
호스트가 패킷을 보낼 때 가장 먼저 하는 판단이 이것이다. 자기 주소와 목적지 주소에 자기 마스크를 AND 해서 결과가 같으면 같은 링크로 바로 보낸다(ARP 로 상대 MAC 을 찾는다). 다르면 기본 게이트웨이로 보낸다. 마스크를 잘못 설정하면 같은 링크에 있는 장비를 라우터로 보내거나, 반대로 라우터를 거쳐야 할 주소에 ARP 를 하다가 실패한다.
CIDR
예전에는 주소 앞자리로 A·B·C 클래스(/8, /16, /24)를 정했다. 낭비가 심해 1990년대에 클래스 없는 주소 지정, CIDR(Classless Inter-Domain Routing, 현재 RFC 4632)로 바뀌었다. 이제 프리픽스 길이는 0~32 아무 값이나 된다. 여러 작은 대역을 큰 대역 하나로 묶어 광고하는 경로 집약(aggregation)도 CIDR 덕분이다.
특수 용도 대역
| 대역 | 용도 | 근거 |
|---|---|---|
| 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 | 사설망 | RFC 1918 |
| 100.64.0.0/10 | 통신사 NAT(CGN) 공유 대역 | RFC 6598 |
| 127.0.0.0/8 | 루프백 | RFC 1122 |
| 169.254.0.0/16 | 링크 로컬(자동 할당) | IANA 특수 목적 레지스트리 |
| 192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24 | 문서·예제용 | RFC 5737 |
사설 대역은 인터넷에서 라우팅되지 않는다. 밖으로 나가려면 NAT 가 필요하다(146번 글). 문서용 대역은 실제로 쓰이지 않도록 예약된 주소라 블로그나 교재 예제에 안심하고 쓸 수 있다.
IPv6
IPv6 주소는 128비트다(RFC 4291). 16비트씩 8묶음을 16진수로 쓰고, 연속된 0 묶음은 :: 로 한 번 줄인다. 표기 규칙은 RFC 5952 가 정리했다.
- 보통 서브넷은
/64다. 앞 64비트가 네트워크, 뒤 64비트가 인터페이스 식별자다. - 브로드캐스트가 없고 멀티캐스트로 대신한다.
- 문서용 대역은
2001:db8::/32다(RFC 3849).
직접 해 보기
파이썬 표준 라이브러리 ipaddress 로 위 계산을 해 보자.
import ipaddress as ip
iface = ip.ip_interface("192.0.2.10/24")
net = iface.network
print("네트워크:", net, "마스크:", net.netmask)
print("브로드캐스트:", net.broadcast_address, "호스트 수:", net.num_addresses - 2)
def same_subnet(a, b, prefix):
return ip.ip_interface(f"{a}/{prefix}").network == ip.ip_interface(f"{b}/{prefix}").network
print("192.0.2.10 과 192.0.2.200 (/24):", same_subnet("192.0.2.10", "192.0.2.200", 24))
print("192.0.2.10 과 192.0.2.200 (/25):", same_subnet("192.0.2.10", "192.0.2.200", 25))
# /22 를 /24 네 개로 쪼개기
big = ip.ip_network("198.51.100.0/22")
print(big, "->", [str(s) for s in big.subnets(new_prefix=24)])
# 대역 겹침 검사
pod = ip.ip_network("10.200.0.0/16")
vpn = ip.ip_network("10.200.128.0/20")
print("겹침:", pod.overlaps(vpn))
# 특수 대역 판별
for a in ["10.1.2.3", "100.64.0.1", "203.0.113.5", "169.254.1.1"]:
x = ip.ip_address(a)
print(a, ("사설" if x.is_private else "공인") + (" | 링크로컬" if x.is_link_local else ""))
v6 = ip.ip_address("2001:0db8:0000:0000:0000:0000:0000:0001")
print("IPv6 축약:", v6.compressed, "| 전체:", v6.exploded)
네트워크: 192.0.2.0/24 마스크: 255.255.255.0
브로드캐스트: 192.0.2.255 호스트 수: 254
192.0.2.10 과 192.0.2.200 (/24): True
192.0.2.10 과 192.0.2.200 (/25): False
198.51.100.0/22 -> ['198.51.100.0/24', '198.51.101.0/24', '198.51.102.0/24', '198.51.103.0/24']
겹침: True
10.1.2.3 사설
100.64.0.1 공인
203.0.113.5 사설
169.254.1.1 사설 | 링크로컬
IPv6 축약: 2001:db8::1 | 전체: 2001:0db8:0000:0000:0000:0000:0000:0001
/22 는 셋째 옥텟이 4의 배수(100)에서 시작해야 경계가 맞는다. 100 은 4의 배수이므로 198.51.100.0/22 는 올바른 네트워크이고, 198.51.100.0 부터 198.51.103.255 까지 1,024개 주소를 덮는다. 만약 198.51.101.0/22 처럼 호스트 비트가 켜진 값을 넣으면 ip_network 는 기본값(strict=True)에서 오류를 낸다. 설정 파일의 오타를 잡는 데 유용하다.
하나 더 흥미로운 점이 있다. 파이썬의 is_private 는 RFC 1918 만이 아니라 “전역 라우팅이 안 되는 특수 대역” 전반을 참으로 본다. 그래서 문서용 대역 203.0.113.5 도 사설로 나오고, CGN 대역 100.64.0.1 은 is_private 가 거짓이다(대신 is_global 도 거짓이다). 라이브러리의 정의를 문서에서 확인하는 습관이 필요하다.
현업에서는
- 클러스터를 처음 만들 때 노드 대역, 파드 대역, 서비스 대역이 서로 겹치지 않고 사내망·VPN 대역과도 겹치지 않게 정한다. 겹치면 특정 대역으로 가는 패킷이 엉뚱한 경로를 탄다. 위 예제의 겹침 검사가 그 확인이다.
- 클라우드 VPC 는 나중에 대역을 넓히기 어렵다. 처음부터 넉넉한
/16을 잡고/20~/24서브넷으로 나누는 설계가 흔하다. - 방화벽·보안 그룹 규칙을 리뷰할 때
/0,/8같은 넓은 대역이 보이면 의도인지 확인한다. - 내부 IP 대응표는 망 구성 정보다. 문서·블로그 예제에는 RFC 5737·RFC 3849 문서용 대역을 쓴다.
확인 문제
203.0.113.77/26의 네트워크 주소와 브로드캐스트 주소는?- 마스크
255.255.255.240은 프리픽스 몇 비트이며, 일반 호스트를 몇 개 둘 수 있는가? - 호스트 A 가
192.0.2.10/25, 호스트 B 가192.0.2.130/25이다. A 는 B 에게 직접 보내는가, 게이트웨이로 보내는가? - IPv6 주소
2001:0db8:0000:0000:0001:0000:0000:0001을 RFC 5952 방식으로 줄여 써라. - 사설 대역 세 개를 RFC 1918 기준으로 써라.
풀이
- 77 을 64 단위로 내리면 64 이므로 네트워크는
203.0.113.64/26, 브로드캐스트는203.0.113.127. - 240 = 11110000 이므로 /28. 16 − 2 = 14개.
- /25 에서 A 는 0~127, B 는 128~255 쪽 서브넷이다. 다른 서브넷이므로 게이트웨이로 보낸다.
2001:db8::1:0:0:1. 0 묶음이 두 군데(2개, 2개)로 길이가 같으면 앞쪽을::로 줄인다.10.0.0.0/8,172.16.0.0/12,192.168.0.0/16.
더 읽을거리 (References)
- RFC 4632, Classless Inter-domain Routing (CIDR): https://www.rfc-editor.org/rfc/rfc4632.html
- RFC 1918, Address Allocation for Private Internets: https://www.rfc-editor.org/rfc/rfc1918.html
- RFC 4291, IP Version 6 Addressing Architecture / RFC 5952 표기 권고: https://www.rfc-editor.org/rfc/rfc4291.html, https://www.rfc-editor.org/rfc/rfc5952.html
- Python
ipaddress문서: https://docs.python.org/3/library/ipaddress.html