Hi, this set of patches contains the initial implementation of the IEEE 802.1Qbg standard: code for the exchange of EVB TLVs in LLDP frames to negotiate VSI capabalities as well as VDP VSI TLVs between a host with virtual machines and an adjacent switch. It supports setting the parameters of the TLV exchange from the command line using lldptool. VDP profiles consisting of mode,mgrid,typeid,typeidversion,instanceid,mac,vlan can be given to lldpad with lldptool or sent to lldpad via netlink messages from the kernel or another program, e.g. libvirt. VDP profiles are processed through the VDP/VSI and ECP state machines and sent out in ECP frames. ACK frames are received and processed through ECP and VDP/VSI state machines. It implements a VDP bridge role for a port together with a lldptool command to switch a port to the bridge role. These patches apply against lldpad 0.9.38. Protocol debug messages are now logged to syslog via LLDPAD_* macros and no longer to console. If you want to see them, configure your syslog daemon to collect debug messages and start lldpad with -V 7. Changes since last posting: - fix to prevent parsing of TLVs across frame boundary - fixed two minor memleaks in lldp_vdp_cmds.c and event_iface.c - fixed typo For more information about lldpad take a look at http://sourceforge.net/projects/e1000/files/DCB%20Tools/lldpad/ Please review and comment. Thanks ! Jens
Jens Osterkamp
2010-Nov-08 16:11 UTC
[PATCH 1/9] consolidation of MIN and MAX macros in common.h
This patch consolidates a modified version of the already existing MIN macro in lldpad to include/common.h and add a MAX macro. Signed-off-by: Jens Osterkamp <jens at linux.vnet.ibm.com> --- include/common.h | 14 ++++++++++++++ include/dcb_protocol.h | 4 ---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/include/common.h b/include/common.h index 01746ea..ab07dc2 100644 --- a/include/common.h +++ b/include/common.h @@ -387,4 +387,18 @@ typedef int socklen_t; const char * wpa_ssid_txt(u8 *ssid, size_t ssid_len); +#define MIN(x,y) \ + ({ \ + typeof (x) __x = (x); \ + typeof (y) __y = (y); \ + __x < __y ? __x : __y; \ + }) + +#define MAX(x,y) \ + ({ \ + typeof (x) __x = (x); \ + typeof (y) __y = (y); \ + __x > __y ? __x : __y; \ + }) + #endif /* COMMON_H */ diff --git a/include/dcb_protocol.h b/include/dcb_protocol.h index 63e2d3c..d7127f5 100644 --- a/include/dcb_protocol.h +++ b/include/dcb_protocol.h @@ -58,10 +58,6 @@ typedef enum { #define DUP_DCBX_TLV_LLINK 0x0020 #define TOO_MANY_NGHBRS 0x0040 -//#ifndef min /*todo: change to min() - #define MIN(x, y) ((x) < (y) ? x : y) -//#endif - #define INIT_DCB_OUI {0x00,0x1b,0x21} int add_adapter(char *device_name); -- 1.7.1
Jens Osterkamp
2010-Nov-08 16:11 UTC
[PATCH 2/9] implementation of IEEE 802.1Qbg in lldpad, part 1
This patch contains the first part of an initial implementation of the IEEE 802.1Qbg standard: it implements code for the exchange of EVB capabilities between a host with virtual machines and an adjacent switch. For this it adds a new EVB TLV to LLDP. Exchange of EVB TLV may be enabled or disabled on a per port basis. Information about the information negotiated by the protocol can be queried on the commandline with lldptool. This patch adds support for querying and setting parameters used in the exchange of EVB TLV messages. The parameters that can be set are: - forwarding mode - host protocol capabilities (RTE, ECP, VDP) - no. of supported VSIs - retransmission timer exponent (RTE) The parameters are implemented as a local policy: all frames received by an adjacent switch are validated against this policy and taken over where appropriate. Negotiated parameters are stored in lldpads config, picked up again and used at the next start. The patch applies to lldpad 0.9.38 and still contains code to log protocol activity more verbosely than it would be necessary in the final version. Signed-off-by: Jens Osterkamp <jens at linux.vnet.ibm.com> --- Makefile.am | 10 +- include/lldp.h | 19 ++ include/lldp_evb.h | 80 ++++++ include/lldp_evb_clif.h | 51 ++++ include/lldp_evb_cmds.h | 31 +++ include/lldp_tlv.h | 1 + lldp_evb.c | 672 +++++++++++++++++++++++++++++++++++++++++++++++ lldp_evb_clif.c | 226 ++++++++++++++++ lldp_evb_cmds.c | 522 ++++++++++++++++++++++++++++++++++++ lldpad.c | 2 + lldptool.c | 2 + 11 files changed, 1612 insertions(+), 4 deletions(-) create mode 100644 include/lldp_evb.h create mode 100644 include/lldp_evb_clif.h create mode 100644 include/lldp_evb_cmds.h create mode 100644 lldp_evb.c create mode 100644 lldp_evb_clif.c create mode 100644 lldp_evb_cmds.c diff --git a/Makefile.am b/Makefile.am index 743e16f..d59a6fa 100644 --- a/Makefile.am +++ b/Makefile.am @@ -37,7 +37,7 @@ lldpad_include_HEADERS = include/dcb_types.h include/dcbtool.h \ include/dcb_osdep.h include/clif.h include/lldp_dcbx_cmds.h include/common.h \ include/lldpad.h include/os.h include/includes.h include/lldp_mand_cmds.h \ include/clif_msgs.h include/lldp_basman_cmds.h include/lldp_8023_cmds.h \ -include/lldp_med_cmds.h include/lldp_dcbx_cfg.h +include/lldp_med_cmds.h include/lldp_dcbx_cfg.h include/lldp_evb_cmds.h noinst_HEADERS = include/config.h include/ctrl_iface.h \ include/dcb_driver_if_types.h include/dcb_driver_interface.h \ @@ -47,7 +47,7 @@ include/event_iface.h include/messages.h include/parse_cli.h include/version.h \ include/lldptool_cli.h include/list.h \ include/lldp_mand_clif.h include/lldp_basman_clif.h include/lldp_med_clif.h \ include/lldp_8023_clif.h include/lldp_dcbx_clif.h include/lldptool.h \ -include/lldp_rtnl.h +include/lldp_rtnl.h include/lldp_evb_clif.h lldpad_SOURCES = lldpad.c config.c drv_cfg.c ctrl_iface.c event_iface.c eloop.c \ common.c os_unix.c lldp_dcbx_cmds.c log.c lldpad_shm.c \ @@ -62,10 +62,12 @@ lldp_dcbx_cfg.c include/lldp_dcbx_cfg.h \ lldp_util.c include/lldp_util.h \ lldp_mand.c include/lldp_mand.h \ lldp_mand_cmds.c lldp_basman_cmds.c lldp_8023_cmds.c lldp_med_cmds.c \ +lldp_evb_cmds.c \ lldp_tlv.c include/lldp_tlv.h \ lldp_basman.c include/lldp_basman.h \ lldp_med.c include/lldp_med.h \ -lldp_8023.c include/lldp_8023.h +lldp_8023.c include/lldp_8023.h \ +lldp_evb.c include/lldp_evb.h @@ -74,7 +76,7 @@ $(lldpad_include_HEADERS) $(noinst_HEADERS) lldptool_SOURCES = lldptool.c clif.c lldptool_cmds.c common.c os_unix.c \ lldp_mand_clif.c lldp_basman_clif.c lldp_med_clif.c lldp_8023_clif.c \ -lldp_dcbx_clif.c $(lldpad_include_HEADERS) $(noinst_HEADERS) +lldp_dcbx_clif.c lldp_evb_clif.c $(lldpad_include_HEADERS) $(noinst_HEADERS) nltest_SOURCES = nltest.c nltest.h diff --git a/include/lldp.h b/include/lldp.h index 66532bd..0371844 100644 --- a/include/lldp.h +++ b/include/lldp.h @@ -45,6 +45,8 @@ /* Telecommunications Industry Association TR-41 Committee */ #define OUI_TIA_TR41 0x0012bb +#define OUI_IEEE_8021Qbg 0x001b3f + /* IEEE 802.3AB Clause 9: TLV Types */ #define CHASSIS_ID_TLV 1 #define PORT_ID_TLV 2 @@ -186,5 +188,22 @@ enum { #define LLDP_8023_LINKAGG_CAPABLE (1 << 0) #define LLDP_8023_LINKAGG_ENABLED (1 << 1) +/* IEEE 802.1Qbg subtype */ +#define LLDP_EVB_SUBTYPE 0 + +/* forwarding mode */ +#define LLDP_EVB_CAPABILITY_FORWARD_STANDARD (1 << 7) +#define LLDP_EVB_CAPABILITY_FORWARD_REFLECTIVE_RELAY (1 << 6) + +/* EVB supported protocols */ +#define LLDP_EVB_CAPABILITY_PROTOCOL_RTE (1 << 2) +#define LLDP_EVB_CAPABILITY_PROTOCOL_ECP (1 << 1) +#define LLDP_EVB_CAPABILITY_PROTOCOL_VDP (1 << 0) + +/* EVB specific values */ +#define LLDP_EVB_DEFAULT_MAX_VSI 65535 +#define LLDP_EVB_DEFAULT_SVSI 3295 +#define LLDP_EVB_DEFAULT_RTE 15 + void somethingChangedLocal(char *ifname); #endif /* _LLDP_H */ diff --git a/include/lldp_evb.h b/include/lldp_evb.h new file mode 100644 index 0000000..667f9ad --- /dev/null +++ b/include/lldp_evb.h @@ -0,0 +1,80 @@ +/******************************************************************************* + + implementation of EVB TLVs for LLDP + (c) Copyright IBM Corp. 2010 + + Author(s): Jens Osterkamp <jens at linux.vnet.ibm.com> + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + +*******************************************************************************/ + +#ifndef _LLDP_EVB_H +#define _LLDP_EVB_H + +#include "lldp_mod.h" + +#define LLDP_MOD_EVB OUI_IEEE_8021Qbg +#define LLDP_OUI_SUBTYPE { 0x00, 0x1b, 0x3f, 0x00 } + +typedef enum { + EVB_OFFER_CAPABILITIES = 0, + EVB_CONFIGURE, + EVB_CONFIRMATION +} evb_state; + +struct tlv_info_evb { + u8 oui[3]; + u8 sub; + /* supported forwarding mode */ + u8 smode; + /* supported capabilities */ + u8 scap; + /* currently configured forwarding mode */ + u8 cmode; + /* currently configured capabilities */ + u8 ccap; + /* supported no. of vsi */ + u16 svsi; + /* currently configured no. of vsi */ + u16 cvsi; + /* retransmission exponent */ + u8 rte; +} __attribute__ ((__packed__)); + +struct evb_data { + char ifname[IFNAMSIZ]; + struct unpacked_tlv *evb; + struct tlv_info_evb *tie; + /* local policy */ + struct tlv_info_evb *policy; + int state; + LIST_ENTRY(evb_data) entry; +}; + +struct evb_user_data { + LIST_HEAD(evb_head, evb_data) head; +}; + +struct lldp_module *evb_register(void); +void evb_unregister(struct lldp_module *mod); +struct packed_tlv *evb_gettlv(struct port *port); +void evb_ifdown(char *); +void evb_ifup(char *); +struct evb_data *evb_data(char *ifname); + +#endif /* _LLDP_EVB_H */ diff --git a/include/lldp_evb_clif.h b/include/lldp_evb_clif.h new file mode 100644 index 0000000..acaee5e --- /dev/null +++ b/include/lldp_evb_clif.h @@ -0,0 +1,51 @@ +/******************************************************************************* + + implementation of EVB TLVs for LLDP + (c) Copyright IBM Corp. 2010 + + Author(s): Jens Osterkamp <jens at linux.vnet.ibm.com> + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + +*******************************************************************************/ + +#ifndef _LLDP_EVB_CLIF_H +#define _LLDP_EVB_CLIF_H + +struct lldp_module *evb_cli_register(void); +void evb_cli_unregister(struct lldp_module *); +int evb_print_tlv(u32, u16, char *); + +#define EVB_BUF_SIZE 256 + +#define ARG_EVB_FORWARDING_MODE "fmode" + +#define VAL_EVB_FMODE_BRIDGE "bridge" +#define VAL_EVB_FMODE_REFLECTIVE_RELAY "reflectiverelay" + +#define ARG_EVB_CAPABILITIES "capabilities" + +#define VAL_EVB_CAPA_RTE "rte" +#define VAL_EVB_CAPA_ECP "ecp" +#define VAL_EVB_CAPA_VDP "vdp" +#define VAL_EVB_CAPA_NONE "none" + +#define ARG_EVB_VSIS "vsis" + +#define ARG_EVB_RTE "rte" + +#endif diff --git a/include/lldp_evb_cmds.h b/include/lldp_evb_cmds.h new file mode 100644 index 0000000..1367e5d --- /dev/null +++ b/include/lldp_evb_cmds.h @@ -0,0 +1,31 @@ +/******************************************************************************* + + implementation of EVB TLVs for LLDP + (c) Copyright IBM Corp. 2010 + + Author(s): Jens Osterkamp <jens at linux.vnet.ibm.com> + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + +*******************************************************************************/ + +#ifndef _LLDP_EVB_CMDS_H +#define _LLDP_EVB_CMDS_H + +struct arg_handlers *evb_get_arg_handlers(); + +#endif diff --git a/include/lldp_tlv.h b/include/lldp_tlv.h index a32cc71..fe3a75a 100644 --- a/include/lldp_tlv.h +++ b/include/lldp_tlv.h @@ -144,6 +144,7 @@ int tlv_ok(struct unpacked_tlv *tlv); #define TLVID_8021(sub) TLVID(OUI_IEEE_8021, (sub)) #define TLVID_8023(sub) TLVID(OUI_IEEE_8023, (sub)) #define TLVID_MED(sub) TLVID(OUI_TIA_TR41, (sub)) +#define TLVID_8021Qbg(sub) TLVID(OUI_IEEE_8021Qbg, (sub)) /* the size in bytes needed for a packed tlv from unpacked tlv */ #define TLVSIZE(t) ((t) ? (2 + (t)->length) : 0) diff --git a/lldp_evb.c b/lldp_evb.c new file mode 100644 index 0000000..50ee17d --- /dev/null +++ b/lldp_evb.c @@ -0,0 +1,672 @@ +/******************************************************************************* + + implementation of EVB TLVs for LLDP + (c) Copyright IBM Corp. 2010 + + Author(s): Jens Osterkamp <jens at linux.vnet.ibm.com> + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + +*******************************************************************************/ + +#include <net/if.h> +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/utsname.h> +#include <linux/if_bridge.h> +#include <string.h> +#include "lldp.h" +#include "lldp_evb.h" +#include "messages.h" +#include "config.h" +#include "common.h" +#include "lldp_mand_clif.h" +#include "lldp_evb_clif.h" +#include "lldp_evb_cmds.h" + +extern struct lldp_head lldp_head; + +struct evb_data *evb_data(char *ifname) +{ + struct evb_user_data *ud; + struct evb_data *ed = NULL; + + ud = find_module_user_data_by_if(ifname, &lldp_head, LLDP_MOD_EVB); + if (ud) { + LIST_FOREACH(ed, &ud->head, entry) { + if (!strncmp(ifname, ed->ifname, IFNAMSIZ)) + return ed; + } + } + return NULL; +} + +static void evb_print_tlvinfo(struct tlv_info_evb *tie) +{ + LLDPAD_INFO("%s(%i): supported forwarding mode: %02x\n", __FILE__, __LINE__, tie->smode); + LLDPAD_INFO("%s(%i): configured forwarding mode: %02x\n", __FILE__, __LINE__, tie->cmode); + LLDPAD_INFO("%s(%i): supported capabilities: %02x\n", __FILE__, __LINE__, tie->scap); + LLDPAD_INFO("%s(%i): configured capabilities: %02x\n", __FILE__, __LINE__, tie->ccap); + LLDPAD_INFO("%s(%i): supported no. of vsis: %04i\n", __FILE__, __LINE__, tie->svsi); + LLDPAD_INFO("%s(%i): configured no. of vsis: %04i\n", __FILE__, __LINE__, tie->cvsi); + LLDPAD_INFO("%s(%i): rte: %02i\n", __FILE__, __LINE__, tie->rte); +} + +static void evb_dump_tlv(struct unpacked_tlv *tlv) +{ + int i; + char *s, *t; + + s = t = malloc(256); + + if (!s) { + LLDPAD_ERR("%s(%i): unable to allocate string !\n", __func__, __LINE__); + } + + LLDPAD_DBG("%s:type %i, length %i, info ", __func__, tlv->type, tlv->length); + + for (i=0; i < tlv->length; i++) { + int c; + c = sprintf(s, "%02x ", tlv->info[i]); + s += c; + } + + LLDPAD_DBG("%s\n", t); + + free(t); +} + +/* + * evb_bld_cfg_tlv - build the EVB TLV + * @ed: the evb data struct + * + * Returns 0 on success + */ +static int evb_bld_cfg_tlv(struct evb_data *ed) +{ + int rc = 0; + int i; + struct unpacked_tlv *tlv = NULL; + + /* free ed->evb if it exists */ + FREE_UNPKD_TLV(ed, evb); + + if (!is_tlv_txenabled(ed->ifname, TLVID_8021Qbg(LLDP_EVB_SUBTYPE))) { + LLDPAD_ERR("%s:%s:EVB tx is currently disabled !\n", + __func__, ed->ifname); + rc = EINVAL; + goto out_err; + } + + if (ed->tie->smode != ed->policy->smode) { + ed->tie->smode = ed->policy->smode; + ed->state = EVB_OFFER_CAPABILITIES; + } + + tlv = create_tlv(); + if (!tlv) + goto out_err; + + tlv->type = ORG_SPECIFIC_TLV; + tlv->length = sizeof(struct tlv_info_evb); + tlv->info = (u8 *)malloc(tlv->length); + if(!tlv->info) { + free(tlv); + tlv = NULL; + rc = ENOMEM; + goto out_err; + } + memcpy(tlv->info, ed->tie, tlv->length); + + LLDPAD_DBG("%s(%i): TLV about to be sent out:", __func__, __LINE__); + evb_dump_tlv(tlv); + + ed->evb = tlv; +out_err: + return rc; +} + +static void evb_free_tlv(struct evb_data *ed) +{ + if (ed) + FREE_UNPKD_TLV(ed, evb); +} + +/* evb_init_cfg_tlv: + * + * fill up tlv_info_evb structure with reasonable info + */ +static int evb_init_cfg_tlv(struct evb_data *ed) +{ + char arg_path[EVB_BUF_SIZE]; + char *param; + + /* load policy from config */ + ed->policy = (struct tlv_info_evb *) calloc(1, sizeof(struct tlv_info_evb)); + + if (!ed->policy) + return ENOMEM; + + /* set defaults */ + hton24(ed->policy->oui, LLDP_MOD_EVB); + ed->policy->smode = LLDP_EVB_CAPABILITY_FORWARD_STANDARD; + ed->policy->scap = LLDP_EVB_CAPABILITY_PROTOCOL_RTE | LLDP_EVB_CAPABILITY_PROTOCOL_ECP | + LLDP_EVB_CAPABILITY_PROTOCOL_VDP; + ed->policy->cmode = 0; + ed->policy->ccap = 0; + ed->policy->svsi = LLDP_EVB_DEFAULT_SVSI; + ed->policy->rte = LLDP_EVB_DEFAULT_RTE; + + /* pull forwarding mode into policy */ + snprintf(arg_path, sizeof(arg_path), "%s%08x.fmode", + TLVID_PREFIX, TLVID_8021Qbg(LLDP_EVB_SUBTYPE)); + + if (get_cfg(ed->ifname, arg_path, (void *) ¶m, CONFIG_TYPE_STRING)) { + LLDPAD_WARN("%s:%s: loading EVB policy for forwarding mode failed, using default.\n", + __func__, ed->ifname); + } else { + if (strcasestr(param, VAL_EVB_FMODE_BRIDGE)) { + ed->policy->smode = LLDP_EVB_CAPABILITY_FORWARD_STANDARD; + } + + if (strcasestr(param, VAL_EVB_FMODE_REFLECTIVE_RELAY)) { + ed->policy->smode = LLDP_EVB_CAPABILITY_FORWARD_REFLECTIVE_RELAY; + } + + LLDPAD_DBG("%s:%s: policy param fmode = %s.\n", __func__, ed->ifname, param); + LLDPAD_DBG("%s:%s: policy param smode = %x.\n", __func__, ed->ifname, ed->policy->smode); + } + + /* pull capabilities into policy */ + snprintf(arg_path, sizeof(arg_path), "%s%08x.capabilities", + TLVID_PREFIX, TLVID_8021Qbg(LLDP_EVB_SUBTYPE)); + + if (get_cfg(ed->ifname, arg_path, (void *) ¶m, CONFIG_TYPE_STRING)) { + LLDPAD_WARN("%s:%s: loading EVB policy for capabilities failed, using default.\n", + __func__, ed->ifname); + } else { + if (strcasestr(param, VAL_EVB_CAPA_RTE)) { + ed->policy->scap |= LLDP_EVB_CAPABILITY_PROTOCOL_RTE; + } + + if (strcasestr(param, VAL_EVB_CAPA_ECP)) { + ed->policy->scap |= LLDP_EVB_CAPABILITY_PROTOCOL_ECP; + } + + if (strcasestr(param, VAL_EVB_CAPA_VDP)) { + ed->policy->scap |= LLDP_EVB_CAPABILITY_PROTOCOL_VDP; + } + + LLDPAD_DBG("%s:%s: policy param capabilities = %s.\n", __func__, ed->ifname, param); + LLDPAD_DBG("%s:%s: policy param scap = %x.\n", __func__, ed->ifname, ed->policy->scap); + } + + /* pull rte into policy */ + snprintf(arg_path, sizeof(arg_path), "%s%08x.rte", + TLVID_PREFIX, TLVID_8021Qbg(LLDP_EVB_SUBTYPE)); + + if (get_cfg(ed->ifname, arg_path, (void *) ¶m, CONFIG_TYPE_STRING)) { + LLDPAD_WARN("%s:%s: loading EVB policy for rte failed, using default.\n", + __func__, ed->ifname); + } else { + ed->policy->rte = atoi(param); + + LLDPAD_DBG("%s:%s: policy param rte = %s.\n", __func__, ed->ifname, param); + LLDPAD_DBG("%s:%s: policy param rte = %i.\n", __func__, ed->ifname, ed->policy->rte); + } + + /* pull vsis into policy */ + snprintf(arg_path, sizeof(arg_path), "%s%08x.vsis", + TLVID_PREFIX, TLVID_8021Qbg(LLDP_EVB_SUBTYPE)); + + if (get_cfg(ed->ifname, arg_path, (void *) ¶m, CONFIG_TYPE_STRING)) { + LLDPAD_WARN("%s:%s: loading EVB policy for vsis failed, using default.\n", + __func__, ed->ifname); + } else { + ed->policy->svsi = atoi(param); + + LLDPAD_DBG("%s:%s: policy param vsis = %s.\n", __func__, ed->ifname, param); + LLDPAD_DBG("%s:%s: policy param vsis = %i.\n", __func__, ed->ifname, ed->policy->svsi); + } + + /* load last used EVB TLV ... */ + ed->tie = (struct tlv_info_evb *) calloc(1, sizeof(struct tlv_info_evb)); + + if (!ed->tie) { + free(ed->policy); + return ENOMEM; + } + + if (get_config_tlvinfo_bin(ed->ifname, TLVID_8021Qbg(LLDP_EVB_SUBTYPE), + (void *)ed->tie, sizeof(struct tlv_info_evb))) { + LLDPAD_INFO("%s:%s: loading last used EVB TLV failed, using default.\n", + __func__, ed->ifname); + hton24(ed->tie->oui, LLDP_MOD_EVB); + ed->tie->smode = ed->policy->smode; + ed->tie->cmode = 0x0; + ed->tie->scap = ed->policy->scap; + ed->tie->ccap = 0x0; + ed->tie->svsi = LLDP_EVB_DEFAULT_SVSI; + ed->tie->cvsi = 0x0; + ed->tie->rte = LLDP_EVB_DEFAULT_RTE; + } else { + LLDPAD_INFO("%s(%i): loaded last used EVB TLV from file.\n", __FILE__, __LINE__); + } + + return 0; +} + +static int evb_bld_tlv(struct evb_data *ed) +{ + int rc = 0; + + if (!port_find_by_name(ed->ifname)) { + rc = EEXIST; + goto out_err; + } + + if (!init_cfg()) { + rc = ENOENT; + goto out_err; + } + + if (evb_bld_cfg_tlv(ed)) { + LLDPAD_ERR("%s:%s:evb_bld_cfg_tlv() failed\n", + __func__, ed->ifname); + rc = EINVAL; + goto out_err_destroy; + } + + return rc; + +out_err_destroy: + destroy_cfg(); +out_err: + return rc; +} + +static void evb_free_data(struct evb_user_data *ud) +{ + struct evb_data *ed; + if (ud) { + while (!LIST_EMPTY(&ud->head)) { + ed = LIST_FIRST(&ud->head); + LIST_REMOVE(ed, entry); + evb_free_tlv(ed); + free(ed); + } + } +} + +struct packed_tlv *evb_gettlv(struct port *port) +{ + int size; + struct evb_data *ed; + struct packed_tlv *ptlv = NULL; + + ed = evb_data(port->ifname); + if (!ed) + goto out_err; + + evb_free_tlv(ed); + + if (evb_bld_tlv(ed)) { + LLDPAD_ERR("%s:%s evb_bld_tlv failed\n", + __func__, port->ifname); + goto out_err; + } + + size = TLVSIZE(ed->evb); + + if (!size) + goto out_err; + + ptlv = create_ptlv(); + if (!ptlv) + goto out_err; + + ptlv->tlv = malloc(size); + if (!ptlv->tlv) + goto out_free; + + ptlv->size = 0; + PACK_TLV_AFTER(ed->evb, ptlv, size, out_free); + return ptlv; + +out_free: + /* FIXME: free function returns pointer ? */ + ptlv = free_pkd_tlv(ptlv); +out_err: + LLDPAD_ERR("%s:%s: failed\n", __func__, port->ifname); + return NULL; +} + +/* evb_check_and_fill + * + * checks values received in TLV and takes over some values + */ +int evb_check_and_fill(struct evb_data *ed, struct tlv_info_evb *tie) +{ + /* sanity check of received data in tie */ + if ((tie->smode & (LLDP_EVB_CAPABILITY_FORWARD_STANDARD | + LLDP_EVB_CAPABILITY_FORWARD_REFLECTIVE_RELAY)) == 0) { + LLDPAD_ERR("Neither standard nor rr set as forwarding mode !"); + return TLV_ERR; + } + + if ((tie->svsi < 0) || (tie->svsi > LLDP_EVB_DEFAULT_MAX_VSI)) { + LLDPAD_ERR("nr of supported vsis (%i) exceeds allow value range !", tie->svsi); + return TLV_ERR; + } + + if ((tie->cvsi < 0) || (tie->cvsi > LLDP_EVB_DEFAULT_MAX_VSI)) { + LLDPAD_ERR("nr of configured vsis (%i) exceeds allow value range !", tie->cvsi); + return TLV_ERR; + } + + /* check bridge capabilities against local policy*/ + /* if bridge supports RR and we support it as well, request it + * by setting smode in tlv to be sent out (ed->tie->smode) */ + if ( (tie->smode & ed->policy->smode) =+ LLDP_EVB_CAPABILITY_FORWARD_REFLECTIVE_RELAY ) { + ed->tie->smode = LLDP_EVB_CAPABILITY_FORWARD_REFLECTIVE_RELAY; + } else { + ed->tie->smode = LLDP_EVB_CAPABILITY_FORWARD_STANDARD; + } + + /* If both sides support RTE, set it */ + if ((tie->scap & ed->policy->scap) & LLDP_EVB_CAPABILITY_PROTOCOL_RTE) + ed->tie->scap |= LLDP_EVB_CAPABILITY_PROTOCOL_RTE; + + /* If both sides support ECP, set it */ + if ((tie->scap & ed->policy->scap) & LLDP_EVB_CAPABILITY_PROTOCOL_ECP) + ed->tie->scap |= LLDP_EVB_CAPABILITY_PROTOCOL_ECP; + + /* If both sides support VDP, set it */ + if ((tie->scap & ed->policy->scap) & LLDP_EVB_CAPABILITY_PROTOCOL_VDP) + ed->tie->scap |= LLDP_EVB_CAPABILITY_PROTOCOL_VDP; + + /* If supported caps include VDP take over min value of both */ + if (ed->tie->scap & LLDP_EVB_CAPABILITY_PROTOCOL_VDP) + ed->tie->cvsi = MIN(ed->policy->svsi,tie->svsi); + + /* If both sides support RTE and value offer is > 0, set it */ + if ((ed->tie->scap & LLDP_EVB_CAPABILITY_PROTOCOL_RTE) && + (tie->rte > 0) && (ed->policy->rte > 0)) + ed->tie->rte = MAX(ed->policy->rte,tie->rte); + + if (set_config_tlvinfo_bin(ed->ifname, TLVID_8021Qbg(LLDP_EVB_SUBTYPE), + (void *)ed->tie, sizeof(struct tlv_info_evb))) { + LLDPAD_ERR("%s(%i): error saving tlv_info_evb !\n", __FILE__, __LINE__); + } else { + LLDPAD_INFO("%s(%i): saved tlv_info_evb to config !\n", __FILE__, __LINE__); + } + + /* maybe switch has already set the mode based on the saved info sent + * out on ifup */ + + if (tie->cmode == ed->tie->smode) + ed->tie->cmode = tie->cmode; + + return TLV_OK; +} + +/* evb_compare + * + * compare our own and received tlv_info_evb + */ +static int evb_compare(struct evb_data *ed, struct tlv_info_evb *tie) +{ + LLDPAD_DBG("%s(%i): \n", __func__, __LINE__); + + if (ed->tie->cmode == tie->cmode) + return 0; + else + return 1; +} + +/* evb_statemachine: + * + * handle possible states during EVB capabilities exchange + * + * possible states: EVB_OFFER_CAPABILITIES + * EVB_CONFIGURE + * EVB_CONFIRMATION + */ +static void evb_statemachine(struct evb_data *ed, struct tlv_info_evb *tie) +{ + switch(ed->state) { + case EVB_OFFER_CAPABILITIES: + /* waiting for valid packets to pour in + * if valid packet was received, + * - check parameters with what we have offered for this if, + * - fill structure with data, + * - enable local tx + * - switch to EVB_CONFIGURE + */ + LLDPAD_DBG("%s: state -> EVB_OFFER_CAPABILITIES\n", __func__); + if (evb_check_and_fill(ed, tie) != TLV_OK) { + LLDPAD_ERR("Invalid contents of EVB Cfg TLV !\n"); + return; + } + somethingChangedLocal(ed->ifname); /* trigger tx with new values */ + ed->state = EVB_CONFIGURE; + break; + case EVB_CONFIGURE: + /* we received a valid packet, if contents is same with our local settings + * we can switch state to EVB_CONFIRMATION.*/ + LLDPAD_DBG("%s: state -> EVB_CONFIGURE\n", __func__); + if (evb_compare(ed, tie)) { + ed->state= EVB_OFFER_CAPABILITIES; + } else { + LLDPAD_ERR("tlv_info_evb now equal !\n"); + ed->state = EVB_CONFIRMATION; + } + somethingChangedLocal(ed->ifname); + break; + case EVB_CONFIRMATION: + /* we are already in confirmation and received a new packet with + * different parameters ? Check parameters. switch state back to + * EVB_CONFIGURE ? */ + LLDPAD_DBG("%s: state -> EVB_CONFIRMATION\n", __func__); + break; + default: + LLDPAD_ERR("EVB statemachine reached invalid state !\n"); + break; + } +} + +/* + * evb_rchange: process RX TLV LLDPDU + * + * TLV not consumed on error + */ +static int evb_rchange(struct port *port, struct unpacked_tlv *tlv) +{ + int i; + struct evb_data *ed; + struct tlv_info_evb *tie = (struct tlv_info_evb *) tlv->info; + u8 oui_subtype[OUI_SUB_SIZE] = LLDP_OUI_SUBTYPE; + + if (!init_cfg()) { + return SUBTYPE_INVALID; + } + + ed = evb_data(port->ifname); + + if (!ed) + return SUBTYPE_INVALID; + + if (tlv->type == TYPE_127) { + /* check for length */ + if (tlv->length < (OUI_SUB_SIZE)) { + return TLV_ERR; + } + + /* check for oui */ + if (memcmp(tlv->info, &oui_subtype, OUI_SUB_SIZE)) { + return SUBTYPE_INVALID; + } + + /* disable rx if tx has been disabled by administrator */ + if (!is_tlv_txenabled(ed->ifname, TLVID_8021Qbg(LLDP_EVB_SUBTYPE))) { + LLDPAD_WARN("%s:%s:EVB Config disabled\n", + __func__, ed->ifname); + return TLV_OK; + } + + LLDPAD_DBG("%s(%i): received tlv:", __func__, __LINE__); + evb_dump_tlv(tlv); + evb_print_tlvinfo(tie); + + /* change state */ + evb_statemachine(ed, tie); + + /* check which values have been taken over */ + LLDPAD_DBG("%s(%i): new tlv:", __func__, __LINE__); + evb_print_tlvinfo(ed->tie); + } + + return TLV_OK; +} + +void evb_ifdown(char *ifname) +{ + struct evb_data *ed; + + LLDPAD_DBG("%s called !\n", __func__); + + ed = evb_data(ifname); + if (!ed) + goto out_err; + + free(ed->policy); + free(ed->tie); + LIST_REMOVE(ed, entry); + evb_free_tlv(ed); + free(ed); + LLDPAD_INFO("%s:port %s removed\n", __func__, ifname); + return; +out_err: + LLDPAD_ERR("%s:port %s remove failed\n", __func__, ifname); + + return; +} + +void evb_ifup(char *ifname) +{ + struct evb_data *ed; + struct evb_user_data *ud; + + ed = evb_data(ifname); + if (ed) { + LLDPAD_DBG("%s:%s exists\n", __func__, ifname); + goto out_err; + } + + /* not found, alloc/init per-port tlv data */ + ed = (struct evb_data *) calloc(1, sizeof(struct evb_data)); + if (!ed) { + LLDPAD_ERR("%s:%s malloc %ld failed\n", + __func__, ifname, sizeof(*ed)); + goto out_err; + } + strncpy(ed->ifname, ifname, IFNAMSIZ); + + if (!init_cfg()) { + LLDPAD_ERR("%s:%s init_cfg failed\n", __func__, ifname); + goto out_free; + } + + if (evb_init_cfg_tlv(ed)) { + LLDPAD_ERR("%s:%s evb_init_cfg_tlv failed\n", __func__, ifname); + goto out_free; + } + + ed->state = EVB_OFFER_CAPABILITIES; + + if (evb_bld_tlv(ed)) { + LLDPAD_ERR("%s:%s evb_bld_tlv failed\n", __func__, ifname); + goto out_free; + } + + ud = find_module_user_data_by_if(ifname, &lldp_head, LLDP_MOD_EVB); + LIST_INSERT_HEAD(&ud->head, ed, entry); + LLDPAD_DBG("%s:port %s added\n", __func__, ifname); + return; + +out_free: + free(ed->tie); + free(ed->policy); + free(ed); + +out_err: + return; +} + +static const struct lldp_mod_ops evb_ops = { + .lldp_mod_register = evb_register, + .lldp_mod_unregister = evb_unregister, + .lldp_mod_gettlv = evb_gettlv, + .lldp_mod_rchange = evb_rchange, + .lldp_mod_ifup = evb_ifup, + .lldp_mod_ifdown = evb_ifdown, + .get_arg_handler = evb_get_arg_handlers, +}; + +struct lldp_module *evb_register(void) +{ + struct lldp_module *mod; + struct evb_user_data *ud; + + mod = malloc(sizeof(*mod)); + if (!mod) { + LLDPAD_ERR("failed to malloc module data\n"); + log_message(MSG_ERR_SERVICE_START_FAILURE, + "%s", "failed to malloc module data"); + goto out_err; + } + ud = malloc(sizeof(struct evb_user_data)); + if (!ud) { + free(mod); + LLDPAD_ERR("failed to malloc module user data\n"); + log_message(MSG_ERR_SERVICE_START_FAILURE, + "%s", "failed to malloc module user data"); + goto out_err; + } + LIST_INIT(&ud->head); + mod->id = LLDP_MOD_EVB; + mod->ops = &evb_ops; + mod->data = ud; + LLDPAD_DBG("%s:done\n", __func__); + return mod; + +out_err: + LLDPAD_ERR("%s:failed\n", __func__); + return NULL; +} + +void evb_unregister(struct lldp_module *mod) +{ + if (mod->data) { + evb_free_data((struct evb_user_data *) mod->data); + free(mod->data); + } + free(mod); + LLDPAD_DBG("%s:done", __func__); +} diff --git a/lldp_evb_clif.c b/lldp_evb_clif.c new file mode 100644 index 0000000..7479d74 --- /dev/null +++ b/lldp_evb_clif.c @@ -0,0 +1,226 @@ +/******************************************************************************* + + implementation of EVB TLVs for LLDP + (c) Copyright IBM Corp. 2010 + + Author(s): Jens Osterkamp <jens at linux.vnet.ibm.com> + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + +*******************************************************************************/ + +#include "includes.h" +#include "common.h" +#include <stdio.h> +#include <syslog.h> +#include <sys/un.h> +#include <sys/stat.h> +#include "lldp_mod.h" +#include "lldptool.h" +#include "lldp.h" +#include "lldp_evb.h" +#include "lldp_evb_clif.h" + +void evb_print_cfg_tlv(u16, char *info); +int evb_print_help(); + +u32 evb_lookup_tlv_name(char *tlvid_str); + +static const struct lldp_mod_ops evb_ops_clif = { + .lldp_mod_register = evb_cli_register, + .lldp_mod_unregister = evb_cli_unregister, + .print_tlv = evb_print_tlv, + .lookup_tlv_name = evb_lookup_tlv_name, + .print_help = evb_print_help, +}; + +struct type_name_info evb_tlv_names[] = { + { (LLDP_MOD_EVB << 8) | LLDP_EVB_SUBTYPE, + "EVB Configuration TLV", + "evbCfg", evb_print_cfg_tlv }, + { INVALID_TLVID, NULL, NULL } +}; + +int evb_print_help() +{ + struct type_name_info *tn = &evb_tlv_names[0]; + + while (tn->type != INVALID_TLVID) { + if (tn->key && strlen(tn->key) && tn->name) { + printf(" %s", tn->key); + if (strlen(tn->key)+3 <= 8) + printf("\t"); + printf("\t: %s\n", tn->name); + } + tn++; + } + + return 0; +} + +struct lldp_module *evb_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_EVB; + mod->ops = &evb_ops_clif; + + return mod; +} + +void evb_cli_unregister(struct lldp_module *mod) +{ + free(mod); +} + +void evb_print_cfg_tlv(u16 len, char *info) +{ + u8 smode; + u8 scap; + u8 cmode; + u8 ccap; + u16 svsi; + u16 cvsi; + u8 rte; + + if (len != 9) { + printf("Bad Cfg TLV: %s\n", info); + return; + } + + if (!hexstr2bin(info, &smode, sizeof(smode))) { + printf("supported forwarding mode: (0x%02hhx)", smode); + + if (smode & LLDP_EVB_CAPABILITY_FORWARD_REFLECTIVE_RELAY) + printf(" reflective relay"); + + if (smode & LLDP_EVB_CAPABILITY_FORWARD_STANDARD) + printf(" standard 802.1Q"); + + printf("\n"); + } else { + printf("Unable to decode smode !\n"); + } + + if (!hexstr2bin(info+2, &scap, sizeof(scap))) { + printf("\tsupported capabilities: (0x%02hhx)", scap); + + if ( scap & LLDP_EVB_CAPABILITY_PROTOCOL_RTE) + printf(" RTE"); + + if ( scap & LLDP_EVB_CAPABILITY_PROTOCOL_ECP) + printf(" ECP"); + + if ( scap & LLDP_EVB_CAPABILITY_PROTOCOL_VDP) + printf(" VDP"); + + printf("\n"); + } else { + printf("Unable to decode scap !\n"); + } + + if (!hexstr2bin(info+4, &cmode, sizeof(cmode))) { + printf("\tconfigured forwarding mode: (0x%02hhx)", cmode); + + if (cmode & LLDP_EVB_CAPABILITY_FORWARD_REFLECTIVE_RELAY) + printf(" reflective relay"); + + if (cmode & LLDP_EVB_CAPABILITY_FORWARD_STANDARD) + printf(" standard 802.1Q"); + + printf("\n"); + } else { + printf("Unable to decode cmode !\n"); + } + + if (!hexstr2bin(info+6, &ccap, sizeof(ccap))) { + printf("\tconfigured capabilities: (0x%02hhx)", ccap); + + if ( ccap & LLDP_EVB_CAPABILITY_PROTOCOL_RTE) + printf(" RTE"); + + if ( ccap & LLDP_EVB_CAPABILITY_PROTOCOL_ECP) + printf(" ECP"); + + if ( ccap & LLDP_EVB_CAPABILITY_PROTOCOL_VDP) + printf(" VDP"); + + printf("\n"); + } else { + printf("Unable to decode ccap !\n"); + } + + if (!hexstr2bin(info+8, (u8 *)&svsi, sizeof(svsi))) { + printf("\tno. of supported VSIs: %04i\n",svsi); + } else { + printf("Unable to decode svsi !\n"); + } + + if (!hexstr2bin(info+12, (u8 *)&cvsi, sizeof(cvsi))) { + printf("\tno. of configured VSIs: %04i\n",cvsi); + } else { + printf("Unable to decode cvsi !\n"); + } + + if (!hexstr2bin(info+16, &rte, sizeof(rte))) { + printf("\tRTE: %i\n",rte); + } else { + printf("Unable to decode cvsi !\n"); + } + + printf("\n"); +} + +/* return 1: if it printed the TLV + * 0: if it did not + */ +int evb_print_tlv(u32 tlvid, u16 len, char *info) +{ + struct type_name_info *tn = &evb_tlv_names[0]; + + while (tn->type != INVALID_TLVID) { + if (tlvid == tn->type) { + printf("%s\n", tn->name); + if (tn->print_info) { + printf("\t"); + tn->print_info(len-4, info); + } + return 1; + } + tn++; + } + + return 0; +} + +u32 evb_lookup_tlv_name(char *tlvid_str) +{ + struct type_name_info *tn = &evb_tlv_names[0]; + + while (tn->type != INVALID_TLVID) { + if (!strcasecmp(tn->key, tlvid_str)) + return tn->type; + tn++; + } + return INVALID_TLVID; +} + diff --git a/lldp_evb_cmds.c b/lldp_evb_cmds.c new file mode 100644 index 0000000..bee0ec8 --- /dev/null +++ b/lldp_evb_cmds.c @@ -0,0 +1,522 @@ +/******************************************************************************* + + implementation of EVB TLVs for LLDP + (c) Copyright IBM Corp. 2010 + + Author(s): Jens Osterkamp <jens at linux.vnet.ibm.com> + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + +*******************************************************************************/ + +#include "includes.h" +#include "common.h" +#include <stdio.h> +#include <syslog.h> +#include <sys/un.h> +#include <sys/stat.h> +#include <arpa/inet.h> +#include <string.h> +#include "lldpad.h" +#include "ctrl_iface.h" +#include "lldp.h" +#include "lldp_evb.h" +#include "lldp_mand_clif.h" +#include "lldp_evb_clif.h" +#include "lldp/ports.h" +#include "libconfig.h" +#include "config.h" +#include "clif_msgs.h" +#include "lldp/states.h" + +static int get_arg_tlvtxenable(struct cmd *, char *, char *, char *); +static int set_arg_tlvtxenable(struct cmd *, char *, char *, char *); + +static int get_arg_fmode(struct cmd *, char *, char *, char *); +static int set_arg_fmode(struct cmd *, char *, char *, char *); + +static int get_arg_rte(struct cmd *, char *, char *, char *); +static int set_arg_rte(struct cmd *, char *, char *, char *); + +static int get_arg_vsis(struct cmd *, char *, char *, char *); +static int set_arg_vsis(struct cmd *, char *, char *, char *); + +static int get_arg_capabilities(struct cmd *, char *, char *, char *); +static int set_arg_capabilities(struct cmd *, char *, char *, char *); + +static struct arg_handlers arg_handlers[] = { + { ARG_EVB_FORWARDING_MODE, get_arg_fmode, set_arg_fmode }, + { ARG_EVB_CAPABILITIES, get_arg_capabilities, set_arg_capabilities }, + { ARG_EVB_VSIS, get_arg_vsis, set_arg_vsis }, + { ARG_EVB_RTE, get_arg_rte, set_arg_rte }, + { ARG_TLVTXENABLE, get_arg_tlvtxenable, set_arg_tlvtxenable }, + { NULL } +}; + +static int get_arg_tlvtxenable(struct cmd *cmd, char *arg, char *argvalue, + char *obuf) +{ + int value; + char *s; + char arg_path[EVB_BUF_SIZE]; + + if (cmd->cmd != cmd_gettlv) + return cmd_invalid; + + switch (cmd->tlvid) { + case (LLDP_MOD_EVB << 8) | LLDP_EVB_SUBTYPE: + snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", + TLVID_PREFIX, cmd->tlvid, arg); + + if (get_cfg(cmd->ifname, arg_path, (void *)&value, + CONFIG_TYPE_BOOL)) + value = false; + break; + case INVALID_TLVID: + return cmd_invalid; + default: + return cmd_not_applicable; + } + + if (value) + s = VAL_YES; + else + s = VAL_NO; + + sprintf(obuf, "%02x%s%04x%s", (unsigned int) strlen(arg), arg, + (unsigned int) strlen(s), s); + + return cmd_success; +} + +static int set_arg_tlvtxenable(struct cmd *cmd, char *arg, char *argvalue, + char *obuf) +{ + int value; + char arg_path[EVB_BUF_SIZE]; + + if (cmd->cmd != cmd_settlv) + return cmd_invalid; + + switch (cmd->tlvid) { + case (LLDP_MOD_EVB << 8) | LLDP_EVB_SUBTYPE: + break; + case INVALID_TLVID: + return cmd_invalid; + default: + return cmd_not_applicable; + } + + if (!strcasecmp(argvalue, VAL_YES)) + value = 1; + else if (!strcasecmp(argvalue, VAL_NO)) + value = 0; + else + return cmd_invalid; + + snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", + TLVID_PREFIX, cmd->tlvid, arg); + + if (set_cfg(cmd->ifname, arg_path, (void *)&value, CONFIG_TYPE_BOOL)) + return cmd_failed; + + somethingChangedLocal(cmd->ifname); + + return cmd_success; +} + +static int get_arg_fmode(struct cmd *cmd, char *arg, char *argvalue, + char *obuf) +{ + char *s; + struct evb_data *ed; + + if (cmd->cmd != cmd_gettlv) + return cmd_invalid; + + switch (cmd->tlvid) { + case (LLDP_MOD_EVB << 8) | LLDP_EVB_SUBTYPE: + break; + case INVALID_TLVID: + return cmd_invalid; + default: + return cmd_not_applicable; + } + + ed = evb_data((char *) &cmd->ifname); + + if (!ed) + return cmd_invalid; + + if (ed->policy->smode & LLDP_EVB_CAPABILITY_FORWARD_REFLECTIVE_RELAY) + s = VAL_EVB_FMODE_REFLECTIVE_RELAY; + else + s = VAL_EVB_FMODE_BRIDGE; + + sprintf(obuf, "%02x%s%04x%s", (unsigned int) strlen(arg), arg, + (unsigned int) strlen(s), s); + + return cmd_success; +} + +static int set_arg_fmode(struct cmd *cmd, char *arg, char *argvalue, + char *obuf) +{ + u8 smode; + char arg_path[EVB_BUF_SIZE]; + struct evb_data *ed; + + if (cmd->cmd != cmd_settlv) + return cmd_invalid; + + switch (cmd->tlvid) { + case (LLDP_MOD_EVB << 8) | LLDP_EVB_SUBTYPE: + break; + case INVALID_TLVID: + return cmd_invalid; + default: + return cmd_not_applicable; + } + + ed = evb_data((char *) &cmd->ifname); + + if (!ed) + return cmd_invalid; + + smode = 0; + + if (!strcasecmp(argvalue, VAL_EVB_FMODE_BRIDGE)) { + smode = LLDP_EVB_CAPABILITY_FORWARD_STANDARD; + } + + if (!strcasecmp(argvalue, VAL_EVB_FMODE_REFLECTIVE_RELAY)) { + smode = LLDP_EVB_CAPABILITY_FORWARD_REFLECTIVE_RELAY; + } + + if (smode == 0) { + return cmd_invalid; + } else { + ed->policy->smode = smode; + } + + snprintf(arg_path, sizeof(arg_path), "%s%08x.fmode", + TLVID_PREFIX, cmd->tlvid); + + if (set_cfg(ed->ifname, arg_path, (void *) &argvalue, CONFIG_TYPE_STRING)) { + printf("%s:%s: saving EVB forwarding mode failed.\n", + __func__, ed->ifname); + return cmd_invalid; + } + + somethingChangedLocal(cmd->ifname); + + return cmd_success; +} + +static int get_arg_capabilities(struct cmd *cmd, char *arg, char *argvalue, + char *obuf) +{ + int c; + char *s, *t; + struct evb_data *ed; + + printf("%s(%i): arg %s, argvalue %s !\n", __func__, __LINE__, arg, argvalue); + + s = t = malloc(EVB_BUF_SIZE); + + if (!s) + goto out_free; + + memset(s, 0, EVB_BUF_SIZE); + + if (cmd->cmd != cmd_gettlv) + goto out_free; + + switch (cmd->tlvid) { + case (LLDP_MOD_EVB << 8) | LLDP_EVB_SUBTYPE: + break; + case INVALID_TLVID: + goto out_free; + default: + free(t); + return cmd_not_applicable; + } + + ed = evb_data((char *) &cmd->ifname); + if (!ed) + goto out_free; + + if (ed->policy->scap & LLDP_EVB_CAPABILITY_PROTOCOL_RTE) { + c = sprintf(s, VAL_EVB_CAPA_RTE " "); + if (c <= 0) + goto out_free; + s += c; + } + + if (ed->policy->scap & LLDP_EVB_CAPABILITY_PROTOCOL_ECP) { + c = sprintf(s, VAL_EVB_CAPA_ECP " "); + if (c <= 0) + goto out_free; + s += c; + } + + if (ed->policy->scap & LLDP_EVB_CAPABILITY_PROTOCOL_VDP) { + c = sprintf(s, VAL_EVB_CAPA_VDP " "); + if (c <= 0) + goto out_free; + s += c; + } + + sprintf(obuf, "%02x%s%04x%s", (unsigned int) strlen(arg), arg, + (unsigned int) strlen(t), t); + + free(t); + return cmd_success; + +out_free: + free(t); + return cmd_invalid; +} + +static int set_arg_capabilities(struct cmd *cmd, char *arg, char *argvalue, + char *obuf) +{ + u8 scap = 0; + char arg_path[EVB_BUF_SIZE]; + struct evb_data *ed; + + if (cmd->cmd != cmd_settlv) + return cmd_invalid; + + switch (cmd->tlvid) { + case (LLDP_MOD_EVB << 8) | LLDP_EVB_SUBTYPE: + break; + case INVALID_TLVID: + return cmd_invalid; + default: + return cmd_not_applicable; + } + + ed = evb_data((char *) &cmd->ifname); + + if (!ed) + return cmd_invalid; + + if (strcasestr(argvalue, VAL_EVB_CAPA_RTE)) + scap |= LLDP_EVB_CAPABILITY_PROTOCOL_RTE; + + if (strcasestr(argvalue, VAL_EVB_CAPA_ECP)) + scap |= LLDP_EVB_CAPABILITY_PROTOCOL_ECP; + + if (strcasestr(argvalue, VAL_EVB_CAPA_VDP)) + scap |= LLDP_EVB_CAPABILITY_PROTOCOL_VDP; + + ed->policy->scap = scap; + + snprintf(arg_path, sizeof(arg_path), "%s%08x.capabilities", + TLVID_PREFIX, cmd->tlvid); + + if (set_cfg(ed->ifname, arg_path, (void *) &argvalue, CONFIG_TYPE_STRING)) { + printf("%s:%s: saving EVB capabilities failed.\n", + __func__, ed->ifname); + return cmd_invalid; + } + + somethingChangedLocal(cmd->ifname); + + return cmd_success; +} + +static int get_arg_rte(struct cmd *cmd, char *arg, char *argvalue, + char *obuf) +{ + char s[EVB_BUF_SIZE]; + struct evb_data *ed; + + if (cmd->cmd != cmd_gettlv) + return cmd_invalid; + + switch (cmd->tlvid) { + case (LLDP_MOD_EVB << 8) | LLDP_EVB_SUBTYPE: + break; + case INVALID_TLVID: + return cmd_invalid; + default: + return cmd_not_applicable; + } + + ed = evb_data((char *) &cmd->ifname); + if (!ed) + return cmd_invalid; + + if (sprintf(s, "%i", ed->policy->rte) <= 0) + return cmd_invalid; + + sprintf(obuf, "%02x%s%04x%s", (unsigned int) strlen(arg), arg, + (unsigned int) strlen(s), s); + + return cmd_success; +} + +static int set_arg_rte(struct cmd *cmd, char *arg, char *argvalue, + char *obuf) +{ + int value, err; + char arg_path[EVB_BUF_SIZE]; + char svalue[10]; + struct evb_data *ed = NULL; + + if (cmd->cmd != cmd_settlv) + goto out_err; + + switch (cmd->tlvid) { + case (LLDP_MOD_EVB << 8) | LLDP_EVB_SUBTYPE: + break; + case INVALID_TLVID: + goto out_err; + default: + return cmd_not_applicable; + } + + ed = evb_data((char *) &cmd->ifname); + + if (!ed) + return cmd_invalid; + + value = atoi(argvalue); + + if ((value < 0)) + goto out_err; + + ed->policy->rte = value; + + err = snprintf(arg_path, sizeof(arg_path), "%s%08x.rte", + TLVID_PREFIX, cmd->tlvid); + + if (err < 0) + goto out_err; + + err = snprintf(svalue, sizeof(svalue), "%i", value); + + if (err < 0) + goto out_err; + + if (set_cfg(ed->ifname, arg_path, (void *) svalue, CONFIG_TYPE_STRING)) + goto out_err; + + somethingChangedLocal(cmd->ifname); + + return cmd_success; + +out_err: + printf("%s:%s: saving EVB rte failed.\n", __func__, ed->ifname); + return cmd_invalid; +} + +static int get_arg_vsis(struct cmd *cmd, char *arg, char *argvalue, + char *obuf) +{ + char s[EVB_BUF_SIZE]; + struct evb_data *ed; + + if (cmd->cmd != cmd_gettlv) + return cmd_invalid; + + switch (cmd->tlvid) { + case (LLDP_MOD_EVB << 8) | LLDP_EVB_SUBTYPE: + break; + case INVALID_TLVID: + return cmd_invalid; + default: + return cmd_not_applicable; + } + + ed = evb_data((char *) &cmd->ifname); + if (!ed) + return cmd_invalid; + + if (sprintf(s, "%04i", ed->policy->svsi) <= 0) + return cmd_invalid; + + sprintf(obuf, "%02x%s%04x%s", (unsigned int) strlen(arg), arg, + (unsigned int) strlen(s), s); + + return cmd_success; +} + +static int set_arg_vsis(struct cmd *cmd, char *arg, char *argvalue, + char *obuf) +{ + int value, err; + char arg_path[EVB_BUF_SIZE]; + char svalue[10]; + char *sv; + struct evb_data *ed = NULL; + + ed = evb_data((char *) &cmd->ifname); + + if (!ed) + return cmd_invalid; + + if (cmd->cmd != cmd_settlv) + goto out_err; + + switch (cmd->tlvid) { + case (LLDP_MOD_EVB << 8) | LLDP_EVB_SUBTYPE: + break; + case INVALID_TLVID: + goto out_err; + default: + return cmd_not_applicable; + } + + value = atoi(argvalue); + + if ((value < 0) || (value > LLDP_EVB_DEFAULT_MAX_VSI)) + goto out_err; + + ed->policy->svsi = value; + + err = snprintf(arg_path, sizeof(arg_path), "%s%08x.vsis", + TLVID_PREFIX, cmd->tlvid); + + if (err < 0) + goto out_err; + + err = snprintf(svalue, sizeof(svalue), "%i", value); + + if (err < 0) + goto out_err; + + sv = &svalue[0]; + + if (set_cfg(ed->ifname, arg_path, (void *) &sv, CONFIG_TYPE_STRING)) + goto out_err; + + somethingChangedLocal(cmd->ifname); + + return cmd_success; + +out_err: + printf("%s:%s: saving EVB vsis failed.\n", __func__, ed->ifname); + return cmd_invalid; +} + +struct arg_handlers *evb_get_arg_handlers() +{ + return &arg_handlers[0]; +} diff --git a/lldpad.c b/lldpad.c index a89b5a4..571da31 100644 --- a/lldpad.c +++ b/lldpad.c @@ -49,6 +49,7 @@ #include "lldp_dcbx.h" #include "lldp_med.h" #include "lldp_8023.h" +#include "lldp_evb.h" #include "config.h" #include "lldpad_shm.h" #include "clif.h" @@ -63,6 +64,7 @@ struct lldp_module *(*register_tlv_table[])(void) = { dcbx_register, med_register, ieee8023_register, + evb_register, NULL, }; diff --git a/lldptool.c b/lldptool.c index 5cf2846..7e166fe 100644 --- a/lldptool.c +++ b/lldptool.c @@ -39,6 +39,7 @@ #include "lldp_med_clif.h" #include "lldp_8023_clif.h" #include "lldp_dcbx_clif.h" +#include "lldp_evb_clif.h" #include "lldptool.h" #include "lldptool_cli.h" #include "lldp_mod.h" @@ -156,6 +157,7 @@ struct lldp_module *(*register_tlv_table[])(void) = { ieee8023_cli_register, med_cli_register, dcbx_cli_register, + evb_cli_register, NULL, }; -- 1.7.1
added an additional check for existence of the ifup operation. If it does not exist, just skip it. Signed-off-by: Jens Osterkamp <jens at linux.vnet.ibm.com> --- config.c | 6 ++++-- 1 files changed, 4 insertions(+), 2 deletions(-) diff --git a/config.c b/config.c index 5cfac83..26afe3b 100644 --- a/config.c +++ b/config.c @@ -370,8 +370,10 @@ void init_ports(void) } else if (check_link_status(p->if_name)) { add_adapter(p->if_name); - LIST_FOREACH(np, &lldp_head, lldp) - np->ops->lldp_mod_ifup(p->if_name); + LIST_FOREACH(np, &lldp_head, lldp) { + if (np->ops->lldp_mod_ifup) + np->ops->lldp_mod_ifup(p->if_name); + } set_lldp_port_enable_state(p->if_name, 1); } p++; -- 1.7.1
Jens Osterkamp
2010-Nov-08 16:11 UTC
[PATCH 4/9] implementation of IEEE 802.1Qbg in lldpad, part 2
This is the implementation of the edge control protocol (ECP) and VSI discovery protocol (VDP) currently being specified in IEEE 802.1Qbg. This implementation extends the infrastructure defined lldpad to send and receive ECP frames with a new (yet to be defined) ethertype. Received frames are validated and analyzed before the content is handed to the upper layer protocol (ULP, VDP in this case) for further processing. Frames to be transmitted are compiled from VSI (guest interface) profiles registered on a interface. Reception and transmission of ECP frames is controlled by RX and TX state machines, timeouts are handled timeout functions. The patch still contains a lot of debug code to allow low-level protocol analysis. VDP serves as the upper layer protocol (ULP) for TLVs communicated via the ECP protocol. For this it registers as a new module in lldpad. The VDP module supports a station and a bridge role. As a station, new VSI (virtual station interface) profiles can be registered to the VDP module using lldptool or libvirt. These profiles are then announced to an adjacent switch. Transmitted profiles are processed to the desired state by the VDP station state machine. As a bridge, the VDP module waits for new profiles received in TLVs by ECP. The received profiles are processed to the desired state by a VDP bridge state machine. VDP module parameters are stored in the "vdp" section under the appropriate interface. The patch still contains a lot of debug code to allow analysis of VDP protocol behavior. Signed-off-by: Jens Osterkamp <jens at linux.vnet.ibm.com> --- Makefile.am | 10 +- ecp/ecp.c | 102 +++++ ecp/ecp.h | 101 +++++ ecp/ecp_rx.c | 613 +++++++++++++++++++++++++++ ecp/ecp_tx.c | 506 ++++++++++++++++++++++ include/lldp.h | 1 + include/lldp_evb.h | 6 + include/lldp_vdp.h | 159 +++++++ lldp/l2_packet.h | 2 + lldp/ports.h | 11 +- lldp_vdp.c | 1196 ++++++++++++++++++++++++++++++++++++++++++++++++++++ lldpad.c | 2 + 12 files changed, 2702 insertions(+), 7 deletions(-) create mode 100644 ecp/ecp.c create mode 100644 ecp/ecp.h create mode 100644 ecp/ecp_rx.c create mode 100644 ecp/ecp_tx.c create mode 100644 include/lldp_vdp.h create mode 100644 lldp_vdp.c diff --git a/Makefile.am b/Makefile.am index d59a6fa..4b69389 100644 --- a/Makefile.am +++ b/Makefile.am @@ -56,6 +56,8 @@ $(lldpad_include_HEADERS) $(noinst_HEADERS) \ lldp/ports.c lldp/agent.c lldp/l2_packet_linux.c lldp/tx.c \ lldp/rx.c lldp/agent.h lldp/l2_packet.h lldp/mibdata.h lldp/ports.h \ lldp/states.h \ +ecp/ecp.c ecp/ecp_tx.c \ +ecp/ecp_rx.c \ include/lldp.h include/lldp_mod.h \ lldp_dcbx.c include/lldp_dcbx.h tlv_dcbx.c include/tlv_dcbx.h \ lldp_dcbx_cfg.c include/lldp_dcbx_cfg.h \ @@ -67,16 +69,16 @@ lldp_tlv.c include/lldp_tlv.h \ lldp_basman.c include/lldp_basman.h \ lldp_med.c include/lldp_med.h \ lldp_8023.c include/lldp_8023.h \ -lldp_evb.c include/lldp_evb.h - - +lldp_evb.c include/lldp_evb.h \ +lldp_vdp.c include/lldp_vdp.h dcbtool_SOURCES = dcbtool.c clif.c dcbtool_cmds.c parse_cli.l \ $(lldpad_include_HEADERS) $(noinst_HEADERS) lldptool_SOURCES = lldptool.c clif.c lldptool_cmds.c common.c os_unix.c \ lldp_mand_clif.c lldp_basman_clif.c lldp_med_clif.c lldp_8023_clif.c \ -lldp_dcbx_clif.c lldp_evb_clif.c $(lldpad_include_HEADERS) $(noinst_HEADERS) +lldp_dcbx_clif.c lldp_evb_clif.c $(lldpad_include_HEADERS) \ +$(noinst_HEADERS) nltest_SOURCES = nltest.c nltest.h diff --git a/ecp/ecp.c b/ecp/ecp.c new file mode 100644 index 0000000..44c3ba0 --- /dev/null +++ b/ecp/ecp.c @@ -0,0 +1,102 @@ +/******************************************************************************* + + implementation of ECP according to 802.1Qbg + (c) Copyright IBM Corp. 2010 + + Author(s): Jens Osterkamp <jens at linux.vnet.ibm.com> + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + +*******************************************************************************/ + +#include <net/if.h> +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/utsname.h> +#include <linux/if_bridge.h> +#include "lldp.h" +#include "lldp_evb.h" +#include "lldp_vdp.h" +#include "messages.h" +#include "config.h" +#include "common.h" +#include "lldp/l2_packet.h" +#include "ecp/ecp.h" + +/* ecp_init - initialize ecp module + * @ifname: interface for which the module is initialized + * + * returns 0 on success, -1 on error + * + * finds the port to the interface name, sets up the receive handle for + * incoming ecp frames and initializes the ecp rx and tx state machines. + * should usually be called when a successful exchange of EVB TLVs has been + * made and ECP and VDP protocols are supported by both sides. + */ +int ecp_init(char *ifname) +{ + struct vdp_data *vd; + + LLDPAD_DBG("%s(%i): starting ECP for if %s !\n", __func__, __LINE__, ifname); + + vd = vdp_data(ifname); + + if (!vd) { + LLDPAD_ERR("%s(%i): unable to find vd %s ! \n", __func__, __LINE__, ifname); + goto fail; + } + + vd->ecp.l2 = l2_packet_init(vd->ifname, NULL, ETH_P_ECP, + ecp_rx_ReceiveFrame, vd, 1); + if (!vd->ecp.l2) { + LLDPAD_ERR("ERROR: Failed to open register layer 2 access to " + "ETH_P_ECP\n"); + goto fail; + } + + ecp_tx_run_sm(vd); + ecp_rx_run_sm(vd); + + return 0; + +fail: + return -1; +} + +int ecp_deinit(char *ifname) +{ + struct vdp_data *vd; + + LLDPAD_DBG("%s(%i): stopping ECP for if %s !\n", __func__, __LINE__, ifname); + + vd = vdp_data(ifname); + + if (!vd) { + LLDPAD_ERR("%s(%i): unable to find vd %s ! \n", __func__, __LINE__, ifname); + goto fail; + } + + ecp_tx_stop_ackTimer(vd); + + l2_packet_deinit(vd->ecp.l2); + + return 0; + +fail: + return -1; +} diff --git a/ecp/ecp.h b/ecp/ecp.h new file mode 100644 index 0000000..cc9ca2b --- /dev/null +++ b/ecp/ecp.h @@ -0,0 +1,101 @@ +/******************************************************************************* + + implementation of ECP according to 802.1Qbg + (c) Copyright IBM Corp. 2010 + + Author(s): Jens Osterkamp <jens at linux.vnet.ibm.com> + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + +*******************************************************************************/ + +#ifndef _ECP_H +#define _ECP_H + +#include "lldp_mod.h" +#include "include/lldp_vdp.h" + +#define ECP_SUBTYPE 0x0 + +#define ECP_MAX_RETRIES 3 +#define ECP_SEQUENCE_NR_START 0x0 + +#define ECP_TRANSMISSION_TIMER EVB_RTM(RTE)*EVB_RTG +#define ECP_TRANSMISSION_DIVIDER 10000 + +typedef enum { + ECP_REQUEST = 0, + ECP_ACK +} ecp_mode; + +struct ecp { + struct l2_packet_data *l2; + int sequence; + int retries; + int ackReceived; + int ackTimerExpired; + u16 lastSequence; + u16 seqECPDU; + struct portrx rx; + struct porttx tx; + struct portstats stats; +}; + +struct ecp_hdr { + u8 oui[3]; + u8 pad1; + u16 subtype; + u8 mode; + u16 seqnr; +} __attribute__ ((__packed__)); + +enum { + ECP_TX_INIT_TRANSMIT, + ECP_TX_TRANSMIT_ECPDU, + ECP_TX_WAIT_FOR_ACK, + ECP_TX_REQUEST_PDU +}; + +static const char *ecp_tx_states[] = { + "ECP_TX_IDLE", + "ECP_TX_INIT_TRANSMIT", + "ECP_TX_TRANSMIT_ECPDU", + "ECP_TX_WAIT_FOR_ACK", + "ECP_TX_REQUEST_PDU" +}; + +enum { + ECP_RX_IDLE, + ECP_RX_INIT_RECEIVE, + ECP_RX_RECEIVE_WAIT, + ECP_RX_RECEIVE_ECPDU, + ECP_RX_SEND_ACK, + ECP_RX_RESEND_ACK, +}; + +static const char *ecp_rx_states[] = { + "ECP_RX_IDLE", + "ECP_RX_INIT_RECEIVE", + "ECP_RX_RECEIVE_WAIT", + "ECP_RX_RECEIVE_ECPDU", + "ECP_RX_SEND_ACK", + "ECP_RX_RESEND_ACK", +}; + +void ecp_rx_ReceiveFrame(void *, unsigned int, const u8 *, size_t ); + +#endif /* _ECP_H */ diff --git a/ecp/ecp_rx.c b/ecp/ecp_rx.c new file mode 100644 index 0000000..22153e2 --- /dev/null +++ b/ecp/ecp_rx.c @@ -0,0 +1,613 @@ +/******************************************************************************* + + implementation of ECP according to 802.1Qbg + (c) Copyright IBM Corp. 2010 + + Author(s): Jens Osterkamp <jens at linux.vnet.ibm.com> + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + +*******************************************************************************/ + +#include "dcb_osdep.h" +#include "lldp/ports.h" +#include "lldp/l2_packet.h" +#include "messages.h" +#include "lldp.h" +#include "lldpad.h" +#include "lldp_mod.h" +#include "clif_msgs.h" +#include "lldp_mand.h" +#include "lldp_vdp.h" + +/* ecp_rx_Initialize - initializes the ecp rx state machine + * @vd: vd for the state machine + * + * no return value + * + * initialize some variables, get rid of old frame if necessary + */ +void ecp_rx_Initialize(struct vdp_data *vd) +{ + vd->ecp.rx.rcvFrame = false; + vd->ecp.rx.badFrame = false; + + vd->ecp.ackReceived = 0; + + if (vd->ecp.rx.framein) { + free(vd->ecp.rx.framein); + vd->ecp.rx.framein = NULL; + } + vd->ecp.rx.sizein = 0; + + return; +} + +/* ecp_rx_freeFrame - free up received frame + * @vd: vd for the state machine + * + * no return value + * + * frees up an old received frame, set pointer to NULL and size to 0. + */ +void ecp_rx_freeFrame(struct vdp_data *vd) +{ + free(vd->ecp.rx.framein); + vd->ecp.rx.framein = NULL; + vd->ecp.rx.sizein = 0; +} + +/* ecp_print_framein - print raw received frame + * @vd: vd for the state machine + * + * no return value + * + * prints out a raw version of a received frame. useful for low-level protocol + * debugging. + */ +void ecp_print_framein(struct vdp_data *vd) +{ + int i; + char *s, *t; + + s = t = malloc(256); + + if (!s) { + LLDPAD_ERR("%s(%i): unable to allocate string !\n", __func__, __LINE__); + } + + for (i=0; i < vd->ecp.rx.sizein; i++) { + int c; + c = sprintf(s, "%02x ", vd->ecp.rx.framein[i]); + s += c; + if (!((i+1) % 16)) { + LLDPAD_DBG("%s\n", t); + s = t; + } + } + + LLDPAD_DBG("%s\n", t); + + free(t); +} + +/* ecp_rx_SendAckFrame - send ack frame + * @vd: port used by ecp + * + * currently always returns 0 + * + * copies current received frame over to frame out, fills in address of this + * port and set mode field to ACK. used by ecp_rx_send_ack_frame. + */ +int ecp_rx_SendAckFrame(struct vdp_data *vd) +{ + u16 tlv_offset = 0; + struct ecp_hdr *ecp_hdr; + struct l2_ethhdr *hdr; + u8 own_addr[ETH_ALEN]; + + LLDPAD_DBG("%s(%i)-%s: acking frame \n", __func__, __LINE__, vd->ifname); + + assert(vd->ecp.rx.framein && vd->ecp.rx.sizein); + + /* copy over to frameout */ + vd->ecp.tx.frameout = (u8 *)malloc(ETH_FRAME_LEN); + memcpy(vd->ecp.tx.frameout, vd->ecp.rx.framein, vd->ecp.rx.sizein); + vd->ecp.tx.sizeout = vd->ecp.rx.sizein; + + /* use my own addr to send ACK */ + hdr = (struct l2_ethhdr *)vd->ecp.tx.frameout; + l2_packet_get_own_src_addr(vd->ecp.l2,(u8 *)&own_addr); + memcpy(hdr->h_source, &own_addr, ETH_ALEN); + + tlv_offset = sizeof(struct l2_ethhdr); + ecp_hdr = (struct ecp_hdr *)&vd->ecp.tx.frameout[tlv_offset]; + ecp_hdr->mode = ECP_ACK; + + return 0; +} + +/* ecp_rx_send_ack_frame - send out ack frame for received frame + * @vd: vd for the state machine + * + * no return value + * + * creates an ack frame for a just received frame, prints the about to be + * sent frame and finally transmits it. + */ +void ecp_rx_send_ack_frame(struct vdp_data *vd) +{ + ecp_rx_SendAckFrame(vd); + + ecp_print_frameout(vd); + + ecp_txFrame(vd); + + return; +} + +/* ecp_rx_ReceiveFrame - receive ecp frame + * @ctx: rx callback context, struct vd * in this case + * @ifindex: index of interface + * @buf: buffer which contains the frame just received + * @len: size of buffer (frame) + * + * no return value + * + * creates a local copy of the buffer and checks the header. keeps some + * statistics about ecp frames. Checks if it is a request or an ack frame and branches + * to ecp rx or ecp tx state machine. + */ +void ecp_rx_ReceiveFrame(void *ctx, unsigned int ifindex, const u8 *buf, size_t len) +{ + + struct vdp_data *vd = NULL; + struct port *port; + u8 frame_error = 0; + u16 tlv_offset; + struct l2_ethhdr *hdr; + struct l2_ethhdr example_hdr,*ex; + struct ecp_hdr *ecp_hdr; + + if (ctx) + vd = (struct vdp_data *)ctx; + + port = port_find_by_name(vd->ifname); + + LLDPAD_DBG("%s(%i)-%s: received packet with size %i\n", __func__, __LINE__, + vd->ifname, (int) len); + + if (port->adminStatus == disabled || port->adminStatus == enabledTxOnly) + return; + + if (vd->ecp.rx.framein && + vd->ecp.rx.sizein == len && + (memcmp(buf, vd->ecp.rx.framein, len) == 0)) { + vd->ecp.stats.statsFramesInTotal++; + return; + } + + if (vd->ecp.rx.framein) + free(vd->ecp.rx.framein); + + vd->ecp.rx.framein = (u8 *)malloc(len); + if (vd->ecp.rx.framein == NULL) { + LLDPAD_ERR("ERROR - could not allocate memory for rx'ed frame\n"); + return; + } + memset(vd->ecp.rx.framein, 0, len); + memcpy(vd->ecp.rx.framein, buf, len); + + vd->ecp.rx.sizein = (u16)len; + ex = &example_hdr; + memcpy(ex->h_dest, multi_cast_source, ETH_ALEN); + ex->h_proto = htons(ETH_P_ECP); + hdr = (struct l2_ethhdr *)vd->ecp.rx.framein; + + if ((memcmp(hdr->h_dest,ex->h_dest, ETH_ALEN) != 0)) { + LLDPAD_ERR("ERROR multicast address error in incoming frame. " + "Dropping frame.\n"); + frame_error++; + free(vd->ecp.rx.framein); + vd->ecp.rx.framein = NULL; + vd->ecp.rx.sizein = 0; + return; + } + + if (hdr->h_proto != example_hdr.h_proto) { + LLDPAD_ERR("ERROR Ethertype not ECP ethertype but ethertype " + "'%x' in incoming frame.\n", htons(hdr->h_proto)); + frame_error++; + free(vd->ecp.rx.framein); + vd->ecp.rx.framein = NULL; + vd->ecp.rx.sizein = 0; + return; + } + + if (!frame_error) { + vd->ecp.stats.statsFramesInTotal++; + vd->ecp.rx.rcvFrame = 1; + } + + tlv_offset = sizeof(struct l2_ethhdr); + + ecp_hdr = (struct ecp_hdr *)&vd->ecp.rx.framein[tlv_offset]; + + vd->ecp.seqECPDU = ntohs(ecp_hdr->seqnr); + + switch(ecp_hdr->mode) { + case ECP_REQUEST: + vd->ecp.ackReceived = false; + ecp_print_framein(vd); + ecp_rx_run_sm(vd); + break; + case ECP_ACK: + vd->ecp.ackReceived = true; + LLDPAD_DBG("%s(%i)-%s: received ack frame \n", __func__, __LINE__, vd->ifname); + ecp_print_framein(vd); + ecp_tx_run_sm(vd); + vd->ecp.ackReceived = false; + break; + default: + LLDPAD_ERR("ERROR: unknown mode %i!\n", ecp_hdr->mode); + return; + } + + ecp_rx_freeFrame(vd); +} + +/* ecp_rx_validateFrame - validates received frame + * @vd: vdp_data used by ecp + * + * no return value + * + * checks wether received frame has correct subtype and mode + */ + +void ecp_rx_validateFrame(struct vdp_data *vd) +{ + u16 tlv_offset = 0; + struct ecp_hdr *ecp_hdr; + + LLDPAD_DBG("%s(%i)-%s: validating frame \n", __func__, __LINE__, vd->ifname); + + assert(vd->ecp.rx.framein && vd->ecp.rx.sizein); + + tlv_offset = sizeof(struct l2_ethhdr); + + ecp_hdr = (struct ecp_hdr *)&vd->ecp.rx.framein[tlv_offset]; + + LLDPAD_DBG("%s(%i)-%s: ecp packet with subtype %04x, mode %02x, sequence nr %04x\n", + __func__, __LINE__, vd->ifname, ecp_hdr->subtype, ecp_hdr->mode, ntohs(ecp_hdr->seqnr)); + + if (ecp_hdr->subtype != ECP_SUBTYPE) { + LLDPAD_ERR("ERROR: unknown subtype !\n"); + return; + } + + if ((ecp_hdr->oui[0] != 0x0) || (ecp_hdr->oui[1] != 0x1b) || + (ecp_hdr->oui[2] != 0x3f)) { + LLDPAD_ERR("ERROR: incorrect OUI 0x%02x%02x%02x !\n", + ecp_hdr->oui[0], ecp_hdr->oui[1], ecp_hdr->oui[2]); + return; + } + + switch(ecp_hdr->mode) { + case ECP_REQUEST: + break; + case ECP_ACK: + break; + default: + LLDPAD_ERR("ERROR: unknown mode %i!\n", ecp_hdr->mode); + return; + } + + /* FIXME: also done in ecp_rx_ReceiveFrame, + * are both necessary ? */ + vd->ecp.seqECPDU = ntohs(ecp_hdr->seqnr); +} + +/* ecp_rx_validate_frame - wrapper around ecp_rx_validateFrame + * @vd: currently used port + * + * no return value + * + * sets rcvFrame to false and validates frame. used in ECP_RX_RECEIVE_ECPDU + * state of ecp_rx_run_sm + */ +void ecp_rx_validate_frame(struct vdp_data *vd) +{ + vd->ecp.rx.rcvFrame = false; + ecp_rx_validateFrame(vd); + return; +} + +/* ecp_rx_ProcessFrame - process received ecp frames + * @vd: currently used port + * + * no return value + * + * walks through the packed vsi tlvs in an ecp frame, extracts them + * and passes them to the VDP ULP with vdp_indicate. + */ +void ecp_rx_ProcessFrame(struct vdp_data *vd) +{ + u16 tlv_cnt = 0; + u8 tlv_type = 0; + u16 tlv_length = 0; + u16 tlv_offset = 0; + u16 *tlv_head_ptr = NULL; + u8 frame_error = 0; + bool tlv_stored = false; + struct ecp_hdr *ecp_hdr; + + LLDPAD_DBG("%s(%i)-%s: processing frame \n", __func__, __LINE__, vd->ifname); + + assert(vd->ecp.rx.framein && vd->ecp.rx.sizein); + + tlv_offset = sizeof(struct l2_ethhdr); + + ecp_hdr = (struct ecp_hdr *)&vd->ecp.rx.framein[tlv_offset]; + + LLDPAD_DBG("%s(%i)-%s: ecp packet with subtype %04x, mode %02x, sequence nr %04x\n", + __func__, __LINE__, vd->ifname, ecp_hdr->subtype, ecp_hdr->mode, ntohs(ecp_hdr->seqnr)); + + if (ecp_hdr->mode == ECP_ACK) + return; + + /* processing of VSI_TLVs starts here */ + + tlv_offset += sizeof(struct ecp_hdr); + + do { + tlv_cnt++; + + if (tlv_offset > vd->ecp.rx.sizein) { + LLDPAD_ERR("%s(%i)-%s: ERROR: Frame overrun! tlv_offset %i, sizein %i cnt %i\n", + __func__, __LINE__, vd->ifname, tlv_offset, vd->ecp.rx.sizein, tlv_cnt); + frame_error++; + goto out; + } + + tlv_head_ptr = (u16 *)&vd->ecp.rx.framein[tlv_offset]; + tlv_length = htons(*tlv_head_ptr) & 0x01FF; + tlv_type = (u8)(htons(*tlv_head_ptr) >> 9); + + u16 tmp_offset = tlv_offset + tlv_length; + if (tmp_offset > vd->ecp.rx.sizein) { + LLDPAD_ERR("ERROR: Frame overflow error: offset=%d, " + "rx.size=%d \n", tmp_offset, vd->ecp.rx.sizein); + frame_error++; + goto out; + } + + u8 *info = (u8 *)&vd->ecp.rx.framein[tlv_offset + + sizeof(*tlv_head_ptr)]; + + struct unpacked_tlv *tlv = create_tlv(); + + if (!tlv) { + LLDPAD_ERR("ERROR: Failed to malloc space for " + "incoming TLV. \n"); + goto out; + } + + if ((tlv_length == 0) && (tlv->type != TYPE_0)) { + LLDPAD_ERR("ERROR: tlv_length == 0\n"); + free_unpkd_tlv(tlv); + goto out; + } + + tlv->type = tlv_type; + tlv->length = tlv_length; + tlv->info = (u8 *)malloc(tlv_length); + if (tlv->info) { + memset(tlv->info,0, tlv_length); + memcpy(tlv->info, info, tlv_length); + } else { + LLDPAD_ERR("ERROR: Failed to malloc space for incoming " + "TLV info \n"); + free_unpkd_tlv(tlv); + goto out; + } + + /* Validate the TLV */ + tlv_offset += sizeof(*tlv_head_ptr) + tlv_length; + + if (tlv->type == TYPE_127) { /* private TLV */ + /* give VSI TLV to VDP */ + if (!vdp_indicate(vd, tlv, ecp_hdr->mode)) + tlv_stored = true; + else { + /* TODO: put it in a list and try again later until + * timer and retries have expired */ + tlv_stored = false; + } + } + + if ((tlv->type != TYPE_0) && !tlv_stored) { + LLDPAD_DBG("\n%s: allocated TLV (%u) " + " was not stored! (%p)\n", __func__, tlv->type, + tlv); + tlv = free_unpkd_tlv(tlv); + vd->ecp.stats.statsTLVsUnrecognizedTotal++; + } + + tlv = NULL; + tlv_stored = false; + + } while (tlv_offset < vd->ecp.rx.sizein); + +out: + if (frame_error) { + vd->ecp.stats.statsFramesDiscardedTotal++; + vd->ecp.stats.statsFramesInErrorsTotal++; + vd->ecp.rx.badFrame = true; + } + + return; +} + +/* ecp_rx_change_state - changes the ecp rx sm state + * @vd: currently used port + * @newstate: new state for the sm + * + * no return value + * + * checks state transistion for consistency and finally changes the state of + * the profile. + */ +void ecp_rx_change_state(struct vdp_data *vd, u8 newstate) +{ + switch(newstate) { + case ECP_RX_IDLE: + break; + case ECP_RX_INIT_RECEIVE: + break; + case ECP_RX_RECEIVE_WAIT: + assert((vd->ecp.rx.state == ECP_RX_INIT_RECEIVE) || + (vd->ecp.rx.state == ECP_RX_IDLE) || + (vd->ecp.rx.state == ECP_RX_SEND_ACK) || + (vd->ecp.rx.state == ECP_RX_RESEND_ACK)); + break; + case ECP_RX_RECEIVE_ECPDU: + assert(vd->ecp.rx.state == ECP_RX_RECEIVE_WAIT); + break; + case ECP_RX_SEND_ACK: + assert(vd->ecp.rx.state == ECP_RX_RECEIVE_ECPDU); + break; + case ECP_RX_RESEND_ACK: + assert(vd->ecp.rx.state == ECP_RX_RECEIVE_ECPDU); + break; + default: + LLDPAD_ERR("ERROR: The ECP_RX State Machine is broken!\n"); + log_message(MSG_ERR_RX_SM_INVALID, "%s", vd->ifname); + } + + LLDPAD_DBG("%s(%i)-%s: state change %s -> %s\n", __func__, __LINE__, + vd->ifname, ecp_rx_states[vd->ecp.rx.state], ecp_rx_states[newstate]); + + vd->ecp.rx.state = newstate; +} + +/* ecp_set_rx_state - sets the ecp rx sm state + * @vd: currently used port + * + * returns true or false + * + * switches the state machine to the next state depending on the input + * variables. returns true or false depending on wether the state machine + * can be run again with the new state or can stop at the current state. + */ +bool ecp_set_rx_state(struct vdp_data *vd) +{ + struct port *port = port_find_by_name(vd->ifname); + + if (port->portEnabled == false) { + ecp_rx_change_state(vd, ECP_RX_IDLE); + } + + switch(vd->ecp.rx.state) { + case ECP_RX_IDLE: + if (port->portEnabled == true) { + ecp_rx_change_state(vd, ECP_RX_INIT_RECEIVE); + return true; + } + return false; + case ECP_RX_INIT_RECEIVE: + if ((port->adminStatus == enabledRxTx) || + (port->adminStatus == enabledRxOnly)) { + ecp_rx_change_state(vd, ECP_RX_RECEIVE_WAIT); + return true; + } + return false; + case ECP_RX_RECEIVE_WAIT: + if ((port->adminStatus == disabled) || + (port->adminStatus == enabledTxOnly)) { + ecp_rx_change_state(vd, ECP_RX_IDLE); + return true; + } + if (vd->ecp.rx.rcvFrame == true) { + ecp_rx_change_state(vd, ECP_RX_RECEIVE_ECPDU); + return true; + } + return false; + case ECP_RX_RECEIVE_ECPDU: + if (vd->ecp.seqECPDU == vd->ecp.lastSequence) { + LLDPAD_DBG("%s(%i):-(%s) seqECPDU %x, lastSequence %x\n", __func__, __LINE__, + vd->ifname, vd->ecp.seqECPDU, vd->ecp.lastSequence); + ecp_rx_change_state(vd, ECP_RX_RESEND_ACK); + return true; + } + if (vd->ecp.seqECPDU != vd->ecp.lastSequence) { + ecp_rx_change_state(vd, ECP_RX_RESEND_ACK); + return true; + } + return false; + case ECP_RX_SEND_ACK: + case ECP_RX_RESEND_ACK: + ecp_rx_change_state(vd, ECP_RX_RECEIVE_WAIT); + return false; + default: + LLDPAD_ERR("ERROR: The ECP_RX State Machine is broken!\n"); + log_message(MSG_ERR_RX_SM_INVALID, "%s", vd->ifname); + return false; + } +} + +/* ecp_rx_run_sm - state machine for ecp rx + * @vd: currently used port + * + * no return value + * + * runs the state machine for ecp rx. + */ +void ecp_rx_run_sm(struct vdp_data *vd) +{ + ecp_set_rx_state(vd); + do { + LLDPAD_DBG("%s(%i)-%s: ecp_rx - %s\n", __func__, __LINE__, + vd->ifname, ecp_rx_states[vd->ecp.tx.state]); + + switch(vd->ecp.rx.state) { + case ECP_RX_IDLE: + break; + case ECP_RX_INIT_RECEIVE: + ecp_rx_Initialize(vd); + break; + case ECP_RX_RECEIVE_WAIT: + break; + case ECP_RX_RECEIVE_ECPDU: + ecp_rx_validate_frame(vd); + break; + case ECP_RX_SEND_ACK: + ecp_rx_ProcessFrame(vd); + break; + case ECP_RX_RESEND_ACK: + ecp_rx_ProcessFrame(vd); + if (!vd->ecp.ackReceived) { + ecp_rx_send_ack_frame(vd); + ecp_rx_freeFrame(vd); + } + break; + default: + LLDPAD_ERR("ERROR: The ECP_RX State Machine is broken!\n"); + log_message(MSG_ERR_TX_SM_INVALID, "%s", vd->ifname); + } + } while (ecp_set_rx_state(vd) == true); + +} diff --git a/ecp/ecp_tx.c b/ecp/ecp_tx.c new file mode 100644 index 0000000..a87f3ac --- /dev/null +++ b/ecp/ecp_tx.c @@ -0,0 +1,506 @@ +/******************************************************************************* + + implementation of ECP according to 802.1Qbg + (c) Copyright IBM Corp. 2010 + + Author(s): Jens Osterkamp <jens at linux.vnet.ibm.com> + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + +*******************************************************************************/ + +#include "dcb_osdep.h" +#include "lldp/ports.h" +#include "lldp/l2_packet.h" +#include "eloop.h" +#include "messages.h" +#include "lldpad.h" +#include "lldp_tlv.h" +#include "lldp_mod.h" +#include "lldp_mand.h" +#include "lldp_evb.h" +#include "include/lldp_vdp.h" + +void ecp_tx_run_sm(struct vdp_data *); + +/* ecp_somethingChangedLocal - set flag if port has changed + * @vd: port to set the flag for + * @mode: mode to set the flag to + * + * no return value + * + * set the localChange flag with a mode to indicate a port has changed. + * used to signal an ecpdu needs to be sent out. + */ + +void ecp_somethingChangedLocal(struct vdp_data *vd, int mode) +{ + if (!vd) + return; + + vd->ecp.tx.localChange |= mode; + + return; +} + +/* ecp_print_frameout - print outbound frame + * @vd: currently used port + * + * no return value + * + * prints a raw dump of an outbound ecp frame. useful for low-level protocol + * debugging. + */ +void ecp_print_frameout(struct vdp_data *vd) +{ + int i; + char *s, *t; + + s = t = malloc(256); + + if (!s) { + LLDPAD_ERR("%s(%i): unable to allocate string !\n", __func__, __LINE__); + } + + for (i=0; i < vd->ecp.tx.sizeout; i++) { + int c; + c = sprintf(s, "%02x ", vd->ecp.tx.frameout[i]); + s += c; + if (!((i+1) % 16)) { + LLDPAD_DBG("%s", t); + s = t; + } + } + + LLDPAD_DBG("%s\n", t); + + free(t); +} + +/* ecp_build_ECPDU - create an ecp protocol data unit + * @vd: currently used port + * @mode: mode to create pdu with (REQ or ACK) + * + * returns true on success, false on failure + * + * creates the frame header with the ports mac address, the ecp header with REQ + * or ACK mode, plus a list of packed TLVs created from the profiles on this + * port. + */ +bool ecp_build_ECPDU(struct vdp_data *vd, int mode) +{ + struct l2_ethhdr eth; + struct ecp_hdr ecp_hdr; + u8 own_addr[ETH_ALEN]; + u32 fb_offset = 0; + u32 datasize = 0; + struct packed_tlv *ptlv = NULL; + struct vsi_profile *p; + + /* TODO: different multicast address for sending ECP over S-channel (multi_cast_source_s) + * S-channels to implement later */ + memcpy(eth.h_dest, multi_cast_source, ETH_ALEN); + l2_packet_get_own_src_addr(vd->ecp.l2,(u8 *)&own_addr); + memcpy(eth.h_source, &own_addr, ETH_ALEN); + eth.h_proto = htons(ETH_P_ECP); + vd->ecp.tx.frameout = (u8 *)malloc(ETH_FRAME_LEN); + if (vd->ecp.tx.frameout == NULL) { + LLDPAD_ERR("InfoECPDU: Failed to malloc frame buffer \n"); + return false; + } + memset(vd->ecp.tx.frameout,0,ETH_FRAME_LEN); + memcpy(vd->ecp.tx.frameout, (void *)ð, sizeof(struct l2_ethhdr)); + fb_offset += sizeof(struct l2_ethhdr); + + ecp_hdr.oui[0] = 0x0; + ecp_hdr.oui[1] = 0x1b; + ecp_hdr.oui[2] = 0x3f; + + ecp_hdr.pad1 = 0x0; + + ecp_hdr.subtype = ECP_SUBTYPE; + switch(mode) { + case VDP_PROFILE_REQ: + ecp_hdr.mode = ECP_REQUEST; + break; + case VDP_PROFILE_ACK: + ecp_hdr.mode = ECP_ACK; + break; + default: + LLDPAD_ERR("%s(%i): unknown mode for %s !\n", __func__, __LINE__, + vd->ifname); + } + + vd->ecp.lastSequence++; + ecp_hdr.seqnr = htons(vd->ecp.lastSequence); + + if ((sizeof(struct ecp_hdr)+fb_offset) > ETH_MAX_DATA_LEN) + goto error; + memcpy(vd->ecp.tx.frameout+fb_offset, (void *)&ecp_hdr, sizeof(struct ecp_hdr)); + datasize += sizeof(struct ecp_hdr); + fb_offset += sizeof(struct ecp_hdr); + + /* create packed_tlvs for all profiles on this interface */ + LIST_FOREACH(p, &vd->profile_head, profile) { + if(!p) { + LLDPAD_ERR("%s(%i): list vd->profile_head empty !\n", __func__, __LINE__); + continue; + } + + if (p->localChange != mode) { + LLDPAD_ERR("%s(%i): not sending out profile !\n", __func__, __LINE__); + continue; + } + + ptlv = vdp_gettlv(vd, p); + + if (!ptlv) { + LLDPAD_ERR("%s(%i): ptlv not created !\n", __func__, __LINE__); + continue; + } + + if (ptlv) { + if ((ptlv->size+fb_offset) > ETH_MAX_DATA_LEN) + goto error; + memcpy(vd->ecp.tx.frameout+fb_offset, + ptlv->tlv, ptlv->size); + datasize += ptlv->size; + fb_offset += ptlv->size; + } + + ptlv = free_pkd_tlv(ptlv); + } + + /* The End TLV marks the end of the LLDP PDU */ + ptlv = pack_end_tlv(); + if (!ptlv || ((ptlv->size + fb_offset) > ETH_MAX_DATA_LEN)) + goto error; + memcpy(vd->ecp.tx.frameout + fb_offset, ptlv->tlv, ptlv->size); + datasize += ptlv->size; + fb_offset += ptlv->size; + ptlv = free_pkd_tlv(ptlv); + + if (datasize > ETH_MAX_DATA_LEN) + goto error; + + if (datasize < ETH_MIN_DATA_LEN) + vd->ecp.tx.sizeout = ETH_MIN_PKT_LEN; + else + vd->ecp.tx.sizeout = fb_offset; + + return true; + +error: + ptlv = free_pkd_tlv(ptlv); + if (vd->ecp.tx.frameout) + free(vd->ecp.tx.frameout); + vd->ecp.tx.frameout = NULL; + LLDPAD_ERR("InfoECPDU: packed TLV too large for tx frame\n"); + return false; +} + +/* ecp_tx_Initialize - initializes the ecp tx state machine + * @vd: currently used port + * + * no return value + * + * initializes some variables for the ecp tx state machine. + */ +void ecp_tx_Initialize(struct vdp_data *vd) +{ + if (vd->ecp.tx.frameout) { + free(vd->ecp.tx.frameout); + vd->ecp.tx.frameout = NULL; + } + vd->ecp.tx.localChange = VDP_PROFILE_REQ; + vd->ecp.lastSequence = ECP_SEQUENCE_NR_START; + vd->ecp.stats.statsFramesOutTotal = 0; + vd->ecp.ackTimerExpired = false; + vd->ecp.retries = 0; + + struct port *port = port_find_by_name(vd->ifname); + l2_packet_get_port_state(vd->ecp.l2, (u8 *)&(port->portEnabled)); + + return; +} + +/* ecp_txFrame - transmit ecp frame + * @vd: currently used port + * + * returns the number of characters sent on success, -1 on failure + * + * sends out the frame stored in the frameout structure using l2_packet_send. + */ +u8 ecp_txFrame(struct vdp_data *vd) +{ + int status = 0; + + status = l2_packet_send(vd->ecp.l2, (u8 *)&multi_cast_source, + htons(ETH_P_ECP),vd->ecp.tx.frameout,vd->ecp.tx.sizeout); + vd->ecp.stats.statsFramesOutTotal++; + + free(vd->ecp.tx.frameout); + + return status; +} + +/* ecp_tx_create_frame - create ecp frame + * @vd: currently used port + * + * no return value + * + * + */ +void ecp_tx_create_frame(struct vdp_data *vd) +{ + /* send REQs */ + if (vd->ecp.tx.localChange & VDP_PROFILE_REQ) { + LLDPAD_DBG("%s(%i)-%s: sending REQs\n", __func__, __LINE__, vd->ifname); + ecp_build_ECPDU(vd, VDP_PROFILE_REQ); + ecp_print_frameout(vd); + ecp_txFrame(vd); + } + + /* send ACKs */ + if (vd->ecp.tx.localChange & VDP_PROFILE_ACK) { + LLDPAD_DBG("%s(%i)-%s: sending ACKs\n", __func__, __LINE__, vd->ifname); + ecp_build_ECPDU(vd, VDP_PROFILE_ACK); + ecp_print_frameout(vd); + ecp_txFrame(vd); + } + + vd->ecp.tx.localChange = 0; + return; +} + +/* ecp_timeout_handler - handles the ack timer expiry + * @eloop_data: data structure of event loop + * @user_ctx: user context, port here + * + * no return value + * + * called when the ECP ack timer has expired. sets a flag and calls the ECP + * state machine. + */ +static void ecp_tx_timeout_handler(void *eloop_data, void *user_ctx) +{ + struct vdp_data *vd; + + vd = (struct vdp_data *) user_ctx; + + vd->ecp.ackTimerExpired = true; + + LLDPAD_DBG("%s(%i)-%s: timer expired\n", __func__, __LINE__, + vd->ifname); + + ecp_tx_run_sm(vd); +} + +/* ecp_tx_stop_ackTimer - stop the ECP ack timer + * @vd: currently used port + * + * returns the number of removed handlers + * + * stops the ECP ack timer. used when a ack frame for the port has been + * received. + */ +int ecp_tx_stop_ackTimer(struct vdp_data *vd) +{ + LLDPAD_DBG("%s(%i)-%s: stopping timer\n", __func__, __LINE__, + vd->ifname); + + return eloop_cancel_timeout(ecp_tx_timeout_handler, NULL, (void *) vd); +} + +/* ecp_tx_start_ackTimer - starts the ECP ack timer + * @profile: profile to process + * + * returns 0 on success, -1 on error + * + * starts the ack timer when a frame has been sent out. + */ +static void ecp_tx_start_ackTimer(struct vdp_data *vd) +{ + unsigned int secs, usecs; + + vd->ecp.ackTimerExpired = false; + + secs = ECP_TRANSMISSION_TIMER / ECP_TRANSMISSION_DIVIDER; + usecs = ECP_TRANSMISSION_TIMER % ECP_TRANSMISSION_DIVIDER; + + LLDPAD_DBG("%s(%i)-%s: starting timer\n", __func__, __LINE__, + vd->ifname); + + eloop_register_timeout(secs, usecs, ecp_tx_timeout_handler, NULL, (void *) vd); +} + +/* ecp_tx_change_state - changes the ecp tx sm state + * @vd: currently used port + * @newstate: new state for the sm + * + * no return value + * + * checks state transistion for consistency and finally changes the state of + * the profile. + */ +static void ecp_tx_change_state(struct vdp_data *vd, u8 newstate) +{ + switch(newstate) { + case ECP_TX_INIT_TRANSMIT: + break; + case ECP_TX_TRANSMIT_ECPDU: + assert((vd->ecp.tx.state == ECP_TX_INIT_TRANSMIT) || + (vd->ecp.tx.state == ECP_TX_WAIT_FOR_ACK) || + (vd->ecp.tx.state == ECP_TX_REQUEST_PDU)); + break; + case ECP_TX_WAIT_FOR_ACK: + assert(vd->ecp.tx.state == ECP_TX_TRANSMIT_ECPDU); + break; + case ECP_TX_REQUEST_PDU: + assert(vd->ecp.tx.state == ECP_TX_WAIT_FOR_ACK); + break; + default: + LLDPAD_ERR("ERROR: The ECP_TX State Machine is broken!\n"); + log_message(MSG_ERR_TX_SM_INVALID, "%s", vd->ifname); + } + + LLDPAD_DBG("%s(%i)-%s: state change %s -> %s\n", __func__, __LINE__, + vd->ifname, ecp_tx_states[vd->ecp.tx.state], ecp_tx_states[newstate]); + + vd->ecp.tx.state = newstate; + return; +} + +/* ecp_set_tx_state - sets the ecp tx sm state + * @vd: currently used port + * + * returns true or false + * + * switches the state machine to the next state depending on the input + * variables. returns true or false depending on wether the state machine + * can be run again with the new state or can stop at the current state. + */ +static bool ecp_set_tx_state(struct vdp_data *vd) +{ + struct port *port = port_find_by_name(vd->ifname); + + if (!port) { + LLDPAD_ERR("%s(%i): port not found !\n", __func__, __LINE__); + return 0; + } + + if ((port->portEnabled == false) && (port->prevPortEnabled == true)) { + LLDPAD_ERR("set_tx_state: port was disabled\n"); + ecp_tx_change_state(vd, ECP_TX_INIT_TRANSMIT); + } + port->prevPortEnabled = port->portEnabled; + + switch (vd->ecp.tx.state) { + case ECP_TX_INIT_TRANSMIT: + if (port->portEnabled && ((port->adminStatus == enabledRxTx) || + (port->adminStatus == enabledTxOnly)) && vd->ecp.tx.localChange) { + ecp_tx_change_state(vd, ECP_TX_TRANSMIT_ECPDU); + return true; + } + return false; + case ECP_TX_TRANSMIT_ECPDU: + if ((port->adminStatus == disabled) || + (port->adminStatus == enabledRxOnly)) { + ecp_tx_change_state(vd, ECP_TX_INIT_TRANSMIT); + return true; + } + ecp_tx_change_state(vd, ECP_TX_WAIT_FOR_ACK); + return true; + case ECP_TX_WAIT_FOR_ACK: + if (vd->ecp.ackTimerExpired) { + vd->ecp.retries++; + if (vd->ecp.retries < ECP_MAX_RETRIES) { + ecp_somethingChangedLocal(vd, VDP_PROFILE_REQ); + ecp_tx_change_state(vd, ECP_TX_TRANSMIT_ECPDU); + return true; + } + if (vd->ecp.retries == ECP_MAX_RETRIES) { + LLDPAD_DBG("%s(%i)-%s: 1 \n", __func__, __LINE__, + vd->ifname); + ecp_tx_change_state(vd, ECP_TX_REQUEST_PDU); + return true; + } + } + if (vd->ecp.ackReceived && vd->ecp.seqECPDU == vd->ecp.lastSequence) { + vd->ecp.ackReceived = false; + ecp_tx_change_state(vd, ECP_TX_REQUEST_PDU); + return true; + } + return false; + case ECP_TX_REQUEST_PDU: + if (vd->ecp.tx.localChange & VDP_PROFILE_REQ) { + ecp_tx_change_state(vd, ECP_TX_TRANSMIT_ECPDU); + return true; + } + return false; + default: + LLDPAD_ERR("ERROR: The TX State Machine is broken!\n"); + log_message(MSG_ERR_TX_SM_INVALID, "%s", vd->ifname); + return false; + } +} + +/* ecp_tx_run_sm - state machine for ecp tx + * @vd: currently used vdp_data + * + * no return value + * + * runs the state machine for ecp tx. + */ +void ecp_tx_run_sm(struct vdp_data *vd) +{ + do { + LLDPAD_DBG("%s(%i)-%s: ecp_tx - %s\n", __func__, __LINE__, + vd->ifname, ecp_tx_states[vd->ecp.tx.state]); + + switch(vd->ecp.tx.state) { + case ECP_TX_INIT_TRANSMIT: + ecp_tx_Initialize(vd); + break; + case ECP_TX_TRANSMIT_ECPDU: + ecp_tx_create_frame(vd); + ecp_tx_start_ackTimer(vd); + break; + case ECP_TX_WAIT_FOR_ACK: + if (vd->ecp.ackReceived) { + LLDPAD_DBG("%s(%i)-%s: ECP_TX_WAIT_FOR_ACK ackReceived\n", __func__, __LINE__, + vd->ifname); + LLDPAD_DBG("%s(%i)-%s: seqECPDU %x lastSequence %x \n", __func__, __LINE__, + vd->ifname, vd->ecp.seqECPDU, vd->ecp.lastSequence); + vd->ecp.tx.localChange = 0; + ecp_tx_stop_ackTimer(vd); + } + break; + case ECP_TX_REQUEST_PDU: + vd->ecp.retries = 0; + LLDPAD_DBG("%s(%i)-%s: ECP_TX_REQUEST_PDU lastSequence %x\n", __func__, __LINE__, + vd->ifname, vd->ecp.lastSequence); + break; + default: + LLDPAD_ERR("%s(%i): ERROR The TX State Machine is broken!\n", __func__, + __LINE__); + log_message(MSG_ERR_TX_SM_INVALID, "%s", vd->ifname); + } + } while (ecp_set_tx_state(vd) == true); + + return; +} diff --git a/include/lldp.h b/include/lldp.h index 0371844..476773c 100644 --- a/include/lldp.h +++ b/include/lldp.h @@ -190,6 +190,7 @@ enum { /* IEEE 802.1Qbg subtype */ #define LLDP_EVB_SUBTYPE 0 +#define LLDP_VDP_SUBTYPE 0x2 /* forwarding mode */ #define LLDP_EVB_CAPABILITY_FORWARD_STANDARD (1 << 7) diff --git a/include/lldp_evb.h b/include/lldp_evb.h index 667f9ad..d028c21 100644 --- a/include/lldp_evb.h +++ b/include/lldp_evb.h @@ -37,6 +37,12 @@ typedef enum { EVB_CONFIRMATION } evb_state; +#define RTE 13 +/* retransmission granularity (RTG) in microseconds */ +#define EVB_RTG 10 +/* retransmission multiplier (RTM) */ +#define EVB_RTM(rte) (2<<(RTE-1)) + struct tlv_info_evb { u8 oui[3]; u8 sub; diff --git a/include/lldp_vdp.h b/include/lldp_vdp.h new file mode 100644 index 0000000..b97d8c0 --- /dev/null +++ b/include/lldp_vdp.h @@ -0,0 +1,159 @@ +/******************************************************************************* + + implementation of according to IEEE 802.1Qbg + (c) Copyright IBM Corp. 2010 + + Author(s): Jens Osterkamp <jens at linux.vnet.ibm.com> + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + +*******************************************************************************/ + +#ifndef _LLDP_VDP_H +#define _LLDP_VDP_H + +#include "lldp_mod.h" +#include "ecp/ecp.h" + +#define LLDP_MOD_VDP OUI_IEEE_8021Qbg+1 + +#define VDP_MODE_PREASSOCIATE 0x0 +#define VDP_MODE_PREASSOCIATE_WITH_RR 0x1 +#define VDP_MODE_ASSOCIATE 0x2 +#define VDP_MODE_DEASSOCIATE 0x3 + +#define VDP_RESPONSE_SUCCESS 0x0 +#define VDP_RESPONSE_INVALID_FORMAT 0x1 +#define VDP_RESPONSE_INSUFF_RESOURCES 0x2 +#define VDP_RESPONSE_UNUSED_VTID 0x3 +#define VDP_RESPONSE_VTID_VIOLATION 0x4 +#define VDP_RESPONSE_VTID_VER_VIOLATION 0x5 +#define VDP_RESPONSE_OUT_OF_SYNC 0x6 + +enum { + VDP_PROFILE_NOCHANGE = 0, + VDP_PROFILE_REQ, + VDP_PROFILE_ACK, + VDP_PROFILE_NACK, +}; + +#define VDP_MACVLAN_FORMAT_1 1 + +#define VDP_TRANSMISSION_TIMER 3*EVB_RTM(RTE)*EVB_RTG +#define VDP_TRANSMISSION_DIVIDER 10000 + +#define VDP_ROLE_STATION 0 +#define VDP_ROLE_BRIDGE 1 + +enum { + VSI_UNASSOCIATED = 0, + VSI_ASSOC_PROCESSING, + VSI_ASSOCIATED, + VSI_PREASSOC_PROCESSING, + VSI_PREASSOCIATED, + VSI_DEASSOC_PROCESSING, + VSI_EXIT, +}; + +static char *vsi_states[] = { + "VSI_UNASSOCIATED", + "VSI_ASSOC_PROCESSING", + "VSI_ASSOCIATED", + "VSI_PREASSOC_PROCESSING", + "VSI_PREASSOCIATED", + "VSI_DEASSOC_PROCESSING", + "VSI_EXIT" +}; + +struct mac_vlan { + u8 mac[6]; + u16 vlan; +} __attribute__ ((__packed__)); + +struct tlv_info_vdp { + u8 oui[3]; + u8 sub; + u8 mode; + u8 response; + u8 mgrid; + u8 id[3]; + u8 version; + u8 instance[16]; + u8 format; + u16 entries; + struct mac_vlan mac_vlan; +} __attribute__ ((__packed__)); + +struct vsi_profile { + int mode; + int response; + u8 mgrid; + int id; + u8 version; + u8 instance[16]; + u8 mac[6]; /* TODO: currently only one MAC/VLAN pair supported, more later */ + u16 vlan; + struct port *port; + int ackTimerExpired; + int ackReceived; + int state; + int localChange; + LIST_ENTRY(vsi_profile) profile; +}; + +struct vdp_data { + char ifname[IFNAMSIZ]; + struct ecp ecp; + struct unpacked_tlv *vdp; + int role; + LIST_HEAD(profile_head, vsi_profile) profile_head; + LIST_ENTRY(vdp_data) entry; +}; + +struct vdp_user_data { + LIST_HEAD(vdp_head, vdp_data) head; +}; + +struct lldp_module *vdp_register(void); +void vdp_unregister(struct lldp_module *mod); +struct vdp_data *vdp_data(char *ifname); +struct packed_tlv *vdp_gettlv(struct vdp_data *vd, struct vsi_profile *profile); +void vdp_vsi_sm_station(struct vsi_profile *profile); +struct vsi_profile *vdp_add_profile(struct vsi_profile *profile); + +#define MAC_ADDR_STRLEN 18 +#define INSTANCE_STRLEN 32 + +#define PRINT_PROFILE(s, p) \ +{ int c; \ + c = sprintf(s, "\nmode: %i\n", p->mode); s += c; \ + c = sprintf(s, "response: %i\n", p->response); s += c; \ + c = sprintf(s, "state: %i\n", p->state); s += c; \ + c = sprintf(s, "mgrid: %i\n", p->mgrid); s += c; \ + c = sprintf(s, "id: %x\n", p->id); \ + s += c; \ + c = sprintf(s, "version: %i\n", p->version); s += c; \ + char instance[INSTANCE_STRLEN+2]; \ + instance2str(p->instance, instance, sizeof(instance)); \ + c = sprintf(s, "instance: %s\n", &instance[0]); s += c; \ + char macbuf[MAC_ADDR_STRLEN+1]; \ + mac2str(p->mac, macbuf, MAC_ADDR_STRLEN); \ + c = sprintf(s, "mac: %s\n", macbuf); s += c; \ + c = sprintf(s, "vlan: %i\n\n", p->vlan); s += c; \ +} + +#endif /* _LLDP_VDP_H */ diff --git a/lldp/l2_packet.h b/lldp/l2_packet.h index 16f3683..0962429 100644 --- a/lldp/l2_packet.h +++ b/lldp/l2_packet.h @@ -36,6 +36,8 @@ #define ETH_P_LLDP 0x88cc +/* TODO: use extended ethertype until final ethertype is available */ +#define ETH_P_ECP 0x88b7 #define ETH_FRAME_LEN 1514 diff --git a/lldp/ports.h b/lldp/ports.h index 0138efe..44dc5f1 100644 --- a/lldp/ports.h +++ b/lldp/ports.h @@ -142,15 +142,20 @@ struct port { u8 portEnabled; u8 prevPortEnabled; u8 adminStatus; - u8 rxChanges; - u16 lldpdu; + + /* protocol specific */ struct l2_packet_data *l2; struct portrx rx; struct porttx tx; - struct porttlvs tlvs; struct portstats stats; struct porttimers timers; + u8 rxChanges; + u16 lldpdu; struct msap msap; + + /* not sure */ + struct porttlvs tlvs; + struct port *next; }; diff --git a/lldp_vdp.c b/lldp_vdp.c new file mode 100644 index 0000000..01bcbf7 --- /dev/null +++ b/lldp_vdp.c @@ -0,0 +1,1196 @@ +/******************************************************************************* + + implementation of VDP according to IEEE 802.1Qbg + (c) Copyright IBM Corp. 2010 + + Author(s): Jens Osterkamp <jens at linux.vnet.ibm.com> + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + +*******************************************************************************/ + +#include <net/if.h> +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/utsname.h> +#include <linux/if_bridge.h> +#include "lldp.h" +#include "lldp_vdp.h" +#include "ecp/ecp.h" +#include "eloop.h" +#include "lldp_evb.h" +#include "messages.h" +#include "config.h" +#include "common.h" +#include "lldp_vdp_clif.h" +#include "lldp_vdp_cmds.h" + +/* vdp_data - searches vdp_data in the list of modules for this port + * @ifname: interface name to search for + * + * returns vdp_data on success, NULL on error + * + * searches the list of user_data for the VDP module user_data. + */ +struct vdp_data *vdp_data(char *ifname) +{ + struct vdp_user_data *ud; + struct vdp_data *vd = NULL; + + ud = find_module_user_data_by_if(ifname, &lldp_head, LLDP_MOD_VDP); + if (ud) { + LIST_FOREACH(vd, &ud->head, entry) { + if (!strncmp(ifname, vd->ifname, IFNAMSIZ)) + return vd; + } + } + return NULL; +} + +/* vdp_free_tlv - free tlv in vdp_data + * @vd: vdp_data + * + * no return value + * + * frees up tlv in vdp_data. used in vdp_free_data. + */ +static void vdp_free_tlv(struct vdp_data *vd) +{ + if (vd) { + FREE_UNPKD_TLV(vd, vdp); + } +} + +/* vdp_free_data - frees up vdp data + * @ud: user data structure + * + * no return value + * + * removes vd_structure from the user_data list. frees up tlv in vdp_data. + * used in vdp_unregister. + */ +static void vdp_free_data(struct vdp_user_data *ud) +{ + struct vdp_data *vd; + if (ud) { + while (!LIST_EMPTY(&ud->head)) { + vd = LIST_FIRST(&ud->head); + LIST_REMOVE(vd, entry); + vdp_free_tlv(vd); + free(vd); + } + } +} + +/* vdp_print_profile - print a vsi profile + * @profile: profile to print + * + * no return value + * + * prints the contents of a profile first to a string using the PRINT_PROFILE + * macro, and then to the screen. Used for debug purposes. + */ +static inline void vdp_print_profile(struct vsi_profile *profile) +{ + LLDPAD_DBG("profile:\n"); + + LLDPAD_DBG("mode: %i\n", profile->mode); + LLDPAD_DBG("response: %i\n", profile->response); + LLDPAD_DBG("state: %i\n", profile->state); + LLDPAD_DBG("mgrid: %i\n", profile->mgrid); + LLDPAD_DBG("id: %x\n", profile->id); + LLDPAD_DBG("version: %i\n", profile->version); + + char instance[INSTANCE_STRLEN+2]; + instance2str(profile->instance, instance, sizeof(instance)); + LLDPAD_DBG("instance: %s\n", &instance[0]); + + char macbuf[MAC_ADDR_STRLEN+1]; + mac2str(profile->mac, macbuf, MAC_ADDR_STRLEN); + LLDPAD_DBG("mac: %s\n", macbuf); + + LLDPAD_DBG("vlan: %i\n", profile->vlan); +} + +/* vdp_somethingChangedLocal - set flag if profile has changed + * @profile: profile to set the flag for + * @mode: mode to set the flag to + * + * no return value + * + * set the localChange flag with a mode to indicate a profile has changed. + * used next time when a ecpdu with profiles is sent out. + */ +void vdp_somethingChangedLocal(struct vsi_profile *profile, int mode) +{ + profile->localChange = mode; +} + +/* vdp_ackTimer_expired - checks for expired ack timer + * @profile: profile to be checked + * + * returns true or false + * + * returns value of profile->ackTimerExpired, true if ack timer has expired, + * false otherwise. + */ +static bool vdp_ackTimer_expired(struct vsi_profile *profile) +{ + return profile->ackTimerExpired; +} + +/* vdp_timeout_handler - handles the ack timer expiry + * @eloop_data: data structure of event loop + * @user_ctx: user context, profile here + * + * no return value + * + * called when the VDP ack timer has expired. sets a flag and calls the VDP + * state machine. + */ +void vdp_timeout_handler(void *eloop_data, void *user_ctx) +{ + struct vsi_profile *profile; + + profile = (struct vsi_profile *) user_ctx; + + profile->ackTimerExpired = true; + + LLDPAD_DBG("%s(%i)-%s: timer expired\n", __func__, __LINE__, + profile->port->ifname); + + vdp_vsi_sm_station(profile); +} + +/* vdp_stop_ackTimer - stop the VDP ack timer + * @profile: profile to process + * + * returns the number of removed handlers + * + * stops the VDP ack timer. used when a ack frame for the profile has been + * received. + */ +static int vdp_stop_ackTimer(struct vsi_profile *profile) +{ + LLDPAD_DBG("%s(%i)-%s: stopping timer\n", __func__, __LINE__, + profile->port->ifname); + + return eloop_cancel_timeout(vdp_timeout_handler, NULL, (void *) profile); +} + +/* vdp_start_ackTimer - starts the VDP ack timer + * @profile: profile to process + * + * returns 0 on success, -1 on error + * + * starts the ack timer when a frame has been sent out. + */ +static int vdp_start_ackTimer(struct vsi_profile *profile) +{ + unsigned int secs, usecs; + + profile->ackTimerExpired = false; + + secs = VDP_TRANSMISSION_TIMER / VDP_TRANSMISSION_DIVIDER; + usecs = VDP_TRANSMISSION_TIMER % VDP_TRANSMISSION_DIVIDER; + + LLDPAD_DBG("%s(%i)-%s: starting timer\n", __func__, __LINE__, + profile->port->ifname); + + return eloop_register_timeout(secs, usecs, vdp_timeout_handler, NULL, (void *) profile); +} + +/* vdp_vsi_change_station_state - changes the VDP station sm state + * @profile: profile to process + * @newstate: new state for the sm + * + * no return value + * + * actually changes the state of the profile + */ +void vdp_vsi_change_station_state(struct vsi_profile *profile, u8 newstate) +{ + switch(newstate) { + case VSI_UNASSOCIATED: + break; + case VSI_ASSOC_PROCESSING: + assert((profile->state == VSI_PREASSOCIATED) || + (profile->state == VSI_UNASSOCIATED)); + break; + case VSI_ASSOCIATED: + assert(profile->state == VSI_ASSOC_PROCESSING); + break; + case VSI_PREASSOC_PROCESSING: + assert(profile->state == VSI_UNASSOCIATED); + break; + case VSI_PREASSOCIATED: + assert(profile->state == VSI_PREASSOC_PROCESSING); + break; + case VSI_DEASSOC_PROCESSING: + assert((profile->state == VSI_PREASSOCIATED) || + (profile->state == VSI_ASSOCIATED)); + break; + case VSI_EXIT: + assert((profile->state == VSI_ASSOC_PROCESSING) || + (profile->state == VSI_PREASSOC_PROCESSING) || + (profile->state == VSI_DEASSOC_PROCESSING) || + (profile->state == VSI_PREASSOCIATED) || + (profile->state == VSI_ASSOCIATED)); + break; + default: + LLDPAD_ERR("ERROR: The VDP station State Machine is broken!\n"); + break; + } + + LLDPAD_DBG("%s(%i)-%s: state change %s -> %s\n", __func__, __LINE__, + profile->port->ifname, vsi_states[profile->state], vsi_states[newstate]); + + profile->state = newstate; +} + +/* vdp_vsi_set_station_state - sets the vdp sm station state + * @profile: profile to process + * + * returns true or false + * + * switches the state machine to the next state depending on the input + * variables. returns true or false depending on wether the state machine + * can be run again with the new state or can stop at the current state. + */ +static bool vdp_vsi_set_station_state(struct vsi_profile *profile) +{ + switch(profile->state) { + case VSI_UNASSOCIATED: + if ((profile->mode == VDP_MODE_PREASSOCIATE) || + (profile->mode == VDP_MODE_PREASSOCIATE_WITH_RR)) { + vdp_vsi_change_station_state(profile, VSI_PREASSOC_PROCESSING); + return true; + } else if (profile->mode == VDP_MODE_ASSOCIATE) { + vdp_vsi_change_station_state(profile, VSI_ASSOC_PROCESSING); + return true; + } + return false; + case VSI_ASSOC_PROCESSING: + if (profile->ackReceived) { + vdp_vsi_change_station_state(profile, VSI_ASSOCIATED); + return true; + } else if (!profile->ackReceived && vdp_ackTimer_expired(profile)) { + vdp_vsi_change_station_state(profile, VSI_EXIT); + return true; + } + return false; + case VSI_ASSOCIATED: + if (profile->mode == VDP_MODE_PREASSOCIATE) { + vdp_vsi_change_station_state(profile, VSI_PREASSOC_PROCESSING); + return true; + } else if (profile->mode == VDP_MODE_DEASSOCIATE) { + vdp_vsi_change_station_state(profile, VSI_DEASSOC_PROCESSING); + return true; + } + return false; + case VSI_PREASSOC_PROCESSING: + if (profile->ackReceived) { + vdp_vsi_change_station_state(profile, VSI_PREASSOCIATED); + return true; + } else if (vdp_ackTimer_expired(profile)) { + vdp_vsi_change_station_state(profile, VSI_EXIT); + return true; + } + case VSI_PREASSOCIATED: + if (profile->mode == VDP_MODE_DEASSOCIATE) { + vdp_vsi_change_station_state(profile, VSI_DEASSOC_PROCESSING); + return true; + } + if (profile->mode == VDP_MODE_ASSOCIATE) { + vdp_vsi_change_station_state(profile, VSI_ASSOC_PROCESSING); + return true; + } + return false; + case VSI_DEASSOC_PROCESSING: + if ((profile->ackReceived) || vdp_ackTimer_expired(profile)) { + vdp_vsi_change_station_state(profile, VSI_EXIT); + return true; + } + return false; + case VSI_EXIT: + return false; + default: + LLDPAD_ERR("ERROR: The VSI RX State Machine is broken!\n"); + log_message(MSG_ERR_RX_SM_INVALID, ""); + return false; + } +} + +/* vdp_vsi_sm_station - state machine for vdp station role + * @profile: profile for which the state is processed + * + * no return value + * + * runs the state machine for the station role of VDP. + */ +void vdp_vsi_sm_station(struct vsi_profile *profile) +{ + struct vdp_data *vd = vdp_data(profile->port->ifname); + + vdp_vsi_set_station_state(profile); + do { + LLDPAD_DBG("%s(%i)-%s: station - %s\n", __func__, __LINE__, + profile->port->ifname, vsi_states[profile->state]); + + switch(profile->state) { + case VSI_UNASSOCIATED: + break; + case VSI_ASSOC_PROCESSING: + vdp_somethingChangedLocal(profile, VDP_PROFILE_REQ); + ecp_somethingChangedLocal(vd, VDP_PROFILE_REQ); + ecp_tx_run_sm(vd); + vdp_start_ackTimer(profile); + break; + case VSI_ASSOCIATED: + vdp_stop_ackTimer(profile); + /* TODO: + * vsiError = ProcRxandSetCfg(remoteTLV, localtlv, vsistate); + * if (!vsiError) vsistate=ASSOCIATED */ + break; + case VSI_PREASSOC_PROCESSING: + /* send out profile */ + vdp_somethingChangedLocal(profile, VDP_PROFILE_REQ); + ecp_somethingChangedLocal(vd, VDP_PROFILE_REQ); + ecp_tx_run_sm(vd); + vdp_start_ackTimer(profile); + break; + case VSI_PREASSOCIATED: + profile->ackReceived = false; + vdp_somethingChangedLocal(profile, VDP_PROFILE_NOCHANGE); + vdp_stop_ackTimer(profile); + /* TODO vsiError = ProcRxandSetCfg(remoteTLV, localtlv, vsistate); + * if (!vsiError) vsistate=PREASSOCIATED */ + break; + case VSI_DEASSOC_PROCESSING: + vdp_somethingChangedLocal(profile, VDP_PROFILE_REQ); + ecp_somethingChangedLocal(vd, VDP_PROFILE_REQ); + ecp_tx_run_sm(vd); + vdp_start_ackTimer(profile); + break; + case VSI_EXIT: + /* TODO: send DEASSOC here ? */ + vdp_stop_ackTimer(profile); + vdp_remove_profile(profile); + break; + default: + LLDPAD_ERR("ERROR: The VSI RX station State Machine is broken!\n"); + log_message(MSG_ERR_TX_SM_INVALID, ""); + } + } while (vdp_vsi_set_station_state(profile) == true); + +} + +/* vdp_vsi_change_bridge_state - changes the VDP bridge sm state + * @profile: profile to process + * @newstate: new state for the sm + * + * no return value + * + * actually changes the state of the profile + */ +static void vdp_vsi_change_bridge_state(struct vsi_profile *profile, u8 newstate) +{ + switch(newstate) { + case VSI_UNASSOCIATED: + break; + case VSI_ASSOC_PROCESSING: + assert((profile->state == VSI_UNASSOCIATED) || + (profile->state == VSI_PREASSOCIATED) || + (profile->state == VSI_ASSOCIATED)); + break; + case VSI_ASSOCIATED: + assert(profile->state == VSI_ASSOC_PROCESSING); + break; + case VSI_PREASSOC_PROCESSING: + assert((profile->state == VSI_UNASSOCIATED) || + (profile->state == VSI_PREASSOCIATED) || + (profile->state == VSI_ASSOCIATED)); + break; + case VSI_PREASSOCIATED: + assert(profile->state == VSI_PREASSOC_PROCESSING); + break; + case VSI_DEASSOC_PROCESSING: + assert((profile->state == VSI_UNASSOCIATED) || + (profile->state == VSI_PREASSOCIATED) || + (profile->state == VSI_ASSOCIATED)); + break; + case VSI_EXIT: + assert((profile->state == VSI_DEASSOC_PROCESSING) || + (profile->state == VSI_PREASSOC_PROCESSING) || + (profile->state == VSI_ASSOC_PROCESSING)); + break; + default: + LLDPAD_ERR("ERROR: The VDP bridge State Machine is broken!\n"); + break; + } + profile->state = newstate; +} + +/* vdp_vsi_set_bridge_state - sets the vdp sm bridge state + * @profile: profile to process + * + * returns true or false + * + * switches the state machine to the next state depending on the input + * variables. returns true or false depending on wether the state machine + * can be run again with the new state or can stop at the current state. + */ +static bool vdp_vsi_set_bridge_state(struct vsi_profile *profile) +{ + switch(profile->state) { + case VSI_UNASSOCIATED: + if ((profile->mode == VDP_MODE_DEASSOCIATE)) /* || (INACTIVE)) */ { + vdp_vsi_change_bridge_state(profile, VSI_DEASSOC_PROCESSING); + return true; + } else if (profile->mode == VDP_MODE_ASSOCIATE) { + vdp_vsi_change_bridge_state(profile, VSI_ASSOC_PROCESSING); + return true; + } else if (profile->mode == VDP_MODE_PREASSOCIATE) { + vdp_vsi_change_bridge_state(profile, VSI_PREASSOC_PROCESSING); + return true; + } + return false; + case VSI_ASSOC_PROCESSING: + /* TODO: handle error case + if (!vsiError) || + (vsiError && vsiState == Assoc) { + */ + if (profile->mode == VDP_MODE_ASSOCIATE) { + vdp_vsi_change_bridge_state(profile, VSI_ASSOCIATED); + return true; + } + return false; + case VSI_ASSOCIATED: + if (profile->mode == VDP_MODE_ASSOCIATE) /* || ( INACTIVE )*/ { + vdp_vsi_change_bridge_state(profile, VSI_DEASSOC_PROCESSING); + return true; + } else if (profile->mode == VDP_MODE_PREASSOCIATE) { + vdp_vsi_change_bridge_state(profile, VSI_PREASSOC_PROCESSING); + return true; + } else if (profile->mode == VDP_MODE_ASSOCIATE) { + vdp_vsi_change_bridge_state(profile, VSI_ASSOC_PROCESSING); + return true; + } + return false; + case VSI_PREASSOC_PROCESSING: + if (profile->response != VDP_RESPONSE_SUCCESS) { + vdp_vsi_change_bridge_state(profile, VSI_EXIT); + return true; + } + vdp_vsi_change_bridge_state(profile, VSI_PREASSOCIATED); + return false; + case VSI_PREASSOCIATED: + if (profile->mode == VDP_MODE_ASSOCIATE) { + vdp_vsi_change_bridge_state(profile, VSI_ASSOC_PROCESSING); + return true; + } else if (profile->mode == VDP_MODE_DEASSOCIATE ) { + vdp_vsi_change_bridge_state(profile, VSI_DEASSOC_PROCESSING); + return true; + } else if (profile->mode == VDP_MODE_PREASSOCIATE ) { + vdp_vsi_change_bridge_state(profile, VSI_PREASSOC_PROCESSING); + return true; + } + return false; + case VSI_DEASSOC_PROCESSING: + vdp_vsi_change_bridge_state(profile, VSI_EXIT); + return false; + case VSI_EXIT: + return false; + default: + LLDPAD_ERR("ERROR: The VSI RX State Machine (bridge) is broken!\n"); + log_message(MSG_ERR_RX_SM_INVALID, ""); + return false; + } +} + +/* vdp_vsi_sm_bridge - state machine for vdp bridge role + * @profile: profile for which the state is processed + * + * no return value + * + * runs the state machine for the bridge role of VDP. + */ +static void vdp_vsi_sm_bridge(struct vsi_profile *profile) +{ + struct vdp_data *vd = vdp_data(profile->port->ifname); + + vdp_vsi_set_bridge_state(profile); + do { + LLDPAD_DBG("%s(%i)-%s: bridge - %s\n", __func__, __LINE__, + profile->port->ifname, vsi_states[profile->state]); + switch(profile->state) { + case VSI_UNASSOCIATED: + break; + case VSI_ASSOC_PROCESSING: + /* TODO: vsiError = ProcRxandSetCfg(remoteTLV, localtlv, vsistate); + * if (vsiError) + * txTLV(Assoc NACK) + * else + * txTLV(Assoc ACK) + */ + break; + case VSI_ASSOCIATED: + break; + case VSI_PREASSOC_PROCESSING: + /* TODO: vsiError = ProcRxandSetCfg(remoteTLV, localtlv, vsistate); + * if (vsiError) + * txTLV(PreAssoc NACK) + * else + * txTLV(PreAssoc ACK) + */ + /* for now, we always succeed */ + profile->response = VDP_RESPONSE_SUCCESS; + LLDPAD_DBG("%s(%i)-%s: framein %p, sizein %i\n", __func__, __LINE__, + profile->port->ifname, vd->ecp.rx.framein, + vd->ecp.rx.sizein); + ecp_rx_send_ack_frame(profile->port); + break; + case VSI_PREASSOCIATED: + LLDPAD_DBG("%s(%i)-%s: \n", __func__, __LINE__, profile->port->ifname); + break; + case VSI_DEASSOC_PROCESSING: + /* TODO: txTLV(DeAssoc ACK) */ + break; + case VSI_EXIT: + vdp_remove_profile(profile); + break; + default: + LLDPAD_ERR("ERROR: The VSI RX bridge State Machine is broken!\n"); + log_message(MSG_ERR_TX_SM_INVALID, ""); + } + } while (vdp_vsi_set_bridge_state(profile) == true); + +} + +/* + * vdp_print_vsi_tlv - print the raw contents of a VSI TLV + * @tlv: the unpacked tlv which gets printed + * + * No return value + * + * used for protocol debug purposes + */ +static void vdp_print_vsi_tlv(struct unpacked_tlv *tlv) +{ + int i; + + LLDPAD_DBG("%s:type %i, length %i, info:\n", __func__, tlv->type, tlv->length); + + for (i=0; i < tlv->length; i++) { + LLDPAD_DBG("%02x ", tlv->info[i]); + if (!((i+1) % 16)) + LLDPAD_DBG("\n"); + } + + LLDPAD_DBG("\n"); +} + +/* + * vdp_validate_tlv - validates vsi tlvs + * @vdp: decoded vsi tlv + * + * Returns 0 on success, 1 on error + * + * checks the contents of an already decoded vsi tlv for inconsistencies + */ +static int vdp_validate_tlv(struct tlv_info_vdp *vdp) +{ + if (ntoh24(vdp->oui) != OUI_IEEE_8021Qbg) { + LLDPAD_DBG("vdp->oui %06x \n", ntoh24(vdp->oui)); + goto out_err; + } + + if (vdp->sub != LLDP_VDP_SUBTYPE) { + LLDPAD_DBG("vdp->sub %02x \n", vdp->sub); + goto out_err; + } + + if ((vdp->mode < VDP_MODE_PREASSOCIATE) || + (vdp->mode > VDP_MODE_DEASSOCIATE)) { + LLDPAD_DBG("Unknown mode %02x in vsi tlv !\n", vdp->mode); + goto out_err; + } + + if ((vdp->response < VDP_RESPONSE_SUCCESS) || + (vdp->response > VDP_RESPONSE_OUT_OF_SYNC)) { + LLDPAD_DBG("Unknown response %02x \n", vdp->response); + goto out_err; + } + + if (vdp->format != VDP_MACVLAN_FORMAT_1) { + LLDPAD_DBG("Unknown format %02x in vsi tlv !\n", vdp->format); + goto out_err; + } + + if (ntohs(vdp->entries) != 1) { + LLDPAD_DBG("Multiple entries %02x in vsi tlv !\n", vdp->entries); + goto out_err; + } + + return 0; + +out_err: + return 1; +} + +/* + * vdp_indicate - receive VSI TLVs from ECP + * @port: the port on which the tlv was received + * @tlv: the unpacked tlv to receive + * @ecp_mode: the mode under which the tlv was received (ACK or REQ) + * + * Returns 0 on success + * + * receives a vsi tlv and creates a profile. Take appropriate action + * depending on the role of the (receive) port + */ +int vdp_indicate(struct vdp_data *vd, struct unpacked_tlv *tlv, int ecp_mode) +{ + struct tlv_info_vdp *vdp; + struct vsi_profile *p, *profile; + struct port *port = port_find_by_name(vd->ifname); + + LLDPAD_DBG("%s(%i): indicating vdp for %s !\n", __func__, __LINE__, vd->ifname); + + if (!port) { + LLDPAD_ERR("%s(%i): port not found for %s !\n", __func__, __LINE__, vd->ifname); + goto out_err; + } + + vdp = malloc(sizeof(struct tlv_info_vdp)); + + if (!vdp) { + LLDPAD_ERR("%s(%i): unable to allocate vdp !\n", __func__, __LINE__); + goto out_err; + } + + memset(vdp, 0, sizeof(struct tlv_info_vdp)); + memcpy(vdp, tlv->info, tlv->length); + + if (vdp_validate_tlv(vdp)) { + LLDPAD_ERR("%s(%i): Invalid TLV received !\n", __func__, __LINE__); + goto out_vdp; + } + + profile = malloc(sizeof(struct vsi_profile)); + + if (!profile) { + LLDPAD_ERR("%s(%i): unable to allocate profile !\n", __func__, __LINE__); + goto out_vdp; + } + + memset(profile, 0, sizeof(struct vsi_profile)); + + profile->mode = vdp->mode; + profile->response = vdp->response; + + profile->mgrid = vdp->mgrid; + profile->id = ntoh24(vdp->id); + profile->version = vdp->version; + memcpy(&profile->instance, &vdp->instance, 16); + memcpy(&profile->mac, &vdp->mac_vlan.mac, MAC_ADDR_LEN); + profile->vlan = ntohs(vdp->mac_vlan.vlan); + + profile->port = port; + + if (vd->role == VDP_ROLE_STATION) { + /* do we have the profile already ? */ + LIST_FOREACH(p, &vd->profile_head, profile) { + if (vdp_profile_equal(p, profile)) { + LLDPAD_DBG("%s(%i): station: profile found, localChange %i ackReceived %i!\n", + __func__, __LINE__, p->localChange, p->ackReceived); + + p->ackReceived = true; + + vdp_vsi_sm_station(p); + } else { + LLDPAD_DBG("%s(%i): station: profile not found !\n", __func__, __LINE__); + /* ignore profile */ + } + } + } + + if (vd->role == VDP_ROLE_BRIDGE) { + /* do we have the profile already ? */ + LIST_FOREACH(p, &vd->profile_head, profile) { + if (vdp_profile_equal(p, profile)) { + break; + } + } + + if (p) { + LLDPAD_DBG("%s(%i): bridge: profile found !\n", __func__, __LINE__); + } else { + LLDPAD_DBG("%s(%i): bridge: profile not found !\n", __func__, __LINE__); + /* put it in the list */ + profile->state = VSI_UNASSOCIATED; + LIST_INSERT_HEAD(&vd->profile_head, profile, profile ); + } + + vdp_vsi_sm_bridge(profile); + } + + return 0; + +out_vdp: + free(vdp); +out_err: + LLDPAD_ERR("%s(%i): error !\n", __func__, __LINE__); + return 1; + +} + +/* + * vdp_bld_vsi_tlv - build the VDP VSI TLV + * @vd: vdp_data structure for this port + * @profile: profile the vsi tlv is created from + * + * Returns 0 on success, ENOMEM otherwise + * + * creates a vdp structure from an existing profile + */ +static int vdp_bld_vsi_tlv(struct vdp_data *vd, struct vsi_profile *profile) +{ + int rc = 0; + struct unpacked_tlv *tlv = NULL; + struct tlv_info_vdp vdp; + + FREE_UNPKD_TLV(vd, vdp); + + memset(&vdp, 0, sizeof(vdp)); + + hton24(vdp.oui, OUI_IEEE_8021Qbg); + vdp.sub = LLDP_VDP_SUBTYPE; + vdp.mode = profile->mode; + vdp.response = 0; + vdp.mgrid = profile->mgrid; + hton24(vdp.id, profile->id); + vdp.version = profile->version; + memcpy(&vdp.instance,&profile->instance, 16); + vdp.format = VDP_MACVLAN_FORMAT_1; + vdp.entries = htons(1); + memcpy(&vdp.mac_vlan.mac,&profile->mac, MAC_ADDR_LEN); + vdp.mac_vlan.vlan = htons(profile->vlan); + + tlv = create_tlv(); + if (!tlv) + goto out_err; + + tlv->type = ORG_SPECIFIC_TLV; + tlv->length = sizeof(vdp); + tlv->info = (u8 *)malloc(tlv->length); + if(!tlv->info) { + free(tlv); + tlv = NULL; + rc = ENOMEM; + goto out_err; + } + memcpy(tlv->info, &vdp, tlv->length); + + vd->vdp = tlv; + +out_err: + return rc; +} + +/* vdp_bld_tlv - builds a tlv from a profile + * @vd: vdp_data structure for this port + * @profile: profile the vsi tlv is created from + * + * returns 0 on success, != 0 on error + * + * wrapper function around vdp_bld_vsi_tlv. adds some checks and calls + * vdp_bld_vsi_tlv. +*/ + +static int vdp_bld_tlv(struct vdp_data *vd, struct vsi_profile *profile) +{ + int rc = 0; + + if (!port_find_by_name(vd->ifname)) { + rc = EEXIST; + goto out_err; + } + + if (!init_cfg()) { + rc = ENOENT; + goto out_err; + } + + if (vdp_bld_vsi_tlv(vd, profile)) { + LLDPAD_ERR("%s:%s:vdp_bld_vsi_tlv() failed\n", + __func__, vd->ifname); + rc = EINVAL; + goto out_err_destroy; + } + +out_err_destroy: + destroy_cfg(); + +out_err: + return rc; +} + +/* vdp_gettlv - get the tlv for a profile + * @port: the port on which the tlv was received + * @profile: profile the vsi tlv is created from + * + * returns 0 on success + * + * this is the interface function called from ecp_build_ECPDU. It returns the + * packed tlv for a profile. + */ +struct packed_tlv *vdp_gettlv(struct vdp_data *vd, struct vsi_profile *profile) +{ + int size; + struct packed_tlv *ptlv = NULL; + + /* frees the unpacked_tlv in vdp_data + * also done in vdp_bld_vsi_tlv */ + vdp_free_tlv(vd); + + if (vdp_bld_tlv(vd, profile)) { + LLDPAD_ERR("%s:%s vdp_bld_tlv failed\n", + __func__, vd->ifname); + goto out_err; + } + + size = TLVSIZE(vd->vdp); + + if (!size) { + LLDPAD_ERR("%s(%i): size %i of unpacked_tlv not correct !\n", __func__, __LINE__, + size); + goto out_err; + } + + ptlv = create_ptlv(); + if (!ptlv) + goto out_err; + + ptlv->tlv = malloc(size); + if (!ptlv->tlv) + goto out_free; + + ptlv->size = 0; + PACK_TLV_AFTER(vd->vdp, ptlv, size, out_free); + + return ptlv; + +out_free: + ptlv = free_pkd_tlv(ptlv); +out_err: + LLDPAD_ERR("%s:%s: failed\n", __func__, vd->ifname); + return NULL; +} + +/* vdp_profile_equal - checks for equality of 2 profiles + * @p1: profile 1 + * @p2: profile 2 + * + * returns 1 on success, 0 on error + * + * compares mgrid, id, version, instance, mac and vlan of 2 profiles to find + * out if they are equal. + */ +int vdp_profile_equal(struct vsi_profile *p1, struct vsi_profile *p2) +{ + if (p1->mgrid != p2->mgrid) + return 0; + + if (p1->id != p2->id) + return 0; + + if (p1->version != p2->version) + return 0; + + if (memcmp(p1->instance, p2->instance, 16)) + return 0; + + if (memcmp(p1->mac, p2->mac, MAC_ADDR_LEN)) + return 0; + + if (p1->vlan != p2->vlan) + return 0; + + return 1; +} + +/* vdp_add_profile - adds a profile to a per port list + * @profile: profile to add + * + * returns the profile that has been found or added, NULL otherwise. + * + * main interface function which adds a profile to a list kept on a per-port + * basis. Checks if the profile is already in the list, adds it if necessary. + */ +struct vsi_profile *vdp_add_profile(struct vsi_profile *profile) +{ + struct vsi_profile *p; + struct vdp_data *vd; + + LLDPAD_DBG("%s(%i): adding vdp profile for %s !\n", __func__, __LINE__, + profile->port->ifname); + + vd = vdp_data(profile->port->ifname); + if (!vd) { + LLDPAD_ERR("%s(%i): Could not find vdp_data for %s !\n", __func__, __LINE__, + profile->port->ifname); + return NULL; + } + + vdp_print_profile(profile); + + /* loop over all existing profiles and check wether + * one for this combination already exists. If yes, check, + * if the MAC/VLAN pair already exists. If not, add it. + * Note: currently only one MAC/VLAN pair supported ! */ + LIST_FOREACH(p, &vd->profile_head, profile) { + if (p) { + if (vdp_profile_equal(p, profile)) { + if (p->mode == profile->mode) { + LLDPAD_DBG("%s(%i): profile already exists, ignoring !\n", + __func__, __LINE__); + return NULL; + } else { + LLDPAD_DBG("%s(%i): taking new mode !\n", __func__, + __LINE__); + p->mode = profile->mode; + return p; + } + } + } + } + + LIST_INSERT_HEAD(&vd->profile_head, profile, profile ); + + return profile; +} + +/* vdp_remove_profile - remove a profile from a per port list + * @profile: profile to remove + * + * returns 0 if removal was successful, -1 if removal failed + * + * function used in the state machines to remove a profile from a list kept on + * a per-port basis. Checks if the profile is in the list, removes it if there. + */ +int vdp_remove_profile(struct vsi_profile *profile) +{ + struct vsi_profile *p; + struct vdp_data *vd; + + LLDPAD_DBG("%s(%i): removing vdp profile on %s !\n", __func__, __LINE__, + profile->port->ifname); + + vd = vdp_data(profile->port->ifname); + if (!vd) { + LLDPAD_ERR("%s(%i): Could not find vdp_data for %s !\n", __func__, __LINE__, + profile->port->ifname); + return -1; + } + + /* loop over all existing profiles and check wether + * it exists. If yes, remove it. */ + LIST_FOREACH(p, &vd->profile_head, profile) { + if (p) { + vdp_print_profile(p); + if (vdp_profile_equal(p, profile)) { + LIST_REMOVE(p, profile); + free(p); + } + } else { + return -1; + } + } + + return 0; +} + +/* vdp_ifdown - tear down vdp structures for a interface + * @ifname: name of the interface + * + * no return value + * + * interface function to lldpad. tears down vdp specific structures if + * interface "ifname" goes down. + */ +void vdp_ifdown(char *ifname) +{ + struct vdp_data *vd; + struct vsi_profile *p; + + LLDPAD_DBG("%s called on interface %s !\n", __func__, ifname); + + vd = vdp_data(ifname); + if (!vd) + goto out_err; + + if (ecp_deinit(ifname)) + goto out_err; + + LIST_REMOVE(vd, entry); + + LIST_FOREACH(p, &vd->profile_head, profile) { + vdp_stop_ackTimer(p); + LIST_REMOVE(p, profile); + free(p); + } + vdp_free_tlv(vd); + free(vd); + LLDPAD_INFO("%s:%s vdp data removed\n", __func__, ifname); + return; +out_err: + LLDPAD_ERR("%s:%s vdp data remove failed\n", __func__, ifname); + + return; +} + +/* vdp_ifup - build up vdp structures for a interface + * @ifname: name of the interface + * + * no return value + * + * interface function to lldpad. builds up vdp specific structures if + * interface "ifname" goes down. + */ +void vdp_ifup(char *ifname) +{ + char *p; + struct vdp_data *vd; + struct vdp_user_data *ud; + + LLDPAD_DBG("%s(%i): starting VDP for if %s !\n", __func__, __LINE__, ifname); + + vd = vdp_data(ifname); + if (vd) { + LLDPAD_WARN("%s:%s vdp data already exists !\n", __func__, ifname); + return; + } + + /* not found, alloc/init per-port module data */ + vd = (struct vdp_data *) calloc(1, sizeof(struct vdp_data)); + if (!vd) { + LLDPAD_ERR("%s:%s malloc %ld failed\n", + __func__, ifname, sizeof(*vd)); + goto out_err; + } + strncpy(vd->ifname, ifname, IFNAMSIZ); + + if (!init_cfg()) { + free(vd); + goto out_err; + } + + vd->role = VDP_ROLE_STATION; + + if (!get_cfg(ifname, "vdp.role", (void *)&p, + CONFIG_TYPE_STRING)) { + if (!strcasecmp(p, VAL_BRIDGE)) { + vd->role = VDP_ROLE_BRIDGE; + } + } + + LLDPAD_DBG("%s: configured for %s mode !\n", ifname, + (vd->role ==VDP_ROLE_BRIDGE) ? "bridge" : "station"); + + LIST_INIT(&vd->profile_head); + + ud = find_module_user_data_by_if(ifname, &lldp_head, LLDP_MOD_VDP); + LIST_INSERT_HEAD(&ud->head, vd, entry); + + ecp_init(ifname); + + LLDPAD_DBG("%s:%s vdp added\n", __func__, ifname); + return; + +out_err: + LLDPAD_ERR("%s:%s vdp adding failed\n", __func__, ifname); + return; +} + +static const struct lldp_mod_ops vdp_ops = { + .lldp_mod_register = vdp_register, + .lldp_mod_unregister = vdp_unregister, + .lldp_mod_ifup = vdp_ifup, + .lldp_mod_ifdown = vdp_ifdown, + .get_arg_handler = vdp_get_arg_handlers, +}; + +/* vdp_register - register vdp module to lldpad + * @none + * + * returns lldp_module struct on success, NULL on error + * + * allocates a module structure with vdp module information and returns it + * to lldpad. + */ +struct lldp_module *vdp_register(void) +{ + struct lldp_module *mod; + struct vdp_user_data *ud; + + mod = malloc(sizeof(*mod)); + if (!mod) { + LLDPAD_ERR("failed to malloc module data\n"); + log_message(MSG_ERR_SERVICE_START_FAILURE, + "%s", "failed to malloc module data"); + goto out_err; + } + ud = malloc(sizeof(struct vdp_user_data)); + if (!ud) { + free(mod); + LLDPAD_ERR("failed to malloc module user data\n"); + log_message(MSG_ERR_SERVICE_START_FAILURE, + "%s", "failed to malloc module user data"); + goto out_err; + } + LIST_INIT(&ud->head); + mod->id = LLDP_MOD_VDP; + mod->ops = &vdp_ops; + mod->data = ud; + LLDPAD_DBG("%s:done\n", __func__); + return mod; + +out_err: + LLDPAD_ERR("%s:failed\n", __func__); + return NULL; +} + +/* vdp_unregister - unregister vdp module from lldpad + * @none + * + * no return value + * + * frees vdp module structure. + */ +void vdp_unregister(struct lldp_module *mod) +{ + if (mod->data) { + vdp_free_data((struct vdp_user_data *) mod->data); + free(mod->data); + } + free(mod); + LLDPAD_DBG("%s:done\n", __func__); +} + + diff --git a/lldpad.c b/lldpad.c index 571da31..c0938af 100644 --- a/lldpad.c +++ b/lldpad.c @@ -50,6 +50,7 @@ #include "lldp_med.h" #include "lldp_8023.h" #include "lldp_evb.h" +#include "lldp_vdp.h" #include "config.h" #include "lldpad_shm.h" #include "clif.h" @@ -65,6 +66,7 @@ struct lldp_module *(*register_tlv_table[])(void) = { med_register, ieee8023_register, evb_register, + vdp_register, NULL, }; -- 1.7.1
This patch implements the command line interface to control the VDP module. In station role, it allows to register a new VSI (guest interface) profile and query its state. In bridge role, it allows to query the state of all registered profiles. With lldptool it is possible to set the role from default "station" to "bridge". The configuration of the role for a port is saved to lldpads config file. Signed-off-by: Jens Osterkamp <jens at linux.vnet.ibm.com> --- Makefile.am | 12 +- include/lldp_vdp_clif.h | 39 +++++ include/lldp_vdp_cmds.h | 44 +++++ lldp_vdp_clif.c | 136 ++++++++++++++++ lldp_vdp_cmds.c | 410 +++++++++++++++++++++++++++++++++++++++++++++++ lldptool.c | 2 + 6 files changed, 638 insertions(+), 5 deletions(-) create mode 100644 include/lldp_vdp_clif.h create mode 100644 include/lldp_vdp_cmds.h create mode 100644 lldp_vdp_clif.c create mode 100644 lldp_vdp_cmds.c diff --git a/Makefile.am b/Makefile.am index 4b69389..9a3baf4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -37,7 +37,8 @@ lldpad_include_HEADERS = include/dcb_types.h include/dcbtool.h \ include/dcb_osdep.h include/clif.h include/lldp_dcbx_cmds.h include/common.h \ include/lldpad.h include/os.h include/includes.h include/lldp_mand_cmds.h \ include/clif_msgs.h include/lldp_basman_cmds.h include/lldp_8023_cmds.h \ -include/lldp_med_cmds.h include/lldp_dcbx_cfg.h include/lldp_evb_cmds.h +include/lldp_med_cmds.h include/lldp_dcbx_cfg.h include/lldp_evb_cmds.h \ +include/lldp_vdp_cmds.h noinst_HEADERS = include/config.h include/ctrl_iface.h \ include/dcb_driver_if_types.h include/dcb_driver_interface.h \ @@ -47,7 +48,7 @@ include/event_iface.h include/messages.h include/parse_cli.h include/version.h \ include/lldptool_cli.h include/list.h \ include/lldp_mand_clif.h include/lldp_basman_clif.h include/lldp_med_clif.h \ include/lldp_8023_clif.h include/lldp_dcbx_clif.h include/lldptool.h \ -include/lldp_rtnl.h include/lldp_evb_clif.h +include/lldp_rtnl.h include/lldp_evb_clif.h include/lldp_vdp_clif.h lldpad_SOURCES = lldpad.c config.c drv_cfg.c ctrl_iface.c event_iface.c eloop.c \ common.c os_unix.c lldp_dcbx_cmds.c log.c lldpad_shm.c \ @@ -64,20 +65,21 @@ lldp_dcbx_cfg.c include/lldp_dcbx_cfg.h \ lldp_util.c include/lldp_util.h \ lldp_mand.c include/lldp_mand.h \ lldp_mand_cmds.c lldp_basman_cmds.c lldp_8023_cmds.c lldp_med_cmds.c \ -lldp_evb_cmds.c \ +lldp_evb_cmds.c lldp_vdp_cmds.c \ lldp_tlv.c include/lldp_tlv.h \ lldp_basman.c include/lldp_basman.h \ lldp_med.c include/lldp_med.h \ lldp_8023.c include/lldp_8023.h \ lldp_evb.c include/lldp_evb.h \ -lldp_vdp.c include/lldp_vdp.h +lldp_vdp.c include/lldp_vdp.h \ +lldp_vdp_cmds.h dcbtool_SOURCES = dcbtool.c clif.c dcbtool_cmds.c parse_cli.l \ $(lldpad_include_HEADERS) $(noinst_HEADERS) lldptool_SOURCES = lldptool.c clif.c lldptool_cmds.c common.c os_unix.c \ lldp_mand_clif.c lldp_basman_clif.c lldp_med_clif.c lldp_8023_clif.c \ -lldp_dcbx_clif.c lldp_evb_clif.c $(lldpad_include_HEADERS) \ +lldp_dcbx_clif.c lldp_evb_clif.c lldp_vdp_clif.c $(lldpad_include_HEADERS) \ $(noinst_HEADERS) nltest_SOURCES = nltest.c nltest.h diff --git a/include/lldp_vdp_clif.h b/include/lldp_vdp_clif.h new file mode 100644 index 0000000..adb6333 --- /dev/null +++ b/include/lldp_vdp_clif.h @@ -0,0 +1,39 @@ +/******************************************************************************* + + implementation of VDP according to IEEE 802.1Qbg + (c) Copyright IBM Corp. 2010 + + Author(s): Jens Osterkamp <jens at linux.vnet.ibm.com> + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + +*******************************************************************************/ + +#ifndef _LLDP_VDP_CLIF_H +#define _LLDP_VDP_CLIF_H + +struct lldp_module *vdp_cli_register(void); +void vdp_cli_unregister(struct lldp_module *); +int vdp_print_tlv(u32, u16, char *); + +#define VDP_BUF_SIZE 256 + +#define VDP_PREFIX "vdp" +#define ARG_VDP_MODE "mode" +#define ARG_VDP_ROLE "role" + +#endif diff --git a/include/lldp_vdp_cmds.h b/include/lldp_vdp_cmds.h new file mode 100644 index 0000000..5e64c27 --- /dev/null +++ b/include/lldp_vdp_cmds.h @@ -0,0 +1,44 @@ +/******************************************************************************* + + implementation of VDP according to IEEE 802.1Qbg + (c) Copyright IBM Corp. 2010 + + Author(s): Jens Osterkamp <jens at linux.vnet.ibm.com> + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + +*******************************************************************************/ + +#ifndef _LLDP_VDP_CMDS_H +#define _LLDP_VDP_CMDS_H + +struct arg_handlers *vdp_get_arg_handlers(); + +enum { + MODE = 0, + MGRID, + TYPEID, + TYPEIDVERSION, + INSTANCEID, + MAC, + VLAN, +}; + +#define VAL_STATION "station" +#define VAL_BRIDGE "bridge" + +#endif diff --git a/lldp_vdp_clif.c b/lldp_vdp_clif.c new file mode 100644 index 0000000..aa357e9 --- /dev/null +++ b/lldp_vdp_clif.c @@ -0,0 +1,136 @@ +/******************************************************************************* + + implementation of VDP according to IEEE 802.1Qbg + (c) Copyright IBM Corp. 2010 + + Author(s): Jens Osterkamp <jens at linux.vnet.ibm.com> + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + +*******************************************************************************/ + +#include "includes.h" +#include "common.h" +#include <stdio.h> +#include <syslog.h> +#include <sys/un.h> +#include <sys/stat.h> +#include "lldp_mod.h" +#include "lldptool.h" +#include "lldp.h" +#include "lldp_vdp.h" +#include "lldp_vdp_clif.h" + +void vdp_print_cfg_tlv(u16, char *info); +int vdp_print_help(); + +u32 vdp_lookup_tlv_name(char *tlvid_str); + +static const struct lldp_mod_ops vdp_ops_clif = { + .lldp_mod_register = vdp_cli_register, + .lldp_mod_unregister = vdp_cli_unregister, + .print_tlv = vdp_print_tlv, + .lookup_tlv_name = vdp_lookup_tlv_name, + .print_help = vdp_print_help, +}; + +struct type_name_info vdp_tlv_names[] = { + { ((LLDP_MOD_VDP) << 8) | LLDP_VDP_SUBTYPE, + "VDP protocol configuration", + "vdp", vdp_print_cfg_tlv }, + { INVALID_TLVID, NULL, NULL } +}; + +int vdp_print_help() +{ + struct type_name_info *tn = &vdp_tlv_names[0]; + + while (tn->type != INVALID_TLVID) { + if (tn->key && strlen(tn->key) && tn->name) { + printf(" %s", tn->key); + if (strlen(tn->key)+3 <= 8) + printf("\t"); + printf("\t: %s\n", tn->name); + } + tn++; + } + + return 0; +} + +struct lldp_module *vdp_cli_register(void) +{ + struct lldp_module *mod; + + mod = malloc(sizeof(*mod)); + if (!mod) { + fprintf(stderr, "failed to malloc module data\n"); + return NULL; + } + mod->id = LLDP_MOD_VDP; + mod->ops = &vdp_ops_clif; + + return mod; +} + +void vdp_cli_unregister(struct lldp_module *mod) +{ + free(mod); +} + +void vdp_print_cfg_tlv(u16 len, char *info) +{ + /* TODO: this should print out all associated VSI mac/vlan pairs */ + printf("This should print out all associated VSI mac/vlan pairs !\n"); + + return; +} + +/* return 1: if it printed the TLV + * 0: if it did not + */ +int vdp_print_tlv(u32 tlvid, u16 len, char *info) +{ + struct type_name_info *tn = &vdp_tlv_names[0]; + + while (tn->type != INVALID_TLVID) { + if (tlvid == tn->type) { + printf("%s\n", tn->name); + if (tn->print_info) { + printf("\t"); + tn->print_info(len-4, info); + } + return 1; + } + tn++; + } + + return 0; +} + +u32 vdp_lookup_tlv_name(char *tlvid_str) +{ + struct type_name_info *tn = &vdp_tlv_names[0]; + + while (tn->type != INVALID_TLVID) { + if (!strcasecmp(tn->key, tlvid_str)) + return tn->type; + tn++; + } + return INVALID_TLVID; +} + diff --git a/lldp_vdp_cmds.c b/lldp_vdp_cmds.c new file mode 100644 index 0000000..9416a9e --- /dev/null +++ b/lldp_vdp_cmds.c @@ -0,0 +1,410 @@ +/****************************************************************************** + + implementation of VDP according to IEEE 802.1Qbg + (c) Copyright IBM Corp. 2010 + + Author(s): Jens Osterkamp <jens at linux.vnet.ibm.com> + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + +*******************************************************************************/ + +#include "includes.h" +#include "common.h" +#include <stdio.h> +#include <syslog.h> +#include <sys/un.h> +#include <sys/stat.h> +#include <arpa/inet.h> +#include "lldpad.h" +#include "ctrl_iface.h" +#include "lldp.h" +#include "lldp_vdp.h" +#include "lldp_mand_clif.h" +#include "lldp_vdp_clif.h" +#include "lldp_vdp_cmds.h" +#include "lldp/ports.h" +#include "messages.h" +#include "libconfig.h" +#include "config.h" +#include "clif_msgs.h" +#include "lldp/states.h" + +static int get_arg_tlvtxenable(struct cmd *, char *, char *, char *); +static int set_arg_tlvtxenable(struct cmd *, char *, char *, char *); + +static int get_arg_mode(struct cmd *, char *, char *, char *); +static int set_arg_mode(struct cmd *, char *, char *, char *); + +static int get_arg_role(struct cmd *, char *, char *, char *); +static int set_arg_role(struct cmd *, char *, char *, char *); + +static struct arg_handlers arg_handlers[] = { + { ARG_VDP_MODE, get_arg_mode, set_arg_mode }, + { ARG_VDP_ROLE, get_arg_role, set_arg_role }, + { ARG_TLVTXENABLE, get_arg_tlvtxenable, set_arg_tlvtxenable }, + { NULL } +}; + +static int get_arg_tlvtxenable(struct cmd *cmd, char *arg, char *argvalue, + char *obuf) +{ + int value; + char *s; + char arg_path[VDP_BUF_SIZE]; + + if (cmd->cmd != cmd_gettlv) + return cmd_invalid; + + switch (cmd->tlvid) { + case ((LLDP_MOD_VDP) << 8) | LLDP_VDP_SUBTYPE: + snprintf(arg_path, sizeof(arg_path), "%s.%s", + VDP_PREFIX, arg); + + if (get_cfg(cmd->ifname, arg_path, (void *)&value, + CONFIG_TYPE_BOOL)) + value = false; + break; + case INVALID_TLVID: + return cmd_invalid; + default: + return cmd_not_applicable; + } + + if (value) + s = VAL_YES; + else + s = VAL_NO; + + sprintf(obuf, "%02x%s%04x%s", (unsigned int) strlen(arg), arg, + (unsigned int) strlen(s), s); + + return cmd_success; +} + +static int set_arg_tlvtxenable(struct cmd *cmd, char *arg, char *argvalue, + char *obuf) +{ + int value; + char arg_path[VDP_BUF_SIZE]; + + if (cmd->cmd != cmd_settlv) + return cmd_invalid; + + switch (cmd->tlvid) { + case ((LLDP_MOD_VDP) << 8) | LLDP_VDP_SUBTYPE: + break; + case INVALID_TLVID: + return cmd_invalid; + default: + return cmd_not_applicable; + } + + if (!strcasecmp(argvalue, VAL_YES)) + value = 1; + else if (!strcasecmp(argvalue, VAL_NO)) + value = 0; + else + return cmd_invalid; + + snprintf(arg_path, sizeof(arg_path), "%s.%s", VDP_PREFIX, arg); + + if (set_cfg(cmd->ifname, arg_path, (void *)&value, CONFIG_TYPE_BOOL)) + return cmd_failed; + + return cmd_success; +} + +static int get_arg_mode(struct cmd *cmd, char *arg, char *argvalue, + char *obuf) +{ + char *s, *t; + struct vsi_profile *np; + struct vdp_data *vd; + int count=0; + + if (cmd->cmd != cmd_gettlv) + return cmd_invalid; + + switch (cmd->tlvid) { + case ((LLDP_MOD_VDP) << 8) | LLDP_VDP_SUBTYPE: + break; + case INVALID_TLVID: + return cmd_invalid; + default: + return cmd_not_applicable; + } + + vd = vdp_data(cmd->ifname); + if (!vd) { + LLDPAD_ERR("%s(%i): vdp_data for %s not found !\n", __func__, __LINE__, + cmd->ifname); + free(t); + return cmd_invalid; + } + + LIST_FOREACH(np, &vd->profile_head, profile) { + count++; + } + + s = t = malloc(count*VDP_BUF_SIZE); + if (!s) + return cmd_invalid; + memset(s, 0, count*VDP_BUF_SIZE); + + LIST_FOREACH(np, &vd->profile_head, profile) { + PRINT_PROFILE(t, np); + } + + sprintf(obuf, "%02x%s%04x%s", (unsigned int) strlen(arg), arg, + (unsigned int) strlen(s), s); + + free(s); + + return cmd_success; +} + +static void str2instance(struct vsi_profile *profile, char *buffer) +{ + int i, j = 0; + + for(i=0; i <= strlen(buffer); i++) { + if (buffer[i] == '-') { + continue; + } + + if ((sscanf(&buffer[i], "%02x", &profile->instance[j]) == 1) || + (sscanf(&buffer[i], "%02X", &profile->instance[j]) == 1)) { + i++; + j++; + continue; + } + } + +} + +/* INSTANCE_STRLEN = strlen("fa9b7fff-b0a0-4893e-beef4ff18f8f") */ +#define INSTANCE_STRLEN 32 + +int instance2str(const u8 *p, char *dst, size_t size) +{ + if (dst && size > INSTANCE_STRLEN) { + snprintf(dst, size, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + p[0], p[1], p[2], p[3], + p[4], p[5], p[6], p[7], + p[8], p[9], p[10], p[11], + p[12], p[13], p[14], p[15]); + return 0; + } + return -1; +} + +static void vdp_fill_profile(struct vsi_profile *profile, char *buffer, int field) +{ + switch(field) { + case MODE: + profile->mode = atoi(buffer); + break; + case MGRID: + profile->mgrid = atoi(buffer); + break; + case TYPEID: + profile->id = atoi(buffer); + break; + case TYPEIDVERSION: + profile->version = atoi(buffer); + break; + case INSTANCEID: + str2instance(profile, buffer); + break; + case MAC: + str2mac(buffer, &profile->mac[0], MAC_ADDR_LEN); + break; + case VLAN: + profile->vlan = atoi(buffer); + break; + default: + LLDPAD_ERR("Unknown field in buffer !\n"); + break; + } +} + +static struct vsi_profile *vdp_parse_mode_line(char * argvalue) +{ + int i, arglen, field; + char *cmdstring, *buf; + char *buffer; + struct vsi_profile *profile; + + profile = malloc(sizeof(struct vsi_profile)); + if (!profile) + return NULL; + memset(profile, 0, sizeof(struct vsi_profile)); + + arglen = strlen(argvalue); + cmdstring = argvalue; + buffer = malloc(arglen); + if (!buffer) + goto out_free; + buf = buffer; + field = 0; + + for (i=0; i <= arglen; i++) { + *buffer = *cmdstring; + + if ((*cmdstring == ',') || (*cmdstring == '\0')) { + *buffer++ = '\0'; + vdp_fill_profile(profile, buf, field); + field++; + buffer = buf; + memset(buffer, 0, arglen); + cmdstring++; + continue; + } + + buffer++; + cmdstring++; + } + + free(buffer); + + return profile; + +out_free: + free(profile); + return NULL; +} + +static int set_arg_mode(struct cmd *cmd, char *arg, char *argvalue, + char *obuf) +{ + int arglen; + struct vsi_profile *profile, *p; + + arglen = strlen(argvalue); + + if (cmd->cmd != cmd_settlv) + return cmd_invalid; + + switch (cmd->tlvid) { + case ((LLDP_MOD_VDP) << 8) | LLDP_VDP_SUBTYPE: + break; + case INVALID_TLVID: + return cmd_invalid; + default: + return cmd_not_applicable; + } + + profile = vdp_parse_mode_line(argvalue); + profile->port = port_find_by_name(cmd->ifname); + + if (!profile->port) { + free(profile); + return cmd_invalid; + } + + p = vdp_add_profile(profile); + + if (!p) { + free(profile); + return cmd_invalid; + } + + vdp_somethingChangedLocal(profile, VDP_PROFILE_REQ); + vdp_vsi_sm_station(p); + + return cmd_success; +} + +static int get_arg_role(struct cmd *cmd, char *arg, char *argvalue, + char *obuf) +{ + char *p; + char arg_path[VDP_BUF_SIZE]; + + if (cmd->cmd != cmd_gettlv) + return cmd_invalid; + + switch (cmd->tlvid) { + case ((LLDP_MOD_VDP) << 8) | LLDP_VDP_SUBTYPE: + snprintf(arg_path, sizeof(arg_path), "%s.%s", + VDP_PREFIX, arg); + + if (get_cfg(cmd->ifname, arg_path, (void *)&p, + CONFIG_TYPE_STRING)) + return cmd_failed; + break; + case INVALID_TLVID: + return cmd_invalid; + default: + return cmd_not_applicable; + } + + sprintf(obuf, "%02x%s%04x%s", (unsigned int) strlen(arg), arg, + (unsigned int) strlen(p), p); + + return cmd_success; +} + +static int set_arg_role(struct cmd *cmd, char *arg, char *argvalue, + char *obuf) +{ + struct vdp_data *vd; + char arg_path[VDP_BUF_SIZE]; + + if (cmd->cmd != cmd_settlv) + return cmd_invalid; + + switch (cmd->tlvid) { + case ((LLDP_MOD_VDP) << 8) | LLDP_VDP_SUBTYPE: + break; + case INVALID_TLVID: + return cmd_invalid; + default: + return cmd_not_applicable; + } + + vd = vdp_data(cmd->ifname); + + if (!vd) { + LLDPAD_ERR("%s(%i): could not find vdp_data for %s !\n", + __FILE__, __LINE__, cmd->ifname); + return cmd_invalid; + } + + if (!strcasecmp(argvalue, VAL_BRIDGE)) { + vd->role = VDP_ROLE_BRIDGE; + } else if (!strcasecmp(argvalue, VAL_STATION)) { + vd->role = VDP_ROLE_STATION; + } else { + return cmd_invalid; + } + + snprintf(arg_path, sizeof(arg_path), "%s.%s", VDP_PREFIX, arg); + + char *p = &argvalue[0]; + if (set_cfg(cmd->ifname, arg_path, (void *)&p, CONFIG_TYPE_STRING)) + return cmd_failed; + + return cmd_success; +} + + +struct arg_handlers *vdp_get_arg_handlers() +{ + return &arg_handlers[0]; +} diff --git a/lldptool.c b/lldptool.c index 7e166fe..bfed101 100644 --- a/lldptool.c +++ b/lldptool.c @@ -40,6 +40,7 @@ #include "lldp_8023_clif.h" #include "lldp_dcbx_clif.h" #include "lldp_evb_clif.h" +#include "lldp_vdp_clif.h" #include "lldptool.h" #include "lldptool_cli.h" #include "lldp_mod.h" @@ -158,6 +159,7 @@ struct lldp_module *(*register_tlv_table[])(void) = { med_cli_register, dcbx_cli_register, evb_cli_register, + vdp_cli_register, NULL, }; -- 1.7.1
Signed-off-by: Jens Osterkamp <jens at linux.vnet.ibm.com> --- configure.ac | 5 +++++ 1 files changed, 5 insertions(+), 0 deletions(-) diff --git a/configure.ac b/configure.ac index 619c6c4..8b9d08a 100644 --- a/configure.ac +++ b/configure.ac @@ -12,6 +12,11 @@ PKG_CHECK_MODULES(LIBCONFIG, libconfig, AC_SUBST(LIBCONFIG_CFLAGS) AC_SUBST(LIBCONFIG_LIBS) +PKG_CHECK_MODULES(LIBNL, libnl-1 >= 1.1) +AC_SUBST(LIBNL_CFLAGS) +AC_SUBST(LIBNL_LIBS) + +AC_CHECK_LIB(nl, rtnl_link_get_by_name) AC_CONFIG_SUBDIRS([libconfig-1.3.2]) AC_CONFIG_FILES([Makefile include/version.h lldpad.spec lldpad.pc dcbd.pc]) AC_OUTPUT -- 1.7.1
modifies the setup of the netlink socket in drv_cfg.c to use pid 0 instead of the processes pid. Also replaces bind with connect to allow the reception of netlink messages from libvirt. Preparation patch for communication between libvirt and lldpad. Signed-off-by: Gerhard Stenzel <gstenzel at linux.vnet.ibm.com> --- drv_cfg.c | 5 +++-- 1 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drv_cfg.c b/drv_cfg.c index 6c02555..23c11f3 100644 --- a/drv_cfg.c +++ b/drv_cfg.c @@ -71,9 +71,9 @@ static int init_socket(void) memset((void *)&snl, 0, sizeof(struct sockaddr_nl)); snl.nl_family = AF_NETLINK; - snl.nl_pid = getpid(); + snl.nl_pid = 0; - if (bind(sd, (struct sockaddr *)&snl, sizeof(struct sockaddr_nl)) < 0) { + if (connect(sd, (struct sockaddr *)&snl, sizeof(struct sockaddr_nl)) < 0) { close(sd); return -EIO; } @@ -195,6 +195,7 @@ static struct nlmsghdr *get_msg(unsigned int seq) nlh = NULL; break; } + fprintf(stderr, "%s:%s: nlmsg_type: %d\n", __FILE__, __FUNCTION__, nlh->nlmsg_type); if ((nlh->nlmsg_type == RTM_GETDCB || nlh->nlmsg_type == RTM_SETDCB) && nlh->nlmsg_seq == seq) { -- 1.7.1
Jens Osterkamp
2010-Nov-08 16:11 UTC
[PATCH 8/9] lldpad support for libvirt netlink message
This code receives a IEEE 802.1Qbg virtual station instance from libvirt in a SETLINK message. The parsed VSI is then handed over to VDP for processing. The VDP state machine processes the VSI while libvirt polls the result using GETLINK. Requires at least Linux kernel 2.6.35-rc1. Signed-off-by: Jens Osterkamp <jens at linux.vnet.ibm.com> Signed-off-by: Gerhard Stenzel <gstenzel at linux.vnet.ibm.com> Signed-off-by: Stefan Berger <stefanb at linux.vnet.ibm.com> --- event_iface.c | 600 ++++++++++++++++++++++++++++++++++++++++++++++++- include/event_iface.h | 1 + include/lldp_vdp.h | 2 + lldp_vdp.c | 2 +- lldpad.c | 10 + 5 files changed, 604 insertions(+), 11 deletions(-) diff --git a/event_iface.c b/event_iface.c index 439e4d0..00ac3b1 100644 --- a/event_iface.c +++ b/event_iface.c @@ -3,6 +3,13 @@ LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2010 Intel Corporation. + implementation of libvirt netlink interface + (c) Copyright IBM Corp. 2010 + + Author(s): Jens Osterkamp <jens at linux.vnet.ibm.com> + Stefan Berger <stefanb at linux.vnet.ibm.com> + Gerhard Stenzel <gstenzel at linux.vnet.ibm.com> + This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. @@ -31,12 +38,16 @@ #include <sys/socket.h> #include <linux/rtnetlink.h> #include <linux/if.h> +#include <linux/if_link.h> #include <linux/if_vlan.h> #include <linux/sockios.h> #include <syslog.h> #include <unistd.h> +#include <linux/netlink.h> +#include <netlink/msg.h> #include "lldpad.h" #include "lldp_mod.h" +#include "lldp_vdp.h" #include "common.h" #include "eloop.h" #include "drv_cfg.h" @@ -47,6 +58,7 @@ #include "lldp/l2_packet.h" #include "config.h" #include "lldp/states.h" +#include "messages.h" static void event_if_decode_rta(int type, struct rtattr *rta); @@ -55,6 +67,32 @@ static void event_if_decode_rta(int type, struct rtattr *rta); static char *device_name = NULL; static int link_status = 0; +static struct nla_policy ifla_vf_policy[IFLA_VF_MAX + 1] +{ + [IFLA_VF_MAC] = { .minlen = sizeof(struct ifla_vf_mac), + .maxlen = sizeof(struct ifla_vf_mac)}, + [IFLA_VF_VLAN] = { .minlen = sizeof(struct ifla_vf_vlan), + .maxlen = sizeof(struct ifla_vf_vlan)}, +}; + +static struct nla_policy ifla_vf_ports_policy[IFLA_VF_PORT_MAX + 1] +{ + [IFLA_VF_PORT] = { .type = NLA_NESTED }, +}; + +static struct nla_policy ifla_port_policy[IFLA_PORT_MAX + 1] +{ + [IFLA_PORT_VF] = { .type = NLA_U32 }, + [IFLA_PORT_PROFILE] = { .type = NLA_STRING }, + [IFLA_PORT_VSI_TYPE] = { .minlen = sizeof(struct ifla_port_vsi) }, + [IFLA_PORT_INSTANCE_UUID] = { .minlen = PORT_UUID_MAX, + .maxlen = PORT_UUID_MAX, }, + [IFLA_PORT_HOST_UUID] = { .minlen = PORT_UUID_MAX, + .maxlen = PORT_UUID_MAX, }, + [IFLA_PORT_REQUEST] = { .type = NLA_U8 }, + [IFLA_PORT_RESPONSE] = { .type = NLA_U16 }, +}; + static void event_if_decode_rta(int type, struct rtattr *rta) { @@ -220,15 +258,517 @@ static void event_if_decode_nlmsg(int route_type, void *data, int len) } } - static void event_if_process_recvmsg(struct nlmsghdr *nlmsg) { - - /* print out details */ + LLDPAD_DBG("%s:%s: nlmsg_type: %d", __FILE__, __FUNCTION__, nlmsg->nlmsg_type); event_if_decode_nlmsg(nlmsg->nlmsg_type, NLMSG_DATA(nlmsg), NLMSG_PAYLOAD(nlmsg, 0)); } +static int event_if_parse_getmsg(struct nlmsghdr *nlh, int *ifindex, + char *ifname) +{ + struct nlattr *tb[IFLA_MAX+1]; + struct ifinfomsg *ifinfo; + + if (nlmsg_parse(nlh, sizeof(struct ifinfomsg), + (struct nlattr **)&tb, IFLA_MAX, NULL)) { + LLDPAD_ERR("Error parsing GETLINK request..."); + return -EINVAL; + } + + if (tb[IFLA_IFNAME]) { + ifname = (char *)RTA_DATA(tb[IFLA_IFNAME]); + LLDPAD_DBG("IFLA_IFNAME=%s", ifname); + } else { + ifinfo = (struct ifinfomsg *)NLMSG_DATA(nlh); + *ifindex = ifinfo->ifi_index; + LLDPAD_DBG("interface index: %d", ifinfo->ifi_index); + } + + return 0; +} + +static int event_if_parse_setmsg(struct nlmsghdr *nlh) +{ + struct nlattr *tb[IFLA_MAX+1], + *tb3[IFLA_PORT_MAX+1], + *tb_vfinfo[IFLA_VF_MAX+1], + *tb_vfinfo_list; + struct vsi_profile *profile, *p; + struct ifinfomsg *ifinfo; + char *ifname; + int rem; + + profile = malloc(sizeof(struct vsi_profile)); + if (!profile) + return -ENOMEM; + memset(profile, 0, sizeof(struct vsi_profile)); + + if (nlmsg_parse(nlh, sizeof(struct ifinfomsg), + (struct nlattr **)&tb, IFLA_MAX, NULL)) { + LLDPAD_ERR("Error parsing request...\n"); + return -EINVAL; + } + + LLDPAD_DBG("%s(%d): nlmsg_len %i", __FILE__, __LINE__, nlh->nlmsg_len); + + if (tb[IFLA_IFNAME]) { + ifname = (char *)RTA_DATA(tb[IFLA_IFNAME]); + LLDPAD_DBG("IFLA_IFNAME=%s", ifname); + } else { + ifinfo = (struct ifinfomsg *)NLMSG_DATA(nlh); + LLDPAD_DBG("interface index: %d", ifinfo->ifi_index); + } + + if (!tb[IFLA_VFINFO_LIST]) { + LLDPAD_ERR("IFLA_VFINFO_LIST missing."); + return -EINVAL; + } else { + LLDPAD_DBG("FOUND IFLA_VFINFO_LIST!"); + } + + nla_for_each_nested(tb_vfinfo_list, tb[IFLA_VFINFO_LIST], rem) { + if (nla_type(tb_vfinfo_list) != IFLA_VF_INFO) { + LLDPAD_ERR("nested parsing of IFLA_VFINFO_LIST failed."); + return -EINVAL; + } + + if (nla_parse_nested(tb_vfinfo, IFLA_VF_MAX, tb_vfinfo_list, + ifla_vf_policy)) { + LLDPAD_ERR("nested parsing of IFLA_VF_INFO failed."); + return -EINVAL; + } + } + + if (tb_vfinfo[IFLA_VF_MAC]) { + struct ifla_vf_mac *mac = RTA_DATA(tb_vfinfo[IFLA_VF_MAC]); + u8 *m = mac->mac; + LLDPAD_DBG("IFLA_VF_MAC=%2x:%2x:%2x:%2x:%2x:%2x", + m[0], m[1], m[2], m[3], m[4], m[5]); + memcpy(&profile->mac, m, ETH_ALEN); + } + + if (tb_vfinfo[IFLA_VF_VLAN]) { + struct ifla_vf_vlan *vlan = RTA_DATA(tb_vfinfo[IFLA_VF_VLAN]); + LLDPAD_DBG("IFLA_VF_VLAN=%d", vlan->vlan); + profile->vlan = (u16) vlan->vlan; + } + + if (tb[IFLA_VF_PORTS]) { + struct nlattr *tb_vf_ports; + + LLDPAD_DBG("FOUND IFLA_VF_PORTS"); + + nla_for_each_nested(tb_vf_ports, tb[IFLA_VF_PORTS], rem) { + + LLDPAD_DBG("ITERATING"); + + if (nla_type(tb_vf_ports) != IFLA_VF_PORT) { + LLDPAD_DBG("not a IFLA_VF_PORT. skipping"); + continue; + } + + if (nla_parse_nested(tb3, IFLA_PORT_MAX, tb_vf_ports, + ifla_port_policy)) { + LLDPAD_ERR("nested parsing on level 2 failed."); + return -EINVAL; + } + + if (tb3[IFLA_PORT_VF]) { + LLDPAD_DBG("IFLA_PORT_VF=%d", *(uint32_t*)(RTA_DATA(tb3[IFLA_PORT_VF]))); + } + + if (tb3[IFLA_PORT_PROFILE]) { + LLDPAD_DBG("IFLA_PORT_PROFILE=%s", (char *)RTA_DATA(tb3[IFLA_PORT_PROFILE])); + } + + if (tb3[IFLA_PORT_VSI_TYPE]) { + struct ifla_port_vsi *pvsi; + int tid = 0; + + pvsi = (struct ifla_port_vsi*)RTA_DATA(tb3[IFLA_PORT_VSI_TYPE]); + tid = pvsi->vsi_type_id[2] << 16 | + pvsi->vsi_type_id[1] << 8 | + pvsi->vsi_type_id[0]; + + LLDPAD_DBG("mgr_id : %d", pvsi->vsi_mgr_id); + LLDPAD_DBG("type_id : %d", tid); + LLDPAD_DBG("type_version : %d", pvsi->vsi_type_version); + + profile->mgrid = pvsi->vsi_mgr_id; + profile->id = tid; + profile->version = pvsi->vsi_type_version; + } + + if (tb3[IFLA_PORT_INSTANCE_UUID]) { + int i; + unsigned char *uuid; + uuid = (unsigned char *)RTA_DATA(tb3[IFLA_PORT_INSTANCE_UUID]); + + char instance[INSTANCE_STRLEN+2]; + instance2str(uuid, instance, sizeof(instance)); + LLDPAD_DBG("IFLA_PORT_INSTANCE_UUID=%s", &instance[0]); + + memcpy(&profile->instance, + RTA_DATA(tb3[IFLA_PORT_INSTANCE_UUID]), 16); + } + + if (tb3[IFLA_PORT_REQUEST]) { + LLDPAD_DBG("IFLA_PORT_REQUEST=%d", + *(uint8_t*)RTA_DATA(tb3[IFLA_PORT_REQUEST])); + profile->mode = *(uint8_t*)RTA_DATA(tb3[IFLA_PORT_REQUEST]); + } + + if (tb3[IFLA_PORT_RESPONSE]) { + LLDPAD_DBG("IFLA_PORT_RESPONSE=%d", + *(uint16_t*)RTA_DATA(tb3[IFLA_PORT_RESPONSE])); + profile->response = *(uint16_t*)RTA_DATA(tb3[IFLA_PORT_RESPONSE]); + } + } + } + + if (ifname) { + struct port *port = port_find_by_name(ifname); + + if (port) { + profile->port = port; + } else { + LLDPAD_ERR("%s(%i): Could not find port for %s", __func__, + __LINE__, ifname); + return -EEXIST; + } + } + + p = vdp_add_profile(profile); + + if (!p) { + free(profile); + return -EINVAL; + } + + vdp_somethingChangedLocal(profile, VDP_PROFILE_REQ); + vdp_vsi_sm_station(p); + + return 0; +} + +static void event_if_parseResponseMsg(struct nlmsghdr *nlh) +{ + struct nlattr *tb[IFLA_MAX+1], + *tb2[IFLA_VF_PORT_MAX + 1], + *tb3[IFLA_PORT_MAX+1]; + + if (nlmsg_parse(nlh, sizeof(struct ifinfomsg), + (struct nlattr **)&tb, IFLA_MAX, NULL)) { + LLDPAD_ERR("Error parsing netlink response..."); + return; + } + + if (tb[IFLA_IFNAME]) { + LLDPAD_DBG("IFLA_IFNAME=%s", (char *)RTA_DATA(tb[IFLA_IFNAME])); + } else { + struct ifinfomsg *ifinfo = (struct ifinfomsg *)NLMSG_DATA(nlh); + LLDPAD_DBG("interface index: %d", ifinfo->ifi_index); + } + + if (tb[IFLA_VF_PORTS]) { + if (nla_parse_nested(tb2, IFLA_VF_PORT_MAX, tb[IFLA_VF_PORTS], + ifla_vf_ports_policy)) { + LLDPAD_ERR("nested parsing on level 1 failed."); + return; + } + + if (tb2[IFLA_VF_PORT]) { + if (nla_parse_nested(tb3, IFLA_PORT_MAX, tb2[IFLA_VF_PORT], + ifla_port_policy)) { + LLDPAD_ERR("nested parsing on level 2 failed."); + return; + } + + if (tb3[IFLA_PORT_VF]) { + LLDPAD_DBG("IFLA_PORT_VF=%d", *(uint32_t*)(RTA_DATA(tb3[IFLA_PORT_VF]))); + } + + if (tb3[IFLA_PORT_PROFILE]) { + LLDPAD_DBG("IFLA_PORT_PROFILE=%s", (char *)RTA_DATA(tb3[IFLA_PORT_PROFILE])); + } + + if (tb3[IFLA_PORT_VSI_TYPE]) { + struct ifla_port_vsi *pvsi; + int tid = 0; + pvsi = (struct ifla_port_vsi*)RTA_DATA(tb3[IFLA_PORT_VSI_TYPE]); + tid = pvsi->vsi_type_id[2] << 16 | + pvsi->vsi_type_id[1] << 8 | + pvsi->vsi_type_id[0]; + LLDPAD_DBG("mgr_id : %d" + "type_id : %d" + "type_version : %d", + pvsi->vsi_mgr_id, + tid, + pvsi->vsi_type_version); + } + + if (tb3[IFLA_PORT_INSTANCE_UUID]) { + int i; + unsigned char *uuid; + uuid = (unsigned char *)RTA_DATA(tb3[IFLA_PORT_INSTANCE_UUID]); + + char instance[INSTANCE_STRLEN+2]; + instance2str(uuid, instance, sizeof(instance)); + LLDPAD_DBG("IFLA_PORT_INSTANCE_UUID=%s", &instance[0]); + } + + if (tb3[IFLA_PORT_REQUEST]) { + LLDPAD_DBG("IFLA_PORT_REQUEST=%d", + *(uint8_t*)RTA_DATA(tb3[IFLA_PORT_REQUEST])); + } + + if (tb3[IFLA_PORT_RESPONSE]) { + LLDPAD_DBG("IFLA_PORT_RESPONSE=%d", + *(uint16_t*)RTA_DATA(tb3[IFLA_PORT_RESPONSE])); + } + } + } +} + +struct nl_msg *event_if_constructResponse(struct nlmsghdr *nlh, int ifindex) +{ + struct nl_msg *nl_msg; + struct nlattr *vf_ports = NULL, *vf_port; + struct ifinfomsg ifinfo; + struct vdp_data *vd; + uint32_t pid = nlh->nlmsg_pid; + uint32_t seq = nlh->nlmsg_seq; + char *ifname = malloc(IFNAMSIZ); + struct vsi_profile *p; + + nl_msg = nlmsg_alloc(); + + if (!nl_msg) { + LLDPAD_ERR("%s(%i): Unable to allocate netlink message !", __func__, __LINE__); + return NULL; + } + + if (!if_indextoname(ifindex, ifname)) { + LLDPAD_ERR("%s(%i): No name found for interface with index %i !", __func__, __LINE__, + ifindex); + } + + vd = vdp_data(ifname); + if (!vd) { + LLDPAD_ERR("%s(%i): Could not find vdp_data for %s !", __func__, __LINE__, + ifname); + return NULL; + } + + free(ifname); + + if (nlmsg_put(nl_msg, pid, seq, NLMSG_DONE, 0, 0) == NULL) + goto err_exit; + + ifinfo.ifi_index = ifindex; + + if (nlmsg_append(nl_msg, &ifinfo, sizeof(ifinfo), NLMSG_ALIGNTO) < 0) + goto err_exit; + + vf_ports = nla_nest_start(nl_msg, IFLA_VF_PORTS); + + if (!vf_ports) + goto err_exit; + + /* loop over all existing profiles on this interface and + * put them into the nested IFLA_VF_PORT structure */ + LIST_FOREACH(p, &vd->profile_head, profile) { + if (p) { + vdp_print_profile(p); + + vf_port = nla_nest_start(nl_msg, IFLA_VF_PORT); + + if (!vf_port) + goto err_exit; + + if (nla_put(nl_msg, IFLA_PORT_INSTANCE_UUID, 16, p->instance) < 0) + goto err_exit; + + if (nla_put_u32(nl_msg, IFLA_PORT_VF, PORT_SELF_VF) < 0) + goto err_exit; + + if (p->mode == p->state) { + if (nla_put_u16(nl_msg, IFLA_PORT_RESPONSE, + PORT_VDP_RESPONSE_SUCCESS) < 0) + goto err_exit; + } else { + if (p->state == VSI_EXIT) { + if (p->response > PORT_VDP_RESPONSE_SUCCESS) { + if (nla_put_u16(nl_msg, IFLA_PORT_RESPONSE, p->response) < 0) + goto err_exit; + } else { + if (nla_put_u16(nl_msg, IFLA_PORT_RESPONSE, + PORT_VDP_RESPONSE_INSUFFICIENT_RESOURCES) < 0) + goto err_exit; + } + } + } + + nla_nest_end(nl_msg, vf_port); + } + } + + if (vf_ports) + nla_nest_end(nl_msg, vf_ports); + + return nl_msg; + +err_exit: + nlmsg_free(nl_msg); + + return NULL; +} + +struct nl_msg *event_if_simpleResponse(uint32_t pid, uint32_t seq, int err) +{ + struct nl_msg *nl_msg = nlmsg_alloc(); + struct nlmsgerr nlmsgerr; + + memset(&nlmsgerr, 0x0, sizeof(nlmsgerr)); + + nlmsgerr.error = err; + LLDPAD_DBG("RESPONSE error code: %d",err); + + if (nlmsg_put(nl_msg, pid, seq, NLMSG_ERROR, 0, 0) == NULL) + goto err_exit; + + if (nlmsg_append(nl_msg, &nlmsgerr, sizeof(nlmsgerr), NLMSG_ALIGNTO) < 0) + goto err_exit; + + return nl_msg; + +err_exit: + nlmsg_free(nl_msg); + + return NULL; +} + +static void event_iface_receive_user_space(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct nlmsghdr *nlh, *nlh2; + struct nl_msg *nl_msg; + struct msghdr msg; + struct sockaddr_nl dest_addr; + struct iovec iov; + int result; + int err; + int ifindex = 0; + char *ifname = NULL; + + nlh = (struct nlmsghdr *)calloc(1, + NLMSG_SPACE(MAX_PAYLOAD)); + if (!nlh) { + LLDPAD_ERR("%s(%i): could not allocate nlh !", __func__, + __LINE__); + return; + } + memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD)); + + memset(&dest_addr, 0, sizeof(dest_addr)); + iov.iov_base = (void *)nlh; + iov.iov_len = NLMSG_SPACE(MAX_PAYLOAD); + msg.msg_name = (void *)&dest_addr; + msg.msg_namelen = sizeof(dest_addr); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + LLDPAD_DBG("Waiting for message"); + result = recvmsg(sock, &msg, MSG_DONTWAIT); + + LLDPAD_DBG("%s(%i): ", __func__, __LINE__); + LLDPAD_DBG("recvmsg received %d bytes", result); + + if(result < 0) { + LLDPAD_ERR("Error receiving from netlink socket : %s", strerror(errno)); + } + + LLDPAD_DBG("dest_addr.nl_pid: %d", dest_addr.nl_pid); + LLDPAD_DBG("nlh.nl_pid: %d", nlh->nlmsg_pid); + LLDPAD_DBG("nlh_type: %d", nlh->nlmsg_type); + LLDPAD_DBG("nlh_seq: 0x%x", nlh->nlmsg_seq); + LLDPAD_DBG("nlh_len: 0x%x", nlh->nlmsg_len); + + switch (nlh->nlmsg_type) { + case RTM_SETLINK: + LLDPAD_DBG("RTM_SETLINK"); + + err = event_if_parse_setmsg(nlh); + + /* send simple response wether profile was accepted + * or not */ + nl_msg = event_if_simpleResponse(nlh->nlmsg_pid, + nlh->nlmsg_seq, + err); + nlh2 = nlmsg_hdr(nl_msg); + break; + case RTM_GETLINK: + LLDPAD_DBG("RTM_GETLINK"); + + err = event_if_parse_getmsg(nlh, &ifindex, ifname); + + if (err) { + nl_msg = event_if_simpleResponse(nlh->nlmsg_pid, + nlh->nlmsg_seq, + err); + } else if (ifname) { + ifindex = if_nametoindex(ifname); + LLDPAD_DBG("%s(%i): ifname %s (%d)", __func__, + __LINE__, ifname, ifindex); + } else { + LLDPAD_DBG("%s(%i): ifindex %i", __func__, + __LINE__, ifindex); + } + + nl_msg = event_if_constructResponse(nlh, ifindex); + + if (!nl_msg) { + LLDPAD_ERR("%s(%i): Unable to construct response !", + __func__, __LINE__); + goto out_err; + } + + nlh2 = nlmsg_hdr(nl_msg); + + LLDPAD_DBG("RESPONSE:"); + + event_if_parseResponseMsg(nlh2); + + break; + } + + iov.iov_base = (void*)nlh2; + iov.iov_len = nlh2->nlmsg_len; + + msg.msg_name = (void *)&dest_addr; + msg.msg_namelen = sizeof(dest_addr); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + result = sendmsg(sock, &msg, 0); + + if (result < 0) { + LLDPAD_ERR("Error sending on netlink socket (%s) !", strerror(errno)); + } else { + LLDPAD_DBG("Sent %d bytes !",result); + } + +out_err: + free(nlh); + nlmsg_free(nl_msg); + + return; +} + static void event_iface_receive(int sock, void *eloop_ctx, void *sock_ctx) { struct nlmsghdr *nlh; @@ -236,12 +776,14 @@ static void event_iface_receive(int sock, void *eloop_ctx, void *sock_ctx) char buf[MAX_MSG_SIZE]; socklen_t fromlen = sizeof(dest_addr); int result; - + result = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, (struct sockaddr *) &dest_addr, &fromlen); + LLDPAD_DBG("%s:%s: result from receive: %d", __FILE__, __FUNCTION__, result); + if (result < 0) { - perror("recvfrom(Event interface)"); + LLDPAD_ERR("recvfrom(event interface): %s", strerror(errno)); if ((errno == ENOBUFS) || (errno == EAGAIN)) eloop_register_timeout(INI_TIMER, 0, scan_port, NULL, NULL); @@ -250,10 +792,15 @@ static void event_iface_receive(int sock, void *eloop_ctx, void *sock_ctx) TRACE("PRINT BUF info.\n") - device_name = NULL; - link_status = IF_OPER_UNKNOWN; - nlh = (struct nlmsghdr *)buf; - event_if_process_recvmsg(nlh); + /* Separate handler for kernel messages from userspace messages*/ + LLDPAD_DBG("%s:%s: dest_addr.nl_pid: %d", __FILE__, __FUNCTION__, dest_addr.nl_pid); + + if (dest_addr.nl_pid == 0) { + device_name = NULL; + link_status = IF_OPER_UNKNOWN; + nlh = (struct nlmsghdr *)buf; + event_if_process_recvmsg(nlh); + } } int event_iface_init() @@ -261,7 +808,7 @@ int event_iface_init() int fd; int rcv_size = MAX_MSG_SIZE; struct sockaddr_nl snl; - + fprintf(stderr, "%s:%s:%d\n", __FILE__, __FUNCTION__, __LINE__); fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (fd < 0) @@ -282,6 +829,39 @@ int event_iface_init() } eloop_register_read_sock(fd, event_iface_receive, NULL, NULL); + + return 0; +} + +int event_iface_init_user_space() +{ + int fd; + int rcv_size = MAX_MSG_SIZE; + struct sockaddr_nl snl; + + fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + + if (fd < 0) + return fd; + + if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcv_size, sizeof(int)) < 0) { + close(fd); + return -EIO; + } + + memset((void *)&snl, 0, sizeof(struct sockaddr_nl)); + snl.nl_family = AF_NETLINK; + snl.nl_pid = getpid(); /* self pid */ + snl.nl_groups = 0; + + if (bind(fd, (struct sockaddr *)&snl, sizeof(struct sockaddr_nl)) < 0) { + close(fd); + LLDPAD_ERR("Error binding to netlink socket (%s) !", strerror(errno)); + return -EIO; + } + + eloop_register_read_sock(fd, event_iface_receive_user_space, NULL, NULL); + return 0; } diff --git a/include/event_iface.h b/include/event_iface.h index b2c93f0..d80158d 100644 --- a/include/event_iface.h +++ b/include/event_iface.h @@ -29,6 +29,7 @@ #define _EVENT_IFACE_H_ int event_iface_init(void); +int event_iface_init_user_space(void); int event_iface_deinit(void); int oper_add_device(char *device_name); diff --git a/include/lldp_vdp.h b/include/lldp_vdp.h index b97d8c0..53f814c 100644 --- a/include/lldp_vdp.h +++ b/include/lldp_vdp.h @@ -134,6 +134,8 @@ struct vdp_data *vdp_data(char *ifname); struct packed_tlv *vdp_gettlv(struct vdp_data *vd, struct vsi_profile *profile); void vdp_vsi_sm_station(struct vsi_profile *profile); struct vsi_profile *vdp_add_profile(struct vsi_profile *profile); +void vdp_somethingChangedLocal(struct vsi_profile *profile, int mode); +void vdp_print_profile(struct vsi_profile *profile); #define MAC_ADDR_STRLEN 18 #define INSTANCE_STRLEN 32 diff --git a/lldp_vdp.c b/lldp_vdp.c index 01bcbf7..3a507e9 100644 --- a/lldp_vdp.c +++ b/lldp_vdp.c @@ -105,7 +105,7 @@ static void vdp_free_data(struct vdp_user_data *ud) * prints the contents of a profile first to a string using the PRINT_PROFILE * macro, and then to the screen. Used for debug purposes. */ -static inline void vdp_print_profile(struct vsi_profile *profile) +void vdp_print_profile(struct vsi_profile *profile) { LLDPAD_DBG("profile:\n"); diff --git a/lldpad.c b/lldpad.c index c0938af..69faa29 100644 --- a/lldpad.c +++ b/lldpad.c @@ -374,6 +374,16 @@ int main(int argc, char *argv[]) exit(1); } + /* setup event netlink interface for user space processes. + * This needs to be setup first to ensure it gets lldpads + * pid as netlink address. + */ + if (event_iface_init_user_space() < 0) { + log_message(MSG_ERR_SERVICE_START_FAILURE, + "%s", "failed to register user space event interface"); + exit(1); + } + init_modules(""); -- 1.7.1
Jens Osterkamp
2010-Nov-08 16:12 UTC
[PATCH 9/9] do not use macv[tap/lan] interfaces as ports
At startup lldpad walks through all network interfaces in the system and creates internal data structures for them. Some interfaces as e.g. vlan and wlan are skipped in the walkthrough, some have to be treated special (e.g. bond devices). This patch adds macvtap and macvlan interfaces to the list of devices that are skipped as we do not want to send out EVB/ECP frames on them. Signed-off-by: Jens Osterkamp <jens at linux.vnet.ibm.com> --- config.c | 2 + include/lldp_util.h | 1 + lldp_util.c | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+), 0 deletions(-) diff --git a/config.c b/config.c index 26afe3b..fdb63f7 100644 --- a/config.c +++ b/config.c @@ -365,6 +365,8 @@ void init_ports(void) p->if_name); } else if (is_vlan(p->if_name)) { ; + } else if (is_macvtap(p->if_name)) { + ; } else if (is_bridge(p->if_name)) { ; /* ignore bridge device */ } else if (check_link_status(p->if_name)) { diff --git a/include/lldp_util.h b/include/lldp_util.h index 3353067..ef2c562 100644 --- a/include/lldp_util.h +++ b/include/lldp_util.h @@ -40,6 +40,7 @@ int is_bridge(const char *ifname); int is_vlan(const char *ifname); int is_vlan_capable(const char *ifname); int is_wlan(const char *ifname); +int is_macvtap(const char *ifname); int is_valid_mac(const u8 *mac); int is_san_mac(u8 *addr); int is_ether(const char *ifname); diff --git a/lldp_util.c b/lldp_util.c index f39fe6b..40db741 100644 --- a/lldp_util.c +++ b/lldp_util.c @@ -34,6 +34,7 @@ #include <sys/types.h> #include <sys/socket.h> #include <net/if_arp.h> +#include <netlink/msg.h> #include <arpa/inet.h> #include <linux/if.h> #include <linux/if_bonding.h> @@ -42,6 +43,7 @@ #include <linux/wireless.h> #include <linux/sockios.h> #include <linux/ethtool.h> +#include <linux/rtnetlink.h> #include <dirent.h> #include "lldp.h" #include "lldp_util.h" @@ -57,6 +59,8 @@ int is_valid_lldp_device(const char *device_name) return 0; if (is_bridge(device_name)) return 0; + if (is_macvtap(device_name)) + return 0; return 1; } @@ -534,6 +538,92 @@ int is_wlan(const char *ifname) return rc; } +#define NLMSG_SIZE 1024 + +static struct nla_policy ifla_info_policy[IFLA_INFO_MAX + 1] +{ + [IFLA_INFO_KIND] = { .type = NLA_STRING}, + [IFLA_INFO_DATA] = { .type = NLA_NESTED }, +}; + +int is_macvtap(const char *ifname) +{ + int ret, s; + struct nlmsghdr *nlh; + struct ifinfomsg *ifinfo; + struct nlattr *tb[IFLA_MAX+1], + *tb2[IFLA_INFO_MAX+1]; + + s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + + if (s < 0) { + goto out; + } + + nlh = malloc(NLMSG_SIZE); + memset(nlh, 0, NLMSG_SIZE); + + if (!nlh) { + goto out; + } + + nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + nlh->nlmsg_type = RTM_GETLINK; + nlh->nlmsg_flags = NLM_F_REQUEST; + + ifinfo = NLMSG_DATA(nlh); + ifinfo->ifi_family = AF_UNSPEC; + ifinfo->ifi_index = get_ifidx(ifname); + + ret = send(s, nlh, nlh->nlmsg_len, 0); + + if (ret < 0) { + goto out_free; + } + + memset(nlh, 0, NLMSG_SIZE); + + do { + ret = recv(s, (void *) nlh, NLMSG_SIZE, MSG_DONTWAIT); + } while ((ret < 0) && errno == EINTR); + + if (nlmsg_parse(nlh, sizeof(struct ifinfomsg), + (struct nlattr **)&tb, IFLA_MAX, NULL)) { + goto out_free; + } + + if (tb[IFLA_IFNAME]) { + ifname = (char *)RTA_DATA(tb[IFLA_IFNAME]); + } else { + ifinfo = (struct ifinfomsg *)NLMSG_DATA(nlh); + } + + if (tb[IFLA_LINKINFO]) { + if (nla_parse_nested(tb2, IFLA_INFO_MAX, tb[IFLA_LINKINFO], + ifla_info_policy)) { + goto out_free; + } + + if (tb2[IFLA_INFO_KIND]) { + char *kind = (char*)(RTA_DATA(tb2[IFLA_INFO_KIND])); + if (!(strcmp("macvtap", kind) && strcmp("macvlan", kind))) { + free(nlh); + close(s); + return true; + } + } + + } else { + goto out_free; + } + +out_free: + free(nlh); +out: + close(s); + return false; +} + int is_router(const char *ifname) { int rc = 0; -- 1.7.1
Hi, this set of patches contains the initial implementation of the IEEE 802.1Qbg standard: code for the exchange of EVB TLVs in LLDP frames to negotiate VSI capabalities as well as VDP VSI TLVs between a host with virtual machines and an adjacent switch. It supports setting the parameters of the TLV exchange from the command line using lldptool. VDP profiles consisting of mode,mgrid,typeid,typeidversion,instanceid,mac,vlan can be given to lldpad with lldptool or sent to lldpad via netlink messages from the kernel or another program, e.g. libvirt. VDP profiles are processed through the VDP/VSI and ECP state machines and sent out in ECP frames. ACK frames are received and processed through ECP and VDP/VSI state machines. It implements a VDP bridge role for a port together with a lldptool command to switch a port to the bridge role. These patches apply against lldpad 0.9.38. Protocol debug messages are now logged to syslog via LLDPAD_* macros and no longer to console. If you want to see them, configure your syslog daemon to collect debug messages and start lldpad with -V 7. Changes since last posting: - implementation of VDP keepalive - better handling of response/reason codes by the switch - better handling of deassoc frame sent by switch - improved (human readable) printout of profile - bugfix to corrected wrong instance strlen - cleanup of vdp state machine - zero out unused part of ack frame - simplify handling of pending profile changes - simplify handling of response to libvirt - bugfix to prevent duplicate sending of TLVs For more information about lldpad take a look at http://sourceforge.net/projects/e1000/files/DCB%20Tools/lldpad/ Please review and comment. Thanks ! Jens
Jens Osterkamp
2010-Nov-22 15:50 UTC
[PATCH 01/10] consolidation of MIN and MAX macros in common.h
This patch consolidates a modified version of the already existing MIN macro in lldpad to include/common.h and add a MAX macro. Signed-off-by: Jens Osterkamp <jens at linux.vnet.ibm.com> --- include/common.h | 14 ++++++++++++++ include/dcb_protocol.h | 4 ---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/include/common.h b/include/common.h index 01746ea..ab07dc2 100644 --- a/include/common.h +++ b/include/common.h @@ -387,4 +387,18 @@ typedef int socklen_t; const char * wpa_ssid_txt(u8 *ssid, size_t ssid_len); +#define MIN(x,y) \ + ({ \ + typeof (x) __x = (x); \ + typeof (y) __y = (y); \ + __x < __y ? __x : __y; \ + }) + +#define MAX(x,y) \ + ({ \ + typeof (x) __x = (x); \ + typeof (y) __y = (y); \ + __x > __y ? __x : __y; \ + }) + #endif /* COMMON_H */ diff --git a/include/dcb_protocol.h b/include/dcb_protocol.h index 63e2d3c..d7127f5 100644 --- a/include/dcb_protocol.h +++ b/include/dcb_protocol.h @@ -58,10 +58,6 @@ typedef enum { #define DUP_DCBX_TLV_LLINK 0x0020 #define TOO_MANY_NGHBRS 0x0040 -//#ifndef min /*todo: change to min() - #define MIN(x, y) ((x) < (y) ? x : y) -//#endif - #define INIT_DCB_OUI {0x00,0x1b,0x21} int add_adapter(char *device_name); -- 1.7.2.3
Jens Osterkamp
2010-Nov-22 15:50 UTC
[PATCH 02/10] implementation of IEEE 802.1Qbg in lldpad, part 1
This patch contains the first part of an initial implementation of the IEEE 802.1Qbg standard: it implements code for the exchange of EVB capabilities between a host with virtual machines and an adjacent switch. For this it adds a new EVB TLV to LLDP. Exchange of EVB TLV may be enabled or disabled on a per port basis. Information about the information negotiated by the protocol can be queried on the commandline with lldptool. This patch adds support for querying and setting parameters used in the exchange of EVB TLV messages. The parameters that can be set are: - forwarding mode - host protocol capabilities (RTE, ECP, VDP) - no. of supported VSIs - retransmission timer exponent (RTE) The parameters are implemented as a local policy: all frames received by an adjacent switch are validated against this policy and taken over where appropriate. Negotiated parameters are stored in lldpads config, picked up again and used at the next start. The patch applies to lldpad 0.9.38 and still contains code to log protocol activity more verbosely than it would be necessary in the final version. Signed-off-by: Jens Osterkamp <jens at linux.vnet.ibm.com> --- Makefile.am | 10 +- include/lldp.h | 19 ++ include/lldp_evb.h | 80 ++++++ include/lldp_evb_clif.h | 51 ++++ include/lldp_evb_cmds.h | 31 +++ include/lldp_tlv.h | 1 + lldp_evb.c | 672 +++++++++++++++++++++++++++++++++++++++++++++++ lldp_evb_clif.c | 226 ++++++++++++++++ lldp_evb_cmds.c | 519 ++++++++++++++++++++++++++++++++++++ lldpad.c | 2 + lldptool.c | 2 + 11 files changed, 1609 insertions(+), 4 deletions(-) create mode 100644 include/lldp_evb.h create mode 100644 include/lldp_evb_clif.h create mode 100644 include/lldp_evb_cmds.h create mode 100644 lldp_evb.c create mode 100644 lldp_evb_clif.c create mode 100644 lldp_evb_cmds.c diff --git a/Makefile.am b/Makefile.am index 743e16f..d59a6fa 100644 --- a/Makefile.am +++ b/Makefile.am @@ -37,7 +37,7 @@ lldpad_include_HEADERS = include/dcb_types.h include/dcbtool.h \ include/dcb_osdep.h include/clif.h include/lldp_dcbx_cmds.h include/common.h \ include/lldpad.h include/os.h include/includes.h include/lldp_mand_cmds.h \ include/clif_msgs.h include/lldp_basman_cmds.h include/lldp_8023_cmds.h \ -include/lldp_med_cmds.h include/lldp_dcbx_cfg.h +include/lldp_med_cmds.h include/lldp_dcbx_cfg.h include/lldp_evb_cmds.h noinst_HEADERS = include/config.h include/ctrl_iface.h \ include/dcb_driver_if_types.h include/dcb_driver_interface.h \ @@ -47,7 +47,7 @@ include/event_iface.h include/messages.h include/parse_cli.h include/version.h \ include/lldptool_cli.h include/list.h \ include/lldp_mand_clif.h include/lldp_basman_clif.h include/lldp_med_clif.h \ include/lldp_8023_clif.h include/lldp_dcbx_clif.h include/lldptool.h \ -include/lldp_rtnl.h +include/lldp_rtnl.h include/lldp_evb_clif.h lldpad_SOURCES = lldpad.c config.c drv_cfg.c ctrl_iface.c event_iface.c eloop.c \ common.c os_unix.c lldp_dcbx_cmds.c log.c lldpad_shm.c \ @@ -62,10 +62,12 @@ lldp_dcbx_cfg.c include/lldp_dcbx_cfg.h \ lldp_util.c include/lldp_util.h \ lldp_mand.c include/lldp_mand.h \ lldp_mand_cmds.c lldp_basman_cmds.c lldp_8023_cmds.c lldp_med_cmds.c \ +lldp_evb_cmds.c \ lldp_tlv.c include/lldp_tlv.h \ lldp_basman.c include/lldp_basman.h \ lldp_med.c include/lldp_med.h \ -lldp_8023.c include/lldp_8023.h +lldp_8023.c include/lldp_8023.h \ +lldp_evb.c include/lldp_evb.h @@ -74,7 +76,7 @@ $(lldpad_include_HEADERS) $(noinst_HEADERS) lldptool_SOURCES = lldptool.c clif.c lldptool_cmds.c common.c os_unix.c \ lldp_mand_clif.c lldp_basman_clif.c lldp_med_clif.c lldp_8023_clif.c \ -lldp_dcbx_clif.c $(lldpad_include_HEADERS) $(noinst_HEADERS) +lldp_dcbx_clif.c lldp_evb_clif.c $(lldpad_include_HEADERS) $(noinst_HEADERS) nltest_SOURCES = nltest.c nltest.h diff --git a/include/lldp.h b/include/lldp.h index 66532bd..0371844 100644 --- a/include/lldp.h +++ b/include/lldp.h @@ -45,6 +45,8 @@ /* Telecommunications Industry Association TR-41 Committee */ #define OUI_TIA_TR41 0x0012bb +#define OUI_IEEE_8021Qbg 0x001b3f + /* IEEE 802.3AB Clause 9: TLV Types */ #define CHASSIS_ID_TLV 1 #define PORT_ID_TLV 2 @@ -186,5 +188,22 @@ enum { #define LLDP_8023_LINKAGG_CAPABLE (1 << 0) #define LLDP_8023_LINKAGG_ENABLED (1 << 1) +/* IEEE 802.1Qbg subtype */ +#define LLDP_EVB_SUBTYPE 0 + +/* forwarding mode */ +#define LLDP_EVB_CAPABILITY_FORWARD_STANDARD (1 << 7) +#define LLDP_EVB_CAPABILITY_FORWARD_REFLECTIVE_RELAY (1 << 6) + +/* EVB supported protocols */ +#define LLDP_EVB_CAPABILITY_PROTOCOL_RTE (1 << 2) +#define LLDP_EVB_CAPABILITY_PROTOCOL_ECP (1 << 1) +#define LLDP_EVB_CAPABILITY_PROTOCOL_VDP (1 << 0) + +/* EVB specific values */ +#define LLDP_EVB_DEFAULT_MAX_VSI 65535 +#define LLDP_EVB_DEFAULT_SVSI 3295 +#define LLDP_EVB_DEFAULT_RTE 15 + void somethingChangedLocal(char *ifname); #endif /* _LLDP_H */ diff --git a/include/lldp_evb.h b/include/lldp_evb.h new file mode 100644 index 0000000..667f9ad --- /dev/null +++ b/include/lldp_evb.h @@ -0,0 +1,80 @@ +/******************************************************************************* + + implementation of EVB TLVs for LLDP + (c) Copyright IBM Corp. 2010 + + Author(s): Jens Osterkamp <jens at linux.vnet.ibm.com> + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + +*******************************************************************************/ + +#ifndef _LLDP_EVB_H +#define _LLDP_EVB_H + +#include "lldp_mod.h" + +#define LLDP_MOD_EVB OUI_IEEE_8021Qbg +#define LLDP_OUI_SUBTYPE { 0x00, 0x1b, 0x3f, 0x00 } + +typedef enum { + EVB_OFFER_CAPABILITIES = 0, + EVB_CONFIGURE, + EVB_CONFIRMATION +} evb_state; + +struct tlv_info_evb { + u8 oui[3]; + u8 sub; + /* supported forwarding mode */ + u8 smode; + /* supported capabilities */ + u8 scap; + /* currently configured forwarding mode */ + u8 cmode; + /* currently configured capabilities */ + u8 ccap; + /* supported no. of vsi */ + u16 svsi; + /* currently configured no. of vsi */ + u16 cvsi; + /* retransmission exponent */ + u8 rte; +} __attribute__ ((__packed__)); + +struct evb_data { + char ifname[IFNAMSIZ]; + struct unpacked_tlv *evb; + struct tlv_info_evb *tie; + /* local policy */ + struct tlv_info_evb *policy; + int state; + LIST_ENTRY(evb_data) entry; +}; + +struct evb_user_data { + LIST_HEAD(evb_head, evb_data) head; +}; + +struct lldp_module *evb_register(void); +void evb_unregister(struct lldp_module *mod); +struct packed_tlv *evb_gettlv(struct port *port); +void evb_ifdown(char *); +void evb_ifup(char *); +struct evb_data *evb_data(char *ifname); + +#endif /* _LLDP_EVB_H */ diff --git a/include/lldp_evb_clif.h b/include/lldp_evb_clif.h new file mode 100644 index 0000000..acaee5e --- /dev/null +++ b/include/lldp_evb_clif.h @@ -0,0 +1,51 @@ +/******************************************************************************* + + implementation of EVB TLVs for LLDP + (c) Copyright IBM Corp. 2010 + + Author(s): Jens Osterkamp <jens at linux.vnet.ibm.com> + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + +*******************************************************************************/ + +#ifndef _LLDP_EVB_CLIF_H +#define _LLDP_EVB_CLIF_H + +struct lldp_module *evb_cli_register(void); +void evb_cli_unregister(struct lldp_module *); +int evb_print_tlv(u32, u16, char *); + +#define EVB_BUF_SIZE 256 + +#define ARG_EVB_FORWARDING_MODE "fmode" + +#define VAL_EVB_FMODE_BRIDGE "bridge" +#define VAL_EVB_FMODE_REFLECTIVE_RELAY "reflectiverelay" + +#define ARG_EVB_CAPABILITIES "capabilities" + +#define VAL_EVB_CAPA_RTE "rte" +#define VAL_EVB_CAPA_ECP "ecp" +#define VAL_EVB_CAPA_VDP "vdp" +#define VAL_EVB_CAPA_NONE "none" + +#define ARG_EVB_VSIS "vsis" + +#define ARG_EVB_RTE "rte" + +#endif diff --git a/include/lldp_evb_cmds.h b/include/lldp_evb_cmds.h new file mode 100644 index 0000000..1367e5d --- /dev/null +++ b/include/lldp_evb_cmds.h @@ -0,0 +1,31 @@ +/******************************************************************************* + + implementation of EVB TLVs for LLDP + (c) Copyright IBM Corp. 2010 + + Author(s): Jens Osterkamp <jens at linux.vnet.ibm.com> + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + +*******************************************************************************/ + +#ifndef _LLDP_EVB_CMDS_H +#define _LLDP_EVB_CMDS_H + +struct arg_handlers *evb_get_arg_handlers(); + +#endif diff --git a/include/lldp_tlv.h b/include/lldp_tlv.h index a32cc71..fe3a75a 100644 --- a/include/lldp_tlv.h +++ b/include/lldp_tlv.h @@ -144,6 +144,7 @@ int tlv_ok(struct unpacked_tlv *tlv); #define TLVID_8021(sub) TLVID(OUI_IEEE_8021, (sub)) #define TLVID_8023(sub) TLVID(OUI_IEEE_8023, (sub)) #define TLVID_MED(sub) TLVID(OUI_TIA_TR41, (sub)) +#define TLVID_8021Qbg(sub) TLVID(OUI_IEEE_8021Qbg, (sub)) /* the size in bytes needed for a packed tlv from unpacked tlv */ #define TLVSIZE(t) ((t) ? (2 + (t)->length) : 0) diff --git a/lldp_evb.c b/lldp_evb.c new file mode 100644 index 0000000..50ee17d --- /dev/null +++ b/lldp_evb.c @@ -0,0 +1,672 @@ +/******************************************************************************* + + implementation of EVB TLVs for LLDP + (c) Copyright IBM Corp. 2010 + + Author(s): Jens Osterkamp <jens at linux.vnet.ibm.com> + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + +*******************************************************************************/ + +#include <net/if.h> +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/utsname.h> +#include <linux/if_bridge.h> +#include <string.h> +#include "lldp.h" +#include "lldp_evb.h" +#include "messages.h" +#include "config.h" +#include "common.h" +#include "lldp_mand_clif.h" +#include "lldp_evb_clif.h" +#include "lldp_evb_cmds.h" + +extern struct lldp_head lldp_head; + +struct evb_data *evb_data(char *ifname) +{ + struct evb_user_data *ud; + struct evb_data *ed = NULL; + + ud = find_module_user_data_by_if(ifname, &lldp_head, LLDP_MOD_EVB); + if (ud) { + LIST_FOREACH(ed, &ud->head, entry) { + if (!strncmp(ifname, ed->ifname, IFNAMSIZ)) + return ed; + } + } + return NULL; +} + +static void evb_print_tlvinfo(struct tlv_info_evb *tie) +{ + LLDPAD_INFO("%s(%i): supported forwarding mode: %02x\n", __FILE__, __LINE__, tie->smode); + LLDPAD_INFO("%s(%i): configured forwarding mode: %02x\n", __FILE__, __LINE__, tie->cmode); + LLDPAD_INFO("%s(%i): supported capabilities: %02x\n", __FILE__, __LINE__, tie->scap); + LLDPAD_INFO("%s(%i): configured capabilities: %02x\n", __FILE__, __LINE__, tie->ccap); + LLDPAD_INFO("%s(%i): supported no. of vsis: %04i\n", __FILE__, __LINE__, tie->svsi); + LLDPAD_INFO("%s(%i): configured no. of vsis: %04i\n", __FILE__, __LINE__, tie->cvsi); + LLDPAD_INFO("%s(%i): rte: %02i\n", __FILE__, __LINE__, tie->rte); +} + +static void evb_dump_tlv(struct unpacked_tlv *tlv) +{ + int i; + char *s, *t; + + s = t = malloc(256); + + if (!s) { + LLDPAD_ERR("%s(%i): unable to allocate string !\n", __func__, __LINE__); + } + + LLDPAD_DBG("%s:type %i, length %i, info ", __func__, tlv->type, tlv->length); + + for (i=0; i < tlv->length; i++) { + int c; + c = sprintf(s, "%02x ", tlv->info[i]); + s += c; + } + + LLDPAD_DBG("%s\n", t); + + free(t); +} + +/* + * evb_bld_cfg_tlv - build the EVB TLV + * @ed: the evb data struct + * + * Returns 0 on success + */ +static int evb_bld_cfg_tlv(struct evb_data *ed) +{ + int rc = 0; + int i; + struct unpacked_tlv *tlv = NULL; + + /* free ed->evb if it exists */ + FREE_UNPKD_TLV(ed, evb); + + if (!is_tlv_txenabled(ed->ifname, TLVID_8021Qbg(LLDP_EVB_SUBTYPE))) { + LLDPAD_ERR("%s:%s:EVB tx is currently disabled !\n", + __func__, ed->ifname); + rc = EINVAL; + goto out_err; + } + + if (ed->tie->smode != ed->policy->smode) { + ed->tie->smode = ed->policy->smode; + ed->state = EVB_OFFER_CAPABILITIES; + } + + tlv = create_tlv(); + if (!tlv) + goto out_err; + + tlv->type = ORG_SPECIFIC_TLV; + tlv->length = sizeof(struct tlv_info_evb); + tlv->info = (u8 *)malloc(tlv->length); + if(!tlv->info) { + free(tlv); + tlv = NULL; + rc = ENOMEM; + goto out_err; + } + memcpy(tlv->info, ed->tie, tlv->length); + + LLDPAD_DBG("%s(%i): TLV about to be sent out:", __func__, __LINE__); + evb_dump_tlv(tlv); + + ed->evb = tlv; +out_err: + return rc; +} + +static void evb_free_tlv(struct evb_data *ed) +{ + if (ed) + FREE_UNPKD_TLV(ed, evb); +} + +/* evb_init_cfg_tlv: + * + * fill up tlv_info_evb structure with reasonable info + */ +static int evb_init_cfg_tlv(struct evb_data *ed) +{ + char arg_path[EVB_BUF_SIZE]; + char *param; + + /* load policy from config */ + ed->policy = (struct tlv_info_evb *) calloc(1, sizeof(struct tlv_info_evb)); + + if (!ed->policy) + return ENOMEM; + + /* set defaults */ + hton24(ed->policy->oui, LLDP_MOD_EVB); + ed->policy->smode = LLDP_EVB_CAPABILITY_FORWARD_STANDARD; + ed->policy->scap = LLDP_EVB_CAPABILITY_PROTOCOL_RTE | LLDP_EVB_CAPABILITY_PROTOCOL_ECP | + LLDP_EVB_CAPABILITY_PROTOCOL_VDP; + ed->policy->cmode = 0; + ed->policy->ccap = 0; + ed->policy->svsi = LLDP_EVB_DEFAULT_SVSI; + ed->policy->rte = LLDP_EVB_DEFAULT_RTE; + + /* pull forwarding mode into policy */ + snprintf(arg_path, sizeof(arg_path), "%s%08x.fmode", + TLVID_PREFIX, TLVID_8021Qbg(LLDP_EVB_SUBTYPE)); + + if (get_cfg(ed->ifname, arg_path, (void *) ¶m, CONFIG_TYPE_STRING)) { + LLDPAD_WARN("%s:%s: loading EVB policy for forwarding mode failed, using default.\n", + __func__, ed->ifname); + } else { + if (strcasestr(param, VAL_EVB_FMODE_BRIDGE)) { + ed->policy->smode = LLDP_EVB_CAPABILITY_FORWARD_STANDARD; + } + + if (strcasestr(param, VAL_EVB_FMODE_REFLECTIVE_RELAY)) { + ed->policy->smode = LLDP_EVB_CAPABILITY_FORWARD_REFLECTIVE_RELAY; + } + + LLDPAD_DBG("%s:%s: policy param fmode = %s.\n", __func__, ed->ifname, param); + LLDPAD_DBG("%s:%s: policy param smode = %x.\n", __func__, ed->ifname, ed->policy->smode); + } + + /* pull capabilities into policy */ + snprintf(arg_path, sizeof(arg_path), "%s%08x.capabilities", + TLVID_PREFIX, TLVID_8021Qbg(LLDP_EVB_SUBTYPE)); + + if (get_cfg(ed->ifname, arg_path, (void *) ¶m, CONFIG_TYPE_STRING)) { + LLDPAD_WARN("%s:%s: loading EVB policy for capabilities failed, using default.\n", + __func__, ed->ifname); + } else { + if (strcasestr(param, VAL_EVB_CAPA_RTE)) { + ed->policy->scap |= LLDP_EVB_CAPABILITY_PROTOCOL_RTE; + } + + if (strcasestr(param, VAL_EVB_CAPA_ECP)) { + ed->policy->scap |= LLDP_EVB_CAPABILITY_PROTOCOL_ECP; + } + + if (strcasestr(param, VAL_EVB_CAPA_VDP)) { + ed->policy->scap |= LLDP_EVB_CAPABILITY_PROTOCOL_VDP; + } + + LLDPAD_DBG("%s:%s: policy param capabilities = %s.\n", __func__, ed->ifname, param); + LLDPAD_DBG("%s:%s: policy param scap = %x.\n", __func__, ed->ifname, ed->policy->scap); + } + + /* pull rte into policy */ + snprintf(arg_path, sizeof(arg_path), "%s%08x.rte", + TLVID_PREFIX, TLVID_8021Qbg(LLDP_EVB_SUBTYPE)); + + if (get_cfg(ed->ifname, arg_path, (void *) ¶m, CONFIG_TYPE_STRING)) { + LLDPAD_WARN("%s:%s: loading EVB policy for rte failed, using default.\n", + __func__, ed->ifname); + } else { + ed->policy->rte = atoi(param); + + LLDPAD_DBG("%s:%s: policy param rte = %s.\n", __func__, ed->ifname, param); + LLDPAD_DBG("%s:%s: policy param rte = %i.\n", __func__, ed->ifname, ed->policy->rte); + } + + /* pull vsis into policy */ + snprintf(arg_path, sizeof(arg_path), "%s%08x.vsis", + TLVID_PREFIX, TLVID_8021Qbg(LLDP_EVB_SUBTYPE)); + + if (get_cfg(ed->ifname, arg_path, (void *) ¶m, CONFIG_TYPE_STRING)) { + LLDPAD_WARN("%s:%s: loading EVB policy for vsis failed, using default.\n", + __func__, ed->ifname); + } else { + ed->policy->svsi = atoi(param); + + LLDPAD_DBG("%s:%s: policy param vsis = %s.\n", __func__, ed->ifname, param); + LLDPAD_DBG("%s:%s: policy param vsis = %i.\n", __func__, ed->ifname, ed->policy->svsi); + } + + /* load last used EVB TLV ... */ + ed->tie = (struct tlv_info_evb *) calloc(1, sizeof(struct tlv_info_evb)); + + if (!ed->tie) { + free(ed->policy); + return ENOMEM; + } + + if (get_config_tlvinfo_bin(ed->ifname, TLVID_8021Qbg(LLDP_EVB_SUBTYPE), + (void *)ed->tie, sizeof(struct tlv_info_evb))) { + LLDPAD_INFO("%s:%s: loading last used EVB TLV failed, using default.\n", + __func__, ed->ifname); + hton24(ed->tie->oui, LLDP_MOD_EVB); + ed->tie->smode = ed->policy->smode; + ed->tie->cmode = 0x0; + ed->tie->scap = ed->policy->scap; + ed->tie->ccap = 0x0; + ed->tie->svsi = LLDP_EVB_DEFAULT_SVSI; + ed->tie->cvsi = 0x0; + ed->tie->rte = LLDP_EVB_DEFAULT_RTE; + } else { + LLDPAD_INFO("%s(%i): loaded last used EVB TLV from file.\n", __FILE__, __LINE__); + } + + return 0; +} + +static int evb_bld_tlv(struct evb_data *ed) +{ + int rc = 0; + + if (!port_find_by_name(ed->ifname)) { + rc = EEXIST; + goto out_err; + } + + if (!init_cfg()) { + rc = ENOENT; + goto out_err; + } + + if (evb_bld_cfg_tlv(ed)) { + LLDPAD_ERR("%s:%s:evb_bld_cfg_tlv() failed\n", + __func__, ed->ifname); + rc = EINVAL; + goto out_err_destroy; + } + + return rc; + +out_err_destroy: + destroy_cfg(); +out_err: + return rc; +} + +static void evb_free_data(struct evb_user_data *ud) +{ + struct evb_data *ed; + if (ud) { + while (!LIST_EMPTY(&ud->head)) { + ed = LIST_FIRST(&ud->head); + LIST_REMOVE(ed, entry); + evb_free_tlv(ed); + free(ed); + } + } +} + +struct packed_tlv *evb_gettlv(struct port *port) +{ + int size; + struct evb_data *ed; + struct packed_tlv *ptlv = NULL; + + ed = evb_data(port->ifname); + if (!ed) + goto out_err; + + evb_free_tlv(ed); + + if (evb_bld_tlv(ed)) { + LLDPAD_ERR("%s:%s evb_bld_tlv failed\n", + __func__, port->ifname); + goto out_err; + } + + size = TLVSIZE(ed->evb); + + if (!size) + goto out_err; + + ptlv = create_ptlv(); + if (!ptlv) + goto out_err; + + ptlv->tlv = malloc(size); + if (!ptlv->tlv) + goto out_free; + + ptlv->size = 0; + PACK_TLV_AFTER(ed->evb, ptlv, size, out_free); + return ptlv; + +out_free: + /* FIXME: free function returns pointer ? */ + ptlv = free_pkd_tlv(ptlv); +out_err: + LLDPAD_ERR("%s:%s: failed\n", __func__, port->ifname); + return NULL; +} + +/* evb_check_and_fill + * + * checks values received in TLV and takes over some values + */ +int evb_check_and_fill(struct evb_data *ed, struct tlv_info_evb *tie) +{ + /* sanity check of received data in tie */ + if ((tie->smode & (LLDP_EVB_CAPABILITY_FORWARD_STANDARD | + LLDP_EVB_CAPABILITY_FORWARD_REFLECTIVE_RELAY)) == 0) { + LLDPAD_ERR("Neither standard nor rr set as forwarding mode !"); + return TLV_ERR; + } + + if ((tie->svsi < 0) || (tie->svsi > LLDP_EVB_DEFAULT_MAX_VSI)) { + LLDPAD_ERR("nr of supported vsis (%i) exceeds allow value range !", tie->svsi); + return TLV_ERR; + } + + if ((tie->cvsi < 0) || (tie->cvsi > LLDP_EVB_DEFAULT_MAX_VSI)) { + LLDPAD_ERR("nr of configured vsis (%i) exceeds allow value range !", tie->cvsi); + return TLV_ERR; + } + + /* check bridge capabilities against local policy*/ + /* if bridge supports RR and we support it as well, request it + * by setting smode in tlv to be sent out (ed->tie->smode) */ + if ( (tie->smode & ed->policy->smode) =+ LLDP_EVB_CAPABILITY_FORWARD_REFLECTIVE_RELAY ) { + ed->tie->smode = LLDP_EVB_CAPABILITY_FORWARD_REFLECTIVE_RELAY; + } else { + ed->tie->smode = LLDP_EVB_CAPABILITY_FORWARD_STANDARD; + } + + /* If both sides support RTE, set it */ + if ((tie->scap & ed->policy->scap) & LLDP_EVB_CAPABILITY_PROTOCOL_RTE) + ed->tie->scap |= LLDP_EVB_CAPABILITY_PROTOCOL_RTE; + + /* If both sides support ECP, set it */ + if ((tie->scap & ed->policy->scap) & LLDP_EVB_CAPABILITY_PROTOCOL_ECP) + ed->tie->scap |= LLDP_EVB_CAPABILITY_PROTOCOL_ECP; + + /* If both sides support VDP, set it */ + if ((tie->scap & ed->policy->scap) & LLDP_EVB_CAPABILITY_PROTOCOL_VDP) + ed->tie->scap |= LLDP_EVB_CAPABILITY_PROTOCOL_VDP; + + /* If supported caps include VDP take over min value of both */ + if (ed->tie->scap & LLDP_EVB_CAPABILITY_PROTOCOL_VDP) + ed->tie->cvsi = MIN(ed->policy->svsi,tie->svsi); + + /* If both sides support RTE and value offer is > 0, set it */ + if ((ed->tie->scap & LLDP_EVB_CAPABILITY_PROTOCOL_RTE) && + (tie->rte > 0) && (ed->policy->rte > 0)) + ed->tie->rte = MAX(ed->policy->rte,tie->rte); + + if (set_config_tlvinfo_bin(ed->ifname, TLVID_8021Qbg(LLDP_EVB_SUBTYPE), + (void *)ed->tie, sizeof(struct tlv_info_evb))) { + LLDPAD_ERR("%s(%i): error saving tlv_info_evb !\n", __FILE__, __LINE__); + } else { + LLDPAD_INFO("%s(%i): saved tlv_info_evb to config !\n", __FILE__, __LINE__); + } + + /* maybe switch has already set the mode based on the saved info sent + * out on ifup */ + + if (tie->cmode == ed->tie->smode) + ed->tie->cmode = tie->cmode; + + return TLV_OK; +} + +/* evb_compare + * + * compare our own and received tlv_info_evb + */ +static int evb_compare(struct evb_data *ed, struct tlv_info_evb *tie) +{ + LLDPAD_DBG("%s(%i): \n", __func__, __LINE__); + + if (ed->tie->cmode == tie->cmode) + return 0; + else + return 1; +} + +/* evb_statemachine: + * + * handle possible states during EVB capabilities exchange + * + * possible states: EVB_OFFER_CAPABILITIES + * EVB_CONFIGURE + * EVB_CONFIRMATION + */ +static void evb_statemachine(struct evb_data *ed, struct tlv_info_evb *tie) +{ + switch(ed->state) { + case EVB_OFFER_CAPABILITIES: + /* waiting for valid packets to pour in + * if valid packet was received, + * - check parameters with what we have offered for this if, + * - fill structure with data, + * - enable local tx + * - switch to EVB_CONFIGURE + */ + LLDPAD_DBG("%s: state -> EVB_OFFER_CAPABILITIES\n", __func__); + if (evb_check_and_fill(ed, tie) != TLV_OK) { + LLDPAD_ERR("Invalid contents of EVB Cfg TLV !\n"); + return; + } + somethingChangedLocal(ed->ifname); /* trigger tx with new values */ + ed->state = EVB_CONFIGURE; + break; + case EVB_CONFIGURE: + /* we received a valid packet, if contents is same with our local settings + * we can switch state to EVB_CONFIRMATION.*/ + LLDPAD_DBG("%s: state -> EVB_CONFIGURE\n", __func__); + if (evb_compare(ed, tie)) { + ed->state= EVB_OFFER_CAPABILITIES; + } else { + LLDPAD_ERR("tlv_info_evb now equal !\n"); + ed->state = EVB_CONFIRMATION; + } + somethingChangedLocal(ed->ifname); + break; + case EVB_CONFIRMATION: + /* we are already in confirmation and received a new packet with + * different parameters ? Check parameters. switch state back to + * EVB_CONFIGURE ? */ + LLDPAD_DBG("%s: state -> EVB_CONFIRMATION\n", __func__); + break; + default: + LLDPAD_ERR("EVB statemachine reached invalid state !\n"); + break; + } +} + +/* + * evb_rchange: process RX TLV LLDPDU + * + * TLV not consumed on error + */ +static int evb_rchange(struct port *port, struct unpacked_tlv *tlv) +{ + int i; + struct evb_data *ed; + struct tlv_info_evb *tie = (struct tlv_info_evb *) tlv->info; + u8 oui_subtype[OUI_SUB_SIZE] = LLDP_OUI_SUBTYPE; + + if (!init_cfg()) { + return SUBTYPE_INVALID; + } + + ed = evb_data(port->ifname); + + if (!ed) + return SUBTYPE_INVALID; + + if (tlv->type == TYPE_127) { + /* check for length */ + if (tlv->length < (OUI_SUB_SIZE)) { + return TLV_ERR; + } + + /* check for oui */ + if (memcmp(tlv->info, &oui_subtype, OUI_SUB_SIZE)) { + return SUBTYPE_INVALID; + } + + /* disable rx if tx has been disabled by administrator */ + if (!is_tlv_txenabled(ed->ifname, TLVID_8021Qbg(LLDP_EVB_SUBTYPE))) { + LLDPAD_WARN("%s:%s:EVB Config disabled\n", + __func__, ed->ifname); + return TLV_OK; + } + + LLDPAD_DBG("%s(%i): received tlv:", __func__, __LINE__); + evb_dump_tlv(tlv); + evb_print_tlvinfo(tie); + + /* change state */ + evb_statemachine(ed, tie); + + /* check which values have been taken over */ + LLDPAD_DBG("%s(%i): new tlv:", __func__, __LINE__); + evb_print_tlvinfo(ed->tie); + } + + return TLV_OK; +} + +void evb_ifdown(char *ifname) +{ + struct evb_data *ed; + + LLDPAD_DBG("%s called !\n", __func__); + + ed = evb_data(ifname); + if (!ed) + goto out_err; + + free(ed->policy); + free(ed->tie); + LIST_REMOVE(ed, entry); + evb_free_tlv(ed); + free(ed); + LLDPAD_INFO("%s:port %s removed\n", __func__, ifname); + return; +out_err: + LLDPAD_ERR("%s:port %s remove failed\n", __func__, ifname); + + return; +} + +void evb_ifup(char *ifname) +{ + struct evb_data *ed; + struct evb_user_data *ud; + + ed = evb_data(ifname); + if (ed) { + LLDPAD_DBG("%s:%s exists\n", __func__, ifname); + goto out_err; + } + + /* not found, alloc/init per-port tlv data */ + ed = (struct evb_data *) calloc(1, sizeof(struct evb_data)); + if (!ed) { + LLDPAD_ERR("%s:%s malloc %ld failed\n", + __func__, ifname, sizeof(*ed)); + goto out_err; + } + strncpy(ed->ifname, ifname, IFNAMSIZ); + + if (!init_cfg()) { + LLDPAD_ERR("%s:%s init_cfg failed\n", __func__, ifname); + goto out_free; + } + + if (evb_init_cfg_tlv(ed)) { + LLDPAD_ERR("%s:%s evb_init_cfg_tlv failed\n", __func__, ifname); + goto out_free; + } + + ed->state = EVB_OFFER_CAPABILITIES; + + if (evb_bld_tlv(ed)) { + LLDPAD_ERR("%s:%s evb_bld_tlv failed\n", __func__, ifname); + goto out_free; + } + + ud = find_module_user_data_by_if(ifname, &lldp_head, LLDP_MOD_EVB); + LIST_INSERT_HEAD(&ud->head, ed, entry); + LLDPAD_DBG("%s:port %s added\n", __func__, ifname); + return; + +out_free: + free(ed->tie); + free(ed->policy); + free(ed); + +out_err: + return; +} + +static const struct lldp_mod_ops evb_ops = { + .lldp_mod_register = evb_register, + .lldp_mod_unregister = evb_unregister, + .lldp_mod_gettlv = evb_gettlv, + .lldp_mod_rchange = evb_rchange, + .lldp_mod_ifup = evb_ifup, + .lldp_mod_ifdown = evb_ifdown, + .get_arg_handler = evb_get_arg_handlers, +}; + +struct lldp_module *evb_register(void) +{ + struct lldp_module *mod; + struct evb_user_data *ud; + + mod = malloc(sizeof(*mod)); + if (!mod) { + LLDPAD_ERR("failed to malloc module data\n"); + log_message(MSG_ERR_SERVICE_START_FAILURE, + "%s", "failed to malloc module data"); + goto out_err; + } + ud = malloc(sizeof(struct evb_user_data)); + if (!ud) { + free(mod); + LLDPAD_ERR("failed to malloc module user data\n"); + log_message(MSG_ERR_SERVICE_START_FAILURE, + "%s", "failed to malloc module user data"); + goto out_err; + } + LIST_INIT(&ud->head); + mod->id = LLDP_MOD_EVB; + mod->ops = &evb_ops; + mod->data = ud; + LLDPAD_DBG("%s:done\n", __func__); + return mod; + +out_err: + LLDPAD_ERR("%s:failed\n", __func__); + return NULL; +} + +void evb_unregister(struct lldp_module *mod) +{ + if (mod->data) { + evb_free_data((struct evb_user_data *) mod->data); + free(mod->data); + } + free(mod); + LLDPAD_DBG("%s:done", __func__); +} diff --git a/lldp_evb_clif.c b/lldp_evb_clif.c new file mode 100644 index 0000000..7479d74 --- /dev/null +++ b/lldp_evb_clif.c @@ -0,0 +1,226 @@ +/******************************************************************************* + + implementation of EVB TLVs for LLDP + (c) Copyright IBM Corp. 2010 + + Author(s): Jens Osterkamp <jens at linux.vnet.ibm.com> + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + +*******************************************************************************/ + +#include "includes.h" +#include "common.h" +#include <stdio.h> +#include <syslog.h> +#include <sys/un.h> +#include <sys/stat.h> +#include "lldp_mod.h" +#include "lldptool.h" +#include "lldp.h" +#include "lldp_evb.h" +#include "lldp_evb_clif.h" + +void evb_print_cfg_tlv(u16, char *info); +int evb_print_help(); + +u32 evb_lookup_tlv_name(char *tlvid_str); + +static const struct lldp_mod_ops evb_ops_clif = { + .lldp_mod_register = evb_cli_register, + .lldp_mod_unregister = evb_cli_unregister, + .print_tlv = evb_print_tlv, + .lookup_tlv_name = evb_lookup_tlv_name, + .print_help = evb_print_help, +}; + +struct type_name_info evb_tlv_names[] = { + { (LLDP_MOD_EVB << 8) | LLDP_EVB_SUBTYPE, + "EVB Configuration TLV", + "evbCfg", evb_print_cfg_tlv }, + { INVALID_TLVID, NULL, NULL } +}; + +int evb_print_help() +{ + struct type_name_info *tn = &evb_tlv_names[0]; + + while (tn->type != INVALID_TLVID) { + if (tn->key && strlen(tn->key) && tn->name) { + printf(" %s", tn->key); + if (strlen(tn->key)+3 <= 8) + printf("\t"); + printf("\t: %s\n", tn->name); + } + tn++; + } + + return 0; +} + +struct lldp_module *evb_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_EVB; + mod->ops = &evb_ops_clif; + + return mod; +} + +void evb_cli_unregister(struct lldp_module *mod) +{ + free(mod); +} + +void evb_print_cfg_tlv(u16 len, char *info) +{ + u8 smode; + u8 scap; + u8 cmode; + u8 ccap; + u16 svsi; + u16 cvsi; + u8 rte; + + if (len != 9) { + printf("Bad Cfg TLV: %s\n", info); + return; + } + + if (!hexstr2bin(info, &smode, sizeof(smode))) { + printf("supported forwarding mode: (0x%02hhx)", smode); + + if (smode & LLDP_EVB_CAPABILITY_FORWARD_REFLECTIVE_RELAY) + printf(" reflective relay"); + + if (smode & LLDP_EVB_CAPABILITY_FORWARD_STANDARD) + printf(" standard 802.1Q"); + + printf("\n"); + } else { + printf("Unable to decode smode !\n"); + } + + if (!hexstr2bin(info+2, &scap, sizeof(scap))) { + printf("\tsupported capabilities: (0x%02hhx)", scap); + + if ( scap & LLDP_EVB_CAPABILITY_PROTOCOL_RTE) + printf(" RTE"); + + if ( scap & LLDP_EVB_CAPABILITY_PROTOCOL_ECP) + printf(" ECP"); + + if ( scap & LLDP_EVB_CAPABILITY_PROTOCOL_VDP) + printf(" VDP"); + + printf("\n"); + } else { + printf("Unable to decode scap !\n"); + } + + if (!hexstr2bin(info+4, &cmode, sizeof(cmode))) { + printf("\tconfigured forwarding mode: (0x%02hhx)", cmode); + + if (cmode & LLDP_EVB_CAPABILITY_FORWARD_REFLECTIVE_RELAY) + printf(" reflective relay"); + + if (cmode & LLDP_EVB_CAPABILITY_FORWARD_STANDARD) + printf(" standard 802.1Q"); + + printf("\n"); + } else { + printf("Unable to decode cmode !\n"); + } + + if (!hexstr2bin(info+6, &ccap, sizeof(ccap))) { + printf("\tconfigured capabilities: (0x%02hhx)", ccap); + + if ( ccap & LLDP_EVB_CAPABILITY_PROTOCOL_RTE) + printf(" RTE"); + + if ( ccap & LLDP_EVB_CAPABILITY_PROTOCOL_ECP) + printf(" ECP"); + + if ( ccap & LLDP_EVB_CAPABILITY_PROTOCOL_VDP) + printf(" VDP"); + + printf("\n"); + } else { + printf("Unable to decode ccap !\n"); + } + + if (!hexstr2bin(info+8, (u8 *)&svsi, sizeof(svsi))) { + printf("\tno. of supported VSIs: %04i\n",svsi); + } else { + printf("Unable to decode svsi !\n"); + } + + if (!hexstr2bin(info+12, (u8 *)&cvsi, sizeof(cvsi))) { + printf("\tno. of configured VSIs: %04i\n",cvsi); + } else { + printf("Unable to decode cvsi !\n"); + } + + if (!hexstr2bin(info+16, &rte, sizeof(rte))) { + printf("\tRTE: %i\n",rte); + } else { + printf("Unable to decode cvsi !\n"); + } + + printf("\n"); +} + +/* return 1: if it printed the TLV + * 0: if it did not + */ +int evb_print_tlv(u32 tlvid, u16 len, char *info) +{ + struct type_name_info *tn = &evb_tlv_names[0]; + + while (tn->type != INVALID_TLVID) { + if (tlvid == tn->type) { + printf("%s\n", tn->name); + if (tn->print_info) { + printf("\t"); + tn->print_info(len-4, info); + } + return 1; + } + tn++; + } + + return 0; +} + +u32 evb_lookup_tlv_name(char *tlvid_str) +{ + struct type_name_info *tn = &evb_tlv_names[0]; + + while (tn->type != INVALID_TLVID) { + if (!strcasecmp(tn->key, tlvid_str)) + return tn->type; + tn++; + } + return INVALID_TLVID; +} + diff --git a/lldp_evb_cmds.c b/lldp_evb_cmds.c new file mode 100644 index 0000000..bda81c9 --- /dev/null +++ b/lldp_evb_cmds.c @@ -0,0 +1,519 @@ +/******************************************************************************* + + implementation of EVB TLVs for LLDP + (c) Copyright IBM Corp. 2010 + + Author(s): Jens Osterkamp <jens at linux.vnet.ibm.com> + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + +*******************************************************************************/ + +#include "includes.h" +#include "common.h" +#include <stdio.h> +#include <syslog.h> +#include <sys/un.h> +#include <sys/stat.h> +#include <arpa/inet.h> +#include <string.h> +#include "lldpad.h" +#include "ctrl_iface.h" +#include "lldp.h" +#include "lldp_evb.h" +#include "lldp_mand_clif.h" +#include "lldp_evb_clif.h" +#include "lldp/ports.h" +#include "libconfig.h" +#include "config.h" +#include "clif_msgs.h" +#include "lldp/states.h" + +static int get_arg_tlvtxenable(struct cmd *, char *, char *, char *); +static int set_arg_tlvtxenable(struct cmd *, char *, char *, char *); + +static int get_arg_fmode(struct cmd *, char *, char *, char *); +static int set_arg_fmode(struct cmd *, char *, char *, char *); + +static int get_arg_rte(struct cmd *, char *, char *, char *); +static int set_arg_rte(struct cmd *, char *, char *, char *); + +static int get_arg_vsis(struct cmd *, char *, char *, char *); +static int set_arg_vsis(struct cmd *, char *, char *, char *); + +static int get_arg_capabilities(struct cmd *, char *, char *, char *); +static int set_arg_capabilities(struct cmd *, char *, char *, char *); + +static struct arg_handlers arg_handlers[] = { + { ARG_EVB_FORWARDING_MODE, get_arg_fmode, set_arg_fmode }, + { ARG_EVB_CAPABILITIES, get_arg_capabilities, set_arg_capabilities }, + { ARG_EVB_VSIS, get_arg_vsis, set_arg_vsis }, + { ARG_EVB_RTE, get_arg_rte, set_arg_rte }, + { ARG_TLVTXENABLE, get_arg_tlvtxenable, set_arg_tlvtxenable }, + { NULL } +}; + +static int get_arg_tlvtxenable(struct cmd *cmd, char *arg, char *argvalue, + char *obuf) +{ + int value; + char *s; + char arg_path[EVB_BUF_SIZE]; + + if (cmd->cmd != cmd_gettlv) + return cmd_invalid; + + switch (cmd->tlvid) { + case (LLDP_MOD_EVB << 8) | LLDP_EVB_SUBTYPE: + snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", + TLVID_PREFIX, cmd->tlvid, arg); + + if (get_cfg(cmd->ifname, arg_path, (void *)&value, + CONFIG_TYPE_BOOL)) + value = false; + break; + case INVALID_TLVID: + return cmd_invalid; + default: + return cmd_not_applicable; + } + + if (value) + s = VAL_YES; + else + s = VAL_NO; + + sprintf(obuf, "%02x%s%04x%s", (unsigned int) strlen(arg), arg, + (unsigned int) strlen(s), s); + + return cmd_success; +} + +static int set_arg_tlvtxenable(struct cmd *cmd, char *arg, char *argvalue, + char *obuf) +{ + int value; + char arg_path[EVB_BUF_SIZE]; + + if (cmd->cmd != cmd_settlv) + return cmd_invalid; + + switch (cmd->tlvid) { + case (LLDP_MOD_EVB << 8) | LLDP_EVB_SUBTYPE: + break; + case INVALID_TLVID: + return cmd_invalid; + default: + return cmd_not_applicable; + } + + if (!strcasecmp(argvalue, VAL_YES)) + value = 1; + else if (!strcasecmp(argvalue, VAL_NO)) + value = 0; + else + return cmd_invalid; + + snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", + TLVID_PREFIX, cmd->tlvid, arg); + + if (set_cfg(cmd->ifname, arg_path, (void *)&value, CONFIG_TYPE_BOOL)) + return cmd_failed; + + somethingChangedLocal(cmd->ifname); + + return cmd_success; +} + +static int get_arg_fmode(struct cmd *cmd, char *arg, char *argvalue, + char *obuf) +{ + char *s; + struct evb_data *ed; + + if (cmd->cmd != cmd_gettlv) + return cmd_invalid; + + switch (cmd->tlvid) { + case (LLDP_MOD_EVB << 8) | LLDP_EVB_SUBTYPE: + break; + case INVALID_TLVID: + return cmd_invalid; + default: + return cmd_not_applicable; + } + + ed = evb_data((char *) &cmd->ifname); + + if (!ed) + return cmd_invalid; + + if (ed->policy->smode & LLDP_EVB_CAPABILITY_FORWARD_REFLECTIVE_RELAY) + s = VAL_EVB_FMODE_REFLECTIVE_RELAY; + else + s = VAL_EVB_FMODE_BRIDGE; + + sprintf(obuf, "%02x%s%04x%s", (unsigned int) strlen(arg), arg, + (unsigned int) strlen(s), s); + + return cmd_success; +} + +static int set_arg_fmode(struct cmd *cmd, char *arg, char *argvalue, + char *obuf) +{ + u8 smode; + char arg_path[EVB_BUF_SIZE]; + struct evb_data *ed; + + if (cmd->cmd != cmd_settlv) + return cmd_invalid; + + switch (cmd->tlvid) { + case (LLDP_MOD_EVB << 8) | LLDP_EVB_SUBTYPE: + break; + case INVALID_TLVID: + return cmd_invalid; + default: + return cmd_not_applicable; + } + + ed = evb_data((char *) &cmd->ifname); + + if (!ed) + return cmd_invalid; + + smode = 0; + + if (!strcasecmp(argvalue, VAL_EVB_FMODE_BRIDGE)) { + smode = LLDP_EVB_CAPABILITY_FORWARD_STANDARD; + } + + if (!strcasecmp(argvalue, VAL_EVB_FMODE_REFLECTIVE_RELAY)) { + smode = LLDP_EVB_CAPABILITY_FORWARD_REFLECTIVE_RELAY; + } + + if (smode == 0) { + return cmd_invalid; + } else { + ed->policy->smode = smode; + } + + snprintf(arg_path, sizeof(arg_path), "%s%08x.fmode", + TLVID_PREFIX, cmd->tlvid); + + if (set_cfg(ed->ifname, arg_path, (void *) &argvalue, CONFIG_TYPE_STRING)) { + printf("%s:%s: saving EVB forwarding mode failed.\n", + __func__, ed->ifname); + return cmd_invalid; + } + + somethingChangedLocal(cmd->ifname); + + return cmd_success; +} + +static int get_arg_capabilities(struct cmd *cmd, char *arg, char *argvalue, + char *obuf) +{ + int c; + char *s, *t; + struct evb_data *ed; + + printf("%s(%i): arg %s, argvalue %s !\n", __func__, __LINE__, arg, argvalue); + + s = t = malloc(EVB_BUF_SIZE); + + if (!s) + goto out_free; + + memset(s, 0, EVB_BUF_SIZE); + + if (cmd->cmd != cmd_gettlv) + goto out_free; + + switch (cmd->tlvid) { + case (LLDP_MOD_EVB << 8) | LLDP_EVB_SUBTYPE: + break; + case INVALID_TLVID: + goto out_free; + default: + free(t); + return cmd_not_applicable; + } + + ed = evb_data((char *) &cmd->ifname); + if (!ed) + goto out_free; + + if (ed->policy->scap & LLDP_EVB_CAPABILITY_PROTOCOL_RTE) { + c = sprintf(s, VAL_EVB_CAPA_RTE " "); + if (c <= 0) + goto out_free; + s += c; + } + + if (ed->policy->scap & LLDP_EVB_CAPABILITY_PROTOCOL_ECP) { + c = sprintf(s, VAL_EVB_CAPA_ECP " "); + if (c <= 0) + goto out_free; + s += c; + } + + if (ed->policy->scap & LLDP_EVB_CAPABILITY_PROTOCOL_VDP) { + c = sprintf(s, VAL_EVB_CAPA_VDP " "); + if (c <= 0) + goto out_free; + s += c; + } + + sprintf(obuf, "%02x%s%04x%s", (unsigned int) strlen(arg), arg, + (unsigned int) strlen(t), t); + + free(t); + return cmd_success; + +out_free: + free(t); + return cmd_invalid; +} + +static int set_arg_capabilities(struct cmd *cmd, char *arg, char *argvalue, + char *obuf) +{ + u8 scap = 0; + char arg_path[EVB_BUF_SIZE]; + struct evb_data *ed; + + if (cmd->cmd != cmd_settlv) + return cmd_invalid; + + switch (cmd->tlvid) { + case (LLDP_MOD_EVB << 8) | LLDP_EVB_SUBTYPE: + break; + case INVALID_TLVID: + return cmd_invalid; + default: + return cmd_not_applicable; + } + + ed = evb_data((char *) &cmd->ifname); + + if (!ed) + return cmd_invalid; + + if (strcasestr(argvalue, VAL_EVB_CAPA_RTE)) + scap |= LLDP_EVB_CAPABILITY_PROTOCOL_RTE; + + if (strcasestr(argvalue, VAL_EVB_CAPA_ECP)) + scap |= LLDP_EVB_CAPABILITY_PROTOCOL_ECP; + + if (strcasestr(argvalue, VAL_EVB_CAPA_VDP)) + scap |= LLDP_EVB_CAPABILITY_PROTOCOL_VDP; + + ed->policy->scap = scap; + + snprintf(arg_path, sizeof(arg_path), "%s%08x.capabilities", + TLVID_PREFIX, cmd->tlvid); + + if (set_cfg(ed->ifname, arg_path, (void *) &argvalue, CONFIG_TYPE_STRING)) { + printf("%s:%s: saving EVB capabilities failed.\n", + __func__, ed->ifname); + return cmd_invalid; + } + + somethingChangedLocal(cmd->ifname); + + return cmd_success; +} + +static int get_arg_rte(struct cmd *cmd, char *arg, char *argvalue, + char *obuf) +{ + char s[EVB_BUF_SIZE]; + struct evb_data *ed; + + if (cmd->cmd != cmd_gettlv) + return cmd_invalid; + + switch (cmd->tlvid) { + case (LLDP_MOD_EVB << 8) | LLDP_EVB_SUBTYPE: + break; + case INVALID_TLVID: + return cmd_invalid; + default: + return cmd_not_applicable; + } + + ed = evb_data((char *) &cmd->ifname); + if (!ed) + return cmd_invalid; + + if (sprintf(s, "%i", ed->policy->rte) <= 0) + return cmd_invalid; + + sprintf(obuf, "%02x%s%04x%s", (unsigned int) strlen(arg), arg, + (unsigned int) strlen(s), s); + + return cmd_success; +} + +static int set_arg_rte(struct cmd *cmd, char *arg, char *argvalue, + char *obuf) +{ + int value, err; + char arg_path[EVB_BUF_SIZE]; + struct evb_data *ed = NULL; + + if (cmd->cmd != cmd_settlv) + goto out_err; + + switch (cmd->tlvid) { + case (LLDP_MOD_EVB << 8) | LLDP_EVB_SUBTYPE: + break; + case INVALID_TLVID: + goto out_err; + default: + return cmd_not_applicable; + } + + ed = evb_data((char *) &cmd->ifname); + + if (!ed) + return cmd_invalid; + + value = atoi(argvalue); + + if ((value < 0)) + goto out_err; + + ed->policy->rte = value; + + err = snprintf(arg_path, sizeof(arg_path), "%s%08x.rte", + TLVID_PREFIX, cmd->tlvid); + + if (err < 0) + goto out_err; + + if (err < 0) + goto out_err; + + if (set_cfg(ed->ifname, arg_path, (void *) &argvalue, CONFIG_TYPE_STRING)) + goto out_err; + + somethingChangedLocal(cmd->ifname); + + return cmd_success; + +out_err: + printf("%s:%s: saving EVB rte failed.\n", __func__, ed->ifname); + return cmd_invalid; +} + +static int get_arg_vsis(struct cmd *cmd, char *arg, char *argvalue, + char *obuf) +{ + char s[EVB_BUF_SIZE]; + struct evb_data *ed; + + if (cmd->cmd != cmd_gettlv) + return cmd_invalid; + + switch (cmd->tlvid) { + case (LLDP_MOD_EVB << 8) | LLDP_EVB_SUBTYPE: + break; + case INVALID_TLVID: + return cmd_invalid; + default: + return cmd_not_applicable; + } + + ed = evb_data((char *) &cmd->ifname); + if (!ed) + return cmd_invalid; + + if (sprintf(s, "%04i", ed->policy->svsi) <= 0) + return cmd_invalid; + + sprintf(obuf, "%02x%s%04x%s", (unsigned int) strlen(arg), arg, + (unsigned int) strlen(s), s); + + return cmd_success; +} + +static int set_arg_vsis(struct cmd *cmd, char *arg, char *argvalue, + char *obuf) +{ + int value, err; + char arg_path[EVB_BUF_SIZE]; + char svalue[10]; + char *sv; + struct evb_data *ed = NULL; + + ed = evb_data((char *) &cmd->ifname); + + if (!ed) + return cmd_invalid; + + if (cmd->cmd != cmd_settlv) + goto out_err; + + switch (cmd->tlvid) { + case (LLDP_MOD_EVB << 8) | LLDP_EVB_SUBTYPE: + break; + case INVALID_TLVID: + goto out_err; + default: + return cmd_not_applicable; + } + + value = atoi(argvalue); + + if ((value < 0) || (value > LLDP_EVB_DEFAULT_MAX_VSI)) + goto out_err; + + ed->policy->svsi = value; + + err = snprintf(arg_path, sizeof(arg_path), "%s%08x.vsis", + TLVID_PREFIX, cmd->tlvid); + + if (err < 0) + goto out_err; + + err = snprintf(svalue, sizeof(svalue), "%i", value); + + if (err < 0) + goto out_err; + + sv = &svalue[0]; + + if (set_cfg(ed->ifname, arg_path, (void *) &sv, CONFIG_TYPE_STRING)) + goto out_err; + + somethingChangedLocal(cmd->ifname); + + return cmd_success; + +out_err: + printf("%s:%s: saving EVB vsis failed.\n", __func__, ed->ifname); + return cmd_invalid; +} + +struct arg_handlers *evb_get_arg_handlers() +{ + return &arg_handlers[0]; +} diff --git a/lldpad.c b/lldpad.c index a89b5a4..571da31 100644 --- a/lldpad.c +++ b/lldpad.c @@ -49,6 +49,7 @@ #include "lldp_dcbx.h" #include "lldp_med.h" #include "lldp_8023.h" +#include "lldp_evb.h" #include "config.h" #include "lldpad_shm.h" #include "clif.h" @@ -63,6 +64,7 @@ struct lldp_module *(*register_tlv_table[])(void) = { dcbx_register, med_register, ieee8023_register, + evb_register, NULL, }; diff --git a/lldptool.c b/lldptool.c index 5cf2846..7e166fe 100644 --- a/lldptool.c +++ b/lldptool.c @@ -39,6 +39,7 @@ #include "lldp_med_clif.h" #include "lldp_8023_clif.h" #include "lldp_dcbx_clif.h" +#include "lldp_evb_clif.h" #include "lldptool.h" #include "lldptool_cli.h" #include "lldp_mod.h" @@ -156,6 +157,7 @@ struct lldp_module *(*register_tlv_table[])(void) = { ieee8023_cli_register, med_cli_register, dcbx_cli_register, + evb_cli_register, NULL, }; -- 1.7.2.3
added an additional check for existence of the ifup operation. If it does not exist, just skip it. Signed-off-by: Jens Osterkamp <jens at linux.vnet.ibm.com> --- config.c | 6 ++++-- 1 files changed, 4 insertions(+), 2 deletions(-) diff --git a/config.c b/config.c index 5cfac83..26afe3b 100644 --- a/config.c +++ b/config.c @@ -370,8 +370,10 @@ void init_ports(void) } else if (check_link_status(p->if_name)) { add_adapter(p->if_name); - LIST_FOREACH(np, &lldp_head, lldp) - np->ops->lldp_mod_ifup(p->if_name); + LIST_FOREACH(np, &lldp_head, lldp) { + if (np->ops->lldp_mod_ifup) + np->ops->lldp_mod_ifup(p->if_name); + } set_lldp_port_enable_state(p->if_name, 1); } p++; -- 1.7.2.3
Signed-off-by: Jens Osterkamp <jens at linux.vnet.ibm.com> --- configure.ac | 5 +++++ 1 files changed, 5 insertions(+), 0 deletions(-) diff --git a/configure.ac b/configure.ac index 619c6c4..8b9d08a 100644 --- a/configure.ac +++ b/configure.ac @@ -12,6 +12,11 @@ PKG_CHECK_MODULES(LIBCONFIG, libconfig, AC_SUBST(LIBCONFIG_CFLAGS) AC_SUBST(LIBCONFIG_LIBS) +PKG_CHECK_MODULES(LIBNL, libnl-1 >= 1.1) +AC_SUBST(LIBNL_CFLAGS) +AC_SUBST(LIBNL_LIBS) + +AC_CHECK_LIB(nl, rtnl_link_get_by_name) AC_CONFIG_SUBDIRS([libconfig-1.3.2]) AC_CONFIG_FILES([Makefile include/version.h lldpad.spec lldpad.pc dcbd.pc]) AC_OUTPUT -- 1.7.2.3
modifies the setup of the netlink socket in drv_cfg.c to use pid 0 instead of the processes pid. Also replaces bind with connect to allow the reception of netlink messages from libvirt. Preparation patch for communication between libvirt and lldpad. Signed-off-by: Gerhard Stenzel <gstenzel at linux.vnet.ibm.com> --- drv_cfg.c | 5 +++-- 1 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drv_cfg.c b/drv_cfg.c index 6c02555..23c11f3 100644 --- a/drv_cfg.c +++ b/drv_cfg.c @@ -71,9 +71,9 @@ static int init_socket(void) memset((void *)&snl, 0, sizeof(struct sockaddr_nl)); snl.nl_family = AF_NETLINK; - snl.nl_pid = getpid(); + snl.nl_pid = 0; - if (bind(sd, (struct sockaddr *)&snl, sizeof(struct sockaddr_nl)) < 0) { + if (connect(sd, (struct sockaddr *)&snl, sizeof(struct sockaddr_nl)) < 0) { close(sd); return -EIO; } @@ -195,6 +195,7 @@ static struct nlmsghdr *get_msg(unsigned int seq) nlh = NULL; break; } + fprintf(stderr, "%s:%s: nlmsg_type: %d\n", __FILE__, __FUNCTION__, nlh->nlmsg_type); if ((nlh->nlmsg_type == RTM_GETDCB || nlh->nlmsg_type == RTM_SETDCB) && nlh->nlmsg_seq == seq) { -- 1.7.2.3
Jens Osterkamp
2010-Nov-22 15:50 UTC
[PATCH 08/10] lldpad support for libvirt netlink message
This code receives a IEEE 802.1Qbg virtual station instance from libvirt in a SETLINK message. The parsed VSI is then handed over to VDP for processing. The VDP state machine processes the VSI while libvirt polls the result using GETLINK. Requires at least Linux kernel 2.6.35-rc1. Signed-off-by: Jens Osterkamp <jens at linux.vnet.ibm.com> Signed-off-by: Gerhard Stenzel <gstenzel at linux.vnet.ibm.com> Signed-off-by: Stefan Berger <stefanb at linux.vnet.ibm.com> --- event_iface.c | 589 ++++++++++++++++++++++++++++++++++++++++++++++++- include/event_iface.h | 1 + lldp_vdp.c | 2 +- lldpad.c | 10 + 4 files changed, 591 insertions(+), 11 deletions(-) diff --git a/event_iface.c b/event_iface.c index 439e4d0..e4cb8eb 100644 --- a/event_iface.c +++ b/event_iface.c @@ -3,6 +3,13 @@ LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2010 Intel Corporation. + implementation of libvirt netlink interface + (c) Copyright IBM Corp. 2010 + + Author(s): Jens Osterkamp <jens at linux.vnet.ibm.com> + Stefan Berger <stefanb at linux.vnet.ibm.com> + Gerhard Stenzel <gstenzel at linux.vnet.ibm.com> + This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. @@ -31,12 +38,16 @@ #include <sys/socket.h> #include <linux/rtnetlink.h> #include <linux/if.h> +#include <linux/if_link.h> #include <linux/if_vlan.h> #include <linux/sockios.h> #include <syslog.h> #include <unistd.h> +#include <linux/netlink.h> +#include <netlink/msg.h> #include "lldpad.h" #include "lldp_mod.h" +#include "lldp_vdp.h" #include "common.h" #include "eloop.h" #include "drv_cfg.h" @@ -47,6 +58,7 @@ #include "lldp/l2_packet.h" #include "config.h" #include "lldp/states.h" +#include "messages.h" static void event_if_decode_rta(int type, struct rtattr *rta); @@ -55,6 +67,32 @@ static void event_if_decode_rta(int type, struct rtattr *rta); static char *device_name = NULL; static int link_status = 0; +static struct nla_policy ifla_vf_policy[IFLA_VF_MAX + 1] +{ + [IFLA_VF_MAC] = { .minlen = sizeof(struct ifla_vf_mac), + .maxlen = sizeof(struct ifla_vf_mac)}, + [IFLA_VF_VLAN] = { .minlen = sizeof(struct ifla_vf_vlan), + .maxlen = sizeof(struct ifla_vf_vlan)}, +}; + +static struct nla_policy ifla_vf_ports_policy[IFLA_VF_PORT_MAX + 1] +{ + [IFLA_VF_PORT] = { .type = NLA_NESTED }, +}; + +static struct nla_policy ifla_port_policy[IFLA_PORT_MAX + 1] +{ + [IFLA_PORT_VF] = { .type = NLA_U32 }, + [IFLA_PORT_PROFILE] = { .type = NLA_STRING }, + [IFLA_PORT_VSI_TYPE] = { .minlen = sizeof(struct ifla_port_vsi) }, + [IFLA_PORT_INSTANCE_UUID] = { .minlen = PORT_UUID_MAX, + .maxlen = PORT_UUID_MAX, }, + [IFLA_PORT_HOST_UUID] = { .minlen = PORT_UUID_MAX, + .maxlen = PORT_UUID_MAX, }, + [IFLA_PORT_REQUEST] = { .type = NLA_U8 }, + [IFLA_PORT_RESPONSE] = { .type = NLA_U16 }, +}; + static void event_if_decode_rta(int type, struct rtattr *rta) { @@ -220,15 +258,506 @@ static void event_if_decode_nlmsg(int route_type, void *data, int len) } } - static void event_if_process_recvmsg(struct nlmsghdr *nlmsg) { - - /* print out details */ + LLDPAD_DBG("%s:%s: nlmsg_type: %d", __FILE__, __FUNCTION__, nlmsg->nlmsg_type); event_if_decode_nlmsg(nlmsg->nlmsg_type, NLMSG_DATA(nlmsg), NLMSG_PAYLOAD(nlmsg, 0)); } +static int event_if_parse_getmsg(struct nlmsghdr *nlh, int *ifindex, + char *ifname) +{ + struct nlattr *tb[IFLA_MAX+1]; + struct ifinfomsg *ifinfo; + + if (nlmsg_parse(nlh, sizeof(struct ifinfomsg), + (struct nlattr **)&tb, IFLA_MAX, NULL)) { + LLDPAD_ERR("Error parsing GETLINK request..."); + return -EINVAL; + } + + if (tb[IFLA_IFNAME]) { + ifname = (char *)RTA_DATA(tb[IFLA_IFNAME]); + LLDPAD_DBG("IFLA_IFNAME=%s", ifname); + } else { + ifinfo = (struct ifinfomsg *)NLMSG_DATA(nlh); + *ifindex = ifinfo->ifi_index; + LLDPAD_DBG("interface index: %d", ifinfo->ifi_index); + } + + return 0; +} + +static int event_if_parse_setmsg(struct nlmsghdr *nlh) +{ + struct nlattr *tb[IFLA_MAX+1], + *tb3[IFLA_PORT_MAX+1], + *tb_vfinfo[IFLA_VF_MAX+1], + *tb_vfinfo_list; + struct vsi_profile *profile, *p; + struct ifinfomsg *ifinfo; + char *ifname; + int rem; + + profile = malloc(sizeof(struct vsi_profile)); + if (!profile) + return -ENOMEM; + memset(profile, 0, sizeof(struct vsi_profile)); + + if (nlmsg_parse(nlh, sizeof(struct ifinfomsg), + (struct nlattr **)&tb, IFLA_MAX, NULL)) { + LLDPAD_ERR("Error parsing request...\n"); + return -EINVAL; + } + + LLDPAD_DBG("%s(%d): nlmsg_len %i", __FILE__, __LINE__, nlh->nlmsg_len); + + if (tb[IFLA_IFNAME]) { + ifname = (char *)RTA_DATA(tb[IFLA_IFNAME]); + LLDPAD_DBG("IFLA_IFNAME=%s", ifname); + } else { + ifinfo = (struct ifinfomsg *)NLMSG_DATA(nlh); + LLDPAD_DBG("interface index: %d", ifinfo->ifi_index); + } + + if (!tb[IFLA_VFINFO_LIST]) { + LLDPAD_ERR("IFLA_VFINFO_LIST missing."); + return -EINVAL; + } else { + LLDPAD_DBG("FOUND IFLA_VFINFO_LIST!"); + } + + nla_for_each_nested(tb_vfinfo_list, tb[IFLA_VFINFO_LIST], rem) { + if (nla_type(tb_vfinfo_list) != IFLA_VF_INFO) { + LLDPAD_ERR("nested parsing of IFLA_VFINFO_LIST failed."); + return -EINVAL; + } + + if (nla_parse_nested(tb_vfinfo, IFLA_VF_MAX, tb_vfinfo_list, + ifla_vf_policy)) { + LLDPAD_ERR("nested parsing of IFLA_VF_INFO failed."); + return -EINVAL; + } + } + + if (tb_vfinfo[IFLA_VF_MAC]) { + struct ifla_vf_mac *mac = RTA_DATA(tb_vfinfo[IFLA_VF_MAC]); + u8 *m = mac->mac; + LLDPAD_DBG("IFLA_VF_MAC=%2x:%2x:%2x:%2x:%2x:%2x", + m[0], m[1], m[2], m[3], m[4], m[5]); + memcpy(&profile->mac, m, ETH_ALEN); + } + + if (tb_vfinfo[IFLA_VF_VLAN]) { + struct ifla_vf_vlan *vlan = RTA_DATA(tb_vfinfo[IFLA_VF_VLAN]); + LLDPAD_DBG("IFLA_VF_VLAN=%d", vlan->vlan); + profile->vlan = (u16) vlan->vlan; + } + + if (tb[IFLA_VF_PORTS]) { + struct nlattr *tb_vf_ports; + + LLDPAD_DBG("FOUND IFLA_VF_PORTS"); + + nla_for_each_nested(tb_vf_ports, tb[IFLA_VF_PORTS], rem) { + + LLDPAD_DBG("ITERATING"); + + if (nla_type(tb_vf_ports) != IFLA_VF_PORT) { + LLDPAD_DBG("not a IFLA_VF_PORT. skipping"); + continue; + } + + if (nla_parse_nested(tb3, IFLA_PORT_MAX, tb_vf_ports, + ifla_port_policy)) { + LLDPAD_ERR("nested parsing on level 2 failed."); + return -EINVAL; + } + + if (tb3[IFLA_PORT_VF]) { + LLDPAD_DBG("IFLA_PORT_VF=%d", *(uint32_t*)(RTA_DATA(tb3[IFLA_PORT_VF]))); + } + + if (tb3[IFLA_PORT_PROFILE]) { + LLDPAD_DBG("IFLA_PORT_PROFILE=%s", (char *)RTA_DATA(tb3[IFLA_PORT_PROFILE])); + } + + if (tb3[IFLA_PORT_VSI_TYPE]) { + struct ifla_port_vsi *pvsi; + int tid = 0; + + pvsi = (struct ifla_port_vsi*)RTA_DATA(tb3[IFLA_PORT_VSI_TYPE]); + tid = pvsi->vsi_type_id[2] << 16 | + pvsi->vsi_type_id[1] << 8 | + pvsi->vsi_type_id[0]; + + LLDPAD_DBG("mgr_id : %d", pvsi->vsi_mgr_id); + LLDPAD_DBG("type_id : %d", tid); + LLDPAD_DBG("type_version : %d", pvsi->vsi_type_version); + + profile->mgrid = pvsi->vsi_mgr_id; + profile->id = tid; + profile->version = pvsi->vsi_type_version; + } + + if (tb3[IFLA_PORT_INSTANCE_UUID]) { + int i; + unsigned char *uuid; + uuid = (unsigned char *)RTA_DATA(tb3[IFLA_PORT_INSTANCE_UUID]); + + char instance[INSTANCE_STRLEN+2]; + instance2str(uuid, instance, sizeof(instance)); + LLDPAD_DBG("IFLA_PORT_INSTANCE_UUID=%s", instance); + + memcpy(&profile->instance, + RTA_DATA(tb3[IFLA_PORT_INSTANCE_UUID]), 16); + } + + if (tb3[IFLA_PORT_REQUEST]) { + LLDPAD_DBG("IFLA_PORT_REQUEST=%d", + *(uint8_t*)RTA_DATA(tb3[IFLA_PORT_REQUEST])); + profile->mode = *(uint8_t*)RTA_DATA(tb3[IFLA_PORT_REQUEST]); + } + + if (tb3[IFLA_PORT_RESPONSE]) { + LLDPAD_DBG("IFLA_PORT_RESPONSE=%d", + *(uint16_t*)RTA_DATA(tb3[IFLA_PORT_RESPONSE])); + profile->response = *(uint16_t*)RTA_DATA(tb3[IFLA_PORT_RESPONSE]); + } + } + } + + if (ifname) { + struct port *port = port_find_by_name(ifname); + + if (port) { + profile->port = port; + } else { + LLDPAD_ERR("%s(%i): Could not find port for %s", __func__, + __LINE__, ifname); + return -EEXIST; + } + } + + p = vdp_add_profile(profile); + + if (!p) { + free(profile); + return -EINVAL; + } + + vdp_somethingChangedLocal(profile, VDP_PROFILE_REQ); + vdp_vsi_sm_station(p); + + return 0; +} + +static void event_if_parseResponseMsg(struct nlmsghdr *nlh) +{ + struct nlattr *tb[IFLA_MAX+1], + *tb2[IFLA_VF_PORT_MAX + 1], + *tb3[IFLA_PORT_MAX+1]; + + if (nlmsg_parse(nlh, sizeof(struct ifinfomsg), + (struct nlattr **)&tb, IFLA_MAX, NULL)) { + LLDPAD_ERR("Error parsing netlink response..."); + return; + } + + if (tb[IFLA_IFNAME]) { + LLDPAD_DBG("IFLA_IFNAME=%s", (char *)RTA_DATA(tb[IFLA_IFNAME])); + } else { + struct ifinfomsg *ifinfo = (struct ifinfomsg *)NLMSG_DATA(nlh); + LLDPAD_DBG("interface index: %d", ifinfo->ifi_index); + } + + if (tb[IFLA_VF_PORTS]) { + if (nla_parse_nested(tb2, IFLA_VF_PORT_MAX, tb[IFLA_VF_PORTS], + ifla_vf_ports_policy)) { + LLDPAD_ERR("nested parsing on level 1 failed."); + return; + } + + if (tb2[IFLA_VF_PORT]) { + if (nla_parse_nested(tb3, IFLA_PORT_MAX, tb2[IFLA_VF_PORT], + ifla_port_policy)) { + LLDPAD_ERR("nested parsing on level 2 failed."); + return; + } + + if (tb3[IFLA_PORT_VF]) { + LLDPAD_DBG("IFLA_PORT_VF=%d", *(uint32_t*)(RTA_DATA(tb3[IFLA_PORT_VF]))); + } + + if (tb3[IFLA_PORT_PROFILE]) { + LLDPAD_DBG("IFLA_PORT_PROFILE=%s", (char *)RTA_DATA(tb3[IFLA_PORT_PROFILE])); + } + + if (tb3[IFLA_PORT_VSI_TYPE]) { + struct ifla_port_vsi *pvsi; + int tid = 0; + pvsi = (struct ifla_port_vsi*)RTA_DATA(tb3[IFLA_PORT_VSI_TYPE]); + tid = pvsi->vsi_type_id[2] << 16 | + pvsi->vsi_type_id[1] << 8 | + pvsi->vsi_type_id[0]; + LLDPAD_DBG("mgr_id : %d" + "type_id : %d" + "type_version : %d", + pvsi->vsi_mgr_id, + tid, + pvsi->vsi_type_version); + } + + if (tb3[IFLA_PORT_INSTANCE_UUID]) { + int i; + unsigned char *uuid; + uuid = (unsigned char *)RTA_DATA(tb3[IFLA_PORT_INSTANCE_UUID]); + + char instance[INSTANCE_STRLEN+2]; + instance2str(uuid, instance, sizeof(instance)); + LLDPAD_DBG("IFLA_PORT_INSTANCE_UUID=%s", &instance[0]); + } + + if (tb3[IFLA_PORT_REQUEST]) { + LLDPAD_DBG("IFLA_PORT_REQUEST=%d", + *(uint8_t*)RTA_DATA(tb3[IFLA_PORT_REQUEST])); + } + + if (tb3[IFLA_PORT_RESPONSE]) { + LLDPAD_DBG("IFLA_PORT_RESPONSE=%d", + *(uint16_t*)RTA_DATA(tb3[IFLA_PORT_RESPONSE])); + } + } + } +} + +struct nl_msg *event_if_constructResponse(struct nlmsghdr *nlh, int ifindex) +{ + struct nl_msg *nl_msg; + struct nlattr *vf_ports = NULL, *vf_port; + struct ifinfomsg ifinfo; + struct vdp_data *vd; + uint32_t pid = nlh->nlmsg_pid; + uint32_t seq = nlh->nlmsg_seq; + char *ifname = malloc(IFNAMSIZ); + struct vsi_profile *p; + + nl_msg = nlmsg_alloc(); + + if (!nl_msg) { + LLDPAD_ERR("%s(%i): Unable to allocate netlink message !", __func__, __LINE__); + return NULL; + } + + if (!if_indextoname(ifindex, ifname)) { + LLDPAD_ERR("%s(%i): No name found for interface with index %i !", __func__, __LINE__, + ifindex); + } + + vd = vdp_data(ifname); + if (!vd) { + LLDPAD_ERR("%s(%i): Could not find vdp_data for %s !", __func__, __LINE__, + ifname); + return NULL; + } + + free(ifname); + + if (nlmsg_put(nl_msg, pid, seq, NLMSG_DONE, 0, 0) == NULL) + goto err_exit; + + ifinfo.ifi_index = ifindex; + + if (nlmsg_append(nl_msg, &ifinfo, sizeof(ifinfo), NLMSG_ALIGNTO) < 0) + goto err_exit; + + vf_ports = nla_nest_start(nl_msg, IFLA_VF_PORTS); + + if (!vf_ports) + goto err_exit; + + /* loop over all existing profiles on this interface and + * put them into the nested IFLA_VF_PORT structure */ + LIST_FOREACH(p, &vd->profile_head, profile) { + if (p) { + vdp_print_profile(p); + + vf_port = nla_nest_start(nl_msg, IFLA_VF_PORT); + + if (!vf_port) + goto err_exit; + + if (nla_put(nl_msg, IFLA_PORT_INSTANCE_UUID, 16, p->instance) < 0) + goto err_exit; + + if (nla_put_u32(nl_msg, IFLA_PORT_VF, PORT_SELF_VF) < 0) + goto err_exit; + + if (p->response != VDP_RESPONSE_NO_RESPONSE) { + if (nla_put_u16(nl_msg, IFLA_PORT_RESPONSE, + p->response) < 0) + goto err_exit; + } + + nla_nest_end(nl_msg, vf_port); + } + } + + if (vf_ports) + nla_nest_end(nl_msg, vf_ports); + + return nl_msg; + +err_exit: + nlmsg_free(nl_msg); + + return NULL; +} + +struct nl_msg *event_if_simpleResponse(uint32_t pid, uint32_t seq, int err) +{ + struct nl_msg *nl_msg = nlmsg_alloc(); + struct nlmsgerr nlmsgerr; + + memset(&nlmsgerr, 0x0, sizeof(nlmsgerr)); + + nlmsgerr.error = err; + LLDPAD_DBG("RESPONSE error code: %d",err); + + if (nlmsg_put(nl_msg, pid, seq, NLMSG_ERROR, 0, 0) == NULL) + goto err_exit; + + if (nlmsg_append(nl_msg, &nlmsgerr, sizeof(nlmsgerr), NLMSG_ALIGNTO) < 0) + goto err_exit; + + return nl_msg; + +err_exit: + nlmsg_free(nl_msg); + + return NULL; +} + +static void event_iface_receive_user_space(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct nlmsghdr *nlh, *nlh2; + struct nl_msg *nl_msg; + struct msghdr msg; + struct sockaddr_nl dest_addr; + struct iovec iov; + int result; + int err; + int ifindex = 0; + char *ifname = NULL; + + nlh = (struct nlmsghdr *)calloc(1, + NLMSG_SPACE(MAX_PAYLOAD)); + if (!nlh) { + LLDPAD_ERR("%s(%i): could not allocate nlh !", __func__, + __LINE__); + return; + } + memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD)); + + memset(&dest_addr, 0, sizeof(dest_addr)); + iov.iov_base = (void *)nlh; + iov.iov_len = NLMSG_SPACE(MAX_PAYLOAD); + msg.msg_name = (void *)&dest_addr; + msg.msg_namelen = sizeof(dest_addr); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + LLDPAD_DBG("Waiting for message"); + result = recvmsg(sock, &msg, MSG_DONTWAIT); + + LLDPAD_DBG("%s(%i): ", __func__, __LINE__); + LLDPAD_DBG("recvmsg received %d bytes", result); + + if(result < 0) { + LLDPAD_ERR("Error receiving from netlink socket : %s", strerror(errno)); + } + + LLDPAD_DBG("dest_addr.nl_pid: %d", dest_addr.nl_pid); + LLDPAD_DBG("nlh.nl_pid: %d", nlh->nlmsg_pid); + LLDPAD_DBG("nlh_type: %d", nlh->nlmsg_type); + LLDPAD_DBG("nlh_seq: 0x%x", nlh->nlmsg_seq); + LLDPAD_DBG("nlh_len: 0x%x", nlh->nlmsg_len); + + switch (nlh->nlmsg_type) { + case RTM_SETLINK: + LLDPAD_DBG("RTM_SETLINK"); + + err = event_if_parse_setmsg(nlh); + + /* send simple response wether profile was accepted + * or not */ + nl_msg = event_if_simpleResponse(nlh->nlmsg_pid, + nlh->nlmsg_seq, + err); + nlh2 = nlmsg_hdr(nl_msg); + break; + case RTM_GETLINK: + LLDPAD_DBG("RTM_GETLINK"); + + err = event_if_parse_getmsg(nlh, &ifindex, ifname); + + if (err) { + nl_msg = event_if_simpleResponse(nlh->nlmsg_pid, + nlh->nlmsg_seq, + err); + } else if (ifname) { + ifindex = if_nametoindex(ifname); + LLDPAD_DBG("%s(%i): ifname %s (%d)", __func__, + __LINE__, ifname, ifindex); + } else { + LLDPAD_DBG("%s(%i): ifindex %i", __func__, + __LINE__, ifindex); + } + + nl_msg = event_if_constructResponse(nlh, ifindex); + + if (!nl_msg) { + LLDPAD_ERR("%s(%i): Unable to construct response !", + __func__, __LINE__); + goto out_err; + } + + nlh2 = nlmsg_hdr(nl_msg); + + LLDPAD_DBG("RESPONSE:"); + + event_if_parseResponseMsg(nlh2); + + break; + } + + iov.iov_base = (void*)nlh2; + iov.iov_len = nlh2->nlmsg_len; + + msg.msg_name = (void *)&dest_addr; + msg.msg_namelen = sizeof(dest_addr); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + result = sendmsg(sock, &msg, 0); + + if (result < 0) { + LLDPAD_ERR("Error sending on netlink socket (%s) !", strerror(errno)); + } else { + LLDPAD_DBG("Sent %d bytes !",result); + } + +out_err: + free(nlh); + nlmsg_free(nl_msg); + + return; +} + static void event_iface_receive(int sock, void *eloop_ctx, void *sock_ctx) { struct nlmsghdr *nlh; @@ -236,12 +765,14 @@ static void event_iface_receive(int sock, void *eloop_ctx, void *sock_ctx) char buf[MAX_MSG_SIZE]; socklen_t fromlen = sizeof(dest_addr); int result; - + result = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, (struct sockaddr *) &dest_addr, &fromlen); + LLDPAD_DBG("%s:%s: result from receive: %d", __FILE__, __FUNCTION__, result); + if (result < 0) { - perror("recvfrom(Event interface)"); + LLDPAD_ERR("recvfrom(event interface): %s", strerror(errno)); if ((errno == ENOBUFS) || (errno == EAGAIN)) eloop_register_timeout(INI_TIMER, 0, scan_port, NULL, NULL); @@ -250,10 +781,15 @@ static void event_iface_receive(int sock, void *eloop_ctx, void *sock_ctx) TRACE("PRINT BUF info.\n") - device_name = NULL; - link_status = IF_OPER_UNKNOWN; - nlh = (struct nlmsghdr *)buf; - event_if_process_recvmsg(nlh); + /* Separate handler for kernel messages from userspace messages*/ + LLDPAD_DBG("%s:%s: dest_addr.nl_pid: %d", __FILE__, __FUNCTION__, dest_addr.nl_pid); + + if (dest_addr.nl_pid == 0) { + device_name = NULL; + link_status = IF_OPER_UNKNOWN; + nlh = (struct nlmsghdr *)buf; + event_if_process_recvmsg(nlh); + } } int event_iface_init() @@ -261,7 +797,7 @@ int event_iface_init() int fd; int rcv_size = MAX_MSG_SIZE; struct sockaddr_nl snl; - + fprintf(stderr, "%s:%s:%d\n", __FILE__, __FUNCTION__, __LINE__); fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (fd < 0) @@ -282,6 +818,39 @@ int event_iface_init() } eloop_register_read_sock(fd, event_iface_receive, NULL, NULL); + + return 0; +} + +int event_iface_init_user_space() +{ + int fd; + int rcv_size = MAX_MSG_SIZE; + struct sockaddr_nl snl; + + fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + + if (fd < 0) + return fd; + + if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcv_size, sizeof(int)) < 0) { + close(fd); + return -EIO; + } + + memset((void *)&snl, 0, sizeof(struct sockaddr_nl)); + snl.nl_family = AF_NETLINK; + snl.nl_pid = getpid(); /* self pid */ + snl.nl_groups = 0; + + if (bind(fd, (struct sockaddr *)&snl, sizeof(struct sockaddr_nl)) < 0) { + close(fd); + LLDPAD_ERR("Error binding to netlink socket (%s) !", strerror(errno)); + return -EIO; + } + + eloop_register_read_sock(fd, event_iface_receive_user_space, NULL, NULL); + return 0; } diff --git a/include/event_iface.h b/include/event_iface.h index b2c93f0..d80158d 100644 --- a/include/event_iface.h +++ b/include/event_iface.h @@ -29,6 +29,7 @@ #define _EVENT_IFACE_H_ int event_iface_init(void); +int event_iface_init_user_space(void); int event_iface_deinit(void); int oper_add_device(char *device_name); diff --git a/lldp_vdp.c b/lldp_vdp.c index 2014c2a..cb23d90 100644 --- a/lldp_vdp.c +++ b/lldp_vdp.c @@ -105,7 +105,7 @@ static void vdp_free_data(struct vdp_user_data *ud) * prints the contents of a profile first to a string using the PRINT_PROFILE * macro, and then to the screen. Used for debug purposes. */ -static inline void vdp_print_profile(struct vsi_profile *profile) +void vdp_print_profile(struct vsi_profile *profile) { LLDPAD_DBG("profile:\n"); diff --git a/lldpad.c b/lldpad.c index c0938af..69faa29 100644 --- a/lldpad.c +++ b/lldpad.c @@ -374,6 +374,16 @@ int main(int argc, char *argv[]) exit(1); } + /* setup event netlink interface for user space processes. + * This needs to be setup first to ensure it gets lldpads + * pid as netlink address. + */ + if (event_iface_init_user_space() < 0) { + log_message(MSG_ERR_SERVICE_START_FAILURE, + "%s", "failed to register user space event interface"); + exit(1); + } + init_modules(""); -- 1.7.2.3
Jens Osterkamp
2010-Nov-22 15:50 UTC
[PATCH 09/10] do not use macv[tap/lan] interfaces as ports
At startup lldpad walks through all network interfaces in the system and creates internal data structures for them. Some interfaces as e.g. vlan and wlan are skipped in the walkthrough, some have to be treated special (e.g. bond devices). This patch adds macvtap and macvlan interfaces to the list of devices that are skipped as we do not want to send out EVB/ECP frames on them. Signed-off-by: Jens Osterkamp <jens at linux.vnet.ibm.com> --- config.c | 2 + include/lldp_util.h | 1 + lldp_util.c | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+), 0 deletions(-) diff --git a/config.c b/config.c index 26afe3b..fdb63f7 100644 --- a/config.c +++ b/config.c @@ -365,6 +365,8 @@ void init_ports(void) p->if_name); } else if (is_vlan(p->if_name)) { ; + } else if (is_macvtap(p->if_name)) { + ; } else if (is_bridge(p->if_name)) { ; /* ignore bridge device */ } else if (check_link_status(p->if_name)) { diff --git a/include/lldp_util.h b/include/lldp_util.h index 3353067..ef2c562 100644 --- a/include/lldp_util.h +++ b/include/lldp_util.h @@ -40,6 +40,7 @@ int is_bridge(const char *ifname); int is_vlan(const char *ifname); int is_vlan_capable(const char *ifname); int is_wlan(const char *ifname); +int is_macvtap(const char *ifname); int is_valid_mac(const u8 *mac); int is_san_mac(u8 *addr); int is_ether(const char *ifname); diff --git a/lldp_util.c b/lldp_util.c index f39fe6b..40db741 100644 --- a/lldp_util.c +++ b/lldp_util.c @@ -34,6 +34,7 @@ #include <sys/types.h> #include <sys/socket.h> #include <net/if_arp.h> +#include <netlink/msg.h> #include <arpa/inet.h> #include <linux/if.h> #include <linux/if_bonding.h> @@ -42,6 +43,7 @@ #include <linux/wireless.h> #include <linux/sockios.h> #include <linux/ethtool.h> +#include <linux/rtnetlink.h> #include <dirent.h> #include "lldp.h" #include "lldp_util.h" @@ -57,6 +59,8 @@ int is_valid_lldp_device(const char *device_name) return 0; if (is_bridge(device_name)) return 0; + if (is_macvtap(device_name)) + return 0; return 1; } @@ -534,6 +538,92 @@ int is_wlan(const char *ifname) return rc; } +#define NLMSG_SIZE 1024 + +static struct nla_policy ifla_info_policy[IFLA_INFO_MAX + 1] +{ + [IFLA_INFO_KIND] = { .type = NLA_STRING}, + [IFLA_INFO_DATA] = { .type = NLA_NESTED }, +}; + +int is_macvtap(const char *ifname) +{ + int ret, s; + struct nlmsghdr *nlh; + struct ifinfomsg *ifinfo; + struct nlattr *tb[IFLA_MAX+1], + *tb2[IFLA_INFO_MAX+1]; + + s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + + if (s < 0) { + goto out; + } + + nlh = malloc(NLMSG_SIZE); + memset(nlh, 0, NLMSG_SIZE); + + if (!nlh) { + goto out; + } + + nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + nlh->nlmsg_type = RTM_GETLINK; + nlh->nlmsg_flags = NLM_F_REQUEST; + + ifinfo = NLMSG_DATA(nlh); + ifinfo->ifi_family = AF_UNSPEC; + ifinfo->ifi_index = get_ifidx(ifname); + + ret = send(s, nlh, nlh->nlmsg_len, 0); + + if (ret < 0) { + goto out_free; + } + + memset(nlh, 0, NLMSG_SIZE); + + do { + ret = recv(s, (void *) nlh, NLMSG_SIZE, MSG_DONTWAIT); + } while ((ret < 0) && errno == EINTR); + + if (nlmsg_parse(nlh, sizeof(struct ifinfomsg), + (struct nlattr **)&tb, IFLA_MAX, NULL)) { + goto out_free; + } + + if (tb[IFLA_IFNAME]) { + ifname = (char *)RTA_DATA(tb[IFLA_IFNAME]); + } else { + ifinfo = (struct ifinfomsg *)NLMSG_DATA(nlh); + } + + if (tb[IFLA_LINKINFO]) { + if (nla_parse_nested(tb2, IFLA_INFO_MAX, tb[IFLA_LINKINFO], + ifla_info_policy)) { + goto out_free; + } + + if (tb2[IFLA_INFO_KIND]) { + char *kind = (char*)(RTA_DATA(tb2[IFLA_INFO_KIND])); + if (!(strcmp("macvtap", kind) && strcmp("macvlan", kind))) { + free(nlh); + close(s); + return true; + } + } + + } else { + goto out_free; + } + +out_free: + free(nlh); +out: + close(s); + return false; +} + int is_router(const char *ifname) { int rc = 0; -- 1.7.2.3
Once a VSI is associated with the adjacent switch, it needs to be renewed in the switch from time to time. This patch implements the renewal of the VSI. As a time base it uses the RTE (ReTransmission Exponent) exchanged in the EVB TLV. Signed-off-by: Jens Osterkamp <jens at linux.vnet.ibm.com> --- ecp/ecp.h | 2 +- ecp/ecp_tx.c | 8 ++- include/lldp_evb.h | 4 +- include/lldp_vdp.h | 3 +- lldp_evb.c | 7 +++ lldp_vdp.c | 109 +++++++++++++++++++++++++++++++++++++++++++++------- 6 files changed, 112 insertions(+), 21 deletions(-) diff --git a/ecp/ecp.h b/ecp/ecp.h index cc9ca2b..89c1154 100644 --- a/ecp/ecp.h +++ b/ecp/ecp.h @@ -34,7 +34,7 @@ #define ECP_MAX_RETRIES 3 #define ECP_SEQUENCE_NR_START 0x0 -#define ECP_TRANSMISSION_TIMER EVB_RTM(RTE)*EVB_RTG +#define ECP_TRANSMISSION_TIMER(rte) EVB_RTM(rte)*EVB_RTG #define ECP_TRANSMISSION_DIVIDER 10000 typedef enum { diff --git a/ecp/ecp_tx.c b/ecp/ecp_tx.c index 1db9ac3..6518a79 100644 --- a/ecp/ecp_tx.c +++ b/ecp/ecp_tx.c @@ -316,12 +316,14 @@ int ecp_tx_stop_ackTimer(struct vdp_data *vd) */ static void ecp_tx_start_ackTimer(struct vdp_data *vd) { - unsigned int secs, usecs; + unsigned int secs, usecs, rte; vd->ecp.ackTimerExpired = false; - secs = ECP_TRANSMISSION_TIMER / ECP_TRANSMISSION_DIVIDER; - usecs = ECP_TRANSMISSION_TIMER % ECP_TRANSMISSION_DIVIDER; + rte = evb_get_rte(vd->ifname); + + secs = ECP_TRANSMISSION_TIMER(rte) / ECP_TRANSMISSION_DIVIDER; + usecs = ECP_TRANSMISSION_TIMER(rte) % ECP_TRANSMISSION_DIVIDER; LLDPAD_DBG("%s(%i)-%s: starting timer\n", __func__, __LINE__, vd->ifname); diff --git a/include/lldp_evb.h b/include/lldp_evb.h index d028c21..549f2b2 100644 --- a/include/lldp_evb.h +++ b/include/lldp_evb.h @@ -37,11 +37,11 @@ typedef enum { EVB_CONFIRMATION } evb_state; -#define RTE 13 +#define EVB_RTE 13 /* retransmission granularity (RTG) in microseconds */ #define EVB_RTG 10 /* retransmission multiplier (RTM) */ -#define EVB_RTM(rte) (2<<(RTE-1)) +#define EVB_RTM(rte) (2<<(rte-1)) struct tlv_info_evb { u8 oui[3]; diff --git a/include/lldp_vdp.h b/include/lldp_vdp.h index 8866d51..5cd2b2e 100644 --- a/include/lldp_vdp.h +++ b/include/lldp_vdp.h @@ -71,7 +71,7 @@ enum { #define VDP_MACVLAN_FORMAT_1 1 -#define VDP_TRANSMISSION_TIMER 3*EVB_RTM(RTE)*EVB_RTG +#define VDP_TRANSMISSION_TIMER(rte) 3*EVB_RTM(rte)*EVB_RTG #define VDP_TRANSMISSION_DIVIDER 10000 #define VDP_ROLE_STATION 0 @@ -128,6 +128,7 @@ struct vsi_profile { struct port *port; int ackTimerExpired; int ackReceived; + int keepaliveTimerExpired; int state; bool localChange; LIST_ENTRY(vsi_profile) profile; diff --git a/lldp_evb.c b/lldp_evb.c index 50ee17d..051559c 100644 --- a/lldp_evb.c +++ b/lldp_evb.c @@ -91,6 +91,13 @@ static void evb_dump_tlv(struct unpacked_tlv *tlv) free(t); } +unsigned int evb_get_rte(char *ifname) +{ + struct evb_data *ed = evb_data(ifname); + + return (unsigned int) ed->tie->rte; +} + /* * evb_bld_cfg_tlv - build the EVB TLV * @ed: the evb data struct diff --git a/lldp_vdp.c b/lldp_vdp.c index cb23d90..bc17ee4 100644 --- a/lldp_vdp.c +++ b/lldp_vdp.c @@ -141,6 +141,82 @@ void vdp_somethingChangedLocal(struct vsi_profile *profile, bool mode) profile->localChange = mode; } +/* vdp_keepaliveTimer_expired - checks for expired ack timer + * @profile: profile to be checked + * + * returns true or false + * + * returns value of profile->ackTimerExpired, true if ack timer has expired, + * false otherwise. + */ +static bool vdp_keepaliveTimer_expired(struct vsi_profile *profile) +{ + return profile->keepaliveTimerExpired; +} + +/* vdp_acktimeout_handler - handles the ack timer expiry + * @eloop_data: data structure of event loop + * @user_ctx: user context, profile here + * + * no return value + * + * called when the VDP ack timer has expired. sets a flag and calls the VDP + * state machine. + */ +void vdp_keepalivetimeout_handler(void *eloop_data, void *user_ctx) +{ + struct vsi_profile *profile; + + profile = (struct vsi_profile *) user_ctx; + + profile->keepaliveTimerExpired = true; + + LLDPAD_DBG("%s(%i)-%s: keepalive timer expired\n", __func__, __LINE__, + profile->port->ifname); + + vdp_vsi_sm_station(profile); +} + +/* vdp_stop_keepaliveTimer - stop the VDP keepalive timer + * @profile: profile to process + * + * returns the number of removed handlers + * + * stops the VDP keepalive timer. Used when the profile changes state e.g. a deassoc + * has been requested. + */ +static int vdp_stop_keepaliveTimer(struct vsi_profile *profile) +{ + LLDPAD_DBG("%s(%i)-%s: stopping keepalive timer\n", __func__, __LINE__, + profile->port->ifname); + + return eloop_cancel_timeout(vdp_keepalivetimeout_handler, NULL, (void *) profile); +} + +/* vdp_start_keepaliveTimer - starts the VDP keepalive timer + * @profile: profile to process + * + * returns 0 on success, -1 on error + * + * starts the keepalive timer after a ack frame has received. + */ +static int vdp_start_keepaliveTimer(struct vsi_profile *profile) +{ + unsigned int secs, usecs, rte; + + profile->keepaliveTimerExpired = false; + + rte = evb_get_rte(profile->port->ifname); + + secs = VDP_TRANSMISSION_TIMER(rte) / VDP_TRANSMISSION_DIVIDER; + usecs = VDP_TRANSMISSION_TIMER(rte) % VDP_TRANSMISSION_DIVIDER; + + LLDPAD_DBG("%s(%i)-%s: starting keepalive timer (%i secs, %i usecs) \n", __func__, __LINE__, + profile->port->ifname, secs, usecs); + + return eloop_register_timeout(secs, usecs, vdp_keepalivetimeout_handler, NULL, (void *) profile); +} + /* vdp_ackTimer_expired - checks for expired ack timer * @profile: profile to be checked * @@ -154,7 +230,7 @@ static bool vdp_ackTimer_expired(struct vsi_profile *profile) return profile->ackTimerExpired; } -/* vdp_timeout_handler - handles the ack timer expiry +/* vdp_acktimeout_handler - handles the ack timer expiry * @eloop_data: data structure of event loop * @user_ctx: user context, profile here * @@ -163,7 +239,7 @@ static bool vdp_ackTimer_expired(struct vsi_profile *profile) * called when the VDP ack timer has expired. sets a flag and calls the VDP * state machine. */ -void vdp_timeout_handler(void *eloop_data, void *user_ctx) +void vdp_acktimeout_handler(void *eloop_data, void *user_ctx) { struct vsi_profile *profile; @@ -171,7 +247,7 @@ void vdp_timeout_handler(void *eloop_data, void *user_ctx) profile->ackTimerExpired = true; - LLDPAD_DBG("%s(%i)-%s: timer expired\n", __func__, __LINE__, + LLDPAD_DBG("%s(%i)-%s: ack timer expired\n", __func__, __LINE__, profile->port->ifname); vdp_vsi_sm_station(profile); @@ -187,10 +263,10 @@ void vdp_timeout_handler(void *eloop_data, void *user_ctx) */ static int vdp_stop_ackTimer(struct vsi_profile *profile) { - LLDPAD_DBG("%s(%i)-%s: stopping timer\n", __func__, __LINE__, + LLDPAD_DBG("%s(%i)-%s: stopping ack timer\n", __func__, __LINE__, profile->port->ifname); - return eloop_cancel_timeout(vdp_timeout_handler, NULL, (void *) profile); + return eloop_cancel_timeout(vdp_acktimeout_handler, NULL, (void *) profile); } /* vdp_start_ackTimer - starts the VDP ack timer @@ -202,17 +278,19 @@ static int vdp_stop_ackTimer(struct vsi_profile *profile) */ static int vdp_start_ackTimer(struct vsi_profile *profile) { - unsigned int secs, usecs; + unsigned int secs, usecs, rte; profile->ackTimerExpired = false; - secs = VDP_TRANSMISSION_TIMER / VDP_TRANSMISSION_DIVIDER; - usecs = VDP_TRANSMISSION_TIMER % VDP_TRANSMISSION_DIVIDER; + rte = evb_get_rte(profile->port->ifname); - LLDPAD_DBG("%s(%i)-%s: starting timer\n", __func__, __LINE__, - profile->port->ifname); + secs = VDP_TRANSMISSION_TIMER(rte) / VDP_TRANSMISSION_DIVIDER; + usecs = VDP_TRANSMISSION_TIMER(rte) % VDP_TRANSMISSION_DIVIDER; - return eloop_register_timeout(secs, usecs, vdp_timeout_handler, NULL, (void *) profile); + LLDPAD_DBG("%s(%i)-%s: starting ack timer (%i secs, %i usecs)\n", __func__, __LINE__, + profile->port->ifname, secs, usecs); + + return eloop_register_timeout(secs, usecs, vdp_acktimeout_handler, NULL, (void *) profile); } /* vdp_vsi_change_station_state - changes the VDP station sm state @@ -233,13 +311,15 @@ void vdp_vsi_change_station_state(struct vsi_profile *profile, u8 newstate) (profile->state == VSI_UNASSOCIATED)); break; case VSI_ASSOCIATED: - assert(profile->state == VSI_ASSOC_PROCESSING); + assert((profile->state == VSI_ASSOC_PROCESSING) || + (profile->state == VSI_ASSOCIATED)); break; case VSI_PREASSOC_PROCESSING: assert(profile->state == VSI_UNASSOCIATED); break; case VSI_PREASSOCIATED: - assert(profile->state == VSI_PREASSOC_PROCESSING); + assert((profile->state == VSI_PREASSOC_PROCESSING) || + (profile->state == VSI_PREASSOCIATED)); break; case VSI_DEASSOC_PROCESSING: assert((profile->state == VSI_PREASSOCIATED) || @@ -371,8 +451,8 @@ void vdp_vsi_sm_station(struct vsi_profile *profile) vdp_somethingChangedLocal(profile, true); ecp_somethingChangedLocal(vd); ecp_tx_run_sm(vd); - vdp_start_keepaliveTimer(profile); } + vdp_start_keepaliveTimer(profile); /* TODO: * vsiError = ProcRxandSetCfg(remoteTLV, localtlv, vsistate); * if (!vsiError) vsistate=ASSOCIATED */ @@ -1080,6 +1160,7 @@ void vdp_ifdown(char *ifname) LIST_FOREACH(p, &vd->profile_head, profile) { vdp_stop_ackTimer(p); + vdp_stop_keepaliveTimer(p); LIST_REMOVE(p, profile); free(p); } -- 1.7.2.3