1/* $Id: iptcrdr.c,v 1.53 2015/02/08 09:10:00 nanard Exp $ */
2/* MiniUPnP project
3 * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
4 * (c) 2006-2015 Thomas Bernard
5 * This software is subject to the conditions detailed
6 * in the LICENCE file provided within the distribution */
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10#include <syslog.h>
11#include <sys/errno.h>
12#include <sys/socket.h>
13#include <netinet/in.h>
14#include <arpa/inet.h>
15#include <dlfcn.h>
16#include <iptables.h>
17#include <linux/netfilter/xt_DSCP.h>
18#include <libiptc/libiptc.h>
19
20#include <linux/version.h>
21
22#if IPTABLES_143
23/* IPTABLES API version >= 1.4.3 */
24
25/* added in order to compile on gentoo :
26 * http://miniupnp.tuxfamily.org/forum/viewtopic.php?p=2183 */
27#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
28#define __must_be_array(a) \
29	BUILD_BUG_ON_ZERO(__builtin_types_compatible_p(typeof(a), typeof(&a[0])))
30#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr))
31#define LIST_POISON2  ((void *) 0x00200200 )
32
33#if 0
34#include <linux/netfilter/nf_nat.h>
35#else
36#include "tiny_nf_nat.h"
37#endif
38#define ip_nat_multi_range	nf_nat_multi_range
39#define ip_nat_range		nf_nat_range
40#define IPTC_HANDLE		struct iptc_handle *
41#else
42/* IPTABLES API version < 1.4.3 */
43#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
44#include <linux/netfilter_ipv4/ip_nat.h>
45#else
46#if 0
47#include <linux/netfilter/nf_nat.h>
48#else
49#include "tiny_nf_nat.h"
50#endif
51#define ip_nat_multi_range	nf_nat_multi_range
52#define ip_nat_range		nf_nat_range
53#endif
54#define IPTC_HANDLE		iptc_handle_t
55#endif
56
57/* IPT_ALIGN was renamed XT_ALIGN in iptables-1.4.11 */
58#ifndef IPT_ALIGN
59#define IPT_ALIGN XT_ALIGN
60#endif
61
62#include "../macros.h"
63#include "../config.h"
64#include "iptcrdr.h"
65#include "../upnpglobalvars.h"
66
67/* local functions declarations */
68static int
69addnatrule(int proto, unsigned short eport,
70           const char * iaddr, unsigned short iport,
71           const char * rhost);
72
73static int
74add_filter_rule(int proto, const char * rhost,
75                const char * iaddr, unsigned short iport);
76
77static int
78addpeernatrule(int proto,
79           const char * eaddr, unsigned short eport,
80           const char * iaddr, unsigned short iport,
81           const char * rhost, unsigned short rport);
82
83static int
84addpeerdscprule(int proto, unsigned char dscp,
85           const char * iaddr, unsigned short iport,
86           const char * rhost, unsigned short rport);
87
88/* dummy init and shutdown functions */
89int init_redirect(void)
90{
91	return 0;
92}
93
94void shutdown_redirect(void)
95{
96	return;
97}
98
99/* convert an ip address to string */
100static int snprintip(char * dst, size_t size, uint32_t ip)
101{
102	return snprintf(dst, size,
103	       "%u.%u.%u.%u", ip >> 24, (ip >> 16) & 0xff,
104	       (ip >> 8) & 0xff, ip & 0xff);
105}
106
107/* netfilter cannot store redirection descriptions, so we use our
108 * own structure to store them */
109struct rdr_desc {
110	struct rdr_desc * next;
111	unsigned int timestamp;
112	unsigned short eport;
113	short proto;
114	char str[];
115};
116
117/* pointer to the chained list where descriptions are stored */
118static struct rdr_desc * rdr_desc_list = 0;
119
120/* add a description to the list of redirection descriptions */
121static void
122add_redirect_desc(unsigned short eport, int proto,
123                  const char * desc, unsigned int timestamp)
124{
125	struct rdr_desc * p;
126	size_t l;
127	/* set a default description if none given */
128	if(!desc)
129		desc = "miniupnpd";
130	l = strlen(desc) + 1;
131	p = malloc(sizeof(struct rdr_desc) + l);
132	if(p)
133	{
134		p->next = rdr_desc_list;
135		p->timestamp = timestamp;
136		p->eport = eport;
137		p->proto = (short)proto;
138		memcpy(p->str, desc, l);
139		rdr_desc_list = p;
140	}
141}
142
143/* delete a description from the list */
144static void
145del_redirect_desc(unsigned short eport, int proto)
146{
147	struct rdr_desc * p, * last;
148	p = rdr_desc_list;
149	last = 0;
150	while(p)
151	{
152		if(p->eport == eport && p->proto == proto)
153		{
154			if(!last)
155				rdr_desc_list = p->next;
156			else
157				last->next = p->next;
158			free(p);
159			return;
160		}
161		last = p;
162		p = p->next;
163	}
164}
165
166/* go through the list to find the description */
167static void
168get_redirect_desc(unsigned short eport, int proto,
169                  char * desc, int desclen,
170                  unsigned int * timestamp)
171{
172	struct rdr_desc * p;
173	for(p = rdr_desc_list; p; p = p->next)
174	{
175		if(p->eport == eport && p->proto == (short)proto)
176		{
177			if(desc)
178				strncpy(desc, p->str, desclen);
179			if(timestamp)
180				*timestamp = p->timestamp;
181			return;
182		}
183	}
184	/* if no description was found, return miniupnpd as default */
185	if(desc)
186		strncpy(desc, "miniupnpd", desclen);
187	if(timestamp)
188		*timestamp = 0;
189}
190
191#if USE_INDEX_FROM_DESC_LIST
192static int
193get_redirect_desc_by_index(int index, unsigned short * eport, int * proto,
194                  char * desc, int desclen, unsigned int * timestamp)
195{
196	int i = 0;
197	struct rdr_desc * p;
198	if(!desc || (desclen == 0))
199		return -1;
200	for(p = rdr_desc_list; p; p = p->next, i++)
201	{
202		if(i == index)
203		{
204			*eport = p->eport;
205			*proto = (int)p->proto;
206			strncpy(desc, p->str, desclen);
207			if(timestamp)
208				*timestamp = p->timestamp;
209			return 0;
210		}
211	}
212	return -1;
213}
214#endif
215
216/* add_redirect_rule2() */
217int
218add_redirect_rule2(const char * ifname,
219                   const char * rhost, unsigned short eport,
220                   const char * iaddr, unsigned short iport, int proto,
221				   const char * desc, unsigned int timestamp)
222{
223	int r;
224	UNUSED(ifname);
225
226	r = addnatrule(proto, eport, iaddr, iport, rhost);
227	if(r >= 0)
228		add_redirect_desc(eport, proto, desc, timestamp);
229	return r;
230}
231
232/* add_redirect_rule2() */
233int
234add_peer_redirect_rule2(const char * ifname,
235                   const char * rhost, unsigned short rport,
236                   const char * eaddr, unsigned short eport,
237                   const char * iaddr, unsigned short iport, int proto,
238                   const char * desc, unsigned int timestamp)
239{
240	int r;
241	UNUSED(ifname);
242
243	r = addpeernatrule(proto, eaddr, eport, iaddr, iport, rhost, rport);
244	if(r >= 0)
245		add_redirect_desc(eport, proto, desc, timestamp);
246	return r;
247}
248
249int
250add_peer_dscp_rule2(const char * ifname,
251                   const char * rhost, unsigned short rport,
252                   unsigned char dscp,
253                   const char * iaddr, unsigned short iport, int proto,
254                   const char * desc, unsigned int timestamp)
255{
256	int r;
257	UNUSED(ifname);
258	UNUSED(desc);
259	UNUSED(timestamp);
260
261	r = addpeerdscprule(proto, dscp, iaddr, iport, rhost, rport);
262/*	if(r >= 0)
263		add_redirect_desc(dscp, proto, desc, timestamp); */
264	return r;
265}
266
267int
268add_filter_rule2(const char * ifname,
269                 const char * rhost, const char * iaddr,
270                 unsigned short eport, unsigned short iport,
271                 int proto, const char * desc)
272{
273	UNUSED(ifname);
274	UNUSED(eport);
275	UNUSED(desc);
276
277	return add_filter_rule(proto, rhost, iaddr, iport);
278}
279
280/* get_redirect_rule()
281 * returns -1 if the rule is not found */
282int
283get_redirect_rule(const char * ifname, unsigned short eport, int proto,
284                  char * iaddr, int iaddrlen, unsigned short * iport,
285                  char * desc, int desclen,
286                  char * rhost, int rhostlen,
287                  unsigned int * timestamp,
288                  u_int64_t * packets, u_int64_t * bytes)
289{
290	return get_nat_redirect_rule(miniupnpd_nat_chain,
291	                             ifname, eport, proto,
292	                             iaddr, iaddrlen, iport,
293	                             desc, desclen,
294	                             rhost, rhostlen,
295	                             timestamp, packets, bytes);
296}
297
298int
299get_nat_redirect_rule(const char * nat_chain_name, const char * ifname, unsigned short eport, int proto,
300                  char * iaddr, int iaddrlen, unsigned short * iport,
301                  char * desc, int desclen,
302                  char * rhost, int rhostlen,
303                  unsigned int * timestamp,
304                  u_int64_t * packets, u_int64_t * bytes)
305{
306	int r = -1;
307	IPTC_HANDLE h;
308	const struct ipt_entry * e;
309	const struct ipt_entry_target * target;
310	const struct ip_nat_multi_range * mr;
311	const struct ipt_entry_match *match;
312	UNUSED(ifname);
313
314	h = iptc_init("nat");
315	if(!h)
316	{
317		syslog(LOG_ERR, "get_redirect_rule() : "
318		                "iptc_init() failed : %s",
319		       iptc_strerror(errno));
320		return -1;
321	}
322	if(!iptc_is_chain(nat_chain_name, h))
323	{
324		syslog(LOG_ERR, "chain %s not found", nat_chain_name);
325	}
326	else
327	{
328#ifdef IPTABLES_143
329		for(e = iptc_first_rule(nat_chain_name, h);
330		    e;
331			e = iptc_next_rule(e, h))
332#else
333		for(e = iptc_first_rule(nat_chain_name, &h);
334		    e;
335			e = iptc_next_rule(e, &h))
336#endif
337		{
338			if(proto==e->ip.proto)
339			{
340				match = (const struct ipt_entry_match *)&e->elems;
341				if(0 == strncmp(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN))
342				{
343					const struct ipt_tcp * info;
344					info = (const struct ipt_tcp *)match->data;
345					if(eport != info->dpts[0])
346						continue;
347				}
348				else
349				{
350					const struct ipt_udp * info;
351					info = (const struct ipt_udp *)match->data;
352					if(eport != info->dpts[0])
353						continue;
354				}
355				target = (void *)e + e->target_offset;
356				/* target = ipt_get_target(e); */
357				mr = (const struct ip_nat_multi_range *)&target->data[0];
358				snprintip(iaddr, iaddrlen, ntohl(mr->range[0].min_ip));
359				*iport = ntohs(mr->range[0].min.all);
360				get_redirect_desc(eport, proto, desc, desclen, timestamp);
361				if(packets)
362					*packets = e->counters.pcnt;
363				if(bytes)
364					*bytes = e->counters.bcnt;
365				/* rhost */
366				if(e->ip.src.s_addr && rhost) {
367					snprintip(rhost, rhostlen, ntohl(e->ip.src.s_addr));
368				}
369				r = 0;
370				break;
371			}
372		}
373	}
374	if(h)
375#ifdef IPTABLES_143
376		iptc_free(h);
377#else
378		iptc_free(&h);
379#endif
380	return r;
381}
382
383/* get_redirect_rule_by_index()
384 * return -1 when the rule was not found */
385int
386get_redirect_rule_by_index(int index,
387                           char * ifname, unsigned short * eport,
388                           char * iaddr, int iaddrlen, unsigned short * iport,
389                           int * proto, char * desc, int desclen,
390                           char * rhost, int rhostlen,
391                           unsigned int * timestamp,
392                           u_int64_t * packets, u_int64_t * bytes)
393{
394	int r = -1;
395#if USE_INDEX_FROM_DESC_LIST
396	r = get_redirect_desc_by_index(index, eport, proto,
397	                               desc, desclen, timestamp);
398	if (r==0)
399	{
400		r = get_redirect_rule(ifname, *eport, *proto, iaddr, iaddrlen, iport,
401				      0, 0, packets, bytes);
402	}
403#else
404	int i = 0;
405	IPTC_HANDLE h;
406	const struct ipt_entry * e;
407	const struct ipt_entry_target * target;
408	const struct ip_nat_multi_range * mr;
409	const struct ipt_entry_match *match;
410	UNUSED(ifname);
411
412	h = iptc_init("nat");
413	if(!h)
414	{
415		syslog(LOG_ERR, "get_redirect_rule_by_index() : "
416		                "iptc_init() failed : %s",
417		       iptc_strerror(errno));
418		return -1;
419	}
420	if(!iptc_is_chain(miniupnpd_nat_chain, h))
421	{
422		syslog(LOG_ERR, "chain %s not found", miniupnpd_nat_chain);
423	}
424	else
425	{
426#ifdef IPTABLES_143
427		for(e = iptc_first_rule(miniupnpd_nat_chain, h);
428		    e;
429			e = iptc_next_rule(e, h))
430#else
431		for(e = iptc_first_rule(miniupnpd_nat_chain, &h);
432		    e;
433			e = iptc_next_rule(e, &h))
434#endif
435		{
436			if(i==index)
437			{
438				*proto = e->ip.proto;
439				match = (const struct ipt_entry_match *)&e->elems;
440				if(0 == strncmp(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN))
441				{
442					const struct ipt_tcp * info;
443					info = (const struct ipt_tcp *)match->data;
444					*eport = info->dpts[0];
445				}
446				else
447				{
448					const struct ipt_udp * info;
449					info = (const struct ipt_udp *)match->data;
450					*eport = info->dpts[0];
451				}
452				target = (void *)e + e->target_offset;
453				mr = (const struct ip_nat_multi_range *)&target->data[0];
454				snprintip(iaddr, iaddrlen, ntohl(mr->range[0].min_ip));
455				*iport = ntohs(mr->range[0].min.all);
456				get_redirect_desc(*eport, *proto, desc, desclen, timestamp);
457				if(packets)
458					*packets = e->counters.pcnt;
459				if(bytes)
460					*bytes = e->counters.bcnt;
461				/* rhost */
462				if(rhost && rhostlen > 0) {
463					if(e->ip.src.s_addr) {
464						snprintip(rhost, rhostlen, ntohl(e->ip.src.s_addr));
465					} else {
466						rhost[0] = '\0';
467					}
468				}
469				r = 0;
470				break;
471			}
472			i++;
473		}
474	}
475	if(h)
476#ifdef IPTABLES_143
477		iptc_free(h);
478#else
479		iptc_free(&h);
480#endif
481#endif
482	return r;
483}
484
485/* get_peer_rule_by_index()
486 * return -1 when the rule was not found */
487int
488get_peer_rule_by_index(int index,
489                           char * ifname, unsigned short * eport,
490                           char * iaddr, int iaddrlen, unsigned short * iport,
491                           int * proto, char * desc, int desclen,
492                           char * rhost, int rhostlen, unsigned short * rport,
493                           unsigned int * timestamp,
494                           u_int64_t * packets, u_int64_t * bytes)
495{
496	int r = -1;
497#if USE_INDEX_FROM_DESC_LIST && 0
498	r = get_redirect_desc_by_index(index, eport, proto,
499	                               desc, desclen, timestamp);
500	if (r==0)
501	{
502		r = get_redirect_rule(ifname, *eport, *proto, iaddr, iaddrlen, iport,
503				      0, 0, packets, bytes);
504	}
505#else
506	int i = 0;
507	IPTC_HANDLE h;
508	const struct ipt_entry * e;
509	const struct ipt_entry_target * target;
510	const struct ip_nat_multi_range * mr;
511	const struct ipt_entry_match *match;
512	UNUSED(ifname);
513
514	h = iptc_init("nat");
515	if(!h)
516	{
517		syslog(LOG_ERR, "get_peer_rule_by_index() : "
518		                "iptc_init() failed : %s",
519		       iptc_strerror(errno));
520		return -1;
521	}
522	if(!iptc_is_chain(miniupnpd_peer_chain, h))
523	{
524		syslog(LOG_ERR, "chain %s not found", miniupnpd_peer_chain);
525	}
526	else
527	{
528#ifdef IPTABLES_143
529		for(e = iptc_first_rule(miniupnpd_peer_chain, h);
530		    e;
531			e = iptc_next_rule(e, h))
532#else
533		for(e = iptc_first_rule(miniupnpd_peer_chain, &h);
534		    e;
535			e = iptc_next_rule(e, &h))
536#endif
537		{
538			if(i==index)
539			{
540				*proto = e->ip.proto;
541				match = (const struct ipt_entry_match *)&e->elems;
542				if(0 == strncmp(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN))
543				{
544					const struct ipt_tcp * info;
545					info = (const struct ipt_tcp *)match->data;
546					if (rport)
547						*rport = info->dpts[0];
548					if (iport)
549						*iport = info->spts[0];
550				}
551				else
552				{
553					const struct ipt_udp * info;
554					info = (const struct ipt_udp *)match->data;
555					if (rport)
556						*rport = info->dpts[0];
557					if (iport)
558						*iport = info->spts[0];
559				}
560				target = (void *)e + e->target_offset;
561				mr = (const struct ip_nat_multi_range *)&target->data[0];
562				*eport = ntohs(mr->range[0].min.all);
563				get_redirect_desc(*eport, *proto, desc, desclen, timestamp);
564				if(packets)
565					*packets = e->counters.pcnt;
566				if(bytes)
567					*bytes = e->counters.bcnt;
568				/* rhost */
569				if(rhost && rhostlen > 0) {
570					if(e->ip.dst.s_addr) {
571						snprintip(rhost, rhostlen, ntohl(e->ip.dst.s_addr));
572					} else {
573						rhost[0] = '\0';
574					}
575				}
576				if(iaddr && iaddrlen > 0) {
577					if(e->ip.src.s_addr) {
578						snprintip(iaddr, iaddrlen, ntohl(e->ip.src.s_addr));
579					} else {
580						rhost[0] = '\0';
581					}
582				}
583				r = 0;
584				break;
585			}
586			i++;
587		}
588	}
589	if(h)
590#ifdef IPTABLES_143
591		iptc_free(h);
592#else
593		iptc_free(&h);
594#endif
595#endif
596	return r;
597}
598
599/* delete_rule_and_commit() :
600 * subfunction used in delete_redirect_and_filter_rules() */
601static int
602delete_rule_and_commit(unsigned int index, IPTC_HANDLE h,
603                       const char * miniupnpd_chain,
604                       const char * logcaller)
605{
606	int r = 0;
607#ifdef IPTABLES_143
608	if(!iptc_delete_num_entry(miniupnpd_chain, index, h))
609#else
610	if(!iptc_delete_num_entry(miniupnpd_chain, index, &h))
611#endif
612	{
613		syslog(LOG_ERR, "%s() : iptc_delete_num_entry(): %s\n",
614	    	   logcaller, iptc_strerror(errno));
615		r = -1;
616	}
617#ifdef IPTABLES_143
618	else if(!iptc_commit(h))
619#else
620	else if(!iptc_commit(&h))
621#endif
622	{
623		syslog(LOG_ERR, "%s() : iptc_commit(): %s\n",
624	    	   logcaller, iptc_strerror(errno));
625		r = -1;
626	}
627	if(h)
628#ifdef IPTABLES_143
629		iptc_free(h);
630#else
631		iptc_free(&h);
632#endif
633	return r;
634}
635
636/* delete_redirect_and_filter_rules()
637 */
638int
639delete_redirect_and_filter_rules(unsigned short eport, int proto)
640{
641	int r = -1, r2 = -1;
642	unsigned index = 0;
643	unsigned i = 0;
644	IPTC_HANDLE h;
645	const struct ipt_entry * e;
646	const struct ipt_entry_target * target;
647	const struct ip_nat_multi_range * mr;
648	const struct ipt_entry_match *match;
649	unsigned short iport = 0;
650	uint32_t iaddr = 0;
651
652	h = iptc_init("nat");
653	if(!h)
654	{
655		syslog(LOG_ERR, "delete_redirect_and_filter_rules() : "
656		                "iptc_init() failed : %s",
657		       iptc_strerror(errno));
658		return -1;
659	}
660	/* First step : find the right nat rule */
661	if(!iptc_is_chain(miniupnpd_nat_chain, h))
662	{
663		syslog(LOG_ERR, "chain %s not found", miniupnpd_nat_chain);
664	}
665	else
666	{
667#ifdef IPTABLES_143
668		for(e = iptc_first_rule(miniupnpd_nat_chain, h);
669		    e;
670			e = iptc_next_rule(e, h), i++)
671#else
672		for(e = iptc_first_rule(miniupnpd_nat_chain, &h);
673		    e;
674			e = iptc_next_rule(e, &h), i++)
675#endif
676		{
677			if(proto==e->ip.proto)
678			{
679				match = (const struct ipt_entry_match *)&e->elems;
680				if(0 == strncmp(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN))
681				{
682					const struct ipt_tcp * info;
683					info = (const struct ipt_tcp *)match->data;
684					if(eport != info->dpts[0])
685						continue;
686				}
687				else
688				{
689					const struct ipt_udp * info;
690					info = (const struct ipt_udp *)match->data;
691					if(eport != info->dpts[0])
692						continue;
693				}
694				/* get the index, the internal address and the internal port
695				 * of the rule */
696				index = i;
697				target = (void *)e + e->target_offset;
698				mr = (const struct ip_nat_multi_range *)&target->data[0];
699				iaddr = mr->range[0].min_ip;
700				iport = ntohs(mr->range[0].min.all);
701				r = 0;
702				break;
703			}
704		}
705	}
706	if(h)
707#ifdef IPTABLES_143
708		iptc_free(h);
709#else
710		iptc_free(&h);
711#endif
712	if(r == 0)
713	{
714		syslog(LOG_INFO, "Trying to delete nat rule at index %u", index);
715		/* Now delete both rules */
716		/* first delete the nat rule */
717		h = iptc_init("nat");
718		if(h)
719		{
720			r = delete_rule_and_commit(index, h, miniupnpd_nat_chain, "delete_redirect_rule");
721		}
722		if((r == 0) && (h = iptc_init("filter")))
723		{
724			i = 0;
725			/* we must find the right index for the filter rule */
726#ifdef IPTABLES_143
727			for(e = iptc_first_rule(miniupnpd_forward_chain, h);
728			    e;
729				e = iptc_next_rule(e, h), i++)
730#else
731			for(e = iptc_first_rule(miniupnpd_forward_chain, &h);
732			    e;
733				e = iptc_next_rule(e, &h), i++)
734#endif
735			{
736				if(proto==e->ip.proto)
737				{
738					match = (const struct ipt_entry_match *)&e->elems;
739					/*syslog(LOG_DEBUG, "filter rule #%u: %s %s",
740					       i, match->u.user.name, inet_ntoa(e->ip.dst));*/
741					if(0 == strncmp(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN))
742					{
743						const struct ipt_tcp * info;
744						info = (const struct ipt_tcp *)match->data;
745						if(iport != info->dpts[0])
746							continue;
747					}
748					else
749					{
750						const struct ipt_udp * info;
751						info = (const struct ipt_udp *)match->data;
752						if(iport != info->dpts[0])
753							continue;
754					}
755					if(iaddr != e->ip.dst.s_addr)
756						continue;
757					index = i;
758					syslog(LOG_INFO, "Trying to delete filter rule at index %u", index);
759					r = delete_rule_and_commit(index, h, miniupnpd_forward_chain, "delete_filter_rule");
760					h = NULL;
761					break;
762				}
763			}
764		}
765		if(h)
766#ifdef IPTABLES_143
767			iptc_free(h);
768#else
769			iptc_free(&h);
770#endif
771	}
772
773	/*delete PEER rule*/
774	if((h = iptc_init("nat")))
775	{
776		i = 0;
777		/* we must find the right index for the filter rule */
778#ifdef IPTABLES_143
779		for(e = iptc_first_rule(miniupnpd_peer_chain, h);
780		    e;
781			e = iptc_next_rule(e, h), i++)
782#else
783		for(e = iptc_first_rule(miniupnpd_peer_chain, &h);
784		    e;
785			e = iptc_next_rule(e, &h), i++)
786#endif
787		{
788			if(proto==e->ip.proto)
789			{
790				target = (void *)e + e->target_offset;
791				mr = (const struct ip_nat_multi_range *)&target->data[0];
792				if (eport != ntohs(mr->range[0].min.all)) {
793					continue;
794				}
795				iaddr = e->ip.src.s_addr;
796				match = (const struct ipt_entry_match *)&e->elems;
797				if(0 == strncmp(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN))
798				{
799					const struct ipt_tcp * info;
800					info = (const struct ipt_tcp *)match->data;
801					iport = info->spts[0];
802				}
803				else
804				{
805					const struct ipt_udp * info;
806					info = (const struct ipt_udp *)match->data;
807					iport = info->dpts[0];
808				}
809
810				index = i;
811				syslog(LOG_INFO, "Trying to delete peer rule at index %u", index);
812				r2 = delete_rule_and_commit(index, h, miniupnpd_peer_chain, "delete_peer_rule");
813				h = NULL;
814				break;
815			}
816		}
817	}
818
819	if(h)
820#ifdef IPTABLES_143
821		iptc_free(h);
822#else
823		iptc_free(&h);
824#endif
825	/*delete DSCP rule*/
826	if((r2==0)&&(h = iptc_init("mangle")))
827	{
828		i = 0;
829		index = -1;
830		/* we must find the right index for the filter rule */
831#ifdef IPTABLES_143
832		for(e = iptc_first_rule(miniupnpd_nat_chain, h);
833		    e;
834			e = iptc_next_rule(e, h), i++)
835#else
836		for(e = iptc_first_rule(miniupnpd_nat_chain, &h);
837		    e;
838			e = iptc_next_rule(e, &h), i++)
839#endif
840		{
841			if(proto==e->ip.proto)
842			{
843				match = (const struct ipt_entry_match *)&e->elems;
844				/*syslog(LOG_DEBUG, "filter rule #%u: %s %s",
845				       i, match->u.user.name, inet_ntoa(e->ip.dst));*/
846				if(0 == strncmp(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN))
847				{
848					const struct ipt_tcp * info;
849					info = (const struct ipt_tcp *)match->data;
850					if(iport != info->spts[0])
851						continue;
852				}
853				else
854				{
855					const struct ipt_udp * info;
856					info = (const struct ipt_udp *)match->data;
857					if(iport != info->spts[0])
858						continue;
859				}
860				if(iaddr != e->ip.src.s_addr)
861					continue;
862				index = i;
863				syslog(LOG_INFO, "Trying to delete dscp rule at index %u", index);
864				r2 = delete_rule_and_commit(index, h, miniupnpd_nat_chain, "delete_dscp_rule");
865				h = NULL;
866				break;
867			}
868		}
869	if (h)
870	#ifdef IPTABLES_143
871		iptc_free(h);
872	#else
873		iptc_free(&h);
874	#endif
875	}
876
877	del_redirect_desc(eport, proto);
878	return r*r2;
879}
880
881/* ==================================== */
882/* TODO : add the -m state --state NEW,ESTABLISHED,RELATED
883 * only for the filter rule */
884static struct ipt_entry_match *
885get_tcp_match(unsigned short dport, unsigned short sport)
886{
887	struct ipt_entry_match *match;
888	struct ipt_tcp * tcpinfo;
889	size_t size;
890	size =   IPT_ALIGN(sizeof(struct ipt_entry_match))
891	       + IPT_ALIGN(sizeof(struct ipt_tcp));
892	match = calloc(1, size);
893	match->u.match_size = size;
894	strncpy(match->u.user.name, "tcp", sizeof(match->u.user.name));
895	tcpinfo = (struct ipt_tcp *)match->data;
896	if (sport == 0) {
897		tcpinfo->spts[0] = 0;		/* all source ports */
898		tcpinfo->spts[1] = 0xFFFF;
899	} else {
900		tcpinfo->spts[0] = sport;	   /* specified source port */
901		tcpinfo->spts[1] = sport;
902	}
903	if (dport == 0) {
904		tcpinfo->dpts[0] = 0;	   /* all destination ports */
905		tcpinfo->dpts[1] = 0xFFFF;
906	} else {
907		tcpinfo->dpts[0] = dport;	/* specified destination port */
908		tcpinfo->dpts[1] = dport;
909	}
910	return match;
911}
912
913static struct ipt_entry_match *
914get_udp_match(unsigned short dport, unsigned short sport)
915{
916	struct ipt_entry_match *match;
917	struct ipt_udp * udpinfo;
918	size_t size;
919	size =   IPT_ALIGN(sizeof(struct ipt_entry_match))
920	       + IPT_ALIGN(sizeof(struct ipt_udp));
921	match = calloc(1, size);
922	match->u.match_size = size;
923	strncpy(match->u.user.name, "udp", sizeof(match->u.user.name));
924	udpinfo = (struct ipt_udp *)match->data;
925	if (sport == 0) {
926		udpinfo->spts[0] = 0;	   /* all source ports */
927		udpinfo->spts[1] = 0xFFFF;
928	} else {
929		udpinfo->spts[0] = sport;	   /* specified source port */
930		udpinfo->spts[1] = sport;
931	}
932	if (dport == 0) {
933		udpinfo->dpts[0] = 0;	   /* all destination ports */
934		udpinfo->dpts[1] = 0xFFFF;
935	} else {
936		udpinfo->dpts[0] = dport;   /* specified destination port */
937		udpinfo->dpts[1] = dport;
938	}
939	return match;
940}
941
942static struct ipt_entry_target *
943get_dnat_target(const char * daddr, unsigned short dport)
944{
945	struct ipt_entry_target * target;
946	struct ip_nat_multi_range * mr;
947	struct ip_nat_range * range;
948	size_t size;
949
950	size =   IPT_ALIGN(sizeof(struct ipt_entry_target))
951	       + IPT_ALIGN(sizeof(struct ip_nat_multi_range));
952	target = calloc(1, size);
953	target->u.target_size = size;
954	strncpy(target->u.user.name, "DNAT", sizeof(target->u.user.name));
955	/* one ip_nat_range already included in ip_nat_multi_range */
956	mr = (struct ip_nat_multi_range *)&target->data[0];
957	mr->rangesize = 1;
958	range = &mr->range[0];
959	range->min_ip = range->max_ip = inet_addr(daddr);
960	range->flags |= IP_NAT_RANGE_MAP_IPS;
961	range->min.all = range->max.all = htons(dport);
962	range->flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
963	return target;
964}
965
966static struct ipt_entry_target *
967get_snat_target(const char * saddr, unsigned short sport)
968{
969	struct ipt_entry_target * target;
970	struct ip_nat_multi_range * mr;
971	struct ip_nat_range * range;
972	size_t size;
973
974	size =   IPT_ALIGN(sizeof(struct ipt_entry_target))
975	       + IPT_ALIGN(sizeof(struct ip_nat_multi_range));
976	target = calloc(1, size);
977	target->u.target_size = size;
978	strncpy(target->u.user.name, "SNAT", sizeof(target->u.user.name));
979	/* one ip_nat_range already included in ip_nat_multi_range */
980	mr = (struct ip_nat_multi_range *)&target->data[0];
981	mr->rangesize = 1;
982	range = &mr->range[0];
983	range->min_ip = range->max_ip = inet_addr(saddr);
984	range->flags |= IP_NAT_RANGE_MAP_IPS;
985	range->min.all = range->max.all = htons(sport);
986	range->flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
987	return target;
988}
989
990static struct ipt_entry_target *
991get_dscp_target(unsigned char dscp)
992{
993	struct ipt_entry_target * target;
994	struct xt_DSCP_info * di;
995	size_t size;
996
997	size =   IPT_ALIGN(sizeof(struct ipt_entry_target))
998	       + IPT_ALIGN(sizeof(struct xt_DSCP_info));
999	target = calloc(1, size);
1000	target->u.target_size = size;
1001	strncpy(target->u.user.name, "DSCP", sizeof(target->u.user.name));
1002	/* one ip_nat_range already included in ip_nat_multi_range */
1003	di = (struct xt_DSCP_info *)&target->data[0];
1004	di->dscp=dscp;
1005	return target;
1006}
1007
1008/* iptc_init_verify_and_append()
1009 * return 0 on success, -1 on failure */
1010static int
1011iptc_init_verify_and_append(const char * table,
1012                            const char * miniupnpd_chain,
1013                            struct ipt_entry * e,
1014                            const char * logcaller)
1015{
1016	IPTC_HANDLE h;
1017	h = iptc_init(table);
1018	if(!h)
1019	{
1020		syslog(LOG_ERR, "%s : iptc_init() error : %s\n",
1021		       logcaller, iptc_strerror(errno));
1022		return -1;
1023	}
1024	if(!iptc_is_chain(miniupnpd_chain, h))
1025	{
1026		syslog(LOG_ERR, "%s : chain %s not found",
1027		       logcaller, miniupnpd_chain);
1028		if(h)
1029#ifdef IPTABLES_143
1030			iptc_free(h);
1031#else
1032			iptc_free(&h);
1033#endif
1034		return -1;
1035	}
1036	/* iptc_insert_entry(miniupnpd_chain, e, n, h/&h) could also be used */
1037#ifdef IPTABLES_143
1038	if(!iptc_append_entry(miniupnpd_chain, e, h))
1039#else
1040	if(!iptc_append_entry(miniupnpd_chain, e, &h))
1041#endif
1042	{
1043		syslog(LOG_ERR, "%s : iptc_append_entry() error : %s\n",
1044		       logcaller, iptc_strerror(errno));
1045		if(h)
1046#ifdef IPTABLES_143
1047			iptc_free(h);
1048#else
1049			iptc_free(&h);
1050#endif
1051		return -1;
1052	}
1053#ifdef IPTABLES_143
1054	if(!iptc_commit(h))
1055#else
1056	if(!iptc_commit(&h))
1057#endif
1058	{
1059		syslog(LOG_ERR, "%s : iptc_commit() error : %s\n",
1060		       logcaller, iptc_strerror(errno));
1061		if(h)
1062#ifdef IPTABLES_143
1063			iptc_free(h);
1064#else
1065			iptc_free(&h);
1066#endif
1067		return -1;
1068	}
1069	if(h)
1070#ifdef IPTABLES_143
1071		iptc_free(h);
1072#else
1073		iptc_free(&h);
1074#endif
1075	return 0;
1076}
1077
1078/* add nat rule
1079 * iptables -t nat -A MINIUPNPD -p proto --dport eport -j DNAT --to iaddr:iport
1080 * */
1081static int
1082addnatrule(int proto, unsigned short eport,
1083           const char * iaddr, unsigned short iport,
1084           const char * rhost)
1085{
1086	int r = 0;
1087	struct ipt_entry * e;
1088	struct ipt_entry * tmp;
1089	struct ipt_entry_match *match = NULL;
1090	struct ipt_entry_target *target = NULL;
1091
1092	e = calloc(1, sizeof(struct ipt_entry));
1093	if(!e) {
1094		syslog(LOG_ERR, "%s: calloc(%d) error", "addnatrule",
1095		       (int)sizeof(struct ipt_entry));
1096		return -1;
1097	}
1098	e->ip.proto = proto;
1099	if(proto == IPPROTO_TCP) {
1100		match = get_tcp_match(eport, 0);
1101	} else {
1102		match = get_udp_match(eport, 0);
1103	}
1104	e->nfcache = NFC_IP_DST_PT;
1105	target = get_dnat_target(iaddr, iport);
1106	e->nfcache |= NFC_UNKNOWN;
1107	tmp = realloc(e, sizeof(struct ipt_entry)
1108	               + match->u.match_size
1109				   + target->u.target_size);
1110	if(!tmp) {
1111		syslog(LOG_ERR, "%s: realloc(%d) error", "addnatrule",
1112		       (int)(sizeof(struct ipt_entry) + match->u.match_size + target->u.target_size));
1113		free(e);
1114		free(match);
1115		free(target);
1116		return -1;
1117	}
1118	e = tmp;
1119	memcpy(e->elems, match, match->u.match_size);
1120	memcpy(e->elems + match->u.match_size, target, target->u.target_size);
1121	e->target_offset = sizeof(struct ipt_entry)
1122	                   + match->u.match_size;
1123	e->next_offset = sizeof(struct ipt_entry)
1124	                 + match->u.match_size
1125					 + target->u.target_size;
1126	/* remote host */
1127	if(rhost && (rhost[0] != '\0') && (0 != strcmp(rhost, "*"))) {
1128		e->ip.src.s_addr = inet_addr(rhost);
1129		e->ip.smsk.s_addr = INADDR_NONE;
1130	}
1131
1132	r = iptc_init_verify_and_append("nat", miniupnpd_nat_chain, e, "addnatrule()");
1133	free(target);
1134	free(match);
1135	free(e);
1136	return r;
1137}
1138
1139/* iptables -t nat -A MINIUPNPD-PCP-PEER -s iaddr -d rhost
1140 *    -p proto --sport iport --dport rport -j SNAT
1141 *    --to-source ext_ip:eport */
1142static int
1143addpeernatrule(int proto,
1144           const char * eaddr, unsigned short eport,
1145           const char * iaddr, unsigned short iport,
1146           const char * rhost, unsigned short rport)
1147{
1148	int r = 0;
1149	struct ipt_entry * e;
1150	struct ipt_entry * tmp;
1151	struct ipt_entry_match *match = NULL;
1152	struct ipt_entry_target *target = NULL;
1153
1154	e = calloc(1, sizeof(struct ipt_entry));
1155	if(!e) {
1156		syslog(LOG_ERR, "%s: calloc(%d) error", "addpeernatrule",
1157		       (int)sizeof(struct ipt_entry));
1158		return -1;
1159	}
1160	e->ip.proto = proto;
1161	/* TODO: Fill port matches and SNAT */
1162	if(proto == IPPROTO_TCP) {
1163		match = get_tcp_match(rport, iport);
1164	} else {
1165		match = get_udp_match(rport, iport);
1166	}
1167	e->nfcache = NFC_IP_DST_PT | NFC_IP_SRC_PT;
1168	target = get_snat_target(eaddr, eport);
1169	e->nfcache |= NFC_UNKNOWN;
1170	tmp = realloc(e, sizeof(struct ipt_entry)
1171	               + match->u.match_size
1172				   + target->u.target_size);
1173	if(!tmp) {
1174		syslog(LOG_ERR, "%s: realloc(%d) error", "addpeernatrule",
1175		       (int)(sizeof(struct ipt_entry) + match->u.match_size + target->u.target_size));
1176		free(e);
1177		free(match);
1178		free(target);
1179		return -1;
1180	}
1181	e = tmp;
1182	memcpy(e->elems, match, match->u.match_size);
1183	memcpy(e->elems + match->u.match_size, target, target->u.target_size);
1184	e->target_offset = sizeof(struct ipt_entry)
1185	                   + match->u.match_size;
1186	e->next_offset = sizeof(struct ipt_entry)
1187	                 + match->u.match_size
1188					 + target->u.target_size;
1189
1190	/* internal host */
1191	if(iaddr && (iaddr[0] != '\0') && (0 != strcmp(iaddr, "*")))
1192	{
1193		e->ip.src.s_addr = inet_addr(iaddr);
1194		e->ip.smsk.s_addr = INADDR_NONE;
1195	}
1196	/* remote host */
1197	if(rhost && (rhost[0] != '\0') && (0 != strcmp(rhost, "*")))
1198	{
1199		e->ip.dst.s_addr = inet_addr(rhost);
1200		e->ip.dmsk.s_addr = INADDR_NONE;
1201	}
1202
1203	r = iptc_init_verify_and_append("nat", miniupnpd_peer_chain, e, "addpeernatrule()");
1204	free(target);
1205	free(match);
1206	free(e);
1207	return r;
1208}
1209
1210/* iptables -t mangle -A MINIUPNPD -s iaddr -d rhost
1211 *    -p proto --sport iport --dport rport -j DSCP
1212 *    --set-dscp 0xXXXX                   */
1213static int
1214addpeerdscprule(int proto, unsigned char dscp,
1215           const char * iaddr, unsigned short iport,
1216           const char * rhost, unsigned short rport)
1217{
1218	int r = 0;
1219	struct ipt_entry * e;
1220	struct ipt_entry * tmp;
1221	struct ipt_entry_match *match = NULL;
1222	struct ipt_entry_target *target = NULL;
1223
1224	e = calloc(1, sizeof(struct ipt_entry));
1225	if(!e) {
1226		syslog(LOG_ERR, "%s: calloc(%d) error", "addpeerdscprule",
1227		       (int)sizeof(struct ipt_entry));
1228		return -1;
1229	}
1230	e->ip.proto = proto;
1231	/* TODO: Fill port matches and SNAT */
1232	if(proto == IPPROTO_TCP) {
1233		match = get_tcp_match(rport, iport);
1234	} else {
1235		match = get_udp_match(rport, iport);
1236	}
1237	e->nfcache = NFC_IP_DST_PT | NFC_IP_SRC_PT;
1238	target = get_dscp_target(dscp);
1239	e->nfcache |= NFC_UNKNOWN;
1240	tmp = realloc(e, sizeof(struct ipt_entry)
1241	               + match->u.match_size
1242				   + target->u.target_size);
1243	if(!tmp) {
1244		syslog(LOG_ERR, "%s: realloc(%d) error", "addpeerdscprule",
1245		       (int)(sizeof(struct ipt_entry) + match->u.match_size + target->u.target_size));
1246		free(e);
1247		free(match);
1248		free(target);
1249		return -1;
1250	}
1251	e = tmp;
1252	memcpy(e->elems, match, match->u.match_size);
1253	memcpy(e->elems + match->u.match_size, target, target->u.target_size);
1254	e->target_offset = sizeof(struct ipt_entry)
1255	                   + match->u.match_size;
1256	e->next_offset = sizeof(struct ipt_entry)
1257	                 + match->u.match_size
1258					 + target->u.target_size;
1259
1260	/* internal host */
1261	if(iaddr && (iaddr[0] != '\0') && (0 != strcmp(iaddr, "*")))
1262	{
1263		e->ip.src.s_addr = inet_addr(iaddr);
1264		e->ip.smsk.s_addr = INADDR_NONE;
1265	}
1266	/* remote host */
1267	if(rhost && (rhost[0] != '\0') && (0 != strcmp(rhost, "*")))
1268	{
1269		e->ip.dst.s_addr = inet_addr(rhost);
1270		e->ip.dmsk.s_addr = INADDR_NONE;
1271	}
1272
1273	r = iptc_init_verify_and_append("mangle", miniupnpd_nat_chain, e,
1274	                                "addpeerDSCPrule()");
1275	free(target);
1276	free(match);
1277	free(e);
1278	return r;
1279}
1280
1281
1282/* ================================= */
1283static struct ipt_entry_target *
1284get_accept_target(void)
1285{
1286	struct ipt_entry_target * target = NULL;
1287	size_t size;
1288	size =   IPT_ALIGN(sizeof(struct ipt_entry_target))
1289	       + IPT_ALIGN(sizeof(int));
1290	target = calloc(1, size);
1291	target->u.user.target_size = size;
1292	strncpy(target->u.user.name, "ACCEPT", sizeof(target->u.user.name));
1293	return target;
1294}
1295
1296/* add_filter_rule()
1297 * */
1298static int
1299add_filter_rule(int proto, const char * rhost,
1300                const char * iaddr, unsigned short iport)
1301{
1302	int r = 0;
1303	struct ipt_entry * e;
1304	struct ipt_entry * tmp;
1305	struct ipt_entry_match *match = NULL;
1306	struct ipt_entry_target *target = NULL;
1307
1308	e = calloc(1, sizeof(struct ipt_entry));
1309	if(!e) {
1310		syslog(LOG_ERR, "%s: calloc(%d) error", "add_filter_rule",
1311		       (int)sizeof(struct ipt_entry));
1312		return -1;
1313	}
1314	e->ip.proto = proto;
1315	if(proto == IPPROTO_TCP) {
1316		match = get_tcp_match(iport,0);
1317	} else {
1318		match = get_udp_match(iport,0);
1319	}
1320	e->nfcache = NFC_IP_DST_PT;
1321	e->ip.dst.s_addr = inet_addr(iaddr);
1322	e->ip.dmsk.s_addr = INADDR_NONE;
1323	target = get_accept_target();
1324	e->nfcache |= NFC_UNKNOWN;
1325	tmp = realloc(e, sizeof(struct ipt_entry)
1326	               + match->u.match_size
1327				   + target->u.target_size);
1328	if(!tmp) {
1329		syslog(LOG_ERR, "%s: realloc(%d) error", "add_filter_rule",
1330		       (int)(sizeof(struct ipt_entry) + match->u.match_size + target->u.target_size));
1331		free(e);
1332		free(match);
1333		free(target);
1334		return -1;
1335	}
1336	e = tmp;
1337	memcpy(e->elems, match, match->u.match_size);
1338	memcpy(e->elems + match->u.match_size, target, target->u.target_size);
1339	e->target_offset = sizeof(struct ipt_entry)
1340	                   + match->u.match_size;
1341	e->next_offset = sizeof(struct ipt_entry)
1342	                 + match->u.match_size
1343					 + target->u.target_size;
1344	/* remote host */
1345	if(rhost && (rhost[0] != '\0') && (0 != strcmp(rhost, "*")))
1346	{
1347		e->ip.src.s_addr = inet_addr(rhost);
1348		e->ip.smsk.s_addr = INADDR_NONE;
1349	}
1350
1351	r = iptc_init_verify_and_append("filter", miniupnpd_forward_chain, e, "add_filter_rule()");
1352	free(target);
1353	free(match);
1354	free(e);
1355	return r;
1356}
1357
1358/* return an (malloc'ed) array of "external" port for which there is
1359 * a port mapping. number is the size of the array */
1360unsigned short *
1361get_portmappings_in_range(unsigned short startport, unsigned short endport,
1362                          int proto, unsigned int * number)
1363{
1364	unsigned short * array;
1365	unsigned int capacity;
1366	unsigned short eport;
1367	IPTC_HANDLE h;
1368	const struct ipt_entry * e;
1369	const struct ipt_entry_match *match;
1370
1371	*number = 0;
1372	capacity = 128;
1373	array = calloc(capacity, sizeof(unsigned short));
1374	if(!array)
1375	{
1376		syslog(LOG_ERR, "get_portmappings_in_range() : calloc error");
1377		return NULL;
1378	}
1379
1380	h = iptc_init("nat");
1381	if(!h)
1382	{
1383		syslog(LOG_ERR, "get_redirect_rule_by_index() : "
1384		                "iptc_init() failed : %s",
1385		       iptc_strerror(errno));
1386		free(array);
1387		return NULL;
1388	}
1389	if(!iptc_is_chain(miniupnpd_nat_chain, h))
1390	{
1391		syslog(LOG_ERR, "chain %s not found", miniupnpd_nat_chain);
1392		free(array);
1393		array = NULL;
1394	}
1395	else
1396	{
1397#ifdef IPTABLES_143
1398		for(e = iptc_first_rule(miniupnpd_nat_chain, h);
1399		    e;
1400			e = iptc_next_rule(e, h))
1401#else
1402		for(e = iptc_first_rule(miniupnpd_nat_chain, &h);
1403		    e;
1404			e = iptc_next_rule(e, &h))
1405#endif
1406		{
1407			if(proto == e->ip.proto)
1408			{
1409				match = (const struct ipt_entry_match *)&e->elems;
1410				if(0 == strncmp(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN))
1411				{
1412					const struct ipt_tcp * info;
1413					info = (const struct ipt_tcp *)match->data;
1414					eport = info->dpts[0];
1415				}
1416				else
1417				{
1418					const struct ipt_udp * info;
1419					info = (const struct ipt_udp *)match->data;
1420					eport = info->dpts[0];
1421				}
1422				if(startport <= eport && eport <= endport)
1423				{
1424					if(*number >= capacity)
1425					{
1426						unsigned short * tmp;
1427						/* need to increase the capacity of the array */
1428						tmp = realloc(array, sizeof(unsigned short)*capacity);
1429						if(!tmp)
1430						{
1431							syslog(LOG_ERR, "get_portmappings_in_range() : realloc(%u) error",
1432							       (unsigned)sizeof(unsigned short)*capacity);
1433							*number = 0;
1434							free(array);
1435							array = NULL;
1436							break;
1437						}
1438						array = tmp;
1439					}
1440					array[*number] = eport;
1441					(*number)++;
1442				}
1443			}
1444		}
1445	}
1446	if(h)
1447#ifdef IPTABLES_143
1448		iptc_free(h);
1449#else
1450		iptc_free(&h);
1451#endif
1452	return array;
1453}
1454
1455/* ================================ */
1456#ifdef DEBUG
1457static int
1458print_match(const struct ipt_entry_match *match)
1459{
1460	printf("match %s\n", match->u.user.name);
1461	if(0 == strncmp(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN))
1462	{
1463		struct ipt_tcp * tcpinfo;
1464		tcpinfo = (struct ipt_tcp *)match->data;
1465		printf("srcport = %hu:%hu dstport = %hu:%hu\n",
1466		       tcpinfo->spts[0], tcpinfo->spts[1],
1467			   tcpinfo->dpts[0], tcpinfo->dpts[1]);
1468	}
1469	else if(0 == strncmp(match->u.user.name, "udp", IPT_FUNCTION_MAXNAMELEN))
1470	{
1471		struct ipt_udp * udpinfo;
1472		udpinfo = (struct ipt_udp *)match->data;
1473		printf("srcport = %hu:%hu dstport = %hu:%hu\n",
1474		       udpinfo->spts[0], udpinfo->spts[1],
1475			   udpinfo->dpts[0], udpinfo->dpts[1]);
1476	}
1477	return 0;
1478}
1479
1480static void
1481print_iface(const char * iface, const unsigned char * mask, int invert)
1482{
1483	unsigned i;
1484	if(mask[0] == 0)
1485		return;
1486	if(invert)
1487		printf("! ");
1488	for(i=0; i<IFNAMSIZ; i++)
1489	{
1490		if(mask[i])
1491		{
1492			if(iface[i])
1493				putchar(iface[i]);
1494		}
1495		else
1496		{
1497			if(iface[i-1])
1498				putchar('+');
1499			break;
1500		}
1501	}
1502}
1503
1504static void
1505printip(uint32_t ip)
1506{
1507	printf("%u.%u.%u.%u", ip >> 24, (ip >> 16) & 0xff,
1508	       (ip >> 8) & 0xff, ip & 0xff);
1509}
1510
1511/* for debug */
1512/* read the "filter" and "nat" tables */
1513int
1514list_redirect_rule(const char * ifname)
1515{
1516	IPTC_HANDLE h;
1517	const struct ipt_entry * e;
1518	const struct ipt_entry_target * target;
1519	const struct ip_nat_multi_range * mr;
1520	const char * target_str;
1521	char addr[16], mask[16];
1522	(void)ifname;
1523
1524	h = iptc_init("nat");
1525	if(!h)
1526	{
1527		printf("iptc_init() error : %s\n", iptc_strerror(errno));
1528		return -1;
1529	}
1530	if(!iptc_is_chain(miniupnpd_nat_chain, h))
1531	{
1532		printf("chain %s not found\n", miniupnpd_nat_chain);
1533#ifdef IPTABLES_143
1534		iptc_free(h);
1535#else
1536		iptc_free(&h);
1537#endif
1538		return -1;
1539	}
1540#ifdef IPTABLES_143
1541	for(e = iptc_first_rule(miniupnpd_nat_chain, h);
1542		e;
1543		e = iptc_next_rule(e, h))
1544	{
1545		target_str = iptc_get_target(e, h);
1546#else
1547	for(e = iptc_first_rule(miniupnpd_nat_chain, &h);
1548		e;
1549		e = iptc_next_rule(e, &h))
1550	{
1551		target_str = iptc_get_target(e, &h);
1552#endif
1553		printf("===\n");
1554		inet_ntop(AF_INET, &e->ip.src, addr, sizeof(addr));
1555		inet_ntop(AF_INET, &e->ip.smsk, mask, sizeof(mask));
1556		printf("src = %s%s/%s\n", (e->ip.invflags & IPT_INV_SRCIP)?"! ":"",
1557		       /*inet_ntoa(e->ip.src), inet_ntoa(e->ip.smsk)*/
1558		       addr, mask);
1559		inet_ntop(AF_INET, &e->ip.dst, addr, sizeof(addr));
1560		inet_ntop(AF_INET, &e->ip.dmsk, mask, sizeof(mask));
1561		printf("dst = %s%s/%s\n", (e->ip.invflags & IPT_INV_DSTIP)?"! ":"",
1562		       /*inet_ntoa(e->ip.dst), inet_ntoa(e->ip.dmsk)*/
1563		       addr, mask);
1564		/*printf("in_if = %s  out_if = %s\n", e->ip.iniface, e->ip.outiface);*/
1565		printf("in_if = ");
1566		print_iface(e->ip.iniface, e->ip.iniface_mask,
1567		            e->ip.invflags & IPT_INV_VIA_IN);
1568		printf(" out_if = ");
1569		print_iface(e->ip.outiface, e->ip.outiface_mask,
1570		            e->ip.invflags & IPT_INV_VIA_OUT);
1571		printf("\n");
1572		printf("ip.proto = %s%d\n", (e->ip.invflags & IPT_INV_PROTO)?"! ":"",
1573		       e->ip.proto);
1574		/* display matches stuff */
1575		if(e->target_offset)
1576		{
1577			IPT_MATCH_ITERATE(e, print_match);
1578			/*printf("\n");*/
1579		}
1580		printf("target = %s\n", target_str);
1581		target = (void *)e + e->target_offset;
1582		mr = (const struct ip_nat_multi_range *)&target->data[0];
1583		printf("ips ");
1584		printip(ntohl(mr->range[0].min_ip));
1585		printf(" ");
1586		printip(ntohl(mr->range[0].max_ip));
1587		printf("\nports %hu %hu\n", ntohs(mr->range[0].min.all),
1588		          ntohs(mr->range[0].max.all));
1589		printf("flags = %x\n", mr->range[0].flags);
1590	}
1591	if(h)
1592#ifdef IPTABLES_143
1593		iptc_free(h);
1594#else
1595		iptc_free(&h);
1596#endif
1597	return 0;
1598}
1599#endif
1600