1/* $Id: ipfwrdr.c,v 1.12 2012/02/11 13:10:57 nanard Exp $ */ 2/* 3 * MiniUPnP project 4 * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ 5 * (c) 2009 Jardel Weyrich 6 * (c) 2011-2012 Thomas Bernard 7 * This software is subject to the conditions detailed 8 * in the LICENCE file provided within the distribution 9 */ 10 11#include "../config.h" 12 13#include <sys/param.h> 14#include <sys/types.h> 15#include <sys/file.h> 16 17/* 18This is a workaround for <sys/uio.h> troubles on FreeBSD, HPUX, OpenBSD. 19Needed here because on some systems <sys/uio.h> gets included by things 20like <sys/socket.h> 21*/ 22#ifndef _KERNEL 23# define ADD_KERNEL 24# define _KERNEL 25# define KERNEL 26#endif 27#ifdef __OpenBSD__ 28struct file; 29#endif 30#include <sys/uio.h> 31#ifdef ADD_KERNEL 32# undef _KERNEL 33# undef KERNEL 34#endif 35 36#include <sys/time.h> 37#include <sys/socket.h> 38#include <sys/syslog.h> 39#include <sys/ioctl.h> 40#include <net/if.h> 41#if __FreeBSD_version >= 300000 42# include <net/if_var.h> 43#endif 44#include <netinet/in.h> 45#include <netinet/in_systm.h> 46#include <netinet/ip.h> 47#include <netinet/ip_icmp.h> 48#include <netinet/tcp.h> 49#include <netinet/udp.h> 50#include <arpa/inet.h> 51 52#include <sys/types.h> 53#include <sys/queue.h> 54#include <sys/socket.h> 55#include <errno.h> 56#include <limits.h> 57#include <netdb.h> 58#include <stdlib.h> 59#include <fcntl.h> 60#include <syslog.h> 61#include <stddef.h> 62#include <stdio.h> 63#include <strings.h> 64#include <string.h> 65#include <unistd.h> 66#include <netinet/ip_fw.h> 67#include "ipfwaux.h" 68#include "ipfwrdr.h" 69 70#include "../upnpglobalvars.h" 71 72/* init and shutdown functions */ 73 74int init_redirect(void) { 75 return ipfw_exec(IP_FW_INIT, NULL, 0); 76} 77 78void shutdown_redirect(void) { 79 ipfw_exec(IP_FW_TERM, NULL, 0); 80} 81 82/* ipfw cannot store descriptions and timestamp for port mappings so we keep 83 * our own list in memory */ 84struct mapping_desc_time { 85 struct mapping_desc_time * next; 86 unsigned int timestamp; 87 unsigned short eport; 88 short proto; 89 char desc[]; 90}; 91 92static struct mapping_desc_time * mappings_list = NULL; 93 94/* add an element to the port mappings descriptions & timestamp list */ 95static void 96add_desc_time(unsigned short eport, int proto, 97 const char * desc, unsigned int timestamp) 98{ 99 struct mapping_desc_time * tmp; 100 size_t l; 101 if(!desc) 102 desc = "miniupnpd"; 103 l = strlen(desc) + 1; 104 tmp = malloc(sizeof(struct mapping_desc_time) + l); 105 if(tmp) { 106 /* fill the element and insert it as head of the list */ 107 tmp->next = mappings_list; 108 tmp->timestamp = timestamp; 109 tmp->eport = eport; 110 tmp->proto = (short)proto; 111 memcpy(tmp->desc, desc, l); 112 mappings_list = tmp; 113 } 114} 115 116/* remove an element to the port mappings descriptions & timestamp list */ 117static void 118del_desc_time(unsigned short eport, int proto) 119{ 120 struct mapping_desc_time * e; 121 struct mapping_desc_time * * p; 122 p = &mappings_list; 123 e = *p; 124 while(e) { 125 if(e->eport == eport && e->proto == (short)proto) { 126 *p = e->next; 127 free(e); 128 return; 129 } else { 130 p = &e->next; 131 e = *p; 132 } 133 } 134} 135 136/* go through the list and find the description and timestamp */ 137static void 138get_desc_time(unsigned short eport, int proto, 139 char * desc, int desclen, 140 unsigned int * timestamp) 141{ 142 struct mapping_desc_time * e; 143 144 for(e = mappings_list; e; e = e->next) { 145 if(e->eport == eport && e->proto == (short)proto) { 146 if(desc) 147 strlcpy(desc, e->desc, desclen); 148 if(timestamp) 149 *timestamp = e->timestamp; 150 return; 151 } 152 } 153} 154 155/* --- */ 156int add_redirect_rule2( 157 const char * ifname, 158 const char * rhost, 159 unsigned short eport, 160 const char * iaddr, 161 unsigned short iport, 162 int proto, 163 const char * desc, 164 unsigned int timestamp) 165{ 166 struct ip_fw rule; 167 int r; 168 169 if (ipfw_validate_protocol(proto) < 0) 170 return -1; 171 if (ipfw_validate_ifname(ifname) < 0) 172 return -1; 173 174 memset(&rule, 0, sizeof(struct ip_fw)); 175 rule.version = IP_FW_CURRENT_API_VERSION; 176#if 0 177 rule.fw_number = 1000; /* rule number */ 178 rule.context = (void *)desc; /* The description is kept in a separate list */ 179#endif 180 rule.fw_prot = proto; /* protocol */ 181 rule.fw_flg |= IP_FW_F_IIFACE; /* interfaces to check */ 182 rule.fw_flg |= IP_FW_F_IIFNAME; /* interfaces to check by name */ 183 rule.fw_flg |= (IP_FW_F_IN | IP_FW_F_OUT); /* packet direction */ 184 rule.fw_flg |= IP_FW_F_FWD; /* forward action */ 185#ifdef USE_IFNAME_IN_RULES 186 if (ifname != NULL) { 187 strlcpy(rule.fw_in_if.fu_via_if.name, ifname, IFNAMSIZ); /* src interface */ 188 rule.fw_in_if.fu_via_if.unit = -1; 189 } 190#endif 191 if (inet_aton(iaddr, &rule.fw_out_if.fu_via_ip) == 0) { 192 syslog(LOG_ERR, "inet_aton(): %m"); 193 return -1; 194 } 195 memcpy(&rule.fw_dst, &rule.fw_out_if.fu_via_ip, sizeof(struct in_addr)); 196 memcpy(&rule.fw_fwd_ip.sin_addr, &rule.fw_out_if.fu_via_ip, sizeof(struct in_addr)); 197 rule.fw_dmsk.s_addr = INADDR_BROADCAST; /* TODO check this */ 198 IP_FW_SETNDSTP(&rule, 1); /* number of external ports */ 199 rule.fw_uar.fw_pts[0] = eport; /* external port */ 200 rule.fw_fwd_ip.sin_port = iport; /* internal port */ 201 if (rhost && rhost[0] != '\0') { 202 inet_aton(rhost, &rule.fw_src); 203 rule.fw_smsk.s_addr = htonl(INADDR_NONE); 204 } 205 206 r = ipfw_exec(IP_FW_ADD, &rule, sizeof(rule)); 207 if(r >= 0) 208 add_desc_time(eport, proto, desc, timestamp); 209 return r; 210} 211 212/* get_redirect_rule() 213 * return value : 0 success (found) 214 * -1 = error or rule not found */ 215int get_redirect_rule( 216 const char * ifname, 217 unsigned short eport, 218 int proto, 219 char * iaddr, 220 int iaddrlen, 221 unsigned short * iport, 222 char * desc, 223 int desclen, 224 char * rhost, 225 int rhostlen, 226 unsigned int * timestamp, 227 u_int64_t * packets, 228 u_int64_t * bytes) 229{ 230 int i, count_rules, total_rules = 0; 231 struct ip_fw * rules = NULL; 232 233 if (ipfw_validate_protocol(proto) < 0) 234 return -1; 235 if (ipfw_validate_ifname(ifname) < 0) 236 return -1; 237 if (timestamp) 238 *timestamp = 0; 239 240 do { 241 count_rules = ipfw_fetch_ruleset(&rules, &total_rules, 10); 242 if (count_rules < 0) 243 goto error; 244 } while (count_rules == 10); 245 246 for (i=0; i<total_rules-1; i++) { 247 const struct ip_fw const * ptr = &rules[i]; 248 if (proto == ptr->fw_prot && eport == ptr->fw_uar.fw_pts[0]) { 249 if (packets != NULL) 250 *packets = ptr->fw_pcnt; 251 if (bytes != NULL) 252 *bytes = ptr->fw_bcnt; 253 if (iport != NULL) 254 *iport = ptr->fw_fwd_ip.sin_port; 255 if (iaddr != NULL && iaddrlen > 0) { 256 /* looks like fw_out_if.fu_via_ip is zero */ 257 /*if (inet_ntop(AF_INET, &ptr->fw_out_if.fu_via_ip, iaddr, iaddrlen) == NULL) {*/ 258 if (inet_ntop(AF_INET, &ptr->fw_fwd_ip.sin_addr, iaddr, iaddrlen) == NULL) { 259 syslog(LOG_ERR, "inet_ntop(): %m"); 260 goto error; 261 } 262 } 263 if (rhost != NULL && rhostlen > 0) { 264 if (ptr->fw_src.s_addr == 0) 265 rhost[0] = '\0'; 266 else if (inet_ntop(AF_INET, &ptr->fw_src.s_addr, rhost, rhostlen) == NULL) { 267 syslog(LOG_ERR, "inet_ntop(): %m"); 268 goto error; 269 } 270 } 271 /* And what if we found more than 1 matching rule? */ 272 ipfw_free_ruleset(&rules); 273 get_desc_time(eport, proto, desc, desclen, timestamp); 274 return 0; 275 } 276 } 277 278error: 279 if (rules != NULL) 280 ipfw_free_ruleset(&rules); 281 return -1; 282} 283 284int delete_redirect_rule( 285 const char * ifname, 286 unsigned short eport, 287 int proto) 288{ 289 int i, count_rules, total_rules = 0; 290 struct ip_fw * rules = NULL; 291 292 if (ipfw_validate_protocol(proto) < 0) 293 return -1; 294 if (ipfw_validate_ifname(ifname) < 0) 295 return -1; 296 297 do { 298 count_rules = ipfw_fetch_ruleset(&rules, &total_rules, 10); 299 if (count_rules < 0) 300 goto error; 301 } while (count_rules == 10); 302 303 for (i=0; i<total_rules-1; i++) { 304 const struct ip_fw const * ptr = &rules[i]; 305 if (proto == ptr->fw_prot && eport == ptr->fw_uar.fw_pts[0]) { 306 if (ipfw_exec(IP_FW_DEL, (struct ip_fw *)ptr, sizeof(*ptr)) < 0) 307 goto error; 308 /* And what if we found more than 1 matching rule? */ 309 ipfw_free_ruleset(&rules); 310 del_desc_time(eport, proto); 311 return 0; 312 } 313 } 314 315error: 316 if (rules != NULL) 317 ipfw_free_ruleset(&rules); 318 return -1; 319} 320 321int add_filter_rule2( 322 const char * ifname, 323 const char * rhost, 324 const char * iaddr, 325 unsigned short eport, 326 unsigned short iport, 327 int proto, 328 const char * desc) 329{ 330 return 0; /* nothing to do, always success */ 331} 332 333int delete_filter_rule( 334 const char * ifname, 335 unsigned short eport, 336 int proto) 337{ 338 return 0; /* nothing to do, always success */ 339} 340 341int get_redirect_rule_by_index( 342 int index, 343 char * ifname, 344 unsigned short * eport, 345 char * iaddr, 346 int iaddrlen, 347 unsigned short * iport, 348 int * proto, 349 char * desc, 350 int desclen, 351 char * rhost, 352 int rhostlen, 353 unsigned int * timestamp, 354 u_int64_t * packets, 355 u_int64_t * bytes) 356{ 357 int total_rules = 0; 358 struct ip_fw * rules = NULL; 359 360 if (index < 0) /* TODO shouldn't we also validate the maximum? */ 361 return -1; 362 363 if(timestamp) 364 *timestamp = 0; 365 366 ipfw_fetch_ruleset(&rules, &total_rules, index + 1); 367 368 if (total_rules > index) { 369 const struct ip_fw const * ptr = &rules[index]; 370 if (ptr->fw_prot == 0) /* invalid rule */ 371 goto error; 372 if (proto != NULL) 373 *proto = ptr->fw_prot; 374 if (eport != NULL) 375 *eport = ptr->fw_uar.fw_pts[0]; 376 if (iport != NULL) 377 *iport = ptr->fw_fwd_ip.sin_port; 378 if (ifname != NULL) 379 strlcpy(ifname, ptr->fw_in_if.fu_via_if.name, IFNAMSIZ); 380 if (packets != NULL) 381 *packets = ptr->fw_pcnt; 382 if (bytes != NULL) 383 *bytes = ptr->fw_bcnt; 384 if (iport != NULL) 385 *iport = ptr->fw_fwd_ip.sin_port; 386 if (iaddr != NULL && iaddrlen > 0) { 387 /* looks like fw_out_if.fu_via_ip is zero */ 388 /*if (inet_ntop(AF_INET, &ptr->fw_out_if.fu_via_ip, iaddr, iaddrlen) == NULL) {*/ 389 if (inet_ntop(AF_INET, &ptr->fw_fwd_ip.sin_addr, iaddr, iaddrlen) == NULL) { 390 syslog(LOG_ERR, "inet_ntop(): %m"); 391 goto error; 392 } 393 } 394 if (rhost != NULL && rhostlen > 0) { 395 if (ptr->fw_src.s_addr == 0) 396 rhost[0] = '\0'; 397 else if (inet_ntop(AF_INET, &ptr->fw_src.s_addr, rhost, rhostlen) == NULL) { 398 syslog(LOG_ERR, "inet_ntop(): %m"); 399 goto error; 400 } 401 } 402 ipfw_free_ruleset(&rules); 403 get_desc_time(*eport, *proto, desc, desclen, timestamp); 404 return 0; 405 } 406 407error: 408 if (rules != NULL) 409 ipfw_free_ruleset(&rules); 410 return -1; 411} 412 413/* upnp_get_portmappings_in_range() 414 * return a list of all "external" ports for which a port 415 * mapping exists */ 416unsigned short * 417get_portmappings_in_range(unsigned short startport, 418 unsigned short endport, 419 int proto, 420 unsigned int * number) 421{ 422 unsigned short * array = NULL; 423 unsigned int capacity = 128; 424 int i, count_rules, total_rules = 0; 425 struct ip_fw * rules = NULL; 426 427 if (ipfw_validate_protocol(proto) < 0) 428 return NULL; 429 430 do { 431 count_rules = ipfw_fetch_ruleset(&rules, &total_rules, 10); 432 if (count_rules < 0) 433 goto error; 434 } while (count_rules == 10); 435 436 array = calloc(capacity, sizeof(unsigned short)); 437 if(!array) { 438 syslog(LOG_ERR, "get_portmappings_in_range() : calloc error"); 439 goto error; 440 } 441 *number = 0; 442 443 for (i=0; i<total_rules-1; i++) { 444 const struct ip_fw const * ptr = &rules[i]; 445 unsigned short eport = ptr->fw_uar.fw_pts[0]; 446 if (proto == ptr->fw_prot 447 && startport <= eport 448 && eport <= endport) { 449 if(*number >= capacity) { 450 capacity += 128; 451 array = realloc(array, sizeof(unsigned short)*capacity); 452 if(!array) { 453 syslog(LOG_ERR, "get_portmappings_in_range() : realloc(%lu) error", sizeof(unsigned short)*capacity); 454 *number = 0; 455 goto error; 456 } 457 } 458 array[*number] = eport; 459 (*number)++; 460 } 461 } 462error: 463 if (rules != NULL) 464 ipfw_free_ruleset(&rules); 465 return array; 466} 467 468