Home | History | Annotate | Download | only in extensions
      1 #include <stdio.h>
      2 #include <string.h>
      3 #include <stdlib.h>
      4 #include <xtables.h>
      5 #include <limits.h> /* INT_MAX in ip_tables.h */
      6 #include <linux/netfilter_ipv4/ip_tables.h>
      7 #include <linux/netfilter/nf_nat.h>
      8 
      9 enum {
     10 	O_TO_PORTS = 0,
     11 	O_RANDOM,
     12 	F_TO_PORTS = 1 << O_TO_PORTS,
     13 	F_RANDOM   = 1 << O_RANDOM,
     14 };
     15 
     16 static void REDIRECT_help(void)
     17 {
     18 	printf(
     19 "REDIRECT target options:\n"
     20 " --to-ports <port>[-<port>]\n"
     21 "				Port (range) to map to.\n"
     22 " [--random]\n");
     23 }
     24 
     25 static const struct xt_option_entry REDIRECT_opts[] = {
     26 	{.name = "to-ports", .id = O_TO_PORTS, .type = XTTYPE_STRING},
     27 	{.name = "random", .id = O_RANDOM, .type = XTTYPE_NONE},
     28 	XTOPT_TABLEEND,
     29 };
     30 
     31 static void REDIRECT_init(struct xt_entry_target *t)
     32 {
     33 	struct nf_nat_ipv4_multi_range_compat *mr = (struct nf_nat_ipv4_multi_range_compat *)t->data;
     34 
     35 	/* Actually, it's 0, but it's ignored at the moment. */
     36 	mr->rangesize = 1;
     37 }
     38 
     39 /* Parses ports */
     40 static void
     41 parse_ports(const char *arg, struct nf_nat_ipv4_multi_range_compat *mr)
     42 {
     43 	char *end = "";
     44 	unsigned int port, maxport;
     45 
     46 	mr->range[0].flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
     47 
     48 	if (!xtables_strtoui(arg, &end, &port, 0, UINT16_MAX) &&
     49 	    (port = xtables_service_to_port(arg, NULL)) == (unsigned)-1)
     50 		xtables_param_act(XTF_BAD_VALUE, "REDIRECT", "--to-ports", arg);
     51 
     52 	switch (*end) {
     53 	case '\0':
     54 		mr->range[0].min.tcp.port
     55 			= mr->range[0].max.tcp.port
     56 			= htons(port);
     57 		return;
     58 	case '-':
     59 		if (!xtables_strtoui(end + 1, NULL, &maxport, 0, UINT16_MAX) &&
     60 		    (maxport = xtables_service_to_port(end + 1, NULL)) == (unsigned)-1)
     61 			break;
     62 
     63 		if (maxport < port)
     64 			break;
     65 
     66 		mr->range[0].min.tcp.port = htons(port);
     67 		mr->range[0].max.tcp.port = htons(maxport);
     68 		return;
     69 	default:
     70 		break;
     71 	}
     72 	xtables_param_act(XTF_BAD_VALUE, "REDIRECT", "--to-ports", arg);
     73 }
     74 
     75 static void REDIRECT_parse(struct xt_option_call *cb)
     76 {
     77 	const struct ipt_entry *entry = cb->xt_entry;
     78 	struct nf_nat_ipv4_multi_range_compat *mr = (void *)(*cb->target)->data;
     79 	int portok;
     80 
     81 	if (entry->ip.proto == IPPROTO_TCP
     82 	    || entry->ip.proto == IPPROTO_UDP
     83 	    || entry->ip.proto == IPPROTO_SCTP
     84 	    || entry->ip.proto == IPPROTO_DCCP
     85 	    || entry->ip.proto == IPPROTO_ICMP)
     86 		portok = 1;
     87 	else
     88 		portok = 0;
     89 
     90 	xtables_option_parse(cb);
     91 	switch (cb->entry->id) {
     92 	case O_TO_PORTS:
     93 		if (!portok)
     94 			xtables_error(PARAMETER_PROBLEM,
     95 				   "Need TCP, UDP, SCTP or DCCP with port specification");
     96 		parse_ports(cb->arg, mr);
     97 		if (cb->xflags & F_RANDOM)
     98 			mr->range[0].flags |= NF_NAT_RANGE_PROTO_RANDOM;
     99 		break;
    100 	case O_RANDOM:
    101 		if (cb->xflags & F_TO_PORTS)
    102 			mr->range[0].flags |= NF_NAT_RANGE_PROTO_RANDOM;
    103 		break;
    104 	}
    105 }
    106 
    107 static void REDIRECT_print(const void *ip, const struct xt_entry_target *target,
    108                            int numeric)
    109 {
    110 	const struct nf_nat_ipv4_multi_range_compat *mr = (const void *)target->data;
    111 	const struct nf_nat_ipv4_range *r = &mr->range[0];
    112 
    113 	if (r->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
    114 		printf(" redir ports ");
    115 		printf("%hu", ntohs(r->min.tcp.port));
    116 		if (r->max.tcp.port != r->min.tcp.port)
    117 			printf("-%hu", ntohs(r->max.tcp.port));
    118 		if (mr->range[0].flags & NF_NAT_RANGE_PROTO_RANDOM)
    119 			printf(" random");
    120 	}
    121 }
    122 
    123 static void REDIRECT_save(const void *ip, const struct xt_entry_target *target)
    124 {
    125 	const struct nf_nat_ipv4_multi_range_compat *mr = (const void *)target->data;
    126 	const struct nf_nat_ipv4_range *r = &mr->range[0];
    127 
    128 	if (r->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
    129 		printf(" --to-ports ");
    130 		printf("%hu", ntohs(r->min.tcp.port));
    131 		if (r->max.tcp.port != r->min.tcp.port)
    132 			printf("-%hu", ntohs(r->max.tcp.port));
    133 		if (mr->range[0].flags & NF_NAT_RANGE_PROTO_RANDOM)
    134 			printf(" --random");
    135 	}
    136 }
    137 
    138 static int REDIRECT_xlate(struct xt_xlate *xl,
    139 			  const struct xt_xlate_tg_params *params)
    140 {
    141 	const struct nf_nat_ipv4_multi_range_compat *mr =
    142 		(const void *)params->target->data;
    143 	const struct nf_nat_ipv4_range *r = &mr->range[0];
    144 
    145 	if (r->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
    146 		xt_xlate_add(xl, "redirect to :%hu", ntohs(r->min.tcp.port));
    147 		if (r->max.tcp.port != r->min.tcp.port)
    148 			xt_xlate_add(xl, "-%hu ", ntohs(r->max.tcp.port));
    149 		if (mr->range[0].flags & NF_NAT_RANGE_PROTO_RANDOM)
    150 			xt_xlate_add(xl, " random ");
    151 	}
    152 
    153 	return 1;
    154 }
    155 
    156 static struct xtables_target redirect_tg_reg = {
    157 	.name		= "REDIRECT",
    158 	.version	= XTABLES_VERSION,
    159 	.family		= NFPROTO_IPV4,
    160 	.size		= XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)),
    161 	.userspacesize	= XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)),
    162 	.help		= REDIRECT_help,
    163 	.init		= REDIRECT_init,
    164  	.x6_parse	= REDIRECT_parse,
    165 	.print		= REDIRECT_print,
    166 	.save		= REDIRECT_save,
    167 	.x6_options	= REDIRECT_opts,
    168 	.xlate		= REDIRECT_xlate,
    169 };
    170 
    171 void _init(void)
    172 {
    173 	xtables_register_target(&redirect_tg_reg);
    174 }
    175