libxt_ipvs.c 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. /*
  2. * Shared library add-on to iptables to add IPVS matching.
  3. *
  4. * Detailed doc is in the kernel module source net/netfilter/xt_ipvs.c
  5. *
  6. * Author: Hannes Eder <heder@google.com>
  7. */
  8. #include <stdbool.h>
  9. #include <stdio.h>
  10. #include <string.h>
  11. #include <xtables.h>
  12. #include <linux/ip_vs.h>
  13. #include <linux/netfilter/xt_ipvs.h>
  14. enum {
  15. /* For xt_ipvs: make sure this matches up with %XT_IPVS_*'s order */
  16. O_IPVS = 0,
  17. O_VPROTO,
  18. O_VADDR,
  19. O_VPORT,
  20. O_VDIR,
  21. O_VMETHOD,
  22. O_VPORTCTL,
  23. };
  24. #define s struct xt_ipvs_mtinfo
  25. static const struct xt_option_entry ipvs_mt_opts[] = {
  26. {.name = "ipvs", .id = O_IPVS, .type = XTTYPE_NONE,
  27. .flags = XTOPT_INVERT},
  28. {.name = "vproto", .id = O_VPROTO, .type = XTTYPE_STRING,
  29. .flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, l4proto)},
  30. {.name = "vaddr", .id = O_VADDR, .type = XTTYPE_HOSTMASK,
  31. .flags = XTOPT_INVERT},
  32. {.name = "vport", .id = O_VPORT, .type = XTTYPE_PORT,
  33. .flags = XTOPT_NBO | XTOPT_INVERT | XTOPT_PUT,
  34. XTOPT_POINTER(s, vport)},
  35. {.name = "vdir", .id = O_VDIR, .type = XTTYPE_STRING},
  36. {.name = "vmethod", .id = O_VMETHOD, .type = XTTYPE_STRING,
  37. .flags = XTOPT_INVERT},
  38. {.name = "vportctl", .id = O_VPORTCTL, .type = XTTYPE_PORT,
  39. .flags = XTOPT_NBO | XTOPT_INVERT | XTOPT_PUT,
  40. XTOPT_POINTER(s, vportctl)},
  41. XTOPT_TABLEEND,
  42. };
  43. #undef s
  44. static void ipvs_mt_help(void)
  45. {
  46. printf(
  47. "IPVS match options:\n"
  48. "[!] --ipvs packet belongs to an IPVS connection\n"
  49. "\n"
  50. "Any of the following options implies --ipvs (even negated)\n"
  51. "[!] --vproto protocol VIP protocol to match; by number or name,\n"
  52. " e.g. \"tcp\"\n"
  53. "[!] --vaddr address[/mask] VIP address to match\n"
  54. "[!] --vport port VIP port to match; by number or name,\n"
  55. " e.g. \"http\"\n"
  56. " --vdir {ORIGINAL|REPLY} flow direction of packet\n"
  57. "[!] --vmethod {GATE|IPIP|MASQ} IPVS forwarding method used\n"
  58. "[!] --vportctl port VIP port of the controlling connection to\n"
  59. " match, e.g. 21 for FTP\n"
  60. );
  61. }
  62. static void ipvs_mt_parse(struct xt_option_call *cb)
  63. {
  64. struct xt_ipvs_mtinfo *data = cb->data;
  65. xtables_option_parse(cb);
  66. switch (cb->entry->id) {
  67. case O_VPROTO:
  68. data->l4proto = cb->val.protocol;
  69. break;
  70. case O_VADDR:
  71. memcpy(&data->vaddr, &cb->val.haddr, sizeof(cb->val.haddr));
  72. memcpy(&data->vmask, &cb->val.hmask, sizeof(cb->val.hmask));
  73. break;
  74. case O_VDIR:
  75. if (strcasecmp(cb->arg, "ORIGINAL") == 0) {
  76. data->bitmask |= XT_IPVS_DIR;
  77. data->invert &= ~XT_IPVS_DIR;
  78. } else if (strcasecmp(cb->arg, "REPLY") == 0) {
  79. data->bitmask |= XT_IPVS_DIR;
  80. data->invert |= XT_IPVS_DIR;
  81. } else {
  82. xtables_param_act(XTF_BAD_VALUE,
  83. "ipvs", "--vdir", cb->arg);
  84. }
  85. break;
  86. case O_VMETHOD:
  87. if (strcasecmp(cb->arg, "GATE") == 0)
  88. data->fwd_method = IP_VS_CONN_F_DROUTE;
  89. else if (strcasecmp(cb->arg, "IPIP") == 0)
  90. data->fwd_method = IP_VS_CONN_F_TUNNEL;
  91. else if (strcasecmp(cb->arg, "MASQ") == 0)
  92. data->fwd_method = IP_VS_CONN_F_MASQ;
  93. else
  94. xtables_param_act(XTF_BAD_VALUE,
  95. "ipvs", "--vmethod", cb->arg);
  96. break;
  97. }
  98. data->bitmask |= 1 << cb->entry->id;
  99. if (cb->invert)
  100. data->invert |= 1 << cb->entry->id;
  101. }
  102. static void ipvs_mt_check(struct xt_fcheck_call *cb)
  103. {
  104. struct xt_ipvs_mtinfo *info = cb->data;
  105. if (cb->xflags == 0)
  106. xtables_error(PARAMETER_PROBLEM,
  107. "IPVS: At least one option is required");
  108. if (info->bitmask & XT_IPVS_ONCE_MASK) {
  109. if (info->invert & XT_IPVS_IPVS_PROPERTY)
  110. xtables_error(PARAMETER_PROBLEM,
  111. "! --ipvs cannot be together with"
  112. " other options");
  113. info->bitmask |= XT_IPVS_IPVS_PROPERTY;
  114. }
  115. }
  116. /* Shamelessly copied from libxt_conntrack.c */
  117. static void ipvs_mt_dump_addr(const union nf_inet_addr *addr,
  118. const union nf_inet_addr *mask,
  119. unsigned int family, bool numeric)
  120. {
  121. char buf[BUFSIZ];
  122. if (family == NFPROTO_IPV4) {
  123. if (!numeric && addr->ip == 0) {
  124. printf(" anywhere");
  125. return;
  126. }
  127. if (numeric)
  128. strcpy(buf, xtables_ipaddr_to_numeric(&addr->in));
  129. else
  130. strcpy(buf, xtables_ipaddr_to_anyname(&addr->in));
  131. strcat(buf, xtables_ipmask_to_numeric(&mask->in));
  132. printf(" %s", buf);
  133. } else if (family == NFPROTO_IPV6) {
  134. if (!numeric && addr->ip6[0] == 0 && addr->ip6[1] == 0 &&
  135. addr->ip6[2] == 0 && addr->ip6[3] == 0) {
  136. printf(" anywhere");
  137. return;
  138. }
  139. if (numeric)
  140. strcpy(buf, xtables_ip6addr_to_numeric(&addr->in6));
  141. else
  142. strcpy(buf, xtables_ip6addr_to_anyname(&addr->in6));
  143. strcat(buf, xtables_ip6mask_to_numeric(&mask->in6));
  144. printf(" %s", buf);
  145. }
  146. }
  147. static void ipvs_mt_dump(const void *ip, const struct xt_ipvs_mtinfo *data,
  148. unsigned int family, bool numeric, const char *prefix)
  149. {
  150. if (data->bitmask == XT_IPVS_IPVS_PROPERTY) {
  151. if (data->invert & XT_IPVS_IPVS_PROPERTY)
  152. printf(" !");
  153. printf(" %sipvs", prefix);
  154. }
  155. if (data->bitmask & XT_IPVS_PROTO) {
  156. if (data->invert & XT_IPVS_PROTO)
  157. printf(" !");
  158. printf(" %sproto %u", prefix, data->l4proto);
  159. }
  160. if (data->bitmask & XT_IPVS_VADDR) {
  161. if (data->invert & XT_IPVS_VADDR)
  162. printf(" !");
  163. printf(" %svaddr", prefix);
  164. ipvs_mt_dump_addr(&data->vaddr, &data->vmask, family, numeric);
  165. }
  166. if (data->bitmask & XT_IPVS_VPORT) {
  167. if (data->invert & XT_IPVS_VPORT)
  168. printf(" !");
  169. printf(" %svport %u", prefix, ntohs(data->vport));
  170. }
  171. if (data->bitmask & XT_IPVS_DIR) {
  172. if (data->invert & XT_IPVS_DIR)
  173. printf(" %svdir REPLY", prefix);
  174. else
  175. printf(" %svdir ORIGINAL", prefix);
  176. }
  177. if (data->bitmask & XT_IPVS_METHOD) {
  178. if (data->invert & XT_IPVS_METHOD)
  179. printf(" !");
  180. printf(" %svmethod", prefix);
  181. switch (data->fwd_method) {
  182. case IP_VS_CONN_F_DROUTE:
  183. printf(" GATE");
  184. break;
  185. case IP_VS_CONN_F_TUNNEL:
  186. printf(" IPIP");
  187. break;
  188. case IP_VS_CONN_F_MASQ:
  189. printf(" MASQ");
  190. break;
  191. default:
  192. /* Hu? */
  193. printf(" UNKNOWN");
  194. break;
  195. }
  196. }
  197. if (data->bitmask & XT_IPVS_VPORTCTL) {
  198. if (data->invert & XT_IPVS_VPORTCTL)
  199. printf(" !");
  200. printf(" %svportctl %u", prefix, ntohs(data->vportctl));
  201. }
  202. }
  203. static void ipvs_mt4_print(const void *ip, const struct xt_entry_match *match,
  204. int numeric)
  205. {
  206. const struct xt_ipvs_mtinfo *data = (const void *)match->data;
  207. ipvs_mt_dump(ip, data, NFPROTO_IPV4, numeric, "");
  208. }
  209. static void ipvs_mt6_print(const void *ip, const struct xt_entry_match *match,
  210. int numeric)
  211. {
  212. const struct xt_ipvs_mtinfo *data = (const void *)match->data;
  213. ipvs_mt_dump(ip, data, NFPROTO_IPV6, numeric, "");
  214. }
  215. static void ipvs_mt4_save(const void *ip, const struct xt_entry_match *match)
  216. {
  217. const struct xt_ipvs_mtinfo *data = (const void *)match->data;
  218. ipvs_mt_dump(ip, data, NFPROTO_IPV4, true, "--");
  219. }
  220. static void ipvs_mt6_save(const void *ip, const struct xt_entry_match *match)
  221. {
  222. const struct xt_ipvs_mtinfo *data = (const void *)match->data;
  223. ipvs_mt_dump(ip, data, NFPROTO_IPV6, true, "--");
  224. }
  225. static struct xtables_match ipvs_matches_reg[] = {
  226. {
  227. .version = XTABLES_VERSION,
  228. .name = "ipvs",
  229. .revision = 0,
  230. .family = NFPROTO_IPV4,
  231. .size = XT_ALIGN(sizeof(struct xt_ipvs_mtinfo)),
  232. .userspacesize = XT_ALIGN(sizeof(struct xt_ipvs_mtinfo)),
  233. .help = ipvs_mt_help,
  234. .x6_parse = ipvs_mt_parse,
  235. .x6_fcheck = ipvs_mt_check,
  236. .print = ipvs_mt4_print,
  237. .save = ipvs_mt4_save,
  238. .x6_options = ipvs_mt_opts,
  239. },
  240. {
  241. .version = XTABLES_VERSION,
  242. .name = "ipvs",
  243. .revision = 0,
  244. .family = NFPROTO_IPV6,
  245. .size = XT_ALIGN(sizeof(struct xt_ipvs_mtinfo)),
  246. .userspacesize = XT_ALIGN(sizeof(struct xt_ipvs_mtinfo)),
  247. .help = ipvs_mt_help,
  248. .x6_parse = ipvs_mt_parse,
  249. .x6_fcheck = ipvs_mt_check,
  250. .print = ipvs_mt6_print,
  251. .save = ipvs_mt6_save,
  252. .x6_options = ipvs_mt_opts,
  253. },
  254. };
  255. void _init(void)
  256. {
  257. xtables_register_matches(ipvs_matches_reg,
  258. ARRAY_SIZE(ipvs_matches_reg));
  259. }