From 9e1ffd5cd9836053cfd83d95014c729ad0e36872 Mon Sep 17 00:00:00 2001 From: Peter Tatrai Date: Tue, 9 Jul 2013 15:36:53 +0200 Subject: [PATCH] Add initial PCP support --- miniupnpd/Makefile.linux | 25 +- miniupnpd/genconfig.sh | 32 + miniupnpd/getifaddr.c | 52 +- miniupnpd/getifaddr.h | 4 + miniupnpd/miniupnpd.c | 59 +- miniupnpd/miniupnpd.conf | 5 + miniupnpd/natpmp.c | 57 +- miniupnpd/natpmp.h | 6 +- miniupnpd/netfilter/Makefile | 25 +- miniupnpd/netfilter/iptables_display.sh | 3 + miniupnpd/netfilter/iptables_flush.sh | 2 + miniupnpd/netfilter/iptables_init.sh | 7 + miniupnpd/netfilter/iptables_removeall.sh | 10 + miniupnpd/netfilter/iptcrdr.c | 491 +++++++- miniupnpd/netfilter/iptcrdr.h | 25 + miniupnpd/netfilter/nfct_get.c | 258 ++++ miniupnpd/netfilter/test_nfct_get.c | 50 + miniupnpd/netfilter/testiptcrdr.c | 8 +- miniupnpd/netfilter/testiptcrdr_dscp.c | 73 ++ miniupnpd/netfilter/testiptcrdr_peer.c | 74 ++ miniupnpd/options.c | 49 + miniupnpd/options.h | 2 + miniupnpd/pcp_msg_struct.h | 290 +++++ miniupnpd/pcplearndscp.c | 298 +++++ miniupnpd/pcplearndscp.h | 51 + miniupnpd/pcpserver.c | 1334 +++++++++++++++++++++ miniupnpd/pcpserver.h | 45 + miniupnpd/upnpglobalvars.c | 8 + miniupnpd/upnpglobalvars.h | 9 + miniupnpd/upnpredirect.c | 26 + 30 files changed, 3322 insertions(+), 56 deletions(-) create mode 100644 miniupnpd/netfilter/nfct_get.c create mode 100644 miniupnpd/netfilter/test_nfct_get.c create mode 100644 miniupnpd/netfilter/testiptcrdr_dscp.c create mode 100644 miniupnpd/netfilter/testiptcrdr_peer.c create mode 100644 miniupnpd/pcp_msg_struct.h create mode 100644 miniupnpd/pcplearndscp.c create mode 100644 miniupnpd/pcplearndscp.h create mode 100644 miniupnpd/pcpserver.c create mode 100644 miniupnpd/pcpserver.h diff --git a/miniupnpd/Makefile.linux b/miniupnpd/Makefile.linux index 900f702..35c96ae 100644 --- a/miniupnpd/Makefile.linux +++ b/miniupnpd/Makefile.linux @@ -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 diff --git a/miniupnpd/genconfig.sh b/miniupnpd/genconfig.sh index 9e08327..ac546ff 100755 --- a/miniupnpd/genconfig.sh +++ b/miniupnpd/genconfig.sh @@ -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} diff --git a/miniupnpd/getifaddr.c b/miniupnpd/getifaddr.c index 8752e2c..2a84852 100644 --- a/miniupnpd/getifaddr.c +++ b/miniupnpd/getifaddr.c @@ -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 #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, diff --git a/miniupnpd/getifaddr.h b/miniupnpd/getifaddr.h index 77f77e8..a04ee15 100644 --- a/miniupnpd/getifaddr.h +++ b/miniupnpd/getifaddr.h @@ -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 diff --git a/miniupnpd/miniupnpd.c b/miniupnpd/miniupnpd.c index c1fe908..2d770a0 100644 --- a/miniupnpd/miniupnpd.c +++ b/miniupnpd/miniupnpd.c @@ -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 diff --git a/miniupnpd/miniupnpd.conf b/miniupnpd/miniupnpd.conf index e34e088..53957af 100644 --- a/miniupnpd/miniupnpd.conf +++ b/miniupnpd/miniupnpd.conf @@ -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 diff --git a/miniupnpd/natpmp.c b/miniupnpd/natpmp.c index 2add04a..daad014 100644 --- a/miniupnpd/natpmp.c +++ b/miniupnpd/natpmp.c @@ -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 #include #include +#include #include #include @@ -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); diff --git a/miniupnpd/netfilter/iptcrdr.h b/miniupnpd/netfilter/iptcrdr.h index 861b51e..3d00ebe 100644 --- a/miniupnpd/netfilter/iptcrdr.h +++ b/miniupnpd/netfilter/iptcrdr.h @@ -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); diff --git a/miniupnpd/netfilter/nfct_get.c b/miniupnpd/netfilter/nfct_get.c new file mode 100644 index 0000000..cbffd28 --- /dev/null +++ b/miniupnpd/netfilter/nfct_get.c @@ -0,0 +1,258 @@ +#include +#include +#include +#include +#include +#include + +#ifdef USE_NFCT +#include +#include + +#include + +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 diff --git a/miniupnpd/netfilter/test_nfct_get.c b/miniupnpd/netfilter/test_nfct_get.c new file mode 100644 index 0000000..af8c07d --- /dev/null +++ b/miniupnpd/netfilter/test_nfct_get.c @@ -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; +} diff --git a/miniupnpd/netfilter/testiptcrdr.c b/miniupnpd/netfilter/testiptcrdr.c index a4c5c3b..3d35c7a 100644 --- a/miniupnpd/netfilter/testiptcrdr.c +++ b/miniupnpd/netfilter/testiptcrdr.c @@ -10,7 +10,7 @@ #include #include -#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; diff --git a/miniupnpd/netfilter/testiptcrdr_dscp.c b/miniupnpd/netfilter/testiptcrdr_dscp.c new file mode 100644 index 0000000..4ad56d7 --- /dev/null +++ b/miniupnpd/netfilter/testiptcrdr_dscp.c @@ -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 +#include +#include +#include + +#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 \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), + ×tamp, + &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; +} + diff --git a/miniupnpd/netfilter/testiptcrdr_peer.c b/miniupnpd/netfilter/testiptcrdr_peer.c new file mode 100644 index 0000000..6e1d175 --- /dev/null +++ b/miniupnpd/netfilter/testiptcrdr_peer.c @@ -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 +#include +#include +#include + +#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 \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), + ×tamp, + &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; +} + diff --git a/miniupnpd/options.c b/miniupnpd/options.c index dc886a5..32cb800 100644 --- a/miniupnpd/options.c +++ b/miniupnpd/options.c @@ -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 */ diff --git a/miniupnpd/options.h b/miniupnpd/options.h index 270ce00..5a44e27 100644 --- a/miniupnpd/options.h +++ b/miniupnpd/options.h @@ -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, diff --git a/miniupnpd/pcp_msg_struct.h b/miniupnpd/pcp_msg_struct.h new file mode 100644 index 0000000..3c956ea --- /dev/null +++ b/miniupnpd/pcp_msg_struct.h @@ -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 diff --git a/miniupnpd/pcplearndscp.c b/miniupnpd/pcplearndscp.c new file mode 100644 index 0000000..4a325f6 --- /dev/null +++ b/miniupnpd/pcplearndscp.c @@ -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 +#include +#include +#include +#include + +#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 diff --git a/miniupnpd/pcplearndscp.h b/miniupnpd/pcplearndscp.h new file mode 100644 index 0000000..ab720ff --- /dev/null +++ b/miniupnpd/pcplearndscp.h @@ -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 diff --git a/miniupnpd/pcpserver.c b/miniupnpd/pcpserver.c new file mode 100644 index 0000000..9988e67 --- /dev/null +++ b/miniupnpd/pcpserver.c @@ -0,0 +1,1334 @@ +/* $Id: pcpserver.c $ */ +/* 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. +*/ + +#include "config.h" + +#ifdef ENABLE_PCP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pcpserver.h" +#include "macros.h" +#include "upnpglobalvars.h" +#include "pcplearndscp.h" +#include "upnpredirect.h" +#include "commonrdr.h" +#include "getifaddr.h" +#include "pcp_msg_struct.h" + +#ifdef PCP_PEER +//TODO make this platform independent +#include "netfilter/iptcrdr.h" +#endif + +#define IPV6_ADDR_COPY(dest, src) \ + do { \ + (dest)[0] = (src)[0]; \ + (dest)[1] = (src)[1]; \ + (dest)[2] = (src)[2]; \ + (dest)[3] = (src)[3]; \ + } while (0) + + typedef struct options_occur { + int third_party_occur; + int pfailure_occur; +} options_occur_t; + +/* server specific information */ +struct pcp_server_info { + uint8_t server_version; +}; + +/* default server settings, highest version supported is the default */ +struct pcp_server_info this_server_info = {2}; + +/* structure holding information from PCP msg*/ +/* all variables are in host byte order except IP addresses */ +typedef struct pcp_info { + uint8_t version; + uint8_t opcode; + uint8_t result_code; + uint32_t lifetime; /* lifetime of the mapping */ + uint32_t epochtime; + /* both MAP and PEER opcode specific information */ + uint8_t protocol; + uint16_t int_port; + const struct in6_addr *int_ip; /* in network order */ + uint16_t ext_port; + const struct in6_addr *ext_ip; /* Suggested external IP in network order*/ + /* PEER specific information */ +#ifdef PCP_PEER + uint16_t peer_port; + const struct in6_addr *peer_ip; /* Destination IP in network order */ +#endif //PCP_PEER + +#ifdef PCP_SADSCP + /* SADSCP specific information */ + uint8_t delay_tolerance; + uint8_t loss_tolerance; + uint8_t jitter_tolerance; + uint8_t app_name_len; + const char* app_name; + uint8_t sadscp_dscp; + uint8_t matched_name; + int8_t is_sadscp_op; +#endif + +#ifdef PCP_FLOWP + uint8_t dscp_up; + uint8_t dscp_down; + int flowp_present; +#endif + uint8_t is_map_op; + uint8_t is_peer_op; + int thirdp_present; /* indicate presence of the options */ + int pfailure_present; + char senderaddrstr[INET_ADDRSTRLEN]; + +}pcp_info_t; + + +#ifdef PCP_SADSCP +int get_dscp_value(pcp_info_t *pcp_msg_info) { + + unsigned int ind; + + for (ind = 0; ind < num_dscp_values; ind++) { + + if ((dscp_values_list[ind].app_name) && + (!strncmp( dscp_values_list[ind].app_name, + pcp_msg_info->app_name, pcp_msg_info->app_name_len)) && + (pcp_msg_info->delay_tolerance == dscp_values_list[ind].delay) && + (pcp_msg_info->loss_tolerance == dscp_values_list[ind].loss) && + (pcp_msg_info->jitter_tolerance == dscp_values_list[ind].jitter) + ) + { + pcp_msg_info->sadscp_dscp = dscp_values_list[ind].dscp_value; + pcp_msg_info->matched_name = 1; + return 0; + } else + if ((pcp_msg_info->app_name_len==0) && + (dscp_values_list[ind].app_name_len==0) && + (pcp_msg_info->delay_tolerance == dscp_values_list[ind].delay) && + (pcp_msg_info->loss_tolerance == dscp_values_list[ind].loss) && + (pcp_msg_info->jitter_tolerance == dscp_values_list[ind].jitter) + ) + { + pcp_msg_info->sadscp_dscp = dscp_values_list[ind].dscp_value; + pcp_msg_info->matched_name = 0; + return 0; + } else + if ((dscp_values_list[ind].app_name_len==0) && + (pcp_msg_info->delay_tolerance == dscp_values_list[ind].delay) && + (pcp_msg_info->loss_tolerance == dscp_values_list[ind].loss) && + (pcp_msg_info->jitter_tolerance == dscp_values_list[ind].jitter) + ) + { + pcp_msg_info->sadscp_dscp = dscp_values_list[ind].dscp_value; + pcp_msg_info->matched_name = 0; + return 0; + } + } + //if nothing matched return Default value i.e. 0 + pcp_msg_info->sadscp_dscp = 0; + pcp_msg_info->matched_name = 0; + return 0; +} +#endif +/* + * Function extracting information from common_req (common request header) + * into pcp_msg_info. + * @return : when no problem occurred 0 is returned, 1 otherwise and appropriate + * result code is assigned to pcp_msg_info->result_code to indicate + * what kind of error occurred + */ +static int parseCommonRequestHeader(pcp_request_t *common_req, pcp_info_t *pcp_msg_info) +{ + pcp_msg_info->version = common_req->ver ; + pcp_msg_info->opcode = common_req->r_opcode &0x7f ; + pcp_msg_info->lifetime = ntohl(common_req->req_lifetime); + pcp_msg_info->int_ip = (struct in6_addr*)common_req->ip; + + + if ( (common_req->ver > this_server_info.server_version) ) { + pcp_msg_info->result_code = PCP_ERR_UNSUPP_VERSION; + return 1; + } + + if (pcp_msg_info->lifetime > max_lifetime ) { + pcp_msg_info->lifetime = max_lifetime; + } + + if ( (pcp_msg_info->lifetime < min_lifetime) && (pcp_msg_info->lifetime != 0) ) { + pcp_msg_info->lifetime = min_lifetime; + } + + return 0; +} + +#ifdef DEBUG +static void printMAPOpcodeVersion1(pcp_map_v1_t *map_buf) +{ + char map_addr[INET6_ADDRSTRLEN]; + syslog(LOG_DEBUG, "PCP MAP: v1 Opcode specific information. \n"); + syslog(LOG_DEBUG, "MAP protocol: \t\t %d\n",map_buf->protocol ); + syslog(LOG_DEBUG, "MAP int port: \t\t %d\n", ntohs(map_buf->int_port) ); + syslog(LOG_DEBUG, "MAP ext port: \t\t %d\n", ntohs(map_buf->ext_port) ); + syslog(LOG_DEBUG, "MAP Ext IP: \t\t %s\n", inet_ntop(AF_INET6, + map_buf->ext_ip, map_addr, INET6_ADDRSTRLEN)); +} + +static void printMAPOpcodeVersion2(pcp_map_v2_t *map_buf) +{ + char map_addr[INET6_ADDRSTRLEN]; + syslog(LOG_DEBUG, "PCP MAP: v2 Opcode specific information. \n"); + syslog(LOG_DEBUG, "MAP protocol: \t\t %d\n",map_buf->protocol ); + syslog(LOG_DEBUG, "MAP int port: \t\t %d\n", ntohs(map_buf->int_port) ); + syslog(LOG_DEBUG, "MAP ext port: \t\t %d\n", ntohs(map_buf->ext_port) ); + syslog(LOG_DEBUG, "MAP Ext IP: \t\t %s\n", inet_ntop(AF_INET6, + map_buf->ext_ip, map_addr, INET6_ADDRSTRLEN)); +} +#endif //DEBUG + +static int parsePCPMAP_version1(pcp_map_v1_t *map_v1, \ + pcp_info_t *pcp_msg_info) +{ + pcp_msg_info->is_map_op = 1; + pcp_msg_info->protocol = map_v1->protocol; + pcp_msg_info->int_port = ntohs(map_v1->int_port); + pcp_msg_info->ext_port = ntohs(map_v1->ext_port); + + pcp_msg_info->ext_ip = (struct in6_addr*)map_v1->ext_ip; + + if (pcp_msg_info->protocol == 0 && pcp_msg_info->int_port !=0 ){ + syslog(LOG_ERR, "PCP MAP: Protocol was ZERO, but internal port has non-ZERO value."); + pcp_msg_info->result_code = PCP_ERR_MALFORMED_REQUEST; + return 1; + } + return 0; +} + +static int parsePCPMAP_version2(pcp_map_v2_t *map_v2, \ + pcp_info_t *pcp_msg_info) +{ + pcp_msg_info->is_map_op = 1; + pcp_msg_info->protocol = map_v2->protocol; + pcp_msg_info->int_port = ntohs(map_v2->int_port); + pcp_msg_info->ext_port = ntohs(map_v2->ext_port); + + pcp_msg_info->ext_ip = (struct in6_addr*)map_v2->ext_ip; + + if (pcp_msg_info->protocol == 0 && pcp_msg_info->int_port !=0 ) { + syslog(LOG_ERR, "PCP MAP: Protocol was ZERO, but internal port has non-ZERO value."); + pcp_msg_info->result_code = PCP_ERR_MALFORMED_REQUEST; + return PCP_ERR_MALFORMED_REQUEST; + } + + return 0; +} + +#ifdef PCP_PEER +#ifdef DEBUG +static void printPEEROpcodeVersion1(pcp_peer_v1_t *peer_buf) +{ + char ext_addr[INET6_ADDRSTRLEN]; + char peer_addr[INET6_ADDRSTRLEN]; + syslog(LOG_DEBUG, "PCP PEER: v1 Opcode specific information. \n"); + syslog(LOG_DEBUG, "Protocol: \t\t %d\n",peer_buf->protocol ); + syslog(LOG_DEBUG, "Internal port: \t\t %d\n", ntohs(peer_buf->int_port) ); + syslog(LOG_DEBUG, "External IP: \t\t %s\n", inet_ntop(AF_INET6, peer_buf->ext_ip, + ext_addr,INET6_ADDRSTRLEN)); + syslog(LOG_DEBUG, "External port port: \t\t %d\n", ntohs(peer_buf->ext_port) ); + syslog(LOG_DEBUG, "PEER IP: \t\t %s\n", inet_ntop(AF_INET6, peer_buf->peer_ip, + peer_addr,INET6_ADDRSTRLEN)); + syslog(LOG_DEBUG, "PEER port port: \t\t %d\n", ntohs(peer_buf->peer_port) ); +} + +static void printPEEROpcodeVersion2(pcp_peer_v2_t *peer_buf) +{ + char ext_addr[INET6_ADDRSTRLEN]; + char peer_addr[INET6_ADDRSTRLEN]; + + syslog(LOG_DEBUG, "PCP PEER: v2 Opcode specific information. \n"); + syslog(LOG_DEBUG, "Protocol: \t\t %d\n",peer_buf->protocol ); + syslog(LOG_DEBUG, "Internal port: \t\t %d\n", ntohs(peer_buf->int_port) ); + syslog(LOG_DEBUG, "External IP: \t\t %s\n", inet_ntop(AF_INET6, peer_buf->ext_ip, + ext_addr,INET6_ADDRSTRLEN)); + syslog(LOG_DEBUG, "External port port: \t\t %d\n", ntohs(peer_buf->ext_port) ); + syslog(LOG_DEBUG, "PEER IP: \t\t %s\n", inet_ntop(AF_INET6, peer_buf->peer_ip, + peer_addr,INET6_ADDRSTRLEN)); + syslog(LOG_DEBUG, "PEER port port: \t\t %d\n", ntohs(peer_buf->peer_port) ); +} +#endif //DEBUG + +/* + * Function extracting information from peer_buf to pcp_msg_info + * @return : when no problem occurred 0 is returned, 1 otherwise + */ +static int parsePCPPEER_version1(pcp_peer_v1_t *peer_buf, \ + pcp_info_t *pcp_msg_info) +{ + pcp_msg_info->is_peer_op = 1; + pcp_msg_info->protocol = peer_buf->protocol; + pcp_msg_info->int_port = ntohs(peer_buf->int_port); + pcp_msg_info->ext_port = ntohs(peer_buf->ext_port); + pcp_msg_info->peer_port = ntohs(peer_buf->peer_port); + + pcp_msg_info->ext_ip = (struct in6_addr*)peer_buf->ext_ip; + pcp_msg_info->peer_ip = (struct in6_addr*)peer_buf->peer_ip; + + if (pcp_msg_info->protocol == 0 && pcp_msg_info->int_port !=0 ){ + syslog(LOG_ERR, "PCP PEER: protocol was ZERO, but internal port has non-ZERO value."); + pcp_msg_info->result_code = PCP_ERR_MALFORMED_REQUEST; + return 1; + } + return 0; +} + +/* + * Function extracting information from peer_buf to pcp_msg_info + * @return : when no problem occurred 0 is returned, 1 otherwise + */ +static int parsePCPPEER_version2(pcp_peer_v2_t *peer_buf, \ + pcp_info_t *pcp_msg_info) +{ + pcp_msg_info->is_peer_op = 1; + pcp_msg_info->protocol = peer_buf->protocol; + pcp_msg_info->int_port = ntohs(peer_buf->int_port); + pcp_msg_info->ext_port = ntohs(peer_buf->ext_port); + pcp_msg_info->peer_port = ntohs(peer_buf->peer_port); + + pcp_msg_info->ext_ip = (struct in6_addr*)peer_buf->ext_ip; + pcp_msg_info->peer_ip = (struct in6_addr*)peer_buf->peer_ip; + + if (pcp_msg_info->protocol == 0 && pcp_msg_info->int_port !=0 ){ + syslog(LOG_ERR, "PCP PEER: protocol was ZERO, but internal port has non-ZERO value."); + pcp_msg_info->result_code = PCP_ERR_MALFORMED_REQUEST; + return 1; + } + return 0; +} +#endif //PCP_PEER + +#ifdef PCP_SADSCP +#ifdef DEBUG +static void printSADSCPOpcode(pcp_sadscp_req_t *sadscp) { + unsigned char sadscp_tol; + sadscp_tol = sadscp->tolerance_fields; + + syslog(LOG_DEBUG, "PCP SADSCP: Opcode specific information.\n"); + syslog(LOG_DEBUG, "Delay tolerance %d \n", (sadscp_tol>>6)&3 ); + syslog(LOG_DEBUG, "Loss tolerance %d \n", (sadscp_tol>>4)&3); + syslog(LOG_DEBUG, "Jitter tolerance %d \n", (sadscp_tol>>2)&3); + syslog(LOG_DEBUG, "RRR %d \n", sadscp_tol&3); + syslog(LOG_DEBUG, "AppName Length %d \n", sadscp->app_name_length); + if (sadscp->app_name) { + syslog(LOG_DEBUG, "Application name %.*s \n", sadscp->app_name_length, + sadscp->app_name); + } +} +#endif //DEBUG + +static int parseSADSCP(pcp_sadscp_req_t *sadscp, pcp_info_t *pcp_msg_info) { + + pcp_msg_info->delay_tolerance = (sadscp->tolerance_fields>>6)&3; + pcp_msg_info->loss_tolerance = (sadscp->tolerance_fields>>4)&3; + pcp_msg_info->jitter_tolerance = (sadscp->tolerance_fields>>2)&3; + + if (pcp_msg_info->delay_tolerance == 3 ) { + pcp_msg_info->result_code = PCP_ERR_MALFORMED_REQUEST; + return 1; + } + if ( pcp_msg_info->loss_tolerance == 3 ) { + pcp_msg_info->result_code = PCP_ERR_MALFORMED_REQUEST; + return 1; + } + if ( pcp_msg_info->jitter_tolerance == 3 ) { + pcp_msg_info->result_code = PCP_ERR_MALFORMED_REQUEST; + return 1; + } + + pcp_msg_info->app_name = sadscp->app_name; + pcp_msg_info->app_name_len = sadscp->app_name_length; + + return 0; +} +#endif + +static int parsePCPOptions(void* pcp_buf, int* remainingSize, int* processedSize, \ + pcp_info_t *pcp_msg_info) +{ + int remain = *remainingSize; + int processed = *processedSize; +#ifdef DEBUG + char third_addr[INET6_ADDRSTRLEN]; +#endif + unsigned short option_length; + + pcp_3rd_party_option_t* opt_3rd; +#ifdef PCP_FLOWP + pcp_flow_priority_option_t* opt_flp; +#endif + pcp_filter_option_t* opt_filter; + pcp_prefer_fail_option_t* opt_prefail; + pcp_options_hdr_t* opt_hdr; + + opt_hdr = (pcp_options_hdr_t*)(pcp_buf + processed); + option_length = 0; + + switch (opt_hdr->code){ + + case PCP_OPTION_3RD_PARTY: + + opt_3rd = (pcp_3rd_party_option_t*) (pcp_buf + processed); + option_length = ntohs(opt_3rd->len); + + if (option_length != (sizeof(pcp_3rd_party_option_t) - sizeof(pcp_options_hdr_t)) || + (int)sizeof(pcp_3rd_party_option_t) > remain) { + pcp_msg_info->result_code = PCP_ERR_MALFORMED_OPTION; + remain = 0; + break; + } +#ifdef DEBUG + syslog(LOG_DEBUG, "PCP OPTION: \t Third party \n"); + syslog(LOG_DEBUG, "Third PARTY IP: \t %s\n", inet_ntop(AF_INET6, + opt_3rd->ip, third_addr, INET6_ADDRSTRLEN)); +#endif + if (pcp_msg_info->thirdp_present != 0 ) { + + syslog(LOG_ERR, "PCP: THIRD PARTY OPTION was already present. \n"); + pcp_msg_info->result_code = PCP_ERR_MALFORMED_OPTION; + } + else { + pcp_msg_info->thirdp_present = 1; + } + + processed += sizeof(pcp_3rd_party_option_t); + remain -= sizeof(pcp_3rd_party_option_t); + break; + + case PCP_OPTION_PREF_FAIL: + + opt_prefail = (pcp_prefer_fail_option_t*)(pcp_buf+processed); + option_length = ntohs(opt_prefail->len); + + if ( option_length != ( sizeof(pcp_prefer_fail_option_t) - sizeof(pcp_options_hdr_t)) || + (int)sizeof(pcp_prefer_fail_option_t) > remain) { + pcp_msg_info->result_code = PCP_ERR_MALFORMED_OPTION; + remain = 0; + break; + } +#ifdef DEBUG + syslog(LOG_DEBUG, "PCP OPTION: \t Prefer failure \n"); +#endif + if (pcp_msg_info->opcode != PCP_OPCODE_MAP) { + syslog(LOG_DEBUG, "PCP: Unsupported OPTION for given OPCODE.\n"); + pcp_msg_info->result_code = PCP_ERR_MALFORMED_REQUEST; + } + if (pcp_msg_info->pfailure_present != 0 ) { + syslog(LOG_DEBUG, "PCP: PREFER FAILURE OPTION was already present. \n"); + pcp_msg_info->result_code = PCP_ERR_MALFORMED_OPTION; + } + else { + pcp_msg_info->pfailure_present = 1; + processed += sizeof(pcp_prefer_fail_option_t); + remain -= sizeof(pcp_prefer_fail_option_t); + } + break; + + case PCP_OPTION_FILTER: + // TODO fully implement filter + opt_filter = (pcp_filter_option_t*) (pcp_buf + processed); + option_length = ntohs(opt_filter->len); + + if ( option_length != ( sizeof(pcp_filter_option_t) - sizeof(pcp_options_hdr_t)) || + (int)sizeof(pcp_filter_option_t) > remain) { + pcp_msg_info->result_code = PCP_ERR_MALFORMED_OPTION; + remain = 0; + break; + } +#ifdef DEBUG + syslog(LOG_DEBUG, "PCP OPTION: \t Filter\n"); +#endif + if (pcp_msg_info->opcode != PCP_OPCODE_MAP) { + syslog(LOG_ERR, "PCP: Unsupported OPTION for given OPCODE.\n"); + pcp_msg_info->result_code = PCP_ERR_MALFORMED_REQUEST; + } + processed += sizeof(pcp_filter_option_t); + remain -= sizeof(pcp_filter_option_t); + break; + +#ifdef PCP_FLOWP + case PCP_OPTION_FLOW_PRIORITY: + +#ifdef DEBUG + syslog(LOG_DEBUG, "PCP OPTION: \t Flow priority\n"); +#endif + opt_flp = (pcp_flow_priority_option_t*) (pcp_buf + processed); + option_length = ntohs(opt_flp->len); + + if ( option_length != ( sizeof(pcp_flow_priority_option_t) - sizeof(pcp_options_hdr_t)) || + ((int)sizeof(pcp_flow_priority_option_t) > remain) ) { + syslog(LOG_ERR, "PCP: Error processing DSCP. sizeof %d and remaining %d . flow len %d \n", + (int)sizeof(pcp_flow_priority_option_t), remain, opt_flp->len); + pcp_msg_info->result_code = PCP_ERR_MALFORMED_OPTION; + remain = 0; + break; + } + +#ifdef DEBUG + syslog(LOG_DEBUG, "DSCP UP: \t %d \n", opt_flp->dscp_up); + syslog(LOG_DEBUG, "DSCP DOWN: \t %d \n", opt_flp->dscp_down); +#endif + pcp_msg_info->dscp_up = opt_flp->dscp_up; + pcp_msg_info->dscp_down = opt_flp->dscp_down; + pcp_msg_info->flowp_present = 1; + + processed += sizeof(pcp_flow_priority_option_t); + remain -= sizeof(pcp_flow_priority_option_t); + break; +#endif + default: + syslog(LOG_ERR, "PCP: Unrecognized PCP OPTION: %d \n", opt_hdr->code); + remain = 0; + break; + } + + // shift processed and remaining values to new values + *remainingSize = remain; + *processedSize = processed; + return pcp_msg_info->result_code; +} + + +static int CheckExternalAddress(pcp_info_t* pcp_msg_info) +{ + static struct in6_addr external_addr; + + if(use_ext_ip_addr) { + if (inet_pton(AF_INET, use_ext_ip_addr, + ((uint32_t*)external_addr.s6_addr)+3) == 1) { + ((uint32_t*)external_addr.s6_addr)[0] = 0; + ((uint32_t*)external_addr.s6_addr)[1] = 0; + ((uint32_t*)external_addr.s6_addr)[2] = htonl(0xFFFF); + } else if (inet_pton(AF_INET6, use_ext_ip_addr, external_addr.s6_addr) + != 1) { + pcp_msg_info->result_code = PCP_ERR_NETWORK_FAILURE; + return -1; + } + } else { + if(!ext_if_name || ext_if_name[0]=='\0') { + pcp_msg_info->result_code = PCP_ERR_NETWORK_FAILURE; + return -1; + } + if(getifaddr_in6(ext_if_name, &external_addr) < 0) { + pcp_msg_info->result_code = PCP_ERR_NETWORK_FAILURE; + return -1; + } + } + + if (IN6_IS_ADDR_UNSPECIFIED(pcp_msg_info->ext_ip)) { + + pcp_msg_info->ext_ip = &external_addr; + + return 0; + } + + if (!IN6_ARE_ADDR_EQUAL(pcp_msg_info->ext_ip, &external_addr)) { + syslog(LOG_ERR, + "PCP: External IP in request didn't match interface IP \n"); +#ifdef DEBUG + { + char s[INET6_ADDRSTRLEN]; + syslog(LOG_DEBUG, "Interface IP %s \n", + inet_ntop(AF_INET6, &external_addr.s6_addr, s, sizeof(s))); + syslog(LOG_DEBUG, "IP in the PCP request %s \n", + inet_ntop(AF_INET6, pcp_msg_info->ext_ip, s, sizeof(s))); + } +#endif + + if (pcp_msg_info->pfailure_present) { + pcp_msg_info->result_code = PCP_ERR_CANNOT_PROVIDE_EXTERNAL; + return -1; + } else { + pcp_msg_info->ext_ip = &external_addr; + } + + } + + return 0; +} + + +#ifdef PCP_PEER +static void FillSA(struct sockaddr *sa, const struct in6_addr *in6, + uint16_t port) +{ + if (IN6_IS_ADDR_V4MAPPED(in6)) { + struct sockaddr_in *sa4 = (struct sockaddr_in *)sa; + sa4->sin_family = AF_INET; + sa4->sin_addr.s_addr = ((uint32_t*)(in6)->s6_addr)[3]; + sa4->sin_port = htons(port); + } else { + struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa; + sa6->sin6_family = AF_INET6; + IPV6_ADDR_COPY((uint32_t*)sa6->sin6_addr.s6_addr, + (uint32_t*)in6->s6_addr); + sa6->sin6_port = htons(port); + } +} + +static const char* inet_satop(struct sockaddr* sa, char* buf, size_t buf_len) +{ + if (sa->sa_family == AF_INET) { + return inet_ntop(AF_INET, &(((struct sockaddr_in*)sa)->sin_addr), buf, buf_len); + } else { + return inet_ntop(AF_INET6, &(((struct sockaddr_in6*)sa)->sin6_addr), buf, buf_len); + } +} + +static const char* inet_n46top(struct in6_addr* in, char* buf, size_t buf_len) +{ + if (IN6_IS_ADDR_V4MAPPED(in)) { + return inet_ntop(AF_INET, ((uint32_t*)(in->s6_addr))+3, buf, buf_len); + } else { + return inet_ntop(AF_INET6, in, buf, buf_len); + } +} + +static int CreatePCPPeer(pcp_info_t *pcp_msg_info) +{ + struct sockaddr_storage intip; + struct sockaddr_storage peerip; + struct sockaddr_storage extip; + struct sockaddr_storage ret_extip; + + uint8_t proto = pcp_msg_info->protocol; + + uint16_t eport = pcp_msg_info->ext_port; /* public port */ + + FillSA((struct sockaddr*)&intip, pcp_msg_info->int_ip, + pcp_msg_info->int_port); + FillSA((struct sockaddr*)&peerip, pcp_msg_info->peer_ip, + pcp_msg_info->peer_port); + FillSA((struct sockaddr*)&extip, pcp_msg_info->ext_ip, + eport); + + /* check if connection with given peer exists, if it was */ + /* already established use this external port */ + if (get_nat_ext_addr( (struct sockaddr*)&intip, (struct sockaddr*)&peerip, + proto, (struct sockaddr*)&ret_extip) == 1) { + if (ret_extip.ss_family == AF_INET) { + struct sockaddr_in* ret_ext4 = (struct sockaddr_in*)&ret_extip; + uint16_t ret_eport = ntohs(ret_ext4->sin_port); + eport = ret_eport; + } else if (ret_extip.ss_family == AF_INET6) { + struct sockaddr_in6* ret_ext6 = (struct sockaddr_in6*)&ret_extip; + uint16_t ret_eport = ntohs(ret_ext6->sin6_port); + eport = ret_eport; + } else { + pcp_msg_info->result_code = PCP_ERR_CANNOT_PROVIDE_EXTERNAL; + return 0; + } + } + //Create Peer Mapping + { + char desc[64]; + char peerip_s[INET_ADDRSTRLEN], extip_s[INET_ADDRSTRLEN]; + time_t timestamp = time(NULL) + pcp_msg_info->lifetime; + + if (eport == 0) { + eport = pcp_msg_info->int_port; + } + + snprintf(desc, sizeof(desc), "PCP %hu %s", + eport, (proto==IPPROTO_TCP)?"tcp":"udp"); + + inet_satop((struct sockaddr*)&peerip, peerip_s, sizeof(peerip_s)); + inet_satop((struct sockaddr*)&extip, extip_s, sizeof(extip_s)); + +#ifdef PCP_FLOWP + if (pcp_msg_info->flowp_present && pcp_msg_info->dscp_up) { + if (add_peer_dscp_rule2(ext_if_name, peerip_s, + pcp_msg_info->peer_port, pcp_msg_info->dscp_up, + pcp_msg_info->senderaddrstr, pcp_msg_info->int_port, + proto, desc, timestamp) < 0 ) { + syslog(LOG_ERR, "PCP: failed to add flowp upstream mapping %s %s:%hu->%s:%hu '%s'", + (pcp_msg_info->protocol==IPPROTO_TCP)?"TCP":"UDP", + pcp_msg_info->senderaddrstr, + pcp_msg_info->int_port, + peerip_s, + pcp_msg_info->peer_port, + desc); + pcp_msg_info->result_code = PCP_ERR_NO_RESOURCES; + } + } + + if (pcp_msg_info->flowp_present && pcp_msg_info->dscp_down) { + if (add_peer_dscp_rule2(ext_if_name, pcp_msg_info->senderaddrstr, + pcp_msg_info->int_port, pcp_msg_info->dscp_down, + peerip_s, pcp_msg_info->peer_port, proto, desc, timestamp) + < 0 ) { + syslog(LOG_ERR, "PCP: failed to add flowp downstream mapping %s %s:%hu->%s:%hu '%s'", + (pcp_msg_info->protocol==IPPROTO_TCP)?"TCP":"UDP", + pcp_msg_info->senderaddrstr, + pcp_msg_info->int_port, + peerip_s, + pcp_msg_info->peer_port, + desc); + pcp_msg_info->result_code = PCP_ERR_NO_RESOURCES; + } + } +#endif + //TODO: add upnp function for PI + if (add_peer_redirect_rule2(ext_if_name, + peerip_s, + pcp_msg_info->peer_port, + extip_s, + eport, + pcp_msg_info->senderaddrstr, + pcp_msg_info->int_port, + pcp_msg_info->protocol, + desc, + timestamp) < 0 ) { + + syslog(LOG_ERR, "PCP PEER: failed to add peer mapping %s %s:%hu(%hu)->%s:%hu '%s'", + (pcp_msg_info->protocol==IPPROTO_TCP)?"TCP":"UDP", + pcp_msg_info->senderaddrstr, + pcp_msg_info->int_port, + eport, + peerip_s, + pcp_msg_info->peer_port, + desc); + + pcp_msg_info->result_code = PCP_ERR_NO_RESOURCES; + + return 0; + } else { + pcp_msg_info->ext_port = eport; + syslog(LOG_INFO, "PCP PEER: added mapping %s %s:%hu(%hu)->%s:%hu '%s'", + (pcp_msg_info->protocol==IPPROTO_TCP)?"TCP":"UDP", + pcp_msg_info->senderaddrstr, + pcp_msg_info->int_port, + eport, + peerip_s, + pcp_msg_info->peer_port, + desc); + } + } + + return 1; +} + +static void DeletePCPPeer(pcp_info_t *pcp_msg_info) +{ + uint16_t iport = pcp_msg_info->int_port; /* private port */ + uint16_t rport = pcp_msg_info->peer_port; /* private port */ + uint8_t proto = pcp_msg_info->protocol; + char rhost[INET6_ADDRSTRLEN]; + int r=-1; + + /* remove requested mappings for this client */ + int index = 0; + unsigned short eport2, iport2, rport2; + char iaddr2[INET6_ADDRSTRLEN], rhost2[INET6_ADDRSTRLEN]; + int proto2; + char desc[64]; + unsigned int timestamp; + + inet_n46top((struct in6_addr*)pcp_msg_info->peer_ip, rhost, sizeof(rhost)); + + while(get_peer_rule_by_index(index, 0, + &eport2, iaddr2, sizeof(iaddr2), + &iport2, &proto2, + desc, sizeof(desc), + rhost2, sizeof(rhost2), &rport2, ×tamp, 0, 0) >= 0) { + if((0 == strncmp(iaddr2, pcp_msg_info->senderaddrstr, sizeof(iaddr2))) + && (0 == strncmp(rhost2, rhost, sizeof(rhost2))) + && (proto2==proto) + && (0 == strncmp(desc, "PCP", sizeof("PCP")-1)) + && (iport2==iport) && (rport2==rport)) { + r = _upnp_delete_redir(eport2, proto2); + if(r<0) { + syslog(LOG_ERR, "PCP PEER: failed to remove peer mapping"); + index++; + } else { + syslog(LOG_INFO, "PCP PEER: %s port %hu peer mapping removed", + proto2==IPPROTO_TCP?"TCP":"UDP", eport2); + } + } else { + index++; + } + } + if (r==-1) { + syslog(LOG_ERR, "PCP PEER: Failed to remove PCP mapping internal port %hu, protocol %s", + iport, (pcp_msg_info->protocol == IPPROTO_TCP)?"TCP":"UDP"); + pcp_msg_info->result_code = PCP_ERR_NO_RESOURCES; + } +} +#endif //PCP_PEER + +static void CreatePCPMap(pcp_info_t *pcp_msg_info) +{ + char desc[64]; + char iaddr_old[INET_ADDRSTRLEN]; + uint16_t iport_old; + unsigned int timestamp = time(NULL) + pcp_msg_info->lifetime; + int r=0; + + if (pcp_msg_info->ext_port == 0) { + pcp_msg_info->ext_port = pcp_msg_info->int_port; + } + do { + r = get_redirect_rule(ext_if_name, + pcp_msg_info->ext_port, + pcp_msg_info->protocol, + iaddr_old, sizeof(iaddr_old), + &iport_old, 0, 0, 0, 0, + ×tamp, 0, 0); + + if(r==0) { + if((strncmp(pcp_msg_info->senderaddrstr, iaddr_old, + sizeof(iaddr_old))!=0) + || (pcp_msg_info->int_port != iport_old)) { + /* redirection already existing */ + if (pcp_msg_info->pfailure_present) { + pcp_msg_info->result_code = PCP_ERR_CANNOT_PROVIDE_EXTERNAL; + return; + } + } else { + if (_upnp_delete_redir(pcp_msg_info->ext_port, + pcp_msg_info->protocol)==0) { + return; + } + } + pcp_msg_info->ext_port++; + } + } while (r==0); + + if (pcp_msg_info->ext_port == 0) { + pcp_msg_info->result_code = PCP_ERR_CANNOT_PROVIDE_EXTERNAL; + return; + } + + snprintf(desc, sizeof(desc), "PCP %hu %s", + pcp_msg_info->ext_port, + (pcp_msg_info->protocol==IPPROTO_TCP)?"tcp":"udp"); + + if(upnp_redirect_internal(NULL, + pcp_msg_info->ext_port, + pcp_msg_info->senderaddrstr, + pcp_msg_info->int_port, + pcp_msg_info->protocol, + desc, + timestamp) < 0) { + + syslog(LOG_ERR, "PCP MAP: Failed to add mapping %s %hu->%s:%hu '%s'", + (pcp_msg_info->protocol==IPPROTO_TCP)?"TCP":"UDP", + pcp_msg_info->ext_port, + pcp_msg_info->senderaddrstr, + pcp_msg_info->int_port, + desc); + + } else { + syslog(LOG_INFO, "PCP MAP: added mapping %s %hu->%s:%hu '%s'", + (pcp_msg_info->protocol==IPPROTO_TCP)?"TCP":"UDP", + pcp_msg_info->ext_port, + pcp_msg_info->senderaddrstr, + pcp_msg_info->int_port, + desc); + } +} + +static void DeletePCPMap(pcp_info_t *pcp_msg_info) +{ + uint16_t iport = pcp_msg_info->int_port; /* private port */ + uint8_t proto = pcp_msg_info->protocol; + int r=-1; + /* remove the mapping */ + /* remove all the mappings for this client */ + int index = 0; + unsigned short eport2, iport2; + char iaddr2[16]; + int proto2; + char desc[64]; + unsigned int timestamp; + + //iterate through all rules and delete the requested ones + while(get_redirect_rule_by_index(index, 0, + &eport2, iaddr2, sizeof(iaddr2), + &iport2, &proto2, + desc, sizeof(desc), + 0, 0, ×tamp, 0, 0) >= 0) { + + if(0 == strncmp(iaddr2, pcp_msg_info->senderaddrstr, sizeof(iaddr2)) + && (proto2==proto) + && (0 == strncmp(desc, "PCP", 3)) //starts with PCP + && ((iport2==iport) || (iport==0))) { + + r = _upnp_delete_redir(eport2, proto2); + if(r<0) { + syslog(LOG_ERR, "PCP: failed to remove port mapping"); + index++; + } else { + syslog(LOG_INFO, "PCP: %s port %hu mapping removed", + proto2==IPPROTO_TCP?"TCP":"UDP", eport2); + } + } else { + index++; + } + } + if (r==-1) { + syslog(LOG_ERR, "Failed to remove PCP mapping internal port %hu, protocol %s", + iport, (pcp_msg_info->protocol == IPPROTO_TCP)?"TCP":"UDP"); + pcp_msg_info->result_code = PCP_ERR_NO_RESOURCES; + } +} + +static int ValidatePCPMsg(pcp_info_t *pcp_msg_info) +{ + if (pcp_msg_info->result_code) { + return 0; + } + + if (pcp_msg_info->protocol == 0 && pcp_msg_info->int_port !=0 ){ + pcp_msg_info->result_code = PCP_ERR_MALFORMED_REQUEST; + return 0; + } + + if (pcp_msg_info->pfailure_present) { + if ( (IN6_IS_ADDR_UNSPECIFIED(pcp_msg_info->ext_ip) || + ((IN6_IS_ADDR_V4MAPPED(pcp_msg_info->ext_ip)) && + (((uint32_t*)pcp_msg_info->ext_ip->s6_addr)[3] == 0))) && + (pcp_msg_info->ext_port == 0) + ) + { + pcp_msg_info->result_code = PCP_ERR_MALFORMED_OPTION; + return 0; + } + } + + if (CheckExternalAddress(pcp_msg_info)) { + return 0; + } + + return 1; +} + +/* + * return value indicates whether the request is valid or not. + * Based on the return value simple response can be formed. + */ +static int processPCPRequest(void * req, int req_size, pcp_info_t *pcp_msg_info) +{ + int remainingSize; + int processedSize; + + pcp_request_t* common_req; + pcp_map_v1_t* map_v1; + pcp_map_v2_t* map_v2; +#ifdef PCP_PEER + pcp_peer_v1_t* peer_v1; + pcp_peer_v2_t* peer_v2; +#endif + +#ifdef PCP_SADSCP + pcp_sadscp_req_t* sadscp; +#endif + // start with PCP_SUCCESS as result code, if everything is OK value will be unchanged + pcp_msg_info->result_code = PCP_SUCCESS; + + remainingSize = req_size; + processedSize = 0; + + // discard request that exceeds maximal length, + // or that is shorter than 3 + // or that is not the multiple of 4 + if (req_size < PCP_MIN_LEN) + return 0; //ignore msg + + if ( (req_size > PCP_MAX_LEN) || ( (req_size & 3) != 0)) { + syslog(LOG_ERR, "PCP: Size of PCP packet(%d) is larger than %d bytes or " + "the size is not multiple of 4.\n", req_size, PCP_MAX_LEN); + pcp_msg_info->result_code = PCP_ERR_MALFORMED_REQUEST; + return 1; // send response + } + + //first print out info from common request header + common_req = (pcp_request_t*)req; + + if (parseCommonRequestHeader(common_req, pcp_msg_info) ) { + return 1; + } + + remainingSize -= sizeof(pcp_request_t); + processedSize += sizeof(pcp_request_t); + + if (common_req->ver == 1) { + + switch ( common_req->r_opcode & 0x7F ) { + case PCP_OPCODE_MAP: + + remainingSize -= sizeof(pcp_map_v1_t); + if (remainingSize < 0) { + pcp_msg_info->result_code = PCP_ERR_MALFORMED_REQUEST; + return pcp_msg_info->result_code; + } + + map_v1 = (pcp_map_v1_t*)(req + processedSize); +#ifdef DEBUG + printMAPOpcodeVersion1(map_v1); +#endif //DEBUG + if ( parsePCPMAP_version1(map_v1, pcp_msg_info) ) { + return pcp_msg_info->result_code; + } + + processedSize += sizeof(pcp_map_v1_t); + + while (remainingSize > 0) { + parsePCPOptions(req, &remainingSize, &processedSize, pcp_msg_info); + } + if (ValidatePCPMsg(pcp_msg_info)) { + if (pcp_msg_info->lifetime == 0) { + DeletePCPMap(pcp_msg_info); + } else { + CreatePCPMap(pcp_msg_info); + } + } else { + syslog(LOG_ERR, "PCP: Invalid PCP v1 MAP message."); + } + + + break; + +#ifdef PCP_PEER + case PCP_OPCODE_PEER: + + remainingSize -= sizeof(pcp_peer_v1_t); + if (remainingSize < 0) { + pcp_msg_info->result_code = PCP_ERR_MALFORMED_REQUEST; + return pcp_msg_info->result_code; + } + peer_v1 = (pcp_peer_v1_t*)(req + processedSize); + +#ifdef DEBUG + printPEEROpcodeVersion1(peer_v1); +#endif //DEBUG + if ( parsePCPPEER_version1(peer_v1, pcp_msg_info) ) { + return pcp_msg_info->result_code; + } + + processedSize += sizeof(pcp_peer_v1_t); + + while (remainingSize > 0) { + parsePCPOptions(req, &remainingSize, &processedSize, pcp_msg_info); + } + + if (ValidatePCPMsg(pcp_msg_info)) { + if (pcp_msg_info->lifetime == 0) { + DeletePCPPeer(pcp_msg_info); + } else { + CreatePCPPeer(pcp_msg_info); + } + } else { + syslog(LOG_ERR, "PCP: Invalid PCP v1 PEER message."); + } + + + break; +#endif //PCP_PEER + default: + pcp_msg_info->result_code = PCP_ERR_UNSUPP_OPCODE; + break; + } + + } else if (common_req->ver == 2) { + + switch ( common_req->r_opcode & 0x7F) { + case PCP_OPCODE_MAP: + + remainingSize -= sizeof(pcp_map_v2_t); + if (remainingSize < 0) { + pcp_msg_info->result_code = PCP_ERR_MALFORMED_REQUEST; + return pcp_msg_info->result_code; + } + + map_v2 = (pcp_map_v2_t*)(req + processedSize); + +#ifdef DEBUG + printMAPOpcodeVersion2(map_v2); +#endif //DEBUG + + if (parsePCPMAP_version2(map_v2, pcp_msg_info) ) { + return pcp_msg_info->result_code; + } + processedSize += sizeof(pcp_map_v2_t); + + while (remainingSize > 0) { + parsePCPOptions(req, &remainingSize, &processedSize, pcp_msg_info); + } + + if (ValidatePCPMsg(pcp_msg_info)) { + if (pcp_msg_info->lifetime == 0) { + DeletePCPMap(pcp_msg_info); + } else { + CreatePCPMap(pcp_msg_info); + } + } else { + syslog(LOG_ERR, "PCP: Invalid PCP v2 MAP message."); + } + + + break; + +#ifdef PCP_PEER + case PCP_OPCODE_PEER: + + remainingSize -= sizeof(pcp_peer_v2_t); + if (remainingSize < 0) { + pcp_msg_info->result_code = PCP_ERR_MALFORMED_REQUEST; + return pcp_msg_info->result_code; + } + peer_v2 = (pcp_peer_v2_t*)(req + processedSize); + +#ifdef DEBUG + printPEEROpcodeVersion2(peer_v2); +#endif //DEBUG + parsePCPPEER_version2(peer_v2, pcp_msg_info); + processedSize += sizeof(pcp_peer_v2_t); + + if (pcp_msg_info->result_code != 0) { + return pcp_msg_info->result_code; + } + + while (remainingSize > 0) { + parsePCPOptions(req, &remainingSize, &processedSize, pcp_msg_info); + } + + if (ValidatePCPMsg(pcp_msg_info)) { + if (pcp_msg_info->lifetime == 0) { + DeletePCPPeer(pcp_msg_info); + } else { + CreatePCPPeer(pcp_msg_info); + } + } else { + syslog(LOG_ERR, "PCP: Invalid PCP v2 PEER message."); + } + + break; +#endif //PCP_PEER + +#ifdef PCP_SADSCP + case PCP_OPCODE_SADSCP: + remainingSize -= sizeof(pcp_sadscp_req_t); + if (remainingSize < 0) { + pcp_msg_info->result_code = PCP_ERR_MALFORMED_REQUEST; + return pcp_msg_info->result_code; + } + + sadscp = (pcp_sadscp_req_t*)(req + processedSize); + + if (sadscp->app_name_length > remainingSize) { + pcp_msg_info->result_code = PCP_ERR_MALFORMED_OPTION; + } + +#ifdef DEBUG + printSADSCPOpcode(sadscp); +#endif + if (parseSADSCP(sadscp, pcp_msg_info)) { + return pcp_msg_info->result_code; + } + + get_dscp_value(pcp_msg_info); + + processedSize += sizeof(pcp_sadscp_req_t); + + break; +#endif + default: + pcp_msg_info->result_code = PCP_ERR_UNSUPP_OPCODE; + break; + } + } else { + pcp_msg_info->result_code = PCP_ERR_UNSUPP_VERSION; + return pcp_msg_info->result_code; + } + return 1; +} + + +static void createPCPResponse(unsigned char *response, pcp_info_t *pcp_msg_info) +{ + pcp_response_t *resp = (pcp_response_t*)response; + + resp->reserved = 0; + resp->reserved1[0]=0; + resp->reserved1[1]=0; + resp->reserved1[2]=0; + if (pcp_msg_info->result_code == PCP_ERR_UNSUPP_VERSION ) { + /* highest supported version */ + resp->ver = this_server_info.server_version; + } else { + resp->ver = pcp_msg_info->version; + } + + resp->r_opcode |= 0x80; + resp->result_code = pcp_msg_info->result_code; + resp->epochtime = htonl(time(NULL) - startup_time); + switch (pcp_msg_info->result_code) { + //long lifetime errors + case PCP_ERR_UNSUPP_VERSION: + case PCP_ERR_NOT_AUTHORIZED: + case PCP_ERR_MALFORMED_REQUEST: + case PCP_ERR_UNSUPP_OPCODE: + case PCP_ERR_UNSUPP_OPTION: + case PCP_ERR_MALFORMED_OPTION: + case PCP_ERR_UNSUPP_PROTOCOL: + case PCP_ERR_ADDRESS_MISMATCH: + case PCP_ERR_CANNOT_PROVIDE_EXTERNAL: + case PCP_ERR_EXCESSIVE_REMOTE_PEERS: + resp->lifetime = 0; + break; + + case PCP_ERR_NETWORK_FAILURE: + case PCP_ERR_NO_RESOURCES: + case PCP_ERR_USER_EX_QUOTA: + resp->lifetime = htonl(30); + break; + + case PCP_SUCCESS: + default: + resp->lifetime = htonl(pcp_msg_info->lifetime); + break; + } + + if (resp->r_opcode == 0x81) { /* MAP response */ + if (resp->ver == 1 ) { + pcp_map_v1_t *mapr = (pcp_map_v1_t *)resp->next_data; + IPV6_ADDR_COPY((uint32_t*)mapr->ext_ip, + (uint32_t*)pcp_msg_info->ext_ip); + mapr->ext_port = htons(pcp_msg_info->ext_port); + mapr->int_port = htons(pcp_msg_info->int_port); + } + else if (resp->ver == 2 ) { + pcp_map_v2_t *mapr = (pcp_map_v2_t *)resp->next_data; + IPV6_ADDR_COPY((uint32_t*)mapr->ext_ip, + (uint32_t*)pcp_msg_info->ext_ip); + mapr->ext_port = htons(pcp_msg_info->ext_port); + mapr->int_port = htons(pcp_msg_info->int_port); + } + } +#ifdef PCP_PEER + else if (resp->r_opcode == 0x82) { /* PEER response */ + if (resp->ver == 1 ){ + pcp_peer_v1_t* peer_resp = (pcp_peer_v1_t*)resp->next_data; + peer_resp->ext_port = htons(pcp_msg_info->ext_port); + peer_resp->int_port = htons(pcp_msg_info->int_port); + peer_resp->peer_port = htons(pcp_msg_info->peer_port); + IPV6_ADDR_COPY((uint32_t*)peer_resp->ext_ip, + (uint32_t*)pcp_msg_info->ext_ip); + } + else if (resp->ver == 2 ){ + pcp_peer_v2_t* peer_resp = (pcp_peer_v2_t*)resp->next_data; + peer_resp->ext_port = htons(pcp_msg_info->ext_port); + peer_resp->int_port = htons(pcp_msg_info->int_port); + peer_resp->peer_port = htons(pcp_msg_info->peer_port); + IPV6_ADDR_COPY((uint32_t*)peer_resp->ext_ip, + (uint32_t*)pcp_msg_info->ext_ip); + } + } +#endif //PCP_PEER + +#ifdef PCP_SADSCP + else if (resp->r_opcode == 0x83) { /*SADSCP response*/ + pcp_sadscp_resp_t *sadscp_resp = (pcp_sadscp_resp_t*)resp->next_data; + sadscp_resp->a_r_dscp_value = pcp_msg_info->matched_name<<7; + sadscp_resp->a_r_dscp_value &= ~(1<<6); + sadscp_resp->a_r_dscp_value |= (pcp_msg_info->sadscp_dscp & PCP_SADSCP_MASK); + memset(sadscp_resp->reserved, 0, sizeof(sadscp_resp->reserved)); + } +#endif //PCP_SADSCP +} + +int ProcessIncomingPCPPacket(int s, unsigned char *buff, int len, \ + struct sockaddr_in *senderaddr) +{ + pcp_info_t pcp_msg_info; + + memset(&pcp_msg_info, 0, sizeof(pcp_info_t)); + + if(!inet_ntop(AF_INET, &senderaddr->sin_addr, + pcp_msg_info.senderaddrstr, + sizeof(pcp_msg_info.senderaddrstr))) { + syslog(LOG_ERR, "inet_ntop(pcpserver): %m"); + } + syslog(LOG_DEBUG, "PCP request received from %s:%hu %dbytes", + pcp_msg_info.senderaddrstr, ntohs(senderaddr->sin_port), len); + + if(buff[1] & 128) { + /* discarding PCP responses silently */ + return 0; + } + + if (processPCPRequest(buff, len, &pcp_msg_info) ) { + + createPCPResponse(buff, &pcp_msg_info); + + if ((len&0x03)!=0) { + len = len+4-(len&0x03); //round up resp. length to multiple of 4 + } + + len = sendto(s, buff, len, 0, + (struct sockaddr *)senderaddr, sizeof(struct sockaddr_in)); + if( len < 0 ) { + syslog(LOG_ERR, "sendto(pcpserver): %m"); + } + } + + return 0; +} +#endif /*ENABLE_PCP*/ diff --git a/miniupnpd/pcpserver.h b/miniupnpd/pcpserver.h new file mode 100644 index 0000000..ee46fee --- /dev/null +++ b/miniupnpd/pcpserver.h @@ -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 */ diff --git a/miniupnpd/upnpglobalvars.c b/miniupnpd/upnpglobalvars.c index b339183..bf07eae 100644 --- a/miniupnpd/upnpglobalvars.c +++ b/miniupnpd/upnpglobalvars.c @@ -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"; diff --git a/miniupnpd/upnpglobalvars.h b/miniupnpd/upnpglobalvars.h index ad9b4e0..f7d5705 100644 --- a/miniupnpd/upnpglobalvars.h +++ b/miniupnpd/upnpglobalvars.h @@ -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; diff --git a/miniupnpd/upnpredirect.c b/miniupnpd/upnpredirect.c index 5c9beae..7b398ec 100644 --- a/miniupnpd/upnpredirect.c +++ b/miniupnpd/upnpredirect.c @@ -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, ×tamp, + &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)