libipt_icmp.c revision bf97128c7262f17a02fec41cdae75b472ba77f88
1/* Shared library add-on to iptables to add ICMP support. */ 2#include <stdio.h> 3#include <netdb.h> 4#include <string.h> 5#include <stdlib.h> 6#include <getopt.h> 7#include <xtables.h> 8#include <limits.h> /* INT_MAX in ip_tables.h */ 9#include <linux/netfilter_ipv4/ip_tables.h> 10 11/* special hack for icmp-type 'any': 12 * Up to kernel <=2.4.20 the problem was: 13 * '-p icmp ' matches all icmp packets 14 * '-p icmp -m icmp' matches _only_ ICMP type 0 :( 15 * This is now fixed by initializing the field * to icmp type 0xFF 16 * See: https://bugzilla.netfilter.org/cgi-bin/bugzilla/show_bug.cgi?id=37 17 */ 18 19struct icmp_names { 20 const char *name; 21 u_int8_t type; 22 u_int8_t code_min, code_max; 23}; 24 25static const struct icmp_names icmp_codes[] = { 26 { "any", 0xFF, 0, 0xFF }, 27 { "echo-reply", 0, 0, 0xFF }, 28 /* Alias */ { "pong", 0, 0, 0xFF }, 29 30 { "destination-unreachable", 3, 0, 0xFF }, 31 { "network-unreachable", 3, 0, 0 }, 32 { "host-unreachable", 3, 1, 1 }, 33 { "protocol-unreachable", 3, 2, 2 }, 34 { "port-unreachable", 3, 3, 3 }, 35 { "fragmentation-needed", 3, 4, 4 }, 36 { "source-route-failed", 3, 5, 5 }, 37 { "network-unknown", 3, 6, 6 }, 38 { "host-unknown", 3, 7, 7 }, 39 { "network-prohibited", 3, 9, 9 }, 40 { "host-prohibited", 3, 10, 10 }, 41 { "TOS-network-unreachable", 3, 11, 11 }, 42 { "TOS-host-unreachable", 3, 12, 12 }, 43 { "communication-prohibited", 3, 13, 13 }, 44 { "host-precedence-violation", 3, 14, 14 }, 45 { "precedence-cutoff", 3, 15, 15 }, 46 47 { "source-quench", 4, 0, 0xFF }, 48 49 { "redirect", 5, 0, 0xFF }, 50 { "network-redirect", 5, 0, 0 }, 51 { "host-redirect", 5, 1, 1 }, 52 { "TOS-network-redirect", 5, 2, 2 }, 53 { "TOS-host-redirect", 5, 3, 3 }, 54 55 { "echo-request", 8, 0, 0xFF }, 56 /* Alias */ { "ping", 8, 0, 0xFF }, 57 58 { "router-advertisement", 9, 0, 0xFF }, 59 60 { "router-solicitation", 10, 0, 0xFF }, 61 62 { "time-exceeded", 11, 0, 0xFF }, 63 /* Alias */ { "ttl-exceeded", 11, 0, 0xFF }, 64 { "ttl-zero-during-transit", 11, 0, 0 }, 65 { "ttl-zero-during-reassembly", 11, 1, 1 }, 66 67 { "parameter-problem", 12, 0, 0xFF }, 68 { "ip-header-bad", 12, 0, 0 }, 69 { "required-option-missing", 12, 1, 1 }, 70 71 { "timestamp-request", 13, 0, 0xFF }, 72 73 { "timestamp-reply", 14, 0, 0xFF }, 74 75 { "address-mask-request", 17, 0, 0xFF }, 76 77 { "address-mask-reply", 18, 0, 0xFF } 78}; 79 80static void 81print_icmptypes(void) 82{ 83 unsigned int i; 84 printf("Valid ICMP Types:"); 85 86 for (i = 0; i < ARRAY_SIZE(icmp_codes); ++i) { 87 if (i && icmp_codes[i].type == icmp_codes[i-1].type) { 88 if (icmp_codes[i].code_min == icmp_codes[i-1].code_min 89 && (icmp_codes[i].code_max 90 == icmp_codes[i-1].code_max)) 91 printf(" (%s)", icmp_codes[i].name); 92 else 93 printf("\n %s", icmp_codes[i].name); 94 } 95 else 96 printf("\n%s", icmp_codes[i].name); 97 } 98 printf("\n"); 99} 100 101static void icmp_help(void) 102{ 103 printf( 104"icmp match options:\n" 105"[!] --icmp-type typename match icmp type\n" 106"[!] --icmp-type type[/code] (or numeric type or type/code)\n"); 107 print_icmptypes(); 108} 109 110static const struct option icmp_opts[] = { 111 { "icmp-type", 1, NULL, '1' }, 112 { .name = NULL } 113}; 114 115static void 116parse_icmp(const char *icmptype, u_int8_t *type, u_int8_t code[]) 117{ 118 static const unsigned int limit = ARRAY_SIZE(icmp_codes); 119 unsigned int match = limit; 120 unsigned int i; 121 122 for (i = 0; i < limit; i++) { 123 if (strncasecmp(icmp_codes[i].name, icmptype, strlen(icmptype)) 124 == 0) { 125 if (match != limit) 126 xtables_error(PARAMETER_PROBLEM, 127 "Ambiguous ICMP type `%s':" 128 " `%s' or `%s'?", 129 icmptype, 130 icmp_codes[match].name, 131 icmp_codes[i].name); 132 match = i; 133 } 134 } 135 136 if (match != limit) { 137 *type = icmp_codes[match].type; 138 code[0] = icmp_codes[match].code_min; 139 code[1] = icmp_codes[match].code_max; 140 } else { 141 char *slash; 142 char buffer[strlen(icmptype) + 1]; 143 unsigned int number; 144 145 strcpy(buffer, icmptype); 146 slash = strchr(buffer, '/'); 147 148 if (slash) 149 *slash = '\0'; 150 151 if (!xtables_strtoui(buffer, NULL, &number, 0, UINT8_MAX)) 152 xtables_error(PARAMETER_PROBLEM, 153 "Invalid ICMP type `%s'\n", buffer); 154 *type = number; 155 if (slash) { 156 if (!xtables_strtoui(slash+1, NULL, &number, 0, UINT8_MAX)) 157 xtables_error(PARAMETER_PROBLEM, 158 "Invalid ICMP code `%s'\n", 159 slash+1); 160 code[0] = code[1] = number; 161 } else { 162 code[0] = 0; 163 code[1] = 0xFF; 164 } 165 } 166} 167 168static void icmp_init(struct xt_entry_match *m) 169{ 170 struct ipt_icmp *icmpinfo = (struct ipt_icmp *)m->data; 171 172 icmpinfo->type = 0xFF; 173 icmpinfo->code[1] = 0xFF; 174} 175 176static int icmp_parse(int c, char **argv, int invert, unsigned int *flags, 177 const void *entry, struct xt_entry_match **match) 178{ 179 struct ipt_icmp *icmpinfo = (struct ipt_icmp *)(*match)->data; 180 181 switch (c) { 182 case '1': 183 if (*flags == 1) 184 xtables_error(PARAMETER_PROBLEM, 185 "icmp match: only use --icmp-type once!"); 186 xtables_check_inverse(optarg, &invert, &optind, 0, argv); 187 parse_icmp(argv[optind-1], &icmpinfo->type, 188 icmpinfo->code); 189 if (invert) 190 icmpinfo->invflags |= IPT_ICMP_INV; 191 *flags = 1; 192 break; 193 194 default: 195 return 0; 196 } 197 198 return 1; 199} 200 201static void print_icmptype(u_int8_t type, 202 u_int8_t code_min, u_int8_t code_max, 203 int invert, 204 int numeric) 205{ 206 if (!numeric) { 207 unsigned int i; 208 209 for (i = 0; i < ARRAY_SIZE(icmp_codes); ++i) 210 if (icmp_codes[i].type == type 211 && icmp_codes[i].code_min == code_min 212 && icmp_codes[i].code_max == code_max) 213 break; 214 215 if (i != ARRAY_SIZE(icmp_codes)) { 216 printf("%s%s ", 217 invert ? "!" : "", 218 icmp_codes[i].name); 219 return; 220 } 221 } 222 223 if (invert) 224 printf("!"); 225 226 printf("type %u", type); 227 if (code_min == 0 && code_max == 0xFF) 228 printf(" "); 229 else if (code_min == code_max) 230 printf(" code %u ", code_min); 231 else 232 printf(" codes %u-%u ", code_min, code_max); 233} 234 235static void icmp_print(const void *ip, const struct xt_entry_match *match, 236 int numeric) 237{ 238 const struct ipt_icmp *icmp = (struct ipt_icmp *)match->data; 239 240 printf("icmp "); 241 print_icmptype(icmp->type, icmp->code[0], icmp->code[1], 242 icmp->invflags & IPT_ICMP_INV, 243 numeric); 244 245 if (icmp->invflags & ~IPT_ICMP_INV) 246 printf("Unknown invflags: 0x%X ", 247 icmp->invflags & ~IPT_ICMP_INV); 248} 249 250static void icmp_save(const void *ip, const struct xt_entry_match *match) 251{ 252 const struct ipt_icmp *icmp = (struct ipt_icmp *)match->data; 253 254 if (icmp->invflags & IPT_ICMP_INV) 255 printf("! "); 256 257 /* special hack for 'any' case */ 258 if (icmp->type == 0xFF) { 259 printf("--icmp-type any "); 260 } else { 261 printf("--icmp-type %u", icmp->type); 262 if (icmp->code[0] != 0 || icmp->code[1] != 0xFF) 263 printf("/%u", icmp->code[0]); 264 printf(" "); 265 } 266} 267 268static struct xtables_match icmp_mt_reg = { 269 .name = "icmp", 270 .version = XTABLES_VERSION, 271 .family = NFPROTO_IPV4, 272 .size = XT_ALIGN(sizeof(struct ipt_icmp)), 273 .userspacesize = XT_ALIGN(sizeof(struct ipt_icmp)), 274 .help = icmp_help, 275 .init = icmp_init, 276 .parse = icmp_parse, 277 .print = icmp_print, 278 .save = icmp_save, 279 .extra_opts = icmp_opts, 280}; 281 282void _init(void) 283{ 284 xtables_register_match(&icmp_mt_reg); 285} 286