From ba91c4ec23879c3dd0a68370f08a5adf8c6cdad8 Mon Sep 17 00:00:00 2001 From: Thomas Bernard Date: Fri, 11 Dec 2015 10:29:59 +0100 Subject: [PATCH] add addmasqueraderule() see issue #166 http://miniupnp.tuxfamily.org/forum/viewtopic.php?t=1820 iptables -t nat -I POSTROUTING -o -s -p UDP --sport -j MASQUERADE --to-ports --- miniupnpd/netfilter/iptcrdr.c | 110 +++++++++++++++++++++++++++++- miniupnpd/netfilter/testiptcrdr.c | 29 +++++--- 2 files changed, 130 insertions(+), 9 deletions(-) diff --git a/miniupnpd/netfilter/iptcrdr.c b/miniupnpd/netfilter/iptcrdr.c index 28538eb..8b4831f 100644 --- a/miniupnpd/netfilter/iptcrdr.c +++ b/miniupnpd/netfilter/iptcrdr.c @@ -72,6 +72,12 @@ static int add_filter_rule(int proto, const char * rhost, const char * iaddr, unsigned short iport); +static int +addmasqueraderule(int proto, + unsigned short eport, + const char * iaddr, unsigned short iport, + const char * rhost, const char * extif); + static int addpeernatrule(int proto, const char * eaddr, unsigned short eport, @@ -222,8 +228,13 @@ add_redirect_rule2(const char * ifname, UNUSED(ifname); r = addnatrule(proto, eport, iaddr, iport, rhost); - if(r >= 0) + if(r >= 0) { add_redirect_desc(eport, proto, desc, timestamp); + r = addmasqueraderule(proto, eport, iaddr, iport, rhost, ifname); + if(r <= 0) { + syslog(LOG_NOTICE, "add_redirect_rule2(): addmasqueraderule returned %d", r); + } + } return r; } @@ -1003,6 +1014,29 @@ get_dscp_target(unsigned char dscp) return target; } +static struct ipt_entry_target * +get_masquerade_target(unsigned short port) +{ + struct ipt_entry_target * target; + struct ip_nat_multi_range * mr; + struct ip_nat_range * range; + size_t size; + + size = IPT_ALIGN(sizeof(struct ipt_entry_target)) + + IPT_ALIGN(sizeof(struct ip_nat_multi_range)); + target = calloc(1, size); + target->u.target_size = size; + strncpy(target->u.user.name, "MASQUERADE", sizeof(target->u.user.name)); + /* one ip_nat_range already included in ip_nat_multi_range */ + mr = (struct ip_nat_multi_range *)&target->data[0]; + mr->rangesize = 1; + range = &mr->range[0]; + range->min.tcp.port = range->max.tcp.port = htons(port); + /*range->min.all = range->max.all = htons(port);*/ + range->flags |= IP_NAT_RANGE_PROTO_SPECIFIED; + return target; +} + /* iptc_init_verify_and_append() * return 0 on success, -1 on failure */ static int @@ -1134,6 +1168,80 @@ addnatrule(int proto, unsigned short eport, return r; } +/* for "Port Triggering" + * Section 2.5.16 figure 2.2 in UPnP-gw-WANIPConnection-v2-Service.pdf + * iptables -t nat -I POSTROUTING -o extif -s iaddr -p UDP --sport iport -j MASQUERADE --to-ports eport + * iptables -t nat -A MINIUPNPD-PCP-PEER -o extif -s iaddr -p UDP --sport iport -j MASQUERADE --to-ports eport + */ +static int +addmasqueraderule(int proto, + unsigned short eport, + const char * iaddr, unsigned short iport, + const char * rhost, const char * extif) +{ + int r = 0; + struct ipt_entry * e; + struct ipt_entry * tmp; + struct ipt_entry_match *match = NULL; + struct ipt_entry_target *target = NULL; + + e = calloc(1, sizeof(struct ipt_entry)); + if(!e) { + syslog(LOG_ERR, "%s: calloc(%d) error", "addmasqueraderule", + (int)sizeof(struct ipt_entry)); + return -1; + } + e->ip.proto = proto; + if(proto == IPPROTO_TCP) { + match = get_tcp_match(0, iport); + } else { + match = get_udp_match(0, iport); + } + e->nfcache = NFC_IP_DST_PT; + target = get_masquerade_target(eport); + e->nfcache |= NFC_UNKNOWN; + tmp = realloc(e, sizeof(struct ipt_entry) + + match->u.match_size + + target->u.target_size); + if(!tmp) { + syslog(LOG_ERR, "%s: realloc(%d) error", "addmasqueraderule", + (int)(sizeof(struct ipt_entry) + match->u.match_size + target->u.target_size)); + free(e); + free(match); + free(target); + return -1; + } + e = tmp; + memcpy(e->elems, match, match->u.match_size); + memcpy(e->elems + match->u.match_size, target, target->u.target_size); + e->target_offset = sizeof(struct ipt_entry) + + match->u.match_size; + e->next_offset = sizeof(struct ipt_entry) + + match->u.match_size + + target->u.target_size; + if(extif != NULL) { + strncpy(e->ip.outiface, extif, sizeof(e->ip.outiface)); + memset(e->ip.outiface_mask, 0xff, strlen(e->ip.outiface) + 1);/* Include nul-terminator in match */ + } + /* internal host */ + if(iaddr && (iaddr[0] != '\0') && (0 != strcmp(iaddr, "*"))) + { + e->ip.src.s_addr = inet_addr(iaddr); + e->ip.smsk.s_addr = INADDR_NONE; + } + /* remote host */ + if(rhost && (rhost[0] != '\0') && (0 != strcmp(rhost, "*"))) { + e->ip.dst.s_addr = inet_addr(rhost); + e->ip.dmsk.s_addr = INADDR_NONE; + } + + r = iptc_init_verify_and_append("nat", miniupnpd_peer_chain, e, "addmasqueraderule"); + free(target); + free(match); + free(e); + return r; +} + /* iptables -t nat -A MINIUPNPD-PCP-PEER -s iaddr -d rhost * -p proto --sport iport --dport rport -j SNAT * --to-source ext_ip:eport */ diff --git a/miniupnpd/netfilter/testiptcrdr.c b/miniupnpd/netfilter/testiptcrdr.c index 3d35c7a..a40ba0b 100644 --- a/miniupnpd/netfilter/testiptcrdr.c +++ b/miniupnpd/netfilter/testiptcrdr.c @@ -1,7 +1,7 @@ /* $Id: testiptcrdr.c,v 1.18 2012/04/24 22:41:53 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ - * (c) 2006-2012 Thomas Bernard + * (c) 2006-2015 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ @@ -11,7 +11,7 @@ #include #include "iptcrdr.c" -#include "../commonrdr.h" +/*#include "../commonrdr.h"*/ #ifndef PRIu64 #define PRIu64 "llu" @@ -22,7 +22,9 @@ main(int argc, char ** argv) { unsigned short eport, iport; const char * iaddr; - printf("Usage %s \n", argv[0]); + int r; + int proto = IPPROTO_TCP; + printf("Usage %s [TCP/UDP]\n", argv[0]); if(argc<4) return -1; @@ -30,11 +32,22 @@ main(int argc, char ** argv) eport = (unsigned short)atoi(argv[1]); iaddr = argv[2]; iport = (unsigned short)atoi(argv[3]); - printf("trying to redirect port %hu to %s:%hu\n", eport, iaddr, iport); - if(addnatrule(IPPROTO_TCP, eport, iaddr, iport, NULL) < 0) + if(argc >= 4) { + if(strcasecmp(argv[4], "udp") == 0) + proto = IPPROTO_UDP; + } + printf("trying to redirect port %hu to %s:%hu proto %d\n", + eport, iaddr, iport, proto); + if(addnatrule(proto, eport, iaddr, iport, NULL) < 0) return -1; - if(add_filter_rule(IPPROTO_TCP, NULL, iaddr, iport) < 0) + r = addmasqueraderule(proto, eport, iaddr, iport, NULL, "ppp0"); + syslog(LOG_DEBUG, "addmasqueraderule() returned %d", r); + if(add_filter_rule(proto, NULL, iaddr, iport) < 0) return -1; + if(proto == IPPROTO_UDP) { + if(addpeernatrule(proto, "8.8.8.8"/*eaddr*/, eport, iaddr, iport, NULL, 0) < 0) + fprintf(stderr, "addpeenatrule failed\n"); + } /* test */ { unsigned short p1, p2; @@ -63,8 +76,8 @@ main(int argc, char ** argv) } printf("trying to list nat rules :\n"); list_redirect_rule(argv[1]); - printf("deleting\n"); - delete_redirect_and_filter_rules(eport, IPPROTO_TCP); + //printf("deleting\n"); + //delete_redirect_and_filter_rules(eport, proto); return 0; }