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