1 /* $Id: ipfrdr.c,v 1.19 2016/12/16 09:11:19 nanard Exp $ */
2 /* MiniUPnP project
3  * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
4  * (c) 2007 Darren Reed
5  * This software is subject to the conditions detailed
6  * in the LICENCE file provided within the distribution */
7 
8 #include <sys/param.h>
9 #include <sys/types.h>
10 #include <sys/file.h>
11 /*
12  * This is a workaround for <sys/uio.h> troubles on FreeBSD, HPUX, OpenBSD.
13  * Needed here because on some systems <sys/uio.h> gets included by things
14  * like <sys/socket.h>
15  */
16 #ifndef _KERNEL
17 # define ADD_KERNEL
18 # define _KERNEL
19 # define KERNEL
20 #endif
21 #ifdef __OpenBSD__
22 struct file;
23 #endif
24 #include <sys/uio.h>
25 #ifdef ADD_KERNEL
26 # undef _KERNEL
27 # undef KERNEL
28 #endif
29 #include <sys/time.h>
30 #include <sys/socket.h>
31 #include <sys/syslog.h>
32 #include <sys/ioctl.h>
33 #include <net/if.h>
34 #if __FreeBSD_version >= 300000
35 # include <net/if_var.h>
36 #endif
37 #include <netinet/in.h>
38 #include <netinet/in_systm.h>
39 #include <netinet/ip.h>
40 #include <netinet/ip_icmp.h>
41 #ifndef	TCP_PAWS_IDLE	/* IRIX */
42 # include <netinet/tcp.h>
43 #endif
44 #include <netinet/udp.h>
45 
46 #include <arpa/inet.h>
47 
48 #include <errno.h>
49 #include <limits.h>
50 #include <netdb.h>
51 #include <stdlib.h>
52 #include <fcntl.h>
53 #include <syslog.h>
54 #include <stddef.h>
55 #include <stdio.h>
56 #if !defined(__SVR4) && !defined(__svr4__) && defined(sun)
57 # include <strings.h>
58 #endif
59 #include <string.h>
60 #include <unistd.h>
61 
62 #include "../config.h"
63 #include "netinet/ipl.h"
64 #include "netinet/ip_compat.h"
65 #include "netinet/ip_fil.h"
66 #include "netinet/ip_nat.h"
67 #include "netinet/ip_state.h"
68 
69 #include "../macros.h"
70 
71 #ifndef __P
72 # ifdef __STDC__
73 #  define	__P(x)	x
74 # else
75 #  define	__P(x)	()
76 # endif
77 #endif
78 #ifndef __STDC__
79 # undef		const
80 # define	const
81 #endif
82 
83 #ifndef	U_32_T
84 # define	U_32_T	1
85 # if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__) || \
86 	defined(__sgi)
87 typedef	u_int32_t	u_32_t;
88 # else
89 #  if defined(__alpha__) || defined(__alpha) || defined(_LP64)
90 typedef unsigned int	u_32_t;
91 #  else
92 #   if SOLARIS2 >= 6
93 typedef uint32_t	u_32_t;
94 #   else
95 typedef unsigned int	u_32_t;
96 #   endif
97 #  endif
98 # endif /* __NetBSD__ || __OpenBSD__ || __FreeBSD__ || __sgi */
99 #endif /* U_32_T */
100 
101 
102 #if defined(__NetBSD__) || defined(__OpenBSD__) ||			\
103         (_BSDI_VERSION >= 199701) || (__FreeBSD_version >= 300000) ||	\
104 	SOLARIS || defined(__sgi) || defined(__osf__) || defined(linux)
105 # include <stdarg.h>
106 typedef	int	(* ioctlfunc_t) __P((int, ioctlcmd_t, ...));
107 #else
108 typedef	int	(* ioctlfunc_t) __P((dev_t, ioctlcmd_t, void *));
109 #endif
110 typedef	void	(* addfunc_t) __P((int, ioctlfunc_t, void *));
111 typedef	int	(* copyfunc_t) __P((void *, void *, size_t));
112 
113 
114 /*
115  * SunOS4
116  */
117 #if defined(sun) && !defined(__SVR4) && !defined(__svr4__)
118 extern	int	ioctl __P((int, int, void *));
119 #endif
120 
121 #include "../upnpglobalvars.h"
122 
123 /* group name */
124 static const char group_name[] = "miniupnpd";
125 
126 static int dev = -1;
127 static int dev_ipl = -1;
128 
129 /* IPFilter cannot store redirection descriptions, so we use our
130  * own structure to store them */
131 struct rdr_desc {
132 	struct rdr_desc * next;
133 	unsigned short eport;
134 	int proto;
135 	unsigned int timestamp;
136 	char str[];
137 };
138 
139 /* pointer to the chained list where descriptions are stored */
140 static struct rdr_desc * rdr_desc_list;
141 
142 static void
add_redirect_desc(unsigned short eport,int proto,unsigned int timestamp,const char * desc)143 add_redirect_desc(unsigned short eport, int proto,
144                   unsigned int timestamp, const char * desc)
145 {
146 	struct rdr_desc * p;
147 	size_t l;
148 
149 	if (desc != NULL) {
150 		l = strlen(desc) + 1;
151 		p = malloc(sizeof(struct rdr_desc) + l);
152 		if (p) {
153 			p->next = rdr_desc_list;
154 			p->eport = eport;
155 			p->proto = proto;
156 			p->timestamp = timestamp;
157 			memcpy(p->str, desc, l);
158 			rdr_desc_list = p;
159 		}
160 	}
161 }
162 
163 static void
del_redirect_desc(unsigned short eport,int proto)164 del_redirect_desc(unsigned short eport, int proto)
165 {
166 	struct rdr_desc * p, * last;
167 
168 	last = NULL;
169 	for (p = rdr_desc_list; p; p = p->next) {
170 		if(p->eport == eport && p->proto == proto) {
171 			if (last == NULL)
172 				rdr_desc_list = p->next;
173 			else
174 				last->next = p->next;
175 			free(p);
176 			return;
177 		}
178 	}
179 }
180 
181 static void
get_redirect_desc(unsigned short eport,int proto,char * desc,int desclen,unsigned int * timestamp)182 get_redirect_desc(unsigned short eport, int proto, char * desc, int desclen, unsigned int * timestamp)
183 {
184 	struct rdr_desc * p;
185 
186 	if (desc == NULL || desclen == 0)
187 		return;
188 	for (p = rdr_desc_list; p; p = p->next) {
189 		if (p->eport == eport && p->proto == proto)
190 		{
191 			strncpy(desc, p->str, desclen);
192 			*timestamp = p->timestamp;
193 			return;
194 		}
195 	}
196 	return;
197 }
198 
init_redirect(void)199 int init_redirect(void)
200 {
201 
202 	dev = open(IPNAT_NAME, O_RDWR);
203 	if (dev < 0) {
204 		syslog(LOG_ERR, "open(\"%s\"): %m", IPNAT_NAME);
205 		return -1;
206 	}
207 	dev_ipl = open(IPL_NAME, O_RDWR);
208 	if (dev_ipl < 0) {
209 		syslog(LOG_ERR, "open(\"%s\"): %m", IPL_NAME);
210 		return -1;
211 	}
212 	return 0;
213 }
214 
shutdown_redirect(void)215 void shutdown_redirect(void)
216 {
217 
218 	if (dev >= 0) {
219 		close(dev);
220 		dev = -1;
221 	}
222 	if (dev_ipl >= 0) {
223 		close(dev_ipl);
224 		dev = -1;
225 	}
226 	return;
227 }
228 
229 int
add_redirect_rule2(const char * ifname,const char * rhost,unsigned short eport,const char * iaddr,unsigned short iport,int proto,const char * desc,unsigned int timestamp)230 add_redirect_rule2(const char * ifname, const char * rhost,
231     unsigned short eport, const char * iaddr, unsigned short iport,
232     int proto, const char * desc, unsigned int timestamp)
233 {
234 	struct ipnat ipnat;
235 	struct ipfobj obj;
236 	int r;
237 
238 	if (dev < 0) {
239 		syslog(LOG_ERR, "%s not open", IPNAT_NAME);
240 		return -1;
241 	}
242 
243 	memset(&obj, 0, sizeof(obj));
244 	memset(&ipnat, 0, sizeof(ipnat));
245 
246 	ipnat.in_redir = NAT_REDIRECT;
247 #if IPFILTER_VERSION >= 5000000
248 	ipnat.in_pr[0] = proto;
249 	ipnat.in_pr[1] = proto;
250 #else
251 	ipnat.in_p = proto;
252 #endif
253 	if (proto == IPPROTO_TCP)
254 		ipnat.in_flags = IPN_TCP;
255 	if (proto == IPPROTO_UDP)
256 		ipnat.in_flags = IPN_UDP;
257 	ipnat.in_dcmp = FR_EQUAL;
258 #if IPFILTER_VERSION >= 5000000
259 	ipnat.in_dpmin = htons(eport);
260 	ipnat.in_dpmax = htons(eport);
261 	ipnat.in_dpnext = htons(iport);
262 	ipnat.in_v[0] = 4;
263 	ipnat.in_v[1] = 4;
264 #else
265 	ipnat.in_pmin = htons(eport);
266 	ipnat.in_pmax = htons(eport);
267 	ipnat.in_pnext = htons(iport);
268 	ipnat.in_v = 4;
269 #endif
270 	strlcpy(ipnat.in_tag.ipt_tag, group_name, IPFTAG_LEN);
271 
272 #ifdef USE_IFNAME_IN_RULES
273 	if (ifname) {
274 #if IPFILTER_VERSION >= 5000000
275 		/* XXX check for stack overflow ! */
276 		ipnat.in_ifnames[0] = 0;
277 		ipnat.in_ifnames[1] = 0;
278 		strlcpy(ipnat.in_names, ifname, IFNAMSIZ);
279 		ipnat.in_namelen = strlen(ipnat.in_names) + 1;
280 #else
281 		strlcpy(ipnat.in_ifnames[0], ifname, IFNAMSIZ);
282 		strlcpy(ipnat.in_ifnames[1], ifname, IFNAMSIZ);
283 #endif
284 	}
285 #endif
286 
287 	if(rhost && rhost[0] != '\0' && rhost[0] != '*')
288 	{
289 #if IPFILTER_VERSION >= 5000000
290 		inet_pton(AF_INET, rhost, &ipnat.in_nsrc.na_addr[0].in4); /* in_nsrcip */
291 		ipnat.in_nsrc.na_addr[1].in4.s_addr = 0xffffffff;	/* in_nsrcmsk */
292 #else
293 		inet_pton(AF_INET, rhost, &ipnat.in_src[0].in4);
294 		ipnat.in_src[1].in4.s_addr = 0xffffffff;
295 #endif
296 	}
297 
298 #if IPFILTER_VERSION >= 5000000
299 	inet_pton(AF_INET, iaddr, &ipnat.in_ndst.na_addr[0].in4); /* in_ndstip */
300 	ipnat.in_ndst.na_addr[1].in4.s_addr = 0xffffffff; /* in_ndstmsk */
301 #else
302 	inet_pton(AF_INET, iaddr, &ipnat.in_in[0].in4);
303 	ipnat.in_in[1].in4.s_addr = 0xffffffff;
304 #endif
305 
306 	obj.ipfo_rev = IPFILTER_VERSION;
307 	obj.ipfo_size = sizeof(ipnat);
308 	obj.ipfo_ptr = &ipnat;
309 	obj.ipfo_type = IPFOBJ_IPNAT;
310 
311 	r = ioctl(dev, SIOCADNAT, &obj);
312 	if (r == -1)
313 		syslog(LOG_ERR, "ioctl(SIOCADNAT): %m");
314 	else
315 		add_redirect_desc(eport, proto, timestamp, desc);
316 	return r;
317 }
318 
319 /* get_redirect_rule()
320  * return value : 0 success (found)
321  * -1 = error or rule not found */
322 int
get_redirect_rule(const char * ifname,unsigned short eport,int proto,char * iaddr,int iaddrlen,unsigned short * iport,char * desc,int desclen,char * rhost,int rhostlen,unsigned int * timestamp,u_int64_t * packets,u_int64_t * bytes)323 get_redirect_rule(const char * ifname, unsigned short eport, int proto,
324     char * iaddr, int iaddrlen, unsigned short * iport,
325     char * desc, int desclen,
326     char * rhost, int rhostlen,
327     unsigned int * timestamp,
328     u_int64_t * packets, u_int64_t * bytes)
329 {
330 	ipfgeniter_t iter;
331 	ipfobj_t obj;
332 	ipnat_t ipn;
333 	int r;
334 	UNUSED(ifname);
335 
336 	memset(&obj, 0, sizeof(obj));
337 	obj.ipfo_rev = IPFILTER_VERSION;
338 	obj.ipfo_type = IPFOBJ_GENITER;
339 	obj.ipfo_size = sizeof(iter);
340 	obj.ipfo_ptr = &iter;
341 
342 	iter.igi_type = IPFGENITER_IPNAT;
343 #if IPFILTER_VERSION > 4011300
344 	iter.igi_nitems = 1;
345 #endif
346 	iter.igi_data = &ipn;
347 
348 	if (dev < 0) {
349 		syslog(LOG_ERR, "%s not open", IPNAT_NAME);
350 		return -1;
351 	}
352 
353 	r = -1;
354 	do {
355 		if (ioctl(dev, SIOCGENITER, &obj) == -1) {
356 			syslog(LOG_ERR, "ioctl(dev, SIOCGENITER): %m");
357 			break;
358 		}
359 #if IPFILTER_VERSION >= 5000000
360 		if (eport == ntohs(ipn.in_dpmin) &&
361 		    eport == ntohs(ipn.in_dpmax) &&
362 		    strcmp(ipn.in_tag.ipt_tag, group_name) == 0 &&
363 		    ipn.in_pr[0] == proto)
364 #else
365 		if (eport == ntohs(ipn.in_pmin) &&
366 		    eport == ntohs(ipn.in_pmax) &&
367 		    strcmp(ipn.in_tag.ipt_tag, group_name) == 0 &&
368 		    ipn.in_p == proto)
369 #endif
370 		{
371 			strlcpy(desc, "", desclen);
372 			if (packets != NULL)
373 				*packets = 0;
374 			if (bytes != NULL)
375 				*bytes = 0;
376 			if (iport != NULL)
377 #if IPFILTER_VERSION >= 5000000
378 				*iport = ntohs(ipn.in_dpnext);
379 #else
380 				*iport = ntohs(ipn.in_pnext);
381 #endif
382 			if ((desc != NULL) && (timestamp != NULL))
383 				get_redirect_desc(eport, proto, desc, desclen, timestamp);
384 			if ((rhost != NULL) && (rhostlen > 0))
385 #if IPFILTER_VERSION >= 5000000
386 				inet_ntop(AF_INET, &ipn.in_nsrc.na_addr[0].in4, rhost, rhostlen);	/* in_nsrcip */
387 #else
388 				inet_ntop(AF_INET, &ipn.in_src[0].in4, rhost, rhostlen);
389 #endif
390 #if IPFILTER_VERSION >= 5000000
391 			inet_ntop(AF_INET, &ipn.in_ndst.na_addr[0].in4, iaddr, iaddrlen);	/* in_ndstip */
392 #else
393 			inet_ntop(AF_INET, &ipn.in_in[0].in4, iaddr, iaddrlen);
394 #endif
395 			r = 0;
396 		}
397 	} while (ipn.in_next != NULL);
398 	return r;
399 }
400 
401 
402 int
get_redirect_rule_by_index(int index,char * ifname,unsigned short * eport,char * iaddr,int iaddrlen,unsigned short * iport,int * proto,char * desc,int desclen,char * rhost,int rhostlen,unsigned int * timestamp,u_int64_t * packets,u_int64_t * bytes)403 get_redirect_rule_by_index(int index,
404     char * ifname, unsigned short * eport,
405     char * iaddr, int iaddrlen, unsigned short * iport,
406     int * proto, char * desc, int desclen,
407     char * rhost, int rhostlen,
408     unsigned int * timestamp,
409     u_int64_t * packets, u_int64_t * bytes)
410 {
411 	ipfgeniter_t iter;
412 	ipfobj_t obj;
413 	ipnat_t ipn;
414 	int n, r;
415 
416 	if (index < 0)
417 		return -1;
418 
419 	if (dev < 0) {
420 		syslog(LOG_ERR, "%s not open", IPNAT_NAME);
421 		return -1;
422 	}
423 
424 	memset(&obj, 0, sizeof(obj));
425 	obj.ipfo_rev = IPFILTER_VERSION;
426 	obj.ipfo_ptr = &iter;
427 	obj.ipfo_size = sizeof(iter);
428 	obj.ipfo_type = IPFOBJ_GENITER;
429 
430 	iter.igi_type = IPFGENITER_IPNAT;
431 #if IPFILTER_VERSION > 4011300
432 	iter.igi_nitems = 1;
433 #endif
434 	iter.igi_data = &ipn;
435 
436 	n = 0;
437 	r = -1;
438 	do {
439 		if (ioctl(dev, SIOCGENITER, &obj) == -1) {
440 			syslog(LOG_ERR, "%s:ioctl(SIOCGENITER): %m",
441 			    "get_redirect_rule_by_index");
442 			break;
443 		}
444 
445 		if (strcmp(ipn.in_tag.ipt_tag, group_name) != 0)
446 			continue;
447 
448 		if (index == n++) {
449 #if IPFILTER_VERSION >= 5000000
450 			*proto = ipn.in_pr[0];
451 			*eport = ntohs(ipn.in_dpmax);
452 			*iport = ntohs(ipn.in_dpnext);
453 #else
454 			*proto = ipn.in_p;
455 			*eport = ntohs(ipn.in_pmax);
456 			*iport = ntohs(ipn.in_pnext);
457 #endif
458 
459 			if (ifname)
460 #if IPFILTER_VERSION >= 5000000
461 				strlcpy(ifname, ipn.in_names + ipn.in_ifnames[0], IFNAMSIZ);
462 #else
463 				strlcpy(ifname, ipn.in_ifnames[0], IFNAMSIZ);
464 #endif
465 			if (packets != NULL)
466 				*packets = 0;
467 			if (bytes != NULL)
468 				*bytes = 0;
469 			if ((desc != NULL) && (timestamp != NULL))
470 				get_redirect_desc(*eport, *proto, desc, desclen, timestamp);
471 			if ((rhost != NULL) && (rhostlen > 0))
472 #if IPFILTER_VERSION >= 5000000
473 				inet_ntop(AF_INET, &ipn.in_nsrc.na_addr[0].in4, rhost, rhostlen);	/* in_nsrcip */
474 #else
475 				inet_ntop(AF_INET, &ipn.in_src[0].in4, rhost, rhostlen);
476 #endif
477 #if IPFILTER_VERSION >= 5000000
478 			inet_ntop(AF_INET, &ipn.in_ndst.na_addr[0].in4, iaddr, iaddrlen);	/* in_ndstip */
479 #else
480 			inet_ntop(AF_INET, &ipn.in_in[0].in4, iaddr, iaddrlen);
481 #endif
482 			r = 0;
483 		}
484 	} while (ipn.in_next != NULL);
485 	return r;
486 }
487 
488 static int
real_delete_redirect_rule(const char * ifname,unsigned short eport,int proto)489 real_delete_redirect_rule(const char * ifname, unsigned short eport, int proto)
490 {
491 	ipfgeniter_t iter;
492 	ipfobj_t obj;
493 	ipnat_t ipn;
494 	int r;
495 	UNUSED(ifname);
496 
497 	memset(&obj, 0, sizeof(obj));
498 	obj.ipfo_rev = IPFILTER_VERSION;
499 	obj.ipfo_type = IPFOBJ_GENITER;
500 	obj.ipfo_size = sizeof(iter);
501 	obj.ipfo_ptr = &iter;
502 
503 	iter.igi_type = IPFGENITER_IPNAT;
504 #if IPFILTER_VERSION > 4011300
505 	iter.igi_nitems = 1;
506 #endif
507 	iter.igi_data = &ipn;
508 
509 	if (dev < 0) {
510 		syslog(LOG_ERR, "%s not open", IPNAT_NAME);
511 		return -1;
512 	}
513 
514 	r = -1;
515 	do {
516 		if (ioctl(dev, SIOCGENITER, &obj) == -1) {
517 			syslog(LOG_ERR, "%s:ioctl(SIOCGENITER): %m",
518 			    "delete_redirect_rule");
519 			break;
520 		}
521 #if IPFILTER_VERSION >= 5000000
522 		if (eport == ntohs(ipn.in_dpmin) &&
523 		    eport == ntohs(ipn.in_dpmax) &&
524 		    strcmp(ipn.in_tag.ipt_tag, group_name) == 0 &&
525 		    ipn.in_pr[0] == proto)
526 #else
527 		if (eport == ntohs(ipn.in_pmin) &&
528 		    eport == ntohs(ipn.in_pmax) &&
529 		    strcmp(ipn.in_tag.ipt_tag, group_name) == 0 &&
530 		    ipn.in_p == proto)
531 #endif
532 		{
533 			obj.ipfo_rev = IPFILTER_VERSION;
534 			obj.ipfo_size = sizeof(ipn);
535 			obj.ipfo_ptr = &ipn;
536 			obj.ipfo_type = IPFOBJ_IPNAT;
537 			r = ioctl(dev, SIOCRMNAT, &obj);
538 			if (r == -1)
539 				syslog(LOG_ERR, "%s:ioctl(SIOCRMNAT): %m",
540 				    "delete_redirect_rule");
541 			/* Delete the desc even if the above failed */
542 			del_redirect_desc(eport, proto);
543 			break;
544 		}
545 	} while (ipn.in_next != NULL);
546 	return r;
547 }
548 
549 /* FIXME: For some reason, the iter isn't reset every other delete,
550  * so we attempt 2 deletes. */
551 int
delete_redirect_rule(const char * ifname,unsigned short eport,int proto)552 delete_redirect_rule(const char * ifname, unsigned short eport, int proto)
553 {
554 	int r;
555 
556 	r = real_delete_redirect_rule(ifname, eport, proto);
557 	if (r == -1)
558 		r = real_delete_redirect_rule(ifname, eport, proto);
559 	return r;
560 }
561 
562 /* thanks to Seth Mos for this function */
563 int
add_filter_rule2(const char * ifname,const char * rhost,const char * iaddr,unsigned short eport,unsigned short iport,int proto,const char * desc)564 add_filter_rule2(const char * ifname, const char * rhost,
565     const char * iaddr, unsigned short eport, unsigned short iport,
566     int proto, const char * desc)
567 {
568 	ipfobj_t obj;
569 	frentry_t fr;
570 	fripf_t ipffr;
571 	int r;
572 	UNUSED(ifname); UNUSED(desc);
573 	UNUSED(iport);
574 
575 	if (dev_ipl < 0) {
576 		syslog(LOG_ERR, "%s not open", IPL_NAME);
577 		return -1;
578 	}
579 
580 	memset(&obj, 0, sizeof(obj));
581 	memset(&fr, 0, sizeof(fr));
582 	memset(&ipffr, 0, sizeof(ipffr));
583 
584 	fr.fr_flags = FR_PASS|FR_KEEPSTATE|FR_QUICK|FR_INQUE;
585 	if (GETFLAG(LOGPACKETSMASK))
586 		fr.fr_flags |= FR_LOG|FR_LOGFIRST;
587 #if IPFILTER_VERSION >= 5000000
588 	fr.fr_family = PF_INET;
589 #else
590 	fr.fr_v = 4;
591 #endif
592 
593 	fr.fr_type = FR_T_IPF;
594 	fr.fr_dun.fru_ipf = &ipffr;
595 	fr.fr_dsize = sizeof(ipffr);
596 	fr.fr_isc = (void *)-1;
597 
598 	fr.fr_proto = proto;
599 	fr.fr_mproto = 0xff;
600 	fr.fr_dcmp = FR_EQUAL;
601 	fr.fr_dport = eport;
602 #ifdef USE_IFNAME_IN_RULES
603 	if (ifname) {
604 #if IPFILTER_VERSION >= 5000000
605 		/* XXX check for stack overflow ! */
606 		fr.fr_ifnames[0] = fr.fr_namelen;
607 		strlcpy(fr.fr_names + fr.fr_ifnames[0], ifname, IFNAMSIZ);
608 		fr.fr_namelen += strlen(ifname) + 1;
609 #else
610 		strlcpy(fr.fr_ifnames[0], ifname, IFNAMSIZ);
611 #endif
612 	}
613 #endif
614 #if IPFILTER_VERSION >= 5000000
615 	/* XXX check for stack overflow ! */
616 	fr.fr_group = fr.fr_namelen;
617 	strlcpy(fr.fr_names + fr.fr_group, group_name, FR_GROUPLEN);
618 	fr.fr_namelen += strlen(group_name) + 1;
619 #else
620 	strlcpy(fr.fr_group, group_name, sizeof(fr.fr_group));
621 #endif
622 
623 	if (proto == IPPROTO_TCP) {
624 		fr.fr_tcpf = TH_SYN;
625 		fr.fr_tcpfm = TH_SYN|TH_ACK|TH_RST|TH_FIN|TH_URG|TH_PUSH;
626 	}
627 
628 	if(rhost && rhost[0] != '\0' && rhost[0] != '*')
629 	{
630 		inet_pton(AF_INET, rhost, &fr.fr_saddr);
631 		fr.fr_smask = 0xffffffff;
632 	}
633 
634 	inet_pton(AF_INET, iaddr, &fr.fr_daddr);
635 	fr.fr_dmask = 0xffffffff;
636 
637 	obj.ipfo_rev = IPFILTER_VERSION;
638 	obj.ipfo_ptr = &fr;
639 	obj.ipfo_size = sizeof(fr);
640 
641 	r = ioctl(dev_ipl, SIOCINAFR, &obj);
642 	if (r == -1) {
643 		if (errno == ESRCH)
644 			syslog(LOG_ERR,
645 			    "SIOCINAFR(missing 'head %s' rule?):%m",
646 			    group_name);
647 		else
648 			syslog(LOG_ERR, "SIOCINAFR:%m");
649 	}
650 	return r;
651 }
652 
653 int
delete_filter_rule(const char * ifname,unsigned short eport,int proto)654 delete_filter_rule(const char * ifname, unsigned short eport, int proto)
655 {
656 	ipfobj_t wobj, dobj;
657 	ipfruleiter_t rule;
658 	u_long darray[1000];
659 	u_long array[1000];
660 	friostat_t fio;
661 	frentry_t *fp;
662 	int r;
663 	UNUSED(ifname);
664 
665 	if (dev_ipl < 0) {
666 		syslog(LOG_ERR, "%s not open", IPL_NAME);
667 		return -1;
668 	}
669 
670 	wobj.ipfo_rev = IPFILTER_VERSION;
671 	wobj.ipfo_type = IPFOBJ_IPFSTAT;
672 	wobj.ipfo_size = sizeof(fio);
673 	wobj.ipfo_ptr = &fio;
674 
675 	if (ioctl(dev_ipl, SIOCGETFS, &wobj) == -1) {
676 		syslog(LOG_ERR, "ioctl(SIOCGETFS): %m");
677 		return -1;
678 	}
679 
680 	wobj.ipfo_rev = IPFILTER_VERSION;
681 	wobj.ipfo_ptr = &rule;
682 	wobj.ipfo_size = sizeof(rule);
683 	wobj.ipfo_type = IPFOBJ_IPFITER;
684 
685 	fp = (frentry_t *)array;
686 	fp->fr_dun.fru_data = darray;
687 	fp->fr_dsize = sizeof(darray);
688 
689 	rule.iri_inout = 0;
690 	rule.iri_active = fio.f_active;
691 #if IPFILTER_VERSION > 4011300
692 	rule.iri_nrules = 1;
693 	rule.iri_v = 4;
694 #endif
695 	rule.iri_rule = fp;
696 	strlcpy(rule.iri_group, group_name, sizeof(rule.iri_group));
697 
698 	dobj.ipfo_rev = IPFILTER_VERSION;
699 	dobj.ipfo_size = sizeof(*fp);
700 	dobj.ipfo_type = IPFOBJ_FRENTRY;
701 
702 	r = -1;
703 	do {
704 		memset(array, 0xff, sizeof(array));
705 
706 		if (ioctl(dev_ipl, SIOCIPFITER, &wobj) == -1) {
707 			syslog(LOG_ERR, "ioctl(SIOCIPFITER): %m");
708 			break;
709 		}
710 
711 		if (fp->fr_data != NULL)
712 			fp->fr_data = (char *)fp + sizeof(*fp);
713 		if ((fp->fr_type & ~FR_T_BUILTIN) == FR_T_IPF &&
714 		    fp->fr_dport == eport &&
715 		    fp->fr_proto == proto)
716 		{
717 			dobj.ipfo_ptr = fp;
718 
719 			r = ioctl(dev_ipl, SIOCRMAFR, &dobj);
720 			if (r == -1)
721 				syslog(LOG_ERR, "ioctl(SIOCRMAFR): %m");
722 			break;
723 		}
724 	} while (fp->fr_next != NULL);
725 	return r;
726 }
727 
728 unsigned short *
get_portmappings_in_range(unsigned short startport,unsigned short endport,int proto,unsigned int * number)729 get_portmappings_in_range(unsigned short startport, unsigned short endport,
730                           int proto, unsigned int * number)
731 {
732 	unsigned short *array, *array2;
733 	unsigned int capacity;
734 	unsigned short eport;
735 	ipfgeniter_t iter;
736 	ipfobj_t obj;
737 	ipnat_t ipn;
738 
739 	*number = 0;
740 	if (dev < 0) {
741 		syslog(LOG_ERR, "%s not open", IPNAT_NAME);
742 		return NULL;
743 	}
744 	capacity = 128;
745 	array = calloc(capacity, sizeof(unsigned short));
746 	if(!array)
747 	{
748 		syslog(LOG_ERR, "get_portmappings_in_range() : calloc error");
749 		return NULL;
750 	}
751 
752 	memset(&obj, 0, sizeof(obj));
753 	obj.ipfo_rev = IPFILTER_VERSION;
754 	obj.ipfo_ptr = &iter;
755 	obj.ipfo_size = sizeof(iter);
756 	obj.ipfo_type = IPFOBJ_GENITER;
757 
758 	iter.igi_type = IPFGENITER_IPNAT;
759 #if IPFILTER_VERSION > 4011300
760 	iter.igi_nitems = 1;
761 #endif
762 	iter.igi_data = &ipn;
763 
764 	do {
765 		if (ioctl(dev, SIOCGENITER, &obj) == -1) {
766 			syslog(LOG_ERR, "%s:ioctl(SIOCGENITER): %m",
767 			    "get_portmappings_in_range");
768 			break;
769 		}
770 
771 		if (strcmp(ipn.in_tag.ipt_tag, group_name) != 0)
772 			continue;
773 
774 #if IPFILTER_VERSION >= 5000000
775 		eport = ntohs(ipn.in_dpmin);
776 		if( (eport == ntohs(ipn.in_dpmax))
777 		  && (ipn.in_pr[0] == proto)
778 		  && (startport <= eport) && (eport <= endport) )
779 #else
780 		eport = ntohs(ipn.in_pmin);
781 		if( (eport == ntohs(ipn.in_pmax))
782 		  && (ipn.in_p == proto)
783 		  && (startport <= eport) && (eport <= endport) )
784 #endif
785 		{
786 			if(*number >= capacity)
787 			{
788 				/* need to increase the capacity of the array */
789 				capacity += 128;
790 				array2 = realloc(array, sizeof(unsigned short)*capacity);
791 				if(!array2)
792 				{
793 					syslog(LOG_ERR, "get_portmappings_in_range() : realloc(%lu) error", sizeof(unsigned short)*capacity);
794 					*number = 0;
795 					free(array);
796 					return NULL;
797 				}
798 				array = array2;
799 			}
800 			array[*number] = eport;
801 			(*number)++;
802 		}
803 	} while (ipn.in_next != NULL);
804 	return array;
805 }
806 
807 /* update the port mapping internal port, decription and timestamp */
808 int
update_portmapping(const char * ifname,unsigned short eport,int proto,unsigned short iport,const char * desc,unsigned int timestamp)809 update_portmapping(const char * ifname, unsigned short eport, int proto,
810                    unsigned short iport, const char * desc,
811                    unsigned int timestamp)
812 {
813 	UNUSED(ifname); UNUSED(eport); UNUSED(proto);
814 	UNUSED(iport); UNUSED(desc); UNUSED(timestamp);
815 	/* TODO: implement update_portmapping() */
816 	syslog(LOG_ERR, __FILE__ " update_portmapping() is not implemented");
817 	return -1;
818 }
819 
820 /* update the port mapping decription and timestamp */
821 int
update_portmapping_desc_timestamp(const char * ifname,unsigned short eport,int proto,const char * desc,unsigned int timestamp)822 update_portmapping_desc_timestamp(const char * ifname,
823                    unsigned short eport, int proto,
824                    const char * desc, unsigned int timestamp)
825 {
826 	UNUSED(ifname);
827 	del_redirect_desc(eport, proto);
828 	add_redirect_desc(eport,proto, timestamp, desc);
829 	return 0;
830 }
831 
832