From 35bfd16af8f8316de5ee392fe06020a7d7ed7a33 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Tue, 12 Apr 2011 21:03:35 +0200 Subject: Sending through the quicktun interface is working now. --- quicktun.c | 321 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 321 insertions(+) create mode 100644 quicktun.c (limited to 'quicktun.c') 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 +#include +#include +#include +#include +#include + +#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"); -- cgit v1.2.3