1 #include <stdint.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <xtables.h>
5 #include <limits.h> /* INT_MAX in ip6_tables.h */
6 #include <linux/netfilter_ipv4/ip_tables.h>
7 
8 /* special hack for icmp-type 'any':
9  * Up to kernel <=2.4.20 the problem was:
10  * '-p icmp ' matches all icmp packets
11  * '-p icmp -m icmp' matches _only_ ICMP type 0 :(
12  * This is now fixed by initializing the field * to icmp type 0xFF
13  * See: https://bugzilla.netfilter.org/cgi-bin/bugzilla/show_bug.cgi?id=37
14  */
15 
16 enum {
17 	O_ICMP_TYPE = 0,
18 };
19 
20 struct icmp_names {
21 	const char *name;
22 	uint8_t type;
23 	uint8_t code_min, code_max;
24 };
25 
26 static const struct icmp_names icmp_codes[] = {
27 	{ "any", 0xFF, 0, 0xFF },
28 	{ "echo-reply", 0, 0, 0xFF },
29 	/* Alias */ { "pong", 0, 0, 0xFF },
30 
31 	{ "destination-unreachable", 3, 0, 0xFF },
32 	{   "network-unreachable", 3, 0, 0 },
33 	{   "host-unreachable", 3, 1, 1 },
34 	{   "protocol-unreachable", 3, 2, 2 },
35 	{   "port-unreachable", 3, 3, 3 },
36 	{   "fragmentation-needed", 3, 4, 4 },
37 	{   "source-route-failed", 3, 5, 5 },
38 	{   "network-unknown", 3, 6, 6 },
39 	{   "host-unknown", 3, 7, 7 },
40 	{   "network-prohibited", 3, 9, 9 },
41 	{   "host-prohibited", 3, 10, 10 },
42 	{   "TOS-network-unreachable", 3, 11, 11 },
43 	{   "TOS-host-unreachable", 3, 12, 12 },
44 	{   "communication-prohibited", 3, 13, 13 },
45 	{   "host-precedence-violation", 3, 14, 14 },
46 	{   "precedence-cutoff", 3, 15, 15 },
47 
48 	{ "source-quench", 4, 0, 0xFF },
49 
50 	{ "redirect", 5, 0, 0xFF },
51 	{   "network-redirect", 5, 0, 0 },
52 	{   "host-redirect", 5, 1, 1 },
53 	{   "TOS-network-redirect", 5, 2, 2 },
54 	{   "TOS-host-redirect", 5, 3, 3 },
55 
56 	{ "echo-request", 8, 0, 0xFF },
57 	/* Alias */ { "ping", 8, 0, 0xFF },
58 
59 	{ "router-advertisement", 9, 0, 0xFF },
60 
61 	{ "router-solicitation", 10, 0, 0xFF },
62 
63 	{ "time-exceeded", 11, 0, 0xFF },
64 	/* Alias */ { "ttl-exceeded", 11, 0, 0xFF },
65 	{   "ttl-zero-during-transit", 11, 0, 0 },
66 	{   "ttl-zero-during-reassembly", 11, 1, 1 },
67 
68 	{ "parameter-problem", 12, 0, 0xFF },
69 	{   "ip-header-bad", 12, 0, 0 },
70 	{   "required-option-missing", 12, 1, 1 },
71 
72 	{ "timestamp-request", 13, 0, 0xFF },
73 
74 	{ "timestamp-reply", 14, 0, 0xFF },
75 
76 	{ "address-mask-request", 17, 0, 0xFF },
77 
78 	{ "address-mask-reply", 18, 0, 0xFF }
79 };
80 
81 static void
print_icmptypes(void)82 print_icmptypes(void)
83 {
84 	unsigned int i;
85 	printf("Valid ICMP Types:");
86 
87 	for (i = 0; i < ARRAY_SIZE(icmp_codes); ++i) {
88 		if (i && icmp_codes[i].type == icmp_codes[i-1].type) {
89 			if (icmp_codes[i].code_min == icmp_codes[i-1].code_min
90 			    && (icmp_codes[i].code_max
91 				== icmp_codes[i-1].code_max))
92 				printf(" (%s)", icmp_codes[i].name);
93 			else
94 				printf("\n   %s", icmp_codes[i].name);
95 		}
96 		else
97 			printf("\n%s", icmp_codes[i].name);
98 	}
99 	printf("\n");
100 }
101 
icmp_help(void)102 static void icmp_help(void)
103 {
104 	printf(
105 "icmp match options:\n"
106 "[!] --icmp-type typename	match icmp type\n"
107 "[!] --icmp-type type[/code]	(or numeric type or type/code)\n");
108 	print_icmptypes();
109 }
110 
111 static const struct xt_option_entry icmp_opts[] = {
112 	{.name = "icmp-type", .id = O_ICMP_TYPE, .type = XTTYPE_STRING,
113 	 .flags = XTOPT_MAND | XTOPT_INVERT},
114 	XTOPT_TABLEEND,
115 };
116 
117 static void
parse_icmp(const char * icmptype,uint8_t * type,uint8_t code[])118 parse_icmp(const char *icmptype, uint8_t *type, uint8_t code[])
119 {
120 	static const unsigned int limit = ARRAY_SIZE(icmp_codes);
121 	unsigned int match = limit;
122 	unsigned int i;
123 
124 	for (i = 0; i < limit; i++) {
125 		if (strncasecmp(icmp_codes[i].name, icmptype, strlen(icmptype))
126 		    == 0) {
127 			if (match != limit)
128 				xtables_error(PARAMETER_PROBLEM,
129 					   "Ambiguous ICMP type `%s':"
130 					   " `%s' or `%s'?",
131 					   icmptype,
132 					   icmp_codes[match].name,
133 					   icmp_codes[i].name);
134 			match = i;
135 		}
136 	}
137 
138 	if (match != limit) {
139 		*type = icmp_codes[match].type;
140 		code[0] = icmp_codes[match].code_min;
141 		code[1] = icmp_codes[match].code_max;
142 	} else {
143 		char *slash;
144 		char buffer[strlen(icmptype) + 1];
145 		unsigned int number;
146 
147 		strcpy(buffer, icmptype);
148 		slash = strchr(buffer, '/');
149 
150 		if (slash)
151 			*slash = '\0';
152 
153 		if (!xtables_strtoui(buffer, NULL, &number, 0, UINT8_MAX))
154 			xtables_error(PARAMETER_PROBLEM,
155 				   "Invalid ICMP type `%s'\n", buffer);
156 		*type = number;
157 		if (slash) {
158 			if (!xtables_strtoui(slash+1, NULL, &number, 0, UINT8_MAX))
159 				xtables_error(PARAMETER_PROBLEM,
160 					   "Invalid ICMP code `%s'\n",
161 					   slash+1);
162 			code[0] = code[1] = number;
163 		} else {
164 			code[0] = 0;
165 			code[1] = 0xFF;
166 		}
167 	}
168 }
169 
icmp_init(struct xt_entry_match * m)170 static void icmp_init(struct xt_entry_match *m)
171 {
172 	struct ipt_icmp *icmpinfo = (struct ipt_icmp *)m->data;
173 
174 	icmpinfo->type = 0xFF;
175 	icmpinfo->code[1] = 0xFF;
176 }
177 
icmp_parse(struct xt_option_call * cb)178 static void icmp_parse(struct xt_option_call *cb)
179 {
180 	struct ipt_icmp *icmpinfo = cb->data;
181 
182 	xtables_option_parse(cb);
183 	parse_icmp(cb->arg, &icmpinfo->type, icmpinfo->code);
184 	if (cb->invert)
185 		icmpinfo->invflags |= IPT_ICMP_INV;
186 }
187 
print_icmptype(uint8_t type,uint8_t code_min,uint8_t code_max,int invert,int numeric)188 static void print_icmptype(uint8_t type,
189 			   uint8_t code_min, uint8_t code_max,
190 			   int invert,
191 			   int numeric)
192 {
193 	if (!numeric) {
194 		unsigned int i;
195 
196 		for (i = 0; i < ARRAY_SIZE(icmp_codes); ++i)
197 			if (icmp_codes[i].type == type
198 			    && icmp_codes[i].code_min == code_min
199 			    && icmp_codes[i].code_max == code_max)
200 				break;
201 
202 		if (i != ARRAY_SIZE(icmp_codes)) {
203 			printf(" %s%s",
204 			       invert ? "!" : "",
205 			       icmp_codes[i].name);
206 			return;
207 		}
208 	}
209 
210 	if (invert)
211 		printf(" !");
212 
213 	printf("type %u", type);
214 	if (code_min == code_max)
215 		printf(" code %u", code_min);
216 	else if (code_min != 0 || code_max != 0xFF)
217 		printf(" codes %u-%u", code_min, code_max);
218 }
219 
icmp_print(const void * ip,const struct xt_entry_match * match,int numeric)220 static void icmp_print(const void *ip, const struct xt_entry_match *match,
221                        int numeric)
222 {
223 	const struct ipt_icmp *icmp = (struct ipt_icmp *)match->data;
224 
225 	printf(" icmp");
226 	print_icmptype(icmp->type, icmp->code[0], icmp->code[1],
227 		       icmp->invflags & IPT_ICMP_INV,
228 		       numeric);
229 
230 	if (icmp->invflags & ~IPT_ICMP_INV)
231 		printf(" Unknown invflags: 0x%X",
232 		       icmp->invflags & ~IPT_ICMP_INV);
233 }
234 
icmp_save(const void * ip,const struct xt_entry_match * match)235 static void icmp_save(const void *ip, const struct xt_entry_match *match)
236 {
237 	const struct ipt_icmp *icmp = (struct ipt_icmp *)match->data;
238 
239 	if (icmp->invflags & IPT_ICMP_INV)
240 		printf(" !");
241 
242 	/* special hack for 'any' case */
243 	if (icmp->type == 0xFF) {
244 		printf(" --icmp-type any");
245 	} else {
246 		printf(" --icmp-type %u", icmp->type);
247 		if (icmp->code[0] != 0 || icmp->code[1] != 0xFF)
248 			printf("/%u", icmp->code[0]);
249 	}
250 }
251 
252 static struct xtables_match icmp_mt_reg = {
253 	.name		= "icmp",
254 	.version	= XTABLES_VERSION,
255 	.family		= NFPROTO_IPV4,
256 	.size		= XT_ALIGN(sizeof(struct ipt_icmp)),
257 	.userspacesize	= XT_ALIGN(sizeof(struct ipt_icmp)),
258 	.help		= icmp_help,
259 	.init		= icmp_init,
260 	.print		= icmp_print,
261 	.save		= icmp_save,
262 	.x6_parse	= icmp_parse,
263 	.x6_options	= icmp_opts,
264 };
265 
_init(void)266 void _init(void)
267 {
268 	xtables_register_match(&icmp_mt_reg);
269 }
270