| #include "tests.h" |
| #include <stdio.h> |
| #include <stddef.h> |
| #include <sys/socket.h> |
| #include <linux/if_packet.h> |
| #include "print_fields.h" |
| |
| static const char *errstr; |
| |
| struct tp_stats { |
| unsigned int tp_packets, tp_drops, tp_freeze_q_cnt; |
| }; |
| |
| static long |
| get_tpacket_stats(void *optval, socklen_t *len) |
| { |
| struct tp_stats *tpstats = optval; |
| socklen_t optlen = *len; |
| long rc = getsockopt(-1, SOL_PACKET, PACKET_STATISTICS, tpstats, len); |
| errstr = sprintrc(rc); |
| #ifdef INJECT_RETVAL |
| if (rc != INJECT_RETVAL) |
| error_msg_and_fail("Got a return value of %ld != %d", |
| rc, INJECT_RETVAL); |
| |
| static char inj_errstr[4096]; |
| |
| snprintf(inj_errstr, sizeof(inj_errstr), "%s (INJECTED)", errstr); |
| errstr = inj_errstr; |
| #endif |
| printf("getsockopt(-1, SOL_PACKET, PACKET_STATISTICS"); |
| if (rc < 0 || optlen <= 0) { |
| printf(", %p", tpstats); |
| } else if (optlen < sizeof(tpstats->tp_packets)) { |
| printf(", {tp_packets="); |
| print_quoted_hex(tpstats, optlen); |
| printf("}"); |
| } else { |
| PRINT_FIELD_U(", {", *tpstats, tp_packets); |
| |
| if (optlen > offsetof(struct tp_stats, tp_drops)) { |
| optlen -= offsetof(struct tp_stats, tp_drops); |
| if (optlen < sizeof(tpstats->tp_drops)) { |
| printf(", tp_drops="); |
| print_quoted_hex(tpstats, optlen); |
| } else { |
| PRINT_FIELD_U(", ", *tpstats, tp_drops); |
| |
| if (optlen > offsetof(struct tp_stats, tp_freeze_q_cnt) - |
| offsetof(struct tp_stats, tp_drops)) { |
| optlen -= offsetof(struct tp_stats, tp_freeze_q_cnt) - |
| offsetof(struct tp_stats, tp_drops); |
| if (optlen < sizeof(tpstats->tp_freeze_q_cnt)) { |
| printf(", tp_freeze_q_cnt="); |
| print_quoted_hex(tpstats, optlen); |
| } else { |
| PRINT_FIELD_U(", ", *tpstats, tp_freeze_q_cnt); |
| } |
| } |
| } |
| } |
| printf("}"); |
| } |
| printf(", [%d]) = %s\n", *len, errstr); |
| |
| return rc; |
| } |
| |
| int |
| main(void) |
| { |
| TAIL_ALLOC_OBJECT_CONST_PTR(struct tp_stats, tp_stats); |
| TAIL_ALLOC_OBJECT_CONST_PTR(socklen_t, len); |
| |
| /* offset of (truncated) struct tp_stats.tp_packets */ |
| const unsigned int offset_tp_packets = offsetofend(struct tp_stats, tp_packets); |
| const unsigned int tp_packets_truncated = offset_tp_packets - 1; |
| /* offset of (truncated) struct tp_stats.tp_drops */ |
| const unsigned int offset_tp_drops = offsetofend(struct tp_stats, tp_drops); |
| const unsigned int tp_drops_truncated = offset_tp_drops - 1; |
| /* offset of (truncated) struct tp_stats.tp_freeze_q_cnt */ |
| const unsigned int offset_tp_freeze_q_cnt = offsetofend(struct tp_stats, tp_freeze_q_cnt); |
| const unsigned int tp_freeze_q_cnt_truncated = offset_tp_freeze_q_cnt - 1; |
| |
| *len = sizeof(*tp_stats); |
| |
| /* classic getsockopt */ |
| unsigned int optlen = *len; |
| get_tpacket_stats(tp_stats, &optlen); |
| |
| /* getsockopt with zero optlen */ |
| optlen = 0; |
| get_tpacket_stats(tp_stats, &optlen); |
| |
| /* |
| * getsockopt with optlen less than offsetofend(struct tp_stats.tp_packets): |
| * the part of struct tp_stats.tp_packets is printed in hex. |
| */ |
| optlen = tp_packets_truncated; |
| get_tpacket_stats(tp_stats, &optlen); |
| |
| /* |
| * getsockopt with optlen equals to offsetofend(struct tp_stats.tp_packets): |
| * struct tp_stats.tp_drops and struct tp_stats.offset_tp_freeze_q_cnt |
| * are not printed. |
| */ |
| optlen = offset_tp_packets; |
| get_tpacket_stats(tp_stats, &optlen); |
| |
| /* |
| * getsockopt with optlen greater than offsetofend(struct tp_stats.tp_packets) |
| * but less than offsetofend(struct tp_stats, tp_drops): |
| * the part of struct tp_stats.tp_drops is printed in hex. |
| */ |
| optlen = tp_drops_truncated; |
| get_tpacket_stats(tp_stats, &optlen); |
| |
| /* |
| * getsockopt with optlen equals to offsetofend(struct tp_stats.tp_drops): |
| * struct tp_stats.tp_freeze_q_cnt is not printed. |
| */ |
| optlen = offset_tp_drops; |
| get_tpacket_stats(tp_stats, &optlen); |
| |
| /* |
| * getsockopt with optlen greater than offsetofend(struct tp_stats.tp_drops) |
| * but less than offsetofend(struct tp_stats, tp_freeze_q_cnt): |
| * the part of struct tp_stats.tp_freeze_q_cnt is printed in hex. |
| */ |
| optlen = tp_freeze_q_cnt_truncated; |
| get_tpacket_stats(tp_stats, &optlen); |
| |
| /* |
| * getsockopt with optlen equals to offsetofend(struct tp_stats.tp_freeze_q_cnt): |
| */ |
| optlen = offset_tp_freeze_q_cnt; |
| get_tpacket_stats(tp_stats, &optlen); |
| |
| /* |
| * getsockopt with optlen greater than sizeof(struct tp_stats) |
| */ |
| optlen = offset_tp_freeze_q_cnt + 1; |
| get_tpacket_stats(tp_stats, &optlen); |
| |
| puts("+++ exited with 0 +++"); |
| return 0; |
| } |