diff --git a/felix/bpf-gpl/tc.c b/felix/bpf-gpl/tc.c index 98b1c13ddec..ecd532af2a6 100644 --- a/felix/bpf-gpl/tc.c +++ b/felix/bpf-gpl/tc.c @@ -55,6 +55,12 @@ int calico_tc_main(struct __sk_buff *skb) */ skb->mark = SKB_MARK; #endif + + if (CALI_F_LO && CALI_F_TO_HOST) { + /* Do nothing, it is a packet that just looped around. */ + return TC_ACT_UNSPEC; + } + /* Optimisation: if another BPF program has already pre-approved the packet, * skip all processing. */ if (CALI_F_FROM_HOST && skb->mark == CALI_SKB_MARK_BYPASS) { @@ -263,6 +269,16 @@ static CALI_BPF_INLINE void calico_tc_process_ct_lookup(struct cali_tc_ctx *ctx) skb_mark_equals(ctx->skb, CALI_SKB_MARK_BYPASS_MASK, CALI_SKB_MARK_SKIP_FIB))); if (HAS_HOST_CONFLICT_PROG && + /* Do not do conflict resolution for host-self loop. Unlike with + * traffic to another backend, we are not able to tell traffic to + * self via service from straight to self. + */ + !CALI_F_LO && + /* Do conflict resolution on other device if it clashes with + * traffic looped via the NAT_IF but it hasn't been seen yet and + * is not looped via the NAT_IF, that is, it is from host, but not + * to a service. + */ (ctx->state->ct_result.flags & CALI_CT_FLAG_VIA_NAT_IF) && !(ctx->skb->mark & (CALI_SKB_MARK_FROM_NAT_IFACE_OUT | CALI_SKB_MARK_SEEN))) { CALI_DEBUG("Host source SNAT conflict\n"); diff --git a/felix/fv/bpf_test.go b/felix/fv/bpf_test.go index 90dc9cb4b8f..d0aa7af415a 100644 --- a/felix/fv/bpf_test.go +++ b/felix/fv/bpf_test.go @@ -4257,6 +4257,35 @@ func describeBPFTests(opts ...bpfTestOpt) bool { }) }) + It("should have connectivity from host-networked pods via service to host-networked backend", func() { + By("Setting up the service") + hostW[0].ConfigureInInfra(infra) + testSvc := k8sService("host-svc", clusterIP, hostW[0], 80, 8055, 0, testOpts.protocol) + testSvcNamespace := testSvc.ObjectMeta.Namespace + k8sClient := infra.(*infrastructure.K8sDatastoreInfra).K8sClient + _, err := k8sClient.CoreV1().Services(testSvcNamespace).Create(context.Background(), testSvc, metav1.CreateOptions{}) + Expect(err).NotTo(HaveOccurred()) + Eventually(k8sGetEpsForServiceFunc(k8sClient, testSvc), "10s").Should(HaveLen(1), + "Service endpoints didn't get created? Is controller-manager happy?") + + By("Testing connectivity") + port := uint16(testSvc.Spec.Ports[0].Port) + + hostW0SrcIP := ExpectWithSrcIPs(felixIP(0)) + hostW1SrcIP := ExpectWithSrcIPs(felixIP(1)) + if !testOpts.connTimeEnabled { + switch testOpts.tunnel { + case "ipip": + hostW0SrcIP = ExpectWithSrcIPs(tc.Felixes[0].ExpectedIPIPTunnelAddr) + hostW1SrcIP = ExpectWithSrcIPs(tc.Felixes[1].ExpectedIPIPTunnelAddr) + } + } + + cc.Expect(Some, hostW[0], TargetIP(clusterIP), ExpectWithPorts(port), hostW0SrcIP) + cc.Expect(Some, hostW[1], TargetIP(clusterIP), ExpectWithPorts(port), hostW1SrcIP) + cc.CheckConnectivity() + }) + }) Describe("with BPF disabled to begin with", func() {