-
Notifications
You must be signed in to change notification settings - Fork 52
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This adds TCP support to the dataplane and includes integration tests to verify `TCPRoute` functionality. Checksums and conntrack cleanup will be handled by follow-up issues: - #25 - #85
- Loading branch information
Showing
11 changed files
with
188 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
bases: | ||
- ../../samples/tcproute |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
pub mod icmp; | ||
pub mod tcp; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
use core::mem; | ||
|
||
use aya_bpf::{ | ||
bindings::{TC_ACT_OK, TC_ACT_PIPE}, | ||
helpers::bpf_csum_diff, | ||
programs::TcContext, | ||
}; | ||
use aya_log_ebpf::info; | ||
|
||
use crate::{ | ||
bindings::{iphdr, tcphdr}, | ||
utils::{csum_fold_helper, ptr_at, ETH_HDR_LEN, IP_HDR_LEN}, | ||
BLIXT_CONNTRACK, | ||
}; | ||
|
||
pub fn handle_tcp_egress(ctx: TcContext) -> Result<i32, i64> { | ||
// gather the TCP header | ||
let ip_hdr: *mut iphdr = unsafe { ptr_at(&ctx, ETH_HDR_LEN) }?; | ||
let tcp_header_offset = ETH_HDR_LEN + IP_HDR_LEN; | ||
let tcp_hdr: *mut tcphdr = unsafe { ptr_at(&ctx, tcp_header_offset)? }; | ||
|
||
// capture some IP and port information | ||
let client_addr = unsafe { (*ip_hdr).daddr }; | ||
let dest_port = unsafe { (*tcp_hdr).dest.to_be() }; | ||
let ip_port_tuple = unsafe { BLIXT_CONNTRACK.get(&client_addr) }.ok_or(TC_ACT_PIPE)?; | ||
|
||
// verify traffic destination | ||
if ip_port_tuple.1 as u16 != dest_port { | ||
return Ok(TC_ACT_PIPE); | ||
} | ||
|
||
info!( | ||
&ctx, | ||
"Received TCP packet destined for tracked IP {:ipv4}:{} setting source IP to VIP {:ipv4}", | ||
u32::from_be(client_addr), | ||
ip_port_tuple.1 as u16, | ||
u32::from_be(ip_port_tuple.0), | ||
); | ||
|
||
unsafe { | ||
(*ip_hdr).saddr = ip_port_tuple.0; | ||
}; | ||
|
||
if (ctx.data() + ETH_HDR_LEN + mem::size_of::<iphdr>()) > ctx.data_end() { | ||
info!(&ctx, "Iphdr is out of bounds"); | ||
return Ok(TC_ACT_OK); | ||
} | ||
|
||
unsafe { (*ip_hdr).check = 0 }; | ||
let full_cksum = unsafe { | ||
bpf_csum_diff( | ||
mem::MaybeUninit::zeroed().assume_init(), | ||
0, | ||
ip_hdr as *mut u32, | ||
mem::size_of::<iphdr>() as u32, | ||
0, | ||
) | ||
} as u64; | ||
unsafe { (*ip_hdr).check = csum_fold_helper(full_cksum) }; | ||
unsafe { (*tcp_hdr).check = 0 }; | ||
|
||
// TODO: connection tracking cleanup https://github.com/kong/blixt/issues/85 | ||
|
||
Ok(TC_ACT_PIPE) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
//go:build integration_tests | ||
// +build integration_tests | ||
|
||
package integration | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"net/http" | ||
"strings" | ||
"testing" | ||
"time" | ||
|
||
"github.com/kong/kubernetes-testing-framework/pkg/clusters" | ||
"github.com/stretchr/testify/require" | ||
corev1 "k8s.io/api/core/v1" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
gatewayv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" | ||
|
||
testutils "github.com/kong/blixt/internal/test/utils" | ||
) | ||
|
||
const ( | ||
tcprouteSampleKustomize = "../../config/tests/tcproute" | ||
tcprouteSampleName = "blixt-tcproute-sample" | ||
) | ||
|
||
func TestTCPRouteBasics(t *testing.T) { | ||
tcpRouteBasicsCleanupKey := "tcproutebasics" | ||
defer func() { | ||
testutils.DumpDiagnosticsIfFailed(ctx, t, env.Cluster()) | ||
runCleanup(tcpRouteBasicsCleanupKey) | ||
}() | ||
|
||
t.Log("deploying config/samples/tcproute kustomize") | ||
require.NoError(t, clusters.KustomizeDeployForCluster(ctx, env.Cluster(), tcprouteSampleKustomize)) | ||
addCleanup(tcpRouteBasicsCleanupKey, func(ctx context.Context) error { | ||
cleanupLog("cleaning up config/samples/tcproute kustomize") | ||
return clusters.KustomizeDeleteForCluster(ctx, env.Cluster(), tcprouteSampleKustomize) | ||
}) | ||
|
||
t.Log("waiting for Gateway to have an address") | ||
var gw *gatewayv1beta1.Gateway | ||
require.Eventually(t, func() bool { | ||
var err error | ||
gw, err = gwclient.GatewayV1beta1().Gateways(corev1.NamespaceDefault).Get(ctx, tcprouteSampleName, metav1.GetOptions{}) | ||
require.NoError(t, err) | ||
return len(gw.Status.Addresses) > 0 | ||
}, time.Minute, time.Second) | ||
require.NotNil(t, gw.Status.Addresses[0].Type) | ||
require.Equal(t, gatewayv1beta1.IPAddressType, *gw.Status.Addresses[0].Type) | ||
gwaddr := fmt.Sprintf("%s:8080", gw.Status.Addresses[0].Value) | ||
|
||
t.Log("waiting for HTTP server to be available") | ||
require.Eventually(t, func() bool { | ||
server, err := env.Cluster().Client().AppsV1().Deployments(corev1.NamespaceDefault).Get(ctx, tcprouteSampleName, metav1.GetOptions{}) | ||
require.NoError(t, err) | ||
return server.Status.AvailableReplicas > 0 | ||
}, time.Minute, time.Second) | ||
|
||
t.Log("verifying HTTP connectivity to the server") | ||
httpc := http.Client{Timeout: time.Second * 10} | ||
require.Eventually(t, func() bool { | ||
resp, err := httpc.Get(fmt.Sprintf("http://%s/status/%d", gwaddr, http.StatusTeapot)) | ||
if err != nil { | ||
t.Logf("received error checking HTTP server: [%s], retrying...", err) | ||
return false | ||
} | ||
defer resp.Body.Close() | ||
return resp.StatusCode == http.StatusTeapot | ||
}, time.Minute, time.Second) | ||
|
||
t.Log("deleting the TCPRoute and verifying that HTTP traffic stops") | ||
require.NoError(t, gwclient.GatewayV1alpha2().TCPRoutes(corev1.NamespaceDefault).Delete(ctx, tcprouteSampleName, metav1.DeleteOptions{})) | ||
httpc = http.Client{Timeout: time.Second * 3} | ||
require.Eventually(t, func() bool { | ||
resp, err := httpc.Get(fmt.Sprintf("http://%s/status/%d", gwaddr, http.StatusTeapot)) | ||
if err != nil { | ||
if strings.Contains(err.Error(), "context deadline exceeded") { | ||
return true | ||
} | ||
t.Logf("received unexpected error waiting for TCPRoute to decomission: %s", err) | ||
return false | ||
} | ||
defer resp.Body.Close() | ||
return false | ||
}, time.Minute, time.Second) | ||
} |