blob: c30f4ba18533ef846d0a8e0fd70b9fad69f35ace [file] [log] [blame]
/*
* (C) 2013 by Pablo Neira Ayuso <pablo@netfilter.org>
*
* Port this helper to userspace.
*/
/* SANE connection tracking helper
* (SANE = Scanner Access Now Easy)
* For documentation about the SANE network protocol see
* http://www.sane-project.org/html/doc015.html
*/
/*
* Copyright (C) 2007 Red Hat, Inc.
* Author: Michal Schmidt <mschmidt@redhat.com>
* Based on the FTP conntrack helper (net/netfilter/nf_conntrack_ftp.c):
* (C) 1999-2001 Paul `Rusty' Russell
* (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
* (C) 2003,2004 USAGI/WIDE Project <http://www.linux-ipv6.org>
* (C) 2003 Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include "conntrackd.h"
#include "helper.h"
#include "myct.h"
#include "log.h"
#include <errno.h>
#include <netinet/ip.h>
#define _GNU_SOURCE
#include <netinet/tcp.h>
#include <libmnl/libmnl.h>
#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
#include <libnetfilter_queue/libnetfilter_queue.h>
#include <libnetfilter_queue/libnetfilter_queue_tcp.h>
#include <libnetfilter_queue/pktbuff.h>
#include <linux/netfilter.h>
enum sane_state {
SANE_STATE_NORMAL,
SANE_STATE_START_REQUESTED,
};
struct sane_request {
uint32_t RPC_code;
#define SANE_NET_START 7 /* RPC code */
uint32_t handle;
};
struct sane_reply_net_start {
uint32_t status;
#define SANE_STATUS_SUCCESS 0
uint16_t zero;
uint16_t port;
/* other fields aren't interesting for conntrack */
};
struct nf_ct_sane_master {
enum sane_state state;
};
static int
sane_helper_cb(struct pkt_buff *pkt, uint32_t protoff,
struct myct *myct, uint32_t ctinfo)
{
unsigned int dataoff, datalen;
const struct tcphdr *th;
void *sb_ptr;
int ret = NF_ACCEPT;
int dir = CTINFO2DIR(ctinfo);
struct nf_ct_sane_master *ct_sane_info = myct->priv_data;
struct nf_expect *exp;
struct sane_request *req;
struct sane_reply_net_start *reply;
union nfct_attr_grp_addr saddr;
union nfct_attr_grp_addr daddr;
/* Until there's been traffic both ways, don't look in packets. */
if (ctinfo != IP_CT_ESTABLISHED &&
ctinfo != IP_CT_ESTABLISHED_REPLY)
return NF_ACCEPT;
th = (struct tcphdr *)(pktb_network_header(pkt) + protoff);
/* No data? */
dataoff = protoff + th->doff * 4;
if (dataoff >= pktb_len(pkt))
return NF_ACCEPT;
datalen = pktb_len(pkt) - dataoff;
sb_ptr = pktb_network_header(pkt) + dataoff;
if (dir == MYCT_DIR_ORIG) {
if (datalen != sizeof(struct sane_request))
goto out;
req = sb_ptr;
if (req->RPC_code != htonl(SANE_NET_START)) {
/* Not an interesting command */
ct_sane_info->state = SANE_STATE_NORMAL;
goto out;
}
/* We're interested in the next reply */
ct_sane_info->state = SANE_STATE_START_REQUESTED;
goto out;
}
/* Is it a reply to an uninteresting command? */
if (ct_sane_info->state != SANE_STATE_START_REQUESTED)
goto out;
/* It's a reply to SANE_NET_START. */
ct_sane_info->state = SANE_STATE_NORMAL;
if (datalen < sizeof(struct sane_reply_net_start)) {
pr_debug("nf_ct_sane: NET_START reply too short\n");
goto out;
}
reply = sb_ptr;
if (reply->status != htonl(SANE_STATUS_SUCCESS)) {
/* saned refused the command */
pr_debug("nf_ct_sane: unsuccessful SANE_STATUS = %u\n",
ntohl(reply->status));
goto out;
}
/* Invalid saned reply? Ignore it. */
if (reply->zero != 0)
goto out;
exp = nfexp_new();
if (exp == NULL)
return NF_DROP;
cthelper_get_addr_src(myct->ct, MYCT_DIR_ORIG, &saddr);
cthelper_get_addr_dst(myct->ct, MYCT_DIR_ORIG, &daddr);
if (cthelper_expect_init(exp, myct->ct, 0, &saddr, &daddr,
IPPROTO_TCP, NULL, &reply->port, 0)) {
nfexp_destroy(exp);
return NF_DROP;
}
myct->exp = exp;
out:
return ret;
}
static struct ctd_helper sane_helper = {
.name = "sane",
.l4proto = IPPROTO_TCP,
.priv_data_len = sizeof(struct nf_ct_sane_master),
.cb = sane_helper_cb,
.policy = {
[0] = {
.name = "sane",
.expect_max = 1,
.expect_timeout = 5 * 60,
},
},
};
static void __attribute__ ((constructor)) sane_init(void)
{
helper_register(&sane_helper);
}