Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Skip docker-user ipv6 manipulations if the docker-user chain is not found #2410

Merged
merged 2 commits into from
Jan 18, 2025
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
4 changes: 4 additions & 0 deletions docs/rn/0.62.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,8 @@ The Cisco IOL node type now supports the [`save`](../cmd/save.md) command that s

* fix iptables handling for both mgmt network and bridge nodes. The rules are now set up in both directions #2406

### 0.62.2

* do not attempt manipulate iptables rules in the DOCKER-USER chain if it does not exist #2410

[^1]: You can add it for your platform of interest.
105 changes: 63 additions & 42 deletions runtime/docker/firewall/nftables/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ type NftablesClient struct {
nftConn *nftables.Conn
// is ip6_tables supported
ip6_tables bool
// is ip6 for docker supported
ip6_docker bool
}

// NewNftablesClient returns a new NftablesClient.
Expand All @@ -42,22 +44,31 @@ func NewNftablesClient() (*NftablesClient, error) {

nftC := &NftablesClient{
nftConn: nftConn,
ip6_tables: true,
ip6_tables: false,
ip6_docker: false,
}

chains, err := nftC.getChains(definitions.DockerUserChain, nftables.TableFamilyIPv4)
chain, err := nftC.getChain(definitions.ForwardChain, definitions.FilterTable, nftables.TableFamilyIPv4)
if err != nil {
return nil, err
}
if len(chains) == 0 {
if chain == nil {
log.Debugf("nftables does not seem to be in use, no %s chain found.", definitions.DockerUserChain)
return nil, definitions.ErrNotAvailable
}

// check if ip6_tables is available
v6Tables, err := nftC.nftConn.ListTablesOfFamily(nftables.TableFamilyIPv6)
if err != nil || len(v6Tables) == 0 {
nftC.ip6_tables = false
// check if ip6_tables is generally available
v6Chains, err := nftC.nftConn.ListChainsOfTableFamily(nftables.TableFamilyIPv6)
if err == nil && len(v6Chains) > 0 {
log.Debugf("nftables: ip6 address family generally supported")
nftC.ip6_tables = true
}

// check if ip6 docker-user chain is available
v6DockerChain, err := nftC.getChain(definitions.DockerUserChain, definitions.FilterTable, nftables.TableFamilyIPv6)
if err == nil && v6DockerChain != nil {
log.Debugf("nftables: ip6 docker is supported")
nftC.ip6_docker = true
}

return nftC, nil
Expand All @@ -83,15 +94,23 @@ func (c *NftablesClient) DeleteForwardingRules(rule definitions.FirewallRule) er
return fmt.Errorf("%w. See http://containerlab.dev/manual/network/#external-access", err)
}

var v6rules []*nftables.Rule
if c.ip6_tables {
v6rules, err = c.getRules(rule.Chain, rule.Table, nftables.TableFamilyIPv6)
if c.ip6_tables && rule.Chain == definitions.ForwardChain {
v6rules, err := c.getRules(rule.Chain, rule.Table, nftables.TableFamilyIPv6)
if err != nil {
return fmt.Errorf("%w. See http://containerlab.dev/manual/network/#external-access", err)
return fmt.Errorf("failed to get iptables rules for chain %s, table %s, family %s: %w",
rule.Chain, rule.Table, afMap[nftables.TableFamilyIPv6], err)
}
allRules = append(allRules, v6rules...)
}

allRules = append(allRules, v6rules...)
if c.ip6_docker && rule.Chain == definitions.DockerUserChain {
v6rules, err := c.getRules(rule.Chain, rule.Table, nftables.TableFamilyIPv6)
if err != nil {
return fmt.Errorf("failed to get iptables rules for chain %s, table %s, family %s: %w",
rule.Chain, rule.Table, afMap[nftables.TableFamilyIPv6], err)
}
allRules = append(allRules, v6rules...)
}

clabRules := c.getClabRulesForInterface(iface, allRules)
if len(clabRules) == 0 {
Expand Down Expand Up @@ -129,11 +148,18 @@ func (c *NftablesClient) InstallForwardingRules(rule definitions.FirewallRule) e
return err
}

if c.ip6_tables {
err = c.InstallForwardingRulesForAF(nftables.TableFamilyIPv6, rule)
if !c.ip6_tables && rule.Chain == definitions.ForwardChain {
log.Debug("ip6_tables is not supported, skipping installation of ip6 forwarding rules")
return nil
}

if !c.ip6_docker && rule.Chain == definitions.DockerUserChain {
log.Debug("ip6_tables is not supported by docker, skipping installation of ip6 forwarding rules")
return nil
}

return err
// now it is safe to install the ip6 rules
return c.InstallForwardingRulesForAF(nftables.TableFamilyIPv6, rule)
}

// InstallForwardingRulesForAF installs the forwarding rules for the specified address family
Expand Down Expand Up @@ -184,21 +210,20 @@ func (c *NftablesClient) InstallForwardingRulesForAF(af nftables.TableFamily, ru
return nil
}

// getChains returns all chains with the provided name and family.
func (nftC *NftablesClient) getChains(name string, family nftables.TableFamily) ([]*nftables.Chain, error) {
var result []*nftables.Chain

// getChain returns a chain for the provided name, table name and family.
func (nftC *NftablesClient) getChain(name, table string, family nftables.TableFamily) (*nftables.Chain, error) {
chains, err := nftC.nftConn.ListChainsOfTableFamily(family)
if err != nil {
return nil, err
}

for _, c := range chains {
if c.Name == name {
result = append(result, c)
if c.Name == name && c.Table.Name == table {
return c, nil
}
}
return result, nil

return nil, nil
}

func (nftC *NftablesClient) deleteRule(r *nftables.Rule) error {
Expand All @@ -207,44 +232,40 @@ func (nftC *NftablesClient) deleteRule(r *nftables.Rule) error {

// getRules returns all rules for the provided chain name, table name and family.
func (nftC *NftablesClient) getRules(chainName, tableName string, family nftables.TableFamily) ([]*nftables.Rule, error) {
// get chain reference
chains, err := nftC.getChains(chainName, family)
chain, err := nftC.getChain(chainName, tableName, family)
if err != nil {
return nil, err
}

for _, c := range chains {
if c.Table.Name == tableName && c.Table.Family == family {
return nftC.nftConn.GetRules(c.Table, c)
}
if chain == nil {
return nil, fmt.Errorf("no match for chain %q, table %q with family %q found", chainName, tableName, family)
}

return nil, fmt.Errorf("no match for chain %q, table %q with family %q found", chainName, tableName, family)
return nftC.nftConn.GetRules(chain.Table, chain)

}

func (nftC *NftablesClient) newClabNftablesRule(chainName, tableName string,
family nftables.TableFamily, position uint64,
) (*clabNftablesRule, error) {
chains, err := nftC.getChains(chainName, family)
chain, err := nftC.getChain(chainName, tableName, family)
if err != nil {
return nil, err
}

for _, c := range chains {
if c.Table.Name == tableName && c.Table.Family == family {
r := &nftables.Rule{
Handle: 0,
Position: position,
Table: c.Table,
Chain: c,
Exprs: []expr.Any{},
}
if chain == nil {
return nil, errors.New("chain " + chainName + " not found in table " + tableName + " with family " + string(family))
}

return &clabNftablesRule{rule: r}, nil
}
r := &nftables.Rule{
Handle: 0,
Position: position,
Table: chain.Table,
Chain: chain,
Exprs: []expr.Any{},
}

return nil, errors.New("chain " + chainName + " not found in table " + tableName + " with family " + string(family))
return &clabNftablesRule{rule: r}, nil
}

// InsertRule inserts a rule.
Expand Down
Loading