libipt_icmp.c revision 967279231a9ecfa99f26694a954afc535c63db1d
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 <iptables.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 100/* Function which prints out usage message. */ 101static void icmp_help(void) 102{ 103 printf( 104"icmp match options:\n" 105"[!] --icmp-type typename match icmp type\n" 106" (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 unsigned int limit = sizeof(icmp_codes)/sizeof(struct icmp_names); 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 exit_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 (string_to_number(buffer, 0, 255, &number) == -1) 152 exit_error(PARAMETER_PROBLEM, 153 "Invalid ICMP type `%s'\n", buffer); 154 *type = number; 155 if (slash) { 156 if (string_to_number(slash+1, 0, 255, &number) == -1) 157 exit_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 168/* Initialize the match. */ 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 177/* Function which parses command options; returns true if it 178 ate an option */ 179static int icmp_parse(int c, char **argv, int invert, unsigned int *flags, 180 const void *entry, struct xt_entry_match **match) 181{ 182 struct ipt_icmp *icmpinfo = (struct ipt_icmp *)(*match)->data; 183 184 switch (c) { 185 case '1': 186 if (*flags == 1) 187 exit_error(PARAMETER_PROBLEM, 188 "icmp match: only use --icmp-type once!"); 189 check_inverse(optarg, &invert, &optind, 0); 190 parse_icmp(argv[optind-1], &icmpinfo->type, 191 icmpinfo->code); 192 if (invert) 193 icmpinfo->invflags |= IPT_ICMP_INV; 194 *flags = 1; 195 break; 196 197 default: 198 return 0; 199 } 200 201 return 1; 202} 203 204static void print_icmptype(u_int8_t type, 205 u_int8_t code_min, u_int8_t code_max, 206 int invert, 207 int numeric) 208{ 209 if (!numeric) { 210 unsigned int i; 211 212 for (i = 0; 213 i < sizeof(icmp_codes)/sizeof(struct icmp_names); 214 i++) { 215 if (icmp_codes[i].type == type 216 && icmp_codes[i].code_min == code_min 217 && icmp_codes[i].code_max == code_max) 218 break; 219 } 220 221 if (i != sizeof(icmp_codes)/sizeof(struct icmp_names)) { 222 printf("%s%s ", 223 invert ? "!" : "", 224 icmp_codes[i].name); 225 return; 226 } 227 } 228 229 if (invert) 230 printf("!"); 231 232 printf("type %u", type); 233 if (code_min == 0 && code_max == 0xFF) 234 printf(" "); 235 else if (code_min == code_max) 236 printf(" code %u ", code_min); 237 else 238 printf(" codes %u-%u ", code_min, code_max); 239} 240 241/* Prints out the union ipt_matchinfo. */ 242static void icmp_print(const void *ip, const struct xt_entry_match *match, 243 int numeric) 244{ 245 const struct ipt_icmp *icmp = (struct ipt_icmp *)match->data; 246 247 printf("icmp "); 248 print_icmptype(icmp->type, icmp->code[0], icmp->code[1], 249 icmp->invflags & IPT_ICMP_INV, 250 numeric); 251 252 if (icmp->invflags & ~IPT_ICMP_INV) 253 printf("Unknown invflags: 0x%X ", 254 icmp->invflags & ~IPT_ICMP_INV); 255} 256 257/* Saves the match in parsable form to stdout. */ 258static void icmp_save(const void *ip, const struct xt_entry_match *match) 259{ 260 const struct ipt_icmp *icmp = (struct ipt_icmp *)match->data; 261 262 if (icmp->invflags & IPT_ICMP_INV) 263 printf("! "); 264 265 /* special hack for 'any' case */ 266 if (icmp->type == 0xFF) { 267 printf("--icmp-type any "); 268 } else { 269 printf("--icmp-type %u", icmp->type); 270 if (icmp->code[0] != 0 || icmp->code[1] != 0xFF) 271 printf("/%u", icmp->code[0]); 272 printf(" "); 273 } 274} 275 276static struct xtables_match icmp_mt_reg = { 277 .name = "icmp", 278 .version = XTABLES_VERSION, 279 .family = PF_INET, 280 .size = XT_ALIGN(sizeof(struct ipt_icmp)), 281 .userspacesize = XT_ALIGN(sizeof(struct ipt_icmp)), 282 .help = icmp_help, 283 .init = icmp_init, 284 .parse = icmp_parse, 285 .print = icmp_print, 286 .save = icmp_save, 287 .extra_opts = icmp_opts, 288}; 289 290void _init(void) 291{ 292 xtables_register_match(&icmp_mt_reg); 293} 294