Initial import of the muennet Linux kernel module
authorReto Buerki <reet@codelabs.ch>
Thu, 9 Apr 2015 08:44:28 +0000 (10:44 +0200)
committerReto Buerki <reet@codelabs.ch>
Thu, 11 Jun 2015 07:11:37 +0000 (09:11 +0200)
The muennet Linux kernel module implements a virtual network interface
driver which sends and receives data via shared memory channels provided
by the Muen Separation Kernel [1]. See the README.md file for more
information.

[1] - http://muen.sk

COPYING [new file with mode: 0644]
Makefile [new file with mode: 0644]
README.md [new file with mode: 0644]
debug.c [new file with mode: 0644]
internal.h [new file with mode: 0644]
net.c [new file with mode: 0644]
reader.c [new file with mode: 0644]
writer.c [new file with mode: 0644]

diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..d159169
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,339 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    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.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..0d16506
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,17 @@
+obj-m        := muennet.o
+muennet-objs := debug.o net.o reader.o writer.o
+
+KERNEL_SOURCE ?= /lib/modules/$(shell uname -r)/build
+
+PWD := $(shell pwd)
+
+all: compile-module
+compile-module:
+       $(MAKE) -C $(KERNEL_SOURCE) M=$(PWD) modules
+
+install:
+       install -d $(DESTDIR)/lib/modules/extra
+       install muennet.ko $(DESTDIR)/lib/modules/extra
+
+clean:
+       $(MAKE) -C $(KERNEL_SOURCE) M=$(PWD) clean
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..f70cc0d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,58 @@
+# Muennet
+
+## Introduction
+
+The *muennet* Linux kernel module implements a virtual network interface driver
+which sends and receives data via shared memory channels provided by the [Muen
+Separation Kernel][1].
+
+From a user-space perspective, a network interface created using the *muennet*
+kernel module behaves just like an ordinary network interface.
+
+## Protocols
+
+Currently, the following protocols are supported:
+
+- Raw mode (`SOCK_RAW`) for custom data
+- IPv4/IPv6 traffic
+
+## Usage
+
+The following command inserts the *muennet* module into the kernel. The module
+parameters configure the module to create a virtual *net0* network interface
+using *channel_in* for data input and *channel_out* for data output. The reader
+and writer protocols are arbitrary values which must match between
+communicating endpoints.
+
+       $ modprobe muennet    \
+               name=net0         \
+               reader_protocol=2 \
+               writer_protocol=2 \
+               in=channel_in     \
+               out=channel_out   \
+               flags=net_hdr
+
+The *net_hdr* flag is required to send IP traffic over the network interface.
+It can be omitted if the communicating endpoints apply a custom protocol over
+raw data.
+
+Configure the newly created network interface as usual:
+
+    $ ifconfig net0 192.168.1.1
+    $ ip route add 192.168.1.0/24 dev net0
+
+The module parameters accept a list of values in order to create multiple
+network interfaces with associated settings:
+
+       $ modprobe muennet                  \
+               name=net0,net1                  \
+               reader_protocol=12,2            \
+               writer_protocol=8,2             \
+               in=testchannel_1,testchannel_3  \
+               out=testchannel_2,testchannel_4 \
+               flags=net_hdr,net_hdr
+
+Use the `modinfo` command to see all supported module parameters with their
+explanation.
+
+[1]: http://muen.sk
diff --git a/debug.c b/debug.c
new file mode 100644 (file)
index 0000000..e28717f
--- /dev/null
+++ b/debug.c
@@ -0,0 +1,328 @@
+/*
+ * Muen virtual network driver.
+ *
+ * Copyright (C) 2015  secunet Security Networks AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/debugfs.h>
+
+#include "internal.h"
+
+/**
+ * @file debug.c
+ * @brief debugfs support
+ *
+ * The functions in this file provide developer information to the network
+ * interfaces implemented in the module. For each interface a directory is
+ * created beneath the top-level "muennet" directory. Each directory contains
+ * an 'info' file which provides data about the private information stored in
+ * the network interface.
+ */
+
+/**
+ * @addtogroup debug
+ */
+/*@{*/
+
+/**
+ * @brief Toplevel directory.
+ *
+ * This variable stores the toplevel directory name "muennet".
+ */
+static struct dentry *debugfs_topdir;
+
+/**
+ * This function is called to initialize the debugfs. It creates the "muennet"
+ * directory.
+ */
+void debug_initialize(void)
+{
+       debugfs_topdir = debugfs_create_dir("muennet", NULL);
+}
+
+/**
+ * This function is called to remove the "muennet" directory when the module is
+ * unloaded.
+ */
+void debug_shutdown(void)
+{
+       debugfs_remove(debugfs_topdir);
+       debugfs_topdir = NULL;
+}
+
+/**
+ * @brief Simple memory buffer.
+ *
+ * This is a memory buffer used for multiple buffer_append() operations.
+ */
+struct buffer {
+       size_t offset; /**< current write offset */
+       size_t length; /**< length of buffer     */
+       char *buffer;  /**< the data buffer      */
+};
+
+/**
+ * @brief Initialize buffer.
+ *
+ * This function initializes the buffer to use the given data array for
+ * storage. The current #buffer.offset is initialized to 0.
+ *
+ * @param buffer the buffer
+ * @param data   pointer to memory buffer
+ * @param length the maximum number of elements that can be stored in data
+ */
+static void buffer_init(struct buffer *buffer, char *data, size_t length)
+{
+       buffer->offset = 0;
+       buffer->length = length;
+       buffer->buffer = data;
+}
+
+/**
+ * @brief Append to buffer.
+ *
+ * This function uses a sprintf-like operation to append to the data buffer.
+ *
+ * @param buffer a previously initialized buffer
+ * @param format the format string to use
+ * @param ...    additional parameters
+ * @return 0 if data could be appended
+ * @return -1 if buffer would overflow
+ */
+static int buffer_append(struct buffer *buffer, const char *format, ...)
+{
+       va_list args;
+       int len;
+
+       if (buffer->offset < buffer->length) {
+               va_start(args, format);
+               len = vsnprintf(buffer->buffer + buffer->offset,
+                               buffer->length - buffer->offset,
+                               format, args);
+               va_end(args);
+               if (buffer->offset + len < buffer->length) {
+                       buffer->offset += len;
+                       return 0;
+               }
+       }
+       buffer->offset = buffer->length;
+       return -1;
+}
+
+/**
+ * @brief Fetches output from buffer.
+ *
+ * This function should be used to retrieve the data of a buffer. It ensures
+ * that the final 0 byte is written. The returned pointer can also be used to
+ * free the buffer if the pointer to #buffer_init was dynamically allocated.
+ *
+ * @param buffer the data buffer
+ * @return the pointer that was used in buffer_initialize
+ */
+static char *buffer_retrieve(struct buffer *buffer)
+{
+       buffer->buffer[buffer->length - 1] = 0;
+       return buffer->buffer;
+}
+
+/**
+ * @brief Data gatherer for the "info" file.
+ *
+ * This function is called for opening the info file. It allocates a page for
+ * temporary storage and formats all information into this page. The buffer is
+ * the store in the file's private data to be used in #debug_info_read and
+ * freed in #debug_info_release.
+ *
+ * @param inode the inode of the debugfs entry
+ * @param file  the file pointer
+ * @return 0 on success
+ * @return -ENOMEM if the temporary memory could not be allocated
+ */
+static int debug_info_open(struct inode *inode, struct file *file)
+{
+       struct dev_info *dev_info;
+       struct buffer buffer;
+       size_t i;
+       char *page = (char *)__get_free_page(GFP_KERNEL);
+
+       if (!page)
+               return -ENOMEM;
+
+       buffer_init(&buffer, page, PAGE_SIZE);
+
+       dev_info = inode->i_private;
+       buffer_append(&buffer, "in/out: %s\n", dev_info->bus_info);
+       buffer_append(&buffer, "mtu: %d\n", dev_info->mtu);
+       buffer_append(&buffer, "flags: ");
+       if (dev_info->flags == 0) {
+               buffer_append(&buffer, "(none)\n");
+       } else {
+               for (i = 0; flag_names[i].name != NULL; i++) {
+                       if (dev_info->flags & flag_names[i].value) {
+                               buffer_append(&buffer, "%s ",
+                                             flag_names[i].name);
+                       }
+               }
+               buffer_append(&buffer, "\n");
+       }
+
+       buffer_append(&buffer, "poll: every %u µs\n", dev_info->poll_interval);
+
+       if (dev_info->writer_element_size) {
+               buffer_append(&buffer, "writer is enabled\n");
+               buffer_append(&buffer, "writer.element_size: %zu\n",
+                             dev_info->writer_element_size);
+       } else {
+               buffer_append(&buffer, "writer is disabled\n");
+       }
+
+       if (dev_info->reader_element_size) {
+               buffer_append(&buffer, "reader is enabled\n");
+               buffer_append(&buffer, "reader.element_size: %zu\n",
+                             dev_info->reader_element_size);
+               buffer_append(&buffer, "reader.active: %d\n",
+                             atomic_read(&dev_info->should_read));
+       } else {
+               buffer_append(&buffer, "reader is disabled\n");
+       }
+
+       buffer_append(&buffer, "stats.rx_packets: %lu\n",
+                     dev_info->stats.rx_packets);
+       buffer_append(&buffer, "stats.rx_bytes: %lu\n",
+                     dev_info->stats.rx_bytes);
+       buffer_append(&buffer, "stats.rx_errors: %lu\n",
+                     dev_info->stats.rx_errors);
+       buffer_append(&buffer, "stats.rx_over_errors: %lu\n",
+                     dev_info->stats.rx_over_errors);
+       buffer_append(&buffer, "stats.rx_frame_errors: %lu\n",
+                     dev_info->stats.rx_frame_errors);
+       buffer_append(&buffer, "stats.tx_packets: %lu\n",
+                     dev_info->stats.tx_packets);
+       buffer_append(&buffer, "stats.tx_bytes: %lu\n",
+                     dev_info->stats.tx_bytes);
+
+       file->private_data = buffer_retrieve(&buffer);
+       return 0;
+}
+
+/**
+ * @brief Read function for the "info" file.
+ *
+ * This function simply retrieves the information prepared by #debug_info_open
+ * and sends it to the user.
+ *
+ * @param file   the file pointer
+ * @param buf    the user buffer where to place the information
+ * @param nbytes the number of bytes to be stored
+ * @param ppos   pointer to the user offset pointer.
+ * @return the number of bytes written
+ */
+static ssize_t debug_info_read(struct file *file, char __user *buf,
+                              size_t nbytes, loff_t *ppos)
+{
+       char *page = file->private_data;
+
+       return simple_read_from_buffer(buf, nbytes, ppos, page, strlen(page));
+}
+
+/**
+ * @brief Called for close operation on "info" file.
+ *
+ * This function retrieves the allocated memory stored in the file's private
+ * data and releases it.
+ *
+ * @param inode the inode of the debugfs
+ * @param file  the file pointer
+ * @return 0 to indicate success
+ */
+
+static int debug_info_release(struct inode *inode, struct file *file)
+{
+       char *page = file->private_data;
+
+       file->private_data = NULL;
+       free_page((unsigned long)page);
+       return 0;
+}
+
+/**
+ * @brief The "info" file operations.
+ *
+ * This provides the operation for file opening, reading and closing. It also
+ * provides a llseek function to avoid the big kernel lock in the default
+ * implementation.
+ */
+static const struct file_operations debug_info_fops = {
+       .owner   = THIS_MODULE,
+       .open    = debug_info_open,
+       .read    = debug_info_read,
+       .release = debug_info_release,
+       .llseek  = generic_file_llseek,
+};
+
+/**
+ * This function registers the "info" file for the network interface specified
+ * by dev_info.
+ *
+ * @param dev_info private information of the networking interface
+ * @return 0 on success
+ * @return -ENOMEM if memory allocation failed
+ */
+int debug_create_device(struct dev_info *dev_info)
+{
+       int ret = 0;
+
+       if (!debugfs_topdir)
+               goto err;
+
+       dev_info->debugfs_dir = debugfs_create_dir(dev_info->dev->name,
+                                                  debugfs_topdir);
+       if (!dev_info->debugfs_dir) {
+               ret = -ENOMEM;
+               goto err;
+       }
+       dev_info->debugfs_info = debugfs_create_file
+               ("info", 0400, dev_info->debugfs_dir, dev_info,
+                &debug_info_fops);
+       if (!dev_info->debugfs_info) {
+               ret = -ENOMEM;
+               goto err_create_file;
+       }
+
+       return ret;
+
+err_create_file:
+       debugfs_remove(dev_info->debugfs_dir);
+err:
+       return ret;
+}
+
+/**
+ * This function removes the previously created "info" files of a network
+ * interface.
+ *
+ * @param dev_info private information of the networking interface.
+ */
+
+void debug_remove_device(struct dev_info *dev_info)
+{
+       debugfs_remove(dev_info->debugfs_info);
+       dev_info->debugfs_info = NULL;
+       debugfs_remove(dev_info->debugfs_dir);
+       dev_info->debugfs_dir = NULL;
+}
+
+/*@}*/
diff --git a/internal.h b/internal.h
new file mode 100644 (file)
index 0000000..8bec5e9
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * Muen virtual network driver.
+ *
+ * Copyright (C) 2015  secunet Security Networks AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef INTERNAL_H_
+#define INTERNAL_H_
+
+#include <linux/netdevice.h>
+#include <muen/sinfo.h>
+#include <muen/reader.h>
+
+/**
+ * @file internal.h
+ * @brief Interface between the modules
+ * @defgroup common Commonly used functions
+ *
+ * This module contains mainly the data structure that is shared between the
+ * other components. The transfer mechanism (muchannel) uses a fixed packet
+ * size. This size is directly used for raw mode transfers. To support IP
+ * traffic, the net_hdr protocol must be used to output a specific header
+ * before the actual data. In this mode the networking interface determines the
+ * length from the transferred IP headers.
+ */
+/*@{*/
+
+/**
+ * @brief Networking device private information.
+ *
+ * This is the private information associated with each created networking
+ * device.
+ */
+struct dev_info {
+       struct list_head list;             /**< list head for chaining into #dev_list                       */
+       struct net_device *dev;            /**< reference to networking interface                           */
+       struct net_device_stats stats;     /**< contains receive and transmit information                   */
+       char *bus_info;                    /**< text representation for input and output association        */
+       int mtu;                           /**< MTU for this interface                                      */
+       unsigned long flags;               /**< flags given on the command line                             */
+       spinlock_t writer_lock;            /**< lock for accessing the writer part                          */
+       struct muchannel *channel_out;     /**< output channel for write operations                         */
+       size_t writer_element_size;        /**< size of elements for write operations                       */
+       size_t writer_region_size;         /**< total size of writer buffer                                 */
+       u64 writer_protocol;               /**< protocol id for writer                                      */
+       u64 reader_protocol;               /**< protocol id for reader                                      */
+       unsigned int poll_interval;        /**< sleep period for the reader thread                          */
+       struct muchannel_reader reader;    /**< channel reader                                              */
+       size_t reader_element_size;        /**< size of elements for read operations                        */
+       struct muchannel *channel_in;      /**< input channel for read operations                           */
+       struct task_struct *reader_thread; /**< reference to reader thread                                  */
+       atomic_t should_read;              /**< tells the reader thread if reading is active                */
+       wait_queue_head_t read_wq;         /**< wait queue for signalling reader thread about state changes */
+       struct dentry *debugfs_dir;        /**< directory entry for debugfs directory                       */
+       struct dentry *debugfs_info;       /**< directory entry for information file                        */
+};
+
+/**
+ * @brief Flag bit values
+ *
+ * These are the values for the bits that can be stored in #dev_info.flags.
+ */
+enum muennet_flags {
+       MUENNET_HDR = 1, /**< add network information needed for IPv4/IPv6 */
+};
+
+/**
+ * @brief Name-value mappings for the flags.
+ *
+ * This data type is used to map symbolic names of the flags to the bit values.
+ */
+struct flag_name {
+       const char *name;
+       unsigned long value;
+};
+
+/**
+ * @brief Mapping of the currently implemented flags.
+ *
+ * This variable stores the currently known flags with their symbolic names
+ * that can be given when loading the module. These symbolic names are also
+ * used when writing information in the debugfs.
+ */
+static const struct flag_name flag_names[] = {
+       { .name = "net_hdr",
+         .value = MUENNET_HDR },
+       {},
+};
+
+/**
+ * @brief Header format for network header flag.
+ *
+ * This header encodes additional information in the data transferred via
+ * the shared channel to avoid having to guess some of the packet properties.
+ */
+struct net_hdr {
+       u32 mark;    /**< netfilter mark                          */
+       u16 length;  /**< length of the payload                   */
+       u8 protocol; /**< the IP protocol embedded in the payload */
+} __packed;
+/*@}*/
+
+/**
+ * @defgroup debug Debugging and tracing tools
+ *
+ * This module can be split into two parts. The functions are used to setup the
+ * debugfs entries for the module and for each individual networking device.
+ */
+/*@{*/
+
+/**
+ * @brief Initialize debugfs for driver
+ */
+void debug_initialize(void);
+
+/**
+ * @brief Removes debugfs for driver
+ */
+void debug_shutdown(void);
+
+/**
+ * @brief Initialize directory for single device
+ */
+int debug_create_device(struct dev_info *dev_info);
+
+/**
+ * @brief Remove directory for single device
+ */
+void debug_remove_device(struct dev_info *dev_info);
+/*@}*/
+
+/**
+ * @defgroup reader Network reader
+ */
+/*@{*/
+
+/**
+ * @brief Initializes the network device reader part
+ */
+int initialize_reader(struct dev_info *dev_info,
+                     const struct muen_channel_info * const region);
+
+/**
+ * @brief Shuts down the network device reader part
+ */
+void cleanup_reader(struct dev_info *dev_info);
+/*@}*/
+
+/**
+ * @defgroup writer Network writer
+ */
+/*@{*/
+
+/**
+ * @brief Activate specified network writer
+ */
+void writer_up(struct dev_info *dev_info);
+
+/**
+ * @brief Deactivate specified network writer
+ */
+void writer_down(struct dev_info *dev_info);
+
+/**
+ * @brief Initializes the network device writer part
+ */
+int initialize_writer(struct dev_info *dev_info,
+                     const struct muen_channel_info * const region);
+
+/**
+ * @brief Shuts down the network device writer part
+ */
+void cleanup_writer(struct dev_info *dev_info);
+
+/**
+ * @brief Transmit given sbk using the specified network device
+ */
+int muennet_xmit(struct sk_buff *skb, struct net_device *dev);
+/*@}*/
+
+#endif
diff --git a/net.c b/net.c
new file mode 100644 (file)
index 0000000..d0b9b5f
--- /dev/null
+++ b/net.c
@@ -0,0 +1,629 @@
+/*
+ * Muen virtual network driver.
+ *
+ * Copyright (C) 2015  secunet Security Networks AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define DRV_NAME       "muennet"
+#define DRV_VERSION    "0.2"
+#define DRV_DESCRIPTION        "Muen SK virtual network driver"
+
+#include <linux/module.h>
+#include <linux/if_arp.h>
+
+#include "internal.h"
+
+/**
+ * @file net.c
+ * @brief Networking interface
+ *
+ * @defgroup net Networking interface
+ *
+ * This module defines the module initialization and finalization code together
+ * with the whole implementation of network operations.
+ *
+ * The data is transferred via the #muennet_xmit function and received by the
+ * #muennet_reader_thread function run as kernel thread.
+ */
+/*@{*/
+
+/**
+ * @brief List of networking drivers.
+ *
+ * This list is used to chain all initialized network interfaces together. The
+ * list head points to the #dev_info::list element of the network interfaces
+ * private data.
+ */
+static LIST_HEAD(dev_list);
+
+/**
+ * @brief Setup networking interface link.
+ *
+ * This function starts the network queue for the given interface and informs
+ * the reader thread (if there is one) that data may be received.
+ *
+ * @param dev networking device to operate on
+ * @return always 0 to indicate success
+ */
+static int muennet_open(struct net_device *dev)
+{
+       struct dev_info *dev_info = netdev_priv(dev);
+
+       writer_up(dev_info);
+       netif_start_queue(dev);
+
+       if (dev_info->reader_thread) {
+               /* inform the thread of the changed state */
+               atomic_set(&dev_info->should_read, 1);
+               wake_up(&dev_info->read_wq);
+       }
+       return 0;
+}
+
+/**
+ * @brief Teardown networking interface link.
+ *
+ * This function stops the network queue for this driver and informs the reader
+ * thread (if there is one) that no more packets are to be retrieved.
+ *
+ * @param dev the networking interface
+ * @return always returns 0
+ */
+static int muennet_close(struct net_device *dev)
+{
+       struct dev_info *dev_info = netdev_priv(dev);
+
+       netif_stop_queue(dev);
+       writer_down(dev_info);
+       if (dev_info->reader_thread)
+               atomic_set(&dev_info->should_read, 0);
+
+       return 0;
+}
+
+/**
+ * @brief Retrieve statistics about the networking interface.
+ *
+ * These statistics are shown in ifconfig or with "ip -s link". The following
+ * values are used:
+ * - rx_errors      : receive errors
+ * - rx_over_errors : reader was overrun by writer
+ * - rx_frame_error : invalid packet received from writer
+ * - rx_packets     : packets successfully received
+ * - rx_bytes       : sum of packet sizes successfully received
+ * - tx_dropped     : packet dropped because no writing memory region associated
+ * - tx_packets     : packets sent
+ * - tx_bytes       : sum of packet sizes successfully received
+ *
+ * @param dev network device
+ * @return reference to net_device_stats structure stored within private
+ * information #dev_info.
+ */
+static struct net_device_stats *muennet_stats(struct net_device *dev)
+{
+       struct dev_info *dev_info = netdev_priv(dev);
+
+       return &dev_info->stats;
+}
+
+/**
+ * @brief Retrieve ethtool settings.
+ *
+ * This function provides some dummy content to make ethtool happy.
+ *
+ * @param dev network interface
+ * @param cmd structure to be filled
+ * @return always returns 0
+ */
+static int muennet_get_settings(struct net_device *dev,
+                               struct ethtool_cmd *cmd)
+{
+       cmd->supported = 0;
+       cmd->advertising = 0;
+       cmd->speed = SPEED_10;
+       cmd->duplex = DUPLEX_FULL;
+       cmd->port = PORT_TP;
+       cmd->phy_address = 0;
+       cmd->transceiver = XCVR_INTERNAL;
+       cmd->autoneg = AUTONEG_DISABLE;
+       cmd->maxtxpkt = 0;
+       cmd->maxrxpkt = 0;
+       return 0;
+}
+
+/**
+ * @brief Interface around strlcpy.
+ *
+ * This function is used to safely copy data into an array.
+ *
+ * @param array the (real) array to operate on
+ * @param value the string data to copy into the array
+ */
+#define copy(array, value) strlcpy(array, value, sizeof(array))
+
+/**
+ * @brief Retrieve driver information.
+ *
+ * This function is called to retrieve the information shown by "ethtool -i".
+ *
+ * @param dev  network interface
+ * @param info the structure to fill with the information
+ */
+static void muennet_get_drvinfo(struct net_device *dev,
+                               struct ethtool_drvinfo *info)
+{
+       struct dev_info *dev_info = netdev_priv(dev);
+
+       copy(info->driver, DRV_NAME);
+       copy(info->version, DRV_VERSION);
+       copy(info->fw_version, "N/A");
+       copy(info->bus_info, dev_info->bus_info);
+}
+
+/**
+ * @brief Retrieve link information.
+ *
+ * This function tells the caller if the network interface has a link. A link
+ * is there if either a reader or a writer memory region is associated with
+ * this networking interface.
+ *
+ * @param dev network interface
+ * @return true if link is there
+ */
+static u32 muennet_get_link(struct net_device *dev)
+{
+       struct dev_info *dev_info = netdev_priv(dev);
+
+       return (dev_info->writer_element_size != 0 ||
+               dev_info->reader_element_size != 0);
+}
+
+/**
+ * @brief ethtool operations
+ *
+ * This structure defines the ethtool operations available on this networking
+ * interface.
+ */
+static struct ethtool_ops muennet_ethtool_ops = {
+       .get_settings = muennet_get_settings,
+       .get_drvinfo  = muennet_get_drvinfo,
+       .get_link     = muennet_get_link,
+};
+
+/**
+ * @brief Networking interface destructor.
+ *
+ * This function is used to shutdown the reader and writer part and to free the
+ * allocated memory. It is called during unregister_netdev.
+ *
+ * @param dev the network interface
+ */
+
+static void muennet_free(struct net_device *dev)
+{
+       struct dev_info *dev_info = netdev_priv(dev);
+
+       cleanup_reader(dev_info);
+       cleanup_writer(dev_info);
+       kfree(dev_info->bus_info);
+       free_netdev(dev);
+}
+
+static const struct net_device_ops muennet_device_ops = {
+       .ndo_open       = muennet_open,
+       .ndo_stop       = muennet_close,
+       .ndo_start_xmit = muennet_xmit,
+       .ndo_get_stats  = muennet_stats,
+};
+
+/**
+ * @brief Setup the network interface.
+ *
+ * This function is called during alloc_netdev to initialize the network
+ * operations for this interface.
+ *
+ * @param dev the network interface
+ */
+static void muennet_setup(struct net_device *dev)
+{
+       dev->netdev_ops  = &muennet_device_ops;
+       dev->ethtool_ops = &muennet_ethtool_ops;
+       dev->destructor  = muennet_free;
+}
+
+/**
+ * @brief Add a new networking interface.
+ *
+ * This function creates a new networking interface in the kernel based on the
+ * provided information.
+ *
+ * @param device_name name of the networking interface (%d is supported)
+ * @param input       name of the memory region to read from (use NULL or empty
+ *                    string to indicate no reading)
+ * @param output      name of the memory region to write from (use NULL or
+ *                    empty string to indicate no writing)
+ * @param mtu         the maximum transmission unit of this interface
+ * @param flags       flags that control the operation of the networking
+ *                    interface (see #muennet_flags for possible values)
+ * @param poll        poll interval to use for this network interface
+ *
+ * @return 0 on success
+ * @return -ENOMEM on memory allocation failure
+ * @return errors returned by #initialize_reader or #initialize_writer
+ */
+static int add_device(const char *device_name,
+                     const char *input,
+                     const char *output,
+                     int mtu,
+                     u64 writer_protocol,
+                     u64 reader_protocol,
+                     unsigned long flags,
+                     unsigned int poll)
+{
+       int ret = -ENOMEM;
+       struct net_device *dev;
+       struct dev_info *dev_info;
+       size_t bus_info_len = 2; /* place for separator and finishing \0 */
+       struct muen_channel_info reader_channel, writer_channel;
+
+       if (input)
+               bus_info_len += strlen(input);
+       if (output)
+               bus_info_len += strlen(output);
+
+       dev = alloc_netdev(sizeof(struct dev_info), device_name,
+                       NET_NAME_UNKNOWN, muennet_setup);
+       if (!dev)
+               goto err;
+
+       /* do further initialization of device */
+       dev->hard_header_len = 0;
+       if (flags & MUENNET_HDR)
+               dev->hard_header_len += sizeof(struct net_hdr);
+
+       dev->addr_len = 0;
+       dev->mtu = mtu;
+
+       dev->type = ARPHRD_NONE;
+       dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
+
+       dev_info = netdev_priv(dev);
+       dev_info->dev = dev;
+
+       dev_info->bus_info = kmalloc(bus_info_len, GFP_KERNEL);
+       if (!dev_info->bus_info)
+               goto err_free_netdev;
+       dev_info->bus_info[0] = 0;
+
+       if (input)
+               strlcat(dev_info->bus_info, input, bus_info_len);
+       strlcat(dev_info->bus_info, ":", bus_info_len);
+       if (output)
+               strlcat(dev_info->bus_info, output, bus_info_len);
+
+       dev_info->poll_interval = poll;
+       dev_info->mtu = mtu;
+       dev_info->flags = flags;
+       dev_info->writer_protocol = writer_protocol;
+       dev_info->reader_protocol = reader_protocol;
+
+       /* first check all the names */
+       if (input && strlen(input) > 0) {
+               if (!muen_get_channel_info(input, &reader_channel)) {
+                       netdev_err(dev_info->dev,
+                                  "Input channel '%s' not found\n", input);
+                       goto err_free_businfo;
+               }
+               ret = initialize_reader(dev_info, &reader_channel);
+               if (ret < 0) {
+                       netdev_err(dev_info->dev,
+                                  "Unable to init reader (status: %d)\n", ret);
+                       goto err_free_businfo;
+               }
+       }
+
+       if (output && strlen(output) > 0) {
+               if (!muen_get_channel_info(output, &writer_channel)) {
+                       netdev_err(dev_info->dev,
+                                  "Output channel '%s' not found\n", output);
+                       goto err_cleanup_reader;
+               }
+               ret = initialize_writer(dev_info, &writer_channel);
+               if (ret < 0) {
+                       netdev_err(dev_info->dev,
+                                  "Unable to init writer (status: %d)\n", ret);
+                       goto err_cleanup_reader;
+               }
+       }
+
+       ret = register_netdev(dev_info->dev);
+       if (ret < 0) {
+               netdev_err(dev_info->dev,
+                          "register_netdev failed with status %d\n", ret);
+               goto err_cleanup_writer;
+       }
+
+       list_add_tail(&dev_info->list, &dev_list);
+       debug_create_device(dev_info);
+       netdev_info(dev_info->dev, "Interface added\n");
+
+       return 0;
+
+err_cleanup_writer:
+       cleanup_writer(dev_info);
+err_cleanup_reader:
+       cleanup_reader(dev_info);
+err_free_businfo:
+       kfree(dev_info->bus_info);
+err_free_netdev:
+       free_netdev(dev);
+err:
+       return ret;
+}
+
+/**
+ * @brief Maximum number of interfaces
+ *
+ * This is the maximum number of interfaces supported by the module parameters.
+ */
+#define MAX_INTERFACES 32
+
+/**
+ * @brief Poll interval (in µs)
+ *
+ * This is the default poll interval, use "poll" module parameter to override.
+ */
+static unsigned int poll = 1;
+
+/**
+ * @brief Interface names
+ *
+ * This array is filled with the list of interfaces specified with the "name"
+ * module parameter.
+ */
+static char *name[MAX_INTERFACES];
+
+/**
+ * @brief Input memory regions
+ *
+ * This array is filled with the list of input memory regions specified with
+ * the "in" module parameter.
+ */
+static char *in[MAX_INTERFACES];
+
+/**
+ * @brief Output memory regions
+ *
+ * This array is filled with the list of output memory regions specified with
+ * the "out" module parameter.
+ */
+static char *out[MAX_INTERFACES];
+
+/**
+ * @brief Maximum transfer unit
+ *
+ * This array is filled with the list of mtu specified with the "mtu" module
+ * parameter. If no MTU is given via module parameter a default value of 1500
+ * is used.
+ */
+static char *mtu[MAX_INTERFACES];
+
+/**
+ * @brief Interface flags
+ *
+ * This array is filled with the list of interface flags specified with the
+ * "flags" module parameter. The flags are a list of names (see #flag_names for
+ * valid names) where the values for each interface are separated with "+" and
+ * the value list for all interfaces are separated with ",". If no flags are
+ * given for a interface a default value of 0 is used.
+ */
+static char *flags[MAX_INTERFACES];
+
+/**
+  * @brief Writer protocol
+  *
+  * This array is filled with the list of writer protocols specified with the
+  * writer_protocol parameter
+  */
+static char *writer_protocol[MAX_INTERFACES];
+
+/**
+  * @brief Reader protocol
+  *
+  * This array is filled with the list of reader protocols specified with the
+  * reader_protocol parameter
+  */
+static char *reader_protocol[MAX_INTERFACES];
+
+/**
+ * @brief Count of interface names
+ *
+ * This should be set to the number of interface names specified in the module
+ * parameters.
+ */
+static int name_count;
+
+module_param_array(name, charp, &name_count, 0444);
+MODULE_PARM_DESC(name, "List of interface names, separated with comma");
+module_param_array(in, charp, NULL, 0444);
+MODULE_PARM_DESC(in, "List of input memregions, separated with comma (empty values permitted)");
+module_param_array(out, charp, NULL, 0444);
+MODULE_PARM_DESC(out, "List of output memregions, separated with comma (empty values permitted)");
+module_param_array(mtu, charp, NULL, 0444);
+MODULE_PARM_DESC(mtu, "List of MTUs to use, separated with comma (default is 1500)");
+module_param_array(writer_protocol, charp, NULL, 0444);
+MODULE_PARM_DESC(writer_protocol, "List of writer protocol IDs, separated with comma");
+module_param_array(reader_protocol, charp, NULL, 0444);
+MODULE_PARM_DESC(reader_protocol, "List of reader protocol IDs, separated with comma");
+module_param_array(flags, charp, NULL, 0444);
+MODULE_PARM_DESC(flags, "List of flags separated with comma (flags for a device separated with +)");
+module_param(poll, uint, 0444);
+MODULE_PARM_DESC(poll, "Wait period in reader thread (in µs)");
+
+/**
+ * @brief Parse interface flags for one interface.
+ *
+ * This function parses the interface flag names which are separated with "+".
+ * Case is important when comparing flag names. Unknown flag names result in an
+ * error.
+ *
+ * @param names list of flag names separated with "+" *
+ * @return resulting bit value. *
+ * @return -EINVAL if a flag is unknown
+ */
+static int parse_flags(const char *names)
+{
+       int result = 0;
+       int last_value = 0;
+       const char *next_pos;
+
+       while (!last_value) {
+               size_t i;
+               int found = 0;
+
+               next_pos = strchr(names, '+');
+               if (next_pos == NULL) {
+                       next_pos = names + strlen(names);
+                       last_value = 1;
+               }
+
+               for (i = 0; flag_names[i].name != NULL; i++) {
+                       if (strncmp(flag_names[i].name, names,
+                                   next_pos - names) == 0) {
+                               result |= flag_names[i].value;
+                               found = 1;
+                       }
+               }
+
+               if (!found) {
+                       pr_err(DRV_NAME ": Invalid flag name found in '%s'\n",
+                              names);
+                       return -EINVAL;
+               }
+
+               names = next_pos + 1;
+       }
+       return result;
+}
+
+/**
+ * @brief Module cleanup routine.
+ *
+ * This function is called during module unloading. It removes all debugfs
+ * entries and networking interfaces.
+ */
+static void muennet_cleanup(void)
+{
+       struct dev_info *dev_info;
+       struct dev_info *next;
+
+       list_for_each_entry_safe(dev_info, next, &dev_list, list) {
+               list_del(&dev_info->list);
+               debug_remove_device(dev_info);
+               unregister_netdev(dev_info->dev);
+       }
+       debug_shutdown();
+}
+
+/**
+ * @brief Module initialization routine.
+ *
+ * This function parses the module parameters. For each interface specified by
+ * the "name" parameter a networking interface is created (with the memory
+ * region given by "in" and "out" parameters). If the setup of a network
+ * interface failed, all previously created network interfaces will be cleaned
+ * up and the error is returned to user space.
+ *
+ * @return 0 for successful module loading
+ * @return errors returned by #add_device
+ */
+
+static int __init muennet_init(void)
+{
+       int i;
+       int ret;
+
+       debug_initialize();
+
+       for (i = 0; i < name_count; i++) {
+               unsigned int device_mtu = 1500;
+               unsigned long flag_value = 0;
+               u64 device_writer_protocol = 0;
+               u64 device_reader_protocol = 0;
+
+               if (!name[i] || strlen(name[i]) == 0)
+                       continue;
+
+               if (mtu[i] != NULL && strlen(mtu[i]) > 0)
+                       if (kstrtouint(mtu[i], 10, &device_mtu) != 0) {
+                               pr_err(DRV_NAME ": MTU invalid\n");
+                               ret = -EINVAL;
+                               goto error;
+                       };
+
+               if (writer_protocol[i] != NULL &&
+                               strlen(writer_protocol[i]) > 0)
+                       if (kstrtoull(writer_protocol[i], 16,
+                                     &device_writer_protocol) != 0) {
+                               pr_err(DRV_NAME ": writer_protocol invalid\n");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+               if (reader_protocol[i] != NULL &&
+                               strlen(reader_protocol[i]) > 0)
+                       if (kstrtoull(reader_protocol[i], 16,
+                                     &device_reader_protocol) != 0) {
+                               pr_err(DRV_NAME ": reader_protocol invalid\n");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+               if (!device_reader_protocol) {
+                       pr_err(DRV_NAME ": reader_protocol missing\n");
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               if (flags[i] != NULL && strlen(flags[i]) > 0) {
+                       ret = parse_flags(flags[i]);
+                       if (ret < 0)
+                               goto error;
+
+                       flag_value = ret;
+               }
+
+               ret = add_device(name[i], in[i], out[i], device_mtu,
+                                device_writer_protocol, device_reader_protocol,
+                                flag_value, poll);
+               if (ret < 0)
+                       goto error;
+       }
+       return 0;
+error:
+       /* try to cleanup already created interfaces */
+       muennet_cleanup();
+       return ret;
+}
+
+module_init(muennet_init);
+module_exit(muennet_cleanup);
+
+MODULE_DESCRIPTION(DRV_DESCRIPTION);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Torsten Hilbrich <torsten.hilbrich@secunet.com>");
+
+/*@}*/
diff --git a/reader.c b/reader.c
new file mode 100644 (file)
index 0000000..949f9c0
--- /dev/null
+++ b/reader.c
@@ -0,0 +1,314 @@
+/*
+ * Muen virtual network driver.
+ *
+ * Copyright (C) 2015  secunet Security Networks AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/kthread.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+
+#include "internal.h"
+
+/**
+ * @file reader.c
+ * @brief Implementation of reader part.
+ *
+ * This file implements the functions to setup and cleanup the reader part of
+ * the network interface as well as the thread function responsible for polling
+ * the channel for new data and injecting them into the network stack.
+ */
+
+/**
+ * @brief Cleanup reader.
+ *
+ * This function frees all previously allocated memory after stopping the
+ * reader thread.
+ *
+ * @param dev_info private information stored in the network interface
+ */
+void cleanup_reader(struct dev_info *dev_info)
+{
+       dev_info->reader_element_size = 0;
+       if (dev_info->reader_thread) {
+               kthread_stop(dev_info->reader_thread);
+               dev_info->reader_thread = NULL;
+       }
+       iounmap(dev_info->channel_in);
+       dev_info->channel_in = NULL;
+}
+
+/**
+ * @brief Allocates (or reuses) an skb.
+ *
+ * This little utility function retrieves a previously allocated skb (that was
+ * never used) or allocates a new one if no previous skb is found.
+ *
+ * @param skb      address of skb to reuse or allocate
+ * @param dev_info contains reader_element_size and flags from which the size
+ *                 of the skb is derived
+ *
+ * @return 0 on success
+ * @return -ENOMEM if memory allocation failed
+ */
+static int get_skb(struct sk_buff **skb, struct dev_info *dev_info)
+{
+       size_t size = dev_info->reader_element_size;
+
+       if (dev_info->flags & MUENNET_HDR)
+               size += sizeof(struct net_hdr);
+
+       /* only do allocation if actually needed */
+       if (*skb == NULL) {
+               struct sk_buff *new_skb = alloc_skb(size, GFP_KERNEL);
+               if (new_skb == NULL)
+                       return -ENOMEM;
+               *skb = new_skb;
+       }
+       return 0;
+}
+
+/**
+ * @ingroup reader
+ * @brief Reader thread
+ *
+ * This function will query the memory region for new data. It first waits for
+ * the should_read element in the #dev_info to be set. Then it starts reading
+ * data. If an overrun is detected it will update the interface statistics.
+ *
+ * As long as data is available, it will be read and injected into the
+ * networking code. After each processed packet, control is returned to the
+ * kernel to allow scheduling.
+ *
+ * If no data is available, the reader thread will pause (interruptible) as long
+ * as requested by the #dev_info::poll_interval value.
+ *
+ * Access to the reader part of #dev_info is protected with
+ * #dev_info::reader_lock.
+ *
+ * @param data pointer to #dev_info for this networking interface
+ * @return always returns 0 (on thread_stop)
+ */
+static int muennet_reader_thread(void *data)
+{
+       struct dev_info *dev_info = data;
+       struct sk_buff *skb = NULL;
+
+       netdev_dbg(dev_info->dev, "Starting reader thread\n");
+
+       while (1) {
+               enum muchannel_reader_result result = MUCHANNEL_SUCCESS;
+
+               /* first wait until there is something to do */
+               wait_event_interruptible(dev_info->read_wq,
+                                        atomic_read(&dev_info->should_read) ||
+                                        kthread_should_stop());
+
+               /* check abort conditions */
+               if (kthread_should_stop())
+                       break;
+
+               while (result == MUCHANNEL_SUCCESS ||
+                               result == MUCHANNEL_OVERRUN_DETECTED) {
+                       uint32_t len;
+                       __be16 protocol = 0;
+                       struct iphdr *ipv4_hdr;
+                       struct ipv6hdr *ipv6_hdr;
+
+                       /*
+                        * fetch an skb, either a new one or the previous one if
+                        * still available. re-schedule if no memory is
+                        * available
+                        */
+                       if (get_skb(&skb, dev_info)) {
+                               netdev_warn(dev_info->dev,
+                                           "Failed to allocate skb\n");
+                               break;
+                       }
+
+                       result = muen_channel_read(dev_info->channel_in,
+                               &dev_info->reader,
+                               skb->data);
+
+                       if (result == MUCHANNEL_EPOCH_CHANGED) {
+                               /* TODO: Check protocol */
+                               dev_info->reader_element_size = dev_info->reader.size;
+
+                               if (dev_info->reader_element_size > 0x100000) {
+                                       netdev_err(dev_info->dev,
+                                                  "Element size to big %zu\n",
+                                                  dev_info->reader_element_size);
+                                       result = MUCHANNEL_INCOMPATIBLE_INTERFACE;
+                                       dev_info->reader_element_size = 0;
+                                       break;
+                               } else {
+                                       consume_skb(skb);
+                                       skb = NULL;
+
+                                       if (get_skb(&skb, dev_info)) {
+                                               netdev_warn(dev_info->dev,
+                                                           "Failed to allocate skb after consume\n");
+                                               break;
+                                       }
+                               }
+                       }
+
+                       /* check if data is present */
+                       if (result == MUCHANNEL_NO_DATA ||
+                                       result == MUCHANNEL_INACTIVE)
+                               goto schedule;
+
+                       /* check if overrun by writer */
+                       if (result == MUCHANNEL_OVERRUN_DETECTED) {
+                               netdev_info(dev_info->dev,
+                                           "Reader overrun detected\n");
+                               dev_info->stats.rx_errors++;
+                               dev_info->stats.rx_over_errors++;
+                               goto schedule;
+                       }
+
+                       /* check if net_hdr flag is given */
+                       if (dev_info->flags & MUENNET_HDR) {
+                               struct net_hdr *hdr = (struct net_hdr *)skb->data;
+
+                               skb->mark = hdr->mark;
+                               skb_reserve(skb, sizeof(struct net_hdr));
+                               switch (hdr->protocol) {
+                               case IPPROTO_IPIP:
+                                       protocol = htons(ETH_P_IP);
+                                       break;
+                               case IPPROTO_IPV6:
+                                       protocol = htons(ETH_P_IPV6);
+                                       break;
+                               }
+                       }
+
+                       /* read the data to determine the protocol */
+                       ipv4_hdr = (void *)skb->data;
+                       ipv6_hdr = (void *)skb->data;
+
+                       /* determine the payload length */
+                       if (protocol == htons(ETH_P_IP))
+                               len = be16_to_cpu(ipv4_hdr->tot_len);
+                       else if (protocol == htons(ETH_P_IPV6))
+                               len = be16_to_cpu(ipv6_hdr->payload_len) + 40;
+                       else
+                               len = dev_info->reader_element_size;
+
+                       if (len > dev_info->reader_element_size ||
+                               len > skb_tailroom(skb)) {
+                               netdev_warn(dev_info->dev,
+                                           "Invalid length: %u\n",
+                                           (unsigned int)len);
+                               dev_info->stats.rx_errors++;
+                               dev_info->stats.rx_frame_errors++;
+                               goto schedule;
+                       }
+
+                       /*
+                        * now the skb is ready to be processed, but correct
+                        * some data first
+                        */
+                       skb->dev = dev_info->dev;
+                       skb_put(skb, len);
+                       skb->protocol = protocol;
+
+                       /* process and update stats */
+                       netif_rx_ni(skb);
+                       dev_info->stats.rx_packets++;
+                       dev_info->stats.rx_bytes += skb->len;
+
+                       /* now mark the skb as processed */
+                       skb = NULL;
+
+                       /* immediately try to read the next packet */
+                       continue;
+
+               schedule:
+                       /* allow other threads to run after each
+                        * attempt to read a packet */
+                       schedule();
+               }
+
+               /* schedule with some time to wait */
+               if (dev_info->poll_interval > 0)
+                       schedule_timeout_interruptible(usecs_to_jiffies
+                                       (dev_info->poll_interval * 1000));
+       }
+
+       netdev_dbg(dev_info->dev, "Stopping reader thread\n");
+       return 0;
+}
+
+/**
+ * @brief Initialize reader.
+ *
+ * This function initializes the reader part of the networking interface. It
+ * uses the region given as parameter and starts the thread executing the
+ * #muennet_reader_thread function.
+ *
+ * The size of the elements is calculated based on the MTU configured for this
+ * networking interface. The number of elements is limited by the maximum
+ * region size. Both reader and writer using the same memory region must be
+ * configured in a consistent way.
+ *
+ * @param dev_info the private information of the networking interface
+ * @param region   the memory region to use for reading
+ * @return 0 on success
+ * @return -EPERM  if channel is writable
+ * @return -EFAULT if ioremap fails
+ * @return -ENOMEM if memory allocation failed
+ * @return errors returned by kthread_create()
+ */
+int initialize_reader(struct dev_info *dev_info,
+                     const struct muen_channel_info * const channel)
+{
+       int ret;
+
+       if (channel->writable) {
+               netdev_err(dev_info->dev, "Reader channel '%s' writable\n",
+                          channel->name);
+               return -EPERM;
+       }
+
+       ret = -ENOMEM;
+       dev_info->reader_element_size = 0;
+
+       dev_info->channel_in = ioremap_cache(channel->address, channel->size);
+       if (dev_info->channel_in == NULL) {
+               netdev_err(dev_info->dev, "Unable to map reader channel\n");
+               return -EFAULT;
+       }
+
+       muen_channel_init_reader(&dev_info->reader, dev_info->reader_protocol);
+
+       dev_info->reader_thread = kthread_create
+               (muennet_reader_thread, dev_info, "%s/reader",
+                dev_info->dev->name);
+       if (IS_ERR(dev_info->reader_thread)) {
+               ret = PTR_ERR(dev_info->reader_thread);
+               dev_info->reader_thread = NULL;
+               iounmap(dev_info->channel_in);
+               dev_info->channel_in = NULL;
+               return ret;
+       }
+
+       init_waitqueue_head(&dev_info->read_wq);
+       atomic_set(&dev_info->should_read, 0);
+       wake_up_process(dev_info->reader_thread);
+
+       return 0;
+}
diff --git a/writer.c b/writer.c
new file mode 100644 (file)
index 0000000..e8582a3
--- /dev/null
+++ b/writer.c
@@ -0,0 +1,223 @@
+/*
+ * Muen virtual network driver.
+ *
+ * Copyright (C) 2015  secunet Security Networks AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <muen/writer.h>
+
+#include "internal.h"
+
+/**
+ * @file writer.c
+ * @brief Functions for the writer part of the networking interface.
+ *
+ * The functions in this file implement the writer initialization and cleanup
+ * as well as the actual transmit function for a network interface.
+ */
+
+static size_t gross_packet_size(size_t net_size, unsigned long flags)
+{
+       size_t element_size = net_size;
+
+       /* raw / annotated IP transfers? */
+       if (flags & MUENNET_HDR)
+               element_size += sizeof(struct net_hdr);
+
+       return element_size;
+}
+
+void writer_down(struct dev_info *dev_info)
+{
+       if (dev_info->writer_element_size > 0)
+               muen_channel_deactivate(dev_info->channel_out);
+
+       dev_info->writer_element_size = 0;
+}
+
+void writer_up(struct dev_info *dev_info)
+{
+       u64 epoch;
+
+       get_random_bytes(&epoch, sizeof(epoch));
+
+       dev_info->writer_element_size = gross_packet_size(dev_info->mtu,
+               dev_info->flags);
+       muen_channel_init_writer
+               (dev_info->channel_out,
+                dev_info->writer_protocol,
+                dev_info->writer_element_size,
+                dev_info->writer_region_size,
+                epoch);
+       netdev_info(dev_info->dev,
+                   "Using protocol %llu, channel/element size 0x%zx/0x%zx bytes\n",
+                   dev_info->writer_protocol, dev_info->writer_region_size,
+                   dev_info->writer_element_size);
+}
+
+/**
+ * @brief Cleanup writer.
+ *
+ * This function releases all the memory allocated for the writer.
+ *
+ * @param dev_info private information stored in the network interface
+ */
+void cleanup_writer(struct dev_info *dev_info)
+{
+       if (dev_info->channel_out)
+               writer_down(dev_info);
+       iounmap(dev_info->channel_out);
+       dev_info->channel_out = NULL;
+}
+
+/**
+ * @brief Initialize writer.
+ *
+ * This function initializes a writer for the given memory region.
+ *
+ * The size of the elements is calculated based on the MTU configured for this
+ * networking interface and whether the net_hdr transfer mode is used. The
+ * number of elements is limited by the maximum region size. Both reader and
+ * writer operating on the same memory region must be configured in a
+ * consistent way.
+ *
+ * @param dev_info the private information stored in the network interface
+ * @param region   memory region info structure
+ *
+ * @return 0 on success
+ * @return -EPERM  if channel is not writable
+ * @return -EFAULT if ioremap fails
+ * @return -ENOMEM if memory allocation failed
+ * @return errors returned by #common_check_region
+ */
+int initialize_writer(struct dev_info *dev_info,
+                     const struct muen_channel_info * const channel)
+{
+       /* some sanity checks */
+       if (!channel->writable) {
+               netdev_err(dev_info->dev, "Writer channel '%s' not writable\n",
+                          channel->name);
+               return -EPERM;
+       }
+
+       dev_info->writer_region_size = channel->size;
+
+       /* writer_element_size is determined when the interface is set up */
+       dev_info->writer_element_size = 0;
+
+       /* now remember the start of the region and initialize it */
+       dev_info->channel_out = ioremap_cache(channel->address, channel->size);
+       if (dev_info->channel_out == NULL) {
+               netdev_err(dev_info->dev, "Unable to map writer channel\n");
+               return -EFAULT;
+       }
+
+       /* initialize the lock */
+       spin_lock_init(&dev_info->writer_lock);
+
+       return 0;
+}
+
+/**
+ * @brief Transmit network packet.
+ *
+ * This function transmits the network packet to the memory region associated
+ * with the given networking device.
+ *
+ * If no memory region is associated to this writer, the packet is dropped.
+ * Otherwise it is written to the memory region.
+ *
+ * For locking between multiple writers the #dev_info::writer_lock is used.
+ *
+ * @param skb the network packet to transmit
+ * @param dev the networking interface to use
+ *
+ * @return NET_XMIT_SUCCESS if packet could be transmitted or was dropped
+ * @return NETDEV_TX_LOCKED if another writer is blocking access to
+ *                          the data structure (should never happen)
+ */
+int muennet_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       struct dev_info *dev_info = netdev_priv(dev);
+       unsigned long flags, max_size;
+
+       /* check if writing is possible */
+       if (!dev_info->writer_element_size) {
+               dev_info->stats.tx_dropped++;
+               dev_kfree_skb(skb);
+               return NET_XMIT_SUCCESS;
+       }
+
+       /* check for supported network protocols */
+       if (dev_info->flags & MUENNET_HDR) {
+               if (skb->protocol != htons(ETH_P_IP) &&
+                   skb->protocol != htons(ETH_P_IPV6)) {
+                       dev_info->stats.tx_dropped++;
+                       dev_kfree_skb(skb);
+                       return NET_XMIT_SUCCESS;
+               }
+       }
+
+       /* check if exclusive access is granted, if not request a requeue */
+       if (!spin_trylock_irqsave(&dev_info->writer_lock, flags))
+               return NETDEV_TX_LOCKED;
+
+       if ((dev_info->flags & MUENNET_HDR)) {
+               int len = skb->len;
+               struct net_hdr *hdr;
+
+               hdr = (struct net_hdr *)skb_push(skb, sizeof(struct net_hdr));
+
+               hdr->mark = skb->mark;
+               hdr->length = len;
+
+               switch (skb->protocol) {
+               case htons(ETH_P_IP):
+                       hdr->protocol = IPPROTO_IPIP;
+                       break;
+               case htons(ETH_P_IPV6):
+                       hdr->protocol = IPPROTO_IPV6;
+                       break;
+               default:
+                       hdr->protocol = 0;
+                       break;
+               }
+
+               max_size = dev_info->writer_element_size -
+                       sizeof(struct net_hdr);
+               if (len > max_size) {
+                       netdev_warn(dev_info->dev,
+                                   "Oversized packet dropped (size = %u, max = %lu, MTU = %u)\n",
+                                   len, max_size, dev_info->mtu);
+                       dev_info->stats.tx_dropped++;
+                       spin_unlock_irqrestore(&dev_info->writer_lock, flags);
+                       dev_kfree_skb(skb);
+                       return NET_XMIT_SUCCESS;
+               }
+       }
+
+       muen_channel_write(dev_info->channel_out, skb->data);
+
+       /* update stats */
+       dev_info->stats.tx_packets++;
+       dev_info->stats.tx_bytes += skb->len;
+
+       /* end of protected section */
+       spin_unlock_irqrestore(&dev_info->writer_lock, flags);
+
+       dev_kfree_skb(skb);
+       return NET_XMIT_SUCCESS;
+}