Commit 539c7e67 authored by Kui-Feng Lee's avatar Kui-Feng Lee Committed by Daniel Borkmann

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: default avatarKui-Feng Lee <kuifeng@meta.com>
Signed-off-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
Link: https://lore.kernel.org/bpf/20230624014600.576756-3-kuifeng@meta.com
parent 223f5f79
...@@ -277,6 +277,18 @@ int join_cgroup(const char *relative_path) ...@@ -277,6 +277,18 @@ int join_cgroup(const char *relative_path)
return join_cgroup_from_top(cgroup_path); return join_cgroup_from_top(cgroup_path);
} }
/**
* join_root_cgroup() - Join the root cgroup
*
* This function joins the root cgroup.
*
* On success, it returns 0, otherwise on failure it returns 1.
*/
int join_root_cgroup(void)
{
return join_cgroup_from_top(CGROUP_MOUNT_PATH);
}
/** /**
* join_parent_cgroup() - Join a cgroup in the parent process workdir * join_parent_cgroup() - Join a cgroup in the parent process workdir
* @relative_path: The cgroup path, relative to parent process workdir, to join * @relative_path: The cgroup path, relative to parent process workdir, to join
......
...@@ -22,6 +22,7 @@ void remove_cgroup(const char *relative_path); ...@@ -22,6 +22,7 @@ void remove_cgroup(const char *relative_path);
unsigned long long get_cgroup_id(const char *relative_path); unsigned long long get_cgroup_id(const char *relative_path);
int join_cgroup(const char *relative_path); int join_cgroup(const char *relative_path);
int join_root_cgroup(void);
int join_parent_cgroup(const char *relative_path); int join_parent_cgroup(const char *relative_path);
int setup_cgroup_environment(void); int setup_cgroup_environment(void);
......
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */
/* Define states of a socket to tracking messages sending to and from the
* socket.
*
* These states are based on rfc9293 with some modifications to support
* tracking of messages sent out from a socket. For example, when a SYN is
* received, a new socket is transiting to the SYN_RECV state defined in
* rfc9293. But, we put it in SYN_RECV_SENDING_SYN_ACK state and when
* SYN-ACK is sent out, it moves to SYN_RECV state. With this modification,
* we can track the message sent out from a socket.
*/
#ifndef __CGROUP_TCP_SKB_H__
#define __CGROUP_TCP_SKB_H__
enum {
INIT,
CLOSED,
SYN_SENT,
SYN_RECV_SENDING_SYN_ACK,
SYN_RECV,
ESTABLISHED,
FIN_WAIT1,
FIN_WAIT2,
CLOSE_WAIT_SENDING_ACK,
CLOSE_WAIT,
CLOSING,
LAST_ACK,
TIME_WAIT_SENDING_ACK,
TIME_WAIT,
};
#endif /* __CGROUP_TCP_SKB_H__ */
This diff is collapsed.
// 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;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment