|  | #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; | 
|  | } |