libipt_REDIRECT.c revision 84d758b3bc3121a5603261699c474f64672ef9f6
1c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)/* Shared library add-on to iptables to add redirect support. */ 2c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include <stdio.h> 3c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include <netdb.h> 4c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include <string.h> 5c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include <stdlib.h> 6c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include <getopt.h> 7c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include <xtables.h> 8eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include <limits.h> /* INT_MAX in ip_tables.h */ 9c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include <linux/netfilter_ipv4/ip_tables.h> 10c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include <net/netfilter/nf_nat.h> 11c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 12c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#define IPT_REDIRECT_OPT_DEST 0x01 13c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#define IPT_REDIRECT_OPT_RANDOM 0x02 14c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 15c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)static void REDIRECT_help(void) 16c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles){ 17c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) printf( 18c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)"REDIRECT target options:\n" 19c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)" --to-ports <port>[-<port>]\n" 20c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)" Port (range) to map to.\n"); 21c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)} 22c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 23c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)static const struct option REDIRECT_opts[] = { 24c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) { "to-ports", 1, NULL, '1' }, 25c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) { "random", 0, NULL, '2' }, 26c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) { .name = NULL } 275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}; 28c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 29c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)static void REDIRECT_init(struct xt_entry_target *t) 30c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles){ 31c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) struct nf_nat_multi_range *mr = (struct nf_nat_multi_range *)t->data; 321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 331320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci /* Actually, it's 0, but it's ignored at the moment. */ 341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci mr->rangesize = 1; 351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 371320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 381320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci/* Parses ports */ 391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccistatic void 401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciparse_ports(const char *arg, struct nf_nat_multi_range *mr) 41c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles){ 42c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) char *end; 43eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch unsigned int port, maxport; 443551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) 45c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) mr->range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED; 46c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 47c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (!xtables_strtoui(arg, &end, &port, 0, UINT16_MAX) && 48c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) (port = xtables_service_to_port(arg, NULL)) == (unsigned)-1) 49c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) xtables_param_act(XTF_BAD_VALUE, "REDIRECT", "--to-ports", arg); 50c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 51c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) switch (*end) { 52eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch case '\0': 53c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) mr->range[0].min.tcp.port 54c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) = mr->range[0].max.tcp.port 55c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) = htons(port); 56c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) return; 57c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) case '-': 58c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (!xtables_strtoui(end + 1, NULL, &maxport, 0, UINT16_MAX) && 59c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) (maxport = xtables_service_to_port(end + 1, NULL)) == (unsigned)-1) 60c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) break; 61c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 62c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (maxport < port) 63c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) break; 64c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 65c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) mr->range[0].min.tcp.port = htons(port); 66c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) mr->range[0].max.tcp.port = htons(maxport); 67c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) return; 68c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) default: 69c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) break; 70c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } 71c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) xtables_param_act(XTF_BAD_VALUE, "REDIRECT", "--to-ports", arg); 72c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)} 73c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 74c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)static int REDIRECT_parse(int c, char **argv, int invert, unsigned int *flags, 75c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) const void *e, struct xt_entry_target **target) 76c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles){ 77c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) const struct ipt_entry *entry = e; 78 struct nf_nat_multi_range *mr 79 = (struct nf_nat_multi_range *)(*target)->data; 80 int portok; 81 82 if (entry->ip.proto == IPPROTO_TCP 83 || entry->ip.proto == IPPROTO_UDP 84 || entry->ip.proto == IPPROTO_SCTP 85 || entry->ip.proto == IPPROTO_DCCP 86 || entry->ip.proto == IPPROTO_ICMP) 87 portok = 1; 88 else 89 portok = 0; 90 91 switch (c) { 92 case '1': 93 if (!portok) 94 xtables_error(PARAMETER_PROBLEM, 95 "Need TCP, UDP, SCTP or DCCP with port specification"); 96 97 if (xtables_check_inverse(optarg, &invert, NULL, 0, argv)) 98 xtables_error(PARAMETER_PROBLEM, 99 "Unexpected `!' after --to-ports"); 100 101 parse_ports(optarg, mr); 102 if (*flags & IPT_REDIRECT_OPT_RANDOM) 103 mr->range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM; 104 *flags |= IPT_REDIRECT_OPT_DEST; 105 return 1; 106 107 case '2': 108 if (*flags & IPT_REDIRECT_OPT_DEST) { 109 mr->range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM; 110 *flags |= IPT_REDIRECT_OPT_RANDOM; 111 } else 112 *flags |= IPT_REDIRECT_OPT_RANDOM; 113 return 1; 114 115 default: 116 return 0; 117 } 118} 119 120static void REDIRECT_print(const void *ip, const struct xt_entry_target *target, 121 int numeric) 122{ 123 const struct nf_nat_multi_range *mr = (const void *)target->data; 124 const struct nf_nat_range *r = &mr->range[0]; 125 126 if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) { 127 printf("redir ports "); 128 printf("%hu", ntohs(r->min.tcp.port)); 129 if (r->max.tcp.port != r->min.tcp.port) 130 printf("-%hu", ntohs(r->max.tcp.port)); 131 printf(" "); 132 if (mr->range[0].flags & IP_NAT_RANGE_PROTO_RANDOM) 133 printf("random "); 134 } 135} 136 137static void REDIRECT_save(const void *ip, const struct xt_entry_target *target) 138{ 139 const struct nf_nat_multi_range *mr = (const void *)target->data; 140 const struct nf_nat_range *r = &mr->range[0]; 141 142 if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) { 143 printf("--to-ports "); 144 printf("%hu", ntohs(r->min.tcp.port)); 145 if (r->max.tcp.port != r->min.tcp.port) 146 printf("-%hu", ntohs(r->max.tcp.port)); 147 printf(" "); 148 if (mr->range[0].flags & IP_NAT_RANGE_PROTO_RANDOM) 149 printf("--random "); 150 } 151} 152 153static struct xtables_target redirect_tg_reg = { 154 .name = "REDIRECT", 155 .version = XTABLES_VERSION, 156 .family = NFPROTO_IPV4, 157 .size = XT_ALIGN(sizeof(struct nf_nat_multi_range)), 158 .userspacesize = XT_ALIGN(sizeof(struct nf_nat_multi_range)), 159 .help = REDIRECT_help, 160 .init = REDIRECT_init, 161 .parse = REDIRECT_parse, 162 .print = REDIRECT_print, 163 .save = REDIRECT_save, 164 .extra_opts = REDIRECT_opts, 165}; 166 167void _init(void) 168{ 169 xtables_register_target(&redirect_tg_reg); 170} 171