Annotation of embedaddon/miniupnpd/netfilter/iptpinhole.c, revision 1.1.1.1
1.1 misho 1: /* $Id: iptpinhole.c,v 1.8 2012/09/18 08:29:17 nanard Exp $ */
2: /* MiniUPnP project
3: * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
4: * (c) 2012 Thomas Bernard
5: * This software is subject to the conditions detailed
6: * in the LICENCE file provided within the distribution */
7:
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>
14:
15: #include "../config.h"
16: #include "iptpinhole.h"
17: #include "../upnpglobalvars.h"
18:
19: #ifdef ENABLE_6FC_SERVICE
20:
21: #include <xtables.h>
22: #include <libiptc/libip6tc.h>
23: #include "tiny_nf_nat.h"
24:
25: #define IP6TC_HANDLE struct ip6tc_handle *
26:
27: static int next_uid = 1;
28:
29: static LIST_HEAD(pinhole_list_t, pinhole_t) pinhole_list;
30:
31: static struct pinhole_t *
32: get_pinhole(unsigned short uid);
33:
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: };
44:
45: void init_iptpinhole(void)
46: {
47: LIST_INIT(&pinhole_list);
48: }
49:
50: void shutdown_iptpinhole(void)
51: {
52: /* TODO empty list */
53: }
54:
55: /* return uid */
56: static int
57: add_to_pinhole_list(struct in6_addr * saddr, unsigned short sport,
58: struct in6_addr * daddr, unsigned short dport,
59: int proto, unsigned int timestamp)
60: {
61: struct pinhole_t * p;
62:
63: p = calloc(1, sizeof(struct pinhole_t));
64: if(!p) {
65: syslog(LOG_ERR, "add_to_pinhole_list calloc() error");
66: return -1;
67: }
68: memcpy(&p->saddr, saddr, sizeof(struct in6_addr));
69: p->sport = sport;
70: memcpy(&p->daddr, daddr, sizeof(struct in6_addr));
71: p->dport = dport;
72: p->timestamp = timestamp;
73: p->proto = (unsigned char)proto;
74: LIST_INSERT_HEAD(&pinhole_list, p, entries);
75: while(get_pinhole(next_uid) != NULL) {
76: next_uid++;
77: if(next_uid > 65535)
78: next_uid = 1;
79: }
80: p->uid = next_uid;
81: next_uid++;
82: if(next_uid > 65535)
83: next_uid = 1;
84: return p->uid;
85: }
86:
87: static struct pinhole_t *
88: get_pinhole(unsigned short uid)
89: {
90: struct pinhole_t * p;
91:
92: for(p = pinhole_list.lh_first; p != NULL; p = p->entries.le_next) {
93: if(p->uid == uid)
94: return p;
95: }
96: return NULL; /* not found */
97: }
98:
99: /* new_match()
100: * Allocate and set a new ip6t_entry_match structure
101: * The caller must free() it after usage */
102: static struct ip6t_entry_match *
103: new_match(int proto, unsigned short sport, unsigned short dport)
104: {
105: struct ip6t_entry_match *match;
106: struct ip6t_tcp *info; /* TODO : use ip6t_udp if needed */
107: size_t size;
108: const char * name;
109: size = XT_ALIGN(sizeof(struct ip6t_entry_match))
110: + XT_ALIGN(sizeof(struct ip6t_tcp));
111: match = calloc(1, size);
112: match->u.user.match_size = size;
113: switch(proto) {
114: case IPPROTO_TCP:
115: name = "tcp";
116: break;
117: case IPPROTO_UDP:
118: name = "udp";
119: break;
120: case IPPROTO_UDPLITE:
121: name = "udplite";
122: break;
123: default:
124: name = NULL;
125: }
126: if(name)
127: strncpy(match->u.user.name, name, sizeof(match->u.user.name));
128: else
129: syslog(LOG_WARNING, "no name for protocol %d", proto);
130: info = (struct ip6t_tcp *)match->data;
131: if(sport) {
132: info->spts[0] = sport; /* specified source port */
133: info->spts[1] = sport;
134: } else {
135: info->spts[0] = 0; /* all source ports */
136: info->spts[1] = 0xFFFF;
137: }
138: info->dpts[0] = dport; /* specified destination port */
139: info->dpts[1] = dport;
140: return match;
141: }
142:
143: static struct ip6t_entry_target *
144: get_accept_target(void)
145: {
146: struct ip6t_entry_target * target = NULL;
147: size_t size;
148: size = XT_ALIGN(sizeof(struct ip6t_entry_target))
149: + XT_ALIGN(sizeof(int));
150: target = calloc(1, size);
151: target->u.user.target_size = size;
152: strncpy(target->u.user.name, "ACCEPT", sizeof(target->u.user.name));
153: return target;
154: }
155:
156: static int
157: ip6tc_init_verify_append(const char * table,
158: const char * chain,
159: struct ip6t_entry * e)
160: {
161: IP6TC_HANDLE h;
162:
163: h = ip6tc_init(table);
164: if(!h) {
165: syslog(LOG_ERR, "ip6tc_init error : %s", ip6tc_strerror(errno));
166: return -1;
167: }
168: if(!ip6tc_is_chain(chain, h)) {
169: syslog(LOG_ERR, "chain %s not found", chain);
170: goto error;
171: }
172: if(!ip6tc_append_entry(chain, e, h)) {
173: syslog(LOG_ERR, "ip6tc_append_entry() error : %s", ip6tc_strerror(errno));
174: goto error;
175: }
176: if(!ip6tc_commit(h)) {
177: syslog(LOG_ERR, "ip6tc_commit() error : %s", ip6tc_strerror(errno));
178: goto error;
179: }
180: return 0; /* ok */
181: error:
182: ip6tc_free(h);
183: return -1;
184: }
185:
186: /*
187: ip6tables -I %s %d -p %s -i %s -s %s --sport %hu -d %s --dport %hu -j ACCEPT
188: ip6tables -I %s %d -p %s -i %s --sport %hu -d %s --dport %hu -j ACCEPT
189:
190: miniupnpd_forward_chain, line_number, proto, ext_if_name, raddr, rport, iaddr, iport
191:
192: ip6tables -t raw -I PREROUTING %d -p %s -i %s -s %s --sport %hu -d %s --dport %hu -j TRACE
193: ip6tables -t raw -I PREROUTING %d -p %s -i %s --sport %hu -d %s --dport %hu -j TRACE
194: */
195: int add_pinhole(const char * ifname,
196: const char * rem_host, unsigned short rem_port,
197: const char * int_client, unsigned short int_port,
198: int proto, unsigned int timestamp)
199: {
200: int uid;
201: struct ip6t_entry * e;
202: struct ip6t_entry_match *match = NULL;
203: struct ip6t_entry_target *target = NULL;
204:
205: e = calloc(1, sizeof(struct ip6t_entry));
206: e->ipv6.proto = proto;
207:
208: if(ifname)
209: strncpy(e->ipv6.iniface, ifname, IFNAMSIZ);
210: if(rem_host) {
211: inet_pton(AF_INET6, rem_host, &e->ipv6.src);
212: memset(&e->ipv6.smsk, 0xff, sizeof(e->ipv6.smsk));
213: }
214: inet_pton(AF_INET6, int_client, &e->ipv6.dst);
215: memset(&e->ipv6.dmsk, 0xff, sizeof(e->ipv6.dmsk));
216:
217: /*e->nfcache = NFC_IP_DST_PT;*/
218: /*e->nfcache |= NFC_UNKNOWN;*/
219:
220: match = new_match(proto, rem_port, int_port);
221: target = get_accept_target();
222: e = realloc(e, sizeof(struct ip6t_entry)
223: + match->u.match_size
224: + target->u.target_size);
225: memcpy(e->elems, match, match->u.match_size);
226: memcpy(e->elems + match->u.match_size, target, target->u.target_size);
227: e->target_offset = sizeof(struct ip6t_entry)
228: + match->u.match_size;
229: e->next_offset = sizeof(struct ip6t_entry)
230: + match->u.match_size
231: + target->u.target_size;
232: free(match);
233: free(target);
234:
235: if(ip6tc_init_verify_append("filter", miniupnpd_v6_filter_chain, e) < 0) {
236: free(e);
237: return -1;
238: }
239: uid = add_to_pinhole_list(&e->ipv6.src, rem_port,
240: &e->ipv6.dst, int_port,
241: proto, timestamp);
242: free(e);
243: return uid;
244: }
245:
246: int
247: delete_pinhole(unsigned short uid)
248: {
249: struct pinhole_t * p;
250: IP6TC_HANDLE h;
251: const struct ip6t_entry * e;
252: const struct ip6t_entry_match *match = NULL;
253: /*const struct ip6t_entry_target *target = NULL;*/
254: unsigned int index;
255:
256: p = get_pinhole(uid);
257: if(!p)
258: return -2; /* not found */
259:
260: h = ip6tc_init("filter");
261: if(!h) {
262: syslog(LOG_ERR, "ip6tc_init error : %s", ip6tc_strerror(errno));
263: return -1;
264: }
265: if(!ip6tc_is_chain(miniupnpd_v6_filter_chain, h)) {
266: syslog(LOG_ERR, "chain %s not found", miniupnpd_v6_filter_chain);
267: goto error;
268: }
269: index = 0;
270: for(e = ip6tc_first_rule(miniupnpd_v6_filter_chain, h);
271: e;
272: e = ip6tc_next_rule(e, h)) {
273: if((e->ipv6.proto == p->proto) &&
274: (0 == memcmp(&e->ipv6.src, &p->saddr, sizeof(e->ipv6.src))) &&
275: (0 == memcmp(&e->ipv6.dst, &p->daddr, sizeof(e->ipv6.dst)))) {
276: const struct ip6t_tcp * info;
277: match = (const struct ip6t_entry_match *)&e->elems;
278: info = (const struct ip6t_tcp *)&match->data;
279: if((info->spts[0] == p->sport) && (info->dpts[0] == p->dport)) {
280: if(!ip6tc_delete_num_entry(miniupnpd_v6_filter_chain, index, h)) {
281: syslog(LOG_ERR, "ip6tc_delete_num_entry(%s,%d,...): %s",
282: miniupnpd_v6_filter_chain, index, ip6tc_strerror(errno));
283: goto error;
284: }
285: if(!ip6tc_commit(h)) {
286: syslog(LOG_ERR, "ip6tc_commit(): %s",
287: ip6tc_strerror(errno));
288: goto error;
289: }
290: ip6tc_free(h);
291: LIST_REMOVE(p, entries);
292: return 0; /* ok */
293: }
294: }
295: index++;
296: }
297: ip6tc_free(h);
298: syslog(LOG_WARNING, "delete_pinhole() rule with PID=%hu not found", uid);
299: return -2; /* not found */
300: error:
301: ip6tc_free(h);
302: return -1;
303: }
304:
305: int
306: update_pinhole(unsigned short uid, unsigned int timestamp)
307: {
308: struct pinhole_t * p;
309:
310: p = get_pinhole(uid);
311: if(p) {
312: p->timestamp = timestamp;
313: return 0;
314: } else {
315: return -2; /* Not found */
316: }
317: }
318:
319: int
320: get_pinhole_info(unsigned short uid,
321: char * rem_host, int rem_hostlen, unsigned short * rem_port,
322: char * int_client, int int_clientlen, unsigned short * int_port,
323: int * proto, unsigned int * timestamp,
324: u_int64_t * packets, u_int64_t * bytes)
325: {
326: struct pinhole_t * p;
327:
328: p = get_pinhole(uid);
329: if(!p)
330: return -2; /* Not found */
331: if(rem_host) {
332: if(inet_ntop(AF_INET6, &p->saddr, rem_host, rem_hostlen) == NULL)
333: return -1;
334: }
335: if(rem_port)
336: *rem_port = p->sport;
337: if(int_client) {
338: if(inet_ntop(AF_INET6, &p->daddr, int_client, int_clientlen) == NULL)
339: return -1;
340: }
341: if(int_port)
342: *int_port = p->dport;
343: if(proto)
344: *proto = p->proto;
345: if(timestamp)
346: *timestamp = p->timestamp;
347: if(packets || bytes) {
348: /* theses informations need to be read from netfilter */
349: IP6TC_HANDLE h;
350: const struct ip6t_entry * e;
351: const struct ip6t_entry_match * match;
352: h = ip6tc_init("filter");
353: if(!h) {
354: syslog(LOG_ERR, "ip6tc_init error : %s", ip6tc_strerror(errno));
355: return -1;
356: }
357: for(e = ip6tc_first_rule(miniupnpd_v6_filter_chain, h);
358: e;
359: e = ip6tc_next_rule(e, h)) {
360: if((e->ipv6.proto == p->proto) &&
361: (0 == memcmp(&e->ipv6.src, &p->saddr, sizeof(e->ipv6.src))) &&
362: (0 == memcmp(&e->ipv6.dst, &p->daddr, sizeof(e->ipv6.dst)))) {
363: const struct ip6t_tcp * info;
364: match = (const struct ip6t_entry_match *)&e->elems;
365: info = (const struct ip6t_tcp *)&match->data;
366: if((info->spts[0] == p->sport) && (info->dpts[0] == p->dport)) {
367: if(packets)
368: *packets = e->counters.pcnt;
369: if(bytes)
370: *bytes = e->counters.bcnt;
371: break;
372: }
373: }
374: }
375: ip6tc_free(h);
376: }
377: return 0;
378: }
379:
380: int
381: clean_pinhole_list(unsigned int * next_timestamp)
382: {
383: unsigned int min_ts = UINT_MAX;
384: struct pinhole_t * p;
385: time_t current_time;
386: int n = 0;
387:
388: current_time = time(NULL);
389: p = pinhole_list.lh_first;
390: while(p != NULL) {
391: if(p->timestamp <= (unsigned int)current_time) {
392: unsigned short uid = p->uid;
393: syslog(LOG_INFO, "removing expired pinhole with uid=%hu", uid);
394: p = p->entries.le_next;
395: if(delete_pinhole(uid) == 0)
396: n++;
397: else
398: break;
399: } else {
400: if(p->timestamp < min_ts)
401: min_ts = p->timestamp;
402: p = p->entries.le_next;
403: }
404: }
405: if(next_timestamp && (min_ts != UINT_MAX))
406: *next_timestamp = min_ts;
407: return n;
408: }
409:
410: #endif
411:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>