diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go new file mode 100644 index 000000000..faa098bbe --- /dev/null +++ b/pkg/constants/constants.go @@ -0,0 +1,19 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package constants + +const ( + PinDirDotPlaceholder = "__dot__" +) diff --git a/pkg/ebpf/ingress_node_firewall_loader.go b/pkg/ebpf/ingress_node_firewall_loader.go index 76b1a827f..ae6ae0ad6 100644 --- a/pkg/ebpf/ingress_node_firewall_loader.go +++ b/pkg/ebpf/ingress_node_firewall_loader.go @@ -16,6 +16,7 @@ import ( "github.com/openshift/ingress-node-firewall/api/v1alpha1" ingressnodefwiov1alpha1 "github.com/openshift/ingress-node-firewall/api/v1alpha1" + "github.com/openshift/ingress-node-firewall/pkg/constants" "github.com/openshift/ingress-node-firewall/pkg/interfaces" "github.com/openshift/ingress-node-firewall/pkg/utils" @@ -319,7 +320,7 @@ func (infc *IngNodeFwController) IngressNodeFwAttach(ifacesName ...string) error } } // Pin the XDP program. - lPinDir := path.Join(infc.pinPath, ifaceName+linkSuffix) + lPinDir := sanitizePinDir(path.Join(infc.pinPath, ifaceName+linkSuffix)) if err := l.Pin(lPinDir); err != nil { errors = append(errors, fmt.Errorf("failed to pin link to pinDir %s: %s", lPinDir, err)) continue @@ -477,6 +478,7 @@ func (infc *IngNodeFwController) loadPinnedLinks() error { for _, file := range files { if re.Match([]byte(file.Name())) { interfaceName := strings.TrimSuffix(file.Name(), linkSuffix) + interfaceName = sanitizePinDir(interfaceName) if _, ok := infc.links[interfaceName]; !ok { l, err := link.LoadPinnedLink(path.Join(infc.pinPath, file.Name()), nil) if err != nil { @@ -729,3 +731,14 @@ func (infc *IngNodeFwController) purgeKeys(keys []BpfLpmIpKeySt) error { } return nil } + +// bpffs does not allow dots in filenames: https://github.com/torvalds/linux/blob/6146a0f1dfae5d37442a9ddcba012add260bceb0/kernel/bpf/inode.c#L371-L381 +// sanitizePinDir converts dots with a placeholder and vice-versa to support subinterface +func sanitizePinDir(filename string) string { + + if strings.Contains(filename, ".") { + return strings.ReplaceAll(filename, ".", constants.PinDirDotPlaceholder) + } + + return strings.ReplaceAll(filename, constants.PinDirDotPlaceholder, ".") +} diff --git a/pkg/ebpf/ingress_node_firewall_loader_test.go b/pkg/ebpf/ingress_node_firewall_loader_test.go index 5377efffe..a93c854a2 100644 --- a/pkg/ebpf/ingress_node_firewall_loader_test.go +++ b/pkg/ebpf/ingress_node_firewall_loader_test.go @@ -128,3 +128,31 @@ func cleanup(t *testing.T) { t.Log(err) } } + +func TestSanitizePinDir(t *testing.T) { + tests := []struct { + in string + expected string + }{ + { + in: "eth0", + expected: "eth0", + }, + { + in: "bond0.100", + expected: "bond0__dot__100", + }, + { + in: "eth0__dot__100", + expected: "eth0.100", + }, + } + + for _, tc := range tests { + result := sanitizePinDir(tc.in) + if result != tc.expected { + t.Fatalf("Failed to sanitize, expected %s, got %s", + tc.expected, result) + } + } +} diff --git a/pkg/webhook/webhook.go b/pkg/webhook/webhook.go index d9015dbbe..34745d31a 100644 --- a/pkg/webhook/webhook.go +++ b/pkg/webhook/webhook.go @@ -8,6 +8,7 @@ import ( "strings" ingressnodefwv1alpha1 "github.com/openshift/ingress-node-firewall/api/v1alpha1" + "github.com/openshift/ingress-node-firewall/pkg/constants" "github.com/openshift/ingress-node-firewall/pkg/failsaferules" "github.com/openshift/ingress-node-firewall/pkg/utils" @@ -104,6 +105,11 @@ func validateINFInterfaces(ctx context.Context, infInterfaces []string, infName field.Invalid(field.NewPath("Spec").Child("interfaces").Index(index), infName, fmt.Sprintf("interface %q can't start with a number", inf))) } + if strings.Contains(inf, constants.PinDirDotPlaceholder) { + allErrs = append(allErrs, + field.Invalid(field.NewPath("Spec").Child("interfaces").Index(index), + infName, fmt.Sprintf("interface %q can't contain '%s'", inf, constants.PinDirDotPlaceholder))) + } } return allErrs } diff --git a/pkg/webhook/webhook_suite_test.go b/pkg/webhook/webhook_suite_test.go index 8a2fc6fc5..1ab343ede 100644 --- a/pkg/webhook/webhook_suite_test.go +++ b/pkg/webhook/webhook_suite_test.go @@ -32,6 +32,7 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" admissionv1beta1 "k8s.io/api/admission/v1beta1" + //+kubebuilder:scaffold:imports metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -179,6 +180,17 @@ var _ = Describe("Interfaces", func() { configInterfaces(inf, []string{"0th"}) Expect(createIngressNodeFirewall(inf)).ToNot(Succeed()) }) + It("interfaces config with interface name containing dot", func() { + initCIDRICMPRule(inf, ipv4CIDR, validOrder, false, icmpTypeEchoReply, icmpTypeEchoReply, ingressnodefwv1alpha1.IngressNodeFirewallAllow) + configInterfaces(inf, []string{"eth0.100"}) + Expect(createIngressNodeFirewall(inf)).To(Succeed()) + Expect(deleteIngressNodeFirewall(inf)).To(Succeed()) + }) + It("interfaces config with interface name containing dot placeholder", func() { + initCIDRICMPRule(inf, ipv4CIDR, validOrder, false, icmpTypeEchoReply, icmpTypeEchoReply, ingressnodefwv1alpha1.IngressNodeFirewallAllow) + configInterfaces(inf, []string{"eth0__dot__100"}) + Expect(createIngressNodeFirewall(inf)).ToNot(Succeed()) + }) }) })