diff --git a/Makefile b/Makefile new file mode 100755 index 0000000..b85395e --- /dev/null +++ b/Makefile @@ -0,0 +1,21 @@ + +all: natpmpd + +CC=$(COMPILE_PREFIX)gcc +AR=$(COMPILE_PREFIX)ar rcs + +%.o : %.c + $(CC) $(CFLAGS) -c $< -o $@ + +%.o : %.cpp + $(CXX) $(CFLAGS) -c $< -o $@ + +OBJS = natpmp.o main.o firewall.o iface.o + +# +natpmpd: $(OBJS) + $(CC) -o natpmpd $(LDFLAGS) $(OBJS) -levent # -levent_core + + +clean: + rm *.o natpmpd diff --git a/README.md b/README.md old mode 100644 new mode 100755 index a5668bb..d3bb121 --- a/README.md +++ b/README.md @@ -1 +1,4 @@ -# tinynatpmpd +# tinynatpmpd 是一个在OpenWrt上运行的NAT-PMP协议服务器端。 +我的OpenWRT装上miniupnp后,在防火墙穿透仍然存有问题,这让我将目光投向其他的替代方案,遗憾的是我没有快速找到解决的办法,无奈之下只能根据现成的协议和源码进行修改,快速实现这样一个在防火墙上运行的路由转发功能。 +目前的限制有: 不支持删除转发功能、一台机器只能有一个防火墙端口(但是这个多我而言够用了) +我的邮件是root#envcat.com,我是广州市孚海软件技术有限公司的创办人。 diff --git a/firewall.c b/firewall.c new file mode 100755 index 0000000..1787263 --- /dev/null +++ b/firewall.c @@ -0,0 +1,236 @@ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "natpmp.h" +#define FUHAI_NATPMP_SNAT_CHAIN "FUHAI_SNATPMP" +#define FUHAI_NATPMP_DNAT_CHAIN "FUHAI_DNATPMP" +#define TABLE_FUHAI_WIFI_TO_ROUTER "WIFI2Router" + +static int execute(char *cmd_line, int quiet) +{ + int pid, + status, + rc; + + const char *new_argv[4]; + new_argv[0] = "/bin/sh"; + new_argv[1] = "-c"; + new_argv[2] = cmd_line; + new_argv[3] = NULL; + + //printf("Exec [%s]\n", cmd_line); + + pid = fork(); + if (pid == 0) { /* for the child process: */ + /* We don't want to see any errors if quiet flag is on */ + if (quiet) close(2); + if (execvp("/bin/sh", (char *const *)new_argv) == -1) { /* execute the command */ + LOG_ERROR( "execvp(): %s", strerror(errno)); + } else { + LOG_ERROR( "execvp() failed"); + } + exit(21); + } + + /* for the parent: */ + //LOG_DBG( "Waiting for PID %d to exit", pid); + rc = waitpid(pid, &status, 0); + //LOG_DBG( "Process PID %d exited", rc); + + return (WEXITSTATUS(status)); +} + + +int safe_asprintf(char **strp, const char *fmt, ...) { + va_list ap; + int retval; + + va_start(ap, fmt); + retval = safe_vasprintf(strp, fmt, ap); + va_end(ap); + + return (retval); +} + +int safe_vasprintf(char **strp, const char *fmt, va_list ap) { + int retval; + + retval = vasprintf(strp, fmt, ap); + + if (retval == -1) { + LOG_ERROR( "Failed to vasprintf: %s. Bailing out", strerror(errno)); + exit (16); + } + return (retval); +} + +static int iptables_do_command(const char *format, ...) +{ + va_list vlist; + char *fmt_cmd; + char *cmd; + int rc; + + va_start(vlist, format); + safe_vasprintf(&fmt_cmd, format, vlist); + va_end(vlist); + + safe_asprintf(&cmd, "iptables %s", fmt_cmd); + free(fmt_cmd); + + LOG_DBG( "Executing command: [%s]", cmd); + + rc = execute(cmd, 0); + if (rc!=0) { + // If quiet, do not display the error + LOG_ERROR( "iptables command failed(%d): %s", rc, cmd); + } + free(cmd); + return rc; +} + +/* +delete_redirect_and_filter_rules +*/ +int _fw_delete_redir(unsigned short eport, int proto) +{ + char tmpbuf[128]; + char* netproto = (proto == IPPROTO_UDP ? "udp" : "tcp"); + //清理旧的转发规则 + snprintf(tmpbuf,sizeof(tmpbuf),"%s dpt:%d",netproto, eport); // tcp dpt:26633 to:192.168.16.226:51832 + iptables_fw_destroy_mention("nat", "PREROUTING", tmpbuf); + + //iptables -t nat -D chain myindex + //iptables -t nat -F chain + + printf("redirect remove eport %d\n", eport); + printf("redirect remove proto %s\n", proto == IPPROTO_UDP ? "UDP" : "TCP"); + return 0; +} + + +/* +*/ +int fw_redirect_internal(const char * rhost, unsigned short eport, + const char * iaddr, unsigned short iport, + int proto, const char * desc, + unsigned int timestamp) +{ + char snat_cmd[256]; + char dnat_cmd[256]; + char *netproto; + + netproto = (proto == IPPROTO_UDP ? "udp" : "tcp"); + if (rhost != NULL) + { + printf("redirect rhost %s\n", rhost); + } + + printf("redirect iaddr %s\n", iaddr); + printf("redirect desc %s\n", desc); + + printf("redirect eport %d\n", eport); + printf("redirect iport %d\n", iport); + printf("redirect proto %s\n", netproto); + + char tmpbuf[128]; + //清理旧的转发规则 + snprintf(tmpbuf,sizeof(tmpbuf),"%s", iaddr); // tcp dpt:26633 to:192.168.16.226:51832 + iptables_fw_destroy_mention("nat", "PREROUTING", tmpbuf); + iptables_fw_destroy_mention("nat", "POSTROUTING", tmpbuf); + + /*dnat*/ + snprintf(dnat_cmd,sizeof(dnat_cmd), + "-t nat -A PREROUTING -p %s --dport %d -j DNAT --to-destination %s:%d", + netproto, eport, iaddr, iport); + iptables_do_command(dnat_cmd); + + /*snat*/ + snprintf(snat_cmd,sizeof(snat_cmd), + "-t nat -A POSTROUTING -d %s -p %s --dport %d -j SNAT --to %s", + iaddr,netproto, iport, ifstrlanaddr); + iptables_do_command(snat_cmd); + return 0; +} + +int iptables_fw_destroy_mention( + const char * table, + const char * chain, + const char * mention ) { + FILE *p = NULL; + char *command = NULL; + char *command2 = NULL; + char line[4096]; + char rulenum[10]; + char *victim = strdup(mention); + int deleted = 0; + + safe_asprintf(&command, "iptables -t %s -L %s -n --line-numbers -v", + table, chain); + + if ((p = popen(command, "r"))) { + while (!feof(p) && fgetc(p) != '\n'); + while (!feof(p) && fgetc(p) != '\n'); + while (fgets(line, sizeof(line), p)) { + if (strstr(line, victim)) { + if (sscanf(line, "%9[0-9]", rulenum) == 1) { + LOG_DBG( "Deleting rule %s from %s.%s because it mentions %s", rulenum, table, chain, victim); + safe_asprintf(&command2, "-t %s -D %s %s", table, chain, rulenum); + iptables_do_command(command2); + free(command2); + deleted = 1; + break; + } + } + } + pclose(p); + } + + free(command); + free(victim); + + if (deleted) { + /* Recurse just in case there are more in the same table+chain */ + iptables_fw_destroy_mention(table, chain, mention); + } + + return (deleted); +} + +void fw_init() +{ +#if 1 + iptables_do_command("-t nat -N " FUHAI_NATPMP_DNAT_CHAIN); + + /* Assign links and rules to these new chains */ + iptables_do_command("-t nat -A PREROUTING -i %s -j "FUHAI_NATPMP_DNAT_CHAIN, ext_if_name); +#endif + +#if 0 + iptables_do_command("-t nat -A " FUHAI_NATPMP_DNAT_CHAIN " -d %s -j " TABLE_FUHAI_WIFI_TO_ROUTER, ifstrwanaddr); + iptables_do_command("-t nat -A " TABLE_FUHAI_WIFI_TO_ROUTER " -j ACCEPT"); +#endif + +} + +void fw_destroy() +{ + iptables_fw_destroy_mention("nat", "PREROUTING", FUHAI_NATPMP_DNAT_CHAIN); + + iptables_do_command("-t nat -F " FUHAI_NATPMP_DNAT_CHAIN); + iptables_do_command("-t nat -X " FUHAI_NATPMP_DNAT_CHAIN); + + iptables_do_command("-t nat -F " TABLE_FUHAI_WIFI_TO_ROUTER); + iptables_do_command("-t nat -X " TABLE_FUHAI_WIFI_TO_ROUTER); +} + diff --git a/iface.c b/iface.c new file mode 100755 index 0000000..bdb0df4 --- /dev/null +++ b/iface.c @@ -0,0 +1,191 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "natpmp.h" + +extern volatile sig_atomic_t should_send_public_address_change_notif; + + +static int open_router_watch_socket(void) +{ + int s; + struct sockaddr_nl addr; + + s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (s == -1) + { + LOG_ERROR( "socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE): %m"); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.nl_family = AF_NETLINK; + addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR; + + if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) + { + LOG_ERROR( "bind(netlink): %m"); + close(s); + return -1; + } + return s; +} + + +static void process_watch_notify(int s) +{ + char buffer[4096]; + struct iovec iov; + struct msghdr hdr; + struct nlmsghdr *nlhdr; + struct ifinfomsg *ifi; + struct ifaddrmsg *ifa; + int len; + + struct rtattr *rth; + int rtl; + + unsigned int ext_if_name_index = 0; + + iov.iov_base = buffer; + iov.iov_len = sizeof(buffer); + + memset(&hdr, 0, sizeof(hdr)); + hdr.msg_iov = &iov; + hdr.msg_iovlen = 1; + + len = recvmsg(s, &hdr, 0); + if (len < 0) + { + LOG_ERROR( "recvmsg(s, &hdr, 0): %m"); + return; + } + + if(ext_if_name) { + ext_if_name_index = if_nametoindex(ext_if_name); + } + + for (nlhdr = (struct nlmsghdr *) buffer; + NLMSG_OK (nlhdr, (unsigned int)len); + nlhdr = NLMSG_NEXT (nlhdr, len)) + { + int is_del = 0; + char address[48]; + char ifname[IFNAMSIZ]; + address[0] = '\0'; + ifname[0] = '\0'; + if (nlhdr->nlmsg_type == NLMSG_DONE) + break; + switch(nlhdr->nlmsg_type) { + case RTM_DELLINK: + is_del = 1; + case RTM_NEWLINK: + ifi = (struct ifinfomsg *) NLMSG_DATA(nlhdr); +#if 0 + if(is_del) { + if(ProcessInterfaceDown(ifi) < 0) + LOG_ERROR( "ProcessInterfaceDown(ifi) failed"); + } else { + if(ProcessInterfaceUp(ifi) < 0) + LOG_ERROR( "ProcessInterfaceUp(ifi) failed"); + } +#endif + break; + case RTM_DELADDR: + is_del = 1; + case RTM_NEWADDR: + /* see /usr/include/linux/netlink.h + * and /usr/include/linux/rtnetlink.h */ + ifa = (struct ifaddrmsg *) NLMSG_DATA(nlhdr); + LOG_DBG( "%s %s index=%d fam=%d", "ProcessInterfaceWatchNotify", + is_del ? "RTM_DELADDR" : "RTM_NEWADDR", + ifa->ifa_index, ifa->ifa_family); + for(rth = IFA_RTA(ifa), rtl = IFA_PAYLOAD(nlhdr); + rtl && RTA_OK(rth, rtl); + rth = RTA_NEXT(rth, rtl)) { + char tmp[128]; + memset(tmp, 0, sizeof(tmp)); + switch(rth->rta_type) { + case IFA_ADDRESS: + case IFA_LOCAL: + case IFA_BROADCAST: + case IFA_ANYCAST: + inet_ntop(ifa->ifa_family, RTA_DATA(rth), tmp, sizeof(tmp)); + if(rth->rta_type == IFA_ADDRESS) + strncpy(address, tmp, sizeof(address)); + break; + case IFA_LABEL: + strncpy(tmp, RTA_DATA(rth), sizeof(tmp)); + strncpy(ifname, tmp, sizeof(ifname)); + break; + case IFA_CACHEINFO: + { + struct ifa_cacheinfo *cache_info; + cache_info = RTA_DATA(rth); + snprintf(tmp, sizeof(tmp), "valid=%u prefered=%u", + cache_info->ifa_valid, cache_info->ifa_prefered); + } + break; + default: + strncpy(tmp, "*unknown*", sizeof(tmp)); + } + LOG_DBG( " - %u - %s type=%d", + ifa->ifa_index, tmp, + rth->rta_type); + } + if(ifa->ifa_index == ext_if_name_index) { + should_send_public_address_change_notif = 1; + } + break; + default: + LOG_DBG( "%s type %d ignored", + "ProcessInterfaceWatchNotify", nlhdr->nlmsg_type); + } + } + +} + +static void iface_recvmsg(int fd, short event, void *arg) +{ + process_watch_notify(fd); + if (should_send_public_address_change_notif) + { + send_public_address_change_notification(NULL,0); + should_send_public_address_change_notif = 0; + } +} + +int iface_init(struct event_base *base) +{ + int fd; + static struct event ctrl_ev; + + fd = open_router_watch_socket(); + if (fd < 0) + { + LOG_ERROR("iface_init failed\n"); + return -1; + } + event_set(&ctrl_ev, fd, EV_READ|EV_PERSIST, + iface_recvmsg, NULL); + if (base) + event_base_set(base, &ctrl_ev); + event_add(&ctrl_ev, NULL); + return 0; +} diff --git a/main.c b/main.c new file mode 100755 index 0000000..8aaefd0 --- /dev/null +++ b/main.c @@ -0,0 +1,160 @@ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "natpmp.h" + +volatile sig_atomic_t should_send_public_address_change_notif; +char *ext_if_name; +char *lan_if_name; +char ifstrwanaddr[64]; +struct in_addr if_ip_addr; +struct in_addr if_ip_mask; + +char ifstrlanaddr[64]; +struct in_addr lan_ip_addr; +struct in_addr lan_ip_mask; +time_t startup_time = 0; + +static void +handle_signal(int sig, short event, void *arg) +{ + fw_destroy(); + exit(144); +} + +void update_boot_time() +{ + char buff[64]; + int uptime = 0, fd; + + startup_time = time(NULL); + + fd = open("/proc/uptime", O_RDONLY); + if(fd < 0) + { + LOG_ERROR( "open(\"/proc/uptime\" : %m"); + } + else + { + memset(buff, 0, sizeof(buff)); + if(read(fd, buff, sizeof(buff) - 1) < 0) + { + LOG_ERROR( "read(\"/proc/uptime\" : %m"); + } + else + { + uptime = atoi(buff); + LOG_ERROR("system uptime is %d seconds", uptime); + } + close(fd); + startup_time -= uptime; + } +} + +int main(int argc, char *argv[]) +{ + int ret = 0; + int debug = 0; + struct event ev_sighup; + struct event ev_sigint; + struct event ev_sigterm; + int c; + struct event_base *base = NULL; + struct natpmpd *env = NULL; + + ext_if_name = "pppoe-wan"; //"eth0.2"; + lan_if_name = "br-lan"; + while ((c = getopt(argc, argv, "de:f:v")) != -1) { + switch (c) { + case 'd': + debug = 1; + break; + case 'e': + lan_if_name = strdup(optarg); + break; + case 'f': + ext_if_name = strdup(optarg); + break; + case 'v': + break; + default: + break; + } + } + + ret = geteuid(); + update_boot_time(); + base = event_init(); + //base = event_base_new(); + + //if ((pw = getpwnam(NATPMPD_USER)) == NULL) + // errx(1, "unknown user %s", NATPMPD_USER); + + if (getifaddr(ext_if_name, ifstrwanaddr, 64, &if_ip_addr, &if_ip_mask) < 0) + { + LOG_ERROR("getifaddr(%s, ifaddr, 16) failed", ext_if_name); + //return 1; + } + + if (getifaddr(lan_if_name, ifstrlanaddr, 64, &lan_ip_addr, &lan_ip_mask) < 0) + { + LOG_ERROR("getifaddr(%s, ifaddr, 16) failed", lan_if_name); + return 1; + } + + LOG_INFO("LAN At %s - %s\n", lan_if_name, ifstrlanaddr); + LOG_INFO("WAN At %s - %s\n", ext_if_name, ifstrwanaddr); + + signal(SIGPIPE, SIG_IGN); + + + should_send_public_address_change_notif = 0; + + ret = iface_init(base); + if (ret < 0) + { + LOG_ERROR("iface_init failed"); + } + ret = natpmp_init(base); + if (ret < 0) + { + LOG_ERROR("natpmp_init failed"); + return 1; + } + send_public_address_change_notification(NULL,0); + fw_init(); + LOG_ERROR("natpmp RUNNING"); + if (debug == 0) + { + signal_set(&ev_sighup, SIGHUP, handle_signal, env); + signal_set(&ev_sigint, SIGINT, handle_signal, env); + signal_set(&ev_sigterm, SIGTERM, handle_signal, env); + signal_add(&ev_sighup, NULL); + signal_add(&ev_sigint, NULL); + signal_add(&ev_sigterm, NULL); + if (daemon(1, 0) == -1){ + printf("failed to daemonize"); + exit(1); + } + } + + if (base && 0) + { + event_base_dispatch(base); + event_base_free(base); + } + else + event_dispatch(); + fw_destroy(); + return 0; +} + diff --git a/natpmp.c b/natpmp.c new file mode 100755 index 0000000..8ed0c54 --- /dev/null +++ b/natpmp.c @@ -0,0 +1,610 @@ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "natpmp.h" + +#define NATPMP_MAX_VERSION 0 +#define NATPMP_MAX_RETRIES 10 +#define NATPMP_MAX_DELETES 3 +#define NATPMP_WORK_PORT 5351 +#define NATPMP_NOTIFI_PORT 5350 +#define NATPMP_MAX_PACKET_SIZE 16 +#define NATPMP_NOTIF_ADDR ("224.0.0.1") + +#define NATPMP_COMMAND_PROBE 0 + +void natpmp_recv_client_msg(int, short, void *); +void route_recvmsg(int, short, void *); +void natpmp_probe(int, short, void *); +int default_gateway(in_addr_t *); + +int mcast_fd; +int ctrl_fd; +in_addr_t gateway; +int probe_count; +struct event probe_ev; + + +typedef enum { + NATPMP_UNKNOWN = 0, + NATPMP_DISABLED, + NATPMP_ENABLED, +} natpmp_status_t; + +//natpmp_status_t status = NATPMP_UNKNOWN; + +#define INLINE static inline + +INLINE uint32_t readnu32(const uint8_t * p) +{ + return (p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]); +} +#define READNU32(p) readnu32(p) +INLINE uint16_t readnu16(const uint8_t * p) +{ + return (p[0] << 8 | p[1]); +} +#define READNU16(p) readnu16(p) +INLINE void writenu32(uint8_t * p, uint32_t n) +{ + p[0] = (n & 0xff000000) >> 24; + p[1] = (n & 0xff0000) >> 16; + p[2] = (n & 0xff00) >> 8; + p[3] = n & 0xff; +} +#define WRITENU32(p, n) writenu32(p, n) +INLINE void writenu16(uint8_t * p, uint16_t n) +{ + p[0] = (n & 0xff00) >> 8; + p[1] = n & 0xff; +} +#define WRITENU16(p, n) writenu16(p, n) + + +ssize_t +sendto_or_schedule(int sockfd, const void *buf, size_t len, int flags, + const struct sockaddr *dest_addr, socklen_t addrlen) +{ + return sendto(sockfd, buf, len, flags, dest_addr, addrlen); +} + +static void FillPublicAddressResponse(unsigned char * resp, in_addr_t senderaddr) +{ + char tmp[16]; + + if(!ext_if_name || ext_if_name[0]=='\0') { + resp[3] = 3; /* Network Failure (e.g. NAT box itself + * has not obtained a DHCP lease) */ + } else if(getifaddr(ext_if_name, tmp, INET_ADDRSTRLEN, NULL, NULL) < 0) { + LOG_ERROR( "Failed to get IP for interface %s", ext_if_name); + resp[3] = 3; /* Network Failure (e.g. NAT box itself + * has not obtained a DHCP lease) */ + } else { + inet_pton(AF_INET, tmp, resp+8); /* ok */ + } + +} + +static void process_incoming_packet(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; + int n = len; + char senderaddrstr[16]; + + update_boot_time(); + + if(!inet_ntop(AF_INET, &senderaddr->sin_addr, + senderaddrstr, sizeof(senderaddrstr))) { + LOG_ERROR( "inet_ntop(natpmp): %m"); + } + + LOG_INFO( "NAT-PMP request received from %s:%hu %dbytes", + senderaddrstr, ntohs(senderaddr->sin_port), n); + + if(n<2 || ((((req[1]-1)&~1)==0) && n<12)) { + LOG_ERROR( "discarding NAT-PMP request (too short) %dBytes", + n); + return; + } + if(req[1] & 128) { + /* discarding NAT-PMP responses silently */ + return; + } + memset(resp, 0, sizeof(resp)); + resplen = 8; + resp[1] = 128 + req[1]; /* response OPCODE is request OPCODE + 128 */ + /* setting response TIME STAMP : + * time elapsed since its port mapping table was initialized on + * startup or reset for any other reason */ + WRITENU32(resp+4, time(NULL) - startup_time); + if(req[0] > 0) { + /* invalid version */ + LOG_ERROR( "unsupported NAT-PMP version : %u", + (unsigned)req[0]); + resp[3] = 1; /* unsupported version */ + } else switch(req[1]) { + case 0: /* Public address request */ + LOG_INFO( "NAT-PMP public address request"); + FillPublicAddressResponse(resp, senderaddr->sin_addr.s_addr); + resplen = 12; + break; + case 1: /* UDP port mapping request */ + case 2: /* TCP port mapping request */ + { + unsigned short iport; /* private port */ + unsigned short eport; /* public port */ + uint32_t lifetime; /* lifetime=0 => remove port mapping */ + int r; + int proto; + char iaddr_old[16]; + unsigned short iport_old; + unsigned int timestamp; + + iport = READNU16(req+4); + eport = READNU16(req+6); + lifetime = READNU32(req+8); + proto = (req[1]==1)?IPPROTO_UDP:IPPROTO_TCP; + LOG_INFO( "NAT-PMP port mapping request : " + "%hu->%s:%hu %s lifetime=%us", + eport, senderaddrstr, iport, + (req[1]==1)?"udp":"tcp", lifetime); + + if(lifetime == 0) { + LOG_INFO( " REMOVE MAPPING\n"); + int index = 0; + unsigned short eport2, iport2; + char iaddr2[16]; + int proto2; + char desc[64]; + eport = 0; /* to indicate correct removing of port mapping */ + #if 0 + while(get_redirect_rule_by_index(index, 0, + &eport2, iaddr2, sizeof(iaddr2), + &iport2, &proto2, + desc, sizeof(desc), + 0, 0, ×tamp, 0, 0) >= 0) { + LOG_DBG( "%d %d %hu->'%s':%hu '%s'", + index, proto2, eport2, iaddr2, iport2, desc); + if(0 == strcmp(iaddr2, senderaddrstr) + && 0 == memcmp(desc, "NAT-PMP", 7)) { + /* (iport == 0) => remove all the mappings for this client */ + if((iport == 0) || ((iport == iport2) && (proto == proto2))) { + r = _fw_delete_redir(eport2, proto2); + if(r < 0) { + LOG_ERROR( "Failed to remove NAT-PMP mapping eport %hu, protocol %s", + eport2, (proto2==IPPROTO_TCP)?"TCP":"UDP"); + resp[3] = 2; /* Not Authorized/Refused */ + break; + } else { + LOG_INFO( "NAT-PMP %s port %hu mapping removed", + proto2==IPPROTO_TCP?"TCP":"UDP", eport2); + index--; + } + } + } + + index++; + } + #endif + } else if(iport==0) { + resp[3] = 2; /* Not Authorized/Refused */ + } else { /* iport > 0 && lifetime > 0 */ + unsigned short eport_first = 0; + int any_eport_allowed = 0; + char desc[64]; + if(eport==0) /* if no suggested external port, use same a internal port */ + eport = iport; + while(resp[3] == 0) { + if(eport_first == 0) { /* first time in loop */ + eport_first = eport; + } else if(eport == eport_first) { /* no eport available */ + if(any_eport_allowed == 0) { /* all eports rejected by permissions */ + LOG_ERROR( "No allowed eport for NAT-PMP %hu %s->%s:%hu", + eport, (proto==IPPROTO_TCP)?"tcp":"udp", senderaddrstr, iport); + resp[3] = 2; /* Not Authorized/Refused */ + } else { /* at least one eport allowed (but none available) */ + LOG_ERROR( "Failed to find available eport for NAT-PMP %hu %s->%s:%hu", + eport, (proto==IPPROTO_TCP)?"tcp":"udp", senderaddrstr, iport); + resp[3] = 4; /* Out of resources */ + } + break; + } + #if 0 + if(!check_fw_rule_against_permissions(upnppermlist, num_upnpperm, eport, senderaddr->sin_addr, iport)) { + eport++; + if(eport == 0) eport++; /* skip port zero */ + continue; + } + #endif + any_eport_allowed = 1; /* at lease one eport is allowed */ +#ifdef CHECK_PORTINUSE + if (port_in_use(ext_if_name, eport, proto, senderaddrstr, iport) > 0) { + LOG_INFO( "port %hu protocol %s already in use", + eport, (proto==IPPROTO_TCP)?"tcp":"udp"); + eport++; + if(eport == 0) eport++; /* skip port zero */ + continue; + } +#endif + #if 0 + r = get_redirect_rule(ext_if_name, eport, proto, + iaddr_old, sizeof(iaddr_old), + &iport_old, 0, 0, 0, 0, + ×tamp, 0, 0); + if(r==0) { + if(strcmp(senderaddrstr, iaddr_old)==0 + && iport==iport_old) { + /* redirection allready existing */ + LOG_INFO( "port %hu %s already redirected to %s:%hu, replacing", + eport, (proto==IPPROTO_TCP)?"tcp":"udp", iaddr_old, iport_old); + /* remove and then add again */ + if(_fw_delete_redir(eport, proto) < 0) { + LOG_ERROR( "failed to remove port mapping"); + break; + } + } else { + eport++; + if(eport == 0) eport++; /* skip port zero */ + continue; + } + } + #endif + /* do the redirection */ + + timestamp = time(NULL) + lifetime; + snprintf(desc, sizeof(desc), "NAT-PMP %hu %s", + eport, (proto==IPPROTO_TCP)?"tcp":"udp"); + + + /* TODO : check return code */ + if(fw_redirect_internal(NULL, eport, senderaddrstr, + iport, proto, desc, + timestamp) < 0) { + LOG_ERROR( "Failed to add NAT-PMP %hu %s->%s:%hu '%s'", + eport, (proto==IPPROTO_TCP)?"tcp":"udp", senderaddrstr, iport, desc); + resp[3] = 3; /* Failure */ + } + break; + } + } + WRITENU16(resp+8, iport); /* private port */ + WRITENU16(resp+10, eport); /* public port */ + WRITENU32(resp+12, lifetime); /* Port Mapping lifetime */ + } + resplen = 16; + break; + default: + resp[3] = 5; /* Unsupported OPCODE */ + } + n = sendto_or_schedule(s, resp, resplen, 0, + (struct sockaddr *)senderaddr, sizeof(*senderaddr)); + if(n<0) { + LOG_ERROR( "sendto(natpmp): %m"); + } else if(n 0) { /* skip the first line */ + p = buf; + /* skip the interface name */ + while(*p && !isspace(*p)) + p++; + while(*p && isspace(*p)) + p++; + if(sscanf(p, "%lx%lx", &d, &g)==2) { + if(d == 0 && g != 0) { /* default */ + *addr = g; + fclose(f); + return 0; + } + } + } + line++; + } + /* default route not found ! */ + if(f) + fclose(f); + return -1; +} + +int +getifaddr(const char * ifname, char * buf, int len, + struct in_addr * addr, struct in_addr * mask) +{ + /* use ioctl SIOCGIFADDR. Works only for ip v4 */ + /* SIOCGIFADDR struct ifreq * */ + int s; + struct ifreq ifr; + int ifrlen; + struct sockaddr_in * ifaddr; + ifrlen = sizeof(ifr); + + if(!ifname || ifname[0]=='\0') + return -1; + s = socket(PF_INET, SOCK_DGRAM, 0); + if(s < 0) + { + LOG_ERROR( "socket(PF_INET, SOCK_DGRAM): %m"); + return -1; + } + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + if(ioctl(s, SIOCGIFFLAGS, &ifr, &ifrlen) < 0) + { + LOG_DBG( "ioctl(s, SIOCGIFFLAGS, ...): %m"); + close(s); + return -1; + } + if ((ifr.ifr_flags & IFF_UP) == 0) + { + LOG_DBG( "network interface %s is down", ifname); + close(s); + return -1; + } + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + if(ioctl(s, SIOCGIFADDR, &ifr, &ifrlen) < 0) + { + LOG_ERROR( "ioctl(s, SIOCGIFADDR, ...): %m"); + close(s); + return -1; + } + ifaddr = (struct sockaddr_in *)&ifr.ifr_addr; + if(addr) *addr = ifaddr->sin_addr; + if(buf) + { + if(!inet_ntop(AF_INET, &ifaddr->sin_addr, buf, len)) + { + LOG_ERROR( "inet_ntop(): %m"); + close(s); + return -1; + } + } + if(mask) + { + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + if(ioctl(s, SIOCGIFNETMASK, &ifr, &ifrlen) < 0) + { + LOG_ERROR( "ioctl(s, SIOCGIFNETMASK, ...): %m"); + close(s); + return -1; + } +#ifdef ifr_netmask + *mask = ((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr; +#else + *mask = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr; +#endif + } + close(s); + + return 0; +} + diff --git a/natpmp.h b/natpmp.h new file mode 100755 index 0000000..92ad484 --- /dev/null +++ b/natpmp.h @@ -0,0 +1,57 @@ + +#ifndef _NATPMP_H +#define _NATPMP_H +#include + + +#define LOG_INFO( ...) do{ \ + printf(__VA_ARGS__); \ + printf("\n"); \ + } \ + while (0) + +#define LOG_DBG LOG_INFO +#define LOG_ERROR LOG_INFO + +struct natpmpd { + u_int8_t sc_flags; +#define NATPMPD_F_VERBOSE 0x01; + + const char *sc_confpath; + struct in_addr sc_address; +#if 0 + TAILQ_HEAD(listen_addrs, listen_addr) listen_addrs; + u_int8_t listen_all; + char sc_interface[IF_NAMESIZE]; + struct timeval sc_starttime; + int sc_delay; +#endif + struct event sc_announce_ev; + struct event sc_expire_ev; +}; + + +int natpmp_init(struct event_base *); + +extern char *ext_if_name; + +extern struct in_addr if_ip_addr; +extern struct in_addr if_ip_mask; +extern time_t startup_time; + +struct in_addr lan_ip_addr; +struct in_addr lan_ip_mask; +extern char ifstrlanaddr[64]; +extern char ifstrwanaddr[64]; + +int getifaddr(const char * ifname, char * buf, int len, + struct in_addr * addr, struct in_addr * mask); + +int upnp_redirect_internal(const char * rhost, unsigned short eport, + const char * iaddr, unsigned short iport, + int proto, const char * desc, + unsigned int timestamp); + +int _upnp_delete_redir(unsigned short eport, int proto); + +#endif diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..8e42b5b --- /dev/null +++ b/run.sh @@ -0,0 +1,28 @@ + +cat /proc/sys/net/ipv4/forwarding +cat /proc/sys/net/ipv4/conf/br-lan/forwarding +cat /proc/sys/net/ipv4/conf/pppoe-wan/forwarding + + +iptables -A INPUT -p tcp --dport 26633 -j ACCEPT +iptables -A OUTPUT -p tcp --dport 26633 -j ACCEPT + +#iptables -t nat -A PREROUTING -i eth0 -p tcp --dport $srcPortNumber -j REDIRECT --to-port $dstPortNumbe +#iptables -t nat -I PREROUTING --src $SRC_IP_MASK --dst $DST_IP -p tcp --dport $portNumber -j REDIRECT --to-ports $rediectPort + +iptables -t nat -A PREROUTING -p tcp --dport 63306 -j DNAT --to-destination 192.168.16.226:50648 +iptables -t nat -A POSTROUTING -d 192.168.16.226 -p tcp --dport 50648 -j SNAT --to 192.168.16.1 + +iptables -t nat -L PREROUTING -n --line-numbers -v +iptables -t nat -L POSTROUTING -n --line-numbers -v + +iptables -t nat -L -n -v +iptables-save + +#允许特定转发 +iptables -N MINIUPNPD +iptables -I FORWARD -i pppoe-wan -o br-lan -j MINIUPNPD +#DNAT端口映射 +iptables -t nat -N MINIUPNPD +iptables -t nat -I PREROUTING -i pppoe-wan -j MINIUPNPD +