1 /* $Id: nftnlrdr.c,v 1.10 2020/11/11 12:08:43 nanard Exp $
2  * vim: tabstop=4 shiftwidth=4 noexpandtab
3  * MiniUPnP project
4  * http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
5  * (c) 2015 Tomofumi Hayashi
6  * (c) 2019 Sven Auhagen
7  * (c) 2019 Paul Chambers
8  * (c) 2020 Thomas Bernard
9  *
10  * This software is subject to the conditions detailed
11  * in the LICENCE file provided within the distribution.
12  */
13 #include <stdio.h>
14 #include <stddef.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <syslog.h>
18 #include <errno.h>
19 #include <sys/socket.h>
20 #include <sys/types.h>
21 #include <netinet/in.h>
22 #include <netinet/ip.h>
23 #include <netinet/tcp.h>
24 #include <arpa/inet.h>
25 #include <dlfcn.h>
26 #include <net/if.h>
27 
28 #include <linux/version.h>
29 
30 #include <linux/netfilter.h>
31 #include <linux/netfilter_ipv4.h>
32 #include <linux/netfilter/nfnetlink.h>
33 #include <linux/netfilter/nf_tables.h>
34 
35 #include <libmnl/libmnl.h>
36 #include <libnftnl/table.h>
37 #include <libnftnl/chain.h>
38 #include <libnftnl/rule.h>
39 #include <libnftnl/expr.h>
40 
41 #include "tiny_nf_nat.h"
42 
43 #include "config.h"
44 #include "../macros.h"
45 #include "../commonrdr.h"
46 #include "nftnlrdr.h"
47 
48 #include "nftnlrdr_misc.h"
49 
50 #ifdef DEBUG
51 #define d_printf(x) do { printf x; } while (0)
52 #else
53 #define d_printf(x)
54 #endif
55 
56 /* list to keep timestamps for port mappings having a lease duration */
57 struct timestamp_entry {
58 	struct timestamp_entry * next;
59 	unsigned int timestamp;
60 	unsigned short eport;
61 	short protocol;
62 };
63 
64 static struct timestamp_entry * timestamp_list = NULL;
65 
66 #define NAT_CHAIN_TYPE		"nat"
67 #define FILTER_CHAIN_TYPE	"filter"
68 
69 /* init and shutdown functions */
70 int
init_redirect(void)71 init_redirect(void)
72 {
73 	int result;
74 
75 	/* requires elevated privileges */
76 	result = nft_mnl_connect();
77 
78 	/* 'inet' family */
79 	if (result == 0) {
80 		result = table_op(NFT_MSG_NEWTABLE, NFPROTO_INET, nft_table);
81 	}
82 	if (result == 0) {
83 		result = chain_op(NFT_MSG_NEWCHAIN, NFPROTO_INET, nft_table,
84 						  nft_forward_chain, FILTER_CHAIN_TYPE, NF_INET_FORWARD, NF_IP_PRI_FILTER - 25);
85 	}
86 
87 	/* 'ip' family */
88 	if (result == 0) {
89 		result = table_op(NFT_MSG_NEWTABLE, NFPROTO_IPV4, nft_table);
90 	}
91 	if (result == 0) {
92 		result = chain_op(NFT_MSG_NEWCHAIN, NFPROTO_IPV4, nft_table,
93 						  nft_prerouting_chain, NAT_CHAIN_TYPE, NF_INET_PRE_ROUTING, NF_IP_PRI_NAT_DST);
94 	}
95 	if (result == 0) {
96 		result = chain_op(NFT_MSG_NEWCHAIN, NFPROTO_IPV4, nft_table,
97 						  nft_postrouting_chain, NAT_CHAIN_TYPE, NF_INET_POST_ROUTING, NF_IP_PRI_NAT_SRC);
98 	}
99 
100 	/* 'ip6' family */
101 	if (result == 0) {
102 		result = table_op(NFT_MSG_NEWTABLE, NFPROTO_IPV6, nft_table);
103 	}
104 	if (result == 0) {
105 		result = chain_op(NFT_MSG_NEWCHAIN, NFPROTO_IPV6, nft_table,
106 						  nft_prerouting_chain, NAT_CHAIN_TYPE, NF_INET_PRE_ROUTING, NF_IP_PRI_NAT_DST);
107 	}
108 	if (result == 0) {
109 		result = chain_op(NFT_MSG_NEWCHAIN, NFPROTO_IPV6, nft_table,
110 						  nft_postrouting_chain, NAT_CHAIN_TYPE, NF_INET_POST_ROUTING, NF_IP_PRI_NAT_SRC);
111 	}
112 
113 	return result;
114 }
115 
116 void
shutdown_redirect(void)117 shutdown_redirect(void)
118 {
119 	int result;
120 
121 	/* 'inet' family */
122 	result = chain_op(NFT_MSG_DELCHAIN, NFPROTO_INET, nft_table,
123 					  nft_forward_chain, FILTER_CHAIN_TYPE, NF_INET_FORWARD, NF_IP_PRI_FILTER - 25);
124 	if (result == 0) {
125 		result = table_op(NFT_MSG_DELTABLE, NFPROTO_INET, nft_table);
126 	}
127 
128 	/* 'ip' family */
129 	result = chain_op(NFT_MSG_DELCHAIN, NFPROTO_IPV4, nft_table,
130 					  nft_prerouting_chain, NAT_CHAIN_TYPE, NF_INET_PRE_ROUTING, NF_IP_PRI_NAT_DST);
131 	if (result == 0) {
132 		result = chain_op(NFT_MSG_DELCHAIN, NFPROTO_IPV4, nft_table,
133 						  nft_postrouting_chain, NAT_CHAIN_TYPE, NF_INET_POST_ROUTING, NF_IP_PRI_NAT_SRC);
134 	}
135 	if (result == 0) {
136 		result = table_op(NFT_MSG_DELTABLE, NFPROTO_IPV4, nft_table);
137 	}
138 
139 	/* 'ip6' family */
140 	if (result == 0) {
141 		result = chain_op(NFT_MSG_DELCHAIN, NFPROTO_IPV6, nft_table,
142 						  nft_prerouting_chain, NAT_CHAIN_TYPE, NF_INET_PRE_ROUTING, NF_IP_PRI_NAT_DST);
143 	}
144 	if (result == 0) {
145 		result = chain_op(NFT_MSG_DELCHAIN, NFPROTO_IPV6, nft_table,
146 						  nft_postrouting_chain, NAT_CHAIN_TYPE, NF_INET_POST_ROUTING, NF_IP_PRI_NAT_SRC);
147 	}
148 	if (result == 0) {
149 		result = table_op(NFT_MSG_DELTABLE, NFPROTO_IPV6, nft_table);
150 	}
151 
152 	nft_mnl_disconnect();
153 }
154 
155 /**
156  * used by the core to override default chain names if specified in config file
157  * @param param which string to set
158  * @param string the new name to use. Do not dispose after setting (i.e. use strdup if not static).
159  * @return 0 if successful
160  */
161 int
set_rdr_name(rdr_name_type param,const char * string)162 set_rdr_name(rdr_name_type param, const char *string)
163 {
164 	if (string == NULL || strlen(string) > 30 || string[0] == '\0') {
165 		syslog(LOG_ERR, "%s(): invalid string argument '%s'", "set_rdr_name", string);
166 		return -1;
167 	}
168 	switch (param) {
169 	case RDR_TABLE_NAME:
170 		nft_table = string;
171 		break;
172 	case RDR_NAT_PREROUTING_CHAIN_NAME:
173 		nft_prerouting_chain = string;
174 		break;
175 	case RDR_NAT_POSTROUTING_CHAIN_NAME:
176 		nft_postrouting_chain = string;
177 		break;
178 	case RDR_FORWARD_CHAIN_NAME:
179 		nft_forward_chain = string;
180 		break;
181 	default:
182 		syslog(LOG_ERR, "%s(): tried to set invalid string parameter: %d", "set_rdr_name", param);
183 		return -2;
184 	}
185 	return 0;
186 }
187 
188 static unsigned int
get_timestamp(unsigned short eport,int proto)189 get_timestamp(unsigned short eport, int proto)
190 {
191 	struct timestamp_entry * e;
192 	e = timestamp_list;
193 	while(e) {
194 		if(e->eport == eport && e->protocol == (short)proto) {
195 			syslog(LOG_DEBUG, "timestamp entry found (%hu, %d, %u)", eport, proto, e->timestamp);
196 			return e->timestamp;
197 		}
198 		e = e->next;
199 	}
200 	syslog(LOG_WARNING, "get_timestamp(%hu, %d) no entry found", eport, proto);
201 	return 0;
202 }
203 
204 static void
remove_timestamp_entry(unsigned short eport,int proto)205 remove_timestamp_entry(unsigned short eport, int proto)
206 {
207 	struct timestamp_entry * e;
208 	struct timestamp_entry * * p;
209 	p = &timestamp_list;
210 	e = *p;
211 	while(e) {
212 		if(e->eport == eport && e->protocol == (short)proto) {
213 			syslog(LOG_DEBUG, "timestamp entry removed (%hu, %d, %u)", eport, proto, e->timestamp);
214 			/* remove the entry */
215 			*p = e->next;
216 			free(e);
217 			return;
218 		}
219 		p = &(e->next);
220 		e = *p;
221 	}
222 	syslog(LOG_WARNING, "remove_timestamp_entry(%hu, %d) no entry found", eport, proto);
223 }
224 
225 static void
add_timestamp_entry(unsigned short eport,int proto,unsigned timestamp)226 add_timestamp_entry(unsigned short eport, int proto, unsigned timestamp)
227 {
228 	struct timestamp_entry * tmp;
229 	tmp = malloc(sizeof(struct timestamp_entry));
230 	if(tmp)
231 	{
232 		tmp->next = timestamp_list;
233 		tmp->timestamp = timestamp;
234 		tmp->eport = eport;
235 		tmp->protocol = (short)proto;
236 		timestamp_list = tmp;
237 		syslog(LOG_DEBUG, "timestamp entry added (%hu, %d, %u)", eport, proto, timestamp);
238 	}
239 	else
240 	{
241 		syslog(LOG_ERR, "add_timestamp_entry() malloc(%lu) error",
242 		       sizeof(struct timestamp_entry));
243 	}
244 }
245 
246 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)247 add_redirect_rule2(const char * ifname,
248 		   const char * rhost, unsigned short eport,
249 		   const char * iaddr, unsigned short iport, int proto,
250 		   const char * desc, unsigned int timestamp)
251 {
252 	int ret;
253 	struct nftnl_rule *r;
254 	UNUSED(rhost);
255 
256 	d_printf(("add redirect rule2(%s, %s, %u, %s, %u, %d, %s)!\n",
257 	          ifname, rhost, eport, iaddr, iport, proto, desc));
258 
259 	r = rule_set_dnat(NFPROTO_IPV4, ifname, proto,
260 	                  0, eport,
261 	                  inet_addr(iaddr), iport,  desc, NULL);
262 
263 	ret = nft_send_rule(r, NFT_MSG_NEWRULE, RULE_CHAIN_REDIRECT);
264 	if (ret >= 0) {
265 		add_timestamp_entry(eport, proto, timestamp);
266 	}
267 	return ret;
268 }
269 
270 /*
271  * This function submit the rule as following:
272  * nft add rule nat miniupnpd-pcp-peer ip
273  *    saddr <iaddr> ip daddr <rhost> tcp sport <iport>
274  *    tcp dport <rport> snat <eaddr>:<eport>
275  */
276 int
add_peer_redirect_rule2(const char * ifname,const char * rhost,unsigned short rport,const char * eaddr,unsigned short eport,const char * iaddr,unsigned short iport,int proto,const char * desc,unsigned int timestamp)277 add_peer_redirect_rule2(const char * ifname,
278 			const char * rhost, unsigned short rport,
279 			const char * eaddr, unsigned short eport,
280 			const char * iaddr, unsigned short iport, int proto,
281 			const char * desc, unsigned int timestamp)
282 {
283 	struct nftnl_rule *r;
284 	UNUSED(ifname); UNUSED(timestamp);
285 
286 	d_printf(("add peer redirect rule2()!\n"));
287 
288 	r = rule_set_snat(NFPROTO_IPV4, proto,
289 	                  inet_addr(rhost), rport,
290 	                  inet_addr(eaddr), eport,
291 	                  inet_addr(iaddr), iport, desc, NULL);
292 
293 	return nft_send_rule(r, NFT_MSG_NEWRULE, RULE_CHAIN_PEER);
294 }
295 
296 /*
297  * This function submit the rule as following:
298  * nft add rule filter miniupnpd
299  *    ip daddr <iaddr> tcp dport <iport> accept
300  *
301  */
302 int
add_filter_rule2(const char * ifname,const char * rhost,const char * iaddr,unsigned short eport,unsigned short iport,int proto,const char * desc)303 add_filter_rule2(const char * ifname,
304 		 const char * rhost, const char * iaddr,
305 		 unsigned short eport, unsigned short iport,
306 		 int proto, const char * desc)
307 {
308 	struct nftnl_rule *r = NULL;
309 	in_addr_t rhost_addr = 0;
310 
311 	d_printf(("add_filter_rule2(%s, %s, %s, %d, %d, %d, %s)\n",
312 	          ifname, rhost, iaddr, eport, iport, proto, desc));
313 
314 	if (rhost != NULL && strcmp(rhost, "") != 0 && strcmp(rhost, "*") != 0) {
315 		rhost_addr = inet_addr(rhost);
316 	}
317 	r = rule_set_filter(NFPROTO_INET, ifname, proto,
318 	                    rhost_addr, inet_addr(iaddr),
319 	                    eport, iport, 0,
320 	                    desc, 0);
321 
322 	return nft_send_rule(r, NFT_MSG_NEWRULE, RULE_CHAIN_FILTER);
323 }
324 
325 /*
326  * add_peer_dscp_rule2() is not supported due to nft does not support
327  * dscp set.
328  */
329 int
add_peer_dscp_rule2(const char * ifname,const char * rhost,unsigned short rport,unsigned char dscp,const char * iaddr,unsigned short iport,int proto,const char * desc,unsigned int timestamp)330 add_peer_dscp_rule2(const char * ifname,
331 		    const char * rhost, unsigned short rport,
332 		    unsigned char dscp,
333 		    const char * iaddr, unsigned short iport, int proto,
334 		    const char * desc, unsigned int timestamp)
335 {
336 	UNUSED(ifname); UNUSED(rhost); UNUSED(rport);
337 	UNUSED(dscp); UNUSED(iaddr); UNUSED(iport); UNUSED(proto);
338 	UNUSED(desc); UNUSED(timestamp);
339 	syslog(LOG_ERR, "add_peer_dscp_rule2: not supported");
340 	return 0;
341 }
342 
343 int
delete_filter_rule(const char * ifname,unsigned short port,int proto)344 delete_filter_rule(const char * ifname, unsigned short port, int proto)
345 {
346 	rule_t *p;
347 	struct nftnl_rule *r;
348 	UNUSED(ifname);
349 
350 	refresh_nft_cache_filter();
351 	LIST_FOREACH(p, &head_filter, entry) {
352 		if (p->eport == port && p->proto == proto && p->type == RULE_FILTER) {
353 			r = rule_del_handle(p);
354 			nft_send_rule(r, NFT_MSG_DELRULE, RULE_CHAIN_FILTER);
355 			break;
356 		}
357 	}
358 
359 	return 0;
360 }
361 
362 /*
363  * Clear all rules corresponding eport/proto
364  */
365 int
delete_redirect_and_filter_rules(unsigned short eport,int proto)366 delete_redirect_and_filter_rules(unsigned short eport, int proto)
367 {
368 	rule_t *p;
369 	struct nftnl_rule *r = NULL;
370 	in_addr_t iaddr = 0;
371 	uint16_t iport = 0;
372 
373 	d_printf(("delete_redirect_and_filter_rules(%d %d)\n", eport, proto));
374 	refresh_nft_cache_redirect();
375 
376 	// Delete Redirect Rule
377 	LIST_FOREACH(p, &head_redirect, entry) {
378 		if (p->eport == eport && p->proto == proto &&
379 		    (p->type == RULE_NAT && p->nat_type == NFT_NAT_DNAT)) {
380 			iaddr = p->iaddr;
381 			iport = p->iport;
382 
383 			r = rule_del_handle(p);
384 			/* Todo: send bulk request */
385 			nft_send_rule(r, NFT_MSG_DELRULE, RULE_CHAIN_REDIRECT);
386 			break;
387 		}
388 	}
389 
390 	if (iaddr != 0 && iport != 0) {
391 		refresh_nft_cache_filter();
392 		// Delete Forward Rule
393 		LIST_FOREACH(p, &head_filter, entry) {
394 			if (p->eport == iport &&
395 				p->iaddr == iaddr && p->type == RULE_FILTER) {
396 				r = rule_del_handle(p);
397 				/* Todo: send bulk request */
398 				nft_send_rule(r, NFT_MSG_DELRULE, RULE_CHAIN_FILTER);
399 				break;
400 			}
401 		}
402 	}
403 
404 	iaddr = 0;
405 	iport = 0;
406 
407 	refresh_nft_cache_peer();
408 	// Delete Peer Rule
409 	LIST_FOREACH(p, &head_peer, entry) {
410 		if (p->eport == eport && p->proto == proto &&
411 		    (p->type == RULE_NAT && p->nat_type == NFT_NAT_SNAT)) {
412 			iaddr = p->iaddr;
413 			iport = p->iport;
414 
415 			r = rule_del_handle(p);
416 			/* Todo: send bulk request */
417 			nft_send_rule(r, NFT_MSG_DELRULE, RULE_CHAIN_PEER);
418 			break;
419 		}
420 	}
421 
422 	if (iaddr != 0 && iport != 0) {
423 		refresh_nft_cache_filter();
424 		// Delete Forward Rule
425 		LIST_FOREACH(p, &head_filter, entry) {
426 			if (p->eport == iport &&
427 				p->iaddr == iaddr && p->type == RULE_FILTER) {
428 				r = rule_del_handle(p);
429 				/* Todo: send bulk request */
430 				nft_send_rule(r, NFT_MSG_DELRULE, RULE_CHAIN_FILTER);
431 				break;
432 			}
433 		}
434 	}
435 
436 	return 0;
437 }
438 
439 /*
440  * get peer by index as array.
441  * return -1 when not found.
442  */
443 int
get_peer_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 short * rport,unsigned int * timestamp,u_int64_t * packets,u_int64_t * bytes)444 get_peer_rule_by_index(int index,
445 		       char * ifname, unsigned short * eport,
446 		       char * iaddr, int iaddrlen, unsigned short * iport,
447 		       int * proto, char * desc, int desclen,
448 		       char * rhost, int rhostlen, unsigned short * rport,
449 		       unsigned int * timestamp,
450 		       u_int64_t * packets, u_int64_t * bytes)
451 {
452 	rule_t *r;
453 	int i = 0;
454 
455 	d_printf(("get_peer_rule_by_index()\n"));
456 	refresh_nft_cache_peer();
457 
458 	LIST_FOREACH(r, &head_peer, entry) {
459 		if (i++ == index) {
460 			if (ifname != NULL) {
461 				if_indextoname(r->ingress_ifidx, ifname);
462 			}
463 
464 			if (eport != NULL) {
465 				*eport = r->eport;
466 			}
467 
468 			if (iaddr != NULL) {
469 				if (inet_ntop(AF_INET, &r->iaddr, iaddr, iaddrlen) == NULL) {
470 					syslog(LOG_ERR, "%s: inet_ntop: %m",
471 					       "get_peer_rule_by_index");
472 				}
473 			}
474 
475 			if (iport != NULL) {
476 				*iport = r->iport;
477 			}
478 
479 			if (proto != NULL) {
480 				*proto = r->proto;
481 			}
482 
483 			if (rhost != NULL) {
484 				if (r->rhost) {
485 					if (inet_ntop(AF_INET, &r->rhost, rhost, rhostlen) == NULL) {
486 						syslog(LOG_ERR, "%s: inet_ntop: %m",
487 						       "get_peer_rule_by_index");
488 					}
489 				} else {
490 					rhost[0] = '\0';
491 				}
492 			}
493 
494 			if (rport != NULL) {
495 				*rport = r->rport;
496 			}
497 
498 			if (desc != NULL) {
499 				strncpy(desc, r->desc, desclen);
500 			}
501 
502 			if (packets) {
503 				*packets = r->packets;
504 			}
505 			if (bytes) {
506 				*bytes = r->bytes;
507 			}
508 
509 			if (timestamp) {
510 				*timestamp = get_timestamp(r->eport, r->proto);
511 			}
512 			/*
513 			 * TODO: Implement counter in case of add {nat,filter}
514 			 */
515 			return 0;
516 		}
517 	}
518 
519 	return -1;
520 }
521 
522 /*
523  * get_redirect_rule()
524  * returns -1 if the rule is not found
525  */
526 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)527 get_redirect_rule(const char * ifname, unsigned short eport, int proto,
528 		  char * iaddr, int iaddrlen, unsigned short * iport,
529 		  char * desc, int desclen,
530 		  char * rhost, int rhostlen,
531 		  unsigned int * timestamp,
532 		  u_int64_t * packets, u_int64_t * bytes)
533 {
534 	return get_nat_redirect_rule(nft_prerouting_chain,
535 	                             ifname, eport, proto,
536 	                             iaddr, iaddrlen, iport,
537 	                             desc, desclen,
538 	                             rhost, rhostlen,
539 	                             timestamp, packets, bytes);
540 }
541 
542 /* get_redirect_rule_count()
543  * return value : -1 for error or the number of redirection rules */
544 int
get_redirect_rule_count(const char * ifname)545 get_redirect_rule_count(const char * ifname)
546 {
547 	rule_t *r;
548 	int n = 0;
549 	UNUSED(ifname);
550 
551 	refresh_nft_cache_redirect();
552 	LIST_FOREACH(r, &head_redirect, entry) {
553 		n++;
554 	}
555 	return n;
556 }
557 
558 /*
559  * get_redirect_rule_by_index()
560  * return -1 when the rule was not found
561  */
562 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)563 get_redirect_rule_by_index(int index,
564 			   char * ifname, unsigned short * eport,
565 			   char * iaddr, int iaddrlen, unsigned short * iport,
566 			   int * proto, char * desc, int desclen,
567 			   char * rhost, int rhostlen,
568 			   unsigned int * timestamp,
569 			   u_int64_t * packets, u_int64_t * bytes)
570 {
571 	rule_t *r;
572 	int i = 0;
573 
574 	d_printf(("get_redirect_rule_by_index()\n"));
575 	refresh_nft_cache_redirect();
576 
577 	LIST_FOREACH(r, &head_redirect, entry) {
578 		if (i++ == index) {
579 			if (ifname != NULL) {
580 				if_indextoname(r->ingress_ifidx, ifname);
581 			}
582 
583 			if (eport != NULL) {
584 				*eport = r->eport;
585 			}
586 
587 			if (iaddr != NULL) {
588 				if (inet_ntop(AF_INET, &r->iaddr, iaddr, iaddrlen) == NULL) {
589 					syslog(LOG_ERR, "%s: inet_ntop: %m",
590 					       "get_redirect_rule_by_index");
591 				}
592 			}
593 
594 			if (iport != NULL) {
595 				*iport = r->iport;
596 			}
597 
598 			if (proto != NULL) {
599 				*proto = r->proto;
600 			}
601 
602 			if (rhost != NULL) {
603 				if (r->rhost) {
604 					if (inet_ntop(AF_INET, &r->rhost, rhost, rhostlen) == NULL) {
605 						syslog(LOG_ERR, "%s: inet_ntop: %m",
606 						       "get_redirect_rule_by_index");
607 					}
608 				} else {
609 					rhost[0] = '\0';
610 				}
611 			}
612 
613 			if (desc != NULL && r->desc) {
614 				strncpy(desc, r->desc, desclen);
615 			}
616 
617 			if (timestamp != NULL) {
618 				*timestamp = get_timestamp(*eport, *proto);
619 			}
620 
621 			if (packets || bytes) {
622 				if (packets)
623 					*packets = r->packets;
624 				if (bytes)
625 					*bytes = r->bytes;
626 			}
627 
628 			/*
629 			 * TODO: Implement counter in case of add {nat,filter}
630 			 */
631 			return 0;
632 		}
633 	}
634 
635 	return -1;
636 }
637 
638 /*
639  * return -1 not found.
640  * return 0 found
641  */
642 int
get_nat_redirect_rule(const char * nat_chain_name,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)643 get_nat_redirect_rule(const char * nat_chain_name, const char * ifname,
644 		      unsigned short eport, int proto,
645 		      char * iaddr, int iaddrlen, unsigned short * iport,
646 		      char * desc, int desclen,
647 		      char * rhost, int rhostlen,
648 		      unsigned int * timestamp,
649 		      u_int64_t * packets, u_int64_t * bytes)
650 {
651 	rule_t *p;
652 	struct in_addr addr;
653 	UNUSED(nat_chain_name);
654 	UNUSED(ifname);
655 	UNUSED(packets);
656 	UNUSED(bytes);
657 	UNUSED(rhost);
658 	UNUSED(rhostlen);
659 
660 	refresh_nft_cache_redirect();
661 
662 	LIST_FOREACH(p, &head_redirect, entry) {
663 		if (p->proto == proto &&
664 		    p->eport == eport) {
665 
666 			if (p->iaddr && iaddr) {
667 				addr.s_addr = p->iaddr;
668 				if (inet_ntop(AF_INET, &addr, iaddr, iaddrlen) == NULL) {
669 					syslog(LOG_ERR, "%s: inet_ntop: %m",
670 					       "get_nat_redirect_rule");
671 				}
672 			}
673 
674 			if (desc != NULL && p->desc) {
675 				strncpy(desc, p->desc, desclen);
676 			}
677 
678 			if (iport)
679 				*iport = p->iport;
680 
681 			if(timestamp != NULL)
682 				*timestamp = get_timestamp(eport, proto);
683 
684 			return 0;
685 		}
686 	}
687 
688 	return -1;
689 }
690 
691 /*
692  * return an (malloc'ed) array of "external" port for which there is
693  * a port mapping. number is the size of the array
694  */
695 unsigned short *
get_portmappings_in_range(unsigned short startport,unsigned short endport,int proto,unsigned int * number)696 get_portmappings_in_range(unsigned short startport, unsigned short endport,
697 			  int proto, unsigned int * number)
698 {
699 	uint32_t capacity;
700 	rule_t *p;
701 	unsigned short *array;
702 	unsigned short *tmp;
703 
704 	d_printf(("get_portmappings_in_range()\n"));
705 
706 	*number = 0;
707 	capacity = 128;
708 	array = calloc(capacity, sizeof(unsigned short));
709 
710 	if (array == NULL) {
711 		syslog(LOG_ERR, "get_portmappings_in_range(): calloc error");
712 		return NULL;
713 	}
714 
715 	refresh_nft_cache_redirect();
716 
717 	LIST_FOREACH(p, &head_redirect, entry) {
718 		if (p->proto == proto &&
719 		    startport <= p->eport &&
720 		    p->eport <= endport) {
721 
722 			if (*number >= capacity) {
723 				tmp = realloc(array,
724 					      sizeof(unsigned short)*capacity);
725 				if (tmp == NULL) {
726 					syslog(LOG_ERR,
727 					       "get_portmappings_in_range(): "
728 					       "realloc(%u) error",
729 					       (unsigned)sizeof(unsigned short)*capacity);
730 					*number = 0;
731 					free(array);
732 					return NULL;
733 				}
734 				array = tmp;
735 			}
736 			array[*number] = p->eport;
737 			(*number)++;
738 		}
739 	}
740 	return array;
741 }
742 
743 int
update_portmapping_desc_timestamp(const char * ifname,unsigned short eport,int proto,const char * desc,unsigned int timestamp)744 update_portmapping_desc_timestamp(const char * ifname,
745                    unsigned short eport, int proto,
746                    const char * desc, unsigned int timestamp)
747 {
748 	UNUSED(ifname);
749 	UNUSED(desc);
750 	remove_timestamp_entry(eport, proto);
751 	add_timestamp_entry(eport, proto, timestamp);
752 	return 0;
753 }
754 
755 int
update_portmapping(const char * ifname,unsigned short eport,int proto,unsigned short iport,const char * desc,unsigned int timestamp)756 update_portmapping(const char * ifname, unsigned short eport, int proto,
757                    unsigned short iport, const char * desc,
758                    unsigned int timestamp)
759 {
760 	char iaddr_str[INET_ADDRSTRLEN];
761 	char rhost[INET_ADDRSTRLEN];
762 	int r;
763 
764 	d_printf(("update_portmapping()\n"));
765 
766 	if (get_redirect_rule(NULL, eport, proto, iaddr_str, INET_ADDRSTRLEN, NULL, NULL, 0, rhost, INET_ADDRSTRLEN, NULL, 0, 0) < 0)
767 		return -1;
768 
769 	r = delete_redirect_and_filter_rules(eport, proto);
770 	if (r < 0)
771 		return -1;
772 
773 	if (add_redirect_rule2(ifname, rhost, eport, iaddr_str, iport, proto,
774 						  desc, timestamp) < 0)
775 		return -1;
776 
777 	if (add_filter_rule2(ifname, rhost, iaddr_str, eport, iport, proto, desc) < 0)
778 		return -1;
779 
780 	return 0;
781 }
782