1/* $Id: upnppinhole.c,v 1.7 2014/12/09 09:13:53 nanard Exp $ */ 2/* MiniUPnP project 3 * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ 4 * (c) 2006-2012 Thomas Bernard 5 * This software is subject to the conditions detailed 6 * in the LICENCE file provided within the distribution */ 7 8#include <stdlib.h> 9#include <string.h> 10#include <syslog.h> 11#include <sys/types.h> 12#include <sys/socket.h> 13#include <netinet/in.h> 14#include <net/if.h> 15#include <arpa/inet.h> 16 17#include <stdio.h> 18#include <ctype.h> 19#include <unistd.h> 20 21#include "macros.h" 22#include "config.h" 23#include "upnpredirect.h" 24#include "upnpglobalvars.h" 25#include "upnpevents.h" 26#include "upnppinhole.h" 27#ifdef __APPLE__ 28/* XXX - Apple version of PF API seems to differ from what 29 * pf/pfpinhole.c expects so don't use that at least.. */ 30#ifdef USE_PF 31#undef USE_PF 32#endif /* USE_PF */ 33#endif /* __APPLE__ */ 34#if defined(USE_NETFILTER) 35#include "netfilter/iptpinhole.h" 36#endif 37#if defined(USE_PF) 38#include "pf/pfpinhole.h" 39#endif 40#if defined(USE_IPF) 41#endif 42#if defined(USE_IPFW) 43#endif 44 45#ifdef ENABLE_UPNPPINHOLE 46 47#if 0 48int 49upnp_check_outbound_pinhole(int proto, int * timeout) 50{ 51 int s, tmptimeout, tmptime_out; 52 switch(proto) 53 { 54 case IPPROTO_UDP: 55 s = retrieve_timeout("udp_timeout", timeout); 56 return s; 57 break; 58 case IPPROTO_UDPLITE: 59 s = retrieve_timeout("udp_timeout_stream", timeout); 60 return s; 61 break; 62 case IPPROTO_TCP: 63 s = retrieve_timeout("tcp_timeout_established", timeout); 64 return s; 65 break; 66 case 65535: 67 s = retrieve_timeout("udp_timeout", timeout); 68 s = retrieve_timeout("udp_timeout_stream", &tmptimeout); 69 s = retrieve_timeout("tcp_timeout_established", &tmptime_out); 70 if(tmptimeout<tmptime_out) 71 { 72 if(tmptimeout<*timeout) 73 *timeout = tmptimeout; 74 } 75 else 76 { 77 if(tmptime_out<*timeout) 78 *timeout = tmptimeout; 79 } 80 return s; 81 break; 82 default: 83 return -5; 84 break; 85 } 86 return 0; 87} 88#endif 89 90/* upnp_add_inboundpinhole() 91 * returns: 1 on success 92 * -1 Pinhole space exhausted 93 * -4 invalid arguments 94 * -42 not implemented 95 * TODO : return uid on success (positive) or error value (negative) 96 */ 97int 98upnp_add_inboundpinhole(const char * raddr, 99 unsigned short rport, 100 const char * iaddr, 101 unsigned short iport, 102 int proto, 103 char * desc, 104 unsigned int leasetime, 105 int * uid) 106{ 107 int r; 108 time_t current; 109 unsigned int timestamp; 110 struct in6_addr address; 111 112 r = inet_pton(AF_INET6, iaddr, &address); 113 if(r <= 0) { 114 syslog(LOG_ERR, "inet_pton(%d, %s, %p) FAILED", 115 AF_INET6, iaddr, &address); 116 return -4; 117 } 118 current = time(NULL); 119 timestamp = current + leasetime; 120 r = 0; 121 122#if 0 123 if(r == 1 && strcmp(iaddr, iaddr_old)==0 && iport==iport_old) 124 { 125 syslog(LOG_INFO, "Pinhole for inbound traffic from [%s]:%hu to [%s]:%hu with protocol %s already done. Updating it.", raddr, rport, iaddr_old, iport_old, protocol); 126 t = upnp_update_inboundpinhole(idfound, leaseTime); 127 *uid = atoi(idfound); 128 return t; 129 } 130 else 131#endif 132#if defined(USE_PF) || defined(USE_NETFILTER) 133 *uid = add_pinhole (0/*ext_if_name*/, raddr, rport, 134 iaddr, iport, proto, desc, timestamp); 135 return *uid >= 0 ? 1 : -1; 136#else 137 return -42; /* not implemented */ 138#endif 139} 140 141#if 0 142int 143upnp_add_inboundpinhole_internal(const char * raddr, unsigned short rport, 144 const char * iaddr, unsigned short iport, 145 const char * proto, int * uid) 146{ 147 int c = 9999; 148 char cmd[256], cmd_raw[256], cuid[42]; 149#if 0 150 static const char cmdval_full_udptcp[] = "ip6tables -I %s %d -p %s -i %s -s %s --sport %hu -d %s --dport %hu -j ACCEPT"; 151 static const char cmdval_udptcp[] = "ip6tables -I %s %d -p %s -i %s --sport %hu -d %s --dport %hu -j ACCEPT"; 152 static const char cmdval_full_udplite[] = "ip6tables -I %s %d -p %s -i %s -s %s -d %s -j ACCEPT"; 153 static const char cmdval_udplite[] = "ip6tables -I %s %d -p %s -i %s -d %s -j ACCEPT"; 154 // raw table command 155 static const char cmdval_full_udptcp_raw[] = "ip6tables -t raw -I PREROUTING %d -p %s -i %s -s %s --sport %hu -d %s --dport %hu -j TRACE"; 156 static const char cmdval_udptcp_raw[] = "ip6tables -t raw -I PREROUTING %d -p %s -i %s --sport %hu -d %s --dport %hu -j TRACE"; 157 static const char cmdval_full_udplite_raw[] = "ip6tables -t raw -I PREROUTING %d -p %s -i %s -s %s -d %s -j TRACE"; 158 static const char cmdval_udplite_raw[] = "ip6tables -t raw -I PREROUTING %d -p %s -i %s -d %s -j TRACE"; 159#endif 160 /*printf("%s\n", raddr);*/ 161 if(raddr!=NULL) 162 { 163#ifdef IPPROTO_UDPLITE 164 if(atoi(proto) == IPPROTO_UDPLITE) 165 { 166 /* snprintf(cmd, sizeof(cmd), cmdval_full_udplite, miniupnpd_forward_chain, line_number, proto, ext_if_name, raddr, iaddr); 167 snprintf(cmd_raw, sizeof(cmd_raw), cmdval_full_udplite_raw, line_number, proto, ext_if_name, raddr, iaddr);*/ 168 } 169 else 170#endif 171 { 172 /* snprintf(cmd, sizeof(cmd), cmdval_full_udptcp, miniupnpd_forward_chain, line_number, proto, ext_if_name, raddr, rport, iaddr, iport); 173 snprintf(cmd_raw, sizeof(cmd_raw), cmdval_full_udptcp_raw, line_number, proto, ext_if_name, raddr, rport, iaddr, iport);*/ 174 } 175 } 176 else 177 { 178#ifdef IPPROTO_UDPLITE 179 if(atoi(proto) == IPPROTO_UDPLITE) 180 { 181 /*snprintf(cmd, sizeof(cmd), cmdval_udplite, miniupnpd_forward_chain, line_number, proto, ext_if_name, iaddr); 182 snprintf(cmd_raw, sizeof(cmd_raw), cmdval_udplite_raw, line_number, proto, ext_if_name, iaddr);*/ 183 } 184 else 185#endif 186 { 187 /*snprintf(cmd, sizeof(cmd), cmdval_udptcp, miniupnpd_forward_chain, line_number, proto, ext_if_name, rport, iaddr, iport); 188 snprintf(cmd_raw, sizeof(cmd_raw), cmdval_udptcp_raw, line_number, proto, ext_if_name, rport, iaddr, iport); 189*/ 190 } 191 } 192#ifdef DEBUG 193 syslog(LOG_INFO, "Adding following ip6tables rule:"); 194 syslog(LOG_INFO, " -> %s", cmd); 195 syslog(LOG_INFO, " -> %s", cmd_raw); 196#endif 197 /* TODO Add a better checking error.*/ 198 if(system(cmd) < 0 || system(cmd_raw) < 0) 199 { 200 return 0; 201 } 202 srand(time(NULL)); 203 snprintf(cuid, sizeof(cuid), "%.4d", rand()%c); 204 *uid = atoi(cuid); 205 printf("\t_add_ uid: %s\n", cuid); 206 return 1; 207} 208#endif 209 210/* upnp_get_pinhole_info() 211 * return values : 212 * 0 OK 213 * -1 Internal error 214 * -2 NOT FOUND (no such entry) 215 * .. 216 * -42 Not implemented 217 */ 218int 219upnp_get_pinhole_info(unsigned short uid, 220 char * raddr, int raddrlen, 221 unsigned short * rport, 222 char * iaddr, int iaddrlen, 223 unsigned short * iport, 224 int * proto, char * desc, int desclen, 225 unsigned int * leasetime, 226 unsigned int * packets) 227{ 228 /* Call Firewall specific code to get IPv6 pinhole infos */ 229#if defined(USE_PF) || defined(USE_NETFILTER) 230 int r; 231 unsigned int timestamp; 232 u_int64_t packets_tmp; 233 /*u_int64_t bytes_tmp;*/ 234 235 r = get_pinhole_info(uid, raddr, raddrlen, rport, 236 iaddr, iaddrlen, iport, 237 proto, desc, desclen, 238 leasetime ? ×tamp : NULL, 239 packets ? &packets_tmp : NULL, 240 NULL/*&bytes_tmp*/); 241 if(r >= 0) { 242 if(leasetime) { 243 time_t current_time; 244 current_time = time(NULL); 245 if(timestamp > (unsigned int)current_time) 246 *leasetime = timestamp - current_time; 247 else 248 *leasetime = 0; 249 } 250 if(packets) 251 *packets = (unsigned int)packets_tmp; 252 } 253 return r; 254#else 255 UNUSED(uid); 256 UNUSED(raddr); UNUSED(raddrlen); UNUSED(rport); 257 UNUSED(iaddr); UNUSED(iaddrlen); UNUSED(iport); 258 UNUSED(proto); UNUSED(desc); UNUSED(desclen); 259 UNUSED(leasetime); UNUSED(packets); 260 return -42; /* not implemented */ 261#endif 262} 263 264int 265upnp_get_pinhole_uid_by_index(int index) 266{ 267#if defined (USE_NETFILTER) 268 return get_pinhole_uid_by_index(index); 269#else 270 UNUSED(index); 271 return -42; 272#endif /* defined (USE_NETFILTER) */ 273} 274 275int 276upnp_update_inboundpinhole(unsigned short uid, unsigned int leasetime) 277{ 278#if defined(USE_PF) || defined(USE_NETFILTER) 279 unsigned int timestamp; 280 281 timestamp = time(NULL) + leasetime; 282 return update_pinhole(uid, timestamp); 283#else 284 UNUSED(uid); UNUSED(leasetime); 285 286 return -42; /* not implemented */ 287#endif 288} 289 290int 291upnp_delete_inboundpinhole(unsigned short uid) 292{ 293#if defined(USE_PF) || defined(USE_NETFILTER) 294 return delete_pinhole(uid); 295#else 296 UNUSED(uid); 297 298 return -1; 299#endif 300} 301 302#if 0 303/* 304 * Result: 305 * 1: Found Result 306 * -4: No result 307 * -5: Result in another table 308 * -6: Result in another chain 309 * -7: Result in a chain not a rule 310*/ 311int 312upnp_check_pinhole_working(const char * uid, 313 char * eaddr, 314 char * iaddr, 315 unsigned short * eport, 316 unsigned short * iport, 317 char * protocol, 318 int * rulenum_used) 319{ 320 /* TODO : to be implemented */ 321#if 0 322 FILE * fd; 323 time_t expire = time(NULL); 324 char buf[1024], filename[] = "/var/log/kern.log", expire_time[32]=""; 325 int res = -4, str_len; 326 327 str_len = strftime(expire_time, sizeof(expire_time), "%b %d %H:%M:%S", localtime(&expire)); 328 329 fd = fopen(filename, "r"); 330 if (fd==NULL) 331 { 332 syslog(LOG_ERR, "Get_rule: could not open file: %s", filename); 333 return -1; 334 } 335 336 syslog(LOG_INFO, "Get_rule: Starting getting info in file %s for %s\n", filename, uid); 337 buf[sizeof(buf)-1] = 0; 338 while(fgets(buf, sizeof(buf)-1, fd) != NULL && res != 1) 339 { 340 //printf("line: %s\n", buf); 341 char * r, * t, * c, * p; 342 // looking for something like filter:FORWARD:rule: or filter:MINIUPNPD:rule: 343 r = strstr(buf, ":rule:"); 344 p = strstr(buf, ":policy:"); 345 t = strstr(buf, "TRACE:"); // table pointeur 346 t += 7; 347 c = t + 7; // chain pointeur 348 if(r) 349 { 350 printf("\t** Found %.*s\n", 24 ,t); 351 char * src, * dst, * sport, * dport, * proto, * line; 352 char time[15]="", src_addr[40], dst_addr[40], proto_tmp[8]; 353 int proto_int; 354 strncpy(time, buf, sizeof(time)); 355 /*if(compare_time(time, expire_time)<0) 356 { 357 printf("\t\tNot corresponding time\n"); 358 continue; 359 }*/ 360 361 line = r + 6; 362 printf("\trule line = %d\n", atoi(line)); 363 364 src = strstr(buf, "SRC="); 365 src += 4; 366 snprintf(src_addr, sizeof(src_addr), "%.*s", 39, src); 367#if 0 368 del_char(src_addr); 369 add_char(src_addr); 370#endif 371 372 dst = strstr(buf, "DST="); 373 dst += 4; 374 snprintf(dst_addr, sizeof(dst_addr), "%.*s", 39, dst); 375#if 0 376 del_char(dst_addr); 377 add_char(dst_addr); 378#endif 379 380 proto = strstr(buf, "PROTO="); 381 proto += 6; 382 proto_int = atoi(protocol); 383 if(proto_int == IPPROTO_UDP) 384 strcpy(proto_tmp, "UDP"); 385 else if(proto_int == IPPROTO_TCP) 386 strcpy(proto_tmp, "TCP"); 387#ifdef IPPROTO_UDPLITE 388 else if(proto_int == IPPROTO_UDPLITE) 389 strcpy(proto_tmp, "UDPLITE"); 390#endif 391 else 392 strcpy(proto_tmp, "UnsupportedProto"); 393 394 // printf("\tCompare eaddr: %s // protocol: %s\n\t to addr: %s // protocol: %.*s\n", eaddr, proto_tmp, src_addr, strlen(proto_tmp), proto); 395 // printf("\tCompare iaddr: %s // protocol: %s\n\t to addr: %s // protocol: %.*s\n", iaddr, proto_tmp, dst_addr, strlen(proto_tmp), proto); 396 // TODO Check time 397 // Check that the paquet found in trace correspond to the one we are looking for 398 if( /*(strcmp(eaddr, src_addr) == 0) &&*/ (strcmp(iaddr, dst_addr) == 0) && (strncmp(proto_tmp, proto, strlen(proto_tmp))==0)) 399 { 400 sport = strstr(buf, "SPT="); 401 sport += 4; 402 dport = strstr(buf, "DPT="); 403 dport += 4; 404 printf("\tCompare eport: %hu\n\t to port: %d\n", *eport, atoi(sport)); 405 printf("\tCompare iport: %hu\n\t to port: %d\n", *iport, atoi(dport)); 406 if(/*eport != atoi(sport) &&*/ *iport != atoi(dport)) 407 { 408 printf("\t\tPort not corresponding\n"); 409 continue; 410 } 411 printf("\ttable found: %.*s\n", 6, t); 412 printf("\tchain found: %.*s\n", 9, c); 413 // Check that the table correspond to the filter table 414 if(strncmp(t, "filter", 6)==0) 415 { 416 // Check that the table correspond to the MINIUPNP table 417 if(strncmp(c, "MINIUPNPD", 9)==0) 418 { 419 *rulenum_used = atoi(line); 420 res = 1; 421 } 422 else 423 { 424 res = -6; 425 continue; 426 } 427 } 428 else 429 { 430 res = -5; 431 continue; 432 } 433 } 434 else 435 { 436 printf("Packet information not corresponding\n"); 437 continue; 438 } 439 } 440 if(!r && p) 441 { 442 printf("\t** Policy case\n"); 443 char * src, * dst, * sport, * dport, * proto, * line; 444 char time[15], src_addr[40], dst_addr[40], proto_tmp[8]; 445 int proto_int; 446 strncpy(time, buf, sizeof(time)); 447 /*if(compare_time(time, expire_time)<0) 448 { 449 printf("\t\tNot corresponding time\n"); 450 continue; 451 }*/ 452 453 line = p + 8; 454 printf("\trule line = %d\n", atoi(line)); 455 456 src = strstr(buf, "SRC="); 457 src += 4; 458 snprintf(src_addr, sizeof(src_addr), "%.*s", 39, src); 459#if 0 460 del_char(src_addr); 461 add_char(src_addr); 462#endif 463 464 dst = strstr(buf, "DST="); 465 dst += 4; 466 snprintf(dst_addr, sizeof(dst_addr), "%.*s", 39, dst); 467#if 0 468 del_char(dst_addr); 469 add_char(dst_addr); 470#endif 471 472 proto = strstr(buf, "PROTO="); 473 proto += 6; 474 proto_int = atoi(protocol); 475 if(proto_int == IPPROTO_UDP) 476 strcpy(proto_tmp, "UDP"); 477 else if(proto_int == IPPROTO_TCP) 478 strcpy(proto_tmp, "TCP"); 479#ifdef IPPROTO_UDPLITE 480 else if(proto_int == IPPROTO_UDPLITE) 481 strcpy(proto_tmp, "UDPLITE"); 482#endif 483 else 484 strcpy(proto_tmp, "UnsupportedProto"); 485 486 // printf("\tCompare eaddr: %s // protocol: %s\n\t to addr: %s // protocol: %.*s\n", eaddr, proto_tmp, src_addr, strlen(proto_tmp), proto); 487 // printf("\tCompare iaddr: %s // protocol: %s\n\t to addr: %s // protocol: %.*s\n", iaddr, proto_tmp, dst_addr, strlen(proto_tmp), proto); 488 // Check that the paquet found in trace correspond to the one we are looking for 489 if( (strcmp(eaddr, src_addr) == 0) && (strcmp(iaddr, dst_addr) == 0) && (strncmp(proto_tmp, proto, 5)==0)) 490 { 491 sport = strstr(buf, "SPT="); 492 sport += 4; 493 dport = strstr(buf, "DPT="); 494 dport += 4; 495 printf("\tCompare eport: %hu\n\t to port: %d\n", *eport, atoi(sport)); 496 printf("\tCompare iport: %hu\n\t to port: %d\n", *iport, atoi(dport)); 497 if(*eport != atoi(sport) && *iport != atoi(dport)) 498 { 499 printf("\t\tPort not corresponding\n"); 500 continue; 501 } 502 else 503 { 504 printf("Find a corresponding policy trace in the chain: %.*s\n", 10, c); 505 res = -7; 506 continue; 507 } 508 } 509 else 510 continue; 511 } 512 } 513 fclose(fd); 514 return res; 515#else 516 return -42; /* to be implemented */ 517#endif 518} 519#endif 520 521int 522upnp_clean_expired_pinholes(unsigned int * next_timestamp) 523{ 524#if defined(USE_PF) || defined(USE_NETFILTER) 525 return clean_pinhole_list(next_timestamp); 526#else 527 UNUSED(next_timestamp); 528 529 return 0; /* nothing to do */ 530#endif 531} 532 533#endif /* ENABLE_UPNPPINHOLE */ 534