libipt_icmp.c revision 7ac405297ec38449b30e3b05fd6bf2082fd3d803
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 default: 196 return 0; 197 } 198 199 return 1; 200} 201 202static void print_icmptype(uint8_t type, 203 uint8_t code_min, uint8_t code_max, 204 int invert, 205 int numeric) 206{ 207 if (!numeric) { 208 unsigned int i; 209 210 for (i = 0; i < ARRAY_SIZE(icmp_codes); ++i) 211 if (icmp_codes[i].type == type 212 && icmp_codes[i].code_min == code_min 213 && icmp_codes[i].code_max == code_max) 214 break; 215 216 if (i != ARRAY_SIZE(icmp_codes)) { 217 printf("%s%s ", 218 invert ? "!" : "", 219 icmp_codes[i].name); 220 return; 221 } 222 } 223 224 if (invert) 225 printf("!"); 226 227 printf("type %u", type); 228 if (code_min == 0 && code_max == 0xFF) 229 printf(" "); 230 else if (code_min == code_max) 231 printf(" code %u ", code_min); 232 else 233 printf(" codes %u-%u ", code_min, code_max); 234} 235 236static void icmp_print(const void *ip, const struct xt_entry_match *match, 237 int numeric) 238{ 239 const struct ipt_icmp *icmp = (struct ipt_icmp *)match->data; 240 241 printf("icmp "); 242 print_icmptype(icmp->type, icmp->code[0], icmp->code[1], 243 icmp->invflags & IPT_ICMP_INV, 244 numeric); 245 246 if (icmp->invflags & ~IPT_ICMP_INV) 247 printf("Unknown invflags: 0x%X ", 248 icmp->invflags & ~IPT_ICMP_INV); 249} 250 251static void icmp_save(const void *ip, const struct xt_entry_match *match) 252{ 253 const struct ipt_icmp *icmp = (struct ipt_icmp *)match->data; 254 255 if (icmp->invflags & IPT_ICMP_INV) 256 printf("! "); 257 258 /* special hack for 'any' case */ 259 if (icmp->type == 0xFF) { 260 printf("--icmp-type any "); 261 } else { 262 printf("--icmp-type %u", icmp->type); 263 if (icmp->code[0] != 0 || icmp->code[1] != 0xFF) 264 printf("/%u", icmp->code[0]); 265 printf(" "); 266 } 267} 268 269static struct xtables_match icmp_mt_reg = { 270 .name = "icmp", 271 .version = XTABLES_VERSION, 272 .family = NFPROTO_IPV4, 273 .size = XT_ALIGN(sizeof(struct ipt_icmp)), 274 .userspacesize = XT_ALIGN(sizeof(struct ipt_icmp)), 275 .help = icmp_help, 276 .init = icmp_init, 277 .parse = icmp_parse, 278 .print = icmp_print, 279 .save = icmp_save, 280 .extra_opts = icmp_opts, 281}; 282 283void _init(void) 284{ 285 xtables_register_match(&icmp_mt_reg); 286} 287