L2 Switching and L3 Routing
frame, MAC address, switch, ARP, VLAN, router가 같은 link 안과 link 너머로 data를 전달하는 방식을 host A, B, C 예시로 정리한다.
Ethernet은 같은 link에 연결된 장비 사이에서 frame을 전달하는 L2 기술이다. frame은 MAC address로 받을 장비를 가리키고, switch는 그 MAC address를 보고 frame을 내보낼 port를 고른다. 다른 subnet으로 가는 packet은 router가 받아서, 다음 link에 맞는 새 frame에 다시 넣어 보낸다.
L2 and L3
L2와 L3라는 단어를 들어봤을 것이다. OSI 7 Layer에서 L2는 data link 계층, L3는 network 계층을 뜻한다.
L2는 MAC address를 기반으로 서로를 식별하고, 같은 network 내 전송 및 오류를 제어하는 계층이다.
이렇게 들으면 전혀 와닿지 않는다.
일단 MAC address를 알아야 한다.
우리는 결국 server 간 통신에서 L1부터 L7까지 어떻게 통신하는지 전부 알게 될 것이다. 일단 받는 쪽부터 살펴보자.
들어가기 전에 단어 하나만 정해 두자. L2에서 장비끼리 주고받는 data 단위를 frame이라고 한다. 일단은 모종의 data라고 생각해도 좋고, frame 안에 무엇이 들어 있는지는 뒤의 Frame section에서 본다.
MAC Address
MAC address는 48비트 주소이고, 02:00:00:00:00:0a처럼 byte 여섯 개를 16진수로 쓴다. NIC 제조사는 앞쪽 3 byte(OUI)에 자기에게 할당된 값을 넣고, 뒤쪽 3 byte로 NIC를 하나씩 구분한다. 첫 byte의 하위 두 비트에는 다음과 같은 의미가 있다.
- 가장 낮은 비트가 1이면 multicast address다. 모든 비트가 1인
ff:ff:ff:ff:ff:ff는 broadcast address이며, 같은 broadcast domain에 있는 모든 장비가 이 주소로 보낸 frame을 받는다. - 두 번째로 낮은 비트가 1이면 locally administered address다. 제조사가 아니라 관리자나 software가 정한 주소이고, VM, container, veth에서 자주 보인다. 이 글의 예시에서 쓰는
02:...는 첫 byte에서 이 비트만 켠 주소다.
MAC address는 같은 link 안에서만 쓰인다. router를 지나면 frame이 새로 만들어지므로, 앞 link에서 쓰던 MAC address는 다음 link로 전달되지 않는다.
macOS에서 ifconfig 명령어를 실행해보면 나의 MAC address를 알 수 있고, Ubuntu machine에서는 ip link show로 확인 가능하다.
NIC and Interface
위에서 나온 NIC를 살펴보자. NIC(Network Interface Card)는 frame을 전기 신호나 빛으로 바꿔 cable로 내보내는 hardware다. 이름처럼 PCIe slot에 꽂는 card 형태가 많지만, 노트북처럼 mainboard에 chip으로 붙어 있는 경우도 있다. 노트북의 Wi-Fi chip도 cable 대신 전파로 frame을 주고받는 NIC다.
computer 한 대에 NIC가 꼭 하나만 있는 것도 아니다. server는 용도별로 network를 나누고, network마다 NIC를 따로 둔다. 예를 들어 NVIDIA DGX B300 한 대에는 다른 server의 GPU와 통신하는 ConnectX-8 8개, storage와 관리 network용 BlueField-3 2개, 그리고 관리용 1GbE port가 따로 있는 식이다.
그런데 macOS에서 ifconfig를 실행해 보면 en0, lo0, bridge0, utun0처럼 이름이 잔뜩 나오고, MAC address도 여러 개 보인다. 내 노트북에 NIC가 이렇게 많은 걸까? 아니다. 이 이름 하나하나는 interface다.
kernel의 driver가 NIC를 인식하면 그 NIC에 en0(macOS)이나 eth0, enp3s0(Linux) 같은 interface 이름을 붙이고, 이후 kernel은 이 interface를 통해 frame을 주고받는다. cable 단자가 두 개인 NIC라면 interface도 두 개 생긴다.
이 interface라는 게 좀 애매한 게, 모든 interface가 NIC에 연결되어 있지는 않다는 점이다. lo0(loopback), VPN이 만드는 utun, VM이나 container에 붙는 가상 interface처럼 hardware 없이 kernel 안에만 있는 interface도 있다. 그래서 ifconfig에 보이는 MAC address도 상당수는 hardware가 아니라 software가 만든 interface에 붙은 값이다.
엄밀히 말하면 MAC address는 interface에 붙는다. NIC에 연결된 interface는 보통 그 NIC에 기록된 MAC address를 쓰고, 가상 interface는 software가 정한 MAC address를 쓴다. 이 interface라는 개념은 매우 중요하지만, 일단 여기서는 넘어간다.
L2 Switching
자, 다시 돌아와서, “L2는 MAC address를 기반으로 서로를 식별하고, 같은 network 내 전송 및 오류를 제어하는 계층이다”라는 문장을 다시 보자.
이제 MAC address는 알았는데, MAC address만 보고 어디서 왔는지 어떻게 알 수 있을까? 사실 이것은 switch가 하는 일이다.
Switch
switch는 여러 장비를 연결하고, frame의 dst MAC을 보고 그 frame을 내보낼 port를 고르는 장치다. 여기서 port는 switch가 frame을 받거나 내보내는 지점이다. 물리 switch에서는 아래 사진처럼 cable을 꽂는 connector 하나가 port 하나이고, Linux bridge처럼 software로 만든 switch에서는 bridge에 붙인 interface 하나가 port 하나다.
참고로 Linux bridge는 개념적으로 switch와 같은 장치이다. kernel 안에 있는 L2 switch라고 생각해도 된다.
TCP와 UDP에도 port라는 말이 나오지만, 이름만 같을 뿐 switch의 port와는 다른 개념이다. TCP/UDP port는 TCP나 UDP header에 들어 있는 16비트 번호이고, host 안에서 어느 socket이 data를 받을지 정한다. 이 글에서 port라고 하면 switch의 port를 말한다.
| 용어 | 가리키는 대상 | 예 |
|---|---|---|
| NIC | frame을 신호로 바꿔 내보내는 hardware | server의 ConnectX, 노트북의 Wi-Fi chip |
| interface | kernel이 frame을 주고받는 지점에 붙인 이름 | en0, lo0, eth0, bridge0 |
| switch port | switch에서 frame이 들어오고 나가는 지점 | 물리 switch의 RJ45 port, Linux bridge에 붙인 interface |
| TCP/UDP port | TCP, UDP header에 들어 있는 16비트 번호 | 443(HTTPS) |
switch는 frame이 어느 port로 들어왔는지를 알고 있다.
frame이 들어왔을 때, frame 안에는 누가 보냈는지(src MAC)가 같이 적혀 있어서, switch는 “이 MAC으로 가는 frame은 이 port(왔던 곳)로 내보내면 되겠군” 하고 기록할 수 있다.
이 과정을 MAC learning이라고 한다.
Switch는 frame을 받을 때마다 다음 순서로 처리한다.
- src MAC과 frame이 들어온 port를 MAC table에 기록한다. frame이 port 1로 들어왔다면 그 frame을 보낸 장비가 port 1 쪽에 있다는 것은 확실하므로, switch는 dst MAC이 아니라 src MAC으로 학습한다.
- dst MAC이 MAC table에 있으면 그 port로만 내보낸다.
- dst MAC이 broadcast이거나 MAC table에 없으면, frame이 들어온 port를 뺀 모든 port로 복사한다. 이 동작을 flooding이라고 하고, MAC table에 없는 unicast frame을 unknown unicast라고 부른다.
- 일정 시간 동안 frame이 들어오지 않은 MAC의 entry는 MAC table에서 지운다. 이 동작을 aging이라고 한다.
broadcast, unicast, multicast 같은 용어들은 뒤에서 자세히 다룬다.

여기서 보내는 쪽이 자기 MAC address를 frame에 적어서 보낸다는 것을 유추할 수 있다. 이제 frame을 까보자.
Frame
Ethernet frame은 header, payload, FCS로 이루어진다. IP packet은 payload에 들어가고, header에는 같은 link 안에서 이 frame을 누가 받고 누가 보냈는지와 payload의 protocol이 들어간다. FCS는 전송 중에 frame이 손상되었는지 확인하는 값이다.
IP packet이 뭐지? link가 뭐 말하는 거지? FCS가 뭐지? 일일이 외우려고 하지 마라. 어차피 뒤에 가면 다 알게 된다.
인류는 Ethernet이라는 물건을 발명해냈다. Ethernet은 소위 말해서 protocol이다. 즉, 일종의 “이렇게 소통합시다” 하고 만든 약속이다. 일반적인 Ethernet frame의 field는 다음과 같다.
packet-beta
accTitle: 최소 크기 Ethernet frame의 byte 배치
accDescr: Destination MAC부터 FCS까지 64 byte인 최소 크기 Ethernet frame이다. 0~5 byte는 Destination MAC, 6~11 byte는 Source MAC, 12~13 byte는 EtherType, 14~59 byte는 46 byte payload, 60~63 byte는 FCS다. Preamble과 SFD는 frame 크기에 들어가지 않으므로 그림에서 뺐다.
title Minimum Ethernet frame (byte offsets)
0-5: "Dst MAC"
6-11: "Src MAC"
12-13: "Type"
14-59: "Payload"
60-63: "FCS"
| Field | Size | Meaning |
|---|---|---|
| Preamble + SFD | 8 bytes | receiver가 bit stream의 시작 위치를 맞추는 동기화 신호 |
| Destination MAC | 6 bytes | 이 frame을 받을 장비의 MAC address |
| Source MAC | 6 bytes | 이 frame을 보낸 장비의 MAC address |
| EtherType | 2 bytes | payload의 protocol (IPv4는 0x0800, ARP는 0x0806) |
| Payload | 46-1500 bytes | 상위 계층 data |
| FCS | 4 bytes | frame 손상 여부를 확인하는 checksum |
이제 Destination/Source MAC은 설명을 안 해도 우리는 알 수 있다. 지금은 각각 “frame을 받을 NIC”와 “frame을 보낸 NIC”에 딸린 MAC이라고 생각할 수 있다.
다만 조금만 더 정확하게 짚자면 “받을 NIC”는 frame이 최종적으로 도착할 곳이 아니라, 같은 link 안에서 이 frame을 받을 NIC다.
같은 link 안에 있다는 말은 router를 거치지 않고 도달할 수 있는 범위라는 뜻이다. 중간에 switch가 100대 1000대가 있어도 router를 지나지 않으면 같은 link에 속한다. MAC address는 이 범위 안에서만 의미가 있다. router를 지나면 frame이 새로 만들어지기 때문인데, 자세한 사항은 뒤에서 router를 다룰 때 이어서 한다.
흔히 말하는 최소 frame 크기 64 byte는 Destination MAC부터 FCS까지를 센 값이고, Preamble과 SFD는 포함하지 않는다. payload가 46 byte보다 작으면 보내는 쪽이 padding을 붙여 최소 크기를 맞춘다.
이제 위에서 언급했던 unicast, broadcast, multicast를 알아보자.
dst MAC이 몇 대의 장비를 가리키는지에 따라서 frame을 세 가지로 나눌 수 있다.
이 구분은 frame을 받는 NIC의 입장에서 보면 이해하기 쉽다. NIC는 frame이 들어오면 dst MAC을 확인하고, 이 frame을 받을지 버릴지 정한다.
제일 단순한 상황을 가정해보자. 위에서 switch를 다뤘으니, host A, B, C가 모두 하나의 switch에 연결되어 있고, 이 switch는 각 host의 MAC address를 이미 알고 있다고 해보자. C는 같은 switch에 연결되어 있지만 이번 통신과는 상관없는 host다. 그럼 A에서 switch를 거쳐 B로 frame이 가는 상황을 상상하라.
flowchart TB
accTitle: switch 하나에 연결된 host A, B, C
accDescr: host A, B, C가 switch 하나의 port p1, p2, p3에 각각 연결되어 있다. A의 MAC address는 02:00:00:00:00:0a, B는 02:00:00:00:00:0b, C는 02:00:00:00:00:0c이고, switch의 MAC table에는 세 MAC address와 port가 이미 기록되어 있다.
SW["Switch<br/>MAC table<br/>…:0a → p1<br/>…:0b → p2<br/>…:0c → p3"]
A["Host A<br/>MAC …:0a"]
B["Host B<br/>MAC …:0b"]
C["Host C<br/>MAC …:0c"]
SW ---|p1| A
SW ---|p2| B
SW ---|p3| C
Case 1: A already knows B’s MAC address
A는 “이 MAC address는 B의 것이다” 하고 dst MAC에 B의 MAC address를 적어서 frame을 내보낸다. switch는 MAC table을 보고 이 frame을 B가 연결된 port로만 내보낸다.
B의 NIC는 frame을 받았을 때 “내 MAC address랑 같네? 그럼 나한테 온 거네?” 하고 받는다. 이 상황이 바로 unicast이다.
핵심은 dst MAC이 장비 한 대(B)만 가리킨다는 것이다. 그래서 이 frame은 C에게 가지 않고, frame을 받는 NIC도 B의 NIC 하나뿐이다. A가 B의 MAC address를 어떻게 알게 됐는지는 Case 2에서 본다.
Case 2: A knows B’s IP address, but not its MAC address
똑같이 A에서 B로 frame을 보내려고 하는데, 이번에는 B의 IP address는 알지만 MAC address는 모른다. dst MAC에 뭘 적어야 할지 모르는 거다. 이 경우는 어떻게 할까?
이때 나오는 것이 바로 ARP request이다. Address Resolution Protocol, 즉 주소를 결정하는 protocol이다. 손을 번쩍 들어서 “이 IP를 가진 host의 MAC address 삽니다” 하는 행위이다.
이 경우 dst MAC에 ff:ff:ff:ff:ff:ff를 적어서 보내는데, 이렇게 생긴 걸 broadcast frame이라고 한다.
switch는 broadcast frame을 들어온 port를 뺀 모든 port로 보내버린다. switch의 MAC table에 B가 있어도 소용없다. dst MAC이 B의 MAC address가 아니라 ff:ff:ff:ff:ff:ff이기 때문이다. 그래서 이번에는 B뿐 아니라 C의 NIC도 이 frame을 받는다. 이렇게 하나의 NIC에서 broadcast frame을 보냈을 때 닿는 범위 전체를 broadcast domain이라고 한다.
그런데 B와 C가 모두 받았다고 해서 둘 다 대답하지는 않는다. B와 C는 둘 다 frame 안의 ARP request를 열어서 적힌 IP를 확인한다. 그 IP가 자기 IP인 B만 “그거 나야” 하고 대답하고, C는 대답하지 않고 넘어간다.
이때 B가 보내는 ARP reply는 dst MAC에 A의 MAC address를 적은 unicast frame이다. 이제 A는 B의 MAC address를 알게 됐으니, 그다음부터는 Case 1처럼 unicast로 보내면 된다.
Case 3: A sends to whoever joined a group
이번에는 A가 같은 영상을 여러 host에게 보내야 한다고 해보자. 그런데 A는 이 영상을 보고 싶은 host가 누구인지, 몇 대인지 모른다.
unicast로 보내려면 받을 host마다 MAC address를 알아야 하고, 받을 host 수만큼 frame을 따로 만들어 보내야 한다. broadcast로 보내면 한 번에 끝나지만, 영상에 관심 없는 C까지 frame을 받아서 처리해야 한다.
broadcast가 안 된다는 것은 아니다. 그냥 비효율적이라는 것이다.
이럴 때는 dst MAC에 host 한 대의 주소가 아니라 group 주소를 적어서 보낸다. 이렇게 dst MAC에 group 주소를 적은 frame을 multicast frame이라고 한다. 영상을 보고 싶은 host는 자기 NIC에 “이 group 주소로 오는 frame도 받아라”라고 미리 등록해 두는데, 이것을 group에 가입한다고 한다.
switch는 multicast frame도 기본적으로 모든 port로 복사한다. 그래서 B와 C의 NIC 모두에 frame이 도착하지만, 받아들이는 건 그 group에 가입한 NIC뿐이다. B가 가입해 있다면 B의 NIC는 “내가 가입한 group이네?” 하고 받고, 가입하지 않은 C의 NIC는 “나랑 상관없네” 하고 버린다. Case 2에서는 C가 frame 안의 ARP request까지 열어 보고 나서야 넘어갔지만, 여기서는 C의 NIC가 frame을 받는 단계에서 바로 버린다.
핵심은 받을 host를 A가 정하지 않는다는 것이다. 나중에 C도 이 group에 가입하면, A가 아무것도 바꾸지 않아도 C의 NIC가 같은 frame을 받기 시작한다.
printer나 AirPlay 기기를 찾을 때 쓰는 mDNS도 multicast를 쓴다. mDNS frame의 dst MAC은
01:00:5e:00:00:fb라는 group 주소이고, mDNS를 쓰는 기기만 이 group에 가입해 있다.
정리하면 dst MAC이 장비 한 대를 가리키면 unicast, 같은 broadcast domain의 모든 장비를 가리키면 broadcast, group에 가입한 장비들을 가리키면 multicast다.
VLAN
Case 2에서 broadcast frame은 같은 broadcast domain에 있는 모든 host가 받는다고 했다. 그럼 switch에 host가 1,000대 연결되어 있다면 어떻게 될까? ARP request 하나가 1,000대에 모두 복사되고, 1,000대가 모두 frame을 열어서 확인해야 한다. 게다가 회사라면 개발 server와 회계 server처럼 서로 떼어 놓아야 하는 host가 같은 broadcast domain에 섞이게 된다.
network를 나누려면 switch를 따로 사서 cable을 따로 깔면 된다. 하지만 VLAN(Virtual LAN)을 쓰면 switch 한 대의 port를 여러 개의 독립된 L2 network로 나눌 수 있다. 장비는 그대로 두고 switch 설정만 바꾸면 된다.
Access Port
앞에서 본 그림을 다시 가져오자. 이번에는 A와 B가 연결된 p1, p2를 VLAN 10에, C가 연결된 p3를 VLAN 20에 넣었다.
flowchart LR
accTitle: VLAN 두 개로 나눈 switch 하나
accDescr: switch 하나에서 port p1과 p2는 VLAN 10, port p3는 VLAN 20에 속한다. 그래서 host A와 B는 VLAN 10, host C는 VLAN 20에 있다.
SW["Switch"]
subgraph V20["VLAN 20"]
C["Host C"]
end
subgraph V10["VLAN 10"]
A["Host A"]
B["Host B"]
end
SW ---|p1| A
SW ---|p2| B
SW ---|p3| C
이 상태에서 A가 C에게 frame을 보내려고 한다고 해보자. A는 C의 MAC address를 모르니 Case 2처럼 ARP request를 broadcast로 보낸다. 그런데 이번에는 switch가 “이 frame은 VLAN 10 port인 p1으로 들어왔으니 VLAN 10 frame이네” 하고, 같은 VLAN 10에 속한 p2로만 복사한다. p3는 VLAN 20이라 복사하지 않는다. 그래서 B의 NIC는 frame을 받지만, C에게는 frame이 아예 도착하지 않는다. A는 끝내 C의 MAC address를 알아내지 못하고, C와 통신할 수 없다.
같은 switch에 연결되어 있어도 VLAN이 다르면, 서로 다른 switch에 연결된 것처럼 동작한다는 뜻이다. 즉 VLAN 하나가 broadcast domain 하나가 된다. MAC table도 VLAN을 함께 기록해서, switch는 VLAN과 MAC address를 묶어서 port를 찾는다.
여기서 A, B, C는 자기가 어느 VLAN에 있는지 모른다. host가 보내는 frame에는 VLAN 정보가 없고, switch가 frame이 들어온 port를 보고 VLAN을 정하기 때문이다. 이렇게 VLAN 하나에만 속하고 VLAN 정보가 없는 frame만 주고받는 port를 access port라고 한다.
관리 기능이 있는 switch는 대부분 모든 port를 기본으로 VLAN 1에 넣어 둔다. 그래서 지금까지 본 switch는 VLAN이 하나뿐인 switch였던 셈이다.
Trunk Port and 802.1Q Tag
flowchart TB
accTitle: trunk로 연결한 switch 두 대
accDescr: 1층 switch 1에는 VLAN 10의 host A와 VLAN 20의 host C가, 2층 switch 2에는 VLAN 10의 host B와 VLAN 20의 host E가 access port로 연결되어 있다. host와 switch 사이를 지나는 frame에는 tag가 없다. 두 switch는 trunk port끼리 cable 하나로 연결되어 있고, 이 cable을 지나는 frame에는 VLAN 10 또는 VLAN 20을 적은 802.1Q tag가 붙는다.
A["Host A<br/>VLAN 10"]
C["Host C<br/>VLAN 20"]
S1["Switch 1<br/>Floor 1"]
S2["Switch 2<br/>Floor 2"]
B["Host B<br/>VLAN 10"]
E["Host E<br/>VLAN 20"]
A ---|untagged| S1
C ---|untagged| S1
S1 ===|"trunk<br/>tag: VLAN 10 or 20"| S2
S2 ---|untagged| B
S2 ---|untagged| E
이번에는 switch가 두 대라고 해보자. 1층 switch와 2층 switch를 cable 하나로 연결했고, 양쪽 모두에 VLAN 10과 VLAN 20 host가 있다.
1층 switch에는 A(VLAN 10)와 C(VLAN 20)가, 2층 switch에는 B(VLAN 10)와 E(VLAN 20)가 연결되어 있다.
그러면 두 switch를 잇는 cable로는 두 VLAN의 frame이 모두 지나가야 한다. 그런데 frame에는 VLAN 정보가 없으니, 받는 switch는 이 frame이 어느 VLAN의 것인지 알 수 없다.
그래서 switch 사이의 cable로 frame을 보낼 때는 frame에 VLAN 번호를 적은 tag를 붙인다.
이 tag를 IEEE 802.1Q tag라고 하고, 여러 VLAN의 frame이 tag를 단 채 지나가는 port를 trunk port라고 한다.
받는 switch는 tag를 보고 VLAN을 정하고, 그 frame을 access port로 host에게 내보낼 때는 tag를 다시 뗀다.
예를 들어 A가 B에게 보낸 frame은 tag 없이 Switch 1로 들어온다. Switch 1은 이 frame을 trunk로 내보내면서 VLAN 10 tag를 붙이고, Switch 2는 tag를 보고 VLAN 10 frame임을 안 뒤 tag를 떼고 B에게 내보낸다. 그래서 A와 B는 tag를 한 번도 보지 않는다.
tag는 frame의 Source MAC과 EtherType 사이에 4 byte로 들어간다.
| Field | Size | Meaning |
|---|---|---|
| TPID | 2 bytes | 항상 0x8100이다. 원래 EtherType이 있던 자리에 이 값이 있으면, 받는 쪽은 tag가 붙은 frame임을 안다 |
| PCP, DEI | 4 bits | 앞의 3 bit(PCP)는 frame의 전송 우선순위이고, 마지막 1 bit(DEI)는 혼잡할 때 먼저 버려도 되는 frame인지를 표시한다 |
| VLAN ID | 12 bits | 이 frame이 속한 VLAN 번호 |
VLAN이 다른 host끼리 통신하려면 L2를 벗어나 router를 거쳐야 한다. 그래서 실제로는 VLAN마다 subnet을 따로 정하고, VLAN 사이를 router가 연결한다. 이 이야기는 L3에서 이어진다.
L3 Routing
지금까지는 같은 switch에 연결된 host끼리 frame을 주고받는 경우만 봤다. 그런데 우리가 실제로 접속하는 server는 대부분 같은 switch에 연결되어 있지 않다. 이제 L3로 올라가 보자.
browser에서 soobook.io에 접속하면 HTTP 요청은 TCP segment에 들어가고, TCP segment는 IP packet에 들어간다.
TCP segment와 IP packet은 각 계층이 주고받는 data 단위의 이름이다. L4의 TCP는 HTTP data 앞에 TCP header를 붙이는데, 이 단위를 segment라고 부른다. L3의 IP는 그 segment 앞에 다시 IP header를 붙이는데, 이 단위를 packet이라고 부른다. 여기에 L2 Ethernet header까지 붙으면 앞에서 본 frame이 된다.
그리고 IP packet은 다시 Ethernet frame에 들어가 cable로 나간다. 이때 IP가 L3(network layer)를 맡고, Ethernet이 L2(data link layer)를 맡는다. 두 계층은 각자 주소를 가지지만, 주소가 가리키는 대상이 다르다.
IP address는 packet의 최종 목적지를 가리킨다. 노트북에서 인터넷의 server로 보내는 packet은 출발부터 도착까지 dst IP로 server의 IP를 유지한다. 반면 MAC address는 지금 이 link에서 frame을 받을 다음 장비를 가리킨다.
그래서 노트북이 보내는 frame의 dst MAC에는 server의 MAC address가 아니라, 노트북과 같은 link에 있는 router의 MAC address가 들어간다.
앞의 Frame section에서 “받을 NIC는 최종 목적지가 아니라 같은 link 안에서 frame을 받을 NIC”라고 했던 것이 바로 이 이야기다.
IP Address and Subnet
IPv4 address는 32비트 숫자이고, 10.0.0.1/24처럼 prefix 길이와 함께 쓴다. /24는 앞의 24비트가 network 부분이라는 뜻이다. 그래서 10.0.0.1/24를 쓰는 host는 10.0.0.0부터 10.0.0.255까지를 자기와 같은 subnet으로 본다.
subnet 계산은 CIDR and Subnet Calculation에서 자세히 다룬다. 이 글에서는 “dst IP가 내 subnet에 속하는가, 아닌가”만 알면 된다.
host는 packet을 보내기 전에 dst IP가 자기 subnet에 속하는지부터 확인한다.
같은 subnet이면 dst host가 같은 link에 있다고 보고, 그 host의 MAC address를 dst MAC에 적어서 frame을 직접 보낸다.
앞의 Case 1~3이 모두 이 경우였다.
다른 subnet이면 route table에 적힌 gateway의 MAC address를 dst MAC에 적어서 보내고, 그 뒤의 경로는 router가 정한다. gateway는 다른 subnet으로 가는 packet을 대신 받아 주는 router를 말한다.
Linux에서는 이 판단에 쓰는 route table을 ip route로 볼 수 있다. 예를 들어 10.0.0.1/24를 쓰는 host A가 10.0.0.254를 gateway로 쓴다면, route table은 다음과 같다.
$ ip route
default via 10.0.0.254 dev eth0
10.0.0.0/24 dev eth0 proto kernel scope link src 10.0.0.1
참고:
proto kernel은 “이 route kernel이 넣음” 이라는 뜻이고,scope link는 “같은 link 안에 있음” 이라는 뜻이고,src 10.0.0.1은 “여기로 packet을 보낼 때 src IP를 10.0.0.1로 넣으시오” 라는 뜻이다.
두 번째 줄은 “10.0.0.0/24로 가는 packet은 eth0로 직접 보낸다”는 뜻이다.
첫 번째 줄은 “그 밖의 모든 주소는 10.0.0.254(gateway)에게 보낸다”는 뜻이다.
via가 없으면 gateway를 거치지 않고, dst IP 주인에게 바로 frame을 보낸다는 뜻이다. 반대로via가 붙은 route는 gateway의 IP로 ARP를 보내고, gateway의 MAC address를 dst MAC에 적는다.
자세한 건 Router를 파면서 더 알아보자.
Router
router는 서로 다른 subnet을 연결하는 L3 장비다.
여기서 앞의 NIC and Interface section에서 넘어갔던 interface가 다시 나온다.
router는 연결된 subnet마다 interface를 하나씩 가지고, interface마다 그 subnet의 IP address와 자기 MAC address를 따로 가진다.
착각하면 안 되는 것은, router에서는 subnet이 다르면 interface가 다르다가 “대체로” 성립하지만, 그렇지 않은 경우도 있으며, 반대로 interface가 다르다고 해서 subnet이 다르다는 뜻은 아니다.
즉 IP address도 MAC address처럼 장비가 아니라 interface에 붙는다.
이건 비단 router 얘기가 아니라 모든 host에 똑같이 적용된다. IP address는 NIC가 아니라 interface에 붙고, MAC address도 마찬가지다. NIC에는 공장에서 기록한 MAC address가 있지만, frame에 실제로 쓰이는 값은 interface에 설정된 MAC address다. 모든 interface가 IP를 가지는 것은 아니지만 가진다면 interface가 가진다.
| interface | MAC address | IP address | 연결된 subnet |
|---|---|---|---|
| eth0 | 02:00:00:00:00:f0 | 10.0.0.254/24 | A, B, C가 있는 10.0.0.0/24 |
| eth1 | 02:00:00:00:00:f1 | 10.0.1.254/24 | D가 있는 10.0.1.0/24 |
A가 다른 subnet에 있는 D(10.0.1.4)에게 ping을 보내는 상황으로 R이 하는 일을 따라가 보자. B와 C는 이번에도 같은 switch에 연결되어 있지만, 그림에서는 생략했다.
flowchart TB
accTitle: router R로 연결된 두 subnet
accDescr: host A는 10.0.0.0/24 subnet의 switch 1에, host D는 10.0.1.0/24 subnet의 switch 2에 연결되어 있다. router R은 eth0(10.0.0.254, MAC 02:00:00:00:00:f0)로 switch 1에, eth1(10.0.1.254, MAC 02:00:00:00:00:f1)로 switch 2에 연결되어 두 subnet을 잇는다. A의 MAC address는 02:00:00:00:00:0a, D의 MAC address는 02:00:00:00:00:0d다.
R["Router R<br/>eth0: 10.0.0.254, …:f0<br/>eth1: 10.0.1.254, …:f1"]
subgraph N2["Subnet 10.0.1.0/24"]
SW2["Switch 2"]
D["Host D<br/>10.0.1.4<br/>MAC …:0d"]
end
subgraph N1["Subnet 10.0.0.0/24"]
SW1["Switch 1"]
A["Host A<br/>10.0.0.1<br/>MAC …:0a"]
end
R ---|eth0| SW1
R ---|eth1| SW2
SW1 --- A
SW2 --- D
먼저 A의 입장이다. A는 dst IP를 보고 “10.0.1.4? 내 subnet(10.0.0.0/24)이 아니네. 그럼 gateway한테 넘기자” 하고 route table의 default route를 고른다.
그래서 A가 만든 frame의 dst IP는 D의 IP(10.0.1.4)이지만, dst MAC에는 D가 아니라 R의 eth0 MAC address(…:f0)가 들어간다. R의 MAC address를 모르면 Case 2처럼 ARP로 먼저 알아낸다.
이제 R의 입장이다. R의 eth0는 frame을 받고 “dst MAC이 내 거네?” 하고 받는다. 여기까지는 Case 1의 unicast와 같다. 그런데 Ethernet header를 떼고 IP packet을 꺼내 보니, dst IP가 자기 IP가 아니다. R은 “나한테 온 건 아니고, 다른 데로 넘겨 달라는 거구나” 하고 다음 순서로 처리한다.
- route table에서 dst IP(
10.0.1.4)를 찾는다. “10.0.1.0/24는 eth1 쪽에 바로 붙어 있네.” 그래서 packet을 eth1로 내보내고, 다음 장비는 D 자신으로 정한다. - IPv4 header의 TTL을 1 줄인다. A가 64로 보냈다면 이제 63이다. TTL은 packet이 지날 수 있는 router 수를 제한하는 값이다. routing loop에 빠진 packet이 같은 경로를 끝없이 돌지 않도록, router는 TTL이 0이 된 packet을 버린다.
- 새 frame을 만든다. src MAC에는 나가는 interface인 eth1의 MAC address(
…:f1)를, dst MAC에는 D의 MAC address(…:0d)를 적는다. D의 MAC address를 모르면, 이번에는 R이 eth1 쪽에서 ARP request를 보낸다.
R에 들어온 frame과 R에서 나간 frame을 나란히 놓으면 다음과 같다.
| A → R (eth0 쪽) | R → D (eth1 쪽) | |
|---|---|---|
| src MAC | …:0a (A) | …:f1 (R eth1) |
| dst MAC | …:f0 (R eth0) | …:0d (D) |
| src IP | 10.0.0.1 | 10.0.0.1 |
| dst IP | 10.0.1.4 | 10.0.1.4 |
| TTL | 64 | 63 |
핵심은 MAC address는 link를 건널 때마다 바뀌고, IP address는 처음부터 끝까지 그대로라는 것이다. 단, 이것은 router가 NAT를 하지 않을 때의 이야기다. 집이나 회사의 공유기처럼 NAT를 하는 router는 packet의 src IP를 자기 IP로 바꿔서 내보낸다.
link를 건널 때마다 frame을 받을 다음 장비가 router나 목적지로 바뀌므로, dst MAC이 바뀐다.
packet이 router를 하나 지날 때마다 Ethernet frame은 새로 만들어지고, 그 안의 IP packet은 TTL 같은 일부 field만 바뀐 채 다음 link로 넘어간다.
참고로 이름에 switch가 붙어 있지만 router 일도 하는 L3 switch라는 장비도 있다. L3 switch는 switch hardware 안에 router 기능을 넣은 장비다.
같은 subnet으로 가는 frame은 switch처럼 MAC address를 보고 port로 내보내고, 다른 subnet으로 가는 packet은 router처럼 IP address를 보고 위의 과정을 처리한다.
data center에서 쓰는 switch는 대부분 이 L3 기능을 갖추고 있다. 그래서 이 글에서 말하는 switch와 router는 장비의 이름이 아니라, MAC address로 전달하는 동작과 IP address로 전달하는 동작을 가리킨다고 이해하면 된다.
Packet Path
- A의 kernel이 ping 요청을 담은 IP packet을 만든다. dst IP는 D의 IP다.
- A는 route table을 보고 dst IP가 다른 subnet에 있다는 것을 확인한 뒤, gateway인 router R에게 보내기로 한다.
- A는 ARP cache에서 R의 MAC address를 찾고, 없으면 ARP request를 보낸다. (Case 2)
- A는 dst MAC에 R의 MAC address를 적은 frame을 만들어 보낸다. (Case 1)
- switch는 src MAC을 학습하고, dst MAC을 MAC table에서 찾아 R이 연결된 port로 frame을 내보낸다.
- R은 frame에서 IP packet을 꺼내 route table을 보고, TTL을 1 줄인 뒤 D 쪽 interface로 새 frame을 만들어 보낸다.
- D 쪽 switch가 frame을 D에게 전달하고, D의 kernel은 IP packet을 꺼내 reply를 만든다.
D가 A와 같은 subnet에 있다면 2번에서 gateway 대신 D에게 바로 보내고, 3번에서는 D의 MAC address를 찾으며, 6번은 일어나지 않는다.
Summary
이 글에서는 같은 link 안에서 frame이 전달되는 방식(L2)과, link를 넘어 packet이 전달되는 방식(L3)을 살펴봤다. 핵심은 다음과 같다.
- MAC address는 같은 link 안에서 frame을 받을 다음 장비를 가리키고, IP address는 packet의 최종 목적지를 가리킨다. 두 주소 모두 장비가 아니라 interface에 붙는다.
- switch는 frame의 src MAC과 들어온 port를 MAC table에 기록하고, dst MAC을 보고 내보낼 port를 고른다. dst MAC이 broadcast이거나 table에 없으면 들어온 port를 뺀 모든 port로 복사한다.
- frame은 dst MAC이 가리키는 대상에 따라 unicast, broadcast, multicast로 나뉜다. ARP는 상대의 MAC address를 broadcast로 묻고, 답은 unicast로 받는다.
- VLAN은 switch 하나를 여러 broadcast domain으로 나눈다. switch 사이의 trunk에서는 802.1Q tag로 frame의 VLAN을 구분한다.
- host는 dst IP가 자기 subnet에 속하면 상대에게 직접 frame을 보내고, 아니면 gateway에게 보낸다. router는 frame을 새로 만들고 TTL을 줄이지만, NAT를 하지 않는 한 src IP와 dst IP는 바꾸지 않는다.
References
- IEEE 802.3 Working Group: Ethernet
- IEEE 802.1Q-2022: Bridges and Bridged Networks
- RFC 791: Internet Protocol
- RFC 826: An Ethernet Address Resolution Protocol
- RFC 894: A Standard for the Transmission of IP Datagrams over Ethernet Networks
- ip-link(8): network device configuration
- ip-route(8): routing table management
- Linux kernel documentation: Ethernet Bridging
- NVIDIA DGX B300 User Guide: Introduction
- IBM Documentation: Ethernet frame format
- Juniper Networks Pathfinder: Dynamic learning of source and destination MAC addresses