blob: 5e6527f5ca42b84b96d77426944d99e8375fd1d2 [file] [log] [blame]
/* cdu_nf_events.c
*
* This file handles client data usage netfilter conntrack events.
*
* 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/module.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 "cdu_db.h"
#include "cdu_nf_events.h"
#include "cdu_xt_target.h"
#include "cdu_types.h"
#ifndef CONFIG_NF_CONNTRACK_CHAIN_EVENTS
#error Can not use this module without conntrack events
#endif
static u_int8_t nf_registered;
static int cdu_nf_event(struct notifier_block *this, unsigned long events, void *ptr)
{
struct nf_ct_event *item = (struct nf_ct_event *)ptr;
struct nf_conn *ct = item->ct;
if (!ct) {
CDU_DEBUG("No ct available\n");
return -EINVAL;
}
if (ct->orig_direction == CDU_DIR_NOT_SET) {
/* This ct does not have a direction information because it didn't
* travel through the xt hook, which means that we don't care
* about this traffic
*/
return NOTIFY_DONE;
}
/* Update data usage htable entry on end of flow trigger */
if (events & (1 << IPCT_DESTROY)) {
CDU_DEBUG("Caught %s NF event. mac=%pM, dir %s, ct %p\n",
events & (1 << IPCT_NEW)?"NEW":"DESTROY", ct->mac,
(ct->orig_direction == CDU_DIR_SRC)?"DIR_SRC":"DIR_DEST", ct);
cdu_db_update_entry(ct, CDU_DB_UPDATE_PERMA_SET, CDU_DB_CONNTR_NOCLEAR);
}
return NOTIFY_DONE;
}
static struct notifier_block cdu_nf_notifier = {
.notifier_call = cdu_nf_event,
};
int cdu_nf_events_register(void)
{
int result;
if (!nf_registered) {
result = nf_conntrack_register_notifier(&init_net, &cdu_nf_notifier);
if (result < 0) {
CDU_INFO("Failed to register conntrack notifier %d\n", result);
return result;
}
nf_registered = 1;
}
CDU_DEBUG("initialized\n");
return 0;
}
void cdu_nf_events_unregister(void)
{
if (nf_registered) {
nf_conntrack_unregister_notifier(&init_net, &cdu_nf_notifier);
CDU_DEBUG("uninitialized\n");
}
}