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", IPTABLES_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		check_inverse(optarg, &invert, &optind, 0);
187		*nfcache |= parse_icmp(argv[optind-1],
188				       &icmpinfo->type,
189				       icmpinfo->code);
190		if (invert)
191			icmpinfo->invflags |= IPT_ICMP_INV;
192		break;
193
194	default:
195		return 0;
196	}
197
198	return 1;
199}
200
201static void print_icmptype(u_int8_t type,
202			   u_int8_t code_min, u_int8_t code_max,
203			   int invert,
204			   int numeric)
205{
206	if (!numeric) {
207		unsigned int i;
208
209		for (i = 0;
210		     i < sizeof(icmp_codes)/sizeof(struct icmp_names);
211		     i++) {
212			if (icmp_codes[i].type == type
213			    && icmp_codes[i].code_min == code_min
214			    && icmp_codes[i].code_max == code_max)
215				break;
216		}
217
218		if (i != sizeof(icmp_codes)/sizeof(struct icmp_names)) {
219			printf("%s%s ",
220			       invert ? "!" : "",
221			       icmp_codes[i].name);
222			return;
223		}
224	}
225
226	if (invert)
227		printf("!");
228
229	printf("type %u", type);
230	if (code_min == 0 && code_max == 0xFF)
231		printf(" ");
232	else if (code_min == code_max)
233		printf(" code %u ", code_min);
234	else
235		printf(" codes %u-%u ", code_min, code_max);
236}
237
238/* Prints out the union ipt_matchinfo. */
239static void
240print(const struct ipt_ip *ip,
241      const struct ipt_entry_match *match,
242      int numeric)
243{
244	const struct ipt_icmp *icmp = (struct ipt_icmp *)match->data;
245
246	printf("icmp ");
247	print_icmptype(icmp->type, icmp->code[0], icmp->code[1],
248		       icmp->invflags & IPT_ICMP_INV,
249		       numeric);
250
251	if (icmp->invflags & ~IPT_ICMP_INV)
252		printf("Unknown invflags: 0x%X ",
253		       icmp->invflags & ~IPT_ICMP_INV);
254}
255
256/* Saves the match in parsable form to stdout. */
257static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
258{
259	const struct ipt_icmp *icmp = (struct ipt_icmp *)match->data;
260
261	if (icmp->invflags & IPT_ICMP_INV)
262		printf("! ");
263
264	printf("--icmp-type %u", icmp->type);
265	if (icmp->code[0] != 0 || icmp->code[1] != 0xFF)
266		printf("/%u", icmp->code[0]);
267	printf(" ");
268}
269
270/* Final check; we don't care. */
271static void final_check(unsigned int flags)
272{
273}
274
275static
276struct iptables_match icmp
277= { NULL,
278    "icmp",
279    IPTABLES_VERSION,
280    IPT_ALIGN(sizeof(struct ipt_icmp)),
281    IPT_ALIGN(sizeof(struct ipt_icmp)),
282    &help,
283    &init,
284    &parse,
285    &final_check,
286    &print,
287    &save,
288    opts
289};
290
291void _init(void)
292{
293	register_match(&icmp);
294}
295