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