int main(int argc, char * * argv) { #ifndef CHECK_PORTINUSE UNUSED(argc); UNUSED(argv); printf("CHECK_PORTINUSE is not defined.\n"); #else /* CHECK_PORTINUSE */ int r; const char * if_name; unsigned eport; int proto; const char * iaddr; unsigned iport; if(argc <= 5) { fprintf(stderr, "usage: %s if_name eport (tcp|udp) iaddr iport\n", argv[0]); return 1; } openlog("testportinuse", LOG_CONS|LOG_PERROR, LOG_USER); if_name = argv[1]; eport = (unsigned)atoi(argv[2]); proto = (0==strcmp(argv[3], "tcp"))?IPPROTO_TCP:IPPROTO_UDP; iaddr = argv[4]; iport = (unsigned)atoi(argv[5]); r = port_in_use(if_name, eport, proto, iaddr, iport); printf("port_in_use(%s, %u, %d, %s, %u) returned %d\n", if_name, eport, proto, iaddr, iport, r); closelog(); #endif /* CHECK_PORTINUSE */ return 0; }
int bmi_port_interface_add(bmi_port_mgr_t *port_mgr, const char *ifname, int port_num, const char *pcap_input_dump, const char* pcap_output_dump) { if(!port_num_valid(port_num)) return -1; bmi_port_t *port = get_port(port_mgr, port_num); if(port_in_use(port)) return -1; port->ifname = strdup(ifname); bmi_interface_t *bmi; if(bmi_interface_create(&bmi, ifname) != 0) return -1; if(pcap_input_dump) bmi_interface_add_dumper(bmi, pcap_input_dump, 1); if(pcap_output_dump) bmi_interface_add_dumper(bmi, pcap_output_dump, 0); pthread_mutex_lock(&port_mgr->lock); port->bmi = bmi; int fd = bmi_interface_get_fd(port->bmi); port->fd = fd; if(fd > port_mgr->max_fd) port_mgr->max_fd = fd; FD_SET(fd, &port_mgr->fds); pthread_mutex_unlock(&port_mgr->lock); return 0; }
/* upnp_redirect() * calls OS/fw dependant implementation of the redirection. * protocol should be the string "TCP" or "UDP" * returns: 0 on success * -1 failed to redirect * -2 already redirected * -3 permission check failed */ int upnp_redirect(const char * rhost, unsigned short eport, const char * iaddr, unsigned short iport, const char * protocol, const char * desc, unsigned int leaseduration) { int proto, r; char iaddr_old[32]; unsigned short iport_old; struct in_addr address; unsigned int timestamp; proto = proto_atoi(protocol); if(inet_aton(iaddr, &address) < 0) { syslog(LOG_ERR, "inet_aton(%s) : %m", iaddr); return -1; } if(!check_upnp_rule_against_permissions(upnppermlist, num_upnpperm, eport, address, iport)) { syslog(LOG_INFO, "redirection permission check failed for " "%hu->%s:%hu %s", eport, iaddr, iport, protocol); return -3; } 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 existing redirect rule matches redirect request return success * xbox 360 does not keep track of the port it redirects and will * redirect another port when receiving ConflictInMappingEntry */ if(strcmp(iaddr, iaddr_old)==0 && iport==iport_old) { /* redirection allready exists */ 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"); return 0; } } else { syslog(LOG_INFO, "port %hu protocol %s already redirected to %s:%hu", eport, protocol, iaddr_old, iport_old); return -2; } #ifdef CHECK_PORTINUSE } else if (port_in_use(ext_if_name, eport, proto, iaddr, iport) > 0) { syslog(LOG_INFO, "port %hu protocol %s already in use", eport, protocol); return -2; #endif /* CHECK_PORTINUSE */ } timestamp = (leaseduration > 0) ? time(NULL) + leaseduration : 0; syslog(LOG_INFO, "redirecting port %hu to %s:%hu protocol %s for: %s", eport, iaddr, iport, protocol, desc); return upnp_redirect_internal(rhost, eport, iaddr, iport, proto, desc, timestamp); }
int bmi_port_send(bmi_port_mgr_t *port_mgr, int port_num, const char *buffer, int len) { if(!port_num_valid(port_num)) return -1; bmi_port_t *port = get_port(port_mgr, port_num); if(!port_in_use(port)) return -1; if(bmi_interface_send(port->bmi, buffer, len) != 0) return -1; return 0; }
int bmi_port_destroy_mgr(bmi_port_mgr_t *port_mgr) { int i; for(i = 0; i < PORT_COUNT_MAX; i++) { bmi_port_t *port = get_port(port_mgr, i); if(port_in_use(port)) bmi_port_interface_remove(port_mgr, i); } port_mgr->max_fd = -1; // used to signal the thread it needs to terminate pthread_join(port_mgr->select_thread, NULL); pthread_mutex_destroy(&port_mgr->lock); free(port_mgr); return 0; }
int bmi_port_interface_remove(bmi_port_mgr_t *port_mgr, int port_num) { if(!port_num_valid(port_num)) return -1; bmi_port_t *port = get_port(port_mgr, port_num); if(!port_in_use(port)) return -1; free(port->ifname); pthread_mutex_lock(&port_mgr->lock); if(bmi_interface_destroy(port->bmi) != 0) return -1; FD_CLR(port->fd, &port_mgr->fds); memset(port, 0, sizeof(bmi_port_t)); pthread_mutex_unlock(&port_mgr->lock); return 0; }
/* upnp_redirect() * calls OS/fw dependant implementation of the redirection. * protocol should be the string "TCP" or "UDP" * returns: 0 on success * -1 failed to redirect * -2 already redirected * -3 permission check failed * -4 already redirected (other mechanism) */ int upnp_redirect(const char * rhost, unsigned short eport, const char * iaddr, unsigned short iport, const char * protocol, const char * desc, unsigned int leaseduration) { int proto, r; char iaddr_old[32]; char rhost_old[32]; unsigned short iport_old; struct in_addr address; unsigned int timestamp; proto = proto_atoi(protocol); if(inet_aton(iaddr, &address) <= 0) { syslog(LOG_ERR, "inet_aton(%s) FAILED", iaddr); return -1; } if(!check_upnp_rule_against_permissions(upnppermlist, num_upnpperm, eport, address, iport)) { syslog(LOG_INFO, "redirection permission check failed for " "%hu->%s:%hu %s", eport, iaddr, iport, protocol); return -3; } /* IGDv1 (WANIPConnection:1 Service Template Version 1.01 / Nov 12, 2001) * - 2.2.20.PortMappingDescription : * Overwriting Previous / Existing Port Mappings: * If the RemoteHost, ExternalPort, PortMappingProtocol and InternalClient * are exactly the same as an existing mapping, the existing mapping values * for InternalPort, PortMappingDescription, PortMappingEnabled and * PortMappingLeaseDuration are overwritten. * Rejecting a New Port Mapping: * In cases where the RemoteHost, ExternalPort and PortMappingProtocol * are the same as an existing mapping, but the InternalClient is * different, the action is rejected with an appropriate error. * Add or Reject New Port Mapping behavior based on vendor implementation: * In cases where the ExternalPort, PortMappingProtocol and InternalClient * are the same, but RemoteHost is different, the vendor can choose to * support both mappings simultaneously, or reject the second mapping * with an appropriate error. * * - 2.4.16.AddPortMapping * This action creates a new port mapping or overwrites an existing * mapping with the same internal client. If the ExternalPort and * PortMappingProtocol pair is already mapped to another internal client, * an error is returned. * * IGDv2 (WANIPConnection:2 Service Standardized DCP (SDCP) Sep 10, 2010) * Protocol ExternalPort RemoteHost InternalClient Result * = = ≠ ≠ Failure * = = ≠ = Failure or success * (vendor specific) * = = = ≠ Failure * = = = = Success (overwrite) */ rhost_old[0] = '\0'; r = get_redirect_rule(ext_if_name, eport, proto, iaddr_old, sizeof(iaddr_old), &iport_old, 0, 0, rhost_old, sizeof(rhost_old), ×tamp, 0, 0); if(r == 0) { if(strcmp(iaddr, iaddr_old)==0 && ((rhost == NULL && rhost_old[0]=='\0') || (rhost && (strcmp(rhost, "*") == 0) && rhost_old[0]=='\0') || (rhost && (strcmp(rhost, rhost_old) == 0)))) { syslog(LOG_INFO, "updating existing port mapping %hu %s (rhost '%s') => %s:%hu", eport, protocol, rhost_old, iaddr_old, iport_old); timestamp = (leaseduration > 0) ? time(NULL) + leaseduration : 0; if(iport != iport_old) { r = update_portmapping(ext_if_name, eport, proto, iport, desc, timestamp); } else { r = update_portmapping_desc_timestamp(ext_if_name, eport, proto, desc, timestamp); } #ifdef ENABLE_LEASEFILE if(r == 0) { lease_file_remove(eport, proto); lease_file_add(eport, iaddr, iport, proto, desc, timestamp); } #endif /* ENABLE_LEASEFILE */ return r; } else { syslog(LOG_INFO, "port %hu %s (rhost '%s') already redirected to %s:%hu", eport, protocol, rhost_old, iaddr_old, iport_old); return -2; } #ifdef CHECK_PORTINUSE } else if (port_in_use(ext_if_name, eport, proto, iaddr, iport) > 0) { syslog(LOG_INFO, "port %hu protocol %s already in use", eport, protocol); return -4; #endif /* CHECK_PORTINUSE */ } else { timestamp = (leaseduration > 0) ? time(NULL) + leaseduration : 0; syslog(LOG_INFO, "redirecting port %hu to %s:%hu protocol %s for: %s", eport, iaddr, iport, protocol, desc); return upnp_redirect_internal(rhost, eport, iaddr, iport, proto, desc, timestamp); } }
/** 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); } }