Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
3056247
add dscp values
mazdakn Aug 5, 2025
774aae3
Merge remote-tracking branch 'open-source/master' into dscp-dp
mazdakn Aug 6, 2025
39f7258
fix
mazdakn Aug 6, 2025
d70aa12
update tests
mazdakn Aug 6, 2025
4f5b44c
add more UTs
mazdakn Aug 6, 2025
43c866d
more tests
mazdakn Aug 11, 2025
e9fc95c
Merge remote-tracking branch 'open-source/master' into dscp-dp
mazdakn Aug 11, 2025
74b2132
fix
mazdakn Aug 12, 2025
4df1027
Add UT for DSCP type
mazdakn Aug 12, 2025
92511c9
Merge remote-tracking branch 'open-source/master' into dscp-dp
mazdakn Aug 14, 2025
739c28f
First commit
mazdakn Aug 15, 2025
9bea8d0
Merge remote-tracking branch 'open-source/master' into dscp-dp
mazdakn Aug 15, 2025
6994cbd
Merge branch 'dscp-dp' into nftables-dscp-vmap
mazdakn Aug 15, 2025
48ae366
more
mazdakn Aug 15, 2025
18f50fa
Fix
mazdakn Aug 15, 2025
df78fc8
Merge remote-tracking branch 'open-source/master' into dscp-dp
mazdakn Aug 15, 2025
1c909a8
Merge remote-tracking branch 'open-source/master' into dscp-dp
mazdakn Aug 18, 2025
61ce4be
do not enable in bpf
mazdakn Aug 18, 2025
020e8a0
Merge remote-tracking branch 'open-source/master' into dscp-dp
mazdakn Aug 19, 2025
9eb59fb
Merge remote-tracking branch 'open-source/master' into nftables-dscp-…
mazdakn Aug 19, 2025
0804ab1
Merge branch 'dscp-dp' into nftables-dscp-vmap
mazdakn Aug 19, 2025
039dc56
more
mazdakn Aug 20, 2025
2c58488
Merge remote-tracking branch 'open-source/master' into nftables-dscp-…
mazdakn Aug 21, 2025
e6c6642
cleanup
mazdakn Aug 21, 2025
d5a368c
fix
mazdakn Aug 21, 2025
3f7dcb9
Merge remote-tracking branch 'open-source/master' into nftables-dscp-…
mazdakn Aug 21, 2025
8a6ccc5
update
mazdakn Aug 22, 2025
add69cc
update
mazdakn Aug 23, 2025
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: 23 additions & 0 deletions felix/dataplane/linux/dscp_mgr.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@
package intdataplane

import (
"fmt"
"sort"
"strings"

"github.com/sirupsen/logrus"

"github.com/projectcalico/calico/felix/nftables"
"github.com/projectcalico/calico/felix/proto"
"github.com/projectcalico/calico/felix/rules"
"github.com/projectcalico/calico/felix/types"
Expand All @@ -29,6 +31,7 @@ type dscpManager struct {
ipVersion uint8
ruleRenderer rules.RuleRenderer
mangleTable Table
mangleMaps nftables.MapsDataplane

// QoS policies.
wepPolicies map[types.WorkloadEndpointID]rules.DSCPRule
Expand All @@ -40,11 +43,13 @@ type dscpManager struct {

func newDSCPManager(
mangleTable Table,
mangleMaps nftables.MapsDataplane,
ruleRenderer rules.RuleRenderer,
ipVersion uint8,
) *dscpManager {
return &dscpManager{
mangleTable: mangleTable,
mangleMaps: mangleMaps,
ruleRenderer: ruleRenderer,
ipVersion: ipVersion,
wepPolicies: map[types.WorkloadEndpointID]rules.DSCPRule{},
Expand Down Expand Up @@ -151,10 +156,28 @@ func (m *dscpManager) CompleteDeferredWork() error {
return dscpRules[i].SrcAddrs < dscpRules[j].SrcAddrs
})

logrus.Infof("marva0 %v", dscpRules)
if m.mangleMaps != nil {
mappings := nftablesMappings(dscpRules)
logrus.Infof("marva %v", mappings)
mapMeta := nftables.MapMetadata{Name: rules.NftablesQoSPolicyMap, Type: nftables.MapTypeSourceNetMatch}
m.mangleMaps.AddOrReplaceMap(mapMeta, mappings)
}

chain := m.ruleRenderer.EgressDSCPChain(dscpRules)
m.mangleTable.UpdateChain(chain)
m.dirty = false
}

return nil
}

func nftablesMappings(rules []rules.DSCPRule) map[string][]string {
mappings := map[string][]string{}
logrus.Infof("marva1 %v", rules)
for _, r := range rules {
mappings[r.SrcAddrs] = []string{fmt.Sprintf("%d", r.Value)}
}
logrus.Infof("marva2 %v", mappings)
return mappings
}
3 changes: 2 additions & 1 deletion felix/dataplane/linux/dscp_mgr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ func dscpManagerTests(ipVersion uint8) func() {
MarkDrop: 0x10,
MarkEndpoint: 0x11110000,
})
manager = newDSCPManager(mangleTable, ruleRenderer, ipVersion)
// TODO (mazdak): add more tests for new nftables optimization
manager = newDSCPManager(mangleTable, nil, ruleRenderer, ipVersion)
})

It("should program DSCP chain with no rule", func() {
Expand Down
12 changes: 10 additions & 2 deletions felix/dataplane/linux/int_dataplane.go
Original file line number Diff line number Diff line change
Expand Up @@ -1123,7 +1123,11 @@ func NewIntDataplaneDriver(config Config) *InternalDataplane {
dp.RegisterManager(newHostsIPSetManager(ipSetsV4, 4, config))

if !config.BPFEnabled {
dp.RegisterManager(newDSCPManager(mangleTableV4, ruleRenderer, 4))
var mangleMaps nftables.MapsDataplane
if config.RulesConfig.NFTables {
mangleMaps = mangleTableV4.(nftables.MapsDataplane)
}
dp.RegisterManager(newDSCPManager(mangleTableV4, mangleMaps, ruleRenderer, 4))
}

if config.RulesConfig.IPIPEnabled {
Expand Down Expand Up @@ -1324,7 +1328,11 @@ func NewIntDataplaneDriver(config Config) *InternalDataplane {
dp.RegisterManager(newHostsIPSetManager(ipSetsV6, 6, config))

if !config.BPFEnabled {
dp.RegisterManager(newDSCPManager(mangleTableV6, ruleRenderer, 6))
var mangleMapsV6 nftables.MapsDataplane
if config.RulesConfig.NFTables {
mangleMapsV6 = mangleTableV6.(nftables.MapsDataplane)
}
dp.RegisterManager(newDSCPManager(mangleTableV6, mangleMapsV6, ruleRenderer, 6))
}

// Add a manager for IPv6 wireguard configuration. This is added irrespective of whether wireguard is actually enabled
Expand Down
3 changes: 2 additions & 1 deletion felix/fv/dscp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ var _ = infrastructure.DatastoreDescribe("_BPF-SAFE_ dscp tests", []apiconfig.Da
}
})

It("applying DSCP annotation should result is adding correct rules", func() {
It("pepper1 applying DSCP annotation should result is adding correct rules", func() {
dscp0 := numorstring.DSCPFromInt(0) // 0x0
dscp20 := numorstring.DSCPFromInt(20) // 0x14
dscp32 := numorstring.DSCPFromInt(32) // 0x20
Expand Down Expand Up @@ -243,6 +243,7 @@ var _ = infrastructure.DatastoreDescribe("_BPF-SAFE_ dscp tests", []apiconfig.Da
}
ep2_2.UpdateInInfra(infra)

//time.Sleep(time.Minute * 20)
cc.ResetExpectations()
cc.ExpectSome(extClient, hostw)
cc.ExpectSome(extClient, ep1_1)
Expand Down
1 change: 1 addition & 0 deletions felix/generictables/match_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ type MatchCriteria interface {
// Only supported in nftables.
InInterfaceVMAP(mapname string) MatchCriteria
OutInterfaceVMAP(mapname string) MatchCriteria
SourceNetVMAP(mapname string) MatchCriteria
}

type AddrType string
Expand Down
5 changes: 5 additions & 0 deletions felix/iptables/match_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,11 @@ func (m matchCriteria) OutInterfaceVMAP(mapname string) generictables.MatchCrite
return m
}

func (m matchCriteria) SourceNetVMAP(_ string) generictables.MatchCriteria {
log.Panic("SourceNetVMAP not supported in iptables")
return m
}

func PortsToMultiport(ports []uint16) string {
portFragments := make([]string, len(ports))
for i, port := range ports {
Expand Down
3 changes: 2 additions & 1 deletion felix/nftables/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -517,7 +517,8 @@ type DSCPAction struct {
}

func (a DSCPAction) ToFragment(features *environment.Features) string {
return fmt.Sprintf("<IPV> dscp set %d", a.Value)
return "ip dscp"
//return fmt.Sprintf("<IPV> dscp set %d", a.Value)
}

func (a DSCPAction) String() string {
Expand Down
35 changes: 32 additions & 3 deletions felix/nftables/maps.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ var gaugeVecNumMaps = prometheus.NewGaugeVec(prometheus.GaugeOpts{

type MapType string

const MapTypeInterfaceMatch MapType = "interfaceMatch"
const (
MapTypeInterfaceMatch MapType = "interfaceMatch"
MapTypeSourceNetMatch MapType = "sourceNetMatch"
)

type MapsDataplane interface {
AddOrReplaceMap(meta MapMetadata, members map[string][]string)
Expand Down Expand Up @@ -374,7 +377,7 @@ func (s *Maps) LoadDataplaneState() error {
for _, e := range mapData.elems {
logCxt.WithField("element", e).Debug("Processing element")
switch metadata.Type {
case MapTypeInterfaceMatch:
case MapTypeInterfaceMatch, MapTypeSourceNetMatch:
strElems[e.Key[0]] = e.Value
default:
unknownElems.Add(UnknownMapMember(e.Key, e.Value))
Expand Down Expand Up @@ -442,7 +445,7 @@ func (s *Maps) NFTablesMap(name string) *knftables.Map {

var flags []knftables.SetFlag
switch metadata.Type {
case MapTypeInterfaceMatch:
case MapTypeInterfaceMatch, MapTypeSourceNetMatch:
default:
logrus.WithField("type", metadata.Type).Panic("Unexpected map type")
}
Expand Down Expand Up @@ -623,12 +626,32 @@ func CanonicaliseMapMember(mtype MapType, key string, value []string) MapMember
}
// An action and a chain.
return interfaceToChain{key, splits[0], splits[1]}
case MapTypeSourceNetMatch:
return sourceNetToAction{source: key, action: value[0]}
default:
logrus.Errorf("Unknown map type: %v", mtype)
}
return nil
}

// sourceNetToAction is a MapMember that represents a mapping from a source net to an terminal action.
type sourceNetToAction struct {
source string
action string
}

func (m sourceNetToAction) Key() []string {
return []string{m.source}
}

func (m sourceNetToAction) Value() []string {
return []string{m.action}
}

func (m sourceNetToAction) String() string {
return fmt.Sprintf("%s -> %s", m.source, m.action)
}

// interfaceToAction is a MapMember that represents a mapping from an interface to an terminal action.
type interfaceToAction struct {
iface string
Expand Down Expand Up @@ -670,6 +693,12 @@ func mapType(t MapType, ipVersion int) string {
switch t {
case MapTypeInterfaceMatch:
return "ifname : verdict"
case MapTypeSourceNetMatch:
if ipVersion == 4 {
return "ipv4_addr : dscp"
} else {
return "ipv6_addr : dscp"
}
default:
logrus.WithField("type", string(t)).Panic("Unknown MapType")
}
Expand Down
5 changes: 5 additions & 0 deletions felix/nftables/match_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,11 @@ func (m nftMatch) OutInterfaceVMAP(name string) generictables.MatchCriteria {
return m
}

func (m nftMatch) SourceNetVMAP(name string) generictables.MatchCriteria {
m.clauses = append(m.clauses, fmt.Sprintf("<IPV> saddr @<LAYER>-%s", LegalizeSetName(name)))
return m
}

// PortsToMultiport converts a list of ports to a multiport set suitable for inline use in nftables rules.
func PortsToMultiport(ports []uint16) string {
portFragments := make([]string, len(ports))
Expand Down
14 changes: 11 additions & 3 deletions felix/nftables/table_layer.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,20 @@ func (t *tableLayer) namespaceMapMember(m []string) []string {
return m
}

action := strings.Split(strings.TrimSpace(m[0]), " ")[0]

// TODO: We only use "goto" in map members right now. If we add other actions, we'll need to namespace them.
// This is a very brittle implementation that assumes that the map member is a single string that starts with "goto ".
if !strings.HasPrefix(m[0], "goto ") {
logrus.Panicf("Unexpected map member: %s", m)
switch action {
case "goto":
return []string{fmt.Sprintf("goto %s", t.namespaceName(m[0][5:]))}
case "dscp":
return m
//default:
// logrus.Panicf("Unexpected map member: %s", m)
}
return []string{fmt.Sprintf("goto %s", t.namespaceName(m[0][5:]))}

return m
}

func (t *tableLayer) Name() string {
Expand Down
25 changes: 24 additions & 1 deletion felix/rules/dscp.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,31 @@ type DSCPRule struct {
}

func (r *DefaultRuleRenderer) EgressDSCPChain(rules []DSCPRule) *generictables.Chain {
if r.NFTables {
return r.nftablesQoSPolicyRules(rules)
}
return r.defaultQoSPolicyRules(rules)
}

func (r *DefaultRuleRenderer) nftablesQoSPolicyRules(rules []DSCPRule) *generictables.Chain {
var renderedRules []generictables.Rule
// DSCP Ruls are sorted and validated by DSCP manager.

renderedRules = append(renderedRules, generictables.Rule{
Match: r.NewMatch().SourceNetVMAP(NftablesQoSPolicyMap),
//Match: r.NewMatch(),
//Action: r.DSCP("vmap"),
})

return &generictables.Chain{
Name: ChainEgressDSCP,
Rules: renderedRules,
}
}

func (r *DefaultRuleRenderer) defaultQoSPolicyRules(rules []DSCPRule) *generictables.Chain {
var renderedRules []generictables.Rule
// Rules are sorted and validated by DSCP manager.
// DSCP Ruls are sorted and validated by DSCP manager.
for _, rule := range rules {
renderedRules = append(renderedRules, generictables.Rule{
Match: r.NewMatch().SourceNet(rule.SrcAddrs),
Expand Down
3 changes: 2 additions & 1 deletion felix/rules/rule_defs.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ const (
ChainManglePrerouting = ChainNamePrefix + "PREROUTING"
ChainManglePostrouting = ChainNamePrefix + "POSTROUTING"

ChainEgressDSCP = ChainNamePrefix + "egress-dscp"
ChainEgressDSCP = ChainNamePrefix + "egress-dscp"
NftablesQoSPolicyMap = ChainEgressDSCP + "-map"

IPSetIDAllPools = "all-ipam-pools"
IPSetIDNATOutgoingMasqPools = "masq-ipam-pools"
Expand Down
Loading