/*
Prevajanje: make
Namestitev v jedro: insmod tcp_times.ko
Uporaba v C:
#include <stdint.h>
#include "tcp_times.h"
int tcp_times = open("/proc/tcp_times", O_RDWR);
if (tcp_times == -1) {
perror("open tcp_times");
break;
}
int tcpsock = accept(boundsocket, &address, &addrlen);
struct tcp_times tt = {
.fd = tcpsock
};
if (ioctl(tcp_times, 0, &tt) == -1) {
perror("ioctl tcp_times");
break;
}
// sedaj so polja v tt populirana
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/net.h>
#include <linux/tcp.h>
#include <linux/version.h>
#include "tcp_times.h"
#define MIN(a,b) (((a)<(b))?(a):(b))
MODULE_AUTHOR("Anton Luka Šijanec <anton@sijanec.eu>");
MODULE_DESCRIPTION("tcp last received tsval, rtt procfs ioctl driver");
MODULE_LICENSE("");
static struct proc_dir_entry * ent;
static long myioctl (struct file * filep, unsigned int cmd, unsigned long arg) {
switch(cmd) {
case 0:
struct tcp_times tt;
if (copy_from_user(&tt, (void *) arg, sizeof tt))
return -EFAULT;
struct fd f = fdget(tt.fd);
if (!f.file)
return -EBADF;
struct socket * sock = sock_from_file(f.file);
if (!sock) {
fdput(f);
return -ENOTSOCK;
}
if (!(sock->type & SOCK_STREAM)) {
fdput(f);
return -ENOSTR;
}
if (!sock->sk) {
fdput(f);
return -EBADFD;
}
if (sock->sk->sk_protocol != IPPROTO_TCP) {
fdput(f);
return -ESOCKTNOSUPPORT;
}
struct tcp_sock * tp = tcp_sk(sock->sk);
tt.ts_recent_stamp = tp->rx_opt.ts_recent_stamp;
tt.ts_recent = tp->rx_opt.ts_recent;
tt.rcv_tsval = tp->rx_opt.rcv_tsval;
tt.rcv_tsecr = tp->rx_opt.rcv_tsecr;
tt.saw_tstamp = tp->rx_opt.saw_tstamp;
tt.tstamp_ok = tp->rx_opt.tstamp_ok;
tt.dsack = tp->rx_opt.dsack;
tt.wscale_ok = tp->rx_opt.wscale_ok;
tt.sack_ok = tp->rx_opt.sack_ok;
tt.smc_ok = tp->rx_opt.smc_ok;
tt.snd_wscale = tp->rx_opt.snd_wscale;
tt.rcv_wscale = tp->rx_opt.rcv_wscale;
tt.advmss = tp->advmss;
tt.rttvar_us = tp->rttvar_us;
tt.srtt_us = tp->srtt_us;
tt.rcv_rtt_est.rtt_us = tp->rcv_rtt_est.rtt_us;
tt.rcv_rtt_est.seq = tp->rcv_rtt_est.seq;
tt.rcv_rtt_est.time = tp->rcv_rtt_est.time;
tt.rtt_us = tp->rack.rtt_us;
tt.mdev_max_us = tp->mdev_max_us;
fdput(f);
if (copy_to_user((void *) arg, &tt, sizeof tt)) {
fdput(f);
return -EFAULT;
}
return 0;
default:
return -EINVAL;
}
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,6,0)
static const struct file_operations ops = {
.owner = THIS_MODULE,
.unlocked_ioctl = myioctl,
};
#else
static const struct proc_ops ops = {
.proc_ioctl = myioctl,
};
#endif
static int __init custom_init (void) {
ent = proc_create("tcp_times", 0666, NULL, &ops);
if (!ent) {
printk(KERN_INFO "tcp_times failed to create procfs entry.");
return -EINVAL;
}
printk(KERN_INFO "tcp_times kernel module loaded.");
return 0;
}
static void __exit custom_exit (void) {
proc_remove(ent);
printk(KERN_INFO "tcp_times kernel module exiting ...");
}
module_init(custom_init);
module_exit(custom_exit);