148 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			148 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			C
		
	
	
	
| #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;
 | |
| }
 |