blob: 3ce181b79d4f064ffc75492cc6bfae3a9ce5fecf [file] [log] [blame]
/* cdu_xt_target.c
*
* This file defines client data usage xtables target.
*
* Author: Cradlepoint Technology, Inc. <source@cradlepoint.com>
* Adrian Sitterle <asitterle@cradlepoint.com>
*
* Copyright (C) 2019 Cradlepoint Technology, Inc.
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/version.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/netfilter/xt_usage.h>
#include <linux/netfilter/x_tables.h>
#include <net/netfilter/nf_conntrack.h>
#include <net/netfilter/nf_conntrack_core.h>
#include <linux/netfilter/nf_conntrack_common.h>
#include <linux/etherdevice.h>
#include "cdu_types.h"
#include "cdu_xt_target.h"
#include "cdu_nf_events.h"
#include "cdu_seq_file.h"
#include "cdu_db.h"
static unsigned int usage_tg(struct sk_buff *skb, const struct xt_action_param *par)
{
struct nf_conn *ct;
enum ip_conntrack_info ctinfo;
const struct xt_usage_tginfo *info = par->targinfo;
unsigned char direction = (info->src == 1);
ct = nf_ct_get(skb, &ctinfo);
if (unlikely(!ct))
return XT_CONTINUE;
if (likely(ct->orig_direction != CDU_DIR_NOT_SET))
return XT_CONTINUE;
if (direction) {
if (ctinfo == IP_CT_NEW)
ct->orig_direction = CDU_DIR_SRC;
else if (ctinfo == IP_CT_IS_REPLY)
ct->orig_direction = CDU_DIR_DEST;
else
return XT_CONTINUE;
ether_addr_copy(ct->mac, eth_hdr(skb)->h_source);
cdu_db_add_entry_ifmissing(ct);
}
return XT_CONTINUE;
}
static int xt_usage_check(const struct xt_tgchk_param *par)
{
struct xt_usage_tginfo *info = par->targinfo;
if (!info->priv) {
info->priv = cdu_seq_file_create(info->name, info->readreset);
if (!info->priv)
return -ENOMEM;
cdu_nf_events_register();
}
(info->priv->usecount)++;
return 0;
}
static void xt_usage_destroy(const struct xt_tgdtor_param *par)
{
struct xt_usage_tginfo *info = par->targinfo;
if (info->priv) {
CDU_DEBUG("usecount=%u\n", info->priv->usecount);
(info->priv->usecount)--;
if (info->priv->usecount <= 0) {
cdu_seq_file_destroy(info->priv);
info->priv = NULL;
cdu_nf_events_unregister();
}
}
}
static struct xt_target usage_tg_reg __read_mostly = {
.name = "USAGE",
.revision = 0,
.family = NFPROTO_UNSPEC,
.target = usage_tg,
.checkentry = xt_usage_check,
.destroy = xt_usage_destroy,
.targetsize = sizeof(struct xt_usage_tginfo),
.hooks = (1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_IN) |
(1 << NF_INET_FORWARD) | (1 << NF_INET_POST_ROUTING) |
(1 << NF_INET_LOCAL_OUT),
.me = THIS_MODULE,
};
int cdu_xt_target_register(void)
{
int result = xt_register_target(&usage_tg_reg);
if (result < 0) {
CDU_INFO("Failed to register xt target notifier %d\n", result);
return result;
}
CDU_DEBUG("initialized\n");
return 0;
}
void cdu_xt_target_unregister(void)
{
xt_unregister_target(&usage_tg_reg);
CDU_DEBUG("uninitialized\n");
}