1 /*
2  * Copyright (c) 2011 Patrick McHardy <kaber@trash.net>
3  *
4  * Based on Rusty Russell's IPv4 REDIRECT target. Development of IPv6 NAT
5  * funded by Astaro.
6  */
7 
8 #include <stdio.h>
9 #include <string.h>
10 #include <stdlib.h>
11 #include <xtables.h>
12 #include <limits.h> /* INT_MAX in ip_tables.h */
13 #include <linux/netfilter_ipv6/ip6_tables.h>
14 #include <linux/netfilter/nf_nat.h>
15 
16 enum {
17 	O_TO_PORTS = 0,
18 	O_RANDOM,
19 	F_TO_PORTS = 1 << O_TO_PORTS,
20 	F_RANDOM   = 1 << O_RANDOM,
21 };
22 
REDIRECT_help(void)23 static void REDIRECT_help(void)
24 {
25 	printf(
26 "REDIRECT target options:\n"
27 " --to-ports <port>[-<port>]\n"
28 "				Port (range) to map to.\n"
29 " [--random]\n");
30 }
31 
32 static const struct xt_option_entry REDIRECT_opts[] = {
33 	{.name = "to-ports", .id = O_TO_PORTS, .type = XTTYPE_STRING},
34 	{.name = "random", .id = O_RANDOM, .type = XTTYPE_NONE},
35 	XTOPT_TABLEEND,
36 };
37 
38 /* Parses ports */
39 static void
parse_ports(const char * arg,struct nf_nat_range * range)40 parse_ports(const char *arg, struct nf_nat_range *range)
41 {
42 	char *end = "";
43 	unsigned int port, maxport;
44 
45 	range->flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
46 
47 	if (!xtables_strtoui(arg, &end, &port, 0, UINT16_MAX) &&
48 	    (port = xtables_service_to_port(arg, NULL)) == (unsigned)-1)
49 		xtables_param_act(XTF_BAD_VALUE, "REDIRECT", "--to-ports", arg);
50 
51 	switch (*end) {
52 	case '\0':
53 		range->min_proto.tcp.port
54 			= range->max_proto.tcp.port
55 			= htons(port);
56 		return;
57 	case '-':
58 		if (!xtables_strtoui(end + 1, NULL, &maxport, 0, UINT16_MAX) &&
59 		    (maxport = xtables_service_to_port(end + 1, NULL)) == (unsigned)-1)
60 			break;
61 
62 		if (maxport < port)
63 			break;
64 
65 		range->min_proto.tcp.port = htons(port);
66 		range->max_proto.tcp.port = htons(maxport);
67 		return;
68 	default:
69 		break;
70 	}
71 	xtables_param_act(XTF_BAD_VALUE, "REDIRECT", "--to-ports", arg);
72 }
73 
REDIRECT_parse(struct xt_option_call * cb)74 static void REDIRECT_parse(struct xt_option_call *cb)
75 {
76 	const struct ip6t_entry *entry = cb->xt_entry;
77 	struct nf_nat_range *range = (void *)(*cb->target)->data;
78 	int portok;
79 
80 	if (entry->ipv6.proto == IPPROTO_TCP
81 	    || entry->ipv6.proto == IPPROTO_UDP
82 	    || entry->ipv6.proto == IPPROTO_SCTP
83 	    || entry->ipv6.proto == IPPROTO_DCCP
84 	    || entry->ipv6.proto == IPPROTO_ICMP)
85 		portok = 1;
86 	else
87 		portok = 0;
88 
89 	xtables_option_parse(cb);
90 	switch (cb->entry->id) {
91 	case O_TO_PORTS:
92 		if (!portok)
93 			xtables_error(PARAMETER_PROBLEM,
94 				   "Need TCP, UDP, SCTP or DCCP with port specification");
95 		parse_ports(cb->arg, range);
96 		if (cb->xflags & F_RANDOM)
97 			range->flags |= NF_NAT_RANGE_PROTO_RANDOM;
98 		break;
99 	case O_RANDOM:
100 		if (cb->xflags & F_TO_PORTS)
101 			range->flags |= NF_NAT_RANGE_PROTO_RANDOM;
102 		break;
103 	}
104 }
105 
REDIRECT_print(const void * ip,const struct xt_entry_target * target,int numeric)106 static void REDIRECT_print(const void *ip, const struct xt_entry_target *target,
107                            int numeric)
108 {
109 	const struct nf_nat_range *range = (const void *)target->data;
110 
111 	if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
112 		printf(" redir ports ");
113 		printf("%hu", ntohs(range->min_proto.tcp.port));
114 		if (range->max_proto.tcp.port != range->min_proto.tcp.port)
115 			printf("-%hu", ntohs(range->max_proto.tcp.port));
116 		if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
117 			printf(" random");
118 	}
119 }
120 
REDIRECT_save(const void * ip,const struct xt_entry_target * target)121 static void REDIRECT_save(const void *ip, const struct xt_entry_target *target)
122 {
123 	const struct nf_nat_range *range = (const void *)target->data;
124 
125 	if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
126 		printf(" --to-ports ");
127 		printf("%hu", ntohs(range->min_proto.tcp.port));
128 		if (range->max_proto.tcp.port != range->min_proto.tcp.port)
129 			printf("-%hu", ntohs(range->max_proto.tcp.port));
130 		if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
131 			printf(" --random");
132 	}
133 }
134 
135 static struct xtables_target redirect_tg_reg = {
136 	.name		= "REDIRECT",
137 	.version	= XTABLES_VERSION,
138 	.family		= NFPROTO_IPV6,
139 	.size		= XT_ALIGN(sizeof(struct nf_nat_range)),
140 	.userspacesize	= XT_ALIGN(sizeof(struct nf_nat_range)),
141 	.help		= REDIRECT_help,
142 	.x6_parse	= REDIRECT_parse,
143 	.print		= REDIRECT_print,
144 	.save		= REDIRECT_save,
145 	.x6_options	= REDIRECT_opts,
146 };
147 
_init(void)148 void _init(void)
149 {
150 	xtables_register_target(&redirect_tg_reg);
151 }
152