Blame SOURCES/open-lldp-v1.0.1-2-VDP-vdptool-first-version.patch

df64a6
From 3b559d8d0b52e6a254dc3f59833de4308e18711e Mon Sep 17 00:00:00 2001
df64a6
From: Thomas Richter <tmricht@linux.vnet.ibm.com>
df64a6
Date: Wed, 21 Jan 2015 03:36:26 +0000
df64a6
Subject: [PATCH] VDP: vdptool first version
df64a6
df64a6
This is the first version of a vdp command line interface
df64a6
tool to send and retrieve data to the vdp22 module.
df64a6
This tool follows similar concept as the lldptool.
df64a6
The command line options are similar and some intended
df64a6
functionality (such as -n to retrieve neighbor inforamtion,
df64a6
that is tlv data send by bridges) is not yet implemented.
df64a6
df64a6
Signed-off-by: Thomas Richter <tmricht@linux.vnet.ibm.com>
df64a6
Signed-off-by: John Fastabend <john.r.fastabend@intel.com>
df64a6
---
df64a6
 .gitignore               |    1 +
df64a6
 Makefile.am              |    8 +-
df64a6
 docs/vdptool.8           |  280 +++++++++++
df64a6
 include/qbg_vdp22_clif.h |    2 +
df64a6
 qbg/vdp22_clif.c         |  141 ++++++
df64a6
 vdptool.c                | 1149 ++++++++++++++++++++++++++++++++++++++++++++++
df64a6
 6 files changed, 1579 insertions(+), 2 deletions(-)
df64a6
 create mode 100644 docs/vdptool.8
df64a6
 create mode 100644 qbg/vdp22_clif.c
df64a6
 create mode 100644 vdptool.c
df64a6
df64a6
diff --git a/.gitignore b/.gitignore
df64a6
index c2ac5d7..e2230d9 100644
df64a6
--- a/.gitignore
df64a6
+++ b/.gitignore
df64a6
@@ -31,6 +31,7 @@ missing
df64a6
 dcbtool
df64a6
 lldpad
df64a6
 lldptool
df64a6
+vdptool
df64a6
 nltest
df64a6
 vdptest
df64a6
 qbg22sim
df64a6
diff --git a/Makefile.am b/Makefile.am
df64a6
index 4889d32..fc4f8d6 100644
df64a6
--- a/Makefile.am
df64a6
+++ b/Makefile.am
df64a6
@@ -1,5 +1,5 @@
df64a6
 # target programs to be installed in ${sbindir}
df64a6
-sbin_PROGRAMS = lldpad dcbtool lldptool
df64a6
+sbin_PROGRAMS = lldpad dcbtool lldptool vdptool
df64a6
 
df64a6
 # package nltest and vdptest, but do not install it anywhere
df64a6
 if BUILD_DEBUG
df64a6
@@ -41,7 +41,7 @@ include/parse_cli.h include/version.h include/lldptool_cli.h include/list.h \
df64a6
 include/lldp_mand_clif.h include/lldp_basman_clif.h include/lldp_med_clif.h \
df64a6
 include/lldp_8023_clif.h include/lldp_dcbx_clif.h include/lldp_evb_clif.h \
df64a6
 include/lldp_evb22_clif.h include/qbg_vdp_clif.h include/qbg_vdpnl.h \
df64a6
-include/lldp_8021qaz_clif.h \
df64a6
+include/qbg_vdp22_clif.h include/lldp_8021qaz_clif.h \
df64a6
 include/lldp_orgspec_clif.h include/lldp_cisco_clif.h \
df64a6
 include/lldptool.h include/lldp_rtnl.h include/dcbtool.h include/lldp_dcbx_cfg.h
df64a6
 
df64a6
@@ -76,6 +76,10 @@ liblldp_clif_la_LDFLAGS = -version-info 1:0:0
df64a6
 liblldp_clif_includedir = ${srcdir}/include
df64a6
 liblldp_clif_la_SOURCES = clif.c
df64a6
 
df64a6
+vdptool_SOURCES = vdptool.c lldp_util.c qbg/vdp22_clif.c
df64a6
+vdptool_LDADD = ${srcdir}/liblldp_clif.la
df64a6
+vdptool_LDFLAGS = -llldp_clif $(LIBNL_LIBS)
df64a6
+
df64a6
 dcbtool_SOURCES = dcbtool.c dcbtool_cmds.c parse_cli.l \
df64a6
 weak_readline.c $(lldpad_include_HEADERS) $(noinst_HEADERS)
df64a6
 dcbtool_LDADD = ${srcdir}/liblldp_clif.la
df64a6
diff --git a/docs/vdptool.8 b/docs/vdptool.8
df64a6
new file mode 100644
df64a6
index 0000000..5110bb9
df64a6
--- /dev/null
df64a6
+++ b/docs/vdptool.8
df64a6
@@ -0,0 +1,280 @@
df64a6
+.\" LICENSE
df64a6
+.\"
df64a6
+.\" This software program is released under the terms of a license agreement
df64a6
+.\" between you ('Licensee') and Intel.  Do not use or load this software or
df64a6
+.\" any associated materials (collectively, the 'Software') until you have
df64a6
+.\" carefully read the full terms and conditions of the LICENSE located in this
df64a6
+.\" software package.  By loading or using the Software, you agree to the
df64a6
+.\" terms of this Agreement. If you do not agree with the terms of this
df64a6
+.\" Agreement, do not install or use the Software.
df64a6
+.\"
df64a6
+.\" * Other names and brands may be claimed as the property of others.
df64a6
+.\"
df64a6
+.TH vdptool 8 "April 2014" "open-lldp" "Linux"
df64a6
+.SH NAME
df64a6
+vdptool \- manage the VSI associations and status of lldpad
df64a6
+.SH SYNOPSIS
df64a6
+.B vdptool <command> [options] [argument]
df64a6
+.br
df64a6
+.SH DESCRIPTION
df64a6
+.B vdptool
df64a6
+is used to query and configure the VSI associations in
df64a6
+.B lldpad.
df64a6
+Only the ratified stardard version of the VDP protocol
df64a6
+(also refered to as vdp22) is supported.
df64a6
+It connects to the client interface of
df64a6
+.B lldpad
df64a6
+to perform these operations.
df64a6
+.B vdptool
df64a6
+will operate in interactive mode if it is executed without a \fIcommand\fR.
df64a6
+In interactive mode,
df64a6
+.B vdptool
df64a6
+will also function as an event listener to print out events
df64a6
+as they are received asynchronously from
df64a6
+.BR lldpad "(still to be done)."
df64a6
+It will use libreadline for interactive input when available
df64a6
+(still to be done).
df64a6
+.SH OPTIONS
df64a6
+.TP
df64a6
+.B \-i [ifname]
df64a6
+specifies the network interface to which the command applies.  Most
df64a6
+.B vdptool
df64a6
+commands require specifying a network interface.
df64a6
+.TP
df64a6
+.B -V [tlvid]
df64a6
+specifies the VDP tlv identifier to be set or queried.
df64a6
+.br
df64a6
+The tlvid is an integer value used to identify specific
df64a6
+VDP TLVs.  The tlvid value is the type value for types not equal
df64a6
+to 127 (the organizationally specific type).
df64a6
+For organizationally specific
df64a6
+TLVs, the tlvid is the value represented by the 3 byte OUI and 1 byte
df64a6
+subtype - where the subtype is the lowest order byte of the tlvid.
df64a6
+.br
df64a6
+The tlvid can be entered as a numerical value (e.g. 10 or 0xa), or for
df64a6
+supported TLVs, as a keyword (such as assoc, deassoc, preassoc,
df64a6
+preassoc-rr, etc).
df64a6
+Review the
df64a6
+.B vdptool
df64a6
+help output to see the list of supported TLV keywords.
df64a6
+.sp 1
df64a6
+Use option -c to specify the parameters and its values to be set or queried.
df64a6
+.TP
df64a6
+.B \-n
df64a6
+"neighbor" option for commands which can use it (e.g. get-tlv).
df64a6
+Use this flag to retrieve the last VDP22 data returned from the
df64a6
+bridge.
df64a6
+(not yet supported).
df64a6
+.TP
df64a6
+.B \-c <argument list>
df64a6
+Specifies additional parameters for TLV queries and associations commands.
df64a6
+The argument list varies, depending on the command option
df64a6
+.B (-T)
df64a6
+or 
df64a6
+.BR (-t) .
df64a6
+To establish a VSI association use the command option 
df64a6
+.B (-T)
df64a6
+and specify additional information as arguments in the form
df64a6
+of key=value. See the 
df64a6
+.I "VSI Parameter"
df64a6
+subsection and
df64a6
+.I Example
df64a6
+section below.
df64a6
+To query a VSI specific association use the command option 
df64a6
+.B (-t)
df64a6
+and specify the value of the
df64a6
+VSI Instance Identifier (keywork uuid followed be the VSI
df64a6
+UUID value)
df64a6
+of the VSI association as configuration parameter.
df64a6
+.TP
df64a6
+.B \-r
df64a6
+show raw client interface messages
df64a6
+.TP
df64a6
+.B \-R
df64a6
+show only raw Client interface messages
df64a6
+.SS VSI Parameter
df64a6
+Each VDP22 TLVs contains a command mode, manager identifier,
df64a6
+type identifier, type identifier version, VSI instance identifier,
df64a6
+migiration hints and filter information.
df64a6
+The fields are explained next:
df64a6
+.TP
df64a6
+.B "mode (Command Mode):"
df64a6
+The command mode determines the type 
df64a6
+of the VSI association to be established.
df64a6
+It is an ascii string can be one of:
df64a6
+.RS
df64a6
+.IP assoc:
df64a6
+Create an VSI association.
df64a6
+.IP preassoc:
df64a6
+Create an VSI preassociation. The association
df64a6
+is only announced to the switch.
df64a6
+.IP preassoc-rr:
df64a6
+Create an VSI preassociation. The association
df64a6
+is only announced to the switch and the 
df64a6
+switch should reserve the resources.
df64a6
+.IP deassoc:
df64a6
+Delete an VSI association.
df64a6
+.RE
df64a6
+Other strings are not recognized and return an error.
df64a6
+.TP
df64a6
+.B "mgrid2 (Manager identifier):"
df64a6
+The manager identifier is a string of up to 16
df64a6
+alphanumeric characters.
df64a6
+It can also be an UUID according to RFC 4122
df64a6
+with optional dashes in between.
df64a6
+.TP
df64a6
+.B "typeid (Type Identifier):"
df64a6
+The type identifier is a number in the range
df64a6
+of 0 to 2^24 - 1.
df64a6
+.TP
df64a6
+.B "typeidver (Type Identifier Version):"
df64a6
+The type identifer version is a number
df64a6
+in the range of 0 to 255.
df64a6
+.TP
df64a6
+.B "uuid (VSI Instance Identifier):"
df64a6
+The VSI instance identifier is
df64a6
+an UUID according to RFC 4122
df64a6
+with optional dashes in between.
df64a6
+.TP
df64a6
+.B "hints (Migration Hints):"
df64a6
+The migiration hints is a string aiding in 
df64a6
+migration of virtual machines:
df64a6
+.RS
df64a6
+.IP none:
df64a6
+No hints available.
df64a6
+.IP from:
df64a6
+The virtual machine is migrating away.
df64a6
+.IP to:
df64a6
+The virtual machine is migrating to.
df64a6
+.RE
df64a6
+.TP
df64a6
+.B "fid (Filter Information Data):"
df64a6
+The filter information data can be supplied in four
df64a6
+different formats identified by numbers in parathesis.
df64a6
+Multiple filter information fields can be supplied,
df64a6
+but all have to be of the same format.
df64a6
+.RS
df64a6
+.IP "vlan (1)"
df64a6
+A vlan number only, also known as filter information format 1.
df64a6
+The vlan identifier is a number in the range of 1 to 2^16 - 1.
df64a6
+The high order 4 bits are used as quality of service bits.
df64a6
+The vlan identifier can be zero, a vlan identifier is then
df64a6
+selected by the switch. Refer to IEEE 802.1 Qbg ratified
df64a6
+standard for details.
df64a6
+.IP "vlan-mac (2)"
df64a6
+A vlan number and MAC address delimited by a slash ('-'),
df64a6
+also known as filter information format 2.
df64a6
+The MAC address is specified in the format xx:xx:xx:xx:xx:xx.
df64a6
+The colons are mandatory.
df64a6
+For vlan details see (1).
df64a6
+.IP "vlan-mac-group (4)"
df64a6
+A vlan number, MAC address and group identifier, 
df64a6
+each delimited by a slash ('-'),
df64a6
+also known as filter information format 4.
df64a6
+The group identifier is a 32 bit number.
df64a6
+For vlan and MAC address details see (1) and (2).
df64a6
+.IP "vlan--group (3)"
df64a6
+A vlan number and group identifier, 
df64a6
+delimited by two slashes ('--'),
df64a6
+also known as filter information format 3.
df64a6
+For vlan and group details see (1) and (4).
df64a6
+.RE
df64a6
+.SH COMMANDS
df64a6
+.TP
df64a6
+.B license
df64a6
+show license information
df64a6
+.TP
df64a6
+.B \-h, help
df64a6
+show usage information
df64a6
+.TP
df64a6
+.B \-v, version
df64a6
+show version information
df64a6
+.TP
df64a6
+.B \-t, get-tlv
df64a6
+get TLV information for the specified interface
df64a6
+.TP
df64a6
+.B \-T, set-tlv
df64a6
+set TLV information for the specified interface
df64a6
+.TP
df64a6
+.B \-p, ping
df64a6
+display the process identifier of the running lldpad process
df64a6
+.TP
df64a6
+.B \-q, quit
df64a6
+exit from interactive mode
df64a6
+.PP
df64a6
+.SH NOTES
df64a6
+This tool is in its early design and development phase.
df64a6
+It it buggy, incomplete and most of the ideas have not even
df64a6
+been thought of....
df64a6
+It reflects the current state of development when
df64a6
+I had been given another work assignment.
df64a6
+I append it so some else can continue to work on this.
df64a6
+.SH EXAMPLES
df64a6
+.TP
df64a6
+Display process identifier of lldpad
df64a6
+.br
df64a6
+vdptool -p
df64a6
+.TP
df64a6
+Create a VSI association on interface eth2
df64a6
+.br
df64a6
+.nf
df64a6
+Supported today: One config parameter and comma separated list
df64a6
+vdptool -i eth2 -T -V assoc -c vsi=assoc,blabla,5, \\
df64a6
+	1122,4,none,2-52:00:00:11:22:33-200
df64a6
+
df64a6
+Planned for the future:
df64a6
+vdptool -i eth2 -T -V assoc -c mgrid2=blabla -c typeid=5 \\
df64a6
+	-c uuid=1122 -c typeidver=4 -c hints=none -c fid=2-52:00:00:11:22:33-200
df64a6
+.fi
df64a6
+.TP
df64a6
+Query all VSI association on interface eth2
df64a6
+.br
df64a6
+vdptool -i eth2 -t -V assoc
df64a6
+.SH SEE ALSO
df64a6
+.BR lldptool-dcbx (8),
df64a6
+.BR lldptool-ets (8),
df64a6
+.BR lldptool-pfc (8),
df64a6
+.BR lldptool-app (8),
df64a6
+.BR lldptool-med (8),
df64a6
+.BR lldptool-vdp (8),
df64a6
+.BR lldptool-evb (8),
df64a6
+.BR lldptool-evb22 (8),
df64a6
+.BR dcbtool (8),
df64a6
+.BR lldpad (8)
df64a6
+.br
df64a6
+.SH COPYRIGHT
df64a6
+vdptool - VSI configuration utility
df64a6
+.br
df64a6
+.IP Copyright(c)
df64a6
+(c) 2014 IBM Corporation.
df64a6
+.BR
df64a6
+Portions of vdptool are based on:
df64a6
+.IP open-lldp-0.96
df64a6
+.IP "lldptool - LLDP agent configuration utility"
df64a6
+.IP Copyright(c)
df64a6
+2007-2012 Intel Corporation.
df64a6
+.BR
df64a6
+Portions of lldptool are based on:
df64a6
+.IP hostapd-0.5.7
df64a6
+.IP Copyright
df64a6
+(c) 2004-2008, Jouni Malinen <j@w1.fi>
df64a6
+
df64a6
+.SH LICENSE
df64a6
+This program is free software; you can redistribute it and/or modify it
df64a6
+under the terms and conditions of the GNU General Public License,
df64a6
+version 2, as published by the Free Software Foundation.
df64a6
+.LP
df64a6
+This program is distributed in the hope it will be useful, but WITHOUT
df64a6
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
df64a6
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
df64a6
+more details.
df64a6
+.LP
df64a6
+You should have received a copy of the GNU General Public License along with
df64a6
+this program; if not, write to the Free Software Foundation, Inc.,
df64a6
+51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
df64a6
+.LP
df64a6
+The full GNU General Public License is included in this distribution in
df64a6
+the file called "COPYING".
df64a6
+.SH SUPPORT
df64a6
+Contact Information:
df64a6
+open-lldp Mailing List <lldp-devel@open-lldp.org>
df64a6
diff --git a/include/qbg_vdp22_clif.h b/include/qbg_vdp22_clif.h
df64a6
index 20330b8..008022a 100644
df64a6
--- a/include/qbg_vdp22_clif.h
df64a6
+++ b/include/qbg_vdp22_clif.h
df64a6
@@ -52,4 +52,6 @@ typedef enum {
df64a6
 	op_delete = 0x20,
df64a6
 	op_key = 0x40
df64a6
 } vdp22_op;
df64a6
+
df64a6
+struct lldp_module *vdp22_cli_register(void);
df64a6
 #endif
df64a6
diff --git a/qbg/vdp22_clif.c b/qbg/vdp22_clif.c
df64a6
new file mode 100644
df64a6
index 0000000..649305d
df64a6
--- /dev/null
df64a6
+++ b/qbg/vdp22_clif.c
df64a6
@@ -0,0 +1,141 @@
df64a6
+/*******************************************************************************
df64a6
+
df64a6
+  Implementation of VDP 22 (ratified standard) according to IEEE 802.1Qbg
df64a6
+  (c) Copyright IBM Corp. 2014
df64a6
+
df64a6
+  Author(s): Thomas Richter <tmricht@linux.vnet.ibm.com>
df64a6
+
df64a6
+  This program is free software; you can redistribute it and/or modify it
df64a6
+  under the terms and conditions of the GNU General Public License,
df64a6
+  version 2, as published by the Free Software Foundation.
df64a6
+
df64a6
+  This program is distributed in the hope it will be useful, but WITHOUT
df64a6
+  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
df64a6
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
df64a6
+  more details.
df64a6
+
df64a6
+  You should have received a copy of the GNU General Public License along with
df64a6
+  this program; if not, write to the Free Software Foundation, Inc.,
df64a6
+  51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
df64a6
+
df64a6
+  The full GNU General Public License is included in this distribution in
df64a6
+  the file called "COPYING".
df64a6
+
df64a6
+*******************************************************************************/
df64a6
+
df64a6
+#include <stdio.h>
df64a6
+#include <stdlib.h>
df64a6
+#include <syslog.h>
df64a6
+#include <sys/un.h>
df64a6
+
df64a6
+#include "lldp_mod.h"
df64a6
+#include "clif_msgs.h"
df64a6
+#include "lldp.h"
df64a6
+#include "qbg22.h"
df64a6
+#include "qbg_vdp22def.h"
df64a6
+#include "qbg_vdpnl.h"
df64a6
+#include "qbg_vdp22_cmds.h"
df64a6
+#include "qbg_vdp22_clif.h"
df64a6
+#include "qbg_vdp22def.h"
df64a6
+
df64a6
+static struct type_name_info vdp22_tlv_names[] = {
df64a6
+	{
df64a6
+		.name = "VDP VSI Association",
df64a6
+		.key = "assoc",
df64a6
+		.type = VDP22_ASSOC
df64a6
+	},
df64a6
+	{
df64a6
+		.name = "VDP VSI Deassociation",
df64a6
+		.key = "deassoc",
df64a6
+		.type = VDP22_DEASSOC
df64a6
+	},
df64a6
+	{
df64a6
+		.name = "VDP VSI Preassociation",
df64a6
+		.key = "preassoc",
df64a6
+		.type = VDP22_PREASSOC
df64a6
+	},
df64a6
+	{
df64a6
+		.name = "VDP VSI Preassociation with resource reservation",
df64a6
+		.key = "preassoc-rr",
df64a6
+		.type = VDP22_PREASSOC_WITH_RR
df64a6
+	},
df64a6
+	{
df64a6
+		.type = INVALID_TLVID
df64a6
+	}
df64a6
+};
df64a6
+
df64a6
+static int vdp22_print_help(void)
df64a6
+{
df64a6
+	struct type_name_info *tn = &vdp22_tlv_names[0];
df64a6
+
df64a6
+	while (tn->type != INVALID_TLVID) {
df64a6
+		if (tn->key && strlen(tn->key) && tn->name) {
df64a6
+			printf("   %s", tn->key);
df64a6
+			if (strlen(tn->key) + 3 < 8)
df64a6
+				printf("\t");
df64a6
+			printf("\t: %s\n", tn->name);
df64a6
+		}
df64a6
+		tn++;
df64a6
+	}
df64a6
+	return 0;
df64a6
+}
df64a6
+
df64a6
+static u32 vdp22_lookup_tlv_name(char *tlvid_str)
df64a6
+{
df64a6
+	struct type_name_info *tn = &vdp22_tlv_names[0];
df64a6
+
df64a6
+	while (tn->type != INVALID_TLVID) {
df64a6
+		if (!strcasecmp(tn->key, tlvid_str))
df64a6
+			return tn->type;
df64a6
+		tn++;
df64a6
+	}
df64a6
+	return INVALID_TLVID;
df64a6
+}
df64a6
+
df64a6
+static void vdp22_cli_unregister(struct lldp_module *mod)
df64a6
+{
df64a6
+	free(mod);
df64a6
+}
df64a6
+
df64a6
+/* return 1: if it printed the TLV
df64a6
+ *	  0: if it did not
df64a6
+ */
df64a6
+static int vdp22_print_tlv(u32 tlvid, u16 len, char *info)
df64a6
+{
df64a6
+	struct type_name_info *tn = &vdp22_tlv_names[0];
df64a6
+
df64a6
+	while (tn->type != INVALID_TLVID) {
df64a6
+		if (tlvid == tn->type) {
df64a6
+			printf("%s\n", tn->name);
df64a6
+			if (tn->print_info) {
df64a6
+				printf("\t");
df64a6
+				tn->print_info(len - 4, info);
df64a6
+			}
df64a6
+			return 1;
df64a6
+		}
df64a6
+		tn++;
df64a6
+	}
df64a6
+	return 0;
df64a6
+}
df64a6
+
df64a6
+static const struct lldp_mod_ops vdp22_ops_clif = {
df64a6
+	.lldp_mod_register	= vdp22_cli_register,
df64a6
+	.lldp_mod_unregister	= vdp22_cli_unregister,
df64a6
+	.print_tlv		= vdp22_print_tlv,
df64a6
+	.lookup_tlv_name	= vdp22_lookup_tlv_name,
df64a6
+	.print_help		= vdp22_print_help,
df64a6
+};
df64a6
+
df64a6
+struct lldp_module *vdp22_cli_register(void)
df64a6
+{
df64a6
+	struct lldp_module *mod;
df64a6
+
df64a6
+	mod = malloc(sizeof(*mod));
df64a6
+	if (!mod) {
df64a6
+		fprintf(stderr, "failed to malloc module data\n");
df64a6
+		return NULL;
df64a6
+	}
df64a6
+	mod->id = LLDP_MOD_VDP22;
df64a6
+	mod->ops = &vdp22_ops_clif;
df64a6
+	return mod;
df64a6
+}
df64a6
diff --git a/vdptool.c b/vdptool.c
df64a6
new file mode 100644
df64a6
index 0000000..e7d384a
df64a6
--- /dev/null
df64a6
+++ b/vdptool.c
df64a6
@@ -0,0 +1,1149 @@
df64a6
+/*******************************************************************************
df64a6
+
df64a6
+  LLDP Agent Daemon (LLDPAD) Software
df64a6
+  Copyright(c) IBM Corp. 2014
df64a6
+
df64a6
+  Substantially modified from:
df64a6
+  hostapd-0.5.7
df64a6
+  Copyright (c) 2002-2007, Jouni Malinen <jkmaline@cc.hut.fi> and
df64a6
+  contributors
df64a6
+
df64a6
+  This program is free software; you can redistribute it and/or modify it
df64a6
+  under the terms and conditions of the GNU General Public License,
df64a6
+  version 2, as published by the Free Software Foundation.
df64a6
+
df64a6
+  This program is distributed in the hope it will be useful, but WITHOUT
df64a6
+  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
df64a6
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
df64a6
+  more details.
df64a6
+
df64a6
+  You should have received a copy of the GNU General Public License along with
df64a6
+  this program; if not, write to the Free Software Foundation, Inc.,
df64a6
+  51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
df64a6
+
df64a6
+  The full GNU General Public License is included in this distribution in
df64a6
+  the file called "COPYING".
df64a6
+
df64a6
+  Contact Information:
df64a6
+  open-lldp Mailing List <lldp-devel@open-lldp.org>
df64a6
+
df64a6
+*******************************************************************************/
df64a6
+
df64a6
+/*
df64a6
+ * Thomas Richter, IBM LTC Boeblingen, Germany, Feb 2014
df64a6
+ *
df64a6
+ * Command line interface tool to connect to vdp module of lldpad to
df64a6
+ * set and query VSI profile settings.
df64a6
+ */
df64a6
+
df64a6
+#include <stdlib.h>
df64a6
+#include <stdio.h>
df64a6
+#include <unistd.h>
df64a6
+#include <signal.h>
df64a6
+#include <ctype.h>
df64a6
+#include <errno.h>
df64a6
+#include <getopt.h>
df64a6
+
df64a6
+#include <sys/queue.h>
df64a6
+
df64a6
+#include "version.h"
df64a6
+#include "clif.h"
df64a6
+#include "clif_msgs.h"
df64a6
+#include "lldp_mod.h"
df64a6
+
df64a6
+#include "qbg22.h"
df64a6
+#include "qbg_vdp22_clif.h"
df64a6
+
df64a6
+static char *print_status(cmd_status status)
df64a6
+{
df64a6
+	char *str;
df64a6
+
df64a6
+	switch (status) {
df64a6
+	case cmd_success:
df64a6
+		str = "Successful";
df64a6
+		break;
df64a6
+	case cmd_failed:
df64a6
+		str = "Failed";
df64a6
+		break;
df64a6
+	case cmd_device_not_found:
df64a6
+		str = "Device not found or inactive";
df64a6
+		break;
df64a6
+	case cmd_agent_not_found:
df64a6
+		str = "Agent instance for device not found";
df64a6
+		break;
df64a6
+	case cmd_invalid:
df64a6
+		str = "Invalid command";
df64a6
+		break;
df64a6
+	case cmd_bad_params:
df64a6
+		str = "Invalid parameters";
df64a6
+		break;
df64a6
+	case cmd_peer_not_present:
df64a6
+		str = "Peer feature not present";
df64a6
+		break;
df64a6
+	case cmd_ctrl_vers_not_compatible:
df64a6
+		str = "Version not compatible";
df64a6
+		break;
df64a6
+	case cmd_not_capable:
df64a6
+		str = "Device not capable";
df64a6
+		break;
df64a6
+	case cmd_not_applicable:
df64a6
+		str = "Command not applicable";
df64a6
+		break;
df64a6
+	case cmd_no_access:
df64a6
+		str = "Access denied";
df64a6
+		break;
df64a6
+	case cmd_agent_not_supported:
df64a6
+		str = "TLV does not support agent type";
df64a6
+		break;
df64a6
+	default:
df64a6
+		str = "Unknown status";
df64a6
+		break;
df64a6
+	}
df64a6
+	return str;
df64a6
+}
df64a6
+
df64a6
+static void get_arg_value(char *str, char **arg, char **argval)
df64a6
+{
df64a6
+	unsigned int i;
df64a6
+
df64a6
+	for (i = 0; i < strlen(str); i++)
df64a6
+		if (!isprint(str[i]))
df64a6
+			return;
df64a6
+
df64a6
+	for (i = 0; i < strlen(str); i++)
df64a6
+		if (str[i] == '=')
df64a6
+			break;
df64a6
+
df64a6
+	if (i < strlen(str)) {
df64a6
+		str[i] = '\0';
df64a6
+		*argval = &str[i+1];
df64a6
+	}
df64a6
+	*arg = str;
df64a6
+}
df64a6
+
df64a6
+static int render_cmd(struct cmd *cmd, int argc, char **args, char **argvals)
df64a6
+{
df64a6
+	int len;
df64a6
+	int i;
df64a6
+
df64a6
+	len = sizeof(cmd->obuf);
df64a6
+
df64a6
+	/* all command messages begin this way */
df64a6
+	snprintf(cmd->obuf, len, "%c%08x%c%1x%02x%08x%02x%s%02x%08x",
df64a6
+		MOD_CMD, cmd->module_id, CMD_REQUEST, CLIF_MSG_VERSION,
df64a6
+		cmd->cmd, cmd->ops, (unsigned int) strlen(cmd->ifname),
df64a6
+		cmd->ifname, cmd->type, cmd->tlvid);
df64a6
+#if PADDU
df64a6
+	if (cmd->cmd == cmd_settlv) {
df64a6
+		size_t len2 = 0;
df64a6
+		/*
df64a6
+		 * Get total length and append it plus any args and argvals
df64a6
+		 * to the command message
df64a6
+		 */
df64a6
+		for (i = 0; i < argc; i++) {
df64a6
+			if (args[i])
df64a6
+				len2 += 2 + strlen(args[i]);
df64a6
+			if (argvals[i])
df64a6
+				len2 += 4 + strlen(argvals[i]);
df64a6
+		}
df64a6
+		snprintf(cmd->obuf + strlen(cmd->obuf), len - strlen(cmd->obuf),
df64a6
+			 "%04zx", len2);
df64a6
+	}
df64a6
+#endif
df64a6
+	/* Add any args and argvals to the command message */
df64a6
+	for (i = 0; i < argc; i++) {
df64a6
+		if (args[i])
df64a6
+			snprintf(cmd->obuf + strlen(cmd->obuf),
df64a6
+				 len - strlen(cmd->obuf),
df64a6
+				 "%02x%s", (unsigned int)strlen(args[i]),
df64a6
+				 args[i]);
df64a6
+		if (argvals[i])
df64a6
+			snprintf(cmd->obuf + strlen(cmd->obuf),
df64a6
+				 len - strlen(cmd->obuf), "%04x%s",
df64a6
+				 (unsigned int)strlen(argvals[i]), argvals[i]);
df64a6
+	}
df64a6
+	return strlen(cmd->obuf);
df64a6
+}
df64a6
+
df64a6
+int vdp_clif_command(struct clif *, char *, int);
df64a6
+
df64a6
+static int vdp_cmd_gettlv(struct clif *clif, int argc, char *argv[],
df64a6
+			  struct cmd *cmd, int raw)
df64a6
+{
df64a6
+	int numargs = 0;
df64a6
+	char **args;
df64a6
+	char **argvals;
df64a6
+	int i;
df64a6
+
df64a6
+	if (cmd->cmd != cmd_gettlv)
df64a6
+		return cmd_invalid;
df64a6
+
df64a6
+	args = calloc(argc, sizeof(char *));
df64a6
+	if (!args)
df64a6
+		return cmd_failed;
df64a6
+
df64a6
+	argvals = calloc(argc, sizeof(char *));
df64a6
+	if (!argvals) {
df64a6
+		free(args);
df64a6
+		return cmd_failed;
df64a6
+	}
df64a6
+
df64a6
+	for (i = 0; i < argc; i++)
df64a6
+		get_arg_value(argv[i], &args[i], &argvals[i]);
df64a6
+	numargs = i;
df64a6
+
df64a6
+	/* Default is local tlv query */
df64a6
+	if (!(cmd->ops & op_neighbor))
df64a6
+		cmd->ops |= op_local;
df64a6
+
df64a6
+	if (numargs) {
df64a6
+		/* Only commands with the config option should have arguments.*/
df64a6
+		if (!(cmd->ops & op_config)) {
df64a6
+			printf("%s\n", print_status(cmd_invalid));
df64a6
+			goto out;
df64a6
+		}
df64a6
+
df64a6
+		/* Commands to get neighbor TLVs cannot have arguments. */
df64a6
+		if (cmd->ops & op_neighbor) {
df64a6
+			printf("%s\n", print_status(cmd_invalid));
df64a6
+			goto out;
df64a6
+		}
df64a6
+		cmd->ops |= op_arg;
df64a6
+	}
df64a6
+
df64a6
+	for (i = 0; i < numargs; i++) {
df64a6
+		if (argvals[i]) {
df64a6
+			printf("%s\n", print_status(cmd_invalid));
df64a6
+			goto out;
df64a6
+		}
df64a6
+	}
df64a6
+
df64a6
+	render_cmd(cmd, argc, args, argvals);
df64a6
+	free(args);
df64a6
+	free(argvals);
df64a6
+	return vdp_clif_command(clif, cmd->obuf, raw);
df64a6
+out:
df64a6
+	free(args);
df64a6
+	free(argvals);
df64a6
+	return cmd_invalid;
df64a6
+}
df64a6
+
df64a6
+static int vdp_cmd_settlv(struct clif *clif, int argc, char *argv[],
df64a6
+			  struct cmd *cmd, int raw)
df64a6
+{
df64a6
+	int numargs = 0;
df64a6
+	char **args;
df64a6
+	char **argvals;
df64a6
+	int i;
df64a6
+
df64a6
+	if (cmd->cmd != cmd_settlv)
df64a6
+		return cmd_invalid;
df64a6
+	args = calloc(argc, sizeof(char *));
df64a6
+	if (!args)
df64a6
+		return cmd_failed;
df64a6
+
df64a6
+	argvals = calloc(argc, sizeof(char *));
df64a6
+	if (!argvals) {
df64a6
+		free(args);
df64a6
+		return cmd_failed;
df64a6
+	}
df64a6
+
df64a6
+	for (i = 0; i < argc; i++)
df64a6
+		get_arg_value(argv[i], &args[i], &argvals[i]);
df64a6
+	numargs = i;
df64a6
+
df64a6
+	for (i = 0; i < numargs; i++) {
df64a6
+		if (!argvals[i]) {
df64a6
+			printf("%s\n", print_status(cmd_invalid));
df64a6
+			goto out;
df64a6
+		}
df64a6
+	}
df64a6
+
df64a6
+	if (numargs)
df64a6
+		cmd->ops |= (op_arg | op_argval);
df64a6
+
df64a6
+	render_cmd(cmd, argc, args, argvals);
df64a6
+	free(args);
df64a6
+	free(argvals);
df64a6
+	return vdp_clif_command(clif, cmd->obuf, raw);
df64a6
+out:
df64a6
+	free(args);
df64a6
+	free(argvals);
df64a6
+	return cmd_invalid;
df64a6
+}
df64a6
+
df64a6
+static int hex2u8(char *b)
df64a6
+{
df64a6
+	int hex = -1;
df64a6
+
df64a6
+	if (isxdigit(*b) && isxdigit(*(b + 1)))
df64a6
+		sscanf(b, "%02x", &hex;;
df64a6
+	return hex;
df64a6
+}
df64a6
+
df64a6
+static int hex2u16(char *b)
df64a6
+{
df64a6
+	int hex = -1;
df64a6
+
df64a6
+	if (isxdigit(*b) && isxdigit(*(b + 1)) && isxdigit(*(b + 2))
df64a6
+	    && isxdigit(*(b + 3)))
df64a6
+		sscanf(b, "%04x", &hex;;
df64a6
+	return hex;
df64a6
+}
df64a6
+
df64a6
+static int hex2u32(char *b)
df64a6
+{
df64a6
+	int hex;
df64a6
+	char *b_old = b;
df64a6
+
df64a6
+	for (hex = 0; hex < 8; ++hex)
df64a6
+		if (!isxdigit(*b++))
df64a6
+			return -1;
df64a6
+	sscanf(b_old, "%08x", &hex;;
df64a6
+	return hex;
df64a6
+}
df64a6
+
df64a6
+static int vdp_parse_response(char *buf)
df64a6
+{
df64a6
+	return hex2u8(buf + CLIF_STAT_OFF);
df64a6
+}
df64a6
+
df64a6
+static void print_pair(char *arg, size_t arglen, char *value, size_t valuelen)
df64a6
+{
df64a6
+	while (arglen--)
df64a6
+		putchar(*arg++);
df64a6
+	putchar('=');
df64a6
+	while (valuelen--)
df64a6
+		putchar(*value++);
df64a6
+	putchar('\n');
df64a6
+}
df64a6
+
df64a6
+static int print_arg_value(char *ibuf)
df64a6
+{
df64a6
+	int arglen, valuelen, offset = 0, ilen = strlen(ibuf);
df64a6
+	char *arg, *value;
df64a6
+
df64a6
+	while (offset < ilen) {
df64a6
+		/* Length of argument */
df64a6
+		arglen = hex2u8(ibuf + offset);
df64a6
+		if (arglen < 0)
df64a6
+			break;
df64a6
+		offset += 2;
df64a6
+		arg = ibuf + offset;
df64a6
+		offset += arglen;
df64a6
+
df64a6
+		/* Length of argument value */
df64a6
+		valuelen = hex2u16(ibuf + offset);
df64a6
+		if (valuelen < 0)
df64a6
+			break;
df64a6
+		offset += 4;
df64a6
+		value = ibuf + offset;
df64a6
+		offset += valuelen;
df64a6
+
df64a6
+		print_pair(arg, arglen, value, valuelen);
df64a6
+	}
df64a6
+	return offset;
df64a6
+}
df64a6
+
df64a6
+static int get_tlvid(char *ibuf)
df64a6
+{
df64a6
+	return hex2u32(ibuf);
df64a6
+}
df64a6
+
df64a6
+/*
df64a6
+ * Print a TLV.
df64a6
+ */
df64a6
+static void print_tlv2(char *ibuf)
df64a6
+{
df64a6
+	size_t ilen = strlen(ibuf);
df64a6
+	u16 tlv_type;
df64a6
+	u16 tlv_len;
df64a6
+	u32 tlvid;
df64a6
+	int offset = 0;
df64a6
+	int printed;
df64a6
+	struct lldp_module *np;
df64a6
+
df64a6
+	while (ilen > 0) {
df64a6
+		tlv_len = 2 * sizeof(u16);
df64a6
+		if (ilen < 2 * sizeof(u16)) {
df64a6
+			printf("corrupted TLV ilen:%zd, tlv_len:%d\n",
df64a6
+				ilen, tlv_len);
df64a6
+			break;
df64a6
+		}
df64a6
+		tlv_type = hex2u16(ibuf + offset);
df64a6
+		tlv_len = tlv_type;
df64a6
+		tlv_type >>= 9;
df64a6
+		tlv_len &= 0x01ff;
df64a6
+		offset += 2 * sizeof(u16);
df64a6
+		ilen -= 2 * sizeof(u16);
df64a6
+
df64a6
+		if (ilen < (unsigned) 2 * tlv_len) {
df64a6
+			printf("corrupted TLV ilen:%zd, tlv_len:%d\n",
df64a6
+				ilen, tlv_len);
df64a6
+			break;
df64a6
+		}
df64a6
+		tlvid = tlv_type;
df64a6
+		if (tlvid == INVALID_TLVID) {
df64a6
+			tlvid = get_tlvid(ibuf + offset);
df64a6
+			offset += 8;
df64a6
+		}
df64a6
+		printed = 0;
df64a6
+		LIST_FOREACH(np, &lldp_head, lldp) {
df64a6
+			if (np->ops->print_tlv(tlvid, tlv_len, ibuf + offset)) {
df64a6
+				printed = 1;
df64a6
+				break;
df64a6
+			}
df64a6
+		}
df64a6
+
df64a6
+		if (!printed) {
df64a6
+			if (tlvid < INVALID_TLVID)
df64a6
+				printf("Unidentified TLV\n\ttype:%d %*.*s\n",
df64a6
+					tlv_type, tlv_len*2, tlv_len*2,
df64a6
+					ibuf+offset);
df64a6
+			else
df64a6
+				printf("Unidentified Org Specific TLV\n\t"
df64a6
+				      "OUI: 0x%06x, Subtype: %d, Info: %*.*s\n",
df64a6
+					tlvid >> 8, tlvid & 0x0ff,
df64a6
+					tlv_len*2-8, tlv_len*2-8,
df64a6
+					ibuf+offset);
df64a6
+		}
df64a6
+		if (tlvid > INVALID_TLVID)
df64a6
+			offset += (2 * tlv_len - 8);
df64a6
+		else
df64a6
+			offset += 2 * tlv_len;
df64a6
+		ilen -= 2 * tlv_len;
df64a6
+		if (tlvid == END_OF_LLDPDU_TLV)
df64a6
+			break;
df64a6
+	}
df64a6
+}
df64a6
+
df64a6
+/* Print reply from get command */
df64a6
+static void print_tlvs(struct cmd *cmd, char *ibuf)
df64a6
+{
df64a6
+	if (cmd->ops & op_config) {
df64a6
+		print_arg_value(ibuf);
df64a6
+		return;
df64a6
+	}
df64a6
+	print_tlv2(ibuf);
df64a6
+}
df64a6
+
df64a6
+static void print_cmd_response(char *ibuf, int status)
df64a6
+{
df64a6
+	struct cmd cmd;
df64a6
+	unsigned char len;
df64a6
+	int ioff;
df64a6
+
df64a6
+	if (status != cmd_success) {
df64a6
+		printf("%s\n", print_status(status));
df64a6
+		return;
df64a6
+	}
df64a6
+
df64a6
+	cmd.cmd = hex2u8(ibuf + CMD_CODE);
df64a6
+	cmd.ops = hex2u32(ibuf + CMD_OPS);
df64a6
+	len = hex2u8(ibuf + CMD_IF_LEN);
df64a6
+	ioff = CMD_IF;
df64a6
+	if (len < sizeof(cmd.ifname)) {
df64a6
+		memcpy(cmd.ifname, ibuf + CMD_IF, len);
df64a6
+	} else {
df64a6
+		printf("Response ifname too long: %*s\n", (int)len, cmd.ifname);
df64a6
+		return;
df64a6
+	}
df64a6
+	cmd.ifname[len] = '\0';
df64a6
+	ioff += len;
df64a6
+
df64a6
+	if (cmd.cmd == cmd_gettlv || cmd.cmd == cmd_settlv) {
df64a6
+		cmd.tlvid = hex2u32(ibuf + ioff);
df64a6
+		ioff += 2 * sizeof(cmd.tlvid);
df64a6
+	}
df64a6
+
df64a6
+	switch (cmd.cmd) {
df64a6
+	case cmd_gettlv:
df64a6
+		print_tlvs(&cmd, ibuf + ioff);
df64a6
+		break;
df64a6
+	case cmd_settlv:
df64a6
+		printf("%s", ibuf + ioff);
df64a6
+		break;
df64a6
+	default:
df64a6
+		return;
df64a6
+	}
df64a6
+}
df64a6
+
df64a6
+static void vdp_print_response(char *buf, int status)
df64a6
+{
df64a6
+	switch (buf[CLIF_RSP_OFF]) {
df64a6
+	case PING_CMD:
df64a6
+		if (status)
df64a6
+			printf("FAILED:%s\n", print_status(status));
df64a6
+		else
df64a6
+			printf("%s\n", buf + CLIF_RSP_OFF + 5);
df64a6
+		break;
df64a6
+	case ATTACH_CMD:
df64a6
+	case DETACH_CMD:
df64a6
+		if (status)
df64a6
+			printf("FAILED:%s\n", print_status(status));
df64a6
+		else
df64a6
+			printf("OK\n");
df64a6
+		break;
df64a6
+	case CMD_REQUEST:
df64a6
+		print_cmd_response(buf + CLIF_RSP_OFF, status);
df64a6
+		break;
df64a6
+	default:
df64a6
+		printf("Unknown VDP command response: %s\n", buf);
df64a6
+		break;
df64a6
+	}
df64a6
+}
df64a6
+
df64a6
+static void vdp_print_event_msg(char *buf)
df64a6
+{
df64a6
+	printf("%s buf:%s\n", __func__, buf);
df64a6
+}
df64a6
+
df64a6
+/*
df64a6
+ * Dummy function to avoid linkage of many sources
df64a6
+ */
df64a6
+int get_perm_hwaddr(UNUSED const char *ifname, UNUSED unsigned char *buf_perm,
df64a6
+		    UNUSED unsigned char *buf_san)
df64a6
+{
df64a6
+	return -EIO;
df64a6
+}
df64a6
+
df64a6
+static int show_raw;
df64a6
+
df64a6
+static const char *cli_version =
df64a6
+	"vdptool v" LLDPTOOL_VERSION "\n"
df64a6
+	"Copyright (c) 2014, IBM Corporation\n";
df64a6
+
df64a6
+
df64a6
+static const char *cli_license =
df64a6
+"This program is free software. You can distribute it and/or modify it\n"
df64a6
+"under the terms of the GNU General Public License version 2.\n"
df64a6
+"\n";
df64a6
+/*
df64a6
+"Alternatively, this software may be distributed under the terms of the\n"
df64a6
+"BSD license. See README and COPYING for more details.\n";
df64a6
+*/
df64a6
+
df64a6
+static const char *cli_full_license =
df64a6
+"This program is free software; you can redistribute it and/or modify\n"
df64a6
+"it under the terms of the GNU General Public License version 2 as\n"
df64a6
+"published by the Free Software Foundation.\n"
df64a6
+"\n"
df64a6
+"This program is distributed in the hope that it will be useful,\n"
df64a6
+"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
df64a6
+"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
df64a6
+"GNU General Public License for more details.\n"
df64a6
+"\n"
df64a6
+"You should have received a copy of the GNU General Public License\n"
df64a6
+"along with this program; if not, write to the Free Software\n"
df64a6
+"Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n"
df64a6
+"\n"
df64a6
+"Alternatively, this software may be distributed under the terms of the\n"
df64a6
+"BSD license.\n"
df64a6
+"\n"
df64a6
+"Redistribution and use in source and binary forms, with or without\n"
df64a6
+"modification, are permitted provided that the following conditions are\n"
df64a6
+"met:\n"
df64a6
+"\n"
df64a6
+"1. Redistributions of source code must retain the above copyright\n"
df64a6
+"   notice, this list of conditions and the following disclaimer.\n"
df64a6
+"\n"
df64a6
+"2. Redistributions in binary form must reproduce the above copyright\n"
df64a6
+"   notice, this list of conditions and the following disclaimer in the\n"
df64a6
+"   documentation and/or other materials provided with the distribution.\n"
df64a6
+"\n"
df64a6
+"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n"
df64a6
+"   names of its contributors may be used to endorse or promote products\n"
df64a6
+"   derived from this software without specific prior written permission.\n"
df64a6
+"\n"
df64a6
+"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"
df64a6
+"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
df64a6
+"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
df64a6
+"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"
df64a6
+"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
df64a6
+"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
df64a6
+"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
df64a6
+"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
df64a6
+"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
df64a6
+"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
df64a6
+"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
df64a6
+"\n";
df64a6
+
df64a6
+static const char *commands_usage =
df64a6
+"Usage:\n"
df64a6
+"  vdptool <command> [options] [arg]   general command line usage format\n"
df64a6
+"  vdptool                             go into interactive mode\n"
df64a6
+"          <command> [options] [arg]   general interactive command format\n";
df64a6
+
df64a6
+static const char *commands_options =
df64a6
+"Options:\n"
df64a6
+"  -i [ifname]           network interface\n"
df64a6
+"  -V [tlvid]            TLV identifier\n"
df64a6
+"                        may be numeric or keyword (see below)\n"
df64a6
+"  -c <argument list>    used with get TLV command to specify\n"
df64a6
+"                        that the list of configuration elements\n"
df64a6
+"  -n                    \"neighbor\" option for command (To be done)\n"
df64a6
+"  -r                    show raw message\n"
df64a6
+"  -R                    show only raw messages\n";
df64a6
+
df64a6
+static const char *commands_help =
df64a6
+"Commands:\n"
df64a6
+"  license    show license information\n"
df64a6
+"  -h|help    show command usage information\n"
df64a6
+"  -v|version show version\n"
df64a6
+"  -p|ping    ping lldpad and query pid of lldpad\n"
df64a6
+"  -q|quit    exit lldptool (interactive mode)\n"
df64a6
+"  -t|get-tlv get tlvid value\n"
df64a6
+"  -T|set-tlv set arg for tlvid to value\n";
df64a6
+
df64a6
+static struct clif *clif_conn;
df64a6
+static int cli_quit;
df64a6
+static int cli_attached;
df64a6
+
df64a6
+/*
df64a6
+ * insert to head, so first one is last
df64a6
+ */
df64a6
+struct lldp_module *(*register_tlv_table[])(void) = {
df64a6
+	vdp22_cli_register,
df64a6
+	NULL,
df64a6
+};
df64a6
+
df64a6
+static void init_modules(void)
df64a6
+{
df64a6
+	struct lldp_module *module;
df64a6
+	struct lldp_module *premod = NULL;
df64a6
+	int i = 0;
df64a6
+
df64a6
+	LIST_INIT(&lldp_head);
df64a6
+	for (i = 0; register_tlv_table[i]; i++) {
df64a6
+		module = register_tlv_table[i]();
df64a6
+		if (premod)
df64a6
+			LIST_INSERT_AFTER(premod, module, lldp);
df64a6
+		else
df64a6
+			LIST_INSERT_HEAD(&lldp_head, module, lldp);
df64a6
+		premod = module;
df64a6
+	}
df64a6
+}
df64a6
+
df64a6
+void deinit_modules(void)
df64a6
+{
df64a6
+	struct lldp_module *module;
df64a6
+
df64a6
+	while (lldp_head.lh_first != NULL) {
df64a6
+		module = lldp_head.lh_first;
df64a6
+		LIST_REMOVE(lldp_head.lh_first, lldp);
df64a6
+		module->ops->lldp_mod_unregister(module);
df64a6
+	}
df64a6
+}
df64a6
+
df64a6
+static void usage(void)
df64a6
+{
df64a6
+	fprintf(stderr, "%s\n", cli_version);
df64a6
+	fprintf(stderr, "\n%s\n%s\n%s\n",
df64a6
+		commands_usage, commands_options, commands_help);
df64a6
+}
df64a6
+
df64a6
+static void print_raw_message(char *msg, int print)
df64a6
+{
df64a6
+	if (!print || !(print & SHOW_RAW))
df64a6
+		return;
df64a6
+
df64a6
+	if (!(print & SHOW_RAW_ONLY)) {
df64a6
+		switch (msg[MSG_TYPE]) {
df64a6
+		case EVENT_MSG:
df64a6
+			printf("event: ");
df64a6
+			break;
df64a6
+		case CMD_RESPONSE:
df64a6
+			printf("rsp: ");
df64a6
+			break;
df64a6
+		default:
df64a6
+			printf("cmd: ");
df64a6
+			break;
df64a6
+		}
df64a6
+	}
df64a6
+	printf("%s\n", msg);
df64a6
+}
df64a6
+
df64a6
+static int parse_print_message(char *msg, int print)
df64a6
+{
df64a6
+	int status = 0;
df64a6
+
df64a6
+	status = vdp_parse_response(msg);
df64a6
+	print_raw_message(msg, print);
df64a6
+	if (print & SHOW_RAW_ONLY)
df64a6
+		return status;
df64a6
+
df64a6
+	if (msg[MSG_TYPE] == CMD_RESPONSE)
df64a6
+		vdp_print_response(msg, status);
df64a6
+	else if (msg[MSG_TYPE] == MOD_CMD && msg[MOD_MSG_TYPE] == EVENT_MSG)
df64a6
+		vdp_print_event_msg(&msg[MOD_MSG_TYPE]);
df64a6
+	return status;
df64a6
+}
df64a6
+
df64a6
+static void cli_close_connection(void)
df64a6
+{
df64a6
+	if (clif_conn == NULL)
df64a6
+		return;
df64a6
+
df64a6
+	if (cli_attached) {
df64a6
+		clif_detach(clif_conn);
df64a6
+		cli_attached = 0;
df64a6
+	}
df64a6
+	clif_close(clif_conn);
df64a6
+	clif_conn = NULL;
df64a6
+}
df64a6
+
df64a6
+
df64a6
+static void cli_msg_cb(char *msg, UNUSED size_t len)
df64a6
+{
df64a6
+	parse_print_message(msg, SHOW_OUTPUT | show_raw);
df64a6
+}
df64a6
+
df64a6
+
df64a6
+/* structure of the print argument bitmap:
df64a6
+ *     SHOW_NO_OUTPUT (0x0) - don't print anything for the command
df64a6
+ *     SHOW_OUTPUT (0x01)   - print output for the command
df64a6
+ *     SHOW_RAW (0x02)      - print the raw clif command messages
df64a6
+ *     SHOW_RAW_ONLY (0x04) - print only the raw clif command messages
df64a6
+*/
df64a6
+static int _clif_command(struct clif *clif, char *cmd, int print)
df64a6
+{
df64a6
+	char buf[MAX_CLIF_MSGBUF];
df64a6
+	size_t len;
df64a6
+	int ret;
df64a6
+
df64a6
+	print_raw_message(cmd, print);
df64a6
+
df64a6
+	if (clif_conn == NULL) {
df64a6
+		printf("Not connected to lldpad - command dropped.\n");
df64a6
+		return -1;
df64a6
+	}
df64a6
+	len = sizeof(buf) - 1;
df64a6
+	ret = clif_request(clif, cmd, strlen(cmd), buf, &len, cli_msg_cb);
df64a6
+	if (ret == -2) {
df64a6
+		printf("'%s' command timed out.\n", cmd);
df64a6
+		return -2;
df64a6
+	} else if (ret < 0) {
df64a6
+		printf("'%s' command failed.\n", cmd);
df64a6
+		return -1;
df64a6
+	}
df64a6
+	if (print) {
df64a6
+		buf[len] = '\0';
df64a6
+		ret = parse_print_message(buf, print);
df64a6
+	}
df64a6
+
df64a6
+	return ret;
df64a6
+}
df64a6
+
df64a6
+int vdp_clif_command(struct clif *clif, char *cmd, int raw)
df64a6
+{
df64a6
+	return _clif_command(clif, cmd, SHOW_OUTPUT | raw);
df64a6
+}
df64a6
+
df64a6
+static int cli_cmd_ping(struct clif *clif, UNUSED int argc, UNUSED char *argv[],
df64a6
+			UNUSED struct cmd *command, int raw)
df64a6
+{
df64a6
+	return vdp_clif_command(clif, "P", raw);
df64a6
+}
df64a6
+
df64a6
+static int
df64a6
+cli_cmd_nop(UNUSED struct clif *clif, UNUSED int argc, UNUSED char *argv[],
df64a6
+	    UNUSED struct cmd *command, UNUSED int raw)
df64a6
+{
df64a6
+	return 0;
df64a6
+}
df64a6
+
df64a6
+static int
df64a6
+cli_cmd_help(UNUSED struct clif *clif, UNUSED int argc, UNUSED char *argv[],
df64a6
+	     UNUSED struct cmd *command, UNUSED int raw)
df64a6
+{
df64a6
+	struct lldp_module *np;
df64a6
+
df64a6
+	printf("%s\n%s\n%s", commands_usage, commands_options, commands_help);
df64a6
+
df64a6
+	printf("\nTLV identifiers:\n");
df64a6
+	LIST_FOREACH(np, &lldp_head, lldp)
df64a6
+		if (np->ops->print_help)
df64a6
+			np->ops->print_help();
df64a6
+	return 0;
df64a6
+}
df64a6
+
df64a6
+static int
df64a6
+cli_cmd_version(UNUSED struct clif *clif, UNUSED int argc, UNUSED char *argv[],
df64a6
+		UNUSED struct cmd *command, UNUSED int raw)
df64a6
+{
df64a6
+	printf("%s\n", cli_version);
df64a6
+	return 0;
df64a6
+}
df64a6
+
df64a6
+static int
df64a6
+cli_cmd_license(UNUSED struct clif *clif, UNUSED int argc, UNUSED char *argv[],
df64a6
+		UNUSED struct cmd *command, UNUSED int raw)
df64a6
+{
df64a6
+	printf("%s\n", cli_full_license);
df64a6
+	return 0;
df64a6
+}
df64a6
+
df64a6
+static int
df64a6
+cli_cmd_quit(UNUSED struct clif *clif, UNUSED int argc, UNUSED char *argv[],
df64a6
+	     UNUSED struct cmd *command, UNUSED int raw)
df64a6
+{
df64a6
+	cli_quit = 1;
df64a6
+	return 0;
df64a6
+}
df64a6
+
df64a6
+static struct cli_cmd {
df64a6
+	vdp22_cmd cmdcode;
df64a6
+	const char *cmdstr;
df64a6
+	int (*handler)(struct clif *clif, int argc, char *argv[],
df64a6
+		       struct cmd *cmd, int raw);
df64a6
+} cli_commands[] = {
df64a6
+	{ cmd_ping,     "ping",      cli_cmd_ping },
df64a6
+	{ cmd_help,     "help",      cli_cmd_help },
df64a6
+	{ cmd_license,  "license",   cli_cmd_license },
df64a6
+	{ cmd_version,  "version",   cli_cmd_version },
df64a6
+	{ cmd_quit,     "quit",      cli_cmd_quit },
df64a6
+	{ cmd_gettlv,   "gettlv",    vdp_cmd_gettlv },
df64a6
+	{ cmd_gettlv,   "get-tlv",   vdp_cmd_gettlv },
df64a6
+	{ cmd_settlv,   "settlv",    vdp_cmd_settlv },
df64a6
+	{ cmd_settlv,   "set-tlv",   vdp_cmd_settlv },
df64a6
+	{ cmd_nop,       NULL,       cli_cmd_nop }
df64a6
+};
df64a6
+
df64a6
+u32 lookup_tlvid(char *tlvid_str)
df64a6
+{
df64a6
+	struct lldp_module *np;
df64a6
+	u32 tlvid = INVALID_TLVID;
df64a6
+
df64a6
+	LIST_FOREACH(np, &lldp_head, lldp) {
df64a6
+		if (np->ops->lookup_tlv_name) {
df64a6
+			tlvid = np->ops->lookup_tlv_name(tlvid_str);
df64a6
+			if (tlvid != INVALID_TLVID)
df64a6
+				break;
df64a6
+		}
df64a6
+	}
df64a6
+
df64a6
+	return tlvid;
df64a6
+}
df64a6
+
df64a6
+void print_args(int argc, char *argv[])
df64a6
+{
df64a6
+	int i;
df64a6
+
df64a6
+	for (i = 0; i < argc; i++)
df64a6
+		printf("\tremaining arg %d = %s\n", i, argv[i]);
df64a6
+}
df64a6
+
df64a6
+static struct option lldptool_opts[] = {
df64a6
+	{"help", 0, NULL, 'h'},
df64a6
+	{"version", 0, NULL, 'v'},
df64a6
+	{"stats", 0, NULL, 'S'},
df64a6
+	{"get-tlv", 0, NULL, 't'},
df64a6
+	{"set-tlv", 0, NULL, 'T'},
df64a6
+	{"get-lldp", 0, NULL, 'l'},
df64a6
+	{"set-lldp", 0, NULL, 'L'},
df64a6
+	{0, 0, 0, 0}
df64a6
+};
df64a6
+
df64a6
+static int request(struct clif *clif, int argc, char *argv[])
df64a6
+{
df64a6
+	struct cli_cmd *cmd, *match = NULL;
df64a6
+	struct cmd command;
df64a6
+	int count;
df64a6
+	int ret	= 0;
df64a6
+	int newraw = 0;
df64a6
+	int numargs = 0;
df64a6
+	char **argptr = &argv[0];
df64a6
+	char *end;
df64a6
+	int c;
df64a6
+	int option_index;
df64a6
+
df64a6
+	memset((void *)&command, 0, sizeof(command));
df64a6
+	command.cmd = cmd_nop;
df64a6
+	command.type = NEAREST_CUSTOMER_BRIDGE;
df64a6
+	command.module_id = LLDP_MOD_VDP22;
df64a6
+	command.tlvid = INVALID_TLVID;
df64a6
+
df64a6
+	opterr = 0;
df64a6
+	for (;;) {
df64a6
+		c = getopt_long(argc, argv, "i:tThcnvrRpqV:",
df64a6
+				lldptool_opts, &option_index);
df64a6
+		if (c < 0)
df64a6
+			break;
df64a6
+		switch (c) {
df64a6
+		case '?':
df64a6
+			printf("missing argument for option %s\n\n",
df64a6
+			       argv[optind-1]);
df64a6
+			usage();
df64a6
+			return -1;
df64a6
+		case 'i':
df64a6
+			strncpy(command.ifname, optarg, IFNAMSIZ);
df64a6
+			command.ifname[IFNAMSIZ] = '\0';
df64a6
+			break;
df64a6
+		case 'V':
df64a6
+			if (command.tlvid != INVALID_TLVID) {
df64a6
+				printf("\nInvalid command: multiple TLV identifiers: %s\n",
df64a6
+				       optarg);
df64a6
+				return -1;
df64a6
+			}
df64a6
+
df64a6
+			/* Currently tlvid unset lookup and verify parameter */
df64a6
+			errno = 0;
df64a6
+			command.tlvid = strtoul(optarg, &end, 0);
df64a6
+			if (!command.tlvid || errno || *end != '\0' ||
df64a6
+			    end == optarg)
df64a6
+				command.tlvid = lookup_tlvid(optarg);
df64a6
+			if (command.tlvid == INVALID_TLVID) {
df64a6
+				printf("\nInvalid TLV identifier: %s\n",
df64a6
+					optarg);
df64a6
+				return -1;
df64a6
+			}
df64a6
+			break;
df64a6
+		case 'p':
df64a6
+			command.cmd = cmd_ping;
df64a6
+			break;
df64a6
+		case 'q':
df64a6
+			command.cmd = cmd_quit;
df64a6
+			break;
df64a6
+		case 't':
df64a6
+			command.cmd = cmd_gettlv;
df64a6
+			break;
df64a6
+		case 'T':
df64a6
+			command.cmd = cmd_settlv;
df64a6
+			break;
df64a6
+		case 'c':
df64a6
+			command.ops |= op_config;
df64a6
+			break;
df64a6
+		case 'n':
df64a6
+			command.ops |= op_neighbor;
df64a6
+			break;
df64a6
+		case 'h':
df64a6
+			command.cmd = cmd_help;
df64a6
+			break;
df64a6
+		case 'r':
df64a6
+			if (newraw) {
df64a6
+				usage();
df64a6
+				return -1;
df64a6
+			}
df64a6
+			newraw = SHOW_RAW;
df64a6
+			break;
df64a6
+		case 'R':
df64a6
+			if (newraw) {
df64a6
+				usage();
df64a6
+				return -1;
df64a6
+			}
df64a6
+			newraw = (SHOW_RAW | SHOW_RAW_ONLY);
df64a6
+			break;
df64a6
+		case 'v':
df64a6
+			command.cmd = cmd_version;
df64a6
+			break;
df64a6
+		default:
df64a6
+			usage();
df64a6
+			ret = -1;
df64a6
+		}
df64a6
+	}
df64a6
+
df64a6
+	/* if no command was supplied via an option flag, then
df64a6
+	 * the first remaining argument should be the command.
df64a6
+	 */
df64a6
+	count = 0;
df64a6
+	if (command.cmd == cmd_nop && optind < argc) {
df64a6
+		cmd = cli_commands;
df64a6
+		while (cmd->cmdcode != cmd_nop) {
df64a6
+			if (strncasecmp(cmd->cmdstr, argv[optind],
df64a6
+			    strlen(argv[optind])) == 0) {
df64a6
+				match = cmd;
df64a6
+				command.cmd = match->cmdcode;
df64a6
+				count++;
df64a6
+			}
df64a6
+			cmd++;
df64a6
+		}
df64a6
+	}
df64a6
+
df64a6
+	if (count > 1) {
df64a6
+		printf("Ambiguous command '%s'; possible commands:",
df64a6
+			argv[optind]);
df64a6
+		cmd = cli_commands;
df64a6
+		while (cmd->cmdstr) {
df64a6
+			if (strncasecmp(cmd->cmdstr, argv[optind],
df64a6
+			    strlen(argv[optind])) == 0)
df64a6
+				printf(" %s", cmd->cmdstr);
df64a6
+			cmd++;
df64a6
+		}
df64a6
+		printf("\n");
df64a6
+		ret = -1;
df64a6
+	} else {
df64a6
+		if (!match) {
df64a6
+			cmd = cli_commands;
df64a6
+			while (cmd->cmdcode != command.cmd)
df64a6
+				cmd++;
df64a6
+			match = cmd;
df64a6
+		}
df64a6
+		numargs = argc-optind - count;
df64a6
+		if (numargs)
df64a6
+			argptr = &argv[argc-numargs];
df64a6
+		ret = match->handler(clif, numargs, argptr, &command, newraw);
df64a6
+	}
df64a6
+	return ret;
df64a6
+}
df64a6
+
df64a6
+static void cli_recv_pending(struct clif *clif, int in_read)
df64a6
+{
df64a6
+	int first = 1;
df64a6
+
df64a6
+	if (clif == NULL)
df64a6
+		return;
df64a6
+	while (clif_pending(clif)) {
df64a6
+		char buf[256];
df64a6
+		size_t len = sizeof(buf) - 1;
df64a6
+		if (clif_recv(clif, buf, &len) == 0) {
df64a6
+			buf[len] = '\0';
df64a6
+			if (in_read && first)
df64a6
+				printf("\n");
df64a6
+			first = 0;
df64a6
+			cli_msg_cb(buf, len);
df64a6
+		} else {
df64a6
+			printf("Could not read pending message.\n");
df64a6
+			break;
df64a6
+		}
df64a6
+	}
df64a6
+}
df64a6
+
df64a6
+static char *do_readline(const char *prompt)
df64a6
+{
df64a6
+	size_t	size = 0;
df64a6
+	ssize_t	rc;
df64a6
+	char	*line = NULL;
df64a6
+
df64a6
+	fputs(prompt, stdout);
df64a6
+	fflush(stdout);
df64a6
+
df64a6
+	rc = getline(&line, &size, stdin);
df64a6
+	if (rc <= 0)
df64a6
+		return NULL;
df64a6
+	if (line[rc - 1] == '\n')
df64a6
+		line[rc - 1] = 0;
df64a6
+	return line;
df64a6
+}
df64a6
+
df64a6
+static void cli_interactive(void)
df64a6
+{
df64a6
+	const int max_args = 20;
df64a6
+	char *cmd, *argv[max_args], *pos;
df64a6
+	int argc;
df64a6
+
df64a6
+	setlinebuf(stdout);
df64a6
+	printf("\nInteractive mode\n\n");
df64a6
+	do {
df64a6
+		cli_recv_pending(clif_conn, 0);
df64a6
+		alarm(1);
df64a6
+		cmd = do_readline("> ");
df64a6
+		alarm(0);
df64a6
+		if (!cmd)
df64a6
+			break;
df64a6
+		argc = 1;
df64a6
+		pos = cmd;
df64a6
+		for (;;) {
df64a6
+			while (*pos == ' ')
df64a6
+				pos++;
df64a6
+			if (*pos == '\0')
df64a6
+				break;
df64a6
+			argv[argc] = pos;
df64a6
+			argc++;
df64a6
+			if (argc == max_args)
df64a6
+				break;
df64a6
+			while (*pos != '\0' && *pos != ' ')
df64a6
+				pos++;
df64a6
+			if (*pos == ' ')
df64a6
+				*pos++ = '\0';
df64a6
+		}
df64a6
+		if (argc) {
df64a6
+			optind = 0;
df64a6
+			request(clif_conn, argc, argv);
df64a6
+		}
df64a6
+		free(cmd);
df64a6
+	} while (!cli_quit);
df64a6
+}
df64a6
+
df64a6
+static void cli_terminate(UNUSED int sig)
df64a6
+{
df64a6
+	cli_close_connection();
df64a6
+	exit(0);
df64a6
+}
df64a6
+
df64a6
+static void cli_alarm(UNUSED int sig)
df64a6
+{
df64a6
+	if (clif_conn && _clif_command(clif_conn, "P", SHOW_NO_OUTPUT)) {
df64a6
+		printf("Connection to lldpad lost - trying to reconnect\n");
df64a6
+		cli_close_connection();
df64a6
+	}
df64a6
+	if (!clif_conn) {
df64a6
+		clif_conn = clif_open();
df64a6
+		if (clif_conn) {
df64a6
+			char attach_str[9] = "";
df64a6
+			u32 mod_id = LLDP_MOD_VDP22;
df64a6
+			bin2hexstr((u8 *)&mod_id, 4, attach_str, 8);
df64a6
+			printf("Connection to lldpad re-established\n");
df64a6
+			if (clif_attach(clif_conn, attach_str) == 0)
df64a6
+				cli_attached = 1;
df64a6
+			else
df64a6
+				printf("Warning: Failed to attach to lldpad.\n");
df64a6
+		}
df64a6
+	}
df64a6
+	if (clif_conn)
df64a6
+		cli_recv_pending(clif_conn, 1);
df64a6
+	alarm(1);
df64a6
+}
df64a6
+
df64a6
+
df64a6
+int main(int argc, char *argv[])
df64a6
+{
df64a6
+	int interactive = 1;
df64a6
+	int warning_displayed = 0;
df64a6
+	int ret = 0;
df64a6
+
df64a6
+	if (argc > 1)
df64a6
+		interactive = 0;
df64a6
+	if (interactive)
df64a6
+		printf("%s\n\n%s\n\n", cli_version, cli_license);
df64a6
+	for (;;) {
df64a6
+		clif_conn = clif_open();
df64a6
+		if (clif_conn) {
df64a6
+			if (warning_displayed)
df64a6
+				printf("Connection established.\n");
df64a6
+			break;
df64a6
+		}
df64a6
+
df64a6
+		if (!interactive) {
df64a6
+			perror("Failed to connect to lldpad - clif_open");
df64a6
+			return -1;
df64a6
+		}
df64a6
+
df64a6
+		if (!warning_displayed) {
df64a6
+			printf("Could not connect to lldpad - re-trying\n");
df64a6
+			warning_displayed = 1;
df64a6
+		}
df64a6
+		sleep(1);
df64a6
+	}
df64a6
+
df64a6
+	init_modules();
df64a6
+	signal(SIGINT, cli_terminate);
df64a6
+	signal(SIGTERM, cli_terminate);
df64a6
+	signal(SIGALRM, cli_alarm);
df64a6
+
df64a6
+	if (interactive) {
df64a6
+		char attach_str[9] = "";
df64a6
+		u32 mod_id = LLDP_MOD_VDP22;
df64a6
+		bin2hexstr((u8 *)&mod_id, 4, attach_str, 8);
df64a6
+		if (clif_attach(clif_conn, attach_str) == 0)
df64a6
+			cli_attached = 1;
df64a6
+		else
df64a6
+			printf("Warning: Failed to attach to lldpad.\n");
df64a6
+		cli_interactive();
df64a6
+	} else {
df64a6
+		ret = request(clif_conn, argc, &argv[0]);
df64a6
+		ret = !!ret;
df64a6
+	}
df64a6
+	cli_close_connection();
df64a6
+	deinit_modules();
df64a6
+	return ret;
df64a6
+}
df64a6
-- 
df64a6
2.1.0
df64a6