Netdev List
 help / color / mirror / Atom feed
* Re: [PATCH 1/2] export cpu_tlbstate to modules
From: H. Peter Anvin @ 2009-08-10 21:56 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: netdev, virtualization, kvm, linux-kernel, mingo
In-Reply-To: <20090810185302.GB13924@redhat.com>

On 08/10/2009 11:53 AM, Michael S. Tsirkin wrote:
> vhost net module wants to do copy to/from user from a kernel thread,
> which needs switch_mm (like what fs/aio has).
> export cpu_tlbstate to make this possible
> 
> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
> ---
>  arch/x86/mm/tlb.c |    1 +
>  1 files changed, 1 insertions(+), 0 deletions(-)
> 
> diff --git a/arch/x86/mm/tlb.c b/arch/x86/mm/tlb.c
> index 821e970..e33a5f0 100644
> --- a/arch/x86/mm/tlb.c
> +++ b/arch/x86/mm/tlb.c
> @@ -13,6 +13,7 @@
>  
>  DEFINE_PER_CPU_SHARED_ALIGNED(struct tlb_state, cpu_tlbstate)
>  			= { &init_mm, 0, };
> +EXPORT_PER_CPU_SYMBOL_GPL(cpu_tlbstate);
>  
>  /*
>   *	Smarter SMP flushing macros.

Wouldn't it be a *lot* better to move use_mm() from fs/aio.c into common
code, and export that instead?

	-hpa


^ permalink raw reply

* Re: [PATCH 5/5] c/r: Add AF_UNIX support (v8)
From: Dan Smith @ 2009-08-10 21:06 UTC (permalink / raw)
  To: Serge E. Hallyn; +Cc: containers, netdev, Alexey Dobriyan
In-Reply-To: <20090810210235.GB24373@us.ibm.com>

SH> Thanks, Dan.  Apart from the double-free on error case in
SH> sock_copy_buffers(), this looks very good to me.  So with that
SH> change, please feel free to add:

Okay, thanks, I've got that fixed up now.

SH> Dan, do you have any testcases (even just descriptions of how to
SH> test, which i'll then try to script) i can add to cr_tests?

Yeah, I've got a big ugly test program that I've rolled automatic
tests for most scenarios into.  Here's the usage output of the test.
I'll get it cleaned up and posted too:

  Usage: ./testprog [-t (s|d)] [-wn] TEST
  Where:
    -t s, -t d:             Select STREAM or DGRAM sockets
    -w:                     Wait for a response before restarting
    -n:                     Don't terminate the test on successful restart
  
  Where TEST is one of:
    --unix_pair:            Pair of sockets using socketpair()
    --unix_unlinked:        Listen socket with an unlinked path
    --unix_pair_path:       Pair of sockets using paths
    --unix_pair_abstract:   Pair of sockets using abstract names
    --unix_inflight:        Pair of sockets with data in flight
    --unix_inflight_closed: Pair of sockets, one side closed after preflight
    --unix_midaccept:       Listen socket with unaccepted peer
    --unix_fdpass:          Attempt to pass an FD
    --unix_passcred:        Attempt to pass credentials
    --unix_path:            Post-restart path-based connect to listen socket
    --unix_abstract:        Post-restart abstract connect to listen socket
    --unix_path_unlink:     Like pair_path, but unlink socket before restart
    --unix_deadpeer:        Close peer socket with data in flight
    --unix_testopt:         Unix socket setting and testing for a sockopt
    --unix_anoninflight:    Inflight data with unconnected DGRAM sockets
    --inet_connected:       Pair of localhost INET4 sockets
    --inet6_connected:      Pair of localhost INET6 sockets
    --nonet:                No network, just a simple loop
    --sendmail:             Try to start up sendmail and checkpoint it

-- 
Dan Smith
IBM Linux Technology Center
email: danms@us.ibm.com

^ permalink raw reply

* Re: [PATCH 5/5] c/r: Add AF_UNIX support (v8)
From: Serge E. Hallyn @ 2009-08-10 21:02 UTC (permalink / raw)
  To: Dan Smith; +Cc: containers, netdev, Alexey Dobriyan
In-Reply-To: <1249918379-29414-6-git-send-email-danms@us.ibm.com>

Quoting Dan Smith (danms@us.ibm.com):
> This patch adds basic checkpoint/restart support for AF_UNIX sockets.  It
> has been tested with a single and multiple processes, and with data inflight
> at the time of checkpoint.  It supports socketpair()s, path-based, and
> abstract sockets.

Thanks, Dan.  Apart from the double-free on error case in
sock_copy_buffers(), this looks very good to me.  So with that
change, please feel free to add:

Acked-by: Serge Hallyn <serue@us.ibm.com>

Oren, pls do consider pulling this into v18.

Dan, do you have any testcases (even just descriptions of
how to test, which i'll then try to script) i can add to
cr_tests?

thanks,
-serge

^ permalink raw reply

* Re: module loading permissions and request_module permission inconsistencies
From: Eric Paris @ 2009-08-10 20:48 UTC (permalink / raw)
  To: Neil Horman
  Cc: linux-kernel, netdev, linux-security-module, sds, davem,
	shemminger, kees, morgan, serue, casey, dwlash
In-Reply-To: <20090810202308.GA15390@hmsreliant.think-freely.org>

On Mon, 2009-08-10 at 16:23 -0400, Neil Horman wrote:
> On Mon, Aug 10, 2009 at 03:45:13PM -0400, Eric Paris wrote:

> > 1) remove CAP_SYS_MODULE from the networking code and instead check
> > CAP_NET_ADMIN.  Maybe CAP_NET_ADMIN is already being checked and I'll
> > just remove the capable call altogether but at least I can more
> > intelligently limit the powers of these processes and they will still be
> > root limited according to DAC permissions like they are today.
> > 
> Would this have any adverse effect on how user space sees this working.
> Intuitively I would think that if you wanted to load a module (directly or
> indirectly, via an iptables command or whatnot), you would need CAP_SYS_MODULE
> capabilities on the calling process, not just CAP_NET_ADMIN.  I honestly don't
> know the answer here, I'm just raising the question.

While that might make intuitive sense, it's actually proving to be a bad
idea to use the same capability for direct and indirect module loading
(especially considering we have 125 other places in the kernel where you
can do indirect module loading without any security check)  And believe
me, if someone suggests I move a CAP_SYS_MODULE check down into
__request_module I'll scream about what a horrible idea that is (and
then laugh at them behind their back).

While I think there should be some check in __request_module I don't
think it should be CAP_SYS_MODULE.

CAP_NET_ADMIN at least limits us to root and in all reality to the same
situation everyone is in today.  I just checked every single selinux
domain that grants CAP_SYS_MODULE already grants CAP_NET_ADMIN, so we
can somewhat safely say that nothing (on a fedora system at least) would
break with this change.

-Eric

^ permalink raw reply

* Re: module loading permissions and request_module permission inconsistencies
From: Neil Horman @ 2009-08-10 20:23 UTC (permalink / raw)
  To: Eric Paris
  Cc: linux-kernel, netdev, linux-security-module, sds, davem,
	shemminger, kees, morgan, serue, casey, dwlash
In-Reply-To: <1249933513.2501.58.camel@dhcp231-106.rdu.redhat.com>

On Mon, Aug 10, 2009 at 03:45:13PM -0400, Eric Paris wrote:
> I'd like to hear thoughts on how we currently do permissions handling on
> request_module() and if it really makes sense?  request_module() is the
> function which will do an upcall to try to get modprobe to load a
> specified module into the kernel.  It is called in a lot of places
> around the kernel (~128).  Of those places only three check to see if
> the REQUESTING process has some sort of module loading permissions
> (CAP_SYS_RAWIO.)  Those three are in net/core/dev.c::dev_load() and in
> the IPv4 tcp congestion code in tcp_set_default_congestion_control() and
> tcp_set_congestion_control().  All 125 other calls to request_module()
> appear to be done without any permissions check against the triggering
> process.  The actual loading of a module is done in another thread which
> always has permissions, so that side of things appears to not be an
> issue.
> 
> First question, why does networking do it's own CAP_SYS_MODULE checks?
> (this is VERY old code, pre-git days)  And, does it make sense?  In the
> past this has come up in [1] when /sbin/ip triggered the loading of a
> module to get IPv6 tunnel support.  It's perfectly reasonable
> for /sbin/ip to do this.  But is it reasonable for /sbin/ip to need
> CAP_SYS_MODULE?  CAP_SYS_MODULE says that /sbin/ip has permissions to
> load any arbitrary binary it feels like as a kernel module directly.  Is
> this really what we want?  Should SELinux have to give a hacked /sbin/ip
> permissions to load any arbitrary module?  Recently in [2] we find that
> now bluetoothd needs to be granted permissions to directly load any
> kernel module it pleases, just so it can request the upcall loads bnep.
> The same holds basically true for congestion control hooks.  Note that
> I'm saying we are giving permission for these to load kernel modules
> directly, not just through the upcall.
> 
> Nowhere else in the kernel do we do CAP_SYS_MODULE checks on the
> triggering side of request_module() and, as I assume at least one of
> those allows the user to control the module name, it seems to me that
> the current checks are actually lowering the security bar.  We are
> granting wide dangerous permissions to binaries when a more directed
> permission would be much more sensible.  And we are doing it for no
> security gain since I assume we have 10's if not more than 100 other
> ways around it.
> 
> The second problem is this lack of control over the rest of the users of
> request_module().  If we looks at [3] we see that this oversight created
> an interesting and useful situation for a xen framebuffer exploit.  They
> created an invalid binary and tried to execute it.  This caused the
> kernel to enter search_binary_handler() which in turn called
> request_module() which triggered the modprobe upcall and the were able
> to load a module which they controlled.  As soon as they got their
> module into the kernel it was game over for everything.  So we really
> should be trying to prevent modules from getting into the kernel.  There
> is no security hook on the triggering side and the security hook on the
> other side always passes (and needs to always pass)
> 
> I recommend we make 2 changes to better our situation:
> 
> 1) remove CAP_SYS_MODULE from the networking code and instead check
> CAP_NET_ADMIN.  Maybe CAP_NET_ADMIN is already being checked and I'll
> just remove the capable call altogether but at least I can more
> intelligently limit the powers of these processes and they will still be
> root limited according to DAC permissions like they are today.
> 
Would this have any adverse effect on how user space sees this working.
Intuitively I would think that if you wanted to load a module (directly or
indirectly, via an iptables command or whatnot), you would need CAP_SYS_MODULE
capabilities on the calling process, not just CAP_NET_ADMIN.  I honestly don't
know the answer here, I'm just raising the question.

> 2) Add a new security hook inside request_module().  On a non-selinux
> system this would be a noop hook and all 128 callers of request_module()
> would perform exactly as they do today.  On SELinux systems I would add
> a new permission to see if a process was allowed to trigger a module
> load.  This permission would need to be added for things like /sbin/ip
> which are supposed to be allowed to trigger module loading, but would
> allow us to prevent [3] from taking place.
> 
This seems perfectly reasonable to me.

Neil

> Please, comments?  Thoughts?  What did I miss?
> 
> -Eric
> 
> [1] SELinux blocks IPv6 Tunnel
> https://bugzilla.redhat.com/show_bug.cgi?id=241401
> 
> [2] SELinux blocks bluetooth
> https://bugzilla.redhat.com/show_bug.cgi?id=481618
> 
> [3] xenfb exploit
> http://invisiblethingslab.com/pub/xenfb-adventures-10.pdf
>   page 6 section 3.4
> 
> 
> --
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

^ permalink raw reply

* Re: [PATCH 2/2] vhost_net: a kernel-level virtio server
From: Michael S. Tsirkin @ 2009-08-10 20:10 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: virtualization, netdev, kvm, linux-kernel
In-Reply-To: <200908102151.18529.arnd@arndb.de>

On Mon, Aug 10, 2009 at 09:51:18PM +0200, Arnd Bergmann wrote:
> On Monday 10 August 2009, Michael S. Tsirkin wrote:
> > What it is: vhost net is a character device that can be used to reduce
> > the number of system calls involved in virtio networking.
> > Existing virtio net code is used in the guest without modification.
> 
> Very nice, I loved reading it. It's getting rather late in my time
> zone, so this comments only on the network driver. I'll go through
> the rest tomorrow.
> 
> > @@ -293,6 +293,7 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
> >  		err = PTR_ERR(vblk->vq);
> >  		goto out_free_vblk;
> >  	}
> > +	printk(KERN_ERR "vblk->vq = %p\n", vblk->vq);
> >  
> >  	vblk->pool = mempool_create_kmalloc_pool(1,sizeof(struct virtblk_req));
> >  	if (!vblk->pool) {
> > @@ -383,6 +384,8 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
> >  	if (!err)
> >  		blk_queue_logical_block_size(vblk->disk->queue, blk_size);
> >  
> > +	printk(KERN_ERR "virtio_config_val returned %d\n", err);
> > +
> >  	add_disk(vblk->disk);
> >  	return 0;
> 
> I guess you meant to remove these before submitting.

Good catch, thanks!

> > +static void handle_tx_kick(struct work_struct *work);
> > +static void handle_rx_kick(struct work_struct *work);
> > +static void handle_tx_net(struct work_struct *work);
> > +static void handle_rx_net(struct work_struct *work);
> 
> [style] I think the code gets more readable if you reorder it
> so that you don't need forward declarations for static functions.

Right.

> > +static long vhost_net_reset_owner(struct vhost_net *n)
> > +{
> > +	struct socket *sock = NULL;
> > +	long r;
> > +	mutex_lock(&n->dev.mutex);
> > +	r = vhost_dev_check_owner(&n->dev);
> > +	if (r)
> > +		goto done;
> > +	sock = vhost_net_stop(n);
> > +	r = vhost_dev_reset_owner(&n->dev);
> > +done:
> > +	mutex_unlock(&n->dev.mutex);
> > +	if (sock)
> > +		fput(sock->file);
> > +	return r;
> > +}
> 
> what is the difference between vhost_net_reset_owner(n)
> and vhost_net_set_socket(n, -1)?

set socket to -1 will only stop the device.

reset owner will let another process take over the device.
It also needs to reset all parameters to make it safe for that
other process, so in particular the device is stopped.

I tried explaining this in the header vhost.h - does the comment
there help, or do I need to clarify it?

> > +
> > +static struct file_operations vhost_net_fops = {
> > +	.owner          = THIS_MODULE,
> > +	.release        = vhost_net_release,
> > +	.unlocked_ioctl = vhost_net_ioctl,
> > +	.open           = vhost_net_open,
> > +};
> 
> This is missing a compat_ioctl pointer. It should simply be
> 
> static long vhost_net_compat_ioctl(struct file *f,
> 			unsigned int ioctl, unsigned long arg)
> {
> 	return f, ioctl, (unsigned long)compat_ptr(arg);
> }

I had the impression that if there's no compat_ioctl,
unlocked_ioctl will get called automatically. No?

> > +/* Bits from fs/aio.c. TODO: export and use from there? */
> > +/*
> > + * use_mm
> > + *	Makes the calling kernel thread take on the specified
> > + *	mm context.
> > + *	Called by the retry thread execute retries within the
> > + *	iocb issuer's mm context, so that copy_from/to_user
> > + *	operations work seamlessly for aio.
> > + *	(Note: this routine is intended to be called only
> > + *	from a kernel thread context)
> > + */
> > +static void use_mm(struct mm_struct *mm)
> > +{
> > +	struct mm_struct *active_mm;
> > +	struct task_struct *tsk = current;
> > +
> > +	task_lock(tsk);
> > +	active_mm = tsk->active_mm;
> > +	atomic_inc(&mm->mm_count);
> > +	tsk->mm = mm;
> > +	tsk->active_mm = mm;
> > +	switch_mm(active_mm, mm, tsk);
> > +	task_unlock(tsk);
> > +
> > +	mmdrop(active_mm);
> > +}
> 
> Why do you need a kernel thread here? If the data transfer functions
> all get called from a guest intercept, shouldn't you already be
> in the right mm?

several reasons :)
- I get called under lock, so can't block
- eventfd can be passed to another process, and I won't be in guest context at all
- this also gets called outside guest context from socket poll
- vcpu is blocked while it's doing i/o. it is better to free it up
  as all the packet copying might take a while

> > +static void handle_tx(struct vhost_net *net)
> > +{
> > +	struct vhost_virtqueue *vq = &net->dev.vqs[VHOST_NET_VQ_TX];
> > +	unsigned head, out, in;
> > +	struct msghdr msg = {
> > +		.msg_name = NULL,
> > +		.msg_namelen = 0,
> > +		.msg_control = NULL,
> > +		.msg_controllen = 0,
> > +		.msg_iov = (struct iovec *)vq->iov + 1,
> > +		.msg_flags = MSG_DONTWAIT,
> > +	};
> > +	size_t len;
> > +	int err;
> > +	struct socket *sock = rcu_dereference(net->sock);
> > +	if (!sock || !sock_writeable(sock->sk))
> > +		return;
> > +
> > +	use_mm(net->dev.mm);
> > +	mutex_lock(&vq->mutex);
> > +	for (;;) {
> > +		head = vhost_get_vq_desc(&net->dev, vq, vq->iov, &out, &in);
> > +		if (head == vq->num)
> > +			break;
> > +		if (out <= 1 || in) {
> > +			vq_err(vq, "Unexpected descriptor format for TX: "
> > +			       "out %d, int %d\n", out, in);
> > +			break;
> > +		}
> > +		/* Sanity check */
> > +		if (vq->iov->iov_len != sizeof(struct virtio_net_hdr)) {
> > +			vq_err(vq, "Unexpected header len for TX: "
> > +			       "%ld expected %zd\n", vq->iov->iov_len,
> > +			       sizeof(struct virtio_net_hdr));
> > +			break;
> > +		}
> > +		/* Skip header. TODO: support TSO. */
> > +		msg.msg_iovlen = out - 1;
> > +		len = iov_length(vq->iov + 1, out - 1);
> > +		/* TODO: Check specific error and bomb out unless ENOBUFS? */
> > +		err = sock->ops->sendmsg(NULL, sock, &msg, len);
> > +		if (err < 0) {
> > +			vhost_discard_vq_desc(vq);
> > +			break;
> > +		}
> > +		if (err != len)
> > +			pr_err("Truncated TX packet: "
> > +			       " len %d != %zd\n", err, len);
> > +		vhost_add_used_and_trigger(vq, head,
> > +				     len + sizeof(struct virtio_net_hdr));
> > +	}
> > +
> > +	mutex_unlock(&vq->mutex);
> > +	unuse_mm(net->dev.mm);
> > +}
> 
> I guess that this is where one could plug into macvlan directly, using
> sock_alloc_send_skb/memcpy_fromiovec/dev_queue_xmit directly,
> instead of filling a msghdr for each, if we want to combine this
> with the work I did on that.

quite possibly. Or one can just bind a raw socket to macvlan :)

> 	Arnd <><

^ permalink raw reply

* [PATCH] fix memory leak in virtio_net
From: Shirley Ma @ 2009-08-10 20:05 UTC (permalink / raw)
  To: kvm, linux-kernel, netdev

[-- Attachment #1: Type: text/plain, Size: 649 bytes --]

Whild reading virtio code, I found some memory leak in removing
virtio_net.

In virtnet_remove, only skb has been freed not the pages in skb
frags. Here is the patch to fix this, please review it.

Signed-off-by: Shirley Ma <xma@us.ibm.com>

diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 2a6e81d..7e629d9 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -978,6 +978,7 @@ static void virtnet_remove(struct virtio_device *vdev)
 
 	/* Free our skbs in send and recv queues, if any. */
 	while ((skb = __skb_dequeue(&vi->recv)) != NULL) {
+		trim_pages(vi, skb);
 		kfree_skb(skb);
 		vi->num--;
 	}





[-- Attachment #2: memleak.patch --]
[-- Type: text/x-patch, Size: 447 bytes --]

Signed-off-by: Shirley Ma <xma@us.ibm.com>

diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 2a6e81d..7e629d9 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -978,6 +978,7 @@ static void virtnet_remove(struct virtio_device *vdev)
 
 	/* Free our skbs in send and recv queues, if any. */
 	while ((skb = __skb_dequeue(&vi->recv)) != NULL) {
+		trim_pages(vi, skb);
 		kfree_skb(skb);
 		vi->num--;
 	}

^ permalink raw reply related

* Re: [PATCH 2/2] vhost_net: a kernel-level virtio server
From: Arnd Bergmann @ 2009-08-10 19:51 UTC (permalink / raw)
  To: virtualization; +Cc: Michael S. Tsirkin, netdev, kvm, linux-kernel
In-Reply-To: <20090810185340.GC13924@redhat.com>

On Monday 10 August 2009, Michael S. Tsirkin wrote:
> What it is: vhost net is a character device that can be used to reduce
> the number of system calls involved in virtio networking.
> Existing virtio net code is used in the guest without modification.

Very nice, I loved reading it. It's getting rather late in my time
zone, so this comments only on the network driver. I'll go through
the rest tomorrow.

> @@ -293,6 +293,7 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
>  		err = PTR_ERR(vblk->vq);
>  		goto out_free_vblk;
>  	}
> +	printk(KERN_ERR "vblk->vq = %p\n", vblk->vq);
>  
>  	vblk->pool = mempool_create_kmalloc_pool(1,sizeof(struct virtblk_req));
>  	if (!vblk->pool) {
> @@ -383,6 +384,8 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
>  	if (!err)
>  		blk_queue_logical_block_size(vblk->disk->queue, blk_size);
>  
> +	printk(KERN_ERR "virtio_config_val returned %d\n", err);
> +
>  	add_disk(vblk->disk);
>  	return 0;

I guess you meant to remove these before submitting.

> +static void handle_tx_kick(struct work_struct *work);
> +static void handle_rx_kick(struct work_struct *work);
> +static void handle_tx_net(struct work_struct *work);
> +static void handle_rx_net(struct work_struct *work);

[style] I think the code gets more readable if you reorder it
so that you don't need forward declarations for static functions.

> +static long vhost_net_reset_owner(struct vhost_net *n)
> +{
> +	struct socket *sock = NULL;
> +	long r;
> +	mutex_lock(&n->dev.mutex);
> +	r = vhost_dev_check_owner(&n->dev);
> +	if (r)
> +		goto done;
> +	sock = vhost_net_stop(n);
> +	r = vhost_dev_reset_owner(&n->dev);
> +done:
> +	mutex_unlock(&n->dev.mutex);
> +	if (sock)
> +		fput(sock->file);
> +	return r;
> +}

what is the difference between vhost_net_reset_owner(n)
and vhost_net_set_socket(n, -1)?

> +
> +static struct file_operations vhost_net_fops = {
> +	.owner          = THIS_MODULE,
> +	.release        = vhost_net_release,
> +	.unlocked_ioctl = vhost_net_ioctl,
> +	.open           = vhost_net_open,
> +};

This is missing a compat_ioctl pointer. It should simply be

static long vhost_net_compat_ioctl(struct file *f,
			unsigned int ioctl, unsigned long arg)
{
	return f, ioctl, (unsigned long)compat_ptr(arg);
}

> +/* Bits from fs/aio.c. TODO: export and use from there? */
> +/*
> + * use_mm
> + *	Makes the calling kernel thread take on the specified
> + *	mm context.
> + *	Called by the retry thread execute retries within the
> + *	iocb issuer's mm context, so that copy_from/to_user
> + *	operations work seamlessly for aio.
> + *	(Note: this routine is intended to be called only
> + *	from a kernel thread context)
> + */
> +static void use_mm(struct mm_struct *mm)
> +{
> +	struct mm_struct *active_mm;
> +	struct task_struct *tsk = current;
> +
> +	task_lock(tsk);
> +	active_mm = tsk->active_mm;
> +	atomic_inc(&mm->mm_count);
> +	tsk->mm = mm;
> +	tsk->active_mm = mm;
> +	switch_mm(active_mm, mm, tsk);
> +	task_unlock(tsk);
> +
> +	mmdrop(active_mm);
> +}

Why do you need a kernel thread here? If the data transfer functions
all get called from a guest intercept, shouldn't you already be
in the right mm?

> +static void handle_tx(struct vhost_net *net)
> +{
> +	struct vhost_virtqueue *vq = &net->dev.vqs[VHOST_NET_VQ_TX];
> +	unsigned head, out, in;
> +	struct msghdr msg = {
> +		.msg_name = NULL,
> +		.msg_namelen = 0,
> +		.msg_control = NULL,
> +		.msg_controllen = 0,
> +		.msg_iov = (struct iovec *)vq->iov + 1,
> +		.msg_flags = MSG_DONTWAIT,
> +	};
> +	size_t len;
> +	int err;
> +	struct socket *sock = rcu_dereference(net->sock);
> +	if (!sock || !sock_writeable(sock->sk))
> +		return;
> +
> +	use_mm(net->dev.mm);
> +	mutex_lock(&vq->mutex);
> +	for (;;) {
> +		head = vhost_get_vq_desc(&net->dev, vq, vq->iov, &out, &in);
> +		if (head == vq->num)
> +			break;
> +		if (out <= 1 || in) {
> +			vq_err(vq, "Unexpected descriptor format for TX: "
> +			       "out %d, int %d\n", out, in);
> +			break;
> +		}
> +		/* Sanity check */
> +		if (vq->iov->iov_len != sizeof(struct virtio_net_hdr)) {
> +			vq_err(vq, "Unexpected header len for TX: "
> +			       "%ld expected %zd\n", vq->iov->iov_len,
> +			       sizeof(struct virtio_net_hdr));
> +			break;
> +		}
> +		/* Skip header. TODO: support TSO. */
> +		msg.msg_iovlen = out - 1;
> +		len = iov_length(vq->iov + 1, out - 1);
> +		/* TODO: Check specific error and bomb out unless ENOBUFS? */
> +		err = sock->ops->sendmsg(NULL, sock, &msg, len);
> +		if (err < 0) {
> +			vhost_discard_vq_desc(vq);
> +			break;
> +		}
> +		if (err != len)
> +			pr_err("Truncated TX packet: "
> +			       " len %d != %zd\n", err, len);
> +		vhost_add_used_and_trigger(vq, head,
> +				     len + sizeof(struct virtio_net_hdr));
> +	}
> +
> +	mutex_unlock(&vq->mutex);
> +	unuse_mm(net->dev.mm);
> +}

I guess that this is where one could plug into macvlan directly, using
sock_alloc_send_skb/memcpy_fromiovec/dev_queue_xmit directly,
instead of filling a msghdr for each, if we want to combine this
with the work I did on that.

	Arnd <><

^ permalink raw reply

* module loading permissions and request_module permission inconsistencies
From: Eric Paris @ 2009-08-10 19:45 UTC (permalink / raw)
  To: linux-kernel, netdev, linux-security-module
  Cc: sds, davem, shemminger, kees, morgan, serue, casey, dwlash

I'd like to hear thoughts on how we currently do permissions handling on
request_module() and if it really makes sense?  request_module() is the
function which will do an upcall to try to get modprobe to load a
specified module into the kernel.  It is called in a lot of places
around the kernel (~128).  Of those places only three check to see if
the REQUESTING process has some sort of module loading permissions
(CAP_SYS_RAWIO.)  Those three are in net/core/dev.c::dev_load() and in
the IPv4 tcp congestion code in tcp_set_default_congestion_control() and
tcp_set_congestion_control().  All 125 other calls to request_module()
appear to be done without any permissions check against the triggering
process.  The actual loading of a module is done in another thread which
always has permissions, so that side of things appears to not be an
issue.

First question, why does networking do it's own CAP_SYS_MODULE checks?
(this is VERY old code, pre-git days)  And, does it make sense?  In the
past this has come up in [1] when /sbin/ip triggered the loading of a
module to get IPv6 tunnel support.  It's perfectly reasonable
for /sbin/ip to do this.  But is it reasonable for /sbin/ip to need
CAP_SYS_MODULE?  CAP_SYS_MODULE says that /sbin/ip has permissions to
load any arbitrary binary it feels like as a kernel module directly.  Is
this really what we want?  Should SELinux have to give a hacked /sbin/ip
permissions to load any arbitrary module?  Recently in [2] we find that
now bluetoothd needs to be granted permissions to directly load any
kernel module it pleases, just so it can request the upcall loads bnep.
The same holds basically true for congestion control hooks.  Note that
I'm saying we are giving permission for these to load kernel modules
directly, not just through the upcall.

Nowhere else in the kernel do we do CAP_SYS_MODULE checks on the
triggering side of request_module() and, as I assume at least one of
those allows the user to control the module name, it seems to me that
the current checks are actually lowering the security bar.  We are
granting wide dangerous permissions to binaries when a more directed
permission would be much more sensible.  And we are doing it for no
security gain since I assume we have 10's if not more than 100 other
ways around it.

The second problem is this lack of control over the rest of the users of
request_module().  If we looks at [3] we see that this oversight created
an interesting and useful situation for a xen framebuffer exploit.  They
created an invalid binary and tried to execute it.  This caused the
kernel to enter search_binary_handler() which in turn called
request_module() which triggered the modprobe upcall and the were able
to load a module which they controlled.  As soon as they got their
module into the kernel it was game over for everything.  So we really
should be trying to prevent modules from getting into the kernel.  There
is no security hook on the triggering side and the security hook on the
other side always passes (and needs to always pass)

I recommend we make 2 changes to better our situation:

1) remove CAP_SYS_MODULE from the networking code and instead check
CAP_NET_ADMIN.  Maybe CAP_NET_ADMIN is already being checked and I'll
just remove the capable call altogether but at least I can more
intelligently limit the powers of these processes and they will still be
root limited according to DAC permissions like they are today.

2) Add a new security hook inside request_module().  On a non-selinux
system this would be a noop hook and all 128 callers of request_module()
would perform exactly as they do today.  On SELinux systems I would add
a new permission to see if a process was allowed to trigger a module
load.  This permission would need to be added for things like /sbin/ip
which are supposed to be allowed to trigger module loading, but would
allow us to prevent [3] from taking place.

Please, comments?  Thoughts?  What did I miss?

-Eric

[1] SELinux blocks IPv6 Tunnel
https://bugzilla.redhat.com/show_bug.cgi?id=241401

[2] SELinux blocks bluetooth
https://bugzilla.redhat.com/show_bug.cgi?id=481618

[3] xenfb exploit
http://invisiblethingslab.com/pub/xenfb-adventures-10.pdf
  page 6 section 3.4

^ permalink raw reply

* Re: [Bridge] [PATCH] macvlan: add tap device backend
From: Michael S. Tsirkin @ 2009-08-10 19:32 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Fischer, Anna, Paul Congdon (UC Davis), drobbins@funtoo.org,
	herbert@gondor.apana.org.au, netdev@vger.kernel.org,
	bridge@lists.linux-foundation.org, linux-kernel@vger.kernel.org,
	ogerlitz@voltaire.com, evb@yahoogroups.com, davem@davemloft.net
In-Reply-To: <200908102104.54527.arnd@arndb.de>

On Mon, Aug 10, 2009 at 09:04:54PM +0200, Arnd Bergmann wrote:
> On Monday 10 August 2009, Fischer, Anna wrote:
> > If you compare macvtap with traditional QEMU networking interfaces that
> > are typically used in current bridged setups, then yes, performance will be
> > different. However, I think that this is not necessarily a fair 
> > comparison, and the performance difference does not come from the 
> > bridge being slow, but simply because you have implemented a better
> > solution to connect a virtual interface to a backend device that
> > can be assigned to a VM. There is no reason why you could not do this
> > for a bridge port as well.
> 
> It's not necessarily the bridge itself being slow (though some people
> claim it is) but more the bridge preventing optimizations or making
> them hard.
> 
> You already mentioned hardware filtering by unicast and multicast
> mac addresses, which macvlan already does (for unicast) but which would be
> relatively complex with a bridge due to the way it does MAC address
> learning.
> 
> If we want to do zero copy receives, the hardware will on top of
> this have to choose the receive buffer based on the mac address,
> with the buffer provided by the guest. I think this is not easy
> with macvlan but doable, while I have no idea where you would start
> using the bridge code.
> 
> 	Arnd <><

Similar thing for zero copy sends. You need to know when
the buffers have been consumed to notify userspace,
and this is very hard with a generic bridge in the middle.


-- 
MST

^ permalink raw reply

* Re: strange behaviour in ping when specifying timeout in msec
From: Brent Cook @ 2009-08-10 19:09 UTC (permalink / raw)
  To: Michele BOLOGNA; +Cc: yoshfuji, netdev
In-Reply-To: <13b17b8a0908051627n32ce7461j75e8459a2c54909c@mail.gmail.com>

On Wednesday 05 August 2009 06:27:14 pm Michele BOLOGNA wrote:
> Dear Mr. Yoshifuji,
> I recently noticed a strange behaviour in ping from iputils.
>
> First of all some information:
> mbologna@neptune:~$ ping -V
> ping utility, iputils-sss20071127
>
> Now, I want to specify a timeout and after it expires I would like
> that ping exits, regardless of the packets received, so I use the -W
> option:
>
>       -W timeout
>               Time to wait for a response, in seconds. The option affects
> only timeout in absense of any responses, otherwise  ping  waits  for two
> RTTs.

If you check out the source to ping, you will see that the results of non-
integer arguments to -W is undefined, since atoi cannot detect errors. In your 
case, its probably being interpreted as 0. You can enter almost any value for 
-W without it showing an error, e.g. 'cat' or 'dog'.

    case 'W':
        lingertime = atoi(optarg);
        if (lingertime < 0 || lingertime > INT_MAX/1000000) {
            fprintf(stderr, "ping: bad linger time.\n");
            exit(2);
        }
        lingertime *= 1000;
        break;

Your best bet is perhaps to modify ping to parse time using strtof perhaps.

 - Brent

^ permalink raw reply

* Re: [evb] RE: [PATCH][RFC] net/bridge: add basic VEPA support
From: Arnd Bergmann @ 2009-08-10 19:18 UTC (permalink / raw)
  To: virtualization
  Cc: Stephen Hemminger, Fischer, Anna, Paul Congdon (UC Davis),
	evb@yahoogroups.com, netdev@vger.kernel.org,
	bridge@lists.linux-foundation.org, adobriyan@gmail.com,
	davem@davemloft.net
In-Reply-To: <20090810095118.125fe966@nehalam>

On Monday 10 August 2009, Stephen Hemminger wrote:
> On Mon, 10 Aug 2009 16:32:01, "Fischer, Anna" <anna.fischer@hp.com> wrote:
> > How would this work though, if the OS inside the guest wants to register
> > to a particular multicast address? Is this propagated through the backend
> > drivers to the macvlan/macvtap interface?
> 
> Sure filtering is better, but multicast performance with large number
> of guests is really a corner case, not the real performance issue.

Well, right now, qemu does not care at all about this, it essentially
leaves the tun device in ALLMULTI state. I should check whether macvtap
at this stage can receive multicast frames at all, but if it does,
it will get them all ;-).

If we want to implement this with kvm, we would have to start with
the qemu virtio-net implementation, to move the receive filter into
the tap device. With tun/tap that will mean less copying to user
space, with macvtap (after implementing TUNSETTXFILTER) we get already
pretty far because we no longer need to have the external interface
in ALLMULTI state. Once that is in place, we can start thinking about
filtering per virtual device.

	Arnd <><

^ permalink raw reply

* Re: [Bridge] [PATCH] macvlan: add tap device backend
From: Arnd Bergmann @ 2009-08-10 19:04 UTC (permalink / raw)
  To: Fischer, Anna
  Cc: Paul Congdon (UC Davis), drobbins@funtoo.org,
	herbert@gondor.apana.org.au, mst@redhat.com,
	netdev@vger.kernel.org, bridge@lists.linux-foundation.org,
	linux-kernel@vger.kernel.org, ogerlitz@voltaire.com,
	evb@yahoogroups.com, davem@davemloft.net
In-Reply-To: <0199E0D51A61344794750DC57738F58E6D6AE99700@GVW1118EXC.americas.hpqcorp.net>

On Monday 10 August 2009, Fischer, Anna wrote:
> If you compare macvtap with traditional QEMU networking interfaces that
> are typically used in current bridged setups, then yes, performance will be
> different. However, I think that this is not necessarily a fair 
> comparison, and the performance difference does not come from the 
> bridge being slow, but simply because you have implemented a better
> solution to connect a virtual interface to a backend device that
> can be assigned to a VM. There is no reason why you could not do this
> for a bridge port as well.

It's not necessarily the bridge itself being slow (though some people
claim it is) but more the bridge preventing optimizations or making
them hard.

You already mentioned hardware filtering by unicast and multicast
mac addresses, which macvlan already does (for unicast) but which would be
relatively complex with a bridge due to the way it does MAC address
learning.

If we want to do zero copy receives, the hardware will on top of
this have to choose the receive buffer based on the mac address,
with the buffer provided by the guest. I think this is not easy
with macvlan but doable, while I have no idea where you would start
using the bridge code.

	Arnd <><

^ permalink raw reply

* [PATCH 2/2] vhost_net: a kernel-level virtio server
From: Michael S. Tsirkin @ 2009-08-10 18:53 UTC (permalink / raw)
  To: netdev, virtualization, kvm, linux-kernel
In-Reply-To: <cover.1249930169.git.mst@redhat.com>

What it is: vhost net is a character device that can be used to reduce
the number of system calls involved in virtio networking.
Existing virtio net code is used in the guest without modification.

There's similarity with vringfd, with some differences and reduced scope
- uses eventfd for signalling
- structures can be moved around in memory at any time (good for migration)
- support memory table and not just an offset (needed for kvm)

common virtio related code has been put in a separate file vhost.c and
can be made into a separate module if/when more backend appear.  I used
Rusty's lguest.c as the source for developing this part : this supplied
me with witty comments I wouldn't be able to write myself.

What it is not: vhost net is not a bus, and not a generic new system
call. No assumptions are made on how guest performs hypercalls.
Userspace hypervisors are supported as well as kvm.

How it works: Basically, we connect virtio frontend (configured by
userspace) to a backend. The backend could be a network device, or a
tun-like device. In this version I only support raw socket as a backend,
which can be bound to e.g. SR IOV, or to macvlan device.  Backend is
also configured by userspace, including vlan/mac etc.

Status:
This works for me, and I haven't see any crashes.
I have not run any benchmarks yet, compared to userspace, I expect to
see improved latency (as I save up to 4 system calls per packet) but not
yet bandwidth/CPU (as TSO and interrupt mitigation are not yet supported).

Features that I plan to look at in the future:
- TSO
- interrupt mitigation
- zero copy

Signed-off-by: Michael S. Tsirkin <mst@redhat.com>

---
 MAINTAINERS                |   10 +
 arch/x86/kvm/Kconfig       |    1 +
 drivers/Makefile           |    1 +
 drivers/block/virtio_blk.c |    3 +
 drivers/vhost/Kconfig      |   11 +
 drivers/vhost/Makefile     |    2 +
 drivers/vhost/net.c        |  462 ++++++++++++++++++++++++++++++
 drivers/vhost/vhost.c      |  663 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/vhost/vhost.h      |  108 +++++++
 include/linux/Kbuild       |    1 +
 include/linux/miscdevice.h |    1 +
 include/linux/vhost.h      |  100 +++++++
 12 files changed, 1363 insertions(+), 0 deletions(-)
 create mode 100644 drivers/vhost/Kconfig
 create mode 100644 drivers/vhost/Makefile
 create mode 100644 drivers/vhost/net.c
 create mode 100644 drivers/vhost/vhost.c
 create mode 100644 drivers/vhost/vhost.h
 create mode 100644 include/linux/vhost.h

diff --git a/MAINTAINERS b/MAINTAINERS
index ebc2691..eb0c1da 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6312,6 +6312,16 @@ S:	Maintained
 F:	Documentation/filesystems/vfat.txt
 F:	fs/fat/
 
+VIRTIO HOST (VHOST)
+P:	Michael S. Tsirkin
+M:	mst@redhat.com
+L:	kvm@vger.kernel.org
+L:	virtualization@lists.osdl.org
+L:	netdev@vger.kernel.org
+S:	Maintained
+F:	drivers/vhost/
+F:	include/linux/vhost.h
+
 VIA RHINE NETWORK DRIVER
 P:	Roger Luethi
 M:	rl@hellgate.ch
diff --git a/arch/x86/kvm/Kconfig b/arch/x86/kvm/Kconfig
index b84e571..94f44d9 100644
--- a/arch/x86/kvm/Kconfig
+++ b/arch/x86/kvm/Kconfig
@@ -64,6 +64,7 @@ config KVM_AMD
 
 # OK, it's a little counter-intuitive to do this, but it puts it neatly under
 # the virtualization menu.
+source drivers/vhost/Kconfig
 source drivers/lguest/Kconfig
 source drivers/virtio/Kconfig
 
diff --git a/drivers/Makefile b/drivers/Makefile
index bc4205d..1551ae1 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -105,6 +105,7 @@ obj-$(CONFIG_HID)		+= hid/
 obj-$(CONFIG_PPC_PS3)		+= ps3/
 obj-$(CONFIG_OF)		+= of/
 obj-$(CONFIG_SSB)		+= ssb/
+obj-$(CONFIG_VHOST_NET)		+= vhost/
 obj-$(CONFIG_VIRTIO)		+= virtio/
 obj-$(CONFIG_VLYNQ)		+= vlynq/
 obj-$(CONFIG_STAGING)		+= staging/
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
index aa1a3d5..42e61b0 100644
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -293,6 +293,7 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
 		err = PTR_ERR(vblk->vq);
 		goto out_free_vblk;
 	}
+	printk(KERN_ERR "vblk->vq = %p\n", vblk->vq);
 
 	vblk->pool = mempool_create_kmalloc_pool(1,sizeof(struct virtblk_req));
 	if (!vblk->pool) {
@@ -383,6 +384,8 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
 	if (!err)
 		blk_queue_logical_block_size(vblk->disk->queue, blk_size);
 
+	printk(KERN_ERR "virtio_config_val returned %d\n", err);
+
 	add_disk(vblk->disk);
 	return 0;
 
diff --git a/drivers/vhost/Kconfig b/drivers/vhost/Kconfig
new file mode 100644
index 0000000..d955406
--- /dev/null
+++ b/drivers/vhost/Kconfig
@@ -0,0 +1,11 @@
+config VHOST_NET
+	tristate "Host kernel accelerator for virtio net"
+	depends on NET && EVENTFD
+	---help---
+	  This kernel module can be loaded in host kernel to accelerate
+	  guest networking with virtio_net. Not to be confused with virtio_net
+	  module itself which needs to be loaded in guest kernel.
+
+	  To compile this driver as a module, choose M here: the module will
+	  be called vhost_net.
+
diff --git a/drivers/vhost/Makefile b/drivers/vhost/Makefile
new file mode 100644
index 0000000..72dd020
--- /dev/null
+++ b/drivers/vhost/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_VHOST_NET) += vhost_net.o
+vhost_net-y := vhost.o net.o
diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
new file mode 100644
index 0000000..fc3359b
--- /dev/null
+++ b/drivers/vhost/net.c
@@ -0,0 +1,462 @@
+/* Copyright (C) 2009 Red Hat, Inc.
+ * Author: Michael S. Tsirkin <mst@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.
+ *
+ * virtio-net server in host kernel.
+ */
+
+#include <linux/eventfd.h>
+#include <linux/vhost.h>
+#include <linux/virtio_net.h>
+#include <linux/mm.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/rcupdate.h>
+#include <linux/file.h>
+
+#include <linux/net.h>
+#include <linux/if_packet.h>
+#include <linux/if_arp.h>
+
+#include <net/sock.h>
+
+#include <asm/mmu_context.h>
+
+#include "vhost.h"
+
+enum {
+	VHOST_NET_VQ_RX = 0,
+	VHOST_NET_VQ_TX = 1,
+	VHOST_NET_VQ_MAX = 2,
+};
+
+struct vhost_net {
+	struct vhost_dev dev;
+	struct vhost_virtqueue vqs[VHOST_NET_VQ_MAX];
+	/* We use a kind of RCU to access sock pointer.
+	 * All readers access it from workqueue,
+	 * which makes it possible to flush the workqueue
+	 * instead of synchronize_rcu. Therefore readers
+	 * do not need rcu_read_lock/rcu_read_unlock.
+	 * Writers use device mutex. */
+	struct socket *sock;
+	struct vhost_poll poll[VHOST_NET_VQ_MAX];
+};
+
+static void handle_tx_kick(struct work_struct *work);
+static void handle_rx_kick(struct work_struct *work);
+static void handle_tx_net(struct work_struct *work);
+static void handle_rx_net(struct work_struct *work);
+
+static int vhost_net_open(struct inode *inode, struct file *f)
+{
+	struct vhost_net *n = kzalloc(sizeof *n, GFP_KERNEL);
+	int r;
+	if (!n)
+		return -ENOMEM;
+	f->private_data = n;
+	n->vqs[VHOST_NET_VQ_TX].handle_kick = handle_tx_kick;
+	n->vqs[VHOST_NET_VQ_RX].handle_kick = handle_rx_kick;
+	r = vhost_dev_init(&n->dev, n->vqs, VHOST_NET_VQ_MAX);
+	if (r < 0) {
+		kfree(n);
+		return r;
+	}
+
+	vhost_poll_init(n->poll + VHOST_NET_VQ_TX, handle_tx_net, POLLOUT);
+	vhost_poll_init(n->poll + VHOST_NET_VQ_RX, handle_rx_net, POLLIN);
+	return 0;
+}
+
+static struct socket *vhost_net_stop(struct vhost_net *n)
+{
+	struct socket *sock = n->sock;
+	rcu_assign_pointer(n->sock, NULL);
+	if (sock) {
+		vhost_poll_flush(n->poll + VHOST_NET_VQ_TX);
+		vhost_poll_flush(n->poll + VHOST_NET_VQ_RX);
+	}
+	return sock;
+}
+
+static int vhost_net_release(struct inode *inode, struct file *f)
+{
+	struct vhost_net *n = f->private_data;
+	struct socket *sock;
+
+	sock = vhost_net_stop(n);
+	vhost_dev_cleanup(&n->dev);
+	if (sock)
+		fput(sock->file);
+	kfree(n);
+	return 0;
+}
+
+static long vhost_net_set_socket(struct vhost_net *n, int fd)
+{
+	struct {
+		struct sockaddr_ll sa;
+		char  buf[MAX_ADDR_LEN];
+	} uaddr;
+	struct socket *sock, *oldsock = NULL;
+	int uaddr_len = sizeof uaddr, r;
+
+	mutex_lock(&n->dev.mutex);
+	r = vhost_dev_check_owner(&n->dev);
+	if (r)
+		goto done;
+
+	if (fd == -1) {
+		/* Disconnect from socket and device. */
+		oldsock = vhost_net_stop(n);
+		goto done;
+	}
+	
+	sock = sockfd_lookup(fd, &r);
+	if (!sock) {
+		r = -ENOTSOCK;
+		goto done;
+	}
+
+	/* Parameter checking */
+	if (sock->sk->sk_type != SOCK_RAW) {
+		r = -ESOCKTNOSUPPORT;
+		goto done;
+	}
+
+	r = sock->ops->getname(sock, (struct sockaddr *)&uaddr.sa,
+			       &uaddr_len, 0);
+	if (r)
+		goto done;
+
+	if (uaddr.sa.sll_family != AF_PACKET) {
+		r = -EPFNOSUPPORT;
+		goto done;
+	}
+
+	/* start polling new socket */
+	if (sock == oldsock)
+		goto done;
+
+	if (oldsock) {
+		vhost_poll_stop(n->poll + VHOST_NET_VQ_TX);
+		vhost_poll_stop(n->poll + VHOST_NET_VQ_RX);
+	}
+	oldsock = n->sock;
+	rcu_assign_pointer(n->sock, sock);
+	vhost_poll_start(n->poll + VHOST_NET_VQ_TX, sock->file);
+	vhost_poll_start(n->poll + VHOST_NET_VQ_RX, sock->file);
+done:
+	mutex_unlock(&n->dev.mutex);
+	if (oldsock) {
+		vhost_poll_flush(n->poll + VHOST_NET_VQ_TX);
+		vhost_poll_flush(n->poll + VHOST_NET_VQ_RX);
+		vhost_poll_flush(&n->dev.vqs[VHOST_NET_VQ_TX].poll);
+		vhost_poll_flush(&n->dev.vqs[VHOST_NET_VQ_RX].poll);
+		fput(oldsock->file);
+	}
+	return r;
+}
+
+static long vhost_net_reset_owner(struct vhost_net *n)
+{
+	struct socket *sock = NULL;
+	long r;
+	mutex_lock(&n->dev.mutex);
+	r = vhost_dev_check_owner(&n->dev);
+	if (r)
+		goto done;
+	sock = vhost_net_stop(n);
+	r = vhost_dev_reset_owner(&n->dev);
+done:
+	mutex_unlock(&n->dev.mutex);
+	if (sock)
+		fput(sock->file);
+	return r;
+}
+
+static long vhost_net_ioctl(struct file *f, unsigned int ioctl,
+			    unsigned long arg)
+{
+	struct vhost_net *n = f->private_data;
+	void __user *argp = (void __user *)arg;
+	u32 __user *featurep = argp;
+	int __user *fdp = argp;
+	u32 features;
+	int fd, r;
+	switch (ioctl) {
+	case VHOST_NET_SET_SOCKET:
+		r = get_user(fd, fdp);
+		if (r < 0)
+			return r;
+		return vhost_net_set_socket(n, fd);
+	case VHOST_GET_FEATURES:
+		/* No features for now */
+		features = 0;
+		return put_user(features, featurep);
+	case VHOST_ACK_FEATURES:
+		r = get_user(features, featurep);
+		/* No features for now */
+		if (r < 0)
+			return r;
+		if (features)
+			return -EOPNOTSUPP;
+		return 0;
+	case VHOST_RESET_OWNER:
+		return vhost_net_reset_owner(n);
+	default:
+		return vhost_dev_ioctl(&n->dev, ioctl, arg);
+	}
+}
+
+static struct file_operations vhost_net_fops = {
+	.owner          = THIS_MODULE,
+	.release        = vhost_net_release,
+	.unlocked_ioctl = vhost_net_ioctl,
+	.open           = vhost_net_open,
+};
+
+static struct miscdevice vhost_net_misc = {
+	VHOST_NET_MINOR,
+	"vhost-net",
+	&vhost_net_fops,
+};
+
+/* Bits from fs/aio.c. TODO: export and use from there? */
+/*
+ * use_mm
+ *	Makes the calling kernel thread take on the specified
+ *	mm context.
+ *	Called by the retry thread execute retries within the
+ *	iocb issuer's mm context, so that copy_from/to_user
+ *	operations work seamlessly for aio.
+ *	(Note: this routine is intended to be called only
+ *	from a kernel thread context)
+ */
+static void use_mm(struct mm_struct *mm)
+{
+	struct mm_struct *active_mm;
+	struct task_struct *tsk = current;
+
+	task_lock(tsk);
+	active_mm = tsk->active_mm;
+	atomic_inc(&mm->mm_count);
+	tsk->mm = mm;
+	tsk->active_mm = mm;
+	switch_mm(active_mm, mm, tsk);
+	task_unlock(tsk);
+
+	mmdrop(active_mm);
+}
+
+/*
+ * unuse_mm
+ *	Reverses the effect of use_mm, i.e. releases the
+ *	specified mm context which was earlier taken on
+ *	by the calling kernel thread
+ *	(Note: this routine is intended to be called only
+ *	from a kernel thread context)
+ */
+static void unuse_mm(struct mm_struct *mm)
+{
+	struct task_struct *tsk = current;
+
+	task_lock(tsk);
+	tsk->mm = NULL;
+	/* active_mm is still 'mm' */
+	enter_lazy_tlb(mm, tsk);
+	task_unlock(tsk);
+}
+
+static void handle_tx(struct vhost_net *net)
+{
+	struct vhost_virtqueue *vq = &net->dev.vqs[VHOST_NET_VQ_TX];
+	unsigned head, out, in;
+	struct msghdr msg = {
+		.msg_name = NULL,
+		.msg_namelen = 0,
+		.msg_control = NULL,
+		.msg_controllen = 0,
+		.msg_iov = (struct iovec *)vq->iov + 1,
+		.msg_flags = MSG_DONTWAIT,
+	};
+	size_t len;
+	int err;
+	struct socket *sock = rcu_dereference(net->sock);
+	if (!sock || !sock_writeable(sock->sk))
+		return;
+
+	use_mm(net->dev.mm);
+	mutex_lock(&vq->mutex);
+	for (;;) {
+		head = vhost_get_vq_desc(&net->dev, vq, vq->iov, &out, &in);
+		if (head == vq->num)
+			break;
+		if (out <= 1 || in) {
+			vq_err(vq, "Unexpected descriptor format for TX: "
+			       "out %d, int %d\n", out, in);
+			break;
+		}
+		/* Sanity check */
+		if (vq->iov->iov_len != sizeof(struct virtio_net_hdr)) {
+			vq_err(vq, "Unexpected header len for TX: "
+			       "%ld expected %zd\n", vq->iov->iov_len,
+			       sizeof(struct virtio_net_hdr));
+			break;
+		}
+		/* Skip header. TODO: support TSO. */
+		msg.msg_iovlen = out - 1;
+		len = iov_length(vq->iov + 1, out - 1);
+		/* TODO: Check specific error and bomb out unless ENOBUFS? */
+		err = sock->ops->sendmsg(NULL, sock, &msg, len);
+		if (err < 0) {
+			vhost_discard_vq_desc(vq);
+			break;
+		}
+		if (err != len)
+			pr_err("Truncated TX packet: "
+			       " len %d != %zd\n", err, len);
+		vhost_add_used_and_trigger(vq, head,
+				     len + sizeof(struct virtio_net_hdr));
+	}
+
+	mutex_unlock(&vq->mutex);
+	unuse_mm(net->dev.mm);
+}
+
+static void handle_rx(struct vhost_net *net)
+{
+	struct vhost_virtqueue *vq = &net->dev.vqs[VHOST_NET_VQ_RX];
+	unsigned head, out, in;
+	struct msghdr msg = {
+		.msg_name = NULL,
+		.msg_namelen = 0,
+		.msg_control = NULL, /* FIXME: get and handle RX aux data. */
+		.msg_controllen = 0,
+		.msg_iov = vq->iov + 1,
+		.msg_flags = MSG_DONTWAIT,
+	};
+
+	struct virtio_net_hdr hdr = {
+		.flags = 0,
+		.gso_type = VIRTIO_NET_HDR_GSO_NONE
+	};
+
+	size_t len;
+	int err;
+	struct socket *sock = rcu_dereference(net->sock);
+	if (!sock || skb_queue_empty(&sock->sk->sk_receive_queue))
+		return;
+
+	use_mm(net->dev.mm);
+	mutex_lock(&vq->mutex);
+
+	for (;;) {
+		head = vhost_get_vq_desc(&net->dev, vq, vq->iov, &out, &in);
+		if (head == vq->num)
+			break;
+		if (in <= 1 || out) {
+			vq_err(vq, "Unexpected descriptor format for RX: out %d, int %d\n",
+			       out, in);
+			break;
+		}
+		/* Sanity check */
+		if (vq->iov->iov_len != sizeof(struct virtio_net_hdr)) {
+			vq_err(vq, "Unexpected header len for RX: %ld expected %zd\n",
+			       vq->iov->iov_len, sizeof(struct virtio_net_hdr));
+			break;
+		}
+		/* Skip header. TODO: support TSO/mergeable rx buffers. */
+		msg.msg_iovlen = in - 1;
+		len = iov_length(vq->iov + 1, in - 1);
+		err = sock->ops->recvmsg(NULL, sock, &msg,
+					 len, MSG_DONTWAIT | MSG_TRUNC);
+		/* TODO: Check specific error and bomb out unless EAGAIN? */
+		if (err < 0) {
+			vhost_discard_vq_desc(vq);
+			break;
+		}
+		/* TODO: Should check and handle checksum. */
+		if (err > len) {
+			pr_err("Discarded truncated rx packet: "
+			       " len %d > %zd\n", err, len);
+			vhost_discard_vq_desc(vq);
+			continue;
+		}
+		len = err;
+		err = copy_to_user(vq->iov->iov_base, &hdr, sizeof hdr);
+		if (err) {
+			vq_err(vq, "Unable to write vnet_hdr at addr %p: %d\n",
+			       vq->iov->iov_base, err);
+			break;
+		}
+		vhost_add_used_and_trigger(vq, head, len + sizeof hdr);
+	}
+
+	mutex_unlock(&vq->mutex);
+	unuse_mm(net->dev.mm);
+}
+
+static void handle_tx_kick(struct work_struct *work)
+{
+	struct vhost_virtqueue *vq;
+	struct vhost_net *net;
+	vq = container_of(work, struct vhost_virtqueue, poll.work);
+	net = container_of(vq->dev, struct vhost_net, dev);
+	handle_tx(net);
+}
+
+static void handle_rx_kick(struct work_struct *work)
+{
+	struct vhost_virtqueue *vq;
+	struct vhost_net *net;
+	vq = container_of(work, struct vhost_virtqueue, poll.work);
+	net = container_of(vq->dev, struct vhost_net, dev);
+	handle_rx(net);
+}
+
+static void handle_tx_net(struct work_struct *work)
+{
+	struct vhost_net *net;
+	net = container_of(work, struct vhost_net, poll[VHOST_NET_VQ_TX].work);
+	handle_tx(net);
+}
+
+static void handle_rx_net(struct work_struct *work)
+{
+	struct vhost_net *net;
+	net = container_of(work, struct vhost_net, poll[VHOST_NET_VQ_RX].work);
+	handle_rx(net);
+}
+
+int vhost_net_init(void)
+{
+	int r = vhost_init();
+	if (r)
+		goto err_init;
+	r = misc_register(&vhost_net_misc);
+	if (r)
+		goto err_reg;
+	return 0;
+err_reg:
+	vhost_cleanup();
+err_init:
+	return r;
+
+}
+module_init(vhost_net_init);
+
+void vhost_net_exit(void)
+{
+	misc_deregister(&vhost_net_misc);
+	vhost_cleanup();
+}
+module_exit(vhost_net_exit);
+
+MODULE_VERSION("0.0.1");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Michael S. Tsirkin");
+MODULE_DESCRIPTION("Host kernel accelerator for virtio net");
diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c
new file mode 100644
index 0000000..6178ec1
--- /dev/null
+++ b/drivers/vhost/vhost.c
@@ -0,0 +1,663 @@
+/* Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2006 Rusty Russell IBM Corporation
+ *
+ * Author: Michael S. Tsirkin <mst@redhat.com>
+ *
+ * Inspiration, some code, and most witty comments come from
+ * Documentation/lguest/lguest.c, by Rusty Russell
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.
+ *
+ * Generic code for virtio server in host kernel.
+ */
+
+#include <linux/eventfd.h>
+#include <linux/vhost.h>
+#include <linux/virtio_net.h>
+#include <linux/mm.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/rcupdate.h>
+#include <linux/poll.h>
+#include <linux/file.h>
+
+#include <linux/net.h>
+#include <linux/if_packet.h>
+#include <linux/if_arp.h>
+
+#include <net/sock.h>
+
+#include <asm/mmu_context.h>
+
+#include "vhost.h"
+
+enum {
+	VHOST_MEMORY_MAX_NREGIONS = 64,
+};
+
+struct workqueue_struct *vhost_workqueue;
+
+static void vhost_poll_func(struct file *file, wait_queue_head_t *wqh,
+			    poll_table *pt)
+{
+	struct vhost_poll *poll;
+	poll = container_of(pt, struct vhost_poll, table);
+
+	poll->wqh = wqh;
+	add_wait_queue(wqh, &poll->wait);
+}
+
+static int vhost_poll_wakeup(wait_queue_t *wait, unsigned mode, int sync, void *key)
+{
+	struct vhost_poll *poll;
+	poll = container_of(wait, struct vhost_poll, wait);
+	if (!((unsigned long)key & poll->mask))
+		return 0;
+
+	queue_work(vhost_workqueue, &poll->work);
+	return 0;
+}
+
+/* Init poll structure */
+void vhost_poll_init(struct vhost_poll *poll, work_func_t func,
+		     unsigned long mask)
+{
+	INIT_WORK(&poll->work, func);
+	init_waitqueue_func_entry(&poll->wait, vhost_poll_wakeup);
+	init_poll_funcptr(&poll->table, vhost_poll_func);
+	poll->mask = mask;
+}
+
+/* Start polling a file. We add ourselves to file's wait queue. The user must
+ * keep a reference to a file until after vhost_poll_stop is called. */
+void vhost_poll_start(struct vhost_poll *poll, struct file *file)
+{
+	unsigned long mask;
+	mask = file->f_op->poll(file, &poll->table);
+	if (mask)
+		vhost_poll_wakeup(&poll->wait, 0, 0, (void *)mask);
+}
+
+/* Stop polling a file. After this function returns, it becomes safe to drop the
+ * file reference. You must also flush afterwards. */
+void vhost_poll_stop(struct vhost_poll *poll)
+{
+	remove_wait_queue(poll->wqh, &poll->wait);
+}
+
+/* Flush any work that has been scheduled. When calling this, don't hold any
+ * locks that are also used by the callback. */
+void vhost_poll_flush(struct vhost_poll *poll)
+{
+	flush_work(&poll->work);
+}
+
+long vhost_dev_init(struct vhost_dev *dev, struct vhost_virtqueue *vqs, int nvqs)
+{
+	int i;
+	dev->vqs = vqs;
+	dev->nvqs = nvqs;
+	mutex_init(&dev->mutex);
+
+	for(i = 0; i < dev->nvqs; ++i) {
+		dev->vqs[i].dev = dev;
+		mutex_init(&dev->vqs[i].mutex);
+		if (dev->vqs[i].handle_kick)
+			vhost_poll_init(&dev->vqs[i].poll,
+					dev->vqs[i].handle_kick,
+					POLLIN);
+	}
+	return 0;
+}
+
+/* User should have device mutex */
+long vhost_dev_check_owner(struct vhost_dev *dev)
+{
+	return dev->mm == current->mm ? 0 : -EPERM;
+}
+
+/* User should have device mutex */
+static long vhost_dev_set_owner(struct vhost_dev *dev)
+{
+	if (dev->mm)
+		return -EBUSY;
+	dev->mm = get_task_mm(current);
+	return 0;
+}
+
+/* User should have device mutex */
+long vhost_dev_reset_owner(struct vhost_dev *dev)
+{
+	struct vhost_memory *memory;
+
+	/* Restore memory to default 1:1 mapping. */
+	memory = kmalloc(offsetof(struct vhost_memory, regions) +
+			 2 * sizeof *memory->regions, GFP_KERNEL);
+	if (!memory)
+		return -ENOMEM;
+
+	vhost_dev_cleanup(dev);
+
+	memory->nregions = 2;
+	memory->regions[0].guest_phys_addr = 1;
+	memory->regions[0].userspace_addr = 1;
+	memory->regions[0].memory_size = ~0ULL;
+	memory->regions[1].guest_phys_addr = 0;
+	memory->regions[1].userspace_addr = 0;
+	memory->regions[1].memory_size = 1;
+	dev->memory = memory;
+	return 0;
+}
+
+/* User should have device mutex */
+void vhost_dev_cleanup(struct vhost_dev *dev)
+{
+	int i;
+	for(i = 0; i < dev->nvqs; ++i) {
+		if (dev->vqs[i].kick && dev->vqs[i].handle_kick) {
+			vhost_poll_stop(&dev->vqs[i].poll);
+			vhost_poll_flush(&dev->vqs[i].poll);
+		}
+		if (dev->vqs[i].error_ctx)
+			eventfd_ctx_put(dev->vqs[i].error_ctx);
+		if (dev->vqs[i].error)
+			fput(dev->vqs[i].error);
+		if (dev->vqs[i].kick)
+			fput(dev->vqs[i].kick);
+		if (dev->vqs[i].call_ctx)
+			eventfd_ctx_put(dev->vqs[i].call_ctx);
+		if (dev->vqs[i].call)
+			fput(dev->vqs[i].call);
+		dev->vqs[i].error_ctx = NULL;
+		dev->vqs[i].error = NULL;
+		dev->vqs[i].kick = NULL;
+		dev->vqs[i].call_ctx = NULL;
+		dev->vqs[i].call = NULL;
+	}
+	/* No one will access memory at this point */
+	kfree(dev->memory);
+	dev->memory = NULL;
+	if (dev->mm)
+		mmput(dev->mm);
+	dev->mm = NULL;
+}
+
+static long vhost_set_memory(struct vhost_dev *d, struct vhost_memory __user *m)
+{
+	struct vhost_memory mem, *newmem, *oldmem;
+	unsigned long size = offsetof(struct vhost_memory, regions);
+	long r;
+	r = copy_from_user(&mem, m, size);
+	if (r)
+		return r;
+	if (mem.padding)
+		return -EOPNOTSUPP;
+	if (mem.nregions > VHOST_MEMORY_MAX_NREGIONS)
+		return -E2BIG;
+	newmem = kmalloc(size + mem.nregions * sizeof *m->regions, GFP_KERNEL);
+	if (!newmem)
+		return -ENOMEM;
+
+	memcpy(newmem, &mem, size);
+	r = copy_from_user(newmem->regions, m->regions,
+			   mem.nregions * sizeof *m->regions);
+	if (r) {
+		kfree(newmem);
+		return r;
+	}
+	oldmem = d->memory;
+	rcu_assign_pointer(d->memory, newmem);
+	synchronize_rcu();
+	kfree(oldmem);
+	return 0;
+}
+
+static int init_used(struct vhost_virtqueue *vq)
+{
+	u16 flags = 0;
+	int r = put_user(flags, &vq->used->flags);
+	if (r)
+		return r;
+	return get_user(vq->last_used_idx, &vq->used->idx);
+}
+
+static long vhost_set_vring(struct vhost_dev *d, int ioctl, void __user *argp)
+{
+	struct file *eventfp, *filep = NULL, *pollstart = NULL, *pollstop = NULL;
+	struct eventfd_ctx *ctx = NULL;
+	u32 __user *idxp = argp;
+	struct vhost_virtqueue *vq;
+	struct vhost_vring_state s;
+	struct vhost_vring_file f;
+	struct vhost_vring_addr a;
+	u32 idx;
+	long r;
+
+	r = get_user(idx, idxp);
+	if (r < 0)
+		return r;
+	if (idx > d->nvqs)
+		return -ENOBUFS;
+
+	vq = d->vqs + idx;
+
+	mutex_lock(&vq->mutex);
+
+	switch (ioctl) {
+	case VHOST_SET_VRING_NUM:
+		r = copy_from_user(&s, argp, sizeof s);
+		if (r < 0)
+			break;
+		if (s.num > 0xffff) {
+			r = -EINVAL;
+			break;
+		}
+		vq->num = s.num;
+		break;
+	case VHOST_SET_VRING_BASE:
+		r = copy_from_user(&s, argp, sizeof s);
+		if (r < 0)
+			break;
+		if (s.num > 0xffff) {
+			r = -EINVAL;
+			break;
+		}
+		vq->last_avail_idx = s.num;
+		break;
+	case VHOST_GET_VRING_BASE:
+		s.index = idx;
+		s.num = vq->last_avail_idx;
+		r = copy_to_user(argp, &s, sizeof s);
+		break;
+	case VHOST_SET_VRING_DESC:
+		r = copy_from_user(&a, argp, sizeof a);
+		if (r < 0)
+			break;
+		if (a.padding) {
+			r = -EOPNOTSUPP;
+			break;
+		}
+		if ((u64)(long)a.user_addr != a.user_addr) {
+			r = -EFAULT;
+			break;
+		}
+		vq->desc = (void __user *)(long)a.user_addr;
+		break;
+	case VHOST_SET_VRING_AVAIL:
+		r = copy_from_user(&a, argp, sizeof a);
+		if (r < 0)
+			break;
+		if (a.padding) {
+			r = -EOPNOTSUPP;
+			break;
+		}
+		if ((u64)(long)a.user_addr != a.user_addr) {
+			r = -EFAULT;
+			break;
+		}
+		vq->avail = (void __user *)(long)a.user_addr;
+		break;
+	case VHOST_SET_VRING_USED:
+		r = copy_from_user(&a, argp, sizeof a);
+		if (r < 0)
+			break;
+		if (a.padding) {
+			r = -EOPNOTSUPP;
+			break;
+		}
+		if ((u64)(long)a.user_addr != a.user_addr) {
+			r = -EFAULT;
+			break;
+		}
+		vq->used = (void __user *)(long)a.user_addr;
+		r = init_used(vq);
+		if (r)
+			break;
+		break;
+	case VHOST_SET_VRING_KICK:
+		r = copy_from_user(&f, argp, sizeof f);
+		if (r < 0)
+			break;
+		eventfp = f.fd == -1 ? NULL: eventfd_fget(f.fd);
+		if (IS_ERR(eventfp))
+			return PTR_ERR(eventfp);
+		if (eventfp != vq->kick) {
+			pollstop = filep = vq->kick;
+			pollstart = vq->kick = eventfp;
+		} else
+			filep = eventfp;
+		break;
+	case VHOST_SET_VRING_CALL:
+		r = copy_from_user(&f, argp, sizeof f);
+		if (r < 0)
+			break;
+		eventfp = f.fd == -1 ? NULL: eventfd_fget(f.fd);
+		if (IS_ERR(eventfp))
+			return PTR_ERR(eventfp);
+		if (eventfp != vq->call) {
+			filep = vq->call;
+			ctx = vq->call_ctx;
+			vq->call = eventfp;
+			vq->call_ctx = eventfp ?
+				eventfd_ctx_fileget(eventfp) : NULL;
+		} else
+			filep = eventfp;
+		break;
+	case VHOST_SET_VRING_ERR:
+		r = copy_from_user(&f, argp, sizeof f);
+		if (r < 0)
+			break;
+		eventfp = f.fd == -1 ? NULL: eventfd_fget(f.fd);
+		if (IS_ERR(eventfp))
+			return PTR_ERR(eventfp);
+		if (eventfp != vq->error) {
+			filep = vq->error;
+			vq->error = eventfp;
+			ctx = vq->error_ctx;
+			vq->error_ctx = eventfp ?
+				eventfd_ctx_fileget(eventfp) : NULL;
+		} else
+			filep = eventfp;
+		break;
+	default:
+		r = -ENOTTY;
+	}
+
+	if (pollstop && vq->handle_kick)
+		vhost_poll_stop(&vq->poll);
+
+	if (ctx)
+		eventfd_ctx_put(ctx);
+	if (filep)
+		fput(filep);
+
+	if (pollstart && vq->handle_kick)
+		vhost_poll_start(&vq->poll, vq->kick);
+
+	mutex_unlock(&vq->mutex);
+
+	if (pollstop && vq->handle_kick)
+		vhost_poll_flush(&vq->poll);
+	return 0;
+}
+
+long vhost_dev_ioctl(struct vhost_dev *d, unsigned int ioctl, unsigned long arg)
+{
+	void __user *argp = (void __user *)arg;
+	long r;
+
+	mutex_lock(&d->mutex);
+	if (ioctl == VHOST_SET_OWNER) {
+		r = vhost_dev_set_owner(d);
+		goto done;
+	}
+
+	r = vhost_dev_check_owner(d);
+	if (r)
+		goto done;
+		
+	switch (ioctl) {
+	case VHOST_SET_MEM_TABLE:
+		r = vhost_set_memory(d, argp);
+		break;
+	default:
+		r = vhost_set_vring(d, ioctl, argp);
+		break;
+	}
+done:
+	mutex_unlock(&d->mutex);
+	return r;
+}
+
+static const struct vhost_memory_region *find_region(struct vhost_memory *mem,
+						     __u64 addr, __u32 len)
+{
+	struct vhost_memory_region *reg;
+	int i;
+	/* linear search is not brilliant, but we really have on the order of 6
+	 * regions in practice */
+	for (i = 0; i < mem->nregions; ++i) {
+		reg = mem->regions + i;
+		if (reg->guest_phys_addr <= addr &&
+		    reg->guest_phys_addr + reg->memory_size - 1 >= addr)
+			return reg;
+	}
+	return NULL;
+}
+
+/* FIXME: this does not handle a region that spans multiple
+ * address/len pairs */
+int translate_desc(struct vhost_dev *dev, u64 addr, u32 len,
+		   struct iovec iov[], int iov_count, int iov_size,
+		   unsigned *num)
+{
+	const struct vhost_memory_region *reg;
+	struct vhost_memory *mem;
+	struct iovec *_iov;
+	u64 s = 0;
+	int ret = 0;
+
+	rcu_read_lock();
+
+	mem = rcu_dereference(dev->memory);
+	while ((u64)len > s) {
+		u64 size;
+		if (*num + iov_count >= iov_size) {
+			ret = -ENOBUFS;
+			break;
+		}
+		reg = find_region(mem, addr, len);
+		if (!reg) {
+			ret = -EFAULT;
+			break;
+		}
+		_iov = iov + iov_count + *num;
+		size = reg->memory_size - addr + reg->guest_phys_addr;
+		_iov->iov_len = min((u64)len, size);
+		_iov->iov_base = (void *)
+			(reg->userspace_addr + addr - reg->guest_phys_addr);
+		s += size;
+		addr += size;
+		++*num;
+	}
+	
+	rcu_read_unlock();
+	return ret;
+}
+
+/* Each buffer in the virtqueues is actually a chain of descriptors.  This
+ * function returns the next descriptor in the chain, or vq->vring.num if we're
+ * at the end. */
+static unsigned next_desc(struct vhost_virtqueue *vq, struct vring_desc *desc)
+{
+	unsigned int next;
+
+	/* If this descriptor says it doesn't chain, we're done. */
+	if (!(desc->flags & VRING_DESC_F_NEXT))
+		return vq->num;
+
+	/* Check they're not leading us off end of descriptors. */
+	next = desc->next;
+	/* Make sure compiler knows to grab that: we don't want it changing! */
+	/* We will use the result as an index in an array, so most
+	 * architectures only need a compiler barrier here. */
+	read_barrier_depends();
+
+	if (next >= vq->num) {
+		vq_err(vq, "Desc next is %u > %u", next, vq->num);
+		return vq->num;
+	}
+
+	return next;
+}
+
+/* This looks in the virtqueue and for the first available buffer, and converts
+ * it to an iovec for convenient access.  Since descriptors consist of some
+ * number of output then some number of input descriptors, it's actually two
+ * iovecs, but we pack them into one and note how many of each there were.
+ *
+ * This function returns the descriptor number found, or vq->num (which
+ * is never a valid descriptor number) if none was found. */
+unsigned vhost_get_vq_desc(struct vhost_dev *dev, struct vhost_virtqueue *vq,
+			   struct iovec iov[],
+			   unsigned int *out_num, unsigned int *in_num)
+{
+	struct vring_desc desc;
+	unsigned int i, head;
+	u16 last_avail_idx, idx;
+
+	/* Check it isn't doing very strange things with descriptor numbers. */
+	last_avail_idx = vq->last_avail_idx;
+	if (get_user(idx, &vq->avail->idx)) {
+		vq_err(vq, "Failed to access avail idx at %p\n",
+		       &vq->avail->idx);
+		return vq->num;
+	}
+	
+	if ((u16)(idx - last_avail_idx) > vq->num) {
+		vq_err(vq, "Guest moved used index from %u to %u",
+		       last_avail_idx, idx);
+		return vq->num;
+	}
+
+	/* If there's nothing new since last we looked, return invalid. */
+	if (idx == last_avail_idx)
+		return vq->num;
+
+	/* Grab the next descriptor number they're advertising, and increment
+	 * the index we've seen. */
+	if (get_user(head, &vq->avail->ring[last_avail_idx % vq->num])) {
+		vq_err(vq, "Failed to read head: idx %d address %p\n",
+		       idx, &vq->avail->ring[last_avail_idx % vq->num]);
+		return vq->num;
+	}
+
+	/* If their number is silly, that's a fatal mistake. */
+	if (head >= vq->num) {
+		vq_err(vq, "Guest says index %u > %u is available",
+		       head, vq->num);
+		return vq->num;
+	}
+
+	vq->last_avail_idx++;
+
+	/* When we start there are none of either input nor output. */
+	*out_num = *in_num = 0;
+
+	i = head;
+	do {
+		unsigned *num;
+		unsigned iov_count;
+		if (copy_from_user(&desc, vq->desc + i, sizeof desc)) {
+			vq_err(vq, "Failed to get descriptor: idx %d addr %p\n",
+			       i, vq->desc + i);
+			return vq->num;
+		}
+		/* If this is an input descriptor, increment that count. */
+		if (desc.flags & VRING_DESC_F_WRITE) {
+			num = in_num;
+			iov_count = *out_num;
+		} else {
+			/* If it's an output descriptor, they're all supposed
+			 * to come before any input descriptors. */
+			if (*in_num) {
+				vq_err(vq, "Descriptor has out after in: "
+				       "idx %d\n", i);
+				return vq->num;
+			}
+			num = out_num;
+			iov_count = *in_num;
+		}
+		if (translate_desc(dev, desc.addr, desc.len, iov, iov_count,
+				   VHOST_NET_MAX_SG, num)) {
+			vq_err(vq, "Failed to translate descriptor: idx %d\n",
+			       i);
+			return vq->num;
+		}
+
+		/* If we've got too many, that implies a descriptor loop. */
+		if (*out_num + *in_num > vq->num) {
+			vq_err(vq, "Looped descriptor: idx %d\n", i);
+			return vq->num;
+		}
+	} while ((i = next_desc(vq, &desc)) != vq->num);
+
+	vq->inflight++;
+	return head;
+}
+
+/* Reverse the effect of vhost_get_vq_desc. Useful for error handling. */
+void vhost_discard_vq_desc(struct vhost_virtqueue *vq)
+{
+	vq->last_avail_idx--;
+	vq->inflight--;
+}
+
+/* After we've used one of their buffers, we tell them about it.  We'll then
+ * want to send them an interrupt, using vq->call. */
+int vhost_add_used(struct vhost_virtqueue *vq,
+			  unsigned int head, int len)
+{
+	struct vring_used_elem *used;
+
+	/* The virtqueue contains a ring of used buffers.  Get a pointer to the
+	 * next entry in that used ring. */
+	used = &vq->used->ring[vq->last_used_idx % vq->num];
+	if (put_user(head, &used->id)) {
+		vq_err(vq, "Failed to write used id");
+		return -EFAULT;
+	}
+	if (put_user(len, &used->len)) {
+		vq_err(vq, "Failed to write used len");
+		return -EFAULT;
+	}
+	/* Make sure buffer is written before we update index. */
+	wmb();
+	if (put_user(vq->last_used_idx + 1, &vq->used->idx)) {
+		vq_err(vq, "Failed to increment used idx");
+		return -EFAULT;
+	}
+	vq->last_used_idx++;
+	vq->inflight--;
+	return 0;
+}
+
+/* This actually sends the interrupt for this virtqueue */
+void vhost_trigger_irq(struct vhost_virtqueue *vq)
+{
+	__u16 flags = 0;
+	if (get_user(flags, &vq->avail->flags)) {
+		vq_err(vq, "Failed to get flags");
+		return;
+	}
+
+	/* If they don't want an interrupt, don't send one, unless empty. */
+	if ((flags & VRING_AVAIL_F_NO_INTERRUPT) && vq->inflight)
+		return;
+
+	/* Send the Guest an interrupt tell them we used something up. */
+	if (vq->call_ctx)
+		eventfd_signal(vq->call_ctx, 1);
+}
+
+/* And here's the combo meal deal.  Supersize me! */
+void vhost_add_used_and_trigger(struct vhost_virtqueue *vq,
+				unsigned int head, int len)
+{
+	vhost_add_used(vq, head, len);
+	vhost_trigger_irq(vq);
+}
+
+int vhost_init(void)
+{
+	vhost_workqueue = create_workqueue("vhost");
+	if (!vhost_workqueue)
+		return -ENOMEM;
+	return 0;
+}
+
+void vhost_cleanup(void)
+{
+	destroy_workqueue(vhost_workqueue);
+}
diff --git a/drivers/vhost/vhost.h b/drivers/vhost/vhost.h
new file mode 100644
index 0000000..7f7ffcd
--- /dev/null
+++ b/drivers/vhost/vhost.h
@@ -0,0 +1,108 @@
+#ifndef _VHOST_H
+#define _VHOST_H
+
+#include <linux/eventfd.h>
+#include <linux/vhost.h>
+#include <linux/mm.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/poll.h>
+#include <linux/file.h>
+#include <linux/skbuff.h>
+
+struct vhost_device;
+
+enum {
+	VHOST_NET_MAX_SG = MAX_SKB_FRAGS + 2,
+};
+
+/* Poll a file (eventfd or socket) */
+/* Note: there's nothing vhost specific about this structure. */
+struct vhost_poll {
+	poll_table                table;
+	wait_queue_head_t        *wqh;
+	wait_queue_t              wait;
+	/* struct which will handle all actual work. */
+	struct work_struct        work;
+	unsigned long		  mask;
+};
+
+void vhost_poll_init(struct vhost_poll *poll, work_func_t func,
+		     unsigned long mask);
+void vhost_poll_start(struct vhost_poll *poll, struct file *file);
+void vhost_poll_stop(struct vhost_poll *poll);
+void vhost_poll_flush(struct vhost_poll *poll);
+
+/* The virtqueue structure describes a queue attached to a device. */
+struct vhost_virtqueue {
+	struct vhost_dev *dev;
+
+	/* The actual ring of buffers. */
+	struct mutex mutex;
+	unsigned int num;
+	struct vring_desc __user *desc;
+	struct vring_avail __user *avail;
+	struct vring_used __user *used;
+	struct file *kick;
+	struct file *call;
+	struct file *error;
+	struct eventfd_ctx *call_ctx;
+	struct eventfd_ctx *error_ctx;
+
+	struct vhost_poll poll;
+
+	/* The routine to call when the Guest pings us, or timeout. */
+	work_func_t handle_kick;
+
+	/* Last available index we saw. */
+	u16 last_avail_idx;
+
+	/* Last index we used. */
+	u16 last_used_idx;
+
+	/* Outstanding buffers */
+	unsigned int inflight;
+
+	/* Is this blocked? */
+	bool blocked;
+
+	struct iovec iov[VHOST_NET_MAX_SG];
+
+} ____cacheline_aligned;
+
+struct vhost_dev {
+	/* Readers use RCU to access memory table pointer.
+	 * Writers use mutex below.*/
+	struct vhost_memory *memory;
+	struct mm_struct *mm;
+	struct vhost_virtqueue *vqs;
+	int nvqs;
+	struct mutex mutex;
+};
+
+long vhost_dev_init(struct vhost_dev *, struct vhost_virtqueue *vqs, int nvqs);
+long vhost_dev_check_owner(struct vhost_dev *);
+long vhost_dev_reset_owner(struct vhost_dev *);
+void vhost_dev_cleanup(struct vhost_dev *);
+long vhost_dev_ioctl(struct vhost_dev *, unsigned int ioctl, unsigned long arg);
+
+unsigned vhost_get_vq_desc(struct vhost_dev *, struct vhost_virtqueue *,
+			   struct iovec iov[],
+			   unsigned int *out_num, unsigned int *in_num);
+void vhost_discard_vq_desc(struct vhost_virtqueue *);
+
+int vhost_add_used(struct vhost_virtqueue *, unsigned int head, int len);
+void vhost_trigger_irq(struct vhost_virtqueue *);
+void vhost_add_used_and_trigger(struct vhost_virtqueue *,
+				unsigned int head, int len);
+
+int vhost_init(void);
+void vhost_cleanup(void);
+
+#define vq_err(vq, fmt, ...) do {                                  \
+		printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__);       \
+		if ((vq)->error_ctx)                               \
+				eventfd_signal((vq)->error_ctx, 1);\
+	} while (0)
+
+#endif
diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index dec2f18..975df9a 100644
--- a/include/linux/Kbuild
+++ b/include/linux/Kbuild
@@ -360,6 +360,7 @@ unifdef-y += uio.h
 unifdef-y += unistd.h
 unifdef-y += usbdevice_fs.h
 unifdef-y += utsname.h
+unifdef-y += vhost.h
 unifdef-y += videodev2.h
 unifdef-y += videodev.h
 unifdef-y += virtio_config.h
diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h
index 0521177..781a8bb 100644
--- a/include/linux/miscdevice.h
+++ b/include/linux/miscdevice.h
@@ -30,6 +30,7 @@
 #define HPET_MINOR		228
 #define FUSE_MINOR		229
 #define KVM_MINOR		232
+#define VHOST_NET_MINOR		233
 #define MISC_DYNAMIC_MINOR	255
 
 struct device;
diff --git a/include/linux/vhost.h b/include/linux/vhost.h
new file mode 100644
index 0000000..9ec6d5f
--- /dev/null
+++ b/include/linux/vhost.h
@@ -0,0 +1,100 @@
+#ifndef _LINUX_VHOST_H
+#define _LINUX_VHOST_H
+/* Userspace interface for in-kernel virtio accelerators. */
+
+/* vhost is used to reduce the number of system calls involved in virtio.
+ *
+ * Existing virtio net code is used in the guest without modification.
+ *
+ * This header includes interface used by userspace hypervisor for
+ * device configuration.
+ */
+
+#include <linux/types.h>
+#include <linux/compiler.h>
+#include <linux/ioctl.h>
+#include <linux/virtio_config.h>
+#include <linux/virtio_ring.h>
+
+struct vhost_vring_state {
+	unsigned int index;
+	unsigned int num;
+};
+
+struct vhost_vring_file {
+	unsigned int index;
+	int fd;
+};
+
+struct vhost_vring_addr {
+	unsigned int index;
+	unsigned int padding;
+	__u64 user_addr;
+};
+
+struct vhost_memory_region {
+	__u64 guest_phys_addr;
+	__u64 memory_size; /* bytes */
+	__u64 userspace_addr;
+	__u64 padding; /* read/write protection? */
+};
+
+struct vhost_memory {
+	__u32 nregions;
+	__u32 padding;
+	struct vhost_memory_region regions[0];
+};
+
+/* ioctls */
+
+#define VHOST_VIRTIO 0xAF
+
+/* Features bitmask for forward compatibility. Transport bits must be zero. */
+#define VHOST_GET_FEATURES	_IOR(VHOST_VIRTIO, 0x00, __u32)
+#define VHOST_ACK_FEATURES	_IOW(VHOST_VIRTIO, 0x00, __u32)
+
+/* Set current process as the (exclusive) owner of this file descriptor.  This
+ * must be called before any other vhost command.  Further calls to
+ * VHOST_OWNER_SET fail until VHOST_OWNER_RESET is called. */
+#define VHOST_SET_OWNER _IO(VHOST_VIRTIO, 0x01)
+/* Give up ownership, and reset the device to default values.
+ * Allows subsequent call to VHOST_OWNER_SET to succeed. */
+#define VHOST_RESET_OWNER _IO(VHOST_VIRTIO, 0x02)
+
+/* Set up/modify memory layout */
+#define VHOST_SET_MEM_TABLE	_IOW(VHOST_VIRTIO, 0x03, struct vhost_memory)
+
+/* Ring setup. These parameters can not be modified while ring is running
+ * (bound to a device). */
+/* Set number of descriptors in ring */
+#define VHOST_SET_VRING_NUM _IOW(VHOST_VIRTIO, 0x10, struct vhost_vring_state)
+/* Start of array of descriptors (virtually contiguous) */
+#define VHOST_SET_VRING_DESC _IOW(VHOST_VIRTIO, 0x11, struct vhost_vring_addr)
+/* Used structure address */
+#define VHOST_SET_VRING_USED _IOW(VHOST_VIRTIO, 0x12, struct vhost_vring_addr)
+/* Available structure address */
+#define VHOST_SET_VRING_AVAIL _IOW(VHOST_VIRTIO, 0x13, struct vhost_vring_addr)
+/* Base value where queue looks for available descriptors */
+#define VHOST_SET_VRING_BASE _IOW(VHOST_VIRTIO, 0x14, struct vhost_vring_state)
+/* Get accessor: reads index, writes value in num */
+#define VHOST_GET_VRING_BASE _IOWR(VHOST_VIRTIO, 0x14, struct vhost_vring_state)
+
+/* The following ioctls use eventfd file descriptors to signal and poll
+ * for events. */
+
+/* Set eventfd to poll for added buffers */
+#define VHOST_SET_VRING_KICK _IOW(VHOST_VIRTIO, 0x20, struct vhost_vring_file)
+/* Set eventfd to signal when buffers have beed used */
+#define VHOST_SET_VRING_CALL _IOW(VHOST_VIRTIO, 0x21, struct vhost_vring_file)
+/* Set eventfd to signal an error */
+#define VHOST_SET_VRING_ERR _IOW(VHOST_VIRTIO, 0x22, struct vhost_vring_file)
+
+/* VHOST_NET specific defines */
+
+/* Attach virtio net device to a raw socket. The socket must be already
+ * bound to an ethernet device, this device will be used for transmit.
+ * Pass -1 to unbind from the socket and the transmit device.
+ * This can be used to stop the device (e.g. for migration). */
+#define VHOST_NET_SET_SOCKET _IOW(VHOST_VIRTIO, 0x30, int)
+
+#endif
-- 
1.6.2.5

^ permalink raw reply related

* Re: [PATCH] AF_PACKET: Fix PACKET_MMAP on 64-bit kernel/32-bit user systems
From: Chase Douglas @ 2009-08-10 18:52 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: netdev
In-Reply-To: <4A805612.5080709@trash.net>

[-- Attachment #1: Type: text/plain, Size: 277 bytes --]

On Aug 10, 2009, at 1:17 PM, Patrick McHardy wrote:
> The tpacket2_hdr doesn't suffer from this problem.

I hadn't noticed the new header version. I've grabbed the latest  
version of libpcap which utilizes tpacket2_hdr where available, and  
everything is fixed again.

Thanks

[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 3892 bytes --]

^ permalink raw reply

* [PATCH 1/2] export cpu_tlbstate to modules
From: Michael S. Tsirkin @ 2009-08-10 18:53 UTC (permalink / raw)
  To: netdev, virtualization, kvm, linux-kernel, mingo
In-Reply-To: <cover.1249930169.git.mst@redhat.com>

vhost net module wants to do copy to/from user from a kernel thread,
which needs switch_mm (like what fs/aio has).
export cpu_tlbstate to make this possible

Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
---
 arch/x86/mm/tlb.c |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/arch/x86/mm/tlb.c b/arch/x86/mm/tlb.c
index 821e970..e33a5f0 100644
--- a/arch/x86/mm/tlb.c
+++ b/arch/x86/mm/tlb.c
@@ -13,6 +13,7 @@
 
 DEFINE_PER_CPU_SHARED_ALIGNED(struct tlb_state, cpu_tlbstate)
 			= { &init_mm, 0, };
+EXPORT_PER_CPU_SYMBOL_GPL(cpu_tlbstate);
 
 /*
  *	Smarter SMP flushing macros.
-- 
1.6.2.5


^ permalink raw reply related

* [PATCH 0/2] vhost: a kernel-level virtio server
From: Michael S. Tsirkin @ 2009-08-10 18:51 UTC (permalink / raw)
  To: netdev, virtualization, kvm, linux-kernel

This implements vhost: a kernel-level backend for virtio,
The main motivation for this work is to reduce virtualization
overhead for virtio by removing system calls on data path,
without guest changes. For virtio-net, this removes up to
4 system calls per packet: vm exit for kick, reentry for kick,
iothread wakeup for packet, interrupt injection for packet.

Some more detailed description attached to the patch itself.

The patches are against 2.6.31-rc4.  I'd like them to go into linux-next
and down the road 2.6.32 if possible.  Please comment.

Userspace bits using this driver will be posted to kvm@vger shortly.

Michael S. Tsirkin (2):
  export cpu_tlbstate to modules
  vhost_net: a kernel-level virtio server

 MAINTAINERS                |   10 +
 arch/x86/kvm/Kconfig       |    1 +
 arch/x86/mm/tlb.c          |    1 +
 drivers/Makefile           |    1 +
 drivers/block/virtio_blk.c |    3 +
 drivers/vhost/Kconfig      |   11 +
 drivers/vhost/Makefile     |    2 +
 drivers/vhost/net.c        |  462 ++++++++++++++++++++++++++++++
 drivers/vhost/vhost.c      |  663 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/vhost/vhost.h      |  108 +++++++
 include/linux/Kbuild       |    1 +
 include/linux/miscdevice.h |    1 +
 include/linux/vhost.h      |  100 +++++++
 13 files changed, 1364 insertions(+), 0 deletions(-)
 create mode 100644 drivers/vhost/Kconfig
 create mode 100644 drivers/vhost/Makefile
 create mode 100644 drivers/vhost/net.c
 create mode 100644 drivers/vhost/vhost.c
 create mode 100644 drivers/vhost/vhost.h
 create mode 100644 include/linux/vhost.h

^ permalink raw reply

* Re: [PATCH] macvlan: add tap device backend
From: Arnd Bergmann @ 2009-08-10 18:43 UTC (permalink / raw)
  To: Patrick McHardy
  Cc: netdev, Stephen Hemminger, davem, Michael S. Tsirkin, Herbert Xu,
	Or Gerlitz, Fischer, Anna, bridge, linux-kernel,
	Edge Virtual Bridging
In-Reply-To: <4A7FC27F.1090904@trash.net>

On Monday 10 August 2009, Patrick McHardy wrote:

> macvlan will remove all macvlan/vtap devices when the underlying
> device in unregistered, at which time you need to release the
> device references you're holding. I'd suggest to change the
> macvlan_device_event() handler to use
> 
> vlan->dev->rtnl_link_ops->dellink(vlan->dev)
> 
> instead of macvlan_dellink() so the macvtap_dellink callback
> is invoked.

Ok, will do that. Thanks,

	Arnd <><

^ permalink raw reply

* Re: [PATCH 2/2] ieee802154: add virtual loopback driver
From: Oliver Hartkopp @ 2009-08-10 17:28 UTC (permalink / raw)
  To: Dmitry Eremin-Solenikov
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-zigbee-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
	linux-wireless-u79uwXL29TY76Z2rM5mHXA, Sergey Lapin,
	Patrick McHardy
In-Reply-To: <1249913800-10176-3-git-send-email-dbaryshkov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

Dmitry Eremin-Solenikov wrote:
> fakelb is a virtual loopback driver implementing one or several
> interconnected radios. 

> +
> +static int radios = 3;
> +module_param(radios, int, 0444);
> +MODULE_PARM_DESC(radios, "Number of simulated radios");
> +


> +
> +	for (i = 0; i < radios; i++) {
> +		err = ieee802154fake_add_priv(&pdev->dev, priv);
> +		if (err < 0)
> +			goto err_slave;
> +	}
> +

Hi Dimtry,

creating software networking devices like this is IMO not state-of-the-art for
 kernel inclusion.

Please use the NETLINK interface for creating the virtual loopback drivers,
like e.g. the virtual CAN driver (drivers/net/can/vcan.c) does.

Regards,
Oliver
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [RFC PATCH v2 2/2] selinux: Support for the new TUN LSM hooks
From: Paul Moore @ 2009-08-10 17:28 UTC (permalink / raw)
  To: linux-security-module, netdev, selinux
In-Reply-To: <20090810172238.7946.34247.stgit@flek.lan>

Add support for the new TUN LSM hooks: security_tun_dev_create(),
security_tun_dev_post_create() and security_tun_dev_attach().  This includes
the addition of a new object class, tun_socket, which represents the socks
associated with TUN devices.  The _tun_dev_create() and _tun_dev_post_create()
hooks are fairly similar to the standard socket functions but _tun_dev_attach()
is a bit special.  The _tun_dev_attach() is unique because it involves a
domain attaching to an existing TUN device and its associated tun_socket
object, an operation which does not exist with standard sockets and most
closely resembles a relabel operation.

--

NOTE: This relies on some changes to the policy to add the new object class
      and its associated permissions, I will ensure that the policy is sorted
      and merged before pushing this patch upstream.  Also, you will notice
      that the new tun_socket object class simply inherits the base socket
      object class, thoughts?
---

 security/selinux/hooks.c                   |   60 +++++++++++++++++++++++++++-
 security/selinux/include/av_inherit.h      |    1 
 security/selinux/include/av_permissions.h  |   22 ++++++++++
 security/selinux/include/class_to_string.h |    1 
 security/selinux/include/flask.h           |    1 
 5 files changed, 83 insertions(+), 2 deletions(-)

diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 15c2a08..fc7caa0 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -13,8 +13,8 @@
  *					   Eric Paris <eparis@redhat.com>
  *  Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
  *			    <dgoeddel@trustedcs.com>
- *  Copyright (C) 2006, 2007 Hewlett-Packard Development Company, L.P.
- *		Paul Moore <paul.moore@hp.com>
+ *  Copyright (C) 2006, 2007, 2009 Hewlett-Packard Development Company, L.P.
+ *	Paul Moore <paul.moore@hp.com>
  *  Copyright (C) 2007 Hitachi Software Engineering Co., Ltd.
  *		       Yuichi Nakamura <ynakam@hitachisoft.jp>
  *
@@ -4296,6 +4296,59 @@ static void selinux_req_classify_flow(const struct request_sock *req,
 	fl->secid = req->secid;
 }
 
+static int selinux_tun_dev_create(void)
+{
+	u32 sid = current_sid();
+
+	/* we aren't taking into account the "sockcreate" SID since the socket
+	 * that is being created here is not a socket in the traditional sense,
+	 * instead it is a private sock, accessible only to the kernel, and
+	 * representing a wide range of network traffic spanning multiple
+	 * connections unlike traditional sockets - check the TUN driver to
+	 * get a better understanding of why this socket is special */
+
+	return avc_has_perm(sid, sid, SECCLASS_TUN_SOCKET, TUN_SOCKET__CREATE,
+			    NULL);
+}
+
+static void selinux_tun_dev_post_create(struct sock *sk)
+{
+	struct sk_security_struct *sksec = sk->sk_security;
+
+	/* we don't currently perform any NetLabel based labeling here and it
+	 * isn't clear that we would want to do so anyway; while we could apply
+	 * labeling without the support of the TUN user the resulting labeled
+	 * traffic from the other end of the connection would almost certainly
+	 * cause confusion to the TUN user that had no idea network labeling
+	 * protocols were being used */
+
+	/* see the comments in selinux_tun_dev_create() about why we don't use
+	 * the sockcreate SID here */
+
+	sksec->sid = current_sid();
+	sksec->sclass = SECCLASS_TUN_SOCKET;
+}
+
+static int selinux_tun_dev_attach(struct sock *sk)
+{
+	struct sk_security_struct *sksec = sk->sk_security;
+	u32 sid = current_sid();
+	int err;
+
+	err = avc_has_perm(sid, sksec->sid, SECCLASS_TUN_SOCKET,
+			   TUN_SOCKET__RELABELFROM, NULL);
+	if (err)
+		return err;
+	err = avc_has_perm(sid, sid, SECCLASS_RAWIP_SOCKET,
+			   TUN_SOCKET__RELABELTO, NULL);
+	if (err)
+		return err;
+
+	sksec->sid = sid;
+
+	return 0;
+}
+
 static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb)
 {
 	int err = 0;
@@ -5464,6 +5517,9 @@ static struct security_operations selinux_ops = {
 	.inet_csk_clone =		selinux_inet_csk_clone,
 	.inet_conn_established =	selinux_inet_conn_established,
 	.req_classify_flow =		selinux_req_classify_flow,
+	.tun_dev_create =		selinux_tun_dev_create,
+	.tun_dev_post_create = 		selinux_tun_dev_post_create,
+	.tun_dev_attach =		selinux_tun_dev_attach,
 
 #ifdef CONFIG_SECURITY_NETWORK_XFRM
 	.xfrm_policy_alloc_security =	selinux_xfrm_policy_alloc,
diff --git a/security/selinux/include/av_inherit.h b/security/selinux/include/av_inherit.h
index 8377a4b..abedcd7 100644
--- a/security/selinux/include/av_inherit.h
+++ b/security/selinux/include/av_inherit.h
@@ -15,6 +15,7 @@
    S_(SECCLASS_KEY_SOCKET, socket, 0x00400000UL)
    S_(SECCLASS_UNIX_STREAM_SOCKET, socket, 0x00400000UL)
    S_(SECCLASS_UNIX_DGRAM_SOCKET, socket, 0x00400000UL)
+   S_(SECCLASS_TUN_SOCKET, socket, 0x00400000UL)
    S_(SECCLASS_IPC, ipc, 0x00000200UL)
    S_(SECCLASS_SEM, ipc, 0x00000200UL)
    S_(SECCLASS_MSGQ, ipc, 0x00000200UL)
diff --git a/security/selinux/include/av_permissions.h b/security/selinux/include/av_permissions.h
index d645192..0b41ad5 100644
--- a/security/selinux/include/av_permissions.h
+++ b/security/selinux/include/av_permissions.h
@@ -423,6 +423,28 @@
 #define UNIX_DGRAM_SOCKET__RECV_MSG               0x00080000UL
 #define UNIX_DGRAM_SOCKET__SEND_MSG               0x00100000UL
 #define UNIX_DGRAM_SOCKET__NAME_BIND              0x00200000UL
+#define TUN_SOCKET__IOCTL                         0x00000001UL
+#define TUN_SOCKET__READ                          0x00000002UL
+#define TUN_SOCKET__WRITE                         0x00000004UL
+#define TUN_SOCKET__CREATE                        0x00000008UL
+#define TUN_SOCKET__GETATTR                       0x00000010UL
+#define TUN_SOCKET__SETATTR                       0x00000020UL
+#define TUN_SOCKET__LOCK                          0x00000040UL
+#define TUN_SOCKET__RELABELFROM                   0x00000080UL
+#define TUN_SOCKET__RELABELTO                     0x00000100UL
+#define TUN_SOCKET__APPEND                        0x00000200UL
+#define TUN_SOCKET__BIND                          0x00000400UL
+#define TUN_SOCKET__CONNECT                       0x00000800UL
+#define TUN_SOCKET__LISTEN                        0x00001000UL
+#define TUN_SOCKET__ACCEPT                        0x00002000UL
+#define TUN_SOCKET__GETOPT                        0x00004000UL
+#define TUN_SOCKET__SETOPT                        0x00008000UL
+#define TUN_SOCKET__SHUTDOWN                      0x00010000UL
+#define TUN_SOCKET__RECVFROM                      0x00020000UL
+#define TUN_SOCKET__SENDTO                        0x00040000UL
+#define TUN_SOCKET__RECV_MSG                      0x00080000UL
+#define TUN_SOCKET__SEND_MSG                      0x00100000UL
+#define TUN_SOCKET__NAME_BIND                     0x00200000UL
 #define PROCESS__FORK                             0x00000001UL
 #define PROCESS__TRANSITION                       0x00000002UL
 #define PROCESS__SIGCHLD                          0x00000004UL
diff --git a/security/selinux/include/class_to_string.h b/security/selinux/include/class_to_string.h
index 21ec786..7ab9299 100644
--- a/security/selinux/include/class_to_string.h
+++ b/security/selinux/include/class_to_string.h
@@ -77,3 +77,4 @@
     S_(NULL)
     S_(NULL)
     S_("kernel_service")
+    S_("tun_socket")
diff --git a/security/selinux/include/flask.h b/security/selinux/include/flask.h
index 882f27d..f248500 100644
--- a/security/selinux/include/flask.h
+++ b/security/selinux/include/flask.h
@@ -53,6 +53,7 @@
 #define SECCLASS_PEER                                    68
 #define SECCLASS_CAPABILITY2                             69
 #define SECCLASS_KERNEL_SERVICE                          74
+#define SECCLASS_TUN_SOCKET                              75
 
 /*
  * Security identifier indices for initial entities


^ permalink raw reply related

* [RFC PATCH v2 1/2] lsm: Add hooks to the TUN driver
From: Paul Moore @ 2009-08-10 17:28 UTC (permalink / raw)
  To: linux-security-module, netdev, selinux
In-Reply-To: <20090810172238.7946.34247.stgit@flek.lan>

The TUN driver lacks any LSM hooks which makes it difficult for LSM modules,
such as SELinux, to enforce access controls on network traffic generated by
TUN users; this is particularly problematic for virtualization apps such as
QEMU and KVM.  This patch adds three new LSM hooks designed to control the
creation and attachment of TUN devices, the hooks are:

 * security_tun_dev_create()
   Provides access control for the creation of new TUN devices

 * security_tun_dev_post_create()
   Provides the ability to create the necessary socket LSM state for newly
   created TUN devices

 * security_tun_dev_attach()
   Provides access control for attaching to existing, persistent TUN devices
   and the ability to update the TUN device's socket LSM state as necessary
---

 drivers/net/tun.c        |   22 +++++++++++++++-------
 include/linux/security.h |   31 +++++++++++++++++++++++++++++++
 security/capability.c    |   19 +++++++++++++++++++
 security/security.c      |   18 ++++++++++++++++++
 4 files changed, 83 insertions(+), 7 deletions(-)

diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 027f7ab..e6667ce 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -130,17 +130,10 @@ static inline struct tun_sock *tun_sk(struct sock *sk)
 static int tun_attach(struct tun_struct *tun, struct file *file)
 {
 	struct tun_file *tfile = file->private_data;
-	const struct cred *cred = current_cred();
 	int err;
 
 	ASSERT_RTNL();
 
-	/* Check permissions */
-	if (((tun->owner != -1 && cred->euid != tun->owner) ||
-	     (tun->group != -1 && !in_egroup_p(tun->group))) &&
-		!capable(CAP_NET_ADMIN))
-		return -EPERM;
-
 	netif_tx_lock_bh(tun->dev);
 
 	err = -EINVAL;
@@ -926,6 +919,8 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
 
 	dev = __dev_get_by_name(net, ifr->ifr_name);
 	if (dev) {
+		const struct cred *cred = current_cred();
+
 		if (ifr->ifr_flags & IFF_TUN_EXCL)
 			return -EBUSY;
 		if ((ifr->ifr_flags & IFF_TUN) && dev->netdev_ops == &tun_netdev_ops)
@@ -935,6 +930,14 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
 		else
 			return -EINVAL;
 
+		if (((tun->owner != -1 && cred->euid != tun->owner) ||
+		     (tun->group != -1 && !in_egroup_p(tun->group))) &&
+		    !capable(CAP_NET_ADMIN))
+			return -EPERM;
+		err = security_tun_dev_attach(tun->sk);
+		if (err < 0)
+			return err;
+
 		err = tun_attach(tun, file);
 		if (err < 0)
 			return err;
@@ -947,6 +950,9 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
 
 		if (!capable(CAP_NET_ADMIN))
 			return -EPERM;
+		err = security_tun_dev_create();
+		if (err < 0)
+			return err;
 
 		/* Set dev type */
 		if (ifr->ifr_flags & IFF_TUN) {
@@ -989,6 +995,8 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
 		tun->sk = sk;
 		container_of(sk, struct tun_sock, sk)->tun = tun;
 
+		security_tun_dev_post_create(sk);
+
 		tun_net_init(dev);
 
 		if (strchr(dev->name, '%')) {
diff --git a/include/linux/security.h b/include/linux/security.h
index 5eff459..d8efc35 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -974,6 +974,17 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  *	Sets the connection's peersid to the secmark on skb.
  * @req_classify_flow:
  *	Sets the flow's sid to the openreq sid.
+ * @tun_dev_create:
+ *	Check permissions prior to creating a new TUN device.
+ * @tun_dev_post_create:
+ *	This hook allows a module to update or allocate a per-socket security
+ *	structure.
+ *	@sk contains the newly created sock structure.
+ * @tun_dev_attach:
+ *	Check permissions prior to attaching to a persistent TUN device.  This
+ *	hook can also be used by the module to update any security state
+ *	associated with the TUN device's sock structure.
+ *	@sk contains the existing sock structure.
  *
  * Security hooks for XFRM operations.
  *
@@ -1572,6 +1583,9 @@ struct security_operations {
 	void (*inet_csk_clone) (struct sock *newsk, const struct request_sock *req);
 	void (*inet_conn_established) (struct sock *sk, struct sk_buff *skb);
 	void (*req_classify_flow) (const struct request_sock *req, struct flowi *fl);
+	int (*tun_dev_create)(void);
+	void (*tun_dev_post_create)(struct sock *sk);
+	int (*tun_dev_attach)(struct sock *sk);
 #endif	/* CONFIG_SECURITY_NETWORK */
 
 #ifdef CONFIG_SECURITY_NETWORK_XFRM
@@ -2557,6 +2571,9 @@ void security_inet_csk_clone(struct sock *newsk,
 			const struct request_sock *req);
 void security_inet_conn_established(struct sock *sk,
 			struct sk_buff *skb);
+int security_tun_dev_create(void);
+void security_tun_dev_post_create(struct sock *sk);
+int security_tun_dev_attach(struct sock *sk);
 
 #else	/* CONFIG_SECURITY_NETWORK */
 static inline int security_unix_stream_connect(struct socket *sock,
@@ -2707,6 +2724,20 @@ static inline void security_inet_conn_established(struct sock *sk,
 			struct sk_buff *skb)
 {
 }
+
+static inline int security_tun_dev_create(void)
+{
+	return 0;
+}
+
+static inline void security_tun_dev_post_create(struct sock *sk)
+{
+}
+
+static inline int security_tun_dev_attach(struct sock *sk)
+{
+	return 0;
+}
 #endif	/* CONFIG_SECURITY_NETWORK */
 
 #ifdef CONFIG_SECURITY_NETWORK_XFRM
diff --git a/security/capability.c b/security/capability.c
index 21b6cea..a10a44a 100644
--- a/security/capability.c
+++ b/security/capability.c
@@ -710,10 +710,26 @@ static void cap_inet_conn_established(struct sock *sk, struct sk_buff *skb)
 {
 }
 
+
+
 static void cap_req_classify_flow(const struct request_sock *req,
 				  struct flowi *fl)
 {
 }
+
+static int cap_tun_dev_create(void)
+{
+	return 0;
+}
+
+static void cap_tun_dev_post_create(struct sock *sk)
+{
+}
+
+static int cap_tun_dev_attach(struct sock *sk)
+{
+	return 0;
+}
 #endif	/* CONFIG_SECURITY_NETWORK */
 
 #ifdef CONFIG_SECURITY_NETWORK_XFRM
@@ -1029,6 +1045,9 @@ void security_fixup_ops(struct security_operations *ops)
 	set_to_cap_if_null(ops, inet_csk_clone);
 	set_to_cap_if_null(ops, inet_conn_established);
 	set_to_cap_if_null(ops, req_classify_flow);
+	set_to_cap_if_null(ops, tun_dev_create);
+	set_to_cap_if_null(ops, tun_dev_post_create);
+	set_to_cap_if_null(ops, tun_dev_attach);
 #endif	/* CONFIG_SECURITY_NETWORK */
 #ifdef CONFIG_SECURITY_NETWORK_XFRM
 	set_to_cap_if_null(ops, xfrm_policy_alloc_security);
diff --git a/security/security.c b/security/security.c
index dc7674f..dc6953c 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1112,6 +1112,24 @@ void security_inet_conn_established(struct sock *sk,
 	security_ops->inet_conn_established(sk, skb);
 }
 
+int security_tun_dev_create(void)
+{
+	return security_ops->tun_dev_create();
+}
+EXPORT_SYMBOL(security_tun_dev_create);
+
+void security_tun_dev_post_create(struct sock *sk)
+{
+	return security_ops->tun_dev_post_create(sk);
+}
+EXPORT_SYMBOL(security_tun_dev_post_create);
+
+int security_tun_dev_attach(struct sock *sk)
+{
+	return security_ops->tun_dev_attach(sk);
+}
+EXPORT_SYMBOL(security_tun_dev_attach);
+
 #endif	/* CONFIG_SECURITY_NETWORK */
 
 #ifdef CONFIG_SECURITY_NETWORK_XFRM


^ permalink raw reply related

* Re: [PATCH 1/2] mac802154: add a software MAC 802.15.4 implementation
From: Paul E. McKenney @ 2009-08-10 17:28 UTC (permalink / raw)
  To: Dmitry Eremin-Solenikov
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-zigbee-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
	linux-wireless-u79uwXL29TY76Z2rM5mHXA, Sergey Lapin
In-Reply-To: <1249913800-10176-2-git-send-email-dbaryshkov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

On Mon, Aug 10, 2009 at 06:16:39PM +0400, Dmitry Eremin-Solenikov wrote:
> Some of available devices are just dump radios implementing IEEE 802.15.4
> PHY layer. This commit adds a common library that acts like an intermediate
> layer between our socket family and drivers for those dumb devices.
> 
> Currently this is data-only part (no commands, no beacons). Control
> interfaces will follow up shortly.
> 
> Note this implementaion is neither certified, nor feature complete!

One question below, otherwise looks plausible.  (I am not entirely sure
which lists are which...)

							Thanx, Paul

> Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> Signed-off-by: Sergey Lapin <slapin-9cOl001CZnBAfugRpC6u6w@public.gmane.org>
> ---
>  MAINTAINERS               |    1 +
>  include/linux/mac802154.h |   35 ++
>  include/net/mac802154.h   |  106 ++++++
>  net/Kconfig               |    1 +
>  net/Makefile              |    1 +
>  net/mac802154/Kconfig     |   17 +
>  net/mac802154/Makefile    |    4 +
>  net/mac802154/dev.c       |  924 +++++++++++++++++++++++++++++++++++++++++++++
>  net/mac802154/mac802154.h |   85 +++++
>  net/mac802154/mac_cmd.c   |   99 +++++
>  net/mac802154/mdev.c      |  295 +++++++++++++++
>  net/mac802154/mib.h       |   32 ++
>  net/mac802154/rx.c        |   98 +++++
>  13 files changed, 1698 insertions(+), 0 deletions(-)
>  create mode 100644 include/linux/mac802154.h
>  create mode 100644 include/net/mac802154.h
>  create mode 100644 net/mac802154/Kconfig
>  create mode 100644 net/mac802154/Makefile
>  create mode 100644 net/mac802154/dev.c
>  create mode 100644 net/mac802154/mac802154.h
>  create mode 100644 net/mac802154/mac_cmd.c
>  create mode 100644 net/mac802154/mdev.c
>  create mode 100644 net/mac802154/mib.h
>  create mode 100644 net/mac802154/rx.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index d6befb2..5bdb64e 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -2529,6 +2529,7 @@ W:	http://apps.sourceforge.net/trac/linux-zigbee
>  T:	git git://git.kernel.org/pub/scm/linux/kernel/git/lowpan/lowpan.git
>  S:	Maintained
>  F:	net/ieee802154/
> +F:	net/mac802154/
>  F:	drivers/ieee802154/
> 
>  INTEGRITY MEASUREMENT ARCHITECTURE (IMA)
> diff --git a/include/linux/mac802154.h b/include/linux/mac802154.h
> new file mode 100644
> index 0000000..e95e38d
> --- /dev/null
> +++ b/include/linux/mac802154.h
> @@ -0,0 +1,35 @@
> +/*
> + * Copyright (C) 2007, 2008, 2009 Siemens AG
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write to the Free Software Foundation, Inc.,
> + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
> + *
> + */
> +
> +#ifndef LINUX_MAC802154_H
> +#define LINUX_MAC802154_H
> +
> +enum {
> +	IFLA_WPAN_UNSPEC,
> +	IFLA_WPAN_CHANNEL,
> +	IFLA_WPAN_PAN_ID,
> +	IFLA_WPAN_SHORT_ADDR,
> +	IFLA_WPAN_COORD_SHORT_ADDR,
> +	IFLA_WPAN_COORD_EXT_ADDR,
> +	__IFLA_WPAN_MAX,
> +};
> +
> +#define IFLA_WPAN_MAX	(__IFLA_WPAN_MAX - 1)
> +
> +#endif
> +
> diff --git a/include/net/mac802154.h b/include/net/mac802154.h
> new file mode 100644
> index 0000000..db76799
> --- /dev/null
> +++ b/include/net/mac802154.h
> @@ -0,0 +1,106 @@
> +/*
> + * IEEE802.15.4-2003 specification
> + *
> + * Copyright (C) 2007, 2008 Siemens AG
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write to the Free Software Foundation, Inc.,
> + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
> + *
> + * Written by:
> + */
> +#ifndef NET_MAC802154_H
> +#define NET_MAC802154_H
> +
> +struct ieee802154_dev {
> +	int	extra_tx_headroom; /* headroom to reserve for tx skb */
> +	void	*priv;		/* driver-specific data */
> +	u32	channel_mask;
> +	u8	current_channel;
> +	u32	flags; /* Flags for device to set */
> +	struct device *parent;
> +};
> +
> +/* Checksum is in hardware and is omitted from packet */
> +/**
> + * enum ieee802154_hw_flags - hardware flags
> + *
> + * These flags are used to indicate hardware capabilities to
> + * the stack. Generally, flags here should have their meaning
> + * done in a way that the simplest hardware doesn't need setting
> + * any particular flags. There are some exceptions to this rule,
> + * however, so you are advised to review these flags carefully.
> + *
> + * @IEEE802154_HW_OMIT_CKSUM:
> + *	Indicates that receiver omits FCS and transmitter will add
> + *	FCS on it's own.
> + *
> + * @IEEE802154_HW_AACK:
> + * 	Indicates that receiver will autorespond with ACK frames.
> + */
> +enum ieee802154_hw_flags {
> +	IEEE802154_HW_OMIT_CKSUM			= 1 << 0,
> +	IEEE802154_HW_AACK				= 1 << 1,
> +};
> +
> +struct sk_buff;
> +
> +/**
> + * struct ieee802154_ops - callbacks from mac802154 to the driver
> + *
> + * This structure contains various callbacks that the driver may
> + * handle or, in some cases, must handle, for example to transmit
> + * a frame.
> + *
> + * @start: Handler that 802.15.4 module calls for device initialisation.
> + * 	This function is called before the first interface is attached.
> + *
> + * @stop: Handler that 802.15.4 module calls for device cleanup
> + * 	This function is called after the last interface is removed.
> + *
> + * @tx: Handler that 802.15.4 module calls for each transmitted frame.
> + *      skb cntains the buffer starting from the IEEE 802.15.4 header.
> + *      The low-level driver should send the frame based on available
> + *      configuration.
> + *      This function should return zero or negative errno.
> + *
> + * @ed: Handler that 802.15.4 module calls for Energy Detection.
> + *      This function should place the value for detected energy
> + *      (usually device-dependant) in the level pointer and return
> + *      either zero or negative errno.
> + *
> + * @set_channel: Set radio for listening on specific channel.
> + *      Set the device for listening on specified channel.
> + *      Returns either zero, or negative errno.
> + */
> +struct ieee802154_ops {
> +	struct module	*owner;
> +	int		(*start)(struct ieee802154_dev *dev);
> +	void		(*stop)(struct ieee802154_dev *dev);
> +	int		(*xmit)(struct ieee802154_dev *dev,
> +						struct sk_buff *skb);
> +	int		(*ed)(struct ieee802154_dev *dev, u8 *level);
> +	int		(*set_channel)(struct ieee802154_dev *dev,
> +						int channel);
> +};
> +
> +struct ieee802154_dev *ieee802154_alloc_device(size_t priv_size,
> +						struct ieee802154_ops *ops);
> +int ieee802154_register_device(struct ieee802154_dev *dev);
> +void ieee802154_unregister_device(struct ieee802154_dev *dev);
> +void ieee802154_free_device(struct ieee802154_dev *dev);
> +
> +void ieee802154_rx(struct ieee802154_dev *dev, struct sk_buff *skb, u8 lqi);
> +void ieee802154_rx_irqsafe(struct ieee802154_dev *dev, struct sk_buff *skb,
> +		u8 lqi);
> +#endif
> +
> diff --git a/net/Kconfig b/net/Kconfig
> index 7051b97..b42d325 100644
> --- a/net/Kconfig
> +++ b/net/Kconfig
> @@ -180,6 +180,7 @@ source "net/econet/Kconfig"
>  source "net/wanrouter/Kconfig"
>  source "net/phonet/Kconfig"
>  source "net/ieee802154/Kconfig"
> +source "net/mac802154/Kconfig"
>  source "net/sched/Kconfig"
>  source "net/dcb/Kconfig"
> 
> diff --git a/net/Makefile b/net/Makefile
> index ba324ae..81115f6 100644
> --- a/net/Makefile
> +++ b/net/Makefile
> @@ -61,6 +61,7 @@ ifneq ($(CONFIG_DCB),)
>  obj-y				+= dcb/
>  endif
>  obj-y				+= ieee802154/
> +obj-y				+= mac802154/
> 
>  ifeq ($(CONFIG_NET),y)
>  obj-$(CONFIG_SYSCTL)		+= sysctl_net.o
> diff --git a/net/mac802154/Kconfig b/net/mac802154/Kconfig
> new file mode 100644
> index 0000000..2dd7f99
> --- /dev/null
> +++ b/net/mac802154/Kconfig
> @@ -0,0 +1,17 @@
> +config MAC802154
> +	tristate "Generic IEEE 802.15.4 Soft Networking Stack (mac802154)"
> +	depends on IEEE802154 && EXPERIMENTAL
> +	select CRC_CCITT
> +	---help---
> +	  This option enables the hardware independent IEEE 802.15.4
> +	  networking stack for SoftMAC devices (the ones implementing
> +	  only PHY level of IEEE 802.15.4 standard).
> +
> +	  Note: this implementation is neither certified, nor feature
> +	  complete! We do not garantee that it is compatible w/ other
> +	  implementations, etc.
> +
> +	  If you plan to use HardMAC IEEE 802.15.4 devices, you can
> +          say N here. Alternatievly you can say M to compile it as
> +	  module.
> +
> diff --git a/net/mac802154/Makefile b/net/mac802154/Makefile
> new file mode 100644
> index 0000000..07b7821
> --- /dev/null
> +++ b/net/mac802154/Makefile
> @@ -0,0 +1,4 @@
> +obj-$(CONFIG_MAC802154) +=	mac802154.o
> +mac802154-objs		:= rx.o mdev.o dev.o mac_cmd.o
> +
> +EXTRA_CFLAGS += -Wall -DDEBUG
> diff --git a/net/mac802154/dev.c b/net/mac802154/dev.c
> new file mode 100644
> index 0000000..b40f6fd
> --- /dev/null
> +++ b/net/mac802154/dev.c
> @@ -0,0 +1,924 @@
> +/*
> + * Copyright 2007, 2008, 2009 Siemens AG
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write to the Free Software Foundation, Inc.,
> + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
> + *
> + * Written by:
> + * Sergey Lapin <slapin-9cOl001CZnBAfugRpC6u6w@public.gmane.org>
> + * Maxim Gorbachyov <maxim.gorbachev-kv7WeFo6aLtBDgjK7y7TUQ@public.gmane.org>
> + */
> +
> +#include <linux/net.h>
> +#include <linux/capability.h>
> +#include <linux/module.h>
> +#include <linux/if_arp.h>
> +#include <linux/rculist.h>
> +#include <linux/random.h>
> +#include <linux/crc-ccitt.h>
> +#include <linux/mac802154.h>
> +#include <net/rtnetlink.h>
> +
> +#include <net/af_ieee802154.h>
> +#include <net/mac802154.h>
> +#include <net/ieee802154_netdev.h>
> +#include <net/ieee802154.h>
> +
> +#include "mac802154.h"
> +#include "mib.h"
> +
> +static int ieee802154_net_xmit(struct sk_buff *skb, struct net_device *dev)
> +{
> +	struct ieee802154_sub_if_data *priv;
> +	priv = netdev_priv(dev);
> +
> +	if (!(priv->hw->hw.flags & IEEE802154_HW_OMIT_CKSUM)) {
> +		u16 crc = crc_ccitt(0, skb->data, skb->len);
> +		u8 *data = skb_put(skb, 2);
> +		data[0] = crc & 0xff;
> +		data[1] = crc >> 8;
> +	}
> +
> +	read_lock(&priv->mib_lock);
> +	phy_cb(skb)->chan = priv->chan;
> +	read_unlock(&priv->mib_lock);
> +
> +	skb->iif = dev->ifindex;
> +	skb->dev = priv->hw->netdev;
> +	dev->stats.tx_packets++;
> +	dev->stats.tx_bytes += skb->len;
> +
> +	dev->trans_start = jiffies;
> +	dev_queue_xmit(skb);
> +
> +	return 0;
> +}
> +
> +static int ieee802154_slave_open(struct net_device *dev)
> +{
> +	struct ieee802154_sub_if_data *priv = netdev_priv(dev);
> +	int res = 0;
> +
> +	if (priv->hw->open_count++ == 0) {
> +		res = dev_open(priv->hw->netdev);
> +		WARN_ON(res);
> +		if (res)
> +			goto err;
> +	}
> +
> +	netif_start_queue(dev);
> +	return 0;
> +err:
> +	priv->hw->open_count--;
> +
> +	return res;
> +}
> +
> +static int ieee802154_slave_close(struct net_device *dev)
> +{
> +	struct ieee802154_sub_if_data *priv = netdev_priv(dev);
> +
> +	netif_stop_queue(dev);
> +
> +	if ((--priv->hw->open_count) == 0) {
> +		if (netif_running(priv->hw->netdev))
> +			dev_close(priv->hw->netdev);
> +	}
> +
> +	return 0;
> +}
> +
> +
> +static int ieee802154_slave_ioctl(struct net_device *dev, struct ifreq *ifr,
> +		int cmd)
> +{
> +	struct ieee802154_sub_if_data *priv = netdev_priv(dev);
> +	struct sockaddr_ieee802154 *sa =
> +		(struct sockaddr_ieee802154 *)&ifr->ifr_addr;
> +	int err = -ENOIOCTLCMD;
> +
> +	read_lock(&priv->mib_lock);
> +
> +	switch (cmd) {
> +	case SIOCGIFADDR:
> +		if (priv->pan_id == IEEE802154_PANID_BROADCAST ||
> +		    priv->short_addr == IEEE802154_ADDR_BROADCAST) {
> +			err = -EADDRNOTAVAIL;
> +			break;
> +		}
> +
> +		sa->family = AF_IEEE802154;
> +		sa->addr.addr_type = IEEE802154_ADDR_SHORT;
> +		sa->addr.pan_id = priv->pan_id;
> +		sa->addr.short_addr = priv->short_addr;
> +
> +		err = 0;
> +		break;
> +	case SIOCSIFADDR:
> +		dev_warn(&dev->dev,
> +			"Using DEBUGing ioctl SIOCSIFADDR isn't recommened!\n");
> +		if (sa->family != AF_IEEE802154 ||
> +		    sa->addr.addr_type != IEEE802154_ADDR_SHORT ||
> +		    sa->addr.pan_id == IEEE802154_PANID_BROADCAST ||
> +		    sa->addr.short_addr == IEEE802154_ADDR_BROADCAST ||
> +		    sa->addr.short_addr == IEEE802154_ADDR_UNDEF) {
> +			err = -EINVAL;
> +			break;
> +		}
> +
> +		priv->pan_id = sa->addr.pan_id;
> +		priv->short_addr = sa->addr.short_addr;
> +		err = 0;
> +		break;
> +	}
> +	read_unlock(&priv->mib_lock);
> +	return err;
> +}
> +
> +static int ieee802154_slave_mac_addr(struct net_device *dev, void *p)
> +{
> +	struct sockaddr *addr = p;
> +
> +	if (netif_running(dev))
> +		return -EBUSY;
> +	/* FIXME: validate addr */
> +	memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
> +	return 0;
> +}
> +
> +static void ieee802154_haddr_copy_swap(u8 *dest, const u8 *src)
> +{
> +	int i;
> +	for (i = 0; i < IEEE802154_ADDR_LEN; i++)
> +		dest[IEEE802154_ADDR_LEN - i - 1] = src[i];
> +}
> +
> +static int ieee802154_header_create(struct sk_buff *skb,
> +			   struct net_device *dev,
> +			   unsigned short type, const void *_daddr,
> +			   const void *_saddr, unsigned len)
> +{
> +	u8 head[24] = {};
> +	int pos = 0;
> +
> +	u16 fc;
> +	const struct ieee802154_addr *saddr = _saddr;
> +	const struct ieee802154_addr *daddr = _daddr;
> +	struct ieee802154_addr dev_addr;
> +	struct ieee802154_sub_if_data *priv = netdev_priv(dev);
> +
> +	fc = mac_cb_type(skb);
> +	if (mac_cb_is_ackreq(skb))
> +		fc |= IEEE802154_FC_ACK_REQ;
> +
> +	pos = 2;
> +
> +	head[pos++] = mac_cb(skb)->seq; /* DSN/BSN */
> +
> +	if (!daddr)
> +		return -EINVAL;
> +
> +	if (!saddr) {
> +		read_lock(&priv->mib_lock);
> +		if (priv->short_addr == IEEE802154_ADDR_BROADCAST ||
> +		    priv->short_addr == IEEE802154_ADDR_UNDEF ||
> +		    priv->pan_id == IEEE802154_PANID_BROADCAST) {
> +			dev_addr.addr_type = IEEE802154_ADDR_LONG;
> +			memcpy(dev_addr.hwaddr, dev->dev_addr,
> +					IEEE802154_ADDR_LEN);
> +		} else {
> +			dev_addr.addr_type = IEEE802154_ADDR_SHORT;
> +			dev_addr.short_addr = priv->short_addr;
> +		}
> +
> +		dev_addr.pan_id = priv->pan_id;
> +		saddr = &dev_addr;
> +
> +		read_unlock(&priv->mib_lock);
> +	}
> +
> +	if (daddr->addr_type != IEEE802154_ADDR_NONE) {
> +		fc |= (daddr->addr_type << IEEE802154_FC_DAMODE_SHIFT);
> +
> +		head[pos++] = daddr->pan_id & 0xff;
> +		head[pos++] = daddr->pan_id >> 8;
> +
> +		if (daddr->addr_type == IEEE802154_ADDR_SHORT) {
> +			head[pos++] = daddr->short_addr & 0xff;
> +			head[pos++] = daddr->short_addr >> 8;
> +		} else {
> +			ieee802154_haddr_copy_swap(head + pos, daddr->hwaddr);
> +			pos += IEEE802154_ADDR_LEN;
> +		}
> +	}
> +
> +	if (saddr->addr_type != IEEE802154_ADDR_NONE) {
> +		fc |= (saddr->addr_type << IEEE802154_FC_SAMODE_SHIFT);
> +
> +		if ((saddr->pan_id == daddr->pan_id) &&
> +		    (saddr->pan_id != IEEE802154_PANID_BROADCAST))
> +			/* PANID compression/ intra PAN */
> +			fc |= IEEE802154_FC_INTRA_PAN;
> +		else {
> +			head[pos++] = saddr->pan_id & 0xff;
> +			head[pos++] = saddr->pan_id >> 8;
> +		}
> +
> +		if (saddr->addr_type == IEEE802154_ADDR_SHORT) {
> +			head[pos++] = saddr->short_addr & 0xff;
> +			head[pos++] = saddr->short_addr >> 8;
> +		} else {
> +			ieee802154_haddr_copy_swap(head + pos, saddr->hwaddr);
> +			pos += IEEE802154_ADDR_LEN;
> +		}
> +	}
> +
> +	head[0] = fc;
> +	head[1] = fc >> 8;
> +
> +	memcpy(skb_push(skb, pos), head, pos);
> +
> +	return pos;
> +}
> +
> +static int ieee802154_header_parse(const struct sk_buff *skb,
> +		unsigned char *haddr)
> +{
> +	const u8 *hdr = skb_mac_header(skb), *tail = skb_tail_pointer(skb);
> +	struct ieee802154_addr *addr = (struct ieee802154_addr *)haddr;
> +	u16 fc;
> +	int da_type;
> +
> +	if (hdr + 3 > tail)
> +		goto malformed;
> +
> +	fc = hdr[0] | (hdr[1] << 8);
> +
> +	hdr += 3;
> +
> +	da_type = IEEE802154_FC_DAMODE(fc);
> +	addr->addr_type = IEEE802154_FC_SAMODE(fc);
> +
> +	switch (da_type) {
> +	case IEEE802154_ADDR_NONE:
> +		if (fc & IEEE802154_FC_INTRA_PAN)
> +			goto malformed;
> +		break;
> +
> +	case IEEE802154_ADDR_LONG:
> +		if (hdr + 2 > tail)
> +			goto malformed;
> +		if (fc & IEEE802154_FC_INTRA_PAN) {
> +			addr->pan_id = hdr[0] | (hdr[1] << 8);
> +			hdr += 2;
> +		}
> +
> +		if (hdr + IEEE802154_ADDR_LEN > tail)
> +			goto malformed;
> +		hdr += IEEE802154_ADDR_LEN;
> +		break;
> +
> +	case IEEE802154_ADDR_SHORT:
> +		if (hdr + 2 > tail)
> +			goto malformed;
> +		if (fc & IEEE802154_FC_INTRA_PAN) {
> +			addr->pan_id = hdr[0] | (hdr[1] << 8);
> +			hdr += 2;
> +		}
> +
> +		if (hdr + 2 > tail)
> +			goto malformed;
> +		hdr += 2;
> +		break;
> +
> +	default:
> +		goto malformed;
> +
> +	}
> +
> +	switch (addr->addr_type) {
> +	case IEEE802154_ADDR_NONE:
> +		break;
> +
> +	case IEEE802154_ADDR_LONG:
> +		if (hdr + 2 > tail)
> +			goto malformed;
> +		if (!(fc & IEEE802154_FC_INTRA_PAN)) {
> +			addr->pan_id = hdr[0] | (hdr[1] << 8);
> +			hdr += 2;
> +		}
> +
> +		if (hdr + IEEE802154_ADDR_LEN > tail)
> +			goto malformed;
> +		memcpy(addr->hwaddr, hdr, IEEE802154_ADDR_LEN);
> +		hdr += IEEE802154_ADDR_LEN;
> +		break;
> +
> +	case IEEE802154_ADDR_SHORT:
> +		if (hdr + 2 > tail)
> +			goto malformed;
> +		if (!(fc & IEEE802154_FC_INTRA_PAN)) {
> +			addr->pan_id = hdr[0] | (hdr[1] << 8);
> +			hdr += 2;
> +		}
> +
> +		if (hdr + 2 > tail)
> +			goto malformed;
> +		addr->short_addr = hdr[0] | (hdr[1] << 8);
> +		hdr += 2;
> +		break;
> +
> +	default:
> +		goto malformed;
> +
> +	}
> +
> +	return sizeof(struct ieee802154_addr);
> +
> +malformed:
> +	pr_debug("malformed packet\n");
> +	return 0;
> +}
> +
> +static struct header_ops ieee802154_header_ops = {
> +	.create		= ieee802154_header_create,
> +	.parse		= ieee802154_header_parse,
> +};
> +
> +static const struct net_device_ops ieee802154_slave_ops = {
> +	.ndo_open		= ieee802154_slave_open,
> +	.ndo_stop		= ieee802154_slave_close,
> +	.ndo_start_xmit		= ieee802154_net_xmit,
> +	.ndo_do_ioctl		= ieee802154_slave_ioctl,
> +	.ndo_set_mac_address	= ieee802154_slave_mac_addr,
> +};
> +
> +static void ieee802154_netdev_setup(struct net_device *dev)
> +{
> +	dev->addr_len		= IEEE802154_ADDR_LEN;
> +	memset(dev->broadcast, 0xff, IEEE802154_ADDR_LEN);
> +	dev->features		= NETIF_F_NO_CSUM;
> +	dev->hard_header_len	= 2 + 1 + 20 + 14;
> +	dev->header_ops		= &ieee802154_header_ops;
> +	dev->needed_tailroom	= 2; /* FCS */
> +	dev->mtu		= 127;
> +	dev->tx_queue_len	= 10;
> +	dev->type		= ARPHRD_IEEE802154;
> +	dev->flags		= IFF_NOARP | IFF_BROADCAST;
> +	dev->watchdog_timeo	= 0;
> +
> +	dev->destructor		= free_netdev;
> +	dev->netdev_ops		= &ieee802154_slave_ops;
> +	dev->ml_priv		= &mac802154_mlme;
> +}
> +
> +/*
> + * This is for hw unregistration only, as it doesn't do RCU locking

So this list is different than the RCU-protected one, and readers
always hold locks when traversing it?

I am not familiar with this code, so might be missing something, but
it looks to me like the same list that RCU readers traverse.  If so,
need list_del_rcu() and synchronize_rcu().

> + */
> +void ieee802154_drop_slaves(struct ieee802154_dev *hw)
> +{
> +	struct ieee802154_priv *priv = ieee802154_to_priv(hw);
> +	struct ieee802154_sub_if_data *sdata, *next;
> +
> +	ASSERT_RTNL();
> +
> +	list_for_each_entry_safe(sdata, next, &priv->slaves, list) {
> +		mutex_lock(&sdata->hw->slaves_mtx);
> +		list_del(&sdata->list);
> +		mutex_unlock(&sdata->hw->slaves_mtx);
> +
> +		dev_put(sdata->hw->netdev);
> +
> +		unregister_netdevice(sdata->dev);
> +	}
> +}
> +
> +static int ieee802154_netdev_validate(struct nlattr *tb[],
> +		struct nlattr *data[])
> +{
> +	if (tb[IFLA_ADDRESS])
> +		if (nla_len(tb[IFLA_ADDRESS]) != IEEE802154_ADDR_LEN)
> +			return -EINVAL;
> +
> +	if (tb[IFLA_BROADCAST])
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +static int ieee802154_netdev_newlink(struct net_device *dev,
> +					   struct nlattr *tb[],
> +					   struct nlattr *data[])
> +{
> +	struct net_device *mdev;
> +	struct ieee802154_sub_if_data *priv;
> +	struct ieee802154_priv *ipriv;
> +	int err;
> +
> +	if (!tb[IFLA_LINK])
> +		return -EOPNOTSUPP;
> +
> +	mdev = __dev_get_by_index(dev_net(dev), nla_get_u32(tb[IFLA_LINK]));
> +	if (!mdev)
> +		return -ENODEV;
> +
> +	if (mdev->type != ARPHRD_IEEE802154_PHY)
> +		return -EINVAL;
> +
> +	ipriv = netdev_priv(mdev);
> +
> +	priv = netdev_priv(dev);
> +	priv->dev = dev;
> +	priv->hw = ipriv;
> +
> +	rwlock_init(&priv->mib_lock);
> +
> +	get_random_bytes(&priv->bsn, 1);
> +	get_random_bytes(&priv->dsn, 1);
> +
> +	priv->pan_id = IEEE802154_PANID_BROADCAST;
> +	priv->short_addr = IEEE802154_ADDR_BROADCAST;
> +
> +	dev_hold(ipriv->netdev);
> +
> +	dev->needed_headroom = ipriv->hw.extra_tx_headroom;
> +
> +	SET_NETDEV_DEV(dev, &ipriv->netdev->dev);
> +
> +	err = register_netdevice(dev);
> +	if (err < 0)
> +		return err;
> +
> +	mutex_lock(&ipriv->slaves_mtx);
> +	list_add_tail_rcu(&priv->list, &ipriv->slaves);
> +	mutex_unlock(&ipriv->slaves_mtx);
> +
> +	return 0;
> +}
> +
> +static void ieee802154_netdev_dellink(struct net_device *dev)
> +{
> +	struct ieee802154_sub_if_data *sdata;
> +	ASSERT_RTNL();
> +
> +	BUG_ON(dev->type != ARPHRD_IEEE802154);
> +
> +	sdata = netdev_priv(dev);
> +
> +	mutex_lock(&sdata->hw->slaves_mtx);
> +	list_del_rcu(&sdata->list);
> +	mutex_unlock(&sdata->hw->slaves_mtx);
> +
> +	dev_put(sdata->hw->netdev);
> +
> +	synchronize_rcu();
> +	unregister_netdevice(sdata->dev);
> +}
> +
> +static size_t ieee802154_netdev_get_size(const struct net_device *dev)
> +{
> +	return	nla_total_size(2) +	/* IFLA_WPAN_CHANNEL */
> +		nla_total_size(2) +	/* IFLA_WPAN_PAN_ID */
> +		nla_total_size(2) +	/* IFLA_WPAN_SHORT_ADDR */
> +		nla_total_size(2) +	/* IFLA_WPAN_COORD_SHORT_ADDR */
> +		nla_total_size(8);	/* IFLA_WPAN_COORD_EXT_ADDR */
> +}
> +
> +static int ieee802154_netdev_fill_info(struct sk_buff *skb,
> +					const struct net_device *dev)
> +{
> +	struct ieee802154_sub_if_data *priv = netdev_priv(dev);
> +
> +	read_lock(&priv->mib_lock);
> +
> +	NLA_PUT_U16(skb, IFLA_WPAN_CHANNEL, priv->chan);
> +	NLA_PUT_U16(skb, IFLA_WPAN_PAN_ID, priv->pan_id);
> +	NLA_PUT_U16(skb, IFLA_WPAN_SHORT_ADDR, priv->short_addr);
> +	/* TODO: IFLA_WPAN_COORD_SHORT_ADDR */
> +	/* TODO: IFLA_WPAN_COORD_EXT_ADDR */
> +
> +	read_unlock(&priv->mib_lock);
> +
> +	return 0;
> +
> +nla_put_failure:
> +	read_unlock(&priv->mib_lock);
> +	return -EMSGSIZE;
> +}
> +
> +static struct rtnl_link_ops wpan_link_ops __read_mostly = {
> +	.kind		= "wpan",
> +	.priv_size	= sizeof(struct ieee802154_sub_if_data),
> +	.setup		= ieee802154_netdev_setup,
> +	.validate	= ieee802154_netdev_validate,
> +	.newlink	= ieee802154_netdev_newlink,
> +	.dellink	= ieee802154_netdev_dellink,
> +	.get_size	= ieee802154_netdev_get_size,
> +	.fill_info	= ieee802154_netdev_fill_info,
> +};
> +
> +static int ieee802154_process_beacon(struct net_device *dev,
> +		struct sk_buff *skb)
> +{
> +	pr_warning("ieee802154: beacon frames are not yet supported\n");
> +	kfree_skb(skb);
> +	return NET_RX_DROP;
> +}
> +
> +static int ieee802154_process_ack(struct net_device *dev, struct sk_buff *skb)
> +{
> +	pr_debug("got ACK for SEQ=%d\n", mac_cb(skb)->seq);
> +
> +	kfree_skb(skb);
> +	return NET_RX_SUCCESS;
> +}
> +
> +static int ieee802154_process_data(struct net_device *dev, struct sk_buff *skb)
> +{
> +	return netif_rx(skb);
> +}
> +
> +static int ieee802154_subif_frame(struct ieee802154_sub_if_data *sdata,
> +		struct sk_buff *skb)
> +{
> +	pr_debug("%s Getting packet via slave interface %s\n",
> +				__func__, sdata->dev->name);
> +
> +	read_lock(&sdata->mib_lock);
> +
> +	switch (mac_cb(skb)->da.addr_type) {
> +	case IEEE802154_ADDR_NONE:
> +		if (mac_cb(skb)->sa.addr_type != IEEE802154_ADDR_NONE)
> +			/* FIXME: check if we are PAN coordinator :) */
> +			skb->pkt_type = PACKET_OTHERHOST;
> +		else
> +			/* ACK comes with both addresses empty */
> +			skb->pkt_type = PACKET_HOST;
> +		break;
> +	case IEEE802154_ADDR_LONG:
> +		if (mac_cb(skb)->da.pan_id != sdata->pan_id &&
> +		    mac_cb(skb)->da.pan_id != IEEE802154_PANID_BROADCAST)
> +			skb->pkt_type = PACKET_OTHERHOST;
> +		else if (!memcmp(mac_cb(skb)->da.hwaddr, sdata->dev->dev_addr,
> +					IEEE802154_ADDR_LEN))
> +			skb->pkt_type = PACKET_HOST;
> +		else
> +			skb->pkt_type = PACKET_OTHERHOST;
> +		break;
> +	case IEEE802154_ADDR_SHORT:
> +		if (mac_cb(skb)->da.pan_id != sdata->pan_id &&
> +		    mac_cb(skb)->da.pan_id != IEEE802154_PANID_BROADCAST)
> +			skb->pkt_type = PACKET_OTHERHOST;
> +		else if (mac_cb(skb)->da.short_addr == sdata->short_addr)
> +			skb->pkt_type = PACKET_HOST;
> +		else if (mac_cb(skb)->da.short_addr ==
> +					IEEE802154_ADDR_BROADCAST)
> +			skb->pkt_type = PACKET_BROADCAST;
> +		else
> +			skb->pkt_type = PACKET_OTHERHOST;
> +		break;
> +	}
> +
> +	read_unlock(&sdata->mib_lock);
> +
> +	skb->dev = sdata->dev;
> +
> +	if (skb->pkt_type == PACKET_HOST && mac_cb_is_ackreq(skb) &&
> +			!(sdata->hw->hw.flags & IEEE802154_HW_AACK))
> +		dev_warn(&sdata->dev->dev,
> +			"ACK requested, however AACK not supported.\n");
> +
> +	switch (mac_cb_type(skb)) {
> +	case IEEE802154_FC_TYPE_BEACON:
> +		return ieee802154_process_beacon(sdata->dev, skb);
> +	case IEEE802154_FC_TYPE_ACK:
> +		return ieee802154_process_ack(sdata->dev, skb);
> +	case IEEE802154_FC_TYPE_MAC_CMD:
> +		return ieee802154_process_cmd(sdata->dev, skb);
> +	case IEEE802154_FC_TYPE_DATA:
> +		return ieee802154_process_data(sdata->dev, skb);
> +	default:
> +		pr_warning("ieee802154: Bad frame received (type = %d)\n",
> +				mac_cb_type(skb));
> +		kfree_skb(skb);
> +		return NET_RX_DROP;
> +	}
> +}
> +
> +static u8 fetch_skb_u8(struct sk_buff *skb)
> +{
> +	u8 ret;
> +
> +	BUG_ON(skb->len < 1);
> +
> +	ret = skb->data[0];
> +	skb_pull(skb, 1);
> +
> +	return ret;
> +}
> +
> +static u16 fetch_skb_u16(struct sk_buff *skb)
> +{
> +	u16 ret;
> +
> +	BUG_ON(skb->len < 2);
> +
> +	ret = skb->data[0] + (skb->data[1] * 256);
> +	skb_pull(skb, 2);
> +	return ret;
> +}
> +
> +static void fetch_skb_u64(struct sk_buff *skb, void *data)
> +{
> +	BUG_ON(skb->len < IEEE802154_ADDR_LEN);
> +
> +	memcpy(data, skb->data, IEEE802154_ADDR_LEN);
> +	skb_pull(skb, IEEE802154_ADDR_LEN);
> +}
> +
> +#define IEEE802154_FETCH_U8(skb, var)		\
> +	do {					\
> +		if (skb->len < 1)		\
> +			goto exit_error;	\
> +		var = fetch_skb_u8(skb);	\
> +	} while (0)
> +
> +#define IEEE802154_FETCH_U16(skb, var)		\
> +	do {					\
> +		if (skb->len < 2)		\
> +			goto exit_error;	\
> +		var = fetch_skb_u16(skb);	\
> +	} while (0)
> +
> +#define IEEE802154_FETCH_U64(skb, var)			\
> +	do {						\
> +		if (skb->len < IEEE802154_ADDR_LEN)	\
> +			goto exit_error;		\
> +		fetch_skb_u64(skb, &var);		\
> +	} while (0)
> +
> +static int parse_frame_start(struct sk_buff *skb)
> +{
> +	u8 *head = skb->data;
> +	u16 fc;
> +
> +	if (skb->len < 3) {
> +		pr_debug("frame size %d bytes is too short\n", skb->len);
> +		return -EINVAL;
> +	}
> +
> +	IEEE802154_FETCH_U16(skb, fc);
> +	IEEE802154_FETCH_U8(skb, mac_cb(skb)->seq);
> +
> +	pr_debug("%s: %04x dsn%02x\n", __func__, fc, head[2]);
> +
> +	mac_cb(skb)->flags = IEEE802154_FC_TYPE(fc);
> +
> +	if (fc & IEEE802154_FC_ACK_REQ) {
> +		pr_debug("%s(): ACKNOWLEDGE required\n", __func__);
> +		mac_cb(skb)->flags |= MAC_CB_FLAG_ACKREQ;
> +	}
> +
> +	if (fc & IEEE802154_FC_SECEN)
> +		mac_cb(skb)->flags |= MAC_CB_FLAG_SECEN;
> +
> +	if (fc & IEEE802154_FC_INTRA_PAN)
> +		mac_cb(skb)->flags |= MAC_CB_FLAG_INTRAPAN;
> +
> +	/* TODO */
> +	if (mac_cb_is_secen(skb)) {
> +		pr_info("security support is not implemented\n");
> +		return -EINVAL;
> +	}
> +
> +	mac_cb(skb)->sa.addr_type = IEEE802154_FC_SAMODE(fc);
> +	if (mac_cb(skb)->sa.addr_type == IEEE802154_ADDR_NONE)
> +		pr_debug("%s(): src addr_type is NONE\n", __func__);
> +
> +	mac_cb(skb)->da.addr_type = IEEE802154_FC_DAMODE(fc);
> +	if (mac_cb(skb)->da.addr_type == IEEE802154_ADDR_NONE)
> +		pr_debug("%s(): dst addr_type is NONE\n", __func__);
> +
> +	if (IEEE802154_FC_TYPE(fc) == IEEE802154_FC_TYPE_ACK) {
> +		/* ACK can only have NONE-type addresses */
> +		if (mac_cb(skb)->sa.addr_type != IEEE802154_ADDR_NONE ||
> +		    mac_cb(skb)->da.addr_type != IEEE802154_ADDR_NONE)
> +			return -EINVAL;
> +	}
> +
> +	if (mac_cb(skb)->da.addr_type != IEEE802154_ADDR_NONE) {
> +		IEEE802154_FETCH_U16(skb, mac_cb(skb)->da.pan_id);
> +
> +		if (mac_cb_is_intrapan(skb)) { /* ! panid compress */
> +			pr_debug("%s(): src IEEE802154_FC_INTRA_PAN\n",
> +					__func__);
> +			mac_cb(skb)->sa.pan_id = mac_cb(skb)->da.pan_id;
> +			pr_debug("%s(): src PAN address %04x\n",
> +					__func__, mac_cb(skb)->sa.pan_id);
> +		}
> +
> +		pr_debug("%s(): dst PAN address %04x\n",
> +				__func__, mac_cb(skb)->da.pan_id);
> +
> +		if (mac_cb(skb)->da.addr_type == IEEE802154_ADDR_SHORT) {
> +			IEEE802154_FETCH_U16(skb, mac_cb(skb)->da.short_addr);
> +			pr_debug("%s(): dst SHORT address %04x\n",
> +					__func__, mac_cb(skb)->da.short_addr);
> +
> +		} else {
> +			IEEE802154_FETCH_U64(skb, mac_cb(skb)->da.hwaddr);
> +			pr_debug("%s(): dst hardware addr\n", __func__);
> +		}
> +	}
> +
> +	if (mac_cb(skb)->sa.addr_type != IEEE802154_ADDR_NONE) {
> +		pr_debug("%s(): got src non-NONE address\n", __func__);
> +		if (!(mac_cb_is_intrapan(skb))) { /* ! panid compress */
> +			IEEE802154_FETCH_U16(skb, mac_cb(skb)->sa.pan_id);
> +			pr_debug("%s(): src IEEE802154_FC_INTRA_PAN\n",
> +					__func__);
> +		}
> +
> +		if (mac_cb(skb)->sa.addr_type == IEEE802154_ADDR_SHORT) {
> +			IEEE802154_FETCH_U16(skb, mac_cb(skb)->sa.short_addr);
> +			pr_debug("%s(): src IEEE802154_ADDR_SHORT\n",
> +					__func__);
> +		} else {
> +			IEEE802154_FETCH_U64(skb, mac_cb(skb)->sa.hwaddr);
> +			pr_debug("%s(): src hardware addr\n", __func__);
> +		}
> +	}
> +
> +	return 0;
> +
> +exit_error:
> +	return -EINVAL;
> +}
> +
> +void ieee802154_subif_rx(struct ieee802154_dev *hw, struct sk_buff *skb)
> +{
> +	struct ieee802154_priv *priv = ieee802154_to_priv(hw);
> +	struct ieee802154_sub_if_data *sdata, *prev = NULL;
> +	int ret;
> +
> +	BUILD_BUG_ON(sizeof(struct ieee802154_mac_cb) > sizeof(skb->cb));
> +	pr_debug("%s()\n", __func__);
> +
> +	if (!(priv->hw.flags & IEEE802154_HW_OMIT_CKSUM)) {
> +		u16 crc;
> +
> +		if (skb->len < 2) {
> +			pr_debug("%s(): Got invalid frame\n", __func__);
> +			goto out;
> +		}
> +		crc = crc_ccitt(0, skb->data, skb->len);
> +		if (crc) {
> +			pr_debug("%s(): CRC mismatch\n", __func__);
> +			goto out;
> +		}
> +		skb_trim(skb, skb->len - 2); /* CRC */
> +	}
> +
> +	ret = parse_frame_start(skb); /* 3 bytes pulled after this */
> +	if (ret) {
> +		pr_debug("%s(): Got invalid frame\n", __func__);
> +		goto out;
> +	}
> +
> +	pr_debug("%s() frame %d\n", __func__, mac_cb_type(skb));
> +
> +	rcu_read_lock();
> +	list_for_each_entry_rcu(sdata, &priv->slaves, list)
> +	{
> +		if (prev) {
> +			struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
> +			if (skb2)
> +				ieee802154_subif_frame(prev, skb2);
> +		}
> +
> +		prev = sdata;
> +	}
> +
> +	if (prev) {
> +		ieee802154_subif_frame(prev, skb);
> +		skb = NULL;
> +	}
> +
> +	rcu_read_unlock();
> +
> +out:
> +	dev_kfree_skb(skb);
> +	return;
> +}
> +
> +u16 ieee802154_dev_get_pan_id(struct net_device *dev)
> +{
> +	struct ieee802154_sub_if_data *priv = netdev_priv(dev);
> +	u16 ret;
> +
> +	BUG_ON(dev->type != ARPHRD_IEEE802154);
> +
> +	read_lock(&priv->mib_lock);
> +	ret = priv->pan_id;
> +	read_unlock(&priv->mib_lock);
> +
> +	return ret;
> +}
> +
> +u16 ieee802154_dev_get_short_addr(struct net_device *dev)
> +{
> +	struct ieee802154_sub_if_data *priv = netdev_priv(dev);
> +	u16 ret;
> +
> +	BUG_ON(dev->type != ARPHRD_IEEE802154);
> +
> +	read_lock(&priv->mib_lock);
> +	ret = priv->short_addr;
> +	read_unlock(&priv->mib_lock);
> +
> +	return ret;
> +}
> +
> +void ieee802154_dev_set_pan_id(struct net_device *dev, u16 val)
> +{
> +	struct ieee802154_sub_if_data *priv = netdev_priv(dev);
> +
> +	BUG_ON(dev->type != ARPHRD_IEEE802154);
> +
> +	write_lock(&priv->mib_lock);
> +	priv->pan_id = val;
> +	write_unlock(&priv->mib_lock);
> +}
> +void ieee802154_dev_set_short_addr(struct net_device *dev, u16 val)
> +{
> +	struct ieee802154_sub_if_data *priv = netdev_priv(dev);
> +
> +	BUG_ON(dev->type != ARPHRD_IEEE802154);
> +
> +	write_lock(&priv->mib_lock);
> +	priv->short_addr = val;
> +	write_unlock(&priv->mib_lock);
> +}
> +void ieee802154_dev_set_channel(struct net_device *dev, u8 val)
> +{
> +	struct ieee802154_sub_if_data *priv = netdev_priv(dev);
> +
> +	BUG_ON(dev->type != ARPHRD_IEEE802154);
> +
> +	write_lock(&priv->mib_lock);
> +	priv->chan = val;
> +	write_unlock(&priv->mib_lock);
> +}
> +
> +u8 ieee802154_dev_get_dsn(struct net_device *dev)
> +{
> +	struct ieee802154_sub_if_data *priv = netdev_priv(dev);
> +	u16 ret;
> +
> +	BUG_ON(dev->type != ARPHRD_IEEE802154);
> +
> +	write_lock(&priv->mib_lock);
> +	ret = priv->dsn++;
> +	write_unlock(&priv->mib_lock);
> +
> +	return ret;
> +}
> +
> +u8 ieee802154_dev_get_bsn(struct net_device *dev)
> +{
> +	struct ieee802154_sub_if_data *priv = netdev_priv(dev);
> +	u16 ret;
> +
> +	BUG_ON(dev->type != ARPHRD_IEEE802154);
> +
> +	write_lock(&priv->mib_lock);
> +	ret = priv->bsn++;
> +	write_unlock(&priv->mib_lock);
> +
> +	return ret;
> +}
> +
> +static int __init ieee802154_dev_init(void)
> +{
> +	return rtnl_link_register(&wpan_link_ops);
> +}
> +module_init(ieee802154_dev_init);
> +
> +static void __exit ieee802154_dev_exit(void)
> +{
> +	rtnl_link_unregister(&wpan_link_ops);
> +}
> +module_exit(ieee802154_dev_exit);
> +
> +MODULE_ALIAS_RTNL_LINK("wpan");
> +
> diff --git a/net/mac802154/mac802154.h b/net/mac802154/mac802154.h
> new file mode 100644
> index 0000000..f4e8d29
> --- /dev/null
> +++ b/net/mac802154/mac802154.h
> @@ -0,0 +1,85 @@
> +/*
> + * Copyright (C) 2007, 2008 Siemens AG
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write to the Free Software Foundation, Inc.,
> + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
> + *
> + * Written by:
> + * Pavel Smolenskiy <pavel.smolenskiy-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> + * Maxim Gorbachyov <maxim.gorbachev-kv7WeFo6aLtBDgjK7y7TUQ@public.gmane.org>
> + * Dmitry Eremin-Solenikov <dbaryshkov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> + */
> +#ifndef MAC802154_H
> +#define MAC802154_H
> +
> +#include <linux/spinlock.h>
> +struct ieee802154_priv {
> +	struct ieee802154_dev	hw;
> +	struct ieee802154_ops	*ops;
> +
> +	struct net_device *netdev; /* mwpanX device */
> +	int open_count;
> +	/* As in mac80211 slaves list is modified:
> +	 * 1) under the RTNL
> +	 * 2) protected by slaves_mtx;
> +	 * 3) in an RCU manner
> +	 *
> +	 * So atomic readers can use any of this protection methods
> +	 */
> +	struct list_head	slaves;
> +	struct mutex		slaves_mtx;
> +	/* This one is used for scanning and other
> +	 * jobs not to be interfered with serial driver */
> +	struct workqueue_struct	*dev_workqueue;
> +};
> +
> +#define ieee802154_to_priv(_hw)	container_of(_hw, struct ieee802154_priv, hw)
> +
> +struct ieee802154_sub_if_data {
> +	struct list_head list; /* the ieee802154_priv->slaves list */
> +
> +	struct ieee802154_priv *hw;
> +	struct net_device *dev;
> +
> +	rwlock_t mib_lock;
> +
> +	u16 pan_id;
> +	u16 short_addr;
> +
> +	u8 chan;
> +
> +	/* MAC BSN field */
> +	u8 bsn;
> +	/* MAC BSN field */
> +	u8 dsn;
> +};
> +
> +void ieee802154_drop_slaves(struct ieee802154_dev *hw);
> +
> +void ieee802154_subif_rx(struct ieee802154_dev *hw, struct sk_buff *skb);
> +
> +struct ieee802154_phy_cb {
> +	u8 lqi;
> +	u8 chan;
> +};
> +
> +static inline struct ieee802154_phy_cb *phy_cb(struct sk_buff *skb)
> +{
> +	return (struct ieee802154_phy_cb *)skb->cb;
> +}
> +
> +extern struct ieee802154_mlme_ops mac802154_mlme;
> +
> +int ieee802154_process_cmd(struct net_device *dev, struct sk_buff *skb);
> +
> +#endif
> diff --git a/net/mac802154/mac_cmd.c b/net/mac802154/mac_cmd.c
> new file mode 100644
> index 0000000..8941628
> --- /dev/null
> +++ b/net/mac802154/mac_cmd.c
> @@ -0,0 +1,99 @@
> +/*
> + * MAC commands interface
> + *
> + * Copyright 2007, 2008 Siemens AG
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write to the Free Software Foundation, Inc.,
> + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
> + *
> + * Written by:
> + * Sergey Lapin <slapin-9cOl001CZnBAfugRpC6u6w@public.gmane.org>
> + * Dmitry Eremin-Solenikov <dbaryshkov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/skbuff.h>
> +#include <linux/if_arp.h>
> +#include <net/af_ieee802154.h>
> +#include <net/mac802154.h>
> +#include <net/ieee802154.h>
> +#include <net/ieee802154_netdev.h>
> +#include <net/nl802154.h>
> +
> +#include "mac802154.h"
> +#include "mib.h"
> +
> +int ieee802154_process_cmd(struct net_device *dev, struct sk_buff *skb)
> +{
> +	pr_warning("ieee802154: command frames are not yet supported\n");
> +	kfree_skb(skb);
> +	return NET_RX_DROP;
> +}
> +
> +
> +static int ieee802154_mlme_assoc_req(struct net_device *dev,
> +		struct ieee802154_addr *addr, u8 channel, u8 cap)
> +{
> +	/* We simply emulate it here */
> +	return ieee802154_nl_assoc_confirm(dev,
> +			ieee802154_dev_get_short_addr(dev),
> +			IEEE802154_SUCCESS);
> +}
> +
> +static int ieee802154_mlme_assoc_resp(struct net_device *dev,
> +		struct ieee802154_addr *addr, u16 short_addr, u8 status)
> +{
> +	return 0;
> +}
> +
> +static int ieee802154_mlme_disassoc_req(struct net_device *dev,
> +		struct ieee802154_addr *addr, u8 reason)
> +{
> +	return ieee802154_nl_disassoc_confirm(dev, IEEE802154_SUCCESS);
> +}
> +
> +static int ieee802154_mlme_start_req(struct net_device *dev,
> +				struct ieee802154_addr *addr,
> +				u8 channel,
> +				u8 bcn_ord, u8 sf_ord, u8 pan_coord, u8 blx,
> +				u8 coord_realign)
> +{
> +	/* We don't emulate beacons here at all, so START should fail */
> +	ieee802154_nl_start_confirm(dev, IEEE802154_INVALID_PARAMETER);
> +	return 0;
> +}
> +
> +static int ieee802154_mlme_scan_req(struct net_device *dev, u8 type,
> +		u32 channels,
> +		u8 duration)
> +{
> +	u8 edl[27] = {};
> +	return ieee802154_nl_scan_confirm(dev, IEEE802154_SUCCESS, type,
> +			channels,
> +			type == IEEE802154_MAC_SCAN_ED ? edl : NULL);
> +}
> +
> +
> +struct ieee802154_mlme_ops mac802154_mlme = {
> +	.assoc_req = ieee802154_mlme_assoc_req,
> +	.assoc_resp = ieee802154_mlme_assoc_resp,
> +	.disassoc_req = ieee802154_mlme_disassoc_req,
> +	.start_req = ieee802154_mlme_start_req,
> +	.scan_req = ieee802154_mlme_scan_req,
> +
> +	.get_pan_id = ieee802154_dev_get_pan_id,
> +	.get_short_addr = ieee802154_dev_get_short_addr,
> +	.get_dsn = ieee802154_dev_get_dsn,
> +	.get_bsn = ieee802154_dev_get_bsn,
> +};
> +
> diff --git a/net/mac802154/mdev.c b/net/mac802154/mdev.c
> new file mode 100644
> index 0000000..191e942
> --- /dev/null
> +++ b/net/mac802154/mdev.c
> @@ -0,0 +1,295 @@
> +/*
> + * Copyright (C) 2007, 2008 Siemens AG
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write to the Free Software Foundation, Inc.,
> + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
> + *
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/netdevice.h>
> +#include <linux/if_arp.h>
> +#include <net/route.h>
> +
> +#include <net/af_ieee802154.h>
> +#include <net/mac802154.h>
> +
> +#include "mac802154.h"
> +
> +struct xmit_work {
> +	struct sk_buff *skb;
> +	struct work_struct work;
> +	struct ieee802154_priv *priv;
> +};
> +
> +static void ieee802154_xmit_worker(struct work_struct *work)
> +{
> +	struct xmit_work *xw = container_of(work, struct xmit_work, work);
> +	int res;
> +
> +	if (xw->priv->hw.current_channel != phy_cb(xw->skb)->chan) {
> +		res = xw->priv->ops->set_channel(&xw->priv->hw,
> +				phy_cb(xw->skb)->chan);
> +		if (res) {
> +			pr_debug("set_channel failed\n");
> +			goto out;
> +		}
> +	}
> +
> +	res = xw->priv->ops->xmit(&xw->priv->hw, xw->skb);
> +
> +out:
> +	/* FIXME: result processing and/or requeue!!! */
> +	dev_kfree_skb(xw->skb);
> +
> +	kfree(xw);
> +}
> +
> +static int ieee802154_master_hard_start_xmit(struct sk_buff *skb,
> +		struct net_device *dev)
> +{
> +	struct ieee802154_priv *priv = netdev_priv(dev);
> +	struct xmit_work *work;
> +
> +	if (skb_cow_head(skb, priv->hw.extra_tx_headroom)) {
> +		dev_kfree_skb(skb);
> +		return NETDEV_TX_OK;
> +	}
> +
> +	work = kzalloc(sizeof(struct xmit_work), GFP_ATOMIC);
> +	if (!work)
> +		return NETDEV_TX_BUSY;
> +
> +	INIT_WORK(&work->work, ieee802154_xmit_worker);
> +	work->skb = skb;
> +	work->priv = priv;
> +
> +	queue_work(priv->dev_workqueue, &work->work);
> +
> +	return NETDEV_TX_OK;
> +}
> +
> +static int ieee802154_master_open(struct net_device *dev)
> +{
> +	int rc;
> +	struct ieee802154_priv *priv = netdev_priv(dev);
> +
> +	if (!priv) {
> +		pr_debug("%s:%s: unable to get master private data\n",
> +				__FILE__, __func__);
> +		return -ENODEV;
> +	}
> +
> +	if (!priv->open_count)
> +		return -EOPNOTSUPP;
> +
> +	rc = priv->ops->start(&priv->hw);
> +
> +	if (!rc)
> +		netif_tx_start_all_queues(dev);
> +
> +	return rc;
> +}
> +
> +static int ieee802154_master_close(struct net_device *dev)
> +{
> +	struct ieee802154_priv *priv = netdev_priv(dev);
> +	struct ieee802154_sub_if_data *sdata;
> +
> +	ASSERT_RTNL();
> +
> +	/* We are under RTNL, so it's fine to do this */
> +	list_for_each_entry(sdata, &priv->slaves, list)
> +		if (netif_running(sdata->dev))
> +			dev_close(sdata->dev);
> +
> +	priv->ops->stop(&priv->hw);
> +
> +	return 0;
> +}
> +
> +static ssize_t ieee802154_netdev_show(const struct device *dev,
> +		   struct device_attribute *attr, char *buf,
> +		   ssize_t (*format)(const struct net_device *, char *))
> +{
> +	struct net_device *netdev = to_net_dev(dev);
> +	ssize_t ret = -EINVAL;
> +
> +	if (netdev->reg_state <= NETREG_REGISTERED)
> +		ret = (*format)(netdev, buf);
> +
> +	return ret;
> +}
> +#define MASTER_SHOW(field, format_string)				\
> +static ssize_t format_##field(const struct net_device *dev, char *buf)	\
> +{									\
> +	struct ieee802154_priv *priv = netdev_priv(dev);		\
> +	return sprintf(buf, format_string, priv->hw.field);		\
> +}									\
> +static ssize_t show_##field(struct device *dev,				\
> +			    struct device_attribute *attr, char *buf)	\
> +{									\
> +	return ieee802154_netdev_show(dev, attr, buf, format_##field);	\
> +}									\
> +static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL)
> +
> +static const char fmt_long_hex[] = "%#lx\n";
> +static const char fmt_hex[] = "%#x\n";
> +static const char fmt_dec[] = "%d\n";
> +
> +MASTER_SHOW(current_channel, fmt_dec);
> +MASTER_SHOW(channel_mask, fmt_hex);
> +
> +static struct attribute *pmib_attrs[] = {
> +	&dev_attr_current_channel.attr,
> +	&dev_attr_channel_mask.attr,
> +	NULL
> +};
> +
> +static struct attribute_group pmib_group = {
> +	.name  = "pib",
> +	.attrs  = pmib_attrs,
> +};
> +
> +static const struct net_device_ops ieee802154_master_ops = {
> +	.ndo_open		= ieee802154_master_open,
> +	.ndo_stop		= ieee802154_master_close,
> +	.ndo_start_xmit		= ieee802154_master_hard_start_xmit,
> +};
> +
> +static void ieee802154_netdev_setup_master(struct net_device *dev)
> +{
> +	dev->addr_len		= 0;
> +	dev->features		= NETIF_F_NO_CSUM;
> +	dev->hard_header_len	= 0;
> +	dev->mtu		= 127;
> +	dev->tx_queue_len	= 0;
> +	dev->type		= ARPHRD_IEEE802154_PHY;
> +	dev->flags		= IFF_NOARP | IFF_BROADCAST;
> +	dev->watchdog_timeo	= 0;
> +
> +	dev->netdev_ops = &ieee802154_master_ops;
> +}
> +
> +struct ieee802154_dev *ieee802154_alloc_device(size_t priv_size,
> +		struct ieee802154_ops *ops)
> +{
> +	struct net_device *dev;
> +	struct ieee802154_priv *priv;
> +
> +	dev = alloc_netdev(ALIGN(sizeof(*priv), NETDEV_ALIGN) + priv_size,
> +			"mwpan%d", ieee802154_netdev_setup_master);
> +	if (!dev) {
> +		printk(KERN_ERR
> +			"Failure to initialize master IEEE802154 device\n");
> +		return NULL;
> +	}
> +	priv = netdev_priv(dev);
> +	priv->netdev = dev;
> +	priv->hw.priv = (char *)priv + ALIGN(sizeof(*priv), NETDEV_ALIGN);
> +
> +	BUG_ON(!dev);
> +	BUG_ON(!ops);
> +	BUG_ON(!ops->xmit);
> +	BUG_ON(!ops->ed);
> +	BUG_ON(!ops->start);
> +	BUG_ON(!ops->stop);
> +
> +	if (!try_module_get(ops->owner)) {
> +		free_netdev(dev);
> +		return NULL;
> +	}
> +
> +	priv->ops = ops;
> +
> +	INIT_LIST_HEAD(&priv->slaves);
> +	mutex_init(&priv->slaves_mtx);
> +
> +	return &priv->hw;
> +}
> +EXPORT_SYMBOL(ieee802154_alloc_device);
> +
> +void ieee802154_free_device(struct ieee802154_dev *hw)
> +{
> +	struct ieee802154_priv *priv = ieee802154_to_priv(hw);
> +
> +	BUG_ON(!list_empty(&priv->slaves));
> +	BUG_ON(!priv->netdev);
> +
> +	module_put(priv->ops->owner);
> +
> +	free_netdev(priv->netdev);
> +}
> +EXPORT_SYMBOL(ieee802154_free_device);
> +
> +int ieee802154_register_device(struct ieee802154_dev *dev)
> +{
> +	struct ieee802154_priv *priv = ieee802154_to_priv(dev);
> +	struct net_device *ndev = priv->netdev;
> +
> +	int rc;
> +
> +	rtnl_lock();
> +	if (strchr(ndev->name, '%')) {
> +		rc = dev_alloc_name(ndev, ndev->name);
> +		if (rc < 0)
> +			goto out_unlock;
> +	}
> +
> +	priv->dev_workqueue =
> +		create_singlethread_workqueue(ndev->name);
> +	if (!priv->dev_workqueue) {
> +		rc = -ENOMEM;
> +		goto out_unlock;
> +	}
> +
> +	ndev->needed_headroom = priv->hw.extra_tx_headroom;
> +	SET_NETDEV_DEV(ndev, priv->hw.parent);
> +
> +	ndev->sysfs_groups[1] = &pmib_group;
> +
> +	rc = register_netdevice(ndev);
> +	if (rc < 0)
> +		goto out_wq;
> +
> +	rtnl_unlock();
> +
> +	return 0;
> +
> +out_wq:
> +	destroy_workqueue(priv->dev_workqueue);
> +out_unlock:
> +	rtnl_unlock();
> +	return rc;
> +}
> +EXPORT_SYMBOL(ieee802154_register_device);
> +
> +void ieee802154_unregister_device(struct ieee802154_dev *dev)
> +{
> +	struct ieee802154_priv *priv = ieee802154_to_priv(dev);
> +
> +	flush_workqueue(priv->dev_workqueue);
> +	destroy_workqueue(priv->dev_workqueue);
> +
> +	rtnl_lock();
> +
> +	ieee802154_drop_slaves(dev);
> +	unregister_netdevice(priv->netdev);
> +
> +	rtnl_unlock();
> +}
> +EXPORT_SYMBOL(ieee802154_unregister_device);
> +
> +MODULE_DESCRIPTION("IEEE 802.15.4 implementation");
> +MODULE_LICENSE("GPL v2");
> +
> diff --git a/net/mac802154/mib.h b/net/mac802154/mib.h
> new file mode 100644
> index 0000000..57bf03f
> --- /dev/null
> +++ b/net/mac802154/mib.h
> @@ -0,0 +1,32 @@
> +/*
> + * Copyright 2008 Siemens AG
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write to the Free Software Foundation, Inc.,
> + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
> + *
> + */
> +
> +#ifndef MIB802154_H
> +#define MIB802154_H
> +
> +/* FIXME: should be dropped in favour of generic MIB API */
> +u8 ieee802154_dev_get_dsn(struct net_device *dev);
> +u8 ieee802154_dev_get_bsn(struct net_device *dev);
> +u16 ieee802154_dev_get_pan_id(struct net_device *dev);
> +u16 ieee802154_dev_get_short_addr(struct net_device *dev);
> +void ieee802154_dev_set_pan_id(struct net_device *dev, u16 val);
> +void ieee802154_dev_set_short_addr(struct net_device *dev, u16 val);
> +void ieee802154_dev_set_channel(struct net_device *dev, u8 chan);
> +
> +
> +#endif
> diff --git a/net/mac802154/rx.c b/net/mac802154/rx.c
> new file mode 100644
> index 0000000..81a269e
> --- /dev/null
> +++ b/net/mac802154/rx.c
> @@ -0,0 +1,98 @@
> +/*
> + * Copyright (C) 2007, 2008, 2009 Siemens AG
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write to the Free Software Foundation, Inc.,
> + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
> + *
> + * Written by:
> + * Pavel Smolenskiy <pavel.smolenskiy-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> + * Maxim Gorbachyov <maxim.gorbachev-kv7WeFo6aLtBDgjK7y7TUQ@public.gmane.org>
> + * Dmitry Eremin-Solenikov <dbaryshkov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/workqueue.h>
> +#include <linux/netdevice.h>
> +
> +#include <net/mac802154.h>
> +
> +#include "mac802154.h"
> +
> +static void __ieee802154_rx_prepare(struct ieee802154_dev *dev,
> +		struct sk_buff *skb, u8 lqi)
> +{
> +	struct ieee802154_priv *priv = ieee802154_to_priv(dev);
> +
> +	BUG_ON(!skb);
> +
> +	phy_cb(skb)->lqi = lqi;
> +
> +	skb->dev = priv->netdev;
> +
> +	skb->iif = skb->dev->ifindex;
> +
> +	skb->protocol = htons(ETH_P_IEEE802154);
> +
> +	skb_reset_mac_header(skb);
> +}
> +
> +void ieee802154_rx(struct ieee802154_dev *dev, struct sk_buff *skb, u8 lqi)
> +{
> +	struct sk_buff *skb2;
> +
> +	__ieee802154_rx_prepare(dev, skb, lqi);
> +
> +	skb2 = skb_clone(skb, GFP_KERNEL);
> +	netif_rx(skb2);
> +
> +	ieee802154_subif_rx(dev, skb);
> +}
> +EXPORT_SYMBOL(ieee802154_rx);
> +
> +struct rx_work {
> +	struct sk_buff *skb;
> +	struct work_struct work;
> +	struct ieee802154_dev *dev;
> +};
> +
> +static void ieee802154_rx_worker(struct work_struct *work)
> +{
> +	struct rx_work *rw = container_of(work, struct rx_work, work);
> +	struct sk_buff *skb = rw->skb;
> +
> +	struct sk_buff *skb2 = skb_clone(skb, GFP_KERNEL);
> +	netif_rx(skb2);
> +
> +	ieee802154_subif_rx(rw->dev, skb);
> +	kfree(rw);
> +}
> +
> +void ieee802154_rx_irqsafe(struct ieee802154_dev *dev,
> +		struct sk_buff *skb, u8 lqi)
> +{
> +	struct ieee802154_priv *priv = ieee802154_to_priv(dev);
> +	struct rx_work *work = kzalloc(sizeof(struct rx_work), GFP_ATOMIC);
> +
> +	if (!work)
> +		return;
> +
> +	__ieee802154_rx_prepare(dev, skb, lqi);
> +
> +	INIT_WORK(&work->work, ieee802154_rx_worker);
> +	work->skb = skb;
> +	work->dev = dev;
> +
> +	queue_work(priv->dev_workqueue, &work->work);
> +}
> +EXPORT_SYMBOL(ieee802154_rx_irqsafe);
> -- 
> 1.6.3.3
> 
> --
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [RFC PATCH v2 0/2] New LSM hooks for the TUN driver
From: Paul Moore @ 2009-08-10 17:28 UTC (permalink / raw)
  To: linux-security-module, netdev, selinux

Second draft of the new TUN driver LSM hooks, thanks to everyone who sent
comments, especially Serge Hallyn and Eric Paris.  Nothing substantial has
changed in this version with respect to the hooks, just lots of little
changes to cleanup the patches and ensure they build on various different
configurations.  I still need to do more testing but barring any issues this
should be the code that I sign-off for inclusion.

In the meantime I've populated the lblnet-2.6_next tree with these patches so
feel free to pull from there if you want to play with the code.

 * git://git.infradead.org/users/pcmoore/lblnet-2.6_next

---

Paul Moore (2):
      selinux: Support for the new TUN LSM hooks
      lsm: Add hooks to the TUN driver


 drivers/net/tun.c                          |   22 +++++++---
 include/linux/security.h                   |   31 ++++++++++++++
 security/capability.c                      |   19 +++++++++
 security/security.c                        |   18 ++++++++
 security/selinux/hooks.c                   |   60 +++++++++++++++++++++++++++-
 security/selinux/include/av_inherit.h      |    1 
 security/selinux/include/av_permissions.h  |   22 ++++++++++
 security/selinux/include/class_to_string.h |    1 
 security/selinux/include/flask.h           |    1 
 9 files changed, 166 insertions(+), 9 deletions(-)


^ permalink raw reply

* Re: [PATCH] AF_PACKET: Fix PACKET_MMAP on 64-bit kernel/32-bit user systems
From: Patrick McHardy @ 2009-08-10 17:17 UTC (permalink / raw)
  To: cndougla; +Cc: netdev
In-Reply-To: <20090810130359.p2hixk2eoow8kwg8@imap.linux.ibm.com>

cndougla@linux.vnet.ibm.com wrote:
> PACKET_MMAP sets up a ring buffer at a memory location so that kernel
> and user space can read from it without extra overhead. However, the
> data is written with a header, struct tpacket_hdr, which begins with an
> unsigned long field, tp_status. This means in a split 64-bit/32-bit
> environment the kernel- and user-space reads from the mmap'd region will
> be different, since in kernel-space the field is 64-bit while in
> user-space the field is 32-bit.
> 
> The following patch makes the size of the structure constant by changing
> the tp_status field to 32-bits. It does not appear to need 64-bits,
> since only 4-bits are actually used throughout the AF_PACKET code.
> 
> Signed-off-by: Chase Douglas <cndougla@linux.vnet.ibm.com>
> --- a/include/linux/if_packet.h   2009-08-10 16:53:11.000000000 +0000
> +++ b/include/linux/if_packet.h   2009-08-10 16:53:28.000000000 +0000
> @@ -65,7 +65,7 @@ struct tpacket_auxdata
> 
>  struct tpacket_hdr
>  {
> -       unsigned long   tp_status;
> +       unsigned int    tp_status;

This changes the layout on 64 bit and thus breaks compatibility.
The tpacket2_hdr doesn't suffer from this problem.

^ permalink raw reply

* [PATCH] AF_PACKET: Fix PACKET_MMAP on 64-bit kernel/32-bit user systems
From: cndougla @ 2009-08-10 17:03 UTC (permalink / raw)
  To: netdev

PACKET_MMAP sets up a ring buffer at a memory location so that kernel  
and user space can read from it without extra overhead. However, the  
data is written with a header, struct tpacket_hdr, which begins with  
an unsigned long field, tp_status. This means in a split 64-bit/32-bit  
environment the kernel- and user-space reads from the mmap'd region  
will be different, since in kernel-space the field is 64-bit while in  
user-space the field is 32-bit.

The following patch makes the size of the structure constant by  
changing the tp_status field to 32-bits. It does not appear to need  
64-bits, since only 4-bits are actually used throughout the AF_PACKET  
code.

Signed-off-by: Chase Douglas <cndougla@linux.vnet.ibm.com>
--- a/include/linux/if_packet.h   2009-08-10 16:53:11.000000000 +0000
+++ b/include/linux/if_packet.h   2009-08-10 16:53:28.000000000 +0000
@@ -65,7 +65,7 @@ struct tpacket_auxdata

  struct tpacket_hdr
  {
-       unsigned long   tp_status;
+       unsigned int    tp_status;
  #define TP_STATUS_KERNEL       0
  #define TP_STATUS_USER         1
  #define TP_STATUS_COPY         2


^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox