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
icmp_help(void)22 static void icmp_help(void)
23 {
24 printf(
25 "icmp match options:\n"
26 "[!] --icmp-type typename match icmp type\n"
27 "[!] --icmp-type type[/code] (or numeric type or type/code)\n");
28 printf("Valid ICMP Types:");
29 xt_print_icmp_types(icmp_codes, ARRAY_SIZE(icmp_codes));
30 }
31
32 static const struct xt_option_entry icmp_opts[] = {
33 {.name = "icmp-type", .id = O_ICMP_TYPE, .type = XTTYPE_STRING,
34 .flags = XTOPT_MAND | XTOPT_INVERT},
35 XTOPT_TABLEEND,
36 };
37
icmp_init(struct xt_entry_match * m)38 static void icmp_init(struct xt_entry_match *m)
39 {
40 struct ipt_icmp *icmpinfo = (struct ipt_icmp *)m->data;
41
42 icmpinfo->type = 0xFF;
43 icmpinfo->code[1] = 0xFF;
44 }
45
icmp_parse(struct xt_option_call * cb)46 static void icmp_parse(struct xt_option_call *cb)
47 {
48 struct ipt_icmp *icmpinfo = cb->data;
49
50 xtables_option_parse(cb);
51 ipt_parse_icmp(cb->arg, &icmpinfo->type, icmpinfo->code);
52 if (cb->invert)
53 icmpinfo->invflags |= IPT_ICMP_INV;
54 }
55
print_icmptype(uint8_t type,uint8_t code_min,uint8_t code_max,int invert,int numeric)56 static void print_icmptype(uint8_t type,
57 uint8_t code_min, uint8_t code_max,
58 int invert,
59 int numeric)
60 {
61 if (!numeric) {
62 unsigned int i;
63
64 for (i = 0; i < ARRAY_SIZE(icmp_codes); ++i)
65 if (icmp_codes[i].type == type
66 && icmp_codes[i].code_min == code_min
67 && icmp_codes[i].code_max == code_max)
68 break;
69
70 if (i != ARRAY_SIZE(icmp_codes)) {
71 printf(" %s%s",
72 invert ? "!" : "",
73 icmp_codes[i].name);
74 return;
75 }
76 }
77
78 if (invert)
79 printf(" !");
80
81 printf("type %u", type);
82 if (code_min == code_max)
83 printf(" code %u", code_min);
84 else if (code_min != 0 || code_max != 0xFF)
85 printf(" codes %u-%u", code_min, code_max);
86 }
87
icmp_print(const void * ip,const struct xt_entry_match * match,int numeric)88 static void icmp_print(const void *ip, const struct xt_entry_match *match,
89 int numeric)
90 {
91 const struct ipt_icmp *icmp = (struct ipt_icmp *)match->data;
92
93 printf(" icmp");
94 print_icmptype(icmp->type, icmp->code[0], icmp->code[1],
95 icmp->invflags & IPT_ICMP_INV,
96 numeric);
97
98 if (icmp->invflags & ~IPT_ICMP_INV)
99 printf(" Unknown invflags: 0x%X",
100 icmp->invflags & ~IPT_ICMP_INV);
101 }
102
icmp_save(const void * ip,const struct xt_entry_match * match)103 static void icmp_save(const void *ip, const struct xt_entry_match *match)
104 {
105 const struct ipt_icmp *icmp = (struct ipt_icmp *)match->data;
106
107 if (icmp->invflags & IPT_ICMP_INV)
108 printf(" !");
109
110 /* special hack for 'any' case */
111 if (icmp->type == 0xFF &&
112 icmp->code[0] == 0 && icmp->code[1] == 0xFF) {
113 printf(" --icmp-type any");
114 } else {
115 printf(" --icmp-type %u", icmp->type);
116 if (icmp->code[0] != 0 || icmp->code[1] != 0xFF)
117 printf("/%u", icmp->code[0]);
118 }
119 }
120
type_xlate_print(struct xt_xlate * xl,unsigned int icmptype,unsigned int code_min,unsigned int code_max)121 static unsigned int type_xlate_print(struct xt_xlate *xl, unsigned int icmptype,
122 unsigned int code_min,
123 unsigned int code_max)
124 {
125 unsigned int i;
126
127 if (code_min != code_max) {
128 for (i = 0; i < ARRAY_SIZE(icmp_codes); ++i)
129 if (icmp_codes[i].type == icmptype &&
130 icmp_codes[i].code_min == code_min &&
131 icmp_codes[i].code_max == code_max) {
132 xt_xlate_add(xl, "%s", icmp_codes[i].name);
133 return 1;
134 }
135 }
136
137 return 0;
138 }
139
icmp_xlate(struct xt_xlate * xl,const struct xt_xlate_mt_params * params)140 static int icmp_xlate(struct xt_xlate *xl,
141 const struct xt_xlate_mt_params *params)
142 {
143 const struct ipt_icmp *info = (struct ipt_icmp *)params->match->data;
144
145 if (info->type != 0xFF) {
146 xt_xlate_add(xl, "icmp type%s ",
147 (info->invflags & IPT_ICMP_INV) ? " !=" : "");
148
149 if (!type_xlate_print(xl, info->type, info->code[0],
150 info->code[1]))
151 return 0;
152 } else {
153 /* '-m icmp --icmp-type any' is a noop by itself,
154 * but it eats a (mandatory) previous '-p icmp' so
155 * emit it here */
156 xt_xlate_add(xl, "ip protocol icmp");
157 }
158 return 1;
159 }
160
161 static struct xtables_match icmp_mt_reg = {
162 .name = "icmp",
163 .version = XTABLES_VERSION,
164 .family = NFPROTO_IPV4,
165 .size = XT_ALIGN(sizeof(struct ipt_icmp)),
166 .userspacesize = XT_ALIGN(sizeof(struct ipt_icmp)),
167 .help = icmp_help,
168 .init = icmp_init,
169 .print = icmp_print,
170 .save = icmp_save,
171 .x6_parse = icmp_parse,
172 .x6_options = icmp_opts,
173 .xlate = icmp_xlate,
174 };
175
_init(void)176 void _init(void)
177 {
178 xtables_register_match(&icmp_mt_reg);
179 }
180