เครื่องเสมือนสองเครื่องบนโฮสต์เดียวกันเข้าถึง LAN ผ่านบริดจ์ แต่กฎที่วางไว้ในเชน ip forward กลับแสดงตัวนับที่ไม่เคยขยับ เฟรมนั้นมีจริง ทราฟฟิกกำลังผ่านไป และ กฎนั้นก็ไม่ได้อยู่บนเส้นทางของมัน การเปรียบเทียบสองวิธีที่ Linux เปิดเผยทราฟฟิกผ่านบริดจ์อธิบายความเงียบนี้ และยังอธิบายด้วยว่าทำไมการโหลดมอดูลเก่าหนึ่งตัวจึงเป็นคำตอบที่แย่กว่าที่เห็น

สองเส้นทางผ่านโฮสต์เดียว

แพ็กเก็ตสามารถออกจากโฮสต์นี้ได้สองวิธีที่ต่างกันมาก และความต่างนั้นเป็นตัวกำหนดว่าฮุกใดจะเห็นมัน หากปลายทางอยู่อีกฟากของพอร์ตกายภาพและเฟรมถูกสลับระหว่างพอร์ตบริดจ์สองพอร์ต โฮสต์จะส่งต่อเฟรม Ethernet โดยไม่ถามโค้ดเราเตอร์ และไม่มีการกระโดดแบบเราเตอร์เกิดขึ้น หากปลายทางคือโฮสต์เองหรือเครือข่ายที่เข้าถึงผ่านอินเทอร์เฟซอุโมงค์ แพ็กเก็ตจะถูกเราเตอร์และฮุกของชั้น IP มีผลจริง จดจำทางแยกนี้ไว้ เพราะทุกคำถามหลังจากนี้เกี่ยวกับเชนและตระกูลล้วนมาจากเส้นทางที่เฟรมเลือก

ความต่างนั้นคือเรื่องราวทั้งหมดเบื้องหลังกฎที่ไม่นับอะไรเลย เฟรมที่ผ่านบริดจ์จาก veth9a1 ไปยัง enp3s0 ข้ามบริดจ์ ไม่ใช่สแตก IP ดังนั้นเชน ip forward จึงไม่เห็นสิ่งใดให้จับคู่ ชื่ออินเทอร์เฟซในตัวอย่างเก่าไม่ได้บอกว่าคุณมีเส้นทางใด และนั่นคือเหตุผลที่ก้าวแรกคือการดูโครงสร้างเครือข่าย ไม่ใช่ชุดกฎ

อุโมงค์เพิ่มกรณีที่สามที่ควรมีชื่อ อุปกรณ์ TUN ส่งแพ็กเก็ต IP ให้โฮสต์ ดังนั้นแพ็กเก็ตเหล่านั้นจึงถูกเราเตอร์และผ่านเชน IP จริง ขณะที่อุปกรณ์ TAP ที่ต่อกับบริดจ์ส่งเฟรมและทำงานเหมือนกรณีบริดจ์ ประเภทของอุปกรณ์เป็นตัวกำหนด ไม่ใช่ชื่อผลิตภัณฑ์

เปรียบเทียบสองตระกูลที่มองเห็นเฟรมผ่านบริดจ์ได้

Linux รองรับการกรองบริดจ์แบบดั้งเดิมผ่านตระกูล bridge ของ nftables และไม่ต้องใช้มอดูลเก่าใด ๆ ในการตรวจและทิ้งเฟรมขณะเคลื่อนที่ เอกสารบริดจ์ของเคอร์เนลเองให้คำแนะนำนั้นโดยตรง ซึ่งควรรู้ไว้ก่อนที่ผลการค้นหาจะบอกให้คุณโหลดมอดูล

Observation pointWhat it seesWhat it costs
nftables bridge family chainsEthernet frames forwarded between bridge ports, in both directionsRules must be written in bridge syntax with link-layer matches
Legacy br_netfilter plus an ip chainBridged IPv4 and IPv6 packets, re-exposed through IP-layer hooksExisting host rules may begin matching traffic they never saw before
inet chain for routed trafficPackets the host routes, including traffic arriving from a TUN deviceNo visibility into purely switched frames

การใช้คำสั่ง nft แทน iptables ไม่ได้วางกฎไว้ในตระกูลที่ถูกต้องด้วยตัวมันเอง เชนที่ประกาศในตระกูล bridge เห็นเฟรม ส่วนเชนที่ประกาศใน ip, ip6 หรือ inet ไม่เห็น ไม่ว่าไบนารีใดจะสร้างมันขึ้นมา ตระกูลเป็นคุณสมบัติของเชน ไม่ใช่ของเครื่องมือที่พิมพ์มัน

เส้นทางเก่าทำงานต่างออกไป และนั่นคือเหตุผลที่ควรเปรียบเทียบแทนที่จะเลือกข้างตามความเคยชิน การโหลด br_netfilter และเปิดการตั้งค่า bridge-nf-call จะเปิดเผยทราฟฟิก IPv4 และ IPv6 ที่ผ่านบริดจ์อีกครั้งผ่านฮุกของชั้น IP เพื่อให้กฎ iptables ธรรมดาจับคู่กับมันได้ในที่สุด เฟรมไม่เปลี่ยน สิ่งที่เปลี่ยนคือชุดฮุกที่มันผ่าน ซึ่งหมายความว่าทุกกฎของโฮสต์ที่มีอยู่มีผู้ชมใหม่แล้ว

อ่านเส้นทางที่แท้จริงก่อนเพิ่มกฎ

สามคำสั่งจะบอกว่าคุณอยู่ตรงไหน บริดจ์ต่อกับอะไร มอดูลเก่าทำงานอยู่หรือไม่ และมีเชนใดอยู่แล้วในตระกูล bridge รันคำสั่งเหล่านี้ก่อนเขียนอะไร เพราะคำตอบเป็นตัวกำหนดว่าคุณกำลังขยายแบบใดจากสองแบบ การข้ามขั้นตอนนี้คือวิธีที่กฎที่ถูกต้องไปลงเอยในเชนที่ไม่มีอะไรผ่าน

bridge link show
lsmod | grep br_netfilter
nft list ruleset | grep -A4 "chain forward"

ผลลัพธ์แยกกรณีออกจากกันอย่างชัดเจน พอร์ตที่ระบุใต้บริดจ์บอกว่าอินเทอร์เฟซใดถูกสลับ ผลลัพธ์ lsmod ที่ว่างเปล่าหมายถึงไม่มีความมองเห็นที่ชั้น IP ของเฟรมผ่านบริดจ์เลย ส่วนเชน forward ของตระกูล bridge ที่มีอยู่แล้วหมายถึงมีเจ้าของนโยบายอยู่แล้ว และกฎของคุณเป็นของที่นั่น ไม่ใช่เชนใหม่ของตัวเอง

จากนั้นเพิ่มกฎหนึ่งข้อที่อ่านย้อนกลับได้ง่าย มีตัวนับและไม่เปลี่ยนคำตัดสิน แล้วส่งกระแสข้อมูลตัวแทนหนึ่งกระแสผ่านบริดจ์ ตัวนับที่ขยับได้พิสูจน์ว่าเฟรมไปถึงกฎ นั่นคือทั้งหมดที่มันพิสูจน์ ดังนั้นตามด้วยเส้นทางที่ถูกปฏิเสธด้วย เพราะนโยบายที่ยอมรับทุกอย่างก็ทำให้ตัวนับขยับเช่นกัน

nft add rule bridge filter forward iifname "veth9a1" oifname "enp3s0" counter accept

จุดที่มอดูลเก่าเปลี่ยนพฤติกรรม

การเปิดเส้นทางเก่าบนบริดจ์ที่กำลังทำงานไม่ใช่การกระทำที่เป็นกลางในการเพิ่มความมองเห็น กฎที่ก่อนหน้านี้เอื้อมไม่ถึงสำหรับทราฟฟิกผ่านบริดจ์จะเอื้อมถึงได้ รวมถึงกฎของโฮสต์ที่เขียนไว้สำหรับอินเทอร์เฟซอื่น และนโยบาย drop โดยปริยายในเชน ip forward อาจใช้กับเฟรมที่เคยผ่านไปโดยไม่ถูกแตะต้องได้ทันที บนโฮสต์ที่บริหารผ่านบริดจ์นั้น ผลลัพธ์คือการถูกล็อกออกโดยไม่มีคอนโซล นี่ไม่ใช่โหมดความล้มเหลวสมมติ มันคือผลลัพธ์ปกติของการขยายชุดฮุกบนเครื่องที่คุณเข้าถึงผ่านเส้นทางที่คุณเพิ่งขยาย

ลำดับที่ปลอดภัยกว่าคือตัดสินว่าตระกูลใดเป็นเจ้าของนโยบาย เขียนกฎไว้ที่นั่น และเก็บเส้นทางกู้คืนที่เป็นอิสระไว้ก่อนเปลี่ยน sysctl ใด ๆ หากสองตระกูลต่างถือกฎอยู่ ให้บันทึกว่าตระกูลใดมีอำนาจ นโยบายสองชุดที่ทับซ้อนกันคือวิธีที่แพ็กเก็ตถูกยอมรับที่หนึ่งและถูกทิ้งอีกที่หนึ่งโดยไม่มีไฟล์เดียวให้อ่าน

การแยกกันเองยังต้องมีการออกแบบที่สมบูรณ์ การอนุญาตให้ต้นทางหนึ่งไปยังปลายทางหนึ่งพิสูจน์เพียงว่ากระแสหนึ่งทำงาน ขณะที่ข้อกำหนดมักคือ VM ต้องเข้าถึงเพื่อนบ้านหรือโฮสต์ไม่ได้ ตัวนับบนกฎปฏิเสธสำคัญกว่าตัวนับบนกฎยอมรับ เพราะตัวนับการยอมรับคือสิ่งที่คุณคาดไว้แล้วว่าจะขยับ

สุดท้าย แยกกรณีอินพุตของโฮสต์ออกมา ทราฟฟิกที่ส่งถึง IP ของบริดจ์เองมาถึง input ไม่ใช่ forward และกฎใน forward จะไม่มีวันเห็นมัน ไม่ว่าคุณจะเลือกตระกูลใด สามกรณี สองตระกูล บทเรียนเดียว เส้นทางเป็นตัวกำหนดฮุก และฮุกเป็นตัวกำหนดว่ากฎของคุณมีอยู่จริงหรือไม่

บทความที่เกี่ยวข้อง