/* Current implementation of tcp_bpf_recvmsg_parser() invokes data_ready * with sk held if an skb exists in sk_receive_queue. Then for the * data_ready implementation of strparser, it will delay the read * operation if sk is held and EAGAIN is returned.
*/ staticint sockmap_strp_consume_pre_data(int p)
{ int recvd; bool retried = false; char rcv[10];
retry:
errno = 0;
recvd = recv_timeout(p, rcv, sizeof(rcv), 0, 1); if (recvd < 0 && errno == EAGAIN && retried == false) { /* On the first call, EAGAIN will certainly be returned. * A 1-second wait is enough for the workqueue to finish.
*/
sleep(1);
retried = true; goto retry;
}
if (!ASSERT_EQ(recvd, STRP_PKT_FULL_LEN, "recv error or truncated data") ||
!ASSERT_OK(memcmp(packet, rcv, STRP_PKT_FULL_LEN), "data mismatch")) return -1; return 0;
}
staticstruct test_sockmap_strp *sockmap_strp_init(int *out_map, bool pass, bool need_parser)
{ struct test_sockmap_strp *strp = NULL; int verdict, parser; int err;
/* We have multiple packets in one skb * ------------ ------------ ------------ * | packet1 | packet2 | ... * ------------ ------------ ------------
*/ staticvoid test_sockmap_strp_multiple_pkt(int family, int sotype)
{ int i, zero = 0; int sent, recvd, total; int err, map; int c = -1, p = -1; struct test_sockmap_strp *strp = NULL; char *snd = NULL, *rcv = NULL;
strp = sockmap_strp_init(&map, true, true); if (!ASSERT_TRUE(strp, "sockmap_strp_init")) return;
err = create_pair(family, sotype, &c, &p); if (err) goto out;
/* construct multiple packets in one buffer */
total = test_packet_num * STRP_PKT_FULL_LEN;
snd = malloc(total);
rcv = malloc(total + 1); if (!ASSERT_TRUE(snd, "malloc(snd)") ||
!ASSERT_TRUE(rcv, "malloc(rcv)")) goto out_close;
for (i = 0; i < test_packet_num; i++) {
memcpy(snd + i * STRP_PKT_FULL_LEN,
packet, STRP_PKT_FULL_LEN);
}
sent = xsend(c, snd, total, 0); if (!ASSERT_EQ(sent, total, "xsend(c)")) goto out_close;
/* try to recv one more byte to avoid truncation check */
recvd = recv_timeout(p, rcv, total + 1, MSG_DONTWAIT, IO_TIMEOUT_SEC); if (!ASSERT_EQ(recvd, total, "recv(rcv)")) goto out_close;
/* we sent TCP segment with multiple encapsulation * then check whether packets are handled correctly
*/ if (!ASSERT_OK(memcmp(snd, rcv, total), "data mismatch")) goto out_close;
out_close:
close(c);
close(p); if (snd)
free(snd); if (rcv)
free(rcv);
out:
test_sockmap_strp__destroy(strp);
}
/* Test strparser with partial read */ staticvoid test_sockmap_strp_partial_read(int family, int sotype)
{ int zero = 0, recvd, off; int err, map; int c = -1, p = -1; struct test_sockmap_strp *strp = NULL; char rcv[STRP_PKT_FULL_LEN + 1] = "0";
strp = sockmap_strp_init(&map, true, true); if (!ASSERT_TRUE(strp, "sockmap_strp_init")) return;
err = create_pair(family, sotype, &c, &p); if (err) goto out;
/* sk_data_ready of 'p' will be replaced by strparser handler */
err = bpf_map_update_elem(map, &zero, &p, BPF_NOEXIST); if (!ASSERT_OK(err, "bpf_map_update_elem(zero, p)")) goto out_close;
/* 1.1 send partial head, 1 byte header left */
off = STRP_PKT_HEAD_LEN - 1;
xsend(c, packet, off, 0);
recvd = recv_timeout(p, rcv, sizeof(rcv), MSG_DONTWAIT, 1); if (!ASSERT_EQ(-1, recvd, "partial head sent, expected no data")) goto out_close;
/* 1.2 send remaining head and body */
xsend(c, packet + off, STRP_PKT_FULL_LEN - off, 0);
recvd = recv_timeout(p, rcv, sizeof(rcv), MSG_DONTWAIT, IO_TIMEOUT_SEC); if (!ASSERT_EQ(recvd, STRP_PKT_FULL_LEN, "expected full data")) goto out_close;
/* 2.1 send partial head, 1 byte header left */
off = STRP_PKT_HEAD_LEN - 1;
xsend(c, packet, off, 0);
/* 2.2 send remaining head and partial body, 1 byte body left */
xsend(c, packet + off, STRP_PKT_FULL_LEN - off - 1, 0);
off = STRP_PKT_FULL_LEN - 1;
recvd = recv_timeout(p, rcv, sizeof(rcv), MSG_DONTWAIT, 1); if (!ASSERT_EQ(-1, recvd, "partial body sent, expected no data")) goto out_close;
/* 2.3 send remaining body */
xsend(c, packet + off, STRP_PKT_FULL_LEN - off, 0);
recvd = recv_timeout(p, rcv, sizeof(rcv), MSG_DONTWAIT, IO_TIMEOUT_SEC); if (!ASSERT_EQ(recvd, STRP_PKT_FULL_LEN, "expected full data")) goto out_close;
out_close:
close(c);
close(p);
out:
test_sockmap_strp__destroy(strp);
}
/* Test simple socket read/write with strparser + FIONREAD */ staticvoid test_sockmap_strp_pass(int family, int sotype, bool fionread)
{ int zero = 0, pkt_size = STRP_PKT_FULL_LEN, sent, recvd, avail; int err, map; int c = -1, p = -1; int test_cnt = 10, i; struct test_sockmap_strp *strp = NULL; char rcv[STRP_PKT_FULL_LEN + 1] = "0";
strp = sockmap_strp_init(&map, true, true); if (!ASSERT_TRUE(strp, "sockmap_strp_init")) return;
err = create_pair(family, sotype, &c, &p); if (err) goto out;
/* inject some data before bpf process, it should be read * correctly because we check sk_receive_queue in * tcp_bpf_recvmsg_parser().
*/
sent = xsend(c, packet, pkt_size, 0); if (!ASSERT_EQ(sent, pkt_size, "xsend(pre-data)")) goto out_close;
/* sk_data_ready of 'p' will be replaced by strparser handler */
err = bpf_map_update_elem(map, &zero, &p, BPF_NOEXIST); if (!ASSERT_OK(err, "bpf_map_update_elem(p)")) goto out_close;
/* consume previous data we injected */ if (sockmap_strp_consume_pre_data(p)) goto out_close;
/* Previously, we encountered issues such as deadlocks and * sequence errors that resulted in the inability to read * continuously. Therefore, we perform multiple iterations * of testing here.
*/ for (i = 0; i < test_cnt; i++) {
sent = xsend(c, packet, pkt_size, 0); if (!ASSERT_EQ(sent, pkt_size, "xsend(c)")) goto out_close;
/* Test strparser with verdict mode */ staticvoid test_sockmap_strp_verdict(int family, int sotype)
{ int zero = 0, one = 1, sent, recvd, off; int err, map; int c0 = -1, p0 = -1, c1 = -1, p1 = -1; struct test_sockmap_strp *strp = NULL; char rcv[STRP_PKT_FULL_LEN + 1] = "0";
strp = sockmap_strp_init(&map, false, true); if (!ASSERT_TRUE(strp, "sockmap_strp_init")) return;
/* We simulate a reverse proxy server. * When p0 receives data from c0, we forward it to c1. * From c1's perspective, it will consider this data * as being sent by p1.
*/
err = create_socket_pairs(family, sotype, &c0, &c1, &p0, &p1); if (!ASSERT_OK(err, "create_socket_pairs()")) goto out;
sent = xsend(c0, packet, STRP_PKT_FULL_LEN, 0); if (!ASSERT_EQ(sent, STRP_PKT_FULL_LEN, "xsend(c0)")) goto out_close;
recvd = recv_timeout(c1, rcv, sizeof(rcv), MSG_DONTWAIT,
IO_TIMEOUT_SEC); if (!ASSERT_EQ(recvd, STRP_PKT_FULL_LEN, "recv_timeout(c1)") ||
!ASSERT_OK(memcmp(packet, rcv, STRP_PKT_FULL_LEN), "received data does not match the sent data")) goto out_close;
/* send again to ensure the stream is functioning correctly. */
sent = xsend(c0, packet, STRP_PKT_FULL_LEN, 0); if (!ASSERT_EQ(sent, STRP_PKT_FULL_LEN, "second xsend(c0)")) goto out_close;
if (!ASSERT_EQ(recvd, STRP_PKT_FULL_LEN, "partial recv_timeout(c1)") ||
!ASSERT_OK(memcmp(packet, rcv, STRP_PKT_FULL_LEN), "partial received data does not match the sent data")) goto out_close;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.