44package netlink
55
66import (
7+ "bytes"
78 "encoding/binary"
89 "fmt"
910 "net"
@@ -72,10 +73,16 @@ func ensureCtHooksInThisNS(t *testing.T) func() {
7273 var addedInput , addedOutput bool
7374 if ipt (false , "-C" , "INPUT" , "-m" , "conntrack" , "--ctstate" , "NEW,ESTABLISHED" , "-j" , "ACCEPT" ) != nil {
7475 ipt (true , "-I" , "INPUT" , "-m" , "conntrack" , "--ctstate" , "NEW,ESTABLISHED" , "-j" , "ACCEPT" )
76+ // Add a rule to set conntrack label to allocate the label space
77+ // https://lore.kernel.org/netfilter-devel/[email protected] / 78+ ipt (true , "-I" , "INPUT" , "-m" , "connlabel" , "--set" , "--label" , "1" )
7579 addedInput = true
7680 }
7781 if ipt (false , "-C" , "OUTPUT" , "-m" , "conntrack" , "--ctstate" , "ESTABLISHED" , "-j" , "ACCEPT" ) != nil {
7882 ipt (true , "-I" , "OUTPUT" , "-m" , "conntrack" , "--ctstate" , "ESTABLISHED" , "-j" , "ACCEPT" )
83+ // Add a rule to set conntrack label to allocate the label space
84+ // https://lore.kernel.org/netfilter-devel/[email protected] / 85+ ipt (true , "-I" , "OUTPUT" , "-m" , "connlabel" , "--set" , "--label" , "1" )
7986 addedOutput = true
8087 }
8188 return func () {
@@ -98,6 +105,12 @@ func ensureCtHooksInThisNS(t *testing.T) func() {
98105 _ = exec .Command ("nft" , "add" , "chain" , "inet" , "ct_test" , "output" ,
99106 "{" , "type" , "filter" , "hook" , "output" , "priority" , "0" , ";" ,
100107 "ct" , "state" , "established" , "accept" , "}" ).Run ()
108+ // Add a rule to set conntrack label to allocate the label space
109+ // https://lore.kernel.org/netfilter-devel/[email protected] / 110+ _ = exec .Command ("nft" , "add" , "rule" , "inet" , "ct_test" , "output" ,
111+ "ct" , "label" , "set" , "1" ).Run ()
112+ _ = exec .Command ("nft" , "add" , "rule" , "inet" , "ct_test" , "input" ,
113+ "ct" , "label" , "set" , "1" ).Run ()
101114 return func () {
102115 _ = exec .Command ("nft" , "delete" , "table" , "inet" , "ct_test" ).Run ()
103116 }
@@ -1498,6 +1511,138 @@ func TestConntrackCreateV6(t *testing.T) {
14981511 checkProtoInfosEqual (t , flow .ProtoInfo , match .ProtoInfo )
14991512}
15001513
1514+ // TestConntrackLabels test the conntrack table labels
1515+ // Creates some flows and then checks the labels associated
1516+ func TestConntrackLabels (t * testing.T ) {
1517+ skipUnlessRoot (t )
1518+ t .Cleanup (setUpNetlinkTestWithKModule (t , "nf_conntrack" ))
1519+ t .Cleanup (setUpNetlinkTestWithKModule (t , "nf_conntrack_netlink" ))
1520+ k , m , err := KernelVersion ()
1521+ if err != nil {
1522+ t .Fatal (err )
1523+ }
1524+ // conntrack l3proto was unified since 4.19
1525+ // https://github.com/torvalds/linux/commit/a0ae2562c6c4b2721d9fddba63b7286c13517d9f
1526+ if k < 4 || k == 4 && m < 19 {
1527+ t .Cleanup (setUpNetlinkTestWithKModule (t , "nf_conntrack_ipv4" ))
1528+ }
1529+ // Creates a new namespace and bring up the loopback interface
1530+ origns , ns , h := nsCreateAndEnter (t )
1531+ defer netns .Set (* origns )
1532+ defer origns .Close ()
1533+ defer ns .Close ()
1534+ defer runtime .UnlockOSThread ()
1535+
1536+ flow := ConntrackFlow {
1537+ FamilyType : FAMILY_V4 ,
1538+ Forward : IPTuple {
1539+ SrcIP : net.IP {234 , 234 , 234 , 234 },
1540+ DstIP : net.IP {123 , 123 , 123 , 123 },
1541+ SrcPort : 48385 ,
1542+ DstPort : 53 ,
1543+ Protocol : unix .IPPROTO_TCP ,
1544+ },
1545+ Reverse : IPTuple {
1546+ SrcIP : net.IP {123 , 123 , 123 , 123 },
1547+ DstIP : net.IP {234 , 234 , 234 , 234 },
1548+ SrcPort : 53 ,
1549+ DstPort : 48385 ,
1550+ Protocol : unix .IPPROTO_TCP ,
1551+ },
1552+ // No point checking equivalence of timeout, but value must
1553+ // be reasonable to allow for a potentially slow subsequent read.
1554+ TimeOut : 100 ,
1555+ Mark : 12 ,
1556+ Labels : []byte {0 , 0 , 0 , 0 , 3 , 4 , 61 , 141 , 207 , 170 , 2 , 0 , 0 , 0 , 0 , 0 },
1557+ ProtoInfo : & ProtoInfoTCP {
1558+ State : nl .TCP_CONNTRACK_SYN_SENT2 ,
1559+ },
1560+ }
1561+
1562+ err = h .ConntrackUpdate (ConntrackTable , nl .FAMILY_V4 , & flow )
1563+ if err == nil {
1564+ t .Fatalf ("expected an error to occur when trying to update a non-existant conntrack: %+v" , flow )
1565+ }
1566+
1567+ err = h .ConntrackCreate (ConntrackTable , nl .FAMILY_V4 , & flow )
1568+ if err != nil {
1569+ t .Fatalf ("failed to insert conntrack: %s" , err )
1570+ }
1571+
1572+ flows , err := h .ConntrackTableList (ConntrackTable , nl .FAMILY_V4 )
1573+ if err != nil {
1574+ t .Fatalf ("failed to list conntracks following successful insert: %s" , err )
1575+ }
1576+
1577+ filter := ConntrackFilter {
1578+ ipNetFilter : map [ConntrackFilterType ]* net.IPNet {
1579+ ConntrackOrigSrcIP : NewIPNet (flow .Forward .SrcIP ),
1580+ ConntrackOrigDstIP : NewIPNet (flow .Forward .DstIP ),
1581+ ConntrackReplySrcIP : NewIPNet (flow .Reverse .SrcIP ),
1582+ ConntrackReplyDstIP : NewIPNet (flow .Reverse .DstIP ),
1583+ },
1584+ portFilter : map [ConntrackFilterType ]uint16 {
1585+ ConntrackOrigSrcPort : flow .Forward .SrcPort ,
1586+ ConntrackOrigDstPort : flow .Forward .DstPort ,
1587+ },
1588+ protoFilter : unix .IPPROTO_TCP ,
1589+ }
1590+
1591+ var match * ConntrackFlow
1592+ for _ , f := range flows {
1593+ if filter .MatchConntrackFlow (f ) {
1594+ match = f
1595+ break
1596+ }
1597+ }
1598+
1599+ if match == nil {
1600+ t .Fatalf ("Didn't find any matching conntrack entries for original flow: %+v\n Filter used: %+v" , flow , filter )
1601+ } else {
1602+ t .Logf ("Found entry in conntrack table matching original flow: %+v labels=%+v" , match , match .Labels )
1603+ }
1604+ checkFlowsEqual (t , & flow , match )
1605+ checkProtoInfosEqual (t , flow .ProtoInfo , match .ProtoInfo )
1606+
1607+ // Change the conntrack and update the kernel entry.
1608+ flow .Mark = 10
1609+ flow .Labels = make ([]byte , 16 ) // zero labels
1610+ flow .ProtoInfo = & ProtoInfoTCP {
1611+ State : nl .TCP_CONNTRACK_ESTABLISHED ,
1612+ }
1613+ err = h .ConntrackUpdate (ConntrackTable , nl .FAMILY_V4 , & flow )
1614+ if err != nil {
1615+ t .Fatalf ("failed to update conntrack with new mark: %s" , err )
1616+ }
1617+
1618+ // Look for updated conntrack.
1619+ flows , err = h .ConntrackTableList (ConntrackTable , nl .FAMILY_V4 )
1620+ if err != nil {
1621+ t .Fatalf ("failed to list conntracks following successful update: %s" , err )
1622+ }
1623+
1624+ var updatedMatch * ConntrackFlow
1625+ for _ , f := range flows {
1626+ if filter .MatchConntrackFlow (f ) {
1627+ updatedMatch = f
1628+ break
1629+ }
1630+ }
1631+ if updatedMatch == nil {
1632+ t .Fatalf ("Didn't find any matching conntrack entries for updated flow: %+v\n Filter used: %+v" , flow , filter )
1633+ } else {
1634+ t .Logf ("Found entry in conntrack table matching updated flow: %+v labels=%+v" , updatedMatch , updatedMatch .Labels )
1635+ }
1636+
1637+ // To clear the labels we send an empty slice, but when reading back
1638+ // from the kernel we get a nil slice.
1639+ flow .Labels = nil
1640+ checkFlowsEqual (t , & flow , updatedMatch )
1641+ checkProtoInfosEqual (t , flow .ProtoInfo , updatedMatch .ProtoInfo )
1642+ // Switch back to the original namespace
1643+ netns .Set (* origns )
1644+ }
1645+
15011646// TestConntrackFlowToNlData generates a serialized representation of a
15021647// ConntrackFlow and runs the resulting bytes back through `parseRawData` to validate.
15031648func TestConntrackFlowToNlData (t * testing.T ) {
@@ -1518,6 +1663,7 @@ func TestConntrackFlowToNlData(t *testing.T) {
15181663 Protocol : unix .IPPROTO_TCP ,
15191664 },
15201665 Mark : 5 ,
1666+ Labels : []byte {0 , 0 , 0 , 0 , 3 , 4 , 61 , 141 , 207 , 170 , 2 , 0 , 0 , 0 , 0 , 0 },
15211667 TimeOut : 10 ,
15221668 ProtoInfo : & ProtoInfoTCP {
15231669 State : nl .TCP_CONNTRACK_ESTABLISHED ,
@@ -1540,6 +1686,7 @@ func TestConntrackFlowToNlData(t *testing.T) {
15401686 Protocol : unix .IPPROTO_TCP ,
15411687 },
15421688 Mark : 5 ,
1689+ Labels : []byte {0 , 0 , 0 , 0 , 3 , 4 , 61 , 141 , 207 , 170 , 2 , 0 , 0 , 0 , 0 , 0 },
15431690 TimeOut : 10 ,
15441691 ProtoInfo : & ProtoInfoTCP {
15451692 State : nl .TCP_CONNTRACK_ESTABLISHED ,
@@ -1596,6 +1743,11 @@ func checkFlowsEqual(t *testing.T, f1, f2 *ConntrackFlow) {
15961743 t .Logf ("Reverse tuples mismatch. Tuple1 reverse flow: %+v, Tuple2 reverse flow: %+v.\n " , f1 .Reverse , f2 .Reverse )
15971744 t .Fail ()
15981745 }
1746+
1747+ if ! bytes .Equal (f1 .Labels , f2 .Labels ) {
1748+ t .Logf ("Conntrack flow Labels differ. Tuple1: %+v, Tuple2: %+v.\n " , f1 .Labels , f2 .Labels )
1749+ t .Fail ()
1750+ }
15991751}
16001752
16011753func checkProtoInfosEqual (t * testing.T , p1 , p2 ProtoInfo ) {
0 commit comments