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 10struct icmp_names { 11 const char *name; 12 u_int8_t type; 13 u_int8_t code_min, code_max; 14}; 15 16static const struct icmp_names icmp_codes[] = { 17 { "echo-reply", 0, 0, 0xFF }, 18 /* Alias */ { "pong", 0, 0, 0xFF }, 19 20 { "destination-unreachable", 3, 0, 0xFF }, 21 { "network-unreachable", 3, 0, 0 }, 22 { "host-unreachable", 3, 1, 1 }, 23 { "protocol-unreachable", 3, 2, 2 }, 24 { "port-unreachable", 3, 3, 3 }, 25 { "fragmentation-needed", 3, 4, 4 }, 26 { "source-route-failed", 3, 5, 5 }, 27 { "network-unknown", 3, 6, 6 }, 28 { "host-unknown", 3, 7, 7 }, 29 { "network-prohibited", 3, 9, 9 }, 30 { "host-prohibited", 3, 10, 10 }, 31 { "TOS-network-unreachable", 3, 11, 11 }, 32 { "TOS-host-unreachable", 3, 12, 12 }, 33 { "communication-prohibited", 3, 13, 13 }, 34 { "host-precedence-violation", 3, 14, 14 }, 35 { "precedence-cutoff", 3, 15, 15 }, 36 37 { "source-quench", 4, 0, 0xFF }, 38 39 { "redirect", 5, 0, 0xFF }, 40 { "network-redirect", 5, 0, 0 }, 41 { "host-redirect", 5, 1, 1 }, 42 { "TOS-network-redirect", 5, 2, 2 }, 43 { "TOS-host-redirect", 5, 3, 3 }, 44 45 { "echo-request", 8, 0, 0xFF }, 46 /* Alias */ { "ping", 8, 0, 0xFF }, 47 48 { "router-advertisement", 9, 0, 0xFF }, 49 50 { "router-solicitation", 10, 0, 0xFF }, 51 52 { "time-exceeded", 11, 0, 0xFF }, 53 /* Alias */ { "ttl-exceeded", 11, 0, 0xFF }, 54 { "ttl-zero-during-transit", 11, 0, 0 }, 55 { "ttl-zero-during-reassembly", 11, 1, 1 }, 56 57 { "parameter-problem", 12, 0, 0xFF }, 58 { "ip-header-bad", 12, 0, 0 }, 59 { "required-option-missing", 12, 1, 1 }, 60 61 { "timestamp-request", 13, 0, 0xFF }, 62 63 { "timestamp-reply", 14, 0, 0xFF }, 64 65 { "address-mask-request", 17, 0, 0xFF }, 66 67 { "address-mask-reply", 18, 0, 0xFF } 68}; 69 70static void 71print_icmptypes() 72{ 73 unsigned int i; 74 printf("Valid ICMP Types:"); 75 76 for (i = 0; i < sizeof(icmp_codes)/sizeof(struct icmp_names); i++) { 77 if (i && icmp_codes[i].type == icmp_codes[i-1].type) { 78 if (icmp_codes[i].code_min == icmp_codes[i-1].code_min 79 && (icmp_codes[i].code_max 80 == icmp_codes[i-1].code_max)) 81 printf(" (%s)", icmp_codes[i].name); 82 else 83 printf("\n %s", icmp_codes[i].name); 84 } 85 else 86 printf("\n%s", icmp_codes[i].name); 87 } 88 printf("\n"); 89} 90 91/* Function which prints out usage message. */ 92static void 93help(void) 94{ 95 printf( 96"ICMP v%s options:\n" 97" --icmp-type [!] typename match icmp type\n" 98" (or numeric type or type/code)\n" 99"\n", IPTABLES_VERSION); 100 print_icmptypes(); 101} 102 103static struct option opts[] = { 104 { "icmp-type", 1, 0, '1' }, 105 {0} 106}; 107 108static unsigned int 109parse_icmp(const char *icmptype, u_int8_t *type, u_int8_t code[]) 110{ 111 unsigned int limit = sizeof(icmp_codes)/sizeof(struct icmp_names); 112 unsigned int match = limit; 113 unsigned int i; 114 115 for (i = 0; i < limit; i++) { 116 if (strncasecmp(icmp_codes[i].name, icmptype, strlen(icmptype)) 117 == 0) { 118 if (match != limit) 119 exit_error(PARAMETER_PROBLEM, 120 "Ambiguous ICMP type `%s':" 121 " `%s' or `%s'?", 122 icmptype, 123 icmp_codes[match].name, 124 icmp_codes[i].name); 125 match = i; 126 } 127 } 128 129 if (match != limit) { 130 *type = icmp_codes[match].type; 131 code[0] = icmp_codes[match].code_min; 132 code[1] = icmp_codes[match].code_max; 133 } else { 134 char *slash; 135 char buffer[strlen(icmptype) + 1]; 136 unsigned int number; 137 138 strcpy(buffer, icmptype); 139 slash = strchr(buffer, '/'); 140 141 if (slash) 142 *slash = '\0'; 143 144 if (string_to_number(buffer, 0, 255, &number) == -1) 145 exit_error(PARAMETER_PROBLEM, 146 "Invalid ICMP type `%s'\n", buffer); 147 *type = number; 148 if (slash) { 149 if (string_to_number(slash+1, 0, 255, &number) == -1) 150 exit_error(PARAMETER_PROBLEM, 151 "Invalid ICMP code `%s'\n", 152 slash+1); 153 code[0] = code[1] = number; 154 } else { 155 code[0] = 0; 156 code[1] = 0xFF; 157 } 158 } 159 160 if (code[0] == 0 && code[1] == 0xFF) 161 return NFC_IP_SRC_PT; 162 else return NFC_IP_SRC_PT | NFC_IP_DST_PT; 163} 164 165/* Initialize the match. */ 166static void 167init(struct ipt_entry_match *m, unsigned int *nfcache) 168{ 169 struct ipt_icmp *icmpinfo = (struct ipt_icmp *)m->data; 170 171 icmpinfo->code[1] = 0xFF; 172} 173 174/* Function which parses command options; returns true if it 175 ate an option */ 176static int 177parse(int c, char **argv, int invert, unsigned int *flags, 178 const struct ipt_entry *entry, 179 unsigned int *nfcache, 180 struct ipt_entry_match **match) 181{ 182 struct ipt_icmp *icmpinfo = (struct ipt_icmp *)(*match)->data; 183 184 switch (c) { 185 case '1': 186 check_inverse(optarg, &invert, &optind, 0); 187 *nfcache |= parse_icmp(argv[optind-1], 188 &icmpinfo->type, 189 icmpinfo->code); 190 if (invert) 191 icmpinfo->invflags |= IPT_ICMP_INV; 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; 210 i < sizeof(icmp_codes)/sizeof(struct icmp_names); 211 i++) { 212 if (icmp_codes[i].type == type 213 && icmp_codes[i].code_min == code_min 214 && icmp_codes[i].code_max == code_max) 215 break; 216 } 217 218 if (i != sizeof(icmp_codes)/sizeof(struct icmp_names)) { 219 printf("%s%s ", 220 invert ? "!" : "", 221 icmp_codes[i].name); 222 return; 223 } 224 } 225 226 if (invert) 227 printf("!"); 228 229 printf("type %u", type); 230 if (code_min == 0 && code_max == 0xFF) 231 printf(" "); 232 else if (code_min == code_max) 233 printf(" code %u ", code_min); 234 else 235 printf(" codes %u-%u ", code_min, code_max); 236} 237 238/* Prints out the union ipt_matchinfo. */ 239static void 240print(const struct ipt_ip *ip, 241 const struct ipt_entry_match *match, 242 int numeric) 243{ 244 const struct ipt_icmp *icmp = (struct ipt_icmp *)match->data; 245 246 printf("icmp "); 247 print_icmptype(icmp->type, icmp->code[0], icmp->code[1], 248 icmp->invflags & IPT_ICMP_INV, 249 numeric); 250 251 if (icmp->invflags & ~IPT_ICMP_INV) 252 printf("Unknown invflags: 0x%X ", 253 icmp->invflags & ~IPT_ICMP_INV); 254} 255 256/* Saves the match in parsable form to stdout. */ 257static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match) 258{ 259 const struct ipt_icmp *icmp = (struct ipt_icmp *)match->data; 260 261 if (icmp->invflags & IPT_ICMP_INV) 262 printf("! "); 263 264 printf("--icmp-type %u", icmp->type); 265 if (icmp->code[0] != 0 || icmp->code[1] != 0xFF) 266 printf("/%u", icmp->code[0]); 267 printf(" "); 268} 269 270/* Final check; we don't care. */ 271static void final_check(unsigned int flags) 272{ 273} 274 275static 276struct iptables_match icmp 277= { NULL, 278 "icmp", 279 IPTABLES_VERSION, 280 IPT_ALIGN(sizeof(struct ipt_icmp)), 281 IPT_ALIGN(sizeof(struct ipt_icmp)), 282 &help, 283 &init, 284 &parse, 285 &final_check, 286 &print, 287 &save, 288 opts 289}; 290 291void _init(void) 292{ 293 register_match(&icmp); 294} 295