blob: 6bc6d16e02996151dedd316bc05821c6545f8f75 [file] [log] [blame]
Gareth Williamsf98d4192015-03-11 16:55:41 +00001/*
2 **************************************************************************
Kyle Swenson1aa0e0d2021-03-18 14:20:24 -06003 * Copyright (c) 2015, 2020, The Linux Foundation. All rights reserved.
Gareth Williamsf98d4192015-03-11 16:55:41 +00004 * Permission to use, copy, modify, and/or distribute this software for
5 * any purpose with or without fee is hereby granted, provided that the
6 * above copyright notice and this permission notice appear in all copies.
7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
13 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14 **************************************************************************
15 */
16
17#include <linux/version.h>
18#include <linux/types.h>
19#include <linux/ip.h>
20#include <linux/module.h>
Gareth Williamsf98d4192015-03-11 16:55:41 +000021#include <linux/kthread.h>
Gareth Williamsf98d4192015-03-11 16:55:41 +000022#include <linux/string.h>
Murat Sezgin908ecb32015-05-10 20:54:36 -070023#include <linux/debugfs.h>
Gareth Williamsf98d4192015-03-11 16:55:41 +000024#include <asm/unaligned.h>
25#include <asm/uaccess.h> /* for put_user */
26#include <linux/inet.h>
27#include <linux/ipv6.h>
28#include <linux/netfilter_bridge.h>
29
30/*
31 * Debug output levels
32 * 0 = OFF
33 * 1 = ASSERTS / ERRORS
34 * 2 = 1 + WARN
35 * 3 = 2 + INFO
36 * 4 = 3 + TRACE
37 */
38#define DEBUG_LEVEL ECM_STATE_DEBUG_LEVEL
39
Gareth Williamsf98d4192015-03-11 16:55:41 +000040#include "ecm_types.h"
41#include "ecm_db_types.h"
Gareth Williamsd5618a82015-05-20 11:13:32 +010042#include "ecm_state.h"
Gareth Williamsf98d4192015-03-11 16:55:41 +000043#include "ecm_tracker.h"
Gareth Williamsf98d4192015-03-11 16:55:41 +000044#include "ecm_front_end_types.h"
Suman Ghosha33d7df2020-01-31 21:45:20 +053045#include "ecm_classifier.h"
Gareth Williamsf98d4192015-03-11 16:55:41 +000046#include "ecm_classifier_default.h"
47#include "ecm_db.h"
48
49/*
50 * Magic numbers
51 */
52#define ECM_STATE_FILE_INSTANCE_MAGIC 0xB3FE
53
54/*
Murat Sezgin908ecb32015-05-10 20:54:36 -070055 * Debugfs dentry object.
Gareth Williamsf98d4192015-03-11 16:55:41 +000056 */
Murat Sezgin908ecb32015-05-10 20:54:36 -070057static struct dentry *ecm_state_dentry;
Gareth Williamsf98d4192015-03-11 16:55:41 +000058
59/*
60 * Locking of the state - concurrency control
61 */
Murat Sezgin908ecb32015-05-10 20:54:36 -070062static DEFINE_SPINLOCK(ecm_state_lock); /* Protect the table from SMP access. */
Gareth Williamsf98d4192015-03-11 16:55:41 +000063
64/*
65 * Character device stuff - used to communicate status back to user space
66 */
Gareth Williamsf98d4192015-03-11 16:55:41 +000067static int ecm_state_dev_major_id = 0; /* Major ID of registered char dev from which we can dump out state to userspace */
68
Gareth Williamsd5618a82015-05-20 11:13:32 +010069/*
70 * Buffer sizes
71 */
72#define ECM_STATE_FILE_PREFIX_SIZE 128
73#define ECM_STATE_FILE_PREFIX_LEVELS_MAX 10
Murat Sezginf95291d2015-06-17 16:55:20 -070074#define ECM_STATE_FILE_BUFFER_SIZE 32768
Gareth Williamsd5618a82015-05-20 11:13:32 +010075
76/*
77 * Output selection flags
78 */
Gareth Williamsf98d4192015-03-11 16:55:41 +000079#define ECM_STATE_FILE_OUTPUT_CONNECTIONS 1
80#define ECM_STATE_FILE_OUTPUT_MAPPINGS 2
81#define ECM_STATE_FILE_OUTPUT_HOSTS 4
82#define ECM_STATE_FILE_OUTPUT_NODES 8
83#define ECM_STATE_FILE_OUTPUT_INTERFACES 16
84#define ECM_STATE_FILE_OUTPUT_CONNECTIONS_CHAIN 32
85#define ECM_STATE_FILE_OUTPUT_MAPPINGS_CHAIN 64
86#define ECM_STATE_FILE_OUTPUT_HOSTS_CHAIN 128
87#define ECM_STATE_FILE_OUTPUT_NODES_CHAIN 256
88#define ECM_STATE_FILE_OUTPUT_INTERFACES_CHAIN 512
89#define ECM_STATE_FILE_OUTPUT_PROTOCOL_COUNTS 1024
Gareth Williamsb39e7c22015-03-25 10:15:33 +000090#ifdef ECM_DB_CTA_TRACK_ENABLE
Gareth Williamsf98d4192015-03-11 16:55:41 +000091#define ECM_STATE_FILE_OUTPUT_CLASSIFIER_TYPE_ASSIGNMENTS 2048
Gareth Williamsb39e7c22015-03-25 10:15:33 +000092#endif
Gareth Williamsf98d4192015-03-11 16:55:41 +000093
Gareth Williamsf98d4192015-03-11 16:55:41 +000094/*
95 * struct ecm_state_file_instance
96 * Structure used as state per open instance of our db state file
97 */
98struct ecm_state_file_instance {
99 int output_mask; /* The content types wanted by the user */
100 struct ecm_db_connection_instance *ci; /* All connections list iterator */
101 struct ecm_db_mapping_instance *mi; /* All mappings list iterator */
102 struct ecm_db_host_instance *hi; /* All hosts list iterator */
103 struct ecm_db_node_instance *ni; /* All nodes list iterator */
104 struct ecm_db_iface_instance *ii; /* All interfaces list iterator */
Gareth Williamsb39e7c22015-03-25 10:15:33 +0000105#ifdef ECM_DB_CTA_TRACK_ENABLE
Gareth Williamsf98d4192015-03-11 16:55:41 +0000106 struct ecm_db_connection_instance *classifier_type_assignments[ECM_CLASSIFIER_TYPES];
107 /* Classifier type connection assignments iterator, one for each classifier type */
Gareth Williamsb39e7c22015-03-25 10:15:33 +0000108#endif
Gareth Williamsf98d4192015-03-11 16:55:41 +0000109 int connection_hash_index; /* Connection hash table lengths iterator */
110 int mapping_hash_index; /* Mapping hash table lengths iterator */
111 int host_hash_index; /* Host hash table lengths iterator */
112 int node_hash_index; /* Node hash table lengths iterator */
113 int iface_hash_index; /* Interface hash table lengths iterator */
114 int protocol; /* Protocol connection count iterator */
Gareth Williamsd5618a82015-05-20 11:13:32 +0100115
116 char prefix[ECM_STATE_FILE_PREFIX_SIZE]; /* This is the prefix added to every message written */
117 int prefix_levels[ECM_STATE_FILE_PREFIX_LEVELS_MAX];
118 /* How many nested prefixes supported */
119 int prefix_level; /* Prefix nest level */
120
121 char msg[ECM_STATE_FILE_BUFFER_SIZE]; /* The message written / being returned to the reader */
122 char *msgp; /* Points into the msg buffer as we output it to the reader piece by piece */
123 int msg_len; /* Length of the msg buffer still to be written out */
Gareth Williamsf98d4192015-03-11 16:55:41 +0000124#if (DEBUG_LEVEL > 0)
125 uint16_t magic;
126#endif
127};
128static int ecm_state_file_output_mask = ECM_STATE_FILE_OUTPUT_CONNECTIONS;
129 /* Bit mask specifies which data to output in the state file */
130
131/*
Gareth Williamsd5618a82015-05-20 11:13:32 +0100132 * ecm_state_write_reset()
133 * Reset the msg buffer, specifying a new initial prefix
134 *
135 * Returns 0 on success
136 */
137int ecm_state_write_reset(struct ecm_state_file_instance *sfi, char *prefix)
138{
139 int result;
140
Kyle Swenson1aa0e0d2021-03-18 14:20:24 -0600141 DEBUG_CHECK_MAGIC(sfi, ECM_STATE_FILE_INSTANCE_MAGIC, "%px: magic failed", sfi);
Gareth Williamsd5618a82015-05-20 11:13:32 +0100142 sfi->msgp = sfi->msg;
143 sfi->msg_len = 0;
144
145 result = snprintf(sfi->prefix, ECM_STATE_FILE_PREFIX_SIZE, "%s", prefix);
146 if ((result < 0) || (result >= ECM_STATE_FILE_PREFIX_SIZE)) {
147 return -1;
148 }
149 sfi->prefix_level = 0;
150 sfi->prefix_levels[sfi->prefix_level] = result;
151 return 0;
152}
153EXPORT_SYMBOL(ecm_state_write_reset);
154
155/*
156 * ecm_state_prefix_add()
157 * Add another level to the prefix
158 *
159 * Returns 0 on success
160 */
161int ecm_state_prefix_add(struct ecm_state_file_instance *sfi, char *prefix)
162{
163 int pxsz;
164 int pxremain;
165 int result;
166
Kyle Swenson1aa0e0d2021-03-18 14:20:24 -0600167 DEBUG_CHECK_MAGIC(sfi, ECM_STATE_FILE_INSTANCE_MAGIC, "%px: magic failed", sfi);
Gareth Williamsd5618a82015-05-20 11:13:32 +0100168
169 pxsz = sfi->prefix_levels[sfi->prefix_level];
170 pxremain = ECM_STATE_FILE_PREFIX_SIZE - pxsz;
171 result = snprintf(sfi->prefix + pxsz, pxremain, ".%s", prefix);
172 if ((result < 0) || (result >= pxremain)) {
173 return -1;
174 }
175
176 sfi->prefix_level++;
177 DEBUG_ASSERT(sfi->prefix_level < ECM_STATE_FILE_PREFIX_LEVELS_MAX, "Bad prefix handling\n");
178 sfi->prefix_levels[sfi->prefix_level] = pxsz + result;
179 return 0;
180}
181EXPORT_SYMBOL(ecm_state_prefix_add);
182
183/*
184 * ecm_state_prefix_index_add()
185 * Add another level (numeric) to the prefix
186 *
187 * Returns 0 on success
188 */
189int ecm_state_prefix_index_add(struct ecm_state_file_instance *sfi, int index)
190{
191 int pxsz;
192 int pxremain;
193 int result;
194
Kyle Swenson1aa0e0d2021-03-18 14:20:24 -0600195 DEBUG_CHECK_MAGIC(sfi, ECM_STATE_FILE_INSTANCE_MAGIC, "%px: magic failed", sfi);
Gareth Williamsd5618a82015-05-20 11:13:32 +0100196
197 pxsz = sfi->prefix_levels[sfi->prefix_level];
198 pxremain = ECM_STATE_FILE_PREFIX_SIZE - pxsz;
199 result = snprintf(sfi->prefix + pxsz, pxremain, ".%d", index);
200 if ((result < 0) || (result >= pxremain)) {
201 return -1;
202 }
203
204 sfi->prefix_level++;
205 DEBUG_ASSERT(sfi->prefix_level < ECM_STATE_FILE_PREFIX_LEVELS_MAX, "Bad prefix handling\n");
206 sfi->prefix_levels[sfi->prefix_level] = pxsz + result;
207 return 0;
208}
209EXPORT_SYMBOL(ecm_state_prefix_index_add);
210
211/*
212 * ecm_state_prefix_remove()
213 * Remove level from the prefix
214 *
215 * Returns 0 on success
216 */
217int ecm_state_prefix_remove(struct ecm_state_file_instance *sfi)
218{
219 int pxsz;
220
Kyle Swenson1aa0e0d2021-03-18 14:20:24 -0600221 DEBUG_CHECK_MAGIC(sfi, ECM_STATE_FILE_INSTANCE_MAGIC, "%px: magic failed", sfi);
Gareth Williamsd5618a82015-05-20 11:13:32 +0100222
223 sfi->prefix_level--;
224 DEBUG_ASSERT(sfi->prefix_level >= 0, "Bad prefix handling\n");
225 pxsz = sfi->prefix_levels[sfi->prefix_level];
226 sfi->prefix[pxsz] = 0;
227 return 0;
228}
229EXPORT_SYMBOL(ecm_state_prefix_remove);
230
231/*
232 * ecm_state_write()
233 * Write out to the message buffer, prefix is added automatically.
234 *
235 * Returns 0 on success
236 */
237int ecm_state_write(struct ecm_state_file_instance *sfi, char *name, char *fmt, ...)
238{
239 int remain;
240 char *ptr;
241 int result;
242 va_list args;
243
Kyle Swenson1aa0e0d2021-03-18 14:20:24 -0600244 DEBUG_CHECK_MAGIC(sfi, ECM_STATE_FILE_INSTANCE_MAGIC, "%px: magic failed", sfi);
Gareth Williamsd5618a82015-05-20 11:13:32 +0100245
246 remain = ECM_STATE_FILE_BUFFER_SIZE - sfi->msg_len;
247 ptr = sfi->msg + sfi->msg_len;
248 result = snprintf(ptr, remain, "%s.%s=", sfi->prefix, name);
249 if ((result < 0) || (result >= remain)) {
250 return -1;
251 }
252
253 sfi->msg_len += result;
254 remain -= result;
255 ptr += result;
256
257 va_start(args, fmt);
258 result = vsnprintf(ptr, remain, fmt, args);
259 va_end(args);
260 if ((result < 0) || (result >= remain)) {
261 return -2;
262 }
263
264 sfi->msg_len += result;
265 remain -= result;
266 ptr += result;
267
268 result = snprintf(ptr, remain, "\n");
269 if ((result < 0) || (result >= remain)) {
270 return -3;
271 }
272
273 sfi->msg_len += result;
274 return 0;
275}
276EXPORT_SYMBOL(ecm_state_write);
277
278/*
Gareth Williamsf98d4192015-03-11 16:55:41 +0000279 * ecm_state_char_dev_conn_msg_prep()
280 * Prepare a connection message
281 */
282static bool ecm_state_char_dev_conn_msg_prep(struct ecm_state_file_instance *sfi)
283{
Gareth Williamsd5618a82015-05-20 11:13:32 +0100284 int result;
Gareth Williamsf98d4192015-03-11 16:55:41 +0000285
Kyle Swenson1aa0e0d2021-03-18 14:20:24 -0600286 DEBUG_TRACE("%px: Prep conn msg for %px\n", sfi, sfi->ci);
Gareth Williamsf98d4192015-03-11 16:55:41 +0000287
Gareth Williamsd5618a82015-05-20 11:13:32 +0100288 if ((result = ecm_state_write_reset(sfi, "conns"))) {
289 return result;
Gareth Williamsf98d4192015-03-11 16:55:41 +0000290 }
Gareth Williamsd5618a82015-05-20 11:13:32 +0100291 return ecm_db_connection_state_get(sfi, sfi->ci);
Gareth Williamsf98d4192015-03-11 16:55:41 +0000292}
293
294/*
295 * ecm_state_char_dev_mapping_msg_prep()
296 * Prepare a mapping message
297 */
298static bool ecm_state_char_dev_mapping_msg_prep(struct ecm_state_file_instance *sfi)
299{
Gareth Williamsd5618a82015-05-20 11:13:32 +0100300 int result;
Gareth Williamsf98d4192015-03-11 16:55:41 +0000301
Kyle Swenson1aa0e0d2021-03-18 14:20:24 -0600302 DEBUG_TRACE("%px: Prep mapping msg for %px\n", sfi, sfi->mi);
Gareth Williamsf98d4192015-03-11 16:55:41 +0000303
Gareth Williamsd5618a82015-05-20 11:13:32 +0100304 if ((result = ecm_state_write_reset(sfi, "mappings"))) {
305 return result;
Gareth Williamsf98d4192015-03-11 16:55:41 +0000306 }
Gareth Williamsd5618a82015-05-20 11:13:32 +0100307 return ecm_db_mapping_state_get(sfi, sfi->mi);
Gareth Williamsf98d4192015-03-11 16:55:41 +0000308}
309
310/*
311 * ecm_state_char_dev_host_msg_prep()
312 * Prepare a host message
313 */
314static bool ecm_state_char_dev_host_msg_prep(struct ecm_state_file_instance *sfi)
315{
Gareth Williamsd5618a82015-05-20 11:13:32 +0100316 int result;
Gareth Williamsf98d4192015-03-11 16:55:41 +0000317
Kyle Swenson1aa0e0d2021-03-18 14:20:24 -0600318 DEBUG_TRACE("%px: Prep host msg for %px\n", sfi, sfi->hi);
Gareth Williamsf98d4192015-03-11 16:55:41 +0000319
Gareth Williamsd5618a82015-05-20 11:13:32 +0100320 if ((result = ecm_state_write_reset(sfi, "hosts"))) {
321 return result;
Gareth Williamsf98d4192015-03-11 16:55:41 +0000322 }
323
Gareth Williamsd5618a82015-05-20 11:13:32 +0100324 return ecm_db_host_state_get(sfi, sfi->hi);
Gareth Williamsf98d4192015-03-11 16:55:41 +0000325}
326
327/*
Gareth Williamsd5618a82015-05-20 11:13:32 +0100328 * ecm_state_char_dev_node_msg_prep()
Gareth Williamsf98d4192015-03-11 16:55:41 +0000329 * Prepare a node message
330 */
331static bool ecm_state_char_dev_node_msg_prep(struct ecm_state_file_instance *sfi)
332{
Gareth Williamsd5618a82015-05-20 11:13:32 +0100333 int result;
Gareth Williamsf98d4192015-03-11 16:55:41 +0000334
Kyle Swenson1aa0e0d2021-03-18 14:20:24 -0600335 DEBUG_TRACE("%px: Prep node msg for %px\n", sfi, sfi->ni);
Gareth Williamsf98d4192015-03-11 16:55:41 +0000336
Gareth Williamsd5618a82015-05-20 11:13:32 +0100337 if ((result = ecm_state_write_reset(sfi, "nodes"))) {
338 return result;
Gareth Williamsf98d4192015-03-11 16:55:41 +0000339 }
340
Gareth Williamsd5618a82015-05-20 11:13:32 +0100341 return ecm_db_node_state_get(sfi, sfi->ni);
Gareth Williamsf98d4192015-03-11 16:55:41 +0000342}
343
344/*
345 * ecm_state_char_dev_iface_msg_prep()
346 * Prepare an interface message
347 */
Gareth Williamsd5618a82015-05-20 11:13:32 +0100348static int ecm_state_char_dev_iface_msg_prep(struct ecm_state_file_instance *sfi)
Gareth Williamsf98d4192015-03-11 16:55:41 +0000349{
Gareth Williamsd5618a82015-05-20 11:13:32 +0100350 int result;
Gareth Williamsf98d4192015-03-11 16:55:41 +0000351
Kyle Swenson1aa0e0d2021-03-18 14:20:24 -0600352 DEBUG_TRACE("%px: Prep iface msg for %px\n", sfi, sfi->ii);
Gareth Williamsf98d4192015-03-11 16:55:41 +0000353
Gareth Williamsd5618a82015-05-20 11:13:32 +0100354 if ((result = ecm_state_write_reset(sfi, "ifaces"))) {
355 return result;
Gareth Williamsf98d4192015-03-11 16:55:41 +0000356 }
Gareth Williamsd5618a82015-05-20 11:13:32 +0100357 return ecm_db_iface_state_get(sfi, sfi->ii);
Gareth Williamsf98d4192015-03-11 16:55:41 +0000358}
359
360/*
361 * ecm_state_char_dev_conn_chain_msg_prep()
362 * Generate an conn hash table chain message
363 */
364static bool ecm_state_char_dev_conn_chain_msg_prep(struct ecm_state_file_instance *sfi)
365{
Gareth Williamsd5618a82015-05-20 11:13:32 +0100366 int result;
Gareth Williamsf98d4192015-03-11 16:55:41 +0000367 int chain_len;
Kyle Swenson1aa0e0d2021-03-18 14:20:24 -0600368 DEBUG_TRACE("%px: Prep conn chain msg\n", sfi);
Gareth Williamsf98d4192015-03-11 16:55:41 +0000369
370 /*
371 * Get hash table chain length
372 */
373 chain_len = ecm_db_connection_hash_table_lengths_get(sfi->connection_hash_index);
374
Gareth Williamsd5618a82015-05-20 11:13:32 +0100375 if ((result = ecm_state_write_reset(sfi, "conn_chain"))) {
376 return result;
Gareth Williamsf98d4192015-03-11 16:55:41 +0000377 }
Gareth Williamsd5618a82015-05-20 11:13:32 +0100378 if ((result = ecm_state_prefix_index_add(sfi, sfi->connection_hash_index))) {
379 return result;
380 }
381 return ecm_state_write(sfi, "length", "%d", chain_len);
Gareth Williamsf98d4192015-03-11 16:55:41 +0000382}
383
384/*
385 * ecm_state_char_dev_mapping_chain_msg_prep()
386 * Generate an mapping hash table chain message
387 */
388static bool ecm_state_char_dev_mapping_chain_msg_prep(struct ecm_state_file_instance *sfi)
389{
Gareth Williamsd5618a82015-05-20 11:13:32 +0100390 int result;
Gareth Williamsf98d4192015-03-11 16:55:41 +0000391 int chain_len;
Kyle Swenson1aa0e0d2021-03-18 14:20:24 -0600392 DEBUG_TRACE("%px: Prep mapping chain msg\n", sfi);
Gareth Williamsf98d4192015-03-11 16:55:41 +0000393
394 /*
395 * Get hash table chain length
396 */
397 chain_len = ecm_db_mapping_hash_table_lengths_get(sfi->mapping_hash_index);
398
Gareth Williamsd5618a82015-05-20 11:13:32 +0100399 if ((result = ecm_state_write_reset(sfi, "mapping_chain"))) {
400 return result;
Gareth Williamsf98d4192015-03-11 16:55:41 +0000401 }
Gareth Williamsd5618a82015-05-20 11:13:32 +0100402 if ((result = ecm_state_prefix_index_add(sfi, sfi->mapping_hash_index))) {
403 return result;
404 }
405 return ecm_state_write(sfi, "length", "%d", chain_len);
Gareth Williamsf98d4192015-03-11 16:55:41 +0000406}
407
408/*
409 * ecm_state_char_dev_host_chain_msg_prep()
410 * Generate an host hash table chain message
411 */
412static bool ecm_state_char_dev_host_chain_msg_prep(struct ecm_state_file_instance *sfi)
413{
Gareth Williamsd5618a82015-05-20 11:13:32 +0100414 int result;
Gareth Williamsf98d4192015-03-11 16:55:41 +0000415 int chain_len;
Kyle Swenson1aa0e0d2021-03-18 14:20:24 -0600416 DEBUG_TRACE("%px: Prep host chain msg\n", sfi);
Gareth Williamsf98d4192015-03-11 16:55:41 +0000417
418 /*
419 * Get hash table chain length
420 */
421 chain_len = ecm_db_host_hash_table_lengths_get(sfi->host_hash_index);
422
Gareth Williamsd5618a82015-05-20 11:13:32 +0100423 if ((result = ecm_state_write_reset(sfi, "host_chain"))) {
424 return result;
Gareth Williamsf98d4192015-03-11 16:55:41 +0000425 }
Gareth Williamsd5618a82015-05-20 11:13:32 +0100426 if ((result = ecm_state_prefix_index_add(sfi, sfi->host_hash_index))) {
427 return result;
428 }
429 return ecm_state_write(sfi, "length", "%d", chain_len);
Gareth Williamsf98d4192015-03-11 16:55:41 +0000430}
431
432/*
433 * ecm_state_char_dev_node_chain_msg_prep()
434 * Generate an node hash table chain message
435 */
436static bool ecm_state_char_dev_node_chain_msg_prep(struct ecm_state_file_instance *sfi)
437{
Gareth Williamsd5618a82015-05-20 11:13:32 +0100438 int result;
Gareth Williamsf98d4192015-03-11 16:55:41 +0000439 int chain_len;
Kyle Swenson1aa0e0d2021-03-18 14:20:24 -0600440 DEBUG_TRACE("%px: Prep node chain msg\n", sfi);
Gareth Williamsf98d4192015-03-11 16:55:41 +0000441
442 /*
443 * Get hash table chain length
444 */
445 chain_len = ecm_db_node_hash_table_lengths_get(sfi->node_hash_index);
446
Gareth Williamsd5618a82015-05-20 11:13:32 +0100447 if ((result = ecm_state_write_reset(sfi, "node_chain"))) {
448 return result;
Gareth Williamsf98d4192015-03-11 16:55:41 +0000449 }
Gareth Williamsd5618a82015-05-20 11:13:32 +0100450 if ((result = ecm_state_prefix_index_add(sfi, sfi->node_hash_index))) {
451 return result;
452 }
453 return ecm_state_write(sfi, "length", "%d", chain_len);
Gareth Williamsf98d4192015-03-11 16:55:41 +0000454}
455
456/*
457 * ecm_state_char_dev_iface_chain_msg_prep()
458 * Generate an interface hash table chain message
459 */
460static bool ecm_state_char_dev_iface_chain_msg_prep(struct ecm_state_file_instance *sfi)
461{
Gareth Williamsd5618a82015-05-20 11:13:32 +0100462 int result;
Gareth Williamsf98d4192015-03-11 16:55:41 +0000463 int chain_len;
Kyle Swenson1aa0e0d2021-03-18 14:20:24 -0600464 DEBUG_TRACE("%px: Prep iface chain msg\n", sfi);
Gareth Williamsf98d4192015-03-11 16:55:41 +0000465
466 /*
467 * Get hash table chain length
468 */
469 chain_len = ecm_db_iface_hash_table_lengths_get(sfi->iface_hash_index);
470
Gareth Williamsd5618a82015-05-20 11:13:32 +0100471 if ((result = ecm_state_write_reset(sfi, "iface_chain"))) {
472 return result;
Gareth Williamsf98d4192015-03-11 16:55:41 +0000473 }
Gareth Williamsd5618a82015-05-20 11:13:32 +0100474 if ((result = ecm_state_prefix_index_add(sfi, sfi->iface_hash_index))) {
475 return result;
476 }
477 return ecm_state_write(sfi, "length", "%d", chain_len);
Gareth Williamsf98d4192015-03-11 16:55:41 +0000478}
479
480/*
481 * ecm_state_char_dev_protocol_count_msg_prep()
482 * Generate a protocol usage message
483 */
484static bool ecm_state_char_dev_protocol_count_msg_prep(struct ecm_state_file_instance *sfi)
485{
Gareth Williamsd5618a82015-05-20 11:13:32 +0100486 int result;
Gareth Williamsf98d4192015-03-11 16:55:41 +0000487 int count;
Kyle Swenson1aa0e0d2021-03-18 14:20:24 -0600488 DEBUG_TRACE("%px: Prep protocol msg\n", sfi);
Gareth Williamsf98d4192015-03-11 16:55:41 +0000489
490 /*
491 * Get protocol connection total count
492 */
493 count = ecm_db_connection_count_by_protocol_get(sfi->protocol);
494
Gareth Williamsd5618a82015-05-20 11:13:32 +0100495 if ((result = ecm_state_write_reset(sfi, "protocol"))) {
496 return result;
Gareth Williamsf98d4192015-03-11 16:55:41 +0000497 }
Gareth Williamsd5618a82015-05-20 11:13:32 +0100498 if ((result = ecm_state_prefix_index_add(sfi, sfi->protocol))) {
499 return result;
500 }
501 return ecm_state_write(sfi, "connections", "%d", count);
Gareth Williamsf98d4192015-03-11 16:55:41 +0000502}
503
Gareth Williamsb39e7c22015-03-25 10:15:33 +0000504#ifdef ECM_DB_CTA_TRACK_ENABLE
Gareth Williamsf98d4192015-03-11 16:55:41 +0000505/*
506 * ecm_state_char_dev_cta_msg_prep()
507 * Generate a classifier type assignment message
508 */
Gareth Williamsd5618a82015-05-20 11:13:32 +0100509static int ecm_state_char_dev_cta_msg_prep(struct ecm_state_file_instance *sfi, ecm_classifier_type_t ca_type)
Gareth Williamsf98d4192015-03-11 16:55:41 +0000510{
Gareth Williamsf98d4192015-03-11 16:55:41 +0000511 struct ecm_db_connection_instance *ci;
Gareth Williamsd5618a82015-05-20 11:13:32 +0100512 int result;
Gareth Williamsf98d4192015-03-11 16:55:41 +0000513
Kyle Swenson1aa0e0d2021-03-18 14:20:24 -0600514 DEBUG_TRACE("%px: Prep classifier type assignment msg: %d\n", sfi, ca_type);
Gareth Williamsf98d4192015-03-11 16:55:41 +0000515
Gareth Williamsf98d4192015-03-11 16:55:41 +0000516 ci = sfi->classifier_type_assignments[ca_type];
Gareth Williamsd5618a82015-05-20 11:13:32 +0100517 if (!ci) {
518 return 0;
519 }
Gareth Williamsf98d4192015-03-11 16:55:41 +0000520
Gareth Williamsd5618a82015-05-20 11:13:32 +0100521 if ((result = ecm_state_write_reset(sfi, "cta"))) {
522 return result;
523 }
524 if ((result = ecm_state_prefix_index_add(sfi, ca_type))) {
525 return result;
526 }
527 if ((result = ecm_state_write(sfi, "conn.serial", "%u", ecm_db_connection_serial_get(ci)))) {
528 return result;
Gareth Williamsf98d4192015-03-11 16:55:41 +0000529 }
530
531 /*
Gareth Williamsd5618a82015-05-20 11:13:32 +0100532 * Prep next connection for when we are called again, releasing this one.
Gareth Williamsf98d4192015-03-11 16:55:41 +0000533 */
Gareth Williamsd5618a82015-05-20 11:13:32 +0100534 sfi->classifier_type_assignments[ca_type] = ecm_db_connection_by_classifier_type_assignment_get_and_ref_next(ci, ca_type);
535 ecm_db_connection_by_classifier_type_assignment_deref(ci, ca_type);
536 return 0;
Gareth Williamsf98d4192015-03-11 16:55:41 +0000537}
538
539/*
540 * ecm_state_file_classifier_type_assignments_release()
541 * Releases any uniterated classifier assignments
542 */
543static void ecm_state_file_classifier_type_assignments_release(struct ecm_state_file_instance *sfi)
544{
545 ecm_classifier_type_t ca_type;
546
547 for (ca_type = 0; ca_type < ECM_CLASSIFIER_TYPES; ++ca_type) {
548 struct ecm_db_connection_instance *ci;
549
550 ci = sfi->classifier_type_assignments[ca_type];
551 if (!ci) {
552 continue;
553 }
554
555 ecm_db_connection_by_classifier_type_assignment_deref(ci, ca_type);
556 }
557}
Gareth Williamsb39e7c22015-03-25 10:15:33 +0000558#endif
Gareth Williamsf98d4192015-03-11 16:55:41 +0000559
560/*
561 * ecm_state_char_device_open()
562 * Opens the special char device file which we use to dump our state.
563 */
564static int ecm_state_char_device_open(struct inode *inode, struct file *file)
565{
566 struct ecm_state_file_instance *sfi;
567
568 DEBUG_INFO("State open\n");
569
570 /*
571 * Allocate state information for the reading
572 */
Kyle Swenson1aa0e0d2021-03-18 14:20:24 -0600573 DEBUG_ASSERT(file->private_data == NULL, "unexpected double open: %px?\n", file->private_data);
Gareth Williamsf98d4192015-03-11 16:55:41 +0000574
575 sfi = (struct ecm_state_file_instance *)kzalloc(sizeof(struct ecm_state_file_instance), GFP_ATOMIC | __GFP_NOWARN);
576 if (!sfi) {
577 return -ENOMEM;
578 }
579 DEBUG_SET_MAGIC(sfi, ECM_STATE_FILE_INSTANCE_MAGIC);
580 file->private_data = sfi;
581
582 /*
583 * Snapshot output mask for this file
584 */
585 spin_lock_bh(&ecm_state_lock);
586 sfi->output_mask = ecm_state_file_output_mask;
587 spin_unlock_bh(&ecm_state_lock);
588
589 /*
590 * Get the first indicies for hash and protocol stats should they be needed.
591 * NOTE: There are no references held here so it does not matter to get them all even if they are not wanted.
592 */
593 sfi->connection_hash_index = ecm_db_connection_hash_index_get_first();
594 sfi->mapping_hash_index = ecm_db_mapping_hash_index_get_first();
595 sfi->host_hash_index = ecm_db_host_hash_index_get_first();
596 sfi->node_hash_index = ecm_db_node_hash_index_get_first();
597 sfi->iface_hash_index = ecm_db_iface_hash_index_get_first();
598 sfi->protocol = ecm_db_protocol_get_first();
599
600 /*
601 * Take references to each object list that we are going to generate state for.
602 */
603 if (sfi->output_mask & ECM_STATE_FILE_OUTPUT_CONNECTIONS) {
604 sfi->ci = ecm_db_connections_get_and_ref_first();
605 }
606 if (sfi->output_mask & ECM_STATE_FILE_OUTPUT_MAPPINGS) {
607 sfi->mi = ecm_db_mappings_get_and_ref_first();
608 }
609 if (sfi->output_mask & ECM_STATE_FILE_OUTPUT_HOSTS) {
610 sfi->hi = ecm_db_hosts_get_and_ref_first();
611 }
612 if (sfi->output_mask & ECM_STATE_FILE_OUTPUT_NODES) {
613 sfi->ni = ecm_db_nodes_get_and_ref_first();
614 }
615 if (sfi->output_mask & ECM_STATE_FILE_OUTPUT_INTERFACES) {
616 sfi->ii = ecm_db_interfaces_get_and_ref_first();
617 }
Gareth Williamsb39e7c22015-03-25 10:15:33 +0000618#ifdef ECM_DB_CTA_TRACK_ENABLE
Gareth Williamsf98d4192015-03-11 16:55:41 +0000619 if (sfi->output_mask & ECM_STATE_FILE_OUTPUT_CLASSIFIER_TYPE_ASSIGNMENTS) {
620 ecm_classifier_type_t ca_type;
621
622 /*
623 * Iterate all classifier type assignments.
624 * Hold the head of each list to start us off on our iterating process.
625 */
626 for (ca_type = 0; ca_type < ECM_CLASSIFIER_TYPES; ++ca_type) {
Gareth Williamsd5618a82015-05-20 11:13:32 +0100627 sfi->classifier_type_assignments[ca_type] = ecm_db_connection_by_classifier_type_assignment_get_and_ref_first(ca_type);
Gareth Williamsf98d4192015-03-11 16:55:41 +0000628 }
629 }
Gareth Williamsb39e7c22015-03-25 10:15:33 +0000630#endif
Gareth Williamsf98d4192015-03-11 16:55:41 +0000631
Kyle Swenson1aa0e0d2021-03-18 14:20:24 -0600632 DEBUG_INFO("State opened %px\n", sfi);
Gareth Williamsf98d4192015-03-11 16:55:41 +0000633
634 return 0;
635}
636
637/*
638 * ecm_state_char_device_release()
639 * Called when a process closes the device file.
640 */
641static int ecm_state_char_device_release(struct inode *inode, struct file *file)
642{
643 struct ecm_state_file_instance *sfi;
644
645 sfi = (struct ecm_state_file_instance *)file->private_data;
Kyle Swenson1aa0e0d2021-03-18 14:20:24 -0600646 DEBUG_CHECK_MAGIC(sfi, ECM_STATE_FILE_INSTANCE_MAGIC, "%px: magic failed", sfi);
647 DEBUG_INFO("%px: State close\n", sfi);
Gareth Williamsf98d4192015-03-11 16:55:41 +0000648
649 /*
650 * Release any references held
651 */
652 if (sfi->ci) {
653 ecm_db_connection_deref(sfi->ci);
654 }
655 if (sfi->mi) {
656 ecm_db_mapping_deref(sfi->mi);
657 }
658 if (sfi->hi) {
659 ecm_db_host_deref(sfi->hi);
660 }
661 if (sfi->ni) {
662 ecm_db_node_deref(sfi->ni);
663 }
664 if (sfi->ii) {
665 ecm_db_iface_deref(sfi->ii);
666 }
Gareth Williamsb39e7c22015-03-25 10:15:33 +0000667#ifdef ECM_DB_CTA_TRACK_ENABLE
Gareth Williamsf98d4192015-03-11 16:55:41 +0000668 ecm_state_file_classifier_type_assignments_release(sfi);
Gareth Williamsb39e7c22015-03-25 10:15:33 +0000669#endif
Gareth Williamsf98d4192015-03-11 16:55:41 +0000670
671 DEBUG_CLEAR_MAGIC(sfi);
672 kfree(sfi);
673
674 return 0;
675}
676
677/*
678 * ecm_state_char_device_read()
679 * Called to read the state
680 */
681static ssize_t ecm_state_char_device_read(struct file *file, /* see include/linux/fs.h */
682 char *buffer, /* buffer to fill with data */
683 size_t length, /* length of the buffer */
684 loff_t *offset) /* Doesn't apply - this is a char file */
685{
686 struct ecm_state_file_instance *sfi;
687 int bytes_read = 0; /* Number of bytes actually written to the buffer */
Gareth Williamsb39e7c22015-03-25 10:15:33 +0000688#ifdef ECM_DB_CTA_TRACK_ENABLE
Gareth Williamsf98d4192015-03-11 16:55:41 +0000689 ecm_classifier_type_t ca_type;
Gareth Williamsb39e7c22015-03-25 10:15:33 +0000690#endif
Gareth Williamsf98d4192015-03-11 16:55:41 +0000691
692 sfi = (struct ecm_state_file_instance *)file->private_data;
Kyle Swenson1aa0e0d2021-03-18 14:20:24 -0600693 DEBUG_CHECK_MAGIC(sfi, ECM_STATE_FILE_INSTANCE_MAGIC, "%px: magic failed", sfi);
694 DEBUG_TRACE("%px: State read up to length %d bytes\n", sfi, (int)length);
Gareth Williamsf98d4192015-03-11 16:55:41 +0000695
696 /*
697 * If there is still some message remaining to be output then complete that first
698 */
699 if (sfi->msg_len) {
700 goto char_device_read_output;
701 }
702
Gareth Williamsf98d4192015-03-11 16:55:41 +0000703 if (sfi->ci) {
704 struct ecm_db_connection_instance *cin;
Gareth Williamsd5618a82015-05-20 11:13:32 +0100705 if (ecm_state_char_dev_conn_msg_prep(sfi)) {
Gareth Williamsf98d4192015-03-11 16:55:41 +0000706 return -EIO;
707 }
708
709 /*
710 * Next connection for when we return
711 */
712 cin = ecm_db_connection_get_and_ref_next(sfi->ci);
713 ecm_db_connection_deref(sfi->ci);
714 sfi->ci = cin;
715
716 goto char_device_read_output;
717 }
718
719 if (sfi->mi) {
720 struct ecm_db_mapping_instance *min;
Gareth Williamsd5618a82015-05-20 11:13:32 +0100721 if (ecm_state_char_dev_mapping_msg_prep(sfi)) {
Gareth Williamsf98d4192015-03-11 16:55:41 +0000722 return -EIO;
723 }
724
725 /*
726 * Next mapping for when we return
727 */
728 min = ecm_db_mapping_get_and_ref_next(sfi->mi);
729 ecm_db_mapping_deref(sfi->mi);
730 sfi->mi = min;
731
732 goto char_device_read_output;
733 }
734
735 if (sfi->hi) {
736 struct ecm_db_host_instance *hin;
Gareth Williamsd5618a82015-05-20 11:13:32 +0100737 if (ecm_state_char_dev_host_msg_prep(sfi)) {
Gareth Williamsf98d4192015-03-11 16:55:41 +0000738 return -EIO;
739 }
740
741 /*
742 * Next host for when we return
743 */
744 hin = ecm_db_host_get_and_ref_next(sfi->hi);
745 ecm_db_host_deref(sfi->hi);
746 sfi->hi = hin;
747
748 goto char_device_read_output;
749 }
750
751 if (sfi->ni) {
752 struct ecm_db_node_instance *nin;
Gareth Williamsd5618a82015-05-20 11:13:32 +0100753 if (ecm_state_char_dev_node_msg_prep(sfi)) {
Gareth Williamsf98d4192015-03-11 16:55:41 +0000754 return -EIO;
755 }
756
757 /*
758 * Next node for when we return
759 */
760 nin = ecm_db_node_get_and_ref_next(sfi->ni);
761 ecm_db_node_deref(sfi->ni);
762 sfi->ni = nin;
763
764 goto char_device_read_output;
765 }
766
767 if (sfi->ii) {
768 struct ecm_db_iface_instance *iin;
Gareth Williamsd5618a82015-05-20 11:13:32 +0100769 if (ecm_state_char_dev_iface_msg_prep(sfi)) {
Gareth Williamsf98d4192015-03-11 16:55:41 +0000770 return -EIO;
771 }
772
773 /*
774 * Next iface for when we return
775 */
776 iin = ecm_db_interface_get_and_ref_next(sfi->ii);
777 ecm_db_iface_deref(sfi->ii);
778 sfi->ii = iin;
779
780 goto char_device_read_output;
781 }
782
783 if ((sfi->output_mask & ECM_STATE_FILE_OUTPUT_CONNECTIONS_CHAIN) && (sfi->connection_hash_index >= 0)) {
Gareth Williamsd5618a82015-05-20 11:13:32 +0100784 if (ecm_state_char_dev_conn_chain_msg_prep(sfi)) {
Gareth Williamsf98d4192015-03-11 16:55:41 +0000785 return -EIO;
786 }
787 sfi->connection_hash_index = ecm_db_connection_hash_index_get_next(sfi->connection_hash_index);
788 goto char_device_read_output;
789 }
790
791 if ((sfi->output_mask & ECM_STATE_FILE_OUTPUT_MAPPINGS_CHAIN) && (sfi->mapping_hash_index >= 0)) {
Gareth Williamsd5618a82015-05-20 11:13:32 +0100792 if (ecm_state_char_dev_mapping_chain_msg_prep(sfi)) {
Gareth Williamsf98d4192015-03-11 16:55:41 +0000793 return -EIO;
794 }
795 sfi->mapping_hash_index = ecm_db_mapping_hash_index_get_next(sfi->mapping_hash_index);
796 goto char_device_read_output;
797 }
798
799 if ((sfi->output_mask & ECM_STATE_FILE_OUTPUT_HOSTS_CHAIN) && (sfi->host_hash_index >= 0)) {
Gareth Williamsd5618a82015-05-20 11:13:32 +0100800 if (ecm_state_char_dev_host_chain_msg_prep(sfi)) {
Gareth Williamsf98d4192015-03-11 16:55:41 +0000801 return -EIO;
802 }
803 sfi->host_hash_index = ecm_db_host_hash_index_get_next(sfi->host_hash_index);
804 goto char_device_read_output;
805 }
806
807 if ((sfi->output_mask & ECM_STATE_FILE_OUTPUT_NODES_CHAIN) && (sfi->node_hash_index >= 0)) {
Gareth Williamsd5618a82015-05-20 11:13:32 +0100808 if (ecm_state_char_dev_node_chain_msg_prep(sfi)) {
Gareth Williamsf98d4192015-03-11 16:55:41 +0000809 return -EIO;
810 }
811 sfi->node_hash_index = ecm_db_node_hash_index_get_next(sfi->node_hash_index);
812 goto char_device_read_output;
813 }
814
815 if ((sfi->output_mask & ECM_STATE_FILE_OUTPUT_INTERFACES_CHAIN) && (sfi->iface_hash_index >= 0)) {
Gareth Williamsd5618a82015-05-20 11:13:32 +0100816 if (ecm_state_char_dev_iface_chain_msg_prep(sfi)) {
Gareth Williamsf98d4192015-03-11 16:55:41 +0000817 return -EIO;
818 }
819 sfi->iface_hash_index = ecm_db_iface_hash_index_get_next(sfi->iface_hash_index);
820 goto char_device_read_output;
821 }
822
823 if ((sfi->output_mask & ECM_STATE_FILE_OUTPUT_PROTOCOL_COUNTS) && (sfi->protocol >= 0)) {
Gareth Williamsd5618a82015-05-20 11:13:32 +0100824 if (ecm_state_char_dev_protocol_count_msg_prep(sfi)) {
Gareth Williamsf98d4192015-03-11 16:55:41 +0000825 return -EIO;
826 }
827 sfi->protocol = ecm_db_protocol_get_next(sfi->protocol);
828 goto char_device_read_output;
829 }
830
Gareth Williamsb39e7c22015-03-25 10:15:33 +0000831#ifdef ECM_DB_CTA_TRACK_ENABLE
Gareth Williamsf98d4192015-03-11 16:55:41 +0000832 for (ca_type = 0; ca_type < ECM_CLASSIFIER_TYPES; ++ca_type) {
Gareth Williamsd5618a82015-05-20 11:13:32 +0100833 if (!sfi->classifier_type_assignments[ca_type]) continue;
834 if (ecm_state_char_dev_cta_msg_prep(sfi, ca_type)) {
Gareth Williamsf98d4192015-03-11 16:55:41 +0000835 return -EIO;
836 }
837 goto char_device_read_output;
838 }
Gareth Williamsb39e7c22015-03-25 10:15:33 +0000839#endif
Gareth Williamsf98d4192015-03-11 16:55:41 +0000840
Gareth Williamsf98d4192015-03-11 16:55:41 +0000841 /*
842 * EOF
843 */
844 return 0;
845
846char_device_read_output:
847
848 /*
849 * If supplied buffer is small we limit what we output
850 */
851 bytes_read = sfi->msg_len;
852 if (bytes_read > length) {
853 bytes_read = length;
854 }
855 if (copy_to_user(buffer, sfi->msgp, bytes_read)) {
856 return -EIO;
857 }
858 sfi->msg_len -= bytes_read;
859 sfi->msgp += bytes_read;
860
861 DEBUG_TRACE("State read done, bytes_read %d bytes\n", bytes_read);
862
863 /*
864 * Most read functions return the number of bytes put into the buffer
865 */
866 return bytes_read;
867}
868
869/*
870 * ecm_state_char_device_write()
871 */
872static ssize_t ecm_state_char_device_write(struct file *filp, const char *buff, size_t len, loff_t * off)
873{
874 return -EINVAL;
875}
876
877/*
878 * File operations used in the char device
879 * NOTE: The char device is a simple file that allows us to dump our connection tracking state
880 */
881static struct file_operations ecm_state_fops = {
882 .read = ecm_state_char_device_read,
883 .write = ecm_state_char_device_write,
884 .open = ecm_state_char_device_open,
885 .release = ecm_state_char_device_release
886};
887
888/*
889 * ecm_state_init()
890 */
Murat Sezgin908ecb32015-05-10 20:54:36 -0700891int ecm_state_init(struct dentry *dentry)
Gareth Williamsf98d4192015-03-11 16:55:41 +0000892{
Murat Sezgin908ecb32015-05-10 20:54:36 -0700893 int result = -1;
Gareth Williamsf98d4192015-03-11 16:55:41 +0000894 DEBUG_INFO("ECM State init\n");
895
Murat Sezgin908ecb32015-05-10 20:54:36 -0700896 ecm_state_dentry = debugfs_create_dir("ecm_state", dentry);
897 if (!ecm_state_dentry) {
898 DEBUG_ERROR("Failed to create ecm state directory in debugfs\n");
899 return -1;
Gareth Williamsf98d4192015-03-11 16:55:41 +0000900 }
901
Murat Sezgin908ecb32015-05-10 20:54:36 -0700902 if (!debugfs_create_u32("state_dev_major", S_IRUGO, ecm_state_dentry,
903 (u32 *)&ecm_state_dev_major_id)) {
904 DEBUG_ERROR("Failed to create ecm state dev major file in debugfs\n");
905 goto init_cleanup;
Gareth Williamsf98d4192015-03-11 16:55:41 +0000906 }
907
Murat Sezgin908ecb32015-05-10 20:54:36 -0700908 if (!debugfs_create_u32("state_file_output_mask", S_IRUGO | S_IWUSR, ecm_state_dentry,
909 (u32 *)&ecm_state_file_output_mask)) {
910 DEBUG_ERROR("Failed to create ecm state output mask file in debugfs\n");
911 goto init_cleanup;
Gareth Williamsf98d4192015-03-11 16:55:41 +0000912 }
913
914 /*
915 * Register a char device that we will use to provide a dump of our state
916 */
Murat Sezgin908ecb32015-05-10 20:54:36 -0700917 result = register_chrdev(0, "ecm_state", &ecm_state_fops);
Gareth Williamsf98d4192015-03-11 16:55:41 +0000918 if (result < 0) {
919 DEBUG_ERROR("Failed to register chrdev %d\n", result);
Murat Sezgin908ecb32015-05-10 20:54:36 -0700920 goto init_cleanup;
Gareth Williamsf98d4192015-03-11 16:55:41 +0000921 }
922 ecm_state_dev_major_id = result;
923 DEBUG_TRACE("registered chr dev major id assigned %d\n", ecm_state_dev_major_id);
924
925 return 0;
926
Murat Sezgin908ecb32015-05-10 20:54:36 -0700927init_cleanup:
Gareth Williamsf98d4192015-03-11 16:55:41 +0000928
Murat Sezgin908ecb32015-05-10 20:54:36 -0700929 debugfs_remove_recursive(ecm_state_dentry);
Gareth Williamsf98d4192015-03-11 16:55:41 +0000930 return result;
931}
932EXPORT_SYMBOL(ecm_state_init);
933
934/*
935 * ecm_state_exit()
936 */
937void ecm_state_exit(void)
938{
Gareth Williamsf98d4192015-03-11 16:55:41 +0000939 DEBUG_INFO("ECM State exit\n");
940
Murat Sezgin908ecb32015-05-10 20:54:36 -0700941 unregister_chrdev(ecm_state_dev_major_id, "ecm_state");
Gareth Williamsf98d4192015-03-11 16:55:41 +0000942
Murat Sezgin908ecb32015-05-10 20:54:36 -0700943 /*
944 * Remove the debugfs files recursively.
945 */
946 if (ecm_state_dentry) {
947 debugfs_remove_recursive(ecm_state_dentry);
Gareth Williamsf98d4192015-03-11 16:55:41 +0000948 }
Gareth Williamsf98d4192015-03-11 16:55:41 +0000949}
950EXPORT_SYMBOL(ecm_state_exit);