static void print_state(unsigned int statemask) { const char *sep = ""; if (statemask & XT_CONNTRACK_STATE_INVALID) { printf("%sINVALID", sep); sep = ","; } if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_NEW)) { printf("%sNEW", sep); sep = ","; } if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_RELATED)) { printf("%sRELATED", sep); sep = ","; } if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED)) { printf("%sESTABLISHED", sep); sep = ","; } if (statemask & XT_CONNTRACK_STATE_UNTRACKED) { printf("%sUNTRACKED", sep); sep = ","; } if (statemask & XT_CONNTRACK_STATE_SNAT) { printf("%sSNAT", sep); sep = ","; } if (statemask & XT_CONNTRACK_STATE_DNAT) { printf("%sDNAT", sep); sep = ","; } printf(" "); }
static int parse_state(const char *state, size_t len, struct xt_conntrack_info *sinfo) { if (strncasecmp(state, "INVALID", len) == 0) sinfo->statemask |= XT_CONNTRACK_STATE_INVALID; else if (strncasecmp(state, "NEW", len) == 0) sinfo->statemask |= XT_CONNTRACK_STATE_BIT(IP_CT_NEW); else if (strncasecmp(state, "ESTABLISHED", len) == 0) sinfo->statemask |= XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED); else if (strncasecmp(state, "RELATED", len) == 0) sinfo->statemask |= XT_CONNTRACK_STATE_BIT(IP_CT_RELATED); else if (strncasecmp(state, "UNTRACKED", len) == 0) sinfo->statemask |= XT_CONNTRACK_STATE_UNTRACKED; else if (strncasecmp(state, "SNAT", len) == 0) sinfo->statemask |= XT_CONNTRACK_STATE_SNAT; else if (strncasecmp(state, "DNAT", len) == 0) sinfo->statemask |= XT_CONNTRACK_STATE_DNAT; else return 0; return 1; }
static bool conntrack_ps_state(struct xt_conntrack_mtinfo2 *info, const char *state, size_t z) { if (strncasecmp(state, "INVALID", z) == 0) info->state_mask |= XT_CONNTRACK_STATE_INVALID; else if (strncasecmp(state, "NEW", z) == 0) info->state_mask |= XT_CONNTRACK_STATE_BIT(IP_CT_NEW); else if (strncasecmp(state, "ESTABLISHED", z) == 0) info->state_mask |= XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED); else if (strncasecmp(state, "RELATED", z) == 0) info->state_mask |= XT_CONNTRACK_STATE_BIT(IP_CT_RELATED); else if (strncasecmp(state, "UNTRACKED", z) == 0) info->state_mask |= XT_CONNTRACK_STATE_UNTRACKED; else if (strncasecmp(state, "SNAT", z) == 0) info->state_mask |= XT_CONNTRACK_STATE_SNAT; else if (strncasecmp(state, "DNAT", z) == 0) info->state_mask |= XT_CONNTRACK_STATE_DNAT; else return false; return true; }
static bool conntrack_mt_v0(const struct sk_buff *skb, const struct net_device *in, const struct net_device *out, const struct xt_match *match, const void *matchinfo, int offset, unsigned int protoff, bool *hotdrop) { const struct xt_conntrack_info *sinfo = matchinfo; const struct nf_conn *ct; enum ip_conntrack_info ctinfo; unsigned int statebit; ct = nf_ct_get(skb, &ctinfo); #define FWINV(bool, invflg) ((bool) ^ !!(sinfo->invflags & (invflg))) if (ct == &nf_conntrack_untracked) statebit = XT_CONNTRACK_STATE_UNTRACKED; else if (ct) statebit = XT_CONNTRACK_STATE_BIT(ctinfo); else statebit = XT_CONNTRACK_STATE_INVALID; if (sinfo->flags & XT_CONNTRACK_STATE) { if (ct) { if (test_bit(IPS_SRC_NAT_BIT, &ct->status)) statebit |= XT_CONNTRACK_STATE_SNAT; if (test_bit(IPS_DST_NAT_BIT, &ct->status)) statebit |= XT_CONNTRACK_STATE_DNAT; } if (FWINV((statebit & sinfo->statemask) == 0, XT_CONNTRACK_STATE)) return false; } if (ct == NULL) { if (sinfo->flags & ~XT_CONNTRACK_STATE) return false; return true; } if (sinfo->flags & XT_CONNTRACK_PROTO && FWINV(nf_ct_protonum(ct) != sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum, XT_CONNTRACK_PROTO)) return false; if (sinfo->flags & XT_CONNTRACK_ORIGSRC && FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip & sinfo->sipmsk[IP_CT_DIR_ORIGINAL].s_addr) != sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip, XT_CONNTRACK_ORIGSRC)) return false; if (sinfo->flags & XT_CONNTRACK_ORIGDST && FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3.ip & sinfo->dipmsk[IP_CT_DIR_ORIGINAL].s_addr) != sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip, XT_CONNTRACK_ORIGDST)) return false; if (sinfo->flags & XT_CONNTRACK_REPLSRC && FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip & sinfo->sipmsk[IP_CT_DIR_REPLY].s_addr) != sinfo->tuple[IP_CT_DIR_REPLY].src.ip, XT_CONNTRACK_REPLSRC)) return false; if (sinfo->flags & XT_CONNTRACK_REPLDST && FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip & sinfo->dipmsk[IP_CT_DIR_REPLY].s_addr) != sinfo->tuple[IP_CT_DIR_REPLY].dst.ip, XT_CONNTRACK_REPLDST)) return false; if (sinfo->flags & XT_CONNTRACK_STATUS && FWINV((ct->status & sinfo->statusmask) == 0, XT_CONNTRACK_STATUS)) return false; if(sinfo->flags & XT_CONNTRACK_EXPIRES) { unsigned long expires = timer_pending(&ct->timeout) ? (ct->timeout.expires - jiffies)/HZ : 0; if (FWINV(!(expires >= sinfo->expires_min && expires <= sinfo->expires_max), XT_CONNTRACK_EXPIRES)) return false; } return true; #undef FWINV }
static bool conntrack_mt(const struct sk_buff *skb, const struct net_device *in, const struct net_device *out, const struct xt_match *match, const void *matchinfo, int offset, unsigned int protoff, bool *hotdrop) { const struct xt_conntrack_mtinfo1 *info = matchinfo; enum ip_conntrack_info ctinfo; const struct nf_conn *ct; unsigned int statebit; ct = nf_ct_get(skb, &ctinfo); if (ct == &nf_conntrack_untracked) statebit = XT_CONNTRACK_STATE_UNTRACKED; else if (ct != NULL) statebit = XT_CONNTRACK_STATE_BIT(ctinfo); else statebit = XT_CONNTRACK_STATE_INVALID; if (info->match_flags & XT_CONNTRACK_STATE) { if (ct != NULL) { if (test_bit(IPS_SRC_NAT_BIT, &ct->status)) statebit |= XT_CONNTRACK_STATE_SNAT; if (test_bit(IPS_DST_NAT_BIT, &ct->status)) statebit |= XT_CONNTRACK_STATE_DNAT; } if (!!(info->state_mask & statebit) ^ !(info->invert_flags & XT_CONNTRACK_STATE)) return false; } if (ct == NULL) return info->match_flags & XT_CONNTRACK_STATE; if ((info->match_flags & XT_CONNTRACK_DIRECTION) && (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) ^ !!(info->invert_flags & XT_CONNTRACK_DIRECTION)) return false; if (info->match_flags & XT_CONNTRACK_ORIGSRC) if (conntrack_mt_origsrc(ct, info, match->family) ^ !(info->invert_flags & XT_CONNTRACK_ORIGSRC)) return false; if (info->match_flags & XT_CONNTRACK_ORIGDST) if (conntrack_mt_origdst(ct, info, match->family) ^ !(info->invert_flags & XT_CONNTRACK_ORIGDST)) return false; if (info->match_flags & XT_CONNTRACK_REPLSRC) if (conntrack_mt_replsrc(ct, info, match->family) ^ !(info->invert_flags & XT_CONNTRACK_REPLSRC)) return false; if (info->match_flags & XT_CONNTRACK_REPLDST) if (conntrack_mt_repldst(ct, info, match->family) ^ !(info->invert_flags & XT_CONNTRACK_REPLDST)) return false; if (!ct_proto_port_check(info, ct)) return false; if ((info->match_flags & XT_CONNTRACK_STATUS) && (!!(info->status_mask & ct->status) ^ !(info->invert_flags & XT_CONNTRACK_STATUS))) return false; if (info->match_flags & XT_CONNTRACK_EXPIRES) { unsigned long expires = 0; if (timer_pending(&ct->timeout)) expires = (ct->timeout.expires - jiffies) / HZ; if ((expires >= info->expires_min && expires <= info->expires_max) ^ !(info->invert_flags & XT_CONNTRACK_EXPIRES)) return false; } return true; }
static int match(const struct sk_buff *skb, const struct net_device *in, const struct net_device *out, const void *matchinfo, int offset, unsigned int protoff, int *hotdrop) { const struct xt_conntrack_info *sinfo = matchinfo; struct ip_conntrack *ct; enum ip_conntrack_info ctinfo; unsigned int statebit; ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo); #define FWINV(bool,invflg) ((bool) ^ !!(sinfo->invflags & invflg)) if (ct == &ip_conntrack_untracked) statebit = XT_CONNTRACK_STATE_UNTRACKED; else if (ct) statebit = XT_CONNTRACK_STATE_BIT(ctinfo); else statebit = XT_CONNTRACK_STATE_INVALID; if(sinfo->flags & XT_CONNTRACK_STATE) { if (ct) { if(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip != ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip) statebit |= XT_CONNTRACK_STATE_SNAT; if(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip != ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip) statebit |= XT_CONNTRACK_STATE_DNAT; } if (FWINV((statebit & sinfo->statemask) == 0, XT_CONNTRACK_STATE)) return 0; } if(sinfo->flags & XT_CONNTRACK_PROTO) { if (!ct || FWINV(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum != sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum, XT_CONNTRACK_PROTO)) return 0; } if(sinfo->flags & XT_CONNTRACK_ORIGSRC) { if (!ct || FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip&sinfo->sipmsk[IP_CT_DIR_ORIGINAL].s_addr) != sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip, XT_CONNTRACK_ORIGSRC)) return 0; } if(sinfo->flags & XT_CONNTRACK_ORIGDST) { if (!ct || FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip&sinfo->dipmsk[IP_CT_DIR_ORIGINAL].s_addr) != sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip, XT_CONNTRACK_ORIGDST)) return 0; } if(sinfo->flags & XT_CONNTRACK_REPLSRC) { if (!ct || FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip&sinfo->sipmsk[IP_CT_DIR_REPLY].s_addr) != sinfo->tuple[IP_CT_DIR_REPLY].src.ip, XT_CONNTRACK_REPLSRC)) return 0; } if(sinfo->flags & XT_CONNTRACK_REPLDST) { if (!ct || FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip&sinfo->dipmsk[IP_CT_DIR_REPLY].s_addr) != sinfo->tuple[IP_CT_DIR_REPLY].dst.ip, XT_CONNTRACK_REPLDST)) return 0; } if(sinfo->flags & XT_CONNTRACK_STATUS) { if (!ct || FWINV((ct->status & sinfo->statusmask) == 0, XT_CONNTRACK_STATUS)) return 0; } if(sinfo->flags & XT_CONNTRACK_EXPIRES) { unsigned long expires; if(!ct) return 0; expires = timer_pending(&ct->timeout) ? (ct->timeout.expires - jiffies)/HZ : 0; if (FWINV(!(expires >= sinfo->expires_min && expires <= sinfo->expires_max), XT_CONNTRACK_EXPIRES)) return 0; } return 1; }
/* * Get a list of the current firewall entries * @param fw_list list of firewall entries * @return 0 on success and errno on failure */ int netconf_get_fw(netconf_fw_t *fw_list) { const char **table; const char *chain; const struct ipt_entry *entry; struct iptc_handle *handle = NULL; /* Initialize list */ netconf_list_init(fw_list); /* Search all default tables */ for (table = &netconf_table_names[0]; *table; table++) { if (strcmp(*table, "filter") && strcmp(*table, "nat")) continue; if (!(handle = iptc_init(*table))) { fprintf(stderr, "%s\n", iptc_strerror(errno)); goto err; } /* Search all default chains */ for (chain = iptc_first_chain(handle); chain; chain = iptc_next_chain(handle)) { if (strcmp(chain, "INPUT") && strcmp(chain, "FORWARD") && strcmp(chain, "OUTPUT") && strcmp(chain, "PREROUTING") && strcmp(chain, "POSTROUTING") && strcmp(chain, "VSERVER") && strcmp(chain, "UPNP")) continue; /* Search all entries */ for (entry = iptc_first_rule(chain, handle); entry; entry = iptc_next_rule(entry, handle)) { int num = target_num(entry, handle); netconf_fw_t *fw = NULL; netconf_filter_t *filter = NULL; netconf_nat_t *nat = NULL; netconf_app_t *app = NULL; const struct ipt_entry_match *match; const struct ipt_entry_target *target; struct ipt_mac_info *mac = NULL; struct ipt_state_info *state = NULL; struct ipt_conntrack_info *conntrack = NULL; struct ipt_time_info *time = NULL; /* Only know about TCP/UDP */ if (!netconf_valid_ipproto(entry->ip.proto)) continue; /* Only know about target types in the specified tables */ if (!netconf_valid_target(num) || (netconf_table_name[num] && strncmp(netconf_table_name[num], *table, IPT_FUNCTION_MAXNAMELEN) != 0)) continue; /* Only know about specified target types */ if (netconf_valid_filter(num)) fw = (netconf_fw_t *) (filter = calloc(1, sizeof(netconf_filter_t))); else if (netconf_valid_nat(num)) fw = (netconf_fw_t *) (nat = calloc(1, sizeof(netconf_nat_t))); else if (num == NETCONF_APP) fw = (netconf_fw_t *) (app = calloc(1, sizeof(netconf_app_t))); else continue; if (!fw) { perror("calloc"); goto err; } netconf_list_add(fw, fw_list); /* Get IP addresses */ fw->match.src.ipaddr.s_addr = entry->ip.src.s_addr; fw->match.src.netmask.s_addr = entry->ip.smsk.s_addr; fw->match.dst.ipaddr.s_addr = entry->ip.dst.s_addr; fw->match.dst.netmask.s_addr = entry->ip.dmsk.s_addr; fw->match.flags |= (entry->ip.invflags & IPT_INV_SRCIP) ? NETCONF_INV_SRCIP : 0; fw->match.flags |= (entry->ip.invflags & IPT_INV_DSTIP) ? NETCONF_INV_DSTIP : 0; /* Get interface names */ strncpy(fw->match.in.name, entry->ip.iniface, IFNAMSIZ); strncpy(fw->match.out.name, entry->ip.outiface, IFNAMSIZ); fw->match.flags |= (entry->ip.invflags & IPT_INV_VIA_IN) ? NETCONF_INV_IN : 0; fw->match.flags |= (entry->ip.invflags & IPT_INV_VIA_OUT) ? NETCONF_INV_OUT : 0; fw->match.ipproto = entry->ip.proto; /* Get TCP port(s) */ if (entry->ip.proto == IPPROTO_TCP) { struct ipt_tcp *tcp = NULL; for_each_ipt_match(match, entry) { if (strncmp(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN) != 0) continue; tcp = (struct ipt_tcp *) &match->data[0]; break; } if (tcp) { /* Match ports stored in host order for some stupid reason */ fw->match.src.ports[0] = htons(tcp->spts[0]); fw->match.src.ports[1] = htons(tcp->spts[1]); fw->match.dst.ports[0] = htons(tcp->dpts[0]); fw->match.dst.ports[1] = htons(tcp->dpts[1]); fw->match.flags |= (tcp->invflags & IPT_TCP_INV_SRCPT) ? NETCONF_INV_SRCPT : 0; fw->match.flags |= (tcp->invflags & IPT_TCP_INV_DSTPT) ? NETCONF_INV_DSTPT : 0; } } /* Get UDP port(s) */ else if (entry->ip.proto == IPPROTO_UDP) { struct ipt_udp *udp = NULL; for_each_ipt_match(match, entry) { if (strncmp(match->u.user.name, "udp", IPT_FUNCTION_MAXNAMELEN) != 0) continue; udp = (struct ipt_udp *) &match->data[0]; break; } if (udp) { /* Match ports stored in host order for some stupid reason */ fw->match.src.ports[0] = htons(udp->spts[0]); fw->match.src.ports[1] = htons(udp->spts[1]); fw->match.dst.ports[0] = htons(udp->dpts[0]); fw->match.dst.ports[1] = htons(udp->dpts[1]); fw->match.flags |= (udp->invflags & IPT_UDP_INV_SRCPT) ? NETCONF_INV_SRCPT : 0; fw->match.flags |= (udp->invflags & IPT_UDP_INV_DSTPT) ? NETCONF_INV_DSTPT : 0; } } /* Get source MAC address */ for_each_ipt_match(match, entry) { if (strncmp(match->u.user.name, "mac", IPT_FUNCTION_MAXNAMELEN) != 0) continue; mac = (struct ipt_mac_info *) &match->data[0]; break; } if (mac) { memcpy(fw->match.mac.octet, mac->srcaddr, ETHER_ADDR_LEN); fw->match.flags |= mac->invert ? NETCONF_INV_MAC : 0; } /* Get packet state */ for_each_ipt_match(match, entry) { if (strncmp(match->u.user.name, "state", IPT_FUNCTION_MAXNAMELEN) == 0) { state = (struct ipt_state_info *) &match->data[0]; break; } else if (strncmp(match->u.user.name, "conntrack", IPT_FUNCTION_MAXNAMELEN) == 0) { conntrack = (struct ipt_conntrack_info *) &match->data[0]; break; } } if (conntrack && (conntrack->match_flags & XT_CONNTRACK_STATE)) { fw->match.state |= (conntrack->state_mask & XT_CONNTRACK_STATE_INVALID) ? NETCONF_INVALID : 0; fw->match.state |= (conntrack->state_mask & XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED)) ? NETCONF_ESTABLISHED : 0; fw->match.state |= (conntrack->state_mask & XT_CONNTRACK_STATE_BIT(IP_CT_RELATED)) ? NETCONF_RELATED : 0; fw->match.state |= (conntrack->state_mask & XT_CONNTRACK_STATE_BIT(IP_CT_NEW)) ? NETCONF_NEW : 0; fw->match.state |= (conntrack->state_mask & XT_CONNTRACK_STATE_UNTRACKED) ? NETCONF_UNTRACKED : 0; fw->match.state |= (conntrack->state_mask & XT_CONNTRACK_STATE_SNAT) ? NETCONF_STATE_SNAT : 0; fw->match.state |= (conntrack->state_mask & XT_CONNTRACK_STATE_DNAT) ? NETCONF_STATE_DNAT : 0; } else if (state) { fw->match.state |= (state->statemask & IPT_STATE_INVALID) ? NETCONF_INVALID : 0; fw->match.state |= (state->statemask & IPT_STATE_BIT(IP_CT_ESTABLISHED)) ? NETCONF_ESTABLISHED : 0; fw->match.state |= (state->statemask & IPT_STATE_BIT(IP_CT_RELATED)) ? NETCONF_RELATED : 0; fw->match.state |= (state->statemask & IPT_STATE_BIT(IP_CT_NEW)) ? NETCONF_NEW : 0; } /* Get local time */ for_each_ipt_match(match, entry) { if (strncmp(match->u.user.name, "time", IPT_FUNCTION_MAXNAMELEN) != 0) continue; /* We added 8 bytes of day range at the end */ if (match->u.match_size < (IPT_ALIGN(sizeof(struct ipt_entry_match)) + IPT_ALIGN(sizeof(struct ipt_time_info) + 8))) continue; time = (struct ipt_time_info *) &match->data[0]; break; } if (time) { fw->match.days = time->weekdays_match; fw->match.secs[0] = time->daytime_start; fw->match.secs[1] = time->daytime_stop; } /* Set target type */ fw->target = num; target = (struct ipt_entry_target *) ((int) entry + entry->target_offset); /* Get filter target information */ if (filter) { if (!netconf_valid_dir(filter->dir = filter_dir(chain))) { fprintf(stderr, "error direction in %s\n", chain); goto err; } } /* Get NAT target information */ else if (nat) { struct ip_nat_multi_range *mr = (struct ip_nat_multi_range *) &target->data[0]; struct ip_nat_range *range = (struct ip_nat_range *) &mr->range[0]; /* Get mapped IP address */ nat->ipaddr.s_addr = range->min_ip; /* Get mapped TCP port(s) */ if (entry->ip.proto == IPPROTO_TCP) { nat->ports[0] = range->min.tcp.port; nat->ports[1] = range->max.tcp.port; } /* Get mapped UDP port(s) */ else if (entry->ip.proto == IPPROTO_UDP) { nat->ports[0] = range->min.udp.port; nat->ports[1] = range->max.udp.port; } } /* Get application specific port forward information */ else if (app) { struct ip_autofw_info *info = (struct ip_autofw_info *) &target->data[0]; app->proto = info->proto; app->dport[0] = info->dport[0]; app->dport[1] = info->dport[1]; app->to[0] = info->to[0]; app->to[1] = info->to[1]; } } } if (!iptc_commit(handle)) { fprintf(stderr, "%s\n", iptc_strerror(errno)); goto err; } iptc_free(handle); handle = NULL; }