@@ -68,6 +68,9 @@ func TestFirewall_AddRule(t *testing.T) {
6868 ti , err := netip .ParsePrefix ("1.2.3.4/32" )
6969 require .NoError (t , err )
7070
71+ ti6 , err := netip .ParsePrefix ("fd12::34/128" )
72+ require .NoError (t , err )
73+
7174 require .NoError (t , fw .AddRule (true , firewall .ProtoTCP , 1 , 1 , []string {}, "" , netip.Prefix {}, netip.Prefix {}, "" , "" ))
7275 // An empty rule is any
7376 assert .True (t , fw .InRules .TCP [1 ].Any .Any .Any )
@@ -92,12 +95,24 @@ func TestFirewall_AddRule(t *testing.T) {
9295 _ , ok := fw .OutRules .AnyProto [1 ].Any .CIDR .Get (ti )
9396 assert .True (t , ok )
9497
98+ fw = NewFirewall (l , time .Second , time .Minute , time .Hour , c )
99+ require .NoError (t , fw .AddRule (false , firewall .ProtoAny , 1 , 1 , []string {}, "" , ti6 , netip.Prefix {}, "" , "" ))
100+ assert .Nil (t , fw .OutRules .AnyProto [1 ].Any .Any )
101+ _ , ok = fw .OutRules .AnyProto [1 ].Any .CIDR .Get (ti6 )
102+ assert .True (t , ok )
103+
95104 fw = NewFirewall (l , time .Second , time .Minute , time .Hour , c )
96105 require .NoError (t , fw .AddRule (false , firewall .ProtoAny , 1 , 1 , []string {}, "" , netip.Prefix {}, ti , "" , "" ))
97106 assert .NotNil (t , fw .OutRules .AnyProto [1 ].Any .Any )
98107 _ , ok = fw .OutRules .AnyProto [1 ].Any .Any .LocalCIDR .Get (ti )
99108 assert .True (t , ok )
100109
110+ fw = NewFirewall (l , time .Second , time .Minute , time .Hour , c )
111+ require .NoError (t , fw .AddRule (false , firewall .ProtoAny , 1 , 1 , []string {}, "" , netip.Prefix {}, ti6 , "" , "" ))
112+ assert .NotNil (t , fw .OutRules .AnyProto [1 ].Any .Any )
113+ _ , ok = fw .OutRules .AnyProto [1 ].Any .Any .LocalCIDR .Get (ti6 )
114+ assert .True (t , ok )
115+
101116 fw = NewFirewall (l , time .Second , time .Minute , time .Hour , c )
102117 require .NoError (t , fw .AddRule (true , firewall .ProtoUDP , 1 , 1 , []string {"g1" }, "" , netip.Prefix {}, netip.Prefix {}, "ca-name" , "" ))
103118 assert .Contains (t , fw .InRules .UDP [1 ].CANames , "ca-name" )
@@ -117,6 +132,13 @@ func TestFirewall_AddRule(t *testing.T) {
117132 require .NoError (t , fw .AddRule (false , firewall .ProtoAny , 0 , 0 , []string {}, "" , anyIp , netip.Prefix {}, "" , "" ))
118133 assert .True (t , fw .OutRules .AnyProto [0 ].Any .Any .Any )
119134
135+ fw = NewFirewall (l , time .Second , time .Minute , time .Hour , c )
136+ anyIp6 , err := netip .ParsePrefix ("::/0" )
137+ require .NoError (t , err )
138+
139+ require .NoError (t , fw .AddRule (false , firewall .ProtoAny , 0 , 0 , []string {}, "" , anyIp6 , netip.Prefix {}, "" , "" ))
140+ assert .True (t , fw .OutRules .AnyProto [0 ].Any .Any .Any )
141+
120142 // Test error conditions
121143 fw = NewFirewall (l , time .Second , time .Minute , time .Hour , c )
122144 require .Error (t , fw .AddRule (true , math .MaxUint8 , 0 , 0 , []string {}, "" , netip.Prefix {}, netip.Prefix {}, "" , "" ))
@@ -199,6 +221,82 @@ func TestFirewall_Drop(t *testing.T) {
199221 require .NoError (t , fw .Drop (p , true , & h , cp , nil ))
200222}
201223
224+ func TestFirewall_DropV6 (t * testing.T ) {
225+ l := test .NewLogger ()
226+ ob := & bytes.Buffer {}
227+ l .SetOutput (ob )
228+
229+ p := firewall.Packet {
230+ LocalAddr : netip .MustParseAddr ("fd12::34" ),
231+ RemoteAddr : netip .MustParseAddr ("fd12::34" ),
232+ LocalPort : 10 ,
233+ RemotePort : 90 ,
234+ Protocol : firewall .ProtoUDP ,
235+ Fragment : false ,
236+ }
237+
238+ c := dummyCert {
239+ name : "host1" ,
240+ networks : []netip.Prefix {netip .MustParsePrefix ("fd12::34/120" )},
241+ groups : []string {"default-group" },
242+ issuer : "signer-shasum" ,
243+ }
244+ h := HostInfo {
245+ ConnectionState : & ConnectionState {
246+ peerCert : & cert.CachedCertificate {
247+ Certificate : & c ,
248+ InvertedGroups : map [string ]struct {}{"default-group" : {}},
249+ },
250+ },
251+ vpnAddrs : []netip.Addr {netip .MustParseAddr ("fd12::34" )},
252+ }
253+ h .buildNetworks (c .networks , c .unsafeNetworks )
254+
255+ fw := NewFirewall (l , time .Second , time .Minute , time .Hour , & c )
256+ require .NoError (t , fw .AddRule (true , firewall .ProtoAny , 0 , 0 , []string {"any" }, "" , netip.Prefix {}, netip.Prefix {}, "" , "" ))
257+ cp := cert .NewCAPool ()
258+
259+ // Drop outbound
260+ assert .Equal (t , ErrNoMatchingRule , fw .Drop (p , false , & h , cp , nil ))
261+ // Allow inbound
262+ resetConntrack (fw )
263+ require .NoError (t , fw .Drop (p , true , & h , cp , nil ))
264+ // Allow outbound because conntrack
265+ require .NoError (t , fw .Drop (p , false , & h , cp , nil ))
266+
267+ // test remote mismatch
268+ oldRemote := p .RemoteAddr
269+ p .RemoteAddr = netip .MustParseAddr ("fd12::56" )
270+ assert .Equal (t , fw .Drop (p , false , & h , cp , nil ), ErrInvalidRemoteIP )
271+ p .RemoteAddr = oldRemote
272+
273+ // ensure signer doesn't get in the way of group checks
274+ fw = NewFirewall (l , time .Second , time .Minute , time .Hour , & c )
275+ require .NoError (t , fw .AddRule (true , firewall .ProtoAny , 0 , 0 , []string {"nope" }, "" , netip.Prefix {}, netip.Prefix {}, "" , "signer-shasum" ))
276+ require .NoError (t , fw .AddRule (true , firewall .ProtoAny , 0 , 0 , []string {"default-group" }, "" , netip.Prefix {}, netip.Prefix {}, "" , "signer-shasum-bad" ))
277+ assert .Equal (t , fw .Drop (p , true , & h , cp , nil ), ErrNoMatchingRule )
278+
279+ // test caSha doesn't drop on match
280+ fw = NewFirewall (l , time .Second , time .Minute , time .Hour , & c )
281+ require .NoError (t , fw .AddRule (true , firewall .ProtoAny , 0 , 0 , []string {"nope" }, "" , netip.Prefix {}, netip.Prefix {}, "" , "signer-shasum-bad" ))
282+ require .NoError (t , fw .AddRule (true , firewall .ProtoAny , 0 , 0 , []string {"default-group" }, "" , netip.Prefix {}, netip.Prefix {}, "" , "signer-shasum" ))
283+ require .NoError (t , fw .Drop (p , true , & h , cp , nil ))
284+
285+ // ensure ca name doesn't get in the way of group checks
286+ cp .CAs ["signer-shasum" ] = & cert.CachedCertificate {Certificate : & dummyCert {name : "ca-good" }}
287+ fw = NewFirewall (l , time .Second , time .Minute , time .Hour , & c )
288+ require .NoError (t , fw .AddRule (true , firewall .ProtoAny , 0 , 0 , []string {"nope" }, "" , netip.Prefix {}, netip.Prefix {}, "ca-good" , "" ))
289+ require .NoError (t , fw .AddRule (true , firewall .ProtoAny , 0 , 0 , []string {"default-group" }, "" , netip.Prefix {}, netip.Prefix {}, "ca-good-bad" , "" ))
290+ assert .Equal (t , fw .Drop (p , true , & h , cp , nil ), ErrNoMatchingRule )
291+
292+ // test caName doesn't drop on match
293+ cp .CAs ["signer-shasum" ] = & cert.CachedCertificate {Certificate : & dummyCert {name : "ca-good" }}
294+ fw = NewFirewall (l , time .Second , time .Minute , time .Hour , & c )
295+ require .NoError (t , fw .AddRule (true , firewall .ProtoAny , 0 , 0 , []string {"nope" }, "" , netip.Prefix {}, netip.Prefix {}, "ca-good-bad" , "" ))
296+ require .NoError (t , fw .AddRule (true , firewall .ProtoAny , 0 , 0 , []string {"default-group" }, "" , netip.Prefix {}, netip.Prefix {}, "ca-good" , "" ))
297+ require .NoError (t , fw .Drop (p , true , & h , cp , nil ))
298+ }
299+
202300func BenchmarkFirewallTable_match (b * testing.B ) {
203301 f := & Firewall {}
204302 ft := FirewallTable {
@@ -208,6 +306,10 @@ func BenchmarkFirewallTable_match(b *testing.B) {
208306 pfix := netip .MustParsePrefix ("172.1.1.1/32" )
209307 _ = ft .TCP .addRule (f , 10 , 10 , []string {"good-group" }, "good-host" , pfix , netip.Prefix {}, "" , "" )
210308 _ = ft .TCP .addRule (f , 100 , 100 , []string {"good-group" }, "good-host" , netip.Prefix {}, pfix , "" , "" )
309+
310+ pfix6 := netip .MustParsePrefix ("fd11::11/128" )
311+ _ = ft .TCP .addRule (f , 10 , 10 , []string {"good-group" }, "good-host" , pfix6 , netip.Prefix {}, "" , "" )
312+ _ = ft .TCP .addRule (f , 100 , 100 , []string {"good-group" }, "good-host" , netip.Prefix {}, pfix6 , "" , "" )
211313 cp := cert .NewCAPool ()
212314
213315 b .Run ("fail on proto" , func (b * testing.B ) {
@@ -239,6 +341,15 @@ func BenchmarkFirewallTable_match(b *testing.B) {
239341 assert .False (b , ft .match (firewall.Packet {Protocol : firewall .ProtoTCP , LocalPort : 100 , LocalAddr : ip .Addr ()}, true , c , cp ))
240342 }
241343 })
344+ b .Run ("pass proto, port, fail on local CIDRv6" , func (b * testing.B ) {
345+ c := & cert.CachedCertificate {
346+ Certificate : & dummyCert {},
347+ }
348+ ip := netip .MustParsePrefix ("fd99::99/128" )
349+ for n := 0 ; n < b .N ; n ++ {
350+ assert .False (b , ft .match (firewall.Packet {Protocol : firewall .ProtoTCP , LocalPort : 100 , LocalAddr : ip .Addr ()}, true , c , cp ))
351+ }
352+ })
242353
243354 b .Run ("pass proto, port, any local CIDR, fail all group, name, and cidr" , func (b * testing.B ) {
244355 c := & cert.CachedCertificate {
@@ -252,6 +363,18 @@ func BenchmarkFirewallTable_match(b *testing.B) {
252363 assert .False (b , ft .match (firewall.Packet {Protocol : firewall .ProtoTCP , LocalPort : 10 }, true , c , cp ))
253364 }
254365 })
366+ b .Run ("pass proto, port, any local CIDRv6, fail all group, name, and cidr" , func (b * testing.B ) {
367+ c := & cert.CachedCertificate {
368+ Certificate : & dummyCert {
369+ name : "nope" ,
370+ networks : []netip.Prefix {netip .MustParsePrefix ("fd99::99/128" )},
371+ },
372+ InvertedGroups : map [string ]struct {}{"nope" : {}},
373+ }
374+ for n := 0 ; n < b .N ; n ++ {
375+ assert .False (b , ft .match (firewall.Packet {Protocol : firewall .ProtoTCP , LocalPort : 10 }, true , c , cp ))
376+ }
377+ })
255378
256379 b .Run ("pass proto, port, specific local CIDR, fail all group, name, and cidr" , func (b * testing.B ) {
257380 c := & cert.CachedCertificate {
@@ -265,6 +388,18 @@ func BenchmarkFirewallTable_match(b *testing.B) {
265388 assert .False (b , ft .match (firewall.Packet {Protocol : firewall .ProtoTCP , LocalPort : 100 , LocalAddr : pfix .Addr ()}, true , c , cp ))
266389 }
267390 })
391+ b .Run ("pass proto, port, specific local CIDRv6, fail all group, name, and cidr" , func (b * testing.B ) {
392+ c := & cert.CachedCertificate {
393+ Certificate : & dummyCert {
394+ name : "nope" ,
395+ networks : []netip.Prefix {netip .MustParsePrefix ("fd99:99/128" )},
396+ },
397+ InvertedGroups : map [string ]struct {}{"nope" : {}},
398+ }
399+ for n := 0 ; n < b .N ; n ++ {
400+ assert .False (b , ft .match (firewall.Packet {Protocol : firewall .ProtoTCP , LocalPort : 100 , LocalAddr : pfix6 .Addr ()}, true , c , cp ))
401+ }
402+ })
268403
269404 b .Run ("pass on group on any local cidr" , func (b * testing.B ) {
270405 c := & cert.CachedCertificate {
@@ -289,6 +424,17 @@ func BenchmarkFirewallTable_match(b *testing.B) {
289424 assert .True (b , ft .match (firewall.Packet {Protocol : firewall .ProtoTCP , LocalPort : 100 , LocalAddr : pfix .Addr ()}, true , c , cp ))
290425 }
291426 })
427+ b .Run ("pass on group on specific local cidr6" , func (b * testing.B ) {
428+ c := & cert.CachedCertificate {
429+ Certificate : & dummyCert {
430+ name : "nope" ,
431+ },
432+ InvertedGroups : map [string ]struct {}{"good-group" : {}},
433+ }
434+ for n := 0 ; n < b .N ; n ++ {
435+ assert .True (b , ft .match (firewall.Packet {Protocol : firewall .ProtoTCP , LocalPort : 100 , LocalAddr : pfix6 .Addr ()}, true , c , cp ))
436+ }
437+ })
292438
293439 b .Run ("pass on name" , func (b * testing.B ) {
294440 c := & cert.CachedCertificate {
@@ -447,6 +593,42 @@ func TestFirewall_Drop3(t *testing.T) {
447593 require .NoError (t , fw .Drop (p , true , & h1 , cp , nil ))
448594}
449595
596+ func TestFirewall_Drop3V6 (t * testing.T ) {
597+ l := test .NewLogger ()
598+ ob := & bytes.Buffer {}
599+ l .SetOutput (ob )
600+
601+ p := firewall.Packet {
602+ LocalAddr : netip .MustParseAddr ("fd12::34" ),
603+ RemoteAddr : netip .MustParseAddr ("fd12::34" ),
604+ LocalPort : 1 ,
605+ RemotePort : 1 ,
606+ Protocol : firewall .ProtoUDP ,
607+ Fragment : false ,
608+ }
609+
610+ network := netip .MustParsePrefix ("fd12::34/120" )
611+ c := cert.CachedCertificate {
612+ Certificate : & dummyCert {
613+ name : "host-owner" ,
614+ networks : []netip.Prefix {network },
615+ },
616+ }
617+ h := HostInfo {
618+ ConnectionState : & ConnectionState {
619+ peerCert : & c ,
620+ },
621+ vpnAddrs : []netip.Addr {network .Addr ()},
622+ }
623+ h .buildNetworks (c .Certificate .Networks (), c .Certificate .UnsafeNetworks ())
624+
625+ // Test a remote address match
626+ fw := NewFirewall (l , time .Second , time .Minute , time .Hour , c .Certificate )
627+ cp := cert .NewCAPool ()
628+ require .NoError (t , fw .AddRule (true , firewall .ProtoAny , 1 , 1 , []string {}, "" , netip .MustParsePrefix ("fd12::34/120" ), netip.Prefix {}, "" , "" ))
629+ require .NoError (t , fw .Drop (p , true , & h , cp , nil ))
630+ }
631+
450632func TestFirewall_DropConntrackReload (t * testing.T ) {
451633 l := test .NewLogger ()
452634 ob := & bytes.Buffer {}
@@ -727,6 +909,21 @@ func TestAddFirewallRulesFromConfig(t *testing.T) {
727909 require .NoError (t , AddFirewallRulesFromConfig (l , true , conf , mf ))
728910 assert .Equal (t , addRuleCall {incoming : true , proto : firewall .ProtoAny , startPort : 1 , endPort : 1 , groups : nil , ip : netip.Prefix {}, localIp : cidr }, mf .lastCall )
729911
912+ // Test adding rule with cidr ipv6
913+ cidr6 := netip .MustParsePrefix ("fd00::/8" )
914+ conf = config .NewC (l )
915+ mf = & mockFirewall {}
916+ conf .Settings ["firewall" ] = map [string ]any {"inbound" : []any {map [string ]any {"port" : "1" , "proto" : "any" , "cidr" : cidr6 .String ()}}}
917+ require .NoError (t , AddFirewallRulesFromConfig (l , true , conf , mf ))
918+ assert .Equal (t , addRuleCall {incoming : true , proto : firewall .ProtoAny , startPort : 1 , endPort : 1 , groups : nil , ip : cidr6 , localIp : netip.Prefix {}}, mf .lastCall )
919+
920+ // Test adding rule with local_cidr ipv6
921+ conf = config .NewC (l )
922+ mf = & mockFirewall {}
923+ conf .Settings ["firewall" ] = map [string ]any {"inbound" : []any {map [string ]any {"port" : "1" , "proto" : "any" , "local_cidr" : cidr6 .String ()}}}
924+ require .NoError (t , AddFirewallRulesFromConfig (l , true , conf , mf ))
925+ assert .Equal (t , addRuleCall {incoming : true , proto : firewall .ProtoAny , startPort : 1 , endPort : 1 , groups : nil , ip : netip.Prefix {}, localIp : cidr6 }, mf .lastCall )
926+
730927 // Test adding rule with ca_sha
731928 conf = config .NewC (l )
732929 mf = & mockFirewall {}
0 commit comments