libipt_REDIRECT.c revision 69f564e3890976461de0016cd81171ff8bfa8353
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/* Shared library add-on to iptables to add redirect support. */
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <stdio.h>
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <netdb.h>
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string.h>
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <stdlib.h>
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <getopt.h>
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <xtables.h>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <limits.h> /* INT_MAX in ip_tables.h */
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <linux/netfilter_ipv4/ip_tables.h>
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <net/netfilter/nf_nat.h>
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define IPT_REDIRECT_OPT_DEST	0x01
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define IPT_REDIRECT_OPT_RANDOM	0x02
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static void REDIRECT_help(void)
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	printf(
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"REDIRECT target options:\n"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)" --to-ports <port>[-<port>]\n"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"				Port (range) to map to.\n");
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const struct option REDIRECT_opts[] = {
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	{ "to-ports", 1, NULL, '1' },
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	{ "random", 0, NULL, '2' },
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	{ .name = NULL }
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static void REDIRECT_init(struct xt_entry_target *t)
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	struct nf_nat_multi_range *mr = (struct nf_nat_multi_range *)t->data;
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	/* Actually, it's 0, but it's ignored at the moment. */
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	mr->rangesize = 1;
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
381320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci/* Parses ports */
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static void
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)parse_ports(const char *arg, struct nf_nat_multi_range *mr)
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	const char *dash;
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	int port;
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	mr->range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (strchr(arg, '.'))
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		xtables_error(PARAMETER_PROBLEM, "IP address not permitted\n");
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	port = atoi(arg);
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (port == 0)
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		port = xtables_service_to_port(arg, NULL);
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (port == 0 || port > 65535)
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		xtables_error(PARAMETER_PROBLEM, "Port \"%s\" not valid\n", arg);
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	dash = strchr(arg, '-');
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (!dash) {
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		mr->range[0].min.tcp.port
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			= mr->range[0].max.tcp.port
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			= htons(port);
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	} else {
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		int maxport;
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		maxport = atoi(dash + 1);
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		if (maxport == 0 || maxport > 65535)
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			xtables_error(PARAMETER_PROBLEM,
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				   "Port `%s' not valid\n", dash+1);
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		if (maxport < port)
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			/* People are stupid. */
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			xtables_error(PARAMETER_PROBLEM,
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				   "Port range `%s' funky\n", arg);
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		mr->range[0].min.tcp.port = htons(port);
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		mr->range[0].max.tcp.port = htons(maxport);
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static int REDIRECT_parse(int c, char **argv, int invert, unsigned int *flags,
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          const void *e, struct xt_entry_target **target)
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	const struct ipt_entry *entry = e;
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	struct nf_nat_multi_range *mr
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		= (struct nf_nat_multi_range *)(*target)->data;
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	int portok;
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (entry->ip.proto == IPPROTO_TCP
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	    || entry->ip.proto == IPPROTO_UDP
881320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci	    || entry->ip.proto == IPPROTO_SCTP
891320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci	    || entry->ip.proto == IPPROTO_DCCP
901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci	    || entry->ip.proto == IPPROTO_ICMP)
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		portok = 1;
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	else
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		portok = 0;
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	switch (c) {
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	case '1':
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		if (!portok)
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			xtables_error(PARAMETER_PROBLEM,
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				   "Need TCP, UDP, SCTP or DCCP with port specification");
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		if (xtables_check_inverse(optarg, &invert, NULL, 0))
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			xtables_error(PARAMETER_PROBLEM,
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				   "Unexpected `!' after --to-ports");
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		parse_ports(optarg, mr);
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		if (*flags & IPT_REDIRECT_OPT_RANDOM)
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			mr->range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		*flags |= IPT_REDIRECT_OPT_DEST;
1091320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci		return 1;
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	case '2':
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		if (*flags & IPT_REDIRECT_OPT_DEST) {
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			mr->range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			*flags |= IPT_REDIRECT_OPT_RANDOM;
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		} else
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			*flags |= IPT_REDIRECT_OPT_RANDOM;
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		return 1;
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	default:
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		return 0;
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static void REDIRECT_print(const void *ip, const struct xt_entry_target *target,
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           int numeric)
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	const struct nf_nat_multi_range *mr = (const void *)target->data;
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	const struct nf_nat_range *r = &mr->range[0];
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		printf("redir ports ");
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		printf("%hu", ntohs(r->min.tcp.port));
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		if (r->max.tcp.port != r->min.tcp.port)
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			printf("-%hu", ntohs(r->max.tcp.port));
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		printf(" ");
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		if (mr->range[0].flags & IP_NAT_RANGE_PROTO_RANDOM)
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			printf("random ");
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static void REDIRECT_save(const void *ip, const struct xt_entry_target *target)
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	const struct nf_nat_multi_range *mr = (const void *)target->data;
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	const struct nf_nat_range *r = &mr->range[0];
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		printf("--to-ports ");
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		printf("%hu", ntohs(r->min.tcp.port));
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		if (r->max.tcp.port != r->min.tcp.port)
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			printf("-%hu", ntohs(r->max.tcp.port));
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		printf(" ");
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		if (mr->range[0].flags & IP_NAT_RANGE_PROTO_RANDOM)
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			printf("--random ");
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1561320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1571320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccistatic struct xtables_target redirect_tg_reg = {
1581320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci	.name		= "REDIRECT",
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	.version	= XTABLES_VERSION,
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	.family		= NFPROTO_IPV4,
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	.size		= XT_ALIGN(sizeof(struct nf_nat_multi_range)),
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	.userspacesize	= XT_ALIGN(sizeof(struct nf_nat_multi_range)),
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	.help		= REDIRECT_help,
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	.init		= REDIRECT_init,
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 	.parse		= REDIRECT_parse,
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	.print		= REDIRECT_print,
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	.save		= REDIRECT_save,
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	.extra_opts	= REDIRECT_opts,
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void _init(void)
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	xtables_register_target(&redirect_tg_reg);
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1751320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci