objopt.c
/*
* (C) 2005-2011 by Pablo Neira Ayuso <pablo@netfilter.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include "internal/internal.h"
static void __autocomplete(struct nf_conntrack *ct, int dir)
{
struct __nfct_tuple *this = NULL, *other = NULL;
switch(dir) {
case __DIR_ORIG:
this = &ct->head.orig;
other = &ct->repl;
break;
case __DIR_REPL:
this = &ct->repl;
other = &ct->head.orig;
break;
}
this->l3protonum = other->l3protonum;
this->protonum = other->protonum;
memcpy(&this->src.v6, &other->dst.v6, sizeof(union __nfct_address));
memcpy(&this->dst.v6, &other->src.v6, sizeof(union __nfct_address));
switch(this->protonum) {
case IPPROTO_UDP:
case IPPROTO_TCP:
case IPPROTO_SCTP:
case IPPROTO_DCCP:
case IPPROTO_GRE:
case IPPROTO_UDPLITE:
this->l4src.all = other->l4dst.all;
this->l4dst.all = other->l4src.all;
break;
case IPPROTO_ICMP:
case IPPROTO_ICMPV6:
/* the setter already autocompletes the reply tuple. */
break;
}
/* XXX: this is safe but better convert bitset to uint64_t */
ct->head.set[0] |= TS_ORIG | TS_REPL;
}
static void setobjopt_undo_snat(struct nf_conntrack *ct)
{
switch (ct->head.orig.l3protonum) {
case AF_INET:
ct->snat.min_ip.v4 = ct->repl.dst.v4;
ct->snat.max_ip.v4 = ct->snat.min_ip.v4;
ct->repl.dst.v4 = ct->head.orig.src.v4;
set_bit(ATTR_SNAT_IPV4, ct->head.set);
break;
case AF_INET6:
memcpy(&ct->snat.min_ip.v6, &ct->repl.dst.v6,
sizeof(struct in6_addr));
memcpy(&ct->snat.max_ip.v6, &ct->snat.min_ip.v6,
sizeof(struct in6_addr));
memcpy(&ct->repl.dst.v6, &ct->head.orig.src.v6,
sizeof(struct in6_addr));
set_bit(ATTR_SNAT_IPV6, ct->head.set);
break;
default:
break;
}
}
static void setobjopt_undo_dnat(struct nf_conntrack *ct)
{
switch (ct->head.orig.l3protonum) {
case AF_INET:
ct->dnat.min_ip.v4 = ct->repl.src.v4;
ct->dnat.max_ip.v4 = ct->dnat.min_ip.v4;
ct->repl.src.v4 = ct->head.orig.dst.v4;
set_bit(ATTR_DNAT_IPV4, ct->head.set);
break;
case AF_INET6:
memcpy(&ct->dnat.min_ip.v6, &ct->repl.src.v6,
sizeof(struct in6_addr));
memcpy(&ct->dnat.max_ip.v6, &ct->dnat.min_ip.v6,
sizeof(struct in6_addr));
memcpy(&ct->repl.src.v6, &ct->head.orig.dst.v6,
sizeof(struct in6_addr));
set_bit(ATTR_DNAT_IPV6, ct->head.set);
break;
default:
break;
}
}
static void setobjopt_undo_spat(struct nf_conntrack *ct)
{
ct->snat.l4min.all = ct->repl.l4dst.tcp.port;
ct->snat.l4max.all = ct->snat.l4min.all;
ct->repl.l4dst.tcp.port =
ct->head.orig.l4src.tcp.port;
set_bit(ATTR_SNAT_PORT, ct->head.set);
}
static void setobjopt_undo_dpat(struct nf_conntrack *ct)
{
ct->dnat.l4min.all = ct->repl.l4src.tcp.port;
ct->dnat.l4max.all = ct->dnat.l4min.all;
ct->repl.l4src.tcp.port =
ct->head.orig.l4dst.tcp.port;
set_bit(ATTR_DNAT_PORT, ct->head.set);
}
static void setobjopt_setup_orig(struct nf_conntrack *ct)
{
__autocomplete(ct, __DIR_ORIG);
}
static void setobjopt_setup_repl(struct nf_conntrack *ct)
{
__autocomplete(ct, __DIR_REPL);
}
static const setobjopt setobjopt_array[__NFCT_SOPT_MAX] = {
[NFCT_SOPT_UNDO_SNAT] = setobjopt_undo_snat,
[NFCT_SOPT_UNDO_DNAT] = setobjopt_undo_dnat,
[NFCT_SOPT_UNDO_SPAT] = setobjopt_undo_spat,
[NFCT_SOPT_UNDO_DPAT] = setobjopt_undo_dpat,
[NFCT_SOPT_SETUP_ORIGINAL] = setobjopt_setup_orig,
[NFCT_SOPT_SETUP_REPLY] = setobjopt_setup_repl,
};
int __setobjopt(struct nf_conntrack *ct, unsigned int option)
{
if (unlikely(option > NFCT_SOPT_MAX))
return -1;
setobjopt_array[option](ct);
return 0;
}
static int getobjopt_is_snat(const struct nf_conntrack *ct)
{
if (test_bit(ATTR_STATUS, ct->head.set) &&
!(ct->status & IPS_SRC_NAT_DONE))
return 0;
switch (ct->head.orig.l3protonum) {
case AF_INET:
return ct->repl.dst.v4 != ct->head.orig.src.v4;
case AF_INET6:
if (memcmp(&ct->repl.dst.v6, &ct->head.orig.src.v6,
sizeof(struct in6_addr)) != 0)
return 1;
else
return 0;
default:
return 0;
}
}
static int getobjopt_is_dnat(const struct nf_conntrack *ct)
{
if (test_bit(ATTR_STATUS, ct->head.set) &&
!(ct->status & IPS_DST_NAT_DONE))
return 0;
switch (ct->head.orig.l3protonum) {
case AF_INET:
return ct->repl.src.v4 != ct->head.orig.dst.v4;
case AF_INET6:
if (memcmp(&ct->repl.src.v6, &ct->head.orig.dst.v6,
sizeof(struct in6_addr)) != 0)
return 1;
else
return 0;
default:
return 0;
}
}
static int getobjopt_is_spat(const struct nf_conntrack *ct)
{
return ((test_bit(ATTR_STATUS, ct->head.set) ?
ct->status & IPS_SRC_NAT_DONE : 1) &&
ct->repl.l4dst.tcp.port !=
ct->head.orig.l4src.tcp.port);
}
static int getobjopt_is_dpat(const struct nf_conntrack *ct)
{
return ((test_bit(ATTR_STATUS, ct->head.set) ?
ct->status & IPS_DST_NAT_DONE : 1) &&
ct->repl.l4src.tcp.port !=
ct->head.orig.l4dst.tcp.port);
}
static const getobjopt getobjopt_array[__NFCT_GOPT_MAX] = {
[NFCT_GOPT_IS_SNAT] = getobjopt_is_snat,
[NFCT_GOPT_IS_DNAT] = getobjopt_is_dnat,
[NFCT_GOPT_IS_SPAT] = getobjopt_is_spat,
[NFCT_GOPT_IS_DPAT] = getobjopt_is_dpat,
};
int __getobjopt(const struct nf_conntrack *ct, unsigned int option)
{
if (unlikely(option > NFCT_GOPT_MAX))
return -1;
return getobjopt_array[option](ct);
}