/* * Copyright (C) 2006 Lennert Buytenhek * Use and distribution permitted according to the terms of the LGPL. */ #include #include #include #include #include #include #include #include #include #include #include "tcp_snat.h" struct snat_rule { struct ipt_entry basic_rule; struct { struct ipt_entry_match match; struct ipt_tcp tcp; } tcp_match; struct { struct ipt_entry_target target; struct ip_nat_multi_range mr; } nat_target; }; static void build_snat_rule(struct snat_rule *rule, struct tcp_snat_request *tsr) { memset(rule, 0, sizeof(*rule)); rule->basic_rule.ip.src.s_addr = tsr->orig_src_ip; rule->basic_rule.ip.dst.s_addr = tsr->dst_ip; rule->basic_rule.ip.smsk.s_addr = htonl(0xffffffff); rule->basic_rule.ip.dmsk.s_addr = htonl(0xffffffff); #if 0 strcpy(rule->basic_rule.ip.iniface, "eth0"); strcpy(rule->basic_rule.ip.outiface, "eth1"); memcpy(rule->basic_rule.ip.iniface_mask, "\xff\xff\xff\xff", 4); memcpy(rule->basic_rule.ip.outiface_mask, "\xff\xff\xff\xff", 4); #endif rule->basic_rule.ip.proto = IPPROTO_TCP; rule->basic_rule.ip.flags = 0; rule->basic_rule.ip.invflags = 0; rule->basic_rule.nfcache = NFC_UNKNOWN; rule->basic_rule.target_offset = sizeof(rule->basic_rule) + sizeof(rule->tcp_match); rule->basic_rule.next_offset = sizeof(rule->basic_rule) + sizeof(rule->tcp_match) + sizeof(rule->nat_target); rule->basic_rule.comefrom = 0; rule->basic_rule.counters = (struct ipt_counters){ 0, 0 }; rule->tcp_match.match.u.user.match_size = sizeof(rule->tcp_match); strcpy(rule->tcp_match.match.u.user.name, "tcp"); rule->tcp_match.tcp.spts[0] = tsr->src_port; rule->tcp_match.tcp.spts[1] = tsr->src_port; rule->tcp_match.tcp.dpts[0] = tsr->dst_port; rule->tcp_match.tcp.dpts[1] = tsr->dst_port; rule->tcp_match.tcp.option = 0; rule->tcp_match.tcp.flg_mask = 0; rule->tcp_match.tcp.flg_cmp = 0; rule->tcp_match.tcp.invflags = 0; rule->nat_target.target.u.user.target_size = sizeof(rule->nat_target); strcpy(rule->nat_target.target.u.user.name, "SNAT"); rule->nat_target.mr.rangesize = 1; rule->nat_target.mr.range[0].flags = IP_NAT_RANGE_MAP_IPS; rule->nat_target.mr.range[0].min_ip = tsr->new_src_ip; rule->nat_target.mr.range[0].max_ip = tsr->new_src_ip; rule->nat_target.mr.range[0].min.tcp.port = 0; rule->nat_target.mr.range[0].max.tcp.port = 0; } static int try_to_insert_rule(ipt_chainlabel chain, struct snat_rule *rule) { iptc_handle_t table; table = iptc_init("nat"); if (table == NULL) { if (errno == EINVAL) return 0; syslog(LOG_CRIT, "iptc_init: %s\n", iptc_strerror(errno)); abort(); } if (!iptc_flush_entries(chain, &table)) { syslog(LOG_CRIT, "iptc_flush_entries: %s\n", iptc_strerror(errno)); abort(); } if (!iptc_append_entry(chain, (struct ipt_entry *)rule, &table)) { syslog(LOG_CRIT, "iptc_append_entry: %s\n", iptc_strerror(errno)); abort(); } if (!iptc_commit(&table)) { if (errno == EAGAIN || errno == EINVAL) return 0; syslog(LOG_ALERT, "iptc_commit: %s\n", iptc_strerror(errno)); abort(); } return 1; } int insert_snat_rule(struct tcp_snat_request *tsr) { ipt_chainlabel chain; struct snat_rule rule; int ok; int i; memset(&chain, 0, sizeof(chain)); strcpy(chain, "POSTROUTING"); build_snat_rule(&rule, tsr); ok = 0; for (i = 0; i < 5; i++) { if (try_to_insert_rule(chain, &rule)) { ok = 1; break; } sched_yield(); } return ok; }