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