# cat expect_test.c
MODULE_DESCRIPTION("expect helper test");
struct test_proto {
int type;
int port;
union nf_inet_addr addr;
};
static int test_help(struct sk_buff *skb,
unsigned int protoff,
struct nf_conn *ct,
enum ip_conntrack_info ctinfo)
{
unsigned int dataoff, datalen;
const struct tcphdr *th;
struct tcphdr _tcph;
int ret;
char *dt_ptr;
struct nf_conntrack_expect *exp;
int dir = CTINFO2DIR(ctinfo);
struct test_proto prot = {0};
uint16_t port = ntohs((uint16_t)prot.port);
char test_buffer[512];
if (ctinfo != IP_CT_ESTABLISHED
&& ctinfo != IP_CT_ESTABLISHED+IP_CT_IS_REPLY) {
return NF_ACCEPT;
}
th = skb_header_pointer(skb, protoff, sizeof(_tcph), &_tcph);
dataoff = protoff + th->doff * 4;
datalen = skb->len - dataoff;
dt_ptr = skb_header_pointer(skb, dataoff, datalen, test_buffer);
//get proto header
memcpy(&prot, dt_ptr, sizeof(struct test_proto));
if (prot.type != 100) {
ret = NF_ACCEPT;
goto out;
}
exp = nf_ct_expect_alloc(ct);
port = ntohs((uint16_t)prot.port);
nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, AF_INET,
&ct->tuplehash[dir].tuple.src.u3, &prot.addr,
IPPROTO_TCP, NULL, &port);
if (nf_ct_expect_related(exp) != 0)
ret = NF_DROP;
else
ret = NF_ACCEPT;
out:
return ret;
}
static const struct nf_conntrack_expect_policy test_policy = {
.max_expected = 10,
.timeout = 50 * 60,
};
#define PORT 5000
static struct nf_conntrack_helper test = {
.name = "test",
.me = THIS_MODULE,
.tuple.src.l3num = AF_INET,
.tuple.src.u.tcp.port = cpu_to_be16(5000),
.tuple.dst.protonum = IPPROTO_TCP,
.help = test_help,
.expect_policy = &test_policy,
};
static void nf_conntrack_test_fini(void)
{
nf_conntrack_helper_unregister(&test);
}
static int __init nf_conntrack_test_init(void)
{
int ret = nf_conntrack_helper_register(&test);
return ret;
}
module_init(nf_conntrack_test_init);
module_exit(nf_conntrack_test_fini);
usage:
1. 本地防火墙只允许访问192.168.0.2:2152
iptables -A OUTPUT -m conntrack --ctstatus EXPECTED -j ACCEPT
iptables -A OUTPUT -d 192.168.0.2 -j ACCEPT
iptables -A OUTPUT -j DROP
iptables -t raw -A OUTPUT -d 192.168.0.2 -p tcp --dport 12345 -j CT --helper test
2. 本地connect 192.168.0.2:2152后发送定义test_proto协议
struct test_proto {
int type;
int port;
in_addr_t addr;
};
struct test_proto ap = {0};
ap.addr = inet_addr("192.168.0.3");
ap.type = 100;
ap.port = 2152;
len = sizeof(struct aa_proto);
sbuf = ≈
write(sock, (char *)sbuf, len);
3. 本地connect 192.168.0.3:2152成功, 第2步建立了一个expect conntrack,可以通过第1步的第一条firewall规则