Annotation of embedaddon/miniupnpd/netfilter/iptpinhole.c, revision 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>