diff options
-rw-r--r-- | .gitignore | 9 | ||||
-rw-r--r-- | Makefile | 19 | ||||
-rw-r--r-- | qtctl.c | 36 | ||||
-rw-r--r-- | quicktun.c | 321 | ||||
-rw-r--r-- | quicktun.h | 33 |
5 files changed, 418 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8b3e06b --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +.* +*~ +Module.symvers +modules.order +qtctl +*.o +*.ko +*.mod.c + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a26b6e8 --- /dev/null +++ b/Makefile @@ -0,0 +1,19 @@ +ifneq ($(KERNELRELEASE),) +obj-m := quicktun.o + +else +KDIR := /lib/modules/$(shell uname -r)/build +PWD := $(shell pwd) + +all: qtctl + $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules + +qtctl: qtctl.c quicktun.h + cc -o qtctl qtctl.c -lnl +endif + + +.PHONY: clean + +clean: + rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c *.markers *.symvers *.order @@ -0,0 +1,36 @@ +#include <netlink/netlink.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/ctrl.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include "quicktun.h" + + +int main() +{ + struct nl_handle *sock; + struct nl_msg *msg; + struct nl_cb *cb; + int family; + + sock = nl_handle_alloc(); + genl_connect(sock); + family = genl_ctrl_resolve(sock, "quicktun"); + + msg = nlmsg_alloc(); + genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, NLM_F_ECHO, QUICKTUN_CMD_CREATE_DEVICE, 1); + nla_put_u16(msg, QUICKTUN_A_TYPE, QUICKTUN_TAP_DEV); + nla_put_u32(msg, QUICKTUN_A_REMOTE_ADDRESS, ntohl(inet_addr("192.168.0.2"))); + + nl_send_auto_complete(sock, msg); + + nlmsg_free(msg); + + cb = nl_cb_alloc(NL_CB_DEFAULT); + nl_cb_err(cb, NL_CB_DEBUG, NULL, NULL); + + nl_recvmsgs(sock, cb); + + return 0; +} diff --git a/quicktun.c b/quicktun.c new file mode 100644 index 0000000..9d1f722 --- /dev/null +++ b/quicktun.c @@ -0,0 +1,321 @@ +#include <linux/etherdevice.h> +#include <linux/ethtool.h> +#include <linux/if_arp.h> +#include <linux/netdevice.h> +#include <net/genetlink.h> +#include <net/sock.h> + +#include "quicktun.h" + + +#define DRV_NAME "quicktun" +#define DRV_VERSION "0.1" +#define DRV_DESCRIPTION "Quicktun tunnel driver" + +#define QUICKTUN_READQ_SIZE 500 + +#define QUICKTUN_TYPE_MASK 0x000f +#define QUICKTUN_FLAG_REMOTE_FLOAT 0x0010 + +#define MIN_MTU 68 +#define MAX_MTU 65535 + + +struct quicktun_struct { + struct net_device *dev; + unsigned long flags; + + struct sockaddr_in local_address; + struct sockaddr_in remote_address; + + struct socket *sock; +}; + + +static void quicktun_net_uninit(struct net_device *dev) +{ +} + +static netdev_tx_t quicktun_net_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct quicktun_struct *tun = netdev_priv(dev); + struct msghdr msg; + struct kvec vec; + + if (tun->remote_address.sin_addr.s_addr) { + vec.iov_base = skb->data; + vec.iov_len = skb->len; + + msg.msg_name = &tun->remote_address; + msg.msg_namelen = sizeof(tun->remote_address); + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = MSG_DONTWAIT; + + kernel_sendmsg(tun->sock, &msg, &vec, 1, skb->len); + } + + return NETDEV_TX_OK; +} + +static int +quicktun_net_change_mtu(struct net_device *dev, int new_mtu) +{ + if (new_mtu < MIN_MTU || new_mtu + dev->hard_header_len > MAX_MTU) + return -EINVAL; + dev->mtu = new_mtu; + return 0; +} + + +static const struct net_device_ops quicktun_tun_netdev_ops = { + .ndo_uninit = quicktun_net_uninit, + .ndo_start_xmit = quicktun_net_xmit, + .ndo_change_mtu = quicktun_net_change_mtu, +}; + +static const struct net_device_ops quicktun_tap_netdev_ops = { + .ndo_uninit = quicktun_net_uninit, + .ndo_start_xmit = quicktun_net_xmit, + .ndo_change_mtu = quicktun_net_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + + +static void quicktun_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) +{ + struct quicktun_struct *tun = netdev_priv(dev); + + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + strcpy(info->fw_version, "N/A"); + + switch (tun->flags & QUICKTUN_TYPE_MASK) { + case QUICKTUN_TUN_DEV: + strcpy(info->bus_info, "tun"); + break; + case QUICKTUN_TAP_DEV: + strcpy(info->bus_info, "tap"); + break; + } +} + +static u32 quicktun_always_on(struct net_device *dev) +{ + return 1; +} + + +static const struct ethtool_ops quicktun_ethtool_ops = { + .get_drvinfo = quicktun_get_drvinfo, + .get_link = quicktun_always_on, +}; + + +static void quicktun_free_netdev(struct net_device *dev) +{ +} + +static void quicktun_setup(struct net_device *dev) +{ + struct quicktun_struct *tun = netdev_priv(dev); + + tun->local_address.sin_family = AF_INET; + tun->local_address.sin_addr.s_addr = 0; + tun->local_address.sin_port = htons(QUICKTUN_DEFAULT_PORT); + tun->remote_address.sin_family = AF_INET; + tun->remote_address.sin_addr.s_addr = 0; + tun->remote_address.sin_port = htons(QUICKTUN_DEFAULT_PORT); + + dev->ethtool_ops = &quicktun_ethtool_ops; + dev->destructor = quicktun_free_netdev; +} + +static void quicktun_net_init(struct net_device *dev) +{ + struct quicktun_struct *tun = netdev_priv(dev); + + switch (tun->flags & QUICKTUN_TYPE_MASK) { + case QUICKTUN_TUN_DEV: + dev->netdev_ops = &quicktun_tun_netdev_ops; + + dev->hard_header_len = 0; + dev->addr_len = 0; + dev->mtu = 1500; + + dev->type = ARPHRD_NONE; + dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; + break; + + case QUICKTUN_TAP_DEV: + dev->netdev_ops = &quicktun_tap_netdev_ops; + + ether_setup(dev); + + random_ether_addr(dev->dev_addr); + break; + } +} + +static int quicktun_socket_init(struct net_device *dev) +{ + int err; + struct quicktun_struct *tun = netdev_priv(dev); + + + err = sock_create_kern(AF_INET, SOCK_DGRAM, 0, &tun->sock); + if (err < 0) + return err; + + err = kernel_bind(tun->sock, (struct sockaddr*)&tun->local_address, sizeof(tun->local_address)); + if (err < 0) + goto error; + + return 0; + error: + sock_release(tun->sock); + return err; +} + +static int quicktun_cmd_create_device(struct sk_buff *skb, struct genl_info *info) +{ + unsigned long flags = 0; + __u16 type = 0; + struct quicktun_struct *tun; + struct net *net = genl_info_net(info); + struct net_device *dev; + char *name = "qt%d"; + int err; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + if (info->attrs[QUICKTUN_A_TYPE]) + type = nla_get_u16(info->attrs[QUICKTUN_A_TYPE]); + + switch (type) { + case QUICKTUN_TUN_DEV: + case QUICKTUN_TAP_DEV: + flags |= type; + break; + default: + return -EINVAL; + } + + if (info->attrs[QUICKTUN_A_IFNAME]) + name = nla_data(info->attrs[QUICKTUN_A_IFNAME]); + + dev = alloc_netdev(sizeof(struct quicktun_struct), name, quicktun_setup); + + if (!dev) + return -ENOMEM; + + dev_net_set(dev, net); + + tun = netdev_priv(dev); + tun->dev = dev; + tun->flags = flags; + + quicktun_net_init(dev); + + if (info->attrs[QUICKTUN_A_LOCAL_ADDRESS]) + tun->local_address.sin_addr.s_addr = htonl(nla_get_u32(info->attrs[QUICKTUN_A_LOCAL_ADDRESS])); + + /* Set remote port to local port by default */ + if (info->attrs[QUICKTUN_A_LOCAL_PORT]) + tun->remote_address.sin_port = tun->local_address.sin_port = htons(nla_get_u16(info->attrs[QUICKTUN_A_LOCAL_PORT])); + + if (info->attrs[QUICKTUN_A_REMOTE_ADDRESS]) + tun->remote_address.sin_addr.s_addr = htonl(nla_get_u32(info->attrs[QUICKTUN_A_REMOTE_ADDRESS])); + + if (info->attrs[QUICKTUN_A_REMOTE_PORT]) + tun->remote_address.sin_port = htons(nla_get_u16(info->attrs[QUICKTUN_A_REMOTE_PORT])); + + if (info->attrs[QUICKTUN_A_REMOTE_FLOAT]) { + if(nla_get_flag(info->attrs[QUICKTUN_A_REMOTE_FLOAT])) + tun->flags |= QUICKTUN_FLAG_REMOTE_FLOAT; + else + tun->flags &= ~QUICKTUN_FLAG_REMOTE_FLOAT; + } + + err = quicktun_socket_init(dev); + if (err < 0) + goto err_free_dev; + + if (strchr(dev->name, '%')) { + err = dev_alloc_name(dev, dev->name); + if (err < 0) + goto err_free_sk; + } + + err = register_netdevice(tun->dev); + if (err < 0) + goto err_free_sk; + + return 0; + + err_free_sk: + err_free_dev: + free_netdev(dev); + return err; +} + +static int quicktun_cmd_destroy_device(struct sk_buff *skb, struct genl_info *info) +{ + return 0; +} + + +static struct genl_family quicktun_nl_family = { + .id = GENL_ID_GENERATE, + .name = DRV_NAME, + .version = 1, + .hdrsize = 0, + .maxattr = QUICKTUN_A_MAX, +}; + + +static struct nla_policy quicktun_nl_policy[__QUICKTUN_A_MAX] = { + [QUICKTUN_A_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 }, + [QUICKTUN_A_TYPE] = { .type = NLA_U16 }, + [QUICKTUN_A_LOCAL_ADDRESS] = { .type = NLA_U32 }, + [QUICKTUN_A_LOCAL_PORT] = { .type = NLA_U16 }, + [QUICKTUN_A_REMOTE_ADDRESS] = { .type = NLA_U32 }, + [QUICKTUN_A_REMOTE_PORT] = { .type = NLA_U16 }, + [QUICKTUN_A_REMOTE_FLOAT] = { .type = NLA_FLAG }, +}; + +static struct genl_ops quicktun_nl_ops[] = { + { + .cmd = QUICKTUN_CMD_CREATE_DEVICE, + .doit = quicktun_cmd_create_device, + .policy = quicktun_nl_policy, + }, + { + .cmd = QUICKTUN_CMD_DESTROY_DEVICE, + .doit = quicktun_cmd_destroy_device, + .policy = quicktun_nl_policy, + }, +}; + +static int __init quicktun_init(void) +{ + int ret = 0; + + pr_info("%s, %s\n", DRV_DESCRIPTION, DRV_VERSION); + + ret = genl_register_family_with_ops(&quicktun_nl_family, quicktun_nl_ops, ARRAY_SIZE(quicktun_nl_ops)); + return ret; +} + +static void quicktun_exit(void) +{ + genl_unregister_family(&quicktun_nl_family); +} + + +module_init(quicktun_init); +module_exit(quicktun_exit); +MODULE_DESCRIPTION(DRV_DESCRIPTION); +MODULE_LICENSE("GPL"); diff --git a/quicktun.h b/quicktun.h new file mode 100644 index 0000000..a363491 --- /dev/null +++ b/quicktun.h @@ -0,0 +1,33 @@ +#ifndef __QUICKTUN_H +#define __QUICKTUN_H + +#define QUICKTUN_TUN_DEV 0x0001 +#define QUICKTUN_TAP_DEV 0x0002 + +enum { + QUICKTUN_CMD_UNSPEC, + QUICKTUN_CMD_CREATE_DEVICE, + QUICKTUN_CMD_MODIFY_DEVICE, + QUICKTUN_CMD_DESTROY_DEVICE, + QUICKTUN_CMD_GET_DEVICE, + __QUICKTUN_CMD_MAX +}; + +enum { + QUICKTUN_A_UNSPEC, + QUICKTUN_A_IFNAME, + QUICKTUN_A_TYPE, + QUICKTUN_A_LOCAL_ADDRESS, + QUICKTUN_A_LOCAL_PORT, + QUICKTUN_A_REMOTE_ADDRESS, + QUICKTUN_A_REMOTE_PORT, + QUICKTUN_A_REMOTE_FLOAT, + __QUICKTUN_A_MAX +}; + +#define QUICKTUN_CMD_MAX (__QUICKTUN_CMD_MAX - 1) +#define QUICKTUN_A_MAX (__QUICKTUN_A_MAX - 1) + +#define QUICKTUN_DEFAULT_PORT 2998 + +#endif /* __QUICKTUN_H */ |