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