libipt_icmp.c revision 3efb6ead2e51fe1eca55bcb2b06afb4dc4b8cb7c
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		unsigned int number;
137
138		strcpy(buffer, icmptype);
139		slash = strchr(buffer, '/');
140
141		if (slash)
142			*slash = '\0';
143
144		if (string_to_number(buffer, 0, 255, &number) == -1)
145			exit_error(PARAMETER_PROBLEM,
146				   "Invalid ICMP type `%s'\n", buffer);
147		*type = number;
148		if (slash) {
149			if (string_to_number(slash+1, 0, 255, &number) == -1)
150				exit_error(PARAMETER_PROBLEM,
151					   "Invalid ICMP code `%s'\n",
152					   slash+1);
153			code[0] = code[1] = number;
154		} else {
155			code[0] = 0;
156			code[1] = 0xFF;
157		}
158	}
159
160	if (code[0] == 0 && code[1] == 0xFF)
161		return NFC_IP_SRC_PT;
162	else return NFC_IP_SRC_PT | NFC_IP_DST_PT;
163}
164
165/* Initialize the match. */
166static void
167init(struct ipt_entry_match *m, unsigned int *nfcache)
168{
169	struct ipt_icmp *icmpinfo = (struct ipt_icmp *)m->data;
170
171	icmpinfo->code[1] = 0xFF;
172}
173
174/* Function which parses command options; returns true if it
175   ate an option */
176static int
177parse(int c, char **argv, int invert, unsigned int *flags,
178      const struct ipt_entry *entry,
179      unsigned int *nfcache,
180      struct ipt_entry_match **match)
181{
182	struct ipt_icmp *icmpinfo = (struct ipt_icmp *)(*match)->data;
183
184	switch (c) {
185	case '1':
186		if (check_inverse(optarg, &invert))
187			optind++;
188		*nfcache |= parse_icmp(argv[optind-1],
189				       &icmpinfo->type,
190				       icmpinfo->code);
191		if (invert)
192			icmpinfo->invflags |= IPT_ICMP_INV;
193		break;
194
195	default:
196		return 0;
197	}
198
199	return 1;
200}
201
202static void print_icmptype(u_int8_t type,
203			   u_int8_t code_min, u_int8_t code_max,
204			   int invert,
205			   int numeric)
206{
207	if (!numeric) {
208		unsigned int i;
209
210		for (i = 0;
211		     i < sizeof(icmp_codes)/sizeof(struct icmp_names);
212		     i++) {
213			if (icmp_codes[i].type == type
214			    && icmp_codes[i].code_min == code_min
215			    && icmp_codes[i].code_max == code_max)
216				break;
217		}
218
219		if (i != sizeof(icmp_codes)/sizeof(struct icmp_names)) {
220			printf("%s%s ",
221			       invert ? "!" : "",
222			       icmp_codes[i].name);
223			return;
224		}
225	}
226
227	if (invert)
228		printf("!");
229
230	printf("type %u", type);
231	if (code_min == 0 && code_max == 0xFF)
232		printf(" ");
233	else if (code_min == code_max)
234		printf(" code %u ", code_min);
235	else
236		printf(" codes %u-%u ", code_min, code_max);
237}
238
239/* Prints out the union ipt_matchinfo. */
240static void
241print(const struct ipt_ip *ip,
242      const struct ipt_entry_match *match,
243      int numeric)
244{
245	const struct ipt_icmp *icmp = (struct ipt_icmp *)match->data;
246
247	printf("icmp ");
248	print_icmptype(icmp->type, icmp->code[0], icmp->code[1],
249		       icmp->invflags & IPT_ICMP_INV,
250		       numeric);
251
252	if (icmp->invflags & ~IPT_ICMP_INV)
253		printf("Unknown invflags: 0x%X ",
254		       icmp->invflags & ~IPT_ICMP_INV);
255}
256
257/* Saves the match in parsable form to stdout. */
258static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
259{
260	const struct ipt_icmp *icmp = (struct ipt_icmp *)match->data;
261
262	if (icmp->invflags & IPT_ICMP_INV)
263		printf("! ");
264
265	printf("--icmp-type %u", icmp->type);
266	if (icmp->code[0] != 0 || icmp->code[1] != 0xFF)
267		printf("/%u", icmp->code[0]);
268	printf(" ");
269}
270
271/* Final check; we don't care. */
272static void final_check(unsigned int flags)
273{
274}
275
276static
277struct iptables_match icmp
278= { NULL,
279    "icmp",
280    NETFILTER_VERSION,
281    IPT_ALIGN(sizeof(struct ipt_icmp)),
282    IPT_ALIGN(sizeof(struct ipt_icmp)),
283    &help,
284    &init,
285    &parse,
286    &final_check,
287    &print,
288    &save,
289    opts
290};
291
292void _init(void)
293{
294	register_match(&icmp);
295}
296