DSA Tagging 심화
DSA(Distributed Switch Architecture)는 내장 스위치 칩을 Linux 네트워크 스택에 자연스럽게 통합하는 프레임워크입니다. 이 문서는 DSA tagging 메커니즘의 태그 프로토콜 헤더 바이트 분석, TX 인젝션/RX 추출 경로, MTU 헤드룸 관리, 멀티칩 패브릭 아키텍처, FDB/MDB 학습, tc 오프로딩, Device Tree 모델링, 드라이버 디버깅까지 DSA 데이터면의 모든 것을 심층적으로 정리합니다.
핵심 요약
- CPU 포트 -- 스위치와 호스트 CPU를 연결하는 내부 업링크
- 사용자 포트 -- 실제 물리 포트(netdev로 노출)
- tag driver -- 제조사별 DSA 태그 삽입/제거 로직
- dsa_slave -- 사용자 포트용 net_device 구현
- switchdev -- 브리지/VLAN/FDB를 하드웨어로 오프로드
단계별 이해
- 포트 초기화
DSA 트리가 구성되고 사용자 포트 netdev가 생성됩니다. - 송신 시 태깅
CPU 포트로 나가기 직전에 목적 포트 정보를 태그로 삽입합니다. - 수신 시 디태깅
CPU가 받은 프레임에서 소스 포트 정보를 해석해 올바른 netdev로 전달합니다. - 브리지/ACL 오프로드
switchdev 연계로 데이터면 룰을 스위치 칩에 내려 보냅니다.
DSA 데이터 경로
DSA 프레임워크는 내장 이더넷 스위치의 각 포트를 독립적인 net_device로 노출하여,
표준 Linux 네트워크 도구(ip, bridge, tc 등)로 관리할 수 있게 합니다.
핵심은 CPU 포트를 통해 호스트와 스위치 간에 프레임을 교환할 때 태그로 포트 정보를 인코딩하는 것입니다.
| 포트 유형 | 역할 | netdev 노출 | 태그 처리 |
|---|---|---|---|
| User port | 외부 디바이스 연결 | lan0, lan1 등 | TX: 태그 삽입, RX: 태그 제거 |
| CPU port | 호스트 SoC MAC 연결 | eth0 (master) | 태그가 붙은 프레임 전달 |
| DSA port | 스위치 간 cascade 연결 | 없음 (내부) | 다중 칩 태그 전달 |
| Unused port | 미사용 포트 | 없음 | 비활성화 |
DSA 트리 구조
DSA는 하나 이상의 스위치 칩을 트리(tree)로 조직합니다. 각 스위치는 트리 내에서 고유한 인덱스를 가지며,
dsa,member = <tree-index switch-index> DTS 속성으로 식별됩니다.
/* DSA 트리 핵심 자료구조 */
struct dsa_switch_tree {
struct list_head list; /* 전역 트리 목록 */
unsigned int index; /* 트리 인덱스 */
struct kref refcount;
struct list_head ports; /* 모든 포트 목록 */
struct dsa_platform_data *pd;
bool setup;
const struct dsa_device_ops *tag_ops; /* 태그 프로토콜 */
};
struct dsa_switch {
struct dsa_switch_tree *dst; /* 소속 트리 */
unsigned int index; /* 트리 내 스위치 인덱스 */
unsigned int num_ports;
struct dsa_port *ports;
const struct dsa_switch_ops *ops; /* 드라이버 콜백 */
};
주요 태그 프로토콜
DSA 태그 프로토콜은 제조사마다 다르며, 커널 소스의 net/dsa/tag_*.c 파일에 각각 구현되어 있습니다.
태그 위치(header/tail/EtherType 재사용), 크기, 인코딩 방식이 모두 다릅니다.
| 프로토콜 | 커널 파일 | 위치 | 크기 (바이트) | 대표 칩셋 |
|---|---|---|---|---|
| tag_dsa | tag_dsa.c | 헤더 (Src MAC 뒤) | 4 | Marvell 88E6xxx (legacy) |
| tag_edsa | tag_edsa.c | 헤더 (EtherType 재정의) | 8 | Marvell 88E6xxx (확장) |
| tag_ocelot | tag_ocelot.c | 헤더 (프레임 앞) | 16 | Microchip Ocelot/Felix |
| tag_ocelot_8021q | tag_ocelot_8021q.c | 802.1Q 재사용 | 4 | Microchip Ocelot (NPI-less) |
| tag_ksz | tag_ksz.c | 테일 | 1~3 | Microchip KSZ 시리즈 |
| tag_rtl4_a | tag_rtl4_a.c | 헤더 (EtherType) | 4 | Realtek RTL8365MB |
| tag_rtl8_4 | tag_rtl8_4.c | 헤더 (EtherType) | 8 | Realtek RTL8366RB/RTL8365 |
| tag_mtk | tag_mtk.c | 헤더 (특수 EtherType) | 4 | MediaTek MT7530/MT7531 |
| tag_brcm | tag_brcm.c | 헤더 (Broadcom 전용) | 4 | Broadcom SF2/BCM53xx |
| tag_brcm_prepend | tag_brcm.c | 헤더 (프레임 앞) | 4 | Broadcom (prepend 변형) |
| tag_sja1105 | tag_sja1105.c | VLAN 기반 + 메타프레임 | 가변 | NXP SJA1105 |
| tag_8021q | tag_8021q.c | 802.1Q VLAN 재사용 | 4 | 공통 소프트웨어 경로 |
| tag_hellcreek | tag_hellcreek.c | 테일 | 2 | Hirschmann Hellcreek |
| tag_lan9303 | tag_lan9303.c | 헤더 (특수) | 4 | SMSC/Microchip LAN9303 |
| tag_xrs700x | tag_xrs700x.c | 테일 | 1 | Arrow XRS700x |
tag_proto devlink 파라미터로 런타임에 태그 프로토콜을 변경할 수 있는 칩도 있습니다.
# 현재 사용 중인 태그 프로토콜 확인
cat /sys/class/net/lan0/dsa/tagging
# devlink로 태그 프로토콜 변경 (지원하는 드라이버만)
devlink dev param set pci/0000:00:00.0 name tag_protocol value DSA_TAG_PROTO_OCELOT_8021Q cmode runtime
태그 포맷 심화
DSA 태그는 단순히 포트 번호만 담는 필드가 아니라, 디바이스 인덱스, 트랩 이유, VLAN/TC 우선순위, 타임스탬프 플래그 등 다양한 메타데이터를 포함할 수 있습니다. 아래에서 주요 태그 프로토콜의 바이트 레벨 구조를 분석합니다.
Marvell DSA 태그 (4바이트)
Marvell 88E6xxx 시리즈의 기본 DSA 태그는 Src MAC과 EtherType 사이에 4바이트를 삽입합니다.
/* net/dsa/tag_dsa.c - Marvell DSA 태그 xmit 핵심 로직 */
static struct sk_buff *dsa_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct dsa_port *dp = dsa_slave_to_port(dev);
u8 *dsa_header;
/* MAC 헤더 뒤에 4바이트 공간 확보 */
if (skb_cow_head(skb, DSA_HLEN) < 0)
return NULL;
skb_push(skb, DSA_HLEN); /* 4바이트 삽입 공간 확보 */
dsa_alloc_etype_header(skb, DSA_HLEN); /* MAC 헤더 앞쪽으로 이동 */
dsa_header = dsa_etype_header_pos_tx(skb);
/* FROM_CPU 모드, 목적 포트 지정 */
dsa_header[0] = (DSA_CMD_FROM_CPU << 6) |
(dp->ds->index & 0x1f);
dsa_header[1] = dp->index << 3;
dsa_header[2] = 0; /* 우선순위, VLAN 등 */
dsa_header[3] = 0;
return skb;
}
Marvell EDSA 태그 (8바이트)
EDSA(Extended DSA)는 DSA 태그를 8바이트로 확장하여 더 많은 메타데이터를 포함합니다.
EtherType 0xDADA로 식별되며, 추가 4바이트에 트렁크 정보, PTP 타임스탬프 힌트, 정책 미러링 등의 필드가 포함됩니다.
| 오프셋 | 필드 | 비트 | 설명 |
|---|---|---|---|
| 0~1 | EtherType | 16 | 0xDADA (EDSA 식별자) |
| 2 | Mode/TagCmd/SrcDev 상위 | 8 | DSA와 동일한 필드 배치 |
| 3 | SrcDev 하위/SrcPort | 8 | 소스 디바이스/포트 |
| 4 | PRI/CFI/VID 상위 | 8 | 802.1Q 우선순위 정보 |
| 5 | VID 하위 | 8 | VLAN ID 하위 8비트 |
| 6 | Trunk/Timestamp 힌트 | 8 | 트렁크 그룹, PTP 플래그 |
| 7 | 확장 필드 | 8 | FPri, 정책 미러, 코드 |
Microchip KSZ 테일 태그 (1~3바이트)
KSZ 시리즈는 프레임 끝에 태그를 부착합니다. 모델에 따라 1바이트(KSZ8795), 2바이트(KSZ9477), 3바이트 변형이 있습니다.
/* net/dsa/tag_ksz.c - KSZ9477 테일 태그 (2바이트) */
/* TX: 마지막 2바이트에 목적 포트 비트맵 */
/* [바이트0] 오버라이드(1) | 포트 비트맵 상위 */
/* [바이트1] 포트 비트맵 하위 (bit N = port N) */
/* RX: 소스 포트 + 타임스탬프 */
/* [바이트0] 타임스탬프(1) | 소스 포트[2:0] */
/* [바이트1] 타임스탬프 데이터 */
static struct sk_buff *ksz9477_xmit(struct sk_buff *skb,
struct net_device *dev)
{
struct dsa_port *dp = dsa_slave_to_port(dev);
u8 *tag;
u16 val;
/* 프레임 끝에 2바이트 추가 */
if (skb_put_padto(skb, ETH_ZLEN + KSZ9477_INGRESS_TAG_LEN))
return NULL;
tag = skb_put(skb, KSZ9477_INGRESS_TAG_LEN);
val = BIT(dp->index); /* 목적 포트 비트맵 */
val |= KSZ9477_TAIL_TAG_OVERRIDE;
put_unaligned_be16(val, tag);
return skb;
}
MediaTek MT7530 태그 (4바이트)
MediaTek MT7530/MT7531은 EtherType 0x8100과 유사한 위치에 4바이트 태그를 삽입합니다.
상위 2바이트가 0x8100이 아닌 0x05xx 형태로 구분됩니다.
| TX 오프셋 | 비트 | 필드 | 설명 |
|---|---|---|---|
| 바이트 0~1 | [15:0] | 특수 태그 헤더 | DPID(목적 포트 비트맵) |
| 바이트 2~3 | [15:0] | VLAN 정보 | VID, 우선순위 힌트 |
| RX 오프셋 | 비트 | 필드 | 설명 |
|---|---|---|---|
| 바이트 0~1 | [15:0] | 특수 태그 헤더 | 소스 포트, ARL 위반 플래그 |
| 바이트 2~3 | [15:0] | VLAN/이유 | VID, 트랩 이유 코드 |
Microchip Ocelot 태그 (16바이트)
Ocelot/Felix 칩은 가장 정보량이 많은 태그를 사용합니다. 16바이트의 IFH(Injection Frame Header) 또는 EFH(Extraction Frame Header)를 프레임 앞에 삽입합니다.
/* Ocelot IFH (TX) 주요 필드 */
struct ocelot_ifh {
u64 bypass: 1; /* 분류 엔진 우회 */
u64 dest: 12; /* 목적 포트 마스크 */
u64 src_port: 4; /* 소스 포트 */
u64 qos_class:3; /* QoS 클래스 */
u64 dp: 2; /* 드롭 우선순위 */
u64 tag_type: 3; /* VLAN 태그 타입 */
u64 vlan_vid: 12; /* VLAN ID */
u64 rew_op: 10; /* 재작성 명령 (PTP 등) */
/* ... 총 128비트 (16바이트) */
};
tag_8021q 소프트웨어 태그
tag_8021q는 하드웨어 전용 태그가 아닌 표준 802.1Q VLAN 태그를 DSA 메타데이터로 재활용하는 소프트웨어 방식입니다.
하드웨어 NPI(Network Processor Interface) 포트가 없는 구성이나 tag_proto 교체 시 폴백으로 사용됩니다.
/* net/dsa/tag_8021q.c - VID 인코딩 방식 */
#define DSA_8021Q_DIR_SHIFT 11
#define DSA_8021Q_DIR_RX (1 << DSA_8021Q_DIR_SHIFT)
#define DSA_8021Q_DIR_TX (2 << DSA_8021Q_DIR_SHIFT)
#define DSA_8021Q_SWITCH_ID_SHIFT 8
#define DSA_8021Q_PORT_SHIFT 0
/* TX VID: direction(2) | switch_id(3) | port(8) */
static u16 dsa_tag_8021q_tx_vid(struct dsa_port *dp)
{
return DSA_8021Q_DIR_TX |
(dp->ds->index << DSA_8021Q_SWITCH_ID_SHIFT) |
dp->index;
}
커널 구현 포인트
DSA 코어 코드는 net/dsa/ 디렉토리에 위치하며, 태그 드라이버는 net/dsa/tag_*.c,
스위치 드라이버는 drivers/net/dsa/에 위치합니다. 핵심 등록 흐름을 살펴봅니다.
/* DSA 드라이버 등록 흐름 */
/* 1. 태그 드라이버 등록 (net/dsa/tag_*.c) */
static const struct dsa_device_ops my_tag_ops = {
.name = "my_tag",
.proto = DSA_TAG_PROTO_MY,
.xmit = my_tag_xmit, /* TX: 태그 삽입 */
.rcv = my_tag_rcv, /* RX: 태그 제거 + 소스 포트 복원 */
.needed_headroom = MY_TAG_LEN,
.needed_tailroom = 0,
.flow_dissect = my_tag_flow_dissect,
.overhead = MY_TAG_LEN,
};
DSA_TAG_DRIVER(my_tag_ops);
MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_MY);
/* 2. 스위치 드라이버 등록 (drivers/net/dsa/*.c) */
static const struct dsa_switch_ops my_switch_ops = {
.get_tag_protocol = my_get_tag_protocol,
.setup = my_setup,
.port_enable = my_port_enable,
.port_disable = my_port_disable,
.port_fdb_add = my_port_fdb_add,
.port_fdb_del = my_port_fdb_del,
.port_fdb_dump = my_port_fdb_dump,
.port_mdb_add = my_port_mdb_add,
.port_mdb_del = my_port_mdb_del,
.port_bridge_join = my_port_bridge_join,
.port_bridge_leave= my_port_bridge_leave,
.port_vlan_add = my_port_vlan_add,
.port_vlan_del = my_port_vlan_del,
.port_vlan_filtering = my_port_vlan_filtering,
.cls_flower_add = my_cls_flower_add,
.cls_flower_del = my_cls_flower_del,
/* ... */
};
dsa_switch_ops에는 80개 이상의 콜백 함수 포인터가 정의되어 있습니다.
대부분은 선택적(optional)이며, 필수 콜백은 get_tag_protocol과 setup뿐입니다.
/* DSA 송신 경로 핵심 (net/dsa/slave.c) */
static netdev_tx_t dsa_slave_xmit(struct sk_buff *skb,
struct net_device *dev)
{
struct dsa_slave_priv *p = netdev_priv(dev);
struct sk_buff *nskb;
dev_sw_netstats_tx_add(dev, 1, skb->len);
memset(skb->cb, 0, sizeof(skb->cb));
/* 태그 드라이버의 xmit() 호출 */
nskb = p->xmit(skb, dev);
if (!nskb) {
kfree_skb(skb);
return NETDEV_TX_OK;
}
/* master netdev로 전달 */
skb->dev = dsa_slave_to_master(dev);
dev_queue_xmit(skb);
return NETDEV_TX_OK;
}
송수신 파이프라인 상세
TX는 사용자 포트 netdev에서 시작해 CPU 포트 직전에 태그가 삽입되고, RX는 CPU 포트에서 들어온 프레임을 태그 해석 후 해당 사용자 포트 netdev로 fan-out 합니다. 이 경로에서 GRO/LRO, checksum offload, timestamp 처리 순서가 깨지면 성능/정합성 문제가 발생합니다.
/* DSA RX 경로 핵심 (net/dsa/slave.c) */
static rx_handler_result_t dsa_switch_rcv(struct sk_buff **pskb)
{
struct sk_buff *skb = *pskb;
struct dsa_port *cpu_dp;
struct sk_buff *nskb;
cpu_dp = skb->dev->dsa_ptr;
if (unlikely(!cpu_dp))
return RX_HANDLER_PASS;
/* 태그 드라이버의 rcv() 호출 */
nskb = cpu_dp->tag_ops->rcv(skb, skb->dev);
if (!nskb) {
kfree_skb(skb);
return RX_HANDLER_CONSUMED;
}
skb = nskb;
skb_push(skb, ETH_HLEN);
skb->pkt_type = PACKET_HOST;
skb->protocol = eth_type_trans(skb, skb->dev);
/* slave netdev의 통계 업데이트 */
dev_sw_netstats_rx_add(skb->dev, skb->len);
netif_receive_skb(skb);
return RX_HANDLER_CONSUMED;
}
netdev_rx_handler_register()로 dsa_switch_rcv를 등록합니다.
이는 브리지의 br_handle_frame과 동일한 메커니즘이므로, master netdev를 브리지에 직접 추가하면 충돌이 발생합니다.
브리지에 추가하는 것은 slave netdev여야 합니다.
브리지/VLAN 연동
DSA 포트는 Linux bridge에 일반 netdev처럼 붙지만, 실제 데이터면 처리(FDB 학습, VLAN 필터링)는 스위치 칩이 수행합니다. 이 "오프로드" 경로와 소프트웨어 폴백 경로의 차이를 이해해야 합니다.
# DSA 슬레이브를 브리지에 추가
ip link add name br0 type bridge
ip link set br0 up
ip link set lan0 master br0
ip link set lan1 master br0
ip link set lan2 master br0
# VLAN 필터링 활성화
ip link set br0 type bridge vlan_filtering 1
# VLAN 추가 (pvid: 기본 VLAN, untagged: 태그 제거)
bridge vlan add dev lan0 vid 100 pvid untagged
bridge vlan add dev lan1 vid 100 pvid untagged
bridge vlan add dev lan2 vid 200 pvid untagged
bridge vlan add dev br0 vid 100 self
bridge vlan add dev br0 vid 200 self
# 상태 확인
bridge vlan show
bridge -d link show
| 동작 | 오프로드 (하드웨어) | 소프트웨어 폴백 |
|---|---|---|
| FDB 학습 | 스위치 ATU가 자동 학습 | CPU가 소프트웨어 학습 |
| VLAN 필터링 | 스위치 VTU가 필터링 | bridge VLAN 필터 |
| STP 상태 | 포트별 하드웨어 STP 상태 | 소프트웨어 STP 데몬 |
| IGMP 스누핑 | MDB 기반 멀티캐스트 필터링 | CPU 기반 스누핑 |
| 유니캐스트 포워딩 | 라인 레이트 하드웨어 포워딩 | CPU 경유 (느림) |
bridge -d link show에서 offload 플래그가 표시되면 하드웨어 오프로드가 활성화된 것입니다.
FDB 엔트리에서도 offloaded 플래그를 확인하세요.
# 오프로드 상태 확인 예시
$ bridge -d link show
3: lan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master br0 state forwarding
hairpin off guard off root_block off fastleave off
learning on flood on mcast_flood on bcast_flood on mcast_router 1
mcast_to_unicast off neigh_suppress off vlan_tunnel off
isolated off locked off mab off
backup_port eth0 backup_nhid 0
$ bridge -d fdb show
aa:bb:cc:dd:ee:01 dev lan0 master br0 offloaded
aa:bb:cc:dd:ee:02 dev lan1 master br0 offloaded
FDB/MDB 학습과 오프로드
FDB(Forwarding Database)와 MDB(Multicast Database)는 스위치의 핵심 데이터 플레인 테이블입니다. DSA 환경에서는 하드웨어 학습과 소프트웨어 학습이 동시에 존재하며, 이 둘의 동기화가 중요합니다.
/* switchdev FDB 이벤트 처리 (개념 코드) */
static int my_port_fdb_add(struct dsa_switch *ds, int port,
const unsigned char *addr, u16 vid,
struct dsa_db db)
{
/* 하드웨어 ATU에 정적 MAC 엔트리 추가 */
struct atu_entry entry = {
.mac = addr,
.vid = vid,
.port = port,
.state = ATU_STATE_STATIC,
};
return hw_atu_write(ds, &entry);
}
static int my_port_mdb_add(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_mdb *mdb,
struct dsa_db db)
{
/* 멀티캐스트 그룹 MAC을 포트 비트맵에 추가 */
return hw_mdb_add_port(ds, mdb->addr, mdb->vid, port);
}
# FDB/MDB 관리 명령어
# 정적 FDB 엔트리 추가
bridge fdb add aa:bb:cc:dd:ee:01 dev lan0 master static
# FDB 전체 출력 (offloaded 플래그 확인)
bridge fdb show
# MDB 확인
bridge mdb show
# 정적 MDB 엔트리 추가 (멀티캐스트 그룹)
bridge mdb add dev br0 port lan0 grp 239.1.1.1 permanent
# 에이징 타임 조정 (초)
ip link set br0 type bridge ageing_time 30000
switchdev 이벤트 연계
DSA는 switchdev notifier를 통해 브리지/VLAN/FDB 상태를 드라이버에 동기화합니다.
사용자 공간에서 보이는 bridge 명령은 제어면이고, 실제 데이터면은 switch chip 룰 업데이트로 이어집니다.
| switchdev 이벤트 | 트리거 | DSA 드라이버 콜백 | 하드웨어 동작 |
|---|---|---|---|
SWITCHDEV_FDB_ADD_TO_DEVICE | bridge fdb add | port_fdb_add() | ATU 정적 엔트리 추가 |
SWITCHDEV_FDB_DEL_TO_DEVICE | bridge fdb del | port_fdb_del() | ATU 엔트리 삭제 |
SWITCHDEV_PORT_OBJ_ADD (VLAN) | bridge vlan add | port_vlan_add() | VTU/VLAN 테이블 업데이트 |
SWITCHDEV_PORT_OBJ_ADD (MDB) | bridge mdb add / IGMP join | port_mdb_add() | 멀티캐스트 필터 업데이트 |
SWITCHDEV_PORT_ATTR_SET (STP) | STP 상태 변경 | port_stp_state_set() | 포트 STP 상태 변경 |
SWITCHDEV_PORT_ATTR_SET (learning) | bridge link set learning | port_bridge_flags() | ATU 학습 활성화/비활성화 |
/* switchdev notifier 체인 (개념적 흐름) */
/* 1. 사용자: bridge vlan add dev lan0 vid 100 */
/* 2. bridge 코드 -> switchdev notifier 발생 */
/* 3. DSA 코어가 수신 -> 해당 스위치 드라이버 콜백 호출 */
static int my_port_vlan_add(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan,
struct netlink_ext_ack *extack)
{
u16 vid = vlan->vid;
bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
/* 하드웨어 VLAN 테이블에 포트+VID 추가 */
hw_vtu_add_member(ds, vid, port, untagged);
if (pvid)
hw_set_default_vid(ds, port, vid);
return 0;
}
tc 오프로딩
DSA 드라이버는 tc(Traffic Control)의 flower classifier를 하드웨어 ACL/TCAM에 오프로드할 수 있습니다.
이를 통해 라인 레이트로 패킷 필터링, 미러링, 폴리싱, 리디렉션 등을 수행할 수 있습니다.
| tc 동작 | 하드웨어 대응 | 지원 드라이버 예 |
|---|---|---|
tc flower match | TCAM/ACL 룰 프로그래밍 | Ocelot, SJA1105, MT7530 |
action drop | 하드웨어 드롭 액션 | 대부분 |
action mirred | 포트 미러링 | Ocelot, Marvell |
action police | 하드웨어 폴리서 | Ocelot |
action redirect | 포트 리디렉션 | Ocelot, SJA1105 |
action vlan push/pop | VLAN 태그 조작 | Ocelot |
mqprio | QoS 큐 매핑 | Ocelot, taprio |
taprio | 시간 기반 스케줄링 (TSN) | Ocelot, SJA1105, Felix |
# tc flower 오프로드 예제
# 특정 MAC 소스에서 오는 프레임 드롭
tc qdisc add dev lan0 clsact
tc filter add dev lan0 ingress protocol all \
flower src_mac aa:bb:cc:dd:ee:ff \
action drop
# lan0 -> lan1 미러링 (하드웨어)
tc filter add dev lan0 ingress protocol all \
flower \
action mirred egress mirror dev lan1
# VLAN 100 트래픽에 대한 폴리싱 (1Gbps 제한)
tc filter add dev lan0 ingress protocol 802.1q \
flower vlan_id 100 \
action police rate 1gbit burst 256k conform-exceed drop/ok
# taprio (TSN) 설정 예제
tc qdisc replace dev lan0 parent root taprio \
num_tc 4 \
map 0 0 1 1 2 2 3 3 \
queues 1@0 1@1 1@2 1@3 \
base-time 1000000000 \
sched-entry S 0x1 250000 \
sched-entry S 0x2 250000 \
sched-entry S 0x4 250000 \
sched-entry S 0x8 250000 \
flags 0x2
# 오프로드 확인: hw_tc 카운터 증가 확인
tc -s filter show dev lan0 ingress
/* DSA tc flower 오프로드 콜백 */
static int my_cls_flower_add(struct dsa_switch *ds, int port,
struct flow_cls_offload *cls, bool ingress)
{
struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
struct flow_dissector_key_eth_addrs *key_eth;
struct flow_action_entry *act;
/* 매치 키 파싱 */
if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
key_eth = skb_flow_dissector_target(rule->match.dissector,
FLOW_DISSECTOR_KEY_ETH_ADDRS,
rule->match.key);
/* key_eth->src, key_eth->dst를 TCAM에 프로그래밍 */
}
/* 액션 처리 */
flow_action_for_each(i, act, &rule->action) {
switch (act->id) {
case FLOW_ACTION_DROP:
hw_acl_set_action_drop(ds, port);
break;
case FLOW_ACTION_MIRRED:
hw_acl_set_action_mirror(ds, port, act->dev);
break;
}
}
return 0;
}
tc -s filter show에서 in_hw/not_in_hw 플래그로 확인할 수 있습니다.
MTU/헤드룸/트레일러 이슈
DSA 태그는 프레임 길이를 늘립니다. 따라서 CPU 포트와 master netdev에서 충분한 headroom/tailroom 및 MTU를 보장하지 않으면, GSO 분할 실패나 silent drop이 발생합니다.
| 태그 타입 | 오버헤드 (바이트) | 필요 영역 | 영향 |
|---|---|---|---|
| Marvell DSA | 4 | headroom | master MTU += 4 |
| Marvell EDSA | 8 | headroom | master MTU += 8 |
| Ocelot IFH | 16 | headroom | master MTU += 16 |
| KSZ (2B) | 2 | tailroom | master MTU += 2 |
| tag_8021q | 4 | headroom | master MTU += 4 |
| MTK | 4 | headroom | master MTU += 4 |
| Broadcom | 4 | headroom | master MTU += 4 |
/* DSA MTU 관리 핵심 코드 (net/dsa/slave.c) */
static int dsa_slave_change_mtu(struct net_device *dev, int new_mtu)
{
struct dsa_port *dp = dsa_slave_to_port(dev);
struct dsa_port *cpu_dp = dp->cpu_dp;
struct net_device *master = cpu_dp->master;
int overhead = cpu_dp->tag_ops->overhead;
/* slave MTU 변경 시 master MTU도 조정 필요 */
if (new_mtu + overhead > master->max_mtu)
return -ERANGE;
/* master MTU >= slave MTU + tag overhead */
if (new_mtu + overhead > master->mtu) {
int err = dev_set_mtu(master, new_mtu + overhead);
if (err)
return err;
}
dev->mtu = new_mtu;
return 0;
}
# MTU 정합 확인 및 설정
# master와 slave MTU 비교
ip link show dev eth0 | grep mtu # master: 1504 이상이어야
ip link show dev lan0 | grep mtu # slave: 1500
ip link show dev lan1 | grep mtu # slave: 1500
# CPU 포트 측 MTU 상향 (드라이버/하드웨어 허용 범위 내)
ip link set dev eth0 mtu 2000
# slave의 점보 프레임 활성화
ip link set dev lan0 mtu 9000
# headroom/tailroom 확인 (ethtool 또는 디버그 로그)
ethtool -k lan0 | grep -i offload
needed_headroom/needed_tailroom를 정확히 설정해야 합니다.
커널 6.x에서는 DSA 코어가 dev->needed_headroom을 자동으로 조정하지만,
일부 구형 드라이버는 수동 설정이 필요할 수 있습니다.
Device Tree 모델링 포인트
DSA bring-up 실패의 대부분은 DTS 구성 불일치에서 시작됩니다. CPU 포트의 ethernet phandle,
phy-mode, 포트 인덱스, 멀티칩 링크(dsa,member)가 일치해야 태깅 경로가 정상 동작합니다.
/* 완전한 DSA DTS 예시 (싱글 칩 + PHY) */
&gmac0 {
phy-mode = "rgmii-id";
status = "okay";
fixed-link {
speed = <1000>;
full-duplex;
};
};
&mdio {
switch0: ethernet-switch@0 {
compatible = "vendor,switch0";
reg = <0>;
dsa,member = <0 0>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
label = "lan0";
phy-handle = <&sw_phy0>;
};
port@1 {
reg = <1>;
label = "lan1";
phy-handle = <&sw_phy1>;
};
port@2 {
reg = <2>;
label = "lan2";
phy-handle = <&sw_phy2>;
};
port@3 {
reg = <3>;
label = "lan3";
phy-handle = <&sw_phy3>;
};
port@5 {
reg = <5>;
label = "cpu";
ethernet = <&gmac0>;
phy-mode = "rgmii-id";
fixed-link {
speed = <1000>;
full-duplex;
};
};
};
mdio {
#address-cells = <1>;
#size-cells = <0>;
sw_phy0: ethernet-phy@0 { reg = <0>; };
sw_phy1: ethernet-phy@1 { reg = <1>; };
sw_phy2: ethernet-phy@2 { reg = <2>; };
sw_phy3: ethernet-phy@3 { reg = <3>; };
};
};
};
/* 멀티칩 DSA DTS 예시 (2칩 cascade) */
switch0: ethernet-switch@10 {
compatible = "marvell,mv88e6085";
reg = <0x10>;
dsa,member = <0 0>; /* tree 0, switch 0 */
ports {
port@5 {
reg = <5>;
label = "cpu";
ethernet = <&gmac0>;
phy-mode = "rgmii-id";
fixed-link { speed = <1000>; full-duplex; };
};
port@6 {
reg = <6>;
label = "dsa"; /* 스위치 간 연결 포트 */
link = <&switch1_port6>;
phy-mode = "rgmii-id";
fixed-link { speed = <1000>; full-duplex; };
};
};
};
switch1: ethernet-switch@11 {
compatible = "marvell,mv88e6085";
reg = <0x11>;
dsa,member = <0 1>; /* tree 0, switch 1 */
ports {
switch1_port6: port@6 {
reg = <6>;
label = "dsa";
link = <&switch0>;
phy-mode = "rgmii-id";
fixed-link { speed = <1000>; full-duplex; };
};
/* 사용자 포트들 ... */
};
};
멀티칩 DSA Fabric에서 태그 경계 처리
스위치 칩이 여러 개 연결된 DSA fabric에서는 태그가 단일 칩 내부 의미를 넘어 hop 정보를 반영할 수 있습니다. CPU 포트 도착 시점의 포트 decode, VLAN 도메인, STP 상태가 일치하지 않으면 loop/drop이 발생하기 쉽습니다.
| 토폴로지 | 최대 스위치 수 | 태그 인코딩 | 대표 예 |
|---|---|---|---|
| Single chip | 1 | switch_id = 0 고정 | 일반적인 5포트 스위치 |
| Daisy chain | 칩 의존 (보통 2~8) | switch_id + port_id | Marvell cascade |
| Cross-chip bridge | 칩 의존 | fabric-wide FDB | Marvell 멀티칩 브리지 |
# 멀티칩 토폴로지 확인
ip -d link show | grep -E 'master|dsa|link'
# 크로스-칩 브리지 상태
bridge -d link show
bridge -d fdb show # offloaded 플래그 확인
bridge vlan show
# 디버그 로그 확인
dmesg | grep -Ei 'dsa|tag|decode|drop|cascade'
태그 드라이버 연산 경로 (xmit/rcv)
DSA 태그 처리의 핵심은 dsa_device_ops 구현입니다. TX에서 CPU 포트로 내보내기 전 태그를 삽입하고,
RX에서 하드웨어가 붙인 태그를 해석해 source port/metadata를 복원합니다.
/* 완전한 태그 드라이버 구현 예시 (간소화) */
#define MY_TAG_LEN 4
static struct sk_buff *my_tag_xmit(struct sk_buff *skb,
struct net_device *dev)
{
struct dsa_port *dp = dsa_slave_to_port(dev);
u8 *tag;
/* headroom 부족 시 재할당 */
if (skb_cow_head(skb, MY_TAG_LEN) < 0)
return NULL;
/* MAC 헤더 뒤에 태그 삽입 */
skb_push(skb, MY_TAG_LEN);
dsa_alloc_etype_header(skb, MY_TAG_LEN);
tag = dsa_etype_header_pos_tx(skb);
/* FROM_CPU 모드, 목적 포트 지정 */
tag[0] = 0x40 | (dp->ds->index & 0x1f);
tag[1] = dp->index << 3;
tag[2] = 0x00;
tag[3] = 0x00;
return skb;
}
static struct sk_buff *my_tag_rcv(struct sk_buff *skb,
struct net_device *dev)
{
u8 *tag = dsa_etype_header_pos_rx(skb);
u8 switch_id = tag[0] & 0x1f;
u8 src_port = (tag[1] >> 3) & 0x1f;
u8 mode = (tag[0] >> 6) & 0x3;
/* TO_CPU 모드 확인 */
if (mode != 0x01)
return NULL;
/* 태그 제거 */
skb_pull_rcsum(skb, MY_TAG_LEN);
dsa_strip_etype_header(skb, MY_TAG_LEN);
skb->dev = dsa_master_find_slave(dev, switch_id, src_port);
if (!skb->dev)
return NULL;
return skb;
}
오프로딩 검증: bridge/FDB/VLAN 일관성
DSA 환경에서는 소프트웨어 경로와 하드웨어 오프로딩 경로가 동시에 존재합니다. 문제 재현 시에는 "기능 동작"만 보지 말고 오프로딩 여부를 함께 확인해야 합니다.
# 오프로딩 종합 검증 스크립트
#!/bin/bash
echo "=== 1. Bridge 오프로드 상태 ==="
bridge -d link show
echo "=== 2. FDB 오프로드 확인 ==="
bridge -d fdb show | grep -i offload
echo "=== 3. VLAN 상태 ==="
bridge vlan show
echo "=== 4. 포트 통계 ==="
for dev in eth0 lan0 lan1 lan2; do
echo "--- $dev ---"
ethtool -S $dev 2>/dev/null | grep -iE 'drop|error|overflow'
done
echo "=== 5. DSA 관련 커널 메시지 ==="
dmesg | grep -Ei 'dsa|tag|decode|drop|switchdev' | tail -20
커널 설정
| 옵션 | 설명 | 권장 | 의존성 |
|---|---|---|---|
CONFIG_NET_DSA | DSA 코어 프레임워크 | y/m | CONFIG_NET |
CONFIG_NET_DSA_TAG_DSA | Marvell DSA 태그 (4B) | 칩셋 의존 | CONFIG_NET_DSA |
CONFIG_NET_DSA_TAG_EDSA | Marvell EDSA 태그 (8B) | 칩셋 의존 | CONFIG_NET_DSA |
CONFIG_NET_DSA_TAG_OCELOT | Microchip Ocelot IFH 태그 | 칩셋 의존 | CONFIG_NET_DSA |
CONFIG_NET_DSA_TAG_OCELOT_8021Q | Ocelot 802.1Q 기반 태그 | 칩셋 의존 | CONFIG_NET_DSA |
CONFIG_NET_DSA_TAG_KSZ | Microchip KSZ 테일 태그 | 칩셋 의존 | CONFIG_NET_DSA |
CONFIG_NET_DSA_TAG_MTK | MediaTek MT7530 태그 | 칩셋 의존 | CONFIG_NET_DSA |
CONFIG_NET_DSA_TAG_BRCM | Broadcom 태그 | 칩셋 의존 | CONFIG_NET_DSA |
CONFIG_NET_DSA_TAG_RTL4_A | Realtek 4B 태그 | 칩셋 의존 | CONFIG_NET_DSA |
CONFIG_NET_DSA_TAG_RTL8_4 | Realtek 8B 태그 | 칩셋 의존 | CONFIG_NET_DSA |
CONFIG_NET_DSA_TAG_SJA1105 | NXP SJA1105 태그 | 칩셋 의존 | CONFIG_NET_DSA |
CONFIG_NET_DSA_TAG_8021Q | 공통 802.1Q 기반 소프트웨어 태그 | y/m | CONFIG_NET_DSA |
CONFIG_NET_SWITCHDEV | switchdev 오프로드 프레임워크 | y | CONFIG_NET |
CONFIG_BRIDGE | Linux 브리지 (필수) | y/m | CONFIG_NET |
CONFIG_VLAN_8021Q | 802.1Q VLAN 지원 | y/m | CONFIG_NET |
# 커널 config 확인
zcat /proc/config.gz | grep -i net_dsa
# 또는
grep -i net_dsa /boot/config-$(uname -r)
PTP/타임스탬프 연동
TSN(Time-Sensitive Networking) 환경에서는 정밀한 시간 동기화가 필수입니다. DSA 드라이버는 PTP(Precision Time Protocol) 타임스탬프를 태그 메타데이터에 실어 보내거나, 별도의 레지스터에서 타임스탬프를 읽어 skb에 첨부합니다.
| 기능 | DSA 콜백 | 설명 | 지원 예 |
|---|---|---|---|
| TX 타임스탬프 | port_txtstamp() | 송신 시 하드웨어 타임스탬프 첨부 | Ocelot, SJA1105 |
| RX 타임스탬프 | port_rxtstamp() | 수신 태그에서 타임스탬프 추출 | Ocelot, SJA1105, KSZ |
| PTP 클럭 등록 | ptp_clock_register() | 스위치 하드웨어 클럭 노출 | 대부분 |
| taprio 스케줄링 | port_setup_tc() | 시간 기반 게이트 스케줄링 | Ocelot, SJA1105 |
/* PTP RX 타임스탬프 추출 예시 */
static bool my_port_rxtstamp(struct dsa_switch *ds, int port,
struct sk_buff *skb, unsigned int type)
{
struct skb_shared_hwtstamps *shhwtstamps;
u64 ns;
/* 태그에서 타임스탬프 값 추출 */
ns = extract_hw_timestamp(skb);
shhwtstamps = skb_hwtstamps(skb);
memset(shhwtstamps, 0, sizeof(*shhwtstamps));
shhwtstamps->hwtstamp = ns_to_ktime(ns);
return false; /* false = 즉시 전달 */
}
# PTP 클럭 확인
ls /dev/ptp*
ethtool -T lan0
# PTP 동기화 테스트
ptp4l -i lan0 -m -2 -s
devlink 연동
DSA 드라이버는 devlink 인프라를 통해 스위치 리소스, 파라미터, 진단 정보를 노출합니다.
devlink는 DSA 환경에서 태그 프로토콜 변경, 리소스 모니터링, 포트 스플리팅 등을 지원합니다.
# devlink 디바이스 확인
devlink dev show
# 파라미터 확인/변경
devlink dev param show
devlink dev param set pci/0000:00:00.0 name tag_protocol \
value DSA_TAG_PROTO_OCELOT_8021Q cmode runtime
# 리소스 확인 (TCAM, FDB 테이블 등)
devlink resource show
devlink resource set pci/0000:00:00.0 path /fdb size 2048
# 포트 정보
devlink port show
# 스위치 트래픽 트래핑 (CPU로 올라오는 트래픽 제어)
devlink trap show
devlink trap set pci/0000:00:00.0 trap source_mac_is_multicast action trap
devlink trap group show
| devlink 기능 | DSA 연관 | 사용 예 |
|---|---|---|
devlink dev param | 태그 프로토콜 런타임 변경 | NPI -> 8021q 전환 |
devlink resource | 하드웨어 리소스 모니터링 | FDB/TCAM 사용량 확인 |
devlink trap | 트래픽 트래핑 제어 | 특정 패킷 CPU 전달 활성화 |
devlink port | 포트 정보/스플리팅 | 포트 상태 확인 |
devlink health | 드라이버 건강 상태 | FW 오류 진단 |
디버깅 체크리스트
# 1) DSA 트리와 포트 확인
ip -d link show | grep -A2 -E "dsa|master"
# 2) 태그 프로토콜 확인
cat /sys/class/net/lan0/dsa/tagging
# 3) 브리지/VLAN 확인
bridge link show
bridge vlan show
bridge fdb show
# 4) 패킷 경로 관찰 (CPU 포트)
tcpdump -i eth0 -e -nn -vv -c 100
# 5) ethtool 통계와 드롭 카운터
ethtool -S eth0 | grep -iE 'drop|error|overflow'
ethtool -S lan0 | grep -iE 'drop|error|overflow'
# 6) 동적 디버그 활성화
echo 'file net/dsa/* +p' > /sys/kernel/debug/dynamic_debug/control
echo 'file drivers/net/dsa/* +p' > /sys/kernel/debug/dynamic_debug/control
dmesg -w
# 7) switchdev 이벤트 모니터링
bridge monitor all
# 8) MTU 정합 확인
ip link show | grep mtu
# 9) devlink 상태 확인
devlink dev show
devlink trap show
devlink resource show
ip link set eth0 master br0 -- 절대 금지!
일반적인 장애 패턴과 해결
| 증상 | 원인 | 해결 |
|---|---|---|
| lan0 netdev 미생성 | DTS 포트 정의 오류, 드라이버 미로드 | dmesg 확인, DTS 포트 reg/label 검증 |
| 패킷이 CPU에서 보이지 않음 | CPU 포트 링크 다운, phy-mode 불일치 | ethtool eth0, DTS phy-mode 확인 |
| 태그 decode 실패 로그 | 잘못된 태그 프로토콜 또는 칩 불일치 | cat /sys/class/net/lan0/dsa/tagging |
| 오프로드 미동작 | switchdev 드라이버 미구현 | bridge -d link show로 offload 확인 |
| 멀티칩 패킷 루프 | cascade 포트 STP 미설정 | bridge STP 활성화, VLAN 정합 확인 |
| MTU 초과 드롭 | master MTU < slave MTU + tag overhead | ip link set eth0 mtu 상향 |
| PTP 타임스탬프 누락 | 태그에 타임스탬프 필드 미포함 | ethtool -T 확인, PTP 하드웨어 지원 검증 |
| tc 오프로드 실패 | TCAM 리소스 부족 | devlink resource show, 룰 정리 |
/* 장애 패턴 예시: 잘못된 source port decode */
if (unlikely(src_port >= ds->num_ports)) {
netdev_warn(master, "invalid DSA src_port=%u (max=%u)\n",
src_port, ds->num_ports - 1);
return NULL; /* drop */
}
/* 해당 포트에 slave가 없는 경우 */
if (unlikely(!dsa_is_user_port(ds, src_port))) {
netdev_warn(master, "DSA src_port=%u is not user port\n",
src_port);
return NULL; /* drop */
}
실전 디버깅 플레이북
DSA 환경의 디버깅은 네트워크 스택, 스위치 하드웨어, DTS 설정의 세 층을 모두 점검해야 합니다. 아래는 실전에서 자주 사용하는 체계적인 디버깅 절차입니다.
######################################
# DSA 실전 디버깅 플레이북
######################################
# === Phase 1: 기본 토폴로지 확인 ===
# DSA 포트 관계 확인
ip -d link show | sed -n '/dsa\|master/p'
# 태그 프로토콜과 DSA 트리 정보
for dev in /sys/class/net/lan*; do
echo "$(basename $dev): $(cat $dev/dsa/tagging 2>/dev/null)"
done
# === Phase 2: 오프로드 상태 점검 ===
# 스위치 오프로드 상태 확인
bridge -d link show
bridge -d fdb show | head -30
bridge vlan show
# === Phase 3: CPU 포트 패킷 분석 ===
# CPU 포트 캡처 (태그 유무 확인)
tcpdump -i eth0 -e -nn -vv -c 50
# === Phase 4: 카운터 분석 ===
# ethtool 통계와 드롭 카운터
ethtool -S eth0 2>/dev/null | grep -iE 'drop|error|overflow|rx_|tx_'
ethtool -S lan0 2>/dev/null | grep -iE 'drop|error|overflow|rx_|tx_'
# === Phase 5: 동적 디버그 활성화 ===
echo 'file net/dsa/* +p' > /sys/kernel/debug/dynamic_debug/control
echo 'file drivers/net/dsa/* +p' > /sys/kernel/debug/dynamic_debug/control
dmesg -w # 별도 터미널에서 실시간 관찰
# === Phase 6: 트래픽 테스트 ===
# 포트 간 연결 테스트
ping -I lan0 192.168.1.1 -c 5
arping -I lan0 192.168.1.1 -c 5
# iperf3로 처리량 측정 (오프로드 효과 확인)
iperf3 -c 192.168.1.1 -B 192.168.1.100 -t 10
ip -d link show로 DSA 트리 확인
2. bridge -d link show로 offload 플래그 확인
3. tcpdump -i eth0 -e로 CPU 포트 태그 관찰
4. dmesg | grep dsa로 오류 메시지 확인
5. ethtool -S로 드롭 카운터 비교
태그 프로토콜 헤더 바이트 심층 분석
각 태그 프로토콜은 고유한 바이트 레이아웃을 가지며, 패킷 캡처 시 원시 바이트를 해석하는 능력이 실전 디버깅의 핵심입니다. 이 섹션에서는 주요 5개 태그 프로토콜의 비트 레벨 구조를 상세히 분석합니다.
Marvell DSA 4바이트 비트 레벨
/* Marvell DSA 태그 바이트 해석 유틸리티 (디버깅용) */
static void dsa_dump_tag(const u8 *tag, int len)
{
u8 mode = (tag[0] >> 6) & 0x3;
u8 tag_cmd = (tag[0] >> 4) & 0x3;
u8 src_dev = ((tag[0] & 0x1) << 4) | ((tag[1] >> 3) & 0xf);
u8 src_port= tag[1] & 0x1f;
u8 pri = (tag[2] >> 5) & 0x7;
u8 cfi = (tag[2] >> 4) & 0x1;
u16 vid = ((tag[2] & 0xf) << 8) | tag[3];
pr_debug("DSA: mode=%u cmd=%u dev=%u port=%u pri=%u cfi=%u vid=%u\n",
mode, tag_cmd, src_dev, src_port, pri, cfi, vid);
}
/* EDSA 8바이트 확장 필드 해석 */
static void edsa_dump_tag(const u8 *tag)
{
u16 etype = (u16)(tag[0] << 8) | tag[1];
u8 trunk_id = (tag[6] >> 4) & 0xf;
u8 ptp_action = tag[6] & 0x3;
u8 fpri = (tag[7] >> 5) & 0x7;
u8 mirror_code= tag[7] & 0x1f;
pr_debug("EDSA: etype=0x%04x trunk=%u ptp=%u fpri=%u mirror=%u\n",
etype, trunk_id, ptp_action, fpri, mirror_code);
}
Realtek RTL8 태그 바이트 분석 (8바이트)
Realtek RTL8365/RTL8366RB 시리즈는 EtherType 0x8899를 사용하는 8바이트 헤더 태그를 삽입합니다.
TX/RX에서 필드 해석이 다릅니다.
| 오프셋 | TX (FROM_CPU) | RX (TO_CPU) | 비트 |
|---|---|---|---|
| 0~1 | 0x8899 (EtherType) | 16 | |
| 2 | 프로토콜 버전 (0x04) | 프로토콜 버전 | 8 |
| 3 | REASON (cpu_tag 타입) | REASON (트랩 이유) | 8 |
| 4 | 목적 포트 마스크 상위 | 소스 포트 번호 | 8 |
| 5 | 목적 포트 마스크 하위 | 소스 포트 번호 확장 | 8 |
| 6 | QoS 우선순위, Learning 금지 | QoS 우선순위 | 8 |
| 7 | VLAN 관련 플래그 | VLAN 관련 플래그 | 8 |
/* net/dsa/tag_rtl8_4.c - RTL8 태그 핵심 구조 */
#define RTL8_4_TAG_LEN 8
#define RTL8_4_ETHERTYPE 0x8899
#define RTL8_4_PROTOCOL 0x04
static struct sk_buff *rtl8_4_tag_xmit(struct sk_buff *skb,
struct net_device *dev)
{
struct dsa_port *dp = dsa_slave_to_port(dev);
u8 *tag;
if (skb_cow_head(skb, RTL8_4_TAG_LEN) < 0)
return NULL;
skb_push(skb, RTL8_4_TAG_LEN);
dsa_alloc_etype_header(skb, RTL8_4_TAG_LEN);
tag = dsa_etype_header_pos_tx(skb);
tag[0] = (RTL8_4_ETHERTYPE >> 8) & 0xff;
tag[1] = RTL8_4_ETHERTYPE & 0xff;
tag[2] = RTL8_4_PROTOCOL;
tag[3] = 0x00;
tag[4] = 0x00;
tag[5] = BIT(dp->index); /* 목적 포트 비트맵 */
tag[6] = 0x00;
tag[7] = 0x00;
return skb;
}
Broadcom BRCM 태그 바이트 분석 (4바이트)
Broadcom SF2/BCM53xx 시리즈는 두 가지 변형을 사용합니다: tag_brcm(MAC 뒤 삽입)과 tag_brcm_prepend(프레임 앞 삽입).
| 비트 위치 | 필드 | 설명 |
|---|---|---|
| [31:24] | OpCode | 0x20=Ingress, 0x10=Egress |
| [23:21] | TC (Traffic Class) | QoS 우선순위 (0~7) |
| [20] | TE (Tag Enforce) | VLAN 태그 강제 플래그 |
| [19:16] | TS (Timestamp) | 타임스탬프 인덱스 |
| [15:8] | 소스/목적 포트 | TX: 목적 포트 비트맵, RX: 소스 포트 |
| [7:0] | Reason/이유 코드 | TO_CPU 트랩 이유 코드 |
/* net/dsa/tag_brcm.c - Broadcom 태그 구조체 */
#define BRCM_TAG_LEN 4
#define BRCM_TAG_TYPE_INGRESS 0x20
#define BRCM_TAG_TYPE_EGRESS 0x10
/* TX 방향: FROM_CPU -> Ingress OpCode */
static struct sk_buff *brcm_tag_xmit(struct sk_buff *skb,
struct net_device *dev)
{
struct dsa_port *dp = dsa_slave_to_port(dev);
u8 *brcm_tag;
skb_push(skb, BRCM_TAG_LEN);
dsa_alloc_etype_header(skb, BRCM_TAG_LEN);
brcm_tag = dsa_etype_header_pos_tx(skb);
brcm_tag[0] = BRCM_TAG_TYPE_INGRESS;
brcm_tag[1] = 0x00;
brcm_tag[2] = 0x00;
brcm_tag[3] = (1 << dp->index); /* 목적 포트 비트맵 */
return skb;
}
NXP SJA1105 VLAN 기반 태그
SJA1105는 고유한 접근법을 사용합니다. 전용 태그 헤더 대신 VLAN 태그를 메타데이터 캐리어로 재활용하며, 관리 프레임에는 별도의 메타프레임 메커니즘을 사용합니다.
| 경로 | 인코딩 방식 | VID 사용 | 비고 |
|---|---|---|---|
| TX (데이터) | VLAN 태그 VID에 목적 포트 인코딩 | VID 맵핑 테이블 사용 | 스위치 VLAN 테이블에 사전 프로비저닝 필요 |
| RX (데이터) | 소스 포트별 고유 VID 할당 | 포트 ID -> VID 역매핑 | VLAN 필터링과 공존 시 충돌 주의 |
| TX (관리) | 메타프레임 (Follow-up 프레임) | 별도 control 채널 | PTP, SPI 레지스터 접근 |
| RX (관리) | 메타프레임 + link-local 트랩 | 스위치 내부 큐 | STP BPDU, LLDP 등 |
/* SJA1105 VID 매핑 개념 */
/* TX: 사용자 포트 0 -> VID 1xx, 포트 1 -> VID 2xx, ... */
/* RX: VID에서 소스 포트 역산 */
static u16 sja1105_tx_vid(struct dsa_port *dp)
{
/* 스위치 VLAN 테이블에 미리 설정된 매핑 */
return SJA1105_DEFAULT_VLAN(dp->ds->index, dp->index);
}
/* 메타프레임 구조 (64바이트 고정 길이) */
struct sja1105_meta_frame {
u8 dst_mac[6]; /* 01:80:C2:00:00:01 (link-local) */
u8 src_mac[6]; /* 스위치 포트 MAC */
u16 ethertype; /* 0x0008 (SJA1105 전용) */
u64 tstamp; /* PTP 타임스탬프 */
u8 src_port; /* 소스 포트 */
u8 switch_id; /* 스위치 인덱스 */
};
tcpdump -i eth0 -e -xx -c 10으로 헥스 덤프를 확인하고, 위의 비트 레이아웃에 따라 수동 해석이 가능합니다.
slave netdev(lan0 등)에서 캡처하면 태그가 이미 제거된 프레임이 보입니다.
TX 인젝션 상세 메커니즘
TX 인젝션은 호스트 CPU에서 특정 스위치 포트로 프레임을 직접 전송하는 과정입니다.
dsa_slave_xmit()에서 태그 드라이버의 xmit()까지의 전체 경로와
각 단계에서의 skb 조작을 상세히 분석합니다.
/* TX 인젝션 전체 흐름 추적 (커널 내부) */
/* 1단계: 소켓에서 slave netdev로 */
/* sys_sendto() -> sock_sendmsg() -> ... -> dev_hard_start_xmit() */
/* 2단계: dsa_slave_xmit() 진입 */
static netdev_tx_t dsa_slave_xmit(struct sk_buff *skb,
struct net_device *dev)
{
struct dsa_slave_priv *p = netdev_priv(dev);
/* 통계 업데이트 (slave의 TX 카운터) */
dev_sw_netstats_tx_add(dev, 1, skb->len);
/* skb->cb 초기화 (DSA 내부 메타데이터용) */
memset(skb->cb, 0, sizeof(skb->cb));
/* 3단계: 태그 드라이버 xmit() 호출 */
skb = p->xmit(skb, dev);
if (!skb) {
/* xmit() 실패: headroom 부족 또는 skb 조작 실패 */
return NETDEV_TX_OK;
}
/* 4단계: master netdev로 전환 */
skb->dev = dsa_slave_to_master(dev);
/* 5단계: master의 TX 큐에 삽입 */
dev_queue_xmit(skb);
return NETDEV_TX_OK;
}
RX 추출 상세 메커니즘
RX 추출은 스위치 ASIC에서 CPU 포트로 올라온 태그 프레임을 해석하여,
올바른 slave netdev로 전달하는 과정입니다. master netdev의 rx_handler로 등록된
dsa_switch_rcv()가 NAPI poll 이후에 호출됩니다.
/* RX 추출 상세 코드 흐름 */
/* 1. master NIC NAPI poll -> netif_receive_skb() */
/* 2. RX handler 체인 -> dsa_switch_rcv() 호출 */
static rx_handler_result_t dsa_switch_rcv(struct sk_buff **pskb)
{
struct sk_buff *skb = *pskb;
struct dsa_port *cpu_dp = skb->dev->dsa_ptr;
/* CPU 포트 DSA 포인터 유효성 검사 */
if (unlikely(!cpu_dp))
return RX_HANDLER_PASS; /* DSA가 아닌 프레임: 통과 */
/* 3. 태그 드라이버 rcv() 호출 */
skb = cpu_dp->tag_ops->rcv(skb, skb->dev);
if (!skb) {
/* 태그 해석 실패: 잘못된 포트 번호, 손상된 태그 등 */
return RX_HANDLER_CONSUMED;
}
/* 4. skb->dev가 올바른 slave로 설정되었는지 확인 */
/* rcv()가 dsa_master_find_slave()로 매핑 */
/* 5. L2 헤더 재설정 */
skb_push(skb, ETH_HLEN);
skb->pkt_type = PACKET_HOST;
skb->protocol = eth_type_trans(skb, skb->dev);
/* 6. slave의 RX 통계 업데이트 */
dev_sw_netstats_rx_add(skb->dev, skb->len);
/* 7. 상위 프로토콜 스택으로 전달 */
netif_receive_skb(skb);
return RX_HANDLER_CONSUMED;
}
/* 태그 드라이버 rcv()에서 수행하는 핵심 작업 */
/* a. 태그 바이트 위치 확인 (header/tail) */
/* b. 소스 포트/스위치 ID 추출 */
/* c. 트랩 이유 코드 확인 (관리 프레임 등) */
/* d. 태그 바이트 제거 (skb_pull_rcsum / skb_trim) */
/* e. skb->dev = dsa_master_find_slave(dev, sw_id, port) */
flow_dissect() 콜백으로 태그 오프셋을 GRO에 알려줍니다.
master에서 ethtool -K eth0 gro off로 GRO를 비활성화하면 디버깅이 쉬워집니다.
MTU 헤드룸 정밀 계산
DSA 환경에서 MTU 관련 문제는 가장 흔한 장애 원인 중 하나입니다. 태그 오버헤드, VLAN 태그, QinQ 등이 복합되면 프레임 크기가 예상을 초과할 수 있습니다.
# MTU 정합 진단 스크립트
#!/bin/bash
echo "=== MTU 정합 진단 ==="
# master MTU
MASTER_MTU=$(ip -o link show eth0 | grep -oP 'mtu \K\d+')
echo "Master (eth0) MTU: $MASTER_MTU"
# 태그 오버헤드 확인
TAG_PROTO=$(cat /sys/class/net/lan0/dsa/tagging 2>/dev/null)
echo "Tag protocol: $TAG_PROTO"
# 각 slave MTU
for dev in /sys/class/net/lan*; do
NAME=$(basename $dev)
MTU=$(cat $dev/mtu)
echo "Slave $NAME MTU: $MTU"
done
# 경고: 일반적인 태그 오버헤드
echo ""
echo "일반적인 태그 오버헤드:"
echo " DSA (Marvell): 4B -> master >= slave + 4"
echo " EDSA: 8B -> master >= slave + 8"
echo " Ocelot IFH: 16B -> master >= slave + 16"
echo " KSZ (tail): 2B -> master >= slave + 2"
echo " tag_8021q: 4B -> master >= slave + 4"
# 자동 MTU 조정
# ip link set eth0 mtu $((SLAVE_MTU + TAG_OVERHEAD))
max_mtu를 제한하면 slave MTU 변경이 -ERANGE로 실패합니다.
ip link set eth0 mtu 9016이 먼저 성공하는지 확인한 후 slave MTU를 조정하세요.
멀티칩 패브릭 심화 (Cascading)
DSA는 하나의 트리에 최대 4개(칩 의존)의 스위치 칩을 데이지 체인 방식으로 연결할 수 있습니다.
이 구성에서 태그의 switch_id 필드가 핵심이며, 크로스-칩 FDB/VLAN 동기화가 필수적입니다.
/* 크로스-칩 브리지 FDB 동기화 */
/* lan0(SW0)과 lan6(SW2)을 같은 bridge에 추가하면: */
/* 1. 두 스위치 모두에 FDB 엔트리가 설치되어야 함 */
/* 2. cascade 포트의 VLAN 멤버십이 일치해야 함 */
/* 3. STP 상태가 모든 경로에서 Forwarding이어야 함 */
static int dsa_port_fdb_add(struct dsa_port *dp,
const unsigned char *addr,
u16 vid)
{
struct dsa_notifier_fdb_info info = {
.dp = dp,
.addr = addr,
.vid = vid,
};
/* DSA 코어가 트리 내 모든 스위치에 통지 */
return dsa_port_notify(dp, DSA_NOTIFIER_FDB_ADD, &info);
}
/* 각 스위치의 port_fdb_add()가 호출됨 */
/* - 로컬 스위치: 해당 포트에 FDB 추가 */
/* - 원격 스위치: cascade 포트에 FDB 추가 */
bridge link set lan0 learning off로 소프트웨어 학습을 비활성화하고
bridge link set lan0 flood off로 unknown unicast flooding을 비활성화한 후
tcpdump -i eth0 -e -xx로 CPU 포트에서 원인 프레임을 분석하세요.
Bridge Offload 심화
DSA 환경에서 bridge offload는 단순히 FDB/VLAN만이 아닙니다. STP 상태, flooding 제어, 멀티캐스트 라우터 포트, LAG 오프로드 등 브리지의 거의 모든 기능이 하드웨어로 오프로드될 수 있습니다.
| 기능 | switchdev 속성/객체 | DSA 콜백 | 하드웨어 동작 |
|---|---|---|---|
| STP 상태 | SWITCHDEV_ATTR_ID_PORT_STP_STATE | port_stp_state_set() | 포트 STP 상태 변경 (Disabled/Blocking/Listening/Learning/Forwarding) |
| Learning | SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS | port_bridge_flags() | ATU 자동 학습 활성화/비활성화 |
| Flooding | SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS | port_bridge_flags() | Unknown unicast/multicast/broadcast flooding 제어 |
| VLAN 필터링 | SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING | port_vlan_filtering() | 하드웨어 VLAN 필터 모드 활성화 |
| Ageing time | SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME | port_bridge_join() | ATU 에이징 타이머 설정 |
| MDB (멀티캐스트) | SWITCHDEV_OBJ_ID_PORT_MDB | port_mdb_add() | 멀티캐스트 필터 업데이트 |
| VLAN | SWITCHDEV_OBJ_ID_PORT_VLAN | port_vlan_add() | VLAN 테이블 업데이트 |
| MRP (Media Redundancy) | SWITCHDEV_OBJ_ID_MRP | port_mrp_add() | 산업용 링 프로토콜 오프로드 |
| MAB (MAC Auth Bypass) | port locked/mab 플래그 | port_bridge_flags() | 포트 잠금 + MAC 기반 인증 |
| Host FDB | 호스트 MAC 엔트리 | port_fdb_add() | CPU 포트 FDB 엔트리 관리 |
# Bridge Offload 전체 검증
# 브리지 생성 및 포트 추가
ip link add name br0 type bridge vlan_filtering 1 stp_state 1
ip link set br0 up
ip link set lan0 master br0
ip link set lan1 master br0
ip link set lan2 master br0
# 오프로드 상태 확인 (상세 모드)
bridge -d -j link show | python3 -m json.tool
# 포트별 플래그 확인
bridge link show dev lan0
# hairpin off guard off root_block off fastleave off
# learning on flood on mcast_flood on bcast_flood on
# flooding 제어 (unknown unicast 차단)
bridge link set dev lan2 flood off
# 멀티캐스트 라우터 포트 지정
bridge link set dev lan0 mcast_router 2
# STP 상태 확인
bridge link show dev lan0 | grep state
# state forwarding
# Host FDB 엔트리 (CPU MAC)
bridge fdb add 00:11:22:33:44:55 dev br0 self local
# 포트 격리 (isolated 모드)
bridge link set dev lan1 isolated on
# isolated 포트 간 직접 통신 차단 (Private VLAN 유사)
# MAB (MAC Authentication Bypass)
bridge link set dev lan2 locked on mab on
# 미인증 MAC은 CPU로 트랩 -> 인증 서버 연동
/* Bridge Offload 콜백 구현 예시 */
static int my_port_bridge_flags(struct dsa_switch *ds, int port,
struct switchdev_brport_flags flags,
struct netlink_ext_ack *extack)
{
if (flags.mask & BR_LEARNING) {
bool learning = !!(flags.val & BR_LEARNING);
hw_set_atu_learning(ds, port, learning);
}
if (flags.mask & BR_FLOOD) {
bool flood = !!(flags.val & BR_FLOOD);
hw_set_unicast_flood(ds, port, flood);
}
if (flags.mask & BR_MCAST_FLOOD) {
bool mcast_flood = !!(flags.val & BR_MCAST_FLOOD);
hw_set_multicast_flood(ds, port, mcast_flood);
}
if (flags.mask & BR_BCAST_FLOOD) {
bool bcast_flood = !!(flags.val & BR_BCAST_FLOOD);
hw_set_broadcast_flood(ds, port, bcast_flood);
}
if (flags.mask & BR_PORT_LOCKED) {
bool locked = !!(flags.val & BR_PORT_LOCKED);
hw_set_port_locked(ds, port, locked);
}
return 0;
}
/* STP 상태 설정 */
static int my_port_stp_state_set(struct dsa_switch *ds, int port,
u8 state)
{
u8 hw_state;
switch (state) {
case BR_STATE_DISABLED:
hw_state = PORT_STATE_DISABLED;
break;
case BR_STATE_BLOCKING:
case BR_STATE_LISTENING:
hw_state = PORT_STATE_BLOCKING;
break;
case BR_STATE_LEARNING:
hw_state = PORT_STATE_LEARNING;
break;
case BR_STATE_FORWARDING:
hw_state = PORT_STATE_FORWARDING;
break;
}
return hw_set_port_state(ds, port, hw_state);
}
tc 오프로딩 심화: TCAM/ACL 프로그래밍
tc flower 오프로드의 실제 구현은 TCAM(Ternary Content-Addressable Memory) 프로그래밍과 ACL(Access Control List) 룰 설치로 이루어집니다. 하드웨어 리소스 한계와 매치 키 제약을 이해해야 합니다.
| 매치 키 | flow_dissector 키 | 하드웨어 TCAM 필드 | 지원 범위 |
|---|---|---|---|
| Src/Dst MAC | FLOW_DISSECTOR_KEY_ETH_ADDRS | SA/DA 48비트 | 대부분 지원 |
| EtherType | FLOW_DISSECTOR_KEY_BASIC | EtherType 16비트 | 대부분 지원 |
| VLAN ID/PRI | FLOW_DISSECTOR_KEY_VLAN | VID 12비트 + PCP 3비트 | 대부분 지원 |
| Src/Dst IP | FLOW_DISSECTOR_KEY_IPV4_ADDRS | L3 필드 | 일부 (Ocelot) |
| L4 포트 | FLOW_DISSECTOR_KEY_PORTS | TCP/UDP 포트 | 일부 (Ocelot) |
| IP 프로토콜 | FLOW_DISSECTOR_KEY_BASIC | IP Protocol 8비트 | 일부 |
| DSCP/TC | FLOW_DISSECTOR_KEY_IP | TOS/TC 필드 | 일부 (Ocelot) |
# tc 오프로드 고급 예제
# 1. L2 필터링: 특정 MAC에서 특정 VLAN으로만 허용
tc qdisc add dev lan0 clsact
tc filter add dev lan0 ingress protocol 802.1q \
flower src_mac aa:bb:cc:00:00:01 vlan_id 100 \
action pass
tc filter add dev lan0 ingress protocol 802.1q \
flower src_mac aa:bb:cc:00:00:01 \
action drop
# 2. 포트 미러링 (ingress + egress)
tc filter add dev lan0 ingress protocol all \
flower \
action mirred egress mirror dev lan3
tc filter add dev lan0 egress protocol all \
flower \
action mirred egress mirror dev lan3
# 3. 대역폭 폴리싱 (per-port ingress)
tc filter add dev lan1 ingress protocol all \
flower \
action police rate 100mbit burst 64k conform-exceed drop/ok
# 4. VLAN 태그 조작 (push/pop)
tc filter add dev lan0 ingress protocol all \
flower \
action vlan push id 200 priority 3 protocol 802.1q
# 5. 패킷 리디렉션 (포트 간)
tc filter add dev lan0 ingress protocol arp \
flower \
action mirred egress redirect dev lan2
# 6. 오프로드 상태 상세 확인
tc -s -d filter show dev lan0 ingress
# in_hw/not_in_hw 플래그, hw_tc 카운터, used_hw_stats 확인
# 7. TCAM 리소스 사용량
devlink resource show pci/0000:00:00.0
# TCAM: 128/256 used, FDB: 1024/4096 used
ENOSPC 에러가 발생하며, 이후 추가되는 필터는 소프트웨어 폴백으로 동작합니다.
devlink resource show로 사용량을 모니터링하고, 불필요한 룰을 정리하세요.
FDB/MDB 고급 관리
실전 환경에서 FDB/MDB 관련 문제는 MAC 이동(flapping), 에이징 불일치, 크로스-칩 동기화 실패 등 다양합니다. 이 섹션에서는 고급 FDB/MDB 관리 기법과 트러블슈팅 방법을 다룹니다.
/* FDB 동기화 메커니즘 상세 */
/* 하드웨어 -> 소프트웨어 학습 통지 */
static void my_atu_event_handler(struct dsa_switch *ds,
struct atu_event *event)
{
switch (event->type) {
case ATU_LEARNED:
/* 하드웨어가 새 MAC 학습 */
dsa_port_learned_fdb(ds, event->port,
event->mac, event->vid);
break;
case ATU_AGED:
/* 하드웨어 에이징으로 MAC 삭제 */
dsa_port_forgotten_fdb(ds, event->port,
event->mac, event->vid);
break;
case ATU_VIOLATION:
/* MAC 이동 감지 (다른 포트에서 학습된 MAC) */
dsa_port_fdb_violation(ds, event->port,
event->mac, event->vid);
break;
}
}
# FDB/MDB 고급 관리 명령어
# 정적 FDB 엔트리 추가 (특정 포트에 고정)
bridge fdb add aa:bb:cc:dd:ee:01 dev lan0 master static
# 정적 MDB 엔트리 추가 (멀티캐스트 그룹)
bridge mdb add dev br0 port lan0 grp 239.1.1.1 permanent vid 100
bridge mdb add dev br0 port lan1 grp ff02::1 permanent # IPv6
# FDB 덤프 (offloaded 플래그 확인)
bridge -d fdb show | column -t
# FDB 에이징 타임 조정 (초 단위, 0=비활성화)
ip link set br0 type bridge ageing_time 30000 # 300초
# MAC 학습 비활성화 (보안 강화)
bridge link set dev lan2 learning off
# Flooding 제어 (포트별)
bridge link set dev lan2 flood off # unknown unicast
bridge link set dev lan2 mcast_flood off # unknown multicast
# FDB 변경 모니터링 (실시간)
bridge monitor fdb
# MDB IGMP 스누핑 상태
bridge -d mdb show
ip maddr show dev lan0
# FDB 엔트리 수 확인
bridge fdb show | wc -l
bridge fdb show | grep offloaded | wc -l
# MAC flapping 감지
bridge monitor fdb 2>&1 | grep -E 'add|del' | head -50
bridge fdb show에서 offloaded 플래그가 있는 엔트리가 하드웨어에 실제로 존재하는지
스위치 칩의 ATU 덤프 레지스터로 확인하세요.
VLAN-aware vs VLAN-unaware 브리지 모드
DSA 환경에서 브리지의 VLAN 필터링 모드는 데이터면 동작에 근본적인 차이를 만듭니다. VLAN-aware 모드에서는 스위치 하드웨어의 VTU(VLAN Table Unit)가 프레임 필터링을 수행하고, VLAN-unaware 모드에서는 VLAN 태그를 무시하고 포트 기반으로만 포워딩합니다.
# VLAN-aware 브리지 설정 (완전한 예제)
# 브리지 생성 (VLAN 필터링 활성화)
ip link add name br0 type bridge vlan_filtering 1
ip link set br0 up
# 포트 추가
ip link set lan0 master br0
ip link set lan1 master br0
ip link set lan2 master br0
# VLAN 할당 (access 포트 + trunk 포트)
# lan0: access 포트 (VLAN 100, untagged)
bridge vlan add dev lan0 vid 100 pvid untagged
bridge vlan del dev lan0 vid 1 # 기본 VLAN 제거
# lan1: access 포트 (VLAN 200, untagged)
bridge vlan add dev lan1 vid 200 pvid untagged
bridge vlan del dev lan1 vid 1
# lan2: trunk 포트 (VLAN 100 + 200, tagged)
bridge vlan add dev lan2 vid 100
bridge vlan add dev lan2 vid 200
bridge vlan del dev lan2 vid 1
# CPU 포트(br0 self)에도 VLAN 멤버십 필요
bridge vlan add dev br0 vid 100 self
bridge vlan add dev br0 vid 200 self
# 확인
bridge vlan show
# 출력 예시:
# port vlan-id
# lan0 100 PVID Egress Untagged
# lan1 200 PVID Egress Untagged
# lan2 100
# 200
# br0 100
# 200
# VLAN-unaware 브리지 설정 (기본 모드)
# 브리지 생성 (VLAN 필터링 비활성화 - 기본값)
ip link add name br0 type bridge
ip link set br0 up
ip link set lan0 master br0
ip link set lan1 master br0
ip link set lan2 master br0
# 이 모드에서는 VLAN 태그가 투명하게 통과
# 외부 VLAN 스위치가 태깅을 관리하는 경우에 적합
# 주의: tag_8021q 드라이버는 내부적으로 VLAN을 사용하므로
# VLAN-unaware 모드에서도 특정 VID가 예약됨
# 예약 VID 범위 확인:
bridge vlan show
/* VLAN 필터링 모드 전환 콜백 */
static int my_port_vlan_filtering(struct dsa_switch *ds, int port,
bool vlan_filtering,
struct netlink_ext_ack *extack)
{
if (vlan_filtering) {
/* VLAN-aware 모드 활성화 */
/* 1. VTU 필터링 활성화 */
hw_enable_vlan_filtering(ds, port);
/* 2. 기본 PVID 설정 */
hw_set_default_pvid(ds, port, 1);
/* 3. CPU 포트 VLAN 멤버십 확인 */
hw_ensure_cpu_vlan_member(ds);
} else {
/* VLAN-unaware 모드: 모든 프레임 통과 */
hw_disable_vlan_filtering(ds, port);
}
return 0;
}
/* tag_8021q와 VLAN-aware 모드 공존 처리 */
/* SJA1105, Ocelot 등에서 복잡한 VLAN 테이블 관리가 필요 */
static int my_tag_8021q_vlan_add(struct dsa_switch *ds, int port,
u16 vid, u16 flags)
{
/* 내부 DSA VID와 사용자 VID 분리 */
if (dsa_tag_8021q_is_vid(vid)) {
/* DSA 내부 VID: CPU 포트와 해당 사용자 포트만 멤버 */
return hw_vtu_add_internal(ds, vid, port);
}
/* 사용자 VID: 일반 VLAN 처리 */
return hw_vtu_add_user(ds, vid, port, flags);
}
tag_8021q 기반 드라이버(SJA1105, Ocelot 8021q 모드)는
내부적으로 VLAN ID를 DSA 메타데이터로 사용합니다. VLAN-aware 브리지 모드를 활성화하면
사용자 VLAN과 내부 DSA VLAN이 동일 VTU 테이블을 공유하므로,
VID 범위 충돌이 발생할 수 있습니다. 커널 6.x에서는 이를 해결하기 위해
dsa_tag_8021q_bridge_join()에서 VLAN 테이블을 동적으로 재구성합니다.
MRP (Media Redundancy Protocol) 오프로딩
MRP(IEC 62439-2)는 산업용 이더넷 환경에서 링 토폴로지의 고속 장애 복구를 제공하는 프로토콜입니다. DSA 드라이버는 MRP 프레임 처리를 스위치 하드웨어에 오프로드하여 수 밀리초 이내의 절환 시간을 달성할 수 있습니다.
| MRP 역할 | 설명 | DSA 콜백 | 하드웨어 동작 |
|---|---|---|---|
| MRM (Manager) | 링 관리자: 테스트 프레임 생성, 장애 감지 | port_mrp_add() | 하드웨어 테스트 프레임 생성기 활성화 |
| MRC (Client) | 링 클라이언트: MRP 프레임 포워딩 | port_mrp_add() | MRP EtherType 프레임 하드웨어 포워딩 |
| MRA (Auto-manager) | 자동 관리자: 매니저 부재 시 자동 승격 | port_mrp_add() | 매니저 감지 + 자동 역할 전환 |
# MRP 설정 예시 (iproute2 + bridge)
# 브리지 생성 (MRP 필수 설정)
ip link add name br0 type bridge stp_state 0
ip link set br0 up
ip link set lan0 master br0
ip link set lan1 master br0
# MRP 인스턴스 추가 (링 포트 2개 지정)
bridge mrp add dev br0 p_port lan0 s_port lan1 ring_id 1
# MRP 역할 설정 (MRM = Manager)
bridge mrp set ring dev br0 ring_id 1 ring_role mrm
# MRP 상태 확인
bridge mrp show dev br0
# ring_id 1: p_port lan0, s_port lan1, ring_role mrm
# ring_state open, ring_prio 0x8000
# MRP 테스트 프레임 간격 설정 (밀리초)
bridge mrp set ring dev br0 ring_id 1 ring_test_interval 3500
# 오프로드 확인 (in_hw 플래그)
bridge -d mrp show dev br0
/* DSA MRP 오프로드 콜백 구현 */
static int my_port_mrp_add(struct dsa_switch *ds, int port,
const struct switchdev_obj_mrp *mrp)
{
/* 링 포트에 MRP 프레임 포워딩 활성화 */
hw_mrp_enable_ring(ds, mrp->p_port, mrp->s_port, mrp->ring_id);
/* MRP EtherType (0x88E3) 프레임을 링 포트 간 하드웨어 포워딩 */
hw_mrp_set_forwarding(ds, mrp->ring_id, true);
return 0;
}
static int my_port_mrp_add_ring_role(struct dsa_switch *ds, int port,
const struct switchdev_obj_ring_role_mrp *role)
{
if (role->ring_role == BR_MRP_RING_ROLE_MRM) {
/* 매니저 역할: 하드웨어 테스트 프레임 생성기 활성화 */
hw_mrp_start_test_gen(ds, role->ring_id);
} else {
/* 클라이언트 역할: 테스트 프레임 포워딩만 */
hw_mrp_stop_test_gen(ds, role->ring_id);
}
return 0;
}
ocelot(Microchip/Felix), hellcreek(Hirschmann) 등 산업용 스위치 칩이 대표적입니다.
대부분의 가정용/기업용 스위치 칩(Marvell, Realtek, MediaTek)은 MRP 오프로드를 지원하지 않으며,
이 경우 소프트웨어 폴백으로 동작합니다 (절환 시간이 수십~수백 밀리초로 증가).
포트 격리 및 Private VLAN
DSA 환경에서 포트 격리(Port Isolation)는 같은 브리지에 속한 포트 간 직접 통신을 차단하면서 CPU(업링크) 포트와만 통신을 허용하는 기능입니다. 호텔 네트워크, 공유 Wi-Fi, 사설 클라우드 테넌트 분리 등에 사용됩니다.
# 포트 격리 설정 (호텔 네트워크 시나리오)
# 브리지 생성
ip link add name br0 type bridge
ip link set br0 up
# 모든 포트를 브리지에 추가
ip link set lan0 master br0
ip link set lan1 master br0
ip link set lan2 master br0
ip link set lan3 master br0
# 객실 포트를 격리 모드로 설정
bridge link set dev lan0 isolated on
bridge link set dev lan1 isolated on
bridge link set dev lan2 isolated on
# lan3는 공유 서버 (격리하지 않음 = promiscuous)
# 격리 포트 -> lan3: 허용
# 격리 포트 -> 격리 포트: 차단
# 확인
bridge -d link show
# lan0: isolated on
# lan1: isolated on
# lan2: isolated on
# lan3: isolated off (기본값)
# 테스트: lan0에서 lan1으로 ping -> 실패 (격리)
# 테스트: lan0에서 lan3으로 ping -> 성공 (promiscuous)
/* 포트 격리 하드웨어 오프로드 구현 */
static int my_port_bridge_flags(struct dsa_switch *ds, int port,
struct switchdev_brport_flags flags,
struct netlink_ext_ack *extack)
{
if (flags.mask & BR_ISOLATED) {
bool isolated = !!(flags.val & BR_ISOLATED);
if (isolated) {
/* 포트 VLAN 맵에서 다른 격리 포트 제거 */
hw_update_pvlan_map(ds, port, true);
/* CPU 포트와 비격리 포트만 허용 */
} else {
/* 포트 VLAN 맵 복원: 모든 브리지 포트 허용 */
hw_update_pvlan_map(ds, port, false);
}
}
return 0;
}
/* Marvell mv88e6xxx 포트 격리 구현 */
/* Port VLAN Map (PVT) 레지스터를 사용하여 */
/* 각 포트가 통신할 수 있는 포트 비트맵을 설정 */
static void mv88e6xxx_update_pvlan(struct dsa_switch *ds,
int port, bool isolated)
{
u16 output_ports = 0;
int i;
for (i = 0; i < ds->num_ports; i++) {
if (i == port)
continue;
if (isolated && dsa_port_is_isolated(ds, i))
continue; /* 다른 격리 포트 제외 */
output_ports |= BIT(i);
}
/* Port VLAN Map 레지스터 업데이트 */
mv88e6xxx_port_set_vlan_map(ds, port, output_ports);
}
bridge link set dev lan0 locked on mab on isolated on으로 설정하면,
미인증 MAC 주소의 트래픽을 CPU로 트랩하여 RADIUS 서버 등에서 인증 후
동적으로 FDB 엔트리를 추가하는 NAC(Network Access Control) 구성이 가능합니다.
인증된 MAC만 포워딩되고, 격리 모드로 다른 포트와의 직접 통신도 차단됩니다.
실전 스위치 드라이버 비교 (mv88e6xxx, sja1105, mt7530)
DSA 프레임워크를 구현하는 주요 스위치 드라이버 세 가지를 비교합니다. 각 드라이버는 태그 프로토콜, 오프로드 범위, 하드웨어 특성이 크게 다르며, 실전에서 선택 시 이러한 차이를 이해해야 합니다.
| 특성 | mv88e6xxx (Marvell) | sja1105 (NXP) | mt7530 (MediaTek) |
|---|---|---|---|
| 대표 칩셋 | 88E6085, 88E6190, 88E6352 | SJA1105, SJA1110 | MT7530, MT7531 |
| 태그 프로토콜 | DSA (4B) / EDSA (8B) | tag_8021q + 메타프레임 | tag_mtk (4B) |
| 태그 위치 | 헤더 (Src MAC 뒤) | VLAN 재사용 | 헤더 (EtherType) |
| 멀티칩 | 지원 (daisy-chain) | SJA1110에서 cascade 지원 | 미지원 |
| VLAN 테이블 | VTU (4096 엔트리) | 정적 구성 (SPI) | VTU (4096 엔트리) |
| FDB 크기 | 1024~8192 (모델 의존) | 1024 | 2048 |
| TCAM/ACL | 일부 모델 지원 | 미지원 | 제한적 |
| PTP | 일부 모델 지원 | 지원 (하드웨어 타임스탬프) | 미지원 |
| TSN (taprio) | 미지원 | 지원 (8개 TC) | 미지원 |
| MRP | 미지원 | 미지원 | 미지원 |
| tc flower | 제한적 | 미지원 | 제한적 |
| devlink | 리소스/트랩 | 제한적 | 제한적 |
| 구성 인터페이스 | MDIO 레지스터 | SPI + 정적 구성 블롭 | MDIO 레지스터 |
| 포트 수 | 5~11 (모델 의존) | 5 | 5~7 |
| CPU 인터페이스 | RGMII/SGMII | RGMII/SGMII/MII | RGMII/TRGMII |
| 주요 용도 | 임베디드 라우터, NAS, 산업용 | 자동차, TSN, 산업용 | 가정용 라우터 (OpenWrt) |
mv88e6xxx 드라이버 특성
/* drivers/net/dsa/mv88e6xxx/chip.c */
/* Marvell 88E6xxx 시리즈의 핵심 특성 */
/* 1. ATU (Address Translation Unit) 이벤트 인터럽트 */
/* - 하드웨어 FDB 학습/에이징/위반 이벤트를 인터럽트로 통지 */
/* - switchdev FDB notify로 소프트웨어 브리지와 동기화 */
static int mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq, void *dev_id)
{
struct mv88e6xxx_chip *chip = dev_id;
struct mv88e6xxx_atu_entry entry;
int err;
mv88e6xxx_reg_lock(chip);
err = mv88e6xxx_g1_atu_op(chip, 0, MV88E6XXX_G1_ATU_OP_GET_CLR_VIOLATION);
mv88e6xxx_reg_unlock(chip);
if (!err) {
/* ATU 위반 이벤트 처리 (MAC flapping 등) */
mv88e6xxx_handle_atu_violation(chip, &entry);
}
return IRQ_HANDLED;
}
/* 2. EDSA 태그의 멀티칩 라우팅 */
/* - SrcDev/DstDev 필드로 최대 32개 디바이스 주소 지정 */
/* - Cross-chip 브리지에서 cascade 포트로 자동 포워딩 */
/* 3. 포트 VLAN Map (PVT) */
/* - 각 포트별 허용 출력 포트 비트맵 */
/* - 포트 격리, PVLAN 구현의 기반 */
sja1105 드라이버 특성
/* drivers/net/dsa/sja1105/sja1105_main.c */
/* NXP SJA1105 시리즈의 핵심 특성 */
/* 1. SPI 기반 정적 구성 (Static Config) */
/* - 부팅 시 전체 구성을 SPI로 일괄 전송 */
/* - 런타임 변경이 제한적 (일부 테이블만 동적 수정 가능) */
static int sja1105_static_config_upload(struct sja1105_private *priv)
{
struct sja1105_static_config *config = &priv->static_config;
int rc;
/* 정적 구성 블롭을 SPI로 전송 */
rc = sja1105_spi_send_packed_buf(priv, SPI_WRITE,
SJA1105_CONFIG_START_ADDR,
config->buf, config->len);
if (rc < 0)
return rc;
/* 구성 적용 (스위치 리셋) */
return sja1105_clocking_setup(priv);
}
/* 2. TSN (Time-Sensitive Networking) 지원 */
/* - taprio: 시간 기반 게이트 스케줄링 (IEEE 802.1Qbv) */
/* - CBS: 크레딧 기반 셰이퍼 (IEEE 802.1Qav) */
/* - PTP: 하드웨어 타임스탬프 (메타프레임으로 전달) */
/* 3. tag_8021q 기반 태깅 */
/* - 전용 하드웨어 태그 없음 -> VLAN 재사용 */
/* - VLAN-aware 브리지와 공존 시 VID 충돌 처리 필요 */
mt7530 드라이버 특성
/* drivers/net/dsa/mt7530.c */
/* MediaTek MT7530/MT7531 시리즈의 핵심 특성 */
/* 1. SoC 내장 스위치 */
/* - MT7621/MT7622/MT7623 SoC에 내장된 5포트 기가비트 스위치 */
/* - TRGMII (Turbo RGMII) 인터페이스로 CPU 연결 */
static enum dsa_tag_protocol
mtk_get_tag_protocol(struct dsa_switch *ds, int port,
enum dsa_tag_protocol mp)
{
return DSA_TAG_PROTO_MTK;
}
/* 2. MT7531: MT7530의 개선 버전 */
/* - SGMII 인터페이스 추가 (2.5G 포트) */
/* - 개선된 VLAN 처리 */
/* - PHY 내장 (MT7530은 외장 PHY 필요) */
/* 3. OpenWrt에서 광범위하게 사용 */
/* - 가정용 라우터의 사실상 표준 DSA 드라이버 */
/* - swconfig 레거시에서 DSA로 전환 완료 */
/* MT7530 태그 구조 */
/* TX: [15:8]=DPID(목적포트마스크), [7:0]=제어플래그 */
/* RX: [15:8]=소스포트|ARL위반, [7:0]=VID|이유코드 */
static struct sk_buff *mtk_tag_xmit(struct sk_buff *skb,
struct net_device *dev)
{
struct dsa_port *dp = dsa_slave_to_port(dev);
u8 *tag;
if (skb_cow_head(skb, MTK_HDR_LEN) < 0)
return NULL;
skb_push(skb, MTK_HDR_LEN);
dsa_alloc_etype_header(skb, MTK_HDR_LEN);
tag = dsa_etype_header_pos_tx(skb);
/* 특수 EtherType + 목적 포트 비트맵 */
tag[0] = 0x40 | ((1 << dp->index) >> 8);
tag[1] = (1 << dp->index) & 0xff;
tag[2] = 0x00;
tag[3] = 0x00;
return skb;
}
- mv88e6xxx: 멀티칩 cascade가 필요하거나 FDB 이벤트 인터럽트가 중요한 산업용/NAS 환경
- sja1105: TSN(taprio, CBS, PTP) 기능이 핵심인 자동차/산업 자동화 환경
- mt7530: 비용 효율적인 가정용 라우터/AP, OpenWrt 기반 네트워크 장비
DSA LAG (Link Aggregation) 오프로드
DSA 프레임워크는 Linux bonding/team 인터페이스와 연동하여 하드웨어 LAG(Link Aggregation Group)를 스위치 칩에 오프로드할 수 있습니다. 이를 통해 CPU를 거치지 않고 라인 레이트로 LAG 멤버 간 트래픽 분배가 가능합니다.
# DSA LAG 설정 (bonding)
# bond 인터페이스 생성 (802.3ad LACP)
ip link add name bond0 type bond mode 802.3ad
# DSA 포트를 bond에 추가
ip link set lan0 master bond0
ip link set lan1 master bond0
ip link set bond0 up
# bond를 브리지에 추가 (선택적)
ip link add name br0 type bridge
ip link set br0 up
ip link set bond0 master br0
ip link set lan2 master br0
# LAG 상태 확인
cat /proc/net/bonding/bond0
ip -d link show bond0
# 오프로드 확인 (bridge -d에서 offload 플래그)
bridge -d link show
# 해싱 정책 설정
ip link set bond0 type bond xmit_hash_policy layer3+4
# layer2, layer2+3, layer3+4, encap2+3, encap3+4
/* DSA LAG 오프로드 콜백 */
static int my_port_lag_join(struct dsa_switch *ds, int port,
struct dsa_lag lag,
struct netdev_lag_upper_info *info,
struct netlink_ext_ack *extack)
{
int lag_id = lag.id;
/* 하드웨어 트렁크 그룹에 포트 추가 */
hw_trunk_add_port(ds, lag_id, port);
/* 해싱 정책 설정 */
if (info->tx_type == NETDEV_LAG_TX_TYPE_HASH) {
hw_trunk_set_hash(ds, lag_id, info->hash_type);
}
/* FDB에서 LAG 포트 번호 대신 트렁크 ID 사용 */
hw_fdb_set_trunk_mode(ds, port, lag_id);
return 0;
}
static int my_port_lag_leave(struct dsa_switch *ds, int port,
struct dsa_lag lag)
{
/* 하드웨어 트렁크 그룹에서 포트 제거 */
hw_trunk_remove_port(ds, lag.id, port);
/* 개별 포트 모드로 복원 */
hw_fdb_set_port_mode(ds, port);
return 0;
}
- 하드웨어 트렁크 그룹 수가 제한적입니다 (보통 4~8개). 초과 시 소프트웨어 폴백 발생
- 크로스-칩 LAG(서로 다른 스위치의 포트를 하나의 LAG로)는 일부 칩셋만 지원
- LACP PDU(프로토콜 프레임)는 항상 CPU로 트랩되어 소프트웨어에서 처리됨
- 해싱 알고리즘이 소프트웨어 bonding과 다를 수 있어 트래픽 분배 패턴이 상이할 수 있음
DSA 성능 튜닝
DSA 환경의 성능은 CPU 포트 대역폭, 태그 처리 오버헤드, GRO/GSO 설정, RPS/RFS 분배 정책 등 여러 요소에 의해 결정됩니다. 이 섹션에서는 실전 성능 튜닝 기법을 다룹니다.
# DSA 성능 튜닝 스크립트
# === 1. RPS (Receive Packet Steering) ===
# CPU 포트(eth0) 단일 큐 트래픽을 멀티코어로 분배
# 4코어 시스템의 경우 모든 코어 활용: 0xf
echo f > /sys/class/net/eth0/queues/rx-0/rps_cpus
echo 4096 > /sys/class/net/eth0/queues/rx-0/rps_flow_cnt
# RFS (Receive Flow Steering) 활성화
echo 32768 > /proc/sys/net/core/rps_sock_flow_entries
# === 2. GRO 최적화 ===
# master NIC에서 GRO 활성화 (태그가 flow_dissect 지원 시)
ethtool -K eth0 gro on
# 만약 태그 해석 문제 발생 시 GRO 비활성화
# ethtool -K eth0 gro off
# === 3. interrupt coalescing ===
# master NIC 인터럽트 병합 (처리량 vs 지연시간 트레이드오프)
ethtool -C eth0 rx-usecs 50 rx-frames 32
# === 4. 큐 설정 ===
# master NIC TX 큐 길이 조정
ip link set eth0 txqueuelen 2000
# === 5. NAPI budget 조정 ===
# NAPI poll당 처리할 패킷 수 (기본값 64)
sysctl -w net.core.netdev_budget=300
sysctl -w net.core.netdev_budget_usecs=8000
# === 6. needed_headroom 확인 ===
# 태그 드라이버의 headroom 설정 확인
for dev in /sys/class/net/lan*; do
NAME=$(basename $dev)
echo "$NAME: headroom=$(cat /sys/class/net/$NAME/needed_headroom 2>/dev/null)"
done
# === 7. iperf3 성능 측정 ===
# 하드웨어 포워딩 vs CPU 경유 비교
# 오프로드된 경로 (lan0 -> lan1, 같은 브리지)
# (스위치가 직접 포워딩, CPU 미관여)
iperf3 -c 192.168.1.2 -B 192.168.1.1 -t 30
# CPU 경유 경로 (라우팅이 필요한 경우)
iperf3 -c 10.0.1.2 -B 10.0.0.1 -t 30
# === 8. CPU 사용률 모니터링 ===
# DSA 트래픽 처리 중 CPU 사용률 확인
mpstat -P ALL 1
# softirq 비중이 높으면 RPS 분배 검토
cat /proc/softirqs | grep NET
phy-mode = "sgmii"로 변경하고, SoC MAC이 해당 모드를 지원하는지 확인해야 합니다.
MT7531은 SGMII 2.5G를 지원하며, Marvell 88E6190은 10G USXGMII도 지원합니다.
flow_dissect() 콜백이 올바르게 구현되지 않으면, 서로 다른 포트의 프레임이
하나의 GRO 세션으로 병합되어 태그 해석이 실패할 수 있습니다.
이 경우 ethtool -K eth0 gro off로 GRO를 비활성화하거나,
태그 드라이버의 flow_dissect 콜백을 수정해야 합니다.
대부분의 최신 커널(6.x) DSA 태그 드라이버는 이 문제가 해결되어 있습니다.
DSA 관련 sysfs/procfs 인터페이스
DSA 프레임워크는 sysfs를 통해 다양한 런타임 정보를 노출합니다. 디버깅 및 모니터링 시 이러한 인터페이스를 활용하면 커널 로그를 분석하지 않고도 DSA 상태를 빠르게 확인할 수 있습니다.
| 경로 | 설명 | 읽기/쓰기 | 예시 값 |
|---|---|---|---|
/sys/class/net/lanX/dsa/tagging | 현재 태그 프로토콜 | 읽기 | dsa, edsa, ocelot |
/sys/class/net/lanX/operstate | 포트 동작 상태 | 읽기 | up, down |
/sys/class/net/lanX/speed | 링크 속도 (Mbps) | 읽기 | 1000 |
/sys/class/net/lanX/duplex | 듀플렉스 모드 | 읽기 | full |
/sys/class/net/lanX/carrier | 캐리어(링크) 감지 | 읽기 | 1 |
/sys/class/net/lanX/mtu | MTU 값 | 읽기/쓰기 | 1500 |
/sys/class/net/lanX/needed_headroom | 필요 headroom (바이트) | 읽기 | 4, 8, 16 |
/sys/class/net/lanX/needed_tailroom | 필요 tailroom (바이트) | 읽기 | 0, 2 |
/sys/class/net/lanX/statistics/ | 포트 패킷 통계 | 읽기 | rx_bytes, tx_bytes 등 |
/sys/class/net/eth0/dsa_ptr | master의 DSA 포인터 | 읽기 | (커널 내부용) |
# DSA 상태 종합 조회 스크립트
#!/bin/bash
echo "===== DSA 포트 상태 종합 ====="
echo ""
for dev in /sys/class/net/lan*; do
NAME=$(basename $dev)
TAG=$(cat $dev/dsa/tagging 2>/dev/null || echo "N/A")
MTU=$(cat $dev/mtu 2>/dev/null || echo "N/A")
OPER=$(cat $dev/operstate 2>/dev/null || echo "N/A")
SPEED=$(cat $dev/speed 2>/dev/null || echo "N/A")
CARRIER=$(cat $dev/carrier 2>/dev/null || echo "N/A")
HEADROOM=$(cat $dev/needed_headroom 2>/dev/null || echo "N/A")
TAILROOM=$(cat $dev/needed_tailroom 2>/dev/null || echo "N/A")
RX_BYTES=$(cat $dev/statistics/rx_bytes 2>/dev/null || echo "0")
TX_BYTES=$(cat $dev/statistics/tx_bytes 2>/dev/null || echo "0")
RX_DROP=$(cat $dev/statistics/rx_dropped 2>/dev/null || echo "0")
TX_DROP=$(cat $dev/statistics/tx_dropped 2>/dev/null || echo "0")
printf "%-8s tag=%-10s mtu=%-6s oper=%-6s speed=%-6s carrier=%s\n" \
$NAME $TAG $MTU $OPER ${SPEED}Mbps $CARRIER
printf " headroom=%-4s tailroom=%-4s rx=%s tx=%s rx_drop=%s tx_drop=%s\n" \
$HEADROOM $TAILROOM $RX_BYTES $TX_BYTES $RX_DROP $TX_DROP
echo ""
done
# Master 정보
echo "===== Master (CPU 포트) ====="
MASTER=$(ip -o link show | grep 'dsa' | head -1 | awk -F'master ' '{print $2}' | awk '{print $1}')
if [ -n "$MASTER" ]; then
echo "Master device: $MASTER"
ip -s -d link show $MASTER
fi
devlink trap는 어떤 유형의 패킷이 CPU로 올라오는지 상세히 보여줍니다.
devlink trap show으로 지원되는 트랩 목록을 확인하고,
devlink trap group show으로 그룹별 통계를 확인하세요.
불필요한 트래픽이 CPU로 올라오면 성능 저하의 원인이 됩니다.
참고자료
- 커널 공식 문서: DSA
- 커널 문서: DSA 아키텍처
- 커널 문서: switchdev
- 커널 소스: net/dsa
- 커널 소스: drivers/net/dsa
- 커널 문서: DSA 구성 가이드
- Bridge/VLAN/Bonding 문서
- 이더넷 (Ethernet) -- L2 프레임 구조와 드라이버
- Network Device 드라이버 -- net_device 구조와 NAPI
- 802.1Q VLAN -- VLAN 태깅 메커니즘
- sk_buff -- 패킷 버퍼 자료구조
- TC (Traffic Control) -- tc 프레임워크
- devlink -- 디바이스 관리 프레임워크
- Open vSwitch (OVS) -- 소프트웨어 스위치 비교
- Netfilter -- 패킷 필터링 프레임워크