libip6t_SNAT.c 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. /*
  2. * Copyright (c) 2011 Patrick McHardy <kaber@trash.net>
  3. *
  4. * Based on Rusty Russell's IPv4 SNAT target. Development of IPv6 NAT
  5. * funded by Astaro.
  6. */
  7. #include <stdio.h>
  8. #include <netdb.h>
  9. #include <string.h>
  10. #include <stdlib.h>
  11. #include <xtables.h>
  12. #include <iptables.h>
  13. #include <limits.h> /* INT_MAX in ip_tables.h */
  14. #include <linux/netfilter_ipv6/ip6_tables.h>
  15. #include <linux/netfilter/nf_nat.h>
  16. enum {
  17. O_TO_SRC = 0,
  18. O_RANDOM,
  19. O_PERSISTENT,
  20. O_X_TO_SRC,
  21. F_TO_SRC = 1 << O_TO_SRC,
  22. F_RANDOM = 1 << O_RANDOM,
  23. F_X_TO_SRC = 1 << O_X_TO_SRC,
  24. };
  25. static void SNAT_help(void)
  26. {
  27. printf(
  28. "SNAT target options:\n"
  29. " --to-source [<ipaddr>[-<ipaddr>]][:port[-port]]\n"
  30. " Address to map source to.\n"
  31. "[--random] [--persistent]\n");
  32. }
  33. static const struct xt_option_entry SNAT_opts[] = {
  34. {.name = "to-source", .id = O_TO_SRC, .type = XTTYPE_STRING,
  35. .flags = XTOPT_MAND | XTOPT_MULTI},
  36. {.name = "random", .id = O_RANDOM, .type = XTTYPE_NONE},
  37. {.name = "persistent", .id = O_PERSISTENT, .type = XTTYPE_NONE},
  38. XTOPT_TABLEEND,
  39. };
  40. /* Ranges expected in network order. */
  41. static void
  42. parse_to(const char *orig_arg, int portok, struct nf_nat_range *range)
  43. {
  44. char *arg, *start, *end = NULL, *colon = NULL, *dash, *error;
  45. const struct in6_addr *ip;
  46. arg = strdup(orig_arg);
  47. if (arg == NULL)
  48. xtables_error(RESOURCE_PROBLEM, "strdup");
  49. start = strchr(arg, '[');
  50. if (start == NULL) {
  51. start = arg;
  52. /* Lets assume one colon is port information. Otherwise its an IPv6 address */
  53. colon = strchr(arg, ':');
  54. if (colon && strchr(colon+1, ':'))
  55. colon = NULL;
  56. }
  57. else {
  58. start++;
  59. end = strchr(start, ']');
  60. if (end == NULL)
  61. xtables_error(PARAMETER_PROBLEM,
  62. "Invalid address format");
  63. *end = '\0';
  64. colon = strchr(end + 1, ':');
  65. }
  66. if (colon) {
  67. int port;
  68. if (!portok)
  69. xtables_error(PARAMETER_PROBLEM,
  70. "Need TCP, UDP, SCTP or DCCP with port specification");
  71. range->flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
  72. port = atoi(colon+1);
  73. if (port <= 0 || port > 65535)
  74. xtables_error(PARAMETER_PROBLEM,
  75. "Port `%s' not valid\n", colon+1);
  76. error = strchr(colon+1, ':');
  77. if (error)
  78. xtables_error(PARAMETER_PROBLEM,
  79. "Invalid port:port syntax - use dash\n");
  80. dash = strchr(colon, '-');
  81. if (!dash) {
  82. range->min_proto.tcp.port
  83. = range->max_proto.tcp.port
  84. = htons(port);
  85. } else {
  86. int maxport;
  87. maxport = atoi(dash + 1);
  88. if (maxport <= 0 || maxport > 65535)
  89. xtables_error(PARAMETER_PROBLEM,
  90. "Port `%s' not valid\n", dash+1);
  91. if (maxport < port)
  92. /* People are stupid. */
  93. xtables_error(PARAMETER_PROBLEM,
  94. "Port range `%s' funky\n", colon+1);
  95. range->min_proto.tcp.port = htons(port);
  96. range->max_proto.tcp.port = htons(maxport);
  97. }
  98. /* Starts with colon or [] colon? No IP info...*/
  99. if (colon == arg || colon == arg+2) {
  100. free(arg);
  101. return;
  102. }
  103. *colon = '\0';
  104. }
  105. range->flags |= NF_NAT_RANGE_MAP_IPS;
  106. dash = strchr(start, '-');
  107. if (colon && dash && dash > colon)
  108. dash = NULL;
  109. if (dash)
  110. *dash = '\0';
  111. ip = xtables_numeric_to_ip6addr(start);
  112. if (!ip)
  113. xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
  114. start);
  115. range->min_addr.in6 = *ip;
  116. if (dash) {
  117. ip = xtables_numeric_to_ip6addr(dash + 1);
  118. if (!ip)
  119. xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
  120. dash+1);
  121. range->max_addr.in6 = *ip;
  122. } else
  123. range->max_addr = range->min_addr;
  124. free(arg);
  125. return;
  126. }
  127. static void SNAT_parse(struct xt_option_call *cb)
  128. {
  129. const struct ip6t_entry *entry = cb->xt_entry;
  130. struct nf_nat_range *range = cb->data;
  131. int portok;
  132. if (entry->ipv6.proto == IPPROTO_TCP ||
  133. entry->ipv6.proto == IPPROTO_UDP ||
  134. entry->ipv6.proto == IPPROTO_SCTP ||
  135. entry->ipv6.proto == IPPROTO_DCCP ||
  136. entry->ipv6.proto == IPPROTO_ICMP)
  137. portok = 1;
  138. else
  139. portok = 0;
  140. xtables_option_parse(cb);
  141. switch (cb->entry->id) {
  142. case O_TO_SRC:
  143. if (cb->xflags & F_X_TO_SRC) {
  144. if (!kernel_version)
  145. get_kernel_version();
  146. if (kernel_version > LINUX_VERSION(2, 6, 10))
  147. xtables_error(PARAMETER_PROBLEM,
  148. "SNAT: Multiple --to-source not supported");
  149. }
  150. parse_to(cb->arg, portok, range);
  151. break;
  152. case O_PERSISTENT:
  153. range->flags |= NF_NAT_RANGE_PERSISTENT;
  154. break;
  155. }
  156. }
  157. static void SNAT_fcheck(struct xt_fcheck_call *cb)
  158. {
  159. static const unsigned int f = F_TO_SRC | F_RANDOM;
  160. struct nf_nat_range *range = cb->data;
  161. if ((cb->xflags & f) == f)
  162. range->flags |= NF_NAT_RANGE_PROTO_RANDOM;
  163. }
  164. static void print_range(const struct nf_nat_range *range)
  165. {
  166. if (range->flags & NF_NAT_RANGE_MAP_IPS) {
  167. if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED)
  168. printf("[");
  169. printf("%s", xtables_ip6addr_to_numeric(&range->min_addr.in6));
  170. if (memcmp(&range->min_addr, &range->max_addr,
  171. sizeof(range->min_addr)))
  172. printf("-%s", xtables_ip6addr_to_numeric(&range->max_addr.in6));
  173. if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED)
  174. printf("]");
  175. }
  176. if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
  177. printf(":");
  178. printf("%hu", ntohs(range->min_proto.tcp.port));
  179. if (range->max_proto.tcp.port != range->min_proto.tcp.port)
  180. printf("-%hu", ntohs(range->max_proto.tcp.port));
  181. }
  182. }
  183. static void SNAT_print(const void *ip, const struct xt_entry_target *target,
  184. int numeric)
  185. {
  186. const struct nf_nat_range *range = (const void *)target->data;
  187. printf(" to:");
  188. print_range(range);
  189. if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
  190. printf(" random");
  191. if (range->flags & NF_NAT_RANGE_PERSISTENT)
  192. printf(" persistent");
  193. }
  194. static void SNAT_save(const void *ip, const struct xt_entry_target *target)
  195. {
  196. const struct nf_nat_range *range = (const void *)target->data;
  197. printf(" --to-source ");
  198. print_range(range);
  199. if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
  200. printf(" --random");
  201. if (range->flags & NF_NAT_RANGE_PERSISTENT)
  202. printf(" --persistent");
  203. }
  204. static struct xtables_target snat_tg_reg = {
  205. .name = "SNAT",
  206. .version = XTABLES_VERSION,
  207. .family = NFPROTO_IPV6,
  208. .revision = 1,
  209. .size = XT_ALIGN(sizeof(struct nf_nat_range)),
  210. .userspacesize = XT_ALIGN(sizeof(struct nf_nat_range)),
  211. .help = SNAT_help,
  212. .x6_parse = SNAT_parse,
  213. .x6_fcheck = SNAT_fcheck,
  214. .print = SNAT_print,
  215. .save = SNAT_save,
  216. .x6_options = SNAT_opts,
  217. };
  218. void _init(void)
  219. {
  220. xtables_register_target(&snat_tg_reg);
  221. }