MAC 주소 (MAC Address)

MAC(Media Access Control) 주소는 데이터 링크 계층(L2)에서 네트워크 인터페이스를 고유하게 식별하는 하드웨어 주소입니다. 이 문서에서는 IEEE 표준 EUI-48/EUI-64 비트 구조부터 이더넷 프레임에서의 역할, 리눅스 커널의 net_device 주소 관리, ARP를 통한 IP→MAC 해석, Bridge FDB 학습 메커니즘, 수신 필터링, 가상 디바이스(macvlan/macvtap) 할당, SR-IOV VF MAC 관리까지 MAC 주소의 전 영역을 커널 내부 관점에서 다룹니다.

전제 조건: 이 문서를 효과적으로 이해하려면 다음 내용을 먼저 숙지하세요:
일상 비유: MAC 주소는 아파트의 호수(동·호)와 비슷합니다. IP 주소가 "서울시 강남구 역삼동 123번지"라는 논리적 주소라면, MAC 주소는 "A동 1502호"라는 물리적 위치 표시입니다. 같은 건물(브로드캐스트 도메인) 안에서 택배(프레임)를 정확한 문 앞까지 배달하려면 반드시 호수(MAC 주소)를 알아야 합니다. ARP/NDP는 "123번지에 사는 분의 호수가 뭔가요?"라고 묻는 인터폰 역할을 합니다.

핵심 요약

  • EUI-48 (48비트, 6바이트) — OUI(3B, 제조사) + NIC 고유번호(3B)로 구성된 전 세계 고유 하드웨어 주소
  • I/G 비트 (bit 0) — 0이면 유니캐스트, 1이면 멀티캐스트/브로드캐스트를 구분하는 첫 번째 비트
  • U/L 비트 (bit 1) — 0이면 글로벌(IEEE 할당), 1이면 로컬(관리자 수동 설정)을 구분
  • ARP/NDP — IPv4에서는 ARP, IPv6에서는 NDP(Neighbor Discovery)를 통해 IP → MAC 주소를 해석
  • dev_addr — 리눅스 커널 net_device 구조체의 현재 활성 MAC 주소 필드. perm_addr은 원래 하드웨어 MAC

단계별 이해

  1. MAC 주소 구조 파악EUI-48/64 비트 구조에서 OUI, I/G 비트, U/L 비트의 의미를 이해합니다
  2. 이더넷 프레임에서의 역할이더넷 프레임 속 MAC에서 L2 헤더의 Src/Dst MAC이 어떻게 사용되는지 확인합니다
  3. 커널 내부 관리net_device 주소 관리에서 dev_addr, perm_addr, dev_set_mac_address() 흐름을 살펴봅니다
  4. 주소 해석과 실전ARP와 Neighbor 테이블, Bridge FDB, 가상 디바이스 섹션에서 실제 네트워크 통신에서 MAC 주소가 어떻게 활용되는지 파악합니다

개요

MAC 주소는 OSI 모델 2계층(데이터 링크)에서 동작하며, 동일 브로드캐스트 도메인 내의 장치를 식별하는 유일한 수단입니다. IP 주소가 논리적 주소라면, MAC 주소는 NIC(Network Interface Card)에 물리적으로 할당된 하드웨어 주소입니다.

MAC 주소의 핵심 역할

역할설명관련 커널 경로
프레임 전달 이더넷 스위치가 목적지 MAC을 기반으로 포트를 결정 br_handle_frame(), br_fdb_find_rcu()
IP→MAC 해석 ARP/NDP가 IP 주소를 MAC 주소로 변환 arp_process(), neigh_resolve_output()
수신 필터링 NIC가 자신의 MAC과 일치하는 프레임만 수신 __dev_set_rx_mode(), ndo_set_rx_mode
VLAN 태깅 MAC + VLAN ID 조합으로 트래픽 분리 vlan_do_receive()
가상화 격리 VM/컨테이너마다 고유 MAC으로 트래픽 분리 macvlan_handle_frame(), veth_xmit()
역사적 배경: MAC 주소 체계는 1980년대 초 DIX(Digital-Intel-Xerox) 이더넷 표준에서 시작되어, IEEE 802.3으로 표준화되었습니다. 원래 48비트(EUI-48) 주소 공간은 약 281조 개의 고유 주소를 제공하며, IoT 시대를 대비해 64비트(EUI-64)로 확장되었습니다.

OSI 7계층에서 MAC 주소의 위치

OSI 7계층 모델 주소/식별자 L7 응용 (Application) URL, 호스트명 L6 표현 (Presentation) 인코딩, 암호화 L5 세션 (Session) 소켓 세션 L4 전송 (Transport) 포트 번호 (TCP/UDP 포트) L3 네트워크 (Network) IP 주소 (논리적, 라우팅 가능) L2 데이터 링크 (Data Link) LLC + MAC 서브레이어 MAC 주소 (48비트, 하드웨어, L2 전달) L1 물리 (Physical) 전기 신호, 광 신호, 핀 배치 ARP/NDP MAC 주소는 L2에서만 유효합니다. 라우터를 거치면 MAC은 변경되지만 IP는 유지됩니다. 같은 서브넷: IP→(ARP)→MAC으로 직접 전달 | 다른 서브넷: IP→게이트웨이 MAC→라우터가 다음 홉 MAC으로 교체
MAC 주소는 OSI L2(데이터 링크)에서 동작하며, ARP/NDP가 L3(IP)와 L2(MAC)를 연결하는 다리 역할을 한다.

MAC 주소 vs IP 주소 비교

특성MAC 주소IP 주소
계층 L2 (데이터 링크) L3 (네트워크)
크기 48비트 (6바이트) IPv4: 32비트, IPv6: 128비트
할당 방식 제조사가 하드웨어에 영구 기록 (OUI 기반) DHCP/수동 설정 (논리적 할당)
유효 범위 동일 L2 도메인 (같은 서브넷/VLAN) 전 세계 (라우팅 가능)
라우터 통과 홉마다 변경됨 (다음 홉의 MAC으로 교체) 끝까지 유지됨 (출발지/목적지 IP 불변)
주소 형식 00:1A:2B:3C:4D:5E (16진수 콜론 구분) 192.168.1.1 (IPv4) / fe80::1 (IPv6)
커널 저장소 net_device->dev_addr struct in_ifaddr / struct inet6_ifaddr
해석 프로토콜 ARP Request/Reply (IPv4), NDP (IPv6) DNS (호스트명→IP)

라우터 통과 시 MAC 변화 과정

MAC 주소가 라우터(L3 장비)를 통과할 때 변경되는 과정을 이해하는 것이 중요합니다. 출발지/목적지 IP는 종단 간 유지되지만, MAC은 매 홉(hop)마다 갈아 끼워집니다.

[호스트 A] ──→ [라우터 R1] ──→ [라우터 R2] ──→ [호스트 B]

구간 1 (A → R1):
  SRC MAC: A의 MAC    DST MAC: R1의 MAC (기본 게이트웨이)
  SRC IP:  A의 IP     DST IP:  B의 IP

구간 2 (R1 → R2):
  SRC MAC: R1의 MAC   DST MAC: R2의 MAC (다음 홉)
  SRC IP:  A의 IP     DST IP:  B의 IP    ← IP 변경 없음!

구간 3 (R2 → B):
  SRC MAC: R2의 MAC   DST MAC: B의 MAC
  SRC IP:  A의 IP     DST IP:  B의 IP    ← IP 변경 없음!

EUI-48/EUI-64 비트 구조

EUI-48 (48비트 MAC 주소)

가장 널리 사용되는 MAC 주소 형식입니다. 총 48비트(6바이트)로 구성되며, 상위 24비트는 IEEE가 제조사에 할당하는 OUI(Organizationally Unique Identifier), 하위 24비트는 제조사가 자체 관리하는 NIC 고유 번호입니다.

EUI-48 MAC 주소 비트 레이아웃 (48비트 = 6바이트) OUI (Organizationally Unique Identifier) NIC 고유 번호 (Vendor Assigned) bit 47 bit 24 bit 23 ~ 0 bit 0 바이트 0 바이트 1 바이트 2 바이트 3 바이트 4 바이트 5 첫 바이트 (바이트 0) 상세 bit 0: I/G bit 1: U/L bit 2~7: OUI 상위 비트 I/G (Individual/Group): 0 = 유니캐스트, 1 = 멀티캐스트 U/L (Universal/Local): 0 = 글로벌(IEEE 할당), 1 = 로컬(관리자 설정) 예: 00:1A:2B:3C:4D:5E → OUI=00:1A:2B(IEEE 할당), NIC=3C:4D:5E(제조사 관리)
EUI-48: 상위 24비트 OUI + 하위 24비트 NIC 고유번호. 첫 바이트의 bit 0(I/G)과 bit 1(U/L)이 주소 유형을 결정한다.

EUI-64 (64비트 확장 주소)

IPv6의 인터페이스 식별자(Interface Identifier)와 IEEE 802.15.4(IoT/센서 네트워크)에서 사용됩니다. EUI-48에서 변환할 때는 OUI와 NIC 고유 번호 사이에 0xFFFE를 삽입하고, U/L 비트를 반전시킵니다.

형식비트 수주소 공간주요 용도
EUI-48 48비트 (6바이트) 248 ≈ 281조 이더넷, Wi-Fi, Bluetooth
EUI-64 64비트 (8바이트) 264 ≈ 1.8 × 1019 IPv6 SLAAC, IEEE 802.15.4, Zigbee
Modified EUI-64 64비트 EUI-48에서 변환 IPv6 인터페이스 ID (0xFFFE 삽입 + U/L 반전)

EUI-48 → Modified EUI-64 변환 예시

원본 EUI-48:     00:1A:2B:3C:4D:5E
1) OUI 분리:     00:1A:2B | 3C:4D:5E
2) FFFE 삽입:    00:1A:2B:FF:FE:3C:4D:5E
3) U/L 비트 반전: 02:1A:2B:FF:FE:3C:4D:5E   (바이트0: 0x00 → 0x02, bit1 반전)
IPv6 Interface ID: 021a:2bff:fe3c:4d5e
EUI-48 → Modified EUI-64 변환 과정 원본 EUI-48 (6바이트): 00 1A 2B 3C 4D 5E | ← OUI (3바이트) → ← NIC (3바이트) → 0xFFFE 삽입 FFFE 삽입 후 (8바이트): 00 1A 2B FF FE 3C 4D 5E ← 삽입된 0xFFFE → U/L 비트 반전 (bit 1) Modified EUI-64: 02 1A 2B FF FE 3C 4D 5E 00→02 (bit 1 반전) → fe80::021a:2bff:fe3c:4d5e
EUI-48→Modified EUI-64: OUI와 NIC 사이에 0xFFFE 삽입 후, 첫 바이트의 U/L 비트(bit 1)를 반전하여 IPv6 인터페이스 ID 생성

주소 유형과 비트 플래그

MAC 주소의 첫 번째 바이트에 있는 두 개의 특수 비트가 주소의 성격을 완전히 결정합니다. 리눅스 커널은 이 비트들을 검사하여 패킷 수신 경로를 분기합니다.

I/G 비트 (bit 0) — 유니캐스트 vs 멀티캐스트

bit 0 값유형의미커널 매크로
0 유니캐스트 (Individual) 단일 인터페이스 대상 is_unicast_ether_addr()
1 멀티캐스트 (Group) 그룹 대상 (브로드캐스트 포함) is_multicast_ether_addr()

U/L 비트 (bit 1) — 글로벌 vs 로컬

bit 1 값유형의미사용 사례
0 글로벌 (Universal) IEEE가 OUI를 통해 할당한 전 세계 고유 주소 실물 NIC, 공장 출고 MAC
1 로컬 (Local) 관리자가 수동으로 설정한 주소 가상 NIC, 컨테이너, macvlan, VM

특수 MAC 주소

주소이름용도커널 검사 함수
FF:FF:FF:FF:FF:FF 브로드캐스트 동일 L2 도메인의 모든 호스트에게 전달 is_broadcast_ether_addr()
00:00:00:00:00:00 제로 주소 미할당/초기화 전 상태 is_zero_ether_addr()
01:00:5E:xx:xx:xx IPv4 멀티캐스트 IGMP 기반 그룹 통신 I/G bit = 1 확인
33:33:xx:xx:xx:xx IPv6 멀티캐스트 NDP, MLD 등 IPv6 그룹 통신 I/G bit = 1 확인
01:80:C2:00:00:0x STP/LLDP/LACP L2 제어 프레임 (Bridge에서 소비) br_handle_frame() 특수 처리
커널 유틸리티 함수: include/linux/etherdevice.his_valid_ether_addr(), is_local_ether_addr(), eth_random_addr() 등 MAC 주소 판별/생성 인라인 함수가 정의되어 있습니다. 이 함수들은 단순 비트 연산으로 구현되어 성능 오버헤드가 없습니다.

IPv4 멀티캐스트 → MAC 매핑 규칙

IPv4 멀티캐스트 주소(224.0.0.0/4)는 특정 규칙에 따라 MAC 주소로 매핑됩니다. 이 매핑은 1:1이 아니라 32:1이므로, 서로 다른 32개의 멀티캐스트 그룹이 같은 MAC 주소로 매핑될 수 있습니다.

IPv4 멀티캐스트 IP → MAC 매핑 규칙 IPv4 멀티캐스트 주소 (32비트): 1110 5비트 (무시됨) 하위 23비트 하위 23비트 복사 매핑된 MAC 주소 (48비트): 01 00 5E 0x xx xx ← 고정 OUI: 01:00:5E → ← 하위 23비트 → 예: 239.1.2.3 → 01:00:5E:01:02:03 주의: 239.129.2.3도 → 01:00:5E:01:02:03 (충돌!) 5비트 손실 → 32개 그룹이 같은 MAC으로 매핑
멀티캐스트 IP→MAC: 고정 OUI 01:00:5E + IP 하위 23비트. 상위 5비트 손실로 32:1 중복 매핑이 발생한다.
/* include/linux/etherdevice.h — 핵심 판별 함수 */
static inline bool is_multicast_ether_addr(const u8 *addr)
{
    return 0x01 & addr[0];  /* bit 0 = 1이면 멀티캐스트 */
}

static inline bool is_local_ether_addr(const u8 *addr)
{
    return 0x02 & addr[0];  /* bit 1 = 1이면 로컬 관리 주소 */
}

static inline bool is_broadcast_ether_addr(const u8 *addr)
{
    return (*(const u16 *)(addr + 0) &
            *(const u16 *)(addr + 2) &
            *(const u16 *)(addr + 4)) == 0xffff;
}

static inline bool is_valid_ether_addr(const u8 *addr)
{
    return !is_multicast_ether_addr(addr) && !is_zero_ether_addr(addr);
}

이더넷 프레임과 MAC 주소

이더넷 프레임(IEEE 802.3)에서 MAC 주소는 가장 앞에 위치합니다. NIC 하드웨어는 프레임의 처음 6바이트(목적지 MAC)를 먼저 읽어 자신의 주소와 비교하고, 일치하지 않으면 프레임을 버립니다.

이더넷 II 프레임 구조 (DIX / IEEE 802.3) Preamble + SFD 8 바이트 목적지 MAC (Destination) 6 바이트 출발지 MAC (Source) 6 바이트 802.1Q VLAN 4B (선택) EtherType /Length 2 바이트 페이로드 (Payload) IP 헤더 + TCP/UDP + 데이터 46 ~ 1500 바이트 FCS (CRC-32) 4 바이트 와이어 전송 순서 → 주요 EtherType 값 0x0800 — IPv4 0x0806 — ARP 0x86DD — IPv6 0x8100 — 802.1Q sk_buff 포인터 매핑 skb->mac_header skb->network_header skb->transport_header
이더넷 II 프레임: 목적지/출발지 MAC이 프레임 선두에 위치하며, sk_buff의 mac_header가 이를 가리킨다.

커널의 이더넷 헤더 구조체

/* include/uapi/linux/if_ether.h */
#define ETH_ALEN    6       /* 이더넷 주소(MAC) 길이 */
#define ETH_HLEN    14      /* 이더넷 헤더 전체 길이 */
#define ETH_DATA_LEN 1500   /* 최대 페이로드 길이 */

struct ethhdr {
    unsigned char   h_dest[ETH_ALEN];    /* 목적지 MAC (6바이트) */
    unsigned char   h_source[ETH_ALEN];  /* 출발지 MAC (6바이트) */
    __be16          h_proto;             /* EtherType (2바이트) */
} __attribute__((packed));

/* sk_buff에서 이더넷 헤더 접근 */
struct ethhdr *eth = eth_hdr(skb);
/* eth->h_dest: 목적지 MAC
   eth->h_source: 출발지 MAC
   eth->h_proto: EtherType (ntohs()로 호스트 바이트 오더 변환) */

커널 자료구조와 MAC 주소 저장

리눅스 커널에서 네트워크 인터페이스의 MAC 주소는 struct net_device의 여러 필드에 저장됩니다. 각 필드는 서로 다른 용도와 생명주기를 가집니다.

필드크기용도변경 가능성
dev->dev_addr addr_len 바이트 현재 활성 MAC 주소 (프레임 송신 시 출발지 MAC) 런타임 변경 가능 (ip link set dev eth0 address ...)
dev->perm_addr MAX_ADDR_LEN NIC EEPROM/펌웨어에 저장된 공장 출고 MAC 변경 불가 (읽기 전용)
dev->addr_len unsigned char 주소 길이 (이더넷 = 6, InfiniBand = 20) 인터페이스 유형에 따라 고정
dev->broadcast MAX_ADDR_LEN 브로드캐스트 주소 (이더넷: FF:FF:FF:FF:FF:FF) 인터페이스 유형에 따라 고정
dev->uc struct netdev_hw_addr_list 추가 유니캐스트 MAC 목록 (macvlan 등) 동적 추가/제거
dev->mc struct netdev_hw_addr_list 멀티캐스트 MAC 목록 (IGMP 가입 시 추가) 동적 추가/제거
/* net/core/dev_addr_lists.c — 유니캐스트/멀티캐스트 리스트 관리 */
int dev_uc_add(struct net_device *dev, const unsigned char *addr)
{
    /* dev->uc 리스트에 유니캐스트 MAC 추가 */
    int err;
    netif_addr_lock_bh(dev);
    err = __hw_addr_add(&dev->uc, addr, dev->addr_len,
                        NETDEV_HW_ADDR_T_UNICAST);
    if (!err)
        __dev_set_rx_mode(dev);  /* NIC에 필터 업데이트 요청 */
    netif_addr_unlock_bh(dev);
    return err;
}

int dev_mc_add(struct net_device *dev, const unsigned char *addr)
{
    /* dev->mc 리스트에 멀티캐스트 MAC 추가 */
    int err;
    netif_addr_lock_bh(dev);
    err = __hw_addr_add(&dev->mc, addr, dev->addr_len,
                        NETDEV_HW_ADDR_T_MULTICAST);
    if (!err)
        __dev_set_rx_mode(dev);
    netif_addr_unlock_bh(dev);
    return err;
}
dev_addr vs perm_addr: perm_addr은 드라이버가 probe() 시점에 EEPROM에서 읽어 한 번만 설정하며 이후 변경되지 않습니다. dev_addr은 사용자가 ip link set address로 변경할 수 있으며, 원래 주소로 복원할 때 perm_addr을 참조합니다. ethtool -P eth0으로 확인 가능합니다.
struct net_device — MAC 주소 관련 필드 struct net_device dev_addr[6] 현재 활성 MAC perm_addr[32] EEPROM 영구 MAC (읽기 전용) addr_len = 6 주소 길이 (이더넷=6) broadcast[32] FF:FF:FF:FF:FF:FF uc (unicast list) 추가 유니캐스트 MAC 목록 mc (multicast list) 멀티캐스트 MAC 목록 flags IFF_PROMISC, IFF_ALLMULTI ... addr_assign_type PERM/RANDOM/SET NIC 하드웨어 EEPROM / Flash (MAC 영구 저장) RAR (Receive Address Register) 슬롯 0~N MTA (Multicast Table Array) 해시 테이블 probe() ndo_set_mac ndo_set_rx_mode 사용자 공간 인터페이스 ip link show → dev_addr 조회 ip link set address → dev_set_mac_address() ethtool -P → perm_addr 조회 /sys/class/net/*/address → sysfs 읽기
net_device의 MAC 관련 필드: dev_addr(활성), perm_addr(영구), uc/mc 리스트(추가 주소)와 NIC HW 레지스터/사용자 인터페이스의 관계

드라이버의 EEPROM MAC 읽기 과정

NIC 드라이버는 probe() 함수에서 EEPROM/Flash를 읽어 MAC 주소를 가져옵니다. EEPROM이 없거나 유효하지 않은 경우 랜덤 MAC을 생성합니다.

/* 드라이버 probe() 내부 — MAC 초기화 패턴 (e1000e 기반 예시) */
static int e1000_probe(struct pci_dev *pdev, ...)
{
    struct net_device *netdev;
    struct e1000_adapter *adapter;
    /* ... PCI/DMA 설정 ... */

    /* 1) NIC EEPROM에서 MAC 읽기 */
    if (e1000e_read_mac_addr(&adapter->hw))
        dev_err(&pdev->dev, "NIC EEPROM read error\n");

    /* 2) 유효성 검사 */
    if (!is_valid_ether_addr(adapter->hw.mac.addr)) {
        /* EEPROM MAC이 유효하지 않으면 랜덤 생성 */
        dev_warn(&pdev->dev, "Invalid MAC, using random\n");
        eth_hw_addr_random(netdev);
    } else {
        /* 3) 정상: dev_addr과 perm_addr 모두 설정 */
        eth_hw_addr_set(netdev, adapter->hw.mac.addr);
        memcpy(netdev->perm_addr, adapter->hw.mac.addr, ETH_ALEN);
    }
    /* 4) HW RAR 슬롯 0에 MAC 기록 */
    e1000e_rar_set(hw, adapter->hw.mac.addr, 0);
    /* ... */
}

MAC 주소 변경 경로

사용자가 ip link set dev eth0 address XX:XX:XX:XX:XX:XX 명령으로 MAC 주소를 변경하면, 커널 내부에서 다음과 같은 호출 체인이 실행됩니다.

MAC 주소 변경 — 커널 호출 체인 ip link set dev eth0 address ... Netlink RTM_SETLINK do_setlink() dev_set_mac_address() call_netdevice_notifiers(NETDEV_PRE_CHANGEADDR) ops->ndo_set_mac_address() OK eth_mac_addr() (범용) e1000e_set_mac() (인텔) ixgbe_set_mac() (10G) ether_addr_copy(dev->dev_addr, addr) HW 레지스터에 새 MAC 기록 + dev_addr 갱신 NETDEV_CHANGEADDR 알림
MAC 변경 흐름: Netlink → dev_set_mac_address() → 드라이버 ndo_set_mac_address() → HW 레지스터 기록 + 노티파이어 전파
/* net/core/dev.c — dev_set_mac_address() 핵심 경로 */
int dev_set_mac_address(struct net_device *dev, struct sockaddr *sa,
                        struct netlink_ext_ack *extack)
{
    const struct net_device_ops *ops = dev->netdev_ops;
    int err;

    if (!ops->ndo_set_mac_address)
        return -EOPNOTSUPP;

    if (dev->flags & IFF_UP)    /* 인터페이스 활성 상태에서도 변경 허용 */
        ;                         /* (드라이버가 거부할 수 있음) */

    /* 변경 전 노티파이어 — FDB/ARP 테이블 갱신 준비 */
    err = call_netdevice_notifiers(NETDEV_PRE_CHANGEADDR, dev);
    err = notifier_to_errno(err);
    if (err)
        return err;

    /* 드라이버에게 실제 변경 위임 */
    err = ops->ndo_set_mac_address(dev, sa);
    if (err)
        return err;

    /* 변경 완료 알림 — Bridge FDB, ARP 캐시 등 갱신 트리거 */
    call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
    add_device_randomness(dev->dev_addr, dev->addr_len);
    return 0;
}
드라이버별 차이: 범용 eth_mac_addr()는 단순히 dev->dev_addr을 복사합니다. 반면 실제 NIC 드라이버(e1000e, ixgbe 등)는 HW 레지스터(RAR: Receive Address Register)에 새 MAC을 기록하여 하드웨어 수신 필터를 즉시 갱신합니다. 일부 NIC는 IFF_UP 상태에서 MAC 변경을 거부할 수도 있습니다.

MAC 주소 수신 필터링

NIC는 기본적으로 자신의 MAC 주소와 일치하는 프레임만 수신합니다. 커널은 ndo_set_rx_mode 콜백을 통해 NIC의 수신 필터를 동적으로 제어합니다.

수신 모드 비교

모드IFF 플래그수신 대상사용 사례
일반 모드 (기본) 자신의 MAC + 등록된 멀티캐스트 + 브로드캐스트 일반적인 네트워크 운영
프로미스큐어스 IFF_PROMISC 모든 프레임 (MAC 무관) tcpdump, 패킷 캡처, Bridge 포트
올멀티캐스트 IFF_ALLMULTI 자신의 MAC + 모든 멀티캐스트 + 브로드캐스트 멀티캐스트 라우터, IGMP 스누핑

수신 필터 업데이트 경로

/* net/core/dev.c — __dev_set_rx_mode() */
void __dev_set_rx_mode(struct net_device *dev)
{
    const struct net_device_ops *ops = dev->netdev_ops;

    if (!(dev->flags & IFF_UP))
        return;

    if (!netif_device_present(dev))
        return;

    /* 드라이버에게 현재 유니캐스트/멀티캐스트 리스트와 플래그 전달 */
    if (ops->ndo_set_rx_mode)
        ops->ndo_set_rx_mode(dev);
}

/* 드라이버 구현 예시 (ixgbe) — 유니캐스트/멀티캐스트 필터 프로그래밍 */
/* 1. dev->uc 리스트의 MAC들을 RAR(Receive Address Register)에 기록
   2. dev->mc 리스트의 MAC들을 MTA(Multicast Table Array) 해시에 기록
   3. 리스트가 HW 용량 초과 시 IFF_PROMISC 또는 IFF_ALLMULTI로 폴백 */
HW 필터 용량 한계: 대부분의 NIC는 유니캐스트 필터 수가 제한됩니다 (e1000e: 15개, ixgbe: 128개). macvlan이나 SR-IOV VF가 많아져 등록된 유니캐스트 MAC이 HW 용량을 초과하면, 드라이버는 자동으로 프로미스큐어스 모드로 전환하여 소프트웨어 필터링으로 대체합니다. 이 경우 CPU 사용량이 증가합니다.
NIC 수신 필터링 — HW/SW 경로 수신 프레임 NIC HW 필터 RAR 매칭 (유니캐스트) MTA 해시 매칭 (멀티캐스트) 브로드캐스트 항상 통과 MATCH NO MATCH DMA → RX Ring NAPI IFF_PROMISC 확인 ON OFF DROP SW 필터 (커널) 주요 NIC별 HW 유니캐스트 필터(RAR) 용량 e1000e: 15슬롯 ixgbe: 128슬롯 i40e: 256슬롯 mlx5: 512슬롯 RAR 슬롯 초과 시 → 드라이버가 IFF_PROMISC 자동 전환 → 모든 프레임 수신 후 SW 필터링 프로미스큐어스 모드는 CPU 부하 증가 — macvlan 수가 많을 때 성능 병목의 주 원인
수신 필터링: HW 매치(RAR/MTA) → 즉시 DMA 전달 | HW 미매치 → 프로미스큐어스 시 SW 필터, 아니면 DROP

ARP와 Neighbor 서브시스템

IP 패킷을 전송하려면 먼저 다음 홉(next-hop)의 MAC 주소를 알아야 합니다. IPv4에서는 ARP(Address Resolution Protocol), IPv6에서는 NDP(Neighbor Discovery Protocol)가 이 역할을 수행합니다. 커널은 이를 Neighbor 서브시스템으로 통합 추상화합니다.

ARP 해석 흐름 (IP → MAC 변환) 호스트 A 192.168.1.10 호스트 B 192.168.1.20 1단계: IP 패킷 전송 시도 ip_finish_output2() → neigh_resolve_output() Neighbor 캐시 조회 (MISS!) 2단계: ARP Request (브로드캐스트) DST MAC: FF:FF:FF:FF:FF:FF ARP: "192.168.1.20의 MAC을 알려주세요" (arp_send_dst()) 3단계: ARP Reply (유니캐스트) SRC MAC: AA:BB:CC:DD:EE:FF (호스트 B의 실제 MAC) ARP: "192.168.1.20은 AA:BB:CC:DD:EE:FF입니다" (arp_process()) 4단계: Neighbor 캐시 업데이트 + 패킷 전송 neigh_update() → NUD_REACHABLE 상태 전이 대기 중이던 sk_buff 큐 flush → dev_queue_xmit() Neighbor 상태: NUD_NONE → NUD_INCOMPLETE → NUD_REACHABLE → NUD_STALE → NUD_DELAY → NUD_PROBE
ARP 해석: IP 전송 시 Neighbor 캐시 미스 → ARP Request 브로드캐스트 → ARP Reply 수신 → 캐시 갱신 후 대기 패킷 전송

Neighbor 서브시스템 주요 구조체

/* include/net/neighbour.h — struct neighbour */
struct neighbour {
    struct neighbour __rcu *next;  /* 해시 체인 */
    struct neigh_table    *tbl;    /* arp_tbl 또는 nd_tbl */
    struct net_device     *dev;    /* 연결된 인터페이스 */
    unsigned long          updated;/* 마지막 갱신 시각 */
    u8                     nud_state; /* NUD_REACHABLE, NUD_STALE 등 */
    u8                     ha[ALIGN(MAX_ADDR_LEN, sizeof(unsigned long))];
                                   /* ↑ 해석된 하드웨어(MAC) 주소 */
    struct sk_buff_head    arp_queue; /* 해석 대기 중인 패킷 큐 */
    const struct neigh_ops *ops;   /* arp_generic_ops 등 */
    /* ... */
};

/* ARP 테이블 전역 인스턴스 */
struct neigh_table arp_tbl = {
    .family     = AF_INET,
    .key_len    = 4,           /* IPv4: 4바이트 키 */
    .hash       = arp_hash,
    .id         = "arp_cache",
    .gc_interval = 30 * HZ,   /* 가비지 컬렉션 주기: 30초 */
    /* ... */
};

ARP 관련 sysctl 튜닝 파라미터

sysctl 경로기본값설명
net.ipv4.neigh.default.gc_stale_time 60 NUD_STALE 상태 유지 시간 (초)
net.ipv4.neigh.default.base_reachable_time_ms 30000 NUD_REACHABLE 기본 유지 시간 (밀리초)
net.ipv4.neigh.default.gc_thresh1 128 GC 시작 임계값 (이 미만이면 GC 미수행)
net.ipv4.neigh.default.gc_thresh2 512 소프트 상한 (5초 이상 된 항목 GC 대상)
net.ipv4.neigh.default.gc_thresh3 1024 하드 상한 (강제 GC, 초과 시 새 항목 거부)
컨테이너 환경 주의: 많은 컨테이너가 동일 호스트에서 실행될 때 ARP 캐시가 gc_thresh3에 도달하면 새로운 Neighbor 항목을 생성하지 못해 네트워크 연결이 실패할 수 있습니다. Kubernetes 등에서는 gc_thresh1=4096, gc_thresh2=8192, gc_thresh3=16384 수준으로 상향 조정이 필요합니다.

Neighbor 상태 머신 (NUD States)

커널의 Neighbor 서브시스템은 각 이웃 항목을 유한 상태 머신으로 관리합니다. 상태 전이에 따라 ARP 재확인, 에이징, 가비지 컬렉션이 자동으로 수행됩니다.

Neighbor(ARP) 상태 머신 — NUD State Transitions NUD_NONE 초기 (항목 없음) NUD_INCOMPLETE ARP Request 전송 중 NUD_REACHABLE 확인됨 (base_reachable_time) NUD_STALE 오래됨 (재확인 필요) NUD_DELAY 대기 (5초, 상위 힌트) NUD_PROBE 유니캐스트 ARP 재확인 NUD_FAILED 해석 실패 (삭제 예정) 해석 요청 Reply 수신 타이머 만료 트래픽 사용 타이머 만료 Reply 수신 재시도 초과 타이머 만료 주요 전이 타이밍 REACHABLE → STALE: base_reachable_time (기본 30초, 랜덤 범위 15~45초) STALE → DELAY: 실제 트래픽이 이 이웃을 사용할 때 전이 (대기 5초 동안 상위 프로토콜 확인 힌트 기다림) DELAY → PROBE: 힌트 미수신 시 유니캐스트 ARP로 직접 확인 (최대 3회, ucast_solicit) PROBE → FAILED: 재시도 초과 → 항목 삭제 | PROBE → REACHABLE: Reply 수신 시 즉시 복귀
NUD 상태 머신: NONE→INCOMPLETE→REACHABLE→STALE→DELAY→PROBE→FAILED/REACHABLE 순환

Gratuitous ARP와 Proxy ARP

ARP에는 일반적인 해석 외에 두 가지 특수 동작이 있습니다.

유형동작용도커널 설정
Gratuitous ARP 자기 자신의 IP를 목적지로 하는 ARP Request를 브로드캐스트 IP 충돌 감지, MAC 변경 알림, VRRP/HSRP 페일오버 net.ipv4.conf.all.arp_accept
Proxy ARP 다른 호스트를 대신하여 ARP Reply 응답 (라우터/게이트웨이가 수행) 서브넷 간 통신 지원, 브릿지 없는 컨테이너 연결 net.ipv4.conf.eth0.proxy_arp
# Gratuitous ARP 전송 (MAC 변경 후 네트워크에 알림)
arping -U -I eth0 192.168.1.10   # ARP Request 형태
arping -A -I eth0 192.168.1.10   # ARP Reply 형태

# Proxy ARP 활성화 (특정 인터페이스)
sysctl -w net.ipv4.conf.eth0.proxy_arp=1

# Gratuitous ARP 수락 정책
sysctl -w net.ipv4.conf.all.arp_accept=1   # GARP로 새 이웃 항목 생성 허용
sysctl -w net.ipv4.conf.all.arp_accept=0   # 기존 항목 갱신만 허용 (기본값)
Kubernetes의 Proxy ARP 활용: 일부 CNI 플러그인(Calico 등)은 각 Pod에 /32 경로를 할당하고 호스트에서 Proxy ARP를 활성화하여, Pod가 브리지 없이도 외부와 통신할 수 있게 합니다. Pod가 게이트웨이 MAC을 ARP 요청하면, 호스트가 자신의 MAC으로 대신 응답합니다.

IPv6 Neighbor Discovery (NDP)

IPv6에서는 ARP 대신 NDP(Neighbor Discovery Protocol)가 MAC 주소 해석을 담당합니다. NDP는 ICMPv6 위에서 동작하며, 주소 해석뿐 아니라 라우터 발견, 중복 주소 감지(DAD), 자동 주소 구성(SLAAC) 등 IPv4 ARP보다 훨씬 넓은 범위의 기능을 수행합니다.

ARP vs NDP 비교
항목 ARP (IPv4) NDP (IPv6)
프로토콜 독립 L2 프로토콜 (EtherType 0x0806) ICMPv6 (IPv6 Next Header 58)
주소 해석 ARP Request/Reply (브로드캐스트) NS/NA (Solicited-Node 멀티캐스트)
라우터 발견 DHCP 또는 수동 설정 RS/RA (Router Solicitation/Advertisement)
중복 감지 Gratuitous ARP (선택적) DAD (Duplicate Address Detection, 필수)
멀티캐스트 사용 브로드캐스트 (ff:ff:ff:ff:ff:ff) Solicited-Node 멀티캐스트 (ff02::1:ffXX:XXXX)
커널 테이블 arp_tbl (neigh_table) nd_tbl (neigh_table)
보안 ARP Spoofing에 취약 SEND(Secure NDP) 지원, RA Guard 가능
헤더 내 MAC sha/tha 필드 (각 6바이트) Source/Target Link-Layer Address 옵션

ICMPv6 NS/NA 메시지와 커널 함수

NDP의 핵심은 NS(Neighbor Solicitation)NA(Neighbor Advertisement) 메시지입니다. NS는 "이 IPv6 주소의 MAC은 무엇인가?"를 묻고, NA가 응답합니다.

메시지 ICMPv6 Type 목적 커널 함수
NS (Neighbor Solicitation) 135 타겟 IPv6의 MAC 질의, DAD 수행 ndisc_send_ns()
NA (Neighbor Advertisement) 136 NS에 대한 응답, MAC 주소 전달 ndisc_recv_na()
RS (Router Solicitation) 133 라우터 존재 질의 ndisc_send_rs()
RA (Router Advertisement) 134 네트워크 프리픽스, MTU, 기본 라우터 공지 ndisc_router_discovery()
Redirect 137 더 적합한 next-hop 라우터 알림 ndisc_redirect_rcv()

DAD (Duplicate Address Detection)

IPv6 인터페이스가 주소를 사용하기 전에, 해당 주소가 네트워크에 이미 존재하는지 확인하는 과정입니다. 커널은 타겟을 자신의 주소로 설정한 NS를 Solicited-Node 멀티캐스트 그룹으로 전송하고, 지정된 시간 내에 NA 응답이 없으면 주소를 확정합니다.

# DAD 관련 sysctl
# DAD 시도 횟수 (0=DAD 비활성화, 1=기본)
sysctl -w net.ipv6.conf.eth0.dad_transmits=1

# DAD 실패 시 주소를 tentative 상태로 유지하는 대신 제거
sysctl -w net.ipv6.conf.eth0.accept_dad=1

# DAD 진행 중인 주소 확인 (tentative 플래그)
ip -6 addr show dev eth0 tentative

# 강화된 DAD (RFC 7527): 루프백 감지
sysctl -w net.ipv6.conf.eth0.enhanced_dad=1

SLAAC + EUI-64 자동 구성

SLAAC(Stateless Address Autoconfiguration)은 라우터가 RA로 공지한 네트워크 프리픽스(64비트)에 호스트가 자체 생성한 인터페이스 ID(64비트)를 결합하여 전체 128비트 IPv6 주소를 만듭니다. 전통적인 EUI-64 방식은 MAC 주소를 기반으로 인터페이스 ID를 생성합니다.

EUI-64 변환 과정:
  MAC:   AA:BB:CC:DD:EE:FF  (48비트)
       ↓ 중간에 FF:FE 삽입
  EUI-64: AA:BB:CC:FF:FE:DD:EE:FF  (64비트)
       ↓ 7번째 비트(U/L bit) 반전
  IID:   A8:BB:CC:FF:FE:DD:EE:FF

  프리픽스(RA): 2001:db8:1::/64
  최종 주소:    2001:db8:1::a8bb:ccff:fedd:eeff/64
EUI-64의 프라이버시 문제: EUI-64로 생성된 IPv6 주소에는 MAC 주소가 그대로 포함되어 있어, 네트워크를 이동해도 동일한 인터페이스 ID가 노출됩니다. 이를 해결하기 위해 RFC 4941의 Privacy Extensions(use_tempaddr=2)가 도입되었으며, 최신 커널은 RFC 7217의 Stable Privacy Addresses(addr_gen_mode=2)를 권장합니다.
NDP 관련 주요 sysctl 파라미터
파라미터 기본값 설명
net.ipv6.conf.*.dad_transmits 1 DAD NS 전송 횟수. 0이면 DAD 비활성화
net.ipv6.conf.*.accept_dad 1 DAD 실패 시 동작: 0=무시, 1=주소 비활성화, 2=인터페이스 비활성화
net.ipv6.conf.*.use_tempaddr 0 Privacy Extensions: 0=비활성, 1=생성하되 선호X, 2=생성+선호
net.ipv6.conf.*.addr_gen_mode 0 주소 생성 모드: 0=EUI-64, 1=none, 2=stable-privacy, 3=random
net.ipv6.conf.*.accept_ra 1 RA 수락 여부: 0=거부, 1=forwarding 비활성 시 수락, 2=항상 수락
net.ipv6.conf.*.router_solicitations 3 인터페이스 활성화 시 RS 전송 횟수
net.ipv6.neigh.*.base_reachable_time_ms 30000 Neighbor 엔트리 reachable 상태 유지 기본 시간(ms)
net.ipv6.neigh.*.gc_stale_time 60 stale 상태 neighbor 엔트리 GC 대기 시간(초)
NDP Neighbor Solicitation / Advertisement 흐름 Host A 2001:db8::1 MAC: AA:BB:CC:11:22:33 Host B 2001:db8::2 MAC: DD:EE:FF:44:55:66 NS: Who has 2001:db8::2? Neighbor Solicitation (ICMPv6 Type 135) Src: 2001:db8::1 → Dst: ff02::1:ff00:0002 (Solicited-Node) Src MAC: AA:BB:CC:11:22:33 → Dst MAC: 33:33:ff:00:00:02 Target: 2001:db8::2 | Option: Source Link-Layer Addr = AA:BB:CC:11:22:33 커널: ndisc_send_ns() → ndisc_build_skb() NA: 2001:db8::2 is at DD:EE:FF:44:55:66 Neighbor Advertisement (ICMPv6 Type 136) Src: 2001:db8::2 → Dst: 2001:db8::1 (유니캐스트) Flags: S=1(Solicited), O=1(Override) Target: 2001:db8::2 | Option: Target Link-Layer Addr = DD:EE:FF:44:55:66 커널: ndisc_recv_na() → neigh_update() nd_tbl neighbor 캐시 갱신 2001:db8::2 → DD:EE:FF:44:55:66 (REACHABLE 상태, base_reachable_time_ms 후 STALE) IPv6 패킷 전송 가능 이더넷 Dst MAC: DD:EE:FF:44:55:66 → Host B의 NIC가 수신 ip6_output() → neigh_resolve_output() → dev_queue_xmit()
NDP NS/NA 흐름: Solicited-Node 멀티캐스트로 질의 → 유니캐스트 응답 → nd_tbl 캐시 저장
SLAAC + EUI-64 자동구성 흐름 1. 인터페이스 Up Link-Local 주소 생성 fe80::IID 2. DAD 수행 Link-Local 충돌 검사 NS → Solicited-Node 3. RS 전송 라우터 탐색 요청 ff02::2 (all-routers) 4. RA 수신 프리픽스: 2001:db8::/64 M=0, A=1 (SLAAC) 5. EUI-64 인터페이스 ID 생성 (addr_gen_mode=0) MAC 주소: AA BB CC DD EE FF EUI-64 IID: A8 BB CC FF FE DD EE FF U/L 비트 반전 AA(1010 1010) → A8(1010 1000) FF:FE 삽입 6. 글로벌 유니캐스트 주소 조합 2001:db8::a8bb:ccff:fedd:eeff/64 7. DAD 수행 (글로벌 주소) → 확정 후 PREFERRED 상태
SLAAC 흐름: 인터페이스 Up → Link-Local DAD → RS/RA → EUI-64 IID 생성 → 글로벌 주소 DAD → 사용 가능
IPv6 Neighbor 캐시 확인: ip -6 neigh show로 nd_tbl 캐시 상태를 확인할 수 있습니다. 상태 값은 INCOMPLETE → REACHABLE → STALE → DELAY → PROBE → FAILED 순으로 전이됩니다. ip -6 neigh flush dev eth0으로 특정 인터페이스의 캐시를 초기화할 수 있습니다.

Bridge FDB와 MAC 학습

리눅스 브리지는 물리적 이더넷 스위치처럼 MAC 주소를 학습하여 포트 간 프레임을 효율적으로 전달합니다. 이 학습 결과를 저장하는 테이블이 FDB(Forwarding Database)입니다.

Bridge FDB MAC 학습 과정 Linux Bridge (br0) Port 1 (eth0) Port 2 (eth1) Port 3 (eth2) Port 4 (veth0) 학습 과정: 1. Port 1(eth0)에서 프레임 수신 — SRC MAC: AA:11:22:33:44:55 br_handle_frame() → br_handle_frame_finish() 2. 출발지 MAC 학습 — fdb_insert() → br_fdb_update(br, port1, AA:11:22:33:44:55, vid) FDB 엔트리 생성: {MAC=AA:11:22:33:44:55, Port=eth0, Timestamp=jiffies} 3. 목적지 MAC 조회 — br_fdb_find_rcu(br, dst_mac, vid) HIT → 해당 포트로만 전달 | MISS → 모든 포트에 플러딩 (br_flood()) FDB 테이블 예시 (bridge fdb show) AA:11:22:33:44:55 dev eth0 master br0 — 학습 (dynamic) BB:66:77:88:99:00 dev eth1 master br0 — 학습 (dynamic) CC:AA:BB:CC:DD:EE dev eth2 master br0 self permanent — 정적
Bridge FDB: 수신 프레임의 출발지 MAC을 포트와 연결하여 학습하고, 목적지 MAC 조회 결과에 따라 유니캐스트 전달 또는 플러딩 결정

FDB 주요 구조체와 함수

/* net/bridge/br_fdb.c — FDB 엔트리 */
struct net_bridge_fdb_entry {
    struct rhash_head       rhnode;    /* 해시 테이블 노드 */
    struct net_bridge_port  *dst;      /* 학습된 포트 */
    unsigned long           updated;   /* 마지막 갱신 jiffies */
    unsigned long           used;      /* 마지막 사용 jiffies */
    mac_addr                addr;      /* MAC 주소 */
    __u16                   vlan_id;   /* VLAN ID (VLAN 필터링 시) */
    unsigned char           is_local:1,  /* 브리지 자체 MAC */
                            is_static:1, /* 정적 FDB 항목 */
                            added_by_user:1;
    /* ... */
};

/* FDB 에이징: 기본 300초 (5분) 후 동적 항목 만료 */
/* bridge fdb show / bridge fdb add 로 정적 항목 관리 */

FDB 운영 명령

# FDB 테이블 조회
bridge fdb show br br0

# 정적 FDB 항목 추가 (특정 MAC → 특정 포트 고정)
bridge fdb add AA:BB:CC:DD:EE:FF dev eth0 master static

# 정적 항목 삭제
bridge fdb del AA:BB:CC:DD:EE:FF dev eth0 master

# 에이징 시간 변경 (기본 300초)
ip link set br0 type bridge ageing_time 600

대규모 FDB 테이블 관리와 튜닝

클라우드 및 데이터센터 환경에서는 브리지 하나에 수천~수만 개의 MAC 항목이 학습될 수 있습니다. FDB 해시 테이블의 크기, 에이징 정책, VLAN 필터링 설정이 성능에 직접적인 영향을 미칩니다.

Bridge FDB 핵심 파라미터
파라미터 기본값 설명 설정 방법
ageing_time 300 (초) 동적 FDB 항목의 만료 시간. 0으로 설정하면 에이징 비활성화 (정적 환경에 적합) ip link set br0 type bridge ageing_time <초>
hash_max 512 FDB 해시 테이블 버킷 수. 대규모 환경에서는 4096 이상으로 증가 echo 4096 > /sys/class/net/br0/bridge/hash_max
hash_elasticity 4 버킷당 최대 체인 길이. 초과 시 해시 확장 시도 echo 16 > /sys/class/net/br0/bridge/hash_elasticity
multicast_snooping 1 (on) IGMP/MLD 스누핑으로 멀티캐스트 FDB 최적화 ip link set br0 type bridge mcast_snooping 1
vlan_filtering 0 (off) VLAN별 FDB 분리. 대규모 멀티테넌트에서 필수 ip link set br0 type bridge vlan_filtering 1
FDB 규모별 성능 특성 (참고 수치)
FDB 항목 수 해시 조회 시간 메모리 사용 권장 hash_max 비고
~1K ~50ns ~100KB 512 (기본) 일반 사무실/소규모 환경
~10K ~100ns ~1MB 2048 중형 데이터센터, VLAN 필터링 권장
~100K ~200ns+ ~10MB 8192+ 대규모 클라우드. HW offload 또는 EVPN 고려
# 대규모 FDB 환경 튜닝 스크립트
BRIDGE=br0

# 해시 테이블 확대 (항목 수 대비 충분한 버킷)
echo 4096 > /sys/class/net/$BRIDGE/bridge/hash_max
echo 16   > /sys/class/net/$BRIDGE/bridge/hash_elasticity

# 에이징 시간 단축 (오래된 항목 빠르게 제거)
ip link set $BRIDGE type bridge ageing_time 120

# VLAN 필터링 활성화 (멀티테넌트 분리)
ip link set $BRIDGE type bridge vlan_filtering 1

# 멀티캐스트 스누핑 활성화
ip link set $BRIDGE type bridge mcast_snooping 1

# FDB 항목 수 모니터링
bridge fdb show br $BRIDGE | wc -l

# GC(Garbage Collection) 주기 관련 커널 파라미터
sysctl -w net.ipv4.neigh.default.gc_interval=30
sysctl -w net.ipv4.neigh.default.gc_stale_time=60

# 실시간 FDB 변경 모니터링
bridge monitor fdb
HW Offload: 최신 스마트NIC(Mellanox ConnectX, Intel E810 등)은 FDB를 하드웨어 TCAM으로 오프로드합니다. bridge fdb add ... offload 플래그가 표시되면 해당 항목은 하드웨어에서 직접 조회됩니다. 10만 이상의 MAC 항목이 필요한 환경에서는 HW offload 또는 EVPN+VXLAN 구조를 검토하세요.

가상 디바이스와 MAC 할당

가상화와 컨테이너 환경에서는 하나의 물리 NIC 위에 여러 가상 인터페이스가 생성되며, 각각 고유한 MAC 주소가 필요합니다. 리눅스 커널은 다양한 가상 네트워크 디바이스를 제공합니다.

가상 디바이스별 MAC 할당 방식

디바이스 유형MAC 할당 방식물리 NIC 관계성능 특성
macvlan 사용자 지정 또는 랜덤 로컬 MAC 하위 인터페이스의 유니캐스트 리스트에 등록 브리지 없이 직접 L2 분리, 높은 성능
macvtap macvlan과 동일 + TAP 인터페이스 제공 동일 (macvlan 기반) VM 네트워킹에 최적화
veth pair 각 끝에 독립 랜덤 로컬 MAC 가상 케이블, 물리 NIC 무관 컨테이너 네트워킹 표준
bridge port 물리 NIC MAC 또는 별도 할당 프로미스큐어스 모드 강제 범용 L2 스위칭
bond/team 첫 번째 슬레이브 MAC 또는 지정 MAC 모든 슬레이브가 동일 MAC 공유 링크 집선/이중화
dummy 랜덤 로컬 MAC 물리 NIC 없음 루프백 대체, 테스트용

macvlan 동작 모드와 MAC 처리

모드설명MAC 격리
bridge macvlan 인터페이스 간 직접 통신 가능 각 macvlan이 고유 MAC, FDB 기반 전달
vepa 모든 트래픽이 외부 스위치로 전달 (hairpin) 외부 스위치가 MAC 학습/전달 담당
private macvlan 간 완전 격리 같은 하위 인터페이스의 macvlan끼리 통신 불가
passthru 하위 인터페이스를 단일 macvlan이 독점 하위 인터페이스 MAC을 그대로 사용
# macvlan 생성 (로컬 관리 MAC 자동 할당)
ip link add macvlan0 link eth0 type macvlan mode bridge

# 특정 MAC 지정
ip link add macvlan1 link eth0 address 02:42:AC:11:00:01 type macvlan mode bridge

# veth pair 생성 (컨테이너 연결용)
ip link add veth-host type veth peer name veth-ns
# 양쪽 모두 자동으로 랜덤 로컬 MAC 할당됨 (U/L bit = 1)
로컬 관리 MAC 생성 규칙: 커널의 eth_random_addr()는 랜덤 MAC을 생성한 뒤 addr[0] |= 0x02로 U/L 비트를 설정하고 addr[0] &= 0xFE로 I/G 비트를 클리어합니다. 이렇게 하면 IEEE 할당 주소와 절대 충돌하지 않는 로컬 유니캐스트 MAC이 됩니다.

macvlan 모드별 트래픽 경로

macvlan 4가지 모드 — 트래픽 경로 비교 bridge 모드 mv0 mv1 내부 L2 스위칭 eth0 (하위) mv0↔mv1 직접 통신 vepa 모드 mv0 mv1 eth0 (하위) 외부 스위치 경유 (hairpin) private 모드 mv0 mv1 eth0 (하위) mv0↔mv1 완전 차단 passthru 모드 mv0 (1개) eth0 (하위) 단일 macvlan이 독점 모드 비교 요약 bridge: macvlan 간 직접 L2 스위칭. 가장 범용적. Docker/Podman에서 주로 사용. vepa: 모든 트래픽이 외부 스위치로 나감. 스위치 정책(ACL, QoS) 적용 가능. 802.1Qbg 필요. private: 같은 하위 인터페이스의 macvlan끼리 완전 격리. 보안이 중요한 멀티테넌트 환경. passthru: 단일 macvlan이 하위 인터페이스를 독점. 802.1Qbh VN-Tag, SR-IOV 없이 VM 직접 연결. 모든 모드에서 macvlan은 고유한 MAC 주소를 가지며, 하위 NIC의 유니캐스트 리스트에 등록됨
macvlan 4모드: bridge(내부 스위칭), vepa(외부 경유), private(완전 격리), passthru(독점 사용)

Bonding/Teaming MAC 동작

Linux bonding(bond0)과 teaming(team0)은 여러 물리 NIC를 하나의 논리 인터페이스로 묶습니다. 본딩 모드에 따라 슬레이브 NIC들의 MAC 주소 관리 방식이 완전히 다르므로, 모드별 동작을 정확히 이해해야 합니다.

Bonding 모드별 MAC 동작 비교
모드 이름 bond0 MAC 소스 슬레이브 MAC 변경 설명
0 balance-rr 첫 번째 슬레이브 모든 슬레이브 → bond0 MAC으로 통일 패킷을 라운드로빈으로 분배. 스위치 포트채널 필요
1 active-backup 활성 슬레이브 기본: 활성 슬레이브 MAC 유지 (fail_over_mac에 따라 다름) 하나만 활성, 나머지 대기. 가장 안전한 기본 모드
2 balance-xor 첫 번째 슬레이브 모든 슬레이브 → bond0 MAC src/dst MAC XOR 해시로 슬레이브 선택
3 broadcast 첫 번째 슬레이브 모든 슬레이브 → bond0 MAC 모든 슬레이브에 동시 전송. 특수 용도
4 802.3ad (LACP) 첫 번째 슬레이브 모든 슬레이브 → bond0 MAC 스위치와 LACP 협상. 대역폭 집약 + 이중화
5 balance-tlb 첫 번째 슬레이브 각 슬레이브 고유 MAC 유지 TX 부하 분산. 스위치 설정 불필요. 수신은 활성 슬레이브만
6 balance-alb 첫 번째 슬레이브 각 슬레이브 고유 MAC 유지 (ARP 협상으로 수신 분배) TX+RX 부하 분산. ARP reply로 피어에게 다른 슬레이브 MAC 전달

fail_over_mac 정책 (active-backup 모드 전용)

정책 동작 사용 시나리오
none 0 (기본) bond0 MAC을 활성 슬레이브에 강제 설정. 페일오버 시 새 활성 슬레이브의 MAC이 bond0 MAC으로 변경됨 일반적인 환경. 스위치 FDB가 빠르게 갱신됨
active 1 bond0 MAC이 현재 활성 슬레이브의 원래 MAC을 따라감. 페일오버 시 bond0 MAC 자체가 변경됨 슬레이브 MAC을 변경할 수 없는 경우 (일부 하드웨어 제약)
follow 2 bond0 MAC은 첫 번째 활성 슬레이브에 고정. 페일오버 시 새 활성 슬레이브의 MAC이 bond0 MAC으로 변경됨 bond0 MAC 일관성 유지가 중요한 환경
# bonding 생성 (802.3ad LACP 모드)
ip link add bond0 type bond mode 802.3ad

# LACP 파라미터 설정
ip link set bond0 type bond \
  lacp_rate fast \
  xmit_hash_policy layer3+4 \
  miimon 100

# 슬레이브 추가 (슬레이브 MAC → bond0 MAC으로 통일됨)
ip link set eth0 master bond0
ip link set eth1 master bond0
ip link set bond0 up

# MAC 확인 — 모든 슬레이브가 동일 MAC
ip link show bond0
ip link show eth0
ip link show eth1

# active-backup + fail_over_mac=active 설정
ip link add bond1 type bond mode active-backup
ip link set bond1 type bond fail_over_mac active
ip link set eth2 master bond1
ip link set eth3 master bond1
ip link set bond1 up

# teaming 드라이버 (대안)
ip link add team0 type team
teamdctl team0 config dump
active-backup vs 802.3ad (LACP) MAC 처리 비교 Mode 1: active-backup bond0 MAC: AA:BB:CC:DD:EE:01 eth0 (활성) MAC: AA:BB:CC:DD:EE:01 eth1 (대기) MAC: FF:FF:FF:00:00:02 페일오버 발생! eth0 (다운) 링크 단절 eth1 (활성 전환) MAC → AA:BB:CC:DD:EE:01 fail_over_mac=none: bond0 MAC을 새 활성 슬레이브에 강제 설정 Gratuitous ARP 전송 → 스위치 FDB 갱신 Mode 4: 802.3ad (LACP) bond0 MAC: AA:BB:CC:DD:EE:01 eth0 (LACP) MAC: AA:BB:CC:DD:EE:01 eth1 (LACP) MAC: AA:BB:CC:DD:EE:01 스위치 (LAG/Port Channel) 동일 MAC → 하나의 LAG로 인식 모든 슬레이브가 동일 MAC 사용 xmit_hash_policy로 TX 분배 LACPDU 교환으로 링크 상태 감시 lacp_rate: slow(30초) / fast(1초)
active-backup: 페일오버 시 MAC 이전 + Gratuitous ARP / 802.3ad: 모든 슬레이브 동일 MAC + LACP 협상

SR-IOV와 VF MAC 관리

SR-IOV(Single Root I/O Virtualization)는 하나의 물리 NIC(PF)에서 여러 가상 기능(VF)을 생성하여 각각 독립적인 MAC 주소와 TX/RX 큐를 가지게 합니다. VM이나 컨테이너에 VF를 직접 할당(passthrough)하면 소프트웨어 브리지 오버헤드 없이 네이티브에 가까운 성능을 얻습니다.

PF/VF MAC 관리 체계

항목PF (Physical Function)VF (Virtual Function)
MAC 소유권 EEPROM/펌웨어 영구 MAC PF 드라이버가 할당 (관리자 지정 또는 랜덤)
변경 권한 root만 가능 PF 관리자가 허용한 경우에만 VF 자체 변경 가능
HW 필터 RAR 슬롯 0 (고정) RAR 슬롯 1~N (VF별 할당)
스푸핑 방지 N/A ip link set eth0 vf 0 spoofchk on
# VF 생성 (예: ixgbe NIC에서 4개 VF)
echo 4 > /sys/class/net/eth0/device/sriov_numvfs

# VF에 MAC 주소 할당 (PF 관리자 권한)
ip link set eth0 vf 0 mac 02:00:00:00:00:01
ip link set eth0 vf 1 mac 02:00:00:00:00:02

# VF의 MAC 스푸핑 방지 활성화
ip link set eth0 vf 0 spoofchk on

# VF VLAN 태깅
ip link set eth0 vf 0 vlan 100

# VF 상태 확인
ip link show eth0
# → vf 0 MAC 02:00:00:00:00:01, spoof checking on, link-state auto, vlan 100
MAC 스푸핑 방지: spoofchk on이 활성화되면 VF가 자신에게 할당된 MAC과 다른 출발지 MAC으로 프레임을 전송하려 할 때 NIC 하드웨어가 프레임을 드롭합니다. 이는 악의적인 VM이 다른 VM의 트래픽을 가로채는 것을 방지하는 핵심 보안 메커니즘입니다.

MAC 보안 위협과 방어

MAC 주소는 L2 수준에서 신뢰를 기반으로 동작하기 때문에 다양한 공격에 취약합니다. 리눅스 커널과 네트워크 인프라에서 제공하는 방어 메커니즘을 이해하는 것이 중요합니다.

MAC 기반 L2 공격과 방어 메커니즘 공격 (Attack) ARP Spoofing / ARP Poisoning 위조 ARP Reply로 피해자의 ARP 캐시 오염 MAC Flooding 대량의 위조 MAC으로 스위치 FDB 테이블 포화 MAC Spoofing 다른 호스트의 MAC을 도용하여 트래픽 가로채기 DHCP Starvation 위조 MAC으로 DHCP 주소 풀 고갈 방어 (Defense) DAI (Dynamic ARP Inspection) DHCP Snooping DB와 대조하여 위조 ARP 차단 Port Security / FDB 제한 포트당 MAC 학습 수 제한, 초과 시 차단 SR-IOV spoofchk / ebtables 하드웨어/소프트웨어 MAC 스푸핑 차단 DHCP Snooping 신뢰 포트만 DHCP 응답 허용, MAC-IP 바인딩 리눅스 커널 L2 방어 도구 ebtables / nftables bridge vlan filter TC cls_flower eBPF XDP/TC SR-IOV spoofchk ARP Spoofing MITM 시나리오 피해자 A 공격자 M 피해자 B "B의 MAC은 M이야" "A의 MAC은 M이야" A↔B 모든 트래픽이 공격자 M을 경유 (MITM)
L2 공격: ARP Spoofing(MITM), MAC Flooding(FDB 포화), MAC Spoofing(도용), DHCP Starvation과 대응 방어 메커니즘

ARP Spoofing 공격 원리

ARP는 인증 메커니즘이 없어, 누구든 위조된 ARP Reply를 전송할 수 있습니다. 공격자가 "게이트웨이의 IP는 내 MAC이야"라는 위조 ARP Reply를 지속 전송하면, 피해자의 ARP 캐시가 오염되어 모든 외부 트래픽이 공격자를 경유합니다 (MITM).

# ARP Spoofing 탐지: 동일 IP에 대해 MAC이 계속 변경되는지 확인
ip neigh show | grep -i "192.168.1.1"
# STALE/REACHABLE 상태에서 MAC이 자주 바뀌면 의심

# arptables로 정적 ARP 바인딩 (방어)
arp -s 192.168.1.1 AA:BB:CC:DD:EE:FF

# nftables로 특정 포트의 ARP 응답 제한 (Linux Bridge 방어)
nft add rule bridge filter forward ether type arp arp operation reply \
    arp saddr ip 192.168.1.1 arp saddr ether != AA:BB:CC:DD:EE:FF drop

MAC Flooding 공격과 방어

공격자가 임의의 MAC 주소를 대량으로 전송하면 스위치/브리지의 FDB 테이블이 포화되어, 학습되지 않은 프레임을 모든 포트로 플러딩합니다. 이를 통해 공격자는 다른 포트의 트래픽을 엿볼 수 있습니다.

# Linux Bridge: 포트당 FDB 학습 수 제한
bridge link set dev eth0 learning_limit 100

# Bridge FDB 크기 제한 (전체)
ip link set br0 type bridge fdb_max_learned 4096

# ebtables로 특정 포트의 출발지 MAC 화이트리스트
ebtables -A FORWARD -i eth0 -s ! 00:1A:2B:3C:4D:5E -j DROP

리눅스 커널의 MAC 보안 기능 요약

기능계층설명설정 방법
SR-IOV spoofchk HW VF의 출발지 MAC 검증, 불일치 시 HW 드롭 ip link set eth0 vf 0 spoofchk on
Bridge Port Isolation SW 브리지 포트 간 직접 통신 차단 bridge link set dev eth0 isolated on
ebtables MAC 필터 SW L2 프레임의 MAC 기반 필터링 ebtables -A FORWARD -s MAC -j DROP
nftables bridge SW nftables의 bridge family로 L2 필터링 nft add table bridge filter
eBPF TC/XDP SW/HW 프로그래머블 MAC 검증 및 정책 BPF 프로그램 attach
정적 ARP 항목 SW 핵심 호스트의 MAC을 수동 고정 ip neigh add ... nud permanent
Bridge VLAN 필터 SW VLAN별 트래픽 분리로 공격 범위 축소 bridge vlan add dev eth0 vid 100

실전 방어 규칙: ebtables / nftables / eBPF

L2 수준의 MAC 기반 보안은 커널의 브리지 넷필터(ebtables), nftables bridge 패밀리, eBPF tc-bpf 프로그램으로 구현합니다. 각각의 특성과 사용 시나리오가 다르므로 환경에 맞는 도구를 선택합니다.

ebtables: ARP Spoofing 방어

# ARP Spoofing 방어: 특정 포트에서 허용된 MAC만 ARP Reply 가능
ebtables -A FORWARD -p ARP --arp-op Reply \
  --arp-mac-src ! AA:BB:CC:DD:EE:FF -i eth1 -j DROP

# 알려지지 않은 소스 MAC 차단 (화이트리스트 방식)
ebtables -N ALLOWED_MACS
ebtables -A ALLOWED_MACS -s 00:11:22:33:44:55 -j RETURN
ebtables -A ALLOWED_MACS -s 00:11:22:33:44:66 -j RETURN
ebtables -A ALLOWED_MACS -j DROP

# FORWARD/INPUT 체인에 적용
ebtables -A FORWARD -i eth1 -j ALLOWED_MACS
ebtables -A INPUT -i eth1 -j ALLOWED_MACS

# MAC-IP 바인딩 (ARP 정적 검증)
ebtables -A FORWARD -p ARP --arp-ip-src 192.168.1.100 \
  --arp-mac-src ! 00:11:22:33:44:55 -j DROP

nftables: bridge 패밀리 MAC 필터링

# nftables bridge 패밀리로 L2 필터링 (nft 0.9+)
nft add table bridge filter
nft add chain bridge filter forward \
  '{ type filter hook forward priority 0; policy accept; }'

# 특정 소스 MAC 차단
nft add rule bridge filter forward \
  ether saddr AA:BB:CC:DD:EE:FF drop

# VLAN 100에서만 특정 MAC 허용
nft add rule bridge filter forward \
  vlan id 100 ether saddr != { 00:11:22:33:44:55, 00:11:22:33:44:66 } drop

# ARP Reply에서 MAC-IP 바인딩 검증
nft add rule bridge filter forward \
  arp operation reply \
  arp saddr ether != 00:11:22:33:44:55 \
  arp saddr ip 192.168.1.100 \
  drop

# 규칙 목록 확인
nft list table bridge filter

eBPF tc-bpf: 프로그래밍 가능 MAC 필터

/* tc-bpf MAC 필터 예시 (tc ingress에서 허용 MAC만 통과) */
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>

/* 허용 MAC 목록을 담는 해시 맵 */
struct {
    __uint(type, BPF_MAP_TYPE_HASH);
    __uint(max_entries, 256);
    __type(key, __u8[ETH_ALEN]);   /* 6바이트 MAC */
    __type(value, __u32);          /* 1=허용 */
} allowed_macs SEC(".maps");

SEC("tc")
int mac_filter(struct __sk_buff *skb)
{
    void *data     = (void *)(long)skb->data;
    void *data_end = (void *)(long)skb->data_end;

    /* 이더넷 헤더 바운드 체크 */
    struct ethhdr *eth = data;
    if ((void *)(eth + 1) > data_end)
        return TC_ACT_SHOT;

    /* 소스 MAC을 맵에서 조회 */
    __u32 *val = bpf_map_lookup_elem(&allowed_macs, eth->h_source);
    if (!val)
        return TC_ACT_SHOT;  /* 미등록 MAC → 드롭 */

    return TC_ACT_OK;        /* 허용된 MAC → 통과 */
}

char _license[] SEC("license") = "GPL";
# eBPF 프로그램 컴파일 및 적용
clang -O2 -target bpf -c mac_filter.c -o mac_filter.o

# tc ingress에 부착
tc qdisc add dev br0 clsact
tc filter add dev br0 ingress bpf da obj mac_filter.o sec tc

# 허용 MAC 등록 (bpftool 사용)
bpftool map update name allowed_macs \
  key hex 00 11 22 33 44 55 value hex 01 00 00 00

# 필터 상태 확인
tc filter show dev br0 ingress
도구 선택 가이드: ebtables는 간단한 규칙에 적합하지만 nftables로 대체 추세입니다. nftables bridge는 ebtables의 후계자로 VLAN 필터링, 세트 매칭 등 고급 기능을 제공합니다. eBPF tc-bpf는 동적 맵 갱신이 가능하여 대규모 MAC 필터링, 통계 수집, 클라우드 환경에서 가장 유연합니다.
Gratuitous ARP의 양면성: Gratuitous ARP(자기 자신의 IP를 질의하는 ARP)는 IP 충돌 감지, VRRP/HSRP 페일오버, MAC 변경 알림 등 정당한 용도로 사용됩니다. 하지만 공격자도 같은 메커니즘으로 ARP 캐시를 오염시킬 수 있어, 정당한 Gratuitous ARP와 악의적인 것을 구분하는 것이 핵심입니다. net.ipv4.conf.all.arp_accept sysctl로 Gratuitous ARP 수락 정책을 제어할 수 있습니다.

MACsec (IEEE 802.1AE)

MACsec(Media Access Control Security)은 IEEE 802.1AE 표준으로 정의된 L2 홉 바이 홉 암호화 프로토콜입니다. 이더넷 프레임 단위로 기밀성(AES-GCM 암호화)과 무결성(ICV 검증)을 제공하며, IPsec이 L3 이상에서 동작하는 것과 달리 MACsec은 L2에서 직접 보호합니다.

MACsec 핵심 개념
용어 전체 이름 설명
SecY Security Entity MACsec 프로토콜의 논리적 인스턴스. 하나의 네트워크 포트에 하나의 SecY가 대응
SC Secure Channel 단방향 통신 채널. 송신/수신 각각 별도의 SC를 가짐
SA Secure Association SC 내의 암호화 세션. 키 교체 시 새 SA가 생성되며 AN(0~3)으로 구분
SAK Secure Association Key AES-GCM 128/256bit 대칭키. MKA 프로토콜로 자동 분배 또는 수동 설정
SCI Secure Channel Identifier MAC주소(6B) + Port ID(2B) = 8바이트. SecTAG에 포함되어 SC를 식별
PN Packet Number 32/64bit 시퀀스 번호. 리플레이 공격 방지에 사용
MKA MACsec Key Agreement IEEE 802.1X-2010 정의. CAK(Connectivity Association Key)로부터 SAK를 파생·분배
일반 이더넷 vs MACsec 프레임 구조 일반 이더넷: Dst MAC 6B Src MAC 6B EtherType 2B Payload (평문) 46~1500B FCS 4B MACsec (802.1AE): Dst MAC 6B Src MAC 6B SecTAG EtherType 0x88E5 + SCI + AN + PN Secure Data (암호화) 원본 EtherType + Payload (AES-GCM) ICV 16B FCS 4B SecTAG 상세 구조 (8~16바이트) EtherType: 0x88E5 (2B) TCI+AN: Tag Control + Association Number (1B) SL: Short Length (1B) | PN: Packet Number (4B) | SCI: MAC+Port (옵션 8B) ICV (Integrity Check Value) AES-GCM 인증 태그 (16B)
일반 이더넷 프레임과 MACsec 프레임 비교 — SecTAG(보안 태그)과 ICV(무결성 검증값)가 추가됨

리눅스 MACsec 설정

# MACsec 인터페이스 생성 (AES-GCM-128 암호화)
ip link add link eth0 macsec0 type macsec encrypt on

# 수신 SC 및 SA 설정 (상대방의 SCI와 키)
ip macsec add macsec0 rx port 1 address 00:11:22:33:44:55
ip macsec add macsec0 rx port 1 address 00:11:22:33:44:55 sa 0 \
  pn 1 on key 00 \
  aaaabbbbccccddddeeeeffffaaaabbbb

# 송신 SA 설정 (자신의 키)
ip macsec add macsec0 tx sa 0 pn 1 on key 01 \
  11112222333344445555666677778888

# 인터페이스 활성화
ip link set macsec0 up
ip addr add 192.168.100.1/24 dev macsec0

# MACsec 상태 확인
ip macsec show

# AES-GCM-256 사용 (더 강한 암호화)
ip link add link eth0 macsec1 type macsec \
  encrypt on cipher gcm-aes-256
MACsec 커널 소스 참조
소스 파일 역할
drivers/net/macsec.c MACsec 가상 디바이스 드라이버, SecY/SC/SA 관리
include/net/macsec.h MACsec 데이터 구조체 (struct macsec_secy, struct macsec_rx_sc)
include/uapi/linux/if_macsec.h Netlink 인터페이스 상수, 속성 정의
net/8021q/ 802.1Q VLAN — MACsec과 함께 사용 시 스택 순서 참고
HW Offload 지원: 최신 NIC(Intel E810, Mellanox ConnectX-6 Dx 이상)은 MACsec 암호화/복호화를 하드웨어에서 처리합니다. 커널 5.12+에서 macsec_ops 콜백을 통해 오프로드하며, ethtool -k eth0 | grep macsec로 지원 여부를 확인할 수 있습니다. HW offload 시 CPU 오버헤드 없이 라인 레이트에서 L2 암호화가 가능합니다.

Random MAC과 프라이버시

MAC 주소는 디바이스를 추적하는 데 사용될 수 있어, 최신 운영체제와 디바이스들은 MAC 주소 랜덤화(MAC Address Randomization)를 적극 도입하고 있습니다.

MAC 랜덤화 유형

유형시점목적구현
Wi-Fi 프로브 랜덤화 네트워크 스캔 시 AP가 스캔 요청으로 디바이스 추적 방지 iOS 8+, Android 8+, Windows 10+
연결별 랜덤화 네트워크 연결 시 네트워크별 다른 MAC으로 교차 추적 방지 iOS 14+, Android 10+
커널 addr_assign_type 인터페이스 생성 시 가상 디바이스의 자동 MAC 할당 NET_ADDR_RANDOM (3)
/* include/linux/netdevice.h — addr_assign_type 값 */
#define NET_ADDR_PERM       0   /* EEPROM/NIC 펌웨어 영구 주소 */
#define NET_ADDR_RANDOM     1   /* 커널이 랜덤 생성 */
#define NET_ADDR_STOLEN     2   /* 다른 디바이스에서 복사 */
#define NET_ADDR_SET        3   /* 사용자가 수동 설정 */

/* eth_hw_addr_random() — 드라이버가 EEPROM MAC이 없을 때 호출 */
void eth_hw_addr_random(struct net_device *dev)
{
    u8 addr[ETH_ALEN];
    eth_random_addr(addr);          /* 랜덤 + 로컬 유니캐스트 보장 */
    __dev_addr_set(dev, addr, ETH_ALEN);
    dev->addr_assign_type = NET_ADDR_RANDOM;
}
sysfs에서 확인: /sys/class/net/eth0/addr_assign_type으로 현재 MAC이 어떻게 할당되었는지 확인할 수 있습니다. 0이면 EEPROM 영구 주소, 1이면 커널 랜덤 생성, 3이면 사용자 설정입니다.

실전 디버깅과 도구

MAC 주소 관련 주요 명령

명령용도출력 예시
ip link show eth0 현재 MAC, 상태, MTU 조회 link/ether 00:1a:2b:3c:4d:5e brd ff:ff:ff:ff:ff:ff
ethtool -P eth0 영구(EEPROM) MAC 조회 Permanent address: 00:1a:2b:3c:4d:5e
ip neigh show ARP/Neighbor 캐시 조회 192.168.1.1 dev eth0 lladdr aa:bb:cc:dd:ee:ff REACHABLE
bridge fdb show Bridge FDB 테이블 조회 aa:bb:cc:dd:ee:ff dev eth0 master br0
ip maddr show 멀티캐스트 MAC 그룹 조회 link 01:00:5e:00:00:01
cat /sys/class/net/eth0/address sysfs를 통한 MAC 조회 00:1a:2b:3c:4d:5e

tcpdump로 MAC 수준 분석

# 특정 MAC에서 오는 프레임만 캡처
tcpdump -i eth0 ether src 00:1a:2b:3c:4d:5e -nn

# 특정 MAC으로 가는 프레임만 캡처
tcpdump -i eth0 ether dst ff:ff:ff:ff:ff:ff -nn

# ARP 패킷만 캡처 (MAC 해석 과정 관찰)
tcpdump -i eth0 arp -nn -e
# -e 옵션: 이더넷 헤더(MAC 주소) 출력

# VLAN 태그 포함 프레임 캡처
tcpdump -i eth0 vlan -nn -e

MAC 관련 트러블슈팅 체크리스트

증상가능한 원인진단 명령
통신 불가 (ARP 실패) MAC 충돌, 잘못된 VLAN, 스위치 포트 보안 arping -I eth0 192.168.1.1, tcpdump -i eth0 arp -e
간헐적 연결 끊김 MAC 주소 중복 (Duplicate MAC) arping -D -I eth0 -c 3 192.168.1.10 (중복 감지)
Bridge에서 플러딩 과다 FDB 학습 실패, 에이징 시간 과소 bridge fdb show, bridge -s fdb show
VM 네트워크 불통 SR-IOV spoofchk, MAC 미할당 ip link show eth0 (VF 상태), dmesg | grep -i spoof
macvlan 성능 저하 HW 유니캐스트 필터 초과 → 프로미스큐어스 ethtool -S eth0 | grep -i promisc
Neighbor 테이블 오버플로 gc_thresh3 초과 (컨테이너 대규모 환경) dmesg | grep "neighbour table overflow"

커널 tracepoint를 이용한 MAC 이벤트 추적

# Neighbor 이벤트 추적 (ARP 학습/만료)
echo 1 > /sys/kernel/debug/tracing/events/neigh/neigh_update/enable
cat /sys/kernel/debug/tracing/trace_pipe

# Bridge FDB 이벤트 추적
echo 1 > /sys/kernel/debug/tracing/events/bridge/fdb_delete/enable
echo 1 > /sys/kernel/debug/tracing/events/bridge/br_fdb_update/enable
cat /sys/kernel/debug/tracing/trace_pipe

# NET_DEVICE 이벤트 (MAC 변경 포함) perf 추적
perf trace -e 'net:*' -- sleep 5

OUI 레지스트리와 주요 벤더

IEEE는 OUI(Organizationally Unique Identifier)를 제조사에 할당하고 공개 레지스트리를 유지합니다. MAC 주소의 상위 3바이트를 보면 디바이스 제조사를 식별할 수 있습니다.

주요 벤더 OUI 예시

OUI벤더비고
00:50:56VMwareVMware VM NIC
52:54:00QEMU/KVMQEMU 가상 NIC 기본값
02:42:ACDockerDocker 컨테이너 기본 MAC 접두사
00:15:5DMicrosoftHyper-V 가상 NIC
08:00:27OracleVirtualBox 가상 NIC
00:1A:11GoogleGCE 가상 NIC
00:0C:29VMwareVMware 자동 생성 MAC
OUI 조회: IEEE 공식 레지스트리에서 OUI를 조회할 수 있습니다. 리눅스에서는 /usr/share/misc/oui.txt 또는 macchanger -l로 로컬에서 확인할 수도 있습니다. 네트워크 포렌식이나 디바이스 식별에 유용합니다.

커널 소스 주요 경로

파일/디렉터리내용
include/linux/etherdevice.h MAC 주소 판별/생성 인라인 함수 (is_valid_ether_addr, eth_random_addr 등)
include/uapi/linux/if_ether.h struct ethhdr, ETH_ALEN, EtherType 상수
include/net/neighbour.h struct neighbour, struct neigh_table, NUD 상태 상수
net/core/dev.c dev_set_mac_address(), __dev_set_rx_mode()
net/core/dev_addr_lists.c dev_uc_add(), dev_mc_add(), 유니캐스트/멀티캐스트 리스트 관리
net/ipv4/arp.c arp_process(), arp_send_dst(), ARP 프로토콜 처리
net/core/neighbour.c Neighbor 서브시스템 코어 (neigh_update(), GC, 상태 머신)
net/bridge/br_fdb.c Bridge FDB (br_fdb_update(), br_fdb_find_rcu())
net/bridge/br_input.c br_handle_frame(), 브리지 수신 경로
drivers/net/macvlan.c macvlan/macvtap 드라이버
drivers/net/ethernet/intel/ixgbe/ Intel 10G NIC — VF MAC 관리, RAR 프로그래밍 예시
MAC 주소 전체 수명주기 (Lifecycle) 1. NIC 제조 OUI(3바이트) + 시리얼(3바이트) 할당 2. EEPROM 저장 NIC 플래시/EEPROM에 영구 기록 3. 드라이버 probe EEPROM 읽기 → dev->perm_addr 설정 4. dev->dev_addr 현재 활성 MAC (perm_addr 복사) 5. dev_set_mac_address() 사용자 요청 시 MAC 변경 6. HW 레지스터 프로그래밍 NIC RAR/유니캐스트 필터 갱신 사용 (Usage) ARP Reply 소스 MAC NDP NA 응답 (IPv6) Bridge FDB 학습 (소스 기반) 이더넷 프레임 송신 src MAC 수신 필터링 (unicast/multicast) eth_header() → skb→data에 MAC 기록 __netif_receive_skb() → 목적지 MAC 매칭 보안 (Security) spoofchk (SR-IOV) VF MAC 위조 방지 ebtables/nftables L2 MAC 필터링 규칙 eBPF tc-bpf 프로그래밍 가능 MAC 필터 MACsec (802.1AE) L2 암호화/무결성 보장
MAC 주소 수명주기: 제조 → EEPROM → 드라이버 로드 → 활성 주소 → 프레임 송수신 → 보안 검증
필수 관련 문서: 참고 문서: