| lh | 9ed821d | 2023-04-07 01:36:19 -0700 | [diff] [blame] | 1 | #include <stdio.h> | 
 | 2 | #include <string.h> | 
 | 3 | #include <stdlib.h> | 
 | 4 | #include <stddef.h> | 
 | 5 | #include <getopt.h> | 
 | 6 | #include <math.h> | 
 | 7 |  | 
 | 8 | #include <xtables.h> | 
 | 9 | #include <linux/netfilter/x_tables.h> | 
 | 10 | #include <linux/netfilter/xt_RATEEST.h> | 
 | 11 |  | 
 | 12 | /* hack to pass raw values to final_check */ | 
 | 13 | static struct xt_rateest_target_info *RATEEST_info; | 
 | 14 | static unsigned int interval; | 
 | 15 | static unsigned int ewma_log; | 
 | 16 |  | 
 | 17 | static void | 
 | 18 | RATEEST_help(void) | 
 | 19 | { | 
 | 20 | 	printf( | 
 | 21 | "RATEEST target options:\n" | 
 | 22 | "  --rateest-name name		Rate estimator name\n" | 
 | 23 | "  --rateest-interval sec	Rate measurement interval in seconds\n" | 
 | 24 | "  --rateest-ewmalog value	Rate measurement averaging time constant\n"); | 
 | 25 | } | 
 | 26 |  | 
 | 27 | enum RATEEST_options { | 
 | 28 | 	RATEEST_OPT_NAME, | 
 | 29 | 	RATEEST_OPT_INTERVAL, | 
 | 30 | 	RATEEST_OPT_EWMALOG, | 
 | 31 | }; | 
 | 32 |  | 
 | 33 | static const struct option RATEEST_opts[] = { | 
 | 34 | 	{ "rateest-name",	1, NULL, RATEEST_OPT_NAME }, | 
 | 35 | 	{ "rateest-interval",	1, NULL, RATEEST_OPT_INTERVAL }, | 
 | 36 | 	{ "rateest-ewmalog",	1, NULL, RATEEST_OPT_EWMALOG }, | 
 | 37 | 	{ .name = NULL }, | 
 | 38 | }; | 
 | 39 |  | 
 | 40 | /* Copied from iproute */ | 
 | 41 | #define TIME_UNITS_PER_SEC	1000000 | 
 | 42 |  | 
 | 43 | static int | 
 | 44 | RATEEST_get_time(unsigned int *time, const char *str) | 
 | 45 | { | 
 | 46 | 	double t; | 
 | 47 | 	char *p; | 
 | 48 |  | 
 | 49 | 	t = strtod(str, &p); | 
 | 50 | 	if (p == str) | 
 | 51 | 		return -1; | 
 | 52 |  | 
 | 53 | 	if (*p) { | 
 | 54 | 		if (strcasecmp(p, "s") == 0 || strcasecmp(p, "sec")==0 || | 
 | 55 | 		    strcasecmp(p, "secs")==0) | 
 | 56 | 			t *= TIME_UNITS_PER_SEC; | 
 | 57 | 		else if (strcasecmp(p, "ms") == 0 || strcasecmp(p, "msec")==0 || | 
 | 58 | 			 strcasecmp(p, "msecs") == 0) | 
 | 59 | 			t *= TIME_UNITS_PER_SEC/1000; | 
 | 60 | 		else if (strcasecmp(p, "us") == 0 || strcasecmp(p, "usec")==0 || | 
 | 61 | 			 strcasecmp(p, "usecs") == 0) | 
 | 62 | 			t *= TIME_UNITS_PER_SEC/1000000; | 
 | 63 | 		else | 
 | 64 | 			return -1; | 
 | 65 | 	} | 
 | 66 |  | 
 | 67 | 	*time = t; | 
 | 68 | 	return 0; | 
 | 69 | } | 
 | 70 |  | 
 | 71 | static void | 
 | 72 | RATEEST_print_time(unsigned int time) | 
 | 73 | { | 
 | 74 | 	double tmp = time; | 
 | 75 |  | 
 | 76 | 	if (tmp >= TIME_UNITS_PER_SEC) | 
 | 77 | 		printf("%.1fs ", tmp/TIME_UNITS_PER_SEC); | 
 | 78 | 	else if (tmp >= TIME_UNITS_PER_SEC/1000) | 
 | 79 | 		printf("%.1fms ", tmp/(TIME_UNITS_PER_SEC/1000)); | 
 | 80 | 	else | 
 | 81 | 		printf("%uus ", time); | 
 | 82 | } | 
 | 83 |  | 
 | 84 | static void | 
 | 85 | RATEEST_init(struct xt_entry_target *target) | 
 | 86 | { | 
 | 87 | 	interval = 0; | 
 | 88 | 	ewma_log = 0; | 
 | 89 | } | 
 | 90 |  | 
 | 91 | static int | 
 | 92 | RATEEST_parse(int c, char **argv, int invert, unsigned int *flags, | 
 | 93 | 	      const void *entry, struct xt_entry_target **target) | 
 | 94 | { | 
 | 95 | 	struct xt_rateest_target_info *info = (void *)(*target)->data; | 
 | 96 |  | 
 | 97 | 	RATEEST_info = info; | 
 | 98 |  | 
 | 99 | 	switch (c) { | 
 | 100 | 	case RATEEST_OPT_NAME: | 
 | 101 | 		if (*flags & (1 << c)) | 
 | 102 | 			xtables_error(PARAMETER_PROBLEM, | 
 | 103 | 				   "RATEEST: can't specify --rateest-name twice"); | 
 | 104 | 		*flags |= 1 << c; | 
 | 105 |  | 
 | 106 | 		strncpy(info->name, optarg, sizeof(info->name) - 1); | 
 | 107 | 		break; | 
 | 108 |  | 
 | 109 | 	case RATEEST_OPT_INTERVAL: | 
 | 110 | 		if (*flags & (1 << c)) | 
 | 111 | 			xtables_error(PARAMETER_PROBLEM, | 
 | 112 | 				   "RATEEST: can't specify --rateest-interval twice"); | 
 | 113 | 		*flags |= 1 << c; | 
 | 114 |  | 
 | 115 | 		if (RATEEST_get_time(&interval, optarg) < 0) | 
 | 116 | 			xtables_error(PARAMETER_PROBLEM, | 
 | 117 | 				   "RATEEST: bad interval value `%s'", optarg); | 
 | 118 |  | 
 | 119 | 		break; | 
 | 120 |  | 
 | 121 | 	case RATEEST_OPT_EWMALOG: | 
 | 122 | 		if (*flags & (1 << c)) | 
 | 123 | 			xtables_error(PARAMETER_PROBLEM, | 
 | 124 | 				   "RATEEST: can't specify --rateest-ewmalog twice"); | 
 | 125 | 		*flags |= 1 << c; | 
 | 126 |  | 
 | 127 | 		if (RATEEST_get_time(&ewma_log, optarg) < 0) | 
 | 128 | 			xtables_error(PARAMETER_PROBLEM, | 
 | 129 | 				   "RATEEST: bad ewmalog value `%s'", optarg); | 
 | 130 |  | 
 | 131 | 		break; | 
 | 132 |  | 
 | 133 | 	default: | 
 | 134 | 		return 0; | 
 | 135 | 	} | 
 | 136 |  | 
 | 137 | 	return 1; | 
 | 138 | } | 
 | 139 |  | 
 | 140 | static void | 
 | 141 | RATEEST_final_check(unsigned int flags) | 
 | 142 | { | 
 | 143 | 	struct xt_rateest_target_info *info = RATEEST_info; | 
 | 144 |  | 
 | 145 | 	if (!(flags & (1 << RATEEST_OPT_NAME))) | 
 | 146 | 		xtables_error(PARAMETER_PROBLEM, "RATEEST: no name specified"); | 
 | 147 | 	if (!(flags & (1 << RATEEST_OPT_INTERVAL))) | 
 | 148 | 		xtables_error(PARAMETER_PROBLEM, "RATEEST: no interval specified"); | 
 | 149 | 	if (!(flags & (1 << RATEEST_OPT_EWMALOG))) | 
 | 150 | 		xtables_error(PARAMETER_PROBLEM, "RATEEST: no ewmalog specified"); | 
 | 151 |  | 
 | 152 | 	for (info->interval = 0; info->interval <= 5; info->interval++) { | 
 | 153 | 		if (interval <= (1 << info->interval) * (TIME_UNITS_PER_SEC / 4)) | 
 | 154 | 			break; | 
 | 155 | 	} | 
 | 156 |  | 
 | 157 | 	if (info->interval > 5) | 
 | 158 | 		xtables_error(PARAMETER_PROBLEM, | 
 | 159 | 			   "RATEEST: interval value is too large"); | 
 | 160 | 	info->interval -= 2; | 
 | 161 |  | 
 | 162 | 	for (info->ewma_log = 1; info->ewma_log < 32; info->ewma_log++) { | 
 | 163 | 		double w = 1.0 - 1.0 / (1 << info->ewma_log); | 
 | 164 | 		if (interval / (-log(w)) > ewma_log) | 
 | 165 | 			break; | 
 | 166 | 	} | 
 | 167 | 	info->ewma_log--; | 
 | 168 |  | 
 | 169 | 	if (info->ewma_log == 0 || info->ewma_log >= 31) | 
 | 170 | 		xtables_error(PARAMETER_PROBLEM, | 
 | 171 | 			   "RATEEST: ewmalog value is out of range"); | 
 | 172 | } | 
 | 173 |  | 
 | 174 | static void | 
 | 175 | __RATEEST_print(const struct xt_entry_target *target, const char *prefix) | 
 | 176 | { | 
 | 177 | 	struct xt_rateest_target_info *info = (void *)target->data; | 
 | 178 | 	unsigned int local_interval; | 
 | 179 | 	unsigned int local_ewma_log; | 
 | 180 |  | 
 | 181 | 	local_interval = (TIME_UNITS_PER_SEC << (info->interval + 2)) / 4; | 
 | 182 | 	local_ewma_log = local_interval * (1 << (info->ewma_log)); | 
 | 183 |  | 
 | 184 | 	printf("%sname %s ", prefix, info->name); | 
 | 185 | 	printf("%sinterval ", prefix); | 
 | 186 | 	RATEEST_print_time(local_interval); | 
 | 187 | 	printf("%sewmalog ", prefix); | 
 | 188 | 	RATEEST_print_time(local_ewma_log); | 
 | 189 | } | 
 | 190 |  | 
 | 191 | static void | 
 | 192 | RATEEST_print(const void *ip, const struct xt_entry_target *target, | 
 | 193 | 	      int numeric) | 
 | 194 | { | 
 | 195 | 	__RATEEST_print(target, ""); | 
 | 196 | } | 
 | 197 |  | 
 | 198 | static void | 
 | 199 | RATEEST_save(const void *ip, const struct xt_entry_target *target) | 
 | 200 | { | 
 | 201 | 	__RATEEST_print(target, "--rateest-"); | 
 | 202 | } | 
 | 203 |  | 
 | 204 | static struct xtables_target rateest_tg_reg = { | 
 | 205 | 	.family		= AF_UNSPEC, | 
 | 206 | 	.name		= "RATEEST", | 
 | 207 | 	.version	= XTABLES_VERSION, | 
 | 208 | 	.size		= XT_ALIGN(sizeof(struct xt_rateest_target_info)), | 
 | 209 | 	.userspacesize	= XT_ALIGN(sizeof(struct xt_rateest_target_info)), | 
 | 210 | 	.help		= RATEEST_help, | 
 | 211 | 	.init		= RATEEST_init, | 
 | 212 | 	.parse		= RATEEST_parse, | 
 | 213 | 	.final_check	= RATEEST_final_check, | 
 | 214 | 	.print		= RATEEST_print, | 
 | 215 | 	.save		= RATEEST_save, | 
 | 216 | 	.extra_opts	= RATEEST_opts, | 
 | 217 | }; | 
 | 218 |  | 
 | 219 | void _init(void) | 
 | 220 | { | 
 | 221 | 	xtables_register_target(&rateest_tg_reg); | 
 | 222 | } |