Home | History | Annotate | Download | only in extensions
      1 /*
      2  * Copyright (c) 2011 Patrick McHardy <kaber (at) 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 
     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
     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 
     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 
    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 
    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 
    148 void _init(void)
    149 {
    150 	xtables_register_target(&redirect_tg_reg);
    151 }
    152