Två virtuella maskiner på samma värd når LAN via en bro, och en regel i ip forward-kedjan visar en räknare som aldrig rör sig. Ramarna är verkliga, trafiken passerar, och regeln ligger helt enkelt inte på dess väg. Att jämföra de två sätt som Linux exponerar trafik över en bro förklarar denna tystnad, och det förklarar också varför det att läsa in en enda gammal modul är ett sämre svar än det verkar.

Två vägar genom en värd

Ett paket kan lämna den här värden på två mycket olika sätt, och skillnaden avgör vilka krokar som ser det. Om målet ligger bortom en fysisk port och ramen kopplas mellan två broportar vidarebefordrar värden en Ethernet-ram; routningskoden tillfrågas inte, och inget routat hopp sker. Om målet är värden själv, eller ett nätverk som nås via ett tunnelgränssnitt, routas paketet och krokarna på IP-lagret gäller verkligen. Håll den gaffeln i minnet, för varje senare fråga om kedjor och familjer följer av vilken av de två vägarna en ram tar.

Den skillnaden är hela historien bakom en regel som inte räknar något. En ram över bro från veth9a1 till enp3s0 korsar bron, inte IP-stacken, så en ip forward-kedja ser inget att matcha. Gränssnittsnamn i ett äldre exempel avgör inte vilken väg du har, och därför är första steget att titta på topologin i stället för regeluppsättningen.

En tunnel lägger till ett tredje fall som förtjänar ett namn. En TUN-enhet levererar IP-paket till värden, så de routas och går faktiskt genom IP-kedjor, medan en TAP-enhet kopplad till en bro levererar ramar och beter sig som brofallet. Det är enhetstypen, inte produktnamnet, som avgör.

Jämför de två familjer som kan se en ram över en bro

Linux stöder inbyggd brofiltrering via nftables bridge-familj, och det behöver ingen gammal modul för att inspektera och släppa ramar under gång. Kernels egen brodokumentation ger den rekommendationen direkt, vilket är värt att veta innan en sökträff säger åt dig att läsa in en modul.

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

Att använda nft-kommandot i stället för iptables placerar inte i sig självt en regel i rätt familj. En kedja som deklareras i bridge-familjen ser ramar; en kedja som deklareras i ip, ip6 eller inet ser dem inte, oavsett vilket binär som skapade den. Familjen är en egenskap hos kedjan, inte hos verktyget som skrev den.

Den gamla vägen fungerar annorlunda, och det är därför det är värt att jämföra i stället för att välja sida av vana. Att läsa in br_netfilter och slå på bridge-nf-call-inställningarna exponerar återigen IPv4- och IPv6-trafik över bro genom krokarna på IP-lagret, så att en vanlig iptables-regel äntligen kan matcha den. Ramarna förändras inte; uppsättningen krokar de passerar förändras, vilket innebär att varje befintlig värdregel nu har en ny publik.

Läs den effektiva vägen innan du lägger till en regel

Tre kommandon fastställer var du står: vad bron är ansluten till, om den gamla modulen är aktiv och vilka kedjor som redan finns i bridge-familjen. Kör dem innan du skriver något, eftersom svaret avgör vilken av de två designerna du utökar. Att hoppa över detta steg är hur en korrekt regel hamnar i en kedja som ingenting passerar.

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

Utdata skiljer fallen rent. Portar som listas under bron berättar vilka gränssnitt som kopplas; ett tomt lsmod-resultat betyder ingen synlighet på IP-lagret av ramar över bro; en befintlig forward-kedja i bridge-familjen betyder att det redan finns en policyägare och att din regel hör hemma där i stället för i en ny egen kedja.

Lägg sedan till en regel som är lätt att läsa tillbaka, med en räknare och utan att ändra avgörandet, och skicka ett representativt flöde genom bron. En räknare som rör sig bevisar att ramen nådde regeln. Det är allt den bevisar, så följ upp med den nekade vägen också, eftersom en policy som accepterar allt också ger en räknare som rör sig.

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

Där den gamla modulen ändrar beteende

Att slå på den gamla vägen på en bro som körs är inte en neutral handling av att lägga till synlighet. Regler som tidigare var oåtkomliga för trafik över bro blir åtkomliga, inklusive värdregler skrivna för ett annat gränssnitt, och en standard drop-policy i ip forward-kedjan kan plötsligt gälla ramar som tidigare passerade orörda. På en värd som administreras över den bron blir resultatet en utelåsning utan konsol. Detta är inte ett hypotetiskt fel-läge; det är den vanliga konsekvensen av att utöka en uppsättning krokar på en maskin du når genom den väg du just utökade.

Den säkrare ordningen är att avgöra vilken familj som äger policyn, skriva reglerna där, och behålla en oberoende återställningsväg innan du ändrar någon sysctl. Om två familjer båda har regler, dokumentera vilken som är auktoritativ; två överlappande policyer är hur ett paket accepteras på ett ställe och släpps på ett annat utan en enda fil att läsa.

Isoleringen själv behöver fortfarande en fullständig design. Att tillåta en källa till ett mål bevisar bara att ett flöde fungerar, medan kravet vanligen är att en VM inte kan nå sina grannar eller värden. Räknare på nekandereglerna betyder mer än räknaren på accepteregeln, eftersom accepteräknaren är den du redan förväntade dig skulle röra sig.

Slutligen, håll värdinmatningsfallet åtskilt. Trafik adresserad till brons egen IP anländer på input, inte på forward, och en regel i forward kommer aldrig att se den, vilken familj du än väljer. Tre fall, två familjer, en lärdom: vägen avgör kroken, och kroken avgör om din regel överhuvudtaget finns.

Relaterade artiklar