Skip to content

Instantly share code, notes, and snippets.

@ql-owo-lp
Last active December 12, 2015 10:09
Show Gist options
  • Save ql-owo-lp/4756895 to your computer and use it in GitHub Desktop.
Save ql-owo-lp/4756895 to your computer and use it in GitHub Desktop.
CIS644_Lab2_LKM
This is for CIS644_Lab2_LKM part, also the optional part
/* LKM Lab2 Optional task - LWFW++ - by Kevin, 2/8/2013
*
* Based on LWFW written by Owen Klan
*/
#include <linux/string.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/net.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/icmp.h>
#include <linux/inet.h>
#include <asm/errno.h>
#include <asm/uaccess.h>
#include "lwfw.h"
// firewall rules
struct firewall_rule {
struct list_head list; // manage a list of rules using list_head
struct iphdr *ip; // an ip header struture, used for matching src and dst
struct tcphdr *tcp;
struct udphdr *udp;
unsigned int drop;
unsigned int accept;
} rules;
/* Local function prototypes */
static int read_rules(void);
static int append_rule(struct firewall_rule *rule);
static int delete_rule(struct firewall_rule *rule);
static int traverse_firewall_chain(struct sk_buff *skb);
static const char* inet_ntoa(char* ipaddr, unsigned int ip);
/* Some function prototypes to be used by lwfw_fops below. */
static long lwfw_ioctl(struct file *file, unsigned int cmd, unsigned long arg); // changed for Kernel2.6.x
static int lwfw_open(struct inode *inode, struct file *file);
static int lwfw_release(struct inode *inode, struct file *file);
/* Various flags used by the module */
/* This flag makes sure that only one instance of the lwfw device
* can be in use at any one time. */
static int lwfw_ctrl_in_use = 0;
/* This flag marks whether LWFW should actually attempt rule checking.
* If this is zero then LWFW automatically allows all packets. */
static int active = 1;
static int major = 0; /* Control device major number */
/* This struct will describe our hook procedure. */
struct nf_hook_ops lwfw_hook_in, lwfw_hook_out;
/* Actual rule 'definitions'. */
/* TODO: One day LWFW might actually support many simultaneous rules.
* Just as soon as I figure out the list_head mechanism... */
static unsigned int deny_ip = 0x00000000; /* IP address to deny */
static unsigned short deny_port = 0x0000; /* TCP port to deny */
/*
* This is the interface device's file_operations structure
*/
struct file_operations lwfw_fops;
MODULE_AUTHOR("Kevin Wang");
MODULE_DESCRIPTION("(Light-Weight Firewall)++ for Linux 2.4");
MODULE_LICENSE("GPLv3");
/*
* This is the function that will be called by the hook
*/
unsigned int lwfw_hookfn(unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) {
unsigned int ret = NF_ACCEPT;
/* If LWFW is not currently active, immediately return ACCEPT */
if (!active) return NF_ACCEPT;
if (!skb) {
printk("\nLWFW: Unable to load packet");
return NF_ACCEPT;
}
// check IP layer
ret = traverse_firewall_chain(skb);
return ret; /* We are happy to keep the packet */
}
// do filter
static int traverse_firewall_chain(struct sk_buff *skb)
{
char ip_src[16],ip_dst[16];
struct firewall_rule *tmp_rule;
// get ip header
struct iphdr * ip = ip_hdr(skb);
struct udphdr *udp;
struct tcphdr *tcp;
unsigned int sport, dport, defaultAction = NF_ACCEPT;
if (!ip) {
printk("\nLWFW: Not IP packet");
return NF_ACCEPT;
}
inet_ntoa(ip_src, ip->saddr);
inet_ntoa(ip_dst, ip->daddr);
printk("\nLWFW: checking IP src: %s, dst: %s", ip_src, ip_dst);
list_for_each_entry(tmp_rule, &rules.list, list) {
if (!tmp_rule->ip)
continue;
// match ip address
if (tmp_rule->ip->saddr == ip->saddr)
printk(" IP src(%s) matches.", ip_src);
else if (tmp_rule->ip->saddr != 0) // address mismatch, accpet packet
continue;
if (tmp_rule->ip->daddr == ip->daddr)
printk(" IP dst(%s) matches.", ip_dst);
else if (tmp_rule->ip->daddr != 0) // address mismatch, accpet packet
continue;
// start to check Layer 4 protocol: TCP/UDP/ICMP
switch (ip->protocol) {
case IPPROTO_UDP:
if (!tmp_rule->udp)
break;
udp = udp_hdr(skb);
sport = ntohs(udp->source);
dport = ntohs(udp->dest);
printk("\nLWFW: checking UDP sport: %d, dport: %d", sport, dport);
// match ports
if (tmp_rule->udp->dest == udp->dest)
printk(" UDP dport(%d) matches.", dport);
else if (tmp_rule->udp->dest != 0) // port mismatch, accpet packet
continue;
if (tmp_rule->udp->source == udp->source)
printk(" UDP sport(%d) matches.", sport);
else if (tmp_rule->udp->source != 0) // port mismatch, accpet packet
continue;
break;
case IPPROTO_TCP:
if (!tmp_rule->tcp)
break;
tcp = tcp_hdr(skb);
sport = ntohs(tcp->source);
dport = ntohs(tcp->dest);
printk("\nLWFW: checking TCP sport: %d, dport: %d", sport, dport);
// match ports
if (tmp_rule->tcp->dest == tcp->dest)
printk(" TCP dport(%d) matches.", dport);
else if (tmp_rule->tcp->dest != 0) // port mismatch, accpet packet
continue;
if (tmp_rule->tcp->source == tcp->source)
printk(" TCP sport(%d) matches.", sport);
else if (tmp_rule->tcp->source != 0) // port mismatch, accpet packet
continue;
break;
default:
break; // do nothing
}
// well, seems this rule match the packet, drop it
return NF_DROP;
}
return defaultAction;
}
// get ip address from int (linux/inet.h does not provide this function)
static const char* inet_ntoa(char* ipaddr, unsigned int ip) {
sprintf(ipaddr,"%d.%d.%d.%d",ip & 0x000000FF, (ip & 0x0000FF00) >> 8,
(ip & 0x00FF0000) >> 16, (ip & 0xFF000000) >> 24);
return ipaddr;
}
static int append_rule(struct firewall_rule *rule) {
list_add_tail(&(rule->list), &(rules.list));
return 0;
}
static int delete_rule(struct firewall_rule *rule) {
list_del(&(rule->list));
if (rule->ip)
kfree(rule->ip);
if (rule->tcp)
kfree(rule->tcp);
if (rule->udp)
kfree(rule->udp);
kfree(rule);
return 0;
}
/*********************************************/
/*
* File operations functions for control device
*/
static long lwfw_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int ret = 0;
switch (cmd) {
case LWFW_GET_VERS:
return LWFW_VERS;
case LWFW_ACTIVATE:
active = 1;
printk("\nLWFW: Activated.");
if (!deny_ip && !deny_port)
printk("\nLWFW: No deny options set.");
break;
case LWFW_DEACTIVATE:
active ^= active;
printk("\nLWFW: Deactivated.");
break;
default:
ret = -EBADRQC;
};
return ret;
}
/* Called whenever open() is called on the device file */
static int lwfw_open(struct inode *inode, struct file *file)
{
if (lwfw_ctrl_in_use) {
return -EBUSY;
} else {
// MOD_INC_USE_COUNT;
lwfw_ctrl_in_use++;
return 0;
}
return 0;
}
/* Called whenever close() is called on the device file */
static int lwfw_release(struct inode *inode, struct file *file) {
lwfw_ctrl_in_use ^= lwfw_ctrl_in_use;
return 0;
}
static int read_rules(void) {
struct firewall_rule *tmp_rule;
// insert hard-coded rules here. actually we can read from cmd as well
// rule#1 ufw deny out to 66.220.152.19 port 80 proto tcp
tmp_rule = (struct firewall_rule *) kmalloc(sizeof(struct firewall_rule), GFP_KERNEL);
memset(&tmp_rule, 0, sizeof(struct firewall_rule));
if (!tmp_rule) // memory allocation fail
return 0;
INIT_LIST_HEAD(&tmp_rule->list);
tmp_rule->ip = (struct iphdr *) kmalloc(sizeof(struct iphdr), GFP_KERNEL);
memset(&tmp_rule->ip, 0, sizeof(struct iphdr));
tmp_rule->ip->daddr = in_aton("66.220.152.19");
tmp_rule->tcp = (struct tcphdr *) kmalloc(sizeof(struct tcphdr), GFP_KERNEL);
memset(&tmp_rule->tcp, 0, sizeof(struct tcphdr));
tmp_rule->tcp->dest = htons(80);
append_rule(tmp_rule);
// rule#2 ufw deny out to 66.220.152.19 port 443 proto tcp
tmp_rule = (struct firewall_rule *) kmalloc(sizeof(struct firewall_rule), GFP_KERNEL);
memset(&tmp_rule, 0, sizeof(struct firewall_rule));
if (!tmp_rule) // memory allocation fail
return 0;
INIT_LIST_HEAD(&tmp_rule->list);
tmp_rule->ip = (struct iphdr *) kmalloc(sizeof(struct iphdr), GFP_KERNEL);
memset(&tmp_rule->ip, 0, sizeof(struct iphdr));
tmp_rule->ip->daddr = in_aton("66.220.152.19");
tmp_rule->tcp = (struct tcphdr *) kmalloc(sizeof(struct tcphdr), GFP_KERNEL);
memset(&tmp_rule->tcp, 0, sizeof(struct tcphdr));
tmp_rule->tcp->dest = htons(443);
append_rule(tmp_rule);
// rule#3 ufw deny from 10.219.219.203 to 192.168.1.2 port 23 proto tcp
tmp_rule = (struct firewall_rule *) kmalloc(sizeof(struct firewall_rule), GFP_KERNEL);
memset(&tmp_rule, 0, sizeof(struct firewall_rule));
if (!tmp_rule) // memory allocation fail
return 0;
INIT_LIST_HEAD(&tmp_rule->list);
tmp_rule->ip = (struct iphdr *) kmalloc(sizeof(struct iphdr), GFP_KERNEL);
memset(&tmp_rule->ip, 0, sizeof(struct iphdr));
tmp_rule->ip->saddr = in_aton("10.219.219.203");
tmp_rule->ip->daddr = in_aton("192.168.1.2");
tmp_rule->tcp = (struct tcphdr *) kmalloc(sizeof(struct tcphdr), GFP_KERNEL);
memset(&tmp_rule->tcp, 0, sizeof(struct tcphdr));
tmp_rule->tcp->dest = htons(23);
append_rule(tmp_rule);
// rule#4 ufw deny out to 10.219.219.203 port 23 proto tcp
tmp_rule = (struct firewall_rule *) kmalloc(sizeof(struct firewall_rule), GFP_KERNEL);
memset(&tmp_rule, 0, sizeof(struct firewall_rule));
if (!tmp_rule) // memory allocation fail
return 0;
INIT_LIST_HEAD(&tmp_rule->list);
tmp_rule->ip = (struct iphdr *) kmalloc(sizeof(struct iphdr), GFP_KERNEL);
memset(&tmp_rule->ip, 0, sizeof(struct iphdr));
tmp_rule->ip->daddr = in_aton("10.219.219.203");
tmp_rule->tcp = (struct tcphdr *) kmalloc(sizeof(struct tcphdr), GFP_KERNEL);
memset(&tmp_rule->tcp, 0, sizeof(struct tcphdr));
tmp_rule->tcp->dest = htons(23);
append_rule(tmp_rule);
// rule#1 ufw deny out to 8.8.8.8 port 53 proto ucp
// a sample rule for block DNS service of 8.8.8.8
tmp_rule = (struct firewall_rule *) kmalloc(sizeof(struct firewall_rule), GFP_KERNEL);
memset(&tmp_rule, 0, sizeof(struct firewall_rule));
if (!tmp_rule) // memory allocation fail
return 0;
INIT_LIST_HEAD(&tmp_rule->list);
tmp_rule->ip = (struct iphdr *) kmalloc(sizeof(struct iphdr), GFP_KERNEL);
memset(&tmp_rule->ip, 0, sizeof(struct iphdr));
tmp_rule->ip->daddr = in_aton("8.8.8.8");
tmp_rule->udp = (struct udphdr *) kmalloc(sizeof(struct udphdr), GFP_KERNEL);
memset(&tmp_rule->udp, 0, sizeof(struct udphdr));
tmp_rule->udp->dest = htons(53);
append_rule(tmp_rule);
return 0;
}
/*********************************************/
/*
* Module initialisation and cleanup follow...
*/
int init_module() {
lwfw_fops.unlocked_ioctl= &lwfw_ioctl;
lwfw_fops.open=&lwfw_open;
lwfw_fops.release=&lwfw_release;
/* Attempt to register the LWFW control device */
if ((major = register_chrdev(LWFW_MAJOR, LWFW_NAME, &lwfw_fops)) < 0) {
printk("\nLWFW: Failed registering control device!");
printk("\nLWFW: Module installation aborted.");
return major;
}
/* Make sure the usage marker for the control device is cleared */
lwfw_ctrl_in_use ^= lwfw_ctrl_in_use;
printk("\nLWFW: Control device successfully registered.");
/* Now register the network hooks */
lwfw_hook_in.hook = lwfw_hookfn;
lwfw_hook_in.hooknum = NF_INET_PRE_ROUTING;
lwfw_hook_in.pf = PF_INET;
lwfw_hook_in.priority = NF_IP_PRI_FIRST;
nf_register_hook(&lwfw_hook_in);
lwfw_hook_out.hook = lwfw_hookfn;
lwfw_hook_out.hooknum = NF_INET_POST_ROUTING;
lwfw_hook_out.pf = PF_INET;
lwfw_hook_out.priority = NF_IP_PRI_FIRST;
nf_register_hook(&lwfw_hook_out);
// init rules
printk("\nLWFW: Init firewall rules.");
INIT_LIST_HEAD(&rules.list);
read_rules();
printk("\nLWFW: Module installation successful.");
return 0;
}
void cleanup_module() {
struct list_head *pos, *q;
/* Remove IPV4 hook */
nf_unregister_hook(&lwfw_hook_in);
nf_unregister_hook(&lwfw_hook_out);
// free firewall rules chain
printk("\nLWFW: Empty firewall rules.");
list_for_each_safe(pos, q, &rules.list)
delete_rule(list_entry(pos, struct firewall_rule, list));
/* Now unregister control device */
unregister_chrdev(LWFW_MAJOR, LWFW_NAME);
printk("\nLWFW: Removal of module successful.");
}
/* Include file for the Light-weight Fire Wall LKM.
*
* A very simple Netfilter module that drops backets based on either
* their incoming interface or source IP address.
*
* Written by Owen Klan - March 2003
*/
#ifndef __LWFW_INCLUDE__
# define __LWFW_INCLUDE__
/* NOTE: The LWFW_MAJOR symbol is only made available for kernel code.
* Userspace code has no business knowing about it. */
# define LWFW_NAME "lwfw"
/* Version of LWFW */
# define LWFW_VERS 0x0001 /* 0.1 */
/* Definition of the LWFW_TALKATIVE symbol controls whether LWFW will
* print anything with printk(). This is included for debugging purposes.
*/
#define LWFW_TALKATIVE
/* These are the IOCTL codes used for the control device */
#define LWFW_CTRL_SET 0xFEED0000 /* The 0xFEED... prefix is arbitrary */
#define LWFW_GET_VERS 0xFEED0001 /* Get the version of LWFM */
#define LWFW_ACTIVATE 0xFEED0002
#define LWFW_DEACTIVATE 0xFEED0003
#define LWFW_GET_STATS 0xFEED0004
#define LWFW_DENY_IP 0xFEED0006
#define LWFW_DENY_PORT 0xFEED0007
/* Control flags/Options */
#define LWFW_IP_DENY_ACTIVE 0x00000002
#define LWFW_PORT_DENY_ACTIVE 0x00000004
/*
* From here on is used solely for the actual kernel module
*/
#ifdef __KERNEL__
# define LWFW_MAJOR 241 /* This exists in the experimental range */
/* This macro is used to prevent dereferencing of NULL pointers. If
* a pointer argument is NULL, this will return -EINVAL */
#define NULL_CHECK(ptr) \
if ((ptr) == NULL) return -EINVAL
/* Macros for accessing options */
#define DENY_IP_ACTIVE (lwfw_options & LWFW_IP_DENY_ACTIVE)
#define DENY_PORT_ACTIVE (lwfw_options & LWFW_PORT_DENY_ACTIVE)
#endif /* __KERNEL__ */
#endif
obj-m += lwfw.o
#EXTRA_CFLAGS += -I/usr/include
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment