github.com/ti-mo/conntrack@v0.5.0/attribute_types_test.go (about)

     1  package conntrack
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/stretchr/testify/assert"
     7  
     8  	"github.com/mdlayher/netlink"
     9  	"github.com/ti-mo/netfilter"
    10  )
    11  
    12  var (
    13  	adEmpty, _   = netfilter.NewAttributeDecoder([]byte{})
    14  	adOneUnknown = *mustDecodeAttribute(netfilter.Attribute{Type: uint16(ctaUnspec)})
    15  	adTwoUnknown = *mustDecodeAttributes(
    16  		[]netfilter.Attribute{
    17  			{Type: uint16(ctaUnspec)},
    18  			{Type: uint16(ctaUnspec)},
    19  		})
    20  	adThreeUnknown = *mustDecodeAttributes(
    21  		[]netfilter.Attribute{
    22  			{Type: uint16(ctaUnspec)},
    23  			{Type: uint16(ctaUnspec)},
    24  			{Type: uint16(ctaUnspec)},
    25  		})
    26  )
    27  
    28  // mustDecodeAttribute wraps attr in a list of netfilter.Attributes and calls
    29  // mustDecodeAttributes.
    30  func mustDecodeAttribute(attr netfilter.Attribute) *netlink.AttributeDecoder {
    31  	return mustDecodeAttributes([]netfilter.Attribute{attr})
    32  }
    33  
    34  // mustDecodeAttributes marshals a list of netfilter.Attributes and returns
    35  // an AttributeDecoder holding the binary output of the unmarshal.
    36  func mustDecodeAttributes(attrs []netfilter.Attribute) *netlink.AttributeDecoder {
    37  	ba, err := netfilter.MarshalAttributes(attrs)
    38  	if err != nil {
    39  		panic(err)
    40  	}
    41  
    42  	ad, err := netfilter.NewAttributeDecoder(ba)
    43  	if err != nil {
    44  		panic(err)
    45  	}
    46  
    47  	return ad
    48  }
    49  
    50  func TestAttributeTypeString(t *testing.T) {
    51  	if attributeType(255).String() == "" {
    52  		t.Fatal("AttributeType string representation empty - did you run `go generate`?")
    53  	}
    54  }
    55  
    56  func TestAttributeHelper(t *testing.T) {
    57  	hlp := Helper{}
    58  	assert.Equal(t, false, hlp.filled())
    59  	assert.Equal(t, true, Helper{Info: []byte{1}}.filled())
    60  	assert.Equal(t, true, Helper{Name: "1"}.filled())
    61  
    62  	nfaNameInfo := netfilter.Attribute{
    63  		Type:   uint16(ctaHelp),
    64  		Nested: true,
    65  		Children: []netfilter.Attribute{
    66  			{
    67  				Type: uint16(ctaHelpName),
    68  				Data: []byte("foo"),
    69  			},
    70  			{
    71  				Type: uint16(ctaHelpInfo),
    72  				Data: []byte{1, 2},
    73  			},
    74  		},
    75  	}
    76  	assert.Nil(t, hlp.unmarshal(mustDecodeAttributes(nfaNameInfo.Children)))
    77  
    78  	assert.EqualValues(t, hlp.marshal(), nfaNameInfo)
    79  
    80  	ad := adOneUnknown
    81  	assert.ErrorIs(t, hlp.unmarshal(&ad), errUnknownAttribute)
    82  }
    83  
    84  func TestAttributeProtoInfo(t *testing.T) {
    85  	var pi ProtoInfo
    86  	assert.Equal(t, false, pi.filled())
    87  	assert.Equal(t, true, ProtoInfo{DCCP: &ProtoInfoDCCP{}}.filled())
    88  	assert.Equal(t, true, ProtoInfo{TCP: &ProtoInfoTCP{}}.filled())
    89  	assert.Equal(t, true, ProtoInfo{SCTP: &ProtoInfoSCTP{}}.filled())
    90  
    91  	assert.ErrorIs(t, pi.unmarshal(adEmpty), errNeedSingleChild)
    92  
    93  	// Exhaust the AttributeDecoder before passing to unmarshal.
    94  	ead := mustDecodeAttribute(nfaUnspecU16)
    95  	ead.Next()
    96  	assert.NoError(t, pi.unmarshal(ead))
    97  
    98  	ad := adOneUnknown
    99  	assert.ErrorIs(t, pi.unmarshal(&ad), errUnknownAttribute)
   100  
   101  	// Attempt marshal of empty ProtoInfo, expect attribute with zero children.
   102  	assert.Len(t, pi.marshal().Children, 0)
   103  
   104  	// TCP protocol info
   105  	nfaInfoTCP := netfilter.Attribute{
   106  		Type:   uint16(ctaProtoInfo),
   107  		Nested: true,
   108  		Children: []netfilter.Attribute{
   109  			{
   110  				Type:   uint16(ctaProtoInfoTCP),
   111  				Nested: true,
   112  				Children: []netfilter.Attribute{
   113  					{
   114  						Type: uint16(ctaProtoInfoTCPState),
   115  						Data: []byte{1},
   116  					},
   117  					{
   118  						Type: uint16(ctaProtoInfoTCPWScaleOriginal),
   119  						Data: []byte{2},
   120  					},
   121  					{
   122  						Type: uint16(ctaProtoInfoTCPWScaleReply),
   123  						Data: []byte{3},
   124  					},
   125  					{
   126  						Type: uint16(ctaProtoInfoTCPFlagsOriginal),
   127  						Data: []byte{0, 4},
   128  					},
   129  					{
   130  						Type: uint16(ctaProtoInfoTCPFlagsReply),
   131  						Data: []byte{0, 5},
   132  					},
   133  				},
   134  			},
   135  		},
   136  	}
   137  
   138  	// Full ProtoInfoTCP unmarshal.
   139  	var tpi ProtoInfo
   140  	assert.NoError(t, tpi.unmarshal(mustDecodeAttributes(nfaInfoTCP.Children)))
   141  
   142  	// Re-marshal into netfilter Attribute
   143  	assert.EqualValues(t, nfaInfoTCP, tpi.marshal())
   144  
   145  	// DCCP protocol info
   146  	nfaInfoDCCP := netfilter.Attribute{
   147  		Type:   uint16(ctaProtoInfo),
   148  		Nested: true,
   149  		Children: []netfilter.Attribute{
   150  			{
   151  				Type:   uint16(ctaProtoInfoDCCP),
   152  				Nested: true,
   153  				Children: []netfilter.Attribute{
   154  					{
   155  						Type: uint16(ctaProtoInfoDCCPState),
   156  						Data: []byte{1},
   157  					},
   158  					{
   159  						Type: uint16(ctaProtoInfoDCCPRole),
   160  						Data: []byte{2},
   161  					},
   162  					{
   163  						Type: uint16(ctaProtoInfoDCCPHandshakeSeq),
   164  						Data: []byte{3, 4, 5, 6, 7, 8, 9, 10},
   165  					},
   166  				},
   167  			},
   168  		},
   169  	}
   170  
   171  	// Full ProtoInfoDCCP unmarshal
   172  	var dpi ProtoInfo
   173  	assert.Nil(t, dpi.unmarshal(mustDecodeAttributes(nfaInfoDCCP.Children)))
   174  
   175  	// Re-marshal into netfilter Attribute
   176  	assert.EqualValues(t, nfaInfoDCCP, dpi.marshal())
   177  
   178  	nfaInfoSCTP := netfilter.Attribute{
   179  		Type:   uint16(ctaProtoInfo),
   180  		Nested: true,
   181  		Children: []netfilter.Attribute{
   182  			{
   183  				Type:   uint16(ctaProtoInfoSCTP),
   184  				Nested: true,
   185  				Children: []netfilter.Attribute{
   186  					{
   187  						Type: uint16(ctaProtoInfoSCTPState),
   188  						Data: []byte{1},
   189  					},
   190  					{
   191  						Type: uint16(ctaProtoInfoSCTPVTagOriginal),
   192  						Data: []byte{2, 3, 4, 5},
   193  					},
   194  					{
   195  						Type: uint16(ctaProtoInfoSCTPVtagReply),
   196  						Data: []byte{6, 7, 8, 9},
   197  					},
   198  				},
   199  			},
   200  		},
   201  	}
   202  
   203  	// Full ProtoInfoSCTP unmarshal
   204  	var spi ProtoInfo
   205  	assert.Nil(t, spi.unmarshal(mustDecodeAttributes(nfaInfoSCTP.Children)))
   206  
   207  	// Re-marshal into netfilter Attribute
   208  	assert.EqualValues(t, nfaInfoSCTP, spi.marshal())
   209  
   210  	// Attempt to unmarshal into re-used ProtoInfo
   211  	pi.TCP = &ProtoInfoTCP{}
   212  	assert.ErrorIs(t, pi.unmarshal(mustDecodeAttribute(nfaInfoTCP)), errReusedProtoInfo)
   213  }
   214  
   215  func TestProtoInfoTypeString(t *testing.T) {
   216  	ssid := protoInfoType(255)
   217  
   218  	ssidStr := ssid.String()
   219  
   220  	if ssidStr == "" {
   221  		t.Fatal("ProtoInfoType string representation empty - did you run `go generate`?")
   222  	}
   223  }
   224  
   225  func TestAttributeProtoInfoTCP(t *testing.T) {
   226  	var pit ProtoInfoTCP
   227  	assert.ErrorIs(t, pit.unmarshal(adEmpty), errNeedSingleChild)
   228  
   229  	ad := adThreeUnknown
   230  	assert.ErrorIs(t, pit.unmarshal(&ad), errUnknownAttribute)
   231  
   232  	nfaProtoInfoTCP := netfilter.Attribute{
   233  		Type:   uint16(ctaProtoInfoTCP),
   234  		Nested: true,
   235  		Children: []netfilter.Attribute{
   236  			{
   237  				Type: uint16(ctaProtoInfoTCPState),
   238  				Data: []byte{1},
   239  			},
   240  			{
   241  				Type: uint16(ctaProtoInfoTCPFlagsOriginal),
   242  				Data: []byte{0, 2},
   243  			},
   244  			{
   245  				Type: uint16(ctaProtoInfoTCPFlagsReply),
   246  				Data: []byte{0, 3},
   247  			},
   248  			{
   249  				Type: uint16(ctaProtoInfoTCPWScaleOriginal),
   250  				Data: []byte{4},
   251  			},
   252  			{
   253  				Type: uint16(ctaProtoInfoTCPWScaleReply),
   254  				Data: []byte{5},
   255  			},
   256  		},
   257  	}
   258  	assert.NoError(t, pit.unmarshal(mustDecodeAttributes(nfaProtoInfoTCP.Children)))
   259  }
   260  
   261  func TestAttributeProtoInfoDCCP(t *testing.T) {
   262  	var pid ProtoInfoDCCP
   263  	assert.ErrorIs(t, pid.unmarshal(adEmpty), errNeedSingleChild)
   264  
   265  	ad := adThreeUnknown
   266  	assert.ErrorIs(t, pid.unmarshal(&ad), errUnknownAttribute)
   267  
   268  	nfaProtoInfoDCCP := netfilter.Attribute{
   269  		Type:   uint16(ctaProtoInfoDCCP),
   270  		Nested: true,
   271  		Children: []netfilter.Attribute{
   272  			{
   273  				Type: uint16(ctaProtoInfoDCCPState),
   274  				Data: []byte{1},
   275  			},
   276  			{
   277  				Type: uint16(ctaProtoInfoDCCPRole),
   278  				Data: []byte{2},
   279  			},
   280  			{
   281  				Type: uint16(ctaProtoInfoDCCPHandshakeSeq),
   282  				Data: []byte{3, 4, 5, 6, 7, 8, 9, 10},
   283  			},
   284  		},
   285  	}
   286  	assert.NoError(t, pid.unmarshal(mustDecodeAttributes(nfaProtoInfoDCCP.Children)))
   287  }
   288  
   289  func TestAttributeProtoInfoSCTP(t *testing.T) {
   290  	var pid ProtoInfoSCTP
   291  	assert.ErrorIs(t, pid.unmarshal(adEmpty), errNeedSingleChild)
   292  
   293  	ad := adOneUnknown
   294  	assert.ErrorIs(t, pid.unmarshal(&ad), errUnknownAttribute)
   295  
   296  	nfaProtoInfoSCTP := netfilter.Attribute{
   297  		Type:   uint16(ctaProtoInfoSCTP),
   298  		Nested: true,
   299  		Children: []netfilter.Attribute{
   300  			{
   301  				Type: uint16(ctaProtoInfoSCTPState),
   302  				Data: []byte{1},
   303  			},
   304  			{
   305  				Type: uint16(ctaProtoInfoSCTPVTagOriginal),
   306  				Data: []byte{2, 3, 4, 5},
   307  			},
   308  			{
   309  				Type: uint16(ctaProtoInfoSCTPVtagReply),
   310  				Data: []byte{6, 7, 8, 9},
   311  			},
   312  		},
   313  	}
   314  	assert.NoError(t, pid.unmarshal(mustDecodeAttributes(nfaProtoInfoSCTP.Children)))
   315  }
   316  
   317  func TestAttributeCounters(t *testing.T) {
   318  	ctr := Counter{}
   319  	assert.Equal(t, false, ctr.filled())
   320  	assert.Equal(t, true, Counter{Packets: 1, Bytes: 1}.filled())
   321  
   322  	// Counters can be unmarshaled from both ctaCountersOrig and ctaCountersReply
   323  	attrTypes := []attributeType{ctaCountersOrig, ctaCountersReply}
   324  
   325  	for _, at := range attrTypes {
   326  		t.Run(at.String(), func(t *testing.T) {
   327  			assert.ErrorIs(t, ctr.unmarshal(adEmpty), errNeedChildren)
   328  
   329  			nfaCounter := netfilter.Attribute{
   330  				Type:   uint16(at),
   331  				Nested: true,
   332  				Children: []netfilter.Attribute{
   333  					{
   334  						Type: uint16(ctaCountersBytes),
   335  						Data: make([]byte, 8),
   336  					},
   337  					{
   338  						Type: uint16(ctaCountersPackets),
   339  						Data: make([]byte, 8),
   340  					},
   341  					{
   342  						Type: uint16(ctaCountersPad),
   343  						Data: make([]byte, 8),
   344  					},
   345  				},
   346  			}
   347  			assert.NoError(t, ctr.unmarshal(mustDecodeAttributes(nfaCounter.Children)))
   348  
   349  			ad := adTwoUnknown
   350  			assert.ErrorIs(t, ctr.unmarshal(&ad), errUnknownAttribute)
   351  		})
   352  	}
   353  }
   354  
   355  func TestAttributeTimestamp(t *testing.T) {
   356  	var ts Timestamp
   357  	assert.ErrorIs(t, ts.unmarshal(adEmpty), errNeedSingleChild)
   358  
   359  	ad := adOneUnknown
   360  	assert.ErrorIs(t, ts.unmarshal(&ad), errUnknownAttribute)
   361  
   362  	nfaTimestamp := netfilter.Attribute{
   363  		Type:   uint16(ctaTimestamp),
   364  		Nested: true,
   365  		Children: []netfilter.Attribute{
   366  			{
   367  				Type: uint16(ctaTimestampStart),
   368  				Data: make([]byte, 8),
   369  			},
   370  			{
   371  				Type: uint16(ctaTimestampStop),
   372  				Data: make([]byte, 8),
   373  			},
   374  		},
   375  	}
   376  	assert.NoError(t, ts.unmarshal(mustDecodeAttributes(nfaTimestamp.Children)))
   377  }
   378  
   379  func TestAttributeSecCtx(t *testing.T) {
   380  	var sc Security
   381  	assert.ErrorIs(t, sc.unmarshal(adEmpty), errNeedChildren)
   382  
   383  	ad := adOneUnknown
   384  	assert.ErrorIs(t, sc.unmarshal(&ad), errUnknownAttribute)
   385  
   386  	nfaSecurity := netfilter.Attribute{
   387  		Type:   uint16(ctaSecCtx),
   388  		Nested: true,
   389  		Children: []netfilter.Attribute{
   390  			{
   391  				Type: uint16(ctaSecCtxName),
   392  				Data: []byte("foo"),
   393  			},
   394  		},
   395  	}
   396  	assert.NoError(t, sc.unmarshal(mustDecodeAttributes(nfaSecurity.Children)))
   397  }
   398  
   399  func TestAttributeSeqAdj(t *testing.T) {
   400  	sa := SequenceAdjust{}
   401  	assert.Equal(t, false, sa.filled())
   402  	assert.Equal(t, true, SequenceAdjust{Position: 1, OffsetBefore: 1, OffsetAfter: 1}.filled())
   403  
   404  	// SequenceAdjust can be unmarshaled from both ctaSeqAdjOrig and ctaSeqAdjReply
   405  	attrTypes := []attributeType{ctaSeqAdjOrig, ctaSeqAdjReply}
   406  
   407  	for _, at := range attrTypes {
   408  		t.Run(at.String(), func(t *testing.T) {
   409  			assert.ErrorIs(t, sa.unmarshal(adEmpty), errNeedSingleChild)
   410  
   411  			ad := adOneUnknown
   412  			assert.ErrorIs(t, sa.unmarshal(&ad), errUnknownAttribute)
   413  
   414  			nfaSeqAdj := netfilter.Attribute{
   415  				Type:   uint16(at),
   416  				Nested: true,
   417  				Children: []netfilter.Attribute{
   418  					{
   419  						Type: uint16(ctaSeqAdjCorrectionPos),
   420  						Data: make([]byte, 4),
   421  					},
   422  					{
   423  						Type: uint16(ctaSeqAdjOffsetBefore),
   424  						Data: make([]byte, 4),
   425  					},
   426  					{
   427  						Type: uint16(ctaSeqAdjOffsetAfter),
   428  						Data: make([]byte, 4),
   429  					},
   430  				},
   431  			}
   432  			assert.NoError(t, sa.unmarshal(mustDecodeAttributes(nfaSeqAdj.Children)))
   433  
   434  			// The AttributeDecoder unmarshal() no longer has the tuple direction, set it manually.
   435  			// TODO: Remove when marshal() switches to AttributeEncoder.
   436  			if at == ctaSeqAdjReply {
   437  				sa.Direction = true
   438  			} else {
   439  				sa.Direction = false
   440  			}
   441  
   442  			assert.EqualValues(t, nfaSeqAdj, sa.marshal(false))
   443  		})
   444  	}
   445  }
   446  
   447  func TestAttributeSynProxy(t *testing.T) {
   448  	sp := SynProxy{}
   449  	assert.Equal(t, false, sp.filled())
   450  	assert.Equal(t, true, SynProxy{ISN: 1}.filled())
   451  	assert.Equal(t, true, SynProxy{ITS: 1}.filled())
   452  	assert.Equal(t, true, SynProxy{TSOff: 1}.filled())
   453  
   454  	assert.ErrorIs(t, sp.unmarshal(adEmpty), errNeedSingleChild)
   455  
   456  	ad := adOneUnknown
   457  	assert.ErrorIs(t, sp.unmarshal(&ad), errUnknownAttribute)
   458  
   459  	nfaSynProxy := netfilter.Attribute{
   460  		Type:   uint16(ctaSynProxy),
   461  		Nested: true,
   462  		Children: []netfilter.Attribute{
   463  			{
   464  				Type: uint16(ctaSynProxyISN),
   465  				Data: []byte{0, 1, 2, 3},
   466  			},
   467  			{
   468  				Type: uint16(ctaSynProxyITS),
   469  				Data: []byte{4, 5, 6, 7},
   470  			},
   471  			{
   472  				Type: uint16(ctaSynProxyTSOff),
   473  				Data: []byte{8, 9, 10, 11},
   474  			},
   475  		},
   476  	}
   477  	assert.NoError(t, sp.unmarshal(mustDecodeAttributes(nfaSynProxy.Children)))
   478  
   479  	assert.EqualValues(t, nfaSynProxy, sp.marshal())
   480  }