libipt_icmp.c revision d09b6d591ca7d7d7575cb6aa20384c9830f777ab
1/* Shared library add-on to iptables to add ICMP support. */ 2#include <stdbool.h> 3#include <stdio.h> 4#include <netdb.h> 5#include <string.h> 6#include <stdlib.h> 7#include <getopt.h> 8#include <xtables.h> 9#include <limits.h> /* INT_MAX in ip_tables.h */ 10#include <linux/netfilter_ipv4/ip_tables.h> 11 12/* special hack for icmp-type 'any': 13 * Up to kernel <=2.4.20 the problem was: 14 * '-p icmp ' matches all icmp packets 15 * '-p icmp -m icmp' matches _only_ ICMP type 0 :( 16 * This is now fixed by initializing the field * to icmp type 0xFF 17 * See: https://bugzilla.netfilter.org/cgi-bin/bugzilla/show_bug.cgi?id=37 18 */ 19 20struct icmp_names { 21 const char *name; 22 uint8_t type; 23 uint8_t code_min, code_max; 24}; 25 26static 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 81static void 82print_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 102static 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 111static const struct option icmp_opts[] = { 112 {.name = "icmp-type", .has_arg = true, .val = '1'}, 113 XT_GETOPT_TABLEEND, 114}; 115 116static void 117parse_icmp(const char *icmptype, uint8_t *type, uint8_t code[]) 118{ 119 static const unsigned int limit = ARRAY_SIZE(icmp_codes); 120 unsigned int match = limit; 121 unsigned int i; 122 123 for (i = 0; i < limit; i++) { 124 if (strncasecmp(icmp_codes[i].name, icmptype, strlen(icmptype)) 125 == 0) { 126 if (match != limit) 127 xtables_error(PARAMETER_PROBLEM, 128 "Ambiguous ICMP type `%s':" 129 " `%s' or `%s'?", 130 icmptype, 131 icmp_codes[match].name, 132 icmp_codes[i].name); 133 match = i; 134 } 135 } 136 137 if (match != limit) { 138 *type = icmp_codes[match].type; 139 code[0] = icmp_codes[match].code_min; 140 code[1] = icmp_codes[match].code_max; 141 } else { 142 char *slash; 143 char buffer[strlen(icmptype) + 1]; 144 unsigned int number; 145 146 strcpy(buffer, icmptype); 147 slash = strchr(buffer, '/'); 148 149 if (slash) 150 *slash = '\0'; 151 152 if (!xtables_strtoui(buffer, NULL, &number, 0, UINT8_MAX)) 153 xtables_error(PARAMETER_PROBLEM, 154 "Invalid ICMP type `%s'\n", buffer); 155 *type = number; 156 if (slash) { 157 if (!xtables_strtoui(slash+1, NULL, &number, 0, UINT8_MAX)) 158 xtables_error(PARAMETER_PROBLEM, 159 "Invalid ICMP code `%s'\n", 160 slash+1); 161 code[0] = code[1] = number; 162 } else { 163 code[0] = 0; 164 code[1] = 0xFF; 165 } 166 } 167} 168 169static void icmp_init(struct xt_entry_match *m) 170{ 171 struct ipt_icmp *icmpinfo = (struct ipt_icmp *)m->data; 172 173 icmpinfo->type = 0xFF; 174 icmpinfo->code[1] = 0xFF; 175} 176 177static int icmp_parse(int c, char **argv, int invert, unsigned int *flags, 178 const void *entry, struct xt_entry_match **match) 179{ 180 struct ipt_icmp *icmpinfo = (struct ipt_icmp *)(*match)->data; 181 182 switch (c) { 183 case '1': 184 if (*flags == 1) 185 xtables_error(PARAMETER_PROBLEM, 186 "icmp match: only use --icmp-type once!"); 187 xtables_check_inverse(optarg, &invert, &optind, 0, argv); 188 parse_icmp(optarg, &icmpinfo->type, 189 icmpinfo->code); 190 if (invert) 191 icmpinfo->invflags |= IPT_ICMP_INV; 192 *flags = 1; 193 break; 194 } 195 196 return 1; 197} 198 199static void print_icmptype(uint8_t type, 200 uint8_t code_min, uint8_t code_max, 201 int invert, 202 int numeric) 203{ 204 if (!numeric) { 205 unsigned int i; 206 207 for (i = 0; i < ARRAY_SIZE(icmp_codes); ++i) 208 if (icmp_codes[i].type == type 209 && icmp_codes[i].code_min == code_min 210 && icmp_codes[i].code_max == code_max) 211 break; 212 213 if (i != ARRAY_SIZE(icmp_codes)) { 214 printf("%s%s ", 215 invert ? "!" : "", 216 icmp_codes[i].name); 217 return; 218 } 219 } 220 221 if (invert) 222 printf("!"); 223 224 printf("type %u", type); 225 if (code_min == 0 && code_max == 0xFF) 226 printf(" "); 227 else if (code_min == code_max) 228 printf(" code %u ", code_min); 229 else 230 printf(" codes %u-%u ", code_min, code_max); 231} 232 233static void icmp_print(const void *ip, const struct xt_entry_match *match, 234 int numeric) 235{ 236 const struct ipt_icmp *icmp = (struct ipt_icmp *)match->data; 237 238 printf("icmp "); 239 print_icmptype(icmp->type, icmp->code[0], icmp->code[1], 240 icmp->invflags & IPT_ICMP_INV, 241 numeric); 242 243 if (icmp->invflags & ~IPT_ICMP_INV) 244 printf("Unknown invflags: 0x%X ", 245 icmp->invflags & ~IPT_ICMP_INV); 246} 247 248static void icmp_save(const void *ip, const struct xt_entry_match *match) 249{ 250 const struct ipt_icmp *icmp = (struct ipt_icmp *)match->data; 251 252 if (icmp->invflags & IPT_ICMP_INV) 253 printf("! "); 254 255 /* special hack for 'any' case */ 256 if (icmp->type == 0xFF) { 257 printf("--icmp-type any "); 258 } else { 259 printf("--icmp-type %u", icmp->type); 260 if (icmp->code[0] != 0 || icmp->code[1] != 0xFF) 261 printf("/%u", icmp->code[0]); 262 printf(" "); 263 } 264} 265 266static struct xtables_match icmp_mt_reg = { 267 .name = "icmp", 268 .version = XTABLES_VERSION, 269 .family = NFPROTO_IPV4, 270 .size = XT_ALIGN(sizeof(struct ipt_icmp)), 271 .userspacesize = XT_ALIGN(sizeof(struct ipt_icmp)), 272 .help = icmp_help, 273 .init = icmp_init, 274 .parse = icmp_parse, 275 .print = icmp_print, 276 .save = icmp_save, 277 .extra_opts = icmp_opts, 278}; 279 280void _init(void) 281{ 282 xtables_register_match(&icmp_mt_reg); 283} 284