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