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 unsigned int type_xlate_print(struct xt_xlate *xl, unsigned int icmptype, 253 unsigned int code_min, 254 unsigned int code_max) 255 { 256 unsigned int i; 257 258 if (code_min != code_max) { 259 for (i = 0; i < ARRAY_SIZE(icmp_codes); ++i) 260 if (icmp_codes[i].type == icmptype && 261 icmp_codes[i].code_min == code_min && 262 icmp_codes[i].code_max == code_max) { 263 xt_xlate_add(xl, icmp_codes[i].name); 264 return 1; 265 } 266 } 267 268 return 0; 269 } 270 271 static int icmp_xlate(struct xt_xlate *xl, 272 const struct xt_xlate_mt_params *params) 273 { 274 const struct ipt_icmp *info = (struct ipt_icmp *)params->match->data; 275 276 if (info->type != 0xFF) { 277 xt_xlate_add(xl, "icmp type%s ", 278 (info->invflags & IPT_ICMP_INV) ? " !=" : ""); 279 280 if (!type_xlate_print(xl, info->type, info->code[0], 281 info->code[1])) 282 return 0; 283 } 284 return 1; 285 } 286 287 static struct xtables_match icmp_mt_reg = { 288 .name = "icmp", 289 .version = XTABLES_VERSION, 290 .family = NFPROTO_IPV4, 291 .size = XT_ALIGN(sizeof(struct ipt_icmp)), 292 .userspacesize = XT_ALIGN(sizeof(struct ipt_icmp)), 293 .help = icmp_help, 294 .init = icmp_init, 295 .print = icmp_print, 296 .save = icmp_save, 297 .x6_parse = icmp_parse, 298 .x6_options = icmp_opts, 299 .xlate = icmp_xlate, 300 }; 301 302 void _init(void) 303 { 304 xtables_register_match(&icmp_mt_reg); 305 } 306