1 #include "tests.h"
2 #include <stdio.h>
3 #include <stddef.h>
4 #include <sys/socket.h>
5 #include <linux/if_packet.h>
6 #include "print_fields.h"
7 
8 static const char *errstr;
9 
10 struct tp_stats {
11 	unsigned int tp_packets, tp_drops, tp_freeze_q_cnt;
12 };
13 
14 static long
get_tpacket_stats(void * optval,socklen_t * len)15 get_tpacket_stats(void *optval, socklen_t *len)
16 {
17 	struct tp_stats *tpstats = optval;
18 	socklen_t optlen = *len;
19 	long rc = getsockopt(-1, SOL_PACKET, PACKET_STATISTICS, tpstats, len);
20 	errstr = sprintrc(rc);
21 #ifdef INJECT_RETVAL
22 	if (rc != INJECT_RETVAL)
23 		error_msg_and_fail("Got a return value of %ld != %d",
24 				   rc, INJECT_RETVAL);
25 
26 	static char inj_errstr[4096];
27 
28 	snprintf(inj_errstr, sizeof(inj_errstr), "%s (INJECTED)", errstr);
29 	errstr = inj_errstr;
30 #endif
31 	printf("getsockopt(-1, SOL_PACKET, PACKET_STATISTICS");
32 	if (rc < 0 || optlen <= 0) {
33 		printf(", %p", tpstats);
34 	} else if (optlen < sizeof(tpstats->tp_packets)) {
35 		printf(", {tp_packets=");
36 		print_quoted_hex(tpstats, optlen);
37 		printf("}");
38 	} else {
39 		PRINT_FIELD_U(", {", *tpstats, tp_packets);
40 
41 		if (optlen > offsetof(struct tp_stats, tp_drops)) {
42 			optlen -= offsetof(struct tp_stats, tp_drops);
43 			if (optlen < sizeof(tpstats->tp_drops)) {
44 				printf(", tp_drops=");
45 				print_quoted_hex(tpstats, optlen);
46 			} else {
47 				PRINT_FIELD_U(", ", *tpstats, tp_drops);
48 
49 				if (optlen > offsetof(struct tp_stats, tp_freeze_q_cnt) -
50 					   offsetof(struct tp_stats, tp_drops)) {
51 					optlen -= offsetof(struct tp_stats, tp_freeze_q_cnt) -
52 					       offsetof(struct tp_stats, tp_drops);
53 					if (optlen < sizeof(tpstats->tp_freeze_q_cnt)) {
54 						printf(", tp_freeze_q_cnt=");
55 						print_quoted_hex(tpstats, optlen);
56 					} else {
57 						PRINT_FIELD_U(", ", *tpstats, tp_freeze_q_cnt);
58 					}
59 				}
60 			}
61 		}
62 		printf("}");
63 	}
64 	printf(", [%d]) = %s\n", *len, errstr);
65 
66 	return rc;
67 }
68 
69 int
main(void)70 main(void)
71 {
72 	TAIL_ALLOC_OBJECT_CONST_PTR(struct tp_stats, tp_stats);
73 	TAIL_ALLOC_OBJECT_CONST_PTR(socklen_t, len);
74 
75 	/* offset of (truncated) struct tp_stats.tp_packets */
76 	const unsigned int offset_tp_packets = offsetofend(struct tp_stats, tp_packets);
77 	const unsigned int tp_packets_truncated = offset_tp_packets - 1;
78 	/* offset of (truncated) struct tp_stats.tp_drops */
79 	const unsigned int offset_tp_drops = offsetofend(struct tp_stats, tp_drops);
80 	const unsigned int tp_drops_truncated = offset_tp_drops - 1;
81 	/* offset of (truncated) struct tp_stats.tp_freeze_q_cnt */
82 	const unsigned int offset_tp_freeze_q_cnt = offsetofend(struct tp_stats, tp_freeze_q_cnt);
83 	const unsigned int tp_freeze_q_cnt_truncated = offset_tp_freeze_q_cnt - 1;
84 
85 	*len = sizeof(*tp_stats);
86 
87 	/* classic getsockopt */
88 	unsigned int optlen = *len;
89 	get_tpacket_stats(tp_stats, &optlen);
90 
91 	/* getsockopt with zero optlen */
92 	optlen = 0;
93 	get_tpacket_stats(tp_stats, &optlen);
94 
95 	/*
96 	 * getsockopt with optlen less than offsetofend(struct tp_stats.tp_packets):
97 	 * the part of struct tp_stats.tp_packets is printed in hex.
98 	 */
99 	optlen = tp_packets_truncated;
100 	get_tpacket_stats(tp_stats, &optlen);
101 
102 	/*
103 	 * getsockopt with optlen equals to offsetofend(struct tp_stats.tp_packets):
104 	 * struct tp_stats.tp_drops and struct tp_stats.offset_tp_freeze_q_cnt
105 	 * are not printed.
106 	 */
107 	optlen = offset_tp_packets;
108 	get_tpacket_stats(tp_stats, &optlen);
109 
110 	/*
111 	 * getsockopt with optlen greater than offsetofend(struct tp_stats.tp_packets)
112 	 * but less than offsetofend(struct tp_stats, tp_drops):
113 	 * the part of struct tp_stats.tp_drops is printed in hex.
114 	 */
115 	optlen = tp_drops_truncated;
116 	get_tpacket_stats(tp_stats, &optlen);
117 
118 	/*
119 	 * getsockopt with optlen equals to offsetofend(struct tp_stats.tp_drops):
120 	 * struct tp_stats.tp_freeze_q_cnt is not printed.
121 	 */
122 	optlen = offset_tp_drops;
123 	get_tpacket_stats(tp_stats, &optlen);
124 
125 	/*
126 	 * getsockopt with optlen greater than offsetofend(struct tp_stats.tp_drops)
127 	 * but less than offsetofend(struct tp_stats, tp_freeze_q_cnt):
128 	 * the part of struct tp_stats.tp_freeze_q_cnt is printed in hex.
129 	 */
130 	optlen = tp_freeze_q_cnt_truncated;
131 	get_tpacket_stats(tp_stats, &optlen);
132 
133 	/*
134 	 * getsockopt with optlen equals to offsetofend(struct tp_stats.tp_freeze_q_cnt):
135 	 */
136 	optlen = offset_tp_freeze_q_cnt;
137 	get_tpacket_stats(tp_stats, &optlen);
138 
139 	/*
140 	 * getsockopt with optlen greater than sizeof(struct tp_stats)
141 	 */
142 	optlen = offset_tp_freeze_q_cnt + 1;
143 	get_tpacket_stats(tp_stats, &optlen);
144 
145 	puts("+++ exited with 0 +++");
146 	return 0;
147 }
148