Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  sockmap_strp.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0
#include <error.h>
#include <netinet/tcp.h>
#include <test_progs.h>
#include "sockmap_helpers.h"
#include "test_skmsg_load_helpers.skel.h"
#include "test_sockmap_strp.skel.h"

#define STRP_PKT_HEAD_LEN 4
#define STRP_PKT_BODY_LEN 6
#define STRP_PKT_FULL_LEN (STRP_PKT_HEAD_LEN + STRP_PKT_BODY_LEN)

static const char packet[STRP_PKT_FULL_LEN] = "head+body\0";
static const int test_packet_num = 100;

/* 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.
 */

static int 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;
}

static struct 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;

 strp = test_sockmap_strp__open_and_load();
 *out_map = bpf_map__fd(strp->maps.sock_map);

 if (need_parser)
  parser = bpf_program__fd(strp->progs.prog_skb_parser_partial);
 else
  parser = bpf_program__fd(strp->progs.prog_skb_parser);

 if (pass)
  verdict = bpf_program__fd(strp->progs.prog_skb_verdict_pass);
 else
  verdict = bpf_program__fd(strp->progs.prog_skb_verdict);

 err = bpf_prog_attach(parser, *out_map, BPF_SK_SKB_STREAM_PARSER, 0);
 if (!ASSERT_OK(err, "bpf_prog_attach stream parser"))
  goto err;

 err = bpf_prog_attach(verdict, *out_map, BPF_SK_SKB_STREAM_VERDICT, 0);
 if (!ASSERT_OK(err, "bpf_prog_attach stream verdict"))
  goto err;

 return strp;
err:
 test_sockmap_strp__destroy(strp);
 return NULL;
}

/* Dispatch packets to different socket by packet size:
 *
 *                      ------  ------
 *                     | pkt4 || pkt1 |... > remote socket
 *  ------ ------     / ------  ------
 * | pkt8 | pkt7 |...
 *  ------ ------     \ ------  ------
 *                     | pkt3 || pkt2 |... > local socket
 *                      ------  ------
 */

static void test_sockmap_strp_dispatch_pkt(int family, int sotype)
{
 int i, j, zero = 0, one = 1, recvd;
 int err, map;
 int c0 = -1, p0 = -1, c1 = -1, p1 = -1;
 struct test_sockmap_strp *strp = NULL;
 int test_cnt = 6;
 char rcv[10];
 struct {
  char data[7];
  int data_len;
  int send_cnt;
  int *receiver;
 } send_dir[2] = {
  /* data expected to deliver to local */
  {"llllll", 6, 0, &p0},
  /* data expected to deliver to remote */
  {"rrrrr",  5, 0, &c1}
 };

 strp = sockmap_strp_init(&map, falsefalse);
 if (!ASSERT_TRUE(strp, "sockmap_strp_init"))
  return;

 err = create_socket_pairs(family, sotype, &c0, &c1, &p0, &p1);
 if (!ASSERT_OK(err, "create_socket_pairs()"))
  goto out;

 err = bpf_map_update_elem(map, &zero, &p0, BPF_NOEXIST);
 if (!ASSERT_OK(err, "bpf_map_update_elem(p0)"))
  goto out_close;

 err = bpf_map_update_elem(map, &one, &p1, BPF_NOEXIST);
 if (!ASSERT_OK(err, "bpf_map_update_elem(p1)"))
  goto out_close;

 err = setsockopt(c1, IPPROTO_TCP, TCP_NODELAY, &zero, sizeof(zero));
 if (!ASSERT_OK(err, "setsockopt(TCP_NODELAY)"))
  goto out_close;

 /* deliver data with data size greater than 5 to local */
 strp->data->verdict_max_size = 5;

 for (i = 0; i < test_cnt; i++) {
  int d = i % 2;

  xsend(c0, send_dir[d].data, send_dir[d].data_len, 0);
  send_dir[d].send_cnt++;
 }

 for (i = 0; i < 2; i++) {
  for (j = 0; j < send_dir[i].send_cnt; j++) {
   int expected = send_dir[i].data_len;

   recvd = recv_timeout(*send_dir[i].receiver, rcv,
          expected, MSG_DONTWAIT,
          IO_TIMEOUT_SEC);
   if (!ASSERT_EQ(recvd, expected, "recv_timeout()"))
    goto out_close;
   if (!ASSERT_OK(memcmp(send_dir[i].data, rcv, recvd),
           "data mismatch"))
    goto out_close;
  }
 }
out_close:
 close(c0);
 close(c1);
 close(p0);
 close(p1);
out:
 test_sockmap_strp__destroy(strp);
}

/* We have multiple packets in one skb
 * ------------ ------------ ------------
 * |  packet1  |   packet2  |  ...
 * ------------ ------------ ------------
 */

static void 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, truetrue);
 if (!ASSERT_TRUE(strp, "sockmap_strp_init"))
  return;

 err = create_pair(family, sotype, &c, &p);
 if (err)
  goto out;

 err = bpf_map_update_elem(map, &zero, &p, BPF_NOEXIST);
 if (!ASSERT_OK(err, "bpf_map_update_elem(zero, p)"))
  goto out_close;

 /* 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 */
static void 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, truetrue);
 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 */
static void 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, truetrue);
 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;

  recvd = recv_timeout(p, rcv, sizeof(rcv), MSG_DONTWAIT,
         IO_TIMEOUT_SEC);
  if (!ASSERT_EQ(recvd, pkt_size, "recv_timeout(p)") ||
      !ASSERT_OK(memcmp(packet, rcv, pkt_size),
      "memcmp, data mismatch"))
   goto out_close;
 }

 if (fionread) {
  sent = xsend(c, packet, pkt_size, 0);
  if (!ASSERT_EQ(sent, pkt_size, "second xsend(c)"))
   goto out_close;

  err = ioctl(p, FIONREAD, &avail);
  if (!ASSERT_OK(err, "ioctl(FIONREAD) error") ||
      !ASSERT_EQ(avail, pkt_size, "ioctl(FIONREAD)"))
   goto out_close;

  recvd = recv_timeout(p, rcv, sizeof(rcv), MSG_DONTWAIT,
         IO_TIMEOUT_SEC);
  if (!ASSERT_EQ(recvd, pkt_size, "second recv_timeout(p)") ||
      !ASSERT_OK(memcmp(packet, rcv, pkt_size),
         "second memcmp, data mismatch"))
   goto out_close;
 }

out_close:
 close(c);
 close(p);

out:
 test_sockmap_strp__destroy(strp);
}

/* Test strparser with verdict mode */
static void 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, falsetrue);
 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;

 err = bpf_map_update_elem(map, &zero, &p0, BPF_NOEXIST);
 if (!ASSERT_OK(err, "bpf_map_update_elem(p0)"))
  goto out_close;

 err = bpf_map_update_elem(map, &one, &p1, BPF_NOEXIST);
 if (!ASSERT_OK(err, "bpf_map_update_elem(p1)"))
  goto out_close;

 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;

 /* partial read */
 off = STRP_PKT_FULL_LEN / 2;
 recvd = recv_timeout(c1, rcv, off, MSG_DONTWAIT,
        IO_TIMEOUT_SEC);
 recvd += recv_timeout(c1, rcv + off, sizeof(rcv) - off, MSG_DONTWAIT,
         IO_TIMEOUT_SEC);

 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;

out_close:
 close(c0);
 close(c1);
 close(p0);
 close(p1);
out:
 test_sockmap_strp__destroy(strp);
}

void test_sockmap_strp(void)
{
 if (test__start_subtest("sockmap strp tcp pass"))
  test_sockmap_strp_pass(AF_INET, SOCK_STREAM, false);
 if (test__start_subtest("sockmap strp tcp v6 pass"))
  test_sockmap_strp_pass(AF_INET6, SOCK_STREAM, false);
 if (test__start_subtest("sockmap strp tcp pass fionread"))
  test_sockmap_strp_pass(AF_INET, SOCK_STREAM, true);
 if (test__start_subtest("sockmap strp tcp v6 pass fionread"))
  test_sockmap_strp_pass(AF_INET6, SOCK_STREAM, true);
 if (test__start_subtest("sockmap strp tcp verdict"))
  test_sockmap_strp_verdict(AF_INET, SOCK_STREAM);
 if (test__start_subtest("sockmap strp tcp v6 verdict"))
  test_sockmap_strp_verdict(AF_INET6, SOCK_STREAM);
 if (test__start_subtest("sockmap strp tcp partial read"))
  test_sockmap_strp_partial_read(AF_INET, SOCK_STREAM);
 if (test__start_subtest("sockmap strp tcp multiple packets"))
  test_sockmap_strp_multiple_pkt(AF_INET, SOCK_STREAM);
 if (test__start_subtest("sockmap strp tcp dispatch"))
  test_sockmap_strp_dispatch_pkt(AF_INET, SOCK_STREAM);
}

Messung V0.5
C=90 H=86 G=87

¤ Dauer der Verarbeitung: 0.5 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

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.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge