BPFC(8) netsniff-ng toolkit BPFC(8)

bpfc - یک اسمبلر و کامپایلر فیلتر بسته برکلی (Berkeley Packet Filter)

bpfc { [گزینه‌ها] | [پرونده-منبع] }

برنامه bpfc یک اسمبلر و کامپایلر کوچک برای فیلتر بسته برکلی (Berkeley Packet Filter یا BPF) است که قادر است نمادهای یادافزا (mnemonics) شبیه به اسمبلی BPF را به قالبی عددی یا مشابه زبان C ترجمه کند، به گونه‌ای که توسط ابزارهایی مانند netsniff-ng، iptables (xt_bpf) و بسیاری دیگر قابل خواندن باشد. فیلتر BPF تنها ساختار پالایش بالادستی (upstream) است که در ترکیب با سوکت‌های packet(7) و همچنین seccomp-BPF برای سندباکس کردن فراخوانی‌های سیستمی استفاده می‌شود.

هسته لینوکس و هسته‌های BSD ساختارهایی شبیه به "ماشین مجازی" و کامپایلرهای JIT را پیاده‌سازی می‌کنند که یک ماشین مبتنی بر ثبات کوچک را در معماری BPF شبیه‌سازی کرده و کد فیلتری را که برای مثال توسط bpfc تولید شده، بر روی یک بافر داده از بسته‌های شبکه اجرا می‌کنند. هدف از این کار انتقال زمانی محاسبات است تا هسته بتواند بسته‌های ورودی را در زودترین زمان ممکن دور بیندازد یا کوتاه کند (truncate)، بدون آنکه نیاز باشد ابتدا آن‌ها را برای بررسی بیشتر به فضای کاربری ارسال کند. در این میان، ساختارهای BPF در زمینه‌های دیگری نظیر ارتباط میان فضای کاربری و فضای هسته مانند سندباکس کردن فراخوانی‌های سیستمی نیز کاربرد پیدا کرده‌اند.

در زمان نگارش این صفحه راهنما، تنها کامپایلر دیگر موجود برای BPF بخشی از کتابخانه pcap(3) است که از طریق یک زبان فیلتر سطح بالا که ممکن است برای بسیاری از افراد به عنوان فیلترهای شبیه به tcpdump آشنا باشد، در دسترس است.

با این وجود، در موارد متعددی بسیار سودمند است که آن کامپایلر دور زده شود و کدهای بهینه‌سازی‌شده‌ای نوشته شوند که توسط کامپایلر pcap(3) قابل تولید نیستند، یا به اشتباه بهینه‌سازی شده‌اند، یا عمداً به منظور اشکال‌زدایی و آزمودن کد هسته معیوب نوشته شده‌اند. همچنین دلیل دیگر برای استفاده از bpfc می‌تواند آزمودن برخی افزونه‌های جدید BPF باشد که توسط سایر کامپایلرها پشتیبانی نمی‌شوند. افزون بر این، bpfc می‌تواند برای بررسی صحت رفتار کامپایلر JIT یا یافتن اشکالات احتمالی که نیاز به رفع دارند، مفید باشد.

برنامه bpfc با کمک flex(1) و bison(1) پیاده‌سازی شده است؛ در مرحله نخست پرونده منبع را نشانه‌گذاری (tokenize) کرده و محتوای آن را به یک درخت نحو انتزاعی (AST) تجزیه می‌کند. سپس در دو مرحله تولید کد، کدهای عملیاتی (opcodes) هدف را صادر می‌نماید. علاوه بر این، bpfc از افزونه‌های BPF هسته لینوکس نیز پشتیبانی می‌کند. جزئیات بیشتر در این باره در بخش نحو آمده است.

کامپایلر BPF JIT هسته لینوکس در صورت شناسایی توسط netsniff-ng به طور خودکار فعال می‌شود. با این حال، می‌توان آن را به صورت دستی با دستور ''echo "1" > /proc/sys/net/core/bpf_jit_enable'' (حالت کاری عادی) یا ''echo "2" > /proc/sys/net/core/bpf_jit_enable'' (حالت اشکال‌زدایی که در آن کدهای عملیاتی صادر شده از ایمیج در گزارش وقایع هسته چاپ می‌شوند) نیز فعال کرد. یک دی‌اسمبلر مستقل از معماری برای ایمیج BPF JIT را می‌توان در درخت منبع هسته تحت مسیر ''tools/net/bpf_jit_disasm.c'' یا در مخزن گیت netsniff-ng یافت.

خواندن دستورالعمل‌های اسمبلی BPF از یک پرونده ورودی یا از ورودی استاندارد (stdin).
عبور دادن برنامه bpf از پیش‌پردازنده C پیش از خواندن آن در bpfc. این کار امکان استفاده از دستورات هدایت‌کننده #define و #include (به عنوان مثال جهت گنجاندن تعاریف از هدرهای سیستمی) را در برنامه bpf فراهم می‌کند.
افزودن تعریف ماکرو برای پیش‌پردازنده C جهت استفاده در پرونده bpf. این گزینه در ترکیب با گزینه -p/--cpp به کار می‌رود.
تعیین قالب خروجی متفاوت با حالت پیش‌فرض سازگار با netsniff-ng. مشخص‌کننده <format> می‌تواند یکی از موارد C، netsniff-ng، xt_bpf یا tcpdump باشد.
دور زدن اعتبارسنجی پایه‌ای فیلتر هنگام صدور کدهای عملیاتی. این ویژگی می‌تواند برای ایجاد عمدی عبارت‌های BPF نامعتبر جهت تزریق به هسته (مثلاً برای آزمودن اشکالات) مفید باشد.
ارائه گزارش مشروح‌تر و نمایش برخی اطلاعات اشکال‌زدایی bpfc.
تخلیه (dump) تمامی دستورالعمل‌های پشتیبانی‌شده در خروجی استاندارد (stdout).
نمایش اطلاعات نسخه و خروج.
نمایش راهنمای کاربر و خروج.

معماری BPF و به عبارت دیگر ماشین مبتنی بر ثبات آن از عناصر زیر تشکیل شده است:


عنصر توضیحات


A انباره ۳۲ بیتی (accumulator)
X ثبات ۳۲ بیتی X
M[] ۱۶ ثبات متفرقه ۳۲ بیتی موسوم به “حافظه موقت (scratch memory store)”، با قابلیت آدرس‌دهی از 0 تا 15

برنامه‌ای که توسط bpfc به ''کدهای عملیاتی (opcodes)'' ترجمه می‌شود، آرایه‌ای متشکل از عناصر زیر است:


o:16, jt:8, jf:8, k:32

عنصر o یک کد عملیاتی ۱۶ بیتی است که دستورالعمل خاصی در آن کدگذاری شده است؛ jt و jf دو مقصد پرش ۸ بیتی هستند، یکی برای شرط ''درست (true)'' و دیگری برای شرط ''نادرست (false)''. در نهایت، عنصر ۳۲ بیتی k شامل یک آرگومان متفرقه است که بسته به دستورالعمل یا کد عملیاتی مربوطه می‌تواند به روش‌های مختلفی تفسیر شود.

مجموعه دستورالعمل‌ها شامل دستورهای بارگذاری (load)، ذخیره‌سازی (store)، انشعاب (branch)، محاسباتی-منطقی (alu)، متفرقه و بازگشت (return) است که در نحو bpfc نیز پیاده‌سازی شده‌اند. این جدول همچنین شامل افزونه‌های اختصاصی خود bpfc است. تمامی عملیات بر پایه ساختارهای داده بدون علامت (unsigned) انجام می‌شوند:


دستورالعمل حالت آدرس‌دهی توضیحات


ld 1, 2, 3, 4, 10 بارگذاری کلمه (word) در A
ldi 4 بارگذاری کلمه در A
ldh 1, 2 بارگذاری نیم‌کلمه (half-word) در A
ldb 1, 2 بارگذاری بایت در A
ldx 3, 4, 5, 10 بارگذاری کلمه در X
ldxi 4 بارگذاری کلمه در X
ldxb 5 بارگذاری بایت در X


st 3 کپی A در []M
stx 3 کپی X در []M


jmp 6 پرش به برچسب
ja 6 پرش به برچسب
jeq 7, 8 پرش در صورت k == A
jneq 8 پرش در صورت k != A
jne 8 پرش در صورت k != A
jlt 8 پرش در صورت k < A
jle 8 پرش در صورت k <= A
jgt 7, 8 پرش در صورت k > A
jge 7, 8 پرش در صورت k >= A
jset 7, 8 پرش در صورت k & A


add 0, 4 A + <x>
sub 0, 4 A - <x>
mul 0, 4 A * <x>
div 0, 4 A / <x>
mod 0, 4 A % <x>
neg 0, 4 !A
and 0, 4 A & <x>
or 0, 4 A | <x>
xor 0, 4 A ^ <x>
lsh 0, 4 A << <x>
rsh 0, 4 A >> <x>


tax کپی A در X
txa کپی X در A


ret 4, 9 بازگشت


حالت آدرس‌دهی نحو توضیحات


0 x/%x ثبات X
1 [k] مقدار BHW در آفست بایتی k در بسته
2 [x + k] مقدار BHW در آفست X + k در بسته
3 M[k] کلمه در آفست k در []M
4 #k مقدار لفظی (literal) ذخیره‌شده در k
5 4*([k]&0xf) نیم‌بایت پایینی ضربدر ۴ در آفست بایتی k در بسته
6 L برچسب پرش L
7 #k,Lt,Lf پرش به Lt در صورت برقراری شرط، وگرنه پرش به Lf
8 #k,Lt پرش به Lt در صورت برقراری گزاره
9 a/%a انباره A
10 extension افزونه BPF (جدول بعدی را ببینید)


افزونه (و نام مستعار) توضیحات


#len, len, #pktlen, pktlen طول بسته (skb->len)
#pto, pto, #proto, proto فیلد نوع اترنت (skb->protocol)
#type, type نوع بسته (**) (skb->pkt_type)
#poff, poff آفست شناسایی‌شده شروع بار داده (payload)
#ifx, ifx, #ifidx, ifidx شاخص رابط (skb->dev->ifindex)
#nla, nla ویژگی Netlink از نوع X با آفست A
#nlan, nlan ویژگی تودرتوی Netlink از نوع X با آفست A
#mark, mark نشان بسته (skb->mark)
#que, que, #queue, queue, #Q, Q شاخص صف کارت شبکه (skb->queue_mapping)
#hat, hat, #hatype, hatype نوع سخت‌افزار کارت شبکه (**) (skb->dev->type)
#rxh, rxh, #rxhash, rxhash هش دریافت (skb->rxhash)
#cpu, cpu پردازنده فعلی (raw_smp_processor_id())
#vlant, vlant, #vlan_tci, vlan_tci مقدار VLAN TCI (vlan_tx_tag_get(skb))
#vlanp, vlanp وجود VLAN (vlan_tx_tag_present(skb))


جزئیات بیشتر افزونه (**) مقدار


#type, type 0 - به ما / میزبان (to us / host)
1 - به همه / همگانی (to all / broadcast)
2 - به گروه / چندپخشی (to group / multicast)
3 - به دیگران (حالت دریافت بی‌قید / promiscuous mode)
4 - خروجی از هر نوع


#hat, hat, #hatype, hatype 1 - اترنت ۱۰ مگابیت بر ثانیه
8 - پروتکل APPLEtalk
19 - شبکه ATM
24 - پروتکل IEEE 1394 IPv4 - RFC 2734
32 - اینفینی‌باند (InfiniBand)
768 - تونل IPIP
769 - تونل IP6IP6
772 - دستگاه Loopback
778 - پروتکل GRE بر روی IP
783 - پروتکل Linux-IrDA
801 - استاندارد IEEE 802.11
802 - استاندارد IEEE 802.11 + هدر Prism2
803 - استاندارد IEEE 802.11 + هدر radiotap
823 - پروتکل GRE بر روی IP6
824 - Netlink
[...] پرونده include/uapi/linux/if_arp.h را ببینید

توجه داشته باشید که اکثریت افزونه‌های BPF تنها در لینوکس در دسترس هستند.

دو نوع توضیح (کامنت) در پرونده‌های منبع bpfc وجود دارد:


1. توضیحات چندخطی به سبک C: /* متن توضیح در اینجا */
2. توضیحات تک‌خطی به سبک اسمبلی: ; متن توضیح در اینجا

اختصارات به‌کاررفته:


BHW: بایت (byte)، نیم‌کلمه (half-word) یا کلمه (word)

در این بخش، چند مثال از پرونده‌های منبع bpfc آورده شده است، به عبارت دیگر، چند برنامه فیلتر نمونه کوچک:

فقط بازگرداندن هدرهای بسته (کوتاه کردن بسته‌ها):


ld poff
ret a

فقط مجاز کردن بسته‌های ARP:


ldh [12]
jne #0x806, drop
ret #-1
drop: ret #0

فقط مجاز کردن بسته‌های IPv4 TCP:


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

فقط مجاز کردن ترافیک SSH تحت IPv4 TCP:


ldh [12]
jne #0x800, drop
ldb [23]
jneq #6, drop
ldh [20]
jset #0x1fff, drop
ldxb 4 * ([14] & 0xf)
ldh [x + 14]
jeq #0x16, pass
ldh [x + 16]
jne #0x16, drop
pass: ret #-1
drop: ret #0

یک فیلتر seccomp-BPF قابل بارگذاری برای x86_64 جهت مجاز کردن مجموعه‌ای مشخص از فراخوانی‌های سیستمی:


ld [4] /* offsetof(struct seccomp_data, arch) */
jne #0xc000003e, bad /* AUDIT_ARCH_X86_64 */
ld [0] /* offsetof(struct seccomp_data, nr) */
jeq #15, good /* __NR_rt_sigreturn */
jeq #231, good /* __NR_exit_group */
jeq #60, good /* __NR_exit */
jeq #0, good /* __NR_read */
jeq #1, good /* __NR_write */
jeq #5, good /* __NR_fstat */
jeq #9, good /* __NR_mmap */
jeq #14, good /* __NR_rt_sigprocmask */
jeq #13, good /* __NR_rt_sigaction */
jeq #35, good /* __NR_nanosleep */
bad: ret #0 /* SECCOMP_RET_KILL */
good: ret #0x7fff0000 /* SECCOMP_RET_ALLOW */

مجاز کردن هر نوع VLAN (با شتاب‌دهی سخت‌افزاری):


ld vlanp
jeq #0, drop
ret #-1
drop: ret #0

فقط مجاز کردن ترافیک برای VLAN 10 (با شتاب‌دهی سخت‌افزاری):


ld vlant
jneq #10, drop
ret #-1
drop: ret #0

بررسی دقیق‌تر برای مثال VLAN بالا:


ld vlanp
jeq #0, drop
ld vlant
jneq #10, drop
ret #-1
drop: ret #0

فیلتر کردن پیام‌های rtnetlink:


ldh #proto /* A = skb->protocol */


jneq #0, skip /* بررسی برای NETLINK_ROUTE */
ldb [4] /* A = nlmsg_type */


jneq #0x10, skip /* بررسی type == RTNL_NEWLINK */
ldx #16 /* X = offset(ifinfomsg) */


ldb [x + 4] /* offset(ifi_index) */
jneq #0x3, skip /* بررسی ifindex == 3 */


ld #32 /* A = len(nlmsghdr) + len(ifinfomsg), آفست بار داده */
ldx #16 /* X = IFLA_OPERSTATE */
ld #nla /* A = offset(IFLA_OPERSTATE) */
jeq #0, skip
tax
ldb [x + 4] /* A = value(IFLA_OPERSTATE) */
jneq #0x6, skip /* بررسی اینکه وضعیت عملیاتی UP باشد */


ret #-1
skip: ret #0

کامپایل پرونده منبع ''fubar'' به کدهای عملیاتی BPF. کدهای عملیاتی به خروجی استاندارد هدایت می‌شوند.
کامپایل پرونده منبع ''fubar'' به کدهای عملیاتی BPF، دور زدن اعتبارسنجی پایه‌ای فیلتر و صدور کدهای عملیاتی در قالب قابل خواندن برای xt_bpf در netfilter. توجه داشته باشید که پرونده منبع ''fubar'' ابتدا برای جایگزینی‌های متنی به پیش‌پردازنده C فرستاده می‌شود و سپس به کامپایلر bpfc تحویل داده می‌شود.
خواندن دستورالعمل‌های bpfc از ورودی استاندارد و صدور کدهای عملیاتی به خروجی استاندارد.
کامپایل دستورالعمل‌های فیلتر از پرونده foo و هدایت خروجی bpfc به پرونده bar، که سپس می‌تواند توسط netsniff-ng(8) از طریق گزینه -f خوانده شود.
خروجی دادن کدهای عملیاتی از پرونده منبع fubar با رفتاری مشابه ''tcpdump -ddd''.

برنامه bpfc تحت نسخه ۲.۰ از مجوز عمومی همگانی گنو (GNU GPL) منتشر شده است.

bpfc در ابتدا توسط Daniel Borkmann برای جعبه‌ابزار netsniff-ng نوشته شد. در حال حاضر توسط Tobias Klauser <tklauser@distanz.ch> و Daniel Borkmann <dborkma@tik.ee.ethz.ch> نگهداری می‌شود.

netsniff-ng(8), trafgen(8), mausezahn(8), ifpps(8), flowtop(8), astraceroute(8), curvetun(8)

این صفحه راهنما توسط Daniel Borkmann نوشته شده است.

این صفحه بخشی از پروژه جعبه‌ابزار netsniff-ng در لینوکس است. توضیحات مربوط به پروژه و اطلاعات پیرامون گزارش باگ‌ها را می‌توان در http://netsniff-ng.org/ یافت.

03 March 2013 Linux