add addmasqueraderule()

see issue #166
http://miniupnp.tuxfamily.org/forum/viewtopic.php?t=1820

iptables -t nat -I POSTROUTING -o <extif> -s <iaddr> -p UDP --sport <iport> -j MASQUERADE --to-ports <eport>
This commit is contained in:
Thomas Bernard 2015-12-11 10:29:59 +01:00
parent f4324d45e5
commit ba91c4ec23
2 changed files with 130 additions and 9 deletions

View File

@ -72,6 +72,12 @@ static int
add_filter_rule(int proto, const char * rhost, add_filter_rule(int proto, const char * rhost,
const char * iaddr, unsigned short iport); 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 static int
addpeernatrule(int proto, addpeernatrule(int proto,
const char * eaddr, unsigned short eport, const char * eaddr, unsigned short eport,
@ -222,8 +228,13 @@ add_redirect_rule2(const char * ifname,
UNUSED(ifname); UNUSED(ifname);
r = addnatrule(proto, eport, iaddr, iport, rhost); r = addnatrule(proto, eport, iaddr, iport, rhost);
if(r >= 0) if(r >= 0) {
add_redirect_desc(eport, proto, desc, timestamp); 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; return r;
} }
@ -1003,6 +1014,29 @@ get_dscp_target(unsigned char dscp)
return target; 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() /* iptc_init_verify_and_append()
* return 0 on success, -1 on failure */ * return 0 on success, -1 on failure */
static int static int
@ -1134,6 +1168,80 @@ addnatrule(int proto, unsigned short eport,
return r; 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 /* iptables -t nat -A MINIUPNPD-PCP-PEER -s iaddr -d rhost
* -p proto --sport iport --dport rport -j SNAT * -p proto --sport iport --dport rport -j SNAT
* --to-source ext_ip:eport */ * --to-source ext_ip:eport */

View File

@ -1,7 +1,7 @@
/* $Id: testiptcrdr.c,v 1.18 2012/04/24 22:41:53 nanard Exp $ */ /* $Id: testiptcrdr.c,v 1.18 2012/04/24 22:41:53 nanard Exp $ */
/* MiniUPnP project /* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * 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 * This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */ * in the LICENCE file provided within the distribution */
@ -11,7 +11,7 @@
#include <syslog.h> #include <syslog.h>
#include "iptcrdr.c" #include "iptcrdr.c"
#include "../commonrdr.h" /*#include "../commonrdr.h"*/
#ifndef PRIu64 #ifndef PRIu64
#define PRIu64 "llu" #define PRIu64 "llu"
@ -22,7 +22,9 @@ main(int argc, char ** argv)
{ {
unsigned short eport, iport; unsigned short eport, iport;
const char * iaddr; const char * iaddr;
printf("Usage %s <ext_port> <internal_ip> <internal_port>\n", argv[0]); int r;
int proto = IPPROTO_TCP;
printf("Usage %s <ext_port> <internal_ip> <internal_port> [TCP/UDP]\n", argv[0]);
if(argc<4) if(argc<4)
return -1; return -1;
@ -30,11 +32,22 @@ main(int argc, char ** argv)
eport = (unsigned short)atoi(argv[1]); eport = (unsigned short)atoi(argv[1]);
iaddr = argv[2]; iaddr = argv[2];
iport = (unsigned short)atoi(argv[3]); iport = (unsigned short)atoi(argv[3]);
printf("trying to redirect port %hu to %s:%hu\n", eport, iaddr, iport); if(argc >= 4) {
if(addnatrule(IPPROTO_TCP, eport, iaddr, iport, NULL) < 0) 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; 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; 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 */ /* test */
{ {
unsigned short p1, p2; unsigned short p1, p2;
@ -63,8 +76,8 @@ main(int argc, char ** argv)
} }
printf("trying to list nat rules :\n"); printf("trying to list nat rules :\n");
list_redirect_rule(argv[1]); list_redirect_rule(argv[1]);
printf("deleting\n"); //printf("deleting\n");
delete_redirect_and_filter_rules(eport, IPPROTO_TCP); //delete_redirect_and_filter_rules(eport, proto);
return 0; return 0;
} }