1
0
Fork 0
mirror of synced 2025-03-06 20:59:54 +01:00
linux/tools/testing/selftests/bpf/progs/cgroup_tcp_skb.c
Kui-Feng Lee 539c7e67aa selftests/bpf: Verify that the cgroup_skb filters receive expected packets.
This test case includes four scenarios:

1. Connect to the server from outside the cgroup and close the connection
   from outside the cgroup.
2. Connect to the server from outside the cgroup and close the connection
   from inside the cgroup.
3. Connect to the server from inside the cgroup and close the connection
   from outside the cgroup.
4. Connect to the server from inside the cgroup and close the connection
   from inside the cgroup.

The test case is to verify that cgroup_skb/{egress, ingress} filters
receive expected packets including SYN, SYN/ACK, ACK, FIN, and FIN/ACK.

Signed-off-by: Kui-Feng Lee <kuifeng@meta.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Link: https://lore.kernel.org/bpf/20230624014600.576756-3-kuifeng@meta.com
2023-06-30 16:09:27 +02:00

382 lines
7.4 KiB
C

// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */
#include <linux/bpf.h>
#include <bpf/bpf_endian.h>
#include <bpf/bpf_helpers.h>
#include <linux/if_ether.h>
#include <linux/in.h>
#include <linux/in6.h>
#include <linux/ipv6.h>
#include <linux/tcp.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "cgroup_tcp_skb.h"
char _license[] SEC("license") = "GPL";
__u16 g_sock_port = 0;
__u32 g_sock_state = 0;
int g_unexpected = 0;
__u32 g_packet_count = 0;
int needed_tcp_pkt(struct __sk_buff *skb, struct tcphdr *tcph)
{
struct ipv6hdr ip6h;
if (skb->protocol != bpf_htons(ETH_P_IPV6))
return 0;
if (bpf_skb_load_bytes(skb, 0, &ip6h, sizeof(ip6h)))
return 0;
if (ip6h.nexthdr != IPPROTO_TCP)
return 0;
if (bpf_skb_load_bytes(skb, sizeof(ip6h), tcph, sizeof(*tcph)))
return 0;
if (tcph->source != bpf_htons(g_sock_port) &&
tcph->dest != bpf_htons(g_sock_port))
return 0;
return 1;
}
/* Run accept() on a socket in the cgroup to receive a new connection. */
static int egress_accept(struct tcphdr *tcph)
{
if (g_sock_state == SYN_RECV_SENDING_SYN_ACK) {
if (tcph->fin || !tcph->syn || !tcph->ack)
g_unexpected++;
else
g_sock_state = SYN_RECV;
return 1;
}
return 0;
}
static int ingress_accept(struct tcphdr *tcph)
{
switch (g_sock_state) {
case INIT:
if (!tcph->syn || tcph->fin || tcph->ack)
g_unexpected++;
else
g_sock_state = SYN_RECV_SENDING_SYN_ACK;
break;
case SYN_RECV:
if (tcph->fin || tcph->syn || !tcph->ack)
g_unexpected++;
else
g_sock_state = ESTABLISHED;
break;
default:
return 0;
}
return 1;
}
/* Run connect() on a socket in the cgroup to start a new connection. */
static int egress_connect(struct tcphdr *tcph)
{
if (g_sock_state == INIT) {
if (!tcph->syn || tcph->fin || tcph->ack)
g_unexpected++;
else
g_sock_state = SYN_SENT;
return 1;
}
return 0;
}
static int ingress_connect(struct tcphdr *tcph)
{
if (g_sock_state == SYN_SENT) {
if (tcph->fin || !tcph->syn || !tcph->ack)
g_unexpected++;
else
g_sock_state = ESTABLISHED;
return 1;
}
return 0;
}
/* The connection is closed by the peer outside the cgroup. */
static int egress_close_remote(struct tcphdr *tcph)
{
switch (g_sock_state) {
case ESTABLISHED:
break;
case CLOSE_WAIT_SENDING_ACK:
if (tcph->fin || tcph->syn || !tcph->ack)
g_unexpected++;
else
g_sock_state = CLOSE_WAIT;
break;
case CLOSE_WAIT:
if (!tcph->fin)
g_unexpected++;
else
g_sock_state = LAST_ACK;
break;
default:
return 0;
}
return 1;
}
static int ingress_close_remote(struct tcphdr *tcph)
{
switch (g_sock_state) {
case ESTABLISHED:
if (tcph->fin)
g_sock_state = CLOSE_WAIT_SENDING_ACK;
break;
case LAST_ACK:
if (tcph->fin || tcph->syn || !tcph->ack)
g_unexpected++;
else
g_sock_state = CLOSED;
break;
default:
return 0;
}
return 1;
}
/* The connection is closed by the endpoint inside the cgroup. */
static int egress_close_local(struct tcphdr *tcph)
{
switch (g_sock_state) {
case ESTABLISHED:
if (tcph->fin)
g_sock_state = FIN_WAIT1;
break;
case TIME_WAIT_SENDING_ACK:
if (tcph->fin || tcph->syn || !tcph->ack)
g_unexpected++;
else
g_sock_state = TIME_WAIT;
break;
default:
return 0;
}
return 1;
}
static int ingress_close_local(struct tcphdr *tcph)
{
switch (g_sock_state) {
case ESTABLISHED:
break;
case FIN_WAIT1:
if (tcph->fin || tcph->syn || !tcph->ack)
g_unexpected++;
else
g_sock_state = FIN_WAIT2;
break;
case FIN_WAIT2:
if (!tcph->fin || tcph->syn || !tcph->ack)
g_unexpected++;
else
g_sock_state = TIME_WAIT_SENDING_ACK;
break;
default:
return 0;
}
return 1;
}
/* Check the types of outgoing packets of a server socket to make sure they
* are consistent with the state of the server socket.
*
* The connection is closed by the client side.
*/
SEC("cgroup_skb/egress")
int server_egress(struct __sk_buff *skb)
{
struct tcphdr tcph;
if (!needed_tcp_pkt(skb, &tcph))
return 1;
g_packet_count++;
/* Egress of the server socket. */
if (egress_accept(&tcph) || egress_close_remote(&tcph))
return 1;
g_unexpected++;
return 1;
}
/* Check the types of incoming packets of a server socket to make sure they
* are consistent with the state of the server socket.
*
* The connection is closed by the client side.
*/
SEC("cgroup_skb/ingress")
int server_ingress(struct __sk_buff *skb)
{
struct tcphdr tcph;
if (!needed_tcp_pkt(skb, &tcph))
return 1;
g_packet_count++;
/* Ingress of the server socket. */
if (ingress_accept(&tcph) || ingress_close_remote(&tcph))
return 1;
g_unexpected++;
return 1;
}
/* Check the types of outgoing packets of a server socket to make sure they
* are consistent with the state of the server socket.
*
* The connection is closed by the server side.
*/
SEC("cgroup_skb/egress")
int server_egress_srv(struct __sk_buff *skb)
{
struct tcphdr tcph;
if (!needed_tcp_pkt(skb, &tcph))
return 1;
g_packet_count++;
/* Egress of the server socket. */
if (egress_accept(&tcph) || egress_close_local(&tcph))
return 1;
g_unexpected++;
return 1;
}
/* Check the types of incoming packets of a server socket to make sure they
* are consistent with the state of the server socket.
*
* The connection is closed by the server side.
*/
SEC("cgroup_skb/ingress")
int server_ingress_srv(struct __sk_buff *skb)
{
struct tcphdr tcph;
if (!needed_tcp_pkt(skb, &tcph))
return 1;
g_packet_count++;
/* Ingress of the server socket. */
if (ingress_accept(&tcph) || ingress_close_local(&tcph))
return 1;
g_unexpected++;
return 1;
}
/* Check the types of outgoing packets of a client socket to make sure they
* are consistent with the state of the client socket.
*
* The connection is closed by the server side.
*/
SEC("cgroup_skb/egress")
int client_egress_srv(struct __sk_buff *skb)
{
struct tcphdr tcph;
if (!needed_tcp_pkt(skb, &tcph))
return 1;
g_packet_count++;
/* Egress of the server socket. */
if (egress_connect(&tcph) || egress_close_remote(&tcph))
return 1;
g_unexpected++;
return 1;
}
/* Check the types of incoming packets of a client socket to make sure they
* are consistent with the state of the client socket.
*
* The connection is closed by the server side.
*/
SEC("cgroup_skb/ingress")
int client_ingress_srv(struct __sk_buff *skb)
{
struct tcphdr tcph;
if (!needed_tcp_pkt(skb, &tcph))
return 1;
g_packet_count++;
/* Ingress of the server socket. */
if (ingress_connect(&tcph) || ingress_close_remote(&tcph))
return 1;
g_unexpected++;
return 1;
}
/* Check the types of outgoing packets of a client socket to make sure they
* are consistent with the state of the client socket.
*
* The connection is closed by the client side.
*/
SEC("cgroup_skb/egress")
int client_egress(struct __sk_buff *skb)
{
struct tcphdr tcph;
if (!needed_tcp_pkt(skb, &tcph))
return 1;
g_packet_count++;
/* Egress of the server socket. */
if (egress_connect(&tcph) || egress_close_local(&tcph))
return 1;
g_unexpected++;
return 1;
}
/* Check the types of incoming packets of a client socket to make sure they
* are consistent with the state of the client socket.
*
* The connection is closed by the client side.
*/
SEC("cgroup_skb/ingress")
int client_ingress(struct __sk_buff *skb)
{
struct tcphdr tcph;
if (!needed_tcp_pkt(skb, &tcph))
return 1;
g_packet_count++;
/* Ingress of the server socket. */
if (ingress_connect(&tcph) || ingress_close_local(&tcph))
return 1;
g_unexpected++;
return 1;
}