libipt_icmp.c revision 73f72f541ac4dab538d4d418b9bbf1707b31342b
1/* Shared library add-on to iptables to add ICMP support. */
2#include <stdio.h>
3#include <netdb.h>
4#include <string.h>
5#include <stdlib.h>
6#include <getopt.h>
7#include <iptables.h>
8#include <linux/netfilter_ipv4/ip_tables.h>
9
10struct icmp_names {
11	const char *name;
12	u_int8_t type;
13	u_int8_t code_min, code_max;
14};
15
16static const struct icmp_names icmp_codes[] = {
17	{ "echo-reply", 0, 0, 0xFF },
18	/* Alias */ { "pong", 0, 0, 0xFF },
19
20	{ "destination-unreachable", 3, 0, 0xFF },
21	{   "network-unreachable", 3, 0, 0 },
22	{   "host-unreachable", 3, 1, 1 },
23	{   "protocol-unreachable", 3, 2, 2 },
24	{   "port-unreachable", 3, 3, 3 },
25	{   "fragmentation-needed", 3, 4, 4 },
26	{   "source-route-failed", 3, 5, 5 },
27	{   "network-unknown", 3, 6, 6 },
28	{   "host-unknown", 3, 7, 7 },
29	{   "network-prohibited", 3, 9, 9 },
30	{   "host-prohibited", 3, 10, 10 },
31	{   "TOS-network-unreachable", 3, 11, 11 },
32	{   "TOS-host-unreachable", 3, 12, 12 },
33	{   "communication-prohibited", 3, 13, 13 },
34	{   "host-precedence-violation", 3, 14, 14 },
35	{   "precedence-cutoff", 3, 15, 15 },
36
37	{ "source-quench", 4, 0, 0xFF },
38
39	{ "redirect", 5, 0, 0xFF },
40	{   "network-redirect", 5, 0, 0 },
41	{   "host-redirect", 5, 1, 1 },
42	{   "TOS-network-redirect", 5, 2, 2 },
43	{   "TOS-host-redirect", 5, 3, 3 },
44
45	{ "echo-request", 8, 0, 0xFF },
46	/* Alias */ { "ping", 8, 0, 0xFF },
47
48	{ "router-advertisement", 9, 0, 0xFF },
49
50	{ "router-solicitation", 10, 0, 0xFF },
51
52	{ "time-exceeded", 11, 0, 0xFF },
53	/* Alias */ { "ttl-exceeded", 11, 0, 0xFF },
54	{   "ttl-zero-during-transit", 11, 0, 0 },
55	{   "ttl-zero-during-reassembly", 11, 1, 1 },
56
57	{ "parameter-problem", 12, 0, 0xFF },
58	{   "ip-header-bad", 12, 0, 0 },
59	{   "required-option-missing", 12, 1, 1 },
60
61	{ "timestamp-request", 13, 0, 0xFF },
62
63	{ "timestamp-reply", 14, 0, 0xFF },
64
65	{ "address-mask-request", 17, 0, 0xFF },
66
67	{ "address-mask-reply", 18, 0, 0xFF }
68};
69
70static void
71print_icmptypes()
72{
73	unsigned int i;
74	printf("Valid ICMP Types:");
75
76	for (i = 0; i < sizeof(icmp_codes)/sizeof(struct icmp_names); i++) {
77		if (i && icmp_codes[i].type == icmp_codes[i-1].type) {
78			if (icmp_codes[i].code_min == icmp_codes[i-1].code_min
79			    && (icmp_codes[i].code_max
80				== icmp_codes[i-1].code_max))
81				printf(" (%s)", icmp_codes[i].name);
82			else
83				printf("\n   %s", icmp_codes[i].name);
84		}
85		else
86			printf("\n%s", icmp_codes[i].name);
87	}
88	printf("\n");
89}
90
91/* Function which prints out usage message. */
92static void
93help(void)
94{
95	printf(
96"ICMP v%s options:\n"
97" --icmp-type [!] typename	match icmp type\n"
98"				(or numeric type or type/code)\n"
99"\n", NETFILTER_VERSION);
100	print_icmptypes();
101}
102
103static struct option opts[] = {
104	{ "icmp-type", 1, 0, '1' },
105	{0}
106};
107
108static unsigned int
109parse_icmp(const char *icmptype, u_int8_t *type, u_int8_t code[])
110{
111	unsigned int limit = sizeof(icmp_codes)/sizeof(struct icmp_names);
112	unsigned int match = limit;
113	unsigned int i;
114
115	for (i = 0; i < limit; i++) {
116		if (strncasecmp(icmp_codes[i].name, icmptype, strlen(icmptype))
117		    == 0) {
118			if (match != limit)
119				exit_error(PARAMETER_PROBLEM,
120					   "Ambiguous ICMP type `%s':"
121					   " `%s' or `%s'?",
122					   icmptype,
123					   icmp_codes[match].name,
124					   icmp_codes[i].name);
125			match = i;
126		}
127	}
128
129	if (match != limit) {
130		*type = icmp_codes[match].type;
131		code[0] = icmp_codes[match].code_min;
132		code[1] = icmp_codes[match].code_max;
133	} else {
134		char *slash;
135		char buffer[strlen(icmptype) + 1];
136		int number;
137
138		strcpy(buffer, icmptype);
139		slash = strchr(buffer, '/');
140
141		if (slash)
142			*slash = '\0';
143
144		number = string_to_number(buffer, 0, 255);
145		if (number == -1)
146			exit_error(PARAMETER_PROBLEM,
147				   "Invalid ICMP type `%s'\n", buffer);
148		*type = number;
149		if (slash) {
150			number = string_to_number(slash+1, 0, 255);
151			if (number == -1)
152				exit_error(PARAMETER_PROBLEM,
153					   "Invalid ICMP code `%s'\n",
154					   slash+1);
155			code[0] = code[1] = number;
156		} else {
157			code[0] = 0;
158			code[1] = 0xFF;
159		}
160	}
161
162	if (code[0] == 0 && code[1] == 0xFF)
163		return NFC_IP_SRC_PT;
164	else return NFC_IP_SRC_PT | NFC_IP_DST_PT;
165}
166
167/* Initialize the match. */
168static void
169init(struct ipt_entry_match *m, unsigned int *nfcache)
170{
171	struct ipt_icmp *icmpinfo = (struct ipt_icmp *)m->data;
172
173	icmpinfo->code[1] = 0xFF;
174}
175
176/* Function which parses command options; returns true if it
177   ate an option */
178static int
179parse(int c, char **argv, int invert, unsigned int *flags,
180      const struct ipt_entry *entry,
181      unsigned int *nfcache,
182      struct ipt_entry_match **match)
183{
184	struct ipt_icmp *icmpinfo = (struct ipt_icmp *)(*match)->data;
185
186	switch (c) {
187	case '1':
188		if (check_inverse(optarg, &invert))
189			optind++;
190		*nfcache |= parse_icmp(argv[optind-1],
191				       &icmpinfo->type,
192				       icmpinfo->code);
193		if (invert)
194			icmpinfo->invflags |= IPT_ICMP_INV;
195		break;
196
197	default:
198		return 0;
199	}
200
201	return 1;
202}
203
204static void print_icmptype(u_int8_t type,
205			   u_int8_t code_min, u_int8_t code_max,
206			   int invert,
207			   int numeric)
208{
209	if (!numeric) {
210		unsigned int i;
211
212		for (i = 0;
213		     i < sizeof(icmp_codes)/sizeof(struct icmp_names);
214		     i++) {
215			if (icmp_codes[i].type == type
216			    && icmp_codes[i].code_min == code_min
217			    && icmp_codes[i].code_max == code_max)
218				break;
219		}
220
221		if (i != sizeof(icmp_codes)/sizeof(struct icmp_names)) {
222			printf("%s%s ",
223			       invert ? "!" : "",
224			       icmp_codes[i].name);
225			return;
226		}
227	}
228
229	if (invert)
230		printf("!");
231
232	printf("type %u", type);
233	if (code_min == 0 && code_max == 0xFF)
234		printf(" ");
235	else if (code_min == code_max)
236		printf(" code %u ", code_min);
237	else
238		printf(" codes %u-%u ", code_min, code_max);
239}
240
241/* Prints out the union ipt_matchinfo. */
242static void
243print(const struct ipt_ip *ip,
244      const struct ipt_entry_match *match,
245      int numeric)
246{
247	const struct ipt_icmp *icmp = (struct ipt_icmp *)match->data;
248
249	printf("icmp ");
250	print_icmptype(icmp->type, icmp->code[0], icmp->code[1],
251		       icmp->invflags & IPT_ICMP_INV,
252		       numeric);
253
254	if (icmp->invflags & ~IPT_ICMP_INV)
255		printf("Unknown invflags: 0x%X ",
256		       icmp->invflags & ~IPT_ICMP_INV);
257}
258
259/* Saves the match in parsable form to stdout. */
260static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
261{
262	const struct ipt_icmp *icmp = (struct ipt_icmp *)match->data;
263
264	if (icmp->invflags & IPT_ICMP_INV)
265		printf("! ");
266
267	printf("--icmp-type %u", icmp->type);
268	if (icmp->code[0] != 0 || icmp->code[1] != 0xFF)
269		printf("/%u", icmp->code[0]);
270	printf(" ");
271}
272
273/* Final check; we don't care. */
274static void final_check(unsigned int flags)
275{
276}
277
278struct iptables_match icmp
279= { NULL,
280    "icmp",
281    NETFILTER_VERSION,
282    IPT_ALIGN(sizeof(struct ipt_icmp)),
283    IPT_ALIGN(sizeof(struct ipt_icmp)),
284    &help,
285    &init,
286    &parse,
287    &final_check,
288    &print,
289    &save,
290    opts
291};
292
293void _init(void)
294{
295	register_match(&icmp);
296}
297