tc bpf(8) - wariua/manpages-ko GitHub Wiki

NAME

BPF - μž…λ ₯/좜λ ₯ 큐 규제λ₯Ό μœ„ν•œ BPF ν”„λ‘œκ·Έλž¨ λΆ„λ₯˜μž 및 ν–‰μœ„

SYNOPSIS

eBPF λΆ„λ₯˜μž(ν•„ν„°) λ˜λŠ” ν–‰μœ„:

tc filter ... bpf [ object-file OBJ_FILE ] [ section CLS_NAME ]
  [ export UDS_FILE ] [ verbose ] [ skip_hw | skip_sw ] [ police POLICE_SPEC ]
  [ action ACTION_SPEC ] [ classid CLASSID ]
tc action ... bpf [ object-file OBJ_FILE ] [ section CLS_NAME ]
  [ export UDS_FILE ] [ verbose ]

cBPF λΆ„λ₯˜μž(ν•„ν„°) λ˜λŠ” ν–‰μœ„:

tc filter ... bpf [ bytecode-file BPF_FILE | bytecode BPF_BYTECODE ]
  [ police POLICE_SPEC ] [ action ACTION_SPEC ] [ classid CLASSID ]
tc action ... bpf [ bytecode-file BPF_FILE | bytecode BPF_BYTECODE ]

DESCRIPTION

ν™•μž₯ 버클리 νŒ¨ν‚· ν•„ν„°(eBPF)와 전톡적 버클리 νŒ¨ν‚· ν•„ν„°(μ›λž˜λŠ” κ·Έλƒ₯ BPFμ§€λ§Œ ꡬ별을 μœ„ν•΄ μ—¬κΈ°μ„  cBPF라고 뢀름) λͺ¨λ‘λ₯Ό μ™„μ „νžˆ ν”„λ‘œκ·Έλž˜λ° κ°€λŠ₯ν•˜κ³  맀우 효율적인 λΆ„λ₯˜μž(classifier) 및 ν–‰μœ„(action)둜 μ‚¬μš©ν•  수 μžˆλ‹€. λ‘˜ λͺ¨λ‘λŠ” κ°„λ‹¨ν•œ ν”„λ‘œκ·Έλž¨ κ΅¬ν˜„μ„ μœ„ν•œ μž‘μ€ μΈμŠ€νŠΈλŸ­μ…˜ μ„ΈνŠΈλ₯Ό μ œκ³΅ν•˜λ©°, κ·Έ ν”„λ‘œκ·Έλž¨μ„ μ»€λ„λ‘œ μ•ˆμ „ν•˜κ²Œ μ μž¬ν•˜μ—¬ 컀널 κ³΅κ°„μ˜ μž‘μ€ 가상 λ¨Έμ‹ μ—μ„œ μ‹€ν–‰ν•  수 μžˆλ‹€. μ§€μ •ν•œ ν”„λ‘œκ·Έλž¨μ΄ 항상 μ’…λ£Œν•˜λ©° μ£½κ±°λ‚˜ 컀널 데이터λ₯Ό λˆ„μΆœμ‹œν‚€μ§€ μ•ŠμŒμ„ 컀널 λ‚΄ 검증기가 보μž₯ν•œλ‹€.

λ¦¬λˆ…μŠ€μ—μ„œλŠ” 일반적으둜 eBPFκ°€ cBPF의 λ’€λ₯Ό μž‡λŠ”λ‹€κ³  λ³Έλ‹€. 컀널 λ‚΄λΆ€μ—μ„œλŠ” cBPF ν‘œν˜„μ‹μ„ eBPF ν‘œν˜„μ‹μœΌλ‘œ λ³€ν™˜ν•˜μ—¬ μ‹€ν–‰ν•œλ‹€. 싀행을 인터프리터가 μˆ˜ν–‰ν•  μˆ˜λ„ 있고 ꡬ성 μ‹œμ μ— μ €μŠ€νŠΈμΈνƒ€μž„(JIT) 컴파일 λ˜μ–΄ λ„€μ΄ν‹°λΈŒ κΈ°κ³„μ–΄λ‘œ 돌 μˆ˜λ„ μžˆλ‹€. ν˜„μž¬ x86_64, ARM64, s390, ppc64, sparc64 μ•„ν‚€ν…μ²˜μ—μ„œ eBPF JITλ₯Ό μ§€μ›ν•˜λ©° PPC, SPARC, ARM, MIPSμ—λŠ” cBPFλŠ” μ§€μ›ν•˜μ§€λ§Œ (아직) eBPF JIT μ§€μ›μœΌλ‘œ μ „ν™˜ν•˜μ§€ μ•Šμ•˜λ‹€.

eBPF μΈμŠ€νŠΈλŸ­μ…˜ μ„ΈνŠΈμ˜ 기반 μ›λ¦¬λŠ” cBPF μΈμŠ€νŠΈλŸ­μ…˜ μ„ΈνŠΈμ™€ λΉ„μŠ·ν•˜λ˜ 더 λ‚˜μ€ λŸ°νƒ€μž„ μ„±λŠ₯을 μ–»κΈ° μœ„ν•΄ 기반 μ•„ν‚€ν…μ²˜μ— μ’€ 더 κ°€κΉκ²Œ μ„€κ³„ν•΄μ„œ λ„€μ΄ν‹°λΈŒ μΈμŠ€νŠΈλŸ­μ…˜ μ„ΈνŠΈλ₯Ό 더 잘 흉내낼 수 μžˆλ„λ‘ ν•˜μ˜€λ‹€. μΌλŒ€μΌ λ§€ν•‘μœΌλ‘œ JIT ν•  수 있게 μ„€κ³„λ˜μ—ˆλŠ”λ°, 덕뢄에 μ»΄νŒŒμΌλŸ¬κ°€ eBPF λ°±μ—”λ“œλ₯Ό 톡해 λ„€μ΄ν‹°λΈŒ 컴파일 μ½”λ“œμ— κ°€κΉκ²Œ λΉ λ₯΄κ²Œ λ™μž‘ν•˜λŠ” μ΅œμ ν™”λœ eBPF μ½”λ“œλ₯Ό 생성할 κ°€λŠ₯μ„±κΉŒμ§€ μ—΄λ¦°λ‹€. LLVMμ—μ„œ 그런 eBPF λ°±μ—”λ“œλ₯Ό μ œκ³΅ν•˜λ―€λ‘œ eBPF ν”„λ‘œκ·Έλž¨μ„ μ œν•œμ  C μ–Έμ–΄λ‘œ μ‰½κ²Œ μž‘μ„±ν•  수 μžˆλ‹€. 이 외에도 eBPF 기반 κ΅¬μ‘°μ—λŠ” "λ§΅"μ΄λΌλŠ” μš”μ†Œκ°€ μžˆλ‹€. eBPF 맡은 ν‚€/κ°’ μ €μž₯μ†ŒμΈλ°, 이λ₯Ό μ—¬λŸ¬ eBPF ν”„λ‘œκ·Έλž¨λ“€ 사이뿐 μ•„λ‹ˆλΌ eBPF ν”„λ‘œκ·Έλž¨κ³Ό μ‚¬μš©μž 곡간 μ‘μš© μ‚¬μ΄μ—μ„œλ„ κ³΅μœ ν•œλ‹€.

νŠΈλž˜ν”½ μ œμ–΄ μ„œλΈŒμ‹œμŠ€ν…œμ—μ„œ μž…λ ₯ 및 좜λ ₯ qdisc에 뢙일 수 μžˆλŠ” λΆ„λ₯˜μžμ™€ ν–‰μœ„λ₯Ό eBPFλ‚˜ cBPF둜 μž‘μ„±ν•  수 μžˆλ‹€. λ‹€λ₯Έ λΆ„λ₯˜μžμ™€ ν–‰μœ„λ“€μ— λŒ€ν•œ μž₯점은 eBPF/cBPFλŠ” λ²”μš©μ  ν”„λ ˆμž„μ›Œν¬λ₯Ό μ œκ³΅ν•˜κ³  μ‚¬μš©μžκ°€ νŠΉν™”λœ μš©λ„μ— 따라 효율적으둜 κ΅¬ν˜„ν•  수 μžˆλ‹€λŠ” 점이닀. κ·Έλ ‡κ²Œ μž‘μ„±ν•œ λΆ„λ₯˜μž λ‚΄μ§€ ν–‰μœ„λŠ” κ³Όμž‰ κΈ°λŠ₯으둜 κ³ μƒν•˜μ§€ μ•Šμ„ 것이고, λ”°λΌμ„œ 자기 ν•  일을 맀우 효율적으둜 μ‹€ν–‰ν•  수 μžˆλ‹€. 그리고 λΉ„μ„ ν˜• λΆ„λ₯˜κ°€ κ°€λŠ₯ν•΄μ§€κ³  심지어 ν–‰μœ„ 뢀뢄을 λΆ„λ₯˜λ‘œ λ³‘ν•©ν•˜λŠ” 것도 κ°€λŠ₯ν•˜λ‹€. 이λ₯Ό 효율적인 eBPF λ§΅ 자료 ꡬ쑰와 κ²°ν•©ν•˜λ©΄ 예λ₯Ό λ“€μ–΄ μ‚¬μš©μž κ³΅κ°„μ—μ„œ λΆ„λ₯˜μž 재적재 없이 μ»€λ„λ‘œ classid 같은 μƒˆ 정책을 λ°€μ–΄λ„£κ±°λ‚˜, μ–΄λŠ 맡에 μ €μž₯된 톡계λ₯Ό μˆ˜μ§‘ν•΄μ„œ λΆ€ν•˜λ₯Ό μ•Œμ•„λ‚΄κ³  λ‹€λ₯Έ 맡을 μ΄μš©ν•΄ λ™μ μœΌλ‘œ λΆ€ν•˜ 뢄산을 ν•  수 μžˆλ‹€.

PARAMETERS

object-file

μ‹€ν–‰ 및 링크 κ°€λŠ₯ ν˜•μ‹(ELF)이고 eBPF λͺ…λ Ή μ½”λ“œμ™€ eBPF λ§΅ μ •μ˜λ₯Ό 담은 였브젝트 νŒŒμΌμ„ 가리킨닀. eBPF λΆ„λ₯˜μžμ— 쀄 수 μžˆλŠ” eBPF 였브젝트 생성을 μ§€μ›ν•˜λŠ” ν”„λ‘œμ νŠΈλ‘œλŠ” clang(1)을 C μ–Έμ–΄ ν”„λ‘ νŠΈμ—”λ“œλ‘œ ν•˜λŠ” LLVM 컴파일러 인프라가 μžˆλ‹€. (μžμ„Έν•œ λ‚΄μš©μ€ EXAMPLES 절 μ°Έκ³ .) eBPF λΆ„λ₯˜μž λ‚΄μ§€ ν–‰μœ„λ₯Ό μ μž¬ν•  λ•Œ 이 μ˜΅μ…˜μ€ ν•„μˆ˜μ΄λ‹€.

section

였브젝트 νŒŒμΌμ—μ„œ eBPF λΆ„λ₯˜μž λ‚΄μ§€ ν–‰μœ„κ°€ μœ„μΉ˜ν•œ ELF μ„Ήμ…˜μ˜ 이름이닀. λΆ„λ₯˜μžλŠ” κΈ°λ³Έ μ„Ήμ…˜ 이름이 "classifier"이고 ν–‰μœ„λŠ” "action"이닀. ν•œ 였브젝트 νŒŒμΌμ— μ—¬λŸ¬ λΆ„λ₯˜μžμ™€ ν–‰μœ„λ“€μ„ 넣을 수 μžˆμœΌλ―€λ‘œ κΈ°λ³Έκ³Ό λ‹€λ₯Έ 경우 ν•΄λ‹Ή μ„Ήμ…˜ 이름을 λͺ…μ‹œν•΄μ•Ό ν•œλ‹€.

export

μœ λ‹‰μŠ€ 도메인 μ†ŒμΌ“ νŒŒμΌμ„ 가리킨닀. eBPF 였브젝트 νŒŒμΌμ— eBPF λ§΅ λͺ…μ„Έλ₯Ό 담은 "maps"λΌλŠ” μ„Ήμ…˜μ΄ μžˆλŠ” 경우 λ§΅ 파일 λ””μŠ€ν¬λ¦½ν„°λ₯Ό μœ λ‹‰μŠ€ 도메인 μ†ŒμΌ“μ„ 톡해 tc μ’…λ£Œ ν›„ 파일 λ””μŠ€ν¬λ¦½ν„°λ“€μ„ κ΄€λ¦¬ν•˜λŠ” eBPF "μ—μ΄μ „νŠΈ"μ—κ²Œ λ„˜κ²¨μ€„ 수 μžˆλ‹€. importλ₯Ό μœ„ν•œ IPC λ™μž‘μ„ κ΅¬ν˜„ν•˜λ©° κ·Έ λ””μŠ€ν¬λ¦½ν„°λ“€μ„ bpf(2) ν˜ΈμΆœμ— μ‚¬μš©ν•΄μ„œ κ°€λ Ή λͺ¨λ‹ˆν„°λ§μ΄λ‚˜ μƒˆ 정책을 내리기 μœ„ν•΄ μ‚¬μš©μž κ³΅κ°„μ—μ„œ eBPF λ§΅ 데이터λ₯Ό μ½κ±°λ‚˜ κ°±μ‹ ν•˜λŠ” 제3자 μ‘μš© ν”„λ‘œκ·Έλž¨μ΄ μ—μ΄μ „νŠΈκ°€ 될 수 μžˆλ‹€.

verbose

μ„€μ •ν•˜λ©΄ eBPF ν”„λ‘œκ·Έλž¨ μ μž¬κ°€ μ„±κ³΅ν•œ κ²½μš°μ—λ„ eBPF 검증기 좜λ ₯을 μ°λŠ”λ‹€. κΈ°λ³Έμ μœΌλ‘œλŠ” 였λ₯˜ μ‹œμ—λ§Œ μ‚¬μš©μžμ—κ²Œ 검증기 둜그λ₯Ό 좜λ ₯ν•œλ‹€.

skip_hw | skip_sw

ν•˜λ“œμ›¨μ–΄ μ˜€ν”„λ‘œλ“œ μ œμ–΄ ν”Œλž˜κ·Έ. 기본적으둜 TCλŠ” κ°€λŠ₯ν•œ 경우 ν•„ν„°λ₯Ό ν•˜λ“œμ›¨μ–΄λ‘œ μ˜€ν”„λ‘œλ“œ ν•˜λ €κ³  μ‹œλ„ν•œλ‹€. skip_hwλŠ” μ˜€ν”„λ‘œλ“œ μ‹œλ„λ₯Ό λͺ…μ‹œμ μœΌλ‘œ λΉ„ν™œμ„±ν™”ν•œλ‹€. skip_swλŠ” μ˜€ν”„λ‘œλ“œλ₯Ό κ°•μ œν•˜κ³  컀널 λ‚΄ eBPF ν”„λ‘œκ·Έλž¨ 싀행을 λΉ„ν™œμ„±ν™”ν•œλ‹€. 이 ν”Œλž˜κ·Έλ₯Ό μ„€μ •ν–ˆλŠ”λ° ν•˜λ“œμ›¨μ–΄ μ˜€ν”„λ‘œλ“œκ°€ κ°€λŠ₯ν•˜μ§€ μ•ŠμœΌλ©΄ 컀널이 였λ₯˜λ₯Ό μ•Œλ¦¬κ³  ν•„ν„°κ°€ μ•„μ˜ˆ μ„€μΉ˜λ˜μ§€ μ•ŠλŠ”λ‹€.

police

eBPF/cBPF λΆ„λ₯˜μžλ₯Ό μœ„ν•œ 선택적 λ§€κ°œλ³€μˆ˜μ΄λ©°, κ°€λ Ή μž…λ ₯ qdisc μƒμ˜ λΆ„λ₯˜μžμ— 뢙일 tc(1) λ‚΄ policeλ₯Ό μ§€μ •ν•œλ‹€.

action

eBPF/cBPF λΆ„λ₯˜μžλ₯Ό μœ„ν•œ 선택적 λ§€κ°œλ³€μˆ˜μ΄λ©°, λΆ„λ₯˜μžμ— 뢙일 tc(1) λ‚΄ 후속 ν–‰μœ„λ₯Ό μ§€μ •ν•œλ‹€.

classid, flowid

이 eBPF/cBPF λΆ„λ₯˜μžλ₯Ό μœ„ν•œ κΈ°λ³Έ νŠΈλž˜ν”½ μ œμ–΄ 클래슀 μ‹λ³„μžμ΄λ‹€. 이 κΈ°λ³Έ 클래슀 μ‹λ³„μžλ₯Ό eBPF/cBPF ν”„λ‘œκ·Έλž¨ λ°˜ν™˜ μ½”λ“œλ‘œ λŒ€μ²΄ν•  μˆ˜λ„ μžˆλ‹€. κΈ°λ³Έ λ°˜ν™˜ μ½”λ“œ -1은 μ—¬κΈ° μ œκ³΅ν•œ κΈ°λ³Έ 클래슀 μ‹λ³„μžλ₯Ό μ‚¬μš©ν•˜λΌλŠ” λœ»μ΄λ‹€. 그리고 eBPF/cBPF ν”„λ‘œκ·Έλž¨ λ°˜ν™˜ μ½”λ“œκ°€ 0이면 μΌμΉ˜κ°€ μ—†μ—ˆλ‹€λŠ” μ˜λ―Έλ‹€. 이 λ‘˜ μ™Έμ˜ λ‹€λ₯Έ λ°˜ν™˜ μ½”λ“œκ°€ κΈ°λ³Έ classidλ₯Ό λŒ€μ‹ ν•˜κ²Œ λœλ‹€. 이λ₯Ό 톡해 eBPF/cBPF ν”„λ‘œκ·Έλž¨ ν•˜λ‚˜λ§ŒμœΌλ‘œ 효율적인 λΉ„μ„ ν˜• λΆ„λ₯˜κ°€ κ°€λŠ₯ν•˜λ‹€. μ—¬λŸ¬ 클래슀 μ‹λ³„μžλ₯Ό μœ„ν•΄ ν”„λ‘œκ·Έλž¨μ„ μ—¬λŸ¬ 개 μ‚¬μš©ν•΄μ„œ ν”„λ‘œκ·Έλž¨λ§ˆλ‹€ νŒ¨ν‚· λ‚΄μš©μ„ λ‹€μ‹œ νŒŒμ‹± ν•˜μ§€ μ•Šμ•„λ„ λœλ‹€.

bytecode

cBPF λΆ„λ₯˜μž 및 ν–‰μœ„ μ μž¬μ—λ§Œ μ‚¬μš©ν•˜κ³  μžˆλ‹€. cBPF λ°”μ΄νŠΈμ½”λ“œλ₯Ό 's,c t f k,c t f k,c t f k,...' ν˜•νƒœμ˜ ν…μŠ€νŠΈ λ¬Έμžμ—΄λ‘œ 직접 μ „λ‹¬ν•œλ‹€. μ—¬κΈ°μ„œ sλŠ” μ΄μ–΄μ§€λŠ” 4νŠœν”Œλ“€μ˜ 개수λ₯Ό λ‚˜νƒ€λ‚Έλ‹€. 4νŠœλΈ” ν•˜λ‚˜λŠ” μ‹­μ§„μˆ˜λ‘œ 된 c t f k둜 μ΄λ€„μ§€λŠ”λ°, cλŠ” cBPF λͺ…λ Ή μ½”λ“œ(opcode)λ₯Ό, tλŠ” 참일 λ•Œ 점프 λŒ€μƒ μ˜€ν”„μ…‹μ„, fλŠ” 거짓일 λ•Œ 점프 λŒ€μƒ μ˜€ν”„μ…‹μ„, kλŠ” μ¦‰μ‹œμ  μƒμˆ˜/λ¦¬ν„°λŸ΄μ„ λ‚˜νƒ€λ‚Έλ‹€. 이런 ν˜•μ‹μœΌλ‘œ μ½”λ“œλ₯Ό 생성해 μ£ΌλŠ” μ—¬λŸ¬ 도ꡬ듀이 μžˆλŠ”λ°, 예λ₯Ό λ“€μ–΄ λ¦¬λˆ…μŠ€ 컀널 μ†ŒμŠ€ 트리의 tools/net/ μ•„λž˜μ— bpf_asm이 μžˆλ‹€. λ”°λΌμ„œ μ†μœΌλ‘œ 직접 μž‘μ„±ν•  ν•„μš”κ°€ μ—†λ‹€. cBPF λΆ„λ₯˜μžλ‚˜ ν–‰μœ„λ₯Ό μ μž¬ν•˜λ € ν•  λ•ŒλŠ” bytecodeλ‚˜ bytecode-file μ˜΅μ…˜μ΄ ν•„μˆ˜μ΄λ‹€.

bytecode-file

λ§ˆμ°¬κ°€μ§€λ‘œ cBPF λΆ„λ₯˜μž λ‚΄μ§€ ν–‰μœ„λ₯Ό μ μž¬ν•˜λŠ” 데 쓰인닀. νš¨κ³ΌλŠ” bytecode와 κ°™λ‹€. cBPF λ°”μ΄νŠΈμ½”λ“œκ°€ λͺ…λ Ήν–‰μœΌλ‘œ 직접 μ „λ‹¬λ˜λŠ” 게 μ•„λ‹ˆλΌ ν…μŠ€νŠΈ 파일 μ•ˆμ— μžˆμ„ 뿐이닀.

EXAMPLES

eBPF 도ꡬ

eBPF μ—μ΄μ „νŠΈ μ½”λ“œλ₯Ό ν¬ν•¨ν•œ μ œλŒ€λ‘œ 된 예λ₯Ό iproute2 μ†ŒμŠ€ νŒ¨ν‚€μ§€μ˜ examples/bpf/μ—μ„œ 찾을 수 μžˆλ‹€.

μ „μ œ 쑰건으둜 컀널에 bpf(2)λΌλŠ” eBPF μ‹œμŠ€ν…œ 호좜이 ν™œμ„±ν™”λ˜μ–΄ μžˆμ–΄μ•Ό ν•˜κ³  νŠΈλž˜ν”½ μ œμ–΄ μ„œλΈŒμ‹œμŠ€ν…œμ„ μœ„ν•œ cls_bpf 및 act_bpf 컀널 λͺ¨λ“ˆμ΄ μžˆμ–΄μ•Ό ν•œλ‹€. μ•„ν‚€ν…μ²˜μ—μ„œ μ§€μ›ν•˜λŠ” λŒ€λ‘œ eBPF/cBPF JIT 지원을 켜렀면 λ‹€μŒκ³Ό 같이 ν•œλ‹€.

echo 1 > /proc/sys/net/core/bpf_jit_enable

LLVM을 톡해 μ œμ•½λœ C νŒŒμΌμ„ 컴파일 ν•  수 μžˆλ‹€.

clang -O2 -emit-llvm -c bpf.c -o - | llc -march=bpf -filetype=obj -o bpf.o

ν–₯ν›„ 컴파일러 호좜 방법이 더 κ°„λ‹¨ν•΄μ§ˆ μˆ˜λ„ μžˆμ„ ν…Œμ§€λ§Œ 당뢄간은 λ‹€μŒκ³Ό 같이 μ—μΌλ¦¬μ–΄μŠ€ ν•΄ λ‘λŠ” 게 κ°„νŽΈν•˜λ‹€.

__bcc() {
        clang -O2 -emit-llvm -c $1 -o - | \
        llc -march=bpf -filetype=obj -o "`basename $1 .c` .o"
}

alias bcc=__bcc

λͺ¨λ“  νŠΈλž˜ν”½μ΄ κΈ°λ³Έ classid둜 (λ°˜ν™˜ μ½”λ“œ -1) κ±Έλ¦¬λŠ” κ°€μž₯ μž‘μ€ 단독 μ½”λ“œλŠ” λ‹€μŒκ³Ό κ°™λ‹€.

#include <linux/bpf.h>

#ifndef __section
# define __section(x)  __attribute__((section(x), used))
#endif

__section("classifier") int cls_main(struct __sk_buff *skb)
{
        return -1;
}

char __license[] __section("license") = "GPL";

μ•„λž˜ eBPF ν”„λ‘œκ·Έλž˜λ° λΆ€μ ˆμ—μ„œ 더 λ§Žμ€ 예λ₯Ό λ³Ό 수 μžˆλ‹€. μ—¬κΈ°μ„  도ꡬ에 μ§‘μ€‘ν•œλ‹€.

ν–‰μœ„ 등을 μœ„ν•œ λ‹€μ–‘ν•œ μ„Ήμ…˜λ“€μ΄ 더 μžˆμ„ 수 μžˆλ‹€. κ·Έλž˜μ„œ eBPF둜 된 였브젝트 νŒŒμΌμ— μ—¬λŸ¬ μ§„μž…μ μ΄ μžˆμ„ 수 μžˆλ‹€. ν•˜μ§€λ§Œ tc둜 μ„€μ •ν•  λ•ŒλŠ” ꡬ체적 μ§„μž…μ μ„ λͺ…μ‹œν•΄μ•Ό ν•œλ‹€. 그리고 μ œμ•½λœ C μ½”λ“œμ— λΌμ΄μ„ μŠ€κ°€ ν¬ν•¨λ˜μ–΄μ•Ό ν•˜λ©° λΌμ΄μ„ μŠ€ λ¬Έμžμ—΄ 문법은 λ¦¬λˆ…μŠ€ 컀널 λͺ¨λ“ˆμ—μ„œμ™€ κ°™λ‹€. 컀널은 일뢀 eBPF ν•¨μˆ˜λ“€μ„ GPL ν˜Έν™˜ λΌμ΄μ„ μŠ€λ‘œλ§Œ μ œμ•½ν•  κΆŒν•œμ„ κ°€μ§€λ©°, κ·Έλž˜μ„œ 그런 λΌμ΄μ„ μŠ€ 뢈일치 λ°œμƒ μ‹œ ν”„λ‘œκ·Έλž¨μ„ μ»€λ„λ‘œ μ μž¬ν•˜λŠ” 것을 κ±°μ ˆν•  μˆ˜λ„ μžˆλ‹€.

컴파일 ν•΄μ„œ λ‚˜μ˜€λŠ” 였브젝트 νŒŒμΌμ€ 일반 였브젝트 νŒŒμΌμ— μ‚¬μš©ν•˜λŠ” ν‰λ²”ν•œ λ„κ΅¬λ“€λ‘œ μ‚΄νŽ΄λ³Ό 수 μžˆλ‹€. κ°€λ Ή objdump(1)둜 ELF μ„Ήμ…˜ 헀더듀을 μ‚΄νŽ΄λ³Ό 수 μžˆλ‹€.

objdump -h bpf.o
[...]
3 classifier    000007f8  0000000000000000  0000000000000000  00000040  2**3
                CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
4 action-mark   00000088  0000000000000000  0000000000000000  00000838  2**3
                CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
5 action-rand   00000098  0000000000000000  0000000000000000  000008c0  2**3
                CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
6 maps          00000030  0000000000000000  0000000000000000  00000958  2**2
                CONTENTS, ALLOC, LOAD, DATA
7 license       00000004  0000000000000000  0000000000000000  00000988  2**0
                CONTENTS, ALLOC, LOAD, DATA
[...]

κΈ°λ³Έ ELF μ„Ήμ…˜μ— λΆ„λ₯˜μžκ°€ λ‹΄κΈ΄ 였브젝트 νŒŒμΌμ„ κ°€μ§€κ³  eBPF λΆ„λ₯˜μžλ₯Ό μΆ”κ°€ν•˜λŠ” 건 κ°„λ‹¨ν•˜λ‹€. (참고둜 "object-file" λŒ€μ‹  "obj"처럼 쀄여 μ“Έ μˆ˜λ„ μžˆλ‹€.)

bcc bpf.c
tc filter add dev em1 parent 1: bpf obj bpf.o flowid 1:1

λΆ„λ₯˜μžκ°€ "mycls"λΌλŠ” ELF μ„Ήμ…˜μ— μžˆλŠ” 경우 같은 λͺ…령을 λ‹€μŒκ³Ό 같이 ν˜ΈμΆœν•΄μ•Ό ν•œλ‹€.

tc filter add dev em1 parent 1: bpf obj bpf.o sec mycls flowid 1:1

클래슀 섀정을 찍으면 μ‹λ³„μžμ˜ μœ„μΉ˜κ°€ λ‚˜μ˜¨λ‹€. 즉, 였브젝트 파일 "bpf.o"의 "mycls" μ„Ήμ…˜μ—μ„œ μ™”λ‹€κ³  μ•Œλ €μ€€λ‹€.

tc filter show dev em1
filter parent 1: protocol all pref 49152 bpf
filter parent 1: protocol all pref 49152 bpf handle 0x1 flowid 1:1 bpf.o:[mycls]

같은 ν”„λ‘œκ·Έλž¨μ„ 좜λ ₯이 μ•„λ‹ˆλΌ μž…λ ₯ qdisc에 μ„€μΉ˜ν•  μˆ˜λ„ μžˆλ‹€.

tc qdisc add dev em1 handle ffff: ingress
tc filter add dev em1 parent ffff: bpf obj bpf.o sec mycls flowid ffff:1

λ§ˆμ°¬κ°€μ§€λ‘œ 찍어 λ³Ό 수 μžˆλ‹€.

tc filter show dev em1 parent ffff:
filter protocol all pref 49152 bpf
filter protocol all pref 49152 bpf handle 0x1 flowid ffff:1 bpf.o:[mycls]

λΆ„λ₯˜μž 및 ν–‰μœ„λ₯Ό μž…λ ₯에 뢙일 λ•ŒλŠ” μ‹€μ œ 기반 큐 κ·œμ œκ°€ μ—†λ‹€λŠ” μ œμ•½μ΄ μžˆλ‹€. μž…λ ₯μ—μ„œ ν•  수 μžˆλŠ” 건 νŒ¨ν‚·μ„ λΆ„λ₯˜ν•˜κ±°λ‚˜, λ³€μ‘°ν•˜κ±°λ‚˜, μž¬μ§€ν–₯ν•˜κ±°λ‚˜, λ²„λ¦¬λŠ” 것이닀. μž…λ ₯ μͺ½μ—μ„œ 큐 μ‚¬μš©μ΄ ν•„μš”ν•  λ•ŒλŠ” μž…λ ₯μ—μ„œ νŒ¨ν‚·μ„ ifb μž₯λΉ„λ‘œ μž¬μ§€ν–₯ν•΄μ•Ό ν•œλ‹€. μ•„λ‹ˆλ©΄ policing을 μ“Έ 수 μžˆλ‹€. 이 외에도 μ›μΉ˜ μ•ŠλŠ” νŒ¨ν‚·μ΄ λ„€νŠΈμ›Œν¬ μŠ€νƒ μƒμœ„ 계측에 κ°€κΈ° 전에 일찍 λ²„λ¦¬λŠ” 지점을 λ‘κ±°λ‚˜, 좜λ ₯κ³Ό κ³΅μœ ν•  수 μžˆλŠ” eBPF λ§΅λ“€λ‘œ λ„€νŠΈμ›Œν¬ μ‚¬μš© 톡계λ₯Ό κΈ°λ‘ν•˜κ±°λ‚˜, 이λ₯΄κ²Œ μ‘°μž‘ν•˜κ±°λ‚˜ λ‹€λ₯Έ λ„€νŠΈμ›Œν¬ μž₯치둜 μž¬μ§€ν–₯ν•  수 μžˆλŠ” 지점을 λ‘λŠ” 데 μž…λ ₯을 μ΄μš©ν•  수 μžˆλ‹€.

μ—¬λŸ¬ eBPF ν–‰μœ„μ™€ λΆ„λ₯˜μžλ“€μ„ ν•œ 파일 λ‚΄μ˜ μ—¬λŸ¬ μ„Ήμ…˜μ— 넣을 수 μžˆλ‹€. 이 경우 κΈ°λ³Έκ³Ό λ‹€λ₯Έ μ„Ήμ…˜ 이름을 μ•Œλ € μ£Όμ–΄μ•Ό ν•˜λŠ”λ°, λ‹€μŒ μ˜ˆμ—μ„œ 두 ν–‰μœ„ λͺ¨λ‘κ°€ κ·Έλ ‡λ‹€.

tc filter add dev em1 parent 1: bpf obj bpf.o flowid 1:1 \
                         action bpf obj bpf.o sec action-mark \
                         action bpf obj bpf.o sec action-rand ok

이 λ°©μ‹μ˜ μž₯점은 ν”„λ‘œκ·Έλž¨μ— eBPF 맡이 κ΅¬ν˜„λ˜μ–΄ μžˆλŠ” 경우 λΆ„λ₯˜μžμ™€ 두 ν–‰μœ„μ—μ„œ κ³΅μœ ν•  수 μžˆλ‹€λŠ” 점이닀.

tc(8) μ„€μ • 후에도 μ‚¬μš©μž κ³΅κ°„μ—μ„œ eBPF 맡에 μ ‘κ·Όν•  수 μžˆλ„λ‘ ν•˜κΈ° μœ„ν•΄ μœ λ‹‰μŠ€ 도메인 μ†ŒμΌ“μ„ 톡해 eBPF μ—μ΄μ „νŠΈλ‘œ μ†Œμœ κΆŒμ„ 이전할 수 μžˆλ‹€. 이λ₯Ό κ΅¬ν˜„ν•  수 μžˆλŠ” 방법이 두 κ°€μ§€ μžˆλ‹€.

  1. 자체 eBPF μ—μ΄μ „νŠΈλ₯Ό λ§Œλ“€μ–΄μ„œ 직접 μœ λ‹‰μŠ€ 도메인 μ†ŒμΌ“μ„ μ€€λΉ„ν•˜κ³  tc(8)κ°€ μš”κ΅¬ν•˜λŠ” ν”„λ‘œν† μ½œμ„ κ΅¬ν˜„ν•˜κΈ°. iproute2 μ†ŒμŠ€ νŒ¨ν‚€μ§€μ˜ examples/bpf/ μ•ˆμ—μ„œ 이λ₯Ό μœ„ν•œ μ˜ˆμ‹œ μ½”λ“œλ₯Ό λ³Ό 수 μžˆλ‹€.

  2. tc execλ₯Ό μ‚¬μš©ν•΄μ„œ μœ λ‹‰μŠ€ 도메인 μ†ŒμΌ“μœΌλ‘œ eBPF λ§΅ 파일 λ””μŠ€ν¬λ¦½ν„°κ°€ μ΄μ „λ˜λ©΄ sh(1) 같은 μ‘μš© ν”„λ‘œμ„ΈμŠ€κ°€ μƒμ„±λ˜κ²Œ ν•˜κΈ°. 이 λ°©μ‹μ˜ μž₯점은 tcκ°€ 파일 λ””μŠ€ν¬λ¦½ν„°λ“€μ„ μ‹€ν–‰ ν™˜κ²½μ— μ§‘μ–΄λ„£μ–΄ μ£Όλ―€λ‘œ stdin, stdout, stderr 파일 λ””μŠ€ν¬λ¦½ν„°μ²˜λŸΌ μ“Έ 수 μžˆλ‹€λŠ” 점이닀. μ΄λ ‡κ²Œ ν•˜λ©΄ κ·Έ fd μ†Œμœ  μ…Έ μ•ˆμ—μ„œ μ‚¬μš©μž μ‘μš©μ„ μ’…λ£Œν•˜κ³  μž¬μ‹œμž‘ν•΄λ„ eBPF λ§΅ 파일 λ””μŠ€ν¬λ¦½ν„°κ°€ 사라지지 μ•ŠλŠ”λ‹€. μ•žμ˜ λΆ„λ₯˜μžμ™€ ν–‰μœ„ μ‘°ν•©μœΌλ‘œ 예λ₯Ό λ“€μžλ©΄ λ‹€μŒκ³Ό κ°™λ‹€.

tc exec bpf imp /tmp/bpf
tc filter add dev em1 parent 1: bpf obj bpf.o exp /tmp/bpf flowid 1:1 \
                         action bpf obj bpf.o sec action-mark \
                         action bpf obj bpf.o sec action-rand ok

eBPF 맡을 λΆ„λ₯˜μžμ™€ ν–‰μœ„μ—μ„œ κ³΅μœ ν•œλ‹€κ³  ν•˜λ©΄ λΆ„λ₯˜μžλ‚˜ ν–‰μœ„ λͺ…λ Ήμ—μ„œ ν•œ 번만 내보이면 μΆ©λΆ„ν•˜λ‹€. 였브젝트 νŒŒμΌμ„ 처음 νŒŒμ‹± ν•˜λŠ” μ‹œμ μ— tcκ°€ eBPF λ§΅ 파일 λ””μŠ€ν¬λ¦½ν„°λ“€μ„ λͺ¨λ‘ μ€€λΉ„ν•΄ 쀄 것이닀.

셸이 μƒˆλ‘œ 생성될 λ•Œ κ·Έ ν™˜κ²½μ— 두 κ°€μ§€ eBPF κ΄€λ ¨ λ³€μˆ˜κ°€ μžˆλ‹€. BPF_NUM_MAPSλŠ” μœ λ‹‰μŠ€ 도메인 μ†ŒμΌ“μ„ 톡해 μ΄μ „λœ λ§΅λ“€μ˜ 총개수λ₯Ό μ•Œλ € μ€€λ‹€. 그리고 BPF_MAP<X>의 값은 eBPF μ—μ΄μ „νŠΈ μ‘μš©μ—μ„œ μ ‘κ·Όν•  수 μžˆλŠ” 파일 λ””μŠ€ν¬λ¦½ν„° λ²ˆν˜Έμ΄λ‹€. 즉 이 값을 bpf(2) μ‹œμŠ€ν…œ ν˜ΈμΆœμ— κ·ΈλŒ€λ‘œ 파일 λ””μŠ€ν¬λ¦½ν„°λ‘œ μ‚¬μš©ν•΄μ„œ eBPF λ§΅ 값듀을 μ‘°νšŒν•˜κ±°λ‚˜ λ³€κ²½ν•  수 μžˆλ‹€. <X>λŠ” eBPF 맡의 μ‹λ³„μžλ₯Ό λ‚˜νƒ€λ‚Έλ‹€. tc eBPF λ§΅ λͺ…μ„Έμ—μ„œ struct bpf_elf_map의 id 멀버에 ν•΄λ‹Ήν•œλ‹€.

이 μ˜ˆμ—μ„œλŠ” ν™˜κ²½μ΄ λ‹€μŒκ³Ό 같이 λœλ‹€.

sh# env | grep BPF
    BPF_NUM_MAPS=3
    BPF_MAP1=6
    BPF_MAP0=5
    BPF_MAP2=7
sh# ls -la /proc/self/fd
    [...]
    lrwx------. 1 root root 64 Apr 14 16:46 5 -> anon_inode:bpf-map
    lrwx------. 1 root root 64 Apr 14 16:46 6 -> anon_inode:bpf-map
    lrwx------. 1 root root 64 Apr 14 16:46 7 -> anon_inode:bpf-map
sh# my_bpf_agent

eBPF μ—μ΄μ „νŠΈλ₯Ό μ΄μš©ν•˜λ©΄ μ‚¬μš©μž κ³΅κ°„μ—μ„œ eBPF 맡을 미리 μ±„μš°κ³  맡을 톡해 톡계λ₯Ό κ΄€μ°°ν•˜μ—¬ κ·Έ ν”Όλ“œλ°±μ— λ”°λΌμ„œ κ°€λ Ή λŸ°νƒ€μž„ 쀑에 eBPF λ§΅ κ°’μ˜ classidλ₯Ό λ°”κΏ” μ“Έ 수 μžˆλ‹€. eBPF μ—μ΄μ „νŠΈλŠ” 일반적인 μ‘μš©μ²˜λŸΌ κ΅¬ν˜„ν•˜λ―€λ‘œ λ™μ μœΌλ‘œ μ™ΈλΆ€ μ»¨νŠΈλ‘€λŸ¬μ—κ²Œμ„œ νŠΈλž˜ν”½ μ œμ–΄ 정책을 λ°›μ•„μ„œ eBPF 맡으둜 λ‚΄λ € μ£Όμ–΄ 망 쑰건에 λ™μ μœΌλ‘œ 적응할 μˆ˜λ„ μžˆλ‹€. λ˜ν•œ eBPF 맡을 λ‹€λ₯Έ μ’…λ₯˜μ˜ (κ°€λ Ή 좔적을 μœ„ν•œ) eBPF ν”„λ‘œκ·Έλž¨λ“€κ³Ό κ³΅μœ ν•  μˆ˜λ„ μžˆμœΌλ―€λ‘œ μ•„μ£Ό κ°•λ ₯ν•œ 쑰합을 κ΅¬ν˜„ν•  수 μžˆλ‹€.

eBPF ν”„λ‘œκ·Έλž˜λ°

eBPF λΆ„λ₯˜μžμ™€ ν–‰μœ„λŠ” μ œμ•½λœ C λ¬Έλ²•μœΌλ‘œ κ΅¬ν˜„ν•œλ‹€. (ν–₯ν›„ μƒˆλ‘œμš΄ μ–Έμ–΄ ν”„λ‘ νŠΈμ—”λ“œκ°€ μΆ”κ°€λ‘œ 지원될 μˆ˜λ„ μžˆλ‹€.)

헀더 파일 linux/bpf.h에 eBPF ν”„λ‘œκ·Έλž¨μ—μ„œ ν˜ΈμΆœν•  수 μžˆλŠ” eBPF 헬퍼 ν•¨μˆ˜λ“€μ΄ μžˆλ‹€. 이 맨 νŽ˜μ΄μ§€μ—μ„œλŠ” μ΅œμ†Œν•œμ˜ 단독 μ˜ˆμ‹œ 두 κ°€μ§€λ§Œ μ œκ³΅ν•œλ‹€. eBPF의 κ°€λŠ₯성을 더 잘 보여 μ£ΌλŠ” μ œλŒ€λ‘œ 된 흐름 뢄석 ν”„λ‘œκ·Έλž¨μ„ 보렀면 iproute2 μ†ŒμŠ€ νŒ¨ν‚€μ§€μ˜ examples/bpfλ₯Ό μ‚΄νŽ΄λ³΄λ©΄ λœλ‹€.

μ§€μ›ν•˜λŠ” C ν”„λ‘œκ·Έλž¨ 32λΉ„νŠΈ λΆ„λ₯˜μž λ°˜ν™˜ μ½”λ“œμ™€ κ·Έ 의미:

  • 0: 뢈일치λ₯Ό λ‚˜νƒ€λ‚Έλ‹€.
  • -1: λͺ…λ Ήν–‰μ—μ„œ μ„€μ •ν•œ κΈ°λ³Έ classidλ₯Ό λ‚˜νƒ€λ‚Έλ‹€.
  • κ·Έ μ™Έ: κΈ°λ³Έ classidλ₯Ό λŒ€μ‹ ν•˜μ—¬ λΉ„μ„ ν˜• 일치 검사가 κ°€λŠ₯ν•˜κ²Œ ν•΄ μ€€λ‹€.

μ§€μ›ν•˜λŠ” C ν”„λ‘œκ·Έλž¨ 32λΉ„νŠΈ ν–‰μœ„ λ°˜ν™˜ μ½”λ“œμ™€ κ·Έ 의미 (linux/pkt_cls.h):

  • TC_ACT_OK (0): νŒ¨ν‚· 처리 νŒŒμ΄ν”„λΌμΈμ„ μ’…λ£Œν•˜κ²Œ 되며 νŒ¨ν‚·μ΄ 계속 μ§„ν–‰ν•˜λ„λ‘ ν•œλ‹€.
  • TC_ACT_SHOT (2): νŒ¨ν‚· 처리 νŒŒμ΄ν”„λΌμΈμ„ μ’…λ£Œν•˜κ²Œ 되며 νŒ¨ν‚·μ„ 버린닀.
  • TC_ACT_UNSPEC (-1): tcμ—μ„œ μ„€μ •ν•œ κΈ°λ³Έ ν–‰μœ„λ₯Ό μ‚¬μš©ν•œλ‹€. (λΆ„λ₯˜μžμ˜ -1 λ°˜ν™˜κ³Ό λΉ„μŠ·ν•˜λ‹€.)
  • TC_ACT_PIPE (3): λ‹€μŒ ν–‰μœ„κ°€ 있으면 거기둜 λ„˜μ–΄κ°€κ²Œ λœλ‹€.
  • TC_ACT_RECLASSIFY (1): νŒ¨ν‚· 처리 νŒŒμ΄ν”„λΌμΈμ„ μ’…λ£Œν•˜κ²Œ 되며 νŒ¨ν‚· λΆ„λ₯˜λ₯Ό μ²˜μŒλΆ€ν„° μ‹œμž‘ν•œλ‹€.
  • κ·Έ μ™Έ: λ‹€λ₯Έ 값은 λͺ¨λ‘ λΉ„λͺ…μ„Έ λ°˜ν™˜ μ½”λ“œμ΄λ‹€.

λΆ„λ₯˜μž λ°˜ν™˜ μ½”λ“œμ™€ ν–‰μœ„ λ°˜ν™˜ μ½”λ“œ λͺ¨λ‘λ₯Ό eBPF 및 eBPF ν”„λ‘œκ·Έλž¨μ—μ„œ μ‚¬μš©ν•  수 μžˆλ‹€.

쑰그만 λΆ„λ₯˜κΈ° μ˜ˆμ‹œλ₯Ό 톡해 μ œμ•½λœ C 문법을 μ‚΄νŽ΄λ³΄μž. μ—¬κΈ°μ„  κ°€λ Ή μ»¨ν…Œμ΄λ„ˆμ—μ„œ μΆœλ°œν•œ 좜λ ₯ νŒ¨ν‚·λ“€μ— [0, 255] λ²”μœ„λ‘œ 이미 ν‘œμ‹œκ°€ λ˜μ–΄ μžˆλ‹€κ³  κ°€μ •ν•œλ‹€. 이 ν”„λ‘œκ·Έλž¨μ€ μ‚¬μš©μž 곡간을 μœ„ν•΄ ν‘œμ‹œ 값에 λŒ€ν•œ 톡계λ₯Ό λ‚΄λ©° ν‘œμ‹œ κ°’ 자체λ₯Ό λΆ€ ν•Έλ“€λ‘œ ν•΄μ„œ 루트 qdisc둜 classidλ₯Ό λ§€ν•‘ ν•œλ‹€.

#include <stdint.h>
#include <asm/types.h>

#include <linux/bpf.h>
#include <linux/pkt_sched.h>

#include "helpers.h"

struct tuple {
        long packets;
        long bytes;
};

#define BPF_MAP_ID_STATS        1 /* μ—μ΄μ „νŠΈμ˜ λ§΅ μ‹λ³„μž */
#define BPF_MAX_MARK            256

struct bpf_elf_map __section("maps") map_stats = {
        .type           =       BPF_MAP_TYPE_ARRAY,
        .id             =       BPF_MAP_ID_STATS,
        .size_key       =       sizeof(uint32_t),
        .size_value     =       sizeof(struct tuple),
        .max_elem       =       BPF_MAX_MARK,
};

static inline void cls_update_stats(const struct __sk_buff *skb,
                                    uint32_t mark)
{
        struct tuple *tu;

        tu = bpf_map_lookup_elem(&map_stats, &mark);
        if (likely(tu)) {
                __sync_fetch_and_add(&tu->packets, 1);
                __sync_fetch_and_add(&tu->bytes, skb->len);
        }
}

__section("cls") int cls_main(struct __sk_buff *skb)
{
        uint32_t mark = skb->mark;

        if (unlikely(mark >= BPF_MAX_MARK))
                return 0;

        cls_update_stats(skb, mark);

        return TC_H_MAKE(TC_H_ROOT, mark);
}

char __license[] __section("license") = "GPL";

또 λ‹€λ₯Έ μž‘μ€ μ˜ˆλŠ” λͺ©μ  포트 80을 RSS 결과에 따라 [8080, 8087] κ΅¬κ°„μœΌλ‘œ μ—­λ‹€μ€‘ν™”ν•˜λŠ” 포트 리닀이렉터인데 μž…λ ₯ qdisc에 뢙일 수 μžˆλ‹€. 좜λ ₯ μͺ½κ³Ό IPv6 지원을 μΆ”κ°€ν•˜λŠ” 것은 λ…μžμ—κ²Œ μ—°μŠ΅μœΌλ‘œ 남겨둔닀.

#include <asm/types.h>
#include <asm/byteorder.h>

#include <linux/bpf.h>
#include <linux/filter.h>
#include <linux/in.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/tcp.h>

#include "helpers.h"

static inline void set_tcp_dport(struct __sk_buff *skb, int nh_off,
                                 __u16 old_port, __u16 new_port)
{
        bpf_l4_csum_replace(skb, nh_off + offsetof(struct tcphdr, check),
                            old_port, new_port, sizeof(new_port));
        bpf_skb_store_bytes(skb, nh_off + offsetof(struct tcphdr, dest),
                            &new_port, sizeof(new_port), 0);
}

static inline int lb_do_ipv4(struct __sk_buff *skb, int nh_off)
{
        __u16 dport, dport_new = 8080, off;
        __u8 ip_proto, ip_vl;

        ip_proto = load_byte(skb, nh_off +
                             offsetof(struct iphdr, protocol));
        if (ip_proto != IPPROTO_TCP)
                return 0;

        ip_vl = load_byte(skb, nh_off);
        if (likely(ip_vl == 0x45))
                nh_off += sizeof(struct iphdr);
        else
                nh_off += (ip_vl & 0xF) << 2;

        dport = load_half(skb, nh_off + offsetof(struct tcphdr, dest));
        if (dport != 80)
                return 0;

        off = skb->queue_mapping & 7;
        set_tcp_dport(skb, nh_off - BPF_LL_OFF, __constant_htons(80),
                      __cpu_to_be16(dport_new + off));
        return -1;
}

__section("lb") int lb_main(struct __sk_buff *skb)
{
        int ret = 0, nh_off = BPF_LL_OFF + ETH_HLEN;

        if (likely(skb->protocol == __constant_htons(ETH_P_IP)))
                ret = lb_do_ipv4(skb, nh_off);

        return ret;
}

char __license[] __section("license") = "GPL";

λ‘˜ λͺ¨λ‘μ—μ„œ μ‚¬μš©ν•˜λŠ” 헬퍼 헀더 파일 helpers.hλŠ” λ‹€μŒκ³Ό κ°™λ‹€.

/* 기타 헬퍼 맀크둜 */
#define __section(x) __attribute__((section(x), used))
#define offsetof(x, y) __builtin_offsetof(x, y)
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)

/* μ‚¬μš© λ§΅ ꡬ쑰 */
struct bpf_elf_map {
    __u32 type;
    __u32 size_key;
    __u32 size_value;
    __u32 max_elem;
    __u32 id;
};

/* μ‚¬μš© BPF ν•¨μˆ˜ λͺ‡ κ°€μ§€ */
static int (*bpf_skb_store_bytes)(void *ctx, int off, void *from,
                                  int len, int flags) =
     (void *) BPF_FUNC_skb_store_bytes;
static int (*bpf_l4_csum_replace)(void *ctx, int off, int from,
                                  int to, int flags) =
     (void *) BPF_FUNC_l4_csum_replace;
static void *(*bpf_map_lookup_elem)(void *map, void *key) =
     (void *) BPF_FUNC_map_lookup_elem;

/* μ‚¬μš© BPF λ‚΄λΆ€ ν•¨μˆ˜ λͺ‡ κ°€μ§€ */
unsigned long long load_byte(void *skb, unsigned long long off)
    asm ("llvm.bpf.load.byte");
unsigned long long load_half(void *skb, unsigned long long off)
    asm ("llvm.bpf.load.half");

μ΅œμƒ κ΄€ν–‰μœΌλ‘œ κΆŒν•˜λŠ” 것은 κ°œλ³„ λΆ„λ₯˜μžμ™€ λ³„λ„μ˜ ν–‰μœ„λ“€μ„ λ§Œλ“œλŠ” λŒ€μ‹  eBPF λΆ„λ₯˜μž ν•˜λ‚˜λ§Œ tc둜 μ μž¬ν•΄μ„œ κ±°κΈ°μ„œ ν•„μš”ν•œ 검사와 μ‘°μž‘μ„ λͺ¨λ‘ μˆ˜ν–‰ν•˜λŠ” 것이닀. μ£Όμ–΄μ§„ μš©λ„μ— λ§žμΆ°μ§„ λΆ„λ₯˜μž ν•˜λ‚˜λ§Œ μ‹€ν–‰ν•˜λŠ” 게 κ°€μž₯ 효율적일 것이닀.

eBPF 디버깅

bpfλ₯Ό μœ„ν•œ tc의 filter와 action λͺ…λ Ή λͺ¨λ‘μ— 선택적인 verbose λ§€κ°œλ³€μˆ˜κ°€ μžˆμ–΄μ„œ 이λ₯Ό μ΄μš©ν•΄ eBPF 검증기 둜그λ₯Ό 쑰사할 수 μžˆλ‹€. 였λ₯˜ μ‹œμ—λŠ” 기본으둜 μ°νžŒλ‹€.

eBPF/cBPF JIT 컴파일러λ₯Ό μΌ  κ²½μš°μ— λ§Œλ“€μ–΄μ§„ λͺ…λ Ή μ½”λ“œ μ΄λ―Έμ§€μ˜ 디버그 좜λ ₯을 컀널 둜그둜 찍도둝 ν•  μˆ˜λ„ 있으며 dmesg(1)λ₯Ό 톡해 이λ₯Ό 읽을 수 μžˆλ‹€.

echo 2 > /proc/sys/net/core/bpf_jit_enable

λ¦¬λˆ…μŠ€ 컀널 μ†ŒμŠ€ 트리의 tools/net/μ—λŠ” bpf_jit_disasmμ΄λΌλŠ” μž‘μ€ 헬퍼 ν”„λ‘œκ·Έλž¨μ΄ μžˆλ‹€. 컀널 λ‘œκ·Έμ—μ„œ λͺ…λ Ή μ½”λ“œ 이미지 덀프λ₯Ό μ½μ–΄μ„œ μ—­μ–΄μ…ˆλΈ” κ²°κ³Όλ₯Ό 찍어 μ€€λ‹€.

bpf_jit_disasm -o

κ·Έ 외에도 λ¦¬λˆ…μŠ€ μ»€λ„μ—λŠ” test_bpfλΌλŠ” κ΄‘λ²”μœ„ν•œ eBPF/cBPF ν…ŒμŠ€νŠΈ μŠ€μœ„νŠΈ λͺ¨λ“ˆλ„ 포함돼 μžˆλ‹€.

modprobe test_bpf

μ΄λ ‡κ²Œ ν•˜λ©΄ λ‹€μ–‘ν•œ ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€λ“€μ„ μˆ˜ν–‰ν•΄μ„œ κ²°κ³Όλ₯Ό 컀널 둜그둜 μ°λŠ”λ‹€. dmesg(1)둜 이λ₯Ό 쑰사할 수 μžˆλ‹€. JIT μ»΄νŒŒμΌλŸ¬κ°€ 켜져 μžˆλŠ”μ§€ 여뢀에 따라 κ²°κ³Όκ°€ λ‹€λ₯Ό 수 μžˆλ‹€. μ‹€νŒ¨ν•œ ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€κ°€ μžˆλŠ” 경우 λͺ¨λ“ˆ μ μž¬κ°€ μ‹€νŒ¨ν•˜κ²Œ λœλ‹€. κ·Έ 경우 κ΄€λ ¨ JIT μž‘μ„±μžλ“€κ³Ό λ¦¬λˆ…μŠ€ 컀널 및 λ„€νŠΈμ›Œν‚Ή 메일링 리슀트둜 버그λ₯Ό 보고해 μ£ΌκΈ°λ₯Ό κ°•λ ₯히 κΆŒν•œλ‹€.

cBPF

일반적으둜 λΆ„λ₯˜μžμ™€ ν–‰μœ„λ₯Ό eBPF둜 κ΅¬ν˜„ν•˜λŠ” 걸둜 μ „ν™˜ν•˜κΈ°λ₯Ό κΆŒν•œλ‹€. λ‹€λ§Œ 완전성을 μœ„ν•΄ cBPF ν”„λ‘œκ·Έλž¨ μž‘μ„±μ— λŒ€ν•œ μ—¬κΈ° λͺ‡ λ§ˆλ””λ₯Ό 남긴닀.

λ§ˆμ°¬κ°€μ§€λ‘œ μ•žμ„œ μ–ΈκΈ‰ν•œ κ²ƒμ²˜λŸΌ bpf_jit_enable μŠ€μœ„μΉ˜λ₯Ό μΌ€ 수 μžˆλ‹€. bpf_jit_disasm 같은 도ꡬ도 eBPF와 cBPF μ–΄λŠ μͺ½ μ½”λ“œλ₯Ό μ μž¬ν•˜λ €λŠ”μ§€μ™€ λ¬΄κ΄€ν•˜λ‹€.

eBPFμ—μ„œμ²˜λŸΌ λΆ„λ₯˜μžμ™€ ν–‰μœ„λ₯Ό μ œμ•½λœ C둜 κ΅¬ν˜„ν•˜μ§€ μ•Šκ³  λ‹¨μˆœν•œ μ–΄μ…ˆλΈ”λ¦¬ λΉ„μŠ·ν•œ μ–Έμ–΄λ‘œ, λ˜λŠ” λ‹€λ₯Έ λ„κ΅¬μ˜ λ„μ›€μœΌλ‘œ κ΅¬ν˜„ν•œλ‹€.

tc의 μ €μˆ˜μ€€ μΈν„°νŽ˜μ΄μŠ€λŠ” λͺ…λ Ή μ½”λ“œλ₯Ό 직접 λ°›λŠ”λ‹€. 예λ₯Ό λ“€μ–΄ λͺ¨λ“  νŒ¨ν‚·μ— κ±Έλ €μ„œ κΈ°λ³Έ classid 1:1을 λ°˜ν™˜ν•˜λŠ” κ°€μž₯ λ‹¨μˆœν•œ λΆ„λ₯˜μžλŠ” λ‹€μŒκ³Ό κ°™λ‹€.

tc filter add dev em1 parent 1: bpf bytecode '1,6 0 0 4294967295,' flowid 1:1

λ°”μ΄νŠΈμ½”λ“œ μ—΄μ˜ 첫 번째 μ‹­μ§„μˆ˜ μˆ˜λŠ” μ΄μ–΄μ§€λŠ” 4νŠœν”Œ cBPF λͺ…λ Ή μ½”λ“œλ“€μ˜ 개수λ₯Ό λ‚˜νƒ€λ‚Έλ‹€. μ–ΈκΈ‰ν•œ κ²ƒμ²˜λŸΌ 4νŠœν”Œμ€ c t f kλΌλŠ” μ‹­μ§„μˆ˜λ“€λ‘œ 이뀄지며, cλŠ” cBPF λͺ…λ Ή μ½”λ“œλ₯Ό, tλŠ” 참일 λ•Œ 점프 λŒ€μƒ μ˜€ν”„μ…‹μ„, fλŠ” 거짓일 λ•Œ 점프 λŒ€μƒ μ˜€ν”„μ…‹μ„, kλŠ” μ¦‰μ‹œμ  μƒμˆ˜/λ¦¬ν„°λŸ΄μ„ λ‚˜νƒ€λ‚Έλ‹€. μœ„ λͺ…λ Ή μ½”λ“œλŠ” μ¦‰μ‹œ κ°’ -1둜 ν”„λ‘œκ·Έλž¨μ—μ„œ 무쑰건 λ°˜ν™˜ν•˜λŠ” 것을 λ‚˜νƒ€λ‚Έλ‹€.

좜λ ₯ λΆ„λ₯˜μ— μžˆμ–΄μ„ , Willem de Bruijn이 iptables(8) BPF ν™•μž₯을 μœ„ν•΄ GNU 일반 곡쀑 μ‚¬μš© ν—ˆκ°€μ„œ 버전 2둜 κ΅¬ν˜„ν•œ κ°„λ‹¨ν•œ λ‹¨λ…ν˜• 헬퍼 ν”„λ‘œκ·Έλž¨μ΄ μžˆλŠ”λ° libpcap λ‚΄λΆ€μ˜ 전톡적 BPF 컴파일러λ₯Ό μ΄μš©ν•œλ‹€. μ—¬κΈ° μ½”λ“œλŠ” tc(8)μ™€μ˜ μ‚¬μš©μ„ μœ„ν•΄ 그의 μ½”λ“œμ—μ„œ νŒŒμƒλœ 것이닀.

#include <pcap.h>
#include <stdio.h>

int main(int argc, char **argv)
{
        struct bpf_program prog;
        struct bpf_insn *ins;
        int i, ret, dlt = DLT_RAW;

        if (argc < 2 || argc > 3)
                return 1;
        if (argc == 3) {
                dlt = pcap_datalink_name_to_val(argv[1]);
                if (dlt == -1)
                        return 1;
        }

        ret = pcap_compile_nopcap(-1, dlt, &prog, argv[argc - 1],
                                  1, PCAP_NETMASK_UNKNOWN);
        if (ret)
                return 1;

        printf("%d,", prog.bf_len);
        ins = prog.bf_insns;

        for (i = 0; i < prog.bh_len - 1; ++ins, ++i)
                printf("%u %u %u %u,", ins->code,
                       ins->jt, ins->jf, ins->k);
        printf("%u %u %u %u",
               ins->code, ins->jt, ins->jf, ins->k);

        pcap_freecode(&prog);
        return 0;
}

이 μž‘μ€ 헬퍼가 있으면 λΆ„λ₯˜μžμ— μ–΄λ–€ tcpdump(8) ν•„ν„° 식도 μ‚¬μš©ν•  수 μžˆλ‹€. μΌμΉ˜ν•˜λ©΄ κΈ°λ³Έ classidλ₯Ό λ°˜ν™˜ν•œλ‹€.

bpftool EN10MB 'tcp[tcpflags] & tcp-syn != 0' > /var/bpf/tcp-syn
tc filter add dev em1 parent 1: bpf bytecode-file /var/bpf/tcp-syn flowid 1:1

기본적으둜 이 μž‘μ€ μƒμ„±κΈ°λŠ” λ‹€μŒκ³Ό λ™λ“±ν•˜λ‹€.

tcpdump -iem1 -ddd 'tcp[tcpflags] & tcp-syn != 0' | tr '\n' ',' > /var/bpf/tcp-syn

libpcap의 μ»΄νŒŒμΌλŸ¬μ—μ„œλŠ” λ¦¬λˆ…μŠ€ ν•œμ • cBPF ν™•μž₯듀을 λͺ¨λ‘ μ§€μ›ν•˜μ§€ μ•ŠλŠ”λ‹€. κ·Έλž˜μ„œ λ¦¬λˆ…μŠ€ μ»€λ„μ—λŠ” tools/net/ μ•„λž˜μ— bpf_asmμ΄λΌλŠ” κ°„λ‹¨ν•œ BPF μ–΄μ…ˆλΈ”λŸ¬κ°€ μžˆμ–΄μ„œ μ™„μ „ν•œ μ œμ–΄κ°€ κ°€λŠ₯ν•˜κ²Œ ν•΄ μ€€λ‹€. 그런 ν”„λ‘œκ·Έλž¨μ„ 직접 μž‘μ„±ν•˜κΈ° μœ„ν•œ μžμ„Έν•œ 문법과 μ˜λ―Έλ‘ μ€ FURTHER READING의 μ°Έκ³  자료λ₯Ό 보라.

IPv4/TCP νŒ¨ν‚·μ„ λΆ„λ₯˜ν•˜κΈ° μœ„ν•œ bpf_asm ν˜•μ‹μ˜ κ°„λ‹¨ν•œ μ˜ˆκ°€ foobarλΌλŠ” ν…μŠ€νŠΈ νŒŒμΌμ— μ €μž₯λ˜μ–΄ μžˆλ‹€κ³  ν•˜μž.

ldh [12]
jne #0x800, drop
ldb [23]
jneq #6, drop
ret #-1
drop: ret #0

λ§ˆμ°¬κ°€μ§€λ‘œ λ‹€μŒκ³Ό 같이 κ·Έ λΆ„λ₯˜μžλ₯Ό μ μž¬ν•  수 μžˆλ‹€.

bpf_asm foobar > /var/bpf/tcp-syn
tc filter add dev em1 parent 1: bpf bytecode-file /var/bpf/tcp-sync flowid 1:1

BPF λΆ„λ₯˜μžλ₯Ό μœ„ν•΄ λ¦¬λˆ…μŠ€ μ»€λ„μ—μ„œλŠ” tools/net/ μ•„λž˜μ— bpf_dbgλΌλŠ” μž‘μ€ BPF 디버거λ₯Ό μΆ”κ°€λ‘œ μ œκ³΅ν•œλ‹€. 이λ₯Ό μ΄μš©ν•΄ pcap νŒŒμΌμ— λΆ„λ₯˜μžλ₯Ό 돌렀 보고, 전톡적 ν”„λ‘œκ·Έλž¨μ„ 단계 μ‹€ν–‰ ν•˜κ±°λ‚˜ λ‹€μ–‘ν•œ 쀑지점을 μΆ”κ°€ν•˜κ³ , λŸ°νƒ€μž„μ— λ ˆμ§€μŠ€ν„° λ‚΄μš©μ„ 찍어 λ³Ό 수 μžˆλ‹€.

전톡적 BPF둜 ν–‰μœ„λ₯Ό κ΅¬ν˜„ν•˜λŠ” κ²ƒμ—λŠ” μƒλ‹Ήν•œ μ œμ•½μ΄ μžˆλ‹€. νŒ¨ν‚· 변경을 μ§€μ›ν•˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ΄λ‹€. λ”°λΌμ„œ κ°€λŠ₯ν•˜λ‹€λ©΄ eBPF둜 μ „ν™˜ν•˜κΈ°λ₯Ό 일반적으둜 ꢌμž₯ν•œλ‹€.

FURTHER READING

λ¦¬λˆ…μŠ€ 컀널 μ†ŒμŠ€ 트리의 Documentation/networking/filter.txtμ—μ„œ BPF 체계에 λŒ€ν•œ 더 λ§Žμ€ 기술적 μ„ΈλΆ€ λ‚΄μš©μ„ λ³Ό 수 μžˆλ‹€.

iproute2 μ†ŒμŠ€ 트리의 examples/bpf/ λ‚΄μ—μ„œ 더 μžμ„Έν•œ eBPF tc(8) μ˜ˆλ“€μ„ λ³Ό 수 μžˆλ‹€.

SEE ALSO

tc(8), tc-ematch(8), bpf(2), bpf(4)

AUTHORS

Daniel Borkmann이 맨 νŽ˜μ΄μ§€ μž‘μ„±ν•¨.

μˆ˜μ • λ‚΄μ§€ κ°œμ„  사항은 λ¦¬λˆ…μŠ€ 컀널 λ„€νŠΈμ›Œν‚Ή 메일링 리슀트 [email protected]둜 μ•Œλ € μ£ΌκΈ° λ°”λž€λ‹€.


2015λ…„ 5μ›” 18일

⚠️ **GitHub.com Fallback** ⚠️