libipt_icmp.c 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. #include <stdint.h>
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <xtables.h>
  5. #include <limits.h> /* INT_MAX in ip6_tables.h */
  6. #include <linux/netfilter_ipv4/ip_tables.h>
  7. /* special hack for icmp-type 'any':
  8. * Up to kernel <=2.4.20 the problem was:
  9. * '-p icmp ' matches all icmp packets
  10. * '-p icmp -m icmp' matches _only_ ICMP type 0 :(
  11. * This is now fixed by initializing the field * to icmp type 0xFF
  12. * See: https://bugzilla.netfilter.org/cgi-bin/bugzilla/show_bug.cgi?id=37
  13. */
  14. enum {
  15. O_ICMP_TYPE = 0,
  16. };
  17. struct icmp_names {
  18. const char *name;
  19. uint8_t type;
  20. uint8_t code_min, code_max;
  21. };
  22. static const struct icmp_names icmp_codes[] = {
  23. { "any", 0xFF, 0, 0xFF },
  24. { "echo-reply", 0, 0, 0xFF },
  25. /* Alias */ { "pong", 0, 0, 0xFF },
  26. { "destination-unreachable", 3, 0, 0xFF },
  27. { "network-unreachable", 3, 0, 0 },
  28. { "host-unreachable", 3, 1, 1 },
  29. { "protocol-unreachable", 3, 2, 2 },
  30. { "port-unreachable", 3, 3, 3 },
  31. { "fragmentation-needed", 3, 4, 4 },
  32. { "source-route-failed", 3, 5, 5 },
  33. { "network-unknown", 3, 6, 6 },
  34. { "host-unknown", 3, 7, 7 },
  35. { "network-prohibited", 3, 9, 9 },
  36. { "host-prohibited", 3, 10, 10 },
  37. { "TOS-network-unreachable", 3, 11, 11 },
  38. { "TOS-host-unreachable", 3, 12, 12 },
  39. { "communication-prohibited", 3, 13, 13 },
  40. { "host-precedence-violation", 3, 14, 14 },
  41. { "precedence-cutoff", 3, 15, 15 },
  42. { "source-quench", 4, 0, 0xFF },
  43. { "redirect", 5, 0, 0xFF },
  44. { "network-redirect", 5, 0, 0 },
  45. { "host-redirect", 5, 1, 1 },
  46. { "TOS-network-redirect", 5, 2, 2 },
  47. { "TOS-host-redirect", 5, 3, 3 },
  48. { "echo-request", 8, 0, 0xFF },
  49. /* Alias */ { "ping", 8, 0, 0xFF },
  50. { "router-advertisement", 9, 0, 0xFF },
  51. { "router-solicitation", 10, 0, 0xFF },
  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. { "parameter-problem", 12, 0, 0xFF },
  57. { "ip-header-bad", 12, 0, 0 },
  58. { "required-option-missing", 12, 1, 1 },
  59. { "timestamp-request", 13, 0, 0xFF },
  60. { "timestamp-reply", 14, 0, 0xFF },
  61. { "address-mask-request", 17, 0, 0xFF },
  62. { "address-mask-reply", 18, 0, 0xFF }
  63. };
  64. static void
  65. print_icmptypes(void)
  66. {
  67. unsigned int i;
  68. printf("Valid ICMP Types:");
  69. for (i = 0; i < ARRAY_SIZE(icmp_codes); ++i) {
  70. if (i && icmp_codes[i].type == icmp_codes[i-1].type) {
  71. if (icmp_codes[i].code_min == icmp_codes[i-1].code_min
  72. && (icmp_codes[i].code_max
  73. == icmp_codes[i-1].code_max))
  74. printf(" (%s)", icmp_codes[i].name);
  75. else
  76. printf("\n %s", icmp_codes[i].name);
  77. }
  78. else
  79. printf("\n%s", icmp_codes[i].name);
  80. }
  81. printf("\n");
  82. }
  83. static void icmp_help(void)
  84. {
  85. printf(
  86. "icmp match options:\n"
  87. "[!] --icmp-type typename match icmp type\n"
  88. "[!] --icmp-type type[/code] (or numeric type or type/code)\n");
  89. print_icmptypes();
  90. }
  91. static const struct xt_option_entry icmp_opts[] = {
  92. {.name = "icmp-type", .id = O_ICMP_TYPE, .type = XTTYPE_STRING,
  93. .flags = XTOPT_MAND | XTOPT_INVERT},
  94. XTOPT_TABLEEND,
  95. };
  96. static void
  97. parse_icmp(const char *icmptype, uint8_t *type, uint8_t code[])
  98. {
  99. static const unsigned int limit = ARRAY_SIZE(icmp_codes);
  100. unsigned int match = limit;
  101. unsigned int i;
  102. for (i = 0; i < limit; i++) {
  103. if (strncasecmp(icmp_codes[i].name, icmptype, strlen(icmptype))
  104. == 0) {
  105. if (match != limit)
  106. xtables_error(PARAMETER_PROBLEM,
  107. "Ambiguous ICMP type `%s':"
  108. " `%s' or `%s'?",
  109. icmptype,
  110. icmp_codes[match].name,
  111. icmp_codes[i].name);
  112. match = i;
  113. }
  114. }
  115. if (match != limit) {
  116. *type = icmp_codes[match].type;
  117. code[0] = icmp_codes[match].code_min;
  118. code[1] = icmp_codes[match].code_max;
  119. } else {
  120. char *slash;
  121. char buffer[strlen(icmptype) + 1];
  122. unsigned int number;
  123. strcpy(buffer, icmptype);
  124. slash = strchr(buffer, '/');
  125. if (slash)
  126. *slash = '\0';
  127. if (!xtables_strtoui(buffer, NULL, &number, 0, UINT8_MAX))
  128. xtables_error(PARAMETER_PROBLEM,
  129. "Invalid ICMP type `%s'\n", buffer);
  130. *type = number;
  131. if (slash) {
  132. if (!xtables_strtoui(slash+1, NULL, &number, 0, UINT8_MAX))
  133. xtables_error(PARAMETER_PROBLEM,
  134. "Invalid ICMP code `%s'\n",
  135. slash+1);
  136. code[0] = code[1] = number;
  137. } else {
  138. code[0] = 0;
  139. code[1] = 0xFF;
  140. }
  141. }
  142. }
  143. static void icmp_init(struct xt_entry_match *m)
  144. {
  145. struct ipt_icmp *icmpinfo = (struct ipt_icmp *)m->data;
  146. icmpinfo->type = 0xFF;
  147. icmpinfo->code[1] = 0xFF;
  148. }
  149. static void icmp_parse(struct xt_option_call *cb)
  150. {
  151. struct ipt_icmp *icmpinfo = cb->data;
  152. xtables_option_parse(cb);
  153. parse_icmp(cb->arg, &icmpinfo->type, icmpinfo->code);
  154. if (cb->invert)
  155. icmpinfo->invflags |= IPT_ICMP_INV;
  156. }
  157. static void print_icmptype(uint8_t type,
  158. uint8_t code_min, uint8_t code_max,
  159. int invert,
  160. int numeric)
  161. {
  162. if (!numeric) {
  163. unsigned int i;
  164. for (i = 0; i < ARRAY_SIZE(icmp_codes); ++i)
  165. if (icmp_codes[i].type == type
  166. && icmp_codes[i].code_min == code_min
  167. && icmp_codes[i].code_max == code_max)
  168. break;
  169. if (i != ARRAY_SIZE(icmp_codes)) {
  170. printf(" %s%s",
  171. invert ? "!" : "",
  172. icmp_codes[i].name);
  173. return;
  174. }
  175. }
  176. if (invert)
  177. printf(" !");
  178. printf("type %u", type);
  179. if (code_min == code_max)
  180. printf(" code %u", code_min);
  181. else if (code_min != 0 || code_max != 0xFF)
  182. printf(" codes %u-%u", code_min, code_max);
  183. }
  184. static void icmp_print(const void *ip, const struct xt_entry_match *match,
  185. int numeric)
  186. {
  187. const struct ipt_icmp *icmp = (struct ipt_icmp *)match->data;
  188. printf(" icmp");
  189. print_icmptype(icmp->type, icmp->code[0], icmp->code[1],
  190. icmp->invflags & IPT_ICMP_INV,
  191. numeric);
  192. if (icmp->invflags & ~IPT_ICMP_INV)
  193. printf(" Unknown invflags: 0x%X",
  194. icmp->invflags & ~IPT_ICMP_INV);
  195. }
  196. static void icmp_save(const void *ip, const struct xt_entry_match *match)
  197. {
  198. const struct ipt_icmp *icmp = (struct ipt_icmp *)match->data;
  199. if (icmp->invflags & IPT_ICMP_INV)
  200. printf(" !");
  201. /* special hack for 'any' case */
  202. if (icmp->type == 0xFF) {
  203. printf(" --icmp-type any");
  204. } else {
  205. printf(" --icmp-type %u", icmp->type);
  206. if (icmp->code[0] != 0 || icmp->code[1] != 0xFF)
  207. printf("/%u", icmp->code[0]);
  208. }
  209. }
  210. static struct xtables_match icmp_mt_reg = {
  211. .name = "icmp",
  212. .version = XTABLES_VERSION,
  213. .family = NFPROTO_IPV4,
  214. .size = XT_ALIGN(sizeof(struct ipt_icmp)),
  215. .userspacesize = XT_ALIGN(sizeof(struct ipt_icmp)),
  216. .help = icmp_help,
  217. .init = icmp_init,
  218. .print = icmp_print,
  219. .save = icmp_save,
  220. .x6_parse = icmp_parse,
  221. .x6_options = icmp_opts,
  222. };
  223. void _init(void)
  224. {
  225. xtables_register_match(&icmp_mt_reg);
  226. }