blob: 0678aac3939c675a56ea47dbe5e29ebc230b7886 [file] [log] [blame]
lh9ed821d2023-04-07 01:36:19 -07001/* Shared library add-on to ip6tables 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 <xtables.h>
8#include <limits.h> /* INT_MAX in ip6_tables.h */
9#include <linux/netfilter_ipv6/ip6_tables.h>
10
11struct icmpv6_names {
12 const char *name;
13 u_int8_t type;
14 u_int8_t code_min, code_max;
15};
16
17static const struct icmpv6_names icmpv6_codes[] = {
18 { "destination-unreachable", 1, 0, 0xFF },
19 { "no-route", 1, 0, 0 },
20 { "communication-prohibited", 1, 1, 1 },
21 { "address-unreachable", 1, 3, 3 },
22 { "port-unreachable", 1, 4, 4 },
23
24 { "packet-too-big", 2, 0, 0xFF },
25
26 { "time-exceeded", 3, 0, 0xFF },
27 /* Alias */ { "ttl-exceeded", 3, 0, 0xFF },
28 { "ttl-zero-during-transit", 3, 0, 0 },
29 { "ttl-zero-during-reassembly", 3, 1, 1 },
30
31 { "parameter-problem", 4, 0, 0xFF },
32 { "bad-header", 4, 0, 0 },
33 { "unknown-header-type", 4, 1, 1 },
34 { "unknown-option", 4, 2, 2 },
35
36 { "echo-request", 128, 0, 0xFF },
37 /* Alias */ { "ping", 128, 0, 0xFF },
38
39 { "echo-reply", 129, 0, 0xFF },
40 /* Alias */ { "pong", 129, 0, 0xFF },
41
42 { "router-solicitation", 133, 0, 0xFF },
43
44 { "router-advertisement", 134, 0, 0xFF },
45
46 { "neighbour-solicitation", 135, 0, 0xFF },
47 /* Alias */ { "neighbor-solicitation", 135, 0, 0xFF },
48
49 { "neighbour-advertisement", 136, 0, 0xFF },
50 /* Alias */ { "neighbor-advertisement", 136, 0, 0xFF },
51
52 { "redirect", 137, 0, 0xFF },
53
54};
55
56static void
57print_icmpv6types(void)
58{
59 unsigned int i;
60 printf("Valid ICMPv6 Types:");
61
62 for (i = 0; i < sizeof(icmpv6_codes)/sizeof(struct icmpv6_names); i++) {
63 if (i && icmpv6_codes[i].type == icmpv6_codes[i-1].type) {
64 if (icmpv6_codes[i].code_min == icmpv6_codes[i-1].code_min
65 && (icmpv6_codes[i].code_max
66 == icmpv6_codes[i-1].code_max))
67 printf(" (%s)", icmpv6_codes[i].name);
68 else
69 printf("\n %s", icmpv6_codes[i].name);
70 }
71 else
72 printf("\n%s", icmpv6_codes[i].name);
73 }
74 printf("\n");
75}
76
77static void icmp6_help(void)
78{
79 printf(
80"icmpv6 match options:\n"
81"[!] --icmpv6-type typename match icmpv6 type\n"
82" (or numeric type or type/code)\n");
83 print_icmpv6types();
84}
85
86static const struct option icmp6_opts[] = {
87 { "icmpv6-type", 1, NULL, '1' },
88 { .name = NULL }
89};
90
91static void
92parse_icmpv6(const char *icmpv6type, u_int8_t *type, u_int8_t code[])
93{
94 unsigned int limit = sizeof(icmpv6_codes)/sizeof(struct icmpv6_names);
95 unsigned int match = limit;
96 unsigned int i;
97
98 for (i = 0; i < limit; i++) {
99 if (strncasecmp(icmpv6_codes[i].name, icmpv6type, strlen(icmpv6type))
100 == 0) {
101 if (match != limit)
102 xtables_error(PARAMETER_PROBLEM,
103 "Ambiguous ICMPv6 type `%s':"
104 " `%s' or `%s'?",
105 icmpv6type,
106 icmpv6_codes[match].name,
107 icmpv6_codes[i].name);
108 match = i;
109 }
110 }
111
112 if (match != limit) {
113 *type = icmpv6_codes[match].type;
114 code[0] = icmpv6_codes[match].code_min;
115 code[1] = icmpv6_codes[match].code_max;
116 } else {
117 char *slash;
118 char buffer[strlen(icmpv6type) + 1];
119 unsigned int number;
120
121 strcpy(buffer, icmpv6type);
122 slash = strchr(buffer, '/');
123
124 if (slash)
125 *slash = '\0';
126
127 if (!xtables_strtoui(buffer, NULL, &number, 0, UINT8_MAX))
128 xtables_error(PARAMETER_PROBLEM,
129 "Invalid ICMPv6 type `%s'\n", buffer);
130 *type = number;
131 if (slash) {
132 if (!xtables_strtoui(slash+1, NULL, &number, 0, UINT8_MAX))
133 xtables_error(PARAMETER_PROBLEM,
134 "Invalid ICMPv6 code `%s'\n",
135 slash+1);
136 code[0] = code[1] = number;
137 } else {
138 code[0] = 0;
139 code[1] = 0xFF;
140 }
141 }
142}
143
144static void icmp6_init(struct xt_entry_match *m)
145{
146 struct ip6t_icmp *icmpv6info = (struct ip6t_icmp *)m->data;
147
148 icmpv6info->code[1] = 0xFF;
149}
150
151static int icmp6_parse(int c, char **argv, int invert, unsigned int *flags,
152 const void *entry, struct xt_entry_match **match)
153{
154 struct ip6t_icmp *icmpv6info = (struct ip6t_icmp *)(*match)->data;
155
156 switch (c) {
157 case '1':
158 if (*flags == 1)
159 xtables_error(PARAMETER_PROBLEM,
160 "icmpv6 match: only use --icmpv6-type once!");
161 xtables_check_inverse(optarg, &invert, &optind, 0);
162 parse_icmpv6(argv[optind-1], &icmpv6info->type,
163 icmpv6info->code);
164 if (invert)
165 icmpv6info->invflags |= IP6T_ICMP_INV;
166 *flags = 1;
167 break;
168
169 default:
170 return 0;
171 }
172
173 return 1;
174}
175
176static void print_icmpv6type(u_int8_t type,
177 u_int8_t code_min, u_int8_t code_max,
178 int invert,
179 int numeric)
180{
181 if (!numeric) {
182 unsigned int i;
183
184 for (i = 0;
185 i < sizeof(icmpv6_codes)/sizeof(struct icmpv6_names);
186 i++) {
187 if (icmpv6_codes[i].type == type
188 && icmpv6_codes[i].code_min == code_min
189 && icmpv6_codes[i].code_max == code_max)
190 break;
191 }
192
193 if (i != sizeof(icmpv6_codes)/sizeof(struct icmpv6_names)) {
194 printf("%s%s ",
195 invert ? "!" : "",
196 icmpv6_codes[i].name);
197 return;
198 }
199 }
200
201 if (invert)
202 printf("!");
203
204 printf("type %u", type);
205 if (code_min == 0 && code_max == 0xFF)
206 printf(" ");
207 else if (code_min == code_max)
208 printf(" code %u ", code_min);
209 else
210 printf(" codes %u-%u ", code_min, code_max);
211}
212
213static void icmp6_print(const void *ip, const struct xt_entry_match *match,
214 int numeric)
215{
216 const struct ip6t_icmp *icmpv6 = (struct ip6t_icmp *)match->data;
217
218 printf("ipv6-icmp ");
219 print_icmpv6type(icmpv6->type, icmpv6->code[0], icmpv6->code[1],
220 icmpv6->invflags & IP6T_ICMP_INV,
221 numeric);
222
223 if (icmpv6->invflags & ~IP6T_ICMP_INV)
224 printf("Unknown invflags: 0x%X ",
225 icmpv6->invflags & ~IP6T_ICMP_INV);
226}
227
228static void icmp6_save(const void *ip, const struct xt_entry_match *match)
229{
230 const struct ip6t_icmp *icmpv6 = (struct ip6t_icmp *)match->data;
231
232 if (icmpv6->invflags & IP6T_ICMP_INV)
233 printf("! ");
234
235 printf("--icmpv6-type %u", icmpv6->type);
236 if (icmpv6->code[0] != 0 || icmpv6->code[1] != 0xFF)
237 printf("/%u", icmpv6->code[0]);
238 printf(" ");
239}
240
241static void icmp6_check(unsigned int flags)
242{
243 if (!flags)
244 xtables_error(PARAMETER_PROBLEM,
245 "icmpv6 match: You must specify `--icmpv6-type'");
246}
247
248static struct xtables_match icmp6_mt6_reg = {
249 .name = "icmp6",
250 .version = XTABLES_VERSION,
251 .family = NFPROTO_IPV6,
252 .size = XT_ALIGN(sizeof(struct ip6t_icmp)),
253 .userspacesize = XT_ALIGN(sizeof(struct ip6t_icmp)),
254 .help = icmp6_help,
255 .init = icmp6_init,
256 .parse = icmp6_parse,
257 .final_check = icmp6_check,
258 .print = icmp6_print,
259 .save = icmp6_save,
260 .extra_opts = icmp6_opts,
261};
262
263void _init(void)
264{
265 xtables_register_match(&icmp6_mt6_reg);
266}