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