Add initial PCP support

This commit is contained in:
Peter Tatrai 2013-07-09 15:36:53 +02:00
parent 87a7f05be6
commit 9e1ffd5cd9
30 changed files with 3322 additions and 56 deletions

View File

@ -45,12 +45,12 @@ MANINSTALLDIR = $(INSTALLPREFIX)/share/man/man8
BASEOBJS = miniupnpd.o upnphttp.o upnpdescgen.o upnpsoap.o \
upnpreplyparse.o minixml.o \
upnpredirect.o getifaddr.o daemonize.o upnpglobalvars.o \
options.o upnppermissions.o minissdp.o natpmp.o \
options.o upnppermissions.o minissdp.o natpmp.o pcpserver.o\
upnpevents.o upnputils.o getconnstatus.o \
upnppinhole.o
upnppinhole.o pcplearndscp.o
LNXOBJS = linux/getifstats.o linux/ifacewatcher.o linux/getroute.o
NETFILTEROBJS = netfilter/iptcrdr.o netfilter/iptpinhole.o
NETFILTEROBJS = netfilter/iptcrdr.o netfilter/iptpinhole.o netfilter/nfct_get.o
ALLOBJS = $(BASEOBJS) $(LNXOBJS) $(NETFILTEROBJS)
@ -69,10 +69,9 @@ CFLAGS += -DIPTABLES_143
endif
CFLAGS += $(shell pkg-config --cflags libiptc)
LIBS += $(shell pkg-config --libs-only-l libiptc)
LIBS += $(shell pkg-config --static --libs-only-l libiptc)
LDFLAGS += $(shell pkg-config --libs-only-L libiptc)
LDFLAGS += $(shell pkg-config --libs-only-other libiptc)
else
ifeq "$(wildcard /etc/gentoo-release )" ""
@ -140,6 +139,13 @@ endif # ifdef PCFILE_FOUND
LIBS += -lnfnetlink
TEST := $(shell pkg-config --atleast-version=1.0.2 libnetfilter_conntrack && pkg-config --atleast-version=1.0.3 libmnl && echo 1)
ifeq ($(TEST),1)
CFLAGS += -DUSE_NFCT
LIBS += $(shell pkg-config --static --libs-only-l libmnl)
LIBS += $(shell pkg-config --static --libs-only-l libnetfilter_conntrack)
endif # ($(TEST),1)
TESTUPNPDESCGENOBJS = testupnpdescgen.o upnpdescgen.o
EXECUTABLES = miniupnpd testupnpdescgen testgetifstats \
@ -216,7 +222,7 @@ miniupnpd.o: config.h macros.h upnpglobalvars.h upnppermissions.h
miniupnpd.o: miniupnpdtypes.h upnphttp.h upnpdescgen.h miniupnpdpath.h
miniupnpd.o: getifaddr.h upnpsoap.h options.h minissdp.h upnpredirect.h
miniupnpd.o: upnppinhole.h daemonize.h upnpevents.h natpmp.h commonrdr.h
miniupnpd.o: upnputils.h ifacewatcher.h
miniupnpd.o: upnputils.h ifacewatcher.h pcpserver.h pcplearndscp.h
upnphttp.o: config.h upnphttp.h upnpdescgen.h miniupnpdpath.h upnpsoap.h
upnphttp.o: upnpevents.h upnputils.h
upnpdescgen.o: config.h getifaddr.h upnpredirect.h upnpdescgen.h
@ -234,15 +240,18 @@ upnpredirect.o: netfilter/iptcrdr.h commonrdr.h
getifaddr.o: config.h getifaddr.h
daemonize.o: daemonize.h config.h
upnpglobalvars.o: config.h upnpglobalvars.h upnppermissions.h
upnpglobalvars.o: miniupnpdtypes.h
upnpglobalvars.o: miniupnpdtypes.h pcplearndscp.h
options.o: config.h options.h upnppermissions.h upnpglobalvars.h
options.o: miniupnpdtypes.h
options.o: miniupnpdtypes.h pcplearndscp.h
upnppermissions.o: config.h upnppermissions.h
minissdp.o: config.h upnpdescstrings.h miniupnpdpath.h upnphttp.h
minissdp.o: upnpglobalvars.h upnppermissions.h miniupnpdtypes.h minissdp.h
minissdp.o: upnputils.h getroute.h codelength.h
natpmp.o: macros.h config.h natpmp.h upnpglobalvars.h upnppermissions.h
natpmp.o: miniupnpdtypes.h getifaddr.h upnpredirect.h commonrdr.h upnputils.h
pcpserver.o: macros.h config.h pcpserver.h pcp_msg_struct.h upnpglobalvars.h
pcpserver.o: upnpredirect.h
pcplearndscp.o: config.h pcplearndscp.h
upnpevents.o: config.h upnpevents.h miniupnpdpath.h upnpglobalvars.h
upnpevents.o: upnppermissions.h miniupnpdtypes.h upnpdescgen.h upnputils.h
upnputils.o: config.h upnputils.h upnpglobalvars.h upnppermissions.h

View File

@ -12,12 +12,19 @@ case "$argv" in
--igd2) IGD2=1 ;;
--strict) STRICT=1 ;;
--leasefile) LEASEFILE=1 ;;
--pcp) PCP=1 ;;
--pcp-peer)
PCP=1
PCP_PEER=1
;;
--help|-h)
echo "Usage : $0 [options]"
echo " --ipv6 enable IPv6"
echo " --igd2 build an IGDv2 instead of an IGDv1"
echo " --strict be more strict regarding compliance with UPnP specifications"
echo " --leasefile enable lease file"
echo " --pcp enable PCP"
echo " --pcp-peer enable PCP PEER operation"
exit 1
;;
*)
@ -325,6 +332,31 @@ echo "/* Comment the following line to disable NAT-PMP operations */" >> ${CONFI
echo "#define ENABLE_NATPMP" >> ${CONFIGFILE}
echo "" >> ${CONFIGFILE}
if [ -n "$PCP" ]; then
echo "/* Comment the following line to disable PCP operations */" >> ${CONFIGFILE}
echo "#define ENABLE_PCP" >> ${CONFIGFILE}
echo "" >> ${CONFIGFILE}
else
echo "/* Uncomment the following line to enable PCP operations */" >> ${CONFIGFILE}
echo "/*#define ENABLE_PCP*/" >> ${CONFIGFILE}
echo "" >> ${CONFIGFILE}
fi
echo "#ifdef ENABLE_PCP" >> ${CONFIGFILE}
if [ -n "$PCP_PEER" ]; then
echo "/* Comment the following line to disable PCP PEER operation */" >> ${CONFIGFILE}
echo "#define PCP_PEER" >> ${CONFIGFILE}
else
echo "/* Uncomment the following line to enable PCP PEER operation */" >> ${CONFIGFILE}
echo "/*#define PCP_PEER*/" >> ${CONFIGFILE}
fi
echo "#ifdef PCP_PEER" >> ${CONFIGFILE}
echo "/*#define PCP_FLOWP*/" >> ${CONFIGFILE}
echo "#endif //PCP_PEER" >> ${CONFIGFILE}
echo "/*#define PCP_SADSCP*/" >> ${CONFIGFILE}
echo "#endif //ENABLE_PCP" >> ${CONFIGFILE}
echo "" >> ${CONFIGFILE}
echo "/* Uncomment the following line to enable generation of" >> ${CONFIGFILE}
echo " * filter rules with pf */" >> ${CONFIGFILE}
echo "/*#define PF_ENABLE_FILTER_RULES*/">> ${CONFIGFILE}

View File

@ -21,7 +21,7 @@
#include "config.h"
#include "getifaddr.h"
#if defined(USE_GETIFADDRS) || defined(ENABLE_IPV6)
#if defined(USE_GETIFADDRS) || defined(ENABLE_IPV6) || defined(ENABLE_PCP)
#include <ifaddrs.h>
#endif
@ -118,6 +118,56 @@ getifaddr(const char * ifname, char * buf, int len,
return 0;
}
#ifdef ENABLE_PCP
int getifaddr_in6(const char * ifname, struct in6_addr * addr){
struct ifaddrs * ifap;
struct ifaddrs * ife;
if(!ifname || ifname[0]=='\0')
return -1;
if(getifaddrs(&ifap)<0)
{
syslog(LOG_ERR, "getifaddrs: %m");
return -1;
}
for(ife = ifap; ife; ife = ife->ifa_next)
{
int found = 0;
/* skip other interfaces if one was specified */
if(ifname && (0 != strcmp(ifname, ife->ifa_name)))
continue;
if(ife->ifa_addr == NULL)
continue;
switch(ife->ifa_addr->sa_family)
{
case AF_INET:
addr->s6_addr32[0]=0;
addr->s6_addr32[1]=0;
addr->s6_addr32[2]=htonl(0xffff);
addr->s6_addr32[3]=((struct sockaddr_in *)ife->ifa_addr)->sin_addr.s_addr;
//inet_ntop(ife->ifa_addr->sa_family,
// &((struct sockaddr_in *)ife->ifa_addr)->sin_addr,
// buf, len);
found = 1;
break;
case AF_INET6:
if(!IN6_IS_ADDR_LOOPBACK(&addr->s6_addr32)
&& !IN6_IS_ADDR_LINKLOCAL(&addr->s6_addr32)) {
memcpy(addr->s6_addr32, &((struct sockaddr_in6 *)ife->ifa_addr)->sin6_addr, sizeof(addr->s6_addr32));
found = 1;
}
break;
}
if (found) {
break;
}
}
freeifaddrs(ifap);
return 0;
}
#endif
#ifdef ENABLE_IPV6
int
find_ipv6_addr(const char * ifname,

View File

@ -9,6 +9,7 @@
#define GETIFADDR_H_INCLUDED
struct in_addr;
struct in6_addr;
/* getifaddr()
* take a network interface name and write the
@ -18,6 +19,9 @@ int
getifaddr(const char * ifname, char * buf, int len,
struct in_addr * addr, struct in_addr * mask);
int
getifaddr_in6(const char * ifname, struct in6_addr* addr);
/* find a non link local IP v6 address for the interface.
* if ifname is NULL, look for all interfaces */
int

View File

@ -65,6 +65,11 @@
#include "upnpevents.h"
#ifdef ENABLE_NATPMP
#include "natpmp.h"
#ifdef ENABLE_PCP
#include "pcpserver.h"
#else
#define PCP_MAX_LEN 32
#endif
#endif
#include "commonrdr.h"
#include "upnputils.h"
@ -838,6 +843,20 @@ init(int argc, char * * argv, struct runtime_vars * v)
/*enablenatpmp = atoi(ary_options[i].value);*/
break;
#endif
#ifdef ENABLE_PCP
case UPNPPCPMINLIFETIME:
min_lifetime = atoi(ary_options[i].value);
if (min_lifetime > 120 ) {
min_lifetime = 120;
}
break;
case UPNPPCPMAXLIFETIME:
max_lifetime = atoi(ary_options[i].value);
if (max_lifetime > 86400 ) {
max_lifetime = 86400;
}
break;
#endif
#ifdef PF_ENABLE_FILTER_RULES
case UPNPQUICKRULES:
if(strcmp(ary_options[i].value, "no") == 0)
@ -865,6 +884,12 @@ init(int argc, char * * argv, struct runtime_vars * v)
optionsfile);
}
}
/* if lifetimes ae inverse*/
if (min_lifetime >= max_lifetime) {
fprintf(stderr, "Minimum lifetime (%lu) is greater than or equal to maximum lifetime (%lu).\n", min_lifetime, max_lifetime);
fprintf(stderr, "Check your configuration file.\n");
return 1;
}
}
#endif /* DISABLE_CONFIG_FILE */
@ -1362,7 +1387,11 @@ main(int argc, char * * argv)
syslog(LOG_INFO, "Starting%s%swith external interface %s",
#ifdef ENABLE_NATPMP
#ifdef ENABLE_PCP
GETFLAG(ENABLENATPMPMASK) ? " NAT-PMP/PCP " : " ",
#else
GETFLAG(ENABLENATPMPMASK) ? " NAT-PMP " : " ",
#endif
#else
" ",
#endif
@ -1443,12 +1472,21 @@ main(int argc, char * * argv)
if(GETFLAG(ENABLENATPMPMASK))
{
if(OpenAndConfNATPMPSockets(snatpmp) < 0)
#ifdef ENABLE_PCP
{
syslog(LOG_ERR, "Failed to open sockets for NAT-PMP/PCP.");
} else {
syslog(LOG_NOTICE, "Listening for NAT-PMP/PCP traffic on port %u",
NATPMP_PORT);
}
#else
{
syslog(LOG_ERR, "Failed to open sockets for NAT PMP.");
} else {
syslog(LOG_NOTICE, "Listening for NAT-PMP traffic on port %u",
NATPMP_PORT);
}
#endif
#if 0
ScanNATPMPforExpiration();
#endif
@ -1763,7 +1801,26 @@ main(int argc, char * * argv)
{
if((snatpmp[i] >= 0) && FD_ISSET(snatpmp[i], &readset))
{
ProcessIncomingNATPMPPacket(snatpmp[i]);
unsigned char msg_buff[PCP_MAX_LEN];
struct sockaddr_in senderaddr;
int len;
memset(msg_buff, 0, PCP_MAX_LEN);
len = ReceiveNATPMPOrPCPPacket(snatpmp[i], &senderaddr,
msg_buff, sizeof(msg_buff));
if (len < 1)
continue;
#ifdef ENABLE_PCP
if (msg_buff[0]==0) { // version equals to 0 -> means NAT-PMP
ProcessIncomingNATPMPPacket(snatpmp[i], msg_buff, len,
&senderaddr);
} else { // everything else can be PCP
ProcessIncomingPCPPacket(snatpmp[i], msg_buff, len,
&senderaddr);
}
#else
ProcessIncomingNATPMPPacket(snatpmp[i], msg_buff, len, &senderaddr);
#endif
}
}
#endif

View File

@ -31,6 +31,11 @@ enable_natpmp=yes
# enable UPNP support (default is yes)
enable_upnp=yes
# configure minimal and maximal lifetime of the port mapping in seconds
# 120s and 86400s (24h) are suggested values from PCP-base
min_lifetime=120
max_lifetime=86400
# chain names for netfilter (not used for pf or ipf).
# default is MINIUPNPD for both
#upnp_forward_chain=forwardUPnP

View File

@ -118,20 +118,21 @@ static void FillPublicAddressResponse(unsigned char * resp, in_addr_t senderaddr
#endif
}
/** read the request from the socket, process it and then send the
* response back.
/*
* Receives NATPMP and PCP packets and stores them in msg_buff.
* The sender information is stored in senderaddr.
* Returns number of bytes recevied, even if number is negative.
*/
void ProcessIncomingNATPMPPacket(int s)
int ReceiveNATPMPOrPCPPacket(int s, struct sockaddr_in* senderaddr,
unsigned char *msg_buff, size_t msg_buff_size)
{
unsigned char req[32]; /* request udp packet */
unsigned char resp[32]; /* response udp packet */
int resplen;
struct sockaddr_in senderaddr;
socklen_t senderaddrlen = sizeof(senderaddr);
socklen_t senderaddrlen = sizeof(*senderaddr);
int n;
char senderaddrstr[16];
n = recvfrom(s, req, sizeof(req), 0,
(struct sockaddr *)&senderaddr, &senderaddrlen);
n = recvfrom(s, msg_buff, msg_buff_size, 0,
(struct sockaddr *)senderaddr, &senderaddrlen);
if(n<0) {
/* EAGAIN, EWOULDBLOCK and EINTR : silently ignore (retry next time)
* other errors : log to LOG_ERR */
@ -140,14 +141,34 @@ void ProcessIncomingNATPMPPacket(int s)
errno != EINTR) {
syslog(LOG_ERR, "recvfrom(natpmp): %m");
}
return;
return n;
}
if(!inet_ntop(AF_INET, &senderaddr.sin_addr,
senderaddrstr, sizeof(senderaddrstr))) {
return n;
}
/** read the request from the socket, process it and then send the
* response back.
*/
void ProcessIncomingNATPMPPacket(int s, unsigned char *msg_buff, int len,
struct sockaddr_in *senderaddr)
{
unsigned char *req=msg_buff; /* request udp packet */
unsigned char resp[32]; /* response udp packet */
int resplen;
//struct sockaddr_in senderaddr;
//socklen_t senderaddrlen = sizeof(senderaddr);
int n = len;
char senderaddrstr[16];
if(!inet_ntop(AF_INET, &senderaddr->sin_addr,
senderaddrstr, sizeof(senderaddrstr))) {
syslog(LOG_ERR, "inet_ntop(natpmp): %m");
}
syslog(LOG_INFO, "NAT-PMP request received from %s:%hu %dbytes",
senderaddrstr, ntohs(senderaddr.sin_port), n);
senderaddrstr, ntohs(senderaddr->sin_port), n);
if(n<2 || ((((req[1]-1)&~1)==0) && n<12)) {
syslog(LOG_WARNING, "discarding NAT-PMP request (too short) %dBytes",
n);
@ -172,7 +193,7 @@ void ProcessIncomingNATPMPPacket(int s)
} else switch(req[1]) {
case 0: /* Public address request */
syslog(LOG_INFO, "NAT-PMP public address request");
FillPublicAddressResponse(resp, senderaddr.sin_addr.s_addr);
FillPublicAddressResponse(resp, senderaddr->sin_addr.s_addr);
resplen = 12;
break;
case 1: /* UDP port mapping request */
@ -244,7 +265,7 @@ void ProcessIncomingNATPMPPacket(int s)
}
eport = 0; /* to indicate correct removing of port mapping */
} else if(iport==0
|| !check_upnp_rule_against_permissions(upnppermlist, num_upnpperm, eport, senderaddr.sin_addr, iport)) {
|| !check_upnp_rule_against_permissions(upnppermlist, num_upnpperm, eport, senderaddr->sin_addr, iport)) {
resp[3] = 2; /* Not Authorized/Refused */
} else do {
r = get_redirect_rule(ext_if_name, eport, proto,
@ -306,7 +327,7 @@ void ProcessIncomingNATPMPPacket(int s)
resp[3] = 5; /* Unsupported OPCODE */
}
n = sendto(s, resp, resplen, 0,
(struct sockaddr *)&senderaddr, sizeof(senderaddr));
(struct sockaddr *)senderaddr, sizeof(*senderaddr));
if(n<0) {
syslog(LOG_ERR, "sendto(natpmp): %m");
} else if(n<resplen) {

View File

@ -20,7 +20,11 @@
int OpenAndConfNATPMPSockets(int * sockets);
void ProcessIncomingNATPMPPacket(int s);
int ReceiveNATPMPOrPCPPacket(int s, struct sockaddr_in* senderaddr,
unsigned char *msg_buff, size_t msg_buff_size);
void ProcessIncomingNATPMPPacket(int s, unsigned char *msg_buff, int len,
struct sockaddr_in *senderaddr);
#if 0
int ScanNATPMPforExpiration(void);

View File

@ -1,5 +1,5 @@
# $Id: Makefile,v 1.6 2012/04/26 13:50:48 nanard Exp $
CFLAGS?=-Wall -g -D_GNU_SOURCE -DDEBUG -Wstrict-prototypes -Wdeclaration-after-statement -ansi
CFLAGS?=-Wall -g -D_GNU_SOURCE -DDEBUG -Wstrict-prototypes -Wdeclaration-after-statement
CC = gcc
#LIBS = -liptc
@ -38,15 +38,32 @@ endif
endif
endif
all: iptcrdr.o testiptcrdr iptpinhole.o testiptpinhole
LIBS += /lib/libip4tc.so /lib/libip6tc.so
all: iptcrdr.o testiptcrdr iptpinhole.o \
testiptcrdr_peer testiptcrdr_dscp test_nfct_get
# testiptpinhole
clean:
$(RM) *.o testiptcrdr testiptpinhole
$(RM) *.o testiptcrdr testiptpinhole testiptcrdr_peer test_nfct_get \
testiptcrdr_dscp
testiptcrdr: testiptcrdr.o iptcrdr.o upnpglobalvars.o $(LIBS)
testiptcrdr: testiptcrdr.o upnpglobalvars.o $(LIBS)
testiptcrdr_peer: testiptcrdr_peer.o upnpglobalvars.o $(LIBS)
testiptcrdr_dscp: testiptcrdr_dscp.o upnpglobalvars.o $(LIBS)
testiptpinhole: testiptpinhole.o iptpinhole.o upnpglobalvars.o $(LIBS)
test_nfct_get: test_nfct_get.o test_nfct_get.o -lmnl -lnetfilter_conntrack
test_nfct_get.o: test_nfct_get.c
testiptcrdr_peer.o: testiptcrdr_peer.c
testiptcrdr_dscp.o: testiptcrdr_dscp.c
iptcrdr.o: iptcrdr.c iptcrdr.h
iptpinhole.o: iptpinhole.c iptpinhole.h

View File

@ -5,6 +5,9 @@ IPTABLES=/sbin/iptables
#display all chains relative to miniupnpd
$IPTABLES -v -n -t nat -L PREROUTING
$IPTABLES -v -n -t nat -L MINIUPNPD
$IPTABLES -v -n -t nat -L POSTROUTING
$IPTABLES -v -n -t nat -L MINIUPNPD-PCP-PEER
$IPTABLES -v -n -t mangle -L MINIUPNPD
$IPTABLES -v -n -t filter -L FORWARD
$IPTABLES -v -n -t filter -L MINIUPNPD

View File

@ -4,5 +4,7 @@ IPTABLES=/sbin/iptables
#flush all rules owned by miniupnpd
$IPTABLES -t nat -F MINIUPNPD
$IPTABLES -t nat -F MINIUPNPD-PCP-PEER
$IPTABLES -t filter -F MINIUPNPD
$IPTABLES -t mangle -F MINIUPNPD

View File

@ -13,8 +13,15 @@ $IPTABLES -t nat -N MINIUPNPD
#$IPTABLES -t nat -A PREROUTING -d $EXTIP -i $EXTIF -j MINIUPNPD
$IPTABLES -t nat -A PREROUTING -i $EXTIF -j MINIUPNPD
#adding the MINIUPNPD chain for mangle
$IPTABLES -t mangle -N MINIUPNPD
$IPTABLES -t mangle -A PREROUTING -i $EXTIF -j MINIUPNPD
#adding the MINIUPNPD chain for filter
$IPTABLES -t filter -N MINIUPNPD
#adding the rule to MINIUPNPD
$IPTABLES -t filter -A FORWARD -i $EXTIF ! -o $EXTIF -j MINIUPNPD
#adding the MINIUPNPD chain for nat
$IPTABLES -t nat -N MINIUPNPD-PCP-PEER
$IPTABLES -t nat -A POSTROUTING -o $EXTIF -j MINIUPNPD-PCP-PEER

View File

@ -13,9 +13,19 @@ $IPTABLES -t nat -F MINIUPNPD
$IPTABLES -t nat -D PREROUTING -i $EXTIF -j MINIUPNPD
$IPTABLES -t nat -X MINIUPNPD
#removing the MINIUPNPD chain for mangle
$IPTABLES -t mangle -F MINIUPNPD
$IPTABLES -t mangle -D PREROUTING -i $EXTIF -j MINIUPNPD
$IPTABLES -t mangle -X MINIUPNPD
#removing the MINIUPNPD chain for filter
$IPTABLES -t filter -F MINIUPNPD
#adding the rule to MINIUPNPD
$IPTABLES -t filter -D FORWARD -i $EXTIF ! -o $EXTIF -j MINIUPNPD
$IPTABLES -t filter -X MINIUPNPD
#removing the MINIUPNPD-PCP-PEER chain for nat
$IPTABLES -t nat -F MINIUPNPD-PCP-PEER
#removing the rule to MINIUPNPD-PCP-PEER
$IPTABLES -t nat -D POSTROUTING -o $EXTIF -j MINIUPNPD-PCP-PEER
$IPTABLES -t nat -X MINIUPNPD-PCP-PEER

View File

@ -14,6 +14,7 @@
#include <arpa/inet.h>
#include <dlfcn.h>
#include <xtables.h>
#include <linux/netfilter/xt_DSCP.h>
#include <libiptc/libiptc.h>
#include <linux/version.h>
@ -71,6 +72,17 @@ static int
add_filter_rule(int proto, const char * rhost,
const char * iaddr, unsigned short iport);
static int
addpeernatrule(int proto,
const char * eaddr, unsigned short eport,
const char * iaddr, unsigned short iport,
const char * rhost, unsigned short rport);
static int
addpeerdscprule(int proto, unsigned char dscp,
const char * iaddr, unsigned short iport,
const char * rhost, unsigned short rport);
/* dummy init and shutdown functions */
int init_redirect(void)
{
@ -215,6 +227,41 @@ add_redirect_rule2(const char * ifname,
return r;
}
/* add_redirect_rule2() */
int
add_peer_redirect_rule2(const char * ifname,
const char * rhost, unsigned short rport,
const char * eaddr, unsigned short eport,
const char * iaddr, unsigned short iport, int proto,
const char * desc, unsigned int timestamp)
{
int r;
UNUSED(ifname);
r = addpeernatrule(proto, eaddr, eport, iaddr, iport, rhost, rport);
if(r >= 0)
add_redirect_desc(eport, proto, desc, timestamp);
return r;
}
int
add_peer_dscp_rule2(const char * ifname,
const char * rhost, unsigned short rport,
unsigned char dscp,
const char * iaddr, unsigned short iport, int proto,
const char * desc, unsigned int timestamp)
{
int r;
UNUSED(ifname);
UNUSED(desc);
UNUSED(timestamp);
r = addpeerdscprule(proto, dscp, iaddr, iport, rhost, rport);
/* if(r >= 0)
add_redirect_desc(dscp, proto, desc, timestamp); */
return r;
}
int
add_filter_rule2(const char * ifname,
const char * rhost, const char * iaddr,
@ -417,6 +464,120 @@ get_redirect_rule_by_index(int index,
return r;
}
/* get_peer_rule_by_index()
* return -1 when the rule was not found */
int
get_peer_rule_by_index(int index,
char * ifname, unsigned short * eport,
char * iaddr, int iaddrlen, unsigned short * iport,
int * proto, char * desc, int desclen,
char * rhost, int rhostlen, unsigned short * rport,
unsigned int * timestamp,
u_int64_t * packets, u_int64_t * bytes)
{
int r = -1;
#if USE_INDEX_FROM_DESC_LIST && 0
r = get_redirect_desc_by_index(index, eport, proto,
desc, desclen, timestamp);
if (r==0)
{
r = get_redirect_rule(ifname, *eport, *proto, iaddr, iaddrlen, iport,
0, 0, packets, bytes);
}
#else
int i = 0;
IPTC_HANDLE h;
const struct ipt_entry * e;
const struct ipt_entry_target * target;
const struct ip_nat_multi_range * mr;
const struct ipt_entry_match *match;
UNUSED(ifname);
h = iptc_init("nat");
if(!h)
{
syslog(LOG_ERR, "get_peer_rule_by_index() : "
"iptc_init() failed : %s",
iptc_strerror(errno));
return -1;
}
if(!iptc_is_chain(miniupnpd_peer_chain, h))
{
syslog(LOG_ERR, "chain %s not found", miniupnpd_peer_chain);
}
else
{
#ifdef IPTABLES_143
for(e = iptc_first_rule(miniupnpd_peer_chain, h);
e;
e = iptc_next_rule(e, h))
#else
for(e = iptc_first_rule(miniupnpd_peer_chain, &h);
e;
e = iptc_next_rule(e, &h))
#endif
{
if(i==index)
{
*proto = e->ip.proto;
match = (const struct ipt_entry_match *)&e->elems;
if(0 == strncmp(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN))
{
const struct ipt_tcp * info;
info = (const struct ipt_tcp *)match->data;
if (rport)
*rport = info->dpts[0];
if (iport)
*iport = info->spts[0];
}
else
{
const struct ipt_udp * info;
info = (const struct ipt_udp *)match->data;
if (rport)
*rport = info->dpts[0];
if (iport)
*iport = info->spts[0];
}
target = (void *)e + e->target_offset;
mr = (const struct ip_nat_multi_range *)&target->data[0];
*eport = ntohs(mr->range[0].min.all);
get_redirect_desc(*eport, *proto, desc, desclen, timestamp);
if(packets)
*packets = e->counters.pcnt;
if(bytes)
*bytes = e->counters.bcnt;
/* rhost */
if(rhost && rhostlen > 0) {
if(e->ip.dst.s_addr) {
snprintip(rhost, rhostlen, ntohl(e->ip.dst.s_addr));
} else {
rhost[0] = '\0';
}
}
if(iaddr && iaddrlen > 0) {
if(e->ip.src.s_addr) {
snprintip(iaddr, iaddrlen, ntohl(e->ip.src.s_addr));
} else {
rhost[0] = '\0';
}
}
r = 0;
break;
}
i++;
}
}
if(h)
#ifdef IPTABLES_143
iptc_free(h);
#else
iptc_free(&h);
#endif
#endif
return r;
}
/* delete_rule_and_commit() :
* subfunction used in delete_redirect_and_filter_rules() */
static int
@ -459,7 +620,7 @@ delete_rule_and_commit(unsigned int index, IPTC_HANDLE h,
int
delete_redirect_and_filter_rules(unsigned short eport, int proto)
{
int r = -1;
int r = -1, r2 = -1;
unsigned index = 0;
unsigned i = 0;
IPTC_HANDLE h;
@ -576,22 +737,134 @@ delete_redirect_and_filter_rules(unsigned short eport, int proto)
if(iaddr != e->ip.dst.s_addr)
continue;
index = i;
syslog(LOG_INFO, "Trying to delete filter rule at index %u", index);
r = delete_rule_and_commit(index, h, miniupnpd_forward_chain, "delete_filter_rule");
h = NULL;
break;
}
}
syslog(LOG_INFO, "Trying to delete filter rule at index %u", index);
r = delete_rule_and_commit(index, h, miniupnpd_forward_chain, "delete_filter_rule");
}
if(h)
#ifdef IPTABLES_143
iptc_free(h);
#else
iptc_free(&h);
#endif
}
/*delete PEER rule*/
if((h = iptc_init("nat")))
{
i = 0;
/* we must find the right index for the filter rule */
#ifdef IPTABLES_143
for(e = iptc_first_rule(miniupnpd_peer_chain, h);
e;
e = iptc_next_rule(e, h), i++)
#else
for(e = iptc_first_rule(miniupnpd_peer_chain, &h);
e;
e = iptc_next_rule(e, &h), i++)
#endif
{
if(proto==e->ip.proto)
{
target = (void *)e + e->target_offset;
mr = (const struct ip_nat_multi_range *)&target->data[0];
if (eport != ntohs(mr->range[0].min.all)) {
continue;
}
iaddr = e->ip.src.s_addr;
match = (const struct ipt_entry_match *)&e->elems;
if(0 == strncmp(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN))
{
const struct ipt_tcp * info;
info = (const struct ipt_tcp *)match->data;
iport = info->spts[0];
}
else
{
const struct ipt_udp * info;
info = (const struct ipt_udp *)match->data;
iport = info->dpts[0];
}
index = i;
syslog(LOG_INFO, "Trying to delete peer rule at index %u", index);
r2 = delete_rule_and_commit(index, h, miniupnpd_peer_chain, "delete_peer_rule");
h = NULL;
break;
}
}
}
if(h)
#ifdef IPTABLES_143
iptc_free(h);
#else
iptc_free(&h);
#endif
/*delete DSCP rule*/
if((r2==0)&&(h = iptc_init("mangle")))
{
i = 0;
index = -1;
/* we must find the right index for the filter rule */
#ifdef IPTABLES_143
for(e = iptc_first_rule(miniupnpd_nat_chain, h);
e;
e = iptc_next_rule(e, h), i++)
#else
for(e = iptc_first_rule(miniupnpd_nat_chain, &h);
e;
e = iptc_next_rule(e, &h), i++)
#endif
{
if(proto==e->ip.proto)
{
match = (const struct ipt_entry_match *)&e->elems;
/*syslog(LOG_DEBUG, "filter rule #%u: %s %s",
i, match->u.user.name, inet_ntoa(e->ip.dst));*/
if(0 == strncmp(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN))
{
const struct ipt_tcp * info;
info = (const struct ipt_tcp *)match->data;
if(iport != info->spts[0])
continue;
}
else
{
const struct ipt_udp * info;
info = (const struct ipt_udp *)match->data;
if(iport != info->spts[0])
continue;
}
if(iaddr != e->ip.src.s_addr)
continue;
index = i;
syslog(LOG_INFO, "Trying to delete dscp rule at index %u", index);
r2 = delete_rule_and_commit(index, h, miniupnpd_nat_chain, "delete_dscp_rule");
h = NULL;
break;
}
}
if (h)
#ifdef IPTABLES_143
iptc_free(h);
#else
iptc_free(&h);
#endif
}
del_redirect_desc(eport, proto);
return r;
return r*r2;
}
/* ==================================== */
/* TODO : add the -m state --state NEW,ESTABLISHED,RELATED
* only for the filter rule */
static struct ipt_entry_match *
get_tcp_match(unsigned short dport)
get_tcp_match(unsigned short dport, unsigned short sport)
{
struct ipt_entry_match *match;
struct ipt_tcp * tcpinfo;
@ -602,15 +875,25 @@ get_tcp_match(unsigned short dport)
match->u.match_size = size;
strncpy(match->u.user.name, "tcp", sizeof(match->u.user.name));
tcpinfo = (struct ipt_tcp *)match->data;
tcpinfo->spts[0] = 0; /* all source ports */
tcpinfo->spts[1] = 0xFFFF;
tcpinfo->dpts[0] = dport; /* specified destination port */
tcpinfo->dpts[1] = dport;
if (sport == 0) {
tcpinfo->spts[0] = 0; /* all source ports */
tcpinfo->spts[1] = 0xFFFF;
} else {
tcpinfo->spts[0] = sport; /* specified source port */
tcpinfo->spts[1] = sport;
}
if (dport == 0) {
tcpinfo->dpts[0] = 0; /* all destination ports */
tcpinfo->dpts[1] = 0xFFFF;
} else {
tcpinfo->dpts[0] = dport; /* specified destination port */
tcpinfo->dpts[1] = dport;
}
return match;
}
static struct ipt_entry_match *
get_udp_match(unsigned short dport)
get_udp_match(unsigned short dport, unsigned short sport)
{
struct ipt_entry_match *match;
struct ipt_udp * udpinfo;
@ -621,10 +904,20 @@ get_udp_match(unsigned short dport)
match->u.match_size = size;
strncpy(match->u.user.name, "udp", sizeof(match->u.user.name));
udpinfo = (struct ipt_udp *)match->data;
udpinfo->spts[0] = 0; /* all source ports */
udpinfo->spts[1] = 0xFFFF;
udpinfo->dpts[0] = dport; /* specified destination port */
udpinfo->dpts[1] = dport;
if (sport == 0) {
udpinfo->spts[0] = 0; /* all source ports */
udpinfo->spts[1] = 0xFFFF;
} else {
udpinfo->spts[0] = sport; /* specified source port */
udpinfo->spts[1] = sport;
}
if (dport == 0) {
udpinfo->dpts[0] = 0; /* all destination ports */
udpinfo->dpts[1] = 0xFFFF;
} else {
udpinfo->dpts[0] = dport; /* specified destination port */
udpinfo->dpts[1] = dport;
}
return match;
}
@ -652,6 +945,48 @@ get_dnat_target(const char * daddr, unsigned short dport)
return target;
}
static struct ipt_entry_target *
get_snat_target(const char * saddr, unsigned short sport)
{
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, "SNAT", 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_ip = range->max_ip = inet_addr(saddr);
range->flags |= IP_NAT_RANGE_MAP_IPS;
range->min.all = range->max.all = htons(sport);
range->flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
return target;
}
static struct ipt_entry_target *
get_dscp_target(unsigned char dscp)
{
struct ipt_entry_target * target;
struct xt_DSCP_info * di;
size_t size;
size = IPT_ALIGN(sizeof(struct ipt_entry_target))
+ IPT_ALIGN(sizeof(struct xt_DSCP_info));
target = calloc(1, size);
target->u.target_size = size;
strncpy(target->u.user.name, "DSCP", sizeof(target->u.user.name));
/* one ip_nat_range already included in ip_nat_multi_range */
di = (struct xt_DSCP_info *)&target->data[0];
di->dscp=dscp;
return target;
}
/* iptc_init_verify_and_append()
* return 0 on success, -1 on failure */
static int
@ -739,11 +1074,11 @@ addnatrule(int proto, unsigned short eport,
e->ip.proto = proto;
if(proto == IPPROTO_TCP)
{
match = get_tcp_match(eport);
match = get_tcp_match(eport, 0);
}
else
{
match = get_udp_match(eport);
match = get_udp_match(eport, 0);
}
e->nfcache = NFC_IP_DST_PT;
target = get_dnat_target(iaddr, iport);
@ -771,6 +1106,126 @@ addnatrule(int proto, unsigned short eport,
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 */
static int
addpeernatrule(int proto,
const char * eaddr, unsigned short eport,
const char * iaddr, unsigned short iport,
const char * rhost, unsigned short rport)
{
int r = 0;
struct ipt_entry * e;
struct ipt_entry_match *match = NULL;
struct ipt_entry_target *target = NULL;
e = calloc(1, sizeof(struct ipt_entry));
e->ip.proto = proto;
/* TODO: Fill port matches and SNAT */
if(proto == IPPROTO_TCP)
{
match = get_tcp_match(rport, iport);
}
else
{
match = get_udp_match(rport, iport);
}
e->nfcache = NFC_IP_DST_PT | NFC_IP_SRC_PT;
target = get_snat_target(eaddr, eport);
e->nfcache |= NFC_UNKNOWN;
e = realloc(e, sizeof(struct ipt_entry)
+ match->u.match_size
+ target->u.target_size);
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;
/* 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, "addpeernatrule()");
free(target);
free(match);
free(e);
return r;
}
/* iptables -t mangle -A MINIUPNPD -s iaddr -d rhost
* -p proto --sport iport --dport rport -j DSCP
* --set-dscp 0xXXXX */
static int
addpeerdscprule(int proto, unsigned char dscp,
const char * iaddr, unsigned short iport,
const char * rhost, unsigned short rport)
{
int r = 0;
struct ipt_entry * e;
struct ipt_entry_match *match = NULL;
struct ipt_entry_target *target = NULL;
e = calloc(1, sizeof(struct ipt_entry));
e->ip.proto = proto;
/* TODO: Fill port matches and SNAT */
if(proto == IPPROTO_TCP)
{
match = get_tcp_match(rport, iport);
}
else
{
match = get_udp_match(rport, iport);
}
e->nfcache = NFC_IP_DST_PT | NFC_IP_SRC_PT;
target = get_dscp_target(dscp);
e->nfcache |= NFC_UNKNOWN;
e = realloc(e, sizeof(struct ipt_entry)
+ match->u.match_size
+ target->u.target_size);
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;
/* 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("mangle", miniupnpd_nat_chain, e,
"addpeerDSCPrule()");
free(target);
free(match);
free(e);
return r;
}
/* ================================= */
static struct ipt_entry_target *
get_accept_target(void)
@ -800,11 +1255,11 @@ add_filter_rule(int proto, const char * rhost,
e->ip.proto = proto;
if(proto == IPPROTO_TCP)
{
match = get_tcp_match(iport);
match = get_tcp_match(iport,0);
}
else
{
match = get_udp_match(iport);
match = get_udp_match(iport,0);
}
e->nfcache = NFC_IP_DST_PT;
e->ip.dst.s_addr = inet_addr(iaddr);

View File

@ -16,6 +16,13 @@ add_redirect_rule2(const char * ifname,
const char * iaddr, unsigned short iport, int proto,
const char * desc, unsigned int timestamp);
int
add_peer_redirect_rule2(const char * ifname,
const char * rhost, unsigned short rport,
const char * eaddr, unsigned short eport,
const char * iaddr, unsigned short iport, int proto,
const char * desc, unsigned int timestamp);
int
add_filter_rule2(const char * ifname,
const char * rhost, const char * iaddr,
@ -25,6 +32,24 @@ add_filter_rule2(const char * ifname,
int
delete_redirect_and_filter_rules(unsigned short eport, int proto);
int
add_peer_dscp_rule2(const char * ifname,
const char * rhost, unsigned short rport,
unsigned char dscp,
const char * iaddr, unsigned short iport, int proto,
const char * desc, unsigned int timestamp);
int get_nat_ext_addr(struct sockaddr* src, struct sockaddr *dst, uint8_t proto,
struct sockaddr* ret_ext);
int
get_peer_rule_by_index(int index,
char * ifname, unsigned short * eport,
char * iaddr, int iaddrlen, unsigned short * iport,
int * proto, char * desc, int desclen,
char * rhost, int rhostlen, unsigned short * rport,
unsigned int * timestamp,
u_int64_t * packets, u_int64_t * bytes);
/* for debug */
int
list_redirect_rule(const char * ifname);

View File

@ -0,0 +1,258 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <arpa/inet.h>
#ifdef USE_NFCT
#include <libmnl/libmnl.h>
#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
#include <linux/netfilter/nf_conntrack_tcp.h>
struct data_cb_s
{
struct sockaddr_storage * ext;
uint8_t found;
};
static int data_cb(const struct nlmsghdr *nlh, void *data)
{
struct nf_conntrack *ct;
struct data_cb_s * d = (struct data_cb_s*) data;
struct sockaddr_in* ext4 = (struct sockaddr_in*) d->ext;
ct = nfct_new();
if (ct == NULL)
return MNL_CB_OK;
nfct_nlmsg_parse(nlh, ct);
if (data) {
ext4->sin_addr.s_addr = nfct_get_attr_u32(ct, ATTR_REPL_IPV4_DST);
ext4->sin_port = nfct_get_attr_u16(ct, ATTR_REPL_PORT_DST);
}
d->found = 1;
nfct_destroy(ct);
return MNL_CB_OK;
}
int get_nat_ext_addr(struct sockaddr* src, struct sockaddr *dst, uint8_t proto,
struct sockaddr_storage* ret_ext)
{
struct mnl_socket *nl;
struct nlmsghdr *nlh;
struct nfgenmsg *nfh;
char buf[MNL_SOCKET_BUFFER_SIZE];
unsigned int seq, portid;
struct nf_conntrack *ct;
int ret;
struct data_cb_s data;
if ((!src)&&(!dst)) {
return 0;
}
if (src->sa_family != dst->sa_family) {
return 0;
}
nl = mnl_socket_open(NETLINK_NETFILTER);
if (nl == NULL) {
// perror("mnl_socket_open");
goto free_nl;
}
if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
// perror("mnl_socket_bind");
goto free_nl;
}
portid = mnl_socket_get_portid(nl);
memset(buf, 0, sizeof(buf));
nlh = mnl_nlmsg_put_header(buf);
nlh->nlmsg_type = (NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_GET;
nlh->nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK;
nlh->nlmsg_seq = seq = time(NULL);
nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg));
nfh->nfgen_family = src->sa_family;
nfh->version = NFNETLINK_V0;
nfh->res_id = 0;
ct = nfct_new();
if (ct == NULL) {
goto free_nl;
}
nfct_set_attr_u8(ct, ATTR_L3PROTO, src->sa_family);
if (src->sa_family == AF_INET) {
struct sockaddr_in *src4 = (struct sockaddr_in *)src;
struct sockaddr_in *dst4 = (struct sockaddr_in *)dst;
nfct_set_attr_u32(ct, ATTR_IPV4_SRC, src4->sin_addr.s_addr);
nfct_set_attr_u32(ct, ATTR_IPV4_DST, dst4->sin_addr.s_addr);
nfct_set_attr_u16(ct, ATTR_PORT_SRC, src4->sin_port);
nfct_set_attr_u16(ct, ATTR_PORT_DST, dst4->sin_port);
} else if (src->sa_family == AF_INET6) {
struct sockaddr_in6 *src6 = (struct sockaddr_in6 *)src;
struct sockaddr_in6 *dst6 = (struct sockaddr_in6 *)dst;
nfct_set_attr(ct, ATTR_IPV6_SRC, &src6->sin6_addr);
nfct_set_attr(ct, ATTR_IPV6_DST, &dst6->sin6_addr);
nfct_set_attr_u16(ct, ATTR_PORT_SRC, src6->sin6_port);
nfct_set_attr_u16(ct, ATTR_PORT_DST, dst6->sin6_port);
}
nfct_set_attr_u8(ct, ATTR_L4PROTO, proto);
nfct_nlmsg_build(nlh, ct);
ret = mnl_socket_sendto(nl, nlh, nlh->nlmsg_len);
if (ret == -1) {
goto free_ct;
}
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
data.ext = ret_ext;
data.found = 0;
while (ret > 0) {
ret = mnl_cb_run(buf, ret, seq, portid, data_cb, &data);
if (ret <= MNL_CB_STOP)
break;
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
}
free_ct:
nfct_destroy(ct);
free_nl:
mnl_socket_close(nl);
return data.found;
}
#else
#define DST "dst="
#define DST_PORT "dport="
#define SRC "src="
#define SRC_PORT "sport="
#define IP_CONNTRACK_LOCATION "/proc/net/ip_conntrack"
#define NF_CONNTRACK_LOCATION "/proc/net/nf_conntrack"
int get_nat_ext_addr(struct sockaddr* src, struct sockaddr *dst, uint8_t proto,
struct sockaddr_storage* ret_ext)
{
FILE *f;
int af;
if (!src)
return -2;
af = src->sa_family;
if ((f = fopen(NF_CONNTRACK_LOCATION, "r")) == NULL) {
if ((f = fopen(IP_CONNTRACK_LOCATION, "r")) == NULL) {
printf("could not read info about connections from the kernel, "
"make sure netfilter is enabled in kernel or by modules.\n");
return -1;
}
}
while (!feof(f)) {
char line[256], *str;
memset(line, 0, sizeof(line));
str = fgets(line, sizeof(line), f);
if (line[0] != 0) {
char *token, *saveptr;
int j;
uint8_t src_f, src_port_f, dst_f, dst_port_f;
src_f=src_port_f=dst_f=dst_port_f=0;
for (j = 1; ; j++, str = NULL) {
token = strtok_r(str, " ", &saveptr);
if (token == NULL)
break;
if ((j==2)&&(af!=atoi(token)))
break;
if ((j==4)&&(proto!=atoi(token)))
break;
if (j<=4)
continue;
if (strncmp(token, SRC, sizeof(SRC) - 1) == 0) {
char *srcip = token + sizeof(SRC) - 1;
uint32_t buf[4];
memset(buf,0,sizeof(buf));
if (inet_pton(af, srcip, buf)!=1)
break;
if (af==AF_INET) {
struct sockaddr_in *src4=(struct sockaddr_in*)src;
if (!src_f) {
if (src4->sin_addr.s_addr != buf[0])
break;
src_f = 1;
}
}
}
if (strncmp(token, SRC_PORT, sizeof(SRC_PORT) - 1) == 0) {
char *src_port = token + sizeof(SRC_PORT) - 1;
uint16_t port=atoi(src_port);
if (af==AF_INET) {
struct sockaddr_in *src4=(struct sockaddr_in*)src;
if (!src_port_f) {
if (ntohs(src4->sin_port) != port)
break;
src_port_f = 1;
}
}
}
if (strncmp(token, DST, sizeof(DST) - 1) == 0) {
char *dstip = token + sizeof(DST) - 1;
uint32_t buf[4];
memset(buf,0,sizeof(buf));
if (inet_pton(af, dstip, buf)!=1)
break;
if (af==AF_INET) {
struct sockaddr_in *dst4=(struct sockaddr_in*)dst;
if (!dst_f) {
if (dst4->sin_addr.s_addr != buf[0])
break;
dst_f = 1;
} else {
struct sockaddr_in*ret4=(struct sockaddr_in*)ret_ext;
ret_ext->ss_family = AF_INET;
ret4->sin_addr.s_addr = buf[0];
}
}
}
if (strncmp(token, DST_PORT, sizeof(DST_PORT)-1) == 0) {
char *dst_port = token + sizeof(DST_PORT) - 1;
uint16_t port=atoi(dst_port);
if (af==AF_INET) {
struct sockaddr_in *dst4=(struct sockaddr_in*)dst;
if (!dst_port_f) {
if (ntohs(dst4->sin_port) != port)
break;
dst_port_f = 1;
} else {
struct sockaddr_in*ret4=(struct sockaddr_in*)ret_ext;
ret_ext->ss_family = AF_INET;
ret4->sin_port = htons(port);
}
}
}
}
if (src_f && src_port_f && dst_f && dst_port_f) {
fclose(f);
return 1;
}
}
}
fclose(f);
return 0;
}
#endif

View File

@ -0,0 +1,50 @@
#include "nfct_get.c"
int main(int argc, char *argv[])
{
struct sockaddr_storage src, dst, ext;
char buff[INET6_ADDRSTRLEN];
if (argc!=5)
return 0;
if (1 != inet_pton(AF_INET, argv[1],
&((struct sockaddr_in*)&src)->sin_addr)) {
if (1 != inet_pton(AF_INET6, argv[1],
&((struct sockaddr_in6*) &src)->sin6_addr)) {
perror("bad input param");
} else {
((struct sockaddr_in6*)(&src))->sin6_port = htons(atoi(argv[2]));
src.ss_family = AF_INET6;
}
} else {
((struct sockaddr_in*)(&src))->sin_port = htons(atoi(argv[2]));
src.ss_family = AF_INET;
}
if (1 != inet_pton(AF_INET, argv[3],
&((struct sockaddr_in*)&dst)->sin_addr)) {
if (1 != inet_pton(AF_INET6, argv[3],
&((struct sockaddr_in6*) &dst)->sin6_addr)) {
perror("bad input param");
} else {
((struct sockaddr_in6*)(&dst))->sin6_port = htons(atoi(argv[4]));
dst.ss_family = AF_INET6;
}
} else {
((struct sockaddr_in*)(&dst))->sin_port = htons(atoi(argv[4]));
dst.ss_family = AF_INET;
}
if (get_nat_ext_addr((struct sockaddr*)&src, (struct sockaddr*)&dst,
IPPROTO_TCP, &ext)) {
printf("Ext address %s:%d\n",
inet_ntop(src.ss_family,
&((struct sockaddr_in*)&ext)->sin_addr,
buff, sizeof(buff)),
ntohs(((struct sockaddr_in*)(&ext))->sin_port));
} else {
printf("no entry\n");
}
return 0;
}

View File

@ -10,7 +10,7 @@
#include <netinet/in.h>
#include <syslog.h>
#include "iptcrdr.h"
#include "iptcrdr.c"
#include "../commonrdr.h"
#ifndef PRIu64
@ -30,13 +30,11 @@ main(int argc, char ** argv)
eport = (unsigned short)atoi(argv[1]);
iaddr = argv[2];
iport = (unsigned short)atoi(argv[3]);
#if 0
printf("trying to redirect port %hu to %s:%hu\n", eport, iaddr, iport);
if(addnatrule(IPPROTO_TCP, eport, iaddr, iport) < 0)
if(addnatrule(IPPROTO_TCP, eport, iaddr, iport, NULL) < 0)
return -1;
if(add_filter_rule(IPPROTO_TCP, iaddr, iport) < 0)
if(add_filter_rule(IPPROTO_TCP, NULL, iaddr, iport) < 0)
return -1;
#endif
/* test */
{
unsigned short p1, p2;

View File

@ -0,0 +1,73 @@
/* $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
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <syslog.h>
#include "iptcrdr.h"
#include "../commonrdr.h"
#include "iptcrdr.c"
#ifndef PRIu64
#define PRIu64 "llu"
#endif
int
main(int argc, char ** argv)
{
unsigned char dscp;
unsigned short iport, rport;
const char * iaddr, *rhost;
printf("Usage %s <dscp> <internal_ip> <internal_port> <peer_ip> <peer_port>\n", argv[0]);
if(argc<6)
return -1;
openlog("testiptcrdr_peer", LOG_PERROR|LOG_CONS, LOG_LOCAL0);
dscp = (unsigned short)atoi(argv[1]);
iaddr = argv[2];
iport = (unsigned short)atoi(argv[3]);
rhost = argv[4];
rport = (unsigned short)atoi(argv[5]);
#if 1
if(addpeerdscprule(IPPROTO_TCP, dscp, iaddr, iport, rhost, rport) < 0)
return -1;
#endif
/* test */
{
unsigned short p1, p2;
char addr[16];
int proto2;
char desc[256];
char rhost[256];
unsigned int timestamp;
u_int64_t packets, bytes;
desc[0] = '\0';
if(get_redirect_rule_by_index(0, "", &p1,
addr, sizeof(addr), &p2,
&proto2, desc, sizeof(desc),
rhost, sizeof(rhost),
&timestamp,
&packets, &bytes) < 0)
{
printf("rule not found\n");
}
else
{
printf("redirected port %hu to %s:%hu proto %d packets=%" PRIu64 " bytes=%" PRIu64 "\n",
p1, addr, p2, proto2, packets, bytes);
}
}
printf("trying to list nat rules :\n");
list_redirect_rule(argv[1]);
printf("deleting\n");
// delete_redirect_and_filter_rules(eport, IPPROTO_TCP);
return 0;
}

View File

@ -0,0 +1,74 @@
/* $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
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <syslog.h>
#include "iptcrdr.h"
#include "../commonrdr.h"
#include "iptcrdr.c"
#ifndef PRIu64
#define PRIu64 "llu"
#endif
int
main(int argc, char ** argv)
{
unsigned short eport, iport, rport;
const char * eaddr, *iaddr, *rhost;
printf("Usage %s <ext_ip> <ext_port> <internal_ip> <internal_port> <peer_ip> <peer_port>\n", argv[0]);
if(argc<6)
return -1;
openlog("testiptcrdr_peer", LOG_PERROR|LOG_CONS, LOG_LOCAL0);
eaddr = argv[1];
eport = (unsigned short)atoi(argv[2]);
iaddr = argv[3];
iport = (unsigned short)atoi(argv[4]);
rhost = argv[5];
rport = (unsigned short)atoi(argv[6]);
#if 1
printf("trying to redirect port %hu to %s:%hu\n", eport, iaddr, iport);
if(addpeernatrule(IPPROTO_TCP, eaddr, eport, iaddr, iport, rhost, rport) < 0)
return -1;
#endif
/* test */
{
unsigned short p1, p2;
char addr[16];
int proto2;
char desc[256];
char rhost[256];
unsigned int timestamp;
u_int64_t packets, bytes;
desc[0] = '\0';
if(get_redirect_rule_by_index(0, "", &p1,
addr, sizeof(addr), &p2,
&proto2, desc, sizeof(desc),
rhost, sizeof(rhost),
&timestamp,
&packets, &bytes) < 0)
{
printf("rule not found\n");
}
else
{
printf("redirected port %hu to %s:%hu proto %d packets=%" PRIu64 " bytes=%" PRIu64 "\n",
p1, addr, p2, proto2, packets, bytes);
}
}
printf("trying to list nat rules :\n");
list_redirect_rule(argv[1]);
printf("deleting\n");
delete_redirect_and_filter_rules(eport, IPPROTO_TCP);
return 0;
}

View File

@ -14,6 +14,9 @@
#include "config.h"
#include "options.h"
#include "upnppermissions.h"
#ifdef PCP_SADSCP
#include "pcplearndscp.h"
#endif // PCP_SADSPC
#include "upnpglobalvars.h"
#ifndef DISABLE_CONFIG_FILE
@ -47,6 +50,10 @@ static const struct {
#endif
#ifdef ENABLE_NATPMP
{ UPNPENABLENATPMP, "enable_natpmp"},
#endif
#ifdef ENABLE_PCP
{ UPNPPCPMINLIFETIME, "min_lifetime"},
{ UPNPPCPMAXLIFETIME, "max_lifetime"},
#endif
{ UPNPENABLE, "enable_upnp"},
#ifdef USE_PF
@ -147,6 +154,33 @@ readoptionsfile(const char * fname)
}
continue;
}
#ifdef PCP_SADSCP
/* check for DSCP values configuration */
if(0 == memcmp(name, "set_learn_dscp", sizeof("set_learn_dscp")-1) )
{
tmp = realloc(dscp_values_list, sizeof(struct dscp_values) * (num_dscp_values+1));
if(tmp == NULL)
{
fprintf(stderr, "memory allocation error. DSCP line in file %s line %d\n",
fname, linenum);
}
else
{
dscp_values_list = tmp;
/* parse the rule */
if(read_learn_dscp_line(dscp_values_list + num_dscp_values, name) >= 0)
{
num_dscp_values++;
}
else
{
fprintf(stderr, "parsing error file %s line %d : %s\n",
fname, linenum, name);
}
}
continue;
}
#endif //PCP_SADSCP
if(!(equals = strchr(name, '=')))
{
fprintf(stderr, "parsing error file %s line %d : %s\n",
@ -248,6 +282,21 @@ freeoptions(void)
upnppermlist = NULL;
num_upnpperm = 0;
}
#ifdef PCP_SADSCP
if(dscp_values_list)
{
unsigned int i;
for (i = 0; i < num_dscp_values; i++) {
if (dscp_values_list[i].app_name) {
free(dscp_values_list[i].app_name);
dscp_values_list[i].app_name = NULL;
}
}
free(dscp_values_list);
dscp_values_list = NULL;
num_dscp_values = 0;
}
#endif //PCP_SADSCP
}
#endif /* DISABLE_CONFIG_FILE */

View File

@ -32,6 +32,8 @@ enum upnpconfigoptions {
UPNPCLEANTHRESHOLD, /* clean_ruleset_threshold */
UPNPCLEANINTERVAL, /* clean_ruleset_interval */
UPNPENABLENATPMP, /* enable_natpmp */
UPNPPCPMINLIFETIME, /* minimum lifetime for PCP mapping */
UPNPPCPMAXLIFETIME, /* maximum lifetime for PCP mapping */
#ifdef USE_NETFILTER
UPNPFORWARDCHAIN,
UPNPNATCHAIN,

290
miniupnpd/pcp_msg_struct.h Normal file
View File

@ -0,0 +1,290 @@
/* $Id: pcp_msg_struct.h $ */
/* MiniUPnP project
* Website : http://miniupnp.free.fr/
* Author : Peter Tatrai
Copyright (c) 2013 by Cisco Systems, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#define PCP_OPCODE_ANNOUNCE 0
#define PCP_OPCODE_MAP 1
#define PCP_OPCODE_PEER 2
#ifdef PCP_SADSCP
#define PCP_OPCODE_SADSCP 3
#endif
/* Possible response codes sent by server, as a result of client request*/
#define PCP_SUCCESS 0
#define PCP_ERR_UNSUPP_VERSION 1
/** The version number at the start of the PCP Request
* header is not recognized by this PCP server. This is a long
* lifetime error. This document describes PCP version 2.
*/
#define PCP_ERR_NOT_AUTHORIZED 2
/**The requested operation is disabled for this PCP
* client, or the PCP client requested an operation that cannot be
* fulfilled by the PCP server's security policy. This is a long
* lifetime error.
*/
#define PCP_ERR_MALFORMED_REQUEST 3
/**The request could not be successfully parsed.
* This is a long lifetime error.
*/
#define PCP_ERR_UNSUPP_OPCODE 4
/** Unsupported Opcode. This is a long lifetime error.
*/
#define PCP_ERR_UNSUPP_OPTION 5
/**Unsupported Option. This error only occurs if the
* Option is in the mandatory-to-process range. This is a long
* lifetime error.
*/
#define PCP_ERR_MALFORMED_OPTION 6
/**Malformed Option (e.g., appears too many times,
* invalid length). This is a long lifetime error.
*/
#define PCP_ERR_NETWORK_FAILURE 7
/**The PCP server or the device it controls are
* experiencing a network failure of some sort (e.g., has not
* obtained an External IP address). This is a short lifetime error.
*/
#define PCP_ERR_NO_RESOURCES 8
/**Request is well-formed and valid, but the server has
* insufficient resources to complete the requested operation at this
* time. For example, the NAT device cannot create more mappings at
* this time, is short of CPU cycles or memory, or is unable to
* handle the request due to some other temporary condition. The
* same request may succeed in the future. This is a system-wide
* error, different from USER_EX_QUOTA. This can be used as a catch-
* all error, should no other error message be suitable. This is a
* short lifetime error.
*/
#define PCP_ERR_UNSUPP_PROTOCOL 9
/**Unsupported transport protocol, e.g. SCTP in a
* NAT that handles only UDP and TCP. This is a long lifetime error.
*/
#define PCP_ERR_USER_EX_QUOTA 10
/** This attempt to create a new mapping would exceed
* this subscriber's port quota. This is a short lifetime error.
*/
#define PCP_ERR_CANNOT_PROVIDE_EXTERNAL 11
/** The suggested external port and/or
* external address cannot be provided. This error MUST only be
* returned for:
* * MAP requests that included the PREFER_FAILURE Option
* (normal MAP requests will return an available external port)
* * MAP requests for the SCTP protocol (PREFER_FAILURE is implied)
* * PEER requests
*/
#define PCP_ERR_ADDRESS_MISMATCH 12
/** The source IP address of the request packet does
* not match the contents of the PCP Client's IP Address field, due
* to an unexpected NAT on the path between the PCP client and the
* PCP-controlled NAT or firewall. This is a long lifetime error.
*/
#define PCP_ERR_EXCESSIVE_REMOTE_PEERS 13
/** The PCP server was not able to create the
* filters in this request. This result code MUST only be returned
* if the MAP request contained the FILTER Option. See Section 13.3
* for processing information. This is a long lifetime error.
*/
typedef enum pcp_options {
PCP_OPTION_3RD_PARTY = 1,
PCP_OPTION_PREF_FAIL = 2,
PCP_OPTION_FILTER = 3,
#ifdef PCP_FLOWP
PCP_OPTION_FLOW_PRIORITY = 4, /*TODO: change it to correct value*/
#endif
} pcp_options_t;
#ifdef WIN32
#pragma warning (push)
#pragma warning (disable:4200)
#endif // WIN32
#pragma pack(push, 1)
/* PCP common request header*/
typedef struct pcp_request {
uint8_t ver;
uint8_t r_opcode;
uint16_t reserved;
uint32_t req_lifetime;
uint32_t ip[4]; /* ipv4 will be represented
by the ipv4 mapped ipv6 */
uint8_t next_data[0];
} pcp_request_t;
/* PCP common response header*/
typedef struct pcp_response {
uint8_t ver;
uint8_t r_opcode; /* R indicates Request (0) or Response (1)
Opcode is 7 bit value specifying operation MAP or PEER */
uint8_t reserved; /* reserved bits, must be 0 on transmission and must be ignored on reception */
uint8_t result_code; /* */
uint32_t lifetime; /* an unsigned 32-bit integer, in seconds {0, 2^32-1}*/
uint32_t epochtime; /* epoch indicates how long has PCP server had its current mappings
it increases by 1 every second */
uint32_t reserved1[3];/* For requests that were successfully parsed this must be sent as 0 */
uint8_t next_data[0];
} pcp_response_t;
typedef struct pcp_options_hdr {
uint8_t code; /* Most significant bit indicates if this option is mandatory (0) or optional (1) */
uint8_t reserved; /* MUST be set to 0 on transmission and MUST be ignored on reception */
uint16_t len; /* indicates the length of the enclosed data in octets (see RFC) */
uint8_t next_data[0]; /* */
} pcp_options_hdr_t;
/* same for both request and response */
typedef struct pcp_map_v2 {
uint32_t nonce[3];
uint8_t protocol;
uint8_t reserved[3];
uint16_t int_port;
uint16_t ext_port;
uint32_t ext_ip[4]; /* ipv4 will be represented
by the ipv4 mapped ipv6 */
uint8_t next_data[0];
} pcp_map_v2_t;
/* same for both request and response */
typedef struct pcp_map_v1 {
uint8_t protocol;
uint8_t reserved[3];
uint16_t int_port;
uint16_t ext_port;
uint32_t ext_ip[4]; /* ipv4 will be represented
by the ipv4 mapped ipv6 */
uint8_t next_data[0];
} pcp_map_v1_t;
/* same for both request and response */
typedef struct pcp_peer_v1 {
uint8_t protocol;
uint8_t reserved[3];
uint16_t int_port;
uint16_t ext_port;
uint32_t ext_ip[4]; /* ipv4 will be represented
by the ipv4 mapped ipv6 */
uint16_t peer_port;
uint16_t reserved1;
uint32_t peer_ip[4];
uint8_t next_data[0];
} pcp_peer_v1_t;
/* same for both request and response */
typedef struct pcp_peer_v2 {
uint32_t nonce[3];
uint8_t protocol;
uint8_t reserved[3];
uint16_t int_port;
uint16_t ext_port;
uint32_t ext_ip[4]; /* ipv4 will be represented
by the ipv4 mapped ipv6 */
uint16_t peer_port;
uint16_t reserved1;
uint32_t peer_ip[4];
uint8_t next_data[0];
} pcp_peer_v2_t;
#ifdef PCP_SADSCP
typedef struct pcp_sadscp_req {
uint32_t nonce[3];
uint8_t tolerance_fields;
uint8_t app_name_length;
char app_name[0];
} pcp_sadscp_req_t;
typedef struct pcp_sadscp_resp {
uint32_t nonce[3];
#define PCP_SADSCP_MASK ((1<<6)-1)
uint8_t a_r_dscp_value;
uint8_t reserved[3];
} pcp_sadscp_resp_t;
#endif
typedef struct pcp_prefer_fail_option {
uint8_t option;
uint8_t reserved;
uint16_t len;
uint8_t next_data[0];
} pcp_prefer_fail_option_t;
typedef struct pcp_3rd_party_option{
uint8_t option;
uint8_t reserved;
uint16_t len;
uint32_t ip[4];
uint8_t next_data[0];
} pcp_3rd_party_option_t;
#ifdef PCP_FLOWP
typedef struct pcp_flow_priority_option{
uint8_t option;
uint8_t reserved;
uint16_t len;
uint8_t dscp_up;
uint8_t dscp_down;
#define PCP_DSCP_MASK ((1<<6)-1)
uint8_t reserved2;
/* most significant bit is used for response */
uint8_t response_bit;
uint8_t next_data[0];
} pcp_flow_priority_option_t;
#endif
typedef struct pcp_filter_option {
uint8_t option;
uint8_t reserved1;
uint16_t len;
uint8_t reserved2;
uint8_t prefix_len;
uint16_t peer_port;
uint32_t peer_ip[4];
}pcp_filter_option_t;
#pragma pack(pop)
#ifdef WIN32
#pragma warning (pop)
#endif // WIN32

298
miniupnpd/pcplearndscp.c Normal file
View File

@ -0,0 +1,298 @@
/* $Id: pcplearndscp.c $ */
/* MiniUPnP project
* Website : http://miniupnp.free.fr/
* Author : Miroslav Bagljas
Copyright (c) 2013 by Cisco Systems, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <syslog.h>
#include "upnpglobalvars.h"
#include "pcplearndscp.h"
#ifdef PCP_SADSCP
void
print_dscp(void) {
unsigned int i;
for (i=0; i < num_dscp_values; i++){
syslog(LOG_DEBUG, "Appname %*.s, del %d, loss %d, jitter %d, dscp %d",
dscp_values_list[i].app_name_len,
dscp_values_list[i].app_name, dscp_values_list[i].delay,
dscp_values_list[i].loss, dscp_values_list[i].jitter,
dscp_values_list[i].dscp_value
);
}
}
int
read_learn_dscp_line(struct dscp_values *dscpvalues, char *p)
{
char * q;
size_t len;
unsigned int sizeof_first_token = sizeof("set_learn_dscp") - 1;
int af_value;
int cs_value;
/* first token: (set_learn_dscp) skip it */
while(isspace(*p))
p++;
if(0 == memcmp(p, "set_learn_dscp", sizeof_first_token))
{
p += sizeof_first_token;
}
else
{
return -1;
}
while(isspace(*p))
p++;
/* second token: name of the application */
// if
if(!(*p == '"'))
return -1;
p++;
for(q = p; !(*q == '"'); q++);
len = q - p;
if (len != 0) {
dscpvalues->app_name = strndup(p, len);
} else {
dscpvalues->app_name = NULL;
}
dscpvalues->app_name_len = len;
p = q + 1;
/* third token: delay */
while(isspace(*p))
p++;
if(!isdigit(*p))
goto exit_err_and_cleanup;
for(q = p; isdigit(*q); q++);
if(isspace(*q))
{
*q = '\0';
dscpvalues->delay = (unsigned char)atoi(p);
if (dscpvalues->delay >= 3) {
fprintf(stderr, "Wrong delay value %d in \n", dscpvalues->delay);
fprintf(stderr, "Delay can be from set {0,1,2} 0=low delay, 1=medium delay, 2=high delay\n");
goto exit_err_and_cleanup;
}
}
else
{
goto exit_err_and_cleanup;
}
p = q + 1;
/* fourth token: loss */
while(isspace(*p))
p++;
if(!isdigit(*p))
goto exit_err_and_cleanup;
for(q = p; isdigit(*q); q++);
if(isspace(*q))
{
*q = '\0';
dscpvalues->loss = (unsigned char)atoi(p);
if (dscpvalues->loss >= 3) {
fprintf(stderr, "Wrong loss value %d \n", dscpvalues->loss);
fprintf(stderr, "Delay can be from set {0,1,2} 0=low loss, 1=medium loss, 2=high loss\n");
goto exit_err_and_cleanup;
}
}
else
{
goto exit_err_and_cleanup;
}
p = q + 1;
/* fifth token: jitter */
while(isspace(*p))
p++;
if(!isdigit(*p))
goto exit_err_and_cleanup;
for(q = p; isdigit(*q); q++);
if(isspace(*q))
{
*q = '\0';
dscpvalues->jitter = (unsigned char)atoi(p);
if (dscpvalues->jitter >= 3) {
fprintf(stderr, "Wrong jitter value %d \n", dscpvalues->jitter);
fprintf(stderr, "Delay can be from set {0,1,2} 0=low jitter, 1=medium jitter, 2=high jitter \n");
goto exit_err_and_cleanup;
}
}
else
{
goto exit_err_and_cleanup;
}
p = q + 1;
while(isspace(*p))
p++;
/*{
}*/
p = q + 1;
/* sixth token: DSCP value */
while(isspace(*p))
p++;
if(!isdigit(*p) && !( toupper(*p) == 'A' && toupper(*(p+1)) == 'F') &&
!( toupper(*p) == 'C' && toupper(*(p+1)) == 'S') &&
!( toupper(*p) == 'E' && toupper(*(p+1)) == 'F')
)
goto exit_err_and_cleanup;
// for(q = p; isdigit(*q) || (toupper(*q) == 'A') || (toupper(*q) == 'F'); q++);
for(q = p; isdigit(*q) || isalpha(*q); q++);
if(isspace(*q) || *q == '\0')
{
*q = '\0';
if (toupper(*p) == 'A' && toupper(*(p+1)) == 'F'){
p = p+2;
if (*p == '\0') {
dscpvalues->dscp_value = 0;
}
else if (!isdigit(*p)) {
goto exit_err_and_cleanup;
}
else {
af_value = atoi(p);
switch(af_value) {
case 11:
dscpvalues->dscp_value = 10;
break;
case 12:
dscpvalues->dscp_value = 12;
break;
case 13:
dscpvalues->dscp_value = 14;
break;
case 21:
dscpvalues->dscp_value = 18;
break;
case 22:
dscpvalues->dscp_value = 20;
break;
case 23:
dscpvalues->dscp_value = 22;
break;
case 31:
dscpvalues->dscp_value = 26;
break;
case 32:
dscpvalues->dscp_value = 28;
break;
case 33:
dscpvalues->dscp_value = 30;
break;
case 41:
dscpvalues->dscp_value = 34;
break;
case 42:
dscpvalues->dscp_value = 36;
break;
case 43:
dscpvalues->dscp_value = 38;
break;
default:
fprintf(stderr, "Unknown AF value %u \n", af_value);
goto exit_err_and_cleanup;
}
}
}
else if (toupper(*p) == 'C' && toupper(*(p+1)) == 'S'){
p=p+2;
if (*p == '\0') {
dscpvalues->dscp_value = 0;
}
else if (!isdigit(*p)) {
fprintf(stderr, "Not digit after CS but %c \n", *p);
goto exit_err_and_cleanup;
}
else {
cs_value = atoi(p);
switch(cs_value) {
case 1:
dscpvalues->dscp_value = 8;
break;
case 2:
dscpvalues->dscp_value = 16;
break;
case 3:
dscpvalues->dscp_value = 24;
break;
case 4:
dscpvalues->dscp_value = 32;
break;
case 5:
dscpvalues->dscp_value = 40;
break;
case 6:
dscpvalues->dscp_value = 48;
break;
case 7:
dscpvalues->dscp_value = 56;
break;
default:
fprintf(stderr, "Unknown CS value %d \n", cs_value);
goto exit_err_and_cleanup;
}
}
}
else if (toupper(*p) == 'E' && toupper(*(p+1)) == 'F'){
dscpvalues->dscp_value = 46;
}
else {
dscpvalues->dscp_value = (unsigned char)atoi(p);
}
}
else
{
goto exit_err_and_cleanup;
}
return 0;
exit_err_and_cleanup:
free(dscpvalues->app_name);
dscpvalues->app_name = NULL;
dscpvalues->app_name_len = 0;
return -1;
}
#endif

51
miniupnpd/pcplearndscp.h Normal file
View File

@ -0,0 +1,51 @@
/* $Id: pcplearndscp.h $ */
/* MiniUPnP project
* Website : http://miniupnp.free.fr/
* Author : Miroslav Bagljas
Copyright (c) 2013 by Cisco Systems, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef PCPLEARNDSCP_H_INCLUDED
#define PCPLEARNDSCP_H_INCLUDED
struct dscp_values {
char *app_name;
unsigned int app_name_len;
unsigned char delay;
unsigned char loss;
unsigned char jitter;
unsigned char dscp_value;
};
// #set_learn_dscp "Webex" 1 1 1 34
int
read_learn_dscp_line(struct dscp_values *dscpvalues, char *p);
#endif // PCPLEARNDSCP_H_INCLUDED

1334
miniupnpd/pcpserver.c Normal file

File diff suppressed because it is too large Load Diff

45
miniupnpd/pcpserver.h Normal file
View File

@ -0,0 +1,45 @@
/* $Id: pcpserver.h $ */
/* MiniUPnP project
* Website : http://miniupnp.free.fr/
* Author : Peter Tatrai
Copyright (c) 2013 by Cisco Systems, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef PCPSERVER_H_INCLUDED
#define PCPSERVER_H_INCLUDED
#define PCP_MIN_LEN 24
#define PCP_MAX_LEN 1100
/*
* returns 0 upon success 1 otherwise
*/
int ProcessIncomingPCPPacket(int s, unsigned char *msg_buff, int len, \
struct sockaddr_in *senderaddr);
#endif /* PCPSERVER_H_INCLUDED */

View File

@ -32,6 +32,9 @@ unsigned long upstream_bitrate = 0;
/* startup time */
time_t startup_time = 0;
unsigned long int min_lifetime = 120;
unsigned long int max_lifetime = 86400;
int runtime_flags = 0;
const char * pidfilename = "/var/run/miniupnpd.pid";
@ -61,6 +64,10 @@ unsigned int nextnatpmptoclean_timestamp = 0;
unsigned short nextnatpmptoclean_eport = 0;
unsigned short nextnatpmptoclean_proto = 0;
#endif
#ifdef PCP_SADSCP
struct dscp_values* dscp_values_list = 0;
unsigned int num_dscp_values = 0;
#endif //PCP_SADSCP
#endif
/* For automatic removal of expired rules (with LeaseDuration) */
@ -76,6 +83,7 @@ const char * tag = 0;
/* chain name to use, both in the nat table
* and the filter table */
const char * miniupnpd_nat_chain = "MINIUPNPD";
const char * miniupnpd_peer_chain = "MINIUPNPD-PCP-PEER";
const char * miniupnpd_forward_chain = "MINIUPNPD";
#ifdef ENABLE_6FC_SERVICE
const char * miniupnpd_v6_filter_chain = "MINIUPNPD";

View File

@ -32,6 +32,10 @@ extern unsigned long upstream_bitrate;
/* statup time */
extern time_t startup_time;
extern unsigned long int min_lifetime;
extern unsigned long int max_lifetime;
/* runtime boolean flags */
extern int runtime_flags;
#define LOGPACKETSMASK 0x0001
@ -81,6 +85,10 @@ extern unsigned int nextnatpmptoclean_timestamp;
extern unsigned short nextnatpmptoclean_eport;
extern unsigned short nextnatpmptoclean_proto;
#endif
#ifdef PCP_SADSCP
extern struct dscp_values* dscp_values_list;
extern unsigned int num_dscp_values;
#endif
#endif
/* For automatic removal of expired rules (with LeaseDuration) */
@ -95,6 +103,7 @@ extern const char * tag;
#ifdef USE_NETFILTER
extern const char * miniupnpd_nat_chain;
extern const char * miniupnpd_peer_chain;
extern const char * miniupnpd_forward_chain;
#ifdef ENABLE_6FC_SERVICE
extern const char * miniupnpd_v6_filter_chain;

View File

@ -505,6 +505,32 @@ get_upnp_rules_state_list(int max_rules_number_target)
if(!tmp)
break;
}
#ifdef PCP_PEER
i=0;
while(get_peer_rule_by_index(i, /*ifname*/0, &tmp->eport, 0, 0,
&iport, &proto, 0, 0, 0,0,0, &timestamp,
&tmp->packets, &tmp->bytes) >= 0)
{
tmp->to_remove = 0;
if(timestamp > 0) {
/* need to remove this port mapping ? */
if(timestamp <= (unsigned int)current_time)
tmp->to_remove = 1;
else if((nextruletoclean_timestamp <= (unsigned int)current_time)
|| (timestamp < nextruletoclean_timestamp))
nextruletoclean_timestamp = timestamp;
}
tmp->proto = (short)proto;
/* add tmp to list */
tmp->next = list;
list = tmp;
/* prepare next iteration */
i++;
tmp = malloc(sizeof(struct rule_state));
if(!tmp)
break;
}
#endif
free(tmp);
/* remove the redirections that need to be removed */
for(p = &list, tmp = list; tmp; tmp = *p)