Skip to content

Commit fa60fc7

Browse files
kerumetogvisor-bot
authored andcommitted
Add functions deep copy and replace NFTables structure.
This change adds a DeepCopy method to copy all the elements of an NFTables struct and a method to replace the data of a NFTables struct. Needed for atomic, rollback-able changes when processing batch messages. PiperOrigin-RevId: 789489198
1 parent b5a5365 commit fa60fc7

File tree

2 files changed

+140
-0
lines changed

2 files changed

+140
-0
lines changed

pkg/tcpip/nftables/nftables.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121

2222
"gvisor.dev/gvisor/pkg/abi/linux"
2323
"gvisor.dev/gvisor/pkg/atomicbitops"
24+
"gvisor.dev/gvisor/pkg/log"
2425
"gvisor.dev/gvisor/pkg/rand"
2526
"gvisor.dev/gvisor/pkg/sentry/socket/netlink/nlmsg"
2627
"gvisor.dev/gvisor/pkg/syserr"
@@ -270,6 +271,15 @@ func (nf *NFTables) Flush(attrs map[uint16]nlmsg.BytesView, owner uint32) {
270271
continue
271272
}
272273

274+
// TODO: b/434242152 - Support correctly deleting chains once
275+
// rules are deletable.
276+
for chainName := range table.chains {
277+
ok := table.DeleteChain(chainName)
278+
if !ok {
279+
log.Warningf("Failed to delete chain %s", chainName)
280+
}
281+
}
282+
273283
tablesToDelete = append(tablesToDelete, TableInfo{Name: name, Handle: table.GetHandle()})
274284
}
275285

pkg/tcpip/nftables/nftables_types.go

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,7 @@ type Chain struct {
388388

389389
// BaseChainInfo stores hook-related info for attaching a chain to the pipeline.
390390
type BaseChainInfo struct {
391+
// LINT.IfChange(base_chain_info)
391392

392393
// BcType is the base chain type of the chain (filter, nat, route).
393394
BcType BaseChainType
@@ -414,6 +415,8 @@ type BaseChainInfo struct {
414415
// explicitly accepted or rejected by the rules. A chain's policy defaults to
415416
// Accept, but this can be used to specify otherwise.
416417
PolicyDrop bool
418+
419+
// LINT.ThenChange(:base_chain_info_copy)
417420
}
418421

419422
// PolicyBoolToValue converts the policy drop boolean to a uint8.
@@ -1109,3 +1112,130 @@ func HasAttr(attrName uint16, attrs map[uint16]nlmsg.BytesView) bool {
11091112
_, ok := attrs[attrName]
11101113
return ok
11111114
}
1115+
1116+
// deepCopyRule returns a deep copy of the Rule struct.
1117+
func deepCopyRule(rule *Rule, chainCopy *Chain) *Rule {
1118+
return &Rule{
1119+
chain: chainCopy,
1120+
// Because the underlying op data within the slice cannot be
1121+
// modified, creating a shallow copy is sufficient. Even if the
1122+
// original struct is modified and an operation is dropped,
1123+
// the copy will hold a reference to the original operation,
1124+
// preventing it from being destroyed.
1125+
ops: slices.Clone(rule.ops),
1126+
handle: rule.handle,
1127+
udata: slices.Clone(rule.udata),
1128+
}
1129+
}
1130+
1131+
// deepCopyChain returns a deep copy of the Chain struct.
1132+
func deepCopyChain(chain *Chain, tableCopy *Table) *Chain {
1133+
chainCopy := &Chain{
1134+
name: chain.name,
1135+
table: tableCopy,
1136+
handle: chain.handle,
1137+
flags: chain.flags,
1138+
handleToRule: make(map[uint64]*Rule),
1139+
userData: slices.Clone(chain.userData),
1140+
chainUse: chain.chainUse,
1141+
bound: chain.bound,
1142+
comment: chain.comment,
1143+
}
1144+
1145+
// LINT.IfChange(base_chain_info_copy)
1146+
1147+
// BaseChainInfo is immutable after creation and it only contains
1148+
// primitives, so we can safely copy it.
1149+
if chain.baseChainInfo != nil {
1150+
chainCopy.baseChainInfo = &BaseChainInfo{}
1151+
*chainCopy.baseChainInfo = *chain.baseChainInfo
1152+
}
1153+
1154+
// LINT.ThenChange()
1155+
1156+
for _, rule := range chain.rules {
1157+
ruleCopy := deepCopyRule(rule, chainCopy)
1158+
chainCopy.rules = append(chainCopy.rules, ruleCopy)
1159+
chainCopy.handleToRule[ruleCopy.handle] = ruleCopy
1160+
}
1161+
return chainCopy
1162+
}
1163+
1164+
// deepCopyTable returns a deep copy of the Table struct.
1165+
func deepCopyTable(table *Table, afFilter *addressFamilyFilter) *Table {
1166+
tableCopy := &Table{
1167+
name: table.name,
1168+
afFilter: afFilter,
1169+
chains: make(map[string]*Chain),
1170+
chainHandles: make(map[uint64]*Chain),
1171+
flagSet: make(map[TableFlag]struct{}),
1172+
handle: table.handle,
1173+
owner: table.owner,
1174+
userData: slices.Clone(table.userData),
1175+
}
1176+
tableCopy.handleCounter.Store(table.handleCounter.Load())
1177+
1178+
for flag := range table.flagSet {
1179+
tableCopy.flagSet[flag] = struct{}{}
1180+
}
1181+
1182+
for chainName, chain := range table.chains {
1183+
chainCopy := deepCopyChain(chain, tableCopy)
1184+
tableCopy.chains[chainName] = chainCopy
1185+
tableCopy.chainHandles[chainCopy.handle] = chainCopy
1186+
}
1187+
return tableCopy
1188+
}
1189+
1190+
// DeepCopy returns a deep copy of the NFTables struct.
1191+
// Assumes that the caller has already locked the mutex.
1192+
// **********************************************************************
1193+
// TODO: b/436922484: Add a transaction system to avoid deep copying the entire
1194+
// NFTables structure.
1195+
// **********************************************************************
1196+
func (nf *NFTables) DeepCopy() *NFTables {
1197+
nftCopy := &NFTables{
1198+
clock: nf.clock,
1199+
startTime: nf.startTime,
1200+
rng: nf.rng,
1201+
tableHandleCounter: atomicbitops.Uint64{},
1202+
}
1203+
1204+
nftCopy.tableHandleCounter.Store(nf.tableHandleCounter.Load())
1205+
for i, filter := range nf.filters {
1206+
if filter == nil {
1207+
continue
1208+
}
1209+
1210+
nftCopy.filters[i] = &addressFamilyFilter{
1211+
family: filter.family,
1212+
nftState: nftCopy,
1213+
tables: make(map[string]*Table),
1214+
tableHandles: make(map[uint64]*Table),
1215+
hfStacks: make(map[stack.NFHook]*hookFunctionStack),
1216+
}
1217+
1218+
for tableName, table := range filter.tables {
1219+
tableCopy := deepCopyTable(table, nftCopy.filters[i])
1220+
nftCopy.filters[i].tables[tableName] = tableCopy
1221+
nftCopy.filters[i].tableHandles[tableCopy.handle] = tableCopy
1222+
}
1223+
1224+
for hook, hfStack := range filter.hfStacks {
1225+
hfStackCopy := &hookFunctionStack{
1226+
hook: hfStack.hook,
1227+
}
1228+
for _, chain := range hfStack.baseChains {
1229+
hfStackCopy.baseChains = append(hfStackCopy.baseChains, nftCopy.filters[i].tables[chain.table.name].chains[chain.name])
1230+
}
1231+
nftCopy.filters[i].hfStacks[hook] = hfStackCopy
1232+
}
1233+
}
1234+
return nftCopy
1235+
}
1236+
1237+
// ReplaceNFTables replaces the tables of the NFTables struct
1238+
// with the tables of the passed in NFTables struct.
1239+
func (nf *NFTables) ReplaceNFTables(nftCopy *NFTables) {
1240+
nf.filters = nftCopy.filters
1241+
}

0 commit comments

Comments
 (0)