summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKozlov Dmitry <xeb@mail.ru>2012-06-26 19:17:34 +0400
committerKozlov Dmitry <xeb@mail.ru>2012-06-26 19:17:34 +0400
commit07264d491f0605088c94e8be9f2b593dd4882067 (patch)
tree58329f975626fc64ce220403721c03463121a12e
parent2b256df842764409f0d1cd7a37afabcef4e2785b (diff)
downloadaccel-ppp-07264d491f0605088c94e8be9f2b593dd4882067.tar.gz
accel-ppp-07264d491f0605088c94e8be9f2b593dd4882067.zip
ipoe: initial implementation of kernel module
-rw-r--r--CMakeLists.txt7
-rw-r--r--drivers/ipoe/CMakeLists.txt19
-rw-r--r--drivers/ipoe/Makefile4
-rw-r--r--drivers/ipoe/ipoe.c709
-rw-r--r--drivers/ipoe/ipoe.h33
-rw-r--r--drivers/pptp/CMakeLists.txt (renamed from driver/CMakeLists.txt)0
-rw-r--r--drivers/pptp/Makefile (renamed from driver/Makefile)0
-rw-r--r--drivers/pptp/gre.c (renamed from driver/gre.c)0
-rw-r--r--drivers/pptp/gre.h (renamed from driver/gre.h)0
-rw-r--r--drivers/pptp/if_pppox.h (renamed from driver/if_pppox.h)0
-rw-r--r--drivers/pptp/pptp.c (renamed from driver/pptp.c)0
-rw-r--r--ipoe-util/CMakeLists.txt20
l---------ipoe-util/ipoe.h1
-rw-r--r--ipoe-util/ipses-create.c61
-rw-r--r--ipoe-util/ipses-delete.c58
15 files changed, 911 insertions, 1 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 78ff7ad0..f358ce38 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -33,6 +33,11 @@ if (NOT BUILD_DRIVER_ONLY)
endif (NOT BUILD_DRIVER_ONLY)
if (BUILD_DRIVER OR BUILD_DRIVER_ONLY)
- add_subdirectory(driver)
+ add_subdirectory(drivers/pptp)
endif (BUILD_DRIVER OR BUILD_DRIVER_ONLY)
+if (BUILD_IPOE_DRIVER)
+ add_subdirectory(drivers/ipoe)
+ add_subdirectory(ipoe-util)
+
+endif (BUILD_IPOE_DRIVER)
diff --git a/drivers/ipoe/CMakeLists.txt b/drivers/ipoe/CMakeLists.txt
new file mode 100644
index 00000000..fb5a51e4
--- /dev/null
+++ b/drivers/ipoe/CMakeLists.txt
@@ -0,0 +1,19 @@
+if (NOT DEFINED KDIR)
+ set(KDIR "/usr/src/linux")
+endif (NOT DEFINED KDIR)
+
+ADD_CUSTOM_COMMAND(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/driver/ipoe.ko
+ COMMAND rm -rf ${CMAKE_CURRENT_BINARY_DIR}/driver
+ COMMAND mkdir ${CMAKE_CURRENT_BINARY_DIR}/driver
+ COMMAND ln -sf ${CMAKE_CURRENT_SOURCE_DIR}/* ${CMAKE_CURRENT_BINARY_DIR}/driver
+ COMMAND make -C ${KDIR} M=${CMAKE_CURRENT_BINARY_DIR}/driver modules
+ DEPENDS ipoe.c ipoe.h
+)
+
+ADD_CUSTOM_TARGET(isg_drv ALL
+ DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/driver/ipoe.ko
+)
+
+
+INSTALL(CODE "EXECUTE_PROCESS(COMMAND make -C ${KDIR} M=${CMAKE_CURRENT_BINARY_DIR}/drivers/ipoe modules_install)")
+
diff --git a/drivers/ipoe/Makefile b/drivers/ipoe/Makefile
new file mode 100644
index 00000000..22ea273d
--- /dev/null
+++ b/drivers/ipoe/Makefile
@@ -0,0 +1,4 @@
+obj-m += ipoe.o
+
+default:
+ make -C $(KDIR) M=$(PWD) modules
diff --git a/drivers/ipoe/ipoe.c b/drivers/ipoe/ipoe.c
new file mode 100644
index 00000000..58e4bbdd
--- /dev/null
+++ b/drivers/ipoe/ipoe.c
@@ -0,0 +1,709 @@
+#include <linux/capability.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/in.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/if_arp.h>
+#include <linux/mroute.h>
+#include <linux/init.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/if_ether.h>
+#include <linux/semaphore.h>
+#include <linux/rbtree.h>
+#include <linux/version.h>
+#include <net/genetlink.h>
+
+#include <net/sock.h>
+#include <net/ip.h>
+#include <net/icmp.h>
+#include <net/inet_ecn.h>
+#include <net/xfrm.h>
+#include <net/net_namespace.h>
+#include <net/netns/generic.h>
+
+#include "ipoe.h"
+
+#define HASH(addr) (((__force u32)addr^((__force u32)addr>>4))&0xF)
+#define HASH_SIZE 16
+
+#define NEED_UPDATE 1
+#define UPDATE 2
+
+struct ipoe_session
+{
+ struct rb_node node;
+
+ __be32 addr;
+ __be32 peer_addr;
+ //__u8 hwaddr[ETH_ALEN];
+
+ struct net_device *dev;
+ struct net_device *link_dev;
+
+ /*struct tasklet_struct tasklet;
+ int tasklet_pending;
+
+ struct u64_status_sync rsync;
+ u64 rx_packets;
+ u64 rx_bytes;
+ struct sk_buff_head rq;
+
+ struct u64_status_sync tsync;
+ u64 tx_packets;
+ u64 tx_bytes;
+ struct sk_buff_head tq;*/
+
+ int drop:1;
+};
+
+struct rb_root ipoe_rbt = RB_ROOT;
+static atomic_t ipoe_rlock;
+static atomic_t ipoe_update;
+static DEFINE_SEMAPHORE(ipoe_wlock);
+
+struct sk_buff_head ipoe_rq;
+struct tasklet_struct ipoe_rq_tasklet;
+
+static struct ipoe_session *ipoe_lookup(__be32 addr, struct rb_node **r_parent, struct rb_node ***r_p);
+static int ipoe_do_nat(struct sk_buff *skb, __be32 new_addr, int to_peer);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
+static const struct net_device_ops ipoe_netdev_ops;
+#endif
+
+static struct genl_family ipoe_nl_family;
+
+static void ipoe_recv_rq(unsigned long arg)
+{
+ struct sk_buff *skb;
+ int upd;
+ struct iphdr *iph;
+ int noff;
+ struct ipoe_session *ses;
+
+ atomic_inc(&ipoe_rlock);
+ if (atomic_read(&ipoe_update) == UPDATE) {
+ atomic_dec(&ipoe_rlock);
+ tasklet_schedule(&ipoe_rq_tasklet);
+ return;
+ }
+
+ while ((skb = skb_dequeue(&ipoe_rq))) {
+ noff = skb_network_offset(skb);
+
+ iph = ip_hdr(skb);
+
+ ses = ipoe_lookup(iph->saddr, NULL, NULL);
+ if (!ses)
+ goto drop;
+
+ if (ses->drop)
+ goto drop;
+
+ if (ses->addr && ipoe_do_nat(skb, ses->addr, 0))
+ goto drop;
+
+ skb->dev = ses->dev;
+ //skb->skb_iif = ses->link_dev->ifindex;
+
+ netif_rx(skb);
+
+ if (atomic_read(&ipoe_update) == NEED_UPDATE)
+ break;
+
+ continue;
+
+ drop:
+ kfree_skb(skb);
+ }
+
+ upd = atomic_read(&ipoe_update);
+ if (atomic_dec_and_test(&ipoe_rlock) && upd == NEED_UPDATE)
+ atomic_set(&ipoe_update, UPDATE);
+}
+
+static int ipoe_do_nat(struct sk_buff *skb, __be32 new_addr, int to_peer)
+{
+ struct iphdr *iph;
+ int noff;
+ int ihl;
+ __be32 addr;
+
+ noff = skb_network_offset(skb);
+
+ iph = ip_hdr(skb);
+
+ if (to_peer)
+ addr = iph->daddr;
+ else
+ addr = iph->saddr;
+
+ if (skb_cloned(skb) &&
+ !skb_clone_writable(skb, sizeof(*iph) + noff) &&
+ pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
+ return -1;
+
+ iph = ip_hdr(skb);
+
+ if (to_peer)
+ iph->daddr = new_addr;
+ else
+ iph->saddr = new_addr;
+
+ csum_replace4(&iph->check, addr, new_addr);
+
+ ihl = iph->ihl * 4;
+
+ switch (iph->frag_off & htons(IP_OFFSET) ? 0 : iph->protocol) {
+ case IPPROTO_TCP:
+ {
+ struct tcphdr *tcph;
+
+ if (!pskb_may_pull(skb, ihl + sizeof(*tcph) + noff) ||
+ (skb_cloned(skb) &&
+ !skb_clone_writable(skb, ihl + sizeof(*tcph) + noff) &&
+ pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))
+ return -1;
+
+ tcph = (void *)(skb_network_header(skb) + ihl);
+ inet_proto_csum_replace4(&tcph->check, skb, addr, new_addr, 1);
+ break;
+ }
+ case IPPROTO_UDP:
+ {
+ struct udphdr *udph;
+
+ if (!pskb_may_pull(skb, ihl + sizeof(*udph) + noff) ||
+ (skb_cloned(skb) &&
+ !skb_clone_writable(skb, ihl + sizeof(*udph) + noff) &&
+ pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))
+ return -1;
+
+ udph = (void *)(skb_network_header(skb) + ihl);
+ if (udph->check || skb->ip_summed == CHECKSUM_PARTIAL) {
+ inet_proto_csum_replace4(&udph->check, skb, addr, new_addr, 1);
+ if (!udph->check)
+ udph->check = CSUM_MANGLED_0;
+ }
+ break;
+ }
+ case IPPROTO_ICMP:
+ {
+ struct icmphdr *icmph;
+
+ if (!pskb_may_pull(skb, ihl + sizeof(*icmph) + noff))
+ return -1;
+
+ icmph = (void *)(skb_network_header(skb) + ihl);
+
+ if ((icmph->type != ICMP_DEST_UNREACH) &&
+ (icmph->type != ICMP_TIME_EXCEEDED) &&
+ (icmph->type != ICMP_PARAMETERPROB))
+ break;
+
+ if (!pskb_may_pull(skb, ihl + sizeof(*icmph) + sizeof(*iph) +
+ noff))
+ return -1;
+
+ icmph = (void *)(skb_network_header(skb) + ihl);
+ iph = (void *)(icmph + 1);
+
+ if (skb_cloned(skb) &&
+ !skb_clone_writable(skb, ihl + sizeof(*icmph) +
+ sizeof(*iph) + noff) &&
+ pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
+ return -1;
+
+ icmph = (void *)(skb_network_header(skb) + ihl);
+ iph = (void *)(icmph + 1);
+ if (to_peer)
+ iph->saddr = new_addr;
+ else
+ iph->daddr = new_addr;
+
+ inet_proto_csum_replace4(&icmph->checksum, skb, addr, new_addr, 0);
+ break;
+ }
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)
+static int ipoe_xmit(struct sk_buff *skb, struct net_device *dev)
+#else
+static netdev_tx_t ipoe_xmit(struct sk_buff *skb, struct net_device *dev)
+#endif
+{
+ struct ipoe_session *ses = netdev_priv(dev);
+ struct net_device_stats *stats = &dev->stats;
+ struct iphdr *iph;
+ int noff;
+
+ noff = skb_network_offset(skb);
+
+ if (!pskb_may_pull(skb, sizeof(*iph) + noff))
+ goto drop;
+
+ iph = ip_hdr(skb);
+
+ pr_info("ipoe: xmit %08x %08x\n", iph->saddr, iph->daddr);
+
+ /*u64_stats_update_begin(&ses->tsync);
+ ses->tx_packets++;
+ ses->tx_bytes += skb->len;
+ u64_stats_update_end(&ses->tsync);*/
+
+ if (iph->daddr == ses->addr && ipoe_do_nat(skb, ses->peer_addr, 1))
+ goto drop;
+
+ skb->dev = ses->link_dev;
+ skb->skb_iif = dev->ifindex;
+ dev_queue_xmit(skb);
+
+ return NETDEV_TX_OK;
+drop:
+ stats->tx_dropped++;
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+}
+
+static int ipoe_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
+{
+ struct ipoe_session *ses = NULL;
+ struct iphdr *iph;
+ int upd;
+ int noff;
+ int r;
+
+ if (skb->pkt_type == PACKET_OTHERHOST)
+ goto drop;
+
+ noff = skb_network_offset(skb);
+
+ if (!pskb_may_pull(skb, sizeof(*iph) + noff))
+ goto drop;
+
+ iph = ip_hdr(skb);
+
+ //pr_info("ipoe: recv %08x %08x\n", iph->saddr, iph->daddr);
+
+ atomic_inc(&ipoe_rlock);
+ upd = atomic_read(&ipoe_update);
+ if (upd == NEED_UPDATE) {
+ skb_queue_tail(&ipoe_rq, skb);
+
+ if (atomic_dec_and_test(&ipoe_rlock))
+ atomic_set(&ipoe_update, UPDATE);
+
+ return NET_RX_SUCCESS;
+ } else if (upd == UPDATE) {
+ skb_queue_tail(&ipoe_rq, skb);
+ atomic_dec(&ipoe_rlock);
+ return NET_RX_SUCCESS;
+ }
+
+ ses = ipoe_lookup(iph->saddr, NULL, NULL);
+ if (!ses)
+ goto drop_unlock;
+
+ if (ses->drop)
+ goto drop_unlock;
+
+ if (ses->addr && ipoe_do_nat(skb, ses->addr, 0))
+ goto drop_unlock;
+
+ skb->dev = ses->dev;
+ //skb->skb_iif = ses->link_dev->ifindex;
+
+ r = netif_rx(skb);
+
+ atomic_dec(&ipoe_rlock);
+
+ return r;
+
+
+drop_unlock:
+ atomic_dec(&ipoe_rlock);
+drop:
+ kfree_skb(skb);
+ return NET_RX_DROP;
+}
+
+static int ipoe_hard_header(struct sk_buff *skb, struct net_device *dev,
+ unsigned short type, const void *daddr,
+ const void *saddr, unsigned len)
+{
+ const struct ipoe_session *ses = netdev_priv(dev);
+
+ return dev_hard_header(skb, ses->link_dev, type, ses->hwaddr,
+ dev->dev_addr, len);
+}
+
+static void ipoe_netdev_setup(struct net_device *dev)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)
+ dev->hard_start_xmit = ipoe_xmit;
+#else
+ dev->netdev_ops = &ipoe_netdev_ops;
+#endif
+ dev->destructor = free_netdev;
+
+ dev->type = ARPHRD_ETHER;
+ dev->hard_header_len = ETH_ALEN;
+ dev->mtu = ETH_DATA_LEN;
+ dev->flags = IFF_NOARP;
+ dev->iflink = 0;
+ dev->addr_len = 4;
+ dev->features = 0;//|= NETIF_F_NETNS_LOCAL;
+ dev->header_ops = &ipoe_hard_header_ops,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
+ dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
+#endif
+}
+
+static int ipoe_create(__be32 peer_addr, __be32 addr, const char *link_ifname, const __u8 *hwaddr)
+{
+ struct ipoe_session *ses;
+ struct net_device *dev, *link_dev;
+ char name[IFNAMSIZ];
+ int r = 0;
+ struct rb_node **p;
+ struct rb_node *parent;
+
+ link_dev = dev_get_by_name(&init_net, link_ifname);
+ if (!link_dev)
+ return -EINVAL;
+
+ sprintf(name, "ipoe%%d");
+
+ dev = alloc_netdev(sizeof(*ses), name, ipoe_netdev_setup);
+ if (dev == NULL)
+ goto failed;
+
+ dev_net_set(dev, &init_net);
+
+ r = dev_alloc_name(dev, name);
+ if (r < 0)
+ goto failed_free;
+
+ ses = netdev_priv(dev);
+ ses->dev = dev;
+ ses->addr = addr;
+ ses->peer_addr = peer_addr;
+ ses->link_dev = link_dev;
+ memcpy(ses->hwaddr, hwaddr, ETH_ALEN);
+ dev->features = link_dev->features;
+
+ rtnl_lock();
+ r = register_netdevice(dev);
+ rtnl_unlock();
+ if (r < 0)
+ goto failed_free;
+
+ down(&ipoe_wlock);
+ atomic_inc(&ipoe_rlock);
+ atomic_set(&ipoe_update, NEED_UPDATE);
+ if (atomic_dec_and_test(&ipoe_rlock))
+ atomic_set(&ipoe_update, UPDATE);
+ else {
+ while (atomic_read(&ipoe_update) != UPDATE)
+ schedule_timeout_uninterruptible(1);
+ }
+
+ if (ipoe_lookup(peer_addr, &parent, &p))
+ r = -EEXIST;
+ else {
+ rb_link_node(&ses->node, parent, p);
+ rb_insert_color(&ses->node, &ipoe_rbt);
+ }
+
+ up(&ipoe_wlock);
+
+ atomic_set(&ipoe_update, 0);
+
+ tasklet_schedule(&ipoe_rq_tasklet);
+
+ return r;
+
+failed_free:
+ free_netdev(dev);
+failed:
+ dev_put(link_dev);
+ return r;
+}
+
+static struct ipoe_session *ipoe_lookup(__be32 addr, struct rb_node **r_parent, struct rb_node ***r_p)
+{
+ struct ipoe_session *ses;
+ struct rb_node **p = &ipoe_rbt.rb_node;
+ struct rb_node *parent = NULL;
+
+ while (*p) {
+ parent = *p;
+ ses = rb_entry(parent, typeof(*ses), node);
+ if (addr < ses->peer_addr)
+ p = &(*p)->rb_left;
+ else if (addr > ses->peer_addr)
+ p = &(*p)->rb_right;
+ else
+ return ses;
+ }
+
+ if (r_parent) {
+ *r_parent = parent;
+ *r_p = p;
+ }
+
+ return NULL;
+}
+
+
+
+static int ipoe_delete(__be32 addr)
+{
+ struct ipoe_session *ses;
+
+ down(&ipoe_wlock);
+
+ atomic_inc(&ipoe_rlock);
+ atomic_set(&ipoe_update, NEED_UPDATE);
+ if (atomic_dec_and_test(&ipoe_rlock))
+ atomic_set(&ipoe_update, UPDATE);
+ else {
+ while (atomic_read(&ipoe_update) != UPDATE)
+ schedule_timeout_uninterruptible(1);
+ }
+
+ ses = ipoe_lookup(addr, NULL, NULL);
+ if (ses)
+ rb_erase(&ses->node, &ipoe_rbt);
+
+ up(&ipoe_wlock);
+
+ atomic_set(&ipoe_update, 0);
+
+ tasklet_schedule(&ipoe_rq_tasklet);
+
+ if (!ses)
+ return -EINVAL;
+
+ dev_put(ses->link_dev);
+ unregister_netdev(ses->dev);
+
+ return 0;
+}
+
+static int ipoe_nl_cmd_noop(struct sk_buff *skb, struct genl_info *info)
+{
+ struct sk_buff *msg;
+ void *hdr;
+ int ret = -ENOBUFS;
+
+ msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!msg) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ hdr = genlmsg_put(msg, info->snd_pid, info->snd_seq,
+ &ipoe_nl_family, 0, IPOE_CMD_NOOP);
+ if (IS_ERR(hdr)) {
+ ret = PTR_ERR(hdr);
+ goto err_out;
+ }
+
+ genlmsg_end(msg, hdr);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)
+ return genlmsg_unicast(msg, info->snd_pid);
+#else
+ return genlmsg_unicast(genl_info_net(info), msg, info->snd_pid);
+#endif
+
+err_out:
+ nlmsg_free(msg);
+
+out:
+ return ret;
+}
+
+static int ipoe_nl_cmd_create(struct sk_buff *skb, struct genl_info *info)
+{
+ struct ipoe_session *ses;
+ __be32 peer_addr, addr = 0;
+ int ret = 0;
+ char ifname[IFNAMSIZ];
+ __u8 hwaddr[ETH_ALEN];
+ //struct net *net = genl_info_net(info);
+
+ if (!info->attrs[IPOE_ATTR_PEER_ADDR] || !info->attrs[IPOE_ATTR_IFNAME]) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ peer_addr = nla_get_be32(info->attrs[IPOE_ATTR_PEER_ADDR]);
+ if (info->attrs[IPOE_ATTR_ADDR])
+ addr = nla_get_be32(info->attrs[IPOE_ATTR_ADDR]);
+ nla_strlcpy(ifname, info->attrs[IPOE_ATTR_IFNAME], IFNAMSIZ - 1);
+ if (info->attrs[IPOE_ATTR_HWADDR])
+ nla_strlcpy(hwaddr, info->attrs[IPOE_ATTR_HWADDR], ETH_ALEN);
+ else
+ memset(hwaddr, 0, sizeof(hwaddr));
+
+ pr_info("ipoe: create %08x %08x %s\n", peer_addr, addr, ifname);
+
+ ret = ipoe_create(peer_addr, addr, ifname, hwaddr);
+
+out:
+ return ret;
+}
+
+static int ipoe_nl_cmd_delete(struct sk_buff *skb, struct genl_info *info)
+{
+ __be32 addr;
+ //struct net *net = genl_info_net(info);
+
+
+ if (!info->attrs[IPOE_ATTR_PEER_ADDR])
+ return -EINVAL;
+
+ addr = nla_get_u32(info->attrs[IPOE_ATTR_PEER_ADDR]);
+
+ pr_info("ipoe: delete %08x\n", addr);
+
+ return ipoe_delete(addr);
+}
+
+static struct nla_policy ipoe_nl_policy[IPOE_ATTR_MAX + 1] = {
+ [IPOE_ATTR_NONE] = { .type = NLA_UNSPEC, },
+ [IPOE_ATTR_ADDR] = { .type = NLA_U32, },
+ [IPOE_ATTR_PEER_ADDR] = { .type = NLA_U32, },
+ [IPOE_ATTR_IFNAME] = { .type = NLA_STRING, .len = IFNAMSIZ - 1 },
+ [IPOE_ATTR_HWADDR] = { .type = NLA_BINARY, .len = ETH_ALEN },
+};
+
+static struct genl_ops ipoe_nl_ops[] = {
+ {
+ .cmd = IPOE_CMD_NOOP,
+ .doit = ipoe_nl_cmd_noop,
+ .policy = ipoe_nl_policy,
+ /* can be retrieved by unprivileged users */
+ },
+ {
+ .cmd = IPOE_CMD_CREATE,
+ .doit = ipoe_nl_cmd_create,
+ .policy = ipoe_nl_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = IPOE_CMD_DELETE,
+ .doit = ipoe_nl_cmd_delete,
+ .policy = ipoe_nl_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+};
+
+static struct genl_family ipoe_nl_family = {
+ .id = GENL_ID_GENERATE,
+ .name = IPOE_GENL_NAME,
+ .version = IPOE_GENL_VERSION,
+ .hdrsize = 0,
+ .maxattr = IPOE_ATTR_MAX,
+};
+
+static const struct header_ops ipoe_hard_header_ops = {
+ .create = ipoe_hard_header,
+ .rebuild = eth_rebuild_header,
+ .parse = eth_header_parse,
+ .cache = eth_header_cache,
+ .cache_update = eth_header_cache_update,
+};
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
+static const struct net_device_ops ipoe_netdev_ops = {
+ .ndo_start_xmit = ipoe_xmit,
+};
+#endif
+
+static struct packet_type ip_packet_type = {
+ .type = __constant_htons(ETH_P_IP),
+ .func = ipoe_rcv,
+};
+
+/*static struct pernet_operations ipoe_net_ops = {
+ .init = ipoe_init_net,
+ .exit = ipoe_exit_net,
+ .id = &ipoe_net_id,
+ .size = sizeof(struct ipoe_net),
+};*/
+
+static int __init ipoe_init(void)
+{
+ int i, err;
+
+ printk("IPoE session driver v0.1\n");
+
+ /*err = register_pernet_device(&ipoe_net_ops);
+ if (err < 0)
+ return err;*/
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)
+ err = genl_register_family(&ipoe_nl_family);
+ if (err < 0) {
+ printk(KERN_INFO "ipoe: can't register netlink interface\n");
+ goto out;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(ipoe_nl_ops); i++) {
+ err = genl_register_ops(&ipoe_nl_family, &ipoe_nl_ops[i]);
+ if (err)
+ break;
+ }
+
+ if (err < 0) {
+ printk(KERN_INFO "ipoe: can't register netlink interface\n");
+ goto out_unreg;
+ }
+#else
+ err = genl_register_family_with_ops(&ipoe_nl_family, ipoe_nl_ops,
+ ARRAY_SIZE(ipoe_nl_ops));
+#endif
+ if (err < 0) {
+ printk(KERN_INFO "ipoe: can't register netlink interface\n");
+ goto out;
+ }
+
+ tasklet_init(&ipoe_rq_tasklet, ipoe_recv_rq, 0);
+
+ skb_queue_head_init(&ipoe_rq);
+
+ dev_add_pack(&ip_packet_type);
+
+ return 0;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)
+out_unreg:
+#endif
+ genl_unregister_family(&ipoe_nl_family);
+out:
+ return err;
+}
+
+static void __exit ipoe_fini(void)
+{
+ dev_remove_pack(&ip_packet_type);
+ genl_unregister_family(&ipoe_nl_family);
+}
+
+module_init(ipoe_init);
+module_exit(ipoe_fini);
+MODULE_LICENSE("GPL");
diff --git a/drivers/ipoe/ipoe.h b/drivers/ipoe/ipoe.h
new file mode 100644
index 00000000..635c9dab
--- /dev/null
+++ b/drivers/ipoe/ipoe.h
@@ -0,0 +1,33 @@
+#ifndef __LINUX_ISG_H
+#define __LINUX_ISG_H
+
+#include <linux/types.h>
+
+enum {
+ IPOE_CMD_NOOP,
+ IPOE_CMD_CREATE,
+ IPOE_CMD_DELETE,
+ __IPOE_CMD_MAX,
+};
+
+#define IPOE_CMD_MAX (__IPOE_CMD_MAX - 1)
+
+enum {
+ IPOE_ATTR_NONE, /* no data */
+ IPOE_ATTR_ADDR, /* u32 */
+ IPOE_ATTR_PEER_ADDR, /* u32 */
+ IPOE_ATTR_IFNAME, /* u32 */
+ IPOE_ATTR_HWADDR, /* u32 */
+ __IPOE_ATTR_MAX,
+};
+
+#define IPOE_ATTR_MAX (__IPOE_ATTR_MAX - 1)
+
+/*
+ * NETLINK_GENERIC related info
+ */
+#define IPOE_GENL_NAME "IPoE"
+#define IPOE_GENL_VERSION 0x1
+
+#endif
+
diff --git a/driver/CMakeLists.txt b/drivers/pptp/CMakeLists.txt
index fd732e6a..fd732e6a 100644
--- a/driver/CMakeLists.txt
+++ b/drivers/pptp/CMakeLists.txt
diff --git a/driver/Makefile b/drivers/pptp/Makefile
index 8ccbbedf..8ccbbedf 100644
--- a/driver/Makefile
+++ b/drivers/pptp/Makefile
diff --git a/driver/gre.c b/drivers/pptp/gre.c
index 77886d5d..77886d5d 100644
--- a/driver/gre.c
+++ b/drivers/pptp/gre.c
diff --git a/driver/gre.h b/drivers/pptp/gre.h
index 2ca7f749..2ca7f749 100644
--- a/driver/gre.h
+++ b/drivers/pptp/gre.h
diff --git a/driver/if_pppox.h b/drivers/pptp/if_pppox.h
index bc05b533..bc05b533 100644
--- a/driver/if_pppox.h
+++ b/drivers/pptp/if_pppox.h
diff --git a/driver/pptp.c b/drivers/pptp/pptp.c
index 78853fcb..78853fcb 100644
--- a/driver/pptp.c
+++ b/drivers/pptp/pptp.c
diff --git a/ipoe-util/CMakeLists.txt b/ipoe-util/CMakeLists.txt
new file mode 100644
index 00000000..4f2ad01a
--- /dev/null
+++ b/ipoe-util/CMakeLists.txt
@@ -0,0 +1,20 @@
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
+INCLUDE_DIRECTORIES(${CMAKE_HOME_DIRECTORY}/ipses)
+
+if (LIBNL2)
+ ADD_DEFINITIONS("-DLIBNL2")
+endif (LIBNL2)
+
+ADD_EXECUTABLE(ipses-create ipses-create.c)
+
+ADD_EXECUTABLE(ipses-delete ipses-delete.c)
+
+if (LIBNL2)
+ TARGET_LINK_LIBRARIES(ipses-create nl nl-genl m)
+ TARGET_LINK_LIBRARIES(ipses-delete nl nl-genl m)
+else (LIBNL2)
+ TARGET_LINK_LIBRARIES(ipses-create nl)
+ TARGET_LINK_LIBRARIES(ipses-delete nl)
+endif (LIBNL2)
+
+
diff --git a/ipoe-util/ipoe.h b/ipoe-util/ipoe.h
new file mode 120000
index 00000000..7117b41c
--- /dev/null
+++ b/ipoe-util/ipoe.h
@@ -0,0 +1 @@
+../drivers/ipoe/ipoe.h \ No newline at end of file
diff --git a/ipoe-util/ipses-create.c b/ipoe-util/ipses-create.c
new file mode 100644
index 00000000..5aaf1ab7
--- /dev/null
+++ b/ipoe-util/ipses-create.c
@@ -0,0 +1,61 @@
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include <netlink/netlink.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/ctrl.h>
+
+
+#include "ipoe.h"
+
+int main(int argc, char **argv)
+{
+#if LIBNL2
+ struct nl_sock *h;
+#else
+ struct nl_handle *h;
+#endif
+ struct nl_msg *msg;
+ int family;
+ in_addr_t local, remote;
+ int err;
+
+ if (argc != 4) {
+ printf("usage: ipses-create <ifname> <peer_addr> <addr>\n");
+ return 1;
+ }
+
+ local = inet_addr(argv[2]);
+ remote = inet_addr(argv[3]);
+
+#if LIBNL2
+ h = nl_socket_alloc();
+#else
+ h = nl_handle_alloc();
+#endif
+ genl_connect(h);
+ family = genl_ctrl_resolve(h, IPOE_GENL_NAME);
+
+ msg = nlmsg_alloc();
+ genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, NLM_F_REQUEST, IPOE_CMD_CREATE, IPOE_GENL_VERSION);
+ nla_put_u32(msg, IPOE_ATTR_PEER_ADDR, local);
+ nla_put_u32(msg, IPOE_ATTR_ADDR, remote);
+ nla_put_string(msg, IPOE_ATTR_IFNAME, argv[1]);
+
+ nl_send_auto_complete(h, msg);
+ err = nl_recvmsgs_default(h);
+#if LIBNL2
+ printf("recv: %s\n", nl_geterror(err));
+#else
+ nl_perror("recv");
+#endif
+
+ nlmsg_free(msg);
+ nl_close(h);
+
+ return 0;
+}
+
diff --git a/ipoe-util/ipses-delete.c b/ipoe-util/ipses-delete.c
new file mode 100644
index 00000000..4e6a1843
--- /dev/null
+++ b/ipoe-util/ipses-delete.c
@@ -0,0 +1,58 @@
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include <netlink/netlink.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/ctrl.h>
+
+
+#include "ipoe.h"
+
+int main(int argc, char **argv)
+{
+#if LIBNL2
+ struct nl_sock *h;
+#else
+ struct nl_handle *h;
+#endif
+ struct nl_msg *msg;
+ int family;
+ in_addr_t local;
+ int err;
+
+ if (argc != 2) {
+ printf("usage: ipses-delete <addr>\n");
+ return 1;
+ }
+
+ local = inet_addr(argv[1]);
+
+#if LIBNL2
+ h = nl_socket_alloc();
+#else
+ h = nl_handle_alloc();
+#endif
+ genl_connect(h);
+ family = genl_ctrl_resolve(h, IPOE_GENL_NAME);
+
+ msg = nlmsg_alloc();
+ genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, NLM_F_REQUEST, IPOE_CMD_DELETE, IPOE_GENL_VERSION);
+ nla_put_u32(msg, IPOE_ATTR_PEER_ADDR, local);
+
+ nl_send_auto_complete(h, msg);
+ err = nl_recvmsgs_default(h);
+#if LIBNL2
+ printf("recv: %s\n", nl_geterror(err));
+#else
+ nl_perror("recv");
+#endif
+
+ nlmsg_free(msg);
+ nl_close(h);
+
+ return 0;
+}
+