// SPDX-License-Identifier: GPL-2.0// Copyright (c) 2017-2018 Covalent IO, Inc. http://covalent.io#include <stdio.h>#include <stdlib.h>#include <sys/socket.h>#include <sys/ioctl.h>#include <sys/select.h>#include <netinet/in.h>#include <arpa/inet.h>#include <unistd.h>#include <string.h>#include <errno.h>#include <stdbool.h>#include <signal.h>#include <fcntl.h>#include <sys/wait.h>#include <time.h>#include <sched.h> #include <sys/time.h>#include <sys/types.h>#include <sys/sendfile.h> #include <linux/netlink.h>#include <linux/socket.h>#include <linux/sock_diag.h>#include <linux/bpf.h>#include <linux/if_link.h>#include <assert.h>#include <libgen.h> #include <getopt.h> #include <bpf/bpf.h>#include <bpf/libbpf.h> #include "bpf_util.h"#include "cgroup_helpers.h" int running;static void running_handler(int a); /* randomly selected ports for testing on lo */#define S1_PORT 10000#define S2_PORT 10001 #define BPF_SOCKMAP_FILENAME "test_sockmap_kern.bpf.o"#define BPF_SOCKHASH_FILENAME "test_sockhash_kern.bpf.o"#define CG_PATH "/sockmap" #define EDATAINTEGRITY 2001 /* global sockets */int s1, s2, c1, c2, p1, p2;int test_cnt;int passed;int failed;int map_fd[8];struct bpf_map *maps[8];struct bpf_program *progs[8];struct bpf_link *links[8]; int txmsg_pass;int txmsg_redir;int txmsg_drop;int txmsg_apply;int txmsg_cork;int txmsg_start;int txmsg_end;int txmsg_start_push;int txmsg_end_push;int txmsg_start_pop;int txmsg_pop;int txmsg_ingress;int txmsg_redir_skb;int peek_flag;int skb_use_parser;int txmsg_omit_skb_parser;int verify_push_start;int verify_push_len;int verify_pop_start;int verify_pop_len; static const struct option long_options[] = { {"help", no_argument, NULL, 'h' }, {"cgroup", required_argument, NULL, 'c' }, {"rate", required_argument, NULL, 'r' }, {"verbose", optional_argument, NULL, 'v' }, {"iov_count", required_argument, NULL, 'i' }, {"length", required_argument, NULL, 'l' }, {"test", required_argument, NULL, 't' }, {"data_test", no_argument, NULL, 'd' }, {"txmsg", no_argument, &txmsg_pass, 1 }, {"txmsg_redir", no_argument, &txmsg_redir, 1 }, {"txmsg_drop", no_argument, &txmsg_drop, 1 }, {"txmsg_apply", required_argument, NULL, 'a'}, {"txmsg_cork", required_argument, NULL, 'k'}, {"txmsg_start", required_argument, NULL, 's'}, {"txmsg_end", required_argument, NULL, 'e'}, {"txmsg_start_push", required_argument, NULL, 'p'}, {"txmsg_end_push", required_argument, NULL, 'q'}, {"txmsg_start_pop", required_argument, NULL, 'w'}, {"txmsg_pop", required_argument, NULL, 'x'}, {"txmsg_ingress", no_argument, &txmsg_ingress, 1 }, {"txmsg_redir_skb", no_argument, &txmsg_redir_skb, 1 }, {"peek", no_argument, &peek_flag, 1 }, {"txmsg_omit_skb_parser", no_argument, &txmsg_omit_skb_parser, 1}, {"whitelist", required_argument, NULL, 'n' }, {"blacklist", required_argument, NULL, 'b' }, {0, 0, NULL, 0 }}; struct test_env { const char *type; const char *subtest; const char *prepend; int test_num; int subtest_num; int succ_cnt; int fail_cnt; int fail_last;}; struct test_env env; struct sockmap_options { int verbose; bool base; bool sendpage; bool data_test; bool drop_expected; bool check_recved_len; bool tx_wait_mem; int iov_count; int iov_length; int rate; char *map; char *whitelist; char *blacklist; char *prepend;}; struct _test { char *title; void (*tester)(int cg_fd, struct sockmap_options *opt);}; static void test_start(void){ env.subtest_num++;} static void test_fail(void){ env.fail_cnt++;} static void test_pass(void){ env.succ_cnt++;} static void test_reset(void){ txmsg_start = txmsg_end = 0; txmsg_start_pop = txmsg_pop = 0; txmsg_start_push = txmsg_end_push = 0; txmsg_pass = txmsg_drop = txmsg_redir = 0; txmsg_apply = txmsg_cork = 0; txmsg_ingress = txmsg_redir_skb = 0; txmsg_omit_skb_parser = 0; skb_use_parser = 0;} static int test_start_subtest(const struct _test *t, struct sockmap_options *o){ env.type = o->map; env.subtest = t->title; env.prepend = o->prepend; env.test_num++; env.subtest_num = 0; env.fail_last = env.fail_cnt; test_reset(); return 0;} static void test_end_subtest(void){ int error = env.fail_cnt - env.fail_last; int type = strcmp(env.type, BPF_SOCKMAP_FILENAME); if (!error) test_pass(); fprintf(stdout, "#%2d/%2d %8s:%s:%s:%s\n", env.test_num, env.subtest_num, !type ? "sockmap" : "sockhash", env.prepend ? : "", env.subtest, error ? "FAIL" : "OK");} static void test_print_results(void){ fprintf(stdout, "Pass: %d Fail: %d\n", env.succ_cnt, env.fail_cnt);} static void usage(char *argv[]){ int i; printf(" Usage: %s --cgroup <cgroup_path>\n", argv[0]); printf(" options:\n"); for (i = 0; long_options[i].name != 0; i++) { printf(" --%-12s", long_options[i].name); if (long_options[i].flag != NULL) printf(" flag (internal value:%d)\n", *long_options[i].flag); else printf(" -%c\n", long_options[i].val); } printf("\n");} static int sockmap_init_sockets(int verbose){ int i, err, one = 1; struct sockaddr_in addr; int *fds[4] = {&s1, &s2, &c1, &c2}; s1 = s2 = p1 = p2 = c1 = c2 = 0; /* Init sockets */ for (i = 0; i < 4; i++) { *fds[i] = socket(AF_INET, SOCK_STREAM, 0); if (*fds[i] < 0) { perror("socket s1 failed()"); return errno; } } /* Allow reuse */ for (i = 0; i < 2; i++) { err = setsockopt(*fds[i], SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one)); if (err) { perror("setsockopt failed()"); return errno; } } /* Non-blocking sockets */ for (i = 0; i < 2; i++) { err = ioctl(*fds[i], FIONBIO, (char *)&one); if (err < 0) { perror("ioctl s1 failed()"); return errno; } } /* Bind server sockets */ memset(&addr, 0, sizeof(struct sockaddr_in)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr("127.0.0.1"); addr.sin_port = htons(S1_PORT); err = bind(s1, (struct sockaddr *)&addr, sizeof(addr)); if (err < 0) { perror("bind s1 failed()"); return errno; } addr.sin_port = htons(S2_PORT); err = bind(s2, (struct sockaddr *)&addr, sizeof(addr)); if (err < 0) { perror("bind s2 failed()"); return errno; } /* Listen server sockets */ addr.sin_port = htons(S1_PORT); err = listen(s1, 32); if (err < 0) { perror("listen s1 failed()"); return errno; } addr.sin_port = htons(S2_PORT); err = listen(s2, 32); if (err < 0) { perror("listen s1 failed()"); return errno; } /* Initiate Connect */ addr.sin_port = htons(S1_PORT); err = connect(c1, (struct sockaddr *)&addr, sizeof(addr)); if (err < 0 && errno != EINPROGRESS) { perror("connect c1 failed()"); return errno; } addr.sin_port = htons(S2_PORT); err = connect(c2, (struct sockaddr *)&addr, sizeof(addr)); if (err < 0 && errno != EINPROGRESS) { perror("connect c2 failed()"); return errno; } else if (err < 0) { err = 0; } /* Accept Connecrtions */ p1 = accept(s1, NULL, NULL); if (p1 < 0) { perror("accept s1 failed()"); return errno; } p2 = accept(s2, NULL, NULL); if (p2 < 0) { perror("accept s1 failed()"); return errno; } if (verbose > 1) { printf("connected sockets: c1 <-> p1, c2 <-> p2\n"); printf("cgroups binding: c1(%i) <-> s1(%i) - - - c2(%i) <-> s2(%i)\n", c1, s1, c2, s2); } return 0;} struct msg_stats { size_t bytes_sent; size_t bytes_recvd; struct timespec start; struct timespec end;}; static int msg_loop_sendpage(int fd, int iov_length, int cnt, struct msg_stats *s, struct sockmap_options *opt){ bool drop = opt->drop_expected; unsigned char k = 0; int i, j, fp; FILE *file; file = tmpfile(); if (!file) { perror("create file for sendpage"); return 1; } for (i = 0; i < cnt; i++, k = 0) { for (j = 0; j < iov_length; j++, k++) fwrite(&k, sizeof(char), 1, file); } fflush(file); fseek(file, 0, SEEK_SET); fp = fileno(file); clock_gettime(CLOCK_MONOTONIC, &s->start); for (i = 0; i < cnt; i++) { int sent; errno = 0; sent = sendfile(fd, fp, NULL, iov_length); if (!drop && sent < 0) { perror("sendpage loop error"); fclose(file); return sent; } else if (drop && sent >= 0) { printf("sendpage loop error expected: %i errno %i\n", sent, errno); fclose(file); return -EIO; } if (sent > 0) s->bytes_sent += sent; } clock_gettime(CLOCK_MONOTONIC, &s->end); fclose(file); return 0;} static void msg_free_iov(struct msghdr *msg){ int i; for (i = 0; i < msg->msg_iovlen; i++) free(msg->msg_iov[i].iov_base); free(msg->msg_iov); msg->msg_iov = NULL; msg->msg_iovlen = 0;} static int msg_alloc_iov(struct msghdr *msg, int iov_count, int iov_length, bool data, bool xmit){ unsigned char k = 0; struct iovec *iov; int i; iov = calloc(iov_count, sizeof(struct iovec)); if (!iov) return errno; for (i = 0; i < iov_count; i++) { unsigned char *d = calloc(iov_length, sizeof(char)); if (!d) { fprintf(stderr, "iov_count %i/%i OOM\n", i, iov_count); goto unwind_iov; } iov[i].iov_base = d; iov[i].iov_len = iov_length; if (data && xmit) { int j; for (j = 0; j < iov_length; j++) d[j] = k++; } } msg->msg_iov = iov; msg->msg_iovlen = iov_count; return 0;unwind_iov: for (i--; i >= 0 ; i--) free(iov[i].iov_base); free(iov); return -ENOMEM;} /* In push or pop test, we need to do some calculations for msg_verify_data */static void msg_verify_date_prep(void){ int push_range_end = txmsg_start_push + txmsg_end_push - 1; int pop_range_end = txmsg_start_pop + txmsg_pop - 1; if (txmsg_end_push && txmsg_pop && txmsg_start_push <= pop_range_end && txmsg_start_pop <= push_range_end) { /* The push range and the pop range overlap */ int overlap_len; verify_push_start = txmsg_start_push; verify_pop_start = txmsg_start_pop; if (txmsg_start_push < txmsg_start_pop) overlap_len = min(push_range_end - txmsg_start_pop + 1, txmsg_pop); else overlap_len = min(pop_range_end - txmsg_start_push + 1, txmsg_end_push); verify_push_len = max(txmsg_end_push - overlap_len, 0); verify_pop_len = max(txmsg_pop - overlap_len, 0); } else { /* Otherwise */ verify_push_start = txmsg_start_push; verify_pop_start = txmsg_start_pop; verify_push_len = txmsg_end_push; verify_pop_len = txmsg_pop; }} static int msg_verify_data(struct msghdr *msg, int size, int chunk_sz, unsigned char *k_p, int *bytes_cnt_p, int *check_cnt_p, int *push_p){ int bytes_cnt = *bytes_cnt_p, check_cnt = *check_cnt_p, push = *push_p; unsigned char k = *k_p; int i, j; for (i = 0, j = 0; i < msg->msg_iovlen && size; i++, j = 0) { unsigned char *d = msg->msg_iov[i].iov_base; for (; j < msg->msg_iov[i].iov_len && size; j++) { if (push > 0 && check_cnt == verify_push_start + verify_push_len - push) { int skipped;revisit_push: skipped = push; if (j + push >= msg->msg_iov[i].iov_len) skipped = msg->msg_iov[i].iov_len - j; push -= skipped; size -= skipped; j += skipped - 1; check_cnt += skipped; continue; } if (verify_pop_len > 0 && check_cnt == verify_pop_start) { bytes_cnt += verify_pop_len; check_cnt += verify_pop_len; k += verify_pop_len;Lines 1–500 of 2022. Load more
// SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2017-2018 Covalent IO, Inc. http://covalent.io #include <stdio.h> #include <stdlib.h> #include <sys/socket.h> #include <sys/ioctl.h> #include <sys/select.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <string.h> #include <errno.h> #include <stdbool.h> #include <signal.h> #include <fcntl.h> #include <sys/wait.h> #include <time.h> #include <sched.h> #include <sys/time.h> #include <sys/types.h> #include <sys/sendfile.h> #include <linux/netlink.h> #include <linux/socket.h> #include <linux/sock_diag.h> #include <linux/bpf.h> #include <linux/if_link.h> #include <assert.h> #include <libgen.h> #include <getopt.h> #include <bpf/bpf.h> #include <bpf/libbpf.h> #include "bpf_util.h" #include "cgroup_helpers.h" int running; static void running_handler(int a); /* randomly selected ports for testing on lo */ #define S1_PORT 10000 #define S2_PORT 10001 #define BPF_SOCKMAP_FILENAME "test_sockmap_kern.bpf.o" #define BPF_SOCKHASH_FILENAME "test_sockhash_kern.bpf.o" #define CG_PATH "/sockmap" #define EDATAINTEGRITY 2001 /* global sockets */ int s1, s2, c1, c2, p1, p2; int test_cnt; int passed; int failed; int map_fd[8]; struct bpf_map *maps[8]; struct bpf_program *progs[8]; struct bpf_link *links[8]; int txmsg_pass; int txmsg_redir; int txmsg_drop; int txmsg_apply; int txmsg_cork; int txmsg_start; int txmsg_end; int txmsg_start_push; int txmsg_end_push; int txmsg_start_pop; int txmsg_pop; int txmsg_ingress; int txmsg_redir_skb; int peek_flag; int skb_use_parser; int txmsg_omit_skb_parser; int verify_push_start; int verify_push_len; int verify_pop_start; int verify_pop_len; static const struct option long_options[] = { {"help", no_argument, NULL, 'h' }, {"cgroup", required_argument, NULL, 'c' }, {"rate", required_argument, NULL, 'r' }, {"verbose", optional_argument, NULL, 'v' }, {"iov_count", required_argument, NULL, 'i' }, {"length", required_argument, NULL, 'l' }, {"test", required_argument, NULL, 't' }, {"data_test", no_argument, NULL, 'd' }, {"txmsg", no_argument, &txmsg_pass, 1 }, {"txmsg_redir", no_argument, &txmsg_redir, 1 }, {"txmsg_drop", no_argument, &txmsg_drop, 1 }, {"txmsg_apply", required_argument, NULL, 'a'}, {"txmsg_cork", required_argument, NULL, 'k'}, {"txmsg_start", required_argument, NULL, 's'}, {"txmsg_end", required_argument, NULL, 'e'}, {"txmsg_start_push", required_argument, NULL, 'p'}, {"txmsg_end_push", required_argument, NULL, 'q'}, {"txmsg_start_pop", required_argument, NULL, 'w'}, {"txmsg_pop", required_argument, NULL, 'x'}, {"txmsg_ingress", no_argument, &txmsg_ingress, 1 }, {"txmsg_redir_skb", no_argument, &txmsg_redir_skb, 1 }, {"peek", no_argument, &peek_flag, 1 }, {"txmsg_omit_skb_parser", no_argument, &txmsg_omit_skb_parser, 1}, {"whitelist", required_argument, NULL, 'n' }, {"blacklist", required_argument, NULL, 'b' }, {0, 0, NULL, 0 } }; struct test_env { const char *type; const char *subtest; const char *prepend; int test_num; int subtest_num; int succ_cnt; int fail_cnt; int fail_last; }; struct test_env env; struct sockmap_options { int verbose; bool base; bool sendpage; bool data_test; bool drop_expected; bool check_recved_len; bool tx_wait_mem; int iov_count; int iov_length; int rate; char *map; char *whitelist; char *blacklist; char *prepend; }; struct _test { char *title; void (*tester)(int cg_fd, struct sockmap_options *opt); }; static void test_start(void) { env.subtest_num++; } static void test_fail(void) { env.fail_cnt++; } static void test_pass(void) { env.succ_cnt++; } static void test_reset(void) { txmsg_start = txmsg_end = 0; txmsg_start_pop = txmsg_pop = 0; txmsg_start_push = txmsg_end_push = 0; txmsg_pass = txmsg_drop = txmsg_redir = 0; txmsg_apply = txmsg_cork = 0; txmsg_ingress = txmsg_redir_skb = 0; txmsg_omit_skb_parser = 0; skb_use_parser = 0; } static int test_start_subtest(const struct _test *t, struct sockmap_options *o) { env.type = o->map; env.subtest = t->title; env.prepend = o->prepend; env.test_num++; env.subtest_num = 0; env.fail_last = env.fail_cnt; test_reset(); return 0; } static void test_end_subtest(void) { int error = env.fail_cnt - env.fail_last; int type = strcmp(env.type, BPF_SOCKMAP_FILENAME); if (!error) test_pass(); fprintf(stdout, "#%2d/%2d %8s:%s:%s:%s\n", env.test_num, env.subtest_num, !type ? "sockmap" : "sockhash", env.prepend ? : "", env.subtest, error ? "FAIL" : "OK"); } static void test_print_results(void) { fprintf(stdout, "Pass: %d Fail: %d\n", env.succ_cnt, env.fail_cnt); } static void usage(char *argv[]) { int i; printf(" Usage: %s --cgroup <cgroup_path>\n", argv[0]); printf(" options:\n"); for (i = 0; long_options[i].name != 0; i++) { printf(" --%-12s", long_options[i].name); if (long_options[i].flag != NULL) printf(" flag (internal value:%d)\n", *long_options[i].flag); else printf(" -%c\n", long_options[i].val); } printf("\n"); } static int sockmap_init_sockets(int verbose) { int i, err, one = 1; struct sockaddr_in addr; int *fds[4] = {&s1, &s2, &c1, &c2}; s1 = s2 = p1 = p2 = c1 = c2 = 0; /* Init sockets */ for (i = 0; i < 4; i++) { *fds[i] = socket(AF_INET, SOCK_STREAM, 0); if (*fds[i] < 0) { perror("socket s1 failed()"); return errno; } } /* Allow reuse */ for (i = 0; i < 2; i++) { err = setsockopt(*fds[i], SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one)); if (err) { perror("setsockopt failed()"); return errno; } } /* Non-blocking sockets */ for (i = 0; i < 2; i++) { err = ioctl(*fds[i], FIONBIO, (char *)&one); if (err < 0) { perror("ioctl s1 failed()"); return errno; } } /* Bind server sockets */ memset(&addr, 0, sizeof(struct sockaddr_in)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr("127.0.0.1"); addr.sin_port = htons(S1_PORT); err = bind(s1, (struct sockaddr *)&addr, sizeof(addr)); if (err < 0) { perror("bind s1 failed()"); return errno; } addr.sin_port = htons(S2_PORT); err = bind(s2, (struct sockaddr *)&addr, sizeof(addr)); if (err < 0) { perror("bind s2 failed()"); return errno; } /* Listen server sockets */ addr.sin_port = htons(S1_PORT); err = listen(s1, 32); if (err < 0) { perror("listen s1 failed()"); return errno; } addr.sin_port = htons(S2_PORT); err = listen(s2, 32); if (err < 0) { perror("listen s1 failed()"); return errno; } /* Initiate Connect */ addr.sin_port = htons(S1_PORT); err = connect(c1, (struct sockaddr *)&addr, sizeof(addr)); if (err < 0 && errno != EINPROGRESS) { perror("connect c1 failed()"); return errno; } addr.sin_port = htons(S2_PORT); err = connect(c2, (struct sockaddr *)&addr, sizeof(addr)); if (err < 0 && errno != EINPROGRESS) { perror("connect c2 failed()"); return errno; } else if (err < 0) { err = 0; } /* Accept Connecrtions */ p1 = accept(s1, NULL, NULL); if (p1 < 0) { perror("accept s1 failed()"); return errno; } p2 = accept(s2, NULL, NULL); if (p2 < 0) { perror("accept s1 failed()"); return errno; } if (verbose > 1) { printf("connected sockets: c1 <-> p1, c2 <-> p2\n"); printf("cgroups binding: c1(%i) <-> s1(%i) - - - c2(%i) <-> s2(%i)\n", c1, s1, c2, s2); } return 0; } struct msg_stats { size_t bytes_sent; size_t bytes_recvd; struct timespec start; struct timespec end; }; static int msg_loop_sendpage(int fd, int iov_length, int cnt, struct msg_stats *s, struct sockmap_options *opt) { bool drop = opt->drop_expected; unsigned char k = 0; int i, j, fp; FILE *file; file = tmpfile(); if (!file) { perror("create file for sendpage"); return 1; } for (i = 0; i < cnt; i++, k = 0) { for (j = 0; j < iov_length; j++, k++) fwrite(&k, sizeof(char), 1, file); } fflush(file); fseek(file, 0, SEEK_SET); fp = fileno(file); clock_gettime(CLOCK_MONOTONIC, &s->start); for (i = 0; i < cnt; i++) { int sent; errno = 0; sent = sendfile(fd, fp, NULL, iov_length); if (!drop && sent < 0) { perror("sendpage loop error"); fclose(file); return sent; } else if (drop && sent >= 0) { printf("sendpage loop error expected: %i errno %i\n", sent, errno); fclose(file); return -EIO; } if (sent > 0) s->bytes_sent += sent; } clock_gettime(CLOCK_MONOTONIC, &s->end); fclose(file); return 0; } static void msg_free_iov(struct msghdr *msg) { int i; for (i = 0; i < msg->msg_iovlen; i++) free(msg->msg_iov[i].iov_base); free(msg->msg_iov); msg->msg_iov = NULL; msg->msg_iovlen = 0; } static int msg_alloc_iov(struct msghdr *msg, int iov_count, int iov_length, bool data, bool xmit) { unsigned char k = 0; struct iovec *iov; int i; iov = calloc(iov_count, sizeof(struct iovec)); if (!iov) return errno; for (i = 0; i < iov_count; i++) { unsigned char *d = calloc(iov_length, sizeof(char)); if (!d) { fprintf(stderr, "iov_count %i/%i OOM\n", i, iov_count); goto unwind_iov; } iov[i].iov_base = d; iov[i].iov_len = iov_length; if (data && xmit) { int j; for (j = 0; j < iov_length; j++) d[j] = k++; } } msg->msg_iov = iov; msg->msg_iovlen = iov_count; return 0; unwind_iov: for (i--; i >= 0 ; i--) free(iov[i].iov_base); free(iov); return -ENOMEM; } /* In push or pop test, we need to do some calculations for msg_verify_data */ static void msg_verify_date_prep(void) { int push_range_end = txmsg_start_push + txmsg_end_push - 1; int pop_range_end = txmsg_start_pop + txmsg_pop - 1; if (txmsg_end_push && txmsg_pop && txmsg_start_push <= pop_range_end && txmsg_start_pop <= push_range_end) { /* The push range and the pop range overlap */ int overlap_len; verify_push_start = txmsg_start_push; verify_pop_start = txmsg_start_pop; if (txmsg_start_push < txmsg_start_pop) overlap_len = min(push_range_end - txmsg_start_pop + 1, txmsg_pop); else overlap_len = min(pop_range_end - txmsg_start_push + 1, txmsg_end_push); verify_push_len = max(txmsg_end_push - overlap_len, 0); verify_pop_len = max(txmsg_pop - overlap_len, 0); } else { /* Otherwise */ verify_push_start = txmsg_start_push; verify_pop_start = txmsg_start_pop; verify_push_len = txmsg_end_push; verify_pop_len = txmsg_pop; } } static int msg_verify_data(struct msghdr *msg, int size, int chunk_sz, unsigned char *k_p, int *bytes_cnt_p, int *check_cnt_p, int *push_p) { int bytes_cnt = *bytes_cnt_p, check_cnt = *check_cnt_p, push = *push_p; unsigned char k = *k_p; int i, j; for (i = 0, j = 0; i < msg->msg_iovlen && size; i++, j = 0) { unsigned char *d = msg->msg_iov[i].iov_base; for (; j < msg->msg_iov[i].iov_len && size; j++) { if (push > 0 && check_cnt == verify_push_start + verify_push_len - push) { int skipped; revisit_push: skipped = push; if (j + push >= msg->msg_iov[i].iov_len) skipped = msg->msg_iov[i].iov_len - j; push -= skipped; size -= skipped; j += skipped - 1; check_cnt += skipped; continue; } if (verify_pop_len > 0 && check_cnt == verify_pop_start) { bytes_cnt += verify_pop_len; check_cnt += verify_pop_len; k += verify_pop_len;