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_ipv6/ip6_tables.h>
7 #include <netinet/icmp6.h>
8
9 #include "libxt_icmp.h"
10
11 enum {
12 O_ICMPV6_TYPE = 0,
13 };
14
icmp6_help(void)15 static void icmp6_help(void)
16 {
17 printf(
18 "icmpv6 match options:\n"
19 "[!] --icmpv6-type typename match icmpv6 type\n"
20 " (or numeric type or type/code)\n");
21 printf("Valid ICMPv6 Types:");
22 xt_print_icmp_types(icmpv6_codes, ARRAY_SIZE(icmpv6_codes));
23 }
24
25 static const struct xt_option_entry icmp6_opts[] = {
26 {.name = "icmpv6-type", .id = O_ICMPV6_TYPE, .type = XTTYPE_STRING,
27 .flags = XTOPT_MAND | XTOPT_INVERT},
28 XTOPT_TABLEEND,
29 };
30
icmp6_init(struct xt_entry_match * m)31 static void icmp6_init(struct xt_entry_match *m)
32 {
33 struct ip6t_icmp *icmpv6info = (struct ip6t_icmp *)m->data;
34
35 icmpv6info->code[1] = 0xFF;
36 }
37
icmp6_parse(struct xt_option_call * cb)38 static void icmp6_parse(struct xt_option_call *cb)
39 {
40 struct ip6t_icmp *icmpv6info = cb->data;
41
42 xtables_option_parse(cb);
43 ipt_parse_icmpv6(cb->arg, &icmpv6info->type, icmpv6info->code);
44 if (cb->invert)
45 icmpv6info->invflags |= IP6T_ICMP_INV;
46 }
47
print_icmpv6type(uint8_t type,uint8_t code_min,uint8_t code_max,int invert,int numeric)48 static void print_icmpv6type(uint8_t type,
49 uint8_t code_min, uint8_t code_max,
50 int invert,
51 int numeric)
52 {
53 if (!numeric) {
54 unsigned int i;
55
56 for (i = 0; i < ARRAY_SIZE(icmpv6_codes); ++i)
57 if (icmpv6_codes[i].type == type
58 && icmpv6_codes[i].code_min == code_min
59 && icmpv6_codes[i].code_max == code_max)
60 break;
61
62 if (i != ARRAY_SIZE(icmpv6_codes)) {
63 printf(" %s%s",
64 invert ? "!" : "",
65 icmpv6_codes[i].name);
66 return;
67 }
68 }
69
70 if (invert)
71 printf(" !");
72
73 printf("type %u", type);
74 if (code_min == code_max)
75 printf(" code %u", code_min);
76 else if (code_min != 0 || code_max != 0xFF)
77 printf(" codes %u-%u", code_min, code_max);
78 }
79
icmp6_print(const void * ip,const struct xt_entry_match * match,int numeric)80 static void icmp6_print(const void *ip, const struct xt_entry_match *match,
81 int numeric)
82 {
83 const struct ip6t_icmp *icmpv6 = (struct ip6t_icmp *)match->data;
84
85 printf(" ipv6-icmp");
86 print_icmpv6type(icmpv6->type, icmpv6->code[0], icmpv6->code[1],
87 icmpv6->invflags & IP6T_ICMP_INV,
88 numeric);
89
90 if (icmpv6->invflags & ~IP6T_ICMP_INV)
91 printf(" Unknown invflags: 0x%X",
92 icmpv6->invflags & ~IP6T_ICMP_INV);
93 }
94
icmp6_save(const void * ip,const struct xt_entry_match * match)95 static void icmp6_save(const void *ip, const struct xt_entry_match *match)
96 {
97 const struct ip6t_icmp *icmpv6 = (struct ip6t_icmp *)match->data;
98
99 if (icmpv6->invflags & IP6T_ICMP_INV)
100 printf(" !");
101
102 printf(" --icmpv6-type %u", icmpv6->type);
103 if (icmpv6->code[0] != 0 || icmpv6->code[1] != 0xFF)
104 printf("/%u", icmpv6->code[0]);
105 }
106
107 #define XT_ICMPV6_TYPE(type) (type - ND_ROUTER_SOLICIT)
108
109 static const char *icmp6_type_xlate_array[] = {
110 [XT_ICMPV6_TYPE(ND_ROUTER_SOLICIT)] = "nd-router-solicit",
111 [XT_ICMPV6_TYPE(ND_ROUTER_ADVERT)] = "nd-router-advert",
112 [XT_ICMPV6_TYPE(ND_NEIGHBOR_SOLICIT)] = "nd-neighbor-solicit",
113 [XT_ICMPV6_TYPE(ND_NEIGHBOR_ADVERT)] = "nd-neighbor-advert",
114 [XT_ICMPV6_TYPE(ND_REDIRECT)] = "nd-redirect",
115 };
116
icmp6_type_xlate(unsigned int type)117 static const char *icmp6_type_xlate(unsigned int type)
118 {
119 if (type < ND_ROUTER_SOLICIT || type > ND_REDIRECT)
120 return NULL;
121
122 return icmp6_type_xlate_array[XT_ICMPV6_TYPE(type)];
123 }
124
type_xlate_print(struct xt_xlate * xl,unsigned int icmptype,unsigned int code_min,unsigned int code_max)125 static unsigned int type_xlate_print(struct xt_xlate *xl, unsigned int icmptype,
126 unsigned int code_min,
127 unsigned int code_max)
128 {
129 unsigned int i;
130 const char *type_name;
131
132 if (code_min == code_max)
133 return 0;
134
135 type_name = icmp6_type_xlate(icmptype);
136
137 if (type_name) {
138 xt_xlate_add(xl, "%s", type_name);
139 } else {
140 for (i = 0; i < ARRAY_SIZE(icmpv6_codes); ++i)
141 if (icmpv6_codes[i].type == icmptype &&
142 icmpv6_codes[i].code_min == code_min &&
143 icmpv6_codes[i].code_max == code_max)
144 break;
145
146 if (i != ARRAY_SIZE(icmpv6_codes))
147 xt_xlate_add(xl, "%s", icmpv6_codes[i].name);
148 else
149 return 0;
150 }
151
152 return 1;
153 }
154
icmp6_xlate(struct xt_xlate * xl,const struct xt_xlate_mt_params * params)155 static int icmp6_xlate(struct xt_xlate *xl,
156 const struct xt_xlate_mt_params *params)
157 {
158 const struct ip6t_icmp *info = (struct ip6t_icmp *)params->match->data;
159
160 xt_xlate_add(xl, "icmpv6 type%s ",
161 (info->invflags & IP6T_ICMP_INV) ? " !=" : "");
162
163 if (!type_xlate_print(xl, info->type, info->code[0], info->code[1]))
164 return 0;
165
166 return 1;
167 }
168
169 static struct xtables_match icmp6_mt6_reg = {
170 .name = "icmp6",
171 .version = XTABLES_VERSION,
172 .family = NFPROTO_IPV6,
173 .size = XT_ALIGN(sizeof(struct ip6t_icmp)),
174 .userspacesize = XT_ALIGN(sizeof(struct ip6t_icmp)),
175 .help = icmp6_help,
176 .init = icmp6_init,
177 .print = icmp6_print,
178 .save = icmp6_save,
179 .x6_parse = icmp6_parse,
180 .x6_options = icmp6_opts,
181 .xlate = icmp6_xlate,
182 };
183
_init(void)184 void _init(void)
185 {
186 xtables_register_match(&icmp6_mt6_reg);
187 }
188