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