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 enum { 10 O_ICMPV6_TYPE = 0, 11 }; 12 13 struct icmpv6_names { 14 const char *name; 15 uint8_t type; 16 uint8_t code_min, code_max; 17 }; 18 19 static const struct icmpv6_names icmpv6_codes[] = { 20 { "destination-unreachable", 1, 0, 0xFF }, 21 { "no-route", 1, 0, 0 }, 22 { "communication-prohibited", 1, 1, 1 }, 23 { "beyond-scope", 1, 2, 2 }, 24 { "address-unreachable", 1, 3, 3 }, 25 { "port-unreachable", 1, 4, 4 }, 26 { "failed-policy", 1, 5, 5 }, 27 { "reject-route", 1, 6, 6 }, 28 29 { "packet-too-big", 2, 0, 0xFF }, 30 31 { "time-exceeded", 3, 0, 0xFF }, 32 /* Alias */ { "ttl-exceeded", 3, 0, 0xFF }, 33 { "ttl-zero-during-transit", 3, 0, 0 }, 34 { "ttl-zero-during-reassembly", 3, 1, 1 }, 35 36 { "parameter-problem", 4, 0, 0xFF }, 37 { "bad-header", 4, 0, 0 }, 38 { "unknown-header-type", 4, 1, 1 }, 39 { "unknown-option", 4, 2, 2 }, 40 41 { "echo-request", 128, 0, 0xFF }, 42 /* Alias */ { "ping", 128, 0, 0xFF }, 43 44 { "echo-reply", 129, 0, 0xFF }, 45 /* Alias */ { "pong", 129, 0, 0xFF }, 46 47 { "router-solicitation", 133, 0, 0xFF }, 48 49 { "router-advertisement", 134, 0, 0xFF }, 50 51 { "neighbour-solicitation", 135, 0, 0xFF }, 52 /* Alias */ { "neighbor-solicitation", 135, 0, 0xFF }, 53 54 { "neighbour-advertisement", 136, 0, 0xFF }, 55 /* Alias */ { "neighbor-advertisement", 136, 0, 0xFF }, 56 57 { "redirect", 137, 0, 0xFF }, 58 59 }; 60 61 static void 62 print_icmpv6types(void) 63 { 64 unsigned int i; 65 printf("Valid ICMPv6 Types:"); 66 67 for (i = 0; i < ARRAY_SIZE(icmpv6_codes); ++i) { 68 if (i && icmpv6_codes[i].type == icmpv6_codes[i-1].type) { 69 if (icmpv6_codes[i].code_min == icmpv6_codes[i-1].code_min 70 && (icmpv6_codes[i].code_max 71 == icmpv6_codes[i-1].code_max)) 72 printf(" (%s)", icmpv6_codes[i].name); 73 else 74 printf("\n %s", icmpv6_codes[i].name); 75 } 76 else 77 printf("\n%s", icmpv6_codes[i].name); 78 } 79 printf("\n"); 80 } 81 82 static void icmp6_help(void) 83 { 84 printf( 85 "icmpv6 match options:\n" 86 "[!] --icmpv6-type typename match icmpv6 type\n" 87 " (or numeric type or type/code)\n"); 88 print_icmpv6types(); 89 } 90 91 static const struct xt_option_entry icmp6_opts[] = { 92 {.name = "icmpv6-type", .id = O_ICMPV6_TYPE, .type = XTTYPE_STRING, 93 .flags = XTOPT_MAND | XTOPT_INVERT}, 94 XTOPT_TABLEEND, 95 }; 96 97 static void 98 parse_icmpv6(const char *icmpv6type, uint8_t *type, uint8_t code[]) 99 { 100 static const unsigned int limit = ARRAY_SIZE(icmpv6_codes); 101 unsigned int match = limit; 102 unsigned int i; 103 104 for (i = 0; i < limit; i++) { 105 if (strncasecmp(icmpv6_codes[i].name, icmpv6type, strlen(icmpv6type)) 106 == 0) { 107 if (match != limit) 108 xtables_error(PARAMETER_PROBLEM, 109 "Ambiguous ICMPv6 type `%s':" 110 " `%s' or `%s'?", 111 icmpv6type, 112 icmpv6_codes[match].name, 113 icmpv6_codes[i].name); 114 match = i; 115 } 116 } 117 118 if (match != limit) { 119 *type = icmpv6_codes[match].type; 120 code[0] = icmpv6_codes[match].code_min; 121 code[1] = icmpv6_codes[match].code_max; 122 } else { 123 char *slash; 124 char buffer[strlen(icmpv6type) + 1]; 125 unsigned int number; 126 127 strcpy(buffer, icmpv6type); 128 slash = strchr(buffer, '/'); 129 130 if (slash) 131 *slash = '\0'; 132 133 if (!xtables_strtoui(buffer, NULL, &number, 0, UINT8_MAX)) 134 xtables_error(PARAMETER_PROBLEM, 135 "Invalid ICMPv6 type `%s'\n", buffer); 136 *type = number; 137 if (slash) { 138 if (!xtables_strtoui(slash+1, NULL, &number, 0, UINT8_MAX)) 139 xtables_error(PARAMETER_PROBLEM, 140 "Invalid ICMPv6 code `%s'\n", 141 slash+1); 142 code[0] = code[1] = number; 143 } else { 144 code[0] = 0; 145 code[1] = 0xFF; 146 } 147 } 148 } 149 150 static void icmp6_init(struct xt_entry_match *m) 151 { 152 struct ip6t_icmp *icmpv6info = (struct ip6t_icmp *)m->data; 153 154 icmpv6info->code[1] = 0xFF; 155 } 156 157 static void icmp6_parse(struct xt_option_call *cb) 158 { 159 struct ip6t_icmp *icmpv6info = cb->data; 160 161 xtables_option_parse(cb); 162 parse_icmpv6(cb->arg, &icmpv6info->type, icmpv6info->code); 163 if (cb->invert) 164 icmpv6info->invflags |= IP6T_ICMP_INV; 165 } 166 167 static void print_icmpv6type(uint8_t type, 168 uint8_t code_min, uint8_t code_max, 169 int invert, 170 int numeric) 171 { 172 if (!numeric) { 173 unsigned int i; 174 175 for (i = 0; i < ARRAY_SIZE(icmpv6_codes); ++i) 176 if (icmpv6_codes[i].type == type 177 && icmpv6_codes[i].code_min == code_min 178 && icmpv6_codes[i].code_max == code_max) 179 break; 180 181 if (i != ARRAY_SIZE(icmpv6_codes)) { 182 printf(" %s%s", 183 invert ? "!" : "", 184 icmpv6_codes[i].name); 185 return; 186 } 187 } 188 189 if (invert) 190 printf(" !"); 191 192 printf("type %u", type); 193 if (code_min == code_max) 194 printf(" code %u", code_min); 195 else if (code_min != 0 || code_max != 0xFF) 196 printf(" codes %u-%u", code_min, code_max); 197 } 198 199 static void icmp6_print(const void *ip, const struct xt_entry_match *match, 200 int numeric) 201 { 202 const struct ip6t_icmp *icmpv6 = (struct ip6t_icmp *)match->data; 203 204 printf(" ipv6-icmp"); 205 print_icmpv6type(icmpv6->type, icmpv6->code[0], icmpv6->code[1], 206 icmpv6->invflags & IP6T_ICMP_INV, 207 numeric); 208 209 if (icmpv6->invflags & ~IP6T_ICMP_INV) 210 printf(" Unknown invflags: 0x%X", 211 icmpv6->invflags & ~IP6T_ICMP_INV); 212 } 213 214 static void icmp6_save(const void *ip, const struct xt_entry_match *match) 215 { 216 const struct ip6t_icmp *icmpv6 = (struct ip6t_icmp *)match->data; 217 218 if (icmpv6->invflags & IP6T_ICMP_INV) 219 printf(" !"); 220 221 printf(" --icmpv6-type %u", icmpv6->type); 222 if (icmpv6->code[0] != 0 || icmpv6->code[1] != 0xFF) 223 printf("/%u", icmpv6->code[0]); 224 } 225 226 #define XT_ICMPV6_TYPE(type) (type - ND_ROUTER_SOLICIT) 227 228 static const char *icmp6_type_xlate_array[] = { 229 [XT_ICMPV6_TYPE(ND_ROUTER_SOLICIT)] = "nd-router-solicit", 230 [XT_ICMPV6_TYPE(ND_ROUTER_ADVERT)] = "nd-router-advert", 231 [XT_ICMPV6_TYPE(ND_NEIGHBOR_SOLICIT)] = "nd-neighbor-solicit", 232 [XT_ICMPV6_TYPE(ND_NEIGHBOR_ADVERT)] = "nd-neighbor-advert", 233 [XT_ICMPV6_TYPE(ND_REDIRECT)] = "nd-redirect", 234 }; 235 236 static const char *icmp6_type_xlate(unsigned int type) 237 { 238 if (type < ND_ROUTER_SOLICIT || type > ND_REDIRECT) 239 return NULL; 240 241 return icmp6_type_xlate_array[XT_ICMPV6_TYPE(type)]; 242 } 243 244 static unsigned int type_xlate_print(struct xt_xlate *xl, unsigned int icmptype, 245 unsigned int code_min, 246 unsigned int code_max) 247 { 248 unsigned int i; 249 const char *type_name; 250 251 if (code_min == code_max) 252 return 0; 253 254 type_name = icmp6_type_xlate(icmptype); 255 256 if (type_name) { 257 xt_xlate_add(xl, type_name); 258 } else { 259 for (i = 0; i < ARRAY_SIZE(icmpv6_codes); ++i) 260 if (icmpv6_codes[i].type == icmptype && 261 icmpv6_codes[i].code_min == code_min && 262 icmpv6_codes[i].code_max == code_max) 263 break; 264 265 if (i != ARRAY_SIZE(icmpv6_codes)) 266 xt_xlate_add(xl, icmpv6_codes[i].name); 267 else 268 return 0; 269 } 270 271 return 1; 272 } 273 274 static int icmp6_xlate(struct xt_xlate *xl, 275 const struct xt_xlate_mt_params *params) 276 { 277 const struct ip6t_icmp *info = (struct ip6t_icmp *)params->match->data; 278 279 xt_xlate_add(xl, "icmpv6 type%s ", 280 (info->invflags & IP6T_ICMP_INV) ? " !=" : ""); 281 282 if (!type_xlate_print(xl, info->type, info->code[0], info->code[1])) 283 return 0; 284 285 xt_xlate_add(xl, " "); 286 287 return 1; 288 } 289 290 static struct xtables_match icmp6_mt6_reg = { 291 .name = "icmp6", 292 .version = XTABLES_VERSION, 293 .family = NFPROTO_IPV6, 294 .size = XT_ALIGN(sizeof(struct ip6t_icmp)), 295 .userspacesize = XT_ALIGN(sizeof(struct ip6t_icmp)), 296 .help = icmp6_help, 297 .init = icmp6_init, 298 .print = icmp6_print, 299 .save = icmp6_save, 300 .x6_parse = icmp6_parse, 301 .x6_options = icmp6_opts, 302 .xlate = icmp6_xlate, 303 }; 304 305 void _init(void) 306 { 307 xtables_register_match(&icmp6_mt6_reg); 308 } 309