/* iterate through the redirection list to find those who came * from NAT-PMP and select the first to expire */ int ScanNATPMPforExpiration() { char desc[64]; unsigned short iport, eport; int proto; int r, i; unsigned timestamp; nextnatpmptoclean_eport = 0; nextnatpmptoclean_timestamp = 0; for(i = 0; ; i++) { r = get_redirect_rule_by_index(i, 0, &eport, 0, 0, &iport, &proto, desc, sizeof(desc), ×tamp, 0, 0); if(r<0) break; if(sscanf(desc, "NAT-PMP %u", ×tamp) == 1) { if( !nextnatpmptoclean_eport || timestamp < nextnatpmptoclean_timestamp) { nextnatpmptoclean_eport = eport; nextnatpmptoclean_proto = proto; nextnatpmptoclean_timestamp = timestamp; syslog(LOG_DEBUG, "set nextnatpmptoclean_timestamp to %u", timestamp); } } } return 0; }
void write_ruleset_details(int s) { int proto = 0; unsigned short eport, iport; char desc[64]; char iaddr[32]; char rhost[32]; unsigned int timestamp; u_int64_t packets; u_int64_t bytes; int i = 0; char buffer[256]; int n; write(s, "Ruleset :\n", 10); while(get_redirect_rule_by_index(i, 0/*ifname*/, &eport, iaddr, sizeof(iaddr), &iport, &proto, desc, sizeof(desc), rhost, sizeof(rhost), ×tamp, &packets, &bytes) >= 0) { n = snprintf(buffer, sizeof(buffer), "%2d %s %s:%hu->%s:%hu " "'%s' %u %" PRIu64 " %" PRIu64 "\n", /*"'%s' %llu %llu\n",*/ i, proto==IPPROTO_TCP?"TCP":"UDP", rhost, eport, iaddr, iport, desc, timestamp, packets, bytes); write(s, buffer, n); i++; } }
void test_index(void) { char ifname[16/*IFNAMSIZ*/]; char iaddr[32]; char desc[64]; char rhost[32]; unsigned short iport = 0; unsigned short eport = 0; int proto = 0; unsigned int timestamp; ifname[0] = '\0'; iaddr[0] = '\0'; rhost[0] = '\0'; if(get_redirect_rule_by_index(0, ifname, &eport, iaddr, sizeof(iaddr), &iport, &proto, desc, sizeof(desc), rhost, sizeof(rhost), ×tamp, 0, 0) < 0) { printf("get.._by_index : no rule\n"); } else { printf("%s %u -> %s:%u proto %d\n", ifname, (unsigned int)eport, iaddr, (unsigned int)iport, proto); printf("description: \"%s\"\n", desc); } }
void write_ruleset_details(int s) { char ifname[IFNAMSIZ]; int proto = 0; unsigned short eport, iport; char desc[64]; char iaddr[32]; u_int64_t packets; u_int64_t bytes; int i = 0; char buffer[256]; int n; while(get_redirect_rule_by_index(i, ifname, &eport, iaddr, sizeof(iaddr), &iport, &proto, desc, sizeof(desc), &packets, &bytes) >= 0) { n = snprintf(buffer, sizeof(buffer), "%2d %s %s %hu->%s:%hu " "'%s' %llu %llu\n", i, ifname, proto==IPPROTO_TCP?"TCP":"UDP", eport, iaddr, iport, desc, packets, bytes); write(s, buffer, n); i++; } }
int main(int argc, char ** argv) { unsigned short eport, iport; const char * iaddr; printf("Usage %s <ext_port> <internal_ip> <internal_port>\n", argv[0]); if(argc<4) return -1; openlog("testiptcrdr", LOG_PERROR|LOG_CONS, LOG_LOCAL0); eport = (unsigned short)atoi(argv[1]); iaddr = argv[2]; iport = (unsigned short)atoi(argv[3]); printf("trying to redirect port %hu to %s:%hu\n", eport, iaddr, iport); if(addnatrule(IPPROTO_TCP, eport, iaddr, iport, NULL) < 0) return -1; if(add_filter_rule(IPPROTO_TCP, NULL, iaddr, iport) < 0) return -1; /* 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; }
int upnp_get_redirection_infos_by_index(int index, unsigned short * eport, char * protocol, unsigned short * iport, char * iaddr, int iaddrlen, char * desc, int desclen, char * rhost, int rhostlen, unsigned int * leaseduration) { /*char ifname[IFNAMSIZ];*/ int proto = 0; unsigned int timestamp; time_t current_time; if(desc && (desclen > 0)) desc[0] = '\0'; if(rhost && (rhostlen > 0)) rhost[0] = '\0'; if(get_redirect_rule_by_index(index, 0/*ifname*/, eport, iaddr, iaddrlen, iport, &proto, desc, desclen, rhost, rhostlen, ×tamp, 0, 0) < 0) return -1; else { current_time = time(NULL); *leaseduration = (timestamp > (unsigned int)current_time) ? (timestamp - current_time) : 0; if(proto == IPPROTO_TCP) memcpy(protocol, "TCP", 4); #ifdef IPPROTO_UDPLITE else if(proto == IPPROTO_UDPLITE) memcpy(protocol, "UDPLITE", 8); #endif /* IPPROTO_UDPLITE */ else memcpy(protocol, "UDP", 4); return 0; } }
/* functions used to remove unused rules */ struct rule_state * get_upnp_rules_state_list(int max_rules_number_target) { char ifname[IFNAMSIZ]; int proto; unsigned short iport; struct rule_state * tmp; struct rule_state * list = 0; int i = 0; tmp = malloc(sizeof(struct rule_state)); if(!tmp) return 0; while(get_redirect_rule_by_index(i, ifname, &tmp->eport, 0, 0, &iport, &proto, 0, 0, &tmp->packets, &tmp->bytes) >= 0) { 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; } free(tmp); /* return empty list if not enough redirections */ if(i<=max_rules_number_target) while(list) { tmp = list; list = tmp->next; free(tmp); } /* return list */ return list; }
int upnp_get_redirection_infos_by_index(int index, unsigned short * eport, char * protocol, unsigned short * iport, char * iaddr, int iaddrlen, char * desc, int desclen) { /*char ifname[IFNAMSIZ];*/ int proto = 0; if(desc && (desclen > 0)) desc[0] = '\0'; if(get_redirect_rule_by_index(index, 0/*ifname*/, eport, iaddr, iaddrlen, iport, &proto, desc, desclen, 0, 0) < 0) return -1; else { if(proto == IPPROTO_TCP) memcpy(protocol, "TCP", 4); else memcpy(protocol, "UDP", 4); return 0; } }
/* functions used to remove unused rules * As a side effect, delete expired rules (based on LeaseDuration) */ struct rule_state * get_upnp_rules_state_list(int max_rules_number_target) { /*char ifname[IFNAMSIZ];*/ int proto; unsigned short iport; unsigned int timestamp; struct rule_state * tmp; struct rule_state * list = 0; struct rule_state * * p; int i = 0; time_t current_time; /*ifname[0] = '\0';*/ tmp = malloc(sizeof(struct rule_state)); if(!tmp) return 0; current_time = time(NULL); nextruletoclean_timestamp = 0; while(get_redirect_rule_by_index(i, /*ifname*/0, &tmp->eport, 0, 0, &iport, &proto, 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; } free(tmp); /* remove the redirections that need to be removed */ for(p = &list, tmp = list; tmp; tmp = *p) { if(tmp->to_remove) { syslog(LOG_INFO, "remove port mapping %hu %s because it has expired", tmp->eport, (tmp->proto==IPPROTO_TCP)?"TCP":"UDP"); _upnp_delete_redir(tmp->eport, tmp->proto); *p = tmp->next; free(tmp); i--; } else { p = &(tmp->next); } } /* return empty list if not enough redirections */ if(i<=max_rules_number_target) while(list) { tmp = list; list = tmp->next; free(tmp); } /* return list */ return list; }
/** read the request from the socket, process it and then send the * response back. */ void ProcessIncomingNATPMPPacket(int s) { 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); int n; char senderaddrstr[16]; n = recvfrom(s, req, sizeof(req), 0, (struct sockaddr *)&senderaddr, &senderaddrlen); if(n<0) { /* EAGAIN, EWOULDBLOCK and EINTR : silently ignore (retry next time) * other errors : log to LOG_ERR */ if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) { syslog(LOG_ERR, "recvfrom(natpmp): %m"); } return; } 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); if(n<2 || ((((req[1]-1)&~1)==0) && n<12)) { syslog(LOG_WARNING, "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 */ *((uint32_t *)(resp+4)) = htonl(time(NULL) - startup_time); if(req[0] > 0) { /* invalid version */ syslog(LOG_WARNING, "unsupported NAT-PMP version : %u", (unsigned)req[0]); resp[3] = 1; /* unsupported version */ } else switch(req[1]) { case 0: /* Public address request */ syslog(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 = ntohs(*((uint16_t *)(req+4))); eport = ntohs(*((uint16_t *)(req+6))); lifetime = ntohl(*((uint32_t *)(req+8))); proto = (req[1]==1)?IPPROTO_UDP:IPPROTO_TCP; syslog(LOG_INFO, "NAT-PMP port mapping request : " "%hu->%s:%hu %s lifetime=%us", eport, senderaddrstr, iport, (req[1]==1)?"udp":"tcp", lifetime); if(eport==0) eport = iport; /* TODO: accept port mapping if iport ok but eport not ok * (and set eport correctly) */ if(lifetime == 0) { /* remove the mapping */ if(iport == 0) { /* remove all the mappings for this client */ int index = 0; unsigned short eport2, iport2; char iaddr2[16]; int proto2; char desc[64]; while(get_redirect_rule_by_index(index, 0, &eport2, iaddr2, sizeof(iaddr2), &iport2, &proto2, desc, sizeof(desc), 0, 0, ×tamp, 0, 0) >= 0) { syslog(LOG_DEBUG, "%d %d %hu->'%s':%hu '%s'", index, proto2, eport2, iaddr2, iport2, desc); if(0 == strcmp(iaddr2, senderaddrstr) && 0 == memcmp(desc, "NAT-PMP", 7)) { r = _upnp_delete_redir(eport2, proto2); /* TODO : check return value */ if(r<0) { syslog(LOG_ERR, "failed to remove port mapping"); index++; } else { syslog(LOG_INFO, "NAT-PMP %s port %hu mapping removed", proto2==IPPROTO_TCP?"TCP":"UDP", eport2); } } else { index++; } } } else { /* To improve the interworking between nat-pmp and * UPnP, we should check that we remove only NAT-PMP * mappings */ r = _upnp_delete_redir(eport, proto); /*syslog(LOG_DEBUG, "%hu %d r=%d", eport, proto, r);*/ if(r<0) { //syslog(LOG_ERR, "Failed to remove NAT-PMP mapping eport %hu, protocol %s", eport, (proto==IPPROTO_TCP)?"TCP":"UDP"); resp[3] = 2; /* Not Authorized/Refused */ } } 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)) { resp[3] = 2; /* Not Authorized/Refused */ } else do { 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 */ syslog(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(_upnp_delete_redir(eport, proto) < 0) { syslog(LOG_ERR, "failed to remove port mapping"); break; } } else { eport++; continue; } } { /* do the redirection */ char desc[64]; #if 0 timestamp = (unsigned)(time(NULL) - startup_time) + lifetime; snprintf(desc, sizeof(desc), "NAT-PMP %u", timestamp); #else timestamp = time(NULL) + lifetime; snprintf(desc, sizeof(desc), "NAT-PMP %hu %s", eport, (proto==IPPROTO_TCP)?"tcp":"udp"); #endif /* TODO : check return code */ if(upnp_redirect_internal(NULL, eport, senderaddrstr, iport, proto, desc, timestamp) < 0) { syslog(LOG_ERR, "Failed to add NAT-PMP %hu %s->%s:%hu '%s'", eport, (proto==IPPROTO_TCP)?"tcp":"udp", senderaddrstr, iport, desc); resp[3] = 3; /* Failure */ #if 0 } else if( !nextnatpmptoclean_eport || timestamp < nextnatpmptoclean_timestamp) { nextnatpmptoclean_timestamp = timestamp; nextnatpmptoclean_eport = eport; nextnatpmptoclean_proto = proto; #endif } break; } } while(r==0); *((uint16_t *)(resp+8)) = htons(iport); /* private port */ *((uint16_t *)(resp+10)) = htons(eport); /* public port */ *((uint32_t *)(resp+12)) = htonl(lifetime); /* Port Mapping lifetime */ } resplen = 16; break; default: resp[3] = 5; /* Unsupported OPCODE */ } n = sendto(s, resp, resplen, 0, (struct sockaddr *)&senderaddr, sizeof(senderaddr)); if(n<0) { syslog(LOG_ERR, "sendto(natpmp): %m"); } else if(n<resplen) { syslog(LOG_ERR, "sendto(natpmp): sent only %d bytes out of %d", n, resplen); } }
/** 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; 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); if(n<2 || ((((req[1]-1)&~1)==0) && n<12)) { syslog(LOG_WARNING, "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 */ syslog(LOG_WARNING, "unsupported NAT-PMP version : %u", (unsigned)req[0]); resp[3] = 1; /* unsupported version */ } else switch(req[1]) { case 0: /* Public address request */ syslog(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; syslog(LOG_INFO, "NAT-PMP port mapping request : " "%hu->%s:%hu %s lifetime=%us", eport, senderaddrstr, iport, (req[1]==1)?"udp":"tcp", lifetime); /* TODO: accept port mapping if iport ok but eport not ok * (and set eport correctly) */ if(lifetime == 0) { /* remove the mapping */ /* RFC6886 : * A client MAY also send an explicit packet to request deletion of a * mapping that is no longer needed. A client requests explicit * deletion of a mapping by sending a message to the NAT gateway * requesting the mapping, with the Requested Lifetime in Seconds set to * zero. The Suggested External Port MUST be set to zero by the client * on sending, and MUST be ignored by the gateway on reception. */ int index = 0; unsigned short eport2, iport2; char iaddr2[16]; int proto2; char desc[64]; eport = 0; /* to indicate correct removing of port mapping */ while(get_redirect_rule_by_index(index, 0, &eport2, iaddr2, sizeof(iaddr2), &iport2, &proto2, desc, sizeof(desc), 0, 0, ×tamp, 0, 0) >= 0) { syslog(LOG_DEBUG, "%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 = _upnp_delete_redir(eport2, proto2); if(r < 0) { syslog(LOG_ERR, "Failed to remove NAT-PMP mapping eport %hu, protocol %s", eport2, (proto2==IPPROTO_TCP)?"TCP":"UDP"); resp[3] = 2; /* Not Authorized/Refused */ break; } else { syslog(LOG_INFO, "NAT-PMP %s port %hu mapping removed", proto2==IPPROTO_TCP?"TCP":"UDP", eport2); index--; } } } index++; } } 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 */ syslog(LOG_ERR, "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) */ syslog(LOG_ERR, "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(!check_upnp_rule_against_permissions(upnppermlist, num_upnpperm, eport, senderaddr->sin_addr, iport)) { eport++; if(eport == 0) eport++; /* skip port zero */ continue; } 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) { syslog(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 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 already existing */ syslog(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(_upnp_delete_redir(eport, proto) < 0) { syslog(LOG_ERR, "failed to remove port mapping"); break; } } else { eport++; if(eport == 0) eport++; /* skip port zero */ continue; } } /* do the redirection */ #if 0 timestamp = (unsigned)(time(NULL) - startup_time) + lifetime; snprintf(desc, sizeof(desc), "NAT-PMP %u", timestamp); #else timestamp = time(NULL) + lifetime; snprintf(desc, sizeof(desc), "NAT-PMP %hu %s", eport, (proto==IPPROTO_TCP)?"tcp":"udp"); #endif /* TODO : check return code */ if(upnp_redirect_internal(NULL, eport, senderaddrstr, iport, proto, desc, timestamp) < 0) { syslog(LOG_ERR, "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) { syslog(LOG_ERR, "sendto(natpmp): %m"); } else if(n<resplen) { syslog(LOG_ERR, "sendto(natpmp): sent only %d bytes out of %d", n, resplen); } }