* Re: [GIT PULL net-2.6] vhost-net: rcu fixup
From: David Miller @ 2010-11-28 19:27 UTC (permalink / raw)
To: mst; +Cc: kvm, virtualization, netdev, linux-kernel
In-Reply-To: <20101125122301.GA15990@redhat.com>
From: "Michael S. Tsirkin" <mst@redhat.com>
Date: Thu, 25 Nov 2010 14:23:01 +0200
> Please merge the following fix for 2.6.36.
> Thanks!
>
> The following changes since commit a27e13d370415add3487949c60810e36069a23a6:
>
> econet: fix CVE-2010-3848 (2010-11-24 11:51:47 -0800)
>
> are available in the git repository at:
> git://git.kernel.org/pub/scm/linux/kernel/git/mst/vhost.git vhost-net
>
> Michael S. Tsirkin (1):
> vhost/net: fix rcu check usage
>
Pulled, thanks Michael.
^ permalink raw reply
* Re: [PATCH] x86/pvclock-xen: zero last_value on resume
From: Jeremy Fitzhardinge @ 2010-11-26 22:29 UTC (permalink / raw)
To: Ingo Molnar
Cc: Xen-devel@lists.xensource.com, kvm-devel, Glauber Costa,
the arch/x86 maintainers, Linux Kernel Mailing List,
Linux Virtualization, Olivier Hanesse, Avi Kivity, H. Peter Anvin,
Thomas Gleixner, Eelco Dolstra
In-Reply-To: <20101126104047.GA9798@elte.hu>
On 11/26/2010 02:40 AM, Ingo Molnar wrote:
> * Jeremy Fitzhardinge <jeremy@goop.org> wrote:
>
>> On 10/26/2010 09:59 AM, Jeremy Fitzhardinge wrote:
>>> If the guest domain has been suspend/resumed or migrated, then the
>>> system clock backing the pvclock clocksource may revert to a smaller
>>> value (ie, can be non-monotonic across the migration/save-restore).
>>> Make sure we zero last_value in that case so that the domain
>>> continues to see clock updates.
>> Ping? Looks like this fell through the gaps.
> Does not apply cleanly here - mind resending the latest version?
It rebased cleanly to 2.6.37-rc3. You can pull it from:
git://git.kernel.org/pub/scm/linux/kernel/git/jeremy/xen.git pvclock-resume
Jeremy Fitzhardinge (1):
x86/pvclock: zero last_value on resume
arch/x86/include/asm/pvclock.h | 1 +
arch/x86/kernel/pvclock.c | 5 +++++
arch/x86/xen/time.c | 2 ++
3 files changed, 8 insertions(+), 0 deletions(-)
Thanks,
J
Subject: [PATCH] x86/pvclock: zero last_value on resume
If the guest domain has been suspend/resumed or migrated, then the
system clock backing the pvclock clocksource may revert to a smaller
value (ie, can be non-monotonic across the migration/save-restore).
Make sure we zero last_value in that case so that the domain
continues to see clock updates.
Signed-off-by: Jeremy Fitzhardinge <jeremy.fitzhardinge@citrix.com>
diff --git a/arch/x86/include/asm/pvclock.h b/arch/x86/include/asm/pvclock.h
index 7f7e577..31d84ac 100644
--- a/arch/x86/include/asm/pvclock.h
+++ b/arch/x86/include/asm/pvclock.h
@@ -11,6 +11,7 @@ unsigned long pvclock_tsc_khz(struct pvclock_vcpu_time_info *src);
void pvclock_read_wallclock(struct pvclock_wall_clock *wall,
struct pvclock_vcpu_time_info *vcpu,
struct timespec *ts);
+void pvclock_resume(void);
/*
* Scale a 64-bit delta by scaling and multiplying by a 32-bit fraction,
diff --git a/arch/x86/kernel/pvclock.c b/arch/x86/kernel/pvclock.c
index 008b91e..42eb330 100644
--- a/arch/x86/kernel/pvclock.c
+++ b/arch/x86/kernel/pvclock.c
@@ -83,6 +83,11 @@ unsigned long pvclock_tsc_khz(struct pvclock_vcpu_time_info *src)
static atomic64_t last_value = ATOMIC64_INIT(0);
+void pvclock_resume(void)
+{
+ atomic64_set(&last_value, 0);
+}
+
cycle_t pvclock_clocksource_read(struct pvclock_vcpu_time_info *src)
{
struct pvclock_shadow_time shadow;
diff --git a/arch/x86/xen/time.c b/arch/x86/xen/time.c
index b2bb5aa..5da5e53 100644
--- a/arch/x86/xen/time.c
+++ b/arch/x86/xen/time.c
@@ -426,6 +426,8 @@ void xen_timer_resume(void)
{
int cpu;
+ pvclock_resume();
+
if (xen_clockevent != &xen_vcpuop_clockevent)
return;
^ permalink raw reply related
* Re: [PATCH] x86/pvclock-xen: zero last_value on resume
From: Ingo Molnar @ 2010-11-26 10:40 UTC (permalink / raw)
To: Jeremy Fitzhardinge
Cc: Xen-devel@lists.xensource.com, kvm-devel, Glauber Costa,
the arch/x86 maintainers, Linux Kernel Mailing List,
Linux Virtualization, Olivier Hanesse, Avi Kivity, H. Peter Anvin,
Thomas Gleixner, Eelco Dolstra
In-Reply-To: <4CEACFAB.90503@goop.org>
* Jeremy Fitzhardinge <jeremy@goop.org> wrote:
> On 10/26/2010 09:59 AM, Jeremy Fitzhardinge wrote:
> >
> > If the guest domain has been suspend/resumed or migrated, then the
> > system clock backing the pvclock clocksource may revert to a smaller
> > value (ie, can be non-monotonic across the migration/save-restore).
> > Make sure we zero last_value in that case so that the domain
> > continues to see clock updates.
>
> Ping? Looks like this fell through the gaps.
Does not apply cleanly here - mind resending the latest version?
Thanks,
Ingo
^ permalink raw reply
* Re: transport between guest and host
From: Stefan Hajnoczi @ 2010-11-25 21:07 UTC (permalink / raw)
To: devi thapa; +Cc: virtualization
In-Reply-To: <AANLkTimgiSxZsjC+kaNST_Gw4Fw+tEp7+UZTyfb6-k+E@mail.gmail.com>
On Thu, Nov 25, 2010 at 4:43 PM, devi thapa <devi.thapa@gmail.com> wrote:
> I want to transport modules from host and insert in guest and
> transport the result back to host.
Linux kernel modules?
If you describe the problem you're trying to solve in more detail
you'll get more helpful feedback.
Stefan
^ permalink raw reply
* Re: transport between guest and host
From: devi thapa @ 2010-11-25 16:43 UTC (permalink / raw)
To: Stefan Hajnoczi; +Cc: virtualization
In-Reply-To: <AANLkTi=47msiiexrmSX_xQHJsJ=oG5JhwDGWta29npm2@mail.gmail.com>
Hi Stefan,
I want to transport modules from host and insert in guest and
transport the result back to host.
Regards,
Devi.
On Thu, Nov 25, 2010 at 8:30 PM, Stefan Hajnoczi <stefanha@gmail.com> wrote:
> On Thu, Nov 25, 2010 at 11:37 AM, devi thapa <devi.thapa@gmail.com> wrote:
>> I have 2 queries.
>> 1. Is virtio-serial or virtio-console data transfer kernel level and
>> efficient than vhostnet networking.
>> 2. Is 9p efficient for communicating between guests and a host
>> using a virtio based transport. Is this also kernel level transport.
>
> The right choice depends on what you are trying to do. What are you
> trying to do :)?
>
> Stefan
>
^ permalink raw reply
* Re: [PATCH RFC] tools/virtio: virtio_ring testing tool
From: Michael S. Tsirkin @ 2010-11-25 16:35 UTC (permalink / raw)
To: अनुज; +Cc: virtualization
In-Reply-To: <AANLkTinx3g1LnMSVLfoZRVQCTFtTvBspyjDgd8JJN5J3@mail.gmail.com>
On Wed, Nov 24, 2010 at 11:40:40AM +0530, अनुज wrote:
> Hi Michael
>
> 2010/11/24 Michael S. Tsirkin <mst@redhat.com>:
> > On Wed, Nov 24, 2010 at 09:41:20AM +0530, अनुज wrote:
> >> Hi Michael,
> >>
> >> > This implements a virtio simulator:
> >> > - adds stubs for enough support functions to compile
> >> > virtio ring in userspace.
> >> > - Adds a stub vhost based module this can talk to.
> >>
> >> I just want to know can I use this virtio simulator in this case :
> >>
> >> 1. using qemu-kvm as a VMM on Linux Host.
> >> 2. using a proprietary OS as a guest.
> >>
> >> I am developing virtio framework and virtio drivers (network &
> >> SCSI/Block) for this guest OS.
> >> Please explain how can I use this virtio simulator for testing and
> >> optimizing my virtio framework and virtio drivers.
What this tool includes is
1. virtio ring bits from linux guest compiled in userspace
tools/virtio/linux/
2. virtio host bits compiled as a linux kernel module
tools/virtio/vhost_null
3. some bits to connect the two and test
tools/virtio/virtio_test.c
I'm using it to tune linux guest and host code.
If you have some other guest code, you could still use the module (2),
and if you make the API compatible, possibly reuse the test (3) parts of it.
It would be nice if you publish changes to the common files, if any, on
the list.
If you want to distribute the result, the rules for this
are in the file COPYING under the linux source tree.
> >> And also please let me know whether I can compile and test my virtio
> >> things without using Guest OS.
No idea.
> >> Thanks a lot.
> >>
> >> Regards,
> >> --
> >> Anuj Aggarwal
> >>
> >> .''`.
> >> : :Ⓐ : # apt-get install hakuna-matata
> >> `. `'`
> >> `-
> >
> > Please copy all mail related to virtio to
> > virtualization@lists.linux-foundation.org
>
> I will take care of this in future.
>
> >
> > Thanks!
> >
> > --
> > MST
> >
>
>
> Regards,
> --
> Anuj Aggarwal
>
> .''`.
> : :Ⓐ : # apt-get install hakuna-matata
> `. `'`
> `-
> _______________________________________________
> Virtualization mailing list
> Virtualization@lists.linux-foundation.org
> https://lists.linux-foundation.org/mailman/listinfo/virtualization
_______________________________________________
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/virtualization
^ permalink raw reply
* Re: transport between guest and host
From: Stefan Hajnoczi @ 2010-11-25 15:00 UTC (permalink / raw)
To: devi thapa; +Cc: virtualization
In-Reply-To: <AANLkTin7L0OY_Z-RxuSY1TqjfQSK6aCPYgSnTwX1h7ht@mail.gmail.com>
On Thu, Nov 25, 2010 at 11:37 AM, devi thapa <devi.thapa@gmail.com> wrote:
> I have 2 queries.
> 1. Is virtio-serial or virtio-console data transfer kernel level and
> efficient than vhostnet networking.
> 2. Is 9p efficient for communicating between guests and a host
> using a virtio based transport. Is this also kernel level transport.
The right choice depends on what you are trying to do. What are you
trying to do :)?
Stefan
^ permalink raw reply
* [GIT PULL net-2.6] vhost-net: rcu fixup
From: Michael S. Tsirkin @ 2010-11-25 12:23 UTC (permalink / raw)
To: David Miller; +Cc: kvm, virtualization, netdev, linux-kernel
Please merge the following fix for 2.6.36.
Thanks!
The following changes since commit a27e13d370415add3487949c60810e36069a23a6:
econet: fix CVE-2010-3848 (2010-11-24 11:51:47 -0800)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/mst/vhost.git vhost-net
Michael S. Tsirkin (1):
vhost/net: fix rcu check usage
drivers/vhost/net.c | 5 +++--
1 files changed, 3 insertions(+), 2 deletions(-)
--
MST
^ permalink raw reply
* transport between guest and host
From: devi thapa @ 2010-11-25 11:37 UTC (permalink / raw)
To: virtualization
Hi,
I have 2 queries.
1. Is virtio-serial or virtio-console data transfer kernel level and
efficient than vhostnet networking.
2. Is 9p efficient for communicating between guests and a host
using a virtio based transport. Is this also kernel level transport.
Request to bear with my queries.
Regards,
Devi.
^ permalink raw reply
* Re: [PATCH 2/3]: An Implementation of HyperV KVP functionality
From: Evgeniy Polyakov @ 2010-11-24 14:56 UTC (permalink / raw)
To: Ky Srinivasan
Cc: devel, Haiyang Zhang, Greg KH, Virtualization, Stephen Hemminger
In-Reply-To: <4CEA6FBE0200003000090BC0@novprvoes0310.provo.novell.com>
Hi.
I will ack connector part of course, but this hunk is actually quite
bad.
> +static void shutdown_onchannelcallback(void *context)
> +{
> + struct vmbus_channel *channel = context;
> + u8 *buf;
> + u32 buflen, recvlen;
> + u64 requestid;
> + u8 execute_shutdown = false;
> +
> + struct shutdown_msg_data *shutdown_msg;
> +
> + struct icmsg_hdr *icmsghdrp;
> + struct icmsg_negotiate *negop = NULL;
> +
> + buflen = PAGE_SIZE;
> + buf = kmalloc(buflen, GFP_ATOMIC);
> +
> + vmbus_recvpacket(channel, buf, buflen, &recvlen, &requestid);
Boom. I did not read further, since this function returns void and thus
can not propagate error, which is likely not a good idea.
--
Evgeniy Polyakov
^ permalink raw reply
* Re: [PATCH RFC] tools/virtio: virtio_ring testing tool
From: अनुज @ 2010-11-24 6:10 UTC (permalink / raw)
To: Michael S. Tsirkin; +Cc: virtualization
In-Reply-To: <20101124050228.GA18883@redhat.com>
Hi Michael
2010/11/24 Michael S. Tsirkin <mst@redhat.com>:
> On Wed, Nov 24, 2010 at 09:41:20AM +0530, अनुज wrote:
>> Hi Michael,
>>
>> > This implements a virtio simulator:
>> > - adds stubs for enough support functions to compile
>> > virtio ring in userspace.
>> > - Adds a stub vhost based module this can talk to.
>>
>> I just want to know can I use this virtio simulator in this case :
>>
>> 1. using qemu-kvm as a VMM on Linux Host.
>> 2. using a proprietary OS as a guest.
>>
>> I am developing virtio framework and virtio drivers (network &
>> SCSI/Block) for this guest OS.
>> Please explain how can I use this virtio simulator for testing and
>> optimizing my virtio framework and virtio drivers.
>> And also please let me know whether I can compile and test my virtio
>> things without using Guest OS.
>
>> Thanks a lot.
>>
>> Regards,
>> --
>> Anuj Aggarwal
>>
>> .''`.
>> : :Ⓐ : # apt-get install hakuna-matata
>> `. `'`
>> `-
>
> Please copy all mail related to virtio to
> virtualization@lists.linux-foundation.org
I will take care of this in future.
>
> Thanks!
>
> --
> MST
>
Regards,
--
Anuj Aggarwal
.''`.
: :Ⓐ : # apt-get install hakuna-matata
`. `'`
`-
_______________________________________________
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/virtualization
^ permalink raw reply
* [PULL] virtio fixes (and one trivial module cleanup)
From: Rusty Russell @ 2010-11-24 4:58 UTC (permalink / raw)
To: Linus Torvalds
Cc: Michael S. Tsirkin, Anders Kaseorg, linux-kernel, virtualization,
Stephen Hemminger, Jiri Slaby
(lguest is currently broken, but timed out chasing it; expect a patch next
week once I get back).
The following changes since commit 3561d43fd289f590fdae672e5eb831b8d5cf0bf6:
Linux 2.6.37-rc3 (2010-11-21 15:18:56 -0800)
are available in the git repository at:
ssh://master.kernel.org/pub/scm/linux/kernel/git/rusty/linux-2.6-for-linus.git master
Anders Kaseorg (1):
module: Update prototype for ref_module (formerly use_module)
Jiri Slaby (1):
Char: virtio_console, fix memory leak
Michael S. Tsirkin (1):
virtio: return correct capacity to users
Stephen Hemminger (1):
virtio: fix format of sysfs driver/vendor files
drivers/char/virtio_console.c | 37 +++++++++----------------------------
drivers/virtio/virtio.c | 6 +++---
drivers/virtio/virtio_ring.c | 3 ---
include/linux/module.h | 2 +-
4 files changed, 13 insertions(+), 35 deletions(-)
^ permalink raw reply
* Re: [RFC PATCH] virtio: (Partially) enable suspend/resume support
From: Amit Shah @ 2010-11-23 3:18 UTC (permalink / raw)
To: Michael S. Tsirkin; +Cc: Matthew Garrett, Anthony Liguori, Virtualization List
In-Reply-To: <20101122145454.GB30157@redhat.com>
On (Mon) Nov 22 2010 [16:54:54], Michael S. Tsirkin wrote:
> On Mon, Nov 22, 2010 at 07:15:37PM +0530, Amit Shah wrote:
> >
> > The Linux kernel's suspend/resume notifiers currently offer the
> > following notifications:
> > - Preparing to go to suspended state (tasks will be frozen now)
> > - Preparing to restore the image saved at hibernate-time
> > - Restore succeeded (called after user-space threads are thawed)
> >
> >
> > What the Windows driver does is destroy all the virtqueues before
> > suspend and re-init all of them on restore. This works well, but in the
> > Linux case, we don't have a notifier that gets called after restore
> > succeeds and before user-space tasks are thawed,
>
> Let's add one?
Yes, that's where we need to start from.
Amit
^ permalink raw reply
* [PATCH 3/3]: An implementation of HyperV KVP functionality
From: Ky Srinivasan @ 2010-11-22 20:45 UTC (permalink / raw)
To: devel, Virtualization; +Cc: Zbr, Haiyang Zhang, Stephen Hemminger, Greg KH
[-- Attachment #1: Type: text/plain, Size: 308 bytes --]
An implementation of key/value pair feature (KVP) for Linux on HyperV. In this version of the patch I have addressed all the comments I have received to date. I have also included the code for the user-level daemon here for your reference.
Signed-off-by: K. Y. Srinivasan <ksrinivasan@novell.com>
[-- Attachment #2: hv_kvp.patch --]
[-- Type: text/plain, Size: 20510 bytes --]
From: K. Y. Srinivasan <ksrinivasan@novell.com>
Subject: An implementation of key/value pair feature (KVP) for Linux
on HyperV.
Signed-off-by: K. Y. Srinivasan <ksrinivasan@novell.com>
Index: linux.trees.git/drivers/staging/hv/Makefile
===================================================================
--- linux.trees.git.orig/drivers/staging/hv/Makefile 2010-11-19 17:57:16.000000000 -0500
+++ linux.trees.git/drivers/staging/hv/Makefile 2010-11-19 17:57:17.000000000 -0500
@@ -10,4 +10,4 @@ hv_vmbus-y := vmbus_drv.o osd.o \
hv_storvsc-y := storvsc_drv.o storvsc.o
hv_blkvsc-y := blkvsc_drv.o blkvsc.o
hv_netvsc-y := netvsc_drv.o netvsc.o rndis_filter.o
-hv_utils-y := hv_util.o
+hv_utils-y := hv_util.o hv_kvp.o
Index: linux.trees.git/drivers/staging/hv/utils.h
===================================================================
--- linux.trees.git.orig/drivers/staging/hv/utils.h 2010-11-19 17:30:48.000000000 -0500
+++ linux.trees.git/drivers/staging/hv/utils.h 2010-11-19 17:57:17.000000000 -0500
@@ -102,6 +102,7 @@ struct ictimesync_data{
#define HV_SHUTDOWN_MSG 0
#define HV_TIMESYNC_MSG 1
#define HV_HEARTBEAT_MSG 2
+#define HV_KVP_MSG 3
struct hyperv_service_callback {
u8 msg_type;
Index: linux.trees.git/drivers/staging/hv/hv_kvp.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux.trees.git/drivers/staging/hv/hv_kvp.c 2010-11-19 18:02:21.000000000 -0500
@@ -0,0 +1,356 @@
+/*
+ * An implementation of key value pair (KVP) functionality for Linux.
+ *
+ *
+ * Copyright (C) 2010, Novell, Inc.
+ * Author : K. Y. Srinivasan <ksrinivasan@novell.com>
+ *
+ * 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, GOOD TITLE or
+ * NON INFRINGEMENT. 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/net.h>
+#include <linux/nls.h>
+#include <linux/connector.h>
+#include <linux/workqueue.h>
+
+#include "logging.h"
+#include "osd.h"
+#include "vmbus.h"
+#include "vmbus_packet_format.h"
+#include "vmbus_channel_interface.h"
+#include "version_info.h"
+#include "channel.h"
+#include "vmbus_private.h"
+#include "vmbus_api.h"
+#include "utils.h"
+#include "hv_kvp.h"
+
+
+
+/*
+ * Global state maintained for transaction that is being processed.
+ * Note that only one transaction can be active at any point in time.
+ *
+ * This state is set when we receive a request from the host; we
+ * cleanup this state when the transaction is completed - when we respond
+ * to the host with the key value.
+ */
+
+static struct {
+ bool active; /* transaction status - active or not */
+ u8 *recv_buffer; /* the receive buffer that we allocated */
+ int recv_len; /* number of bytes received. */
+ struct vmbus_channel *recv_channel; /* chn we got the request */
+ u64 recv_req_id; /* request ID. */
+} kvp_transaction;
+
+static int kvp_send_key(int index);
+
+static void kvp_respond_to_host(char *key, char *value, int error);
+static void kvp_work_func(struct work_struct *dummy);
+static void kvp_register(void);
+
+static DECLARE_DELAYED_WORK(kvp_work, kvp_work_func);
+
+static struct cb_id kvp_id = { CN_KVP_IDX, CN_KVP_VAL };
+static const char kvp_name[] = "kvp_kernel_module";
+static int timeout_fired;
+
+/*
+ * Register the kernel component with the user-level daemon.
+ * As part of this registration, pass the LIC version number.
+ */
+
+static void
+kvp_register(void)
+{
+
+ struct cn_msg *msg;
+
+ msg = kzalloc(sizeof(*msg) + strlen(HV_DRV_VERSION) + 1 , GFP_ATOMIC);
+
+ if (msg) {
+ msg->id.idx = CN_KVP_IDX;
+ msg->id.val = CN_KVP_VAL;
+ msg->seq = KVP_REGISTER;
+ strcpy(msg->data, HV_DRV_VERSION);
+ msg->len = strlen(HV_DRV_VERSION) + 1;
+ cn_netlink_send(msg, 0, GFP_ATOMIC);
+ kfree(msg);
+ }
+}
+static void
+kvp_work_func(struct work_struct *dummy)
+{
+ /*
+ * If the timer fires, the user-mode component has not responded;
+ * process the pending transaction.
+ */
+ kvp_respond_to_host("Unknown key", "Guest timed out", timeout_fired);
+ timeout_fired = 1;
+}
+
+/*
+ * Callback when data is received from user mode.
+ */
+
+static void
+kvp_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
+{
+ struct hv_ku_msg *message;
+
+ message = (struct hv_ku_msg *)msg->data;
+ if (msg->seq == KVP_REGISTER) {
+ printk(KERN_INFO "KVP: user-mode registering done.\n");
+ kvp_register();
+ }
+
+ if (msg->seq == KVP_USER_SET) {
+ /*
+ * Complete the transaction by forwarding the key value
+ * to the host. But first, cancel the timeout.
+ */
+ if (cancel_delayed_work_sync(&kvp_work))
+ kvp_respond_to_host(message->kvp_key,
+ message->kvp_value,
+ !strlen(message->kvp_key));
+ }
+}
+
+static int
+kvp_send_key(int index)
+{
+ struct cn_msg *msg;
+
+ msg = kzalloc(sizeof(*msg) + sizeof(struct hv_kvp_msg) , GFP_ATOMIC);
+
+ if (msg) {
+ msg->id.idx = CN_KVP_IDX;
+ msg->id.val = CN_KVP_VAL;
+ msg->seq = KVP_KERNEL_GET;
+ ((struct hv_ku_msg *)msg->data)->kvp_index = index;
+ msg->len = sizeof(struct hv_ku_msg);
+ cn_netlink_send(msg, 0, GFP_ATOMIC);
+ kfree(msg);
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Send a response back to the host.
+ */
+
+static void
+kvp_respond_to_host(char *key, char *value, int error)
+{
+ struct hv_kvp_msg *kvp_msg;
+ struct hv_kvp_msg_enumerate *kvp_data;
+ char *key_name;
+ struct icmsg_hdr *icmsghdrp;
+ int keylen, valuelen;
+ u8 *buf;
+ u32 buf_len;
+ struct vmbus_channel *channel;
+ u64 req_id;
+
+ /*
+ * If a transaction is not active; log and return.
+ */
+
+ if (!kvp_transaction.active) {
+ /*
+ * This is a spurious call!
+ */
+ printk(KERN_WARNING "KVP: Transaction not active\n");
+ return;
+ }
+ /*
+ * Copy the global state for completing the transaction. Note that
+ * only one transaction can be active at a time.
+ */
+
+ buf = kvp_transaction.recv_buffer;
+ buf_len = kvp_transaction.recv_len;
+ channel = kvp_transaction.recv_channel;
+ req_id = kvp_transaction.recv_req_id;
+ kvp_transaction.active = false;
+
+ icmsghdrp = (struct icmsg_hdr *)&buf[sizeof(struct vmbuspipe_hdr)];
+ kvp_msg = (struct hv_kvp_msg *)&buf[sizeof(struct vmbuspipe_hdr) +
+ sizeof(struct icmsg_hdr)];
+ kvp_data = &kvp_msg->kvp_data;
+ key_name = key;
+
+ /*
+ * If the error parameter is set, terminate the host's enumeration.
+ */
+ if (error) {
+ /*
+ * We don't support this index or the we have timedout;
+ * terminate the host-side iteration by returning an error.
+ */
+ icmsghdrp->status = HV_E_FAIL;
+ goto response_done;
+ }
+
+ /*
+ * The windows host expects the key/value pair to be encoded
+ * in utf16.
+ */
+ keylen = utf8s_to_utf16s(key_name, strlen(key_name),
+ (wchar_t *)kvp_data->data.key);
+ kvp_data->data.key_size = 2*(keylen + 1); /* utf16 encoding */
+ valuelen = utf8s_to_utf16s(value, strlen(value),
+ (wchar_t *)kvp_data->data.value);
+ kvp_data->data.value_size = 2*(valuelen + 1); /* utf16 encoding */
+
+ kvp_data->data.value_type = REG_SZ; /* all our values are strings */
+ icmsghdrp->status = HV_S_OK;
+
+response_done:
+ icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE;
+
+ vmbus_sendpacket(channel, buf, buf_len, req_id,
+ VmbusPacketTypeDataInBand, 0);
+
+ /*
+ * Free up the buffer that was allocatted when we received a message
+ * from the host.
+ */
+ kfree(buf);
+}
+
+/*
+ * This callback is invoked when we get a KVP message from the host.
+ * The host ensures that only one KVP transaction can be active at a time.
+ * KVP implementation in Linux needs to forward the key to a user-mde
+ * component to retrive the corresponding value. Consequently, we cannot
+ * respond to the host in the conext of this callback. Since the host
+ * guarantees that at most only one transaction can be active at a time,
+ * we stash away the transaction state in a set of global variables.
+ */
+
+void hv_kvp_onchannelcallback(void *context)
+{
+ struct vmbus_channel *channel = context;
+ u8 *buf;
+ u32 buflen, recvlen;
+ u64 requestid;
+
+ struct hv_kvp_msg *kvp_msg;
+ struct hv_kvp_msg_enumerate *kvp_data;
+
+ struct icmsg_hdr *icmsghdrp;
+ struct icmsg_negotiate *negop = NULL;
+
+
+ buflen = PAGE_SIZE;
+ buf = kmalloc(buflen, GFP_ATOMIC);
+
+ vmbus_recvpacket(channel, buf, buflen, &recvlen, &requestid);
+
+ if (recvlen > 0) {
+ DPRINT_DBG(VMBUS, "KVP packet: len=%d, requestid=%lld",
+ recvlen, requestid);
+
+ icmsghdrp = (struct icmsg_hdr *)&buf[
+ sizeof(struct vmbuspipe_hdr)];
+
+ if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
+ prep_negotiate_resp(icmsghdrp, negop, buf);
+ } else {
+ kvp_msg = (struct hv_kvp_msg *)&buf[
+ sizeof(struct vmbuspipe_hdr) +
+ sizeof(struct icmsg_hdr)];
+
+ kvp_data = &kvp_msg->kvp_data;
+
+ /*
+ * We only support the "get" operation on
+ * "KVP_POOL_AUTO" pool.
+ */
+
+ if ((kvp_msg->kvp_hdr.pool != KVP_POOL_AUTO) ||
+ (kvp_msg->kvp_hdr.operation !=
+ KVP_OP_ENUMERATE) ||
+ kvp_transaction.active) {
+ if (kvp_transaction.active)
+ printk(KERN_WARNING
+ "KVP: Invalid call\n");
+ icmsghdrp->status = HV_E_FAIL;
+ goto callback_done;
+ }
+
+ /*
+ * Stash away this global state for completing the
+ * transaction; note transactions are serialized.
+ */
+ kvp_transaction.recv_buffer = buf;
+ kvp_transaction.recv_len = recvlen;
+ kvp_transaction.recv_channel = channel;
+ kvp_transaction.recv_req_id = requestid;
+ kvp_transaction.active = true;
+
+ /*
+ * Get the information from the
+ * user-mode component.
+ * component. This transaction will be
+ * completed when we get the value from
+ * the user-mode component.
+ * Set a timeout to deal with
+ * user-mode not responding.
+ */
+ kvp_send_key(kvp_data->index);
+ schedule_delayed_work(&kvp_work, 100);
+
+ return;
+
+ }
+
+callback_done:
+
+ icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
+ | ICMSGHDRFLAG_RESPONSE;
+
+ vmbus_sendpacket(channel, buf,
+ recvlen, requestid,
+ VmbusPacketTypeDataInBand, 0);
+ }
+
+ kfree(buf);
+
+
+}
+
+int
+hv_kvp_init(void)
+{
+ int err;
+
+ err = cn_add_callback(&kvp_id, kvp_name, kvp_cn_callback);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+void hv_kvp_deinit(void)
+{
+ cn_del_callback(&kvp_id);
+ cancel_delayed_work_sync(&kvp_work);
+}
Index: linux.trees.git/drivers/staging/hv/hv_kvp.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux.trees.git/drivers/staging/hv/hv_kvp.h 2010-11-19 17:59:40.000000000 -0500
@@ -0,0 +1,184 @@
+/*
+ * An implementation of HyperV key value pair (KVP) functionality for Linux.
+ *
+ *
+ * Copyright (C) 2010, Novell, Inc.
+ * Author : K. Y. Srinivasan <ksrinivasan@novell.com>
+ *
+ * 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, GOOD TITLE or
+ * NON INFRINGEMENT. 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 _KVP_H
+#define _KVP_H_
+
+/*
+ * Maximum value size - used for both key names and value data, and includes
+ * any applicable NULL terminators.
+ *
+ * Note: This limit is somewhat arbitrary, but falls easily within what is
+ * supported for all native guests (back to Win 2000) and what is reasonable
+ * for the IC KVP exchange functionality. Note that Windows Me/98/95 are
+ * limited to 255 character key names.
+ *
+ * MSDN recommends not storing data values larger than 2048 bytes in the
+ * registry.
+ *
+ * Note: This value is used in defining the KVP exchange message - this value
+ * cannot be modified without affecting the message size and compatability.
+ */
+
+/*
+ * bytes, including any null terminators
+ */
+#define HV_KVP_EXCHANGE_MAX_VALUE_SIZE (2048)
+
+
+/*
+ * Maximum key size - the registry limit for the length of an entry name
+ * is 256 characters, including the null terminator
+ */
+
+#define HV_KVP_EXCHANGE_MAX_KEY_SIZE (512)
+
+/*
+ * In Linux, we implement the KVP functionality in two components:
+ * 1) The kernel component which is packaged as part of the hv_utils driver
+ * is responsible for communicating with the host and responsible for
+ * implementing the host/guest protocol. 2) A user level daemon that is
+ * responsible for data gathering.
+ *
+ * Host/Guest Protocol: The host iterates over an index and expects the guest
+ * to assign a key name to the index and also return the value corresponding to
+ * the key. The host will have atmost one KVP transaction outstanding at any
+ * given point in time. The host side iteration stops when the guest returns
+ * an error. Microsoft has specified the following mapping of key names to
+ * host specified index:
+ *
+ * Index Key Name
+ * 0 FullyQualifiedDomainName
+ * 1 IntegrationServicesVersion
+ * 2 NetworkAddressIPv4
+ * 3 NetworkAddressIPv6
+ * 4 OSBuildNumber
+ * 5 OSName
+ * 6 OSMajorVersion
+ * 7 OSMinorVersion
+ * 8 OSVersion
+ * 9 ProcessorArchitecture
+ *
+ * The Windows host expects the Key Name and Key Value to be encoded in utf16.
+ *
+ * Guest Kernel/KVP Daemon Protocol: As noted earlier, we implement all of the
+ * data gathering functionality in a user mode daemon. The user level daemon
+ * is also responsible for binding the key name to the index as well. The
+ * kernel and user-level daemon communicate using a connector channel.
+ *
+ * The user mode component first registers with the
+ * the kernel component. Subsequently, the kernel component requests, data
+ * for the specified keys. In response to this message the user mode component
+ * fills in the value corresponding to the specified key. We overload the
+ * sequence field in the cn_msg header to define our KVP message types.
+ *
+ *
+ * The kernel component simply acts as a conduit for communication between the
+ * Windows host and the user-level daemon. The kernel component passes up the
+ * index received from the Host to the user-level daemon. If the index is
+ * valid (supported), the corresponding key as well as its
+ * value (both are strings) is returned. If the index is invalid
+ * (not supported), a NULL key string is returned.
+ */
+
+/*
+ *
+ * The following definitions are shared with the user-mode component; do not
+ * change any of this without making the corresponding changes in
+ * the KVP user-mode component.
+ */
+
+#define CN_KVP_VAL 0x1 /* This supports queries from the kernel */
+#define CN_KVP_USER_VAL 0x2 /* This supports queries from the user */
+
+enum hv_ku_op {
+ KVP_REGISTER = 0, /* Register the user mode component */
+ KVP_KERNEL_GET, /* Kernel is requesting the value */
+ KVP_KERNEL_SET, /* Kernel is providing the value */
+ KVP_USER_GET, /* User is requesting the value */
+ KVP_USER_SET /* User is providing the value */
+};
+
+struct hv_ku_msg {
+ __u32 kvp_index; /* Key index */
+ __u8 kvp_key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; /* Key name */
+ __u8 kvp_value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; /* Key value */
+};
+
+
+
+
+#ifdef __KERNEL__
+
+/*
+ * Registry value types.
+ */
+
+#define REG_SZ 1
+
+enum hv_kvp_exchg_op {
+ KVP_OP_GET = 0,
+ KVP_OP_SET,
+ KVP_OP_DELETE,
+ KVP_OP_ENUMERATE,
+ KVP_OP_COUNT /* Number of operations, must be last. */
+};
+
+enum hv_kvp_exchg_pool {
+ KVP_POOL_EXTERNAL = 0,
+ KVP_POOL_GUEST,
+ KVP_POOL_AUTO,
+ KVP_POOL_AUTO_EXTERNAL,
+ KVP_POOL_AUTO_INTERNAL,
+ KVP_POOL_COUNT /* Number of pools, must be last. */
+};
+
+struct hv_kvp_hdr {
+ u8 operation;
+ u8 pool;
+};
+
+struct hv_kvp_exchg_msg_value {
+ u32 value_type;
+ u32 key_size;
+ u32 value_size;
+ u8 key[HV_KVP_EXCHANGE_MAX_KEY_SIZE];
+ u8 value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE];
+};
+
+struct hv_kvp_msg_enumerate {
+ u32 index;
+ struct hv_kvp_exchg_msg_value data;
+};
+
+struct hv_kvp_msg {
+ struct hv_kvp_hdr kvp_hdr;
+ struct hv_kvp_msg_enumerate kvp_data;
+};
+
+int hv_kvp_init(void);
+void hv_kvp_deinit(void);
+void hv_kvp_onchannelcallback(void *);
+
+#endif /* __KERNEL__ */
+#endif /* _KVP_H */
+
Index: linux.trees.git/drivers/staging/hv/hv_util.c
===================================================================
--- linux.trees.git.orig/drivers/staging/hv/hv_util.c 2010-11-19 17:57:16.000000000 -0500
+++ linux.trees.git/drivers/staging/hv/hv_util.c 2010-11-19 17:57:17.000000000 -0500
@@ -37,6 +37,7 @@
#include "vmbus_private.h"
#include "vmbus_api.h"
#include "utils.h"
+#include "hv_kvp.h"
static void shutdown_onchannelcallback(void *context)
@@ -268,6 +269,10 @@ static int __init init_hyperv_utils(void
{
printk(KERN_INFO "Registering HyperV Utility Driver\n");
+ if (hv_kvp_init())
+ return -ENODEV;
+
+
if (!dmi_check_system(hv_utils_dmi_table))
return -ENODEV;
@@ -283,6 +288,11 @@ static int __init init_hyperv_utils(void
&heartbeat_onchannelcallback;
hv_cb_utils[HV_HEARTBEAT_MSG].callback = &heartbeat_onchannelcallback;
+ hv_cb_utils[HV_KVP_MSG].channel->OnChannelCallback =
+ &hv_kvp_onchannelcallback;
+
+
+
return 0;
}
@@ -301,6 +311,13 @@ static void exit_hyperv_utils(void)
hv_cb_utils[HV_HEARTBEAT_MSG].channel->OnChannelCallback =
&chn_cb_negotiate;
hv_cb_utils[HV_HEARTBEAT_MSG].callback = &chn_cb_negotiate;
+
+ hv_cb_utils[HV_KVP_MSG].channel->OnChannelCallback =
+ &chn_cb_negotiate;
+
+ hv_kvp_deinit();
+
+
}
module_init(init_hyperv_utils);
Index: linux.trees.git/drivers/staging/hv/channel_mgmt.c
===================================================================
--- linux.trees.git.orig/drivers/staging/hv/channel_mgmt.c 2010-11-19 17:30:48.000000000 -0500
+++ linux.trees.git/drivers/staging/hv/channel_mgmt.c 2010-11-19 18:00:40.000000000 -0500
@@ -34,8 +34,8 @@ struct vmbus_channel_message_table_entry
void (*messageHandler)(struct vmbus_channel_message_header *msg);
};
-#define MAX_MSG_TYPES 3
-#define MAX_NUM_DEVICE_CLASSES_SUPPORTED 7
+#define MAX_MSG_TYPES 4
+#define MAX_NUM_DEVICE_CLASSES_SUPPORTED 8
static const struct hv_guid
gSupportedDeviceClasses[MAX_NUM_DEVICE_CLASSES_SUPPORTED] = {
@@ -98,6 +98,15 @@ static const struct hv_guid
0xab, 0x55, 0x38, 0x2f, 0x3b, 0xd5, 0x42, 0x2d
}
},
+ /* {A9A0F4E7-5A45-4d96-B827-8A841E8C03E6} */
+ /* KVP */
+ {
+ .data = {
+ 0xe7, 0xf4, 0xa0, 0xa9, 0x45, 0x5a, 0x96, 0x4d,
+ 0xb8, 0x27, 0x8a, 0x84, 0x1e, 0x8c, 0x3, 0xe6
+ }
+ },
+
};
@@ -231,6 +240,16 @@ struct hyperv_service_callback hv_cb_uti
.callback = chn_cb_negotiate,
.log_msg = "Heartbeat channel functionality initialized"
},
+ /* {A9A0F4E7-5A45-4d96-B827-8A841E8C03E6} */
+ /* KVP */
+ {
+ .data = {
+ 0xe7, 0xf4, 0xa0, 0xa9, 0x45, 0x5a, 0x96, 0x4d,
+ 0xb8, 0x27, 0x8a, 0x84, 0x1e, 0x8c, 0x3, 0xe6
+ },
+ .callback = chn_cb_negotiate,
+ .log_msg = "KVP channel functionality initialized"
+ },
};
EXPORT_SYMBOL(hv_cb_utils);
[-- Attachment #3: kvp_daemon.c --]
[-- Type: application/octet-stream, Size: 11550 bytes --]
/*
* An implementation of key value pair (KVP) functionality for Linux.
*
*
* Copyright (C) 2010, Novell, Inc.
* Author : K. Y. Srinivasan <ksrinivasan@novell.com>
*
* 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, GOOD TITLE or
* NON INFRINGEMENT. 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 <sys/types.h>
#include <sys/socket.h>
#include <sys/poll.h>
#include <sys/utsname.h>
#include <asm/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>
#include <linux/connector.h>
#include <linux/netlink.h>
#include <sys/socket.h>
#include <ifaddrs.h>
#include <netdb.h>
#include <syslog.h>
/*
* KYS: TODO. Need to register these in the kernel.
*
* The following definitions are shared with the in-kernel component; do not
* change any of this without making the corresponding changes in
* the KVP kernel component.
*/
#define CN_KVP_IDX 0x9 /* MSFT KVP functionality */
#define CN_KVP_VAL 0x1 /* This supports queries from the kernel */
#define CN_KVP_USER_VAL 0x2 /* This supports queries from the user */
/*
* KVP protocol: The user mode component first registers with the
* the kernel component. Subsequently, the kernel component requests, data
* for the specified keys. In response to this message the user mode component
* fills in the value corresponding to the specified key. We overload the
* sequence field in the cn_msg header to define our KVP message types.
*
* We use this infrastructure for also supporting queries from user mode
* application for state that may be maintained in the KVP kernel component.
*
* XXXKYS: Have a shared header file between the user and kernel (TODO)
*/
enum kvp_op {
KVP_REGISTER = 0, /* Register the user mode component*/
KVP_KERNEL_GET, /*Kernel is requesting the value for the specified key*/
KVP_KERNEL_SET, /*Kernel is providing the value for the specified key*/
KVP_USER_GET, /*User is requesting the value for the specified key*/
KVP_USER_SET /*User is providing the value for the specified key*/
};
#define HV_KVP_EXCHANGE_MAX_KEY_SIZE 512
#define HV_KVP_EXCHANGE_MAX_VALUE_SIZE 2048
struct hv_ku_msg {
__u32 kvp_index;
__u8 kvp_key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; /* Key name */
__u8 kvp_value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; /* Key value */
};
enum key_index {
FullyQualifiedDomainName = 0,
IntegrationServicesVersion, /*This key is serviced in the kernel*/
NetworkAddressIPv4,
NetworkAddressIPv6,
OSBuildNumber,
OSName,
OSMajorVersion,
OSMinorVersion,
OSVersion,
ProcessorArchitecture
};
/*
* End of shared definitions.
*/
static char kvp_send_buffer[4096];
static char kvp_recv_buffer[4096];
static struct sockaddr_nl addr;
static char os_name[100];
static char os_major[50];
static char os_minor[50];
static char processor_arch[50];
static char os_build[100];
static char *lic_version;
void kvp_get_os_info(void)
{
FILE *file;
char *eol;
struct utsname buf;
uname(&buf);
strcpy(os_build, buf.release);
strcpy(processor_arch, buf.machine);
if ((file = fopen("/etc/SuSE-release", "r")) != NULL)
goto kvp_osinfo_found;
if ((file = fopen("/etc/SuSE-release", "r")) != NULL)
goto kvp_osinfo_found;
/*
* Add code for other supported platforms.
*/
/*
* We don't have information about the os.
*/
strcpy(os_name, "Linux");
strcpy(os_major, "0");
strcpy(os_minor, "0");
return;
kvp_osinfo_found:
fgets(os_name, 99, file);
eol = index(os_name, '\n');
*eol = '\0';
fgets(os_major, 49, file);
eol = index(os_major, '\n');
*eol = '\0';
fgets(os_minor, 49, file);
eol = index(os_minor, '\n');
*eol = '\0';
fclose(file);
return;
}
static int
kvp_get_ip_address(int family, char *buffer, int length)
{
struct ifaddrs *ifap;
struct ifaddrs *curp;
int ipv4_len = strlen("255.255.255.255") + 1;
int ipv6_len = strlen("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")+1;
int offset = 0;
const char *str;
char tmp[50];
int error = 0;
/*
* On entry into this function, the buffer is capable of holding the
* maximum key value (2048 bytes).
*/
if (getifaddrs(&ifap)) {
strcpy(buffer, "getifaddrs failed\n");
return 1;
}
curp = ifap;
while (curp != NULL) {
if ((curp->ifa_addr != NULL) &&
(curp->ifa_addr->sa_family == family)) {
if (family == AF_INET) {
struct sockaddr_in *addr =
(struct sockaddr_in *) curp->ifa_addr;
if (!(str = inet_ntop(family, &addr->sin_addr,
tmp, 50))) {
strcpy(buffer, "inet_ntop failed\n");
error = 1;
goto getaddr_done;
}
if (offset == 0)
strcpy(buffer, tmp);
else
strcat(buffer, tmp);
strcat(buffer, ";");
offset += strlen(str) +1;
if ((length - offset) < (ipv4_len + 1)) {
goto getaddr_done;
}
} else {
/*
* We only support AF_INET and AF_INET6
* and the list of addresses is seperated by a ";".
*/
struct sockaddr_in6 *addr =
(struct sockaddr_in6 *) curp->ifa_addr;
if (!(str = inet_ntop(family,
&addr->sin6_addr.s6_addr,
tmp, 50))) {
strcpy(buffer, "inet_ntop failed\n");
error = 1;
goto getaddr_done;
}
if (offset == 0)
strcpy(buffer, tmp);
else
strcat(buffer, tmp);
strcat(buffer, ";");
offset += strlen(str) +1;
if ((length - offset) < (ipv6_len +1)) {
goto getaddr_done;
}
}
}
curp = curp->ifa_next;
}
getaddr_done:
freeifaddrs(ifap);
return error;
}
static int
kvp_get_domain_name(char *buffer, int length)
{
struct addrinfo hints, *info ;
gethostname(buffer, length);
int error =0;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET; /*Get only ipv4 addrinfo. */
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_CANONNAME;
if ((error = getaddrinfo(buffer, "http", &hints, &info)) !=0) {
strcpy(buffer, "getaddrinfo failed\n");
error = 1;
goto get_domain_done;
}
strcpy(buffer, info->ai_canonname);
get_domain_done:
freeaddrinfo(info);
return (error);
}
static int
netlink_send(int fd, struct cn_msg *msg)
{
struct nlmsghdr *nlh;
unsigned int size;
struct msghdr message;
char buffer[64];
struct iovec iov[2];
size = NLMSG_SPACE(sizeof(struct cn_msg) + msg->len);
nlh = (struct nlmsghdr *)buffer;
nlh->nlmsg_seq = 0;
nlh->nlmsg_pid = getpid();
nlh->nlmsg_type = NLMSG_DONE;
nlh->nlmsg_len = NLMSG_LENGTH(size - sizeof(*nlh));
nlh->nlmsg_flags = 0;
iov[0].iov_base = nlh;
iov[0].iov_len = sizeof(*nlh);
iov[1].iov_base = msg;
iov[1].iov_len = size;
memset(&message, 0, sizeof(message));
message.msg_name = &addr;
message.msg_namelen = sizeof(addr);
message.msg_iov = iov;
message.msg_iovlen = 2;
return (sendmsg(fd, &message, 0));
}
main(void)
{
int fd, len, sock_opt;
int error;
struct cn_msg *message;
struct pollfd pfd;
struct nlmsghdr *incoming_msg;
struct cn_msg *incoming_cn_msg;
char *key_value;
char *key_name;
int key_index;
daemon(1, 0);
openlog("KVP", 0, LOG_USER);
syslog(LOG_INFO, "KVP starting; pid is:%d", getpid());
/*
* Retrieve OS release information.
*/
kvp_get_os_info();
fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
if (fd < 0) {
syslog(LOG_ERR, "netlink socket creation failed; error:%d",fd);
exit -1;
}
addr.nl_family = AF_NETLINK;
addr.nl_pad = 0;
addr.nl_pid = 0;
addr.nl_groups = CN_KVP_IDX;
if ((error = bind(fd, (struct sockaddr *)&addr, sizeof(addr))) < 0) {
syslog(LOG_ERR, "bind failed; error:%d",error);
close(fd);
exit -1;
}
sock_opt = addr.nl_groups;
setsockopt(fd, 270, 1, &sock_opt, sizeof(sock_opt));
/*
* Register ourselves with the kernel.
*/
message = (struct cn_msg *)kvp_send_buffer;
message->id.idx = CN_KVP_IDX;
message->id.val = CN_KVP_VAL;
message->seq = KVP_REGISTER;
message->ack = 0;
message->len = 0;
len = netlink_send(fd, message);
if (len < 0) {
syslog(LOG_ERR, "netlink_send failed; error:%d", len);
close(fd);
exit -1;
}
pfd.fd = fd;
while (1) {
pfd.events = POLLIN;
pfd.revents = 0;
poll(&pfd, 1, -1);
len = recv(fd, kvp_recv_buffer, sizeof(kvp_recv_buffer), 0);
if (len < 0) {
syslog(LOG_ERR, "recv failed; error:%d", len);
close(fd);
return -1;
}
incoming_msg = (struct nlmsghdr *)kvp_recv_buffer;
incoming_cn_msg = (struct cn_msg *)NLMSG_DATA(incoming_msg);
switch (incoming_cn_msg->seq) {
case KVP_REGISTER:
/*
* Driver is registering with us; stash away the version
* information.
*/
lic_version = malloc(strlen(incoming_cn_msg->data) + 1);
if (lic_version) {
strcpy(lic_version, incoming_cn_msg->data);
syslog(LOG_INFO, "KVP LIC Version: %s", lic_version);
} else {
syslog(LOG_ERR, "malloc failed");
}
continue;
case KVP_KERNEL_GET:
break;
default:
continue;
}
key_index =
((struct hv_ku_msg *)incoming_cn_msg->data)->kvp_index;
key_name =
((struct hv_ku_msg *)incoming_cn_msg->data)->kvp_key;
key_value =
((struct hv_ku_msg *)incoming_cn_msg->data)->kvp_value;
switch (key_index) {
case FullyQualifiedDomainName:
kvp_get_domain_name(key_value,
HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
strcpy(key_name, "FullyQualifiedDomainName");
break;
case IntegrationServicesVersion:
strcpy(key_name, "IntegrationServicesVersion");
strcpy(key_value, lic_version);
break;
case NetworkAddressIPv4:
kvp_get_ip_address(AF_INET, key_value,
HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
strcpy(key_name, "NetworkAddressIPv4");
break;
case NetworkAddressIPv6:
kvp_get_ip_address(AF_INET6, key_value,
HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
strcpy(key_name, "NetworkAddressIPv6");
break;
case OSBuildNumber:
strcpy(key_value, os_build);
strcpy(key_name, "OSBuildNumber");
break;
case OSName:
strcpy(key_value, os_name);
strcpy(key_name, "OSName");
break;
case OSMajorVersion:
strcpy(key_value, os_major);
strcpy(key_name, "OSMajorVersion");
break;
case OSMinorVersion:
strcpy(key_value, os_minor);
strcpy(key_name, "OSMinorVersion");
break;
case OSVersion:
strcpy(key_value, os_build);
strcpy(key_name, "OSVersion");
break;
case ProcessorArchitecture:
strcpy(key_value, processor_arch);
strcpy(key_name, "ProcessorArchitecture");
break;
default:
strcpy(key_value, "Unknown Key");
/*
* We use a null key name to terminate enumeration.
*/
strcpy(key_name, "");
break;
}
/*
* Send the value back to the kernel. The response is
* already in the receive buffer. Update the cn_msg header to
* reflect the key value that has been added to the message
*/
incoming_cn_msg->id.idx = CN_KVP_IDX;
incoming_cn_msg->id.val = CN_KVP_VAL;
incoming_cn_msg->seq = KVP_USER_SET;
incoming_cn_msg->ack = 0;
incoming_cn_msg->len = sizeof(struct hv_ku_msg);
len = netlink_send(fd, incoming_cn_msg);
if (len < 0) {
syslog(LOG_ERR, "net_link send failed; error:%d", len);
exit -1;
}
}
}
[-- Attachment #4: Type: text/plain, Size: 159 bytes --]
_______________________________________________
devel mailing list
devel@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/devel
^ permalink raw reply
* [PATCH 2/3]: An Implementation of HyperV KVP functionality
From: Ky Srinivasan @ 2010-11-22 20:27 UTC (permalink / raw)
To: devel, Virtualization; +Cc: Zbr, Haiyang Zhang, Stephen Hemminger, Greg KH
[-- Attachment #1: Type: text/plain, Size: 198 bytes --]
The hv_utils module will be composed of more than one file;
rename hv_utils.c to accommodate this without changing the module name.
Signed-off-by: K. Y. Srinivasan <ksrinivasan@novell.com>
[-- Attachment #2: hv_util_cleanup.patch --]
[-- Type: text/plain, Size: 18894 bytes --]
From: K. Y. Srinivasan <ksrinivasan@novell.com>
Subject: The hv_utils module will be composed of more than one file;
rename hv_utils.c to accommodate this without changing the module name.
Signed-off-by: K. Y. Srinivasan <ksrinivasan@novell.com>
Index: linux.trees.git/drivers/staging/hv/hv_util.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux.trees.git/drivers/staging/hv/hv_util.c 2010-11-19 17:30:48.000000000 -0500
@@ -0,0 +1,311 @@
+/*
+ * Copyright (c) 2010, Microsoft Corporation.
+ *
+ * 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., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * Authors:
+ * Haiyang Zhang <haiyangz@microsoft.com>
+ * Hank Janssen <hjanssen@microsoft.com>
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/sysctl.h>
+#include <linux/reboot.h>
+#include <linux/dmi.h>
+#include <linux/pci.h>
+
+#include "logging.h"
+#include "osd.h"
+#include "vmbus.h"
+#include "vmbus_packet_format.h"
+#include "vmbus_channel_interface.h"
+#include "version_info.h"
+#include "channel.h"
+#include "vmbus_private.h"
+#include "vmbus_api.h"
+#include "utils.h"
+
+
+static void shutdown_onchannelcallback(void *context)
+{
+ struct vmbus_channel *channel = context;
+ u8 *buf;
+ u32 buflen, recvlen;
+ u64 requestid;
+ u8 execute_shutdown = false;
+
+ struct shutdown_msg_data *shutdown_msg;
+
+ struct icmsg_hdr *icmsghdrp;
+ struct icmsg_negotiate *negop = NULL;
+
+ buflen = PAGE_SIZE;
+ buf = kmalloc(buflen, GFP_ATOMIC);
+
+ vmbus_recvpacket(channel, buf, buflen, &recvlen, &requestid);
+
+ if (recvlen > 0) {
+ DPRINT_DBG(VMBUS, "shutdown packet: len=%d, requestid=%lld",
+ recvlen, requestid);
+
+ icmsghdrp = (struct icmsg_hdr *)&buf[
+ sizeof(struct vmbuspipe_hdr)];
+
+ if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
+ prep_negotiate_resp(icmsghdrp, negop, buf);
+ } else {
+ shutdown_msg = (struct shutdown_msg_data *)&buf[
+ sizeof(struct vmbuspipe_hdr) +
+ sizeof(struct icmsg_hdr)];
+
+ switch (shutdown_msg->flags) {
+ case 0:
+ case 1:
+ icmsghdrp->status = HV_S_OK;
+ execute_shutdown = true;
+
+ DPRINT_INFO(VMBUS, "Shutdown request received -"
+ " gracefull shutdown initiated");
+ break;
+ default:
+ icmsghdrp->status = HV_E_FAIL;
+ execute_shutdown = false;
+
+ DPRINT_INFO(VMBUS, "Shutdown request received -"
+ " Invalid request");
+ break;
+ };
+ }
+
+ icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
+ | ICMSGHDRFLAG_RESPONSE;
+
+ vmbus_sendpacket(channel, buf,
+ recvlen, requestid,
+ VmbusPacketTypeDataInBand, 0);
+ }
+
+ kfree(buf);
+
+ if (execute_shutdown == true)
+ orderly_poweroff(false);
+}
+
+/*
+ * Set guest time to host UTC time.
+ */
+static inline void do_adj_guesttime(u64 hosttime)
+{
+ s64 host_tns;
+ struct timespec host_ts;
+
+ host_tns = (hosttime - WLTIMEDELTA) * 100;
+ host_ts = ns_to_timespec(host_tns);
+
+ do_settimeofday(&host_ts);
+}
+
+/*
+ * Synchronize time with host after reboot, restore, etc.
+ *
+ * ICTIMESYNCFLAG_SYNC flag bit indicates reboot, restore events of the VM.
+ * After reboot the flag ICTIMESYNCFLAG_SYNC is included in the first time
+ * message after the timesync channel is opened. Since the hv_utils module is
+ * loaded after hv_vmbus, the first message is usually missed. The other
+ * thing is, systime is automatically set to emulated hardware clock which may
+ * not be UTC time or in the same time zone. So, to override these effects, we
+ * use the first 50 time samples for initial system time setting.
+ */
+static inline void adj_guesttime(u64 hosttime, u8 flags)
+{
+ static s32 scnt = 50;
+
+ if ((flags & ICTIMESYNCFLAG_SYNC) != 0) {
+ do_adj_guesttime(hosttime);
+ return;
+ }
+
+ if ((flags & ICTIMESYNCFLAG_SAMPLE) != 0 && scnt > 0) {
+ scnt--;
+ do_adj_guesttime(hosttime);
+ }
+}
+
+/*
+ * Time Sync Channel message handler.
+ */
+static void timesync_onchannelcallback(void *context)
+{
+ struct vmbus_channel *channel = context;
+ u8 *buf;
+ u32 buflen, recvlen;
+ u64 requestid;
+ struct icmsg_hdr *icmsghdrp;
+ struct ictimesync_data *timedatap;
+
+ buflen = PAGE_SIZE;
+ buf = kmalloc(buflen, GFP_ATOMIC);
+
+ vmbus_recvpacket(channel, buf, buflen, &recvlen, &requestid);
+
+ if (recvlen > 0) {
+ DPRINT_DBG(VMBUS, "timesync packet: recvlen=%d, requestid=%lld",
+ recvlen, requestid);
+
+ icmsghdrp = (struct icmsg_hdr *)&buf[
+ sizeof(struct vmbuspipe_hdr)];
+
+ if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
+ prep_negotiate_resp(icmsghdrp, NULL, buf);
+ } else {
+ timedatap = (struct ictimesync_data *)&buf[
+ sizeof(struct vmbuspipe_hdr) +
+ sizeof(struct icmsg_hdr)];
+ adj_guesttime(timedatap->parenttime, timedatap->flags);
+ }
+
+ icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
+ | ICMSGHDRFLAG_RESPONSE;
+
+ vmbus_sendpacket(channel, buf,
+ recvlen, requestid,
+ VmbusPacketTypeDataInBand, 0);
+ }
+
+ kfree(buf);
+}
+
+/*
+ * Heartbeat functionality.
+ * Every two seconds, Hyper-V send us a heartbeat request message.
+ * we respond to this message, and Hyper-V knows we are alive.
+ */
+static void heartbeat_onchannelcallback(void *context)
+{
+ struct vmbus_channel *channel = context;
+ u8 *buf;
+ u32 buflen, recvlen;
+ u64 requestid;
+ struct icmsg_hdr *icmsghdrp;
+ struct heartbeat_msg_data *heartbeat_msg;
+
+ buflen = PAGE_SIZE;
+ buf = kmalloc(buflen, GFP_ATOMIC);
+
+ vmbus_recvpacket(channel, buf, buflen, &recvlen, &requestid);
+
+ if (recvlen > 0) {
+ DPRINT_DBG(VMBUS, "heartbeat packet: len=%d, requestid=%lld",
+ recvlen, requestid);
+
+ icmsghdrp = (struct icmsg_hdr *)&buf[
+ sizeof(struct vmbuspipe_hdr)];
+
+ icmsghdrp = (struct icmsg_hdr *)&buf[
+ sizeof(struct vmbuspipe_hdr)];
+
+ if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
+ prep_negotiate_resp(icmsghdrp, NULL, buf);
+ } else {
+ heartbeat_msg = (struct heartbeat_msg_data *)&buf[
+ sizeof(struct vmbuspipe_hdr) +
+ sizeof(struct icmsg_hdr)];
+
+ DPRINT_DBG(VMBUS, "heartbeat seq = %lld",
+ heartbeat_msg->seq_num);
+
+ heartbeat_msg->seq_num += 1;
+ }
+
+ icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
+ | ICMSGHDRFLAG_RESPONSE;
+
+ vmbus_sendpacket(channel, buf,
+ recvlen, requestid,
+ VmbusPacketTypeDataInBand, 0);
+ }
+
+ kfree(buf);
+}
+
+static const struct pci_device_id __initconst
+hv_utils_pci_table[] __maybe_unused = {
+ { PCI_DEVICE(0x1414, 0x5353) }, /* Hyper-V emulated VGA controller */
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, hv_utils_pci_table);
+
+
+static const struct dmi_system_id __initconst
+hv_utils_dmi_table[] __maybe_unused = {
+ {
+ .ident = "Hyper-V",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Virtual Machine"),
+ DMI_MATCH(DMI_BOARD_NAME, "Virtual Machine"),
+ },
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(dmi, hv_utils_dmi_table);
+
+
+static int __init init_hyperv_utils(void)
+{
+ printk(KERN_INFO "Registering HyperV Utility Driver\n");
+
+ if (!dmi_check_system(hv_utils_dmi_table))
+ return -ENODEV;
+
+ hv_cb_utils[HV_SHUTDOWN_MSG].channel->OnChannelCallback =
+ &shutdown_onchannelcallback;
+ hv_cb_utils[HV_SHUTDOWN_MSG].callback = &shutdown_onchannelcallback;
+
+ hv_cb_utils[HV_TIMESYNC_MSG].channel->OnChannelCallback =
+ ×ync_onchannelcallback;
+ hv_cb_utils[HV_TIMESYNC_MSG].callback = ×ync_onchannelcallback;
+
+ hv_cb_utils[HV_HEARTBEAT_MSG].channel->OnChannelCallback =
+ &heartbeat_onchannelcallback;
+ hv_cb_utils[HV_HEARTBEAT_MSG].callback = &heartbeat_onchannelcallback;
+
+ return 0;
+}
+
+static void exit_hyperv_utils(void)
+{
+ printk(KERN_INFO "De-Registered HyperV Utility Driver\n");
+
+ hv_cb_utils[HV_SHUTDOWN_MSG].channel->OnChannelCallback =
+ &chn_cb_negotiate;
+ hv_cb_utils[HV_SHUTDOWN_MSG].callback = &chn_cb_negotiate;
+
+ hv_cb_utils[HV_TIMESYNC_MSG].channel->OnChannelCallback =
+ &chn_cb_negotiate;
+ hv_cb_utils[HV_TIMESYNC_MSG].callback = &chn_cb_negotiate;
+
+ hv_cb_utils[HV_HEARTBEAT_MSG].channel->OnChannelCallback =
+ &chn_cb_negotiate;
+ hv_cb_utils[HV_HEARTBEAT_MSG].callback = &chn_cb_negotiate;
+}
+
+module_init(init_hyperv_utils);
+module_exit(exit_hyperv_utils);
+
+MODULE_DESCRIPTION("Hyper-V Utilities");
+MODULE_VERSION(HV_DRV_VERSION);
+MODULE_LICENSE("GPL");
Index: linux.trees.git/drivers/staging/hv/hv_utils.c
===================================================================
--- linux.trees.git.orig/drivers/staging/hv/hv_utils.c 2010-11-15 08:24:46.000000000 -0500
+++ /dev/null 1970-01-01 00:00:00.000000000 +0000
@@ -1,311 +0,0 @@
-/*
- * Copyright (c) 2010, Microsoft Corporation.
- *
- * 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., 59 Temple
- * Place - Suite 330, Boston, MA 02111-1307 USA.
- *
- * Authors:
- * Haiyang Zhang <haiyangz@microsoft.com>
- * Hank Janssen <hjanssen@microsoft.com>
- */
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/sysctl.h>
-#include <linux/reboot.h>
-#include <linux/dmi.h>
-#include <linux/pci.h>
-
-#include "logging.h"
-#include "osd.h"
-#include "vmbus.h"
-#include "vmbus_packet_format.h"
-#include "vmbus_channel_interface.h"
-#include "version_info.h"
-#include "channel.h"
-#include "vmbus_private.h"
-#include "vmbus_api.h"
-#include "utils.h"
-
-
-static void shutdown_onchannelcallback(void *context)
-{
- struct vmbus_channel *channel = context;
- u8 *buf;
- u32 buflen, recvlen;
- u64 requestid;
- u8 execute_shutdown = false;
-
- struct shutdown_msg_data *shutdown_msg;
-
- struct icmsg_hdr *icmsghdrp;
- struct icmsg_negotiate *negop = NULL;
-
- buflen = PAGE_SIZE;
- buf = kmalloc(buflen, GFP_ATOMIC);
-
- vmbus_recvpacket(channel, buf, buflen, &recvlen, &requestid);
-
- if (recvlen > 0) {
- DPRINT_DBG(VMBUS, "shutdown packet: len=%d, requestid=%lld",
- recvlen, requestid);
-
- icmsghdrp = (struct icmsg_hdr *)&buf[
- sizeof(struct vmbuspipe_hdr)];
-
- if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
- prep_negotiate_resp(icmsghdrp, negop, buf);
- } else {
- shutdown_msg = (struct shutdown_msg_data *)&buf[
- sizeof(struct vmbuspipe_hdr) +
- sizeof(struct icmsg_hdr)];
-
- switch (shutdown_msg->flags) {
- case 0:
- case 1:
- icmsghdrp->status = HV_S_OK;
- execute_shutdown = true;
-
- DPRINT_INFO(VMBUS, "Shutdown request received -"
- " gracefull shutdown initiated");
- break;
- default:
- icmsghdrp->status = HV_E_FAIL;
- execute_shutdown = false;
-
- DPRINT_INFO(VMBUS, "Shutdown request received -"
- " Invalid request");
- break;
- };
- }
-
- icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
- | ICMSGHDRFLAG_RESPONSE;
-
- vmbus_sendpacket(channel, buf,
- recvlen, requestid,
- VmbusPacketTypeDataInBand, 0);
- }
-
- kfree(buf);
-
- if (execute_shutdown == true)
- orderly_poweroff(false);
-}
-
-/*
- * Set guest time to host UTC time.
- */
-static inline void do_adj_guesttime(u64 hosttime)
-{
- s64 host_tns;
- struct timespec host_ts;
-
- host_tns = (hosttime - WLTIMEDELTA) * 100;
- host_ts = ns_to_timespec(host_tns);
-
- do_settimeofday(&host_ts);
-}
-
-/*
- * Synchronize time with host after reboot, restore, etc.
- *
- * ICTIMESYNCFLAG_SYNC flag bit indicates reboot, restore events of the VM.
- * After reboot the flag ICTIMESYNCFLAG_SYNC is included in the first time
- * message after the timesync channel is opened. Since the hv_utils module is
- * loaded after hv_vmbus, the first message is usually missed. The other
- * thing is, systime is automatically set to emulated hardware clock which may
- * not be UTC time or in the same time zone. So, to override these effects, we
- * use the first 50 time samples for initial system time setting.
- */
-static inline void adj_guesttime(u64 hosttime, u8 flags)
-{
- static s32 scnt = 50;
-
- if ((flags & ICTIMESYNCFLAG_SYNC) != 0) {
- do_adj_guesttime(hosttime);
- return;
- }
-
- if ((flags & ICTIMESYNCFLAG_SAMPLE) != 0 && scnt > 0) {
- scnt--;
- do_adj_guesttime(hosttime);
- }
-}
-
-/*
- * Time Sync Channel message handler.
- */
-static void timesync_onchannelcallback(void *context)
-{
- struct vmbus_channel *channel = context;
- u8 *buf;
- u32 buflen, recvlen;
- u64 requestid;
- struct icmsg_hdr *icmsghdrp;
- struct ictimesync_data *timedatap;
-
- buflen = PAGE_SIZE;
- buf = kmalloc(buflen, GFP_ATOMIC);
-
- vmbus_recvpacket(channel, buf, buflen, &recvlen, &requestid);
-
- if (recvlen > 0) {
- DPRINT_DBG(VMBUS, "timesync packet: recvlen=%d, requestid=%lld",
- recvlen, requestid);
-
- icmsghdrp = (struct icmsg_hdr *)&buf[
- sizeof(struct vmbuspipe_hdr)];
-
- if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
- prep_negotiate_resp(icmsghdrp, NULL, buf);
- } else {
- timedatap = (struct ictimesync_data *)&buf[
- sizeof(struct vmbuspipe_hdr) +
- sizeof(struct icmsg_hdr)];
- adj_guesttime(timedatap->parenttime, timedatap->flags);
- }
-
- icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
- | ICMSGHDRFLAG_RESPONSE;
-
- vmbus_sendpacket(channel, buf,
- recvlen, requestid,
- VmbusPacketTypeDataInBand, 0);
- }
-
- kfree(buf);
-}
-
-/*
- * Heartbeat functionality.
- * Every two seconds, Hyper-V send us a heartbeat request message.
- * we respond to this message, and Hyper-V knows we are alive.
- */
-static void heartbeat_onchannelcallback(void *context)
-{
- struct vmbus_channel *channel = context;
- u8 *buf;
- u32 buflen, recvlen;
- u64 requestid;
- struct icmsg_hdr *icmsghdrp;
- struct heartbeat_msg_data *heartbeat_msg;
-
- buflen = PAGE_SIZE;
- buf = kmalloc(buflen, GFP_ATOMIC);
-
- vmbus_recvpacket(channel, buf, buflen, &recvlen, &requestid);
-
- if (recvlen > 0) {
- DPRINT_DBG(VMBUS, "heartbeat packet: len=%d, requestid=%lld",
- recvlen, requestid);
-
- icmsghdrp = (struct icmsg_hdr *)&buf[
- sizeof(struct vmbuspipe_hdr)];
-
- icmsghdrp = (struct icmsg_hdr *)&buf[
- sizeof(struct vmbuspipe_hdr)];
-
- if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
- prep_negotiate_resp(icmsghdrp, NULL, buf);
- } else {
- heartbeat_msg = (struct heartbeat_msg_data *)&buf[
- sizeof(struct vmbuspipe_hdr) +
- sizeof(struct icmsg_hdr)];
-
- DPRINT_DBG(VMBUS, "heartbeat seq = %lld",
- heartbeat_msg->seq_num);
-
- heartbeat_msg->seq_num += 1;
- }
-
- icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
- | ICMSGHDRFLAG_RESPONSE;
-
- vmbus_sendpacket(channel, buf,
- recvlen, requestid,
- VmbusPacketTypeDataInBand, 0);
- }
-
- kfree(buf);
-}
-
-static const struct pci_device_id __initconst
-hv_utils_pci_table[] __maybe_unused = {
- { PCI_DEVICE(0x1414, 0x5353) }, /* Hyper-V emulated VGA controller */
- { 0 }
-};
-MODULE_DEVICE_TABLE(pci, hv_utils_pci_table);
-
-
-static const struct dmi_system_id __initconst
-hv_utils_dmi_table[] __maybe_unused = {
- {
- .ident = "Hyper-V",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
- DMI_MATCH(DMI_PRODUCT_NAME, "Virtual Machine"),
- DMI_MATCH(DMI_BOARD_NAME, "Virtual Machine"),
- },
- },
- { },
-};
-MODULE_DEVICE_TABLE(dmi, hv_utils_dmi_table);
-
-
-static int __init init_hyperv_utils(void)
-{
- printk(KERN_INFO "Registering HyperV Utility Driver\n");
-
- if (!dmi_check_system(hv_utils_dmi_table))
- return -ENODEV;
-
- hv_cb_utils[HV_SHUTDOWN_MSG].channel->OnChannelCallback =
- &shutdown_onchannelcallback;
- hv_cb_utils[HV_SHUTDOWN_MSG].callback = &shutdown_onchannelcallback;
-
- hv_cb_utils[HV_TIMESYNC_MSG].channel->OnChannelCallback =
- ×ync_onchannelcallback;
- hv_cb_utils[HV_TIMESYNC_MSG].callback = ×ync_onchannelcallback;
-
- hv_cb_utils[HV_HEARTBEAT_MSG].channel->OnChannelCallback =
- &heartbeat_onchannelcallback;
- hv_cb_utils[HV_HEARTBEAT_MSG].callback = &heartbeat_onchannelcallback;
-
- return 0;
-}
-
-static void exit_hyperv_utils(void)
-{
- printk(KERN_INFO "De-Registered HyperV Utility Driver\n");
-
- hv_cb_utils[HV_SHUTDOWN_MSG].channel->OnChannelCallback =
- &chn_cb_negotiate;
- hv_cb_utils[HV_SHUTDOWN_MSG].callback = &chn_cb_negotiate;
-
- hv_cb_utils[HV_TIMESYNC_MSG].channel->OnChannelCallback =
- &chn_cb_negotiate;
- hv_cb_utils[HV_TIMESYNC_MSG].callback = &chn_cb_negotiate;
-
- hv_cb_utils[HV_HEARTBEAT_MSG].channel->OnChannelCallback =
- &chn_cb_negotiate;
- hv_cb_utils[HV_HEARTBEAT_MSG].callback = &chn_cb_negotiate;
-}
-
-module_init(init_hyperv_utils);
-module_exit(exit_hyperv_utils);
-
-MODULE_DESCRIPTION("Hyper-V Utilities");
-MODULE_VERSION(HV_DRV_VERSION);
-MODULE_LICENSE("GPL");
Index: linux.trees.git/drivers/staging/hv/Makefile
===================================================================
--- linux.trees.git.orig/drivers/staging/hv/Makefile 2010-11-15 08:24:46.000000000 -0500
+++ linux.trees.git/drivers/staging/hv/Makefile 2010-11-19 17:30:48.000000000 -0500
@@ -10,3 +10,4 @@ hv_vmbus-y := vmbus_drv.o osd.o \
hv_storvsc-y := storvsc_drv.o storvsc.o
hv_blkvsc-y := blkvsc_drv.o blkvsc.o
hv_netvsc-y := netvsc_drv.o netvsc.o rndis_filter.o
+hv_utils-y := hv_util.o
[-- Attachment #3: Type: text/plain, Size: 159 bytes --]
_______________________________________________
devel mailing list
devel@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/devel
^ permalink raw reply
* [PATCH 1/3]: An implementation of HyperV KVP functionality
From: Ky Srinivasan @ 2010-11-22 20:21 UTC (permalink / raw)
To: devel, Virtualization; +Cc: Zbr, Haiyang Zhang, Stephen Hemminger, Greg KH
From: K. Y. Srinivasan <ksrinivasan@novell.com>
Subject: Reserve a connector index for implementing HyperV Key Value Pair
(KVP) functionality.
Signed-off-by: K. Y. Srinivasan <ksrinivasan@novell.com>
Index: linux.trees.git/include/linux/connector.h
===================================================================
--- linux.trees.git.orig/include/linux/connector.h 2010-11-15 06:50:30.000000000 -0500
+++ linux.trees.git/include/linux/connector.h 2010-11-15 07:00:52.000000000 -0500
@@ -42,8 +42,9 @@
#define CN_VAL_DM_USERSPACE_LOG 0x1
#define CN_IDX_DRBD 0x8
#define CN_VAL_DRBD 0x1
+#define CN_KVP_IDX 0x9 /* HyperV KVP */
-#define CN_NETLINK_USERS 8
+#define CN_NETLINK_USERS 9
/*
* Maximum connector's message size.
^ permalink raw reply
* Re: [PATCH] x86/pvclock-xen: zero last_value on resume
From: Jeremy Fitzhardinge @ 2010-11-22 20:16 UTC (permalink / raw)
To: H. Peter Anvin
Cc: Xen-devel@lists.xensource.com, kvm-devel, Glauber Costa,
the arch/x86 maintainers, Linux Kernel Mailing List,
Linux Virtualization, Olivier Hanesse, Avi Kivity, Eelco Dolstra
In-Reply-To: <4CC708DE.1070000@goop.org>
On 10/26/2010 09:59 AM, Jeremy Fitzhardinge wrote:
>
> If the guest domain has been suspend/resumed or migrated, then the
> system clock backing the pvclock clocksource may revert to a smaller
> value (ie, can be non-monotonic across the migration/save-restore).
> Make sure we zero last_value in that case so that the domain
> continues to see clock updates.
Ping? Looks like this fell through the gaps.
J
> [ I don't know if kvm needs an analogous fix or not. ]
>
> Signed-off-by: Jeremy Fitzhardinge <jeremy.fitzhardinge@citrix.com>
> Cc: Stable Kernel <stable@kernel.org>
> Reported-by: Eelco Dolstra <e.dolstra@tudelft.nl>
> Reported-by: Olivier Hanesse <olivier.hanesse@gmail.com>
> Bisected-by: Cédric Schieli <cschieli@gmail.com>
> Tested-by: Cédric Schieli <cschieli@gmail.com>
>
> diff --git a/arch/x86/include/asm/pvclock.h b/arch/x86/include/asm/pvclock.h
> index cd02f32..6226870 100644
> --- a/arch/x86/include/asm/pvclock.h
> +++ b/arch/x86/include/asm/pvclock.h
> @@ -11,5 +11,6 @@ unsigned long pvclock_tsc_khz(struct pvclock_vcpu_time_info *src);
> void pvclock_read_wallclock(struct pvclock_wall_clock *wall,
> struct pvclock_vcpu_time_info *vcpu,
> struct timespec *ts);
> +void pvclock_resume(void);
>
> #endif /* _ASM_X86_PVCLOCK_H */
> diff --git a/arch/x86/kernel/pvclock.c b/arch/x86/kernel/pvclock.c
> index 239427c..a4f07c1 100644
> --- a/arch/x86/kernel/pvclock.c
> +++ b/arch/x86/kernel/pvclock.c
> @@ -120,6 +120,11 @@ unsigned long pvclock_tsc_khz(struct pvclock_vcpu_time_info *src)
>
> static atomic64_t last_value = ATOMIC64_INIT(0);
>
> +void pvclock_resume(void)
> +{
> + atomic64_set(&last_value, 0);
> +}
> +
> cycle_t pvclock_clocksource_read(struct pvclock_vcpu_time_info *src)
> {
> struct pvclock_shadow_time shadow;
> diff --git a/arch/x86/xen/time.c b/arch/x86/xen/time.c
> index b2bb5aa..5da5e53 100644
> --- a/arch/x86/xen/time.c
> +++ b/arch/x86/xen/time.c
> @@ -426,6 +426,8 @@ void xen_timer_resume(void)
> {
> int cpu;
>
> + pvclock_resume();
> +
> if (xen_clockevent != &xen_vcpuop_clockevent)
> return;
>
>
>
>
>
> _______________________________________________
> Virtualization mailing list
> Virtualization@lists.linux-foundation.org
> https://lists.linux-foundation.org/mailman/listinfo/virtualization
_______________________________________________
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/virtualization
^ permalink raw reply
* [PATCH 10/10] implement VDP keepalive
From: Jens Osterkamp @ 2010-11-22 15:50 UTC (permalink / raw)
To: e1000-eedc, virtualization, evb; +Cc: chrisw, Jens Osterkamp
In-Reply-To: <1290441057-16729-1-git-send-email-jens@linux.vnet.ibm.com>
Once a VSI is associated with the adjacent switch, it needs to be renewed in
the switch from time to time. This patch implements the renewal of the VSI.
As a time base it uses the RTE (ReTransmission Exponent) exchanged in the
EVB TLV.
Signed-off-by: Jens Osterkamp <jens@linux.vnet.ibm.com>
---
ecp/ecp.h | 2 +-
ecp/ecp_tx.c | 8 ++-
include/lldp_evb.h | 4 +-
include/lldp_vdp.h | 3 +-
lldp_evb.c | 7 +++
lldp_vdp.c | 109 +++++++++++++++++++++++++++++++++++++++++++++-------
6 files changed, 112 insertions(+), 21 deletions(-)
diff --git a/ecp/ecp.h b/ecp/ecp.h
index cc9ca2b..89c1154 100644
--- a/ecp/ecp.h
+++ b/ecp/ecp.h
@@ -34,7 +34,7 @@
#define ECP_MAX_RETRIES 3
#define ECP_SEQUENCE_NR_START 0x0
-#define ECP_TRANSMISSION_TIMER EVB_RTM(RTE)*EVB_RTG
+#define ECP_TRANSMISSION_TIMER(rte) EVB_RTM(rte)*EVB_RTG
#define ECP_TRANSMISSION_DIVIDER 10000
typedef enum {
diff --git a/ecp/ecp_tx.c b/ecp/ecp_tx.c
index 1db9ac3..6518a79 100644
--- a/ecp/ecp_tx.c
+++ b/ecp/ecp_tx.c
@@ -316,12 +316,14 @@ int ecp_tx_stop_ackTimer(struct vdp_data *vd)
*/
static void ecp_tx_start_ackTimer(struct vdp_data *vd)
{
- unsigned int secs, usecs;
+ unsigned int secs, usecs, rte;
vd->ecp.ackTimerExpired = false;
- secs = ECP_TRANSMISSION_TIMER / ECP_TRANSMISSION_DIVIDER;
- usecs = ECP_TRANSMISSION_TIMER % ECP_TRANSMISSION_DIVIDER;
+ rte = evb_get_rte(vd->ifname);
+
+ secs = ECP_TRANSMISSION_TIMER(rte) / ECP_TRANSMISSION_DIVIDER;
+ usecs = ECP_TRANSMISSION_TIMER(rte) % ECP_TRANSMISSION_DIVIDER;
LLDPAD_DBG("%s(%i)-%s: starting timer\n", __func__, __LINE__,
vd->ifname);
diff --git a/include/lldp_evb.h b/include/lldp_evb.h
index d028c21..549f2b2 100644
--- a/include/lldp_evb.h
+++ b/include/lldp_evb.h
@@ -37,11 +37,11 @@ typedef enum {
EVB_CONFIRMATION
} evb_state;
-#define RTE 13
+#define EVB_RTE 13
/* retransmission granularity (RTG) in microseconds */
#define EVB_RTG 10
/* retransmission multiplier (RTM) */
-#define EVB_RTM(rte) (2<<(RTE-1))
+#define EVB_RTM(rte) (2<<(rte-1))
struct tlv_info_evb {
u8 oui[3];
diff --git a/include/lldp_vdp.h b/include/lldp_vdp.h
index 8866d51..5cd2b2e 100644
--- a/include/lldp_vdp.h
+++ b/include/lldp_vdp.h
@@ -71,7 +71,7 @@ enum {
#define VDP_MACVLAN_FORMAT_1 1
-#define VDP_TRANSMISSION_TIMER 3*EVB_RTM(RTE)*EVB_RTG
+#define VDP_TRANSMISSION_TIMER(rte) 3*EVB_RTM(rte)*EVB_RTG
#define VDP_TRANSMISSION_DIVIDER 10000
#define VDP_ROLE_STATION 0
@@ -128,6 +128,7 @@ struct vsi_profile {
struct port *port;
int ackTimerExpired;
int ackReceived;
+ int keepaliveTimerExpired;
int state;
bool localChange;
LIST_ENTRY(vsi_profile) profile;
diff --git a/lldp_evb.c b/lldp_evb.c
index 50ee17d..051559c 100644
--- a/lldp_evb.c
+++ b/lldp_evb.c
@@ -91,6 +91,13 @@ static void evb_dump_tlv(struct unpacked_tlv *tlv)
free(t);
}
+unsigned int evb_get_rte(char *ifname)
+{
+ struct evb_data *ed = evb_data(ifname);
+
+ return (unsigned int) ed->tie->rte;
+}
+
/*
* evb_bld_cfg_tlv - build the EVB TLV
* @ed: the evb data struct
diff --git a/lldp_vdp.c b/lldp_vdp.c
index cb23d90..bc17ee4 100644
--- a/lldp_vdp.c
+++ b/lldp_vdp.c
@@ -141,6 +141,82 @@ void vdp_somethingChangedLocal(struct vsi_profile *profile, bool mode)
profile->localChange = mode;
}
+/* vdp_keepaliveTimer_expired - checks for expired ack timer
+ * @profile: profile to be checked
+ *
+ * returns true or false
+ *
+ * returns value of profile->ackTimerExpired, true if ack timer has expired,
+ * false otherwise.
+ */
+static bool vdp_keepaliveTimer_expired(struct vsi_profile *profile)
+{
+ return profile->keepaliveTimerExpired;
+}
+
+/* vdp_acktimeout_handler - handles the ack timer expiry
+ * @eloop_data: data structure of event loop
+ * @user_ctx: user context, profile here
+ *
+ * no return value
+ *
+ * called when the VDP ack timer has expired. sets a flag and calls the VDP
+ * state machine.
+ */
+void vdp_keepalivetimeout_handler(void *eloop_data, void *user_ctx)
+{
+ struct vsi_profile *profile;
+
+ profile = (struct vsi_profile *) user_ctx;
+
+ profile->keepaliveTimerExpired = true;
+
+ LLDPAD_DBG("%s(%i)-%s: keepalive timer expired\n", __func__, __LINE__,
+ profile->port->ifname);
+
+ vdp_vsi_sm_station(profile);
+}
+
+/* vdp_stop_keepaliveTimer - stop the VDP keepalive timer
+ * @profile: profile to process
+ *
+ * returns the number of removed handlers
+ *
+ * stops the VDP keepalive timer. Used when the profile changes state e.g. a deassoc
+ * has been requested.
+ */
+static int vdp_stop_keepaliveTimer(struct vsi_profile *profile)
+{
+ LLDPAD_DBG("%s(%i)-%s: stopping keepalive timer\n", __func__, __LINE__,
+ profile->port->ifname);
+
+ return eloop_cancel_timeout(vdp_keepalivetimeout_handler, NULL, (void *) profile);
+}
+
+/* vdp_start_keepaliveTimer - starts the VDP keepalive timer
+ * @profile: profile to process
+ *
+ * returns 0 on success, -1 on error
+ *
+ * starts the keepalive timer after a ack frame has received.
+ */
+static int vdp_start_keepaliveTimer(struct vsi_profile *profile)
+{
+ unsigned int secs, usecs, rte;
+
+ profile->keepaliveTimerExpired = false;
+
+ rte = evb_get_rte(profile->port->ifname);
+
+ secs = VDP_TRANSMISSION_TIMER(rte) / VDP_TRANSMISSION_DIVIDER;
+ usecs = VDP_TRANSMISSION_TIMER(rte) % VDP_TRANSMISSION_DIVIDER;
+
+ LLDPAD_DBG("%s(%i)-%s: starting keepalive timer (%i secs, %i usecs) \n", __func__, __LINE__,
+ profile->port->ifname, secs, usecs);
+
+ return eloop_register_timeout(secs, usecs, vdp_keepalivetimeout_handler, NULL, (void *) profile);
+}
+
/* vdp_ackTimer_expired - checks for expired ack timer
* @profile: profile to be checked
*
@@ -154,7 +230,7 @@ static bool vdp_ackTimer_expired(struct vsi_profile *profile)
return profile->ackTimerExpired;
}
-/* vdp_timeout_handler - handles the ack timer expiry
+/* vdp_acktimeout_handler - handles the ack timer expiry
* @eloop_data: data structure of event loop
* @user_ctx: user context, profile here
*
@@ -163,7 +239,7 @@ static bool vdp_ackTimer_expired(struct vsi_profile *profile)
* called when the VDP ack timer has expired. sets a flag and calls the VDP
* state machine.
*/
-void vdp_timeout_handler(void *eloop_data, void *user_ctx)
+void vdp_acktimeout_handler(void *eloop_data, void *user_ctx)
{
struct vsi_profile *profile;
@@ -171,7 +247,7 @@ void vdp_timeout_handler(void *eloop_data, void *user_ctx)
profile->ackTimerExpired = true;
- LLDPAD_DBG("%s(%i)-%s: timer expired\n", __func__, __LINE__,
+ LLDPAD_DBG("%s(%i)-%s: ack timer expired\n", __func__, __LINE__,
profile->port->ifname);
vdp_vsi_sm_station(profile);
@@ -187,10 +263,10 @@ void vdp_timeout_handler(void *eloop_data, void *user_ctx)
*/
static int vdp_stop_ackTimer(struct vsi_profile *profile)
{
- LLDPAD_DBG("%s(%i)-%s: stopping timer\n", __func__, __LINE__,
+ LLDPAD_DBG("%s(%i)-%s: stopping ack timer\n", __func__, __LINE__,
profile->port->ifname);
- return eloop_cancel_timeout(vdp_timeout_handler, NULL, (void *) profile);
+ return eloop_cancel_timeout(vdp_acktimeout_handler, NULL, (void *) profile);
}
/* vdp_start_ackTimer - starts the VDP ack timer
@@ -202,17 +278,19 @@ static int vdp_stop_ackTimer(struct vsi_profile *profile)
*/
static int vdp_start_ackTimer(struct vsi_profile *profile)
{
- unsigned int secs, usecs;
+ unsigned int secs, usecs, rte;
profile->ackTimerExpired = false;
- secs = VDP_TRANSMISSION_TIMER / VDP_TRANSMISSION_DIVIDER;
- usecs = VDP_TRANSMISSION_TIMER % VDP_TRANSMISSION_DIVIDER;
+ rte = evb_get_rte(profile->port->ifname);
- LLDPAD_DBG("%s(%i)-%s: starting timer\n", __func__, __LINE__,
- profile->port->ifname);
+ secs = VDP_TRANSMISSION_TIMER(rte) / VDP_TRANSMISSION_DIVIDER;
+ usecs = VDP_TRANSMISSION_TIMER(rte) % VDP_TRANSMISSION_DIVIDER;
- return eloop_register_timeout(secs, usecs, vdp_timeout_handler, NULL, (void *) profile);
+ LLDPAD_DBG("%s(%i)-%s: starting ack timer (%i secs, %i usecs)\n", __func__, __LINE__,
+ profile->port->ifname, secs, usecs);
+
+ return eloop_register_timeout(secs, usecs, vdp_acktimeout_handler, NULL, (void *) profile);
}
/* vdp_vsi_change_station_state - changes the VDP station sm state
@@ -233,13 +311,15 @@ void vdp_vsi_change_station_state(struct vsi_profile *profile, u8 newstate)
(profile->state == VSI_UNASSOCIATED));
break;
case VSI_ASSOCIATED:
- assert(profile->state == VSI_ASSOC_PROCESSING);
+ assert((profile->state == VSI_ASSOC_PROCESSING) ||
+ (profile->state == VSI_ASSOCIATED));
break;
case VSI_PREASSOC_PROCESSING:
assert(profile->state == VSI_UNASSOCIATED);
break;
case VSI_PREASSOCIATED:
- assert(profile->state == VSI_PREASSOC_PROCESSING);
+ assert((profile->state == VSI_PREASSOC_PROCESSING) ||
+ (profile->state == VSI_PREASSOCIATED));
break;
case VSI_DEASSOC_PROCESSING:
assert((profile->state == VSI_PREASSOCIATED) ||
@@ -371,8 +451,8 @@ void vdp_vsi_sm_station(struct vsi_profile *profile)
vdp_somethingChangedLocal(profile, true);
ecp_somethingChangedLocal(vd);
ecp_tx_run_sm(vd);
- vdp_start_keepaliveTimer(profile);
}
+ vdp_start_keepaliveTimer(profile);
/* TODO:
* vsiError = ProcRxandSetCfg(remoteTLV, localtlv, vsistate);
* if (!vsiError) vsistate=ASSOCIATED */
@@ -1080,6 +1160,7 @@ void vdp_ifdown(char *ifname)
LIST_FOREACH(p, &vd->profile_head, profile) {
vdp_stop_ackTimer(p);
+ vdp_stop_keepaliveTimer(p);
LIST_REMOVE(p, profile);
free(p);
}
--
1.7.2.3
^ permalink raw reply related
* [PATCH 09/10] do not use macv[tap/lan] interfaces as ports
From: Jens Osterkamp @ 2010-11-22 15:50 UTC (permalink / raw)
To: e1000-eedc, virtualization, evb; +Cc: chrisw, Jens Osterkamp
In-Reply-To: <1290441057-16729-1-git-send-email-jens@linux.vnet.ibm.com>
At startup lldpad walks through all network interfaces in the system and
creates internal data structures for them. Some interfaces as e.g. vlan
and wlan are skipped in the walkthrough, some have to be treated special
(e.g. bond devices).
This patch adds macvtap and macvlan interfaces to the list of devices that
are skipped as we do not want to send out EVB/ECP frames on them.
Signed-off-by: Jens Osterkamp <jens@linux.vnet.ibm.com>
---
config.c | 2 +
include/lldp_util.h | 1 +
lldp_util.c | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 93 insertions(+), 0 deletions(-)
diff --git a/config.c b/config.c
index 26afe3b..fdb63f7 100644
--- a/config.c
+++ b/config.c
@@ -365,6 +365,8 @@ void init_ports(void)
p->if_name);
} else if (is_vlan(p->if_name)) {
;
+ } else if (is_macvtap(p->if_name)) {
+ ;
} else if (is_bridge(p->if_name)) {
; /* ignore bridge device */
} else if (check_link_status(p->if_name)) {
diff --git a/include/lldp_util.h b/include/lldp_util.h
index 3353067..ef2c562 100644
--- a/include/lldp_util.h
+++ b/include/lldp_util.h
@@ -40,6 +40,7 @@ int is_bridge(const char *ifname);
int is_vlan(const char *ifname);
int is_vlan_capable(const char *ifname);
int is_wlan(const char *ifname);
+int is_macvtap(const char *ifname);
int is_valid_mac(const u8 *mac);
int is_san_mac(u8 *addr);
int is_ether(const char *ifname);
diff --git a/lldp_util.c b/lldp_util.c
index f39fe6b..40db741 100644
--- a/lldp_util.c
+++ b/lldp_util.c
@@ -34,6 +34,7 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if_arp.h>
+#include <netlink/msg.h>
#include <arpa/inet.h>
#include <linux/if.h>
#include <linux/if_bonding.h>
@@ -42,6 +43,7 @@
#include <linux/wireless.h>
#include <linux/sockios.h>
#include <linux/ethtool.h>
+#include <linux/rtnetlink.h>
#include <dirent.h>
#include "lldp.h"
#include "lldp_util.h"
@@ -57,6 +59,8 @@ int is_valid_lldp_device(const char *device_name)
return 0;
if (is_bridge(device_name))
return 0;
+ if (is_macvtap(device_name))
+ return 0;
return 1;
}
@@ -534,6 +538,92 @@ int is_wlan(const char *ifname)
return rc;
}
+#define NLMSG_SIZE 1024
+
+static struct nla_policy ifla_info_policy[IFLA_INFO_MAX + 1] =
+{
+ [IFLA_INFO_KIND] = { .type = NLA_STRING},
+ [IFLA_INFO_DATA] = { .type = NLA_NESTED },
+};
+
+int is_macvtap(const char *ifname)
+{
+ int ret, s;
+ struct nlmsghdr *nlh;
+ struct ifinfomsg *ifinfo;
+ struct nlattr *tb[IFLA_MAX+1],
+ *tb2[IFLA_INFO_MAX+1];
+
+ s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+
+ if (s < 0) {
+ goto out;
+ }
+
+ nlh = malloc(NLMSG_SIZE);
+ memset(nlh, 0, NLMSG_SIZE);
+
+ if (!nlh) {
+ goto out;
+ }
+
+ nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+ nlh->nlmsg_type = RTM_GETLINK;
+ nlh->nlmsg_flags = NLM_F_REQUEST;
+
+ ifinfo = NLMSG_DATA(nlh);
+ ifinfo->ifi_family = AF_UNSPEC;
+ ifinfo->ifi_index = get_ifidx(ifname);
+
+ ret = send(s, nlh, nlh->nlmsg_len, 0);
+
+ if (ret < 0) {
+ goto out_free;
+ }
+
+ memset(nlh, 0, NLMSG_SIZE);
+
+ do {
+ ret = recv(s, (void *) nlh, NLMSG_SIZE, MSG_DONTWAIT);
+ } while ((ret < 0) && errno == EINTR);
+
+ if (nlmsg_parse(nlh, sizeof(struct ifinfomsg),
+ (struct nlattr **)&tb, IFLA_MAX, NULL)) {
+ goto out_free;
+ }
+
+ if (tb[IFLA_IFNAME]) {
+ ifname = (char *)RTA_DATA(tb[IFLA_IFNAME]);
+ } else {
+ ifinfo = (struct ifinfomsg *)NLMSG_DATA(nlh);
+ }
+
+ if (tb[IFLA_LINKINFO]) {
+ if (nla_parse_nested(tb2, IFLA_INFO_MAX, tb[IFLA_LINKINFO],
+ ifla_info_policy)) {
+ goto out_free;
+ }
+
+ if (tb2[IFLA_INFO_KIND]) {
+ char *kind = (char*)(RTA_DATA(tb2[IFLA_INFO_KIND]));
+ if (!(strcmp("macvtap", kind) && strcmp("macvlan", kind))) {
+ free(nlh);
+ close(s);
+ return true;
+ }
+ }
+
+ } else {
+ goto out_free;
+ }
+
+out_free:
+ free(nlh);
+out:
+ close(s);
+ return false;
+}
+
int is_router(const char *ifname)
{
int rc = 0;
--
1.7.2.3
^ permalink raw reply related
* [PATCH 08/10] lldpad support for libvirt netlink message
From: Jens Osterkamp @ 2010-11-22 15:50 UTC (permalink / raw)
To: e1000-eedc, virtualization, evb
Cc: chrisw, Gerhard Stenzel, Stefan Berger, Jens Osterkamp
In-Reply-To: <1290441057-16729-1-git-send-email-jens@linux.vnet.ibm.com>
This code receives a IEEE 802.1Qbg virtual station instance from libvirt in
a SETLINK message. The parsed VSI is then handed over to VDP for processing.
The VDP state machine processes the VSI while libvirt polls the result using
GETLINK.
Requires at least Linux kernel 2.6.35-rc1.
Signed-off-by: Jens Osterkamp <jens@linux.vnet.ibm.com>
Signed-off-by: Gerhard Stenzel <gstenzel@linux.vnet.ibm.com>
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
---
event_iface.c | 589 ++++++++++++++++++++++++++++++++++++++++++++++++-
include/event_iface.h | 1 +
lldp_vdp.c | 2 +-
lldpad.c | 10 +
4 files changed, 591 insertions(+), 11 deletions(-)
diff --git a/event_iface.c b/event_iface.c
index 439e4d0..e4cb8eb 100644
--- a/event_iface.c
+++ b/event_iface.c
@@ -3,6 +3,13 @@
LLDP Agent Daemon (LLDPAD) Software
Copyright(c) 2007-2010 Intel Corporation.
+ implementation of libvirt netlink interface
+ (c) Copyright IBM Corp. 2010
+
+ Author(s): Jens Osterkamp <jens@linux.vnet.ibm.com>
+ Stefan Berger <stefanb@linux.vnet.ibm.com>
+ Gerhard Stenzel <gstenzel@linux.vnet.ibm.com>
+
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.
@@ -31,12 +38,16 @@
#include <sys/socket.h>
#include <linux/rtnetlink.h>
#include <linux/if.h>
+#include <linux/if_link.h>
#include <linux/if_vlan.h>
#include <linux/sockios.h>
#include <syslog.h>
#include <unistd.h>
+#include <linux/netlink.h>
+#include <netlink/msg.h>
#include "lldpad.h"
#include "lldp_mod.h"
+#include "lldp_vdp.h"
#include "common.h"
#include "eloop.h"
#include "drv_cfg.h"
@@ -47,6 +58,7 @@
#include "lldp/l2_packet.h"
#include "config.h"
#include "lldp/states.h"
+#include "messages.h"
static void event_if_decode_rta(int type, struct rtattr *rta);
@@ -55,6 +67,32 @@ static void event_if_decode_rta(int type, struct rtattr *rta);
static char *device_name = NULL;
static int link_status = 0;
+static struct nla_policy ifla_vf_policy[IFLA_VF_MAX + 1] =
+{
+ [IFLA_VF_MAC] = { .minlen = sizeof(struct ifla_vf_mac),
+ .maxlen = sizeof(struct ifla_vf_mac)},
+ [IFLA_VF_VLAN] = { .minlen = sizeof(struct ifla_vf_vlan),
+ .maxlen = sizeof(struct ifla_vf_vlan)},
+};
+
+static struct nla_policy ifla_vf_ports_policy[IFLA_VF_PORT_MAX + 1] =
+{
+ [IFLA_VF_PORT] = { .type = NLA_NESTED },
+};
+
+static struct nla_policy ifla_port_policy[IFLA_PORT_MAX + 1] =
+{
+ [IFLA_PORT_VF] = { .type = NLA_U32 },
+ [IFLA_PORT_PROFILE] = { .type = NLA_STRING },
+ [IFLA_PORT_VSI_TYPE] = { .minlen = sizeof(struct ifla_port_vsi) },
+ [IFLA_PORT_INSTANCE_UUID] = { .minlen = PORT_UUID_MAX,
+ .maxlen = PORT_UUID_MAX, },
+ [IFLA_PORT_HOST_UUID] = { .minlen = PORT_UUID_MAX,
+ .maxlen = PORT_UUID_MAX, },
+ [IFLA_PORT_REQUEST] = { .type = NLA_U8 },
+ [IFLA_PORT_RESPONSE] = { .type = NLA_U16 },
+};
+
static void event_if_decode_rta(int type, struct rtattr *rta)
{
@@ -220,15 +258,506 @@ static void event_if_decode_nlmsg(int route_type, void *data, int len)
}
}
-
static void event_if_process_recvmsg(struct nlmsghdr *nlmsg)
{
-
- /* print out details */
+ LLDPAD_DBG("%s:%s: nlmsg_type: %d", __FILE__, __FUNCTION__, nlmsg->nlmsg_type);
event_if_decode_nlmsg(nlmsg->nlmsg_type, NLMSG_DATA(nlmsg),
NLMSG_PAYLOAD(nlmsg, 0));
}
+static int event_if_parse_getmsg(struct nlmsghdr *nlh, int *ifindex,
+ char *ifname)
+{
+ struct nlattr *tb[IFLA_MAX+1];
+ struct ifinfomsg *ifinfo;
+
+ if (nlmsg_parse(nlh, sizeof(struct ifinfomsg),
+ (struct nlattr **)&tb, IFLA_MAX, NULL)) {
+ LLDPAD_ERR("Error parsing GETLINK request...");
+ return -EINVAL;
+ }
+
+ if (tb[IFLA_IFNAME]) {
+ ifname = (char *)RTA_DATA(tb[IFLA_IFNAME]);
+ LLDPAD_DBG("IFLA_IFNAME=%s", ifname);
+ } else {
+ ifinfo = (struct ifinfomsg *)NLMSG_DATA(nlh);
+ *ifindex = ifinfo->ifi_index;
+ LLDPAD_DBG("interface index: %d", ifinfo->ifi_index);
+ }
+
+ return 0;
+}
+
+static int event_if_parse_setmsg(struct nlmsghdr *nlh)
+{
+ struct nlattr *tb[IFLA_MAX+1],
+ *tb3[IFLA_PORT_MAX+1],
+ *tb_vfinfo[IFLA_VF_MAX+1],
+ *tb_vfinfo_list;
+ struct vsi_profile *profile, *p;
+ struct ifinfomsg *ifinfo;
+ char *ifname;
+ int rem;
+
+ profile = malloc(sizeof(struct vsi_profile));
+ if (!profile)
+ return -ENOMEM;
+ memset(profile, 0, sizeof(struct vsi_profile));
+
+ if (nlmsg_parse(nlh, sizeof(struct ifinfomsg),
+ (struct nlattr **)&tb, IFLA_MAX, NULL)) {
+ LLDPAD_ERR("Error parsing request...\n");
+ return -EINVAL;
+ }
+
+ LLDPAD_DBG("%s(%d): nlmsg_len %i", __FILE__, __LINE__, nlh->nlmsg_len);
+
+ if (tb[IFLA_IFNAME]) {
+ ifname = (char *)RTA_DATA(tb[IFLA_IFNAME]);
+ LLDPAD_DBG("IFLA_IFNAME=%s", ifname);
+ } else {
+ ifinfo = (struct ifinfomsg *)NLMSG_DATA(nlh);
+ LLDPAD_DBG("interface index: %d", ifinfo->ifi_index);
+ }
+
+ if (!tb[IFLA_VFINFO_LIST]) {
+ LLDPAD_ERR("IFLA_VFINFO_LIST missing.");
+ return -EINVAL;
+ } else {
+ LLDPAD_DBG("FOUND IFLA_VFINFO_LIST!");
+ }
+
+ nla_for_each_nested(tb_vfinfo_list, tb[IFLA_VFINFO_LIST], rem) {
+ if (nla_type(tb_vfinfo_list) != IFLA_VF_INFO) {
+ LLDPAD_ERR("nested parsing of IFLA_VFINFO_LIST failed.");
+ return -EINVAL;
+ }
+
+ if (nla_parse_nested(tb_vfinfo, IFLA_VF_MAX, tb_vfinfo_list,
+ ifla_vf_policy)) {
+ LLDPAD_ERR("nested parsing of IFLA_VF_INFO failed.");
+ return -EINVAL;
+ }
+ }
+
+ if (tb_vfinfo[IFLA_VF_MAC]) {
+ struct ifla_vf_mac *mac = RTA_DATA(tb_vfinfo[IFLA_VF_MAC]);
+ u8 *m = mac->mac;
+ LLDPAD_DBG("IFLA_VF_MAC=%2x:%2x:%2x:%2x:%2x:%2x",
+ m[0], m[1], m[2], m[3], m[4], m[5]);
+ memcpy(&profile->mac, m, ETH_ALEN);
+ }
+
+ if (tb_vfinfo[IFLA_VF_VLAN]) {
+ struct ifla_vf_vlan *vlan = RTA_DATA(tb_vfinfo[IFLA_VF_VLAN]);
+ LLDPAD_DBG("IFLA_VF_VLAN=%d", vlan->vlan);
+ profile->vlan = (u16) vlan->vlan;
+ }
+
+ if (tb[IFLA_VF_PORTS]) {
+ struct nlattr *tb_vf_ports;
+
+ LLDPAD_DBG("FOUND IFLA_VF_PORTS");
+
+ nla_for_each_nested(tb_vf_ports, tb[IFLA_VF_PORTS], rem) {
+
+ LLDPAD_DBG("ITERATING");
+
+ if (nla_type(tb_vf_ports) != IFLA_VF_PORT) {
+ LLDPAD_DBG("not a IFLA_VF_PORT. skipping");
+ continue;
+ }
+
+ if (nla_parse_nested(tb3, IFLA_PORT_MAX, tb_vf_ports,
+ ifla_port_policy)) {
+ LLDPAD_ERR("nested parsing on level 2 failed.");
+ return -EINVAL;
+ }
+
+ if (tb3[IFLA_PORT_VF]) {
+ LLDPAD_DBG("IFLA_PORT_VF=%d", *(uint32_t*)(RTA_DATA(tb3[IFLA_PORT_VF])));
+ }
+
+ if (tb3[IFLA_PORT_PROFILE]) {
+ LLDPAD_DBG("IFLA_PORT_PROFILE=%s", (char *)RTA_DATA(tb3[IFLA_PORT_PROFILE]));
+ }
+
+ if (tb3[IFLA_PORT_VSI_TYPE]) {
+ struct ifla_port_vsi *pvsi;
+ int tid = 0;
+
+ pvsi = (struct ifla_port_vsi*)RTA_DATA(tb3[IFLA_PORT_VSI_TYPE]);
+ tid = pvsi->vsi_type_id[2] << 16 |
+ pvsi->vsi_type_id[1] << 8 |
+ pvsi->vsi_type_id[0];
+
+ LLDPAD_DBG("mgr_id : %d", pvsi->vsi_mgr_id);
+ LLDPAD_DBG("type_id : %d", tid);
+ LLDPAD_DBG("type_version : %d", pvsi->vsi_type_version);
+
+ profile->mgrid = pvsi->vsi_mgr_id;
+ profile->id = tid;
+ profile->version = pvsi->vsi_type_version;
+ }
+
+ if (tb3[IFLA_PORT_INSTANCE_UUID]) {
+ int i;
+ unsigned char *uuid;
+ uuid = (unsigned char *)RTA_DATA(tb3[IFLA_PORT_INSTANCE_UUID]);
+
+ char instance[INSTANCE_STRLEN+2];
+ instance2str(uuid, instance, sizeof(instance));
+ LLDPAD_DBG("IFLA_PORT_INSTANCE_UUID=%s", instance);
+
+ memcpy(&profile->instance,
+ RTA_DATA(tb3[IFLA_PORT_INSTANCE_UUID]), 16);
+ }
+
+ if (tb3[IFLA_PORT_REQUEST]) {
+ LLDPAD_DBG("IFLA_PORT_REQUEST=%d",
+ *(uint8_t*)RTA_DATA(tb3[IFLA_PORT_REQUEST]));
+ profile->mode = *(uint8_t*)RTA_DATA(tb3[IFLA_PORT_REQUEST]);
+ }
+
+ if (tb3[IFLA_PORT_RESPONSE]) {
+ LLDPAD_DBG("IFLA_PORT_RESPONSE=%d",
+ *(uint16_t*)RTA_DATA(tb3[IFLA_PORT_RESPONSE]));
+ profile->response = *(uint16_t*)RTA_DATA(tb3[IFLA_PORT_RESPONSE]);
+ }
+ }
+ }
+
+ if (ifname) {
+ struct port *port = port_find_by_name(ifname);
+
+ if (port) {
+ profile->port = port;
+ } else {
+ LLDPAD_ERR("%s(%i): Could not find port for %s", __func__,
+ __LINE__, ifname);
+ return -EEXIST;
+ }
+ }
+
+ p = vdp_add_profile(profile);
+
+ if (!p) {
+ free(profile);
+ return -EINVAL;
+ }
+
+ vdp_somethingChangedLocal(profile, VDP_PROFILE_REQ);
+ vdp_vsi_sm_station(p);
+
+ return 0;
+}
+
+static void event_if_parseResponseMsg(struct nlmsghdr *nlh)
+{
+ struct nlattr *tb[IFLA_MAX+1],
+ *tb2[IFLA_VF_PORT_MAX + 1],
+ *tb3[IFLA_PORT_MAX+1];
+
+ if (nlmsg_parse(nlh, sizeof(struct ifinfomsg),
+ (struct nlattr **)&tb, IFLA_MAX, NULL)) {
+ LLDPAD_ERR("Error parsing netlink response...");
+ return;
+ }
+
+ if (tb[IFLA_IFNAME]) {
+ LLDPAD_DBG("IFLA_IFNAME=%s", (char *)RTA_DATA(tb[IFLA_IFNAME]));
+ } else {
+ struct ifinfomsg *ifinfo = (struct ifinfomsg *)NLMSG_DATA(nlh);
+ LLDPAD_DBG("interface index: %d", ifinfo->ifi_index);
+ }
+
+ if (tb[IFLA_VF_PORTS]) {
+ if (nla_parse_nested(tb2, IFLA_VF_PORT_MAX, tb[IFLA_VF_PORTS],
+ ifla_vf_ports_policy)) {
+ LLDPAD_ERR("nested parsing on level 1 failed.");
+ return;
+ }
+
+ if (tb2[IFLA_VF_PORT]) {
+ if (nla_parse_nested(tb3, IFLA_PORT_MAX, tb2[IFLA_VF_PORT],
+ ifla_port_policy)) {
+ LLDPAD_ERR("nested parsing on level 2 failed.");
+ return;
+ }
+
+ if (tb3[IFLA_PORT_VF]) {
+ LLDPAD_DBG("IFLA_PORT_VF=%d", *(uint32_t*)(RTA_DATA(tb3[IFLA_PORT_VF])));
+ }
+
+ if (tb3[IFLA_PORT_PROFILE]) {
+ LLDPAD_DBG("IFLA_PORT_PROFILE=%s", (char *)RTA_DATA(tb3[IFLA_PORT_PROFILE]));
+ }
+
+ if (tb3[IFLA_PORT_VSI_TYPE]) {
+ struct ifla_port_vsi *pvsi;
+ int tid = 0;
+ pvsi = (struct ifla_port_vsi*)RTA_DATA(tb3[IFLA_PORT_VSI_TYPE]);
+ tid = pvsi->vsi_type_id[2] << 16 |
+ pvsi->vsi_type_id[1] << 8 |
+ pvsi->vsi_type_id[0];
+ LLDPAD_DBG("mgr_id : %d"
+ "type_id : %d"
+ "type_version : %d",
+ pvsi->vsi_mgr_id,
+ tid,
+ pvsi->vsi_type_version);
+ }
+
+ if (tb3[IFLA_PORT_INSTANCE_UUID]) {
+ int i;
+ unsigned char *uuid;
+ uuid = (unsigned char *)RTA_DATA(tb3[IFLA_PORT_INSTANCE_UUID]);
+
+ char instance[INSTANCE_STRLEN+2];
+ instance2str(uuid, instance, sizeof(instance));
+ LLDPAD_DBG("IFLA_PORT_INSTANCE_UUID=%s", &instance[0]);
+ }
+
+ if (tb3[IFLA_PORT_REQUEST]) {
+ LLDPAD_DBG("IFLA_PORT_REQUEST=%d",
+ *(uint8_t*)RTA_DATA(tb3[IFLA_PORT_REQUEST]));
+ }
+
+ if (tb3[IFLA_PORT_RESPONSE]) {
+ LLDPAD_DBG("IFLA_PORT_RESPONSE=%d",
+ *(uint16_t*)RTA_DATA(tb3[IFLA_PORT_RESPONSE]));
+ }
+ }
+ }
+}
+
+struct nl_msg *event_if_constructResponse(struct nlmsghdr *nlh, int ifindex)
+{
+ struct nl_msg *nl_msg;
+ struct nlattr *vf_ports = NULL, *vf_port;
+ struct ifinfomsg ifinfo;
+ struct vdp_data *vd;
+ uint32_t pid = nlh->nlmsg_pid;
+ uint32_t seq = nlh->nlmsg_seq;
+ char *ifname = malloc(IFNAMSIZ);
+ struct vsi_profile *p;
+
+ nl_msg = nlmsg_alloc();
+
+ if (!nl_msg) {
+ LLDPAD_ERR("%s(%i): Unable to allocate netlink message !", __func__, __LINE__);
+ return NULL;
+ }
+
+ if (!if_indextoname(ifindex, ifname)) {
+ LLDPAD_ERR("%s(%i): No name found for interface with index %i !", __func__, __LINE__,
+ ifindex);
+ }
+
+ vd = vdp_data(ifname);
+ if (!vd) {
+ LLDPAD_ERR("%s(%i): Could not find vdp_data for %s !", __func__, __LINE__,
+ ifname);
+ return NULL;
+ }
+
+ free(ifname);
+
+ if (nlmsg_put(nl_msg, pid, seq, NLMSG_DONE, 0, 0) == NULL)
+ goto err_exit;
+
+ ifinfo.ifi_index = ifindex;
+
+ if (nlmsg_append(nl_msg, &ifinfo, sizeof(ifinfo), NLMSG_ALIGNTO) < 0)
+ goto err_exit;
+
+ vf_ports = nla_nest_start(nl_msg, IFLA_VF_PORTS);
+
+ if (!vf_ports)
+ goto err_exit;
+
+ /* loop over all existing profiles on this interface and
+ * put them into the nested IFLA_VF_PORT structure */
+ LIST_FOREACH(p, &vd->profile_head, profile) {
+ if (p) {
+ vdp_print_profile(p);
+
+ vf_port = nla_nest_start(nl_msg, IFLA_VF_PORT);
+
+ if (!vf_port)
+ goto err_exit;
+
+ if (nla_put(nl_msg, IFLA_PORT_INSTANCE_UUID, 16, p->instance) < 0)
+ goto err_exit;
+
+ if (nla_put_u32(nl_msg, IFLA_PORT_VF, PORT_SELF_VF) < 0)
+ goto err_exit;
+
+ if (p->response != VDP_RESPONSE_NO_RESPONSE) {
+ if (nla_put_u16(nl_msg, IFLA_PORT_RESPONSE,
+ p->response) < 0)
+ goto err_exit;
+ }
+
+ nla_nest_end(nl_msg, vf_port);
+ }
+ }
+
+ if (vf_ports)
+ nla_nest_end(nl_msg, vf_ports);
+
+ return nl_msg;
+
+err_exit:
+ nlmsg_free(nl_msg);
+
+ return NULL;
+}
+
+struct nl_msg *event_if_simpleResponse(uint32_t pid, uint32_t seq, int err)
+{
+ struct nl_msg *nl_msg = nlmsg_alloc();
+ struct nlmsgerr nlmsgerr;
+
+ memset(&nlmsgerr, 0x0, sizeof(nlmsgerr));
+
+ nlmsgerr.error = err;
+ LLDPAD_DBG("RESPONSE error code: %d",err);
+
+ if (nlmsg_put(nl_msg, pid, seq, NLMSG_ERROR, 0, 0) == NULL)
+ goto err_exit;
+
+ if (nlmsg_append(nl_msg, &nlmsgerr, sizeof(nlmsgerr), NLMSG_ALIGNTO) < 0)
+ goto err_exit;
+
+ return nl_msg;
+
+err_exit:
+ nlmsg_free(nl_msg);
+
+ return NULL;
+}
+
+static void event_iface_receive_user_space(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct nlmsghdr *nlh, *nlh2;
+ struct nl_msg *nl_msg;
+ struct msghdr msg;
+ struct sockaddr_nl dest_addr;
+ struct iovec iov;
+ int result;
+ int err;
+ int ifindex = 0;
+ char *ifname = NULL;
+
+ nlh = (struct nlmsghdr *)calloc(1,
+ NLMSG_SPACE(MAX_PAYLOAD));
+ if (!nlh) {
+ LLDPAD_ERR("%s(%i): could not allocate nlh !", __func__,
+ __LINE__);
+ return;
+ }
+ memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));
+
+ memset(&dest_addr, 0, sizeof(dest_addr));
+ iov.iov_base = (void *)nlh;
+ iov.iov_len = NLMSG_SPACE(MAX_PAYLOAD);
+ msg.msg_name = (void *)&dest_addr;
+ msg.msg_namelen = sizeof(dest_addr);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_flags = 0;
+
+ LLDPAD_DBG("Waiting for message");
+ result = recvmsg(sock, &msg, MSG_DONTWAIT);
+
+ LLDPAD_DBG("%s(%i): ", __func__, __LINE__);
+ LLDPAD_DBG("recvmsg received %d bytes", result);
+
+ if(result < 0) {
+ LLDPAD_ERR("Error receiving from netlink socket : %s", strerror(errno));
+ }
+
+ LLDPAD_DBG("dest_addr.nl_pid: %d", dest_addr.nl_pid);
+ LLDPAD_DBG("nlh.nl_pid: %d", nlh->nlmsg_pid);
+ LLDPAD_DBG("nlh_type: %d", nlh->nlmsg_type);
+ LLDPAD_DBG("nlh_seq: 0x%x", nlh->nlmsg_seq);
+ LLDPAD_DBG("nlh_len: 0x%x", nlh->nlmsg_len);
+
+ switch (nlh->nlmsg_type) {
+ case RTM_SETLINK:
+ LLDPAD_DBG("RTM_SETLINK");
+
+ err = event_if_parse_setmsg(nlh);
+
+ /* send simple response wether profile was accepted
+ * or not */
+ nl_msg = event_if_simpleResponse(nlh->nlmsg_pid,
+ nlh->nlmsg_seq,
+ err);
+ nlh2 = nlmsg_hdr(nl_msg);
+ break;
+ case RTM_GETLINK:
+ LLDPAD_DBG("RTM_GETLINK");
+
+ err = event_if_parse_getmsg(nlh, &ifindex, ifname);
+
+ if (err) {
+ nl_msg = event_if_simpleResponse(nlh->nlmsg_pid,
+ nlh->nlmsg_seq,
+ err);
+ } else if (ifname) {
+ ifindex = if_nametoindex(ifname);
+ LLDPAD_DBG("%s(%i): ifname %s (%d)", __func__,
+ __LINE__, ifname, ifindex);
+ } else {
+ LLDPAD_DBG("%s(%i): ifindex %i", __func__,
+ __LINE__, ifindex);
+ }
+
+ nl_msg = event_if_constructResponse(nlh, ifindex);
+
+ if (!nl_msg) {
+ LLDPAD_ERR("%s(%i): Unable to construct response !",
+ __func__, __LINE__);
+ goto out_err;
+ }
+
+ nlh2 = nlmsg_hdr(nl_msg);
+
+ LLDPAD_DBG("RESPONSE:");
+
+ event_if_parseResponseMsg(nlh2);
+
+ break;
+ }
+
+ iov.iov_base = (void*)nlh2;
+ iov.iov_len = nlh2->nlmsg_len;
+
+ msg.msg_name = (void *)&dest_addr;
+ msg.msg_namelen = sizeof(dest_addr);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ result = sendmsg(sock, &msg, 0);
+
+ if (result < 0) {
+ LLDPAD_ERR("Error sending on netlink socket (%s) !", strerror(errno));
+ } else {
+ LLDPAD_DBG("Sent %d bytes !",result);
+ }
+
+out_err:
+ free(nlh);
+ nlmsg_free(nl_msg);
+
+ return;
+}
+
static void event_iface_receive(int sock, void *eloop_ctx, void *sock_ctx)
{
struct nlmsghdr *nlh;
@@ -236,12 +765,14 @@ static void event_iface_receive(int sock, void *eloop_ctx, void *sock_ctx)
char buf[MAX_MSG_SIZE];
socklen_t fromlen = sizeof(dest_addr);
int result;
-
+
result = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT,
(struct sockaddr *) &dest_addr, &fromlen);
+ LLDPAD_DBG("%s:%s: result from receive: %d", __FILE__, __FUNCTION__, result);
+
if (result < 0) {
- perror("recvfrom(Event interface)");
+ LLDPAD_ERR("recvfrom(event interface): %s", strerror(errno));
if ((errno == ENOBUFS) || (errno == EAGAIN))
eloop_register_timeout(INI_TIMER, 0, scan_port,
NULL, NULL);
@@ -250,10 +781,15 @@ static void event_iface_receive(int sock, void *eloop_ctx, void *sock_ctx)
TRACE("PRINT BUF info.\n")
- device_name = NULL;
- link_status = IF_OPER_UNKNOWN;
- nlh = (struct nlmsghdr *)buf;
- event_if_process_recvmsg(nlh);
+ /* Separate handler for kernel messages from userspace messages*/
+ LLDPAD_DBG("%s:%s: dest_addr.nl_pid: %d", __FILE__, __FUNCTION__, dest_addr.nl_pid);
+
+ if (dest_addr.nl_pid == 0) {
+ device_name = NULL;
+ link_status = IF_OPER_UNKNOWN;
+ nlh = (struct nlmsghdr *)buf;
+ event_if_process_recvmsg(nlh);
+ }
}
int event_iface_init()
@@ -261,7 +797,7 @@ int event_iface_init()
int fd;
int rcv_size = MAX_MSG_SIZE;
struct sockaddr_nl snl;
-
+ fprintf(stderr, "%s:%s:%d\n", __FILE__, __FUNCTION__, __LINE__);
fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (fd < 0)
@@ -282,6 +818,39 @@ int event_iface_init()
}
eloop_register_read_sock(fd, event_iface_receive, NULL, NULL);
+
+ return 0;
+}
+
+int event_iface_init_user_space()
+{
+ int fd;
+ int rcv_size = MAX_MSG_SIZE;
+ struct sockaddr_nl snl;
+
+ fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+
+ if (fd < 0)
+ return fd;
+
+ if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcv_size, sizeof(int)) < 0) {
+ close(fd);
+ return -EIO;
+ }
+
+ memset((void *)&snl, 0, sizeof(struct sockaddr_nl));
+ snl.nl_family = AF_NETLINK;
+ snl.nl_pid = getpid(); /* self pid */
+ snl.nl_groups = 0;
+
+ if (bind(fd, (struct sockaddr *)&snl, sizeof(struct sockaddr_nl)) < 0) {
+ close(fd);
+ LLDPAD_ERR("Error binding to netlink socket (%s) !", strerror(errno));
+ return -EIO;
+ }
+
+ eloop_register_read_sock(fd, event_iface_receive_user_space, NULL, NULL);
+
return 0;
}
diff --git a/include/event_iface.h b/include/event_iface.h
index b2c93f0..d80158d 100644
--- a/include/event_iface.h
+++ b/include/event_iface.h
@@ -29,6 +29,7 @@
#define _EVENT_IFACE_H_
int event_iface_init(void);
+int event_iface_init_user_space(void);
int event_iface_deinit(void);
int oper_add_device(char *device_name);
diff --git a/lldp_vdp.c b/lldp_vdp.c
index 2014c2a..cb23d90 100644
--- a/lldp_vdp.c
+++ b/lldp_vdp.c
@@ -105,7 +105,7 @@ static void vdp_free_data(struct vdp_user_data *ud)
* prints the contents of a profile first to a string using the PRINT_PROFILE
* macro, and then to the screen. Used for debug purposes.
*/
-static inline void vdp_print_profile(struct vsi_profile *profile)
+void vdp_print_profile(struct vsi_profile *profile)
{
LLDPAD_DBG("profile:\n");
diff --git a/lldpad.c b/lldpad.c
index c0938af..69faa29 100644
--- a/lldpad.c
+++ b/lldpad.c
@@ -374,6 +374,16 @@ int main(int argc, char *argv[])
exit(1);
}
+ /* setup event netlink interface for user space processes.
+ * This needs to be setup first to ensure it gets lldpads
+ * pid as netlink address.
+ */
+ if (event_iface_init_user_space() < 0) {
+ log_message(MSG_ERR_SERVICE_START_FAILURE,
+ "%s", "failed to register user space event interface");
+ exit(1);
+ }
+
init_modules("");
--
1.7.2.3
^ permalink raw reply related
* [PATCH 07/10] use connect instead of bind
From: Jens Osterkamp @ 2010-11-22 15:50 UTC (permalink / raw)
To: e1000-eedc, virtualization, evb; +Cc: chrisw, Gerhard Stenzel, Jens Osterkamp
In-Reply-To: <1290441057-16729-1-git-send-email-jens@linux.vnet.ibm.com>
modifies the setup of the netlink socket in drv_cfg.c to use pid 0 instead
of the processes pid.
Also replaces bind with connect to allow the reception of netlink messages
from libvirt.
Preparation patch for communication between libvirt and lldpad.
Signed-off-by: Gerhard Stenzel <gstenzel@linux.vnet.ibm.com>
---
drv_cfg.c | 5 +++--
1 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/drv_cfg.c b/drv_cfg.c
index 6c02555..23c11f3 100644
--- a/drv_cfg.c
+++ b/drv_cfg.c
@@ -71,9 +71,9 @@ static int init_socket(void)
memset((void *)&snl, 0, sizeof(struct sockaddr_nl));
snl.nl_family = AF_NETLINK;
- snl.nl_pid = getpid();
+ snl.nl_pid = 0;
- if (bind(sd, (struct sockaddr *)&snl, sizeof(struct sockaddr_nl)) < 0) {
+ if (connect(sd, (struct sockaddr *)&snl, sizeof(struct sockaddr_nl)) < 0) {
close(sd);
return -EIO;
}
@@ -195,6 +195,7 @@ static struct nlmsghdr *get_msg(unsigned int seq)
nlh = NULL;
break;
}
+ fprintf(stderr, "%s:%s: nlmsg_type: %d\n", __FILE__, __FUNCTION__, nlh->nlmsg_type);
if ((nlh->nlmsg_type == RTM_GETDCB ||
nlh->nlmsg_type == RTM_SETDCB) &&
nlh->nlmsg_seq == seq) {
--
1.7.2.3
^ permalink raw reply related
* [PATCH 06/10] add libnl dependency to configure.ac
From: Jens Osterkamp @ 2010-11-22 15:50 UTC (permalink / raw)
To: e1000-eedc, virtualization, evb; +Cc: chrisw, Jens Osterkamp
In-Reply-To: <1290441057-16729-1-git-send-email-jens@linux.vnet.ibm.com>
Signed-off-by: Jens Osterkamp <jens@linux.vnet.ibm.com>
---
configure.ac | 5 +++++
1 files changed, 5 insertions(+), 0 deletions(-)
diff --git a/configure.ac b/configure.ac
index 619c6c4..8b9d08a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -12,6 +12,11 @@ PKG_CHECK_MODULES(LIBCONFIG, libconfig,
AC_SUBST(LIBCONFIG_CFLAGS)
AC_SUBST(LIBCONFIG_LIBS)
+PKG_CHECK_MODULES(LIBNL, libnl-1 >= 1.1)
+AC_SUBST(LIBNL_CFLAGS)
+AC_SUBST(LIBNL_LIBS)
+
+AC_CHECK_LIB(nl, rtnl_link_get_by_name)
AC_CONFIG_SUBDIRS([libconfig-1.3.2])
AC_CONFIG_FILES([Makefile include/version.h lldpad.spec lldpad.pc dcbd.pc])
AC_OUTPUT
--
1.7.2.3
^ permalink raw reply related
* [PATCH 05/10] VDP commandline interface
From: Jens Osterkamp @ 2010-11-22 15:50 UTC (permalink / raw)
To: e1000-eedc, virtualization, evb; +Cc: chrisw, Jens Osterkamp
In-Reply-To: <1290441057-16729-1-git-send-email-jens@linux.vnet.ibm.com>
This patch implements the command line interface to control the VDP module.
In station role, it allows to register a new VSI (guest interface) profile
and query its state. In bridge role, it allows to query the state of all
registered profiles.
With lldptool it is possible to set the role from default "station" to
"bridge".
The configuration of the role for a port is saved to lldpads config file.
Signed-off-by: Jens Osterkamp <jens@linux.vnet.ibm.com>
---
Makefile.am | 12 +-
include/lldp_vdp_clif.h | 39 +++++
include/lldp_vdp_cmds.h | 44 +++++
lldp_vdp_clif.c | 136 ++++++++++++++++
lldp_vdp_cmds.c | 409 +++++++++++++++++++++++++++++++++++++++++++++++
lldptool.c | 2 +
6 files changed, 637 insertions(+), 5 deletions(-)
create mode 100644 include/lldp_vdp_clif.h
create mode 100644 include/lldp_vdp_cmds.h
create mode 100644 lldp_vdp_clif.c
create mode 100644 lldp_vdp_cmds.c
diff --git a/Makefile.am b/Makefile.am
index 4b69389..9a3baf4 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -37,7 +37,8 @@ lldpad_include_HEADERS = include/dcb_types.h include/dcbtool.h \
include/dcb_osdep.h include/clif.h include/lldp_dcbx_cmds.h include/common.h \
include/lldpad.h include/os.h include/includes.h include/lldp_mand_cmds.h \
include/clif_msgs.h include/lldp_basman_cmds.h include/lldp_8023_cmds.h \
-include/lldp_med_cmds.h include/lldp_dcbx_cfg.h include/lldp_evb_cmds.h
+include/lldp_med_cmds.h include/lldp_dcbx_cfg.h include/lldp_evb_cmds.h \
+include/lldp_vdp_cmds.h
noinst_HEADERS = include/config.h include/ctrl_iface.h \
include/dcb_driver_if_types.h include/dcb_driver_interface.h \
@@ -47,7 +48,7 @@ include/event_iface.h include/messages.h include/parse_cli.h include/version.h \
include/lldptool_cli.h include/list.h \
include/lldp_mand_clif.h include/lldp_basman_clif.h include/lldp_med_clif.h \
include/lldp_8023_clif.h include/lldp_dcbx_clif.h include/lldptool.h \
-include/lldp_rtnl.h include/lldp_evb_clif.h
+include/lldp_rtnl.h include/lldp_evb_clif.h include/lldp_vdp_clif.h
lldpad_SOURCES = lldpad.c config.c drv_cfg.c ctrl_iface.c event_iface.c eloop.c \
common.c os_unix.c lldp_dcbx_cmds.c log.c lldpad_shm.c \
@@ -64,20 +65,21 @@ lldp_dcbx_cfg.c include/lldp_dcbx_cfg.h \
lldp_util.c include/lldp_util.h \
lldp_mand.c include/lldp_mand.h \
lldp_mand_cmds.c lldp_basman_cmds.c lldp_8023_cmds.c lldp_med_cmds.c \
-lldp_evb_cmds.c \
+lldp_evb_cmds.c lldp_vdp_cmds.c \
lldp_tlv.c include/lldp_tlv.h \
lldp_basman.c include/lldp_basman.h \
lldp_med.c include/lldp_med.h \
lldp_8023.c include/lldp_8023.h \
lldp_evb.c include/lldp_evb.h \
-lldp_vdp.c include/lldp_vdp.h
+lldp_vdp.c include/lldp_vdp.h \
+lldp_vdp_cmds.h
dcbtool_SOURCES = dcbtool.c clif.c dcbtool_cmds.c parse_cli.l \
$(lldpad_include_HEADERS) $(noinst_HEADERS)
lldptool_SOURCES = lldptool.c clif.c lldptool_cmds.c common.c os_unix.c \
lldp_mand_clif.c lldp_basman_clif.c lldp_med_clif.c lldp_8023_clif.c \
-lldp_dcbx_clif.c lldp_evb_clif.c $(lldpad_include_HEADERS) \
+lldp_dcbx_clif.c lldp_evb_clif.c lldp_vdp_clif.c $(lldpad_include_HEADERS) \
$(noinst_HEADERS)
nltest_SOURCES = nltest.c nltest.h
diff --git a/include/lldp_vdp_clif.h b/include/lldp_vdp_clif.h
new file mode 100644
index 0000000..adb6333
--- /dev/null
+++ b/include/lldp_vdp_clif.h
@@ -0,0 +1,39 @@
+/*******************************************************************************
+
+ implementation of VDP according to IEEE 802.1Qbg
+ (c) Copyright IBM Corp. 2010
+
+ Author(s): Jens Osterkamp <jens@linux.vnet.ibm.com>
+
+ 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.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+*******************************************************************************/
+
+#ifndef _LLDP_VDP_CLIF_H
+#define _LLDP_VDP_CLIF_H
+
+struct lldp_module *vdp_cli_register(void);
+void vdp_cli_unregister(struct lldp_module *);
+int vdp_print_tlv(u32, u16, char *);
+
+#define VDP_BUF_SIZE 256
+
+#define VDP_PREFIX "vdp"
+#define ARG_VDP_MODE "mode"
+#define ARG_VDP_ROLE "role"
+
+#endif
diff --git a/include/lldp_vdp_cmds.h b/include/lldp_vdp_cmds.h
new file mode 100644
index 0000000..5e64c27
--- /dev/null
+++ b/include/lldp_vdp_cmds.h
@@ -0,0 +1,44 @@
+/*******************************************************************************
+
+ implementation of VDP according to IEEE 802.1Qbg
+ (c) Copyright IBM Corp. 2010
+
+ Author(s): Jens Osterkamp <jens@linux.vnet.ibm.com>
+
+ 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.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+*******************************************************************************/
+
+#ifndef _LLDP_VDP_CMDS_H
+#define _LLDP_VDP_CMDS_H
+
+struct arg_handlers *vdp_get_arg_handlers();
+
+enum {
+ MODE = 0,
+ MGRID,
+ TYPEID,
+ TYPEIDVERSION,
+ INSTANCEID,
+ MAC,
+ VLAN,
+};
+
+#define VAL_STATION "station"
+#define VAL_BRIDGE "bridge"
+
+#endif
diff --git a/lldp_vdp_clif.c b/lldp_vdp_clif.c
new file mode 100644
index 0000000..aa357e9
--- /dev/null
+++ b/lldp_vdp_clif.c
@@ -0,0 +1,136 @@
+/*******************************************************************************
+
+ implementation of VDP according to IEEE 802.1Qbg
+ (c) Copyright IBM Corp. 2010
+
+ Author(s): Jens Osterkamp <jens@linux.vnet.ibm.com>
+
+ 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.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+*******************************************************************************/
+
+#include "includes.h"
+#include "common.h"
+#include <stdio.h>
+#include <syslog.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include "lldp_mod.h"
+#include "lldptool.h"
+#include "lldp.h"
+#include "lldp_vdp.h"
+#include "lldp_vdp_clif.h"
+
+void vdp_print_cfg_tlv(u16, char *info);
+int vdp_print_help();
+
+u32 vdp_lookup_tlv_name(char *tlvid_str);
+
+static const struct lldp_mod_ops vdp_ops_clif = {
+ .lldp_mod_register = vdp_cli_register,
+ .lldp_mod_unregister = vdp_cli_unregister,
+ .print_tlv = vdp_print_tlv,
+ .lookup_tlv_name = vdp_lookup_tlv_name,
+ .print_help = vdp_print_help,
+};
+
+struct type_name_info vdp_tlv_names[] = {
+ { ((LLDP_MOD_VDP) << 8) | LLDP_VDP_SUBTYPE,
+ "VDP protocol configuration",
+ "vdp", vdp_print_cfg_tlv },
+ { INVALID_TLVID, NULL, NULL }
+};
+
+int vdp_print_help()
+{
+ struct type_name_info *tn = &vdp_tlv_names[0];
+
+ while (tn->type != INVALID_TLVID) {
+ if (tn->key && strlen(tn->key) && tn->name) {
+ printf(" %s", tn->key);
+ if (strlen(tn->key)+3 <= 8)
+ printf("\t");
+ printf("\t: %s\n", tn->name);
+ }
+ tn++;
+ }
+
+ return 0;
+}
+
+struct lldp_module *vdp_cli_register(void)
+{
+ struct lldp_module *mod;
+
+ mod = malloc(sizeof(*mod));
+ if (!mod) {
+ fprintf(stderr, "failed to malloc module data\n");
+ return NULL;
+ }
+ mod->id = LLDP_MOD_VDP;
+ mod->ops = &vdp_ops_clif;
+
+ return mod;
+}
+
+void vdp_cli_unregister(struct lldp_module *mod)
+{
+ free(mod);
+}
+
+void vdp_print_cfg_tlv(u16 len, char *info)
+{
+ /* TODO: this should print out all associated VSI mac/vlan pairs */
+ printf("This should print out all associated VSI mac/vlan pairs !\n");
+
+ return;
+}
+
+/* return 1: if it printed the TLV
+ * 0: if it did not
+ */
+int vdp_print_tlv(u32 tlvid, u16 len, char *info)
+{
+ struct type_name_info *tn = &vdp_tlv_names[0];
+
+ while (tn->type != INVALID_TLVID) {
+ if (tlvid == tn->type) {
+ printf("%s\n", tn->name);
+ if (tn->print_info) {
+ printf("\t");
+ tn->print_info(len-4, info);
+ }
+ return 1;
+ }
+ tn++;
+ }
+
+ return 0;
+}
+
+u32 vdp_lookup_tlv_name(char *tlvid_str)
+{
+ struct type_name_info *tn = &vdp_tlv_names[0];
+
+ while (tn->type != INVALID_TLVID) {
+ if (!strcasecmp(tn->key, tlvid_str))
+ return tn->type;
+ tn++;
+ }
+ return INVALID_TLVID;
+}
+
diff --git a/lldp_vdp_cmds.c b/lldp_vdp_cmds.c
new file mode 100644
index 0000000..60afe0f
--- /dev/null
+++ b/lldp_vdp_cmds.c
@@ -0,0 +1,409 @@
+/******************************************************************************
+
+ implementation of VDP according to IEEE 802.1Qbg
+ (c) Copyright IBM Corp. 2010
+
+ Author(s): Jens Osterkamp <jens@linux.vnet.ibm.com>
+
+ 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.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+*******************************************************************************/
+
+#include "includes.h"
+#include "common.h"
+#include <stdio.h>
+#include <syslog.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <arpa/inet.h>
+#include "lldpad.h"
+#include "ctrl_iface.h"
+#include "lldp.h"
+#include "lldp_vdp.h"
+#include "lldp_mand_clif.h"
+#include "lldp_vdp_clif.h"
+#include "lldp_vdp_cmds.h"
+#include "lldp/ports.h"
+#include "messages.h"
+#include "libconfig.h"
+#include "config.h"
+#include "clif_msgs.h"
+#include "lldp/states.h"
+
+static int get_arg_tlvtxenable(struct cmd *, char *, char *, char *);
+static int set_arg_tlvtxenable(struct cmd *, char *, char *, char *);
+
+static int get_arg_mode(struct cmd *, char *, char *, char *);
+static int set_arg_mode(struct cmd *, char *, char *, char *);
+
+static int get_arg_role(struct cmd *, char *, char *, char *);
+static int set_arg_role(struct cmd *, char *, char *, char *);
+
+static struct arg_handlers arg_handlers[] = {
+ { ARG_VDP_MODE, get_arg_mode, set_arg_mode },
+ { ARG_VDP_ROLE, get_arg_role, set_arg_role },
+ { ARG_TLVTXENABLE, get_arg_tlvtxenable, set_arg_tlvtxenable },
+ { NULL }
+};
+
+static int get_arg_tlvtxenable(struct cmd *cmd, char *arg, char *argvalue,
+ char *obuf)
+{
+ int value;
+ char *s;
+ char arg_path[VDP_BUF_SIZE];
+
+ if (cmd->cmd != cmd_gettlv)
+ return cmd_invalid;
+
+ switch (cmd->tlvid) {
+ case ((LLDP_MOD_VDP) << 8) | LLDP_VDP_SUBTYPE:
+ snprintf(arg_path, sizeof(arg_path), "%s.%s",
+ VDP_PREFIX, arg);
+
+ if (get_cfg(cmd->ifname, arg_path, (void *)&value,
+ CONFIG_TYPE_BOOL))
+ value = false;
+ break;
+ case INVALID_TLVID:
+ return cmd_invalid;
+ default:
+ return cmd_not_applicable;
+ }
+
+ if (value)
+ s = VAL_YES;
+ else
+ s = VAL_NO;
+
+ sprintf(obuf, "%02x%s%04x%s", (unsigned int) strlen(arg), arg,
+ (unsigned int) strlen(s), s);
+
+ return cmd_success;
+}
+
+static int set_arg_tlvtxenable(struct cmd *cmd, char *arg, char *argvalue,
+ char *obuf)
+{
+ int value;
+ char arg_path[VDP_BUF_SIZE];
+
+ if (cmd->cmd != cmd_settlv)
+ return cmd_invalid;
+
+ switch (cmd->tlvid) {
+ case ((LLDP_MOD_VDP) << 8) | LLDP_VDP_SUBTYPE:
+ break;
+ case INVALID_TLVID:
+ return cmd_invalid;
+ default:
+ return cmd_not_applicable;
+ }
+
+ if (!strcasecmp(argvalue, VAL_YES))
+ value = 1;
+ else if (!strcasecmp(argvalue, VAL_NO))
+ value = 0;
+ else
+ return cmd_invalid;
+
+ snprintf(arg_path, sizeof(arg_path), "%s.%s", VDP_PREFIX, arg);
+
+ if (set_cfg(cmd->ifname, arg_path, (void *)&value, CONFIG_TYPE_BOOL))
+ return cmd_failed;
+
+ return cmd_success;
+}
+
+static int get_arg_mode(struct cmd *cmd, char *arg, char *argvalue,
+ char *obuf)
+{
+ char *s, *t;
+ struct vsi_profile *np;
+ struct vdp_data *vd;
+ int count=0;
+
+ if (cmd->cmd != cmd_gettlv)
+ return cmd_invalid;
+
+ switch (cmd->tlvid) {
+ case ((LLDP_MOD_VDP) << 8) | LLDP_VDP_SUBTYPE:
+ break;
+ case INVALID_TLVID:
+ return cmd_invalid;
+ default:
+ return cmd_not_applicable;
+ }
+
+ vd = vdp_data(cmd->ifname);
+ if (!vd) {
+ LLDPAD_ERR("%s(%i): vdp_data for %s not found !\n", __func__, __LINE__,
+ cmd->ifname);
+ free(t);
+ return cmd_invalid;
+ }
+
+ LIST_FOREACH(np, &vd->profile_head, profile) {
+ count++;
+ }
+
+ s = t = malloc(count*VDP_BUF_SIZE+1);
+ if (!s)
+ return cmd_invalid;
+ memset(s, 0, count*VDP_BUF_SIZE+1);
+
+ LIST_FOREACH(np, &vd->profile_head, profile) {
+ PRINT_PROFILE(t, np);
+ }
+
+ sprintf(obuf, "%02x%s%04x%s", (unsigned int) strlen(arg), arg,
+ (unsigned int) strlen(s), s);
+
+ free(s);
+
+ return cmd_success;
+}
+
+static void str2instance(struct vsi_profile *profile, char *buffer)
+{
+ int i, j = 0;
+ char instance[INSTANCE_STRLEN+2];
+
+ for(i=0; i <= strlen(buffer); i++) {
+ if (buffer[i] == '-') {
+ continue;
+ }
+
+ if ((sscanf(&buffer[i], "%02x", &profile->instance[j]) == 1) ||
+ (sscanf(&buffer[i], "%02X", &profile->instance[j]) == 1)) {
+ i++;
+ j++;
+ }
+ }
+}
+
+/* INSTANCE_STRLEN = strlen("fa9b7fff-b0a0-4893-abcd-beef4ff18f8f") */
+#define INSTANCE_STRLEN 36
+
+int instance2str(const u8 *p, char *dst, size_t size)
+{
+ if (dst && size > INSTANCE_STRLEN) {
+ snprintf(dst, size, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ p[0], p[1], p[2], p[3],
+ p[4], p[5], p[6], p[7],
+ p[8], p[9], p[10], p[11],
+ p[12], p[13], p[14], p[15]);
+ return 0;
+ }
+ return -1;
+}
+
+static void vdp_fill_profile(struct vsi_profile *profile, char *buffer, int field)
+{
+ switch(field) {
+ case MODE:
+ profile->mode = atoi(buffer);
+ break;
+ case MGRID:
+ profile->mgrid = atoi(buffer);
+ break;
+ case TYPEID:
+ profile->id = atoi(buffer);
+ break;
+ case TYPEIDVERSION:
+ profile->version = atoi(buffer);
+ break;
+ case INSTANCEID:
+ str2instance(profile, buffer);
+ break;
+ case MAC:
+ str2mac(buffer, &profile->mac[0], MAC_ADDR_LEN);
+ break;
+ case VLAN:
+ profile->vlan = atoi(buffer);
+ break;
+ default:
+ LLDPAD_ERR("Unknown field in buffer !\n");
+ break;
+ }
+}
+
+static struct vsi_profile *vdp_parse_mode_line(char * argvalue)
+{
+ int i, arglen, field;
+ char *cmdstring, *buf;
+ char *buffer;
+ struct vsi_profile *profile;
+
+ profile = malloc(sizeof(struct vsi_profile));
+ if (!profile)
+ return NULL;
+ memset(profile, 0, sizeof(struct vsi_profile));
+
+ arglen = strlen(argvalue);
+ cmdstring = argvalue;
+ buffer = malloc(arglen);
+ if (!buffer)
+ goto out_free;
+ buf = buffer;
+ field = 0;
+
+ for (i=0; i <= arglen; i++) {
+ *buffer = *cmdstring;
+
+ if ((*cmdstring == ',') || (*cmdstring == '\0')) {
+ *buffer++ = '\0';
+ vdp_fill_profile(profile, buf, field);
+ field++;
+ buffer = buf;
+ memset(buffer, 0, arglen);
+ cmdstring++;
+ continue;
+ }
+
+ buffer++;
+ cmdstring++;
+ }
+
+ free(buffer);
+
+ return profile;
+
+out_free:
+ free(profile);
+ return NULL;
+}
+
+static int set_arg_mode(struct cmd *cmd, char *arg, char *argvalue,
+ char *obuf)
+{
+ int arglen;
+ struct vsi_profile *profile, *p;
+
+ arglen = strlen(argvalue);
+
+ if (cmd->cmd != cmd_settlv)
+ return cmd_invalid;
+
+ switch (cmd->tlvid) {
+ case ((LLDP_MOD_VDP) << 8) | LLDP_VDP_SUBTYPE:
+ break;
+ case INVALID_TLVID:
+ return cmd_invalid;
+ default:
+ return cmd_not_applicable;
+ }
+
+ profile = vdp_parse_mode_line(argvalue);
+ profile->port = port_find_by_name(cmd->ifname);
+
+ if (!profile->port) {
+ free(profile);
+ return cmd_invalid;
+ }
+
+ p = vdp_add_profile(profile);
+
+ if (!p) {
+ free(profile);
+ return cmd_invalid;
+ }
+
+ vdp_somethingChangedLocal(profile, VDP_PROFILE_REQ);
+ vdp_vsi_sm_station(p);
+
+ return cmd_success;
+}
+
+static int get_arg_role(struct cmd *cmd, char *arg, char *argvalue,
+ char *obuf)
+{
+ char *p;
+ char arg_path[VDP_BUF_SIZE];
+
+ if (cmd->cmd != cmd_gettlv)
+ return cmd_invalid;
+
+ switch (cmd->tlvid) {
+ case ((LLDP_MOD_VDP) << 8) | LLDP_VDP_SUBTYPE:
+ snprintf(arg_path, sizeof(arg_path), "%s.%s",
+ VDP_PREFIX, arg);
+
+ if (get_cfg(cmd->ifname, arg_path, (void *)&p,
+ CONFIG_TYPE_STRING))
+ return cmd_failed;
+ break;
+ case INVALID_TLVID:
+ return cmd_invalid;
+ default:
+ return cmd_not_applicable;
+ }
+
+ sprintf(obuf, "%02x%s%04x%s", (unsigned int) strlen(arg), arg,
+ (unsigned int) strlen(p), p);
+
+ return cmd_success;
+}
+
+static int set_arg_role(struct cmd *cmd, char *arg, char *argvalue,
+ char *obuf)
+{
+ struct vdp_data *vd;
+ char arg_path[VDP_BUF_SIZE];
+
+ if (cmd->cmd != cmd_settlv)
+ return cmd_invalid;
+
+ switch (cmd->tlvid) {
+ case ((LLDP_MOD_VDP) << 8) | LLDP_VDP_SUBTYPE:
+ break;
+ case INVALID_TLVID:
+ return cmd_invalid;
+ default:
+ return cmd_not_applicable;
+ }
+
+ vd = vdp_data(cmd->ifname);
+
+ if (!vd) {
+ LLDPAD_ERR("%s(%i): could not find vdp_data for %s !\n",
+ __FILE__, __LINE__, cmd->ifname);
+ return cmd_invalid;
+ }
+
+ if (!strcasecmp(argvalue, VAL_BRIDGE)) {
+ vd->role = VDP_ROLE_BRIDGE;
+ } else if (!strcasecmp(argvalue, VAL_STATION)) {
+ vd->role = VDP_ROLE_STATION;
+ } else {
+ return cmd_invalid;
+ }
+
+ snprintf(arg_path, sizeof(arg_path), "%s.%s", VDP_PREFIX, arg);
+
+ char *p = &argvalue[0];
+ if (set_cfg(cmd->ifname, arg_path, (void *)&p, CONFIG_TYPE_STRING))
+ return cmd_failed;
+
+ return cmd_success;
+}
+
+
+struct arg_handlers *vdp_get_arg_handlers()
+{
+ return &arg_handlers[0];
+}
diff --git a/lldptool.c b/lldptool.c
index 7e166fe..bfed101 100644
--- a/lldptool.c
+++ b/lldptool.c
@@ -40,6 +40,7 @@
#include "lldp_8023_clif.h"
#include "lldp_dcbx_clif.h"
#include "lldp_evb_clif.h"
+#include "lldp_vdp_clif.h"
#include "lldptool.h"
#include "lldptool_cli.h"
#include "lldp_mod.h"
@@ -158,6 +159,7 @@ struct lldp_module *(*register_tlv_table[])(void) = {
med_cli_register,
dcbx_cli_register,
evb_cli_register,
+ vdp_cli_register,
NULL,
};
--
1.7.2.3
^ permalink raw reply related
* [PATCH 04/10] implementation of IEEE 802.1Qbg in lldpad, part 2
From: Jens Osterkamp @ 2010-11-22 15:50 UTC (permalink / raw)
To: e1000-eedc, virtualization, evb; +Cc: chrisw, Jens Osterkamp
In-Reply-To: <1290441057-16729-1-git-send-email-jens@linux.vnet.ibm.com>
This is the implementation of the edge control protocol (ECP) and VSI
discovery protocol (VDP) currently being specified in IEEE 802.1Qbg.
This implementation extends the infrastructure defined lldpad to send and
receive ECP frames with a new (yet to be defined) ethertype.
Received frames are validated and analyzed before the content is handed to the
upper layer protocol (ULP, VDP in this case) for further processing. Frames
to be transmitted are compiled from VSI (guest interface) profiles registered
on a interface.
Reception and transmission of ECP frames is controlled by RX and TX state
machines, timeouts are handled timeout functions.
The patch still contains a lot of debug code to allow low-level protocol
analysis.
VDP serves as the upper layer protocol (ULP) for TLVs communicated via the
ECP protocol.
For this it registers as a new module in lldpad. The VDP module supports a
station and a bridge role. As a station, new VSI (virtual station interface)
profiles can be registered to the VDP module using lldptool or libvirt.
These profiles are then announced to an adjacent switch. Transmitted profiles
are processed to the desired state by the VDP station state machine.
As a bridge, the VDP module waits for new profiles received in TLVs by ECP.
The received profiles are processed to the desired state by a VDP bridge
state machine.
VDP module parameters are stored in the "vdp" section under the appropriate
interface.
The patch still contains a lot of debug code to allow analysis of VDP
protocol behavior.
Signed-off-by: Jens Osterkamp <jens@linux.vnet.ibm.com>
---
Makefile.am | 10 +-
ecp/ecp.c | 102 +++++
ecp/ecp.h | 101 +++++
ecp/ecp_rx.c | 618 ++++++++++++++++++++++++++
ecp/ecp_tx.c | 487 +++++++++++++++++++++
include/lldp.h | 1 +
include/lldp_evb.h | 6 +
include/lldp_vdp.h | 182 ++++++++
lldp/l2_packet.h | 2 +
lldp/ports.h | 11 +-
lldp_vdp.c | 1224 ++++++++++++++++++++++++++++++++++++++++++++++++++++
lldpad.c | 2 +
12 files changed, 2739 insertions(+), 7 deletions(-)
create mode 100644 ecp/ecp.c
create mode 100644 ecp/ecp.h
create mode 100644 ecp/ecp_rx.c
create mode 100644 ecp/ecp_tx.c
create mode 100644 include/lldp_vdp.h
create mode 100644 lldp_vdp.c
diff --git a/Makefile.am b/Makefile.am
index d59a6fa..4b69389 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -56,6 +56,8 @@ $(lldpad_include_HEADERS) $(noinst_HEADERS) \
lldp/ports.c lldp/agent.c lldp/l2_packet_linux.c lldp/tx.c \
lldp/rx.c lldp/agent.h lldp/l2_packet.h lldp/mibdata.h lldp/ports.h \
lldp/states.h \
+ecp/ecp.c ecp/ecp_tx.c \
+ecp/ecp_rx.c \
include/lldp.h include/lldp_mod.h \
lldp_dcbx.c include/lldp_dcbx.h tlv_dcbx.c include/tlv_dcbx.h \
lldp_dcbx_cfg.c include/lldp_dcbx_cfg.h \
@@ -67,16 +69,16 @@ lldp_tlv.c include/lldp_tlv.h \
lldp_basman.c include/lldp_basman.h \
lldp_med.c include/lldp_med.h \
lldp_8023.c include/lldp_8023.h \
-lldp_evb.c include/lldp_evb.h
-
-
+lldp_evb.c include/lldp_evb.h \
+lldp_vdp.c include/lldp_vdp.h
dcbtool_SOURCES = dcbtool.c clif.c dcbtool_cmds.c parse_cli.l \
$(lldpad_include_HEADERS) $(noinst_HEADERS)
lldptool_SOURCES = lldptool.c clif.c lldptool_cmds.c common.c os_unix.c \
lldp_mand_clif.c lldp_basman_clif.c lldp_med_clif.c lldp_8023_clif.c \
-lldp_dcbx_clif.c lldp_evb_clif.c $(lldpad_include_HEADERS) $(noinst_HEADERS)
+lldp_dcbx_clif.c lldp_evb_clif.c $(lldpad_include_HEADERS) \
+$(noinst_HEADERS)
nltest_SOURCES = nltest.c nltest.h
diff --git a/ecp/ecp.c b/ecp/ecp.c
new file mode 100644
index 0000000..44c3ba0
--- /dev/null
+++ b/ecp/ecp.c
@@ -0,0 +1,102 @@
+/*******************************************************************************
+
+ implementation of ECP according to 802.1Qbg
+ (c) Copyright IBM Corp. 2010
+
+ Author(s): Jens Osterkamp <jens@linux.vnet.ibm.com>
+
+ 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.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+*******************************************************************************/
+
+#include <net/if.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/utsname.h>
+#include <linux/if_bridge.h>
+#include "lldp.h"
+#include "lldp_evb.h"
+#include "lldp_vdp.h"
+#include "messages.h"
+#include "config.h"
+#include "common.h"
+#include "lldp/l2_packet.h"
+#include "ecp/ecp.h"
+
+/* ecp_init - initialize ecp module
+ * @ifname: interface for which the module is initialized
+ *
+ * returns 0 on success, -1 on error
+ *
+ * finds the port to the interface name, sets up the receive handle for
+ * incoming ecp frames and initializes the ecp rx and tx state machines.
+ * should usually be called when a successful exchange of EVB TLVs has been
+ * made and ECP and VDP protocols are supported by both sides.
+ */
+int ecp_init(char *ifname)
+{
+ struct vdp_data *vd;
+
+ LLDPAD_DBG("%s(%i): starting ECP for if %s !\n", __func__, __LINE__, ifname);
+
+ vd = vdp_data(ifname);
+
+ if (!vd) {
+ LLDPAD_ERR("%s(%i): unable to find vd %s ! \n", __func__, __LINE__, ifname);
+ goto fail;
+ }
+
+ vd->ecp.l2 = l2_packet_init(vd->ifname, NULL, ETH_P_ECP,
+ ecp_rx_ReceiveFrame, vd, 1);
+ if (!vd->ecp.l2) {
+ LLDPAD_ERR("ERROR: Failed to open register layer 2 access to "
+ "ETH_P_ECP\n");
+ goto fail;
+ }
+
+ ecp_tx_run_sm(vd);
+ ecp_rx_run_sm(vd);
+
+ return 0;
+
+fail:
+ return -1;
+}
+
+int ecp_deinit(char *ifname)
+{
+ struct vdp_data *vd;
+
+ LLDPAD_DBG("%s(%i): stopping ECP for if %s !\n", __func__, __LINE__, ifname);
+
+ vd = vdp_data(ifname);
+
+ if (!vd) {
+ LLDPAD_ERR("%s(%i): unable to find vd %s ! \n", __func__, __LINE__, ifname);
+ goto fail;
+ }
+
+ ecp_tx_stop_ackTimer(vd);
+
+ l2_packet_deinit(vd->ecp.l2);
+
+ return 0;
+
+fail:
+ return -1;
+}
diff --git a/ecp/ecp.h b/ecp/ecp.h
new file mode 100644
index 0000000..cc9ca2b
--- /dev/null
+++ b/ecp/ecp.h
@@ -0,0 +1,101 @@
+/*******************************************************************************
+
+ implementation of ECP according to 802.1Qbg
+ (c) Copyright IBM Corp. 2010
+
+ Author(s): Jens Osterkamp <jens@linux.vnet.ibm.com>
+
+ 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.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+*******************************************************************************/
+
+#ifndef _ECP_H
+#define _ECP_H
+
+#include "lldp_mod.h"
+#include "include/lldp_vdp.h"
+
+#define ECP_SUBTYPE 0x0
+
+#define ECP_MAX_RETRIES 3
+#define ECP_SEQUENCE_NR_START 0x0
+
+#define ECP_TRANSMISSION_TIMER EVB_RTM(RTE)*EVB_RTG
+#define ECP_TRANSMISSION_DIVIDER 10000
+
+typedef enum {
+ ECP_REQUEST = 0,
+ ECP_ACK
+} ecp_mode;
+
+struct ecp {
+ struct l2_packet_data *l2;
+ int sequence;
+ int retries;
+ int ackReceived;
+ int ackTimerExpired;
+ u16 lastSequence;
+ u16 seqECPDU;
+ struct portrx rx;
+ struct porttx tx;
+ struct portstats stats;
+};
+
+struct ecp_hdr {
+ u8 oui[3];
+ u8 pad1;
+ u16 subtype;
+ u8 mode;
+ u16 seqnr;
+} __attribute__ ((__packed__));
+
+enum {
+ ECP_TX_INIT_TRANSMIT,
+ ECP_TX_TRANSMIT_ECPDU,
+ ECP_TX_WAIT_FOR_ACK,
+ ECP_TX_REQUEST_PDU
+};
+
+static const char *ecp_tx_states[] = {
+ "ECP_TX_IDLE",
+ "ECP_TX_INIT_TRANSMIT",
+ "ECP_TX_TRANSMIT_ECPDU",
+ "ECP_TX_WAIT_FOR_ACK",
+ "ECP_TX_REQUEST_PDU"
+};
+
+enum {
+ ECP_RX_IDLE,
+ ECP_RX_INIT_RECEIVE,
+ ECP_RX_RECEIVE_WAIT,
+ ECP_RX_RECEIVE_ECPDU,
+ ECP_RX_SEND_ACK,
+ ECP_RX_RESEND_ACK,
+};
+
+static const char *ecp_rx_states[] = {
+ "ECP_RX_IDLE",
+ "ECP_RX_INIT_RECEIVE",
+ "ECP_RX_RECEIVE_WAIT",
+ "ECP_RX_RECEIVE_ECPDU",
+ "ECP_RX_SEND_ACK",
+ "ECP_RX_RESEND_ACK",
+};
+
+void ecp_rx_ReceiveFrame(void *, unsigned int, const u8 *, size_t );
+
+#endif /* _ECP_H */
diff --git a/ecp/ecp_rx.c b/ecp/ecp_rx.c
new file mode 100644
index 0000000..830e1dc
--- /dev/null
+++ b/ecp/ecp_rx.c
@@ -0,0 +1,618 @@
+/*******************************************************************************
+
+ implementation of ECP according to 802.1Qbg
+ (c) Copyright IBM Corp. 2010
+
+ Author(s): Jens Osterkamp <jens@linux.vnet.ibm.com>
+
+ 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.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+*******************************************************************************/
+
+#include "dcb_osdep.h"
+#include "lldp/ports.h"
+#include "lldp/l2_packet.h"
+#include "messages.h"
+#include "lldp.h"
+#include "lldpad.h"
+#include "lldp_mod.h"
+#include "clif_msgs.h"
+#include "lldp_mand.h"
+#include "lldp_vdp.h"
+
+/* ecp_rx_Initialize - initializes the ecp rx state machine
+ * @vd: vd for the state machine
+ *
+ * no return value
+ *
+ * initialize some variables, get rid of old frame if necessary
+ */
+void ecp_rx_Initialize(struct vdp_data *vd)
+{
+ vd->ecp.rx.rcvFrame = false;
+ vd->ecp.rx.badFrame = false;
+
+ vd->ecp.ackReceived = 0;
+
+ if (vd->ecp.rx.framein) {
+ free(vd->ecp.rx.framein);
+ vd->ecp.rx.framein = NULL;
+ }
+ vd->ecp.rx.sizein = 0;
+
+ return;
+}
+
+/* ecp_rx_freeFrame - free up received frame
+ * @vd: vd for the state machine
+ *
+ * no return value
+ *
+ * frees up an old received frame, set pointer to NULL and size to 0.
+ */
+void ecp_rx_freeFrame(struct vdp_data *vd)
+{
+ free(vd->ecp.rx.framein);
+ vd->ecp.rx.framein = NULL;
+ vd->ecp.rx.sizein = 0;
+}
+
+/* ecp_print_framein - print raw received frame
+ * @vd: vd for the state machine
+ *
+ * no return value
+ *
+ * prints out a raw version of a received frame. useful for low-level protocol
+ * debugging.
+ */
+void ecp_print_framein(struct vdp_data *vd)
+{
+ int i;
+ char *s, *t;
+
+ s = t = malloc(256);
+
+ if (!s) {
+ LLDPAD_ERR("%s(%i): unable to allocate string !\n", __func__, __LINE__);
+ }
+
+ for (i=0; i < vd->ecp.rx.sizein; i++) {
+ int c;
+ c = sprintf(s, "%02x ", vd->ecp.rx.framein[i]);
+ s += c;
+ if (!((i+1) % 16)) {
+ LLDPAD_DBG("%s\n", t);
+ s = t;
+ }
+ }
+
+ LLDPAD_DBG("%s\n", t);
+
+ free(t);
+}
+
+/* ecp_rx_SendAckFrame - send ack frame
+ * @vd: port used by ecp
+ *
+ * currently always returns 0
+ *
+ * copies current received frame over to frame out, fills in address of this
+ * port and set mode field to ACK. used by ecp_rx_send_ack_frame.
+ */
+int ecp_rx_SendAckFrame(struct vdp_data *vd)
+{
+ u16 tlv_offset = 0;
+ struct ecp_hdr *ecp_hdr;
+ struct l2_ethhdr *hdr;
+ u8 own_addr[ETH_ALEN];
+
+ LLDPAD_DBG("%s(%i)-%s: acking frame \n", __func__, __LINE__, vd->ifname);
+
+ assert(vd->ecp.rx.framein && vd->ecp.rx.sizein);
+
+ /* copy over to frameout */
+ vd->ecp.tx.frameout = (u8 *)malloc(ETH_FRAME_LEN);
+ memcpy(vd->ecp.tx.frameout, vd->ecp.rx.framein, vd->ecp.rx.sizein);
+ vd->ecp.tx.sizeout = vd->ecp.rx.sizein;
+
+ /* use my own addr to send ACK */
+ hdr = (struct l2_ethhdr *)vd->ecp.tx.frameout;
+ l2_packet_get_own_src_addr(vd->ecp.l2,(u8 *)&own_addr);
+ memcpy(hdr->h_source, &own_addr, ETH_ALEN);
+
+ tlv_offset = sizeof(struct l2_ethhdr);
+ ecp_hdr = (struct ecp_hdr *)&vd->ecp.tx.frameout[tlv_offset];
+ ecp_hdr->mode = ECP_ACK;
+
+ tlv_offset = sizeof(struct l2_ethhdr) + sizeof(struct ecp_hdr);
+ LLDPAD_DBG("%s(%i)-%s: zeroing out rest of ack frame from %i to %i.\n",
+ __func__, __LINE__, vd->ifname, tlv_offset, vd->ecp.rx.sizein);
+ memset(&vd->ecp.tx.frameout[tlv_offset], 0, vd->ecp.rx.sizein-tlv_offset);
+
+ return 0;
+}
+
+/* ecp_rx_send_ack_frame - send out ack frame for received frame
+ * @vd: vd for the state machine
+ *
+ * no return value
+ *
+ * creates an ack frame for a just received frame, prints the about to be
+ * sent frame and finally transmits it.
+ */
+void ecp_rx_send_ack_frame(struct vdp_data *vd)
+{
+ ecp_rx_SendAckFrame(vd);
+
+ ecp_print_frameout(vd);
+
+ ecp_txFrame(vd);
+
+ return;
+}
+
+/* ecp_rx_ReceiveFrame - receive ecp frame
+ * @ctx: rx callback context, struct vd * in this case
+ * @ifindex: index of interface
+ * @buf: buffer which contains the frame just received
+ * @len: size of buffer (frame)
+ *
+ * no return value
+ *
+ * creates a local copy of the buffer and checks the header. keeps some
+ * statistics about ecp frames. Checks if it is a request or an ack frame and branches
+ * to ecp rx or ecp tx state machine.
+ */
+void ecp_rx_ReceiveFrame(void *ctx, unsigned int ifindex, const u8 *buf, size_t len)
+{
+
+ struct vdp_data *vd = NULL;
+ struct port *port;
+ u8 frame_error = 0;
+ u16 tlv_offset;
+ struct l2_ethhdr *hdr;
+ struct l2_ethhdr example_hdr,*ex;
+ struct ecp_hdr *ecp_hdr;
+
+ if (ctx)
+ vd = (struct vdp_data *)ctx;
+
+ port = port_find_by_name(vd->ifname);
+
+ LLDPAD_DBG("%s(%i)-%s: received packet with size %i\n", __func__, __LINE__,
+ vd->ifname, (int) len);
+
+ if (port->adminStatus == disabled || port->adminStatus == enabledTxOnly)
+ return;
+
+ if (vd->ecp.rx.framein &&
+ vd->ecp.rx.sizein == len &&
+ (memcmp(buf, vd->ecp.rx.framein, len) == 0)) {
+ vd->ecp.stats.statsFramesInTotal++;
+ return;
+ }
+
+ if (vd->ecp.rx.framein)
+ free(vd->ecp.rx.framein);
+
+ vd->ecp.rx.framein = (u8 *)malloc(len);
+ if (vd->ecp.rx.framein == NULL) {
+ LLDPAD_ERR("ERROR - could not allocate memory for rx'ed frame\n");
+ return;
+ }
+ memset(vd->ecp.rx.framein, 0, len);
+ memcpy(vd->ecp.rx.framein, buf, len);
+
+ vd->ecp.rx.sizein = (u16)len;
+ ex = &example_hdr;
+ memcpy(ex->h_dest, multi_cast_source, ETH_ALEN);
+ ex->h_proto = htons(ETH_P_ECP);
+ hdr = (struct l2_ethhdr *)vd->ecp.rx.framein;
+
+ if ((memcmp(hdr->h_dest,ex->h_dest, ETH_ALEN) != 0)) {
+ LLDPAD_ERR("ERROR multicast address error in incoming frame. "
+ "Dropping frame.\n");
+ frame_error++;
+ free(vd->ecp.rx.framein);
+ vd->ecp.rx.framein = NULL;
+ vd->ecp.rx.sizein = 0;
+ return;
+ }
+
+ if (hdr->h_proto != example_hdr.h_proto) {
+ LLDPAD_ERR("ERROR Ethertype not ECP ethertype but ethertype "
+ "'%x' in incoming frame.\n", htons(hdr->h_proto));
+ frame_error++;
+ free(vd->ecp.rx.framein);
+ vd->ecp.rx.framein = NULL;
+ vd->ecp.rx.sizein = 0;
+ return;
+ }
+
+ if (!frame_error) {
+ vd->ecp.stats.statsFramesInTotal++;
+ vd->ecp.rx.rcvFrame = 1;
+ }
+
+ tlv_offset = sizeof(struct l2_ethhdr);
+
+ ecp_hdr = (struct ecp_hdr *)&vd->ecp.rx.framein[tlv_offset];
+
+ vd->ecp.seqECPDU = ntohs(ecp_hdr->seqnr);
+
+ switch(ecp_hdr->mode) {
+ case ECP_REQUEST:
+ vd->ecp.ackReceived = false;
+ ecp_print_framein(vd);
+ ecp_rx_run_sm(vd);
+ break;
+ case ECP_ACK:
+ vd->ecp.ackReceived = true;
+ LLDPAD_DBG("%s(%i)-%s: received ack frame \n", __func__, __LINE__, vd->ifname);
+ ecp_print_framein(vd);
+ ecp_tx_run_sm(vd);
+ vd->ecp.ackReceived = false;
+ break;
+ default:
+ LLDPAD_ERR("ERROR: unknown mode %i!\n", ecp_hdr->mode);
+ return;
+ }
+
+ ecp_rx_freeFrame(vd);
+}
+
+/* ecp_rx_validateFrame - validates received frame
+ * @vd: vdp_data used by ecp
+ *
+ * no return value
+ *
+ * checks wether received frame has correct subtype and mode
+ */
+
+void ecp_rx_validateFrame(struct vdp_data *vd)
+{
+ u16 tlv_offset = 0;
+ struct ecp_hdr *ecp_hdr;
+
+ LLDPAD_DBG("%s(%i)-%s: validating frame \n", __func__, __LINE__, vd->ifname);
+
+ assert(vd->ecp.rx.framein && vd->ecp.rx.sizein);
+
+ tlv_offset = sizeof(struct l2_ethhdr);
+
+ ecp_hdr = (struct ecp_hdr *)&vd->ecp.rx.framein[tlv_offset];
+
+ LLDPAD_DBG("%s(%i)-%s: ecp packet with subtype %04x, mode %02x, sequence nr %04x\n",
+ __func__, __LINE__, vd->ifname, ecp_hdr->subtype, ecp_hdr->mode, ntohs(ecp_hdr->seqnr));
+
+ if (ecp_hdr->subtype != ECP_SUBTYPE) {
+ LLDPAD_ERR("ERROR: unknown subtype !\n");
+ return;
+ }
+
+ if ((ecp_hdr->oui[0] != 0x0) || (ecp_hdr->oui[1] != 0x1b) ||
+ (ecp_hdr->oui[2] != 0x3f)) {
+ LLDPAD_ERR("ERROR: incorrect OUI 0x%02x%02x%02x !\n",
+ ecp_hdr->oui[0], ecp_hdr->oui[1], ecp_hdr->oui[2]);
+ return;
+ }
+
+ switch(ecp_hdr->mode) {
+ case ECP_REQUEST:
+ break;
+ case ECP_ACK:
+ break;
+ default:
+ LLDPAD_ERR("ERROR: unknown mode %i!\n", ecp_hdr->mode);
+ return;
+ }
+
+ /* FIXME: also done in ecp_rx_ReceiveFrame,
+ * are both necessary ? */
+ vd->ecp.seqECPDU = ntohs(ecp_hdr->seqnr);
+}
+
+/* ecp_rx_validate_frame - wrapper around ecp_rx_validateFrame
+ * @vd: currently used port
+ *
+ * no return value
+ *
+ * sets rcvFrame to false and validates frame. used in ECP_RX_RECEIVE_ECPDU
+ * state of ecp_rx_run_sm
+ */
+void ecp_rx_validate_frame(struct vdp_data *vd)
+{
+ vd->ecp.rx.rcvFrame = false;
+ ecp_rx_validateFrame(vd);
+ return;
+}
+
+/* ecp_rx_ProcessFrame - process received ecp frames
+ * @vd: currently used port
+ *
+ * no return value
+ *
+ * walks through the packed vsi tlvs in an ecp frame, extracts them
+ * and passes them to the VDP ULP with vdp_indicate.
+ */
+void ecp_rx_ProcessFrame(struct vdp_data *vd)
+{
+ u16 tlv_cnt = 0;
+ u8 tlv_type = 0;
+ u16 tlv_length = 0;
+ u16 tlv_offset = 0;
+ u16 *tlv_head_ptr = NULL;
+ u8 frame_error = 0;
+ bool tlv_stored = false;
+ struct ecp_hdr *ecp_hdr;
+
+ LLDPAD_DBG("%s(%i)-%s: processing frame \n", __func__, __LINE__, vd->ifname);
+
+ assert(vd->ecp.rx.framein && vd->ecp.rx.sizein);
+
+ tlv_offset = sizeof(struct l2_ethhdr);
+
+ ecp_hdr = (struct ecp_hdr *)&vd->ecp.rx.framein[tlv_offset];
+
+ LLDPAD_DBG("%s(%i)-%s: ecp packet with subtype %04x, mode %02x, sequence nr %04x\n",
+ __func__, __LINE__, vd->ifname, ecp_hdr->subtype, ecp_hdr->mode, ntohs(ecp_hdr->seqnr));
+
+ if (ecp_hdr->mode == ECP_ACK)
+ return;
+
+ /* processing of VSI_TLVs starts here */
+
+ tlv_offset += sizeof(struct ecp_hdr);
+
+ do {
+ tlv_cnt++;
+
+ if (tlv_offset > vd->ecp.rx.sizein) {
+ LLDPAD_ERR("%s(%i)-%s: ERROR: Frame overrun! tlv_offset %i, sizein %i cnt %i\n",
+ __func__, __LINE__, vd->ifname, tlv_offset, vd->ecp.rx.sizein, tlv_cnt);
+ frame_error++;
+ goto out;
+ }
+
+ tlv_head_ptr = (u16 *)&vd->ecp.rx.framein[tlv_offset];
+ tlv_length = htons(*tlv_head_ptr) & 0x01FF;
+ tlv_type = (u8)(htons(*tlv_head_ptr) >> 9);
+
+ u16 tmp_offset = tlv_offset + tlv_length;
+ if (tmp_offset > vd->ecp.rx.sizein) {
+ LLDPAD_ERR("ERROR: Frame overflow error: offset=%d, "
+ "rx.size=%d \n", tmp_offset, vd->ecp.rx.sizein);
+ frame_error++;
+ goto out;
+ }
+
+ u8 *info = (u8 *)&vd->ecp.rx.framein[tlv_offset +
+ sizeof(*tlv_head_ptr)];
+
+ struct unpacked_tlv *tlv = create_tlv();
+
+ if (!tlv) {
+ LLDPAD_ERR("ERROR: Failed to malloc space for "
+ "incoming TLV. \n");
+ goto out;
+ }
+
+ if ((tlv_length == 0) && (tlv->type != TYPE_0)) {
+ LLDPAD_ERR("ERROR: tlv_length == 0\n");
+ free_unpkd_tlv(tlv);
+ goto out;
+ }
+
+ tlv->type = tlv_type;
+ tlv->length = tlv_length;
+ tlv->info = (u8 *)malloc(tlv_length);
+ if (tlv->info) {
+ memset(tlv->info,0, tlv_length);
+ memcpy(tlv->info, info, tlv_length);
+ } else {
+ LLDPAD_ERR("ERROR: Failed to malloc space for incoming "
+ "TLV info \n");
+ free_unpkd_tlv(tlv);
+ goto out;
+ }
+
+ /* Validate the TLV */
+ tlv_offset += sizeof(*tlv_head_ptr) + tlv_length;
+
+ if (tlv->type == TYPE_127) { /* private TLV */
+ /* give VSI TLV to VDP */
+ if (!vdp_indicate(vd, tlv, ecp_hdr->mode))
+ tlv_stored = true;
+ else {
+ /* TODO: put it in a list and try again later until
+ * timer and retries have expired */
+ tlv_stored = false;
+ }
+ }
+
+ if ((tlv->type != TYPE_0) && !tlv_stored) {
+ LLDPAD_DBG("\n%s: allocated TLV (%u) "
+ " was not stored! (%p)\n", __func__, tlv->type,
+ tlv);
+ tlv = free_unpkd_tlv(tlv);
+ vd->ecp.stats.statsTLVsUnrecognizedTotal++;
+ }
+
+ tlv = NULL;
+ tlv_stored = false;
+
+ } while (tlv_offset < vd->ecp.rx.sizein);
+
+out:
+ if (frame_error) {
+ vd->ecp.stats.statsFramesDiscardedTotal++;
+ vd->ecp.stats.statsFramesInErrorsTotal++;
+ vd->ecp.rx.badFrame = true;
+ }
+
+ return;
+}
+
+/* ecp_rx_change_state - changes the ecp rx sm state
+ * @vd: currently used port
+ * @newstate: new state for the sm
+ *
+ * no return value
+ *
+ * checks state transistion for consistency and finally changes the state of
+ * the profile.
+ */
+void ecp_rx_change_state(struct vdp_data *vd, u8 newstate)
+{
+ switch(newstate) {
+ case ECP_RX_IDLE:
+ break;
+ case ECP_RX_INIT_RECEIVE:
+ break;
+ case ECP_RX_RECEIVE_WAIT:
+ assert((vd->ecp.rx.state == ECP_RX_INIT_RECEIVE) ||
+ (vd->ecp.rx.state == ECP_RX_IDLE) ||
+ (vd->ecp.rx.state == ECP_RX_SEND_ACK) ||
+ (vd->ecp.rx.state == ECP_RX_RESEND_ACK));
+ break;
+ case ECP_RX_RECEIVE_ECPDU:
+ assert(vd->ecp.rx.state == ECP_RX_RECEIVE_WAIT);
+ break;
+ case ECP_RX_SEND_ACK:
+ assert(vd->ecp.rx.state == ECP_RX_RECEIVE_ECPDU);
+ break;
+ case ECP_RX_RESEND_ACK:
+ assert(vd->ecp.rx.state == ECP_RX_RECEIVE_ECPDU);
+ break;
+ default:
+ LLDPAD_ERR("ERROR: The ECP_RX State Machine is broken!\n");
+ log_message(MSG_ERR_RX_SM_INVALID, "%s", vd->ifname);
+ }
+
+ LLDPAD_DBG("%s(%i)-%s: state change %s -> %s\n", __func__, __LINE__,
+ vd->ifname, ecp_rx_states[vd->ecp.rx.state], ecp_rx_states[newstate]);
+
+ vd->ecp.rx.state = newstate;
+}
+
+/* ecp_set_rx_state - sets the ecp rx sm state
+ * @vd: currently used port
+ *
+ * returns true or false
+ *
+ * switches the state machine to the next state depending on the input
+ * variables. returns true or false depending on wether the state machine
+ * can be run again with the new state or can stop at the current state.
+ */
+bool ecp_set_rx_state(struct vdp_data *vd)
+{
+ struct port *port = port_find_by_name(vd->ifname);
+
+ if (port->portEnabled == false) {
+ ecp_rx_change_state(vd, ECP_RX_IDLE);
+ }
+
+ switch(vd->ecp.rx.state) {
+ case ECP_RX_IDLE:
+ if (port->portEnabled == true) {
+ ecp_rx_change_state(vd, ECP_RX_INIT_RECEIVE);
+ return true;
+ }
+ return false;
+ case ECP_RX_INIT_RECEIVE:
+ if ((port->adminStatus == enabledRxTx) ||
+ (port->adminStatus == enabledRxOnly)) {
+ ecp_rx_change_state(vd, ECP_RX_RECEIVE_WAIT);
+ return true;
+ }
+ return false;
+ case ECP_RX_RECEIVE_WAIT:
+ if ((port->adminStatus == disabled) ||
+ (port->adminStatus == enabledTxOnly)) {
+ ecp_rx_change_state(vd, ECP_RX_IDLE);
+ return true;
+ }
+ if (vd->ecp.rx.rcvFrame == true) {
+ ecp_rx_change_state(vd, ECP_RX_RECEIVE_ECPDU);
+ return true;
+ }
+ return false;
+ case ECP_RX_RECEIVE_ECPDU:
+ if (vd->ecp.seqECPDU == vd->ecp.lastSequence) {
+ LLDPAD_DBG("%s(%i):-(%s) seqECPDU %x, lastSequence %x\n", __func__, __LINE__,
+ vd->ifname, vd->ecp.seqECPDU, vd->ecp.lastSequence);
+ ecp_rx_change_state(vd, ECP_RX_RESEND_ACK);
+ return true;
+ }
+ if (vd->ecp.seqECPDU != vd->ecp.lastSequence) {
+ ecp_rx_change_state(vd, ECP_RX_RESEND_ACK);
+ return true;
+ }
+ return false;
+ case ECP_RX_SEND_ACK:
+ case ECP_RX_RESEND_ACK:
+ ecp_rx_change_state(vd, ECP_RX_RECEIVE_WAIT);
+ return false;
+ default:
+ LLDPAD_ERR("ERROR: The ECP_RX State Machine is broken!\n");
+ log_message(MSG_ERR_RX_SM_INVALID, "%s", vd->ifname);
+ return false;
+ }
+}
+
+/* ecp_rx_run_sm - state machine for ecp rx
+ * @vd: currently used port
+ *
+ * no return value
+ *
+ * runs the state machine for ecp rx.
+ */
+void ecp_rx_run_sm(struct vdp_data *vd)
+{
+ ecp_set_rx_state(vd);
+ do {
+ LLDPAD_DBG("%s(%i)-%s: ecp_rx - %s\n", __func__, __LINE__,
+ vd->ifname, ecp_rx_states[vd->ecp.tx.state]);
+
+ switch(vd->ecp.rx.state) {
+ case ECP_RX_IDLE:
+ break;
+ case ECP_RX_INIT_RECEIVE:
+ ecp_rx_Initialize(vd);
+ break;
+ case ECP_RX_RECEIVE_WAIT:
+ break;
+ case ECP_RX_RECEIVE_ECPDU:
+ ecp_rx_validate_frame(vd);
+ break;
+ case ECP_RX_SEND_ACK:
+ ecp_rx_ProcessFrame(vd);
+ break;
+ case ECP_RX_RESEND_ACK:
+ ecp_rx_ProcessFrame(vd);
+ if (!vd->ecp.ackReceived) {
+ ecp_rx_send_ack_frame(vd);
+ ecp_rx_freeFrame(vd);
+ }
+ break;
+ default:
+ LLDPAD_ERR("ERROR: The ECP_RX State Machine is broken!\n");
+ log_message(MSG_ERR_TX_SM_INVALID, "%s", vd->ifname);
+ }
+ } while (ecp_set_rx_state(vd) == true);
+
+}
diff --git a/ecp/ecp_tx.c b/ecp/ecp_tx.c
new file mode 100644
index 0000000..1db9ac3
--- /dev/null
+++ b/ecp/ecp_tx.c
@@ -0,0 +1,487 @@
+/*******************************************************************************
+
+ implementation of ECP according to 802.1Qbg
+ (c) Copyright IBM Corp. 2010
+
+ Author(s): Jens Osterkamp <jens@linux.vnet.ibm.com>
+
+ 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.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+*******************************************************************************/
+
+#include "dcb_osdep.h"
+#include "lldp/ports.h"
+#include "lldp/l2_packet.h"
+#include "eloop.h"
+#include "messages.h"
+#include "lldpad.h"
+#include "lldp_tlv.h"
+#include "lldp_mod.h"
+#include "lldp_mand.h"
+#include "lldp_evb.h"
+#include "include/lldp_vdp.h"
+
+void ecp_tx_run_sm(struct vdp_data *);
+
+/* ecp_somethingChangedLocal - set flag if port has changed
+ * @vd: port to set the flag for
+ * @mode: mode to set the flag to
+ *
+ * no return value
+ *
+ * set the localChange flag with a mode to indicate a port has changed.
+ * used to signal an ecpdu needs to be sent out.
+ */
+
+void ecp_somethingChangedLocal(struct vdp_data *vd)
+{
+ if (!vd)
+ return;
+
+ vd->ecp.tx.localChange = 1;
+
+ return;
+}
+
+/* ecp_print_frameout - print outbound frame
+ * @vd: currently used port
+ *
+ * no return value
+ *
+ * prints a raw dump of an outbound ecp frame. useful for low-level protocol
+ * debugging.
+ */
+void ecp_print_frameout(struct vdp_data *vd)
+{
+ int i;
+ char *s, *t;
+
+ s = t = malloc(256);
+
+ if (!s) {
+ LLDPAD_ERR("%s(%i): unable to allocate string !\n", __func__, __LINE__);
+ }
+
+ for (i=0; i < vd->ecp.tx.sizeout; i++) {
+ int c;
+ c = sprintf(s, "%02x ", vd->ecp.tx.frameout[i]);
+ s += c;
+ if (!((i+1) % 16)) {
+ LLDPAD_DBG("%s", t);
+ s = t;
+ }
+ }
+
+ LLDPAD_DBG("%s\n", t);
+
+ free(t);
+}
+
+/* ecp_build_ECPDU - create an ecp protocol data unit
+ * @vd: currently used port
+ *
+ * returns true on success, false on failure
+ *
+ * creates the frame header with the ports mac address, the ecp header with REQ
+ * plus a list of packed TLVs created from the profiles on this
+ * port.
+ */
+bool ecp_build_ECPDU(struct vdp_data *vd)
+{
+ struct l2_ethhdr eth;
+ struct ecp_hdr ecp_hdr;
+ u8 own_addr[ETH_ALEN];
+ u32 fb_offset = 0;
+ u32 datasize = 0;
+ struct packed_tlv *ptlv = NULL;
+ struct vsi_profile *p;
+
+ /* TODO: different multicast address for sending ECP over S-channel (multi_cast_source_s)
+ * S-channels to implement later */
+ memcpy(eth.h_dest, multi_cast_source, ETH_ALEN);
+ l2_packet_get_own_src_addr(vd->ecp.l2,(u8 *)&own_addr);
+ memcpy(eth.h_source, &own_addr, ETH_ALEN);
+ eth.h_proto = htons(ETH_P_ECP);
+ vd->ecp.tx.frameout = (u8 *)malloc(ETH_FRAME_LEN);
+ if (vd->ecp.tx.frameout == NULL) {
+ LLDPAD_ERR("InfoECPDU: Failed to malloc frame buffer \n");
+ return false;
+ }
+ memset(vd->ecp.tx.frameout,0,ETH_FRAME_LEN);
+ memcpy(vd->ecp.tx.frameout, (void *)ð, sizeof(struct l2_ethhdr));
+ fb_offset += sizeof(struct l2_ethhdr);
+
+ ecp_hdr.oui[0] = 0x0;
+ ecp_hdr.oui[1] = 0x1b;
+ ecp_hdr.oui[2] = 0x3f;
+
+ ecp_hdr.pad1 = 0x0;
+
+ ecp_hdr.subtype = ECP_SUBTYPE;
+ ecp_hdr.mode = ECP_REQUEST;
+
+ vd->ecp.lastSequence++;
+ ecp_hdr.seqnr = htons(vd->ecp.lastSequence);
+
+ if ((sizeof(struct ecp_hdr)+fb_offset) > ETH_MAX_DATA_LEN)
+ goto error;
+ memcpy(vd->ecp.tx.frameout+fb_offset, (void *)&ecp_hdr, sizeof(struct ecp_hdr));
+ datasize += sizeof(struct ecp_hdr);
+ fb_offset += sizeof(struct ecp_hdr);
+
+ /* create packed_tlvs for all profiles on this interface */
+ LIST_FOREACH(p, &vd->profile_head, profile) {
+ if(!p) {
+ LLDPAD_ERR("%s(%i): list vd->profile_head empty !\n", __func__, __LINE__);
+ continue;
+ }
+
+ if (!p->localChange) {
+ LLDPAD_DBG("%s(%i): skipping unchanged profile !\n", __func__, __LINE__);
+ continue;
+ }
+
+ ptlv = vdp_gettlv(vd, p);
+
+ if (!ptlv) {
+ LLDPAD_ERR("%s(%i): ptlv not created !\n", __func__, __LINE__);
+ continue;
+ }
+
+ if (ptlv) {
+ if ((ptlv->size+fb_offset) > ETH_MAX_DATA_LEN)
+ goto error;
+ memcpy(vd->ecp.tx.frameout+fb_offset,
+ ptlv->tlv, ptlv->size);
+ datasize += ptlv->size;
+ fb_offset += ptlv->size;
+ }
+
+ ptlv = free_pkd_tlv(ptlv);
+ }
+
+ /* The End TLV marks the end of the LLDP PDU */
+ ptlv = pack_end_tlv();
+ if (!ptlv || ((ptlv->size + fb_offset) > ETH_MAX_DATA_LEN))
+ goto error;
+ memcpy(vd->ecp.tx.frameout + fb_offset, ptlv->tlv, ptlv->size);
+ datasize += ptlv->size;
+ fb_offset += ptlv->size;
+ ptlv = free_pkd_tlv(ptlv);
+
+ if (datasize > ETH_MAX_DATA_LEN)
+ goto error;
+
+ if (datasize < ETH_MIN_DATA_LEN)
+ vd->ecp.tx.sizeout = ETH_MIN_PKT_LEN;
+ else
+ vd->ecp.tx.sizeout = fb_offset;
+
+ return true;
+
+error:
+ ptlv = free_pkd_tlv(ptlv);
+ if (vd->ecp.tx.frameout)
+ free(vd->ecp.tx.frameout);
+ vd->ecp.tx.frameout = NULL;
+ LLDPAD_ERR("InfoECPDU: packed TLV too large for tx frame\n");
+ return false;
+}
+
+/* ecp_tx_Initialize - initializes the ecp tx state machine
+ * @vd: currently used port
+ *
+ * no return value
+ *
+ * initializes some variables for the ecp tx state machine.
+ */
+void ecp_tx_Initialize(struct vdp_data *vd)
+{
+ if (vd->ecp.tx.frameout) {
+ free(vd->ecp.tx.frameout);
+ vd->ecp.tx.frameout = NULL;
+ }
+ vd->ecp.tx.localChange = VDP_PROFILE_REQ;
+ vd->ecp.lastSequence = ECP_SEQUENCE_NR_START;
+ vd->ecp.stats.statsFramesOutTotal = 0;
+ vd->ecp.ackTimerExpired = false;
+ vd->ecp.retries = 0;
+
+ struct port *port = port_find_by_name(vd->ifname);
+ l2_packet_get_port_state(vd->ecp.l2, (u8 *)&(port->portEnabled));
+
+ return;
+}
+
+/* ecp_txFrame - transmit ecp frame
+ * @vd: currently used port
+ *
+ * returns the number of characters sent on success, -1 on failure
+ *
+ * sends out the frame stored in the frameout structure using l2_packet_send.
+ */
+u8 ecp_txFrame(struct vdp_data *vd)
+{
+ int status = 0;
+
+ status = l2_packet_send(vd->ecp.l2, (u8 *)&multi_cast_source,
+ htons(ETH_P_ECP),vd->ecp.tx.frameout,vd->ecp.tx.sizeout);
+ vd->ecp.stats.statsFramesOutTotal++;
+
+ free(vd->ecp.tx.frameout);
+
+ return status;
+}
+
+/* ecp_tx_create_frame - create ecp frame
+ * @vd: currently used port
+ *
+ * no return value
+ *
+ *
+ */
+void ecp_tx_create_frame(struct vdp_data *vd)
+{
+ /* send REQs */
+ if (vd->ecp.tx.localChange) {
+ LLDPAD_DBG("%s(%i)-%s: sending REQs\n", __func__, __LINE__, vd->ifname);
+ ecp_build_ECPDU(vd);
+ ecp_print_frameout(vd);
+ ecp_txFrame(vd);
+ }
+
+ vd->ecp.tx.localChange = 0;
+ return;
+}
+
+/* ecp_timeout_handler - handles the ack timer expiry
+ * @eloop_data: data structure of event loop
+ * @user_ctx: user context, port here
+ *
+ * no return value
+ *
+ * called when the ECP ack timer has expired. sets a flag and calls the ECP
+ * state machine.
+ */
+static void ecp_tx_timeout_handler(void *eloop_data, void *user_ctx)
+{
+ struct vdp_data *vd;
+
+ vd = (struct vdp_data *) user_ctx;
+
+ vd->ecp.ackTimerExpired = true;
+
+ LLDPAD_DBG("%s(%i)-%s: timer expired\n", __func__, __LINE__,
+ vd->ifname);
+
+ ecp_tx_run_sm(vd);
+}
+
+/* ecp_tx_stop_ackTimer - stop the ECP ack timer
+ * @vd: currently used port
+ *
+ * returns the number of removed handlers
+ *
+ * stops the ECP ack timer. used when a ack frame for the port has been
+ * received.
+ */
+int ecp_tx_stop_ackTimer(struct vdp_data *vd)
+{
+ LLDPAD_DBG("%s(%i)-%s: stopping timer\n", __func__, __LINE__,
+ vd->ifname);
+
+ return eloop_cancel_timeout(ecp_tx_timeout_handler, NULL, (void *) vd);
+}
+
+/* ecp_tx_start_ackTimer - starts the ECP ack timer
+ * @profile: profile to process
+ *
+ * returns 0 on success, -1 on error
+ *
+ * starts the ack timer when a frame has been sent out.
+ */
+static void ecp_tx_start_ackTimer(struct vdp_data *vd)
+{
+ unsigned int secs, usecs;
+
+ vd->ecp.ackTimerExpired = false;
+
+ secs = ECP_TRANSMISSION_TIMER / ECP_TRANSMISSION_DIVIDER;
+ usecs = ECP_TRANSMISSION_TIMER % ECP_TRANSMISSION_DIVIDER;
+
+ LLDPAD_DBG("%s(%i)-%s: starting timer\n", __func__, __LINE__,
+ vd->ifname);
+
+ eloop_register_timeout(secs, usecs, ecp_tx_timeout_handler, NULL, (void *) vd);
+}
+
+/* ecp_tx_change_state - changes the ecp tx sm state
+ * @vd: currently used port
+ * @newstate: new state for the sm
+ *
+ * no return value
+ *
+ * checks state transistion for consistency and finally changes the state of
+ * the profile.
+ */
+static void ecp_tx_change_state(struct vdp_data *vd, u8 newstate)
+{
+ switch(newstate) {
+ case ECP_TX_INIT_TRANSMIT:
+ break;
+ case ECP_TX_TRANSMIT_ECPDU:
+ assert((vd->ecp.tx.state == ECP_TX_INIT_TRANSMIT) ||
+ (vd->ecp.tx.state == ECP_TX_WAIT_FOR_ACK) ||
+ (vd->ecp.tx.state == ECP_TX_REQUEST_PDU));
+ break;
+ case ECP_TX_WAIT_FOR_ACK:
+ assert(vd->ecp.tx.state == ECP_TX_TRANSMIT_ECPDU);
+ break;
+ case ECP_TX_REQUEST_PDU:
+ assert(vd->ecp.tx.state == ECP_TX_WAIT_FOR_ACK);
+ break;
+ default:
+ LLDPAD_ERR("ERROR: The ECP_TX State Machine is broken!\n");
+ log_message(MSG_ERR_TX_SM_INVALID, "%s", vd->ifname);
+ }
+
+ LLDPAD_DBG("%s(%i)-%s: state change %s -> %s\n", __func__, __LINE__,
+ vd->ifname, ecp_tx_states[vd->ecp.tx.state], ecp_tx_states[newstate]);
+
+ vd->ecp.tx.state = newstate;
+ return;
+}
+
+/* ecp_set_tx_state - sets the ecp tx sm state
+ * @vd: currently used port
+ *
+ * returns true or false
+ *
+ * switches the state machine to the next state depending on the input
+ * variables. returns true or false depending on wether the state machine
+ * can be run again with the new state or can stop at the current state.
+ */
+static bool ecp_set_tx_state(struct vdp_data *vd)
+{
+ struct port *port = port_find_by_name(vd->ifname);
+
+ if (!port) {
+ LLDPAD_ERR("%s(%i): port not found !\n", __func__, __LINE__);
+ return 0;
+ }
+
+ if ((port->portEnabled == false) && (port->prevPortEnabled == true)) {
+ LLDPAD_ERR("set_tx_state: port was disabled\n");
+ ecp_tx_change_state(vd, ECP_TX_INIT_TRANSMIT);
+ }
+ port->prevPortEnabled = port->portEnabled;
+
+ switch (vd->ecp.tx.state) {
+ case ECP_TX_INIT_TRANSMIT:
+ if (port->portEnabled && ((port->adminStatus == enabledRxTx) ||
+ (port->adminStatus == enabledTxOnly)) && vd->ecp.tx.localChange) {
+ ecp_tx_change_state(vd, ECP_TX_TRANSMIT_ECPDU);
+ return true;
+ }
+ return false;
+ case ECP_TX_TRANSMIT_ECPDU:
+ if ((port->adminStatus == disabled) ||
+ (port->adminStatus == enabledRxOnly)) {
+ ecp_tx_change_state(vd, ECP_TX_INIT_TRANSMIT);
+ return true;
+ }
+ ecp_tx_change_state(vd, ECP_TX_WAIT_FOR_ACK);
+ return true;
+ case ECP_TX_WAIT_FOR_ACK:
+ if (vd->ecp.ackTimerExpired) {
+ vd->ecp.retries++;
+ if (vd->ecp.retries < ECP_MAX_RETRIES) {
+ ecp_somethingChangedLocal(vd);
+ ecp_tx_change_state(vd, ECP_TX_TRANSMIT_ECPDU);
+ return true;
+ }
+ if (vd->ecp.retries == ECP_MAX_RETRIES) {
+ LLDPAD_DBG("%s(%i)-%s: 1 \n", __func__, __LINE__,
+ vd->ifname);
+ ecp_tx_change_state(vd, ECP_TX_REQUEST_PDU);
+ return true;
+ }
+ }
+ if (vd->ecp.ackReceived && vd->ecp.seqECPDU == vd->ecp.lastSequence) {
+ vd->ecp.ackReceived = false;
+ ecp_tx_change_state(vd, ECP_TX_REQUEST_PDU);
+ return true;
+ }
+ return false;
+ case ECP_TX_REQUEST_PDU:
+ if (vd->ecp.tx.localChange & VDP_PROFILE_REQ) {
+ ecp_tx_change_state(vd, ECP_TX_TRANSMIT_ECPDU);
+ return true;
+ }
+ return false;
+ default:
+ LLDPAD_ERR("ERROR: The TX State Machine is broken!\n");
+ log_message(MSG_ERR_TX_SM_INVALID, "%s", vd->ifname);
+ return false;
+ }
+}
+
+/* ecp_tx_run_sm - state machine for ecp tx
+ * @vd: currently used vdp_data
+ *
+ * no return value
+ *
+ * runs the state machine for ecp tx.
+ */
+void ecp_tx_run_sm(struct vdp_data *vd)
+{
+ do {
+ LLDPAD_DBG("%s(%i)-%s: ecp_tx - %s\n", __func__, __LINE__,
+ vd->ifname, ecp_tx_states[vd->ecp.tx.state]);
+
+ switch(vd->ecp.tx.state) {
+ case ECP_TX_INIT_TRANSMIT:
+ ecp_tx_Initialize(vd);
+ break;
+ case ECP_TX_TRANSMIT_ECPDU:
+ ecp_tx_create_frame(vd);
+ ecp_tx_start_ackTimer(vd);
+ break;
+ case ECP_TX_WAIT_FOR_ACK:
+ if (vd->ecp.ackReceived) {
+ LLDPAD_DBG("%s(%i)-%s: ECP_TX_WAIT_FOR_ACK ackReceived\n", __func__, __LINE__,
+ vd->ifname);
+ LLDPAD_DBG("%s(%i)-%s: seqECPDU %x lastSequence %x \n", __func__, __LINE__,
+ vd->ifname, vd->ecp.seqECPDU, vd->ecp.lastSequence);
+ vd->ecp.tx.localChange = 0;
+ ecp_tx_stop_ackTimer(vd);
+ }
+ break;
+ case ECP_TX_REQUEST_PDU:
+ vd->ecp.retries = 0;
+ LLDPAD_DBG("%s(%i)-%s: ECP_TX_REQUEST_PDU lastSequence %x\n", __func__, __LINE__,
+ vd->ifname, vd->ecp.lastSequence);
+ break;
+ default:
+ LLDPAD_ERR("%s(%i): ERROR The TX State Machine is broken!\n", __func__,
+ __LINE__);
+ log_message(MSG_ERR_TX_SM_INVALID, "%s", vd->ifname);
+ }
+ } while (ecp_set_tx_state(vd) == true);
+
+ return;
+}
diff --git a/include/lldp.h b/include/lldp.h
index 0371844..476773c 100644
--- a/include/lldp.h
+++ b/include/lldp.h
@@ -190,6 +190,7 @@ enum {
/* IEEE 802.1Qbg subtype */
#define LLDP_EVB_SUBTYPE 0
+#define LLDP_VDP_SUBTYPE 0x2
/* forwarding mode */
#define LLDP_EVB_CAPABILITY_FORWARD_STANDARD (1 << 7)
diff --git a/include/lldp_evb.h b/include/lldp_evb.h
index 667f9ad..d028c21 100644
--- a/include/lldp_evb.h
+++ b/include/lldp_evb.h
@@ -37,6 +37,12 @@ typedef enum {
EVB_CONFIRMATION
} evb_state;
+#define RTE 13
+/* retransmission granularity (RTG) in microseconds */
+#define EVB_RTG 10
+/* retransmission multiplier (RTM) */
+#define EVB_RTM(rte) (2<<(RTE-1))
+
struct tlv_info_evb {
u8 oui[3];
u8 sub;
diff --git a/include/lldp_vdp.h b/include/lldp_vdp.h
new file mode 100644
index 0000000..8866d51
--- /dev/null
+++ b/include/lldp_vdp.h
@@ -0,0 +1,182 @@
+/*******************************************************************************
+
+ implementation of according to IEEE 802.1Qbg
+ (c) Copyright IBM Corp. 2010
+
+ Author(s): Jens Osterkamp <jens@linux.vnet.ibm.com>
+
+ 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.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+*******************************************************************************/
+
+#ifndef _LLDP_VDP_H
+#define _LLDP_VDP_H
+
+#include "lldp_mod.h"
+#include "ecp/ecp.h"
+
+#define LLDP_MOD_VDP OUI_IEEE_8021Qbg+1
+
+#define VDP_MODE_PREASSOCIATE 0x0
+#define VDP_MODE_PREASSOCIATE_WITH_RR 0x1
+#define VDP_MODE_ASSOCIATE 0x2
+#define VDP_MODE_DEASSOCIATE 0x3
+
+static char *vsi_modes[] = {
+ "VDP_MODE_PREASSOCIATED",
+ "VDP_MODE_PREASSOCIATED_WITH_RR",
+ "VDP_MODE_ASSOCIATED",
+ "VDP_MODE_DEASSOCITATED"
+};
+
+#define VDP_RESPONSE_SUCCESS 0x0
+#define VDP_RESPONSE_INVALID_FORMAT 0x1
+#define VDP_RESPONSE_INSUFF_RESOURCES 0x2
+#define VDP_RESPONSE_UNUSED_VTID 0x3
+#define VDP_RESPONSE_VTID_VIOLATION 0x4
+#define VDP_RESPONSE_VTID_VER_VIOLATION 0x5
+#define VDP_RESPONSE_OUT_OF_SYNC 0x6
+#define VDP_RESPONSE_NO_RESPONSE 0xff
+
+static char *vsi_responses[] = {
+ "success",
+ "invalid format",
+ "insufficient resources",
+ "unused VTID",
+ "VTID violation",
+ "VTID version violation",
+ "out of sync"
+};
+
+enum {
+ VDP_PROFILE_NOCHANGE = 0,
+ VDP_PROFILE_REQ,
+ VDP_PROFILE_ACK,
+ VDP_PROFILE_NACK,
+};
+
+#define VDP_MACVLAN_FORMAT_1 1
+
+#define VDP_TRANSMISSION_TIMER 3*EVB_RTM(RTE)*EVB_RTG
+#define VDP_TRANSMISSION_DIVIDER 10000
+
+#define VDP_ROLE_STATION 0
+#define VDP_ROLE_BRIDGE 1
+
+enum {
+ VSI_UNASSOCIATED = 0,
+ VSI_ASSOC_PROCESSING,
+ VSI_ASSOCIATED,
+ VSI_PREASSOC_PROCESSING,
+ VSI_PREASSOCIATED,
+ VSI_DEASSOC_PROCESSING,
+ VSI_EXIT,
+};
+
+static char *vsi_states[] = {
+ "VSI_UNASSOCIATED",
+ "VSI_ASSOC_PROCESSING",
+ "VSI_ASSOCIATED",
+ "VSI_PREASSOC_PROCESSING",
+ "VSI_PREASSOCIATED",
+ "VSI_DEASSOC_PROCESSING",
+ "VSI_EXIT"
+};
+
+struct mac_vlan {
+ u8 mac[6];
+ u16 vlan;
+} __attribute__ ((__packed__));
+
+struct tlv_info_vdp {
+ u8 oui[3];
+ u8 sub;
+ u8 mode;
+ u8 response;
+ u8 mgrid;
+ u8 id[3];
+ u8 version;
+ u8 instance[16];
+ u8 format;
+ u16 entries;
+ struct mac_vlan mac_vlan;
+} __attribute__ ((__packed__));
+
+struct vsi_profile {
+ int mode;
+ int response;
+ u8 mgrid;
+ int id;
+ u8 version;
+ u8 instance[16];
+ u8 mac[6]; /* TODO: currently only one MAC/VLAN pair supported, more later */
+ u16 vlan;
+ struct port *port;
+ int ackTimerExpired;
+ int ackReceived;
+ int state;
+ bool localChange;
+ LIST_ENTRY(vsi_profile) profile;
+};
+
+struct vdp_data {
+ char ifname[IFNAMSIZ];
+ struct ecp ecp;
+ struct unpacked_tlv *vdp;
+ int role;
+ LIST_HEAD(profile_head, vsi_profile) profile_head;
+ LIST_ENTRY(vdp_data) entry;
+};
+
+struct vdp_user_data {
+ LIST_HEAD(vdp_head, vdp_data) head;
+};
+
+struct lldp_module *vdp_register(void);
+void vdp_unregister(struct lldp_module *mod);
+struct vdp_data *vdp_data(char *ifname);
+struct packed_tlv *vdp_gettlv(struct vdp_data *vd, struct vsi_profile *profile);
+void vdp_vsi_sm_station(struct vsi_profile *profile);
+struct vsi_profile *vdp_add_profile(struct vsi_profile *profile);
+void vdp_somethingChangedLocal(struct vsi_profile *profile, bool mode);
+void vdp_print_profile(struct vsi_profile *profile);
+
+#define MAC_ADDR_STRLEN 18
+#define INSTANCE_STRLEN 36
+
+#define PRINT_PROFILE(s, p) \
+{ int c; \
+ c = sprintf(s, "\nmode: %i", p->mode); s += c; \
+ c = sprintf(s, " (%s)\n", vsi_modes[p->mode]); s+= c; \
+ c = sprintf(s, "response: %i", p->response); s += c; \
+ c = sprintf(s, " (%s)\n", vsi_responses[p->response]); s+= c; \
+ c = sprintf(s, "state: %i", p->state); s += c; \
+ c = sprintf(s, " (%s)\n", vsi_states[p->state]); s+= c; \
+ c = sprintf(s, "mgrid: %i\n", p->mgrid); s += c; \
+ c = sprintf(s, "id: %x\n", p->id); \
+ s += c; \
+ c = sprintf(s, "version: %i\n", p->version); s += c; \
+ char instance[INSTANCE_STRLEN+2]; \
+ instance2str(p->instance, instance, sizeof(instance)); \
+ c = sprintf(s, "instance: %s\n", &instance[0]); s += c; \
+ char macbuf[MAC_ADDR_STRLEN+1]; \
+ mac2str(p->mac, macbuf, MAC_ADDR_STRLEN); \
+ c = sprintf(s, "mac: %s\n", macbuf); s += c; \
+ c = sprintf(s, "vlan: %i\n\n", p->vlan); s += c; \
+}
+
+#endif /* _LLDP_VDP_H */
diff --git a/lldp/l2_packet.h b/lldp/l2_packet.h
index 16f3683..0962429 100644
--- a/lldp/l2_packet.h
+++ b/lldp/l2_packet.h
@@ -36,6 +36,8 @@
#define ETH_P_LLDP 0x88cc
+/* TODO: use extended ethertype until final ethertype is available */
+#define ETH_P_ECP 0x88b7
#define ETH_FRAME_LEN 1514
diff --git a/lldp/ports.h b/lldp/ports.h
index 0138efe..44dc5f1 100644
--- a/lldp/ports.h
+++ b/lldp/ports.h
@@ -142,15 +142,20 @@ struct port {
u8 portEnabled;
u8 prevPortEnabled;
u8 adminStatus;
- u8 rxChanges;
- u16 lldpdu;
+
+ /* protocol specific */
struct l2_packet_data *l2;
struct portrx rx;
struct porttx tx;
- struct porttlvs tlvs;
struct portstats stats;
struct porttimers timers;
+ u8 rxChanges;
+ u16 lldpdu;
struct msap msap;
+
+ /* not sure */
+ struct porttlvs tlvs;
+
struct port *next;
};
diff --git a/lldp_vdp.c b/lldp_vdp.c
new file mode 100644
index 0000000..2014c2a
--- /dev/null
+++ b/lldp_vdp.c
@@ -0,0 +1,1224 @@
+/*******************************************************************************
+
+ implementation of VDP according to IEEE 802.1Qbg
+ (c) Copyright IBM Corp. 2010
+
+ Author(s): Jens Osterkamp <jens@linux.vnet.ibm.com>
+
+ 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.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+*******************************************************************************/
+
+#include <net/if.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/utsname.h>
+#include <linux/if_bridge.h>
+#include "lldp.h"
+#include "lldp_vdp.h"
+#include "ecp/ecp.h"
+#include "eloop.h"
+#include "lldp_evb.h"
+#include "messages.h"
+#include "config.h"
+#include "common.h"
+#include "lldp_vdp_clif.h"
+#include "lldp_vdp_cmds.h"
+
+/* vdp_data - searches vdp_data in the list of modules for this port
+ * @ifname: interface name to search for
+ *
+ * returns vdp_data on success, NULL on error
+ *
+ * searches the list of user_data for the VDP module user_data.
+ */
+struct vdp_data *vdp_data(char *ifname)
+{
+ struct vdp_user_data *ud;
+ struct vdp_data *vd = NULL;
+
+ ud = find_module_user_data_by_if(ifname, &lldp_head, LLDP_MOD_VDP);
+ if (ud) {
+ LIST_FOREACH(vd, &ud->head, entry) {
+ if (!strncmp(ifname, vd->ifname, IFNAMSIZ))
+ return vd;
+ }
+ }
+ return NULL;
+}
+
+/* vdp_free_tlv - free tlv in vdp_data
+ * @vd: vdp_data
+ *
+ * no return value
+ *
+ * frees up tlv in vdp_data. used in vdp_free_data.
+ */
+static void vdp_free_tlv(struct vdp_data *vd)
+{
+ if (vd) {
+ FREE_UNPKD_TLV(vd, vdp);
+ }
+}
+
+/* vdp_free_data - frees up vdp data
+ * @ud: user data structure
+ *
+ * no return value
+ *
+ * removes vd_structure from the user_data list. frees up tlv in vdp_data.
+ * used in vdp_unregister.
+ */
+static void vdp_free_data(struct vdp_user_data *ud)
+{
+ struct vdp_data *vd;
+ if (ud) {
+ while (!LIST_EMPTY(&ud->head)) {
+ vd = LIST_FIRST(&ud->head);
+ LIST_REMOVE(vd, entry);
+ vdp_free_tlv(vd);
+ free(vd);
+ }
+ }
+}
+
+/* vdp_print_profile - print a vsi profile
+ * @profile: profile to print
+ *
+ * no return value
+ *
+ * prints the contents of a profile first to a string using the PRINT_PROFILE
+ * macro, and then to the screen. Used for debug purposes.
+ */
+static inline void vdp_print_profile(struct vsi_profile *profile)
+{
+ LLDPAD_DBG("profile:\n");
+
+ LLDPAD_DBG("mode: %i\n", profile->mode);
+ LLDPAD_DBG("response: %i\n", profile->response);
+ LLDPAD_DBG("state: %i\n", profile->state);
+ LLDPAD_DBG("mgrid: %i\n", profile->mgrid);
+ LLDPAD_DBG("id: %x\n", profile->id);
+ LLDPAD_DBG("version: %i\n", profile->version);
+
+ char instance[INSTANCE_STRLEN+2];
+ instance2str(profile->instance, instance, sizeof(instance));
+ LLDPAD_DBG("instance: %s\n", &instance[0]);
+
+ char macbuf[MAC_ADDR_STRLEN+1];
+ mac2str(profile->mac, macbuf, MAC_ADDR_STRLEN);
+ LLDPAD_DBG("mac: %s\n", macbuf);
+
+ LLDPAD_DBG("vlan: %i\n", profile->vlan);
+}
+
+/* vdp_somethingChangedLocal - set flag if profile has changed
+ * @profile: profile to set the flag for
+ * @mode: mode to set the flag to
+ *
+ * no return value
+ *
+ * set the localChange flag with a mode to indicate a profile has changed.
+ * used next time when a ecpdu with profiles is sent out.
+ */
+void vdp_somethingChangedLocal(struct vsi_profile *profile, bool mode)
+{
+ profile->localChange = mode;
+}
+
+/* vdp_ackTimer_expired - checks for expired ack timer
+ * @profile: profile to be checked
+ *
+ * returns true or false
+ *
+ * returns value of profile->ackTimerExpired, true if ack timer has expired,
+ * false otherwise.
+ */
+static bool vdp_ackTimer_expired(struct vsi_profile *profile)
+{
+ return profile->ackTimerExpired;
+}
+
+/* vdp_timeout_handler - handles the ack timer expiry
+ * @eloop_data: data structure of event loop
+ * @user_ctx: user context, profile here
+ *
+ * no return value
+ *
+ * called when the VDP ack timer has expired. sets a flag and calls the VDP
+ * state machine.
+ */
+void vdp_timeout_handler(void *eloop_data, void *user_ctx)
+{
+ struct vsi_profile *profile;
+
+ profile = (struct vsi_profile *) user_ctx;
+
+ profile->ackTimerExpired = true;
+
+ LLDPAD_DBG("%s(%i)-%s: timer expired\n", __func__, __LINE__,
+ profile->port->ifname);
+
+ vdp_vsi_sm_station(profile);
+}
+
+/* vdp_stop_ackTimer - stop the VDP ack timer
+ * @profile: profile to process
+ *
+ * returns the number of removed handlers
+ *
+ * stops the VDP ack timer. used when a ack frame for the profile has been
+ * received.
+ */
+static int vdp_stop_ackTimer(struct vsi_profile *profile)
+{
+ LLDPAD_DBG("%s(%i)-%s: stopping timer\n", __func__, __LINE__,
+ profile->port->ifname);
+
+ return eloop_cancel_timeout(vdp_timeout_handler, NULL, (void *) profile);
+}
+
+/* vdp_start_ackTimer - starts the VDP ack timer
+ * @profile: profile to process
+ *
+ * returns 0 on success, -1 on error
+ *
+ * starts the ack timer when a frame has been sent out.
+ */
+static int vdp_start_ackTimer(struct vsi_profile *profile)
+{
+ unsigned int secs, usecs;
+
+ profile->ackTimerExpired = false;
+
+ secs = VDP_TRANSMISSION_TIMER / VDP_TRANSMISSION_DIVIDER;
+ usecs = VDP_TRANSMISSION_TIMER % VDP_TRANSMISSION_DIVIDER;
+
+ LLDPAD_DBG("%s(%i)-%s: starting timer\n", __func__, __LINE__,
+ profile->port->ifname);
+
+ return eloop_register_timeout(secs, usecs, vdp_timeout_handler, NULL, (void *) profile);
+}
+
+/* vdp_vsi_change_station_state - changes the VDP station sm state
+ * @profile: profile to process
+ * @newstate: new state for the sm
+ *
+ * no return value
+ *
+ * actually changes the state of the profile
+ */
+void vdp_vsi_change_station_state(struct vsi_profile *profile, u8 newstate)
+{
+ switch(newstate) {
+ case VSI_UNASSOCIATED:
+ break;
+ case VSI_ASSOC_PROCESSING:
+ assert((profile->state == VSI_PREASSOCIATED) ||
+ (profile->state == VSI_UNASSOCIATED));
+ break;
+ case VSI_ASSOCIATED:
+ assert(profile->state == VSI_ASSOC_PROCESSING);
+ break;
+ case VSI_PREASSOC_PROCESSING:
+ assert(profile->state == VSI_UNASSOCIATED);
+ break;
+ case VSI_PREASSOCIATED:
+ assert(profile->state == VSI_PREASSOC_PROCESSING);
+ break;
+ case VSI_DEASSOC_PROCESSING:
+ assert((profile->state == VSI_PREASSOCIATED) ||
+ (profile->state == VSI_ASSOCIATED));
+ break;
+ case VSI_EXIT:
+ assert((profile->state == VSI_ASSOC_PROCESSING) ||
+ (profile->state == VSI_PREASSOC_PROCESSING) ||
+ (profile->state == VSI_DEASSOC_PROCESSING) ||
+ (profile->state == VSI_PREASSOCIATED) ||
+ (profile->state == VSI_ASSOCIATED));
+ break;
+ default:
+ LLDPAD_ERR("ERROR: The VDP station State Machine is broken!\n");
+ break;
+ }
+
+ LLDPAD_DBG("%s(%i)-%s: state change %s -> %s\n", __func__, __LINE__,
+ profile->port->ifname, vsi_states[profile->state], vsi_states[newstate]);
+
+ profile->state = newstate;
+}
+
+/* vdp_vsi_set_station_state - sets the vdp sm station state
+ * @profile: profile to process
+ *
+ * returns true or false
+ *
+ * switches the state machine to the next state depending on the input
+ * variables. returns true or false depending on wether the state machine
+ * can be run again with the new state or can stop at the current state.
+ */
+static bool vdp_vsi_set_station_state(struct vsi_profile *profile)
+{
+ switch(profile->state) {
+ case VSI_UNASSOCIATED:
+ if ((profile->mode == VDP_MODE_PREASSOCIATE) ||
+ (profile->mode == VDP_MODE_PREASSOCIATE_WITH_RR)) {
+ vdp_vsi_change_station_state(profile, VSI_PREASSOC_PROCESSING);
+ return true;
+ } else if (profile->mode == VDP_MODE_ASSOCIATE) {
+ vdp_vsi_change_station_state(profile, VSI_ASSOC_PROCESSING);
+ return true;
+ }
+ return false;
+ case VSI_ASSOC_PROCESSING:
+ if (profile->ackReceived) {
+ vdp_vsi_change_station_state(profile, VSI_ASSOCIATED);
+ return true;
+ } else if (!profile->ackReceived && vdp_ackTimer_expired(profile)) {
+ vdp_vsi_change_station_state(profile, VSI_EXIT);
+ return true;
+ }
+ return false;
+ case VSI_ASSOCIATED:
+ if (profile->mode == VDP_MODE_PREASSOCIATE) {
+ vdp_vsi_change_station_state(profile, VSI_PREASSOC_PROCESSING);
+ return true;
+ } else if (profile->mode == VDP_MODE_DEASSOCIATE) {
+ vdp_vsi_change_station_state(profile, VSI_DEASSOC_PROCESSING);
+ return true;
+ }
+ return false;
+ case VSI_PREASSOC_PROCESSING:
+ if (profile->ackReceived) {
+ vdp_vsi_change_station_state(profile, VSI_PREASSOCIATED);
+ return true;
+ } else if (vdp_ackTimer_expired(profile)) {
+ vdp_vsi_change_station_state(profile, VSI_EXIT);
+ return true;
+ }
+ case VSI_PREASSOCIATED:
+ if (profile->mode == VDP_MODE_DEASSOCIATE) {
+ vdp_vsi_change_station_state(profile, VSI_DEASSOC_PROCESSING);
+ return true;
+ }
+ if (profile->mode == VDP_MODE_ASSOCIATE) {
+ vdp_vsi_change_station_state(profile, VSI_ASSOC_PROCESSING);
+ return true;
+ }
+ return false;
+ case VSI_DEASSOC_PROCESSING:
+ if ((profile->ackReceived) || vdp_ackTimer_expired(profile)) {
+ vdp_vsi_change_station_state(profile, VSI_EXIT);
+ return true;
+ }
+ return false;
+ case VSI_EXIT:
+ return false;
+ default:
+ LLDPAD_ERR("ERROR: The VSI RX State Machine is broken!\n");
+ log_message(MSG_ERR_RX_SM_INVALID, "");
+ return false;
+ }
+}
+
+/* vdp_vsi_sm_station - state machine for vdp station role
+ * @profile: profile for which the state is processed
+ *
+ * no return value
+ *
+ * runs the state machine for the station role of VDP.
+ */
+void vdp_vsi_sm_station(struct vsi_profile *profile)
+{
+ struct vdp_data *vd = vdp_data(profile->port->ifname);
+
+ vdp_vsi_set_station_state(profile);
+ do {
+ LLDPAD_DBG("%s(%i)-%s: station - %s\n", __func__, __LINE__,
+ profile->port->ifname, vsi_states[profile->state]);
+
+ switch(profile->state) {
+ case VSI_UNASSOCIATED:
+ break;
+ case VSI_ASSOC_PROCESSING:
+ profile->response = VDP_RESPONSE_NO_RESPONSE;
+ vdp_somethingChangedLocal(profile, true);
+ ecp_somethingChangedLocal(vd);
+ ecp_tx_run_sm(vd);
+ vdp_somethingChangedLocal(profile, false);
+ vdp_start_ackTimer(profile);
+ break;
+ case VSI_ASSOCIATED:
+ profile->ackReceived = false;
+ vdp_somethingChangedLocal(profile, false);
+ vdp_stop_ackTimer(profile);
+ if (profile->keepaliveTimerExpired) {
+ vdp_somethingChangedLocal(profile, true);
+ ecp_somethingChangedLocal(vd);
+ ecp_tx_run_sm(vd);
+ vdp_start_keepaliveTimer(profile);
+ }
+ /* TODO:
+ * vsiError = ProcRxandSetCfg(remoteTLV, localtlv, vsistate);
+ * if (!vsiError) vsistate=ASSOCIATED */
+ break;
+ case VSI_PREASSOC_PROCESSING:
+ /* send out profile */
+ profile->response = VDP_RESPONSE_NO_RESPONSE;
+ vdp_somethingChangedLocal(profile, true);
+ ecp_somethingChangedLocal(vd);
+ ecp_tx_run_sm(vd);
+ vdp_start_ackTimer(profile);
+ break;
+ case VSI_PREASSOCIATED:
+ profile->ackReceived = false;
+ vdp_somethingChangedLocal(profile, false);
+ vdp_stop_ackTimer(profile);
+ if (profile->keepaliveTimerExpired) {
+ vdp_somethingChangedLocal(profile, true);
+ ecp_somethingChangedLocal(vd);
+ ecp_tx_run_sm(vd);
+ }
+ vdp_start_keepaliveTimer(profile);
+ /* TODO vsiError = ProcRxandSetCfg(remoteTLV, localtlv, vsistate);
+ * if (!vsiError) vsistate=PREASSOCIATED */
+ break;
+ case VSI_DEASSOC_PROCESSING:
+ profile->response = VDP_RESPONSE_NO_RESPONSE;
+ vdp_stop_keepaliveTimer(profile);
+ vdp_somethingChangedLocal(profile, true);
+ ecp_somethingChangedLocal(vd);
+ ecp_tx_run_sm(vd);
+ vdp_start_ackTimer(profile);
+ break;
+ case VSI_EXIT:
+ /* TODO: send DEASSOC here ? */
+ vdp_stop_ackTimer(profile);
+ vdp_remove_profile(profile);
+ break;
+ default:
+ LLDPAD_ERR("ERROR: The VSI RX station State Machine is broken!\n");
+ log_message(MSG_ERR_TX_SM_INVALID, "");
+ }
+ } while (vdp_vsi_set_station_state(profile) == true);
+
+}
+
+/* vdp_vsi_change_bridge_state - changes the VDP bridge sm state
+ * @profile: profile to process
+ * @newstate: new state for the sm
+ *
+ * no return value
+ *
+ * actually changes the state of the profile
+ */
+static void vdp_vsi_change_bridge_state(struct vsi_profile *profile, u8 newstate)
+{
+ switch(newstate) {
+ case VSI_UNASSOCIATED:
+ break;
+ case VSI_ASSOC_PROCESSING:
+ assert((profile->state == VSI_UNASSOCIATED) ||
+ (profile->state == VSI_PREASSOCIATED) ||
+ (profile->state == VSI_ASSOCIATED));
+ break;
+ case VSI_ASSOCIATED:
+ assert(profile->state == VSI_ASSOC_PROCESSING);
+ break;
+ case VSI_PREASSOC_PROCESSING:
+ assert((profile->state == VSI_UNASSOCIATED) ||
+ (profile->state == VSI_PREASSOCIATED) ||
+ (profile->state == VSI_ASSOCIATED));
+ break;
+ case VSI_PREASSOCIATED:
+ assert(profile->state == VSI_PREASSOC_PROCESSING);
+ break;
+ case VSI_DEASSOC_PROCESSING:
+ assert((profile->state == VSI_UNASSOCIATED) ||
+ (profile->state == VSI_PREASSOCIATED) ||
+ (profile->state == VSI_ASSOCIATED));
+ break;
+ case VSI_EXIT:
+ assert((profile->state == VSI_DEASSOC_PROCESSING) ||
+ (profile->state == VSI_PREASSOC_PROCESSING) ||
+ (profile->state == VSI_ASSOC_PROCESSING));
+ break;
+ default:
+ LLDPAD_ERR("ERROR: The VDP bridge State Machine is broken!\n");
+ break;
+ }
+ profile->state = newstate;
+}
+
+/* vdp_vsi_set_bridge_state - sets the vdp sm bridge state
+ * @profile: profile to process
+ *
+ * returns true or false
+ *
+ * switches the state machine to the next state depending on the input
+ * variables. returns true or false depending on wether the state machine
+ * can be run again with the new state or can stop at the current state.
+ */
+static bool vdp_vsi_set_bridge_state(struct vsi_profile *profile)
+{
+ switch(profile->state) {
+ case VSI_UNASSOCIATED:
+ if ((profile->mode == VDP_MODE_DEASSOCIATE)) /* || (INACTIVE)) */ {
+ vdp_vsi_change_bridge_state(profile, VSI_DEASSOC_PROCESSING);
+ return true;
+ } else if (profile->mode == VDP_MODE_ASSOCIATE) {
+ vdp_vsi_change_bridge_state(profile, VSI_ASSOC_PROCESSING);
+ return true;
+ } else if (profile->mode == VDP_MODE_PREASSOCIATE) {
+ vdp_vsi_change_bridge_state(profile, VSI_PREASSOC_PROCESSING);
+ return true;
+ }
+ return false;
+ case VSI_ASSOC_PROCESSING:
+ /* TODO: handle error case
+ if (!vsiError) ||
+ (vsiError && vsiState == Assoc) {
+ */
+ if (profile->mode == VDP_MODE_ASSOCIATE) {
+ vdp_vsi_change_bridge_state(profile, VSI_ASSOCIATED);
+ return true;
+ }
+ return false;
+ case VSI_ASSOCIATED:
+ if (profile->mode == VDP_MODE_ASSOCIATE) /* || ( INACTIVE )*/ {
+ vdp_vsi_change_bridge_state(profile, VSI_DEASSOC_PROCESSING);
+ return true;
+ } else if (profile->mode == VDP_MODE_PREASSOCIATE) {
+ vdp_vsi_change_bridge_state(profile, VSI_PREASSOC_PROCESSING);
+ return true;
+ } else if (profile->mode == VDP_MODE_ASSOCIATE) {
+ vdp_vsi_change_bridge_state(profile, VSI_ASSOC_PROCESSING);
+ return true;
+ }
+ return false;
+ case VSI_PREASSOC_PROCESSING:
+ if (profile->response != VDP_RESPONSE_SUCCESS) {
+ vdp_vsi_change_bridge_state(profile, VSI_EXIT);
+ return true;
+ }
+ vdp_vsi_change_bridge_state(profile, VSI_PREASSOCIATED);
+ return false;
+ case VSI_PREASSOCIATED:
+ if (profile->mode == VDP_MODE_ASSOCIATE) {
+ vdp_vsi_change_bridge_state(profile, VSI_ASSOC_PROCESSING);
+ return true;
+ } else if (profile->mode == VDP_MODE_DEASSOCIATE ) {
+ vdp_vsi_change_bridge_state(profile, VSI_DEASSOC_PROCESSING);
+ return true;
+ } else if (profile->mode == VDP_MODE_PREASSOCIATE ) {
+ vdp_vsi_change_bridge_state(profile, VSI_PREASSOC_PROCESSING);
+ return true;
+ }
+ return false;
+ case VSI_DEASSOC_PROCESSING:
+ vdp_vsi_change_bridge_state(profile, VSI_EXIT);
+ return false;
+ case VSI_EXIT:
+ return false;
+ default:
+ LLDPAD_ERR("ERROR: The VSI RX State Machine (bridge) is broken!\n");
+ log_message(MSG_ERR_RX_SM_INVALID, "");
+ return false;
+ }
+}
+
+/* vdp_vsi_sm_bridge - state machine for vdp bridge role
+ * @profile: profile for which the state is processed
+ *
+ * no return value
+ *
+ * runs the state machine for the bridge role of VDP.
+ */
+static void vdp_vsi_sm_bridge(struct vsi_profile *profile)
+{
+ struct vdp_data *vd = vdp_data(profile->port->ifname);
+
+ vdp_vsi_set_bridge_state(profile);
+ do {
+ LLDPAD_DBG("%s(%i)-%s: bridge - %s\n", __func__, __LINE__,
+ profile->port->ifname, vsi_states[profile->state]);
+ switch(profile->state) {
+ case VSI_UNASSOCIATED:
+ break;
+ case VSI_ASSOC_PROCESSING:
+ /* TODO: vsiError = ProcRxandSetCfg(remoteTLV, localtlv, vsistate);
+ * if (vsiError)
+ * txTLV(Assoc NACK)
+ * else
+ * txTLV(Assoc ACK)
+ */
+ break;
+ case VSI_ASSOCIATED:
+ break;
+ case VSI_PREASSOC_PROCESSING:
+ /* TODO: vsiError = ProcRxandSetCfg(remoteTLV, localtlv, vsistate);
+ * if (vsiError)
+ * txTLV(PreAssoc NACK)
+ * else
+ * txTLV(PreAssoc ACK)
+ */
+ /* for now, we always succeed */
+ profile->response = VDP_RESPONSE_SUCCESS;
+ LLDPAD_DBG("%s(%i)-%s: framein %p, sizein %i\n", __func__, __LINE__,
+ profile->port->ifname, vd->ecp.rx.framein,
+ vd->ecp.rx.sizein);
+ ecp_rx_send_ack_frame(profile->port);
+ break;
+ case VSI_PREASSOCIATED:
+ LLDPAD_DBG("%s(%i)-%s: \n", __func__, __LINE__, profile->port->ifname);
+ break;
+ case VSI_DEASSOC_PROCESSING:
+ /* TODO: txTLV(DeAssoc ACK) */
+ break;
+ case VSI_EXIT:
+ vdp_remove_profile(profile);
+ break;
+ default:
+ LLDPAD_ERR("ERROR: The VSI RX bridge State Machine is broken!\n");
+ log_message(MSG_ERR_TX_SM_INVALID, "");
+ }
+ } while (vdp_vsi_set_bridge_state(profile) == true);
+
+}
+
+/*
+ * vdp_print_vsi_tlv - print the raw contents of a VSI TLV
+ * @tlv: the unpacked tlv which gets printed
+ *
+ * No return value
+ *
+ * used for protocol debug purposes
+ */
+static void vdp_print_vsi_tlv(struct unpacked_tlv *tlv)
+{
+ int i;
+
+ LLDPAD_DBG("%s:type %i, length %i, info:\n", __func__, tlv->type, tlv->length);
+
+ for (i=0; i < tlv->length; i++) {
+ LLDPAD_DBG("%02x ", tlv->info[i]);
+ if (!((i+1) % 16))
+ LLDPAD_DBG("\n");
+ }
+
+ LLDPAD_DBG("\n");
+}
+
+/*
+ * vdp_validate_tlv - validates vsi tlvs
+ * @vdp: decoded vsi tlv
+ *
+ * Returns 0 on success, 1 on error
+ *
+ * checks the contents of an already decoded vsi tlv for inconsistencies
+ */
+static int vdp_validate_tlv(struct tlv_info_vdp *vdp)
+{
+ if (ntoh24(vdp->oui) != OUI_IEEE_8021Qbg) {
+ LLDPAD_DBG("vdp->oui %06x \n", ntoh24(vdp->oui));
+ goto out_err;
+ }
+
+ if (vdp->sub != LLDP_VDP_SUBTYPE) {
+ LLDPAD_DBG("vdp->sub %02x \n", vdp->sub);
+ goto out_err;
+ }
+
+ if ((vdp->mode < VDP_MODE_PREASSOCIATE) ||
+ (vdp->mode > VDP_MODE_DEASSOCIATE)) {
+ LLDPAD_DBG("Unknown mode %02x in vsi tlv !\n", vdp->mode);
+ goto out_err;
+ }
+
+ if ((vdp->response < VDP_RESPONSE_SUCCESS) ||
+ (vdp->response > VDP_RESPONSE_OUT_OF_SYNC)) {
+ LLDPAD_DBG("Unknown response %02x \n", vdp->response);
+ goto out_err;
+ }
+
+ if (vdp->format != VDP_MACVLAN_FORMAT_1) {
+ LLDPAD_DBG("Unknown format %02x in vsi tlv !\n", vdp->format);
+ goto out_err;
+ }
+
+ if (ntohs(vdp->entries) != 1) {
+ LLDPAD_DBG("Multiple entries %02x in vsi tlv !\n", vdp->entries);
+ goto out_err;
+ }
+
+ return 0;
+
+out_err:
+ return 1;
+}
+
+/*
+ * vdp_indicate - receive VSI TLVs from ECP
+ * @port: the port on which the tlv was received
+ * @tlv: the unpacked tlv to receive
+ * @ecp_mode: the mode under which the tlv was received (ACK or REQ)
+ *
+ * Returns 0 on success
+ *
+ * receives a vsi tlv and creates a profile. Take appropriate action
+ * depending on the role of the (receive) port
+ */
+int vdp_indicate(struct vdp_data *vd, struct unpacked_tlv *tlv, int ecp_mode)
+{
+ struct tlv_info_vdp *vdp;
+ struct vsi_profile *p, *profile;
+ struct port *port = port_find_by_name(vd->ifname);
+
+ LLDPAD_DBG("%s(%i): indicating vdp for %s !\n", __func__, __LINE__, vd->ifname);
+
+ if (!port) {
+ LLDPAD_ERR("%s(%i): port not found for %s !\n", __func__, __LINE__, vd->ifname);
+ goto out_err;
+ }
+
+ vdp = malloc(sizeof(struct tlv_info_vdp));
+
+ if (!vdp) {
+ LLDPAD_ERR("%s(%i): unable to allocate vdp !\n", __func__, __LINE__);
+ goto out_err;
+ }
+
+ memset(vdp, 0, sizeof(struct tlv_info_vdp));
+ memcpy(vdp, tlv->info, tlv->length);
+
+ if (vdp_validate_tlv(vdp)) {
+ LLDPAD_ERR("%s(%i): Invalid TLV received !\n", __func__, __LINE__);
+ goto out_vdp;
+ }
+
+ profile = malloc(sizeof(struct vsi_profile));
+
+ if (!profile) {
+ LLDPAD_ERR("%s(%i): unable to allocate profile !\n", __func__, __LINE__);
+ goto out_vdp;
+ }
+
+ memset(profile, 0, sizeof(struct vsi_profile));
+
+ profile->mode = vdp->mode;
+ profile->response = vdp->response;
+
+ profile->mgrid = vdp->mgrid;
+ profile->id = ntoh24(vdp->id);
+ profile->version = vdp->version;
+ memcpy(&profile->instance, &vdp->instance, 16);
+ memcpy(&profile->mac, &vdp->mac_vlan.mac, MAC_ADDR_LEN);
+ profile->vlan = ntohs(vdp->mac_vlan.vlan);
+
+ profile->port = port;
+
+ if (vd->role == VDP_ROLE_STATION) {
+ /* do we have the profile already ? */
+ LIST_FOREACH(p, &vd->profile_head, profile) {
+ if (vdp_profile_equal(p, profile)) {
+ LLDPAD_DBG("%s(%i): station: profile found, localChange %i ackReceived %i!\n",
+ __func__, __LINE__, p->localChange, p->ackReceived);
+
+ p->ackReceived = true;
+ p->keepaliveTimerExpired = false;
+ p->mode = vdp->mode;
+ p->response = vdp->response;
+
+ LLDPAD_DBG("%s(%i): profile response: %s (%i).\n", __func__, __LINE__,
+ vsi_responses[p->response], p->response);
+
+ vdp_vsi_sm_station(p);
+ } else {
+ LLDPAD_DBG("%s(%i): station: profile not found !\n", __func__, __LINE__);
+ /* ignore profile */
+ }
+ }
+ }
+
+ if (vd->role == VDP_ROLE_BRIDGE) {
+ /* do we have the profile already ? */
+ LIST_FOREACH(p, &vd->profile_head, profile) {
+ if (vdp_profile_equal(p, profile)) {
+ break;
+ }
+ }
+
+ if (p) {
+ LLDPAD_DBG("%s(%i): bridge: profile found !\n", __func__, __LINE__);
+ } else {
+ LLDPAD_DBG("%s(%i): bridge: profile not found !\n", __func__, __LINE__);
+ /* put it in the list */
+ profile->state = VSI_UNASSOCIATED;
+ LIST_INSERT_HEAD(&vd->profile_head, profile, profile );
+ }
+
+ vdp_vsi_sm_bridge(profile);
+ }
+
+ return 0;
+
+out_vdp:
+ free(vdp);
+out_err:
+ LLDPAD_ERR("%s(%i): error !\n", __func__, __LINE__);
+ return 1;
+
+}
+
+/*
+ * vdp_bld_vsi_tlv - build the VDP VSI TLV
+ * @vd: vdp_data structure for this port
+ * @profile: profile the vsi tlv is created from
+ *
+ * Returns 0 on success, ENOMEM otherwise
+ *
+ * creates a vdp structure from an existing profile
+ */
+static int vdp_bld_vsi_tlv(struct vdp_data *vd, struct vsi_profile *profile)
+{
+ int rc = 0;
+ struct unpacked_tlv *tlv = NULL;
+ struct tlv_info_vdp vdp;
+
+ FREE_UNPKD_TLV(vd, vdp);
+
+ memset(&vdp, 0, sizeof(vdp));
+
+ hton24(vdp.oui, OUI_IEEE_8021Qbg);
+ vdp.sub = LLDP_VDP_SUBTYPE;
+ vdp.mode = profile->mode;
+ vdp.response = 0;
+ vdp.mgrid = profile->mgrid;
+ hton24(vdp.id, profile->id);
+ vdp.version = profile->version;
+ memcpy(&vdp.instance,&profile->instance, 16);
+ vdp.format = VDP_MACVLAN_FORMAT_1;
+ vdp.entries = htons(1);
+ memcpy(&vdp.mac_vlan.mac,&profile->mac, MAC_ADDR_LEN);
+ vdp.mac_vlan.vlan = htons(profile->vlan);
+
+ tlv = create_tlv();
+ if (!tlv)
+ goto out_err;
+
+ tlv->type = ORG_SPECIFIC_TLV;
+ tlv->length = sizeof(vdp);
+ tlv->info = (u8 *)malloc(tlv->length);
+ if(!tlv->info) {
+ free(tlv);
+ tlv = NULL;
+ rc = ENOMEM;
+ goto out_err;
+ }
+ memcpy(tlv->info, &vdp, tlv->length);
+
+ vd->vdp = tlv;
+
+out_err:
+ return rc;
+}
+
+/* vdp_bld_tlv - builds a tlv from a profile
+ * @vd: vdp_data structure for this port
+ * @profile: profile the vsi tlv is created from
+ *
+ * returns 0 on success, != 0 on error
+ *
+ * wrapper function around vdp_bld_vsi_tlv. adds some checks and calls
+ * vdp_bld_vsi_tlv.
+*/
+
+static int vdp_bld_tlv(struct vdp_data *vd, struct vsi_profile *profile)
+{
+ int rc = 0;
+
+ if (!port_find_by_name(vd->ifname)) {
+ rc = EEXIST;
+ goto out_err;
+ }
+
+ if (!init_cfg()) {
+ rc = ENOENT;
+ goto out_err;
+ }
+
+ if (vdp_bld_vsi_tlv(vd, profile)) {
+ LLDPAD_ERR("%s:%s:vdp_bld_vsi_tlv() failed\n",
+ __func__, vd->ifname);
+ rc = EINVAL;
+ goto out_err_destroy;
+ }
+
+out_err_destroy:
+ destroy_cfg();
+
+out_err:
+ return rc;
+}
+
+/* vdp_gettlv - get the tlv for a profile
+ * @port: the port on which the tlv was received
+ * @profile: profile the vsi tlv is created from
+ *
+ * returns 0 on success
+ *
+ * this is the interface function called from ecp_build_ECPDU. It returns the
+ * packed tlv for a profile.
+ */
+struct packed_tlv *vdp_gettlv(struct vdp_data *vd, struct vsi_profile *profile)
+{
+ int size;
+ struct packed_tlv *ptlv = NULL;
+
+ /* frees the unpacked_tlv in vdp_data
+ * also done in vdp_bld_vsi_tlv */
+ vdp_free_tlv(vd);
+
+ if (vdp_bld_tlv(vd, profile)) {
+ LLDPAD_ERR("%s:%s vdp_bld_tlv failed\n",
+ __func__, vd->ifname);
+ goto out_err;
+ }
+
+ size = TLVSIZE(vd->vdp);
+
+ if (!size) {
+ LLDPAD_ERR("%s(%i): size %i of unpacked_tlv not correct !\n", __func__, __LINE__,
+ size);
+ goto out_err;
+ }
+
+ ptlv = create_ptlv();
+ if (!ptlv)
+ goto out_err;
+
+ ptlv->tlv = malloc(size);
+ if (!ptlv->tlv)
+ goto out_free;
+
+ ptlv->size = 0;
+ PACK_TLV_AFTER(vd->vdp, ptlv, size, out_free);
+
+ return ptlv;
+
+out_free:
+ ptlv = free_pkd_tlv(ptlv);
+out_err:
+ LLDPAD_ERR("%s:%s: failed\n", __func__, vd->ifname);
+ return NULL;
+}
+
+/* vdp_profile_equal - checks for equality of 2 profiles
+ * @p1: profile 1
+ * @p2: profile 2
+ *
+ * returns 1 on success, 0 on error
+ *
+ * compares mgrid, id, version, instance, mac and vlan of 2 profiles to find
+ * out if they are equal.
+ */
+int vdp_profile_equal(struct vsi_profile *p1, struct vsi_profile *p2)
+{
+ if (p1->mgrid != p2->mgrid)
+ return 0;
+
+ if (p1->id != p2->id)
+ return 0;
+
+ if (p1->version != p2->version)
+ return 0;
+
+ if (memcmp(p1->instance, p2->instance, 16))
+ return 0;
+
+ if (memcmp(p1->mac, p2->mac, MAC_ADDR_LEN))
+ return 0;
+
+ if (p1->vlan != p2->vlan)
+ return 0;
+
+ return 1;
+}
+
+/* vdp_add_profile - adds a profile to a per port list
+ * @profile: profile to add
+ *
+ * returns the profile that has been found or added, NULL otherwise.
+ *
+ * main interface function which adds a profile to a list kept on a per-port
+ * basis. Checks if the profile is already in the list, adds it if necessary.
+ */
+struct vsi_profile *vdp_add_profile(struct vsi_profile *profile)
+{
+ struct vsi_profile *p;
+ struct vdp_data *vd;
+
+ LLDPAD_DBG("%s(%i): adding vdp profile for %s !\n", __func__, __LINE__,
+ profile->port->ifname);
+
+ vd = vdp_data(profile->port->ifname);
+ if (!vd) {
+ LLDPAD_ERR("%s(%i): Could not find vdp_data for %s !\n", __func__, __LINE__,
+ profile->port->ifname);
+ return NULL;
+ }
+
+ profile->keepaliveTimerExpired = true;
+ profile->response = VDP_RESPONSE_NO_RESPONSE;
+
+ vdp_print_profile(profile);
+
+ /* loop over all existing profiles and check wether
+ * one for this combination already exists. If yes, check,
+ * if the MAC/VLAN pair already exists. If not, add it.
+ * Note: currently only one MAC/VLAN pair supported ! */
+ LIST_FOREACH(p, &vd->profile_head, profile) {
+ if (p) {
+ if (vdp_profile_equal(p, profile)) {
+ if (p->mode == profile->mode) {
+ LLDPAD_DBG("%s(%i): profile already exists, ignoring !\n",
+ __func__, __LINE__);
+ return NULL;
+ } else {
+ LLDPAD_DBG("%s(%i): taking new mode !\n", __func__,
+ __LINE__);
+ p->mode = profile->mode;
+ return p;
+ }
+ }
+ }
+ }
+
+ LIST_INSERT_HEAD(&vd->profile_head, profile, profile );
+
+ return profile;
+}
+
+/* vdp_remove_profile - remove a profile from a per port list
+ * @profile: profile to remove
+ *
+ * returns 0 if removal was successful, -1 if removal failed
+ *
+ * function used in the state machines to remove a profile from a list kept on
+ * a per-port basis. Checks if the profile is in the list, removes it if there.
+ */
+int vdp_remove_profile(struct vsi_profile *profile)
+{
+ struct vsi_profile *p;
+ struct vdp_data *vd;
+
+ LLDPAD_DBG("%s(%i): removing vdp profile on %s !\n", __func__, __LINE__,
+ profile->port->ifname);
+
+ vd = vdp_data(profile->port->ifname);
+ if (!vd) {
+ LLDPAD_ERR("%s(%i): Could not find vdp_data for %s !\n", __func__, __LINE__,
+ profile->port->ifname);
+ return -1;
+ }
+
+ /* loop over all existing profiles and check wether
+ * it exists. If yes, remove it. */
+ LIST_FOREACH(p, &vd->profile_head, profile) {
+ if (p) {
+ vdp_print_profile(p);
+ if (vdp_profile_equal(p, profile)) {
+ LIST_REMOVE(p, profile);
+ free(p);
+ }
+ } else {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/* vdp_ifdown - tear down vdp structures for a interface
+ * @ifname: name of the interface
+ *
+ * no return value
+ *
+ * interface function to lldpad. tears down vdp specific structures if
+ * interface "ifname" goes down.
+ */
+void vdp_ifdown(char *ifname)
+{
+ struct vdp_data *vd;
+ struct vsi_profile *p;
+
+ LLDPAD_DBG("%s called on interface %s !\n", __func__, ifname);
+
+ vd = vdp_data(ifname);
+ if (!vd)
+ goto out_err;
+
+ if (ecp_deinit(ifname))
+ goto out_err;
+
+ LIST_REMOVE(vd, entry);
+
+ LIST_FOREACH(p, &vd->profile_head, profile) {
+ vdp_stop_ackTimer(p);
+ LIST_REMOVE(p, profile);
+ free(p);
+ }
+ vdp_free_tlv(vd);
+ free(vd);
+ LLDPAD_INFO("%s:%s vdp data removed\n", __func__, ifname);
+ return;
+out_err:
+ LLDPAD_ERR("%s:%s vdp data remove failed\n", __func__, ifname);
+
+ return;
+}
+
+/* vdp_ifup - build up vdp structures for a interface
+ * @ifname: name of the interface
+ *
+ * no return value
+ *
+ * interface function to lldpad. builds up vdp specific structures if
+ * interface "ifname" goes down.
+ */
+void vdp_ifup(char *ifname)
+{
+ char *p;
+ struct vdp_data *vd;
+ struct vdp_user_data *ud;
+
+ LLDPAD_DBG("%s(%i): starting VDP for if %s !\n", __func__, __LINE__, ifname);
+
+ vd = vdp_data(ifname);
+ if (vd) {
+ LLDPAD_WARN("%s:%s vdp data already exists !\n", __func__, ifname);
+ return;
+ }
+
+ /* not found, alloc/init per-port module data */
+ vd = (struct vdp_data *) calloc(1, sizeof(struct vdp_data));
+ if (!vd) {
+ LLDPAD_ERR("%s:%s malloc %ld failed\n",
+ __func__, ifname, sizeof(*vd));
+ goto out_err;
+ }
+ strncpy(vd->ifname, ifname, IFNAMSIZ);
+
+ if (!init_cfg()) {
+ free(vd);
+ goto out_err;
+ }
+
+ vd->role = VDP_ROLE_STATION;
+
+ if (!get_cfg(ifname, "vdp.role", (void *)&p,
+ CONFIG_TYPE_STRING)) {
+ if (!strcasecmp(p, VAL_BRIDGE)) {
+ vd->role = VDP_ROLE_BRIDGE;
+ }
+ }
+
+ LLDPAD_DBG("%s: configured for %s mode !\n", ifname,
+ (vd->role ==VDP_ROLE_BRIDGE) ? "bridge" : "station");
+
+ LIST_INIT(&vd->profile_head);
+
+ ud = find_module_user_data_by_if(ifname, &lldp_head, LLDP_MOD_VDP);
+ LIST_INSERT_HEAD(&ud->head, vd, entry);
+
+ ecp_init(ifname);
+
+ LLDPAD_DBG("%s:%s vdp added\n", __func__, ifname);
+ return;
+
+out_err:
+ LLDPAD_ERR("%s:%s vdp adding failed\n", __func__, ifname);
+ return;
+}
+
+static const struct lldp_mod_ops vdp_ops = {
+ .lldp_mod_register = vdp_register,
+ .lldp_mod_unregister = vdp_unregister,
+ .lldp_mod_ifup = vdp_ifup,
+ .lldp_mod_ifdown = vdp_ifdown,
+ .get_arg_handler = vdp_get_arg_handlers,
+};
+
+/* vdp_register - register vdp module to lldpad
+ * @none
+ *
+ * returns lldp_module struct on success, NULL on error
+ *
+ * allocates a module structure with vdp module information and returns it
+ * to lldpad.
+ */
+struct lldp_module *vdp_register(void)
+{
+ struct lldp_module *mod;
+ struct vdp_user_data *ud;
+
+ mod = malloc(sizeof(*mod));
+ if (!mod) {
+ LLDPAD_ERR("failed to malloc module data\n");
+ log_message(MSG_ERR_SERVICE_START_FAILURE,
+ "%s", "failed to malloc module data");
+ goto out_err;
+ }
+ ud = malloc(sizeof(struct vdp_user_data));
+ if (!ud) {
+ free(mod);
+ LLDPAD_ERR("failed to malloc module user data\n");
+ log_message(MSG_ERR_SERVICE_START_FAILURE,
+ "%s", "failed to malloc module user data");
+ goto out_err;
+ }
+ LIST_INIT(&ud->head);
+ mod->id = LLDP_MOD_VDP;
+ mod->ops = &vdp_ops;
+ mod->data = ud;
+ LLDPAD_DBG("%s:done\n", __func__);
+ return mod;
+
+out_err:
+ LLDPAD_ERR("%s:failed\n", __func__);
+ return NULL;
+}
+
+/* vdp_unregister - unregister vdp module from lldpad
+ * @none
+ *
+ * no return value
+ *
+ * frees vdp module structure.
+ */
+void vdp_unregister(struct lldp_module *mod)
+{
+ if (mod->data) {
+ vdp_free_data((struct vdp_user_data *) mod->data);
+ free(mod->data);
+ }
+ free(mod);
+ LLDPAD_DBG("%s:done\n", __func__);
+}
+
+
diff --git a/lldpad.c b/lldpad.c
index 571da31..c0938af 100644
--- a/lldpad.c
+++ b/lldpad.c
@@ -50,6 +50,7 @@
#include "lldp_med.h"
#include "lldp_8023.h"
#include "lldp_evb.h"
+#include "lldp_vdp.h"
#include "config.h"
#include "lldpad_shm.h"
#include "clif.h"
@@ -65,6 +66,7 @@ struct lldp_module *(*register_tlv_table[])(void) = {
med_register,
ieee8023_register,
evb_register,
+ vdp_register,
NULL,
};
--
1.7.2.3
^ permalink raw reply related
* [PATCH 03/10] BUGFIX: check for existence of ifup
From: Jens Osterkamp @ 2010-11-22 15:50 UTC (permalink / raw)
To: e1000-eedc, virtualization, evb; +Cc: chrisw, Jens Osterkamp
In-Reply-To: <1290441057-16729-1-git-send-email-jens@linux.vnet.ibm.com>
added an additional check for existence of the ifup operation.
If it does not exist, just skip it.
Signed-off-by: Jens Osterkamp <jens@linux.vnet.ibm.com>
---
config.c | 6 ++++--
1 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/config.c b/config.c
index 5cfac83..26afe3b 100644
--- a/config.c
+++ b/config.c
@@ -370,8 +370,10 @@ void init_ports(void)
} else if (check_link_status(p->if_name)) {
add_adapter(p->if_name);
- LIST_FOREACH(np, &lldp_head, lldp)
- np->ops->lldp_mod_ifup(p->if_name);
+ LIST_FOREACH(np, &lldp_head, lldp) {
+ if (np->ops->lldp_mod_ifup)
+ np->ops->lldp_mod_ifup(p->if_name);
+ }
set_lldp_port_enable_state(p->if_name, 1);
}
p++;
--
1.7.2.3
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox