15eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy/*
25eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy * Copyright (c) 2011 Patrick McHardy <kaber@trash.net>
35eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy *
45eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy * Based on Rusty Russell's IPv4 REDIRECT target. Development of IPv6 NAT
55eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy * funded by Astaro.
65eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy */
75eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy
85eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy#include <stdio.h>
95eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy#include <string.h>
105eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy#include <stdlib.h>
115eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy#include <xtables.h>
125eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy#include <limits.h> /* INT_MAX in ip_tables.h */
135eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy#include <linux/netfilter_ipv6/ip6_tables.h>
145eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy#include <linux/netfilter/nf_nat.h>
155eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy
165eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardyenum {
175eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	O_TO_PORTS = 0,
185eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	O_RANDOM,
195eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	F_TO_PORTS = 1 << O_TO_PORTS,
205eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	F_RANDOM   = 1 << O_RANDOM,
215eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy};
225eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy
235eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardystatic void REDIRECT_help(void)
245eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy{
255eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	printf(
265eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy"REDIRECT target options:\n"
275eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy" --to-ports <port>[-<port>]\n"
285eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy"				Port (range) to map to.\n"
295eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy" [--random]\n");
305eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy}
315eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy
325eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardystatic const struct xt_option_entry REDIRECT_opts[] = {
335eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	{.name = "to-ports", .id = O_TO_PORTS, .type = XTTYPE_STRING},
345eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	{.name = "random", .id = O_RANDOM, .type = XTTYPE_NONE},
355eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	XTOPT_TABLEEND,
365eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy};
375eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy
385eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy/* Parses ports */
395eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardystatic void
405eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardyparse_ports(const char *arg, struct nf_nat_range *range)
415eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy{
425eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	char *end = "";
435eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	unsigned int port, maxport;
445eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy
455eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	range->flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
465eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy
475eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	if (!xtables_strtoui(arg, &end, &port, 0, UINT16_MAX) &&
485eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	    (port = xtables_service_to_port(arg, NULL)) == (unsigned)-1)
495eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy		xtables_param_act(XTF_BAD_VALUE, "REDIRECT", "--to-ports", arg);
505eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy
515eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	switch (*end) {
525eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	case '\0':
535eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy		range->min_proto.tcp.port
545eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy			= range->max_proto.tcp.port
555eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy			= htons(port);
565eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy		return;
575eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	case '-':
585eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy		if (!xtables_strtoui(end + 1, NULL, &maxport, 0, UINT16_MAX) &&
595eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy		    (maxport = xtables_service_to_port(end + 1, NULL)) == (unsigned)-1)
605eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy			break;
615eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy
625eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy		if (maxport < port)
635eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy			break;
645eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy
655eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy		range->min_proto.tcp.port = htons(port);
665eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy		range->max_proto.tcp.port = htons(maxport);
675eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy		return;
685eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	default:
695eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy		break;
705eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	}
715eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	xtables_param_act(XTF_BAD_VALUE, "REDIRECT", "--to-ports", arg);
725eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy}
735eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy
745eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardystatic void REDIRECT_parse(struct xt_option_call *cb)
755eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy{
765eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	const struct ip6t_entry *entry = cb->xt_entry;
775eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	struct nf_nat_range *range = (void *)(*cb->target)->data;
785eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	int portok;
795eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy
805eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	if (entry->ipv6.proto == IPPROTO_TCP
815eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	    || entry->ipv6.proto == IPPROTO_UDP
825eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	    || entry->ipv6.proto == IPPROTO_SCTP
835eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	    || entry->ipv6.proto == IPPROTO_DCCP
845eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	    || entry->ipv6.proto == IPPROTO_ICMP)
855eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy		portok = 1;
865eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	else
875eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy		portok = 0;
885eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy
895eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	xtables_option_parse(cb);
905eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	switch (cb->entry->id) {
915eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	case O_TO_PORTS:
925eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy		if (!portok)
935eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy			xtables_error(PARAMETER_PROBLEM,
945eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy				   "Need TCP, UDP, SCTP or DCCP with port specification");
955eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy		parse_ports(cb->arg, range);
965eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy		if (cb->xflags & F_RANDOM)
975eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy			range->flags |= NF_NAT_RANGE_PROTO_RANDOM;
985eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy		break;
995eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	case O_RANDOM:
1005eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy		if (cb->xflags & F_TO_PORTS)
1015eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy			range->flags |= NF_NAT_RANGE_PROTO_RANDOM;
1025eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy		break;
1035eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	}
1045eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy}
1055eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy
1065eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardystatic void REDIRECT_print(const void *ip, const struct xt_entry_target *target,
1075eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy                           int numeric)
1085eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy{
1095eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	const struct nf_nat_range *range = (const void *)target->data;
1105eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy
1115eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
1125eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy		printf(" redir ports ");
1135eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy		printf("%hu", ntohs(range->min_proto.tcp.port));
1145eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy		if (range->max_proto.tcp.port != range->min_proto.tcp.port)
1155eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy			printf("-%hu", ntohs(range->max_proto.tcp.port));
1165eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy		if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
1175eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy			printf(" random");
1185eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	}
1195eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy}
1205eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy
1215eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardystatic void REDIRECT_save(const void *ip, const struct xt_entry_target *target)
1225eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy{
1235eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	const struct nf_nat_range *range = (const void *)target->data;
1245eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy
1255eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
1265eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy		printf(" --to-ports ");
1275eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy		printf("%hu", ntohs(range->min_proto.tcp.port));
1285eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy		if (range->max_proto.tcp.port != range->min_proto.tcp.port)
1295eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy			printf("-%hu", ntohs(range->max_proto.tcp.port));
1305eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy		if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
1315eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy			printf(" --random");
1325eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	}
1335eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy}
1345eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy
1355eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardystatic struct xtables_target redirect_tg_reg = {
1365eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	.name		= "REDIRECT",
1375eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	.version	= XTABLES_VERSION,
1385eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	.family		= NFPROTO_IPV6,
1395eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	.size		= XT_ALIGN(sizeof(struct nf_nat_range)),
1405eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	.userspacesize	= XT_ALIGN(sizeof(struct nf_nat_range)),
1415eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	.help		= REDIRECT_help,
1425eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	.x6_parse	= REDIRECT_parse,
1435eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	.print		= REDIRECT_print,
1445eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	.save		= REDIRECT_save,
1455eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	.x6_options	= REDIRECT_opts,
1465eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy};
1475eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy
1485eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardyvoid _init(void)
1495eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy{
1505eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy	xtables_register_target(&redirect_tg_reg);
1515eca41982d29bc25b241692d03b09b953e7a908aPatrick McHardy}
152