diff --git a/tools/labs/.gitignore b/tools/labs/.gitignore index 0f4ec23c13ab1e..6ee5834cfe2b4b 100644 --- a/tools/labs/.gitignore +++ b/tools/labs/.gitignore @@ -1,4 +1,4 @@ -/skels/ + vmlinux zImage serial.pts diff --git a/tools/labs/skels/networking/1-2-netfilter/kernel/Kbuild b/tools/labs/skels/networking/1-2-netfilter/kernel/Kbuild new file mode 100644 index 00000000000000..8d831f886a624c --- /dev/null +++ b/tools/labs/skels/networking/1-2-netfilter/kernel/Kbuild @@ -0,0 +1,3 @@ +EXTRA_CFLAGS = -g + +obj-m = filter.o diff --git a/tools/labs/skels/networking/1-2-netfilter/kernel/filter.c b/tools/labs/skels/networking/1-2-netfilter/kernel/filter.c new file mode 100644 index 00000000000000..65441e01e6b36f --- /dev/null +++ b/tools/labs/skels/networking/1-2-netfilter/kernel/filter.c @@ -0,0 +1,147 @@ +/* + * SO2 - Networking Lab (#10) + * + * Exercise #1, #2: simple netfilter module + * + * Code skeleton. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "filter.h" + +MODULE_DESCRIPTION("Simple netfilter module"); +MODULE_AUTHOR("SO2"); +MODULE_LICENSE("GPL"); + +#define LOG_LEVEL KERN_ALERT +#define MY_DEVICE "filter" + +static struct cdev my_cdev; +static atomic_t ioctl_set; +static unsigned int ioctl_set_addr; + + +/* Test ioctl_set_addr if it has been set. + */ +static int test_daddr(unsigned int dst_addr) +{ + int ret = 0; + + /* TODO 2: return non-zero if address has been set + * *and* matches dst_addr + */ + + return ret; +} + +/* TODO 1: netfilter hook function */ +static unsigned int my_nf_hookfn(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) { + struct iphdr *iph = ip_hdr(skb); + + if (iph->protocol == IPPROTO_TCP) { + struct tcphdr *tcph = tcp_hdr(skb); + printk("IP address is %pI4\n", &iph->saddr); + printk("Port number is %i\n", ntohs(tcph->source)); + } + + return NF_ACCEPT; +} + +static int my_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int my_close(struct inode *inode, struct file *file) +{ + return 0; +} + +static long my_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case MY_IOCTL_FILTER_ADDRESS: + /* TODO 2: set filter address from arg */ + break; + + default: + return -ENOTTY; + } + + return 0; +} + +static const struct file_operations my_fops = { + .owner = THIS_MODULE, + .open = my_open, + .release = my_close, + .unlocked_ioctl = my_ioctl +}; + +/* TODO 1: define netfilter hook operations structure */ +static struct nf_hook_ops ops = { + .hook = my_nf_hookfn, + .hooknum = NF_INET_LOCAL_OUT, + .pf = PF_INET, + .priority = NF_IP_PRI_FIRST +}; + +int __init my_hook_init(void) +{ + int err; + + /* register filter device */ + err = register_chrdev_region(MKDEV(MY_MAJOR, 0), 1, MY_DEVICE); + if (err != 0) + return err; + + atomic_set(&ioctl_set, 0); + ioctl_set_addr = 0; + + /* init & add device */ + cdev_init(&my_cdev, &my_fops); + cdev_add(&my_cdev, MKDEV(MY_MAJOR, 0), 1); + + /* TODO 1: register netfilter hook */ + err = nf_register_net_hook(&init_net, &ops); + if (err != 0) { + pr_info("nf_register_net_hook failed: %d\n", err); + return err; + } + + return 0; + +out: + /* cleanup */ + cdev_del(&my_cdev); + unregister_chrdev_region(MKDEV(MY_MAJOR, 0), 1); + + return err; +} + +void __exit my_hook_exit(void) +{ + /* TODO 1: unregister hook */ + nf_unregister_net_hook(&init_net, &ops); + /* cleanup device */ + cdev_del(&my_cdev); + unregister_chrdev_region(MKDEV(MY_MAJOR, 0), 1); +} + +module_init(my_hook_init); +module_exit(my_hook_exit); diff --git a/tools/labs/skels/networking/1-2-netfilter/kernel/filter.h b/tools/labs/skels/networking/1-2-netfilter/kernel/filter.h new file mode 100644 index 00000000000000..ad2f73c9fd000a --- /dev/null +++ b/tools/labs/skels/networking/1-2-netfilter/kernel/filter.h @@ -0,0 +1,11 @@ +#ifndef _FILTER_H_ +#define _FILTER_H_ + +#include + +/* ioctl command to pass address to filter driver */ +#define MY_IOCTL_FILTER_ADDRESS _IOW('k', 1, unsigned int) + +#define MY_MAJOR 42 + +#endif /* _FILTER_H_ */ diff --git a/tools/labs/skels/networking/1-2-netfilter/user/.gitignore b/tools/labs/skels/networking/1-2-netfilter/user/.gitignore new file mode 100644 index 00000000000000..ee4c92682341e4 --- /dev/null +++ b/tools/labs/skels/networking/1-2-netfilter/user/.gitignore @@ -0,0 +1 @@ +/test diff --git a/tools/labs/skels/networking/1-2-netfilter/user/Makefile b/tools/labs/skels/networking/1-2-netfilter/user/Makefile new file mode 100644 index 00000000000000..0d5af50006725c --- /dev/null +++ b/tools/labs/skels/networking/1-2-netfilter/user/Makefile @@ -0,0 +1,16 @@ +# +# SO2 - Networking Lab (#10) +# +# Makefile for test filter module +# + +CFLAGS = -Wall -static -m32 + +all: test + +test: test.c + +.PHONY: clean + +clean: + -rm -f test *~ *.o diff --git a/tools/labs/skels/networking/1-2-netfilter/user/test-1.sh b/tools/labs/skels/networking/1-2-netfilter/user/test-1.sh new file mode 100644 index 00000000000000..d78c482564c7ee --- /dev/null +++ b/tools/labs/skels/networking/1-2-netfilter/user/test-1.sh @@ -0,0 +1,25 @@ +#!/bin/sh +# +# SO2 - Networking Lab (#10) +# +# Test script for exercise #1 +# + +# insert module +insmod ../kernel/filter.ko || exit 1 + +# listen for connections on localhost, port 60000 (run in background) +../../netcat -l -p 60000 & + +# wait for netcat to start listening +sleep 1 + +# connect to localhost, port 60000, starting a connection using local +# port number 600001; +echo "Should show up in filter." | ../../netcat -q 2 127.0.0.1 60000 + +# look for filter message in dmesg output +echo "Check dmesg output." + +# remove module +rmmod filter || exit 1 diff --git a/tools/labs/skels/networking/1-2-netfilter/user/test-2.sh b/tools/labs/skels/networking/1-2-netfilter/user/test-2.sh new file mode 100644 index 00000000000000..37d07cedb74ace --- /dev/null +++ b/tools/labs/skels/networking/1-2-netfilter/user/test-2.sh @@ -0,0 +1,41 @@ +#!/bin/sh +# +# SO2 - Networking Lab (#10) +# +# Test script for exercise #2 +# + +# insert module +insmod ../kernel/filter.ko || exit 1 + +# set filter IP address to 127.0.0.1 +./test 127.0.0.1 + +# listen for connections on localhost, port 60000 (run in background) +../../netcat -l -p 60000 & + +# wait for netcat to start listening +sleep 1 + +# connect to localhost, port 60000, starting a connection using local +# port number 600001; +echo "Should show up in filter." | ../../netcat -q 2 127.0.0.1 60000 + +# set filter IP address to 127.0.0.2 +./test 127.0.0.2 + +# listen for connections on localhost, port 60000 (run in background) +../../netcat -l -p 60000 & + +# wait for netcat to start listening +sleep 1 + +# connect to localhost, port 60000, starting a connection using local +# port number 600001; +echo "Should NOT show up in filter." | ../../netcat -q 2 127.0.0.1 60000 + +# look for filter message in dmesg output +echo "Check dmesg output." + +# remove module +rmmod filter || exit 1 diff --git a/tools/labs/skels/networking/1-2-netfilter/user/test.c b/tools/labs/skels/networking/1-2-netfilter/user/test.c new file mode 100644 index 00000000000000..775edb458286c7 --- /dev/null +++ b/tools/labs/skels/networking/1-2-netfilter/user/test.c @@ -0,0 +1,73 @@ +/* + * SO2 - Networking Lab (#11) + * + * Test filter module for exercise #2 + * + * Sends MY_IOCTL_FILTER_ADDRESS to filter module. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../kernel/filter.h" + +#define MY_DEVICE "/dev/filter" + + +static void print_usage(char *argv0) +{ + fprintf(stderr, "Usage: %s
\n" + "\taddress must be a string containing " + "an IP dotted address\n", argv0); +} + +int main(int argc, char **argv) +{ + int fd; + unsigned int addr; + + if (argc != 2) { + print_usage(argv[0]); + exit(EXIT_FAILURE); + } + + /* get address */ + addr = inet_addr(argv[1]); + + /* make device node */ + if (mknod(MY_DEVICE, 0644 | S_IFCHR, makedev(MY_MAJOR, 0)) < 0) { + if (errno != EEXIST) { + perror("mknod " MY_DEVICE); + exit(EXIT_FAILURE); + } + } + + /* open device */ + fd = open(MY_DEVICE, O_RDONLY); + if (fd < 0) { + perror("open " MY_DEVICE); + } else { + /* send ioctl */ + if (ioctl(fd, MY_IOCTL_FILTER_ADDRESS, &addr) < 0) + perror("ioctl MY_IOCTL_FILTER_ADDRESS"); + + /* close device */ + if (close(fd) < 0) + perror("close"); + } + + /* cleanup device node */ + if (unlink(MY_DEVICE) < 0) + perror("unlink " MY_DEVICE); + + return 0; +} diff --git a/tools/labs/skels/networking/3-4-tcp-sock/Kbuild b/tools/labs/skels/networking/3-4-tcp-sock/Kbuild new file mode 100644 index 00000000000000..fa55ec98e71d78 --- /dev/null +++ b/tools/labs/skels/networking/3-4-tcp-sock/Kbuild @@ -0,0 +1,3 @@ +EXTRA_CFLAGS = -Wall -g + +obj-m = tcp_sock.o diff --git a/tools/labs/skels/networking/3-4-tcp-sock/tcp_sock.c b/tools/labs/skels/networking/3-4-tcp-sock/tcp_sock.c new file mode 100644 index 00000000000000..b57ec486e241f5 --- /dev/null +++ b/tools/labs/skels/networking/3-4-tcp-sock/tcp_sock.c @@ -0,0 +1,134 @@ +/* + * SO2 - Networking Lab (#10) + * + * Exercise #3, #4: simple kernel TCP socket + * + * Code skeleton. + */ + +#include +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("Simple kernel TCP socket"); +MODULE_AUTHOR("SO2"); +MODULE_LICENSE("GPL"); + +#define LOG_LEVEL KERN_ALERT +#define MY_TCP_PORT 60000 +#define LISTEN_BACKLOG 5 + +#define ON 1 +#define OFF 0 +#define DEBUG ON + +#if DEBUG == ON +#define LOG(s) \ + do { \ + printk(KERN_DEBUG s "\n"); \ + } while (0) +#else +#define LOG(s) \ + do {} while (0) +#endif + +#define print_sock_address(addr) \ + do { \ + printk(LOG_LEVEL "connection established to " \ + "%pI4:%d\n", \ + &addr.sin_addr.s_addr, \ + ntohs(addr.sin_port)); \ + } while (0) + +static struct socket *sock; /* listening (server) socket */ +static struct socket *new_sock; /* communication socket */ + +int __init my_tcp_sock_init(void) +{ + int err; + struct sockaddr_in sock_addr; + /* address to bind on */ + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_port = htons(MY_TCP_PORT), + .sin_addr = { htonl(INADDR_LOOPBACK) } + }; + int addrlen = sizeof(addr); + /* address of peer */ + struct sockaddr_in raddr; + + /* TODO 1: create listening socket */ + err = sock_create_kern(&init_net, PF_INET, SOCK_STREAM, IPPROTO_TCP, &sock); + if (err < 0) { + pr_info("sock_create_kern failed\n"); + return -ENOMEM; + } + + /* TODO 1: bind socket to loopback on port MY_TCP_PORT */ + err = sock->ops->bind(sock, (struct sockaddr *) &addr, addrlen); + if (err < 0) { + pr_info("bind failed\n"); + goto out_release; + } + + /* TODO 1: start listening */ + err = sock->ops->listen(sock, LISTEN_BACKLOG); + if (err < 0) { + pr_info("listen failed\n"); + goto out_release; + } + + /* TODO 2: create new socket for the accepted connection */ + err = sock_create_lite(PF_INET, SOCK_STREAM, IPPROTO_TCP, &new_sock); + if (err < 0) { + pr_info("sock_create_lite failed\n"); + goto out; + } + new_sock->ops = sock->ops; + + /* TODO 2: accept a connection */ + err = sock->ops->accept(sock, new_sock, 0, true); + if (err == -EAGAIN) { + err = sock->ops->accept(sock, new_sock, 0, true); + } + + if (err) { + pr_info("accept failed\n"); + goto out_release_new_sock; + } + + /* TODO 2: get the address of the peer and print it */ + err = sock->ops->getname(new_sock, + (struct sockaddr *)&sock_addr, 1); + if (err < 0) { + printk(LOG_LEVEL "can't find peer name\n"); + goto out_release_new_sock; + } + print_sock_address(sock_addr); + return 0; + +out_release_new_sock: + /* TODO 2: cleanup socket for accepted connection */ + sock_release(new_sock); +out_release: + /* TODO 1: cleanup listening socket */ + sock_release(sock); +out: + return err; +} + +void __exit my_tcp_sock_exit(void) +{ + /* TODO 2: cleanup socket for accepted connection */ + sock_release(new_sock); + + /* TODO 1: cleanup listening socket */ + sock_release(sock); +} + +module_init(my_tcp_sock_init); +module_exit(my_tcp_sock_exit); diff --git a/tools/labs/skels/networking/3-4-tcp-sock/test-3.sh b/tools/labs/skels/networking/3-4-tcp-sock/test-3.sh new file mode 100644 index 00000000000000..b3289dbbe584ef --- /dev/null +++ b/tools/labs/skels/networking/3-4-tcp-sock/test-3.sh @@ -0,0 +1,18 @@ +#!/bin/sh +# +# SO2 - Networking Lab (#10) +# +# Test script for exercise #3 +# + +set -x + +# insert module +insmod tcp_sock.ko || exit 1 + +# list all currently listening servers and active connections +# for both TCP and UDP, and don't resolve hostnames +netstat -tuan + +# remove module +rmmod tcp_sock || exit 1 diff --git a/tools/labs/skels/networking/3-4-tcp-sock/test-4.sh b/tools/labs/skels/networking/3-4-tcp-sock/test-4.sh new file mode 100644 index 00000000000000..345d85356070f4 --- /dev/null +++ b/tools/labs/skels/networking/3-4-tcp-sock/test-4.sh @@ -0,0 +1,29 @@ +#!/bin/sh +# +# SO2 - Networking Lab (#10) +# +# Test script for exercise #3 +# + +set -x + +# insert module (run in background, it waits for a connection) +insmod tcp_sock.ko & + +# wait for module to start listening +sleep 1 + +# list all currently listening servers and active connections +# for both TCP and UDP, and don't resolve hostnames +netstat -tuan + +# connect to localhost, port 60000, starting a connection using local +# port number 600001; +echo "Should connect." | ../netcat -q 4 127.0.0.1 60000 -p 60001 & + +# wait for connection to be established then remove module +# (and close connection) +sleep 3 + +# remove module +rmmod tcp_sock || exit 1 diff --git a/tools/labs/skels/networking/5-udp-sock/Kbuild b/tools/labs/skels/networking/5-udp-sock/Kbuild new file mode 100644 index 00000000000000..e42a05b84ca634 --- /dev/null +++ b/tools/labs/skels/networking/5-udp-sock/Kbuild @@ -0,0 +1,3 @@ +EXTRA_CFLAGS = -Wall -g + +obj-m = udp_sock.o diff --git a/tools/labs/skels/networking/5-udp-sock/test-5.sh b/tools/labs/skels/networking/5-udp-sock/test-5.sh new file mode 100644 index 00000000000000..9db69a99254e18 --- /dev/null +++ b/tools/labs/skels/networking/5-udp-sock/test-5.sh @@ -0,0 +1,26 @@ +#!/bin/sh +# +# SO2 - Networking Lab (#10) +# +# Test script for bonus exercise +# + +set -x + +# listen for UDP packets on localhost, port 60001 (run in background) +../netcat -l -u -p 60001 & + +# get pid of netcat +pid=$! + +# wait for netcat to start listening +sleep 1 + +# insert module, causing the message to be sent +insmod udp_sock.ko + +# remove module +rmmod udp_sock + +# kill netcat +kill $pid 2>/dev/null diff --git a/tools/labs/skels/networking/5-udp-sock/udp_sock.c b/tools/labs/skels/networking/5-udp-sock/udp_sock.c new file mode 100644 index 00000000000000..d42b37cdf19e61 --- /dev/null +++ b/tools/labs/skels/networking/5-udp-sock/udp_sock.c @@ -0,0 +1,130 @@ +/* + * SO2 - Networking Lab (#10) + * + * Bonus: simple kernel UDP socket + * + * Code skeleton. + */ + +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("Simple kernel UDP socket"); +MODULE_AUTHOR("SO2"); +MODULE_LICENSE("GPL"); + +#define LOG_LEVEL KERN_ALERT +#define MY_UDP_LOCAL_PORT 60000 +#define MY_UDP_REMOTE_PORT 60001 +#define MY_TEST_MESSAGE "kernelsocket\n" + +#define ON 1 +#define OFF 0 +#define DEBUG ON + +#if DEBUG == ON +#define LOG(s) \ + do { \ + printk(KERN_DEBUG s "\n"); \ + } while (0) +#else +#define LOG(s) \ + do {} while (0) +#endif + +#define print_sock_address(addr) \ + do { \ + printk(LOG_LEVEL "connection established to " \ + NIPQUAD_FMT ":%d\n", \ + NIPQUAD(addr.sin_addr.s_addr), \ + ntohs(addr.sin_port)); \ + } while (0) + +static struct socket *sock; /* UDP server */ + +/* send datagram */ +static int my_udp_msgsend(struct socket *s) +{ + /* address to send to */ + struct sockaddr_in raddr = { + .sin_family = AF_INET, + .sin_port = htons(MY_UDP_REMOTE_PORT), + .sin_addr = { htonl(INADDR_LOOPBACK) } + }; + int raddrlen = sizeof(raddr); + /* message */ + struct msghdr msg; + struct iovec iov; + char *buffer = MY_TEST_MESSAGE; + int len = strlen(buffer) + 1; + + /* TODO 1: build message */ + iov.iov_base = buffer; + iov.iov_len = len; + + msg.msg_name = &raddr; + msg.msg_namelen = raddrlen; + msg.msg_flags = 0; + msg.msg_control = NULL; + msg.msg_controllen = 0; + + /* TODO 1: send the message down the socket and return the + * error code. + */ + return kernel_sendmsg(s, &msg, (struct kvec *) &iov, 1, len); + + return 0; +} + +int __init my_udp_sock_init(void) +{ + int err; + /* address to bind on */ + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_port = htons(MY_UDP_LOCAL_PORT), + .sin_addr = { htonl(INADDR_LOOPBACK) } + }; + int addrlen = sizeof(addr); + + /* TODO 1: create UDP socket */ + err = sock_create_kern(&init_net, PF_INET, SOCK_DGRAM, IPPROTO_UDP, &sock); + if (err < 0) { + pr_info("sock_create_kern failed\n"); + goto out; + } + /* TODO 1: bind socket to loopback on port MY_UDP_LOCAL_PORT */ + err = sock->ops->bind(sock, (struct sockaddr *) &addr, addrlen); + if (err < 0) { + pr_info("bind failed\n"); + goto out_release; + } + + /* send message */ + err = my_udp_msgsend(sock); + if (err < 0) { + printk(LOG_LEVEL "can't send message\n"); + goto out_release; + } + + return 0; + +out_release: + /* TODO 1: release socket */ + sock_release(sock); +out: + return err; +} + +void __exit my_udp_sock_exit(void) +{ + /* TODO 1: release socket */ + sock_release(sock); +} + +module_init(my_udp_sock_init); +module_exit(my_udp_sock_exit); diff --git a/tools/labs/skels/networking/netcat b/tools/labs/skels/networking/netcat new file mode 100644 index 00000000000000..27bf43d64c2191 Binary files /dev/null and b/tools/labs/skels/networking/netcat differ