Miniupnpd: update to 1.9 (20150430)
[tomato.git] / release / src / router / miniupnpd / netfilter / iptpinhole.c
blob978e4c0ca0257ca57b1317a4c1a16ea7e7a4f892
1 /* $Id: iptpinhole.c,v 1.14 2015/02/10 15:01:03 nanard Exp $ */
2 /* MiniUPnP project
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 */
8 #include <stdlib.h>
9 #include <string.h>
10 #include <syslog.h>
11 #include <errno.h>
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
21 #include <xtables.h>
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);
34 struct pinhole_t {
35 struct in6_addr saddr;
36 struct in6_addr daddr;
37 LIST_ENTRY(pinhole_t) entries;
38 unsigned int timestamp;
39 unsigned short sport;
40 unsigned short dport;
41 unsigned short uid;
42 unsigned char proto;
43 char desc[];
46 void init_iptpinhole(void)
48 LIST_INIT(&pinhole_list);
51 void shutdown_iptpinhole(void)
53 /* TODO empty list */
56 /* return uid */
57 static int
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)
62 struct pinhole_t * p;
64 p = calloc(1, sizeof(struct pinhole_t) + strlen(desc) + 1);
65 if(!p) {
66 syslog(LOG_ERR, "add_to_pinhole_list calloc() error");
67 return -1;
69 strcpy(p->desc, desc);
70 memcpy(&p->saddr, saddr, sizeof(struct in6_addr));
71 p->sport = sport;
72 memcpy(&p->daddr, daddr, sizeof(struct in6_addr));
73 p->dport = dport;
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) {
78 next_uid++;
79 if(next_uid > 65535)
80 next_uid = 1;
82 p->uid = next_uid;
83 next_uid++;
84 if(next_uid > 65535)
85 next_uid = 1;
86 return p->uid;
89 static struct pinhole_t *
90 get_pinhole(unsigned short uid)
92 struct pinhole_t * p;
94 for(p = pinhole_list.lh_first; p != NULL; p = p->entries.le_next) {
95 if(p->uid == uid)
96 return p;
98 return NULL; /* not found */
101 /* new_match()
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 */
109 size_t size;
110 const char * name;
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;
115 switch(proto) {
116 case IPPROTO_TCP:
117 name = "tcp";
118 break;
119 case IPPROTO_UDP:
120 name = "udp";
121 break;
122 case IPPROTO_UDPLITE:
123 name = "udplite";
124 break;
125 default:
126 name = NULL;
128 if(name)
129 strncpy(match->u.user.name, name, sizeof(match->u.user.name));
130 else
131 syslog(LOG_WARNING, "no name for protocol %d", proto);
132 info = (struct ip6t_tcp *)match->data;
133 if(sport) {
134 info->spts[0] = sport; /* specified source port */
135 info->spts[1] = sport;
136 } else {
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;
142 return match;
145 static struct ip6t_entry_target *
146 get_accept_target(void)
148 struct ip6t_entry_target * target = NULL;
149 size_t size;
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));
155 return target;
158 static int
159 ip6tc_init_verify_append(const char * table,
160 const char * chain,
161 struct ip6t_entry * e)
163 IP6TC_HANDLE h;
165 h = ip6tc_init(table);
166 if(!h) {
167 syslog(LOG_ERR, "ip6tc_init error : %s", ip6tc_strerror(errno));
168 return -1;
170 if(!ip6tc_is_chain(chain, h)) {
171 syslog(LOG_ERR, "chain %s not found", chain);
172 goto error;
174 if(!ip6tc_append_entry(chain, e, h)) {
175 syslog(LOG_ERR, "ip6tc_append_entry() error : %s", ip6tc_strerror(errno));
176 goto error;
178 if(!ip6tc_commit(h)) {
179 syslog(LOG_ERR, "ip6tc_commit() error : %s", ip6tc_strerror(errno));
180 goto error;
182 return 0; /* ok */
183 error:
184 ip6tc_free(h);
185 return -1;
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)
202 int uid;
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));
209 if(!e) {
210 syslog(LOG_ERR, "%s: calloc(%d) failed",
211 "add_pinhole", (int)sizeof(struct ip6t_entry));
212 return -1;
214 e->ipv6.proto = proto;
215 if (proto)
216 e->ipv6.flags |= IP6T_F_PROTO;
218 if(ifname)
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);
235 if(!tmp) {
236 syslog(LOG_ERR, "%s: realloc(%d) failed",
237 "add_pinhole", (int)(sizeof(struct ip6t_entry) + match->u.match_size + target->u.target_size));
238 free(e);
239 free(match);
240 free(target);
241 return -1;
243 e = tmp;
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;
251 free(match);
252 free(target);
254 if(ip6tc_init_verify_append("filter", miniupnpd_v6_filter_chain, e) < 0) {
255 free(e);
256 return -1;
258 uid = add_to_pinhole_list(&e->ipv6.src, rem_port,
259 &e->ipv6.dst, int_port,
260 proto, desc, timestamp);
261 free(e);
262 return uid;
266 delete_pinhole(unsigned short uid)
268 struct pinhole_t * p;
269 IP6TC_HANDLE h;
270 const struct ip6t_entry * e;
271 const struct ip6t_entry_match *match = NULL;
272 /*const struct ip6t_entry_target *target = NULL;*/
273 unsigned int index;
275 p = get_pinhole(uid);
276 if(!p)
277 return -2; /* not found */
279 h = ip6tc_init("filter");
280 if(!h) {
281 syslog(LOG_ERR, "ip6tc_init error : %s", ip6tc_strerror(errno));
282 return -1;
284 if(!ip6tc_is_chain(miniupnpd_v6_filter_chain, h)) {
285 syslog(LOG_ERR, "chain %s not found", miniupnpd_v6_filter_chain);
286 goto error;
288 index = 0;
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));
302 goto error;
304 if(!ip6tc_commit(h)) {
305 syslog(LOG_ERR, "ip6tc_commit(): %s",
306 ip6tc_strerror(errno));
307 goto error;
309 ip6tc_free(h);
310 LIST_REMOVE(p, entries);
311 return 0; /* ok */
314 index++;
316 ip6tc_free(h);
317 syslog(LOG_WARNING, "delete_pinhole() rule with PID=%hu not found", uid);
318 return -2; /* not found */
319 error:
320 ip6tc_free(h);
321 return -1;
325 update_pinhole(unsigned short uid, unsigned int timestamp)
327 struct pinhole_t * p;
329 p = get_pinhole(uid);
330 if(p) {
331 p->timestamp = timestamp;
332 return 0;
333 } else {
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);
351 if(!p)
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)
355 return -1;
357 if(rem_port)
358 *rem_port = p->sport;
359 if(int_client) {
360 if(inet_ntop(AF_INET6, &p->daddr, int_client, int_clientlen) == NULL)
361 return -1;
363 if(int_port)
364 *int_port = p->dport;
365 if(proto)
366 *proto = p->proto;
367 if(timestamp)
368 *timestamp = p->timestamp;
369 if (desc)
370 strncpy(desc, p->desc, desclen);
371 if(packets || bytes) {
372 /* theses informations need to be read from netfilter */
373 IP6TC_HANDLE h;
374 const struct ip6t_entry * e;
375 const struct ip6t_entry_match * match;
376 h = ip6tc_init("filter");
377 if(!h) {
378 syslog(LOG_ERR, "ip6tc_init error : %s", ip6tc_strerror(errno));
379 return -1;
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)) {
391 if(packets)
392 *packets = e->counters.pcnt;
393 if(bytes)
394 *bytes = e->counters.bcnt;
395 break;
399 ip6tc_free(h);
401 return 0;
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)
409 if (!index--)
410 return p->uid;
411 return -1;
415 clean_pinhole_list(unsigned int * next_timestamp)
417 unsigned int min_ts = UINT_MAX;
418 struct pinhole_t * p;
419 time_t current_time;
420 int n = 0;
422 current_time = time(NULL);
423 p = pinhole_list.lh_first;
424 while(p != NULL) {
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)
430 n++;
431 else
432 break;
433 } else {
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;
441 return n;
444 #endif /* ENABLE_UPNPPINHOLE */