Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 5 additions & 18 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,20 +1,7 @@
.PHONY: image-generate generate docker-generate test docker-test publish

image-generate:
docker build -f build/image/generate/Dockerfile -t localhost/generate ./build/image/generate/

generate:
find . -name "*.go" -exec gci write --Section Standard --Section Default --Section "Prefix(github.com/everoute/template-repo)" {} +

docker-generate: image-generate
$(eval WORKDIR := /go/src/github.com/everoute/template-repo)
docker run --rm -iu 0:0 -w $(WORKDIR) -v $(CURDIR):$(WORKDIR) localhost/generate make generate

.PHONY: test
test:
go test ./... --race --coverprofile coverage.out

docker-test:
$(eval WORKDIR := /go/src/github.com/everoute/template-repo)
docker run --rm -iu 0:0 -w $(WORKDIR) -v $(CURDIR):$(WORKDIR) golang:1.19 make test
go test ./... --race -p 1 --coverprofile coverage.out '-gcflags=all=-N -l'

publish:
.PHONY: go-mod-tidy
go-mod-tidy:
go mod tidy
244 changes: 244 additions & 0 deletions ctlabels.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
/*
Conntrack Label
see https://docs.google.com/spreadsheets/d/1jADYgo0tt1-Q9GYglZkRoiDRn0VGHyj2zNVNDPxgxLU/edit?usp=sharing

Traffic Visualization

| 127-126 | 125-124 | 123-108 | 107-92 | 91-88 | 87-32 | 31-4 | 3-0 |
| not | encoding scheme | reply | origin | bridge id | not | bridge id | not |
| set | (0x01) | in_port | in_port | high bits | set | low bits | set |

Micro Segmentation

| 127 | 126 | 125-124 | 123-108 | 107-92 | 91-90 | 89-88 | 87-60 | 59-32 | 31-4 | 3-0 |
| work policy | monitor policy | encoding scheme | reply | origin | reply | origin | work mode | monitor mode | | round |
| action drop | action drop | (0x11) | in_port | in_port | source | source | flow id | flow id | | number |

*/

package ctlabels

import (
"encoding/hex"
"fmt"
"strings"

numeric "github.com/everoute/numeric-go"
)

type EncodingScheme uint8

const (
EncodingSchemeOld EncodingScheme = 0b00
EncodingSchemeTrafficVisualization EncodingScheme = 0b01
EncodingSchemeReserved EncodingScheme = 0b10
EncodingSchemeMicroSegmentation EncodingScheme = 0b11
)

// PacketSource is the source bridge of the packet
// Used 2 bits only
// 0b10: Local Bridge
// 0b11: Uplink Bridge
type PacketSource uint8

const (
PacketSourceLocalBridge PacketSource = 0b10
PacketSourceUplinkBridge PacketSource = 0b11
)

type DecodedTrafficVisualizationConntrackLabels struct {
// RoundNumber uint8 // 4 bits
BridgeID uint32 `json:"bridge_id"`
// MonitorPolicySequence uint32 // 28 bits
// MonitorPolicyID uint32 // Equals to MonitorPolicySequence | RoundNumber << 28
// WorkPolicySequence uint32 // 28 bits
// WorkPolicyID uint32 // Equals to WorkPolicySequence | RoundNumber << 28
OriginInport uint16 `json:"origin_inport"`
ReplyInport uint16 `json:"reply_inport"`
EncodingScheme EncodingScheme `json:"encoding_scheme"`
// MonitorPolicyActionDrop bool
// WorkPolicyActionDrop bool
}

type DecodedMicroSegmentationConntrackLabels struct {
RoundNumber uint8 `json:"round_number"` // 4 bits
MonitorFlowSequence uint32 `json:"monitor_flow_sequence"` // 28 bits
MonitorFlowID uint32 `json:"monitor_flow_id"` // Equals to MonitorFlowSequence | RoundNumber << 28
WorkFlowSequence uint32 `json:"work_flow_sequence"` // 28 bits
WorkFlowID uint32 `json:"work_flow_id"` // Equals to WorkFlowSequence | RoundNumber << 28
OriginPacketSource PacketSource `json:"origin_packet_source"` // 2 bits
ReplyPacketSource PacketSource `json:"reply_packet_source"` // 2 bits
OriginInport uint16 `json:"origin_inport"`
ReplyInport uint16 `json:"reply_inport"`
EncodingScheme EncodingScheme `json:"encoding_scheme"`
MonitorPolicyActionDrop bool `json:"monitor_policy_action_drop"`
WorkPolicyActionDrop bool `json:"work_policy_action_drop"`
}

// Mask for the conntrack labels
var (
// Encoding Scheme
EncodingSchemeShift = uint8(124)
EncodingSchemeLength = uint8(2)
EncodingSchemeMask = numeric.Mask(EncodingSchemeLength).ShiftLeft(EncodingSchemeShift)

// Common bits
ReplyInportShift = uint8(108)
ReplyInportLength = uint8(16)
ReplyInportMask = numeric.Mask(ReplyInportLength).ShiftLeft(ReplyInportShift)
OriginInportShift = uint8(92)
OriginInportLength = uint8(16)
OriginInportMask = numeric.Mask(OriginInportLength).ShiftLeft(OriginInportShift)
RoundNumberShift = uint8(0)
RoundNumberLength = uint8(4)
RoundNumberMask = numeric.Mask(RoundNumberLength).ShiftLeft(RoundNumberShift)

// Traffic Visualization
// Encoding Scheme here 125-124
// ReplyInportMask here 123-108
// OriginInportMask here 107-92
BridgeIDHighBitsShift = uint8(88)
BridgeIDHighBitsLength = uint8(4)
BridgeIDHighBitsMask = numeric.Mask(BridgeIDHighBitsLength).ShiftLeft(BridgeIDHighBitsShift)
BridgeIDLowBitsShift = uint8(4)
BridgeIDLowBitsLength = uint8(28)
BridgeIDLowBitsMask = numeric.Mask(BridgeIDLowBitsLength).ShiftLeft(BridgeIDLowBitsShift)

// Micro Segmentation
WorkPolicyActionDropShift = uint8(127)
WorkPolicyActionDropMask = numeric.Mask(1).ShiftLeft(WorkPolicyActionDropShift)
MonitorPolicyActionDropShift = uint8(126)
MonitorPolicyActionDropMask = numeric.Mask(1).ShiftLeft(MonitorPolicyActionDropShift)
// Encoding Scheme here 125-124
// ReplyInportMask here 123-108
// OriginInportMask here 107-92
ReplyPacketSourceLength = uint8(2)
ReplyPacketSourceShift = uint8(90)
ReplyPacketSourceMask = numeric.Mask(ReplyPacketSourceLength).ShiftLeft(ReplyPacketSourceShift)
OriginPacketSourceLength = uint8(2)
OriginPacketSourceShift = uint8(88)
OriginPacketSourceMask = numeric.Mask(OriginPacketSourceLength).ShiftLeft(OriginPacketSourceShift)
WorkPolicySequenceLength = uint8(28)
WorkPolicySequenceShift = uint8(60)
WorkPolicySequenceMask = numeric.Mask(WorkPolicySequenceLength).ShiftLeft(WorkPolicySequenceShift)
MonitorPolicySequenceLength = uint8(28)
MonitorPolicySequenceShift = uint8(32)
MonitorPolicySequenceMask = numeric.Mask(MonitorPolicySequenceLength).ShiftLeft(MonitorPolicySequenceShift)
// round number here 3-0
)

// DecodeConntrackLabels decodes the conntrack labels.
// Labels is a 16 bytes array, and it's little endian.
func DecodeConntrackLabels(labels []byte) (EncodingScheme, any, error) {
// assert len(labels) == 16
if len(labels) != 16 {
return 0, nil, fmt.Errorf("invalid labels length: %d want 16", len(labels))
}
scheme, err := DecodeScheme(numeric.Uint128FromLittleEndianBytes(labels))
if err != nil {
return 0, nil, err
}
switch scheme {
case EncodingSchemeOld:
fallthrough
case EncodingSchemeReserved:
return scheme, nil, nil
case EncodingSchemeTrafficVisualization:
decoded, err := DecodeTrafficVisualization(numeric.Uint128FromLittleEndianBytes(labels))
if err != nil {
return 0, nil, err
}
return scheme, decoded, nil
case EncodingSchemeMicroSegmentation:
decoded, err := DecodeMicroSegmentation(numeric.Uint128FromLittleEndianBytes(labels))
if err != nil {
return 0, nil, err
}
return scheme, decoded, nil
}
return 0, nil, fmt.Errorf("invalid scheme: %d", scheme)
}

func DecodeScheme(labels numeric.Uint128) (EncodingScheme, error) {
scheme := labels.And(EncodingSchemeMask).ShiftRight(EncodingSchemeShift)
switch EncodingScheme(scheme.Low) {
case EncodingSchemeTrafficVisualization:
return EncodingSchemeTrafficVisualization, nil
case EncodingSchemeMicroSegmentation:
return EncodingSchemeMicroSegmentation, nil
case EncodingSchemeReserved:
return EncodingSchemeReserved, nil
case EncodingSchemeOld:
return EncodingSchemeOld, nil
}
return 0, fmt.Errorf("invalid scheme: %d", scheme)
}

func DecodeTrafficVisualization(labels numeric.Uint128) (DecodedTrafficVisualizationConntrackLabels, error) {
decoded := DecodedTrafficVisualizationConntrackLabels{}

bridgeIDHigh := labels.And(BridgeIDHighBitsMask).ShiftRight(BridgeIDHighBitsShift)
bridgeIDLow := labels.And(BridgeIDLowBitsMask).ShiftRight(BridgeIDLowBitsShift)
decoded.BridgeID = uint32(bridgeIDHigh.Low)<<BridgeIDLowBitsLength | uint32(bridgeIDLow.Low)

decoded.OriginInport = uint16(labels.And(OriginInportMask).ShiftRight(OriginInportShift).Low)
decoded.ReplyInport = uint16(labels.And(ReplyInportMask).ShiftRight(ReplyInportShift).Low)
decoded.EncodingScheme = EncodingScheme(labels.And(EncodingSchemeMask).ShiftRight(EncodingSchemeShift).Low)

return decoded, nil
}

func DecodeMicroSegmentation(labels numeric.Uint128) (DecodedMicroSegmentationConntrackLabels, error) {
decoded := DecodedMicroSegmentationConntrackLabels{}

decoded.RoundNumber = uint8(labels.And(RoundNumberMask).ShiftRight(RoundNumberShift).Low)
decoded.MonitorFlowSequence = uint32(labels.And(MonitorPolicySequenceMask).ShiftRight(MonitorPolicySequenceShift).Low)
if decoded.MonitorFlowSequence != 0 {
decoded.MonitorFlowID = decoded.MonitorFlowSequence | (uint32(decoded.RoundNumber) << MonitorPolicySequenceLength)
}
decoded.WorkFlowSequence = uint32(labels.And(WorkPolicySequenceMask).ShiftRight(WorkPolicySequenceShift).Low)
if decoded.WorkFlowSequence != 0 {
decoded.WorkFlowID = decoded.WorkFlowSequence | (uint32(decoded.RoundNumber) << WorkPolicySequenceLength)
}
decoded.OriginPacketSource = PacketSource(labels.And(OriginPacketSourceMask).ShiftRight(OriginPacketSourceShift).Low)
decoded.ReplyPacketSource = PacketSource(labels.And(ReplyPacketSourceMask).ShiftRight(ReplyPacketSourceShift).Low)
decoded.OriginInport = uint16(labels.And(OriginInportMask).ShiftRight(OriginInportShift).Low)
decoded.ReplyInport = uint16(labels.And(ReplyInportMask).ShiftRight(ReplyInportShift).Low)
decoded.EncodingScheme = EncodingScheme(labels.And(EncodingSchemeMask).ShiftRight(EncodingSchemeShift).Low)
decoded.MonitorPolicyActionDrop = labels.And(MonitorPolicyActionDropMask).ShiftRight(MonitorPolicyActionDropShift).Low != 0
decoded.WorkPolicyActionDrop = labels.And(WorkPolicyActionDropMask).ShiftRight(WorkPolicyActionDropShift).Low != 0

return decoded, nil
}

func removeHexPrefix(str string) string {
if strings.HasPrefix(str, "0x") {
return str[2:]
}
return str
}

func CTLabelsStrToBigEndianBytes(str string) []byte {
str = removeHexPrefix(str)
if len(str) < 32 { // want 128 bits
// prepend 0 to the string
str = strings.Repeat("0", 32-len(str)) + str
}
bigEndianLabels, err := hex.DecodeString(str)
if err != nil {
return nil
}
return bigEndianLabels
}

func CTLabelsStrToLittleEndianBytes(str string) []byte {
return numeric.SwapBytes(CTLabelsStrToBigEndianBytes(str))
}

func CTLabelsStringToBinaryString(str string) string {
bigEndianLabels := CTLabelsStrToBigEndianBytes(str)
if bigEndianLabels == nil {
return ""
}
return numeric.FormatBigEndianBinaryString(bigEndianLabels, "")
}
61 changes: 61 additions & 0 deletions ctlabels_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package ctlabels_test

import (
"encoding/json"
"fmt"
"testing"

ctlabels "github.com/everoute/ctlabels-go"
numeric "github.com/everoute/numeric-go"
. "github.com/onsi/gomega"
)

func TestDecodeMicroSegmentation(t *testing.T) {
RegisterTestingT(t)
labelsStr := "0x70001000ae80002a780010e10000000b"
labels := ctlabels.CTLabelsStrToLittleEndianBytes(labelsStr)
Expect(labels).ShouldNot(BeNil())

decoded, err := ctlabels.DecodeMicroSegmentation(numeric.Uint128FromLittleEndianBytes(labels))
Expect(err).Should(BeNil())
jsonStr, err := json.Marshal(decoded)
Expect(err).Should(BeNil())
expected := ctlabels.DecodedMicroSegmentationConntrackLabels{
RoundNumber: 0b1011,
MonitorFlowSequence: 0b1000000000000001000011100001,
MonitorFlowID: 0b1011_1000000000000001000011100001,
WorkFlowSequence: 0b1000000000000000001010100111,
WorkFlowID: 0b1011_1000000000000000001010100111,
OriginPacketSource: ctlabels.PacketSource(0b10),
ReplyPacketSource: ctlabels.PacketSource(0b11),
OriginInport: 0b0000000000001010,
ReplyInport: 0b0000000000000001,
EncodingScheme: ctlabels.EncodingScheme(0b11),
MonitorPolicyActionDrop: true,
WorkPolicyActionDrop: false,
}
expectJson, err := json.Marshal(expected)
Expect(err).Should(Succeed())
Expect(jsonStr).Should(Equal(expectJson))
}

func TestDecodeTrafficVisualization(t *testing.T) {
RegisterTestingT(t)
labelsStr := "0x10011000240000000000000056789ab0"
labels := ctlabels.CTLabelsStrToLittleEndianBytes(labelsStr)
Expect(labels).ShouldNot(BeNil())
decoded, err := ctlabels.DecodeTrafficVisualization(numeric.Uint128FromLittleEndianBytes(labels))
Expect(err).Should(BeNil())
jsonStr, err := json.Marshal(decoded)
Expect(err).Should(BeNil())
fmt.Println(ctlabels.CTLabelsStringToBinaryString(labelsStr))
expected := ctlabels.DecodedTrafficVisualizationConntrackLabels{
BridgeID: 0b0100_0101_0110_0111_1000_1001_1010_1011, // 0x456789ab
OriginInport: 0b0000_0000_0000_0010,
ReplyInport: 0b0000_0000_0001_0001,
EncodingScheme: ctlabels.EncodingScheme(0b01),
}
expectJson, err := json.Marshal(expected)
Expect(err).Should(Succeed())
Expect(jsonStr).Should(Equal(expectJson))
}
15 changes: 15 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module github.com/everoute/ctlabels-go

go 1.20

require (
github.com/everoute/numeric-go v0.0.0-20251223074813-1affd646f5e4
github.com/onsi/gomega v1.32.0
)

require (
github.com/google/go-cmp v0.6.0 // indirect
golang.org/x/net v0.33.0 // indirect
golang.org/x/text v0.21.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
20 changes: 20 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
github.com/everoute/numeric-go v0.0.0-20251223074813-1affd646f5e4 h1:VEk9Ls84wQv9Tendag5DUMcS7S+yxKPIQ9SCjpC39Ak=
github.com/everoute/numeric-go v0.0.0-20251223074813-1affd646f5e4/go.mod h1:tVM+NiqrlevTmmvG3Z/XK+6rn6Z+/tzW7N6rJ6M+kAE=
github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY=
github.com/onsi/gomega v1.32.0 h1:JRYU78fJ1LPxlckP6Txi/EYqJvjtMrDC04/MM5XRHPk=
github.com/onsi/gomega v1.32.0/go.mod h1:a4x4gW6Pz2yK1MAmvluYme5lvYTn61afQ2ETw/8n4Lg=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Loading