1 /* $Id: iptpinhole.c,v 1.14 2015/02/10 15:01:03 nanard Exp $ */
3 * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
4 * (c) 2012-2015 Thomas Bernard
5 * This software is subject to the conditions detailed
6 * in the LICENCE file provided within the distribution */
12 #include <arpa/inet.h>
13 #include <sys/queue.h>
15 #include "../config.h"
16 #include "iptpinhole.h"
17 #include "../upnpglobalvars.h"
19 #ifdef ENABLE_UPNPPINHOLE
22 #include <libiptc/libip6tc.h>
23 #include "tiny_nf_nat.h"
25 #define IP6TC_HANDLE struct ip6tc_handle *
27 static int next_uid
= 1;
29 static LIST_HEAD(pinhole_list_t
, pinhole_t
) pinhole_list
;
31 static struct pinhole_t
*
32 get_pinhole(unsigned short uid
);
35 struct in6_addr saddr
;
36 struct in6_addr daddr
;
37 LIST_ENTRY(pinhole_t
) entries
;
38 unsigned int timestamp
;
46 void init_iptpinhole(void)
48 LIST_INIT(&pinhole_list
);
51 void shutdown_iptpinhole(void)
58 add_to_pinhole_list(struct in6_addr
* saddr
, unsigned short sport
,
59 struct in6_addr
* daddr
, unsigned short dport
,
60 int proto
, const char *desc
, unsigned int timestamp
)
64 p
= calloc(1, sizeof(struct pinhole_t
) + strlen(desc
) + 1);
66 syslog(LOG_ERR
, "add_to_pinhole_list calloc() error");
69 strcpy(p
->desc
, desc
);
70 memcpy(&p
->saddr
, saddr
, sizeof(struct in6_addr
));
72 memcpy(&p
->daddr
, daddr
, sizeof(struct in6_addr
));
74 p
->timestamp
= timestamp
;
75 p
->proto
= (unsigned char)proto
;
76 LIST_INSERT_HEAD(&pinhole_list
, p
, entries
);
77 while(get_pinhole(next_uid
) != NULL
) {
89 static struct pinhole_t
*
90 get_pinhole(unsigned short uid
)
94 for(p
= pinhole_list
.lh_first
; p
!= NULL
; p
= p
->entries
.le_next
) {
98 return NULL
; /* not found */
102 * Allocate and set a new ip6t_entry_match structure
103 * The caller must free() it after usage */
104 static struct ip6t_entry_match
*
105 new_match(int proto
, unsigned short sport
, unsigned short dport
)
107 struct ip6t_entry_match
*match
;
108 struct ip6t_tcp
*info
; /* TODO : use ip6t_udp if needed */
111 size
= XT_ALIGN(sizeof(struct ip6t_entry_match
))
112 + XT_ALIGN(sizeof(struct ip6t_tcp
));
113 match
= calloc(1, size
);
114 match
->u
.user
.match_size
= size
;
122 case IPPROTO_UDPLITE
:
129 strncpy(match
->u
.user
.name
, name
, sizeof(match
->u
.user
.name
));
131 syslog(LOG_WARNING
, "no name for protocol %d", proto
);
132 info
= (struct ip6t_tcp
*)match
->data
;
134 info
->spts
[0] = sport
; /* specified source port */
135 info
->spts
[1] = sport
;
137 info
->spts
[0] = 0; /* all source ports */
138 info
->spts
[1] = 0xFFFF;
140 info
->dpts
[0] = dport
; /* specified destination port */
141 info
->dpts
[1] = dport
;
145 static struct ip6t_entry_target
*
146 get_accept_target(void)
148 struct ip6t_entry_target
* target
= NULL
;
150 size
= XT_ALIGN(sizeof(struct ip6t_entry_target
))
151 + XT_ALIGN(sizeof(int));
152 target
= calloc(1, size
);
153 target
->u
.user
.target_size
= size
;
154 strncpy(target
->u
.user
.name
, "ACCEPT", sizeof(target
->u
.user
.name
));
159 ip6tc_init_verify_append(const char * table
,
161 struct ip6t_entry
* e
)
165 h
= ip6tc_init(table
);
167 syslog(LOG_ERR
, "ip6tc_init error : %s", ip6tc_strerror(errno
));
170 if(!ip6tc_is_chain(chain
, h
)) {
171 syslog(LOG_ERR
, "chain %s not found", chain
);
174 if(!ip6tc_append_entry(chain
, e
, h
)) {
175 syslog(LOG_ERR
, "ip6tc_append_entry() error : %s", ip6tc_strerror(errno
));
178 if(!ip6tc_commit(h
)) {
179 syslog(LOG_ERR
, "ip6tc_commit() error : %s", ip6tc_strerror(errno
));
189 ip6tables -I %s %d -p %s -i %s -s %s --sport %hu -d %s --dport %hu -j ACCEPT
190 ip6tables -I %s %d -p %s -i %s --sport %hu -d %s --dport %hu -j ACCEPT
192 miniupnpd_forward_chain, line_number, proto, ext_if_name, raddr, rport, iaddr, iport
194 ip6tables -t raw -I PREROUTING %d -p %s -i %s -s %s --sport %hu -d %s --dport %hu -j TRACE
195 ip6tables -t raw -I PREROUTING %d -p %s -i %s --sport %hu -d %s --dport %hu -j TRACE
197 int add_pinhole(const char * ifname
,
198 const char * rem_host
, unsigned short rem_port
,
199 const char * int_client
, unsigned short int_port
,
200 int proto
, const char * desc
, unsigned int timestamp
)
203 struct ip6t_entry
* e
;
204 struct ip6t_entry
* tmp
;
205 struct ip6t_entry_match
*match
= NULL
;
206 struct ip6t_entry_target
*target
= NULL
;
208 e
= calloc(1, sizeof(struct ip6t_entry
));
210 syslog(LOG_ERR
, "%s: calloc(%d) failed",
211 "add_pinhole", (int)sizeof(struct ip6t_entry
));
214 e
->ipv6
.proto
= proto
;
216 e
->ipv6
.flags
|= IP6T_F_PROTO
;
219 strncpy(e
->ipv6
.iniface
, ifname
, IFNAMSIZ
);
220 if(rem_host
&& (rem_host
[0] != '\0')) {
221 inet_pton(AF_INET6
, rem_host
, &e
->ipv6
.src
);
222 memset(&e
->ipv6
.smsk
, 0xff, sizeof(e
->ipv6
.smsk
));
224 inet_pton(AF_INET6
, int_client
, &e
->ipv6
.dst
);
225 memset(&e
->ipv6
.dmsk
, 0xff, sizeof(e
->ipv6
.dmsk
));
227 /*e->nfcache = NFC_IP_DST_PT;*/
228 /*e->nfcache |= NFC_UNKNOWN;*/
230 match
= new_match(proto
, rem_port
, int_port
);
231 target
= get_accept_target();
232 tmp
= realloc(e
, sizeof(struct ip6t_entry
)
233 + match
->u
.match_size
234 + target
->u
.target_size
);
236 syslog(LOG_ERR
, "%s: realloc(%d) failed",
237 "add_pinhole", (int)(sizeof(struct ip6t_entry
) + match
->u
.match_size
+ target
->u
.target_size
));
244 memcpy(e
->elems
, match
, match
->u
.match_size
);
245 memcpy(e
->elems
+ match
->u
.match_size
, target
, target
->u
.target_size
);
246 e
->target_offset
= sizeof(struct ip6t_entry
)
247 + match
->u
.match_size
;
248 e
->next_offset
= sizeof(struct ip6t_entry
)
249 + match
->u
.match_size
250 + target
->u
.target_size
;
254 if(ip6tc_init_verify_append("filter", miniupnpd_v6_filter_chain
, e
) < 0) {
258 uid
= add_to_pinhole_list(&e
->ipv6
.src
, rem_port
,
259 &e
->ipv6
.dst
, int_port
,
260 proto
, desc
, timestamp
);
266 delete_pinhole(unsigned short uid
)
268 struct pinhole_t
* p
;
270 const struct ip6t_entry
* e
;
271 const struct ip6t_entry_match
*match
= NULL
;
272 /*const struct ip6t_entry_target *target = NULL;*/
275 p
= get_pinhole(uid
);
277 return -2; /* not found */
279 h
= ip6tc_init("filter");
281 syslog(LOG_ERR
, "ip6tc_init error : %s", ip6tc_strerror(errno
));
284 if(!ip6tc_is_chain(miniupnpd_v6_filter_chain
, h
)) {
285 syslog(LOG_ERR
, "chain %s not found", miniupnpd_v6_filter_chain
);
289 for(e
= ip6tc_first_rule(miniupnpd_v6_filter_chain
, h
);
291 e
= ip6tc_next_rule(e
, h
)) {
292 if((e
->ipv6
.proto
== p
->proto
) &&
293 (0 == memcmp(&e
->ipv6
.src
, &p
->saddr
, sizeof(e
->ipv6
.src
))) &&
294 (0 == memcmp(&e
->ipv6
.dst
, &p
->daddr
, sizeof(e
->ipv6
.dst
)))) {
295 const struct ip6t_tcp
* info
;
296 match
= (const struct ip6t_entry_match
*)&e
->elems
;
297 info
= (const struct ip6t_tcp
*)&match
->data
;
298 if((info
->spts
[0] == p
->sport
) && (info
->dpts
[0] == p
->dport
)) {
299 if(!ip6tc_delete_num_entry(miniupnpd_v6_filter_chain
, index
, h
)) {
300 syslog(LOG_ERR
, "ip6tc_delete_num_entry(%s,%d,...): %s",
301 miniupnpd_v6_filter_chain
, index
, ip6tc_strerror(errno
));
304 if(!ip6tc_commit(h
)) {
305 syslog(LOG_ERR
, "ip6tc_commit(): %s",
306 ip6tc_strerror(errno
));
310 LIST_REMOVE(p
, entries
);
317 syslog(LOG_WARNING
, "delete_pinhole() rule with PID=%hu not found", uid
);
318 return -2; /* not found */
325 update_pinhole(unsigned short uid
, unsigned int timestamp
)
327 struct pinhole_t
* p
;
329 p
= get_pinhole(uid
);
331 p
->timestamp
= timestamp
;
334 return -2; /* Not found */
339 get_pinhole_info(unsigned short uid
,
340 char * rem_host
, int rem_hostlen
,
341 unsigned short * rem_port
,
342 char * int_client
, int int_clientlen
,
343 unsigned short * int_port
,
344 int * proto
, char * desc
, int desclen
,
345 unsigned int * timestamp
,
346 u_int64_t
* packets
, u_int64_t
* bytes
)
348 struct pinhole_t
* p
;
350 p
= get_pinhole(uid
);
352 return -2; /* Not found */
353 if(rem_host
&& (rem_host
[0] != '\0')) {
354 if(inet_ntop(AF_INET6
, &p
->saddr
, rem_host
, rem_hostlen
) == NULL
)
358 *rem_port
= p
->sport
;
360 if(inet_ntop(AF_INET6
, &p
->daddr
, int_client
, int_clientlen
) == NULL
)
364 *int_port
= p
->dport
;
368 *timestamp
= p
->timestamp
;
370 strncpy(desc
, p
->desc
, desclen
);
371 if(packets
|| bytes
) {
372 /* theses informations need to be read from netfilter */
374 const struct ip6t_entry
* e
;
375 const struct ip6t_entry_match
* match
;
376 h
= ip6tc_init("filter");
378 syslog(LOG_ERR
, "ip6tc_init error : %s", ip6tc_strerror(errno
));
381 for(e
= ip6tc_first_rule(miniupnpd_v6_filter_chain
, h
);
383 e
= ip6tc_next_rule(e
, h
)) {
384 if((e
->ipv6
.proto
== p
->proto
) &&
385 (0 == memcmp(&e
->ipv6
.src
, &p
->saddr
, sizeof(e
->ipv6
.src
))) &&
386 (0 == memcmp(&e
->ipv6
.dst
, &p
->daddr
, sizeof(e
->ipv6
.dst
)))) {
387 const struct ip6t_tcp
* info
;
388 match
= (const struct ip6t_entry_match
*)&e
->elems
;
389 info
= (const struct ip6t_tcp
*)&match
->data
;
390 if((info
->spts
[0] == p
->sport
) && (info
->dpts
[0] == p
->dport
)) {
392 *packets
= e
->counters
.pcnt
;
394 *bytes
= e
->counters
.bcnt
;
404 int get_pinhole_uid_by_index(int index
)
406 struct pinhole_t
* p
;
408 for(p
= pinhole_list
.lh_first
; p
!= NULL
; p
= p
->entries
.le_next
)
415 clean_pinhole_list(unsigned int * next_timestamp
)
417 unsigned int min_ts
= UINT_MAX
;
418 struct pinhole_t
* p
;
422 current_time
= time(NULL
);
423 p
= pinhole_list
.lh_first
;
425 if(p
->timestamp
<= (unsigned int)current_time
) {
426 unsigned short uid
= p
->uid
;
427 syslog(LOG_INFO
, "removing expired pinhole with uid=%hu", uid
);
428 p
= p
->entries
.le_next
;
429 if(delete_pinhole(uid
) == 0)
434 if(p
->timestamp
< min_ts
)
435 min_ts
= p
->timestamp
;
436 p
= p
->entries
.le_next
;
439 if(next_timestamp
&& (min_ts
!= UINT_MAX
))
440 *next_timestamp
= min_ts
;
444 #endif /* ENABLE_UPNPPINHOLE */