-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfail1ban_mod.c
153 lines (108 loc) · 3.86 KB
/
fail1ban_mod.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/ip.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h>
#include <linux/version.h>
#include <linux/netfilter.h>
#include <linux/netlink.h>
#include "ip_str_convert.c"
#define BANNED_IP_MAX 16 //must be power of 2, for line 95
#define PROCFS_MAX_SIZE 32
#define PROCFS_NAME "fail1ban"
static unsigned char ban_tail[256];
static unsigned int readPos, banned_ip[256][BANNED_IP_MAX];
static char procfs_buffer[PROCFS_MAX_SIZE], outBuff[18];
static struct proc_dir_entry *proc_file;
static struct nf_hook_ops hook_ops;
//FILTER HOOK
static int vs_filter(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) {
struct iphdr *iph = ip_hdr(skb); //header of tcp buf
register int ret = 0, idx = iph->saddr & 255; //first octect (for blacklist idx)
for(int i = 0; i < BANNED_IP_MAX; i++) //check if ip is in blacklist
ret += (iph->saddr == banned_ip[idx][i]);
return ret ? NF_DROP : NF_ACCEPT;
}
//CLEAR BANS
static inline void clear_bans(void) {
memset(ban_tail, 0, sizeof(ban_tail));
memset(banned_ip, 0, sizeof(banned_ip));
}
//READ PROC
static ssize_t procfile_read(struct file *file_pointer, char __user *buffer, size_t buffer_length, loff_t *offset) {
unsigned int len, err, written = 0;
readPos = (*offset) ? readPos : 0; //0 if reading first chunk, else rembemer where we left off
for( ; readPos < 256; readPos++) {
for(int pos = 0; pos < BANNED_IP_MAX; pos++) { //read all bans starting with octect `readPos`
if(!banned_ip[readPos][pos])
break;
len = ip_to_str(banned_ip[readPos][pos], outBuff);
outBuff[len++] = '\n';
if(len + written > buffer_length) //filled user's read buff
return written;
err = copy_to_user(buffer + written, outBuff, len);
written += len - err;
*offset = written;
}
}
return written;
}
//WRITE PROC
static ssize_t procfile_write(struct file *file, const char __user *buff, size_t len, loff_t *off) {
//don't allow long msgs. only 1 IP per msg
if(len >= PROCFS_MAX_SIZE)
return -EMSGSIZE;
if(copy_from_user(procfs_buffer, buff, len))
return -EFAULT;
procfs_buffer[len] = 0; //null terminate str
//any str not starting with 0-9 will clear all bans
if(procfs_buffer[0] < '0' || procfs_buffer[0] > '9')
clear_bans();
else { //ban ip
unsigned int ip = str_to_ip(procfs_buffer);
unsigned int idx = ip & 255; //first octet in little-endian order (for blacklist idx)
register int preexisting = 0;
//check if ban already exists
for(int i = 0; i < BANNED_IP_MAX; i++)
preexisting += (banned_ip[idx][i] == ip);
if(!preexisting) {
banned_ip[idx][ban_tail[idx]++] = ip; //ban ip
ban_tail[idx] &= BANNED_IP_MAX - 1;
}
}
//*off += len; //not needed since we don't allow writes larger than one buffer
return len;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)
static const struct proc_ops proc_file_fops = { .proc_read = procfile_read, .proc_write = procfile_write };
#else
static const struct file_operations proc_file_fops = { .read = procfile_read, .write = procfile_write, };
#endif
//INIT MOD
static int __init init_mod(void) {
hook_ops.hook = (nf_hookfn*)vs_filter;
hook_ops.dev = 0;
hook_ops.priv = 0;
hook_ops.pf = NFPROTO_IPV4;
hook_ops.hooknum = NF_INET_LOCAL_IN;
hook_ops.priority = 3;
nf_register_net_hook(&init_net, &hook_ops);
proc_file = proc_create(PROCFS_NAME, 0666, NULL, &proc_file_fops);
if(!proc_file) {
proc_remove(proc_file);
return -ENOMEM;
}
clear_bans(); //zero/init mem
return 0;
}
//EXIT MOD
static void __exit decommission_mod(void) {
nf_unregister_net_hook(&init_net, &hook_ops);
proc_remove(proc_file);
}
module_init(init_mod);
module_exit(decommission_mod);
MODULE_LICENSE("MIT");
MODULE_AUTHOR("Voldrix");
MODULE_DESCRIPTION("Fail1Ban Firewall");