diff --git a/dockers/docker-iccpd/Dockerfile.j2 b/dockers/docker-iccpd/Dockerfile.j2 new file mode 100644 index 000000000000..ce3969f29da5 --- /dev/null +++ b/dockers/docker-iccpd/Dockerfile.j2 @@ -0,0 +1,47 @@ +{% from "dockers/dockerfile-macros.j2" import install_debian_packages, install_python_wheels, copy_files %} +FROM docker-config-engine-stretch + +ARG docker_container_name +RUN [ -f /etc/rsyslog.conf ] && sed -ri "s/%syslogtag%/$docker_container_name#%syslogtag%/;" /etc/rsyslog.conf + +## Make apt-get non-interactive +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && \ + apt-get install -f -y \ + libdbus-1-3 \ + libdaemon0 \ + libpython2.7 \ + # Install redis-tools dependencies + # TODO: implicitly install dependencies + libatomic1 \ + libjemalloc1 \ + liblua5.1-0 \ + lua-bitop \ + lua-cjson + +RUN apt-get -y install ebtables +RUN apt-get -y install -f kmod + +COPY \ +{% for deb in docker_iccpd_debs.split(' ') -%} +debs/{{ deb }}{{' '}} +{%- endfor -%} +debs/ + +RUN dpkg -i \ +{% for deb in docker_iccpd_debs.split(' ') -%} +debs/{{ deb }}{{' '}} +{%- endfor %} + +COPY ["start.sh", "iccpd.sh", "/usr/bin/"] +COPY ["supervisord.conf", "/etc/supervisor/conf.d/"] +COPY ["iccpd.j2", "/usr/share/sonic/templates/"] + +RUN chmod +x /usr/bin/start.sh /usr/bin/iccpd.sh +RUN apt-get clean -y && \ + apt-get autoclean -y && \ + apt-get autoremove -y && \ + rm -rf /debs + +ENTRYPOINT ["/usr/bin/supervisord"] diff --git a/dockers/docker-iccpd/base_image_files/mclagdctl b/dockers/docker-iccpd/base_image_files/mclagdctl new file mode 100755 index 000000000000..7c0b45dd0625 --- /dev/null +++ b/dockers/docker-iccpd/base_image_files/mclagdctl @@ -0,0 +1,10 @@ +#!/bin/bash + +DOCKER_EXEC_FLAGS="i" + +# Determine whether stdout is on a terminal +if [ -t 1 ] ; then + DOCKER_EXEC_FLAGS+="t" +fi + +docker exec -$DOCKER_EXEC_FLAGS iccpd mclagdctl "$@" diff --git a/dockers/docker-iccpd/iccpd.j2 b/dockers/docker-iccpd/iccpd.j2 new file mode 100644 index 000000000000..6c6f9fab1afa --- /dev/null +++ b/dockers/docker-iccpd/iccpd.j2 @@ -0,0 +1,11 @@ +{% for mclag_id in MC_LAG %} +mclag_id:{{mclag_id}} + local_ip:{{MC_LAG[mclag_id]['local_ip']}} + peer_ip:{{MC_LAG[mclag_id]['peer_ip']}} +{% if MC_LAG[mclag_id].has_key('peer_link') %} + peer_link:{{MC_LAG[mclag_id]['peer_link']}} +{% endif %} + mclag_interface:{{MC_LAG[mclag_id]['mclag_interface']}} +{% endfor %} +system_mac:{{DEVICE_METADATA['localhost']['mac']}} + diff --git a/dockers/docker-iccpd/iccpd.sh b/dockers/docker-iccpd/iccpd.sh new file mode 100644 index 000000000000..d3b2888344ac --- /dev/null +++ b/dockers/docker-iccpd/iccpd.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +function start_app { + rm -f /var/run/iccpd/* + mclagsyncd & + iccpd +} + +function clean_up { + pkill -9 mclagsyncd + pkill -9 iccpd + exit +} + +trap clean_up SIGTERM SIGKILL +start_app +read diff --git a/dockers/docker-iccpd/start.sh b/dockers/docker-iccpd/start.sh new file mode 100644 index 000000000000..d17634cb836a --- /dev/null +++ b/dockers/docker-iccpd/start.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + + +ICCPD_CONF_PATH=/etc/iccpd + +rm -rf $ICCPD_CONF_PATH +mkdir -p $ICCPD_CONF_PATH + +sonic-cfggen -d -t /usr/share/sonic/templates/iccpd.j2 > $ICCPD_CONF_PATH/iccpd.conf + +mkdir -p /var/sonic +echo "# Config files managed by sonic-config-engine" > /var/sonic/config_status + +rm -f /var/run/rsyslogd.pid + +supervisorctl start rsyslogd + +supervisorctl start iccpd diff --git a/dockers/docker-iccpd/supervisord.conf b/dockers/docker-iccpd/supervisord.conf new file mode 100644 index 000000000000..dc71f07c6c30 --- /dev/null +++ b/dockers/docker-iccpd/supervisord.conf @@ -0,0 +1,29 @@ +[supervisord] +logfile_maxbytes=1MB +logfile_backups=2 +nodaemon=true + +[program:start.sh] +command=/usr/bin/start.sh +priority=1 +autostart=true +autorestart=false +stdout_logfile=syslog +stderr_logfile=syslog + +[program:rsyslogd] +command=/usr/sbin/rsyslogd -n +priority=2 +autostart=false +autorestart=false +stdout_logfile=syslog +stderr_logfile=syslog + +[program:iccpd] +command=/usr/bin/iccpd.sh +priority=3 +autostart=false +autorestart=false +stdout_logfile=syslog +stderr_logfile=syslog + diff --git a/files/build_templates/docker_image_ctl.j2 b/files/build_templates/docker_image_ctl.j2 index 297c9dd1c2bd..6012ccd8b573 100644 --- a/files/build_templates/docker_image_ctl.j2 +++ b/files/build_templates/docker_image_ctl.j2 @@ -231,6 +231,9 @@ start() { {%- if docker_container_name != "database" %} -v /usr/share/sonic/device/$PLATFORM/$HWSKU/$DEV:/usr/share/sonic/hwsku:ro \ {%- endif %} +{%- if docker_container_name == "iccpd" %} + -v /lib/modules:/lib/modules:ro \ +{%- endif %} {%- if sonic_asic_platform != "mellanox" %} --tmpfs /tmp \ {%- endif %} diff --git a/files/build_templates/iccpd.service.j2 b/files/build_templates/iccpd.service.j2 new file mode 100644 index 000000000000..979c45de72c5 --- /dev/null +++ b/files/build_templates/iccpd.service.j2 @@ -0,0 +1,13 @@ +[Unit] +Description=ICCPD container +Requires=updategraph.service swss.service +After=updategraph.service swss.service + +[Service] +User={{ sonicadmin_user }} +ExecStartPre=/usr/bin/{{docker_container_name}}.sh start +ExecStart=/usr/bin/{{docker_container_name}}.sh wait +ExecStop=/usr/bin/{{docker_container_name}}.sh stop + +[Install] +WantedBy=multi-user.target swss.service diff --git a/files/build_templates/sonic_debian_extension.j2 b/files/build_templates/sonic_debian_extension.j2 index bdecdabe95d1..dad1cb9b8906 100644 --- a/files/build_templates/sonic_debian_extension.j2 +++ b/files/build_templates/sonic_debian_extension.j2 @@ -427,6 +427,9 @@ if [ -f {{service}} ]; then echo "{{service}}" | sudo tee -a $GENERATED_SERVICE_FILE fi {% endfor %} +if [ -f iccpd.service ]; then + sudo LANG=C chroot $FILESYSTEM_ROOT systemctl disable iccpd.service +fi sudo LANG=C chroot $FILESYSTEM_ROOT fuser -km /sys || true sudo LANG=C chroot $FILESYSTEM_ROOT umount -lf /sys {% endif %} diff --git a/rules/config b/rules/config index 70ac38e29669..e9eb460c4dc1 100644 --- a/rules/config +++ b/rules/config @@ -115,6 +115,10 @@ SONIC_DPKG_CACHE_SOURCE ?= /var/cache/sonic/artifacts # Default VS build memory preparation DEFAULT_VS_PREPARE_MEM = yes + +# ENABLE_ICCPD - build docker-iccpd for mclag support +ENABLE_ICCPD = y + # ENABLE_SYSTEM_SFLOW - build docker-sonic-sflow for sFlow support ENABLE_SFLOW = y @@ -126,3 +130,4 @@ ENABLE_RESTAPI = n # ENABLE_NAT - build docker-sonic-nat for nat support ENABLE_NAT = y + diff --git a/rules/docker-iccpd.mk b/rules/docker-iccpd.mk new file mode 100644 index 000000000000..da661e9a2244 --- /dev/null +++ b/rules/docker-iccpd.mk @@ -0,0 +1,18 @@ +# docker image for iccpd agent + +DOCKER_ICCPD = docker-iccpd.gz +$(DOCKER_ICCPD)_PATH = $(DOCKERS_PATH)/docker-iccpd +$(DOCKER_ICCPD)_DEPENDS += $(SWSS) $(REDIS_TOOLS) $(ICCPD) +$(DOCKER_ICCPD)_LOAD_DOCKERS += $(DOCKER_CONFIG_ENGINE_STRETCH) + +ifeq ($(ENABLE_ICCPD), y) +SONIC_DOCKER_IMAGES += $(DOCKER_ICCPD) +SONIC_INSTALL_DOCKER_IMAGES += $(DOCKER_ICCPD) +SONIC_STRETCH_DOCKERS += $(DOCKER_ICCPD) +endif + +$(DOCKER_ICCPD)_CONTAINER_NAME = iccpd +$(DOCKER_ICCPD)_RUN_OPT += --privileged -t +$(DOCKER_ICCPD)_RUN_OPT += -v /etc/sonic:/etc/sonic:ro + +$(DOCKER_ICCPD)_BASE_IMAGE_FILES += mclagdctl:/usr/bin/mclagdctl diff --git a/rules/iccpd.mk b/rules/iccpd.mk new file mode 100644 index 000000000000..d54faaba5b76 --- /dev/null +++ b/rules/iccpd.mk @@ -0,0 +1,14 @@ +# iccpd package + +ICCPD_VERSION = 0.0.5 + +ICCPD = iccpd_$(ICCPD_VERSION)_amd64.deb +$(ICCPD)_DEPENDS += $(LIBNL_GENL3_DEV) $(LIBNL_CLI_DEV) +$(ICCPD)_RDEPENDS += $(LIBNL_GENL3) $(LIBNL_CLI) +$(ICCPD)_SRC_PATH = $(SRC_PATH)/iccpd +SONIC_MAKE_DEBS += $(ICCPD) +# SONIC_STRETCH_DEBS += $(ICCPD) + +# Export these variables so they can be used in a sub-make +export ICCPD_VERSION +export ICCPD diff --git a/src/iccpd/Makefile b/src/iccpd/Makefile new file mode 100644 index 000000000000..401ff394d145 --- /dev/null +++ b/src/iccpd/Makefile @@ -0,0 +1,35 @@ +.ONESHELL: +SHELL = /bin/bash +.SHELLFLAGS += -e + +MAIN_TARGET = iccpd_$(ICCPD_VERSION)_amd64.deb +DEB_PATH = debian + +all: iccpd-build mclagdctl-build + +iccpd-build: + make -C src + +mclagdctl-build: + make -C src/mclagdctl + +$(addprefix $(DEST)/, $(MAIN_TARGET)): $(DEST)/% : + make all + if [ ! -d $(DEB_PATH)/usr/bin ]; then + mkdir -p $(DEB_PATH)/usr/bin + fi + cp iccpd $(DEB_PATH)/usr/bin/iccpd + cp src/mclagdctl/mclagdctl $(DEB_PATH)/usr/bin/mclagdctl + chmod +x $(DEB_PATH)/usr/bin/iccpd + chmod +x $(DEB_PATH)/usr/bin/mclagdctl + md5sum $(DEB_PATH)/usr/bin/iccpd > $(DEB_PATH)/DEBIAN/md5sums + md5sum $(DEB_PATH)/usr/bin/mclagdctl >> $(DEB_PATH)/DEBIAN/md5sums + dpkg-deb -b $(DEB_PATH) $(DEST)/$(MAIN_TARGET) + +clean: iccpd-clean mclagdctl-clean + +iccpd-clean: + make -C src clean + +mclagdctl-clean: + make -C src/mclagdctl clean diff --git a/src/iccpd/debian/DEBIAN/control b/src/iccpd/debian/DEBIAN/control new file mode 100644 index 000000000000..cedfa03f37c0 --- /dev/null +++ b/src/iccpd/debian/DEBIAN/control @@ -0,0 +1,12 @@ +Package: iccpd-0.0.5-amd64 +Source: nps +Version: 0.0.5 +Architecture: amd64 +Maintainer: Simon Ji +Installed-Size: 1508 +Depends: +Section: main +Priority: extra +Homepage: https://github.com/NephosInc/SONiC +Description: + diff --git a/src/iccpd/include/app_csm.h b/src/iccpd/include/app_csm.h new file mode 100644 index 000000000000..a0565556d24c --- /dev/null +++ b/src/iccpd/include/app_csm.h @@ -0,0 +1,73 @@ +/* + * app_csm.h + * + * Copyright(c) 2016-2019 Nephos/Estinet. + * + * 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, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintainer: jianjun, grace Li from nephos + */ + +#ifndef APP_CSM_H_ +#define APP_CSM_H_ + +#include + +#include "../include/mlacp_fsm.h" + +struct CSM; + +enum APP_CONNECTION_STATE +{ + APP_NONEXISTENT, + APP_RESET, + APP_CONNSENT, + APP_CONNREC, + APP_CONNECTING, + APP_OPERATIONAL +}; + +typedef enum APP_CONNECTION_STATE APP_CONNECTION_STATE_E; + +struct AppCSM +{ + struct mLACP mlacp; + APP_CONNECTION_STATE_E current_state; + + uint32_t rx_connect_msg_id; + uint32_t tx_connect_msg_id; + uint32_t invalid_msg_id; + + TAILQ_HEAD(app_msg_list, Msg) app_msg_list; + + uint8_t invalid_msg : 1; + uint8_t nak_msg : 1; +}; + +void app_csm_init(struct CSM*, int all); +void app_csm_finalize(struct CSM*); +void app_csm_transit(struct CSM*); +int app_csm_prepare_iccp_msg(struct CSM*, char*, size_t); +void app_csm_enqueue_msg(struct CSM*, struct Msg*); +struct Msg* app_csm_dequeue_msg(struct CSM*); +void app_csm_correspond_from_msg(struct CSM*, struct Msg*); +void app_csm_correspond_from_connect_msg(struct CSM*, struct Msg*); +void app_csm_correspond_from_connect_ack_msg(struct CSM*, struct Msg*); +int app_csm_prepare_nak_msg(struct CSM*, char*, size_t); +int app_csm_prepare_connect_msg(struct CSM*, char*, size_t); +int app_csm_prepare_connect_ack_msg(struct CSM*, char*, size_t); + +#endif /* APP_CSM_H_ */ diff --git a/src/iccpd/include/cmd_option.h b/src/iccpd/include/cmd_option.h new file mode 100644 index 000000000000..ca5582cfcc60 --- /dev/null +++ b/src/iccpd/include/cmd_option.h @@ -0,0 +1,84 @@ +/* + * cmd_option.h + * + * Copyright(c) 2016-2019 Nephos/Estinet. + * + * 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, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintainer: jianjun, grace Li from nephos + */ + +#ifndef CMD_OPTION_H_ +#define CMD_OPTION_H_ + +#include +#include +#include +#include +#include +#include + +#define OPTION_MAX_LEN 256 +#define MSG_LEN 81 + +#define CMD_OPTION_PARSER_INIT_VALUE \ + { \ + .log_file_path = "/var/log/iccpd.log", \ + .pid_file_path = "/var/run/iccpd/iccpd.pid", \ + .cmd_file_path = "/var/run/iccpd/iccpd.vty", \ + .config_file_path = "/etc/iccpd/iccpd.conf", \ + .mclagdctl_file_path = "/var/run/iccpd/mclagdctl.sock", \ + .console_log = 0, \ + .telnet_port = 2015, \ + .init = cmd_option_parser_init, \ + .finalize = cmd_option_parser_finalize, \ + .dump_usage = cmd_option_parser_dump_usage, \ + .parse = cmd_option_parser_parse, \ + } + +struct CmdOption +{ + char* desc; + char* option; + char* parameter; + LIST_ENTRY(CmdOption) next; +}; + +struct CmdOptionParser +{ + char* log_file_path; + char* pid_file_path; + char* cmd_file_path; + char* config_file_path; + char *mclagdctl_file_path; + uint8_t console_log; + uint16_t telnet_port; + LIST_HEAD(option_list, CmdOption) option_list; + int (*parse)(struct CmdOptionParser*, int, char*[]); + void (*init)(struct CmdOptionParser*); + void (*finalize)(struct CmdOptionParser*); + void (*dump_usage)(struct CmdOptionParser*, char*); +}; + +int cmd_option_parser_parse(struct CmdOptionParser*, int, char*[]); +struct CmdOption* cmd_option_add(struct CmdOptionParser*, char*); +struct CmdOption* cmd_option_find(struct CmdOptionParser*, char*); +void cmd_option_delete(struct CmdOption*); +void cmd_option_parser_init(struct CmdOptionParser*); +void cmd_option_parser_finalize(struct CmdOptionParser*); +void cmd_option_parser_dump_usage(struct CmdOptionParser*, char*); + +#endif /* CMD_OPTION_H_ */ diff --git a/src/iccpd/include/iccp_cli.h b/src/iccpd/include/iccp_cli.h new file mode 100644 index 000000000000..c85fc4316b5f --- /dev/null +++ b/src/iccpd/include/iccp_cli.h @@ -0,0 +1,67 @@ +/* + * iccp_cli.h + * + * Copyright(c) 2016-2019 Nephos/Estinet. + * + * 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, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintainer: jianjun, grace Li from nephos + */ + +#ifndef _ICCP_CLI_H +#define _ICCP_CLI_H + +#include + +struct CSM; + +typedef enum +{ + QU_TYPE_NONE, + QU_TYPE_MLAG_ADD_PO +} cli_queue_type_et; + +typedef struct cli_param_queue +{ + char ifname[16]; + cli_queue_type_et type; + int param; + int itf_add; + LIST_ENTRY(cli_param_queue) cli_queue_next; +} cli_param_queue_st; + +#define MCLAG_ID_STR "mclag_id" +#define LOCAL_IP_STR "local_ip" +#define PEER_IP_STR "peer_ip" +#define PEER_LINK_STR "peer_link" +#define MCLAG_INTF_STR "mclag_interface" +#define SYSTEM_MAC_STR "system_mac" + +int set_mc_lag_id(struct CSM* csm, uint16_t domain); +int set_peer_link(int mid, const char* ifname); +int set_local_address(int mid, const char* addr); +int set_peer_address(int mid, const char* addr); +int unset_mc_lag_id(struct CSM* csm, uint16_t domain); +int unset_peer_link(int mid); +int unset_local_address(int mid); +int unset_peer_address(int mid); + +int iccp_cli_attach_mclag_domain_to_port_channel(int domain, const char* ifname); +int iccp_cli_detach_mclag_domain_to_port_channel(const char* ifname); +int set_local_system_id(const char* mac); +int unset_local_system_id( ); + +#endif diff --git a/src/iccpd/include/iccp_cmd.h b/src/iccpd/include/iccp_cmd.h new file mode 100644 index 000000000000..01f37456b71d --- /dev/null +++ b/src/iccpd/include/iccp_cmd.h @@ -0,0 +1,29 @@ +/* + * iccp_cmd.h + * + * Copyright(c) 2016-2019 Nephos/Estinet. + * + * 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, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintainer: jianjun, grace Li from nephos + */ + +#ifndef ICCP_CMD_H_ +#define ICCP_CMD_H_ + +int iccp_config_from_file(char *config_default_dir); + +#endif /* ICCP_CMD_H_ */ diff --git a/src/iccpd/include/iccp_cmd_show.h b/src/iccpd/include/iccp_cmd_show.h new file mode 100644 index 000000000000..0fedca7cdddd --- /dev/null +++ b/src/iccpd/include/iccp_cmd_show.h @@ -0,0 +1,35 @@ +/* + * iccp_cmd_show.h + * + * Copyright(c) 2016-2019 Nephos/Estinet. + * + * 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, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintainer: jianjun, grace Li from nephos + */ + +#ifndef _ICCP_CMD_SHOW_H +#define _ICCP_CMD_SHOW_H + +#define ICCP_MAX_PORT_NAME 20 +#define ICCP_MAX_IP_STR_LEN 16 + +extern int iccp_mclag_config_dump(char * *buf, int *num, int mclag_id); +extern int iccp_arp_dump(char * *buf, int *num, int mclag_id); +extern int iccp_mac_dump(char * *buf, int *num, int mclag_id); +extern int iccp_local_if_dump(char * *buf, int *num, int mclag_id); +extern int iccp_peer_if_dump(char * *buf, int *num, int mclag_id); +#endif diff --git a/src/iccpd/include/iccp_consistency_check.h b/src/iccpd/include/iccp_consistency_check.h new file mode 100644 index 000000000000..6fa8ea47e2f4 --- /dev/null +++ b/src/iccpd/include/iccp_consistency_check.h @@ -0,0 +1,43 @@ +/* + * iccp_consistency_check.c + * + * Copyright(c) 2016-2019 Nephos/Estinet. + * + * 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, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintainer: jianjun, grace Li from nephos + * + */ + +#ifndef _ICCP_CONSISTENCY_CHECK_H +#define _ICCP_CONSISTENCY_CHECK_H + + +enum Reason_ID +{ + REASON_NONE = 0, + REASON_INTERRFACE_MODE_IS_ASYNC, + REASON_PEER_IF_IP_IS_ASYNC, + REASON_PEER_IF_VLAN_IS_ASYNC, + REASON_MAX_ARRAY_SIZE +}; + +extern const char *reasons[]; + +enum Reason_ID iccp_consistency_check(char* ifname); + + +#endif diff --git a/src/iccpd/include/iccp_csm.h b/src/iccpd/include/iccp_csm.h new file mode 100644 index 000000000000..9cf1022dd10c --- /dev/null +++ b/src/iccpd/include/iccp_csm.h @@ -0,0 +1,165 @@ +/* + * iccp_csm.h + * + * Copyright(c) 2016-2019 Nephos/Estinet. + * + * 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, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintainer: jianjun, grace Li from nephos + */ + +#ifndef ICCP_CSM_H_ +#define ICCP_CSM_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "../include/app_csm.h" +#include "../include/msg_format.h" +#include "../include/port.h" + +#define CSM_BUFFER_SIZE 65536 + +#ifndef IFNAMSIZ +#define IFNAMSIZ 16 +#endif /*IFNAMSIZ*/ + +#ifndef INET_ADDRSTRLEN +#define INET_ADDRSTRLEN 16 +#endif /* INET_ADDRSTRLEN */ + +/* For socket binding */ +#define ICCP_TCP_PORT 8888 +#define MAX_ACCEPT_CONNETIONS 20 + +/* LDP message ID */ +extern uint32_t ICCP_MSG_ID; + +/* Global Buffer */ +extern char g_csm_buf[CSM_BUFFER_SIZE]; + +struct IccpInfo +{ + uint32_t icc_rg_id; + char sender_name[MAX_L_ICC_SENDER_NAME]; + uint32_t status_code; + uint8_t peer_capability_flag : 1; + uint8_t peer_rg_connect_flag : 1; + uint8_t sender_capability_flag : 1; + uint8_t sender_rg_connect_flag : 1; + uint32_t rejected_msg_id; +}; + +/* Receive message node */ +struct Msg +{ + char* buf; + size_t len; + TAILQ_ENTRY(Msg) tail; +}; + +/* Connection state */ +enum ICCP_CONNECTION_STATE +{ + ICCP_NONEXISTENT, + ICCP_INITIALIZED, + ICCP_CAPSENT, + ICCP_CAPREC, + ICCP_CONNECTING, + ICCP_OPERATIONAL +}; + +typedef enum ICCP_CONNECTION_STATE ICCP_CONNECTION_STATE_E; + +typedef enum stp_role_type_e +{ + STP_ROLE_NONE, /* mstp do nothing*/ + STP_ROLE_ACTIVE, /* mstp report port state*/ + STP_ROLE_STANDBY /* mstp fwd bpdu & set port state*/ +} stp_role_type_et; + +/* Connection state machine instance */ +struct CSM +{ + int mlag_id; + + /* Socket info */ + int sock_fd; + pthread_mutex_t conn_mutex; + time_t connTimePrev; + time_t heartbeat_send_time; + time_t heartbeat_update_time; + time_t peer_warm_reboot_time; + time_t warm_reboot_disconn_time; + char peer_itf_name[IFNAMSIZ]; + char peer_ip[INET_ADDRSTRLEN]; + char sender_ip[INET_ADDRSTRLEN]; + void* sock_read_event_ptr; + + /* Msg queue */ + TAILQ_HEAD(msg_list, Msg) msg_list; + + /* STP role */ + stp_role_type_et role_type; + + /* Peers msg */ + struct LocalInterface* peer_link_if; + struct IccpInfo iccp_info; + struct AppCSM app_csm; + ICCP_CONNECTION_STATE_E current_state; + + /* Statistic info */ + uint64_t icc_msg_in_count; /* ICC message input count */ + uint64_t icc_msg_out_count; /* ICC message Output count */ + uint64_t u_msg_in_count; /* Unknown message Input count */ + uint64_t i_msg_in_count; /* Illegal message Input count */ + + /* Log */ + struct MsgLog msg_log; + + LIST_ENTRY(CSM) next; + LIST_HEAD(csm_if_list, If_info) if_bind_list; +}; +int iccp_csm_send(struct CSM*, char*, int); +int iccp_csm_init_msg(struct Msg**, char*, int); +int iccp_csm_prepare_nak_msg(struct CSM*, char*, size_t); +int iccp_csm_prepare_iccp_msg(struct CSM*, char*, size_t); +int iccp_csm_prepare_capability_msg(struct CSM*, char*, size_t); +int iccp_csm_prepare_rg_connect_msg(struct CSM*, char*, size_t); +int iccp_csm_prepare_rg_disconnect_msg(struct CSM*, char*, size_t); +struct Msg* iccp_csm_dequeue_msg(struct CSM*); +void *iccp_get_csm(); +void iccp_csm_init(struct CSM*); +void iccp_csm_transit(struct CSM*); +void iccp_csm_finalize(struct CSM*); +void iccp_csm_status_reset(struct CSM*, int); +void iccp_csm_stp_role_count(struct CSM *csm); +void iccp_csm_msg_list_finalize(struct CSM*); +void iccp_csm_enqueue_msg(struct CSM*, struct Msg*); +void iccp_csm_fill_icc_rg_id_tlv(struct CSM*, ICCHdr*); +void iccp_csm_correspond_from_msg(struct CSM*, struct Msg*); +void iccp_csm_correspond_from_capability_msg(struct CSM*, struct Msg*); +void iccp_csm_correspond_from_rg_connect_msg(struct CSM*, struct Msg*); +void iccp_csm_correspond_from_rg_disconnect_msg(struct CSM*, struct Msg*); + +int mlacp_bind_port_channel_to_csm(struct CSM* csm, const char *ifname); + +#endif /* ICCP_CSM_H_ */ diff --git a/src/iccpd/include/iccp_ifm.h b/src/iccpd/include/iccp_ifm.h new file mode 100644 index 000000000000..7fbb1a8cf4f9 --- /dev/null +++ b/src/iccpd/include/iccp_ifm.h @@ -0,0 +1,41 @@ +/* + * iccp_ifm.h + * + * Copyright(c) 2016-2019 Nephos/Estinet. + * + * 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, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintainer: jianjun, grace Li from nephos + */ + +#ifndef ICCP_IFM_H +#define ICCP_IFM_H + +#include + +int iccp_sys_local_if_list_get_init(); + +int iccp_arp_get_init(); + +void do_arp_update_from_reply_packet(unsigned int ifindex, unsigned int addr, uint8_t mac_addr[ETHER_ADDR_LEN]); + +int do_one_neigh_request(struct nlmsghdr *n); + +void iccp_from_netlink_port_state_handler( char * ifname, int state); + +void iccp_parse_if_vlan_info_from_netlink(struct nlmsghdr *n); +#endif // LACP_IFM_H + diff --git a/src/iccpd/include/iccp_netlink.h b/src/iccpd/include/iccp_netlink.h new file mode 100644 index 000000000000..612a1859591b --- /dev/null +++ b/src/iccpd/include/iccp_netlink.h @@ -0,0 +1,47 @@ +/* Copyright(c) 2016-2019 Nephos/Estinet. + * + * 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, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintainer: jianjun, grace Li from nephos + */ + +#ifndef ICCP_NETLINK_H +#define ICCP_NETLINK_H +#include +#include +#include +#include +#include +#include + +#include + +#include "../include/system.h" +#include "../include/port.h" + +int iccp_get_port_member_list(struct LocalInterface* lif); +void iccp_event_handler_obj_input_newlink(struct nl_object *obj, void *arg); +void iccp_event_handler_obj_input_dellink(struct nl_object *obj, void *arg); +int iccp_system_init_netlink_socket(); +void iccp_system_dinit_netlink_socket(); +int iccp_init_netlink_event_fd(struct System *sys); +int iccp_handle_events(struct System * sys); +void update_if_ipmac_on_standby(struct LocalInterface* lif_po); +int iccp_sys_local_if_list_get_addr(); +int iccp_check_if_addr_from_netlink(int family, uint8_t *addr, struct LocalInterface *lif); + +#endif + diff --git a/src/iccpd/include/logger.h b/src/iccpd/include/logger.h new file mode 100644 index 000000000000..a90fece2a4d9 --- /dev/null +++ b/src/iccpd/include/logger.h @@ -0,0 +1,70 @@ +/* + * logger.h + * + * Copyright(c) 2016-2019 Nephos/Estinet. + * + * 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, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintainer: jianjun, grace Li from nephos + */ + +#ifndef LOGGER_H_ +#define LOGGER_H_ + +#include +#include + +#include "../include/cmd_option.h" + +typedef enum _iccpd_log_level_t +{ + CRITICAL_LOG_LEVEL = 0, + ERR_LOG_LEVEL = 1, + WARN_LOG_LEVEL = 2, + NOTICE_LOG_LEVEL = 3, + INFO_LOG_LEVEL = 4, + DEBUG_LOG_LEVEL = 5 +} _iccpd_log_level_t; + + +#define LOGBUF_SIZE 1024 +#define ICCPD_UTILS_SYSLOG (syslog) + +#define ICCPD_LOG_CRITICAL(tag, format, args ...) write_log(CRITICAL_LOG_LEVEL, tag, format, ## args) +#define ICCPD_LOG_ERR(tag, format, args ...) write_log(ERR_LOG_LEVEL, tag, format, ## args) +#define ICCPD_LOG_WARN(tag, format, args ...) write_log(WARN_LOG_LEVEL, tag, format, ## args) +#define ICCPD_LOG_NOTICE(tag, format, args ...) write_log(NOTICE_LOG_LEVEL, tag, format, ## args) +#define ICCPD_LOG_INFO(tag, format, args ...) write_log(INFO_LOG_LEVEL, tag, format, ## args) +#define ICCPD_LOG_DEBUG(tag, format, args ...) write_log(DEBUG_LOG_LEVEL, tag, format, ## args) + +struct LoggerConfig +{ + uint8_t console_log_enabled; + uint8_t log_level; + uint8_t init; +}; + +struct LoggerConfig* logger_get_configuration(); +void logger_set_configuration(int log_level); +char* log_level_to_string(int level); +void log_setup(char* progname, char* path); +void log_finalize(); +void log_init(struct CmdOptionParser* parser); +void write_log(const int level, const char* tag, const char *format, ...); + +#endif /* LOGGER_H_ */ + + diff --git a/src/iccpd/include/mlacp_fsm.h b/src/iccpd/include/mlacp_fsm.h new file mode 100644 index 000000000000..6442d9ff560d --- /dev/null +++ b/src/iccpd/include/mlacp_fsm.h @@ -0,0 +1,106 @@ +/* + * mlacp_fsm.h + * mLACP finite state machine handler. + * + * Copyright(c) 2016-2019 Nephos/Estinet. + * + * 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, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintainer: jianjun, grace Li from nephos + */ + +#ifndef _MLACP_FSM_H +#define _MLACP_FSM_H + +#include "../include/port.h" + +#define MLCAP_SYNC_PHY_DEV_SEC 1 /*every 1 sec*/ + +#define MLACP(csm_ptr) (csm_ptr->app_csm.mlacp) + +struct CSM; + +enum MLACP_APP_STATE +{ + MLACP_STATE_INIT, + MLACP_STATE_STAGE1, + MLACP_STATE_STAGE2, + MLACP_STATE_EXCHANGE, + MLACP_STATE_ERROR, +}; + +typedef enum MLACP_APP_STATE MLACP_APP_STATE_E; + +/* for sender only*/ +enum MLACP_SYNC_STATE +{ + MLACP_SYNC_SYSCONF=0, + MLACP_SYNC_AGGCONF, + MLACP_SYNC_AGGSTATE, + MLACP_SYNC_AGGINFO, + MLACP_SYNC_PEERLINKINFO, + MLACP_SYNC_ARP_INFO, + MLACP_SYNC_DONE, +}; + +typedef enum MLACP_SYNC_STATE MLACP_SYNC_STATE_E; + +struct Remote_System +{ + uint8_t system_id[ETHER_ADDR_LEN]; + uint16_t system_priority; + uint32_t node_id; +}; + +struct mLACP +{ + int id; + int sync_req_num; + + MLACP_APP_STATE_E current_state; + MLACP_SYNC_STATE_E sync_state; + + uint8_t wait_for_sync_data; + uint8_t need_to_sync; + uint8_t node_id; + uint8_t system_id[ETHER_ADDR_LEN]; + uint16_t system_priority; + uint8_t system_config_changed; + + struct Remote_System remote_system; + const char* error_msg; + TAILQ_HEAD(mlacp_msg_list, Msg) mlacp_msg_list; + TAILQ_HEAD(arp_msg_list, Msg) arp_msg_list; + TAILQ_HEAD(arp_info_list, Msg) arp_list; + TAILQ_HEAD(mac_msg_list, Msg) mac_msg_list; + TAILQ_HEAD(mac_info_list, Msg) mac_list; + + LIST_HEAD(lif_list, LocalInterface) lif_list; + LIST_HEAD(lif_purge_list, LocalInterface) lif_purge_list; + LIST_HEAD(pif_list, PeerInterface) pif_list; +}; + +void mlacp_init(struct CSM* csm, int all); +void mlacp_finalize(struct CSM* csm); +void mlacp_fsm_transit(struct CSM* csm); +void mlacp_enqueue_msg(struct CSM*, struct Msg*); +struct Msg* mlacp_dequeue_msg(struct CSM*); + +/* from app_csm*/ +extern int mlacp_bind_local_if(struct CSM* csm, struct LocalInterface* local_if); +extern int mlacp_unbind_local_if(struct LocalInterface* local_if); + +#endif /* _MLACP_HANDLER_H */ diff --git a/src/iccpd/include/mlacp_link_handler.h b/src/iccpd/include/mlacp_link_handler.h new file mode 100644 index 000000000000..55892d1861c8 --- /dev/null +++ b/src/iccpd/include/mlacp_link_handler.h @@ -0,0 +1,60 @@ +/* + * mlacp_link_handler.h + * Copyright(c) 2016-2019 Nephos/Estinet. + * + * 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, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintainer: jianjun, grace Li from nephos + */ + +#ifndef __MLACP_LINK_HANDLER__ +#define __MLACP_LINK_HANDLER__ + +#include "../include/iccp_csm.h" +#include "../include/mlacp_tlv.h" + +/***************************************** +* Link Handler +* +* ***************************************/ +void mlacp_portchannel_state_handler(struct CSM* csm, struct LocalInterface* local_if, int po_state); +void mlacp_peer_conn_handler(struct CSM* csm); +void mlacp_peer_disconn_handler(struct CSM* csm); +void mlacp_peerlink_up_handler(struct CSM* csm); +void mlacp_peerlink_down_handler(struct CSM* csm); +void update_stp_peer_link(struct CSM *csm, struct PeerInterface *peer_if, int po_state, int new_create); +void update_peerlink_isolate_from_pif(struct CSM *csm, struct PeerInterface *pif, int po_state, int new_create); +void mlacp_mlag_link_add_handler(struct CSM *csm, struct LocalInterface *lif); +void mlacp_mlag_link_del_handler(struct CSM *csm, struct LocalInterface *lif); +void set_peerlink_mlag_port_learn(struct LocalInterface *lif, int enable); +void peerlink_port_isolate_cleanup(struct CSM* csm); +void update_peerlink_isolate_from_all_csm_lif(struct CSM* csm); + +int mlacp_fsm_arp_set(char *ifname, uint32_t ip, char *mac); +int mlacp_fsm_arp_del(char *ifname, uint32_t ip); +void del_mac_from_chip(struct MACMsg* mac_msg); +void add_mac_to_chip(struct MACMsg* mac_msg, uint8_t mac_type); +uint8_t set_mac_local_age_flag(struct CSM *csm, struct MACMsg* mac_msg, uint8_t set ); +void iccp_get_fdb_change_from_syncd( void); + +extern int mclagd_ctl_sock_create(); +extern int mclagd_ctl_sock_accept(int fd); +extern int mclagd_ctl_interactive_process(int client_fd); +char *show_ip_str(uint32_t ipv4_addr); + +void syncd_info_close(); +int iccp_connect_syncd(); +#endif diff --git a/src/iccpd/include/mlacp_sync_prepare.h b/src/iccpd/include/mlacp_sync_prepare.h new file mode 100644 index 000000000000..f4ea7a419f51 --- /dev/null +++ b/src/iccpd/include/mlacp_sync_prepare.h @@ -0,0 +1,55 @@ +/* + * mlacp_sync_prepare.h + * + * Copyright(c) 2016-2019 Nephos/Estinet. + * + * 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, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintainer: jianjun, grace Li from nephos + */ + +#ifndef __MLACP_SYNC_PREPARE__ +#define __MLACP_SYNC_PREPARE__ + +struct CSM; +/***************************************** +* Tool Function +* +* ***************************************/ +void update_system_id(struct CSM* csm); + +/***************************************** +* LACP Sync +* +* ***************************************/ +int mlacp_sync_with_kernel_callback(); + +/***************************************** +* MLACP Sync +* +* ***************************************/ +int mlacp_prepare_for_sync_request_tlv(struct CSM* csm, char* buf, size_t max_buf_size); +int mlacp_prepare_for_sync_data_tlv(struct CSM* csm, char* buf, size_t max_buf_size, int end); +int mlacp_prepare_for_sys_config(struct CSM* csm, char* buf, size_t max_buf_size); +int mlacp_prepare_for_mac_info_to_peer(struct CSM* csm, char* buf, size_t max_buf_size, struct MACMsg* mac_msg, int count); +int mlacp_prepare_for_arp_info(struct CSM* csm, char* buf, size_t max_buf_size, struct ARPMsg* arp_msg, int count); +int mlacp_prepare_for_heartbeat(struct CSM* csm, char* buf, size_t max_buf_size); +int mlacp_prepare_for_Aggport_state(struct CSM* csm, char* buf, size_t max_buf_size, struct LocalInterface* local_if); +int mlacp_prepare_for_Aggport_config(struct CSM* csm, char* buf, size_t max_buf_size, struct LocalInterface* lif, int purge_flag); +int mlacp_prepare_for_port_channel_info(struct CSM* csm, char* buf, size_t max_buf_size, struct LocalInterface* port_channel); +int mlacp_prepare_for_port_peerlink_info(struct CSM* csm, char* buf, size_t max_buf_size, struct LocalInterface* peerlink_port); +int iccp_netlink_if_hwaddr_set(uint32_t ifindex, uint8_t *addr, unsigned int addr_len); +#endif \ No newline at end of file diff --git a/src/iccpd/include/mlacp_sync_update.h b/src/iccpd/include/mlacp_sync_update.h new file mode 100644 index 000000000000..d42abe216af6 --- /dev/null +++ b/src/iccpd/include/mlacp_sync_update.h @@ -0,0 +1,49 @@ +/* + * + * mlacp_sync_update.h + * Copyright(c) 2016-2019 Nephos/Estinet. + * + * 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, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintainer: jianjun, grace Li from nephos + */ + +#ifndef __MLACP_SYNC_UPDATE__ +#define __MLACP_SYNC_UPDATE__ + +#include "iccp_csm.h" +#include "mlacp_tlv.h" + +/***************************************** +* FSM Sync Update API +* +* ***************************************/ +int mlacp_fsm_update_system_conf(struct CSM* csm, mLACPSysConfigTLV* tlv); + +int mlacp_fsm_update_Aggport_state(struct CSM* csm, mLACPAggPortStateTLV* tlv); + +int mlacp_fsm_update_arp_info(struct CSM* csm, struct mLACPARPInfoTLV* tlv); + +int mlacp_fsm_update_heartbeat(struct CSM* csm, struct mLACPHeartbeatTLV* tlv); + +int mlacp_fsm_update_warmboot(struct CSM* csm, struct mLACPWarmbootTLV* tlv); +void mlacp_enqueue_arp(struct CSM* csm, struct Msg* msg); + +int mlacp_fsm_update_Agg_conf(struct CSM* csm, mLACPAggConfigTLV* portconf); +int mlacp_fsm_update_port_channel_info(struct CSM* csm, struct mLACPPortChannelInfoTLV* tlv); +int mlacp_fsm_update_peerlink_info(struct CSM* csm, struct mLACPPeerLinkInfoTLV* tlv); +int mlacp_fsm_update_mac_info_from_peer(struct CSM* csm, struct mLACPMACInfoTLV* tlv); +#endif \ No newline at end of file diff --git a/src/iccpd/include/mlacp_tlv.h b/src/iccpd/include/mlacp_tlv.h new file mode 100644 index 000000000000..86bce2e0e943 --- /dev/null +++ b/src/iccpd/include/mlacp_tlv.h @@ -0,0 +1,456 @@ +/* + * mlacp_tlv.h + * + * Copyright(c) 2016-2019 Nephos/Estinet. + * + * 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, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintainer: jianjun, grace Li from nephos + */ + +#ifndef MLACP_TLV_H_ +#define MLACP_TLV_H_ + +#include + +#include "../include/msg_format.h" +#include "../include/port.h" + +#define MLACP_SYSCONF_NODEID_MSB_MASK 0x80 +#define MLACP_SYSCONF_NODEID_NODEID_MASK 0x70 +#define MLACP_SYSCONF_NODEID_FREE_MASK 0x0F + +/* + * RFC 7275 + * 7.2.3. mLACP System Config TLV + * [Page 51] + */ +struct mLACPSysConfigTLV +{ + ICCParameter icc_parameter; + /* [IEEE-802.1AX], Section 5.3.2. */ + uint8_t sys_id[ETHER_ADDR_LEN]; + /* [IEEE-802.1AX], Section 5.3.2. */ + uint16_t sys_priority; + /* + * RFC 7275 + * 7.2.3. mLACP System Config TLV + * [Page 51] + */ + uint8_t node_id; +} __attribute__ ((packed)); + +typedef struct mLACPSysConfigTLV mLACPSysConfigTLV; + +/* + * RFC 7275 + * 7.2.4. mLACP Aggregator Config TLV + * [Page 52] + * NOTE: In this project, Aggregator configuration and state TLV is not support. + */ +struct mLACPAggConfigTLV +{ + ICCParameter icc_parameter; + /* + * RFC 7275 + * 7.2.4. mLACP Aggregator Config TLV + * [Page 53] + */ + uint8_t ro_id[8]; + /* [IEEE-802.1AX], Section 5.4.6. */ + uint16_t agg_id; + /* + * RFC 7275 + * 7.2.4. mLACP Aggregator Config TLV + * [Page 53] + */ + uint8_t mac_addr[ETHER_ADDR_LEN]; + /* [IEEE-802.1AX], Section 5.3.5. */ + uint16_t actor_key; + /* + * RFC 7275 + * 7.2.4. mLACP Aggregator Config TLV + * [Page 53] + */ + uint16_t member_ports_priority; + uint8_t flags; + /* + * RFC 7275 + * 7.2.4. mLACP Aggregator Config TLV + * [Page 54] + */ + uint8_t agg_name_len; + char agg_name[MAX_L_PORT_NAME]; +} __attribute__ ((packed)); + +typedef struct mLACPAggConfigTLV mLACPAggConfigTLV; + +/* + * RFC 7275 + * 7.2.4. mLACP Port Config TLV + * [Page 54] + */ +struct mLACPPortConfigTLV +{ + ICCParameter icc_parameter; + /* [IEEE-802.1AX], Section 5.3.4. */ + uint16_t port_num; + /* + * RFC 7275 + * 7.2.5. mLACP Port Config TLV + * [Page 55] + */ + uint8_t mac_addr[ETHER_ADDR_LEN]; + /* [IEEE-802.1AX], Section 5.3.5. */ + uint16_t actor_key; + /* [IEEE-802.1AX], Section 5.3.4. */ + uint16_t port_priority; + /* IF-MIB [RFC2863] */ + uint32_t port_speed; + /* + * RFC 7275 + * 7.2.4. mLACP Port Config TLV + * [Page 55] + */ + uint8_t flags; + /* + * RFC 7275 + * 7.2.4. mLACP Port Config TLV + * [Page 56] + */ + uint8_t port_name_len; + /* IF-MIB [RFC2863] */ + char port_name[MAX_L_PORT_NAME]; + + /* NOS */ + uint8_t l3_mode; +} __attribute__ ((packed)); + +typedef struct mLACPPortConfigTLV mLACPPortConfigTLV; + +/* + * RFC 7275 + * 7.2.6. mLACP Port Priority TLV + * [Page 56] + */ +struct mLACPPortPriorityTLV +{ + ICCParameter icc_parameter; + /* + * RFC 7275 + * 7.2.6. mLACP Port Priority TLV + * [Page 57] + */ + uint16_t op_code; + /* [IEEE-802.1AX], Section 5.3.4. */ + uint16_t port_num; + /* [IEEE-802.1AX], Section 5.4.6. */ + uint16_t agg_id; + /* [IEEE-802.1AX], Section 5.3.4. */ + uint16_t last_port_priority; + uint16_t current_port_priority; +} __attribute__ ((packed)); + +typedef struct mLACPPortPriorityTLV mLACPPortPriorityTLV; + +/* + * RFC 7275 + * 7.2.7. mLACP Port State TLV + * [Page 58] + */ +struct mLACPPortStateTLV +{ + ICCParameter icc_parameter; + /* [IEEE-802.1AX], Section 5.4.2.2, item r. */ + uint8_t partner_sys_id[ETHER_ADDR_LEN]; + /* [IEEE-802.1AX], Section 5.4.2.2, item q. */ + uint16_t partner_sys_priority; + /* [IEEE-802.1AX], Section 5.4.2.2, item u. */ + uint16_t partner_port_num; + /* [IEEE-802.1AX], Section 5.4.2.2, item t. */ + uint16_t partner_port_priority; + /* [IEEE-802.1AX], Section 5.4.2.2, item s. */ + uint16_t partner_key; + /* [IEEE-802.1AX], Section 5.4.2.2, item v. */ + uint8_t partner_state; + /* [IEEE-802.1AX], Section 5.4.2.2, item m. */ + uint8_t actor_state; + /* [IEEE-802.1AX], Section 5.3.4. */ + uint16_t actor_port_num; + /* [IEEE-802.1AX], Section 5.3.5. */ + uint16_t actor_key; + /* [IEEE-802.1AX], Section 5.4.8 */ + uint8_t selected; + /* + * RFC 7275 + * 7.2.7. mLACP Port State TLV + * [Page 60] + */ + uint8_t port_state; + /* [IEEE-802.1AX], Section 5.4.6. */ + uint16_t agg_id; + + /* NOS */ + uint16_t port_id; + uint8_t l3_mode; + uint8_t is_peer_link; +} __attribute__ ((packed)); + +typedef struct mLACPPortStateTLV mLACPPortStateTLV; + +/* + * RFC 7275 + * 7.2.8. mLACP Aggregator State TLV + * [Page 60] + * NOTE: In this project, Aggregator configuration and state TLV is not support. + */ +struct mLACPAggPortStateTLV +{ + ICCParameter icc_parameter; + /* [IEEE-802.1AX], Section 5.4.2.2, item r. */ + uint8_t partner_sys_id[ETHER_ADDR_LEN]; + /* [IEEE-802.1AX], Section 5.4.2.2, item q. */ + uint16_t partner_sys_priority; + /* [IEEE-802.1AX], Section 5.4.2.2, item s. */ + uint16_t partner_key; + /* [IEEE-802.1AX], Section 5.4.6. */ + uint16_t agg_id; + /* [IEEE-802.1AX], Section 5.3.5. */ + uint16_t actor_key; + /* + * RFC 7275 + * 7.2.8. mLACP Aggregator State TLV + * [Page 61] + */ + uint8_t agg_state; +} __attribute__ ((packed)); + +typedef struct mLACPAggPortStateTLV mLACPAggPortStateTLV; + +/* + * RFC 7275 + * 7.2.9. mLACP Synchronization Request TLV + * [Page 61] + */ +struct mLACPSyncReqTLV +{ + ICCParameter icc_parameter; + /* + * RFC 7275 + * 7.2.9. mLACP Synchronization Request TLV + * [Page 62] + */ + uint16_t req_num; + +#if __BYTE_ORDER == __BIG_ENDIAN + uint16_t c_bit : 1; + /* + * RFC 7275 + * 7.2.9. mLACP Synchronization Request TLV + * [Page 63] + */ + uint16_t s_bit : 1; + uint16_t req_type : 14; +#elif __BYTE_ORDER == __LITTLE_ENDIAN + uint16_t req_type : 14; + /* + * RFC 7275 + * 7.2.9. mLACP Synchronization Request TLV + * [Page 63] + */ + uint16_t s_bit : 1; + uint16_t c_bit : 1; +#endif + /* [IEEE-802.1AX], Section 5.3.4. */ + /* [IEEE-802.1AX], Section 5.4.6. */ + uint16_t port_num_agg_id; + /* [IEEE-802.1AX], Section 5.3.5. */ + uint16_t actor_key; +} __attribute__ ((packed)); + +typedef struct mLACPSyncReqTLV mLACPSyncReqTLV; + +/* + * RFC 7275 + * 7.2.10. mLACP Synchronization Data TLV + * [Page 63] + */ +struct mLACPSyncDataTLV +{ + ICCParameter icc_parameter; + /* + * RFC 7275 + * 7.2.10. mLACP Synchronization Data TLV + * [Page 64] + */ + uint16_t req_num; + uint16_t flags; +} __attribute__ ((packed)); + +typedef struct mLACPSyncDataTLV mLACPSyncDataTLV; + +/* VLAN Information TLV*/ +struct mLACPVLANData +{ + uint16_t vlan_id; +} __attribute__ ((packed)); + +/* + * Port Channel Information TLV + */ +struct mLACPPortChannelInfoTLV +{ + ICCParameter icc_parameter; + uint16_t agg_id; + char if_name[MAX_L_PORT_NAME]; + uint8_t if_name_len; + uint8_t l3_mode; + uint32_t ipv4_addr; + uint16_t po_id; + uint16_t num_of_vlan_id; + struct mLACPVLANData vlanData[0]; +} __attribute__ ((packed)); + +typedef struct mLACPPortChannelInfoTLV mLACPPortChannelInfoTLV; + +/* + * Port PeerLink Information TLV + */ +struct mLACPPeerLinkInfoTLV +{ + ICCParameter icc_parameter; + char if_name[MAX_L_PORT_NAME]; + uint8_t port_type; +} __attribute__ ((packed)); + +typedef struct mLACPPeerLinkInfoTLV mLACPPeerLinkInfoTLV; + +struct mLACPVLANInfoTLV +{ + ICCParameter icc_parameter; + uint16_t id; /* Local Interface ID, not VLAN ID */ + uint16_t num_of_vlan_id; + struct mLACPVLANData vlanData[0]; +} __attribute__ ((packed)); + +/* Mac entry Information TLV*/ +struct mLACPMACData +{ + uint8_t type;/*add or del*/ + char mac_str[ETHER_ADDR_STR_LEN]; + uint16_t vid; + /*Current if name that set in chip*/ + char ifname[MAX_L_PORT_NAME]; +} __attribute__ ((packed)); + +/* + * MAC Information TLV + */ +struct mLACPMACInfoTLV +{ + ICCParameter icc_parameter; + uint16_t num_of_entry; + struct mLACPMACData MacEntry[0]; +} __attribute__ ((packed)); + +struct ARPMsg +{ + uint8_t op_type; + char ifname[MAX_L_PORT_NAME]; + uint32_t ipv4_addr; + uint8_t mac_addr[ETHER_ADDR_LEN]; +}; + +/* + * ARP Information TLV + */ +struct mLACPARPInfoTLV +{ + ICCParameter icc_parameter; + /* Local Interface ID */ + uint16_t num_of_entry; + struct ARPMsg ArpEntry[0]; +} __attribute__ ((packed)); + +/* + * NOS: STP Information TLV + */ +struct stp_msg_s; +struct mLACPSTPInfoTLV +{ + ICCParameter icc_parameter; + uint8_t stp_msg[0]; +} __attribute__ ((packed)); + +/* + * NOS: Heartbeat + */ +struct mLACPHeartbeatTLV +{ + ICCParameter icc_parameter; + uint8_t heartbeat; +} __attribute__ ((packed)); + +/* + * NOS: Warm_reboot + */ +struct mLACPWarmbootTLV +{ + ICCParameter icc_parameter; + uint8_t warmboot; +} __attribute__ ((packed)); + +enum ARP_OP_TYPE +{ + ARP_SYNC_LIF, + ARP_SYNC_ADD, + ARP_SYNC_DEL, +}; + +enum MAC_AGE_TYPE +{ + MAC_AGE_LOCAL = 1, /*MAC in local switch is ageout*/ + MAC_AGE_PEER = 2, /*MAC in peer switch is ageout*/ +}; + +enum MAC_OP_TYPE +{ + MAC_SYNC_ADD = 1, + MAC_SYNC_DEL = 2, + MAC_SYNC_ACK = 4, +}; + +enum MAC_TYPE +{ + MAC_TYPE_STATIC = 1, + MAC_TYPE_DYNAMIC = 2, +}; + +struct MACMsg +{ + uint8_t op_type; /*add or del*/ + uint8_t fdb_type; /*static or dynamic*/ + char mac_str[ETHER_ADDR_STR_LEN]; + uint16_t vid; + /*Current if name that set in chip*/ + char ifname[MAX_L_PORT_NAME]; + /*if we set the mac to peer-link, origin_ifname store the + original if name that learned from chip*/ + char origin_ifname[MAX_L_PORT_NAME]; + uint8_t age_flag;/*local or peer is age?*/ +}; + +#endif /* MLACP_TLV_H_ */ diff --git a/src/iccpd/include/msg_format.h b/src/iccpd/include/msg_format.h new file mode 100644 index 000000000000..5f4deb18772b --- /dev/null +++ b/src/iccpd/include/msg_format.h @@ -0,0 +1,499 @@ +/* + * msg_format.h + * + * Copyright(c) 2016-2019 Nephos/Estinet. + * + * 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, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintainer: jianjun, grace Li from nephos + */ + +#ifndef MSG_FORMAT_H_ +#define MSG_FORMAT_H_ +#include +#include +#include "../include/port.h" + +#define MAX_MSG_LOG_SIZE 128 + +/* + * RFC 5561 + * 4. Capability Message + * [Page 7] + */ +#define MSG_T_CAPABILITY 0x0202 + +/* + * RFC 7275 + * 6.1.1. ICC Header - Message Length + * [Page 25] + * 2-octet integer specifying the total length of this message in octets, + * excluding the "U-bit", "Message Type", and "Length" fields. + */ +#define MSG_L_INCLUD_U_BIT_MSG_T_L_FIELDS 4 + +/* + * RFC 7275 + * 12.1. Message Type Name Space + * [Page 79] + */ +#define MSG_T_RG_CONNECT 0x0700 +#define MSG_T_RG_DISCONNECT 0x0701 +#define MSG_T_NOTIFICATION 0x0702 +#define MSG_T_RG_APP_DATA 0x0703 + +/* + * RFC 7275 + * 12.2. TLV Type Name Space + * [Page 79] + */ +#define TLV_T_ICCP_CAPABILITY 0x0700 +#define TLV_L_ICCP_CAPABILITY 0x4 + +/* + * RFC 7275 + * 12.3. ICC RG Parameter Type Space + * [Page 80] + */ +#define TLV_T_ICC_SENDER_NAME 0x0001 +#define MAX_L_ICC_SENDER_NAME 80 +#define TLV_T_NAK 0x0002 +#define TLV_T_REQUESTED_PROTOCOL_VER 0x0003 +#define TLV_T_DISCONNECT_CODE 0x0004 +#define TLV_L_DISCONNECT_CODE 0x4 +#define TLV_T_ICC_RG_ID 0x0005 +#define TLV_L_ICC_RG_ID 0x4 + +#define TLV_T_MLACP_CONNECT 0x0030 +#define TLV_T_MLACP_DISCONNECT 0x0031 +#define TLV_T_MLACP_SYSTEM_CONFIG 0x0032 +#define TLV_T_MLACP_PORT_CONFIG 0x0033 //no support +#define TLV_T_MLACP_PORT_PRIORITY 0x0034 //no support +#define TLV_T_MLACP_PORT_STATE 0x0035 //no support +#define TLV_T_MLACP_AGGREGATOR_CONFIG 0x0036 +#define TLV_T_MLACP_AGGREGATOR_STATE 0x0037 +#define TLV_T_MLACP_SYNC_REQUEST 0x0038 +#define TLV_T_MLACP_SYNC_DATA 0x0039 +#define TLV_T_MLACP_HEARTBEAT 0x003A +#define TLV_T_MLACP_DISCONNECT_CAUSE 0x003B //not yet + +/* Self define Feature */ +#define TLV_T_MLACP_ORPHAN_PORT 0x1033 //not yet +#define TLV_T_MLACP_PORT_CHANNEL_INFO 0x1034 +#define TLV_T_MLACP_PEERLINK_INFO 0x1035 +#define TLV_T_MLACP_ARP_INFO 0x1036 +#define TLV_T_MLACP_STP_INFO 0x1037//no support +#define TLV_T_MLACP_MAC_INFO 0x1038 +#define TLV_T_MLACP_WARMBOOT_FLAG 0x1039 +#define TLV_T_MLACP_LIST_END 0x104a //list end + +/* Debug */ +static char* get_tlv_type_string(int type) +{ + switch (type) + { + case TLV_T_ICCP_CAPABILITY: + return "TLV_T_ICCP_CAPABILITY"; + + case TLV_T_ICC_SENDER_NAME: + return "TLV_T_ICC_SENDER_NAME"; + + case TLV_T_NAK: + return "TLV_T_NAK"; + + case TLV_T_REQUESTED_PROTOCOL_VER: + return "TLV_T_REQUESTED_PROTOCOL_VER"; + + case TLV_T_DISCONNECT_CODE: + return "TLV_T_DISCONNECT_CODE"; + + case TLV_T_ICC_RG_ID: + return "TLV_T_ICC_RG_ID"; + + case TLV_T_MLACP_CONNECT: + return "TLV_T_MLACP_CONNECT"; + + case TLV_T_MLACP_DISCONNECT: + return "TLV_T_MLACP_DISCONNECT"; + + case TLV_T_MLACP_SYSTEM_CONFIG: + return "TLV_T_MLACP_SYSTEM_CONFIG"; + + case TLV_T_MLACP_PORT_CONFIG: + return "TLV_T_MLACP_PORT_CONFIG"; + + case TLV_T_MLACP_PORT_PRIORITY: + return "TLV_T_MLACP_PORT_PRIORITY"; + + case TLV_T_MLACP_PORT_STATE: + return "TLV_T_MLACP_PORT_STATE"; + + case TLV_T_MLACP_AGGREGATOR_CONFIG: + return "TLV_T_MLACP_AGGREGATOR_CONFIG"; + + case TLV_T_MLACP_AGGREGATOR_STATE: + return "TLV_T_MLACP_AGGREGATOR_STATE"; + + case TLV_T_MLACP_SYNC_REQUEST: + return "TLV_T_MLACP_SYNC_REQUEST"; + + case TLV_T_MLACP_SYNC_DATA: + return "TLV_T_MLACP_SYNC_DATA"; + + case TLV_T_MLACP_HEARTBEAT: + return "TLV_T_MLACP_HEARTBEAT"; + + case TLV_T_MLACP_DISCONNECT_CAUSE: + return "TLV_T_MLACP_DISCONNECT_CAUSE"; + + /* NOS Feature */ + case TLV_T_MLACP_ORPHAN_PORT: + return "TLV_T_MLACP_ORPHAN_PORT"; + + case TLV_T_MLACP_PORT_CHANNEL_INFO: + return "TLV_T_MLACP_PORT_CHANNEL_INFO"; + + case TLV_T_MLACP_PEERLINK_INFO: + return "TLV_T_MLACP_PEERLINK_INFO"; + + case TLV_T_MLACP_ARP_INFO: + return "TLV_T_MLACP_ARP_INFO"; + + case TLV_T_MLACP_MAC_INFO: + return "TLV_T_MLACP_MAC_INFO"; + + case TLV_T_MLACP_STP_INFO: + return "TLV_T_MLACP_STP_INFO"; + } + + return "UNKNOWN"; +} + +/* + * RFC 7275 + * 12.4. Status Code Name Space + * [Page 81] + */ +#define STATUS_CODE_U_ICCP_RG 0x00010001 +#define STATUS_CODE_ICCP_CONNECTION_COUNT_EXCEEDED 0x00010002 +#define STATUS_CODE_ICCP_APP_CONNECTION_COUNT_EXCEEDED 0x00010003 +#define STATUS_CODE_ICCP_APP_NOT_IN_RG 0x00010004 +#define STATUS_CODE_INCOMPATIBLE_ICCP_PROTOCOL_VER 0x00010005 +#define STATUS_CODE_ICCP_REJECTED_MSG 0x00010006 +#define STATUS_CODE_ICCP_ADMINISTRATIVELY_DISABLED 0x00010007 +#define STATUS_CODE_ICCP_RG_REMOVED 0x00010010 +#define STATUS_CODE_ICCP_APP_REMOVED_FROM_RG 0x00010011 + + +/* Debug */ +static char* get_status_string(int status) +{ + switch (status) + { + case STATUS_CODE_U_ICCP_RG: + return "Unknown ICCP RG"; + + case STATUS_CODE_ICCP_CONNECTION_COUNT_EXCEEDED: + return "ICCP Connection Count Exceeded"; + + case STATUS_CODE_ICCP_APP_CONNECTION_COUNT_EXCEEDED: + return "ICCP Application Connection Count Exceede"; + + case STATUS_CODE_ICCP_APP_NOT_IN_RG: + return "ICCP Application not in RG"; + + case STATUS_CODE_INCOMPATIBLE_ICCP_PROTOCOL_VER: + return "Incompatible ICCP Protocol Version"; + + case STATUS_CODE_ICCP_REJECTED_MSG: + return "ICCP Rejected Message"; + + case STATUS_CODE_ICCP_ADMINISTRATIVELY_DISABLED: + return "ICCP Administratively Disabled"; + + case STATUS_CODE_ICCP_RG_REMOVED: + return "ICCP RG Removed"; + + case STATUS_CODE_ICCP_APP_REMOVED_FROM_RG: + return "ICCP Application Removed from RG"; + } + + return "UNKNOWN"; +} +/* + * RFC 5036 + * 3.5. LDP Messages + * [Page 44] + */ +struct LDPHdr +{ +#if __BYTE_ORDER == __BIG_ENDIAN + uint16_t u_bit : 1; + uint16_t msg_type : 15; +#elif __BYTE_ORDER == __LITTLE_ENDIAN + uint16_t msg_type : 15; + uint16_t u_bit : 1; +#endif + uint16_t msg_len; + uint32_t msg_id; +} __attribute__ ((packed)); + +typedef struct LDPHdr LDPHdr; + +/* + * RFC 7275 + * 6.1.1. ICC Header + * [Page 24] + */ +struct ICCRGIDTLV +{ + uint16_t type; + uint16_t len; + uint32_t icc_rg_id; +} __attribute__ ((packed)); + +typedef struct ICCRGIDTLV ICCRGIDTLV; + +struct ICCHdr +{ + LDPHdr ldp_hdr; + ICCRGIDTLV icc_rg_id_tlv; +} __attribute__ ((packed)); + +typedef struct ICCHdr ICCHdr; + +/* + * RFC 7275 + * 6.1.2. ICC Parameter Encoding + * [Page 26] + */ +struct ICCParameter +{ +#if __BYTE_ORDER == __BIG_ENDIAN + uint16_t u_bit : 1; + uint16_t f_bit : 1; + uint16_t type : 14; +#elif __BYTE_ORDER == __LITTLE_ENDIAN + uint16_t type : 14; + uint16_t f_bit : 1; + uint16_t u_bit : 1; +#endif + uint16_t len; +} __attribute__ ((packed)); + +typedef struct ICCParameter ICCParameter; + +/* + * RFC 7275 + * 6.2.1. ICC Sender Name TLV + * [Page 28] + */ +struct ICCSenderNameTLV +{ + ICCParameter icc_parameter; + char sender_name[MAX_L_ICC_SENDER_NAME]; +} __attribute__ ((packed)); + +typedef struct ICCSenderNameTLV ICCSenderNameTLV; + +/* + * RFC 7275 + * 6.3. RG Disconnect Message + * [Page 29] + */ +struct DisconnectCodeTLV +{ + ICCParameter icc_parameter; + uint32_t iccp_status_code; +} __attribute__ ((packed)); + +typedef struct DisconnectCodeTLV DisconnectCodeTLV; + +/* + * RFC 7275 + * 6.4.1. Notification Message TLVs + * [Page 32] + */ +struct NAKTLV +{ + ICCParameter icc_parameter; + uint32_t iccp_status_code; + uint32_t rejected_msg_id; +} __attribute__ ((packed)); + +typedef struct NAKTLV NAKTLV; + +/* + * RFC 7275 + * 6.4.1. Notification Message TLVs + * [Page 34] + */ +struct RequestedProtocolVerTLV +{ + ICCParameter icc_parameter; + uint16_t connection_ref; + uint16_t requested_ver; +} __attribute__ ((packed)); + +typedef struct RequestedProtocolVerTLV RequestedProtocolVerTLV; + +/* + * RFC 7275 + * 8. LDP Capability Negotiation + * [Page 65] + */ +struct LDPICCPCapabilityTLV +{ + ICCParameter icc_parameter; +#if __BYTE_ORDER == __BIG_ENDIAN + uint16_t s_bit : 1; + uint16_t reserved : 15; +#elif __BYTE_ORDER == __LITTLE_ENDIAN + uint16_t reserved : 15; + uint16_t s_bit : 1; +#endif + uint8_t major_ver; + uint8_t minior_ver; +} __attribute__ ((packed)); + +typedef struct LDPICCPCapabilityTLV LDPICCPCapabilityTLV; + +/* + * RFC 7275 + * 7.2.1. mLACP Connect TLV + * [Page 47] + */ +struct AppConnectTLV +{ + ICCParameter icc_parameter; + uint16_t protocol_version; +#if __BYTE_ORDER == __BIG_ENDIAN + uint16_t a_bit : 1; + uint16_t reserved : 15; +#elif __BYTE_ORDER == __LITTLE_ENDIAN + uint16_t reserved : 15; + uint16_t a_bit : 1; +#endif + + /* Optional Sub-TLVs */ + /* No optional sub-TLVs in this version */ +} __attribute__ ((packed)); + +typedef struct AppConnectTLV AppConnectTLV; + +/* + * RFC 7275 + * 7.2.2. mLACP Disconnect TLV + * [Page 48] + */ +struct AppDisconnectTLV +{ + ICCParameter icc_parameter; + + /* Optional Sub-TLVs */ + /* mLACP Disconnect Cause TLV */ +} __attribute__ ((packed)); + +typedef struct AppDisconnectTLV AppDisconnectTLV; + +/* + * RFC 7275 + * 7.2.2.1. mLACP Disconnect Cause TLV + * [Page 49] + */ +struct AppDisconnectCauseTLV +{ + ICCParameter iccp_parameter; + + /* Disconnect Cause String */ + char cause_string[0]; /* Trick */ +} __attribute__ ((packed)); + +/*syncd send msg type to iccpd*/ +typedef enum mclag_syncd_msg_type_e_ +{ + MCLAG_SYNCD_MSG_TYPE_NONE = 0, + MCLAG_SYNCD_MSG_TYPE_FDB_OPERATION = 1 +}mclag_syncd_msg_type_e; + +typedef enum mclag_msg_type_e_ +{ + MCLAG_MSG_TYPE_NONE = 0, + MCLAG_MSG_TYPE_PORT_ISOLATE = 1, + MCLAG_MSG_TYPE_PORT_MAC_LEARN_MODE = 2, + MCLAG_MSG_TYPE_FLUSH_FDB = 3, + MCLAG_MSG_TYPE_SET_MAC = 4, + MCLAG_MSG_TYPE_SET_FDB = 5, + MCLAG_MSG_TYPE_GET_FDB_CHANGES = 20 +}mclag_msg_type_e; + + +typedef enum mclag_sub_option_type_e_ +{ + MCLAG_SUB_OPTION_TYPE_NONE = 0, + MCLAG_SUB_OPTION_TYPE_ISOLATE_SRC = 1, + MCLAG_SUB_OPTION_TYPE_ISOLATE_DST = 2, + MCLAG_SUB_OPTION_TYPE_MAC_LEARN_ENABLE = 3, + MCLAG_SUB_OPTION_TYPE_MAC_LEARN_DISABLE = 4, + MCLAG_SUB_OPTION_TYPE_SET_MAC_SRC = 5, + MCLAG_SUB_OPTION_TYPE_SET_MAC_DST = 6 +} mclag_sub_option_type_e; + + +struct IccpSyncdHDr +{ + uint8_t ver; + uint8_t type; + uint16_t len; +}; + +typedef struct mclag_sub_option_hdr_t_ +{ + + uint8_t op_type; + + /* + * Length of option value, not including the header. + */ + uint16_t op_len; + uint8_t data[]; +}mclag_sub_option_hdr_t; + +struct mclag_fdb_info +{ + char mac[ETHER_ADDR_STR_LEN]; + unsigned int vid; + char port_name[MAX_L_PORT_NAME]; + short type; /*dynamic or static*/ + short op_type; /*add or del*/ +}; + +/* For storing message log: For Notification TLV */ +struct MsgTypeSet +{ + uint32_t msg_id; + uint16_t type; + uint16_t tlv; + +}; + +struct MsgLog +{ + struct MsgTypeSet msg[MAX_MSG_LOG_SIZE]; + uint32_t end_index; +}; + +#endif /* MSG_FORMAT_H_ */ diff --git a/src/iccpd/include/port.h b/src/iccpd/include/port.h new file mode 100644 index 000000000000..bdcaf66a68ad --- /dev/null +++ b/src/iccpd/include/port.h @@ -0,0 +1,156 @@ +/* + * port.h + * + * Copyright(c) 2016-2019 Nephos/Estinet. + * + * 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, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintainer: jianjun, grace Li from nephos + */ + +#ifndef PORT_H_ +#define PORT_H_ + +#include +#include +#include + +#define ETHER_ADDR_LEN 6 +#define ETHER_ADDR_STR_LEN 18 +/* + * RFC 7275 + * 7.2.4. mLACP Port Config TLV + * [Page 56] + */ +#define MAX_L_PORT_NAME 20 + +/* defined in RFC 7275 - 7.2.7 (p.59) */ +#define PORT_STATE_UP 0x00 +#define PORT_STATE_DOWN 0x01 +#define PORT_STATE_ADMIN_DOWN 0x02 +#define PORT_STATE_TEST 0x03 + +/* Interface Type */ +#define IF_T_UNKNOW -1 +#define IF_T_PORT 0 +#define IF_T_PORT_CHANNEL 1 +#define IF_T_VLAN 2 +#define IF_T_VXLAN 3 +#define IF_T_BRIDGE 4 +typedef struct +{ + char *ifname; + int type; +} itf_type_t; + +struct If_info +{ + char name[MAX_L_PORT_NAME]; + LIST_ENTRY(If_info) csm_next; +}; + +struct VLAN_ID +{ + uint16_t vid; + uint16_t vlan_removed; + struct LocalInterface* vlan_itf; /* loacl vlan interface */ + LIST_ENTRY(VLAN_ID) port_next; +}; + +struct PeerInterface +{ + int ifindex; + int type; + char name[MAX_L_PORT_NAME]; + + uint8_t mac_addr[ETHER_ADDR_LEN]; + uint8_t state; + uint32_t ipv4_addr; + + uint8_t l3_mode; + uint8_t is_peer_link; + int po_id; + uint8_t po_active; + + struct CSM* csm; + + LIST_ENTRY(PeerInterface) mlacp_next; + LIST_HEAD(peer_vlan_list, VLAN_ID) vlan_list; +}; + +struct LocalInterface +{ + int ifindex; + int type; + char name[MAX_L_PORT_NAME]; + + uint8_t mac_addr[ETHER_ADDR_LEN]; + uint8_t mac_addr_ori[ETHER_ADDR_LEN]; + uint8_t state; + uint32_t ipv4_addr; + uint8_t prefixlen; + + uint8_t l3_mode; + uint8_t l3_mac_addr[ETHER_ADDR_LEN]; + uint8_t is_peer_link; + char portchannel_member_buf[512]; + uint8_t is_arp_accept; + int po_id; /* Port Channel ID */ + uint8_t po_active; /* Port Channel is in active status? */ + int mlacp_state; /* Record mlacp state */ + uint8_t isolate_to_peer_link; + + struct CSM* csm; + + uint8_t changed; + uint8_t port_config_sync; + + LIST_HEAD(local_vlan_list, VLAN_ID) vlan_list; + + LIST_ENTRY(LocalInterface) system_next; + LIST_ENTRY(LocalInterface) system_purge_next; + LIST_ENTRY(LocalInterface) mlacp_next; + LIST_ENTRY(LocalInterface) mlacp_purge_next; +}; + +struct LocalInterface* local_if_create(int ifindex, char* ifname, int type); +struct LocalInterface* local_if_find_by_name(const char* ifname); +struct LocalInterface* local_if_find_by_ifindex(int ifindex); +struct LocalInterface* local_if_find_by_po_id(int po_id); + +void local_if_destroy(char *ifname); +void local_if_change_flag_clear(void); +void local_if_purge_clear(void); +int local_if_is_l3_mode(struct LocalInterface* local_if); + +void local_if_init(struct LocalInterface*); +void local_if_finalize(struct LocalInterface*); + +struct PeerInterface* peer_if_create(struct CSM* csm, int peer_if_number, int type); +struct PeerInterface* peer_if_find_by_name(struct CSM* csm, char* name); + +void peer_if_destroy(struct PeerInterface* pif); +int peer_if_add_vlan(struct PeerInterface* peer_if, uint16_t vlan_id); +int peer_if_clean_unused_vlan(struct PeerInterface* peer_if); +/* VLAN manipulation */ +int local_if_add_vlan(struct LocalInterface* local_if, uint16_t vid); +void local_if_del_vlan(struct LocalInterface* local_if, uint16_t vid); +void local_if_del_all_vlan(struct LocalInterface* lif); + +/* ARP manipulation */ +int set_sys_arp_accept_flag(char* ifname, int flag); + +#endif /* PORT_H_ */ diff --git a/src/iccpd/include/scheduler.h b/src/iccpd/include/scheduler.h new file mode 100644 index 000000000000..b3be274282c8 --- /dev/null +++ b/src/iccpd/include/scheduler.h @@ -0,0 +1,59 @@ +/* + * scheduler.h + * + * Copyright(c) 2016-2019 Nephos/Estinet. + * + * 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, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintainer: jianjun, grace Li from nephos + */ + +#ifndef SCHEDULER_H_ +#define SCHEDULER_H_ + +#include + +#include +#include +#include + +#include +#include +#include + +struct CSM; + +#define CONNECT_INTERVAL_SEC 1 +#define CONNECT_TIMEOUT_MSEC 100 +#define HEARTBEAT_TIMEOUT_SEC 15 +#define TRANSIT_INTERVAL_SEC 1 +#define EPOLL_TIMEOUT_MSEC 100 + +int scheduler_prepare_session(struct CSM*); +int scheduler_check_csm_config(struct CSM*); +int scheduler_unregister_sock_read_event_callback(struct CSM*); +void scheduler_session_disconnect_handler(struct CSM*); +void scheduler_init(); +void scheduler_finalize(); +void scheduler_loop(); +void scheduler_start(); +void scheduler_server_sock_init(); +int scheduler_csm_read_callback(struct CSM* csm); +int iccp_get_server_sock_fd(); +int scheduler_server_accept(); +int iccp_receive_signal_handler(struct System* sys); + +#endif /* SCHEDULER_H_ */ diff --git a/src/iccpd/include/system.h b/src/iccpd/include/system.h new file mode 100644 index 000000000000..bb89dbb2f3c2 --- /dev/null +++ b/src/iccpd/include/system.h @@ -0,0 +1,102 @@ +/* + * system.h + * + * Copyright(c) 2016-2019 Nephos/Estinet. + * + * 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, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintainer: jianjun, grace Li from nephos + */ + +#ifndef SYSTEM_H_ +#define SYSTEM_H_ + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "../include/port.h" + +#define FRONT_PANEL_PORT_PREFIX "Ethernet" +#define PORTCHANNEL_PREFIX "PortChannel" +#define VLAN_PREFIX "Vlan" +#define VXLAN_TUNNEL_PREFIX "VTTNL" + +#define WARM_REBOOT 1 + +#define MCLAG_ERROR -1 + +struct CSM; + +#ifndef MAX_BUFSIZE + #define MAX_BUFSIZE 4096 +#endif + +struct System +{ + int server_fd;/* Peer-Link Socket*/ + int sync_fd; + int sync_ctrl_fd; + int arp_receive_fd; + int epoll_fd; + + struct nl_sock * genric_sock; + int genric_sock_seq; + int family; + struct nl_sock * route_sock; + int route_sock_seq; + struct nl_sock * genric_event_sock; + struct nl_sock * route_event_sock; + + int sig_pipe_r; + int sig_pipe_w; + int warmboot_start; + int warmboot_exit; + + /* Info List*/ + LIST_HEAD(csm_list, CSM) csm_list; + LIST_HEAD(lif_all_list, LocalInterface) lif_list; + LIST_HEAD(lif_purge_all_list, LocalInterface) lif_purge_list; + + /* Settings */ + char* log_file_path; + char* cmd_file_path; + char* config_file_path; + char* mclagdctl_file_path; + int pid_file_fd; + int telnet_port; + fd_set readfd; /*record socket need to listen*/ + int readfd_count; + time_t csm_trans_time; + int need_sync_team_again; + int need_sync_netlink_again; +}; + +struct CSM* system_create_csm(); +struct CSM* system_get_csm_by_peer_ip(const char*); +struct CSM* system_get_csm_by_mlacp_id(int id); +struct System* system_get_instance(); +void system_finalize(); +void system_init(struct System*); + +#endif /* SYSTEM_H_ */ diff --git a/src/iccpd/src/Makefile b/src/iccpd/src/Makefile new file mode 100644 index 000000000000..5e8cebc703c0 --- /dev/null +++ b/src/iccpd/src/Makefile @@ -0,0 +1,55 @@ +LIBNL_CFLAGS = -I/usr/include/libnl3 +LIBNL_LIBS = -lnl-cli-3 -lnl-genl-3 -lnl-nf-3 -lnl-route-3 -lnl-3 + +CC = gcc +SOURCES = app_csm.c cmd_option.c iccp_cli.c iccp_cmd_show.c iccp_cmd.c \ +iccp_csm.c iccp_ifm.c iccp_main.c logger.c \ +port.c scheduler.c system.c iccp_consistency_check.c \ +mlacp_link_handler.c \ +mlacp_sync_prepare.c mlacp_sync_update.c\ +mlacp_fsm.c \ +iccp_netlink.c + +OBJECTS = app_csm.o cmd_option.o iccp_cli.o iccp_cmd_show.o iccp_cmd.o \ +iccp_csm.o iccp_ifm.o iccp_main.o logger.o \ +port.o scheduler.o system.o iccp_consistency_check.o\ +mlacp_link_handler.o \ +mlacp_sync_prepare.o mlacp_sync_update.o \ +mlacp_fsm.o \ +iccp_netlink.o + +HEADERS = ../include/app_csm.h ../include/cmd_option.h ../include/iccp_cli.h \ +../include/iccp_cmd_show.h \ +../include/iccp_csm.h ../include/iccp_ifm.h \ +../include/logger.h ../include/mlacp_fsm.h \ +../include/mlacp_tlv.h ../include/msg_format.h ../include/port.h \ +../include/scheduler.h ../include/system.h \ +../include/iccp_consistency_check.h ../include/route_info.h \ +../include/iccp_netlink.h + +//CFLAGS = -g -O2 -fstack-protector-strong -Wformat -Werror=format-security $(LIBNL_CFLAGS) -I../include/ +CFLAGS = -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -I../include/ $(LIBNL_CFLAGS) +LDFLAGS = $(LIBNL_LIBS) -lpthread +LDADD = $(LIBZEBRA_BIN) + +all: iccpd + +%.o: %.c $(HEADERS) + +iccpd: $(OBJECTS) + $(CC) -o ../iccpd $(OBJECTS) $(LDFLAGS) + +DEPS = $(patsubst %.o, %.d, $(OBJECTS)) + +-include $(DEPS) +%.d:%.c + @set -e; rm -f $@; \ + gcc -MM $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + + + +.PHONY: clean +clean: + -rm -f $(OBJECTS) $(DEPS) ../iccpd diff --git a/src/iccpd/src/app_csm.c b/src/iccpd/src/app_csm.c new file mode 100644 index 000000000000..589cf94015d8 --- /dev/null +++ b/src/iccpd/src/app_csm.c @@ -0,0 +1,316 @@ +/* + * app_csm.c + * Copyright(c) 2016-2019 Nephos/Estinet. + * + * 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, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintainer: jianjun, grace Li from nephos + */ + +#include +#include +#include + +#include "../include/iccp_csm.h" +#include "../include/logger.h" +#include "../include/scheduler.h" +#include "../include/system.h" +#include "../include/iccp_netlink.h" +#include "../include/mlacp_link_handler.h" +/***************************************** +* Define +* +* ***************************************/ +#define APP_CSM_QUEUE_REINIT(list) \ + { \ + struct Msg* msg = NULL; \ + while (!TAILQ_EMPTY(&(list))) { \ + msg = TAILQ_FIRST(&(list)); \ + TAILQ_REMOVE(&(list), msg, tail); \ + free(msg->buf); \ + free(msg); \ + } \ + TAILQ_INIT(&(list)); \ + } + +/* Application State Machine instance initialization */ +void app_csm_init(struct CSM* csm, int all) +{ + if (csm == NULL ) + return; + + APP_CSM_QUEUE_REINIT(csm->app_csm.app_msg_list); + + if (all) + { + bzero(&(csm->app_csm), sizeof(struct AppCSM)); + APP_CSM_QUEUE_REINIT(csm->app_csm.app_msg_list); + } + + csm->app_csm.current_state = APP_NONEXISTENT; + csm->app_csm.rx_connect_msg_id = 0; + csm->app_csm.tx_connect_msg_id = 0; + csm->app_csm.invalid_msg_id = 0; + csm->app_csm.invalid_msg = 0; + csm->app_csm.nak_msg = 0; + + mlacp_init(csm, all); +} + +/* Application State Machine instance tear down */ +void app_csm_finalize(struct CSM* csm) +{ + mlacp_finalize(csm); +} + +/* Application State Machine Transition */ +void app_csm_transit(struct CSM* csm) +{ + if (csm == NULL ) + return; + + /* torn down event */ + if (csm->app_csm.current_state != APP_NONEXISTENT && csm->sock_fd <= 0) + { + csm->app_csm.current_state = APP_NONEXISTENT; + return; + } + + if (csm->app_csm.current_state != APP_OPERATIONAL && csm->current_state == ICCP_OPERATIONAL) + { + csm->app_csm.current_state = APP_OPERATIONAL; + } + + return; +} + +/* Add received message into application message list */ +void app_csm_enqueue_msg(struct CSM* csm, struct Msg* msg) +{ + ICCHdr* icc_hdr = NULL; + ICCParameter* param = NULL; + NAKTLV* naktlv = NULL; + int tlv = -1; + int i = 0; + + if (csm == NULL ) + { + if (msg != NULL ) + free(msg); + return; + } + if (msg == NULL ) + return; + + icc_hdr = (ICCHdr*)msg->buf; + param = (ICCParameter*)&msg->buf[sizeof(struct ICCHdr)]; + *(uint16_t *)param = ntohs(*(uint16_t *)param); + + if ( icc_hdr->ldp_hdr.msg_type == MSG_T_RG_APP_DATA) + { + if (param->type > TLV_T_MLACP_CONNECT && param->type < TLV_T_MLACP_LIST_END) + mlacp_enqueue_msg(csm, msg); + else + TAILQ_INSERT_TAIL(&(csm->app_csm.app_msg_list), msg, tail); + } + else if (icc_hdr->ldp_hdr.msg_type == MSG_T_NOTIFICATION) + { + naktlv = (NAKTLV*)&msg->buf[sizeof(ICCHdr)]; + + for (i = 0; i < MAX_MSG_LOG_SIZE; ++i) + { + if (ntohl(naktlv->rejected_msg_id) == csm->msg_log.msg[i].msg_id) + { + tlv = csm->msg_log.msg[i].tlv; + break; + } + } + + if (tlv > TLV_T_MLACP_CONNECT && tlv <= TLV_T_MLACP_MAC_INFO) + mlacp_enqueue_msg(csm, msg); + else + TAILQ_INSERT_TAIL(&(csm->app_csm.app_msg_list), msg, tail); + } + else + { + /* This packet is not for me, ignore it. */ + ICCPD_LOG_DEBUG(__FUNCTION__, "Ignore the packet with msg_type = %d", icc_hdr->ldp_hdr.msg_type); + } +} + +/* Get received message from message list */ +struct Msg* app_csm_dequeue_msg(struct CSM* csm) +{ + struct Msg* msg = NULL; + + if (!TAILQ_EMPTY(&(csm->app_csm.app_msg_list))) + { + msg = TAILQ_FIRST(&(csm->app_csm.app_msg_list)); + TAILQ_REMOVE(&(csm->app_csm.app_msg_list), msg, tail); + } + + return msg; +} + +/* APP NAK message handle function */ +int app_csm_prepare_nak_msg(struct CSM* csm, char* buf, size_t max_buf_size) +{ + ICCHdr* icc_hdr = (ICCHdr*)buf; + NAKTLV* naktlv = (NAKTLV*)&buf[sizeof(ICCHdr)]; + size_t msg_len = sizeof(ICCHdr) + sizeof(NAKTLV); + + ICCPD_LOG_DEBUG(__FUNCTION__, " Response NAK"); + memset(buf, 0, max_buf_size); + icc_hdr->ldp_hdr.u_bit = 0x0; + icc_hdr->ldp_hdr.msg_type = htons(MSG_T_NOTIFICATION); + icc_hdr->ldp_hdr.msg_len = htons(msg_len - MSG_L_INCLUD_U_BIT_MSG_T_L_FIELDS); + icc_hdr->ldp_hdr.msg_id = htonl(ICCP_MSG_ID++); + iccp_csm_fill_icc_rg_id_tlv(csm, icc_hdr); + naktlv->icc_parameter.u_bit = 0; + naktlv->icc_parameter.f_bit = 0; + naktlv->icc_parameter.type = htons(TLV_T_NAK); + naktlv->icc_parameter.len = htons(sizeof(NAKTLV) - 4); + + naktlv->iccp_status_code = htonl(STATUS_CODE_ICCP_REJECTED_MSG); + naktlv->rejected_msg_id = htonl(csm->app_csm.invalid_msg_id); + + return msg_len; +} + +int mlacp_bind_local_if(struct CSM* csm, struct LocalInterface* lif) +{ + struct LocalInterface* lifp = NULL; + struct LocalInterface* lif_po = NULL; + + if (csm == NULL || lif == NULL) + return MCLAG_ERROR; + + if (lif->csm == csm) + return 0; + + /* remove purge from the csm*/ + do { + LIST_FOREACH(lifp, &(MLACP(csm).lif_purge_list), mlacp_purge_next) + { + if (lifp == lif) + break; + } + if (lifp) + LIST_REMOVE(lifp, mlacp_purge_next); + } while (lifp); + + /* already join csm?*/ + LIST_FOREACH(lifp, &(MLACP(csm).lif_list), mlacp_next) + { + if (lifp == lif) + return 0; + } + + /* join another csm beofre? remove from csm*/ + if (lif->csm != NULL) + mlacp_unbind_local_if(lif); + + /* join new csm*/ + LIST_INSERT_HEAD(&(MLACP(csm).lif_list), lif, mlacp_next); + lif->csm = csm; + if (lif->type == IF_T_PORT_CHANNEL) + lif->port_config_sync = 1; + + ICCPD_LOG_INFO(__FUNCTION__, "%s: MLACP bind on csm %p", lif->name, csm); + if (lif->type == IF_T_PORT_CHANNEL) + return 0; + + /* if join a po member, needs to check po joined also*/ + LIST_FOREACH(lif_po, &(MLACP(csm).lif_list), mlacp_next) + { + if (lif_po->type == IF_T_PORT_CHANNEL && lif_po->po_id == lif->po_id) + { + /*if join a po member, may swss restart, reset portchannel ip mac to mclagsyncd*/ + update_if_ipmac_on_standby(lif_po); + return 0; + } + } + + if (lif_po == NULL) + { + lif_po = local_if_find_by_po_id(lif->po_id); + if (lif_po == NULL) + { + ICCPD_LOG_WARN(__FUNCTION__, "Failed to find port_channel instance for %d.", lif->po_id); + return MCLAG_ERROR; + } + + lif_po->csm = csm; + LIST_INSERT_HEAD(&(MLACP(csm).lif_list), lif_po, mlacp_next); + lif_po->port_config_sync = 1; + ICCPD_LOG_INFO(__FUNCTION__, "Add port_channel %d into local_if_list in CSM %p.", lif->po_id, csm); + } + + return 0; +} + +int mlacp_unbind_local_if(struct LocalInterface* lif) +{ + if (lif == NULL ) + return MCLAG_ERROR; + + if (lif->csm == NULL ) + return 0; + + ICCPD_LOG_INFO(__FUNCTION__, "%s: MLACP un-bind from csm %p", lif->name, lif->csm); + LIST_REMOVE(lif, mlacp_next); + + if (MLACP(lif->csm).current_state == MLACP_STATE_EXCHANGE && lif->type == IF_T_PORT_CHANNEL) + LIST_INSERT_HEAD(&(MLACP(lif->csm).lif_purge_list), lif, mlacp_purge_next); + if (lif->type == IF_T_PORT) + lif->po_id = -1; + lif->csm = NULL; + + return 0; +} + +int mlacp_bind_port_channel_to_csm(struct CSM* csm, const char *ifname) +{ + struct System* sys = NULL; + struct LocalInterface *lif_po = NULL; + + sys = system_get_instance(); + if (sys == NULL) + return 0; + + if (csm == NULL) + return 0; + + /* bind po first*/ + lif_po = local_if_find_by_name(ifname); + if (lif_po) + { + mlacp_bind_local_if(csm, lif_po); + iccp_get_port_member_list(lif_po); + } + else + { + ICCPD_LOG_WARN(__FUNCTION__, "%s: Failed to find a port instance .", ifname); + return 0; + } + /* process link state handler after attaching it.*/ + + mlacp_mlag_link_add_handler(csm, lif_po); + + /*ICCPD_LOG_WARN(tag, "po%d active = %d\n", po_id, po_is_active);*/ + return 0; +} + diff --git a/src/iccpd/src/cmd_option.c b/src/iccpd/src/cmd_option.c new file mode 100644 index 000000000000..596dafb73711 --- /dev/null +++ b/src/iccpd/src/cmd_option.c @@ -0,0 +1,266 @@ +/* + * cmd_option.c + * + * Copyright(c) 2016-2019 Nephos/Estinet. + * + * 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, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintainer: jianjun, grace Li from nephos + */ + +#include "../include/cmd_option.h" + +struct CmdOption* cmd_option_find(struct CmdOptionParser* parser, char* opt_name) +{ + struct CmdOption* opt = NULL; + + if (opt_name == NULL) + return NULL; + + LIST_FOREACH(opt, &(parser->option_list), next) + { + if (strcmp(opt->option, opt_name) == 0) + return opt; + } + + return NULL; +} + +void cmd_option_delete(struct CmdOption* opt) +{ + if (opt == NULL) + return; + + LIST_REMOVE(opt, next); + if (opt->option != NULL) + free(opt->option); + if (opt->parameter != NULL) + free(opt->parameter); + if (opt->desc != NULL) + free(opt->desc); + free(opt); +} + +struct CmdOption* cmd_option_add(struct CmdOptionParser* parser, char* opt_name) +{ + struct CmdOption* opt = NULL; + + if (opt_name == NULL) + return NULL; + if ((opt = cmd_option_find(parser, opt_name)) != NULL) + return opt; + + if ((opt = (struct CmdOption*)malloc(sizeof(struct CmdOption))) == NULL) + { + strerror(errno); + } + else + { + opt->option = opt_name; + opt->parameter = NULL; + opt->desc = NULL; + LIST_INSERT_HEAD(&(parser->option_list), opt, next); + } + + return opt; +} + +static void cmd_option_register(struct CmdOptionParser* parser, char* syntax, char* desc) +{ + char buf[OPTION_MAX_LEN]; + struct CmdOption* opt = NULL; + char* opt_name = NULL; + char* param = NULL; + char* desc_copy = NULL; + char* token = NULL; + + if (parser == NULL) + return; + if (syntax == NULL) + return; + + memset(buf, 0, OPTION_MAX_LEN); + snprintf(buf, OPTION_MAX_LEN - 1, "%s", syntax); + + if ((token = strtok(buf, " ")) == NULL) + return; + + opt_name = strdup(token); + if ((token = strtok(NULL, " ")) != NULL) + param = strdup(token); + desc_copy = strdup(desc); + if ((opt = cmd_option_find(parser, opt_name)) != NULL) + goto failed; + if ((opt = cmd_option_add(parser, opt_name)) == NULL) + { + goto failed; + } + opt->parameter = param; + opt->desc = desc_copy; + + return; + + failed: + if (opt_name) + free(opt_name); + if (desc_copy != NULL) + free(desc_copy); + if (param != NULL) + free(param); + if (opt != NULL) + free(opt); +} + +void cmd_option_parser_init(struct CmdOptionParser* parser) +{ + if (parser == NULL) + return; + + LIST_INIT(&parser->option_list); + cmd_option_register(parser, "-l ", "Set log file path.\n(Default: /var/log/iccpd.log)"); + cmd_option_register(parser, "-p ", "Set the port used for telnet listening port.\n(Default: 2015)"); + cmd_option_register(parser, "-c", "Dump log message to console. (Default: No)"); + cmd_option_register(parser, "-h", "Show the usage."); +} + +void cmd_option_parser_finalize(struct CmdOptionParser* parser) +{ + while (!LIST_EMPTY(&(parser->option_list))) + { + struct CmdOption* opt = NULL; + opt = LIST_FIRST(&(parser->option_list)); + cmd_option_delete(opt); + } +} + +void cmd_option_parser_dump_usage(struct CmdOptionParser* parser, char* prog_name) +{ + char buf[MSG_LEN]; + struct CmdOption* opt = NULL; + int index, begin, length; + char first_line = 0; + + fprintf(stdout, "Usage: %s [Options]\n", prog_name); + fprintf(stdout, "\n"); + fprintf(stdout, "Options:\n"); + LIST_FOREACH(opt, &(parser->option_list), next) + { + index = 0; + begin = 0; + length = 0; + first_line = 1; + memset(buf, 0, MSG_LEN); + if (opt->parameter != NULL) + snprintf(buf, MSG_LEN - 1, "%s %s", opt->option, opt->parameter); + else + snprintf(buf, MSG_LEN - 1, "%s", opt->option); + fprintf(stdout, "%24s ", buf); + + while (index < strlen(opt->desc)) + { + while (index < strlen(opt->desc) + && opt->desc[index] != '\n' && length < 49) + { + ++index; + ++length; + } + + memset(buf, 0, MSG_LEN); + strncpy(buf, &(opt->desc[begin]), length); + if (length == 49 && index < strlen(opt->desc) + && opt->desc[index] != '\n' + && opt->desc[index - 1] != ' ' + && opt->desc[index] != ' ') + { + buf[length] = '-'; + buf[length + 1] = '\0'; + } + if (length < 49) + ++index; + begin = index; + length = 0; + if (first_line != 0) + { + fprintf(stdout, "%-52s\n", buf); + first_line = 0; + } + else + fprintf(stdout, "%28c%-52s\n", ' ', buf); + } + + fflush(stdout); + } +} + +int cmd_option_parser_parse(struct CmdOptionParser* parser, int argc, char* argv[]) +{ + int index = 1; + struct CmdOption* opt = NULL; + char* opt_name = NULL; + char* val = NULL; + int num = 0; + + if (parser == NULL) + return -255; + + while (index < argc) + { + opt_name = argv[index]; + opt = cmd_option_find(parser, opt_name); + if (opt == NULL) + { + fprintf(stderr, "Unknown option %s, skip it.\n", opt_name); + ++index; + continue; + } + + if (opt->parameter != NULL) + { + ++index; + if (index >= argc) + { + fprintf(stderr, "Error: Insufficient parameter for option %s\n", opt_name); + cmd_option_parser_dump_usage(parser, argv[0]); + return -1; + } + val = argv[index]; + } + + if (strncmp(opt_name, "-h", 2) == 0) + { + cmd_option_parser_dump_usage(parser, argv[0]); + return -1; + } + + if (strncmp(opt_name, "-l", 2) == 0) + parser->log_file_path = val; + + if (strncmp(opt_name, "-p", 2) == 0) + { + num = atoi(val); + if (num > 0 && num < 65535) + parser->telnet_port = num; + } + else if (strncmp(opt_name, "-c", 2) == 0) + parser->console_log = 1; + else + fprintf(stderr, "Unknown option name %s, skip it.\n", opt_name); + + ++index; + } + + return 0; +} diff --git a/src/iccpd/src/iccp_cli.c b/src/iccpd/src/iccp_cli.c new file mode 100644 index 000000000000..8d0ede8b3feb --- /dev/null +++ b/src/iccpd/src/iccp_cli.c @@ -0,0 +1,498 @@ +/* + * iccp_cli.c + * + * Copyright(c) 2016-2019 Nephos/Estinet. + * + * 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, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintainer: jianjun, grace Li from nephos + */ + +#include + +#include "../include/system.h" +#include "../include/scheduler.h" +#include "../include/logger.h" +#include "../include/iccp_csm.h" +#include "../include/mlacp_link_handler.h" +#include "../include/iccp_netlink.h" +/* + * 'id <1-65535>' command + */ +int set_mc_lag_id( struct CSM *csm, uint16_t id) +{ + if (!csm) + return MCLAG_ERROR; + + ICCPD_LOG_INFO(__FUNCTION__, "Set mlag-id : %d", id); + + /* Mlag-ID, RG-ID, MLACP-ID + Temporary let the three id be the same*/ + csm->mlag_id = id; + csm->iccp_info.icc_rg_id = id; + csm->app_csm.mlacp.id = id; + return 0; +} + +int unset_mc_lag_id( struct CSM *csm, uint16_t id) +{ + if (!csm) + return MCLAG_ERROR; + + /* Mlag-ID, RG-ID, MLACP-ID*/ + csm->mlag_id = 0; + csm->iccp_info.icc_rg_id = 0; + csm->app_csm.mlacp.id = 0; + + iccp_csm_finalize(csm); + + return 0; +} + +/* + * 'peer-link WORD' command + */ +int set_peer_link(int mid, const char* ifname) +{ + struct CSM* csm = NULL; + struct LocalInterface *lif = NULL; + size_t len = 0; + + len = strlen(ifname); + + if (strncmp(ifname, FRONT_PANEL_PORT_PREFIX, strlen(FRONT_PANEL_PORT_PREFIX)) != 0 && strncmp(ifname, PORTCHANNEL_PREFIX, strlen(PORTCHANNEL_PREFIX)) != 0 && strncmp(ifname, VXLAN_TUNNEL_PREFIX, strlen(VXLAN_TUNNEL_PREFIX)) != 0) + { + ICCPD_LOG_ERR(__FUNCTION__, "Peer-link is %s, must be Ethernet or PortChannel or VTTNL(Vxlan tunnel)", ifname); + return MCLAG_ERROR; + } + + csm = system_get_csm_by_mlacp_id(mid); + if (csm == NULL) + return MCLAG_ERROR; + + if (len > IFNAMSIZ) + return MCLAG_ERROR; + + if (strlen(csm->peer_itf_name) > 0) + { + if (strcmp(csm->peer_itf_name, ifname) == 0) + { + ICCPD_LOG_INFO(__FUNCTION__, "Peer-link not be changed"); + return 0; + } + else + { + ICCPD_LOG_INFO(__FUNCTION__, "Change peer-link : %s -> %s", + csm->peer_itf_name, ifname); + + /*disconnect the link for mac and arp sync up before change peer_itf_name*/ + scheduler_session_disconnect_handler(csm); + + if (csm->peer_link_if) + { + csm->peer_link_if->is_peer_link = 0; + csm->peer_link_if = NULL; + } + } + } + else + { + ICCPD_LOG_INFO(__FUNCTION__, "Set mlag %d peer-link : %s", + csm->mlag_id, ifname); + } + + memset(csm->peer_itf_name, 0, IFNAMSIZ); + memcpy(csm->peer_itf_name, ifname, len); + + /* update peer-link link handler*/ + lif = local_if_find_by_name(csm->peer_itf_name); + if (lif) + { + /*When set peer-link, the local-if is already created*/ + csm->peer_link_if = lif; + lif->is_peer_link = 1; + MLACP(csm).system_config_changed = 1; + + if (lif->type == IF_T_PORT_CHANNEL) + iccp_get_port_member_list(lif); + } + + /*disconnect the link for mac and arp sync up*/ + scheduler_session_disconnect_handler(csm); + + return 0; +} + +int unset_peer_link(int mid) +{ + struct CSM* csm = NULL; + + csm = system_get_csm_by_mlacp_id(mid); + if (csm == NULL) + return MCLAG_ERROR; + + if (MLACP(csm).current_state == MLACP_STATE_EXCHANGE) + { + /*must be enabled mac learn*/ + if (csm->peer_link_if) + set_peerlink_mlag_port_learn(csm->peer_link_if, 1); + } + + /* update peer-link link handler*/ + scheduler_session_disconnect_handler(csm); + + /* clean peer-link*/ + memset(csm->peer_itf_name, 0, IFNAMSIZ); + if (csm->peer_link_if) + { + csm->peer_link_if->is_peer_link = 0; + csm->peer_link_if = NULL; + MLACP(csm).system_config_changed = 1; + } + + return 0; +} + +/* + * 'local ip address A.B.C.D' command + */ +int set_local_address(int mid, const char* addr) +{ + struct CSM* csm = NULL; + size_t len = 0; + + csm = system_get_csm_by_mlacp_id(mid); + if (csm == NULL) + return MCLAG_ERROR; + if (addr == NULL) + return MCLAG_ERROR; + + if (strlen(csm->sender_ip) > 0) + { + if (strcmp(csm->sender_ip, addr) == 0) + { + ICCPD_LOG_INFO(__FUNCTION__, "Local-address not be changed"); + return 0; + } + else + { + ICCPD_LOG_INFO(__FUNCTION__, "Change local-address : %s -> %s", + csm->sender_ip, addr); + scheduler_session_disconnect_handler(csm); + } + } + else + { + ICCPD_LOG_INFO(__FUNCTION__, "Set local-address : %s", addr); + } + + len = strlen(addr); + memset(csm->sender_ip, 0, INET_ADDRSTRLEN); + memcpy(csm->sender_ip, addr, len); + memset(csm->iccp_info.sender_name, 0, INET_ADDRSTRLEN); + memcpy(csm->iccp_info.sender_name, addr, len); + + return 0; +} + +int unset_local_address(int mid) +{ + struct CSM* csm = NULL; + + csm = system_get_csm_by_mlacp_id(mid); + if (csm == NULL) + return MCLAG_ERROR; + + memset(csm->sender_ip, 0, INET_ADDRSTRLEN); + memset(csm->iccp_info.sender_name, 0, INET_ADDRSTRLEN); + + /* reset link*/ + scheduler_session_disconnect_handler(csm); + + return 0; +} + +/* + * 'peer-address A.B.C.D' command + */ +int set_peer_address(int mid, const char* addr) +{ + struct CSM* csm = NULL; + size_t len = 0; + + csm = system_get_csm_by_mlacp_id(mid); + if (csm == NULL) + return MCLAG_ERROR; + if (addr == NULL) + return MCLAG_ERROR; + + len = strlen(addr); + + if (strlen(csm->peer_ip) > 0) + { + if (strcmp(csm->peer_ip, addr) == 0) + { + ICCPD_LOG_INFO(__FUNCTION__, "Peer-address not be changed"); + return 0; + } + else + { + ICCPD_LOG_INFO(__FUNCTION__, "Change peer-address : %s -> %s", + csm->peer_ip, addr); + scheduler_session_disconnect_handler(csm); + } + } + else + { + ICCPD_LOG_INFO(__FUNCTION__, "Set peer-address : %s", addr); + } + + memset(csm->peer_ip, 0, INET_ADDRSTRLEN); + memcpy(csm->peer_ip, addr, len); + + return 0; +} + +int unset_peer_address(int mid) +{ + struct CSM* csm = NULL; + + csm = system_get_csm_by_mlacp_id(mid); + if (csm == NULL) + return MCLAG_ERROR; + + memset(csm->peer_ip, 0, INET_ADDRSTRLEN); + + /* reset link*/ + scheduler_session_disconnect_handler(csm); + + return 0; +} + +int iccp_cli_attach_mclag_domain_to_port_channel( int domain, const char* ifname) +{ + struct CSM* csm = NULL; + struct LocalInterface *lif = NULL; + struct If_info * cif = NULL; + + if (!ifname) + return MCLAG_ERROR; + + if (strncmp(ifname, PORTCHANNEL_PREFIX, strlen(PORTCHANNEL_PREFIX)) != 0) + { + ICCPD_LOG_WARN(__FUNCTION__, "Attach interface(%s) is not a port-channel", ifname); + return MCLAG_ERROR; + } + + csm = system_get_csm_by_mlacp_id(domain); + if (csm == NULL) + { + ICCPD_LOG_WARN(__FUNCTION__, "MC-LAG ID %d doesn't exist", domain); + return MCLAG_ERROR; + } + + lif = local_if_find_by_name(ifname); + if (lif) + { + mlacp_bind_port_channel_to_csm(csm, ifname); + } + + LIST_FOREACH(cif, &(csm->if_bind_list), csm_next) + { + if (strcmp(cif->name, ifname) == 0) + break; + } + + if (cif == NULL) + { + cif = (struct If_info *)malloc(sizeof(struct If_info)); + if (!cif) + return MCLAG_ERROR; + + snprintf(cif->name, MAX_L_PORT_NAME, "%s", ifname); + LIST_INSERT_HEAD(&(csm->if_bind_list), cif, csm_next); + } + + return 0; +} + +int iccp_cli_detach_mclag_domain_to_port_channel( const char* ifname) +{ + int unbind_poid = -1; + struct CSM *csm = NULL; + struct LocalInterface *lif_po = NULL; + struct LocalInterface *lif = NULL; + struct If_info * cif = NULL; + + if (!ifname) + return MCLAG_ERROR; + + if (strncmp(ifname, PORTCHANNEL_PREFIX, strlen(PORTCHANNEL_PREFIX)) != 0) + { + ICCPD_LOG_WARN(__FUNCTION__, "Detach interface(%s) is not a port-channel", ifname); + return MCLAG_ERROR; + } + + /* find po*/ + if (!(lif_po = local_if_find_by_name(ifname)) + || lif_po->type != IF_T_PORT_CHANNEL + || lif_po->po_id <= 0 + || lif_po->csm == NULL) + { + return MCLAG_ERROR; + } + + /* find csm*/ + csm = lif_po->csm; + + ICCPD_LOG_DEBUG(__FUNCTION__, "Detach mclag id = %d from ifname = %s", + csm->mlag_id, lif_po->name); + + /* process link state handler before detaching it.*/ + mlacp_mlag_link_del_handler(csm, lif_po); + + unbind_poid = lif_po->po_id; + mlacp_unbind_local_if(lif_po); + LIST_FOREACH(lif, &(csm->app_csm.mlacp.lif_list), mlacp_next) + { + if (lif->type == IF_T_PORT && lif->po_id == unbind_poid) + mlacp_unbind_local_if(lif); + } + + LIST_FOREACH(cif, &(csm->if_bind_list), csm_next) + { + if (strcmp(ifname, cif->name) == 0) + LIST_REMOVE(cif, csm_next); + } + return 0; +} + +/* This function parses a string to a binary mac address (uint8_t[6]) + The string should contain mac address only. No spaces are allowed. + The mac address separators could be either ':' or '-'*/ +int parseMacString(const char * str_mac, uint8_t* bin_mac) +{ + int i; + + if (bin_mac == NULL) + { + return MCLAG_ERROR; + } + + /* 6 hexadecimal numbers (two digits each) + 5 delimiters*/ + if (strlen(str_mac) != ETHER_ADDR_LEN * 2 + 5) + { + return MCLAG_ERROR; + } + + /* first check that all mac address separators are equal to each other + 2, 5, 8, 11, and 14 are MAC address separator positions*/ + if (!(str_mac[2] == str_mac[5] + && str_mac[5] == str_mac[8] + && str_mac[8] == str_mac[11] + && str_mac[11] == str_mac[14])) + { + return MCLAG_ERROR; + } + + /* then check that the first separator is equal to ':' or '-'*/ + if (str_mac[2] != ':' && str_mac[2] != '-') + { + return MCLAG_ERROR; + } + + for (i = 0; i < ETHER_ADDR_LEN; ++i) + { + int left = i * 3; /* left digit position of hexadecimal number*/ + int right = left + 1; /* right digit position of hexadecimal number*/ + + if (str_mac[left] >= '0' && str_mac[left] <= '9') + { + bin_mac[i] = (uint8_t)(str_mac[left] - '0'); + } + else if (str_mac[left] >= 'A' && str_mac[left] <= 'F') + { + bin_mac[i] = (uint8_t)(str_mac[left] - 'A' + 0x0a); + } + else if (str_mac[left] >= 'a' && str_mac[left] <= 'f') + { + bin_mac[i] = (uint8_t)(str_mac[left] - 'a' + 0x0a); + } + else + { + return MCLAG_ERROR; + } + + bin_mac[i] = (uint8_t)(bin_mac[i] << 4); + + if (str_mac[right] >= '0' && str_mac[right] <= '9') + { + bin_mac[i] |= (uint8_t)(str_mac[right] - '0'); + } + else if (str_mac[right] >= 'A' && str_mac[right] <= 'F') + { + bin_mac[i] |= (uint8_t)(str_mac[right] - 'A' + 0x0a); + } + else if (str_mac[right] >= 'a' && str_mac[right] <= 'f') + { + bin_mac[i] |= (uint8_t)(str_mac[right] - 'a' + 0x0a); + } + else + { + return MCLAG_ERROR; + } + } + + return 0; +} + +int set_local_system_id(const char* mac) +{ + struct System* sys = NULL; + struct CSM* csm = NULL; + + if ((sys = system_get_instance()) == NULL ) + return 0; + + LIST_FOREACH(csm, &(sys->csm_list), next) + { + parseMacString(mac, MLACP(csm).system_id); + + ICCPD_LOG_DEBUG(__FUNCTION__, " Set local systemID [%02X:%02X:%02X:%02X:%02X:%02X].", + MLACP(csm).system_id[0], MLACP(csm).system_id[1], MLACP(csm).system_id[2], + MLACP(csm).system_id[3], MLACP(csm).system_id[4], MLACP(csm).system_id[5]); + } + + return 0; +} + +int unset_local_system_id( ) +{ + uint8_t null_mac[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + struct System* sys = NULL; + struct CSM* csm = NULL; + + if ((sys = system_get_instance()) == NULL ) + return 0; + + LIST_FOREACH(csm, &(sys->csm_list), next) + { + memcpy(MLACP(csm).system_id, null_mac, ETHER_ADDR_LEN); + } + + return 0; +} + diff --git a/src/iccpd/src/iccp_cmd.c b/src/iccpd/src/iccp_cmd.c new file mode 100644 index 000000000000..50025a8e3810 --- /dev/null +++ b/src/iccpd/src/iccp_cmd.c @@ -0,0 +1,168 @@ +/* + * iccp_cmd.c + * + * Copyright(c) 2016-2019 Nephos/Estinet. + * + * 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, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintainer: jianjun, grace Li from nephos + */ + +#include +#include +#include + +#include "../include/iccp_csm.h" +#include "../include/msg_format.h" +#include "../include/system.h" + +#include "../include/iccp_cmd_show.h" +#include "../include/iccp_cli.h" +#include "../include/logger.h" + +int set_mc_lag_by_id(uint16_t mid) +{ + int ret = 0; + struct CSM* csm = NULL; + + csm = system_get_csm_by_mlacp_id(mid); + if (!csm) + { + csm = (struct CSM*)iccp_get_csm(); + if (csm == NULL) + { + return MCLAG_ERROR; + } + + ret = set_mc_lag_id(csm, mid); + + return ret; + } + + return ret; +} + +#define CONFIG_LINE_LEN 512 + +int iccp_config_from_command(char * line) +{ + char *cp, *start; + char token[64]; + int slen; + static int mid = 0; + char *end; + + if (line == NULL) + return 0; + + cp = line; + + /* Skip white spaces. */ + while (isspace((int)*cp) && *cp != '\0') + cp++; + + /* Return if there is only white spaces */ + if (*cp == '\0') + return 0; + + end = cp; + + /* Skip end white spaces. */ + while (!isspace((int)*end) && *end != '\0') + end++; + + *end = '\0'; + /*mc-lag id*/ + if (strncmp(cp, MCLAG_ID_STR, strlen(MCLAG_ID_STR)) == 0 ) + { + cp += strlen(MCLAG_ID_STR) + 1; + mid = atoi(cp); + set_mc_lag_by_id(mid); + } + else if (strncmp(cp, LOCAL_IP_STR, strlen(LOCAL_IP_STR)) == 0) /*local ip*/ + { + cp += strlen(LOCAL_IP_STR) + 1; + set_local_address(mid, cp); + } + else if (strncmp(cp, PEER_IP_STR, strlen(PEER_IP_STR)) == 0) /*peer ip*/ + { + cp += strlen(PEER_IP_STR) + 1; + set_peer_address(mid, cp); + } + else if (strncmp(cp, PEER_LINK_STR, strlen(PEER_LINK_STR)) == 0)/*peer link*/ + { + cp += strlen(PEER_LINK_STR) + 1; + set_peer_link(mid, cp); + } + else if (strncmp(cp, MCLAG_INTF_STR, strlen(MCLAG_INTF_STR)) == 0)/*mclag interface*/ + { + cp += strlen(MCLAG_INTF_STR) + 1; + + while (1) + { + start = cp; + + while (!(*cp == ',' || *cp == '\r' || *cp == '\n') && + *cp != '\0') + cp++; + + slen = cp - start; + strncpy(token, start, slen); + *(token + slen) = '\0'; + iccp_cli_attach_mclag_domain_to_port_channel(mid, token); + + while ((isspace((int)*cp) || *cp == '\n' || *cp == '\r' || *cp == ',') && + *cp != '\0') + cp++; + + if (*cp == '\0') + break; + } + } + else if (strncmp(cp, SYSTEM_MAC_STR, strlen(SYSTEM_MAC_STR)) == 0)/*system mac*/ + { + cp += strlen(SYSTEM_MAC_STR) + 1; + set_local_system_id(cp); + } + else + { + /*error*/ + } + + return 1; +} + +/* Configration make from file. */ +int +iccp_config_from_file(char *config_default_dir) +{ + FILE *confp = NULL; + char command_buf[CONFIG_LINE_LEN]; + + confp = fopen(config_default_dir, "r"); + if (confp == NULL) + return (1); + + while (fgets(command_buf, CONFIG_LINE_LEN, confp)) + { + iccp_config_from_command(command_buf); + } + + fclose(confp); + + return 0; +} + diff --git a/src/iccpd/src/iccp_cmd_show.c b/src/iccpd/src/iccp_cmd_show.c new file mode 100644 index 000000000000..e25a653e74b3 --- /dev/null +++ b/src/iccpd/src/iccp_cmd_show.c @@ -0,0 +1,477 @@ +/* + * iccp_cmd_show.c + * + * Copyright(c) 2016-2019 Nephos/Estinet. + * + * 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, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintainer: jianjun, grace Li from nephos + */ +#include +#include +#include +#include + +#include "../include/iccp_csm.h" +#include "../include/mlacp_tlv.h" +#include "../include/system.h" +#include "../include/logger.h" +#include "mclagdctl/mclagdctl.h" +#include "../include/iccp_cmd_show.h" +#include "../include/mlacp_link_handler.h" + +int iccp_mclag_config_dump(char * *buf, int *num, int mclag_id) +{ + struct mclagd_state state_info; + struct System *sys = NULL; + struct CSM *csm = NULL; + struct LocalInterface *peer_link_if = NULL; + struct LocalInterface *lif_po = NULL; + struct LoggerConfig* logconfig; + char unknown[] = { "Unknown" }; + int mclag_num = 0; + int id_exist = 0; + int str_size = 0; + int len = 0; + char *state_buf = NULL; + int state_buf_size = MCLAGDCTL_CMD_SIZE; + + if (!(sys = system_get_instance())) + { + return EXEC_TYPE_NO_EXIST_SYS; + } + + state_buf = (char*)malloc(state_buf_size); + if (!state_buf) + return EXEC_TYPE_FAILED; + + LIST_FOREACH(csm, &(sys->csm_list), next) + { + memset(&state_info, 0, sizeof(struct mclagd_state)); + + if (csm->current_state == ICCP_OPERATIONAL) + state_info.keepalive = 1; + else + state_info.keepalive = 0; + + if (mclag_id > 0) + { + if (csm->mlag_id == mclag_id) + id_exist = 1; + else + continue; + } + + peer_link_if = local_if_find_by_name(csm->peer_itf_name); + + if (csm->mlag_id <= 0) + state_info.mclag_id = -1; + else + state_info.mclag_id = csm->mlag_id; + + memcpy(state_info.local_ip, csm->sender_ip, ICCP_MAX_IP_STR_LEN); + memcpy(state_info.peer_ip, csm->peer_ip, ICCP_MAX_IP_STR_LEN); + + if (peer_link_if) + memcpy(state_info.peer_link_if, peer_link_if->name, ICCP_MAX_PORT_NAME); + else + memcpy(state_info.peer_link_if, unknown, strlen(unknown)); + + if (peer_link_if) + memcpy(state_info.peer_link_mac, peer_link_if->mac_addr, 6); + + logconfig = logger_get_configuration(); + memcpy(state_info.loglevel, log_level_to_string(logconfig->log_level), strlen( log_level_to_string(logconfig->log_level))); + + state_info.role = csm->role_type; + + str_size = MCLAGDCTL_PORT_MEMBER_BUF_LEN; + + LIST_FOREACH(lif_po, &(MLACP(csm).lif_list), mlacp_next) + { + + if (str_size - len < ICCP_MAX_PORT_NAME) + break; + + if (lif_po->type == IF_T_PORT_CHANNEL) + len += snprintf(state_info.enabled_po + len, str_size - len, "%s,", lif_po->name); + } + + /*Skip the last ','*/ + len = strlen(state_info.enabled_po); + if (len > 0) + { + state_info.enabled_po[len - 1] = '\0'; + } + + memcpy(state_buf + MCLAGD_REPLY_INFO_HDR + mclag_num * sizeof(struct mclagd_state), + &state_info, sizeof(struct mclagd_state)); + mclag_num++; + + if ((mclag_num + 1) * sizeof(struct mclagd_state) > (state_buf_size - MCLAGD_REPLY_INFO_HDR)) + { + state_buf_size += MCLAGDCTL_CMD_SIZE; + state_buf = (char*)realloc(state_buf, state_buf_size); + if (!state_buf) + return EXEC_TYPE_FAILED; + } + } + + *buf = state_buf; + *num = mclag_num; + + if (mclag_id > 0 && !id_exist) + return EXEC_TYPE_NO_EXIST_MCLAGID; + + return EXEC_TYPE_SUCCESS; +} + +int iccp_arp_dump(char * *buf, int *num, int mclag_id) +{ + struct System *sys = NULL; + struct CSM *csm = NULL; + struct Msg *msg = NULL; + struct ARPMsg *iccpd_arp = NULL; + struct mclagd_arp_msg mclagd_arp; + int arp_num = 0; + int id_exist = 0; + char * arp_buf = NULL; + int arp_buf_size = MCLAGDCTL_CMD_SIZE; + + if (!(sys = system_get_instance())) + { + return EXEC_TYPE_NO_EXIST_SYS; + } + + arp_buf = (char*)malloc(arp_buf_size); + if (!arp_buf) + return EXEC_TYPE_FAILED; + + LIST_FOREACH(csm, &(sys->csm_list), next) + { + if (mclag_id > 0) + { + if (csm->mlag_id == mclag_id) + id_exist = 1; + else + continue; + } + + TAILQ_FOREACH(msg, &MLACP(csm).arp_list, tail) + { + memset(&mclagd_arp, 0, sizeof(struct mclagd_arp_msg)); + iccpd_arp = (struct ARPMsg*)msg->buf; + + mclagd_arp.op_type = iccpd_arp->op_type; + memcpy(mclagd_arp.ifname, iccpd_arp->ifname, strlen(iccpd_arp->ifname)); + memcpy(mclagd_arp.ipv4_addr, show_ip_str(htonl(iccpd_arp->ipv4_addr)), 16); + memcpy(mclagd_arp.mac_addr, iccpd_arp->mac_addr, 6); + + memcpy(arp_buf + MCLAGD_REPLY_INFO_HDR + arp_num * sizeof(struct mclagd_arp_msg), + &mclagd_arp, sizeof(struct mclagd_arp_msg)); + + arp_num++; + + if ((arp_num + 1) * sizeof(struct mclagd_arp_msg) > (arp_buf_size - MCLAGD_REPLY_INFO_HDR)) + { + arp_buf_size += MCLAGDCTL_CMD_SIZE; + arp_buf = (char*)realloc(arp_buf, arp_buf_size); + if (!arp_buf) + return EXEC_TYPE_FAILED; + } + } + } + + *buf = arp_buf; + *num = arp_num; + + if (mclag_id > 0 && !id_exist) + return EXEC_TYPE_NO_EXIST_MCLAGID; + + return EXEC_TYPE_SUCCESS; +} + +int iccp_mac_dump(char * *buf, int *num, int mclag_id) +{ + struct System *sys = NULL; + struct CSM *csm = NULL; + struct Msg *msg = NULL; + struct MACMsg *iccpd_mac = NULL; + struct mclagd_mac_msg mclagd_mac; + int mac_num = 0; + int id_exist = 0; + char * mac_buf = NULL; + int mac_buf_size = MCLAGDCTL_CMD_SIZE; + + if (!(sys = system_get_instance())) + { + return EXEC_TYPE_NO_EXIST_SYS; + } + + mac_buf = (char*)malloc(mac_buf_size); + if (!mac_buf) + return EXEC_TYPE_FAILED; + + LIST_FOREACH(csm, &(sys->csm_list), next) + { + if (mclag_id > 0) + { + if (csm->mlag_id == mclag_id) + id_exist = 1; + else + continue; + } + + TAILQ_FOREACH(msg, &MLACP(csm).mac_list, tail) + { + memset(&mclagd_mac, 0, sizeof(struct mclagd_mac_msg)); + iccpd_mac = (struct MACMsg*)msg->buf; + + mclagd_mac.op_type = iccpd_mac->op_type; + mclagd_mac.fdb_type = iccpd_mac->fdb_type; + memcpy(mclagd_mac.mac_str, iccpd_mac->mac_str, ETHER_ADDR_STR_LEN); + mclagd_mac.vid = iccpd_mac->vid; + memcpy(mclagd_mac.ifname, iccpd_mac->ifname, strlen(iccpd_mac->ifname)); + memcpy(mclagd_mac.origin_ifname, iccpd_mac->origin_ifname, strlen(iccpd_mac->origin_ifname)); + mclagd_mac.age_flag = iccpd_mac->age_flag; + + memcpy(mac_buf + MCLAGD_REPLY_INFO_HDR + mac_num * sizeof(struct mclagd_mac_msg), + &mclagd_mac, sizeof(struct mclagd_mac_msg)); + + mac_num++; + + if ((mac_num + 1) * sizeof(struct mclagd_mac_msg) > (mac_buf_size - MCLAGD_REPLY_INFO_HDR)) + { + mac_buf_size += MCLAGDCTL_CMD_SIZE; + mac_buf = (char*)realloc(mac_buf, mac_buf_size); + if (!mac_buf) + return EXEC_TYPE_FAILED; + } + } + } + + *buf = mac_buf; + *num = mac_num; + + if (mclag_id > 0 && !id_exist) + return EXEC_TYPE_NO_EXIST_MCLAGID; + + return EXEC_TYPE_SUCCESS; +} + +int iccp_local_if_dump(char * *buf, int *num, int mclag_id) +{ + struct System *sys = NULL; + struct CSM *csm = NULL; + struct LocalInterface *lif_po = NULL; + struct mclagd_local_if mclagd_lif; + struct VLAN_ID* vlan_id = NULL; + char * str_buf = NULL; + int str_size = MCLAGDCTL_PARA3_LEN - 1; + int len = 0; + int lif_num = 0; + int id_exist = 0; + int lif_buf_size = MCLAGDCTL_CMD_SIZE; + char * lif_buf = NULL; + + if (!(sys = system_get_instance())) + { + return EXEC_TYPE_NO_EXIST_SYS; + } + + lif_buf = (char*)malloc(lif_buf_size); + if (!lif_buf) + return EXEC_TYPE_FAILED; + + LIST_FOREACH(csm, &(sys->csm_list), next) + { + if (mclag_id > 0) + { + if (csm->mlag_id == mclag_id) + id_exist = 1; + else + continue; + } + + LIST_FOREACH(lif_po, &(MLACP(csm).lif_list), mlacp_next) + { + memset(&mclagd_lif, 0, sizeof(struct mclagd_local_if)); + + mclagd_lif.ifindex = lif_po->ifindex; + + if (lif_po->type == IF_T_UNKNOW) + memcpy(mclagd_lif.type, "Unknown", 6); + else if (lif_po->type == IF_T_PORT) + memcpy(mclagd_lif.type, "Ethernet", 8); + else if (lif_po->type == IF_T_PORT_CHANNEL) + memcpy(mclagd_lif.type, "PortChannel", 11); + + memcpy(mclagd_lif.name, lif_po->name, MAX_L_PORT_NAME); + memcpy(mclagd_lif.mac_addr, lif_po->mac_addr, ETHER_ADDR_LEN); + + if (lif_po->state == PORT_STATE_UP) + memcpy(mclagd_lif.state, "Up", 2); + else if (lif_po->state == PORT_STATE_DOWN) + memcpy(mclagd_lif.state, "Down", 4); + else if (lif_po->state == PORT_STATE_ADMIN_DOWN) + memcpy(mclagd_lif.state, "Admin-down", 10); + else if (lif_po->state == PORT_STATE_TEST) + memcpy(mclagd_lif.state, "Test", 4); + + memcpy(mclagd_lif.ipv4_addr, show_ip_str(htonl(lif_po->ipv4_addr)), 16); + mclagd_lif.prefixlen = lif_po->prefixlen; + + mclagd_lif.l3_mode = local_if_is_l3_mode(lif_po); + + mclagd_lif.is_peer_link = lif_po->is_peer_link; + + memcpy(mclagd_lif.portchannel_member_buf, lif_po->portchannel_member_buf, 512); + + mclagd_lif.po_id = lif_po->po_id; + mclagd_lif.po_active = lif_po->po_active; + /*mlacp_state*/ + if (lif_po->mlacp_state == MLACP_STATE_INIT) + memcpy(mclagd_lif.mlacp_state, "INIT", 4); + else if (lif_po->mlacp_state == MLACP_STATE_STAGE1) + memcpy(mclagd_lif.mlacp_state, "STAGE1", 6); + else if (lif_po->mlacp_state == MLACP_STATE_STAGE2) + memcpy(mclagd_lif.mlacp_state, "STAGE2", 6); + else if (lif_po->mlacp_state == MLACP_STATE_EXCHANGE) + memcpy(mclagd_lif.mlacp_state, "EXCHANGE", 8); + else if (lif_po->mlacp_state == MLACP_STATE_ERROR) + memcpy(mclagd_lif.mlacp_state, "ERROR", 5); + + mclagd_lif.isolate_to_peer_link = lif_po->isolate_to_peer_link; + + str_buf = mclagd_lif.vlanlist; + + len = 0; + LIST_FOREACH(vlan_id, &(lif_po->vlan_list), port_next) + { + if (vlan_id != NULL ) + { + if (str_size - len < 4) + break; + len += snprintf(str_buf + len, str_size - len, "%d ", vlan_id->vid); + } + } + + memcpy(lif_buf + MCLAGD_REPLY_INFO_HDR + lif_num * sizeof(struct mclagd_local_if), + &mclagd_lif, sizeof(struct mclagd_local_if)); + + lif_num++; + + if ((lif_num + 1) * sizeof(struct mclagd_local_if) > (lif_buf_size - MCLAGD_REPLY_INFO_HDR)) + { + lif_buf_size += MCLAGDCTL_CMD_SIZE; + lif_buf = (char*)realloc(lif_buf, lif_buf_size); + if (!lif_buf) + return EXEC_TYPE_FAILED; + } + } + } + + *buf = lif_buf; + *num = lif_num; + + if (mclag_id > 0 && !id_exist) + return EXEC_TYPE_NO_EXIST_MCLAGID; + + return EXEC_TYPE_SUCCESS; +} + +int iccp_peer_if_dump(char * *buf, int *num, int mclag_id) +{ + struct System *sys = NULL; + struct CSM *csm = NULL; + struct PeerInterface *pif_po = NULL; + struct mclagd_peer_if mclagd_pif; + int pif_num = 0; + int id_exist = 0; + int pif_buf_size = MCLAGDCTL_CMD_SIZE; + char *pif_buf = NULL; + + if (!(sys = system_get_instance())) + { + return EXEC_TYPE_NO_EXIST_SYS; + } + + pif_buf = (char*)malloc(pif_buf_size); + if (!pif_buf) + return EXEC_TYPE_FAILED; + + LIST_FOREACH(csm, &(sys->csm_list), next) + { + if (mclag_id > 0) + { + if (csm->mlag_id == mclag_id) + id_exist = 1; + else + continue; + } + + LIST_FOREACH(pif_po, &(MLACP(csm).pif_list), mlacp_next) + { + memset(&mclagd_pif, 0, sizeof(struct mclagd_peer_if)); + + mclagd_pif.ifindex = pif_po->ifindex; + + if (pif_po->type == IF_T_UNKNOW) + memcpy(mclagd_pif.type, "Unknown", 6); + else if (pif_po->type == IF_T_PORT) + memcpy(mclagd_pif.type, "Ethernet", 8); + else if (pif_po->type == IF_T_PORT_CHANNEL) + memcpy(mclagd_pif.type, "PortChannel", 11); + + memcpy(mclagd_pif.name, pif_po->name, MAX_L_PORT_NAME); + memcpy(mclagd_pif.mac_addr, pif_po->mac_addr, ETHER_ADDR_LEN); + + if (pif_po->state == PORT_STATE_UP) + memcpy(mclagd_pif.state, "Up", 2); + else if (pif_po->state == PORT_STATE_DOWN) + memcpy(mclagd_pif.state, "Down", 4); + else if (pif_po->state == PORT_STATE_ADMIN_DOWN) + memcpy(mclagd_pif.state, "Admin-down", 10); + else if (pif_po->state == PORT_STATE_TEST) + memcpy(mclagd_pif.state, "Test", 4); + + mclagd_pif.po_id = pif_po->po_id; + mclagd_pif.po_active = pif_po->po_active; + + memcpy(pif_buf + MCLAGD_REPLY_INFO_HDR + pif_num * sizeof(struct mclagd_peer_if), + &mclagd_pif, sizeof(struct mclagd_peer_if)); + + pif_num++; + + if ((pif_num + 1) * sizeof(struct mclagd_peer_if) > (pif_buf_size - MCLAGD_REPLY_INFO_HDR)) + { + pif_buf_size += MCLAGDCTL_CMD_SIZE; + pif_buf = (char*)realloc(pif_buf, pif_buf_size); + if (!pif_buf) + return EXEC_TYPE_FAILED; + } + } + } + + *buf = pif_buf; + *num = pif_num; + + if (mclag_id > 0 && !id_exist) + return EXEC_TYPE_NO_EXIST_MCLAGID; + + return EXEC_TYPE_SUCCESS; +} + diff --git a/src/iccpd/src/iccp_consistency_check.c b/src/iccpd/src/iccp_consistency_check.c new file mode 100644 index 000000000000..90ac9cd9e790 --- /dev/null +++ b/src/iccpd/src/iccp_consistency_check.c @@ -0,0 +1,175 @@ +/* + * iccp_consistency_check.c + * + * Copyright(c) 2016-2019 Nephos/Estinet. + * + * 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, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintainer: jianjun, grace Li from nephos + * + */ + +#include "../include/iccp_consistency_check.h" +#include "../include/system.h" +#include "../include/port.h" +#include "../include/logger.h" + +/* Return 0 if the checking procedure is failed; otherwise, 1 (non-zero) will be returned. */ +typedef int (*ConsistencyCheckFunc)(char* ifname); + +const char *reasons[] = { + /* REASON_NONE */ + "Success", + /* REASON_INTERRFACE_MODE_IS_ASYNC */ + "Port-channel interface is not in the same mode in local and peer device, please check whether the ip addr settings is correct or not.", + /* REASON_PEER_IF_IP_IS_ASYNC */ + "IP address of peer interface is not synchronized, please check the IP address setting on the corresponding interface.", + /* REASON_PEER_IF_VLAN_IS_ASYNC */ + "VLAN settings on this port-channel interface is not synchronized, please check your configuration.", + /* REASON_MAX_ARRAY_SIZE */ + NULL +}; + +/* Consistency Checking functions */ +static int iccp_check_interface_mode( char* ifname) +{ + struct CSM* csm = NULL; + struct LocalInterface* local_if = NULL; + struct PeerInterface* peer_if = NULL; + + local_if = local_if_find_by_name(ifname); + if (local_if == NULL) + return -2; + + csm = local_if->csm; + if (csm == NULL) + return -3; + + peer_if = peer_if_find_by_name(csm, ifname); + if (peer_if == NULL) + return -4; + + if (peer_if->l3_mode != local_if->l3_mode) + return -5; + + return 1; +} + +static int iccp_check_interface_layer3_addr(char* ifname) +{ + struct CSM* csm = NULL; + struct LocalInterface* local_if = NULL; + struct PeerInterface* peer_if = NULL; + + local_if = local_if_find_by_name(ifname); + if (local_if == NULL) + return -2; + + csm = local_if->csm; + if (csm == NULL) + return -3; + + peer_if = peer_if_find_by_name(csm, ifname); + if (peer_if == NULL) + return -4; + + if (peer_if->ipv4_addr != local_if->ipv4_addr) + return -5; + + return 1; +} + +static int iccp_check_interface_vlan(char* ifname) +{ + struct CSM* csm = NULL; + struct PeerInterface* peer_if = NULL; + struct VLAN_ID* local_vlan = NULL; + struct VLAN_ID* peer_vlan = NULL; + struct LocalInterface* local_if = NULL; + + local_if = local_if_find_by_name(ifname); + if (local_if == NULL) + return -2; + + csm = local_if->csm; + if (csm == NULL) + return -3; + + peer_if = peer_if_find_by_name(csm, ifname); + if (peer_if == NULL) + return -4; + + LIST_FOREACH(local_vlan, &(local_if->vlan_list), port_next) + { + LIST_FOREACH(peer_vlan, &(peer_if->vlan_list), port_next) + { + if (peer_vlan->vid == local_vlan->vid) + break; + } + + if (peer_vlan == NULL) + { + return -5; + } + } + + LIST_FOREACH(peer_vlan, &(peer_if->vlan_list), port_next) + { + + LIST_FOREACH(local_vlan, &(local_if->vlan_list), port_next) + { + if (peer_vlan->vid == local_vlan->vid) + break; + } + + if (local_vlan == NULL) + { + return -6; + } + } + + return 1; +} + +static const ConsistencyCheckFunc check_func[] = { + NULL, + iccp_check_interface_mode, /* REASON_INTERFACE_MODE_IS_ASYNC */ + iccp_check_interface_layer3_addr, /* REASON_PEER_IF_IP_IS_ASYNC */ + iccp_check_interface_vlan, /* REASON_PEER_IF_VLAN_IS_ASYNC */ + NULL /* REASON_MAX_ARRAY_SIZE */ +}; +#define ARRAY_SIZE(array_name) (sizeof(array_name) / sizeof(array_name[0])) + +enum Reason_ID iccp_consistency_check(char* ifname) +{ + int i = 0; + int ret = 0; + + for (i = REASON_INTERRFACE_MODE_IS_ASYNC; i < REASON_MAX_ARRAY_SIZE; ++i) + { + if (check_func[i] == NULL) + continue; + ret = check_func[i](ifname); + if (ret != 1) + { + ICCPD_LOG_WARN(__FUNCTION__, "%s ret = %d", reasons[i], ret); + fprintf(stdout, "%s \n", reasons[i]); + return i; + } + } + + return REASON_NONE; +} diff --git a/src/iccpd/src/iccp_csm.c b/src/iccpd/src/iccp_csm.c new file mode 100644 index 000000000000..79e17b9e1ba2 --- /dev/null +++ b/src/iccpd/src/iccp_csm.c @@ -0,0 +1,797 @@ +/* + * iccp_csm.c + * + * Copyright(c) 2016-2019 Nephos/Estinet. + * + * 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, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintainer: jianjun, grace Li from nephos + */ + +#include +#include + +#include "../include/logger.h" +#include "../include/system.h" +#include "../include/scheduler.h" +#include "../include/msg_format.h" +#include "../include/iccp_csm.h" +#include "../include/mlacp_link_handler.h" +/***************************************** +* Define +* +* ***************************************/ +#define ICCP_CSM_QUEUE_REINIT(list) \ + { \ + struct Msg* msg = NULL; \ + while (!TAILQ_EMPTY(&(list))) { \ + msg = TAILQ_FIRST(&(list)); \ + TAILQ_REMOVE(&(list), msg, tail); \ + free(msg->buf); \ + free(msg); \ + } \ + TAILQ_INIT(&(list)); \ + } + +/***************************************** +* Global +* +* ***************************************/ +char g_csm_buf[CSM_BUFFER_SIZE] = { 0 }; + +uint32_t ICCP_MSG_ID = 0x1; + +/* Enter Connection State Machine NONEXISTENT handle function */ +static void iccp_csm_enter_state_nonexistent(struct CSM* csm) +{ + iccp_csm_finalize(csm); +} + +/* Enter Connection State Machine INITIALIZED handle function */ +static void iccp_csm_enter_state_initialized(struct CSM* csm) +{ + if (csm == NULL) + return; + + csm->iccp_info.sender_capability_flag = 0x1; +} + +/* Enter Connection State Machine CAPREC handle function */ +static void iccp_csm_enter_state_caprec(struct CSM* csm) +{ + if (csm == NULL) + return; + + csm->iccp_info.sender_capability_flag = 0x1; + csm->iccp_info.peer_capability_flag = 0x1; +} + +/* Enter Connection State Machine CONNECTING handle function */ +static void iccp_csm_enter_state_connecting(struct CSM* csm) +{ + if (csm == NULL) + return; + + csm->iccp_info.sender_rg_connect_flag = 0x1; +} + +/* Enter Connection State Machine OPERATIONAL handle function */ +static void iccp_csm_enter_state_operational(struct CSM* csm) +{ + if (csm == NULL) + return; + + csm->iccp_info.sender_rg_connect_flag = 0x1; + csm->iccp_info.peer_rg_connect_flag = 0x1; +} + +void *iccp_get_csm() +{ + struct CSM* csm = NULL; + struct System* sys = NULL; + + if ((sys = system_get_instance()) == NULL) + { + return NULL; + } + + csm = system_create_csm(); + + return csm; +} + +/* Connection State Machine instance initialization */ +void iccp_csm_init(struct CSM* csm) +{ + iccp_csm_status_reset(csm, 1); + memset(csm->sender_ip, 0, INET_ADDRSTRLEN); + memset(csm->peer_ip, 0, INET_ADDRSTRLEN); + memset(csm->iccp_info.sender_name, 0, MAX_L_ICC_SENDER_NAME); + csm->iccp_info.icc_rg_id = 0x0; +} + +/* Connection State Machine instance status reset */ +void iccp_csm_status_reset(struct CSM* csm, int all) +{ + ICCP_CSM_QUEUE_REINIT(csm->msg_list); + + if (all) + { + bzero(csm, sizeof(struct CSM)); + ICCP_CSM_QUEUE_REINIT(csm->msg_list); + } + + csm->sock_fd = -1; + pthread_mutex_init(&csm->conn_mutex, NULL); + csm->connTimePrev = 0; + csm->heartbeat_send_time = 0; + csm->heartbeat_update_time = 0; + csm->peer_warm_reboot_time = 0; + csm->warm_reboot_disconn_time = 0; + csm->role_type = STP_ROLE_NONE; + csm->sock_read_event_ptr = NULL; + csm->peer_link_if = NULL; + csm->u_msg_in_count = 0x0; + csm->i_msg_in_count = 0x0; + csm->icc_msg_in_count = 0x0; + csm->icc_msg_out_count = 0x0; + csm->iccp_info.status_code = 0x0; + csm->iccp_info.rejected_msg_id = 0x0; + csm->current_state = ICCP_NONEXISTENT; + csm->iccp_info.peer_capability_flag = 0x0; + csm->iccp_info.peer_rg_connect_flag = 0x0; + csm->iccp_info.sender_capability_flag = 0x0; + csm->iccp_info.sender_rg_connect_flag = 0x0; + app_csm_init(csm, all); + + memset(&csm->msg_log, 0, sizeof(struct MsgLog)); +} + +/* Connection State Machine instance tear down */ +void iccp_csm_finalize(struct CSM* csm) +{ + struct If_info * cif = NULL; + struct System* sys = NULL; + + if (csm == NULL) + return; + + if ((sys = system_get_instance()) == NULL) + return; + + /*If warm reboot, don't change port block and peer link MAC learning*/ + if (sys->warmboot_exit != WARM_REBOOT) + { + /*Enable peer link port MAC learning*/ + if (csm->peer_link_if) + set_peerlink_mlag_port_learn(csm->peer_link_if, 1); + } + + /* Disconnect from peer */ + scheduler_session_disconnect_handler(csm); + + /* Release all Connection State Machine instance */ + app_csm_finalize(csm); + + LIST_FOREACH(cif, &(csm->if_bind_list), csm_next) + { + LIST_REMOVE(cif, csm_next); + } + + /* Release iccp_csm */ + pthread_mutex_destroy(&(csm->conn_mutex)); + iccp_csm_msg_list_finalize(csm); + LIST_REMOVE(csm, next); + free(csm); +} + +/* Message list of Connection State Machine instance tear down */ +void iccp_csm_msg_list_finalize(struct CSM* csm) +{ + struct Msg* msg = NULL; + + if (csm == NULL) + return; + + while (!TAILQ_EMPTY(&(csm->msg_list))) + { + msg = TAILQ_FIRST(&(csm->msg_list)); + TAILQ_REMOVE(&(csm->msg_list), msg, tail); + free(msg); + } +} + +/* Send message to peer */ +int iccp_csm_send(struct CSM* csm, char* buf, int msg_len) +{ + LDPHdr* ldp_hdr = (LDPHdr*)buf; + ICCParameter* param = NULL; + + if (csm == NULL || buf == NULL || csm->sock_fd <= 0 || msg_len <= 0) + return MCLAG_ERROR; + + if (ntohs(ldp_hdr->msg_type) == MSG_T_CAPABILITY) + param = (struct ICCParameter*)&buf[sizeof(LDPHdr)]; + else + param = (struct ICCParameter*)&buf[sizeof(ICCHdr)]; + + /*ICCPD_LOG_DEBUG(__FUNCTION__, "Send(%d): len=[%d] msg_type=[%s (0x%X, 0x%X)]", csm->sock_fd, msg_len, get_tlv_type_string(param->type), ldp_hdr->msg_type, param->type);*/ + csm->msg_log.msg[csm->msg_log.end_index].msg_id = ntohl(ldp_hdr->msg_id); + csm->msg_log.msg[csm->msg_log.end_index].type = ntohs(ldp_hdr->msg_type); + csm->msg_log.msg[csm->msg_log.end_index].tlv = ntohs(param->type); + ++csm->msg_log.end_index; + if (csm->msg_log.end_index >= 128) + csm->msg_log.end_index = 0; + + return write(csm->sock_fd, buf, msg_len); +} + +/* Connection State Machine Transition */ +void iccp_csm_transit(struct CSM* csm) +{ + int len = -1; + struct Msg* msg = NULL; + ICCP_CONNECTION_STATE_E prev_state; + char *state_str[] = {"NONEXISTENT", "INITIALIZED", "CAPSENT", "CAPREC", "CONNECTING", "OPERATIONAL"}; + + if (!csm) + return; + + prev_state = csm->current_state; + + /* No connection, but have state change? reset it...*/ + if (csm->current_state != ICCP_NONEXISTENT && csm->sock_fd <= 0) + { + ICCPD_LOG_NOTICE(__FUNCTION__, "csm %d change state from %s to NONEXISTENT.", csm->mlag_id, state_str[csm->current_state]); + csm->current_state = ICCP_NONEXISTENT; + iccp_csm_enter_state_nonexistent(csm); + return; + } + + msg = iccp_csm_dequeue_msg(csm); + + switch (csm->current_state) + { + case ICCP_NONEXISTENT: + scheduler_prepare_session(csm); + if (csm->sock_fd > 0 && scheduler_check_csm_config(csm) > 0) + csm->current_state = ICCP_INITIALIZED; + break; + + case ICCP_INITIALIZED: + if (msg) + iccp_csm_correspond_from_msg(csm, msg); + len = iccp_csm_prepare_iccp_msg(csm, g_csm_buf, CSM_BUFFER_SIZE); + iccp_csm_send(csm, g_csm_buf, len); + if (csm->iccp_info.sender_capability_flag == 0x1 && csm->iccp_info.peer_capability_flag == 0x0) + csm->current_state = ICCP_CAPSENT; + else if (csm->iccp_info.sender_capability_flag == 0x1 && csm->iccp_info.peer_capability_flag == 0x1) + csm->current_state = ICCP_CAPREC; + break; + + case ICCP_CAPSENT: + if (msg) + iccp_csm_correspond_from_msg(csm, msg); + if (csm->iccp_info.sender_capability_flag == 0x1 && csm->iccp_info.peer_capability_flag == 0x1) + csm->current_state = ICCP_CAPREC; + break; + + case ICCP_CAPREC: + if (msg) + iccp_csm_correspond_from_msg(csm, msg); + memset(g_csm_buf, 0, CSM_BUFFER_SIZE); + len = iccp_csm_prepare_iccp_msg(csm, g_csm_buf, CSM_BUFFER_SIZE); + iccp_csm_send(csm, g_csm_buf, len); + if (csm->iccp_info.peer_rg_connect_flag == 0x0 && csm->iccp_info.status_code == 0x0) + csm->current_state = ICCP_CONNECTING; + else if (csm->iccp_info.peer_rg_connect_flag == 0x1 && csm->iccp_info.status_code == 0x0) + csm->current_state = ICCP_OPERATIONAL; + break; + + case ICCP_CONNECTING: + if (msg) + iccp_csm_correspond_from_msg(csm, msg); + memset(g_csm_buf, 0, CSM_BUFFER_SIZE); + len = iccp_csm_prepare_iccp_msg(csm, g_csm_buf, CSM_BUFFER_SIZE); + iccp_csm_send(csm, g_csm_buf, len); + if (csm->iccp_info.status_code > 0x0) + csm->current_state = ICCP_CAPREC; + else if (csm->iccp_info.peer_rg_connect_flag == 0x1 && csm->iccp_info.status_code == 0x0) + csm->current_state = ICCP_OPERATIONAL; + break; + + case ICCP_OPERATIONAL: + if (msg) + iccp_csm_correspond_from_msg(csm, msg); + if (csm->iccp_info.sender_rg_connect_flag == 0x0 || csm->iccp_info.peer_rg_connect_flag == 0x0) + { + memset(g_csm_buf, 0, CSM_BUFFER_SIZE); + len = iccp_csm_prepare_iccp_msg(csm, g_csm_buf, CSM_BUFFER_SIZE); + iccp_csm_send(csm, g_csm_buf, len); + csm->current_state = ICCP_CAPREC; + } + break; + + default: + break; + } + + if (prev_state != csm->current_state || (csm->current_state && msg != NULL)) + { + if (prev_state != csm->current_state) + ICCPD_LOG_NOTICE(__FUNCTION__, "csm %d change state from %s to %s.", csm->mlag_id, state_str[prev_state], state_str[csm->current_state]); + + switch (csm->current_state) + { + case ICCP_NONEXISTENT: + iccp_csm_enter_state_nonexistent(csm); + break; + + case ICCP_INITIALIZED: + iccp_csm_enter_state_initialized(csm); + break; + + case ICCP_CAPSENT: + /* Do nothing on this state */ + break; + + case ICCP_CAPREC: + iccp_csm_enter_state_caprec(csm); + break; + + case ICCP_CONNECTING: + iccp_csm_enter_state_connecting(csm); + break; + + case ICCP_OPERATIONAL: + iccp_csm_enter_state_operational(csm); + break; + + default: + break; + } + } +} + +/* Set up ICCP message */ +int iccp_csm_prepare_iccp_msg(struct CSM* csm, char* buf, size_t max_buf_size) +{ + size_t msg_len = -1; + + if (csm == NULL || buf == NULL) + return MCLAG_ERROR; + + switch (csm->current_state) + { + case ICCP_NONEXISTENT: + /* Do nothing on this state */ + break; + + case ICCP_INITIALIZED: + msg_len = iccp_csm_prepare_capability_msg(csm, buf, max_buf_size); + break; + + case ICCP_CAPSENT: + /* Do nothing on this state */ + break; + + case ICCP_CAPREC: + if (csm->iccp_info.status_code > 0x0) + { + msg_len = iccp_csm_prepare_nak_msg(csm, buf, max_buf_size); + break; + } + msg_len = iccp_csm_prepare_rg_connect_msg(csm, buf, max_buf_size); + break; + + case ICCP_CONNECTING: + if (csm->iccp_info.status_code > 0x0) + { + msg_len = iccp_csm_prepare_nak_msg(csm, buf, max_buf_size); + break; + } + break; + + case ICCP_OPERATIONAL: + if (csm->iccp_info.peer_rg_connect_flag == 0x0) + { + msg_len = iccp_csm_prepare_rg_disconnect_msg(csm, buf, max_buf_size); + break; + } + break; + } + + return msg_len; +} + +/* ICCP capability message handle function */ +int iccp_csm_prepare_capability_msg(struct CSM* csm, char* buf, size_t max_buf_size) +{ + LDPHdr* ldp_hdr = (LDPHdr*)buf; + LDPICCPCapabilityTLV* cap = (LDPICCPCapabilityTLV*)&buf[sizeof(LDPHdr)]; + size_t msg_len = sizeof(LDPHdr) + sizeof(LDPICCPCapabilityTLV); + + memset(buf, 0, max_buf_size); + + /* LDP header */ + ldp_hdr->u_bit = 0x0; + ldp_hdr->msg_type = htons(MSG_T_CAPABILITY); + ldp_hdr->msg_len = htons(msg_len - MSG_L_INCLUD_U_BIT_MSG_T_L_FIELDS); + ldp_hdr->msg_id = htonl(ICCP_MSG_ID++); + + /* LDP ICCP capability TLV */ + cap->icc_parameter.u_bit = 0x1; + cap->icc_parameter.f_bit = 0x0; + cap->icc_parameter.type = TLV_T_ICCP_CAPABILITY; + *(uint16_t *)cap = htons(*(uint16_t *)cap); + + cap->icc_parameter.len = htons(TLV_L_ICCP_CAPABILITY); + + cap->s_bit = csm->iccp_info.sender_capability_flag; + *(uint16_t *)((uint8_t *)cap + sizeof(ICCParameter)) = htons(*(uint16_t *)((uint8_t *)cap + sizeof(ICCParameter))); + + cap->major_ver = 0x1; + cap->minior_ver = 0x0; + + return msg_len; +} + +void iccp_csm_fill_icc_rg_id_tlv(struct CSM* csm, ICCHdr* icc_hdr) +{ + if (!csm || !icc_hdr) + return; + + icc_hdr->icc_rg_id_tlv.type = htons(TLV_T_ICC_RG_ID); + icc_hdr->icc_rg_id_tlv.len = htons(TLV_L_ICC_RG_ID); + icc_hdr->icc_rg_id_tlv.icc_rg_id = htonl(csm->iccp_info.icc_rg_id); +} + +/* ICCP NAK message handle function */ +int iccp_csm_prepare_nak_msg(struct CSM* csm, char* buf, size_t max_buf_size) +{ + ICCHdr* icc_hdr = (ICCHdr*)buf; + NAKTLV* nak = (NAKTLV*)&buf[sizeof(ICCHdr)]; + size_t msg_len = sizeof(ICCHdr) + sizeof(NAKTLV); + + memset(buf, 0, max_buf_size); + + /* ICC header */ + icc_hdr->ldp_hdr.u_bit = 0x0; + icc_hdr->ldp_hdr.msg_type = htons(MSG_T_NOTIFICATION); + icc_hdr->ldp_hdr.msg_len = htons(msg_len - MSG_L_INCLUD_U_BIT_MSG_T_L_FIELDS); + icc_hdr->ldp_hdr.msg_id = htonl(ICCP_MSG_ID++); + iccp_csm_fill_icc_rg_id_tlv(csm, icc_hdr); + + /* NAL TLV */ + nak->icc_parameter.u_bit = 0x0; + nak->icc_parameter.f_bit = 0x0; + nak->icc_parameter.type = htons(TLV_T_NAK); + nak->icc_parameter.len = htons(sizeof(((struct NAKTLV*)0)->iccp_status_code) + sizeof(((struct NAKTLV*)0)->rejected_msg_id)); + + switch (csm->iccp_info.status_code) + { + case STATUS_CODE_U_ICCP_RG: + nak->iccp_status_code = htonl(csm->iccp_info.status_code); + nak->rejected_msg_id = htonl(csm->iccp_info.rejected_msg_id); + break; + + /* Unsupported */ + case STATUS_CODE_ICCP_CONNECTION_COUNT_EXCEEDED: + case STATUS_CODE_ICCP_APP_CONNECTION_COUNT_EXCEEDED: + case STATUS_CODE_ICCP_APP_NOT_IN_RG: + case STATUS_CODE_INCOMPATIBLE_ICCP_PROTOCOL_VER: + case STATUS_CODE_ICCP_REJECTED_MSG: + case STATUS_CODE_ICCP_ADMINISTRATIVELY_DISABLED: + case STATUS_CODE_ICCP_RG_REMOVED: + case STATUS_CODE_ICCP_APP_REMOVED_FROM_RG: + break; + } + + return msg_len; +} + +/* ICCP RG connect handle function */ +int iccp_csm_prepare_rg_connect_msg(struct CSM* csm, char* buf, size_t max_buf_size) +{ + ICCHdr* icc_hdr = (ICCHdr*)buf; + ICCSenderNameTLV* sender = (ICCSenderNameTLV*)&buf[sizeof(ICCHdr)]; + size_t name_len = strlen(csm->iccp_info.sender_name); + size_t msg_len = sizeof(ICCHdr) + sizeof(ICCParameter) + name_len; + + memset(buf, 0, max_buf_size); + + /* ICC header */ + icc_hdr->ldp_hdr.u_bit = 0x0; + icc_hdr->ldp_hdr.msg_type = htons(MSG_T_RG_CONNECT); + icc_hdr->ldp_hdr.msg_len = htons(msg_len - MSG_L_INCLUD_U_BIT_MSG_T_L_FIELDS); + icc_hdr->ldp_hdr.msg_id = htonl(ICCP_MSG_ID++); + iccp_csm_fill_icc_rg_id_tlv(csm, icc_hdr); + + /* ICC sender name TLV */ + sender->icc_parameter.u_bit = 0x0; + sender->icc_parameter.f_bit = 0x0; + sender->icc_parameter.type = htons(TLV_T_ICC_SENDER_NAME); + sender->icc_parameter.len = htons(name_len); + memcpy(sender->sender_name, csm->iccp_info.sender_name, name_len); + + return msg_len; +} + +/* ICCP RG disconnect handle function */ +int iccp_csm_prepare_rg_disconnect_msg(struct CSM* csm, char* buf, size_t max_buf_size) +{ + ICCHdr* icc_hdr = (ICCHdr*)buf; + DisconnectCodeTLV* disconn_code = (DisconnectCodeTLV*)&buf[sizeof(ICCHdr)]; + size_t msg_len = sizeof(ICCHdr) + sizeof(DisconnectCodeTLV); + + memset(buf, 0, max_buf_size); + + /* ICC header */ + icc_hdr->ldp_hdr.u_bit = 0x0; + icc_hdr->ldp_hdr.msg_type = htons(MSG_T_RG_DISCONNECT); + icc_hdr->ldp_hdr.msg_len = htons(msg_len - MSG_L_INCLUD_U_BIT_MSG_T_L_FIELDS); + icc_hdr->ldp_hdr.msg_id = htonl(ICCP_MSG_ID++); + iccp_csm_fill_icc_rg_id_tlv(csm, icc_hdr); + + /* Disconnect code TLV */ + disconn_code->icc_parameter.u_bit = 0x0; + disconn_code->icc_parameter.f_bit = 0x0; + disconn_code->icc_parameter.type = htons(TLV_T_DISCONNECT_CODE); + disconn_code->icc_parameter.len = htons(sizeof(((struct DisconnectCodeTLV*)0)->iccp_status_code)); + disconn_code->iccp_status_code = htonl(csm->iccp_info.status_code); + + return msg_len; +} + +/* Check ID(MC-LAG ID, mLACP ID, RG ID) from received message */ +static void iccp_csm_check_id_from_msg(struct CSM* csm, struct Msg* msg) +{ + ICCHdr* icc_hdr = NULL; + + if (!csm || !msg || !msg->buf) + return; + + icc_hdr = (ICCHdr*)msg->buf; + + /* Capability Message doesn't have ICC RG ID TLV */ + if (icc_hdr->ldp_hdr.msg_type == MSG_T_CAPABILITY) + return; + + /* Check if received message ID same as local configuration */ + if (ntohl(icc_hdr->icc_rg_id_tlv.icc_rg_id) == csm->iccp_info.icc_rg_id) + { + if (csm->iccp_info.status_code == STATUS_CODE_U_ICCP_RG) + { + csm->iccp_info.status_code = 0x0; + csm->iccp_info.rejected_msg_id = 0x0; + } + } + else if (ntohl(icc_hdr->icc_rg_id_tlv.icc_rg_id) != csm->iccp_info.icc_rg_id) + { + csm->iccp_info.status_code = STATUS_CODE_U_ICCP_RG; + csm->iccp_info.rejected_msg_id = ntohl(icc_hdr->icc_rg_id_tlv.icc_rg_id); + } +} + +/* Receive message correspond function */ +void iccp_csm_correspond_from_msg(struct CSM* csm, struct Msg* msg) +{ + ICCHdr* icc_hdr = NULL; + + if (csm == NULL || msg == NULL || msg->buf == NULL) + return; + + icc_hdr = (ICCHdr*)msg->buf; + NAKTLV* nak = (NAKTLV*)( icc_hdr + sizeof(ICCHdr)); + iccp_csm_check_id_from_msg(csm, msg); + + if (icc_hdr->ldp_hdr.msg_type == MSG_T_CAPABILITY) + iccp_csm_correspond_from_capability_msg(csm, msg); + else if (icc_hdr->ldp_hdr.msg_type == MSG_T_RG_CONNECT) + iccp_csm_correspond_from_rg_connect_msg(csm, msg); + else if (icc_hdr->ldp_hdr.msg_type == MSG_T_RG_DISCONNECT) + iccp_csm_correspond_from_rg_disconnect_msg(csm, msg); + else if (icc_hdr->ldp_hdr.msg_type == MSG_T_NOTIFICATION) + { + ICCPD_LOG_DEBUG(__FUNCTION__, "Received MSG_T_NOTIFICATION ,err status %s reason of %s", get_status_string(ntohl(nak->iccp_status_code)), get_status_string(csm->iccp_info.status_code)); + sleep(1); + } + else if (icc_hdr->ldp_hdr.msg_type == MSG_T_RG_APP_DATA) + { + ;// do nothing + } + else + { + ++csm->u_msg_in_count; + } + + free(msg->buf); + free(msg); +} + +/* Receive capability message correspond function */ +void iccp_csm_correspond_from_capability_msg(struct CSM* csm, struct Msg* msg) +{ + LDPICCPCapabilityTLV* cap = (LDPICCPCapabilityTLV*)&(msg->buf)[sizeof(LDPHdr)]; + + *(uint16_t *)cap = ntohs(*(uint16_t *)cap); + *(uint16_t *)((uint8_t *)cap + sizeof(ICCParameter)) = ntohs(*(uint16_t *)((uint8_t *)cap + sizeof(ICCParameter))); + + if (cap->icc_parameter.u_bit == 0x1 + && cap->icc_parameter.f_bit == 0x0 + && cap->icc_parameter.type == TLV_T_ICCP_CAPABILITY + && ntohs(cap->icc_parameter.len) == (TLV_L_ICCP_CAPABILITY) + && cap->s_bit == 1 + && cap->major_ver == 0x1 + && cap->minior_ver == 0x0) + { + csm->iccp_info.peer_capability_flag = 0x1; + } +} + +/* Receive RG connect message correspond function */ +void iccp_csm_correspond_from_rg_connect_msg(struct CSM* csm, struct Msg* msg) +{ + ICCSenderNameTLV* sender = (ICCSenderNameTLV*)&(msg->buf)[sizeof(ICCHdr)]; + + *(uint16_t *)sender = ntohs(*(uint16_t *)sender); + + if (sender->icc_parameter.u_bit == 0x0 && + sender->icc_parameter.f_bit == 0x0 && + sender->icc_parameter.type == TLV_T_ICC_SENDER_NAME) + { + csm->iccp_info.peer_rg_connect_flag = 0x1; + } +} + +/* Receive RG disconnect message correspond function */ +void iccp_csm_correspond_from_rg_disconnect_msg(struct CSM* csm, struct Msg* msg) +{ + DisconnectCodeTLV* diconn_code = (DisconnectCodeTLV*)&(msg->buf)[sizeof(ICCHdr)]; + + *(uint16_t *)diconn_code = ntohs(*(uint16_t *)diconn_code); + + if (diconn_code->icc_parameter.u_bit == 0x0 + && diconn_code->icc_parameter.f_bit == 0x0 + && diconn_code->icc_parameter.type == TLV_T_DISCONNECT_CODE + && ntohs(diconn_code->icc_parameter.len) == (TLV_L_DISCONNECT_CODE) + && ntohl(diconn_code->iccp_status_code) == (STATUS_CODE_ICCP_RG_REMOVED)) + { + csm->iccp_info.sender_rg_connect_flag = 0x0; + csm->iccp_info.peer_rg_connect_flag = 0x0; + } +} + +/* Add received message into message list */ +void iccp_csm_enqueue_msg(struct CSM* csm, struct Msg* msg) +{ + ICCHdr* icc_hdr = NULL; + NAKTLV* naktlv = NULL; + int type = -1; + int i = 0; + + if (csm == NULL) + { + if (msg != NULL) + free(msg); + return; + } + + if (msg == NULL) + return; + + icc_hdr = (ICCHdr*)msg->buf; + + *(uint16_t *)icc_hdr = ntohs(*(uint16_t *)icc_hdr); + + if (icc_hdr->ldp_hdr.msg_type == MSG_T_RG_APP_DATA) + { + app_csm_enqueue_msg(csm, msg); + } + else if (icc_hdr->ldp_hdr.msg_type == MSG_T_NOTIFICATION) + { + naktlv = (NAKTLV*)&msg->buf[sizeof(ICCHdr)]; + + for (i = 0; i < MAX_MSG_LOG_SIZE; ++i) + { + if (ntohl(naktlv->rejected_msg_id) == csm->msg_log.msg[i].msg_id) + { + type = csm->msg_log.msg[i].type; + break; + } + } + + if (type == MSG_T_RG_APP_DATA) + app_csm_enqueue_msg(csm, msg); + else + TAILQ_INSERT_TAIL(&(csm->msg_list), msg, tail); + } + else + { + TAILQ_INSERT_TAIL(&(csm->msg_list), msg, tail); + } +} + +/* Get received message from message list */ +struct Msg* iccp_csm_dequeue_msg(struct CSM* csm) +{ + struct Msg* msg = NULL; + + if (!TAILQ_EMPTY(&(csm->msg_list))) + { + msg = TAILQ_FIRST(&(csm->msg_list)); + TAILQ_REMOVE(&(csm->msg_list), msg, tail); + + } + + return msg; +} + +/* Message initialization */ +int iccp_csm_init_msg(struct Msg** msg, char* data, int len) +{ + struct Msg* iccp_msg = NULL; + + if (msg == NULL) + return -2; + + if (data == NULL || len <= 0) + return MCLAG_ERROR; + + iccp_msg = (struct Msg*)malloc(sizeof(struct Msg)); + if (iccp_msg == NULL) + goto err_ret; + + iccp_msg->buf = (char*)malloc(len); + if (iccp_msg->buf == NULL) + goto err_ret; + + memcpy(iccp_msg->buf, data, len); + iccp_msg->len = len; + *msg = iccp_msg; + + return 0; + + err_ret: + if (iccp_msg) + { + if (iccp_msg->buf) + free(iccp_msg->buf); + free(iccp_msg); + } + + return MCLAG_ERROR; +} + +void iccp_csm_stp_role_count(struct CSM *csm) +{ + /* decide the role, lower ip to be active & socket client*/ + if (csm->role_type == STP_ROLE_NONE) + { + if (inet_addr(csm->sender_ip) < inet_addr(csm->peer_ip)) + { + /* Active*/ + ICCPD_LOG_INFO(__FUNCTION__, "Role: [Active]"); + csm->role_type = STP_ROLE_ACTIVE; + } + else + { + /* Standby*/ + ICCPD_LOG_INFO(__FUNCTION__, "Role [Standby]"); + csm->role_type = STP_ROLE_STANDBY; + } + } +} \ No newline at end of file diff --git a/src/iccpd/src/iccp_ifm.c b/src/iccpd/src/iccp_ifm.c new file mode 100644 index 000000000000..cf874366b97b --- /dev/null +++ b/src/iccpd/src/iccp_ifm.c @@ -0,0 +1,762 @@ +/* + * iccp_ifm.c + * + * Copyright(c) 2016-2019 Nephos/Estinet. + * + * 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, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintainer: jianjun, grace Li from nephos + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "../include/system.h" +#include "../include/iccp_cli.h" +#include "../include/logger.h" +#include "../include/mlacp_sync_update.h" +#include "../include/mlacp_link_handler.h" +#include "../include/port.h" +#include "../include/iccp_netlink.h" + +#define fwd_neigh_state_valid(state) (state & (NUD_REACHABLE | NUD_STALE | NUD_DELAY | NUD_PROBE | NUD_PERMANENT)) + +#ifndef NDA_RTA +#define NDA_RTA(r) \ + ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg)))) +#endif + +static int iccp_valid_handler(struct nl_msg *msg, void *arg) +{ + struct nlmsghdr *nlh = nlmsg_hdr(msg); + unsigned int event = 0; + + if (nlh->nlmsg_type != RTM_NEWLINK) + return 0; + + if (nl_msg_parse(msg, &iccp_event_handler_obj_input_newlink, &event) < 0) + ICCPD_LOG_ERR(__FUNCTION__, "Unknown message type."); + + return 0; +} + +/*Get kernel interfaces and ports during initialization*/ +int iccp_sys_local_if_list_get_init() +{ + struct System *sys = NULL; + struct nl_cb *cb; + struct nl_cb *orig_cb; + struct rtgenmsg rt_hdr = { + .rtgen_family = AF_UNSPEC, + }; + int ret; + int retry = 1; + + if (!(sys = system_get_instance())) + return MCLAG_ERROR; + + while (retry) + { + retry = 0; + ret = nl_send_simple(sys->route_sock, RTM_GETLINK, NLM_F_DUMP, + &rt_hdr, sizeof(rt_hdr)); + if (ret < 0) + { + ICCPD_LOG_ERR(__FUNCTION__, "send netlink msg error."); + return ret; + } + + orig_cb = nl_socket_get_cb(sys->route_sock); + cb = nl_cb_clone(orig_cb); + nl_cb_put(orig_cb); + if (!cb) + { + ICCPD_LOG_ERR(__FUNCTION__, "nl cb clone error."); + return -ENOMEM; + } + + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, iccp_valid_handler, sys); + + ret = nl_recvmsgs(sys->route_sock, cb); + nl_cb_put(cb); + if (ret < 0) + { + ICCPD_LOG_ERR(__FUNCTION__, "receive netlink msg error. ret = %d errno = %d ", ret, errno); + if (ret != -NLE_DUMP_INTR) + return ret; + retry = 1; + } + } + + return ret; +} + +static void do_arp_learn_from_kernel(struct ndmsg *ndm, struct rtattr *tb[], int msgtype) +{ + struct System *sys = NULL; + struct CSM *csm = NULL; + struct Msg *msg = NULL; + struct ARPMsg *arp_msg = NULL, *arp_info = NULL; + struct VLAN_ID *vlan_id_list = NULL; + struct Msg *msg_send = NULL; + + char buf[MAX_BUFSIZE]; + size_t msg_len = 0; + + struct LocalInterface *lif_po = NULL, *arp_lif = NULL; + + int verify_arp = 0; + int arp_update = 0; + + if (!(sys = system_get_instance())) + return; + + /* Find local itf*/ + if (!(arp_lif = local_if_find_by_ifindex(ndm->ndm_ifindex))) + return; + + /* create ARP msg*/ + memset(buf, 0, MAX_BUFSIZE); + msg_len = sizeof(struct ARPMsg); + arp_msg = (struct ARPMsg*)&buf; + arp_msg->op_type = ARP_SYNC_LIF; + sprintf(arp_msg->ifname, "%s", arp_lif->name); + if (tb[NDA_DST]) + memcpy(&arp_msg->ipv4_addr, RTA_DATA(tb[NDA_DST]), RTA_PAYLOAD(tb[NDA_DST])); + if (tb[NDA_LLADDR]) + memcpy(arp_msg->mac_addr, RTA_DATA(tb[NDA_LLADDR]), RTA_PAYLOAD(tb[NDA_LLADDR])); + + arp_msg->ipv4_addr = ntohl(arp_msg->ipv4_addr); + + ICCPD_LOG_DEBUG(__FUNCTION__, "ARP type %s, state (%04X)(%d) ifindex [%d] (%s) ip %s, mac [%02X:%02X:%02X:%02X:%02X:%02X]", + msgtype == RTM_NEWNEIGH ? "New":"Del", ndm->ndm_state, fwd_neigh_state_valid(ndm->ndm_state), + ndm->ndm_ifindex, arp_lif->name, + show_ip_str(htonl(arp_msg->ipv4_addr)), + arp_msg->mac_addr[0], arp_msg->mac_addr[1], arp_msg->mac_addr[2], arp_msg->mac_addr[3], arp_msg->mac_addr[4], arp_msg->mac_addr[5]); + + /*Debug*/ + #if 0 + /* dump receive kernel ARP req*/ + fprintf(stderr, "\n======== Kernel ARP ==========\n"); + fprintf(stderr, " Type = [%d] (New=%d, Del=%d)\n", msgtype, RTM_NEWNEIGH, RTM_DELNEIGH); + fprintf(stderr, " State = (%04X)(%d)\n", ndm->ndm_state, fwd_neigh_state_valid(ndm->ndm_state)); + fprintf(stderr, " ifindex = [%d] (%s)\n", ndm->ndm_ifindex, arp_msg->ifname); + fprintf(stderr, " IP = [%s]\n", show_ip_str(htonl(arp_msg->ipv4_addr))); + fprintf(stderr, " MAC = [%02X:%02X:%02X:%02X:%02X:%02X]\n", + arp_msg->mac_addr[0], arp_msg->mac_addr[1], arp_msg->mac_addr[2], arp_msg->mac_addr[3], + arp_msg->mac_addr[4], arp_msg->mac_addr[5]); + fprintf(stderr, "==============================\n"); + #endif + + /* Find MLACP itf, member of port-channel*/ + LIST_FOREACH(csm, &(sys->csm_list), next) + { + LIST_FOREACH(lif_po, &(MLACP(csm).lif_list), mlacp_next) + { + if (lif_po->type != IF_T_PORT_CHANNEL) + continue; + + if (!local_if_is_l3_mode(lif_po)) + { + /* Is the L2 MLAG itf belong to a vlan?*/ + LIST_FOREACH(vlan_id_list, &(lif_po->vlan_list), port_next) + { + if ( !(vlan_id_list->vlan_itf + && vlan_id_list->vlan_itf->ifindex == ndm->ndm_ifindex)) + continue; + break; + } + + if (!vlan_id_list) + continue; + + ICCPD_LOG_DEBUG(__FUNCTION__, "ARP is from mclag enabled member port of vlan %s", + vlan_id_list->vlan_itf->name); + } + else + { + /* Is the ARP belong to a L3 mode MLAG itf?*/ + if (ndm->ndm_ifindex != lif_po->ifindex) + continue; + + ICCPD_LOG_DEBUG(__FUNCTION__, "ARP is from mclag enabled intf %s", lif_po->name); + } + + verify_arp = 1; + + break; + } + + if (lif_po) + break; + } + + if (!(csm && lif_po)) + return; + if (!verify_arp) + return; + + /* update lif ARP*/ + TAILQ_FOREACH(msg, &MLACP(csm).arp_list, tail) + { + arp_info = (struct ARPMsg*)msg->buf; + if (arp_info->ipv4_addr != arp_msg->ipv4_addr) + continue; + + if (msgtype == RTM_DELNEIGH) + { + /* delete ARP*/ + TAILQ_REMOVE(&MLACP(csm).arp_list, msg, tail); + free(msg->buf); + free(msg); + msg = NULL; + ICCPD_LOG_DEBUG(__FUNCTION__, "Delete ARP %s", show_ip_str(htonl(arp_msg->ipv4_addr))); + } + else + { + /* update ARP*/ + if (arp_info->op_type != arp_msg->op_type + || strcmp(arp_info->ifname, arp_msg->ifname) != 0 + || memcmp(arp_info->mac_addr, arp_msg->mac_addr, + ETHER_ADDR_LEN) != 0) + { + arp_update = 1; + arp_info->op_type = arp_msg->op_type; + sprintf(arp_info->ifname, "%s", arp_msg->ifname); + memcpy(arp_info->mac_addr, arp_msg->mac_addr, ETHER_ADDR_LEN); + ICCPD_LOG_DEBUG(__FUNCTION__, "Update ARP for %s", show_ip_str(htonl(arp_msg->ipv4_addr))); + } + } + break; + } + + if (msg && !arp_update) + return; + + if (msgtype != RTM_DELNEIGH) + { + /* enquene lif_msg (add)*/ + if (!msg) + { + arp_msg->op_type = ARP_SYNC_LIF; + if (iccp_csm_init_msg(&msg, (char*)arp_msg, msg_len) == 0) + { + mlacp_enqueue_arp(csm, msg); + /*ICCPD_LOG_DEBUG(__FUNCTION__, "ARP-list enqueue: %s, add %s", + arp_msg->ifname, show_ip_str(htonl(arp_msg->ipv4_addr)));*/ + } + else + ICCPD_LOG_WARN(__FUNCTION__, "Failed to enqueue ARP-list: %s, add %s", + arp_msg->ifname, show_ip_str(htonl(arp_msg->ipv4_addr))); + } + + /* enqueue iccp_msg (add)*/ + if (MLACP(csm).current_state == MLACP_STATE_EXCHANGE) + { + arp_msg->op_type = ARP_SYNC_ADD; + if (iccp_csm_init_msg(&msg_send, (char*)arp_msg, msg_len) == 0) + { + TAILQ_INSERT_TAIL(&(MLACP(csm).arp_msg_list), msg_send, tail); + /*ICCPD_LOG_DEBUG(__FUNCTION__, "Enqueue ARP[ADD] message for %s", + show_ip_str(htonl(arp_msg->ipv4_addr)));*/ + } + else + ICCPD_LOG_WARN(__FUNCTION__, "Failed to enqueue ARP[ADD] message for %s", + show_ip_str(htonl(arp_msg->ipv4_addr))); + + } + } + else + { + /* enqueue iccp_msg (delete)*/ + if (MLACP(csm).current_state == MLACP_STATE_EXCHANGE) + { + arp_msg->op_type = ARP_SYNC_DEL; + if (iccp_csm_init_msg(&msg_send, (char*)arp_msg, msg_len) == 0) + { + TAILQ_INSERT_TAIL(&(MLACP(csm).arp_msg_list), msg_send, tail); + /*ICCPD_LOG_DEBUG(__FUNCTION__, "Enqueue ARP[DEL] message for %s", + show_ip_str(htonl(arp_msg->ipv4_addr)));*/ + } + else + ICCPD_LOG_WARN(__FUNCTION__, "Failed to enqueue ARP[DEL] message for %s", + show_ip_str(htonl(arp_msg->ipv4_addr))); + + } + } + + /*Debug: dump for dequeue ARP Info*/ + #if 0 + fprintf(stderr, "\n======== ARP Info List ========\n"); + TAILQ_FOREACH(msg, &MLACP(csm).arp_list, tail) + { + arp_msg = (struct ARPMsg*)msg->buf; + fprintf(stderr, "type %d,ifname %s , ip %s\n", arp_msg->op_type, arp_msg->ifname, show_ip_str(htonl(arp_msg->ipv4_addr))); + } + fprintf(stderr, "==============================\n"); + #endif + + /*TEST dump for dequeue ARP message*/ + #if 0 + + while (MLACP(csm).arp_updated && !TAILQ_EMPTY(&(MLACP(csm).arp_msg_list))) + { + msg = TAILQ_FIRST(&(MLACP(csm).arp_msg_list)); + TAILQ_REMOVE(&(MLACP(csm).arp_msg_list), msg, tail); + arp_msg = (struct ARPMsg *)msg->buf; + fprintf(stderr, "\n======== Dequeue ARP ========\n"); + fprintf(stderr, " Type = [%d]\n", arp_msg->op_type); + fprintf(stderr, " State = (%04X)(%d)\n", ndm->ndm_state, fwd_neigh_state_valid(ndm->ndm_state)); + fprintf(stderr, " ifname = [%s]\n", arp_msg->ifname); + fprintf(stderr, " IP = [%s]\n", show_ip_str(arp_msg->ipv4_addr)); + fprintf(stderr, " MAC = [%02X:%02X:%02X:%02X:%02X:%02X]\n", + arp_msg->mac_addr[0], arp_msg->mac_addr[1], arp_msg->mac_addr[2], arp_msg->mac_addr[3], + arp_msg->mac_addr[4], arp_msg->mac_addr[5]); + fprintf(stderr, "==============================\n"); + free(msg->buf); + free(msg); + } + + MLACP(csm).arp_updated = 0; + #endif + + return; +} + +void ifm_parse_rtattr(struct rtattr **tb, int max, struct rtattr *rta, int len) +{ + while (RTA_OK(rta, len)) + { + if (rta->rta_type <= max) + tb[rta->rta_type] = rta; + rta = RTA_NEXT(rta, len); + } +} + +int do_one_neigh_request(struct nlmsghdr *n) +{ + struct ndmsg *ndm = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr * tb[NDA_MAX + 1]; + + if (n->nlmsg_type == NLMSG_DONE) + { + return 0; + } + + /* process msg_type RTM_NEWNEIGH, RTM_GETNEIGH, RTM_DELNEIGH */ + if (n->nlmsg_type != RTM_NEWNEIGH && n->nlmsg_type != RTM_DELNEIGH ) + return(0); + + len -= NLMSG_LENGTH(sizeof(*ndm)); + if (len < 0) + return MCLAG_ERROR; + + ifm_parse_rtattr(tb, NDA_MAX, NDA_RTA(ndm), len); + + if (n->nlmsg_type == RTM_NEWNEIGH + && (ndm->ndm_state == NUD_INCOMPLETE + || ndm->ndm_state == NUD_FAILED + || ndm->ndm_state == NUD_NOARP + || ndm->ndm_state == NUD_PERMANENT + || ndm->ndm_state == NUD_NONE)) + { + return(0); + } + + if (!tb[NDA_DST] || ndm->ndm_type != RTN_UNICAST) + { + return(0); + } + + if (ndm->ndm_family == AF_INET) + { + do_arp_learn_from_kernel(ndm, tb, n->nlmsg_type); + } + + return(0); +} + +/*Handle arp received from kernel*/ +static int iccp_arp_valid_handler(struct nl_msg *msg, void *arg) +{ + struct nlmsghdr *nlh = nlmsg_hdr(msg); + + do_one_neigh_request(nlh); + + return 0; +} + +/*Get kernel arp information during initialization*/ +int iccp_arp_get_init() +{ + struct System *sys = NULL; + struct nl_cb *cb; + struct nl_cb *orig_cb; + struct rtgenmsg rt_hdr = { + .rtgen_family = AF_UNSPEC, + }; + int ret; + int retry = 1; + + if (!(sys = system_get_instance())) + return MCLAG_ERROR; + + while (retry) + { + retry = 0; + ret = nl_send_simple(sys->route_sock, RTM_GETNEIGH, NLM_F_DUMP, + &rt_hdr, sizeof(rt_hdr)); + if (ret < 0) + { + ICCPD_LOG_ERR(__FUNCTION__, "Send netlink msg error."); + return ret; + } + + orig_cb = nl_socket_get_cb(sys->route_sock); + cb = nl_cb_clone(orig_cb); + nl_cb_put(orig_cb); + if (!cb) + { + ICCPD_LOG_ERR(__FUNCTION__, "nl cb clone error."); + return -ENOMEM; + } + + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, iccp_arp_valid_handler, sys); + + ret = nl_recvmsgs(sys->route_sock, cb); + nl_cb_put(cb); + if (ret < 0) + { + ICCPD_LOG_ERR(__FUNCTION__, "Receive netlink msg error."); + if (ret != -NLE_DUMP_INTR) + return ret; + + retry = 1; + } + } + + return ret; +} + +/*When received ARP packets from kernel, update arp information*/ +void do_arp_update_from_reply_packet(unsigned int ifindex, unsigned int addr, uint8_t mac_addr[ETHER_ADDR_LEN]) +{ + struct System *sys = NULL; + struct CSM *csm = NULL; + struct Msg *msg = NULL; + struct ARPMsg *arp_msg = NULL, *arp_info = NULL; + struct VLAN_ID *vlan_id_list = NULL; + struct Msg *msg_send = NULL; + + char buf[MAX_BUFSIZE]; + size_t msg_len = 0; + + struct LocalInterface *lif_po = NULL, *arp_lif = NULL; + + int verify_arp = 0; + + if (!(sys = system_get_instance())) + return; + + /* Find local itf*/ + if (!(arp_lif = local_if_find_by_ifindex(ifindex))) + return; + + /* create ARP msg*/ + memset(buf, 0, MAX_BUFSIZE); + msg_len = sizeof(struct ARPMsg); + arp_msg = (struct ARPMsg*)&buf; + arp_msg->op_type = ARP_SYNC_LIF; + sprintf(arp_msg->ifname, "%s", arp_lif->name); + memcpy(&arp_msg->ipv4_addr, &addr, 4); + memcpy(arp_msg->mac_addr, mac_addr, 6); + + ICCPD_LOG_DEBUG(__FUNCTION__, "ARP ifindex [%d] (%s) ip %s mac [%02X:%02X:%02X:%02X:%02X:%02X]", + ifindex, arp_lif->name, + show_ip_str(htonl(arp_msg->ipv4_addr)), + arp_msg->mac_addr[0], arp_msg->mac_addr[1], arp_msg->mac_addr[2], arp_msg->mac_addr[3], arp_msg->mac_addr[4], arp_msg->mac_addr[5]); + /*Debug*/ + #if 0 + /* dump receive kernel ARP req*/ + fprintf(stderr, "\n======== Kernel ARP Update==========\n"); + fprintf(stderr, " Type = (New=%d)\n", RTM_NEWNEIGH); + fprintf(stderr, " ifindex = [%d] (%s)\n", ifindex, arp_lif->name); + fprintf(stderr, " IP = [%s]\n", show_ip_str(htonl(arp_msg->ipv4_addr))); + fprintf(stderr, " MAC = [%02X:%02X:%02X:%02X:%02X:%02X]\n", + arp_msg->mac_addr[0], arp_msg->mac_addr[1], arp_msg->mac_addr[2], arp_msg->mac_addr[3], + arp_msg->mac_addr[4], arp_msg->mac_addr[5]); + fprintf(stderr, "==============================\n"); + #endif + + /* Find MLACP itf, member of port-channel*/ + LIST_FOREACH(csm, &(sys->csm_list), next) + { + LIST_FOREACH(lif_po, &(MLACP(csm).lif_list), mlacp_next) + { + if (lif_po->type != IF_T_PORT_CHANNEL) + continue; + + if (!local_if_is_l3_mode(lif_po)) + { + /* Is the L2 MLAG itf belong to a vlan?*/ + LIST_FOREACH(vlan_id_list, &(lif_po->vlan_list), port_next) + { + if ( !(vlan_id_list->vlan_itf + && vlan_id_list->vlan_itf->ifindex == ifindex)) + continue; + break; + } + + if (!vlan_id_list) + continue; + ICCPD_LOG_DEBUG(__FUNCTION__, "ARP is from mclag enabled port %s of vlan %s", + lif_po->name, vlan_id_list->vlan_itf->name); + } + else + { + /* Is the ARP belong to a L3 mode MLAG itf?*/ + if (ifindex != lif_po->ifindex) + continue; + ICCPD_LOG_DEBUG(__FUNCTION__, "ARP is from mclag enabled intf %s", lif_po->name); + } + + verify_arp = 1; + + break; + } + + if (lif_po) + break; + } + + if (!(csm && lif_po)) + return; + if (!verify_arp) + return; + + if (iccp_check_if_addr_from_netlink(AF_INET, &addr, arp_lif)) + { + ICCPD_LOG_DEBUG(__FUNCTION__, "ARP %s is identical with the ip address of interface %s", + show_ip_str(htonl(arp_msg->ipv4_addr)), arp_lif->name); + return; + } + + /* update lif ARP*/ + TAILQ_FOREACH(msg, &MLACP(csm).arp_list, tail) + { + arp_info = (struct ARPMsg*)msg->buf; + if (arp_info->ipv4_addr != arp_msg->ipv4_addr) + continue; + + /* update ARP*/ + if (arp_info->op_type != arp_msg->op_type + || strcmp(arp_info->ifname, arp_msg->ifname) != 0 + || memcmp(arp_info->mac_addr, arp_msg->mac_addr, + ETHER_ADDR_LEN) != 0) + { + arp_info->op_type = arp_msg->op_type; + sprintf(arp_info->ifname, "%s", arp_msg->ifname); + memcpy(arp_info->mac_addr, arp_msg->mac_addr, ETHER_ADDR_LEN); + ICCPD_LOG_DEBUG(__FUNCTION__, "Update ARP for %s", + show_ip_str(htonl(arp_msg->ipv4_addr))); + } + break; + } + + /* enquene lif_msg (add)*/ + if (!msg) + { + arp_msg->op_type = ARP_SYNC_LIF; + if (iccp_csm_init_msg(&msg, (char*)arp_msg, msg_len) == 0) + { + mlacp_enqueue_arp(csm, msg); + /*ICCPD_LOG_DEBUG(__FUNCTION__, "ARP-list enqueue: %s, add %s", + arp_msg->ifname, show_ip_str(htonl(arp_msg->ipv4_addr)));*/ + } + else + ICCPD_LOG_WARN(__FUNCTION__, "Failed to enqueue ARP-list: %s, add %s", + arp_msg->ifname, show_ip_str(htonl(arp_msg->ipv4_addr))); + } + + /* enqueue iccp_msg (add)*/ + if (MLACP(csm).current_state == MLACP_STATE_EXCHANGE) + { + arp_msg->op_type = ARP_SYNC_ADD; + if (iccp_csm_init_msg(&msg_send, (char*)arp_msg, msg_len) == 0) + { + TAILQ_INSERT_TAIL(&(MLACP(csm).arp_msg_list), msg_send, tail); + /*ICCPD_LOG_DEBUG(__FUNCTION__, "Enqueue ARP[ADD] for %s", + show_ip_str(htonl(arp_msg->ipv4_addr)));*/ + } + else + ICCPD_LOG_WARN(__FUNCTION__, "Failed to enqueue ARP[ADD] message for %s", + show_ip_str(htonl(arp_msg->ipv4_addr))); + } + + return; +} + +void iccp_from_netlink_port_state_handler( char * ifname, int state) +{ + struct CSM *csm = NULL; + struct LocalInterface *lif_po = NULL; + struct System *sys; + int po_is_active = 0; + + if ((sys = system_get_instance()) == NULL) + { + ICCPD_LOG_WARN(__FUNCTION__, "Failed to obtain System instance."); + return; + } + + po_is_active = (state == PORT_STATE_UP); + + /* traverse all CSM */ + LIST_FOREACH(csm, &(sys->csm_list), next) + { + /*If peer-link changes to down or up */ + if (strcmp(ifname, csm->peer_itf_name) == 0) + { + if (po_is_active == 0) + mlacp_peerlink_down_handler(csm); + else + mlacp_peerlink_up_handler(csm); + + break; + } + + LIST_FOREACH(lif_po, &(MLACP(csm).lif_list), mlacp_next) + { + if (lif_po->type == IF_T_PORT_CHANNEL && strncmp(lif_po->name, ifname, MAX_L_PORT_NAME) == 0) + { + mlacp_portchannel_state_handler(csm, lif_po, po_is_active); + } + } + } + + return; +} + +int parse_rtattr_flags(struct rtattr *tb[], int max, struct rtattr *rta, + int len, unsigned short flags) +{ + unsigned short type; + + memset(tb, 0, sizeof(struct rtattr *) * (max + 1)); + + while (RTA_OK(rta, len)) + { + type = rta->rta_type & ~flags; + if ((type <= max) && (!tb[type])) + tb[type] = rta; + rta = RTA_NEXT(rta, len); + } + + return 0; +} + +int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len) +{ + return parse_rtattr_flags(tb, max, rta, len, 0); +} + +void iccp_parse_if_vlan_info_from_netlink(struct nlmsghdr *n) +{ + struct LocalInterface *lif = NULL; + int msglen = 0; + + msglen = n->nlmsg_len; + + while (NLMSG_OK(n, msglen)) + { + struct ifinfomsg *ifm = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr * tb[IFLA_MAX + 1]; + + if (n->nlmsg_type != RTM_NEWLINK) + { + return; + } + + len -= NLMSG_LENGTH(sizeof(*ifm)); + if (len < 0) + { + ICCPD_LOG_WARN(__FUNCTION__, "BUG: wrong nlmsg len %d\n", len); + return; + } + + if (ifm->ifi_family != AF_BRIDGE) + { + return; + } + + if ((lif = local_if_find_by_ifindex(ifm->ifi_index)) != NULL) + { + parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifm), len); + + /* if AF_SPEC isn't there, vlan table is not preset for this port */ + if (!tb[IFLA_AF_SPEC]) + { + ICCPD_LOG_WARN(__FUNCTION__, "Vlan table is not preset for %d", ifm->ifi_index); + return; + } + else + { + struct rtattr *i, *list = tb[IFLA_AF_SPEC]; + int rem = RTA_PAYLOAD(list); + struct VLAN_ID *vlan = NULL; + + /*set vlan flag is removed*/ + LIST_FOREACH(vlan, &(lif->vlan_list), port_next) + { + vlan->vlan_removed = 1; + } + + for (i = RTA_DATA(list); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) + { + struct bridge_vlan_info *vinfo; + + if (i->rta_type != IFLA_BRIDGE_VLAN_INFO) + continue; + + vinfo = RTA_DATA(i); + + local_if_add_vlan(lif, vinfo->vid); + } + + /*After update vlan list, remove unused item*/ + LIST_FOREACH(vlan, &(lif->vlan_list), port_next) + { + if (vlan->vlan_removed == 1) + { + ICCPD_LOG_DEBUG(__FUNCTION__, "Remove %s from VLAN %d", lif->name, vlan->vid); + + LIST_REMOVE(vlan, port_next); + free(vlan); + } + } + } + } + + n = NLMSG_NEXT(n, msglen); + } +} \ No newline at end of file diff --git a/src/iccpd/src/iccp_main.c b/src/iccpd/src/iccp_main.c new file mode 100644 index 000000000000..86920f07e1fb --- /dev/null +++ b/src/iccpd/src/iccp_main.c @@ -0,0 +1,272 @@ +/* + * iccp_main.c + * + * Copyright(c) 2016-2019 Nephos/Estinet. + * + * 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, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintainer: jianjun, grace Li from nephos + */ + +#include +#include +#include +#include +#include +#include + +#include "../include/cmd_option.h" +#include "../include/logger.h" +#include "../include/scheduler.h" +#include "../include/system.h" + +int check_instance(char* pid_file_path) +{ + int pid_file = 0; + int rc = 0; + + if (pid_file_path == NULL) + return MCLAG_ERROR; + + pid_file = open(pid_file_path, O_CREAT | O_RDWR, 0666); + if (pid_file <= 0 ) + { + fprintf(stderr, "Can't open a pid file. Terminate.\n"); + close(pid_file); + exit(EXIT_FAILURE); + } + + rc = flock(pid_file, LOCK_EX | LOCK_NB); + + if (rc) + { + if (errno == EWOULDBLOCK) + { + fprintf(stderr, "There is another instance running. Terminate.\n"); + close(pid_file); + exit(EXIT_FAILURE); + } + } + + return pid_file; +} + +void init_daemon(char* pid_file_path, int pid_file) +{ + pid_t pid, sid; + + pid = fork(); + if (pid < 0) + { + fprintf(stderr, "Failed to enter daemon mode: %s\n", strerror(errno)); + fprintf(stderr, "Please try to check your system resources.\n"); + close(pid_file); + unlink(pid_file_path); + exit(EXIT_FAILURE); + } + + if (pid > 0) + exit(EXIT_SUCCESS); + + umask(0); + sid = setsid(); + if (sid < 0) + { + fprintf(stderr, "Failed to create a new SID for this program: %s\n", strerror(errno)); + fprintf(stderr, "Please try to check your system resources.\n"); + close(pid_file); + unlink(pid_file_path); + exit(EXIT_FAILURE); + } + + freopen("/dev/null", "r", stdin); + freopen("/dev/null", "w", stdout); + freopen("/dev/null", "w", stderr); +} + +#ifndef ICCPD_RUN_DIR +#define ICCPD_RUN_DIR "/var/run/iccpd/" +#endif + +static inline int iccpd_make_rundir(void) +{ + int ret; + + ret = mkdir(ICCPD_RUN_DIR, 0755); + if (ret && errno != EEXIST) + { + ICCPD_LOG_ERR(__FUNCTION__, "Failed to create directory \"%s\"", + ICCPD_RUN_DIR); + + return -errno; + } + + return 0; +} + +void iccpd_signal_handler(int sig) +{ + int err; + struct System* sys = NULL; + const char warmboot_flag = 'w'; + + sys = system_get_instance(); + if (!sys) + { + return; + } + + retry: + err = write(sys->sig_pipe_w, &warmboot_flag, 1); + if (err == -1 && errno == EINTR) + goto retry; + + return; +} + +static int iccpd_signal_init(struct System* sys) +{ + int fds[2]; + int err; + sigset_t ss; + struct sigaction sa; + struct epoll_event event; + + err = pipe(fds); + if (err) + return -errno; + + sys->sig_pipe_r = fds[0]; + sys->sig_pipe_w = fds[1]; + + if (sigemptyset(&ss) < 0) + { + ICCPD_LOG_ERR(__FUNCTION__, "sigemptyset(): %d", errno); + goto close_pipe; + } + + if (sigaddset(&ss, SIGUSR1) < 0) + { + ICCPD_LOG_ERR(__FUNCTION__, "sigaddset(): %d", errno); + goto close_pipe; + } + + if (sigprocmask(SIG_UNBLOCK, &ss, NULL) < 0) + { + ICCPD_LOG_ERR(__FUNCTION__, "sigprocmask(): %d", errno); + goto close_pipe; + } + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = iccpd_signal_handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + + if (sigaction(SIGUSR1, &sa, NULL) < 0) + { + ICCPD_LOG_ERR(__FUNCTION__, "sigaction(): %d", errno); + goto close_pipe; + } + + event.data.fd = fds[0]; + event.events = EPOLLIN; + err = epoll_ctl(sys->epoll_fd, EPOLL_CTL_ADD, fds[0], &event); + if (err) + { + goto close_pipe; + } + + FD_SET( fds[0], &(sys->readfd)); + sys->readfd_count++; + + return 0; + + close_pipe: + close(sys->sig_pipe_r); + close(sys->sig_pipe_w); + return err; +} + +int main(int argc, char* argv[]) +{ + int pid_file_fd = 0; + struct System* sys = NULL; + int err; + struct CmdOptionParser parser = CMD_OPTION_PARSER_INIT_VALUE; + + err = iccpd_make_rundir(); + if (err) + return 0; + + if (getuid() != 0) + { + fprintf(stderr, + "This program needs root permission to do device manipulation. " + "Please use sudo to execute it or change your user to root.\n"); + exit(EXIT_FAILURE); + } + + parser.init(&parser); + if (parser.parse(&parser, argc, argv) != 0) + { + parser.finalize(&parser); + return MCLAG_ERROR; + } + + pid_file_fd = check_instance(parser.pid_file_path); + if (pid_file_fd < 0) + { + fprintf(stderr, "Check instance with invalidate arguments, iccpd is terminated.\n"); + parser.finalize(&parser); + exit(EXIT_FAILURE); + } + + sys = system_get_instance(); + if (!sys) + { + fprintf(stderr, "Can't get a system instance, iccpd is terminated.\n"); + parser.finalize(&parser); + exit(EXIT_FAILURE); + } + + /*if(!parser.console_log) + init_daemon(parser.pid_file_path, pid_file_fd);*/ + + log_init(&parser); + + if (sys->log_file_path != NULL) + free(sys->log_file_path); + if (sys->cmd_file_path != NULL) + free(sys->cmd_file_path); + if (sys->config_file_path != NULL) + free(sys->config_file_path); + sys->log_file_path = strdup(parser.log_file_path); + sys->cmd_file_path = strdup(parser.cmd_file_path); + sys->config_file_path = strdup(parser.config_file_path); + sys->mclagdctl_file_path = strdup(parser.mclagdctl_file_path); + sys->pid_file_fd = pid_file_fd; + sys->telnet_port = parser.telnet_port; + parser.finalize(&parser); + iccpd_signal_init(sys); + ICCPD_LOG_INFO(__FUNCTION__, "Iccpd is started, process id = %d. uid %d ", getpid(), getuid()); + scheduler_init(); + scheduler_start(); + system_finalize(); + /*scheduler_finalize(); + log_finalize();*/ + + return EXIT_SUCCESS; +} diff --git a/src/iccpd/src/iccp_netlink.c b/src/iccpd/src/iccp_netlink.c new file mode 100644 index 000000000000..ec8971d0ceee --- /dev/null +++ b/src/iccpd/src/iccp_netlink.c @@ -0,0 +1,1545 @@ +/* + * iccp_netlink.c + * + * Copyright(c) 2016-2019 Nephos/Estinet. + * + * 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, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintainer: jianjun, grace Li from nephos + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "../include/system.h" +#include "../include/iccp_ifm.h" +#include "../include/port.h" +#include "../include/iccp_csm.h" +#include "../include/logger.h" +#include "../include/scheduler.h" +#include "../include/mlacp_link_handler.h" +#include "../include/msg_format.h" +/** + * SECTION: Netlink helpers + */ +/* \cond HIDDEN_SYMBOLS */ +#ifndef SOL_NETLINK +#define SOL_NETLINK 270 +#endif + +#define ARRAY_SIZE(array_name) (sizeof(array_name) / sizeof(array_name[0])) + +#ifndef NETLINK_BROADCAST_SEND_ERROR +#define NETLINK_BROADCAST_SEND_ERROR 0x4 +#endif + +static int iccp_ack_handler(struct nl_msg *msg, void *arg) +{ + bool *acked = arg; + + *acked = true; + + return NL_STOP; +} + +static int iccp_seq_check_handler(struct nl_msg *msg, void *arg) +{ + unsigned int *seq = arg; + struct nlmsghdr *hdr = nlmsg_hdr(msg); + + if (hdr->nlmsg_seq != *seq) + return NL_SKIP; + + return NL_OK; +} + +int iccp_send_and_recv(struct System *sys, struct nl_msg *msg, + int (*valid_handler)(struct nl_msg *, void *), + void *valid_data) +{ + int ret; + struct nl_cb *cb; + struct nl_cb *orig_cb; + bool acked; + unsigned int seq = sys->genric_sock_seq++; + int err; + + ret = nl_send_auto(sys->genric_sock, msg); + nlmsg_free(msg); + if (ret < 0) + return ret; + + orig_cb = nl_socket_get_cb(sys->genric_sock); + cb = nl_cb_clone(orig_cb); + nl_cb_put(orig_cb); + if (!cb) + return -ENOMEM; + + nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, iccp_ack_handler, &acked); + nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, iccp_seq_check_handler, &seq); + if (valid_handler) + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, valid_handler, valid_data); + + /* There is a bug in libnl. When implicit sequence number checking is in + * use the expected next number is increased when NLMSG_DONE is + * received. The ACK which comes after that correctly includes the + * original sequence number. However libnl is checking that number + * against the incremented one and therefore ack handler is never called + * and nl_recvmsgs finished with an error. To resolve this, custom + * sequence number checking is used here. + */ + + acked = false; + + while (!acked) + { + ret = nl_recvmsgs(sys->genric_sock, cb); + if (ret) + { + err = ret; + goto put_cb; + } + } + + err = 0; + put_cb: + nl_cb_put(cb); + return err; +} + +int iccp_get_portchannel_member_list_handler(struct nl_msg *msg, void * arg) +{ + struct nlmsghdr *nlh = nlmsg_hdr(msg); + struct nlattr *attrs[TEAM_ATTR_MAX + 1]; + struct nlattr *nl_port; + struct nlattr *port_attrs[TEAM_ATTR_PORT_MAX + 1]; + struct LocalInterface* lif = NULL; + struct LocalInterface* local_if_member = NULL; + struct CSM* csm; + int i; + uint32_t ifindex = 0; + struct System* sys = NULL; + struct LocalInterface* local_if = NULL; + char temp_buf[512]; + int len = 0; + + sys = system_get_instance(); + if (sys == NULL) + return 0; + + genlmsg_parse(nlh, 0, attrs, TEAM_ATTR_MAX, NULL); + + if (attrs[TEAM_ATTR_TEAM_IFINDEX]) + ifindex = nla_get_u32(attrs[TEAM_ATTR_TEAM_IFINDEX]); + + local_if = local_if_find_by_ifindex(ifindex); + + if (!local_if) + return NL_SKIP; + + if (local_if->type != IF_T_PORT_CHANNEL) + return NL_SKIP; + + csm = local_if->csm; + + if (csm) + { + if (!attrs[TEAM_ATTR_LIST_PORT]) + return NL_SKIP; + + nla_for_each_nested(nl_port, attrs[TEAM_ATTR_LIST_PORT], i) + { + uint32_t member_index; + + if (nla_parse_nested(port_attrs, TEAM_ATTR_PORT_MAX, nl_port, NULL)) + { + ICCPD_LOG_ERR(__FUNCTION__, "Failed to parse nested attributes."); + return NL_SKIP; + } + + if (!port_attrs[TEAM_ATTR_PORT_IFINDEX]) + { + ICCPD_LOG_ERR(__FUNCTION__, "ifindex port attribute not found."); + return NL_SKIP; + } + + member_index = nla_get_u32(port_attrs[TEAM_ATTR_PORT_IFINDEX]); + + local_if_member = local_if_find_by_ifindex(member_index); + if (local_if_member == NULL) + { + + ICCPD_LOG_WARN(__FUNCTION__, "%s: Failed to find a port instance (%d).", + local_if->name, member_index); + continue; + } + + if (port_attrs[TEAM_ATTR_PORT_REMOVED] && local_if_member->po_id != -1) + { + local_if_member->po_id = -1; + mlacp_unbind_local_if(local_if_member); + } + else if ( local_if_member->po_id == -1) + { + local_if_member->po_id = local_if->po_id; + mlacp_bind_local_if(local_if->csm, local_if_member); + } + } + + memset(temp_buf, 0, 512); + LIST_FOREACH(lif, &(MLACP(csm).lif_list), mlacp_next) + { + if (lif->type == IF_T_PORT && lif->po_id == local_if->po_id) + { + if (strlen(temp_buf) != 0) + len += snprintf(temp_buf + len, 512 - len, "%s", ","); + + len += snprintf(temp_buf + len, 512 - len, "%s", lif->name); + } + } + + if (strcmp(temp_buf, local_if->portchannel_member_buf)) + { + memset(local_if->portchannel_member_buf, 0, 512); + memcpy(local_if->portchannel_member_buf, temp_buf, sizeof(local_if->portchannel_member_buf) - 1); + + if (MLACP(csm).current_state == MLACP_STATE_EXCHANGE) + { + /* portchannel member changed, update port isolate attribute*/ + update_peerlink_isolate_from_all_csm_lif(csm); + } + } + } + else /*peerlink portchannel */ + { + if (local_if->is_peer_link) + { + LIST_FOREACH(csm, &(sys->csm_list), next) + { + if (csm->peer_link_if == local_if ) + { + break; + } + } + + if (csm == NULL) + return 0; + + nla_for_each_nested(nl_port, attrs[TEAM_ATTR_LIST_PORT], i) + { + uint32_t member_index; + + if (nla_parse_nested(port_attrs, TEAM_ATTR_PORT_MAX, nl_port, NULL)) + { + ICCPD_LOG_WARN(__FUNCTION__, "Failed to parse nested attributes."); + return NL_SKIP; + } + + if (!port_attrs[TEAM_ATTR_PORT_IFINDEX]) + { + ICCPD_LOG_WARN(__FUNCTION__, "ifindex port attribute not found."); + return NL_SKIP; + } + + member_index = nla_get_u32(port_attrs[TEAM_ATTR_PORT_IFINDEX]); + + local_if_member = local_if_find_by_ifindex(member_index); + if (local_if_member == NULL) + { + ICCPD_LOG_WARN(__FUNCTION__, "%s: Failed to find a port instance (%d).", + local_if->name, member_index); + continue; + } + + if (port_attrs[TEAM_ATTR_PORT_REMOVED] && local_if_member->po_id != -1) + { + local_if_member->po_id = -1; + if (MLACP(csm).current_state == MLACP_STATE_EXCHANGE) + { + /*link removed from portchannel, must be enabled mac learn*/ + set_peerlink_mlag_port_learn(local_if_member, 1); + } + + continue; + } + else if ( local_if_member->po_id == -1) + { + local_if_member->po_id = local_if->po_id; + + if (MLACP(csm).current_state == MLACP_STATE_EXCHANGE) + { + /*link add to portchannel, must be disabled mac learn*/ + set_peerlink_mlag_port_learn(local_if, 0); + } + } + } + + memset(temp_buf, 0, 512); + LIST_FOREACH(lif, &(sys->lif_list), system_next) + { + if (lif->type == IF_T_PORT && lif->po_id == local_if->po_id) + { + if (strlen(temp_buf) != 0) + len += snprintf(temp_buf + len, 512 - len, "%s", ","); + + len += snprintf(temp_buf + len, 512 - len, "%s", lif->name); + } + } + + if (strcmp(temp_buf, local_if->portchannel_member_buf)) + { + memset(local_if->portchannel_member_buf, 0, 512); + memcpy(local_if->portchannel_member_buf, temp_buf, sizeof(local_if->portchannel_member_buf) - 1); + #if 0 + if (MLACP(csm).current_state == MLACP_STATE_EXCHANGE) + { + /*peerlink portchannel member changed*/ + update_peerlink_isolate_from_all_csm_lif(csm); + } + #endif + } + } + } + + return 0; +} + +int iccp_genric_socket_team_family_get() +{ + struct System* sys = NULL; + int err = 0; + int grp_id = 0; + + if ((sys = system_get_instance()) == NULL ) + return MCLAG_ERROR; + if (sys->family < 0) + { + sys->family = genl_ctrl_resolve(sys->genric_sock, TEAM_GENL_NAME); + + while (sys->family < 0) + { + sleep(1); + + /*If no portchannel configuration, teamd will not started, genl_ctrl_resolve() will return <0 forever */ + /*Only log error message 5 times*/ + + ICCPD_LOG_ERR(__FUNCTION__, "Failed to resolve netlink family. %d of TEAM_GENL_NAME %s ", sys->family, TEAM_GENL_NAME); + sys->family = genl_ctrl_resolve(sys->genric_sock, TEAM_GENL_NAME); + + } + + grp_id = genl_ctrl_resolve_grp(sys->genric_sock, TEAM_GENL_NAME, + TEAM_GENL_CHANGE_EVENT_MC_GRP_NAME); + if (grp_id < 0) + { + ICCPD_LOG_ERR(__FUNCTION__, "Failed to resolve netlink multicast groups. %d", grp_id); + } + + err = nl_socket_add_membership(sys->genric_event_sock, grp_id); + if (err < 0) + { + ICCPD_LOG_ERR(__FUNCTION__, "Failed to add netlink membership."); + + } + } + + return err; +} + +int iccp_get_port_member_list(struct LocalInterface* lif) +{ + struct System *sys; + struct nl_msg *msg; + int err; + + sys = system_get_instance(); + if (sys == NULL) + return 0; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + err = iccp_genric_socket_team_family_get(); + if (err) + { + ICCPD_LOG_ERR(__FUNCTION__, "genric socket family get err err = %d . errno = %d", err, errno); + return err; + } + + genlmsg_put(msg, NL_AUTO_PID, sys->genric_sock_seq, sys->family, 0, 0, + TEAM_CMD_PORT_LIST_GET, 0); + nla_put_u32(msg, TEAM_ATTR_TEAM_IFINDEX, lif->ifindex); + + err = iccp_send_and_recv(sys, msg, iccp_get_portchannel_member_list_handler, lif); + if (err) + { + ICCPD_LOG_ERR(__FUNCTION__, "recv msg err err = %d . errno = %d", err, errno); + return err; + } + + return 0; +} + +int iccp_netlink_if_hwaddr_set(uint32_t ifindex, uint8_t *addr, unsigned int addr_len) +{ + struct rtnl_link *link; + int err; + struct nl_addr *nl_addr; + struct System* sys = NULL; + + if (!(sys = system_get_instance())) + return MCLAG_ERROR; + + link = rtnl_link_alloc(); + if (!link) + return -ENOMEM; + + nl_addr = nl_addr_build(AF_UNSPEC, (void *)addr, addr_len); + if (!nl_addr) + { + err = -ENOMEM; + goto errout; + } + + rtnl_link_set_ifindex(link, ifindex); + rtnl_link_set_addr(link, nl_addr); + + err = rtnl_link_change(sys->route_sock, link, link, 0); + + nl_addr_put(nl_addr); + + errout: + rtnl_link_put(link); + return err; +} + +#define SET_MAC_STR(buf, macArray) \ + snprintf(buf, 64, "%02x:%02x:%02x:%02x:%02x:%02x", \ + macArray[0], macArray[1], macArray[2], \ + macArray[3], macArray[4], macArray[5]); + +void iccp_set_interface_ipadd_mac(struct LocalInterface *lif, char * mac_addr ) +{ + struct IccpSyncdHDr * msg_hdr; + mclag_sub_option_hdr_t * sub_msg; + char msg_buf[4096]; + struct System *sys; + + int src_len = 0, dst_len = 0; + + sys = system_get_instance(); + if (sys == NULL) + return; + + memset(msg_buf, 0, 4095); + + msg_hdr = (struct IccpSyncdHDr *)msg_buf; + msg_hdr->ver = 1; + msg_hdr->type = MCLAG_MSG_TYPE_SET_MAC; + msg_hdr->len = sizeof(struct IccpSyncdHDr); + + /*sub msg src*/ + sub_msg = (mclag_sub_option_hdr_t *)&msg_buf[msg_hdr->len]; + sub_msg->op_type = MCLAG_SUB_OPTION_TYPE_SET_MAC_SRC; + + /*src_len = snprintf((char *)sub_msg->data, 512, "%s:%s/%d", lif->name,show_ip_str(htonl(lif->ipv4_addr)),lif->prefixlen);*/ + src_len = snprintf((char *)sub_msg->data, 512, "%s", lif->name); + + sub_msg->op_len = src_len; + + /*sub msg dst */ + msg_hdr->len += sub_msg->op_len; + msg_hdr->len += sizeof(mclag_sub_option_hdr_t); + sub_msg = (mclag_sub_option_hdr_t *)&msg_buf[msg_hdr->len]; + sub_msg->op_type = MCLAG_SUB_OPTION_TYPE_SET_MAC_DST; + + dst_len = strlen(mac_addr); + memcpy(sub_msg->data, mac_addr, dst_len); + + ICCPD_LOG_DEBUG(__FUNCTION__, "If name %s ip %s mac %s", lif->name, show_ip_str(htonl(lif->ipv4_addr)), sub_msg->data); + + sub_msg->op_len = dst_len; + msg_hdr->len += sizeof(mclag_sub_option_hdr_t); + msg_hdr->len += sub_msg->op_len; + + /*send msg*/ + if (sys->sync_fd) + write(sys->sync_fd, msg_buf, msg_hdr->len); + + return; +} + +void update_if_ipmac_on_standby(struct LocalInterface* lif_po) +{ + struct CSM* csm; + uint8_t null_mac[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + struct VLAN_ID *vlan = NULL; + + csm = lif_po->csm; + struct LocalInterface* lif_Bri; + char macaddr[64]; + int ret = 0; + + if (!csm) + return; + + if (lif_po->type != IF_T_PORT_CHANNEL) + return; + + if (csm->role_type != STP_ROLE_STANDBY) + { + return; + } + + if (memcmp(MLACP(csm).remote_system.system_id, null_mac, ETHER_ADDR_LEN) == 0) + return; + + /*Set new mac*/ + if (memcmp( lif_po->mac_addr, MLACP(csm).remote_system.system_id, ETHER_ADDR_LEN) != 0) + { + /*Backup old sysmac*/ + memcpy(lif_po->mac_addr_ori, lif_po->mac_addr, ETHER_ADDR_LEN); + + ICCPD_LOG_DEBUG(__FUNCTION__, + "%s Change the system-id of %s from [%02X:%02X:%02X:%02X:%02X:%02X] to [%02X:%02X:%02X:%02X:%02X:%02X].", + (csm->role_type == STP_ROLE_STANDBY) ? "Standby" : "Active", + lif_po->name, lif_po->mac_addr[0], lif_po->mac_addr[1], lif_po->mac_addr[2], lif_po->mac_addr[3], lif_po->mac_addr[4], lif_po->mac_addr[5], + MLACP(csm).remote_system.system_id[0], MLACP(csm).remote_system.system_id[1], MLACP(csm).remote_system.system_id[2], MLACP(csm).remote_system.system_id[3], MLACP(csm).remote_system.system_id[4], MLACP(csm).remote_system.system_id[5]); + + ret = iccp_netlink_if_hwaddr_set(lif_po->ifindex, MLACP(csm).remote_system.system_id, ETHER_ADDR_LEN); + if (ret != 0) + { + ICCPD_LOG_ERR(__FUNCTION__, "Set %s mac error, ret = %d", lif_po->name, ret); + } + } + + /*Set portchannel ip mac */ + memset(macaddr, 0, 64); + SET_MAC_STR(macaddr, MLACP(csm).remote_system.system_id); + if (local_if_is_l3_mode(lif_po)) + { + if (memcmp(lif_po->l3_mac_addr, MLACP(csm).remote_system.system_id, ETHER_ADDR_LEN) != 0) + { + iccp_set_interface_ipadd_mac(lif_po, macaddr ); + memcpy(lif_po->l3_mac_addr, MLACP(csm).remote_system.system_id, ETHER_ADDR_LEN); + } + } + else + { + LIST_FOREACH(vlan, &(lif_po->vlan_list), port_next) + { + if (!vlan->vlan_itf) + continue; + + /*If the po is under a vlan, update vlan mac*/ + if (local_if_is_l3_mode(vlan->vlan_itf)) + { + if (memcmp(vlan->vlan_itf->l3_mac_addr, MLACP(csm).remote_system.system_id, ETHER_ADDR_LEN) != 0) + { + ret = iccp_netlink_if_hwaddr_set(vlan->vlan_itf->ifindex, MLACP(csm).remote_system.system_id, ETHER_ADDR_LEN); + if (ret != 0) + { + ICCPD_LOG_ERR(__FUNCTION__, "Set %s mac error, ret = %d", vlan->vlan_itf->name, ret); + } + + iccp_set_interface_ipadd_mac(vlan->vlan_itf, macaddr ); + memcpy(vlan->vlan_itf->l3_mac_addr, MLACP(csm).remote_system.system_id, ETHER_ADDR_LEN); + } + } + } + } + + return; +} + +void recover_if_ipmac_on_standby(struct LocalInterface* lif_po) +{ + struct CSM* csm; + uint8_t null_mac[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + struct VLAN_ID *vlan = NULL; + + csm = lif_po->csm; + char macaddr[64]; + int ret = 0; + + if (!csm) + return; + + if (lif_po->type != IF_T_PORT_CHANNEL) + return; + + if (csm->role_type != STP_ROLE_STANDBY) + { + return; + } + + /*Recover mac to origin mac, it is the 'mac' value in 'localhost' currently*/ + if (memcmp( lif_po->mac_addr, MLACP(csm).system_id, ETHER_ADDR_LEN) != 0) + { + ICCPD_LOG_DEBUG(__FUNCTION__, + "%s Recover the system-id of %s from [%02X:%02X:%02X:%02X:%02X:%02X] to [%02X:%02X:%02X:%02X:%02X:%02X].", + (csm->role_type == STP_ROLE_STANDBY) ? "Standby" : "Active", + lif_po->name, lif_po->mac_addr[0], lif_po->mac_addr[1], lif_po->mac_addr[2], lif_po->mac_addr[3], lif_po->mac_addr[4], lif_po->mac_addr[5], + MLACP(csm).system_id[0], MLACP(csm).system_id[1], MLACP(csm).system_id[2], MLACP(csm).system_id[3], MLACP(csm).system_id[4], MLACP(csm).system_id[5]); + + ret = iccp_netlink_if_hwaddr_set(lif_po->ifindex, MLACP(csm).system_id, ETHER_ADDR_LEN); + if (ret != 0) + { + ICCPD_LOG_ERR(__FUNCTION__, "Set %s mac error, ret = %d", lif_po->name, ret); + } + } + + /*Set portchannel ip mac */ + memset(macaddr, 0, 64); + SET_MAC_STR(macaddr, MLACP(csm).system_id); + if (local_if_is_l3_mode(lif_po)) + { + iccp_set_interface_ipadd_mac(lif_po, macaddr ); + memcpy(lif_po->l3_mac_addr, MLACP(csm).system_id, ETHER_ADDR_LEN); + } + else + { + LIST_FOREACH(vlan, &(lif_po->vlan_list), port_next) + { + if (!vlan->vlan_itf) + continue; + + /*If the po is under a vlan, update vlan mac*/ + if (local_if_is_l3_mode(vlan->vlan_itf)) + { + ret = iccp_netlink_if_hwaddr_set(vlan->vlan_itf->ifindex, MLACP(csm).system_id, ETHER_ADDR_LEN); + if (ret != 0) + { + ICCPD_LOG_ERR(__FUNCTION__, "Set %s mac error, ret = %d", vlan->vlan_itf->name, ret); + } + + iccp_set_interface_ipadd_mac(vlan->vlan_itf, macaddr); + memcpy(vlan->vlan_itf->l3_mac_addr, MLACP(csm).system_id, ETHER_ADDR_LEN); + } + } + } + + return; +} + +void iccp_event_handler_obj_input_newlink(struct nl_object *obj, void *arg) +{ + struct rtnl_link *link; + unsigned int *event = arg; + uint32_t ifindex; + char * ifname, *p; + struct LocalInterface *lif = NULL; + struct nl_addr *nl_addr; + int addr_type = 0; + int op_state = 0; + int link_flag = 0; + + link = (struct rtnl_link *)obj; + ifindex = rtnl_link_get_ifindex(link); + op_state = rtnl_link_get_operstate(link); + ifname = rtnl_link_get_name(link); + nl_addr = rtnl_link_get_addr(link); + link_flag = rtnl_link_get_flags(link); + + if (nl_addr) + addr_type = nl_addr_guess_family(nl_addr); + + /*Vxlan tunnel dev name is like VTTNL0001-1000, VTTNL0001 is vxlan tunnel name, 1000 is vni*/ + /*If dev is vxlan tunnel, only create the tunnel with name no vni, like VTTNL0001*/ + if ((strncmp(ifname, VXLAN_TUNNEL_PREFIX, strlen(VXLAN_TUNNEL_PREFIX)) == 0)) + { + /*Change ifname from VTTNL0001-1000 to VTTNL0001*/ + if ((p = strchr(ifname, '-')) != NULL) + { + *p = '\0'; + } + /*Create vxlan tunnel dev, state is always UP*/ + lif = local_if_find_by_name(ifname); + if (!lif) + { + lif = local_if_create(ifindex, ifname, IF_T_VXLAN); + lif->state = PORT_STATE_UP; + } + return; + } + else + lif = local_if_find_by_ifindex(ifindex); + + if (!lif) + { + const itf_type_t if_whitelist[] = { + { PORTCHANNEL_PREFIX, IF_T_PORT_CHANNEL }, + { VLAN_PREFIX, IF_T_VLAN }, + { FRONT_PANEL_PORT_PREFIX, IF_T_PORT }, + { VXLAN_TUNNEL_PREFIX, IF_T_VXLAN }, + { NULL, 0 } + }; + int i = 0; + + for (i = 0; if_whitelist[i].ifname != NULL; ++i) + { + if ((strncmp(ifname, + if_whitelist[i].ifname, strlen(if_whitelist[i].ifname)) == 0)) + { + lif = local_if_create(ifindex, ifname, if_whitelist[i].type); + + lif->state = PORT_STATE_DOWN; + + if (IF_OPER_UP == op_state ) + { + lif->state = PORT_STATE_UP; + } + + switch (addr_type) + { + case AF_LLC: + memcpy( lif->mac_addr, nl_addr_get_binary_addr(nl_addr), ETHER_ADDR_LEN); + default: + break; + } + + break; + } + } + } + else /*update*/ + { + /*update*/ + if (lif->state == PORT_STATE_DOWN && op_state == IF_OPER_UP) + { + lif->state = PORT_STATE_UP; + /*if(lif->type ==IF_T_PORT_CHANNEL)*/ + ICCPD_LOG_NOTICE(__FUNCTION__, "Update local port %s state up", ifname); + + iccp_from_netlink_port_state_handler(lif->name, lif->state); + } + else if (lif->state == PORT_STATE_UP && ( IF_OPER_UP != op_state || !(link_flag & IFF_LOWER_UP))) + { + lif->state = PORT_STATE_DOWN; + /*if(lif->type ==IF_T_PORT_CHANNEL)*/ + ICCPD_LOG_NOTICE(__FUNCTION__, "Update local port %s state down", ifname); + + iccp_from_netlink_port_state_handler(lif->name, lif->state); + } + + switch (addr_type) + { + case AF_LLC: + if (memcmp(nl_addr_get_binary_addr(nl_addr), lif->mac_addr, ETHER_ADDR_LEN) != 0) + { + memcpy( lif->mac_addr, nl_addr_get_binary_addr(nl_addr), ETHER_ADDR_LEN); + lif->port_config_sync = 1; + } + + default: + break; + } + } + + return; +} + +void iccp_event_handler_obj_input_dellink(struct nl_object *obj, void *arg) +{ + struct rtnl_link *link; + struct LocalInterface *lif; + uint32_t ifindex; + + link = (struct rtnl_link *)obj; + + ifindex = rtnl_link_get_ifindex(link); + if ((lif = local_if_find_by_ifindex(ifindex)) != NULL) + local_if_destroy(lif->name); + + return; +} + +int iccp_check_if_addr_from_netlink(int family, uint8_t *addr, struct LocalInterface *lif) +{ + struct + { + struct nlmsghdr nlh; + struct ifaddrmsg ifa; + } req; + + struct sockaddr_nl nladdr; + struct iovec iov; + struct msghdr msg = { + .msg_name = &nladdr, + .msg_namelen = sizeof(nladdr), + .msg_iov = &iov, + .msg_iovlen = 1, + }; + + int fd; + struct System *sys; + if ((sys = system_get_instance()) == NULL) + { + return 0; + } + + memset(&req, 0, sizeof(req)); + req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); + req.nlh.nlmsg_type = RTM_GETADDR; + req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST; + req.nlh.nlmsg_pid = 0; + req.nlh.nlmsg_seq = 0; + req.ifa.ifa_family = family; + + fd = nl_socket_get_fd(sys->route_sock); + send(fd, (void*)&req, sizeof(req), 0); + + char * buf = malloc(10000); + iov.iov_base = buf; + + while (1) + { + int status; + int msglen = 0; + iov.iov_len = 10000; + status = recvmsg(fd, &msg, 0); + + if (status < 0 || status == 0) + { + ICCPD_LOG_WARN(__FUNCTION__, "netlink receive error (%d) status %d %d ", fd, status, errno); + free(buf); + return 0; + } + struct nlmsghdr *n = (struct nlmsghdr*)buf; + msglen = status; + + while (NLMSG_OK(n, msglen)) + { + if (n->nlmsg_type != RTM_NEWADDR) + { + free(buf); + return 0; + } + struct ifaddrmsg *ifa; + ifa = NLMSG_DATA(n); + if (lif && lif->ifindex == ifa->ifa_index) + { + struct rtattr *rth = IFA_RTA(ifa); + int rtl = IFA_PAYLOAD(n); + + while (rtl && RTA_OK(rth, rtl)) + { + if (rth->rta_type == IFA_ADDRESS || rth->rta_type == IFA_LOCAL) + { + if (family == AF_INET && ifa->ifa_family == AF_INET) + { + if (*(uint32_t *)addr == ntohl(*((uint32_t *)RTA_DATA(rth)))) + { + free(buf); + return 1; + } + } + + if (family == AF_INET6 && ifa->ifa_family == AF_INET6) + { + void *addr_netlink; + addr_netlink = RTA_DATA(rth); + if (!memcmp((uint8_t *)addr_netlink, addr, 16)) + { + free(buf); + return 1; + } + } + } + rth = RTA_NEXT(rth, rtl); + } + } + n = NLMSG_NEXT(n, msglen); + } + } + + free(buf); + + return 0; +} + +int iccp_local_if_addr_update(struct nl_msg *msg, void *arg) +{ + int len; + struct ifaddrmsg *ifa; + struct LocalInterface *lif; + + struct nlmsghdr *n = nlmsg_hdr(msg); + + if (n->nlmsg_type != RTM_NEWADDR && n->nlmsg_type != RTM_DELADDR) + return 0; + + ifa = NLMSG_DATA(n); + + if (ifa->ifa_family != AF_INET ) + return 0; + + lif = local_if_find_by_ifindex(ifa->ifa_index); + if (!lif) + { + return 0; + } + + if (n->nlmsg_type == RTM_DELADDR) + { + lif->ipv4_addr = 0; + lif->prefixlen = 0; + lif->l3_mode = 0; + memset(lif->l3_mac_addr, 0, ETHER_ADDR_LEN); + } + + len = n->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifaddrmsg)); + if (len < 0) + return 0; + + struct rtattr *rth = IFA_RTA(ifa); + int rtl = IFA_PAYLOAD(n); + + while (rtl && RTA_OK(rth, rtl)) + { + if (rth->rta_type == IFA_ADDRESS) + { + uint32_t ipaddr = ntohl(*((uint32_t *)RTA_DATA(rth))); + lif->ipv4_addr = ipaddr; + lif->prefixlen = ifa->ifa_prefixlen; + lif->l3_mode = 1; + lif->port_config_sync = 1; + update_if_ipmac_on_standby(lif); + ICCPD_LOG_DEBUG(__FUNCTION__, "If name %s index %d ip %s", lif->name, lif->ifindex, show_ip_str(htonl(lif->ipv4_addr))); + } + rth = RTA_NEXT(rth, rtl); + } + + return 0; +} + +int iccp_sys_local_if_list_get_addr() +{ + struct System *sys = NULL; + struct nl_cb *cb; + struct nl_cb *orig_cb; + struct rtgenmsg rt_hdr = { + .rtgen_family = AF_UNSPEC, + }; + int ret; + int retry = 1; + + if (!(sys = system_get_instance())) + return MCLAG_ERROR; + + while (retry) + { + retry = 0; + ret = nl_send_simple(sys->route_sock, RTM_GETADDR, NLM_F_DUMP, + &rt_hdr, sizeof(rt_hdr)); + + if (ret < 0) + { + ICCPD_LOG_ERR(__FUNCTION__, "send netlink msg error."); + return ret; + } + + orig_cb = nl_socket_get_cb(sys->route_sock); + cb = nl_cb_clone(orig_cb); + nl_cb_put(orig_cb); + if (!cb) + { + ICCPD_LOG_ERR(__FUNCTION__, "nl cb clone error."); + return -ENOMEM; + } + + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, iccp_local_if_addr_update, sys); + ret = nl_recvmsgs(sys->route_sock, cb); + nl_cb_put(cb); + if (ret < 0) + { + ICCPD_LOG_ERR(__FUNCTION__, "Receive netlink msg error, ret = %d errno = %d .", ret, errno); + if (ret != -NLE_DUMP_INTR) + return ret; + retry = 1; + } + } + + return ret; +} + +static int iccp_route_event_handler(struct nl_msg *msg, void *arg) +{ + struct nlmsghdr *nlh = nlmsg_hdr(msg); + unsigned int event = 1; + + switch (nlh->nlmsg_type) + { + case RTM_NEWLINK: + if (nl_msg_parse(msg, &iccp_event_handler_obj_input_newlink, &event) < 0) + ICCPD_LOG_DEBUG(__FUNCTION__, "Unknown message type(RTM_NEWLINK)"); + iccp_parse_if_vlan_info_from_netlink(nlh); + break; + + case RTM_DELLINK: + if (nl_msg_parse(msg, &iccp_event_handler_obj_input_dellink, NULL) < 0) + ICCPD_LOG_DEBUG(__FUNCTION__, "Unknown message type(RTM_DELLINK)"); + break; + + case RTM_NEWNEIGH: + case RTM_DELNEIGH: + do_one_neigh_request(nlh); + break; + + case RTM_NEWADDR: + iccp_local_if_addr_update(msg, NULL); + break; + + default: + return NL_OK; + } + + return NL_STOP; +} + +/** + * SECTION: Context functions + */ +static int iccp_genric_event_handler(struct nl_msg *msg, void *arg) +{ + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + + switch (gnlh->cmd) + { + case TEAM_CMD_PORT_LIST_GET: + return iccp_get_portchannel_member_list_handler(msg, NULL); + } + + return NL_SKIP; +} + +/*init netlink socket*/ +int iccp_system_init_netlink_socket() +{ + struct System* sys = NULL; + int val = 0; + int err = 0; + + if ((sys = system_get_instance()) == NULL ) + return MCLAG_ERROR; + + sys->genric_sock = nl_socket_alloc(); + if (!sys->genric_sock) + goto err_genric_sock_alloc; + + sys->genric_sock_seq = time(NULL); + err = genl_connect(sys->genric_sock); + if (err) + { + ICCPD_LOG_ERR(__FUNCTION__, "Failed to connect to netlink sock sys->genric_sock."); + goto err_genric_sock_connect; + } + + sys->genric_event_sock = nl_socket_alloc(); + if (!sys->genric_event_sock) + goto err_genric_event_sock_alloc; + + err = genl_connect(sys->genric_event_sock); + if (err) + { + ICCPD_LOG_ERR(__FUNCTION__, "Failed to connect to netlink sys->genric_event_sock."); + goto err_genric_event_sock_connect; + } + + sys->route_sock = nl_socket_alloc(); + if (!sys->route_sock) + goto err_route_sock_alloc; + err = nl_connect(sys->route_sock, NETLINK_ROUTE); + if (err) + goto err_route_sock_connect; + + err = nl_socket_set_buffer_size(sys->route_sock, 98304, 0); + if (err) + { + ICCPD_LOG_ERR(__FUNCTION__, "Failed to set buffer size of netlink route event sock."); + goto err_route_sock_connect; + } + + sys->route_event_sock = nl_socket_alloc(); + if (!sys->route_event_sock) + goto err_route_event_sock_alloc; + + err = nl_connect(sys->route_event_sock, NETLINK_ROUTE); + if (err) + { + ICCPD_LOG_ERR(__FUNCTION__, "Failed to connect to netlink sys->route_event_sock. "); + goto err_route_event_sock_connect; + } + + err = nl_socket_set_buffer_size(sys->route_event_sock, 983040, 0); + if (err) + { + ICCPD_LOG_ERR(__FUNCTION__, "Failed to set buffer size of netlink route event sock."); + goto err_route_event_sock_connect; + } + + val = NETLINK_BROADCAST_SEND_ERROR; + err = setsockopt(nl_socket_get_fd(sys->genric_event_sock), SOL_NETLINK, + NETLINK_BROADCAST_ERROR, &val, sizeof(val)); + if (err) + { + ICCPD_LOG_ERR(__FUNCTION__, "Failed set NETLINK_BROADCAST_ERROR on netlink event sock."); + goto err_return; + } + + err = nl_socket_set_buffer_size(sys->genric_sock, 98304, 0); + if (err) + { + ICCPD_LOG_ERR(__FUNCTION__, "Failed to set buffer size of netlink sock."); + goto err_return; + } + + err = nl_socket_set_buffer_size(sys->genric_event_sock, 98304, 0); + if (err) + { + ICCPD_LOG_ERR(__FUNCTION__, "Failed to set buffer size of netlink event sock."); + goto err_return; + } +#if 0 + sys->family = genl_ctrl_resolve(sys->genric_sock, TEAM_GENL_NAME); + + while (sys->family < 0) + { + sleep(1); + log_err_period++; + /*If no portchannel configuration, teamd will not started, genl_ctrl_resolve() will return <0 forever */ + /*Only log error message 5 times*/ + if (log_err_period == 1 && log_err_time < 5) + { + ICCPD_LOG_ERR(__FUNCTION__, "Failed to resolve netlink family. %d of TEAM_GENL_NAME %s ", sys->family, TEAM_GENL_NAME); + log_err_time++; + } + else + { + /*Log error message every 30s per time*/ + if (log_err_period == 30) + log_err_period = 0; + } + + sys->family = genl_ctrl_resolve(sys->genric_sock, TEAM_GENL_NAME); + } + + grp_id = genl_ctrl_resolve_grp(sys->genric_sock, TEAM_GENL_NAME, + TEAM_GENL_CHANGE_EVENT_MC_GRP_NAME); + if (grp_id < 0) + { + ICCPD_LOG_ERR(__FUNCTION__, "Failed to resolve netlink multicast groups. %d", grp_id); + goto err_return; + } + + err = nl_socket_add_membership(sys->genric_event_sock, grp_id); + if (err < 0) + { + ICCPD_LOG_ERR(__FUNCTION__, "Failed to add netlink membership."); + goto err_return; + } +#endif + nl_socket_disable_seq_check(sys->genric_event_sock); + nl_socket_modify_cb(sys->genric_event_sock, NL_CB_VALID, NL_CB_CUSTOM, + iccp_genric_event_handler, sys); + + nl_socket_disable_seq_check(sys->route_event_sock); + nl_socket_modify_cb(sys->route_event_sock, NL_CB_VALID, NL_CB_CUSTOM, + iccp_route_event_handler, sys); + + err = nl_socket_add_membership(sys->route_event_sock, RTNLGRP_NEIGH); + if (err < 0) + { + ICCPD_LOG_ERR(__FUNCTION__, "Failed to add netlink membership."); + goto err_return; + } + + err = nl_socket_add_membership(sys->route_event_sock, RTNLGRP_LINK); + if (err < 0) + { + ICCPD_LOG_ERR(__FUNCTION__, "Failed to add netlink membership."); + goto err_return; + } + + err = nl_socket_add_membership(sys->route_event_sock, RTNLGRP_IPV4_IFADDR); + if (err < 0) + { + ICCPD_LOG_ERR(__FUNCTION__, "Failed to add netlink membership."); + goto err_return; + } + + /*receive arp packet socket*/ + sys->arp_receive_fd = socket(PF_PACKET, SOCK_DGRAM, 0); + if (sys->arp_receive_fd < 0) + { + ICCPD_LOG_ERR(__FUNCTION__, "socket error "); + goto err_return; + } + + if (1) + { + struct sockaddr_ll sll; + memset(&sll, 0, sizeof(sll)); + sll.sll_family = AF_PACKET; + sll.sll_protocol = htons(ETH_P_ARP); + sll.sll_ifindex = 0; + if (bind(sys->arp_receive_fd, (struct sockaddr*)&sll, sizeof(sll)) < 0) + { + ICCPD_LOG_ERR(__FUNCTION__, "socket bind error"); + goto err_return; + } + } + + goto succes_return; + + err_return: + + err_route_event_sock_connect: + nl_socket_free(sys->route_event_sock); + + err_route_sock_alloc: + err_route_sock_connect: + nl_socket_free(sys->route_sock); + + err_route_event_sock_alloc: + err_genric_event_sock_connect: + nl_socket_free(sys->genric_event_sock); + + err_genric_event_sock_alloc: + err_genric_sock_connect: + nl_socket_free(sys->genric_sock); + + return err; + + err_genric_sock_alloc: + + succes_return: + return 0; +} + +void iccp_system_dinit_netlink_socket() +{ + struct System* sys = NULL; + + if ((sys = system_get_instance()) == NULL ) + return; + + nl_socket_free(sys->route_event_sock); + nl_socket_free(sys->route_sock); + nl_socket_free(sys->genric_event_sock); + nl_socket_free(sys->genric_sock); + return; +} + +static int iccp_get_netlink_genic_sock_event_fd(struct System *sys) +{ + return nl_socket_get_fd(sys->genric_event_sock); +} + +static int iccp_netlink_genic_sock_event_handler(struct System *sys) +{ + int ret = 0; + + ret = nl_recvmsgs_default(sys->genric_event_sock); + if (ret) + { + sys->need_sync_team_again = 1; + ICCPD_LOG_DEBUG(__FUNCTION__, "genric_event_sock %d recvmsg error ret = %d ", nl_socket_get_fd(sys->genric_event_sock), ret); + } + + return ret; +} + +static int iccp_get_netlink_route_sock_event_fd(struct System *sys) +{ + return nl_socket_get_fd(sys->route_event_sock); +} + +static int iccp_get_receive_arp_packet_sock_fd(struct System *sys) +{ + return sys->arp_receive_fd; +} + +static int iccp_receive_arp_packet_handler(struct System *sys) +{ + unsigned char buf[1024]; + struct sockaddr_ll sll; + socklen_t sll_len = sizeof(sll); + struct arphdr *a = (struct arphdr*)buf; + int n; + unsigned int ifindex; + unsigned int addr; + uint8_t mac_addr[ETHER_ADDR_LEN]; + + n = recvfrom(sys->arp_receive_fd, buf, sizeof(buf), MSG_DONTWAIT, + (struct sockaddr*)&sll, &sll_len); + if (n < 0) + { + ICCPD_LOG_WARN(__FUNCTION__, "ARP recvfrom error: %s", buf); + return MCLAG_ERROR; + } + + /* Sanity checks */ + /*Only process ARPOP_REPLY*/ + if (n < sizeof(*a) || + a->ar_op != htons(ARPOP_REPLY) || + a->ar_pln != 4 || + a->ar_pro != htons(ETH_P_IP) || + a->ar_hln != sll.sll_halen || + sizeof(*a) + 2 * 4 + 2 * a->ar_hln > n) + return 0; + + ifindex = sll.sll_ifindex; + memcpy(mac_addr, (char*)(a + 1), ETHER_ADDR_LEN); + memcpy(&addr, (char*)(a + 1) + a->ar_hln, 4); + + do_arp_update_from_reply_packet(ifindex, ntohl(addr), mac_addr); + + return 0; +} + +void iccp_netlink_sync_again() +{ + struct System* sys = NULL; + struct LocalInterface* lif = NULL; + + if ((sys = system_get_instance()) == NULL ) + return; + + if (sys->need_sync_netlink_again) + { + sys->need_sync_netlink_again = 0; + + /*Get kernel interface and port */ + iccp_sys_local_if_list_get_init(); + } + + if (sys->need_sync_team_again) + { + sys->need_sync_team_again = 0; + + LIST_FOREACH(lif, &(sys->lif_list), system_next) + { + if (lif->type == IF_T_PORT_CHANNEL) + { + iccp_get_port_member_list(lif); + } + } + } + + return; +} + +static int iccp_netlink_route_sock_event_handler(struct System *sys) +{ + int ret = 0; + + ret = nl_recvmsgs_default(sys->route_event_sock); + + if (ret) + { + sys->need_sync_netlink_again = 1; + ICCPD_LOG_DEBUG(__FUNCTION__, "fd %d recvmsg error ret = %d errno = %d ", nl_socket_get_fd(sys->route_event_sock), ret, errno); + } + /*get netlink info again when error happens */ + if (ret == 0 && sys->need_sync_netlink_again == 1) + { + iccp_netlink_sync_again(); + } + + return ret; +} + +extern int iccp_get_receive_fdb_sock_fd(struct System *sys); +extern int iccp_receive_fdb_handler_from_syncd(struct System *sys); + +/* cond HIDDEN_SYMBOLS */ +struct iccp_eventfd +{ + int (*get_fd)(struct System* sys); + int (*event_handler)(struct System* sys); +}; +/* endcond */ + +static const struct iccp_eventfd iccp_eventfds[] = { + { + .get_fd = iccp_get_server_sock_fd, + .event_handler = scheduler_server_accept, + }, + { + .get_fd = iccp_get_netlink_genic_sock_event_fd, + .event_handler = iccp_netlink_genic_sock_event_handler, + }, + { + .get_fd = iccp_get_netlink_route_sock_event_fd, + .event_handler = iccp_netlink_route_sock_event_handler, + }, + { + .get_fd = iccp_get_receive_arp_packet_sock_fd, + .event_handler = iccp_receive_arp_packet_handler, + } +}; + +/* \cond HIDDEN_SYMBOLS */ +#define ICCP_EVENT_FDS_COUNT ARRAY_SIZE(iccp_eventfds) +/* \endcond */ +/* + @return fd. + * + **/ + +int iccp_get_eventfd_fd(struct System *sys) +{ + return sys->epoll_fd; +} + +int iccp_init_netlink_event_fd(struct System *sys) +{ + int efd; + int i; + struct epoll_event event; + int err; + + efd = epoll_create1(0); + if (efd == -1) + return -errno; + + for (i = 0; i < ICCP_EVENT_FDS_COUNT; i++) + { + int fd = iccp_eventfds[i].get_fd(sys); + + event.data.fd = fd; + event.events = EPOLLIN; + err = epoll_ctl(efd, EPOLL_CTL_ADD, fd, &event); + if (err == -1) + { + err = -errno; + goto close_efd; + } + } + + sys->epoll_fd = efd; + + return 0; + + close_efd: + close(efd); + + return err; +} + +/** + * + * @details Handler events which happened on event filedescriptor. + * + * @return Zero on success or negative number in case of an error. + **/ + +int iccp_handle_events(struct System * sys) +{ + struct epoll_event events[ICCP_EVENT_FDS_COUNT + sys->readfd_count]; + struct CSM* csm = NULL; + int nfds; + int n; + int i; + int err; + int max_nfds; + + max_nfds = ICCP_EVENT_FDS_COUNT + sys->readfd_count; + + nfds = epoll_wait(sys->epoll_fd, events, max_nfds, EPOLL_TIMEOUT_MSEC); + + /* Go over list of event fds and handle them sequentially */ + for (i = 0; i < nfds; i++) + { + for (n = 0; n < ICCP_EVENT_FDS_COUNT; n++) + { + const struct iccp_eventfd *eventfd = &iccp_eventfds[n]; + if (events[i].data.fd == eventfd->get_fd(sys)) + { + err = eventfd->event_handler(sys); + if (err) + ICCPD_LOG_INFO(__FUNCTION__, "Scheduler fd %d handler error %d !", events[i].data.fd, err ); + break; + } + } + + if (n < ICCP_EVENT_FDS_COUNT) + continue; + + if (events[i].data.fd == sys->sync_ctrl_fd) + { + int client_fd = mclagd_ctl_sock_accept(sys->sync_ctrl_fd); + if (client_fd > 0) + { + mclagd_ctl_interactive_process(client_fd); + close(client_fd); + } + continue; + } + + if (events[i].data.fd == sys->sync_fd) + { + iccp_receive_fdb_handler_from_syncd(sys); + + continue; + } + + if (events[i].data.fd == sys->sig_pipe_r) + { + iccp_receive_signal_handler(sys); + + continue; + } + + if (FD_ISSET(events[i].data.fd, &sys->readfd)) + { + LIST_FOREACH(csm, &(sys->csm_list), next) + { + if (csm->sock_fd == events[i].data.fd ) + { + scheduler_csm_read_callback(csm); + break; + } + } + } + } + + return 0; +} + diff --git a/src/iccpd/src/logger.c b/src/iccpd/src/logger.c new file mode 100644 index 000000000000..2c17610f7afc --- /dev/null +++ b/src/iccpd/src/logger.c @@ -0,0 +1,140 @@ +/* + * logger.c + * + * Copyright(c) 2016-2019 Nephos/Estinet. + * + * 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, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintainer: jianjun, grace Li from nephos + */ +#include +#include +#include +#include + +#include "../include/cmd_option.h" +#include "../include/logger.h" + +static uint32_t _iccpd_log_level_map[] = +{ + LOG_CRIT, + LOG_ERR, + LOG_WARNING, + LOG_NOTICE, + LOG_INFO, + LOG_DEBUG, +}; + +char* log_level_to_string(int level) +{ + switch (level) + { + case CRITICAL_LOG_LEVEL: + return "CRITICAL"; + + case ERR_LOG_LEVEL: + return "ERROR"; + + case WARN_LOG_LEVEL: + return "WARN"; + + case NOTICE_LOG_LEVEL: + return "NOTICE"; + + case INFO_LOG_LEVEL: + return "INFO"; + + case DEBUG_LOG_LEVEL: + return "DEBUG"; + } + + return "INFO"; +} + +struct LoggerConfig* logger_get_configuration() +{ + static struct LoggerConfig config; + + if (config.init == 0) + { + config.console_log_enabled = 0; + config.log_level = NOTICE_LOG_LEVEL; + config.init = 1; + } + + return &config; +} + +void logger_set_configuration(int log_level) +{ + struct LoggerConfig* config = logger_get_configuration(); + + config->log_level = log_level; + config->init = 1; + + return; +} + +void log_init(struct CmdOptionParser* parser) +{ + struct LoggerConfig* config = logger_get_configuration(); + + config->console_log_enabled = parser->console_log; +} + +void log_finalize() +{ + /*do nothing*/ +} + +void write_log(const int level, const char* tag, const char* format, ...) +{ + struct LoggerConfig* config = logger_get_configuration(); + char buf[LOGBUF_SIZE]; + va_list args; + unsigned int prefix_len; + unsigned int avbl_buf_len; + unsigned int print_len; + +#if 0 + if (!config->console_log_enabled) + return; +#endif + + if (level > config->log_level) + return; + + prefix_len = snprintf(buf, LOGBUF_SIZE, "[%s.%s] ", tag, log_level_to_string(level)); + avbl_buf_len = LOGBUF_SIZE - prefix_len; + + va_start(args, format); + print_len = vsnprintf(buf + prefix_len, avbl_buf_len, format, args); + va_end(args); + + /* Since osal_vsnprintf doesn't always return the exact size written to the buffer, + * we must check if the user string length exceeds the remaing buffer size. + */ + if (print_len > avbl_buf_len) + { + print_len = avbl_buf_len; + } + + buf[prefix_len + print_len] = '\0'; + ICCPD_UTILS_SYSLOG(_iccpd_log_level_map[level], "%s", buf); + + return; +} + diff --git a/src/iccpd/src/mclagdctl/Makefile b/src/iccpd/src/mclagdctl/Makefile new file mode 100644 index 000000000000..5520da22cee2 --- /dev/null +++ b/src/iccpd/src/mclagdctl/Makefile @@ -0,0 +1,28 @@ + + +C = gcc +SOURCES = \ +mclagdctl.c + +OBJECTS = \ +mclagdctl.o + +HEADERS = \ +mclagdctl.h + +CFLAGS = -g -O2 -fstack-protector-strong -Wformat -Werror=format-security +LDFLAGS = +LDADD = + +all: mclagdctl + +%.o: %.c $(HEADERS) + +mclagdctl: $(OBJECTS) + $(CC) -o ./mclagdctl $(OBJECTS) $(LDFLAGS) + +.PHONY: clean +clean: + -rm -f $(OBJECTS) ./mclagdctl + + diff --git a/src/iccpd/src/mclagdctl/mclagdctl.c b/src/iccpd/src/mclagdctl/mclagdctl.c new file mode 100644 index 000000000000..7c5d7284066d --- /dev/null +++ b/src/iccpd/src/mclagdctl/mclagdctl.c @@ -0,0 +1,883 @@ +/* + /* Copyright(c) 2016-2019 Nephos. + * + * 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, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintainer: Jim Jiang from nephos + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "mclagdctl.h" + +static int mclagdctl_sock_fd = -1; +char *mclagdctl_sock_path = "/var/run/iccpd/mclagdctl.sock"; + +/* + Already implemented command: + mclagdctl -i dump state + mclagdctl -i dump arp + mclagdctl -i dump mac + mclagdctl -i dump portlist local + mclagdctl -i dump portlist peer + */ + +static struct command_type command_types[] = +{ + { + .id = ID_CMDTYPE_D, + .name = "dump", + .enca_msg = NULL, + .parse_msg = NULL, + }, + { + .id = ID_CMDTYPE_D_S, + .parent_id = ID_CMDTYPE_D, + .info_type = INFO_TYPE_DUMP_STATE, + .name = "state", + .enca_msg = mclagdctl_enca_dump_state, + .parse_msg = mclagdctl_parse_dump_state, + }, + { + .id = ID_CMDTYPE_D_A, + .parent_id = ID_CMDTYPE_D, + .info_type = INFO_TYPE_DUMP_ARP, + .name = "arp", + .enca_msg = mclagdctl_enca_dump_arp, + .parse_msg = mclagdctl_parse_dump_arp, + }, + { + .id = ID_CMDTYPE_D_A, + .parent_id = ID_CMDTYPE_D, + .info_type = INFO_TYPE_DUMP_MAC, + .name = "mac", + .enca_msg = mclagdctl_enca_dump_mac, + .parse_msg = mclagdctl_parse_dump_mac, + }, + { + .id = ID_CMDTYPE_D_P, + .parent_id = ID_CMDTYPE_D, + .name = "portlist", + }, + { + .id = ID_CMDTYPE_D_P_L, + .parent_id = ID_CMDTYPE_D_P, + .info_type = INFO_TYPE_DUMP_LOCAL_PORTLIST, + .name = "local", + .enca_msg = mclagdctl_enca_dump_local_portlist, + .parse_msg = mclagdctl_parse_dump_local_portlist, + }, + { + .id = ID_CMDTYPE_D_P_P, + .parent_id = ID_CMDTYPE_D_P, + .info_type = INFO_TYPE_DUMP_PEER_PORTLIST, + .name = "peer", + .enca_msg = mclagdctl_enca_dump_peer_portlist, + .parse_msg = mclagdctl_parse_dump_peer_portlist, + }, + { + .id = ID_CMDTYPE_C, + .name = "config", + .enca_msg = NULL, + .parse_msg = NULL, + }, + { + .id = ID_CMDTYPE_C_L, + .parent_id = ID_CMDTYPE_C, + .info_type = INFO_TYPE_CONFIG_LOGLEVEL, + .name = "loglevel", + .enca_msg = mclagdctl_enca_config_loglevel, + .parse_msg = mclagdctl_parse_config_loglevel, + }, +}; + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#define COMMAND_TYPE_COUNT ARRAY_SIZE(command_types) + +int mclagdctl_sock_connect() +{ + struct sockaddr_un addr; + int addrlen = 0; + int ret = 0; + + if (mclagdctl_sock_fd >= 0) + return 0; + + if (strlen(mclagdctl_sock_path) <= 0) + return MCLAG_ERROR; + + mclagdctl_sock_fd = socket(AF_UNIX, SOCK_STREAM, 0); + + if (mclagdctl_sock_fd < 0) + { + return MCLAG_ERROR; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + snprintf(addr.sun_path, sizeof(addr.sun_path) - 1, "%s", mclagdctl_sock_path); + addrlen = sizeof(addr.sun_family) + strlen(mclagdctl_sock_path); + + if ((ret = connect(mclagdctl_sock_fd, (struct sockaddr*)&addr, addrlen)) < 0) + { + close(mclagdctl_sock_fd); + mclagdctl_sock_fd = -1; + return MCLAG_ERROR; + } + + return 0; +} + +void mclagdctl_sock_close() +{ + if (mclagdctl_sock_fd > 0) + { + close(mclagdctl_sock_fd); + mclagdctl_sock_fd = -1; + } + + return; +} + +int mclagdctl_sock_write(int fd, unsigned char *w_buf, int total_len) +{ + int write_len = 0; + int ret = 0; + + while (write_len < total_len) + { + ret = write(fd, w_buf + write_len, total_len - write_len); + if (ret <= 0) + { + return 0; + } + write_len += ret; + } + + return write_len; +} + +int mclagdctl_sock_read(int fd, unsigned char *r_buf, int total_len) +{ + int read_len = 0; + int ret = 0; + struct timeval tv = { 0 }; + fd_set read_fd; + + while (read_len < total_len) + { + FD_ZERO(&read_fd); + FD_SET(fd, &read_fd); + tv.tv_sec = 10; + tv.tv_usec = 0; + + switch ((ret = select(fd + 1, &read_fd, NULL, NULL, &tv))) + { + case -1: // error + fprintf(stdout, "Mclagdctl:Select return error:%s\n", strerror(errno)); + return MCLAG_ERROR; + + case 0: // timeout + fprintf(stdout, "Mclagdctl:Select timeout:%s\n", strerror(errno)); + return MCLAG_ERROR; + + default: + break; + } + + ret = read(fd, r_buf + read_len, total_len - read_len); + if (ret <= 0) + { + return MCLAG_ERROR; + } + read_len += ret; + } + + return read_len; +} + +int mclagdctl_enca_dump_state(char *msg, int mclag_id, int argc, char **argv) +{ + struct mclagdctl_req_hdr req; + + memset(&req, 0, sizeof(struct mclagdctl_req_hdr)); + req.info_type = INFO_TYPE_DUMP_STATE; + req.mclag_id = mclag_id; + memcpy((struct mclagdctl_req_hdr *)msg, &req, sizeof(struct mclagdctl_req_hdr)); + + return 1; +} + +int mclagdctl_parse_dump_state(char *msg, int data_len) +{ + struct mclagd_state * state_info = NULL; + int len = 0; + int count = 0; + int pos = 0; + + len = sizeof(struct mclagd_state); + + for (; data_len >= len; data_len -= len, count++) + { + state_info = (struct mclagd_state*)(msg + len * count); + + fprintf(stdout, "%s: %s\n", "The MCLAG's keepalive is", state_info->keepalive ? "OK" : "ERROR"); + + if (state_info->mclag_id <= 0) + fprintf(stdout, "%s: %s\n", "Domain id", "Unknown"); + else + fprintf(stdout, "%s: %d\n", "Domain id", state_info->mclag_id); + + fprintf(stdout, "%s: %s\n", "Local Ip", state_info->local_ip); + fprintf(stdout, "%s: %s\n", "Peer Ip", state_info->peer_ip); + fprintf(stdout, "%s: %s\n", "Peer Link Interface", state_info->peer_link_if); + + fprintf(stdout, "%s: %02x:%02x:%02x:%02x:%02x:%02x \n", + "Peer Link Mac", + state_info->peer_link_mac[0], state_info->peer_link_mac[1], + state_info->peer_link_mac[2], state_info->peer_link_mac[3], + state_info->peer_link_mac[4], state_info->peer_link_mac[5]); + + if (state_info->role == 0) + fprintf(stdout, "%s: %s\n", "Role", "None"); + else if (state_info->role == 1) + fprintf(stdout, "%s: %s\n", "Role", "Active"); + else if (state_info->role == 2) + fprintf(stdout, "%s: %s\n", "Role", "Standby"); + + fprintf(stdout, "%s: %s\n", "MCLAG Interface", state_info->enabled_po); + + fprintf(stdout, "%s: %s\n", "Loglevel", state_info->loglevel); + } + + return 0; +} + +int mclagdctl_enca_dump_arp(char *msg, int mclag_id, int argc, char **argv) +{ + struct mclagdctl_req_hdr req; + + if (mclag_id <= 0) + { + fprintf(stderr, "Need to specify mclag-id through the parameter i !\n"); + return MCLAG_ERROR; + } + + memset(&req, 0, sizeof(struct mclagdctl_req_hdr)); + req.info_type = INFO_TYPE_DUMP_ARP; + req.mclag_id = mclag_id; + memcpy((struct mclagdctl_req_hdr *)msg, &req, sizeof(struct mclagdctl_req_hdr)); + + return 1; +} + +int mclagdctl_parse_dump_arp(char *msg, int data_len) +{ + struct mclagd_arp_msg * arp_info = NULL; + int len = 0; + int count = 0; + + fprintf(stdout, "%-6s", "No."); + fprintf(stdout, "%-20s", "IP"); + fprintf(stdout, "%-20s", "MAC"); + fprintf(stdout, "%-20s", "DEV"); + fprintf(stdout, "\n"); + + len = sizeof(struct mclagd_arp_msg); + + for (; data_len >= len; data_len -= len, count++) + { + arp_info = (struct mclagd_arp_msg*)(msg + len * count); + + fprintf(stdout, "%-6d", count + 1); + fprintf(stdout, "%-20s", arp_info->ipv4_addr); + fprintf(stdout, "%02x:%02x:%02x:%02x:%02x:%02x", + arp_info->mac_addr[0], arp_info->mac_addr[1], + arp_info->mac_addr[2], arp_info->mac_addr[3], + arp_info->mac_addr[4], arp_info->mac_addr[5]); + fprintf(stdout, " "); + fprintf(stdout, "%-20s", arp_info->ifname); + fprintf(stdout, "\n"); + } + + return 0; +} + +int mclagdctl_enca_dump_mac(char *msg, int mclag_id, int argc, char **argv) +{ + struct mclagdctl_req_hdr req; + + if (mclag_id <= 0) + { + fprintf(stderr, "Need to specify mclag-id through the parameter i !\n"); + return MCLAG_ERROR; + } + + memset(&req, 0, sizeof(struct mclagdctl_req_hdr)); + req.info_type = INFO_TYPE_DUMP_MAC; + req.mclag_id = mclag_id; + memcpy((struct mclagdctl_req_hdr *)msg, &req, sizeof(struct mclagdctl_req_hdr)); + + return 1; +} + +int mclagdctl_parse_dump_mac(char *msg, int data_len) +{ + struct mclagd_mac_msg * mac_info = NULL; + int len = 0; + int count = 0; + + fprintf(stdout, "%-60s\n", "TYPE: S-STATIC, D-DYNAMIC; AGE: L-Local age, P-Peer age"); + + fprintf(stdout, "%-6s", "No."); + fprintf(stdout, "%-5s", "TYPE"); + fprintf(stdout, "%-20s", "MAC"); + fprintf(stdout, "%-5s", "VID"); + fprintf(stdout, "%-20s", "DEV"); + fprintf(stdout, "%-20s", "ORIGIN-DEV"); + fprintf(stdout, "%-5s", "AGE"); + fprintf(stdout, "\n"); + + len = sizeof(struct mclagd_mac_msg); + + for (; data_len >= len; data_len -= len, count++) + { + mac_info = (struct mclagd_mac_msg*)(msg + len * count); + + fprintf(stdout, "%-6d", count + 1); + + if (mac_info->fdb_type == MAC_TYPE_STATIC_CTL) + fprintf(stdout, "%-5s", "S"); + else + fprintf(stdout, "%-5s", "D"); + + fprintf(stdout, "%-20s", mac_info->mac_str); + fprintf(stdout, "%-5d", mac_info->vid); + fprintf(stdout, "%-20s", mac_info->ifname); + fprintf(stdout, "%-20s", mac_info->origin_ifname); + + if ((mac_info->age_flag & MAC_AGE_LOCAL_CTL) && (mac_info->age_flag & MAC_AGE_PEER_CTL)) + fprintf(stdout, "%-5s", "LP"); + else if (mac_info->age_flag & MAC_AGE_LOCAL_CTL) + fprintf(stdout, "%-5s", "L"); + else if (mac_info->age_flag & MAC_AGE_PEER_CTL) + fprintf(stdout, "%-5s", "P"); + else + fprintf(stdout, "%-5s", " "); + fprintf(stdout, "\n"); + } + + return 0; +} + +int mclagdctl_enca_dump_local_portlist(char *msg, int mclag_id, int argc, char **argv) +{ + struct mclagdctl_req_hdr req; + + if (mclag_id <= 0) + { + fprintf(stderr, "Need to specify mclag-id through the parameter i !\n"); + return MCLAG_ERROR; + } + + memset(&req, 0, sizeof(struct mclagdctl_req_hdr)); + req.info_type = INFO_TYPE_DUMP_LOCAL_PORTLIST; + req.mclag_id = mclag_id; + memcpy((struct mclagdctl_req_hdr *)msg, &req, sizeof(struct mclagdctl_req_hdr)); + + return 1; +} + +int mclagdctl_parse_dump_local_portlist(char *msg, int data_len) +{ + struct mclagd_local_if * lif_info = NULL; + int len = 0; + int count = 0; + int pos = 0; + + len = sizeof(struct mclagd_local_if); + + for (; data_len >= len; data_len -= len, count++) + { + lif_info = (struct mclagd_local_if*)(msg + len * count); + + for (pos = 0; pos < 60; ++pos) + fprintf(stdout, "-"); + + fprintf(stdout, "\n"); + + if (memcmp(lif_info->type, "PortChannel", 11) == 0) + { + fprintf(stdout, "%s: %d\n", "Ifindex", lif_info->ifindex); + fprintf(stdout, "%s: %s\n", "Type", lif_info->type); + fprintf(stdout, "%s: %s\n", "PortName", lif_info->name); + fprintf(stdout, "%s: %02x:%02x:%02x:%02x:%02x:%02x \n", + "MAC", + lif_info->mac_addr[0], lif_info->mac_addr[1], + lif_info->mac_addr[2], lif_info->mac_addr[3], + lif_info->mac_addr[4], lif_info->mac_addr[5]); + + fprintf(stdout, "%s: %s\n", "IPv4Address", lif_info->ipv4_addr); + fprintf(stdout, "%s: %d\n", "Prefixlen", lif_info->prefixlen); + fprintf(stdout, "%s: %s\n", "State", lif_info->state); + fprintf(stdout, "%s: %s\n", "IsL3Interface", lif_info->l3_mode ? "Yes" : "No"); + /*fprintf(stdout, "%s: %s\n", "IsPeerlink", lif_info->is_peer_link ? "Yes" : "No");*/ + fprintf(stdout, "%s: %s\n", "MemberPorts", lif_info->portchannel_member_buf); + /*fprintf(stdout,"%s: %d\n" ,"PortchannelId", lif_info->po_id); + fprintf(stdout,"%s: %d\n" ,"PortchannelIsUp", lif_info->po_active); + fprintf(stdout,"%s: %s\n", "MlacpState", lif_info->mlacp_state);*/ + fprintf(stdout, "%s: %s\n", "IsIsolateWithPeerlink", lif_info->isolate_to_peer_link ? "Yes" : "No"); + fprintf(stdout, "%s: %s\n", "VlanList", lif_info->vlanlist); + } + else + { + fprintf(stdout, "%s: %d\n", "Ifindex", lif_info->ifindex); + fprintf(stdout, "%s: %s\n", "Type", lif_info->type); + fprintf(stdout, "%s: %s\n", "PortName", lif_info->name); + fprintf(stdout, "%s: %s\n", "State", lif_info->state); + /*fprintf(stdout,"%s: %d\n" ,"PortchannelId", lif_info->po_id);*/ + } + + for (pos = 0; pos < 60; ++pos) + fprintf(stdout, "-"); + + fprintf(stdout, "\n\n"); + } + + return 0; +} + +int mclagdctl_enca_dump_peer_portlist(char *msg, int mclag_id, int argc, char **argv) +{ + struct mclagdctl_req_hdr req; + + if (mclag_id <= 0) + { + fprintf(stderr, "Need to specify mclag-id through the parameter i !\n"); + return MCLAG_ERROR; + } + + memset(&req, 0, sizeof(struct mclagdctl_req_hdr)); + req.info_type = INFO_TYPE_DUMP_PEER_PORTLIST; + req.mclag_id = mclag_id; + memcpy((struct mclagdctl_req_hdr *)msg, &req, sizeof(struct mclagdctl_req_hdr)); + + return 1; +} + +int mclagdctl_parse_dump_peer_portlist(char *msg, int data_len) +{ + struct mclagd_peer_if * pif_info = NULL; + int len = 0; + int count = 0; + int pos = 0; + + len = sizeof(struct mclagd_peer_if); + + for (; data_len >= len; data_len -= len, count++) + { + pif_info = (struct mclagd_peer_if*)(msg + len * count); + + for (pos = 0; pos < 60; ++pos) + fprintf(stdout, "-"); + + fprintf(stdout, "\n"); + + fprintf(stdout, "%s: %d\n", "Ifindex", pif_info->ifindex); + fprintf(stdout, "%s: %s\n", "Type", pif_info->type); + fprintf(stdout, "%s: %s\n", "PortName", pif_info->name); + fprintf(stdout, "%s: %02x:%02x:%02x:%02x:%02x:%02x \n", + "MAC", + pif_info->mac_addr[0], pif_info->mac_addr[1], + pif_info->mac_addr[2], pif_info->mac_addr[3], + pif_info->mac_addr[4], pif_info->mac_addr[5]); + + fprintf(stdout, "%s: %s\n", "State", pif_info->state); + /*fprintf(stdout,"%s: %d\n" ,"PortchannelId", pif_info->po_id); + fprintf(stdout,"%s: %d\n" ,"PortchannelIsActive", pif_info->po_active);*/ + + for (pos = 0; pos < 60; ++pos) + fprintf(stdout, "-"); + + fprintf(stdout, "\n\n"); + } + + return 0; +} + +int mclagdctl_enca_config_loglevel(char *msg, int log_level, int argc, char **argv) +{ + struct mclagdctl_req_hdr req; + + memset(&req, 0, sizeof(struct mclagdctl_req_hdr)); + req.info_type = INFO_TYPE_CONFIG_LOGLEVEL; + req.mclag_id = log_level; + memcpy((struct mclagdctl_req_hdr *)msg, &req, sizeof(struct mclagdctl_req_hdr)); + + return 1; +} + +int mclagdctl_parse_config_loglevel(char *msg, int data_len) +{ + + int ret = *(int*)msg; + + if (ret == 0) + fprintf(stdout, "%s\n", "Config loglevel success!"); + else + fprintf(stdout, "%s\n", "Config loglevel failed!"); + + return 0; +} + +static bool __mclagdctl_cmd_executable(struct command_type *cmd_type) +{ + if (!cmd_type->enca_msg || !cmd_type->parse_msg) + return 0; + + return 1; +} + +static int __mclagdctl_cmd_param_cnt(struct command_type *cmd_type) +{ + int i = 0; + + while (cmd_type->params[i]) + i++; + + return i; +} + +static struct command_type *__mclagdctl_get_cmd_by_parent(char *cmd_name, + enum id_command_type parent_id) +{ + int i; + + for (i = 0; i < COMMAND_TYPE_COUNT; i++) + { + if (!strncmp(command_types[i].name, cmd_name, strlen(cmd_name)) + && command_types[i].parent_id == parent_id) + return &command_types[i]; + } + + return NULL; +} + +static struct command_type *__mclagdctl_get_cmd_by_id(enum id_command_type id) +{ + int i; + + for (i = 0; i < COMMAND_TYPE_COUNT; i++) + { + if (command_types[i].id == id) + return &command_types[i]; + } + + return NULL; +} + +static int mclagdctl_find_cmd(struct command_type **pcmd_type, int *argc, char ***argv) +{ + char *cmd_name; + enum id_command_type parent_id = ID_CMDTYPE_NONE; + struct command_type *cmd_type; + + while (1) + { + if (!*argc) + { + fprintf(stderr, "None or incomplete command\n"); + return -EINVAL; + } + + cmd_name = *argv[0]; + (*argc)--; + (*argv)++; + cmd_type = __mclagdctl_get_cmd_by_parent(cmd_name, parent_id); + + if (!cmd_type) + { + fprintf(stderr, "Unknown command \"%s\".\n", cmd_name); + return -EINVAL; + } + + if (__mclagdctl_cmd_executable(cmd_type) && __mclagdctl_cmd_param_cnt(cmd_type) >= *argc) + { + *pcmd_type = cmd_type; + return 0; + } + + parent_id = cmd_type->id; + } +} + +static int mclagdctl_check_cmd_params(struct command_type *cmd_type, + int argc, char **argv) +{ + int i = 0; + + while (cmd_type->params[i]) + { + if (i == argc) + { + fprintf(stderr, "Command line parameter \"%s\" expected.\n", cmd_type->params[i]); + return -EINVAL; + } + i++; + } + + return 0; +} + +static void mclagdctl_print_cmd(struct command_type *cmd_type) +{ + if (cmd_type->parent_id != ID_CMDTYPE_NONE) + { + mclagdctl_print_cmd(__mclagdctl_get_cmd_by_id(cmd_type->parent_id)); + fprintf(stdout, " "); + } + fprintf(stdout, "%s", cmd_type->name); +} + +static void mclagdctl_print_help(const char *argv0) +{ + int i, j; + struct command_type *cmd_type; + + fprintf(stdout, "%s [options] command [command args]\n" + " -h --help Show this help\n" + " -i --mclag-id Specify one mclag id\n" + " -l --level Specify log level critical,err,warn,notice,info,debug\n", + argv0); + fprintf(stdout, "Commands:\n"); + + for (i = 0; i < COMMAND_TYPE_COUNT; i++) + { + cmd_type = &command_types[i]; + if (!__mclagdctl_cmd_executable(cmd_type)) + continue; + fprintf(stdout, " "); + mclagdctl_print_cmd(cmd_type); + + for (j = 0; cmd_type->params[j]; j++) + fprintf(stdout, " %s", cmd_type->params[j]); + + fprintf(stdout, "\n"); + } +} + +int main(int argc, char **argv) +{ + char buf[MCLAGDCTL_CMD_SIZE] = { 0 }; + char *argv0 = argv[0]; + char *rcv_buf = NULL; + static const struct option long_options[] = + { + { "help", no_argument, NULL, 'h' }, + { "mclag id", required_argument, NULL, 'i' }, + { "log level", required_argument, NULL, 'l' }, + { NULL, 0, NULL, 0 } + }; + int opt; + int err; + struct command_type *cmd_type; + int ret; + unsigned para_int = 0; + + int len = 0; + char *data; + struct mclagd_reply_hdr *reply; + + while ((opt = getopt_long(argc, argv, "hi:l:", long_options, NULL)) >= 0) + { + switch (opt) + { + case 'h': + mclagdctl_print_help(argv0); + return EXIT_SUCCESS; + + case 'i': + para_int = atoi(optarg); + break; + + case 'l': + switch (tolower(optarg[0])) + { + case 'c': + para_int = CRITICAL; + break; + + case 'e': + para_int = ERR; + break; + + case 'w': + para_int = WARN; + break; + + case 'n': + para_int = NOTICE; + break; + + case 'i': + para_int = INFO; + break; + + case 'd': + para_int = DEBUG; + break; + + default: + fprintf(stderr, "unknown option \"%c\".\n", opt); + mclagdctl_print_help(argv0); + return EXIT_FAILURE; + } + break; + + case '?': + fprintf(stderr, "unknown option.\n"); + mclagdctl_print_help(argv0); + return EXIT_FAILURE; + + default: + fprintf(stderr, "unknown option \"%c\".\n", opt); + mclagdctl_print_help(argv0); + return EXIT_FAILURE; + } + } + + argv += optind; + argc -= optind; + + err = mclagdctl_find_cmd(&cmd_type, &argc, &argv); + if (err) + { + mclagdctl_print_help(argv0); + return EXIT_FAILURE; + } + + err = mclagdctl_check_cmd_params(cmd_type, argc, argv); + if (err) + { + mclagdctl_print_help(argv0); + return EXIT_FAILURE; + } + + if (mclagdctl_sock_fd <= 0) + { + ret = mclagdctl_sock_connect(); + if (ret < 0) + return EXIT_FAILURE; + } + + if (cmd_type->enca_msg(buf, para_int, argc, argv) < 0) + { + ret = EXIT_FAILURE; + goto mclagdctl_disconnect; + } + + ret = mclagdctl_sock_write(mclagdctl_sock_fd, buf, sizeof(struct mclagdctl_req_hdr)); + + if (ret <= 0) + { + fprintf(stderr, "Failed to send command to mclagd\n"); + ret = EXIT_FAILURE; + goto mclagdctl_disconnect; + } + + /*read data length*/ + memset(buf, 0, MCLAGDCTL_CMD_SIZE); + ret = mclagdctl_sock_read(mclagdctl_sock_fd, buf, sizeof(int)); + if (ret <= 0) + { + fprintf(stderr, "Failed to read data length from mclagd\n"); + ret = EXIT_FAILURE; + goto mclagdctl_disconnect; + } + + /*cont length*/ + len = *((int*)buf); + if (len <= 0) + { + ret = EXIT_FAILURE; + fprintf(stderr, "pkt len = %d, error\n", len); + goto mclagdctl_disconnect; + } + + rcv_buf = (char *)malloc(len); + if (!rcv_buf) + { + fprintf(stderr, "Failed to malloc rcv_buf for mclagdctl\n"); + goto mclagdctl_disconnect; + } + + /*read data*/ + ret = mclagdctl_sock_read(mclagdctl_sock_fd, rcv_buf, len); + if (ret <= 0) + { + fprintf(stderr, "Failed to read data from mclagd\n"); + ret = EXIT_FAILURE; + goto mclagdctl_disconnect; + } + + reply = (struct mclagd_reply_hdr *)rcv_buf; + if (reply->info_type != cmd_type->info_type) + { + fprintf(stderr, "Reply info type from mclagd error\n"); + ret = EXIT_FAILURE; + goto mclagdctl_disconnect; + } + + if (reply->exec_result == EXEC_TYPE_NO_EXIST_SYS) + { + fprintf(stderr, "No exist sys in iccpd!\n"); + ret = EXIT_FAILURE; + goto mclagdctl_disconnect; + } + + if (reply->exec_result == EXEC_TYPE_NO_EXIST_MCLAGID) + { + fprintf(stderr, "Mclag-id %d hasn't been configured in iccpd!\n", para_int); + ret = EXIT_FAILURE; + goto mclagdctl_disconnect; + } + + if (reply->exec_result == EXEC_TYPE_FAILED) + { + fprintf(stderr, "exec error in iccpd!\n"); + ret = EXIT_FAILURE; + goto mclagdctl_disconnect; + } + + cmd_type->parse_msg((char *)(rcv_buf + sizeof(struct mclagd_reply_hdr)), len - sizeof(struct mclagd_reply_hdr)); + + ret = EXIT_SUCCESS; + + mclagdctl_disconnect: + mclagdctl_sock_close(); + + if (rcv_buf) + free(rcv_buf); + + return ret; +} + diff --git a/src/iccpd/src/mclagdctl/mclagdctl.h b/src/iccpd/src/mclagdctl/mclagdctl.h new file mode 100644 index 000000000000..1b8c6f278cc5 --- /dev/null +++ b/src/iccpd/src/mclagdctl/mclagdctl.h @@ -0,0 +1,198 @@ +/* Copyright(c) 2016-2019 Nephos. + * + * 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, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintainer: Jim Jiang from nephos + */ + +#define MCLAGDCTL_PARA1_LEN 16 +#define MCLAGDCTL_PARA2_LEN 32 +#define MCLAGDCTL_PARA3_LEN 64 +#define MCLAGDCTL_CMD_SIZE 4096 + +#define MCLAGDCTL_MAX_L_PORT_NANE 32 +#define MCLAGDCTL_INET_ADDR_LEN 32 +#define MCLAGDCTL_ETHER_ADDR_LEN 6 +#define MCLAGDCTL_PORT_MEMBER_BUF_LEN 512 +#define ETHER_ADDR_STR_LEN 18 + +typedef int (*call_enca_msg_fun)(char *msg, int mclag_id, int argc, char **argv); +typedef int (*call_parse_msg_fun)(char *msg, int data_len); + +enum MAC_TYPE_CTL +{ + MAC_TYPE_STATIC_CTL = 1, + MAC_TYPE_DYNAMIC_CTL = 2, +}; + +enum MAC_AGE_TYPE_CTL +{ + MAC_AGE_LOCAL_CTL = 1, /*MAC in local switch is ageout*/ + MAC_AGE_PEER_CTL = 2 /*MAC in peer switch is ageout*/ +}; + +enum id_command_type +{ + ID_CMDTYPE_NONE = 0, + ID_CMDTYPE_D, + ID_CMDTYPE_D_S, + ID_CMDTYPE_D_A, + ID_CMDTYPE_D_P, + ID_CMDTYPE_D_P_L, + ID_CMDTYPE_D_P_P, + ID_CMDTYPE_C, + ID_CMDTYPE_C_L, +}; + +enum mclagdctl_notify_peer_type +{ + INFO_TYPE_NONE = 0, + INFO_TYPE_DUMP_STATE, + INFO_TYPE_DUMP_ARP, + INFO_TYPE_DUMP_MAC, + INFO_TYPE_DUMP_LOCAL_PORTLIST, + INFO_TYPE_DUMP_PEER_PORTLIST, + INFO_TYPE_CONFIG_LOGLEVEL, + INFO_TYPE_FINISH, +}; + +enum log_level_type +{ + CRITICAL = 0, + ERR= 1, + WARN = 2, + NOTICE= 3, + INFO = 4, + DEBUG = 5 +}; + +struct mclagdctl_req_hdr +{ + int info_type; + int mclag_id; + char para1[MCLAGDCTL_PARA2_LEN]; + char para2[MCLAGDCTL_PARA2_LEN]; + char para3[MCLAGDCTL_PARA2_LEN]; +}; + +struct mclagd_reply_hdr +{ + int info_type; + int data_len; + int exec_result; +}; + +#define EXEC_TYPE_SUCCESS -1 +#define EXEC_TYPE_NO_EXIST_SYS -2 +#define EXEC_TYPE_NO_EXIST_MCLAGID -3 +#define EXEC_TYPE_FAILED -4 + +#define MCLAG_ERROR -1 + +#define MCLAGD_REPLY_INFO_HDR (sizeof(struct mclagd_reply_hdr) + sizeof(int)) + +#define MCLAGDCTL_COMMAND_PARAM_MAX_CNT 8 +struct command_type +{ + enum id_command_type id; + enum id_command_type parent_id; + enum mclagdctl_notify_peer_type info_type; + char *name; + char *params[MCLAGDCTL_COMMAND_PARAM_MAX_CNT]; + call_enca_msg_fun enca_msg; + call_parse_msg_fun parse_msg; +}; + +struct mclagd_state +{ + int mclag_id; + int keepalive; + char local_ip[MCLAGDCTL_INET_ADDR_LEN]; + char peer_ip[MCLAGDCTL_INET_ADDR_LEN]; + char peer_link_if[MCLAGDCTL_MAX_L_PORT_NANE]; + unsigned char peer_link_mac[MCLAGDCTL_ETHER_ADDR_LEN]; + int role; + char enabled_po[MCLAGDCTL_PORT_MEMBER_BUF_LEN]; + char loglevel[MCLAGDCTL_PARA1_LEN]; +}; + +struct mclagd_arp_msg +{ + char op_type; + char ifname[MCLAGDCTL_MAX_L_PORT_NANE]; + char ipv4_addr[MCLAGDCTL_INET_ADDR_LEN]; + unsigned char mac_addr[MCLAGDCTL_ETHER_ADDR_LEN]; +}; + +struct mclagd_mac_msg +{ + unsigned char op_type;/*add or del*/ + unsigned char fdb_type;/*static or dynamic*/ + char mac_str[ETHER_ADDR_STR_LEN]; + unsigned short vid; + /*Current if name that set in chip*/ + char ifname[MCLAGDCTL_MAX_L_PORT_NANE]; + /*if we set the mac to peer-link, origin_ifname store the + original if name that learned from chip*/ + char origin_ifname[MCLAGDCTL_MAX_L_PORT_NANE]; + unsigned char age_flag;/*local or peer is age?*/ +}; + +struct mclagd_local_if +{ + int ifindex; + char type[MCLAGDCTL_PARA1_LEN]; + char name[MCLAGDCTL_MAX_L_PORT_NANE]; + unsigned char mac_addr[MCLAGDCTL_ETHER_ADDR_LEN]; + char state[MCLAGDCTL_PARA1_LEN]; + char ipv4_addr[MCLAGDCTL_INET_ADDR_LEN]; + unsigned char prefixlen; + + unsigned char l3_mode; + unsigned char is_peer_link; + char portchannel_member_buf[MCLAGDCTL_PORT_MEMBER_BUF_LEN]; + int po_id; /* Port Channel ID */ + unsigned char po_active; + char mlacp_state[MCLAGDCTL_PARA1_LEN]; + unsigned char isolate_to_peer_link; + + char vlanlist[MCLAGDCTL_PARA3_LEN]; +}; + +struct mclagd_peer_if +{ + int ifindex; + unsigned char type[MCLAGDCTL_PARA1_LEN]; + char name[MCLAGDCTL_MAX_L_PORT_NANE]; + unsigned char mac_addr[MCLAGDCTL_ETHER_ADDR_LEN]; + unsigned char state[MCLAGDCTL_PARA1_LEN]; + int po_id; + unsigned char po_active; +}; + +extern int mclagdctl_enca_dump_state(char *msg, int mclag_id, int argc, char **argv); +extern int mclagdctl_parse_dump_state(char *msg, int data_len); +extern int mclagdctl_enca_dump_arp(char *msg, int mclag_id, int argc, char **argv); +extern int mclagdctl_parse_dump_arp(char *msg, int data_len); +extern int mclagdctl_enca_dump_mac(char *msg, int mclag_id, int argc, char **argv); +extern int mclagdctl_parse_dump_mac(char *msg, int data_len); +extern int mclagdctl_enca_dump_local_portlist(char *msg, int mclag_id, int argc, char **argv); +extern int mclagdctl_parse_dump_local_portlist(char *msg, int data_len); +extern int mclagdctl_enca_dump_peer_portlist(char *msg, int mclag_id, int argc, char **argv); +extern int mclagdctl_parse_dump_peer_portlist(char *msg, int data_len); +int mclagdctl_enca_config_loglevel(char *msg, int log_level, int argc, char **argv); +int mclagdctl_parse_config_loglevel(char *msg, int data_len); + diff --git a/src/iccpd/src/mlacp_fsm.c b/src/iccpd/src/mlacp_fsm.c new file mode 100644 index 000000000000..f479b75f36cc --- /dev/null +++ b/src/iccpd/src/mlacp_fsm.c @@ -0,0 +1,1209 @@ +/* + * mlacp_fsm.c + * mLACP finite state machine handler. + * + * Copyright(c) 2016-2019 Nephos/Estinet. + * + * 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, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintainer: jianjun, grace Li from nephos + */ + +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include "../include/mlacp_tlv.h" +#include "../include/mlacp_sync_prepare.h" +#include "../include/mlacp_link_handler.h" +#include "../include/mlacp_sync_update.h" + +#include + +/***************************************** +* Define +* +* ***************************************/ +#define MLACP_MSG_QUEUE_REINIT(list) \ + { \ + struct Msg* msg = NULL; \ + while (!TAILQ_EMPTY(&(list))) { \ + msg = TAILQ_FIRST(&(list)); \ + TAILQ_REMOVE(&(list), msg, tail); \ + free(msg->buf); \ + free(msg); \ + } \ + TAILQ_INIT(&(list)); \ + } + +#define PIF_QUEUE_REINIT(list) \ + { \ + while (!LIST_EMPTY(&(list))) { \ + struct PeerInterface* peer_if = NULL; \ + peer_if = LIST_FIRST(&(list)); \ + LIST_REMOVE(peer_if, mlacp_next); \ + free(peer_if); \ + } \ + LIST_INIT(&(list)); \ + } + +#define LIF_QUEUE_REINIT(list) \ + { \ + while (!LIST_EMPTY(&(list))) { \ + struct LocalInterface* lif = NULL; \ + lif = LIST_FIRST(&(list)); \ + if (lif->type == IF_T_PORT_CHANNEL && lif->is_arp_accept) { \ + if ((set_sys_arp_accept_flag(lif->name, 0)) == 0) \ + lif->is_arp_accept = 0; \ + } \ + LIST_REMOVE (lif, mlacp_next); \ + } \ + LIST_INIT(&(list)); \ + } + +#define LIF_PURGE_QUEUE_REINIT(list) \ + { \ + while (!LIST_EMPTY(&(list))) { \ + struct LocalInterface* lif = NULL; \ + lif = LIST_FIRST(&(list)); \ + LIST_REMOVE(lif, mlacp_purge_next); \ + } \ + LIST_INIT(&(list)); \ + } + +#define WARM_REBOOT_TIMEOUT 90 + +/***************************************** +* Static Function +* +* ***************************************/ +static char *mlacp_state(struct CSM* csm); +static void mlacp_resync_arp(struct CSM* csm); +static void mlacp_resync_mac(struct CSM* csm); +/* Sync Sender APIs*/ +static void mlacp_sync_send_sysConf(struct CSM* csm); +static void mlacp_sync_send_aggConf(struct CSM* csm); +static void mlacp_sync_send_aggState(struct CSM* csm); +static void mlacp_sync_send_syncArpInfo(struct CSM* csm); +static void mlacp_sync_send_heartbeat(struct CSM* csm); +static void mlacp_sync_send_syncDoneData(struct CSM* csm); +/* Sync Reciever APIs*/ +static void mlacp_sync_recv_sysConf(struct CSM* csm, struct Msg* msg); +static void mlacp_sync_recv_portConf(struct CSM* csm, struct Msg* msg); +static void mlacp_sync_recv_portPrio(struct CSM* csm, struct Msg* msg); +static void mlacp_sync_recv_portState(struct CSM* csm, struct Msg* msg); +static void mlacp_sync_recv_aggConf(struct CSM* csm, struct Msg* msg); +static void mlacp_sync_recv_aggState(struct CSM* csm, struct Msg* msg); +static void mlacp_sync_recv_syncData(struct CSM* csm, struct Msg* msg); +static void mlacp_sync_recv_syncReq(struct CSM* csm, struct Msg* msg); +static void mlacp_sync_recv_portChanInfo(struct CSM* csm, struct Msg* msg); +static void mlacp_sync_recv_peerLlinkInfo(struct CSM* csm, struct Msg* msg); +static void mlacp_sync_recv_arpInfo(struct CSM* csm, struct Msg* msg); +static void mlacp_sync_recv_stpInfo(struct CSM* csm, struct Msg* msg); + +/* Sync Handler*/ +static void mlacp_sync_send_nak_handler(struct CSM* csm, struct Msg* msg); +static void mlacp_sync_recv_nak_handler(struct CSM* csm, struct Msg* msg); +static void mlacp_sync_sender_handler(struct CSM* csm); +static void mlacp_sync_receiver_handler(struct CSM* csm, struct Msg* msg); +static void mlacp_sync_send_all_info_handler(struct CSM* csm); + +/* Sync State Handler*/ +static void mlacp_stage_sync_send_handler(struct CSM* csm, struct Msg* msg); +static void mlacp_stage_sync_request_handler(struct CSM* csm, struct Msg* msg); +static void mlacp_stage_handler(struct CSM* csm, struct Msg* msg); +static void mlacp_exchange_handler(struct CSM* csm, struct Msg* msg); + +/****************************************************************** + * Sync Sender APIs + * + *****************************************************************/ +static void mlacp_sync_send_sysConf(struct CSM* csm) +{ + int msg_len = 0; + + memset(g_csm_buf, 0, CSM_BUFFER_SIZE); + msg_len = mlacp_prepare_for_sys_config(csm, g_csm_buf, CSM_BUFFER_SIZE); + if (msg_len > 0) + iccp_csm_send(csm, g_csm_buf, msg_len); + else + ICCPD_LOG_WARN(__FUNCTION__, "Invalid sysconf packet."); + + /*ICCPD_LOG_DEBUG("mlacp_fsm", " [SYNC_Send] SysConf, len=[%d]", msg_len);*/ + + return; +} + +static void mlacp_sync_send_aggConf(struct CSM* csm) +{ + struct System* sys = NULL; + int msg_len = 0; + struct LocalInterface* local_if = NULL; + + if ((sys = system_get_instance()) == NULL) + return; + + LIST_FOREACH(local_if, &(MLACP(csm).lif_list), mlacp_next) + { + if (local_if->type == IF_T_PORT_CHANNEL) + { + memset(g_csm_buf, 0, CSM_BUFFER_SIZE); + msg_len = mlacp_prepare_for_Aggport_config(csm, g_csm_buf, CSM_BUFFER_SIZE, local_if, 0); + iccp_csm_send(csm, g_csm_buf, msg_len); + local_if->port_config_sync = 0; + /*ICCPD_LOG_DEBUG("mlacp_fsm", " [SYNC_Send] PortChannel, csm-if-name=[%s], len=[%d]", local_if->name, msg_len);*/ + } + } + + return; +} + +static void mlacp_sync_send_aggState(struct CSM* csm) +{ + struct System* sys = NULL; + int msg_len = 0; + struct LocalInterface* local_if = NULL; + + if ((sys = system_get_instance()) == NULL) + return; + + LIST_FOREACH(local_if, &(MLACP(csm).lif_list), mlacp_next) + { + if (local_if->type == IF_T_PORT_CHANNEL) + { + memset(g_csm_buf, 0, CSM_BUFFER_SIZE); + msg_len = mlacp_prepare_for_Aggport_state(csm, g_csm_buf, CSM_BUFFER_SIZE, local_if); + iccp_csm_send(csm, g_csm_buf, msg_len); + local_if->changed = 0; + /*ICCPD_LOG_DEBUG("mlacp_fsm", " [SYNC_Send] PortChannel, csm-if-name=[%s], len=[%d]", local_if->name, msg_len);*/ + } + } + + return; +} +#define MAX_MAC_ENTRY_NUM 30 +#define MAX_ARP_ENTRY_NUM 40 +static void mlacp_sync_send_syncMacInfo(struct CSM* csm) +{ + int msg_len = 0; + struct Msg* msg = NULL; + int count = 0; + + memset(g_csm_buf, 0, CSM_BUFFER_SIZE); + + while (!TAILQ_EMPTY(&(MLACP(csm).mac_msg_list))) + { + msg = TAILQ_FIRST(&(MLACP(csm).mac_msg_list)); + TAILQ_REMOVE(&(MLACP(csm).mac_msg_list), msg, tail); + msg_len = mlacp_prepare_for_mac_info_to_peer(csm, g_csm_buf, CSM_BUFFER_SIZE, (struct MACMsg*)msg->buf, count); + count++; + free(msg->buf); + free(msg); + if (count >= MAX_MAC_ENTRY_NUM) + { + iccp_csm_send(csm, g_csm_buf, msg_len); + count = 0; + memset(g_csm_buf, 0, CSM_BUFFER_SIZE); + } + /*ICCPD_LOG_DEBUG("mlacp_fsm", " [SYNC_Send] MacInfo,len=[%d]", msg_len);*/ + } + + if (count) + iccp_csm_send(csm, g_csm_buf, msg_len); + + return; +} + +static void mlacp_sync_send_syncArpInfo(struct CSM* csm) +{ + int msg_len = 0; + struct Msg* msg = NULL; + int count = 0; + + memset(g_csm_buf, 0, CSM_BUFFER_SIZE); + + while (!TAILQ_EMPTY(&(MLACP(csm).arp_msg_list))) + { + msg = TAILQ_FIRST(&(MLACP(csm).arp_msg_list)); + TAILQ_REMOVE(&(MLACP(csm).arp_msg_list), msg, tail); + + msg_len = mlacp_prepare_for_arp_info(csm, g_csm_buf, CSM_BUFFER_SIZE, (struct ARPMsg*)msg->buf, count); + count++; + free(msg->buf); + free(msg); + if (count >= MAX_ARP_ENTRY_NUM) + { + iccp_csm_send(csm, g_csm_buf, msg_len); + count = 0; + memset(g_csm_buf, 0, CSM_BUFFER_SIZE); + } + /*ICCPD_LOG_DEBUG("mlacp_fsm", " [SYNC_Send] ArpInfo,len=[%d]", msg_len);*/ + } + + if (count) + iccp_csm_send(csm, g_csm_buf, msg_len); + + return; +} + +static void mlacp_sync_send_syncPortChannelInfo(struct CSM* csm) +{ + struct System* sys = NULL; + int msg_len = 0; + struct LocalInterface* local_if = NULL; + + if ((sys = system_get_instance()) == NULL) + return; + + LIST_FOREACH(local_if, &(MLACP(csm).lif_list), mlacp_next) + { + if (local_if->type == IF_T_PORT_CHANNEL) + { + memset(g_csm_buf, 0, CSM_BUFFER_SIZE); + msg_len = mlacp_prepare_for_port_channel_info(csm, g_csm_buf, CSM_BUFFER_SIZE, local_if); + iccp_csm_send(csm, g_csm_buf, msg_len); + local_if->changed = 0; + /*ICCPD_LOG_DEBUG("mlacp_fsm", " [SYNC_Send] PortChannel, csm-if-name=[%s], len=[%d]", local_if->name, msg_len);*/ + } + } + + return; +} + +static void mlacp_sync_send_syncPeerLinkInfo(struct CSM* csm) +{ + struct System* sys = NULL; + int msg_len = 0; + + if ((sys = system_get_instance()) == NULL) + return; + + if (csm->peer_link_if) + { + memset(g_csm_buf, 0, CSM_BUFFER_SIZE); + msg_len = mlacp_prepare_for_port_peerlink_info(csm, g_csm_buf, CSM_BUFFER_SIZE, csm->peer_link_if); + iccp_csm_send(csm, g_csm_buf, msg_len); + } + + return; +} + +static void mlacp_sync_send_heartbeat(struct CSM* csm) +{ + int msg_len = 0; + + if ((csm->heartbeat_send_time == 0) || + ((time(NULL) - csm->heartbeat_send_time) > 1)) + { + memset(g_csm_buf, 0, CSM_BUFFER_SIZE); + msg_len = mlacp_prepare_for_heartbeat(csm, g_csm_buf, CSM_BUFFER_SIZE); + iccp_csm_send(csm, g_csm_buf, msg_len); + time(&csm->heartbeat_send_time); + } + + return; +} + +static void mlacp_sync_send_syncDoneData(struct CSM* csm) +{ + int msg_len = 0; + + /*Sync done & go to next stage*/ + MLACP(csm).wait_for_sync_data = 0; + memset(g_csm_buf, 0, CSM_BUFFER_SIZE); + msg_len = mlacp_prepare_for_sync_data_tlv(csm, g_csm_buf, CSM_BUFFER_SIZE, 1); + iccp_csm_send(csm, g_csm_buf, msg_len); + /*ICCPD_LOG_DEBUG("mlacp_fsm", " [SYNC_Send] SyncDone, len=[%d]", msg_len);*/ + + return; +} + +/****************************************************************** + * Sync Receiver APIs + * + *****************************************************************/ +static void mlacp_sync_recv_sysConf(struct CSM* csm, struct Msg* msg) +{ + mLACPSysConfigTLV* sysconf = NULL; + + sysconf = (mLACPSysConfigTLV*)&(msg->buf[sizeof(ICCHdr)]); + + if (mlacp_fsm_update_system_conf(csm, sysconf) == MCLAG_ERROR) + { + /*NOTE: we just change the node ID local side without sending NAK msg*/ + ICCPD_LOG_DEBUG(__FUNCTION__, "Same Node ID = %d, send NAK", MLACP(csm).remote_system.node_id); + mlacp_sync_send_nak_handler(csm, msg); + } + + return; +} + +static void mlacp_sync_recv_portConf(struct CSM* csm, struct Msg* msg) +{ + /*Don't support currently*/ + return; +} + +static void mlacp_sync_recv_portPrio(struct CSM* csm, struct Msg* msg) +{ + /*Don't support currently*/ + return; +} + +static void mlacp_sync_recv_portState(struct CSM* csm, struct Msg* msg) +{ + /*Don't support currently*/ + return; +} + +static void mlacp_sync_recv_aggConf(struct CSM* csm, struct Msg* msg) +{ + mLACPAggConfigTLV* portconf = NULL; + + portconf = (mLACPAggConfigTLV*)&(msg->buf[sizeof(ICCHdr)]); + if (mlacp_fsm_update_Agg_conf(csm, portconf) == MCLAG_ERROR) + { + mlacp_sync_send_nak_handler(csm, msg); + } + + return; +} + +static void mlacp_sync_recv_aggState(struct CSM* csm, struct Msg* msg) +{ + mLACPAggPortStateTLV* portstate = NULL; + + portstate = (mLACPAggPortStateTLV*)&(msg->buf[sizeof(ICCHdr)]); + if (mlacp_fsm_update_Aggport_state(csm, portstate) == MCLAG_ERROR) + { + mlacp_sync_send_nak_handler(csm, msg); + /*MLACP(csm).error_msg = "Receive a port state update on an non-existed port. It is suggest to check the environment and re-initialize mLACP again.";*/ + return; + } + + return; +} + +static void mlacp_sync_recv_syncData(struct CSM* csm, struct Msg* msg) +{ + mLACPSyncDataTLV* syncdata = NULL; + + syncdata = (mLACPSyncDataTLV*)&(msg->buf[sizeof(ICCHdr)]); + if (ntohs(syncdata->flags) == 1) + { + /* Sync done*/ + MLACP(csm).wait_for_sync_data = 0; + } + + return; +} + +static void mlacp_sync_recv_syncReq(struct CSM* csm, struct Msg* msg) +{ + mLACPSyncReqTLV* mlacp_sync_req = NULL; + + mlacp_sync_req = (mLACPSyncReqTLV*)&msg->buf[sizeof(ICCHdr)]; + MLACP(csm).sync_req_num = ntohs(mlacp_sync_req->req_num); + + /* Reply the peer all sync info*/ + mlacp_sync_send_all_info_handler(csm); + + return; +} + +static void mlacp_sync_recv_portChanInfo(struct CSM* csm, struct Msg* msg) +{ + mLACPPortChannelInfoTLV* portconf = NULL; + + portconf = (mLACPPortChannelInfoTLV*)&(msg->buf[sizeof(ICCHdr)]); + if (mlacp_fsm_update_port_channel_info(csm, portconf) == MCLAG_ERROR) + { + mlacp_sync_send_nak_handler(csm, msg); + } + + return; +} + +static void mlacp_sync_recv_peerLlinkInfo(struct CSM* csm, struct Msg* msg) +{ + mLACPPeerLinkInfoTLV* peerlink = NULL; + + peerlink = (mLACPPeerLinkInfoTLV*)&(msg->buf[sizeof(ICCHdr)]); + mlacp_fsm_update_peerlink_info( csm, peerlink); + + return; +} + +static void mlacp_sync_recv_macInfo(struct CSM* csm, struct Msg* msg) +{ + struct mLACPMACInfoTLV* mac_info = NULL; + + mac_info = (struct mLACPMACInfoTLV *)&(msg->buf[sizeof(ICCHdr)]); + mlacp_fsm_update_mac_info_from_peer(csm, mac_info); + + return; +} + +static void mlacp_sync_recv_arpInfo(struct CSM* csm, struct Msg* msg) +{ + struct mLACPARPInfoTLV* arp_info = NULL; + + arp_info = (struct mLACPARPInfoTLV *)&(msg->buf[sizeof(ICCHdr)]); + mlacp_fsm_update_arp_info(csm, arp_info); + + return; +} + +static void mlacp_sync_recv_stpInfo(struct CSM* csm, struct Msg* msg) +{ + /*Don't support currently*/ + return; +} + +static void mlacp_sync_recv_heartbeat(struct CSM* csm, struct Msg* msg) +{ + struct mLACPHeartbeatTLV *tlv = NULL; + + tlv = (struct mLACPHeartbeatTLV *)(&msg->buf[sizeof(ICCHdr)]); + mlacp_fsm_update_heartbeat(csm, tlv); + + return; +} + +static void mlacp_sync_recv_warmboot(struct CSM* csm, struct Msg* msg) +{ + struct mLACPWarmbootTLV *tlv = NULL; + + tlv = (struct mLACPWarmbootTLV *)(&msg->buf[sizeof(ICCHdr)]); + mlacp_fsm_update_warmboot(csm, tlv); + + return; +} + +/***************************************** +* MLACP Init +* +* ***************************************/ +void mlacp_init(struct CSM* csm, int all) +{ + if (csm == NULL) + return; + + MLACP(csm).sync_req_num = -1; + MLACP(csm).need_to_sync = 0; + MLACP(csm).error_msg = NULL; + + MLACP(csm).current_state = MLACP_STATE_INIT; + memset(MLACP(csm).remote_system.system_id, 0, ETHER_ADDR_LEN); + + MLACP_MSG_QUEUE_REINIT(MLACP(csm).mlacp_msg_list); + MLACP_MSG_QUEUE_REINIT(MLACP(csm).arp_msg_list); + MLACP_MSG_QUEUE_REINIT(MLACP(csm).mac_msg_list); + PIF_QUEUE_REINIT(MLACP(csm).pif_list); + LIF_PURGE_QUEUE_REINIT(MLACP(csm).lif_purge_list); + + if (all != 0) + { + /* if no clean all, keep the arp info & local interface info for next connection*/ + MLACP_MSG_QUEUE_REINIT(MLACP(csm).arp_list); + MLACP_MSG_QUEUE_REINIT(MLACP(csm).mac_list); + LIF_QUEUE_REINIT(MLACP(csm).lif_list); + + MLACP(csm).node_id = MLACP_SYSCONF_NODEID_MSB_MASK; + MLACP(csm).node_id |= (((inet_addr(csm->sender_ip) >> 24) << 4) & MLACP_SYSCONF_NODEID_NODEID_MASK); + MLACP(csm).node_id |= rand() % MLACP_SYSCONF_NODEID_FREE_MASK; + } + + return; +} + +/***************************************** +* MLACP finalize +* +* ***************************************/ +void mlacp_finalize(struct CSM* csm) +{ + if (csm == NULL) + return; + + /* msg destroy*/ + MLACP_MSG_QUEUE_REINIT(MLACP(csm).mlacp_msg_list); + MLACP_MSG_QUEUE_REINIT(MLACP(csm).arp_msg_list); + MLACP_MSG_QUEUE_REINIT(MLACP(csm).mac_msg_list); + MLACP_MSG_QUEUE_REINIT(MLACP(csm).arp_list); + MLACP_MSG_QUEUE_REINIT(MLACP(csm).mac_list); + + /* remove lif & lif-purge queue */ + LIF_QUEUE_REINIT(MLACP(csm).lif_list); + LIF_PURGE_QUEUE_REINIT(MLACP(csm).lif_purge_list); + /* remove & destroy pif queue */ + PIF_QUEUE_REINIT(MLACP(csm).pif_list); + + return; +} + +/***************************************** +* MLACP FSM Transit +* +* ***************************************/ +void mlacp_fsm_transit(struct CSM* csm) +{ + struct System* sys = NULL; + struct Msg* msg = NULL; + static MLACP_APP_STATE_E prev_state = MLACP_SYNC_SYSCONF; + ICCHdr* icc_hdr = NULL; + ICCParameter* icc_param = NULL; + int have_msg = 1; + + if (csm == NULL) + return; + if ((sys = system_get_instance()) == NULL) + return; + + /* torn down event */ + if (csm->sock_fd <= 0 || csm->app_csm.current_state != APP_OPERATIONAL) + { + /* drop all legacy mlacp msg*/ + if (MLACP(csm).current_state != MLACP_STATE_INIT) + { + MLACP_MSG_QUEUE_REINIT(MLACP(csm).mlacp_msg_list); + MLACP_MSG_QUEUE_REINIT(MLACP(csm).arp_msg_list); + MLACP_MSG_QUEUE_REINIT(MLACP(csm).mac_msg_list); + MLACP(csm).current_state = MLACP_STATE_INIT; + } + return; + } + + if (csm->warm_reboot_disconn_time != 0) + { + /*After peer warm reboot and disconnect, if peer connection is not establised more than 90s, + recover peer disconnection to normal process, such as add peer age flag for MACs etc*/ + if ((time(NULL) - csm->warm_reboot_disconn_time) >= WARM_REBOOT_TIMEOUT) + { + csm->warm_reboot_disconn_time = 0; + ICCPD_LOG_NOTICE(__FUNCTION__, "Peer warm reboot, reconnection timeout, recover to normal reboot!"); + mlacp_peer_disconn_handler(csm); + } + } + + mlacp_sync_send_heartbeat(csm); + + /* Dequeue msg if any*/ + while (have_msg) + { + if (MLACP(csm).current_state != MLACP_STATE_INIT) + { + /* Handler NAK First*/ + msg = mlacp_dequeue_msg(csm); + if (msg != NULL) + { + have_msg = 1; + icc_hdr = (ICCHdr*)msg->buf; + icc_param = (ICCParameter*)&msg->buf[sizeof(ICCHdr)]; + /*ICCPD_LOG_DEBUG("mlacp_fsm", " SYNC: Message Type = %X, TLV=%s, Len=%d", icc_hdr->ldp_hdr.msg_type, get_tlv_type_string(icc_param->type), msg->len);*/ + + if (icc_hdr->ldp_hdr.msg_type == MSG_T_NOTIFICATION && icc_param->type == TLV_T_NAK) + { + mlacp_sync_recv_nak_handler(csm, msg); + free(msg->buf); + free(msg); + continue; + } + } + else + { + have_msg = 0; + } + } + + if (prev_state != MLACP(csm).current_state) + { + if (MLACP(csm).current_state == MLACP_STATE_EXCHANGE) + mlacp_peer_conn_handler(csm); + prev_state = MLACP(csm).current_state; + } + + /* Sync State */ + if (MLACP(csm).current_state == MLACP_STATE_INIT) + { + MLACP(csm).wait_for_sync_data = 0; + MLACP(csm).current_state = MLACP_STATE_STAGE1; + mlacp_resync_arp(csm); + } + + switch (MLACP(csm).current_state) + { + case MLACP_STATE_INIT: + case MLACP_STATE_ERROR: + /* should not be here*/ + break; + + case MLACP_STATE_STAGE1: + case MLACP_STATE_STAGE2: + mlacp_stage_handler(csm, msg); + break; + + case MLACP_STATE_EXCHANGE: + mlacp_exchange_handler(csm, msg); + break; + } + + /*ICCPD_LOG_DEBUG("mlacp_fsm", " Next State = %s", mlacp_state(csm));*/ + if (msg) + { + free(msg->buf); + free(msg); + } + } +} + +/* Helper function for dumping application state machine */ +static char* mlacp_state(struct CSM* csm) +{ + if (csm == NULL ) + return "MLACP_NULL"; + + switch (MLACP(csm).current_state) + { + case MLACP_STATE_INIT: + return "MLACP_STATE_INIT"; + + case MLACP_STATE_STAGE1: + return "MLACP_STATE_STAGE1"; + + case MLACP_STATE_STAGE2: + return "MLACP_STATE_STAGE2"; + + case MLACP_STATE_EXCHANGE: + return "MLACP_STATE_EXCHANGE"; + + case MLACP_STATE_ERROR: + return "MLACP_STATE_ERROR"; + } + + return "MLACP_UNKNOWN"; +} + +/* Add received message into message list */ +void mlacp_enqueue_msg(struct CSM* csm, struct Msg* msg) +{ + if (csm == NULL ) + { + if (msg != NULL ) + free(msg); + return; + } + + if (msg == NULL ) + return; + + #if 0 + icc_hdr = (ICCHdr*)msg->buf; + icc_param = (ICCParameter*)&msg->buf[sizeof(ICCHdr)]; + ICCPD_LOG_DEBUG("mlacp_fsm", " mLACP enqueue: tlv = 0x%04x", icc_param->type); + #endif + + TAILQ_INSERT_TAIL(&(MLACP(csm).mlacp_msg_list), msg, tail); + + return; +} + +/* Get received message from message list */ +struct Msg* mlacp_dequeue_msg(struct CSM* csm) +{ + struct Msg* msg = NULL; + + if (!TAILQ_EMPTY(&(MLACP(csm).mlacp_msg_list))) + { + msg = TAILQ_FIRST(&(MLACP(csm).mlacp_msg_list)); + TAILQ_REMOVE(&(MLACP(csm).mlacp_msg_list), msg, tail); + } + + return msg; +} + +/****************************************** +* When peerlink ready, prepare the MACMsg +* +******************************************/ +static void mlacp_resync_mac(struct CSM* csm) +{ + struct Msg* msg = NULL; + struct MACMsg* mac_msg = NULL; + struct Msg *msg_send = NULL; + + /* recover MAC info sync from peer*/ + if (!TAILQ_EMPTY(&(MLACP(csm).mac_list))) + { + TAILQ_FOREACH(msg, &MLACP(csm).mac_list, tail) + { + mac_msg = (struct MACMsg*)msg->buf; + mac_msg->op_type = MAC_SYNC_ADD; + if (iccp_csm_init_msg(&msg_send, (char*)mac_msg, sizeof(struct MACMsg)) == 0) + { + mac_msg->age_flag &= ~MAC_AGE_PEER; + TAILQ_INSERT_TAIL(&(MLACP(csm).mac_msg_list), msg_send, tail); + ICCPD_LOG_DEBUG(__FUNCTION__, "MAC-msg-list enqueue: %s, add %s vlan-id %d, age_flag %d", + mac_msg->ifname, mac_msg->mac_str, mac_msg->vid, mac_msg->age_flag); + } + } + } +} + +/****************************************** +* When peerlink ready, prepare the ARPMsg +* +******************************************/ +static void mlacp_resync_arp(struct CSM* csm) +{ + struct Msg* msg = NULL; + struct ARPMsg* arp_msg = NULL; + struct Msg *msg_send = NULL; + + /* recover ARP info sync from peer*/ + if (!TAILQ_EMPTY(&(MLACP(csm).arp_list))) + { + TAILQ_FOREACH(msg, &MLACP(csm).arp_list, tail) + { + arp_msg = (struct ARPMsg*)msg->buf; + arp_msg->op_type = ARP_SYNC_ADD; + if (iccp_csm_init_msg(&msg_send, (char*)arp_msg, sizeof(struct ARPMsg)) == 0) + { + TAILQ_INSERT_TAIL(&(MLACP(csm).arp_msg_list), msg_send, tail); + } + } + } +} + +/***************************************** +* NAK handler +* +* ***************************************/ +static void mlacp_sync_send_nak_handler(struct CSM* csm, struct Msg* msg) +{ + int msg_len; + ICCHdr* icc_hdr = NULL; + + icc_hdr = (ICCHdr*)msg->buf; + + ICCPD_LOG_WARN(__FUNCTION__, "Send NAK"); + + memset(g_csm_buf, 0, CSM_BUFFER_SIZE); + csm->app_csm.invalid_msg_id = ntohl(icc_hdr->ldp_hdr.msg_id); + msg_len = app_csm_prepare_nak_msg(csm, g_csm_buf, CSM_BUFFER_SIZE); + iccp_csm_send(csm, g_csm_buf, msg_len); +} + +static void mlacp_sync_recv_nak_handler(struct CSM* csm, struct Msg* msg) +{ + NAKTLV* naktlv = NULL; + uint16_t tlvType = -1; + int i; + + ICCPD_LOG_WARN(__FUNCTION__, "Receive NAK "); + + /* Dequeuq NAK*/ + naktlv = (NAKTLV*)&msg->buf[sizeof(ICCHdr)]; + + /* Check NAK Type*/ + for (i = 0; i < MAX_MSG_LOG_SIZE; ++i) + { + if (ntohl(naktlv->rejected_msg_id) == csm->msg_log.msg[i].msg_id) + { + tlvType = csm->msg_log.msg[i].tlv; + break; + } + } + + if (tlvType) + { + switch (tlvType) + { + case TLV_T_MLACP_SYSTEM_CONFIG: + MLACP(csm).node_id--; + MLACP(csm).system_config_changed = 1; + ICCPD_LOG_WARN(__FUNCTION__, "[%X] change NodeID as %d", tlvType & 0x00FF, MLACP(csm).node_id); + break; + + default: + ICCPD_LOG_WARN(__FUNCTION__, " [%X]", tlvType & 0x00FF); + MLACP(csm).need_to_sync = 1; + break; + } + } + else + { + ICCPD_LOG_WARN(__FUNCTION__, "Unknow NAK"); + MLACP(csm).need_to_sync = 1; + } + + return; +} + +/***************************************** +* MLACP sync receiver +* +* ***************************************/ +static void mlacp_sync_receiver_handler(struct CSM* csm, struct Msg* msg) +{ + ICCParameter *icc_param; + + /* No receive message...*/ + if (!csm || !msg) + return; + + icc_param = (ICCParameter*)&(msg->buf[sizeof(ICCHdr)]); + + /*fprintf(stderr, " Recv Type [%d]\n", icc_param->type);*/ + switch (icc_param->type) + { + case TLV_T_MLACP_SYSTEM_CONFIG: + mlacp_sync_recv_sysConf(csm, msg); + break; + + case TLV_T_MLACP_PORT_CONFIG: + mlacp_sync_recv_portConf(csm, msg); + break; + + case TLV_T_MLACP_PORT_PRIORITY: + mlacp_sync_recv_portPrio(csm, msg); + break; + + case TLV_T_MLACP_PORT_STATE: + mlacp_sync_recv_portState(csm, msg); + break; + + case TLV_T_MLACP_AGGREGATOR_CONFIG: + /* The following line will be uncommented when Aggregator related structures are supported. */ + mlacp_sync_recv_aggConf(csm, msg); + break; + + case TLV_T_MLACP_AGGREGATOR_STATE: + mlacp_sync_recv_aggState(csm, msg); + break; + + case TLV_T_MLACP_SYNC_DATA: + mlacp_sync_recv_syncData(csm, msg); + break; + + case TLV_T_MLACP_SYNC_REQUEST: + mlacp_sync_recv_syncReq(csm, msg); + break; + + case TLV_T_MLACP_PORT_CHANNEL_INFO: + mlacp_sync_recv_portChanInfo(csm, msg); + break; + + case TLV_T_MLACP_PEERLINK_INFO: + mlacp_sync_recv_peerLlinkInfo(csm, msg); + break; + + case TLV_T_MLACP_MAC_INFO: + mlacp_sync_recv_macInfo(csm, msg); + break; + + case TLV_T_MLACP_ARP_INFO: + mlacp_sync_recv_arpInfo(csm, msg); + break; + + case TLV_T_MLACP_STP_INFO: + mlacp_sync_recv_stpInfo(csm, msg); + break; + + case TLV_T_MLACP_HEARTBEAT: + mlacp_sync_recv_heartbeat(csm, msg); + break; + + case TLV_T_MLACP_WARMBOOT_FLAG: + mlacp_sync_recv_warmboot(csm, msg); + break; + } + + /*ICCPD_LOG_DEBUG("mlacp_fsm", " [Sync Recv] %s... DONE", get_tlv_type_string(icc_param->type));*/ + + return; +} + +/***************************************** +* MLACP sync sender +* +* ***************************************/ +static void mlacp_sync_sender_handler(struct CSM* csm) +{ + switch (MLACP(csm).sync_state) + { + case MLACP_SYNC_SYSCONF: + mlacp_sync_send_sysConf(csm); + break; + + case MLACP_SYNC_AGGCONF: + /* Do nothing due to no support in this version. */ + mlacp_sync_send_aggConf(csm); + break; + + case MLACP_SYNC_AGGSTATE: + /* Do nothing due to no support in this version. */ + mlacp_sync_send_aggState(csm); + break; + + case MLACP_SYNC_AGGINFO: + mlacp_sync_send_syncPortChannelInfo(csm); + break; + + case MLACP_SYNC_PEERLINKINFO: + mlacp_sync_send_syncPeerLinkInfo(csm); + break; + + case MLACP_SYNC_ARP_INFO: + mlacp_sync_send_syncArpInfo(csm); + break; + + case MLACP_SYNC_DONE: + mlacp_sync_send_syncDoneData(csm); + break; + + default: + break; + } + + return; +} + +static void mlacp_sync_send_all_info_handler(struct CSM* csm) +{ + size_t len = 0; + + /* Prepare for sync start reply*/ + memset(g_csm_buf, 0, CSM_BUFFER_SIZE); + len = mlacp_prepare_for_sync_data_tlv(csm, g_csm_buf, CSM_BUFFER_SIZE, 0); + iccp_csm_send(csm, g_csm_buf, len); + + MLACP(csm).sync_state = MLACP_SYNC_SYSCONF; + + while (1) + { + mlacp_sync_sender_handler(csm); + if (MLACP(csm).sync_state != MLACP_SYNC_DONE) + { + MLACP(csm).sync_state++; + } + else + { + /*Next stage*/ + MLACP(csm).wait_for_sync_data = 0; + MLACP(csm).current_state++; + break; + } + } + + return; +} + +static void mlacp_stage_sync_send_handler(struct CSM* csm, struct Msg* msg) +{ + ICCHdr* icc_hdr = NULL; + ICCParameter* icc_param = NULL; + mLACPSyncReqTLV* mlacp_sync_req = NULL; + + if (MLACP(csm).wait_for_sync_data == 0) + { + /* Waiting the peer sync request*/ + if (msg) + { + icc_hdr = (ICCHdr*)msg->buf; + icc_param = (ICCParameter*)&msg->buf[sizeof(ICCHdr)]; + + if (icc_hdr->ldp_hdr.msg_type == MSG_T_RG_APP_DATA && icc_param->type == TLV_T_MLACP_SYNC_REQUEST) + { + mlacp_sync_req = (mLACPSyncReqTLV*)&msg->buf[sizeof(ICCHdr)]; + MLACP(csm).wait_for_sync_data = 1; + MLACP(csm).sync_req_num = ntohs(mlacp_sync_req->req_num); + + /* Reply the peer all sync info*/ + mlacp_sync_send_all_info_handler(csm); + } + } + } + + return; +} + +static void mlacp_stage_sync_request_handler(struct CSM* csm, struct Msg* msg) +{ + int msg_len = 0; + + /* Socket server send sync request first*/ + if (MLACP(csm).wait_for_sync_data == 0) + { + // Send out the request for ALL + memset(g_csm_buf, 0, CSM_BUFFER_SIZE); + msg_len = mlacp_prepare_for_sync_request_tlv(csm, g_csm_buf, CSM_BUFFER_SIZE); + iccp_csm_send(csm, g_csm_buf, msg_len); + MLACP(csm).wait_for_sync_data = 1; + } + else + { + mlacp_sync_receiver_handler(csm, msg); + if (MLACP(csm).wait_for_sync_data == 0) + { + MLACP(csm).current_state++; + } + } + + return; +} + +static void mlacp_stage_handler(struct CSM* csm, struct Msg* msg) +{ + if (MLACP(csm).current_state == MLACP_STATE_STAGE1) + { + /*Stage 1, role active send info first*/ + if (csm->role_type == STP_ROLE_ACTIVE) + mlacp_stage_sync_send_handler(csm, msg); + else + mlacp_stage_sync_request_handler(csm, msg); + } + else + { + /*Stage 2, role standby send info*/ + if (csm->role_type == STP_ROLE_ACTIVE) + mlacp_stage_sync_request_handler(csm, msg); + else + mlacp_stage_sync_send_handler(csm, msg); + } + + return; +} + +static void mlacp_exchange_handler(struct CSM* csm, struct Msg* msg) +{ + int len; + struct System* sys = NULL; + struct LocalInterface* lif = NULL, *lif_purge = NULL; + + ICCHdr* icc_hdr = NULL; + + if ((sys = system_get_instance()) == NULL) + return; + + /* update system id*/ + /*update_system_id(csm);*/ + + /* Any msg?*/ + if (msg) + { + icc_hdr = (ICCHdr*)msg->buf; + if (icc_hdr->ldp_hdr.msg_type == MSG_T_RG_APP_DATA) + { + /* Process receive APP info*/ + mlacp_sync_receiver_handler(csm, msg); + } + } + + if (MLACP(csm).need_to_sync != 0) + { + /* Send out the request for ALL info*/ + MLACP(csm).need_to_sync = 0; + memset(g_csm_buf, 0, CSM_BUFFER_SIZE); + len = mlacp_prepare_for_sync_request_tlv(csm, g_csm_buf, CSM_BUFFER_SIZE); + iccp_csm_send(csm, g_csm_buf, len); + } + + /* Send system config*/ + if (MLACP(csm).system_config_changed != 0) + { + memset(g_csm_buf, 0, CSM_BUFFER_SIZE); + len = mlacp_prepare_for_sys_config(csm, g_csm_buf, CSM_BUFFER_SIZE); + iccp_csm_send(csm, g_csm_buf, len); + + if (csm->peer_link_if) + { + memset(g_csm_buf, 0, CSM_BUFFER_SIZE); + len = mlacp_prepare_for_port_peerlink_info(csm, g_csm_buf, CSM_BUFFER_SIZE, csm->peer_link_if); + iccp_csm_send(csm, g_csm_buf, len); + } + + MLACP(csm).system_config_changed = 0; + } + + /* Send mlag purge lif*/ + LIST_FOREACH(lif_purge, &(MLACP(csm).lif_purge_list), mlacp_purge_next) + { + /* Purge info*/ + memset(g_csm_buf, 0, CSM_BUFFER_SIZE); + len = mlacp_prepare_for_Aggport_config(csm, g_csm_buf, CSM_BUFFER_SIZE, lif_purge, 1); + iccp_csm_send(csm, g_csm_buf, len); + /* Destroy old interface*/ + if (lif_purge != NULL) + LIST_REMOVE(lif_purge, mlacp_purge_next); + } + + /* Send mlag lif*/ + LIST_FOREACH(lif, &(MLACP(csm).lif_list), mlacp_next) + { + if (lif->type == IF_T_PORT_CHANNEL && lif->port_config_sync) + { + /* Send port channel information*/ + memset(g_csm_buf, 0, CSM_BUFFER_SIZE); + len = mlacp_prepare_for_Aggport_config(csm, g_csm_buf, CSM_BUFFER_SIZE, lif, 0); + iccp_csm_send(csm, g_csm_buf, len); + + memset(g_csm_buf, 0, CSM_BUFFER_SIZE); + len = mlacp_prepare_for_port_channel_info(csm, g_csm_buf, CSM_BUFFER_SIZE, lif); + iccp_csm_send(csm, g_csm_buf, len); + + lif->port_config_sync = 0; + } + + /*send if portchannel state change */ + if (lif->type == IF_T_PORT_CHANNEL && lif->changed) + { + /* Send port channel state information*/ + memset(g_csm_buf, 0, CSM_BUFFER_SIZE); + len = mlacp_prepare_for_Aggport_state(csm, g_csm_buf, CSM_BUFFER_SIZE, lif); + iccp_csm_send(csm, g_csm_buf, len); + lif->changed = 0; + } + } + + /* Send MAC info if any*/ + mlacp_sync_send_syncMacInfo(csm); + + /* Send ARP info if any*/ + mlacp_sync_send_syncArpInfo(csm); + + /*If peer is warm reboot*/ + if (csm->peer_warm_reboot_time != 0) + { + /*Peer warm reboot timeout(connection is not broken more than 90s), recover to normal reboot*/ + if ((time(NULL) - csm->peer_warm_reboot_time) >= WARM_REBOOT_TIMEOUT) + { + csm->peer_warm_reboot_time = 0; + ICCPD_LOG_NOTICE(__FUNCTION__, "Peer warm reboot timeout, recover to normal reboot!"); + } + } + + return; +} diff --git a/src/iccpd/src/mlacp_link_handler.c b/src/iccpd/src/mlacp_link_handler.c new file mode 100644 index 000000000000..66a75c4b8620 --- /dev/null +++ b/src/iccpd/src/mlacp_link_handler.c @@ -0,0 +1,2401 @@ +/* + * mlacp_link_handler.c + * mLACP link handler + * + * Copyright(c) 2016-2019 Nephos/Estinet. + * + * 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, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintainer: jianjun, grace Li from nephos + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../include/system.h" +#include "../include/logger.h" +#include "../include/mlacp_tlv.h" + +#include "../include/iccp_csm.h" +#include "mclagdctl/mclagdctl.h" +#include "../include/iccp_cmd_show.h" +/***************************************** +* Enum +* +* ***************************************/ +typedef enum route_manipulate_type +{ + ROUTE_NONE, + ROUTE_ADD, + ROUTE_DEL +} ROUTE_MANIPULATE_TYPE_E; + + +/***************************************** +* Global +* +* ***************************************/ +char g_ipv4_str[INET_ADDRSTRLEN]; + +/***************************************** +* Tool : show ip string +* +* ***************************************/ +char *show_ip_str(uint32_t ipv4_addr) +{ + struct in_addr in_addr; + + memset(g_ipv4_str, 0, sizeof(g_ipv4_str)); + in_addr.s_addr = ipv4_addr; + inet_ntop(AF_INET, &in_addr, g_ipv4_str, INET_ADDRSTRLEN); + + return g_ipv4_str; +} + +static int getHwAddr(char *buff, char *mac) +{ + int i = 0; + unsigned int p[6]; + + if ( buff == NULL || mac == NULL ) + { + return MCLAG_ERROR; + } + + if (sscanf(mac, "%x:%x:%x:%x:%x:%x", &p[0], &p[1], &p[2], &p[3], &p[4], &p[5]) < 6) + { + return MCLAG_ERROR; + } + + for (i = 0; i < 6; i++) + { + buff[i] = p[i]; + } + + return 0; +} + +/* Set an entry in the ARP cache. */ +int mlacp_fsm_arp_set(char *ifname, uint32_t ip, char *mac) +{ + struct arpreq arpreq; + struct sockaddr_in *sin; + struct in_addr ina; + int flags; + int rc; + int sock_fd = 0; + + ICCPD_LOG_DEBUG(__FUNCTION__, "Set arp entry for IP:%s MAC:%s ifname:%s", show_ip_str(htonl(ip)), mac, ifname); + + if (ifname == NULL || ip == 0 || mac == NULL) + { + return MCLAG_ERROR; + } + + /*you must add this becasue some system will return "Invlid argument" + because some argument isn't zero */ + memset(&arpreq, 0, sizeof(struct arpreq)); + sin = (struct sockaddr_in *)&arpreq.arp_pa; + memset(sin, 0, sizeof(struct sockaddr_in)); + sin->sin_family = AF_INET; + ina.s_addr = htonl(ip); + memcpy(&sin->sin_addr, (char *)&ina, sizeof(struct in_addr)); + + if (getHwAddr((char *)arpreq.arp_ha.sa_data, mac) < 0) + { + return MCLAG_ERROR; + } + + strncpy(arpreq.arp_dev, ifname, 15); + + flags = ATF_COM; //note, must set flag, if not,you will get error + + arpreq.arp_flags = flags; + + sock_fd = socket(AF_INET, SOCK_DGRAM, 0); + if (sock_fd < 0) + { + return MCLAG_ERROR; + } + + rc = ioctl(sock_fd, SIOCSARP, &arpreq); + if (rc < 0) + { + close(sock_fd); + return MCLAG_ERROR; + } + + close(sock_fd); + + return 0; +} + +/* Delete an entry from the ARP cache. */ +int mlacp_fsm_arp_del(char *ifname, uint32_t ip) +{ + struct arpreq arpreq; + struct sockaddr_in *sin; + struct in_addr ina; + int rc; + int sock_fd = 0; + + ICCPD_LOG_DEBUG(__FUNCTION__, "Del arp entry for IP : %s ifname:%s", show_ip_str(htonl(ip)), ifname); + + if (ifname == NULL || ip == 0) + { + return MCLAG_ERROR; + } + + /*you must add this becasue some system will return "Invlid argument" + because some argument isn't zero */ + memset(&arpreq, 0, sizeof(struct arpreq)); + + sin = (struct sockaddr_in *)&arpreq.arp_pa; + memset(sin, 0, sizeof(struct sockaddr_in)); + sin->sin_family = AF_INET; + ina.s_addr = htonl(ip); + memcpy(&sin->sin_addr, (char *)&ina, sizeof(struct in_addr)); + + strncpy(arpreq.arp_dev, ifname, 15); + + sock_fd = socket(AF_INET, SOCK_DGRAM, 0); + if (sock_fd < 0) + { + return MCLAG_ERROR; + } + + rc = ioctl(sock_fd, SIOCDARP, &arpreq); + if (rc < 0) + { + close(sock_fd); + return MCLAG_ERROR; + } + + close(sock_fd); + + return 0; +} + +static int arp_set_handler(struct CSM* csm, + struct LocalInterface* lif, + int add) +{ + struct Msg* msg = NULL; + struct ARPMsg* arp_msg = NULL; + char mac_str[18] = ""; + + if (!csm || !lif) + return 0; + + if (add) + goto add_arp; + else + goto del_arp; + + /* Process Add */ + add_arp: + if (MLACP(csm).current_state != MLACP_STATE_EXCHANGE) + return 0; + + TAILQ_FOREACH(msg, &MLACP(csm).arp_list, tail) + { + mac_str[0] = '\0'; + arp_msg = (struct ARPMsg*)msg->buf; + + /* only process add*/ + if (arp_msg->op_type == ARP_SYNC_DEL) + continue; + + /* find the ARP for lif_list*/ + if (strcmp(lif->name, arp_msg->ifname) != 0) + continue; + + sprintf(mac_str, "%02x:%02x:%02x:%02x:%02x:%02x", arp_msg->mac_addr[0], arp_msg->mac_addr[1], arp_msg->mac_addr[2], + arp_msg->mac_addr[3], arp_msg->mac_addr[4], arp_msg->mac_addr[5]); + + mlacp_fsm_arp_set(arp_msg->ifname, arp_msg->ipv4_addr, mac_str); + /*ICCPD_LOG_DEBUG(__FUNCTION__, "Add dynamic ARP to kernel [%s]", + show_ip_str(htonl(arp_msg->ipv4_addr)));*/ + } + goto done; + + del_arp: + /* Process Del */ + TAILQ_FOREACH(msg, &MLACP(csm).arp_list, tail) + { + arp_msg = (struct ARPMsg*)msg->buf; + + /* find the ARP for lif_list*/ + if (strcmp(lif->name, arp_msg->ifname) != 0) + continue; + + /* don't process del*/ + if (arp_msg->op_type == ARP_SYNC_DEL) + continue; + + /* link broken, del all dynamic arp on the lif*/ + mlacp_fsm_arp_del(arp_msg->ifname, arp_msg->ipv4_addr); + /*ICCPD_LOG_DEBUG(__FUNCTION__, "Del dynamic ARP [%s]", + show_ip_str(htonl(arp_msg->ipv4_addr)));*/ + } + + done: + return 0; +} + +/***************************************** + * Port-Channel Status Handler + * + ****************************************/ +static void set_route_by_linux_route(struct CSM* csm, + struct LocalInterface *local_if, + int is_add) +{ + /* TODO Need to remove this function + when set static route with zebra works fine*/ + + char ipv4_dest_str[INET_ADDRSTRLEN]; + char syscmd[128]; + char *ptr; + int ret = 0; + + /* enable kernel forwarding support*/ + system("echo 1 > /proc/sys/net/ipv4/ip_forward"); + + if (!csm || !local_if) + return; + + sprintf(ipv4_dest_str, "%s", show_ip_str(htonl(local_if->ipv4_addr))); + ptr = strrchr(ipv4_dest_str, '.'); strcpy(ptr, ".0\0"); + +#if 1 + /* set gw route*/ + /*sprintf(syscmd, "ip route %s %s/%d proto static metric 200 nexthop via %s > /dev/null 2>&1",*/ + sprintf(syscmd, "ip route %s %s/%d metric 200 nexthop via %s > /dev/null 2>&1", + (is_add) ? "add" : "del", ipv4_dest_str, local_if->prefixlen, + csm->peer_ip); +#else + // set interface route + sprintf(syscmd, "route %s -net %s/%d %s > /dev/null 2>&1", + (is_add) ? "add" : "del", ipv4_dest_str, local_if->prefixlen, + local_if->name); +#endif + + ret = system(syscmd); + ICCPD_LOG_DEBUG(__FUNCTION__, "%s ret = %d", syscmd, ret); + + return; +} + +static void update_vlan_if_info(struct CSM *csm, + struct LocalInterface *local_if, + struct LocalInterface *vlan_if, + int po_state) +{ + if (!csm || !local_if || !vlan_if) + return; + + vlan_if->mlacp_state = MLACP(csm).current_state; + + return; +} + +static void update_l3_if_info(struct CSM *csm, + struct LocalInterface *local_if, + struct LocalInterface *l3_if, + int po_state) +{ + if (!csm || !l3_if) + return; + + l3_if->mlacp_state = MLACP(csm).current_state; + + return; +} + +static void update_po_if_info(struct CSM *csm, + struct LocalInterface *local_if, + int po_state) +{ + if (!csm || !local_if) + return; + + /* update local po info*/ + if (local_if->po_active != po_state) + { + local_if->changed = 1; + local_if->po_active = (po_state != 0); + /*printf("update po [%s=%d]\n",local_if->name, local_if->po_active);*/ + } + local_if->mlacp_state = MLACP(csm).current_state; + + return; +} + +static void set_l3_itf_state(struct CSM *csm, + struct LocalInterface *set_l3_local_if, + ROUTE_MANIPULATE_TYPE_E route_type) +{ + if (!csm || !set_l3_local_if) + return; + + if (set_l3_local_if && (route_type != ROUTE_NONE)) + { + /*set_default_route(csm);*/ + + /*ICCPD_LOG_DEBUG(__FUNCTION__, " route set Interface = %s route type = %d route = %s nexthop via = %s ", + set_l3_local_if->name, route_type, show_ip_str(htonl(set_l3_local_if->ipv4_addr)), csm->peer_ip );*/ + + /* set static route*/ + if (route_type == ROUTE_ADD) + { + /*set_route_by_linux_route(csm, set_l3_local_if, 1);*/ /*add static route by linux route tool*/ + /*If the L3 intf is not Vlan, del ARP; else wait ARP age*/ + if (strncmp(set_l3_local_if->name, VLAN_PREFIX, 4) != 0) + { + arp_set_handler(csm, set_l3_local_if, 0); /* del arp*/ + } + } + else if (route_type == ROUTE_DEL) + { + /*set_route_by_linux_route(csm, set_l3_local_if, 0);*/ /*del static route by linux route tool*/ + arp_set_handler(csm, set_l3_local_if, 1); /* add arp*/ + } + } + + return; +} + +static int peer_po_is_alive(struct CSM *csm, int po_ifindex) +{ + struct PeerInterface *pif = NULL; + int pif_active = 0; + + if (!csm) + return 0; + + LIST_FOREACH(pif, &(MLACP(csm).pif_list), mlacp_next) + { + if (pif->type != IF_T_PORT_CHANNEL) + continue; + if (pif->po_id != po_ifindex) + continue; + + if (pif->po_active) + pif_active = 1; /*pif alive*/ + break; + } + + return pif_active; +} + +static void mlacp_clean_fdb(void) +{ + struct IccpSyncdHDr * msg_hdr; + char *msg_buf = g_csm_buf; + + struct System *sys; + + sys = system_get_instance(); + if (sys == NULL) + return; + memset(msg_buf, 0, CSM_BUFFER_SIZE); + msg_hdr = (struct IccpSyncdHDr *)msg_buf; + msg_hdr->ver = 1; + msg_hdr->type = MCLAG_MSG_TYPE_FLUSH_FDB; + msg_hdr->len = sizeof(struct IccpSyncdHDr); + + if (sys->sync_fd) + write(sys->sync_fd, msg_buf, msg_hdr->len); + + ICCPD_LOG_DEBUG(__FUNCTION__, "Notify mclagsyncd to clear FDB"); + + return; +} + +void set_peerlink_mlag_port_learn(struct LocalInterface *lif, int enable) +{ + struct IccpSyncdHDr * msg_hdr; + mclag_sub_option_hdr_t * sub_msg; + char *msg_buf = g_csm_buf; + int msg_len; + struct System *sys; + + sys = system_get_instance(); + if (sys == NULL) + return; + + if (!lif) + return; + memset(msg_buf, 0, CSM_BUFFER_SIZE); + msg_hdr = (struct IccpSyncdHDr *)msg_buf; + msg_hdr->ver = 1; + msg_hdr->type = MCLAG_MSG_TYPE_PORT_MAC_LEARN_MODE; + + msg_hdr->len = sizeof(struct IccpSyncdHDr); + + sub_msg = (mclag_sub_option_hdr_t*)&msg_buf[msg_hdr->len]; + sub_msg->op_type = MCLAG_SUB_OPTION_TYPE_MAC_LEARN_DISABLE; + + if (enable) + sub_msg->op_type = MCLAG_SUB_OPTION_TYPE_MAC_LEARN_ENABLE; + + msg_len = strlen(lif->name); + memcpy(sub_msg->data, lif->name, msg_len); + + sub_msg->op_len = msg_len; + msg_hdr->len += sizeof(mclag_sub_option_hdr_t); + msg_hdr->len += sub_msg->op_len; + + ICCPD_LOG_DEBUG(__FUNCTION__, "Send %s port MAC learn msg to mclagsyncd for %s", + sub_msg->op_type == MCLAG_SUB_OPTION_TYPE_MAC_LEARN_DISABLE ? "DISABLE":"ENABLE", lif->name); + + /*send msg*/ + if (sys->sync_fd) + write(sys->sync_fd, msg_buf, msg_hdr->len); + + return; +} + +static void set_peerlink_mlag_port_kernel_forward( + struct CSM *csm, + struct LocalInterface *lif, + int enable) +{ + if (!csm || !csm->peer_link_if || !lif) + return; + + char cmd[256] = { 0 }; + + sprintf(cmd, "ebtables %s FORWARD -i %s -o %s -j DROP", + "-D", csm->peer_link_if->name, lif->name); + ICCPD_LOG_DEBUG(__FUNCTION__, " ebtable cmd %s", cmd ); + system(cmd); + + sprintf(cmd, "ebtables %s FORWARD -i %s -o %s -j DROP", + (enable) ? "-A" : "-D", csm->peer_link_if->name, lif->name); + ICCPD_LOG_DEBUG(__FUNCTION__, " ebtable cmd %s", cmd ); + system(cmd); + + return; +} + +void update_peerlink_isolate_from_all_csm_lif( + struct CSM* csm) +{ + struct LocalInterface *lif = NULL; + struct IccpSyncdHDr * msg_hdr; + mclag_sub_option_hdr_t * sub_msg; + char msg_buf[4096]; + struct System *sys; + + char mlag_po_buf[512]; + int src_len = 0, dst_len = 0; + + sys = system_get_instance(); + if (sys == NULL) + return; + + if (!csm || !csm->peer_link_if) + return; + + memset(msg_buf, 0, 4095); + memset(mlag_po_buf, 0, 511); + + msg_hdr = (struct IccpSyncdHDr *)msg_buf; + msg_hdr->ver = 1; + msg_hdr->type = MCLAG_MSG_TYPE_PORT_ISOLATE; + msg_hdr->len = sizeof(struct IccpSyncdHDr); + + /*sub msg src*/ + sub_msg = (mclag_sub_option_hdr_t *)&msg_buf[msg_hdr->len]; + sub_msg->op_type = MCLAG_SUB_OPTION_TYPE_ISOLATE_SRC; + + if (csm->peer_link_if->type == IF_T_VXLAN) + { + /*TBD: vxlan tunnel port isolation will be supportted later*/ + return; +#if 0 + int begin_eth_port = 0; + + /*VTTNL0001;Ethernet0001,Ethernet0002*/ + /*src_len= strlen(csm->peer_link_if->name); */ + src_len += snprintf(src_buf + src_len, sizeof(src_buf) - src_len, "%s", csm->peer_link_if->name); + src_len += snprintf(src_buf + src_len, sizeof(src_buf) - src_len, "%s", ";"); + + /*traverse all ethernet port */ + LIST_FOREACH(lif, &(sys->lif_list), system_next) + { + if (lif->type != IF_T_PORT) + continue; + + /* need to isolate port, get it's name */ + if (begin_eth_port != 0) + { + src_len += snprintf(src_buf + src_len, sizeof(src_buf) - src_len, "%s", ","); + } + + src_len += snprintf(src_buf + src_len, sizeof(src_buf) - src_len, "%s", lif->name); + begin_eth_port = 1; + } + memcpy(sub_msg->data, src_buf, src_len); + + ICCPD_LOG_DEBUG(__FUNCTION__, "isolate src %s, data %s, len %d", src_buf, sub_msg->data, src_len); +#endif + } + else + { + src_len = strlen(csm->peer_link_if->name); + memcpy(sub_msg->data, csm->peer_link_if->name, src_len); + } + sub_msg->op_len = src_len; + + /*sub msg dst */ + msg_hdr->len += sub_msg->op_len; + msg_hdr->len += sizeof(mclag_sub_option_hdr_t); + sub_msg = (mclag_sub_option_hdr_t *)&msg_buf[msg_hdr->len]; + sub_msg->op_type = MCLAG_SUB_OPTION_TYPE_ISOLATE_DST; + + /*traverse all portchannel member port and send msg to syncd */ + LIST_FOREACH(lif, &(MLACP(csm).lif_list), mlacp_next) + { + if (lif->type != IF_T_PORT_CHANNEL) + continue; + + /* check pif port state and lif pochannel state */ + if (lif->isolate_to_peer_link == 1) + { + /* need to isolate port, get it's member name */ + if (strlen(mlag_po_buf) != 0) + dst_len += snprintf(mlag_po_buf + dst_len, sizeof(mlag_po_buf) - dst_len, "%s", ","); + + dst_len += snprintf(mlag_po_buf + dst_len, sizeof(mlag_po_buf) - dst_len, "%s", lif->portchannel_member_buf); + } + } + + sub_msg->op_len = dst_len; + msg_hdr->len += sizeof(mclag_sub_option_hdr_t); + msg_hdr->len += sub_msg->op_len; + + if (dst_len) + { + memcpy(sub_msg->data, mlag_po_buf, dst_len); + ICCPD_LOG_DEBUG(__FUNCTION__, "Send port isolate msg to mclagsyncd, src port %s, dst port %s", csm->peer_link_if->name, mlag_po_buf); + } + else + { + ICCPD_LOG_DEBUG(__FUNCTION__, "Send port isolate msg to mclagsyncd, src port %s, dst port is NULL", csm->peer_link_if->name); + } + + /*send msg*/ + if (sys->sync_fd) + write(sys->sync_fd, msg_buf, msg_hdr->len); + + return; +} + +static void set_peerlink_mlag_port_isolate( + struct CSM *csm, + struct LocalInterface *lif, + int enable) +{ + if (!lif) + return; + + lif->isolate_to_peer_link = enable; + + if (!csm || !csm->peer_link_if ) + return; + + if (MLACP(csm).current_state != MLACP_STATE_EXCHANGE) + return; + + ICCPD_LOG_DEBUG(__FUNCTION__, "%s port-isolate from %s to %s", + enable ? "Enable" : "Disable", csm->peer_link_if->name, lif->name); + update_peerlink_isolate_from_all_csm_lif(csm); + + /* Kernel also needs to block traffic from peerlink to mlag-port*/ + set_peerlink_mlag_port_kernel_forward(csm, lif, enable); + + return; +} + +void peerlink_port_isolate_cleanup(struct CSM* csm) +{ + struct LocalInterface *local_if = NULL; + + if (!csm) + return; + + /* Clean all port block*/ + LIST_FOREACH(local_if, &(MLACP(csm).lif_list), mlacp_next) + { + if (local_if->type == IF_T_PORT_CHANNEL) + { + set_peerlink_mlag_port_isolate(csm, local_if, 0); + } + } + + return; +} + +void update_peerlink_isolate_from_pif( + struct CSM *csm, + struct PeerInterface *pif, + int pif_po_state, + int new_create) +{ + struct LocalInterface *lif = NULL; + int lif_po_state = 1; + + if (!csm || !csm->peer_link_if || !pif) + return; + if (new_create == 0 && pif_po_state == pif->po_active) + return; + if (MLACP(csm).current_state != MLACP_STATE_EXCHANGE) + return; + + /* peer link changed*/ + LIST_FOREACH(lif, &(MLACP(csm).lif_list), mlacp_next) + { + if (strcmp(lif->name, pif->name) != 0) + continue; + + lif_po_state = lif->po_active; + break; + } + + if (!lif) + { + ICCPD_LOG_WARN(__FUNCTION__, "Can't find local if for %s", pif->name); + return; + } + + ICCPD_LOG_DEBUG(__FUNCTION__, "From if %s local(%s) / peer(%s)", + lif->name, + (lif_po_state) ? "up" : "down", + (pif_po_state) ? "up" : "down"); + + if (lif_po_state == 1) + { + if (pif_po_state == 1) + { + /* both peer-pair link up, enable port-isolate*/ + ICCPD_LOG_DEBUG(__FUNCTION__, "Enable port-isolate from %s to %s", + csm->peer_link_if->name, lif->name); + set_peerlink_mlag_port_isolate(csm, lif, 1); + } + else + { + /* local link up, and peer link changes to down, disable port-isolate*/ + ICCPD_LOG_DEBUG(__FUNCTION__, "Disable port-isolate from %s to %s", + csm->peer_link_if->name, lif->name); + set_peerlink_mlag_port_isolate(csm, lif, 0); + } + } + else + { + ;/* local link down, do nothing*/ + } + + return; +} + +static void update_peerlink_isolate_from_lif( + struct CSM* csm, + struct LocalInterface* lif, + int lif_po_state) +{ + struct PeerInterface *pif = NULL; + int pif_po_state = 1; + + if (!csm || !csm->peer_link_if || !lif) + return; + /*if (lif_po_state == lif->po_active) return;*/ + if (MLACP(csm).current_state != MLACP_STATE_EXCHANGE) + return; + + /* local link changed*/ + LIST_FOREACH(pif, &(MLACP(csm).pif_list), mlacp_next) + { + if (strcmp(pif->name, lif->name) != 0) + continue; + + pif_po_state = pif->po_active; + break; + } + + ICCPD_LOG_DEBUG(__FUNCTION__, "From if %s local(%s) / peer(%s)", + lif->name, (lif_po_state) ? "up" : "down", (pif_po_state) ? "up" : "down"); + + if (lif_po_state == 1) + { + if (pif_po_state == 1) + { + /* both peer-pair link up, enable port-isolate*/ + ICCPD_LOG_DEBUG(__FUNCTION__, "Enable port-isolate from %s to %s", + csm->peer_link_if->name, lif->name); + set_peerlink_mlag_port_isolate(csm, lif, 1); + } + else + { + /* peer link down, local link changes to up, disable port-isolate*/ + ICCPD_LOG_DEBUG(__FUNCTION__, " Disable port-isolate from %s to %s", + csm->peer_link_if->name, lif->name); + set_peerlink_mlag_port_isolate(csm, lif, 0); + } + } + else + { + ;/* local link down, do nothing*/ + } + + return; +} + +static void update_l2_po_state(struct CSM *csm, + struct LocalInterface *lif, + int po_state) +{ + ROUTE_MANIPULATE_TYPE_E route_type = ROUTE_NONE; + struct VLAN_ID *vlan = NULL; + struct LocalInterface *set_l3_vlan_if = NULL; + + if (!csm || !lif) + return; + + /*L2 po*/ + /*if (po_state != lif->po_active && po_state == 0) + { + mlacp_clean_fdb(); + }*/ + + /*Is there any L3 vlan over L2 po?*/ + LIST_FOREACH(vlan, &(lif->vlan_list), port_next) + { + route_type = ROUTE_NONE; + + if (!vlan->vlan_itf) + continue; + + /* If the po is under a vlan, update vlan state first*/ + update_vlan_if_info(csm, lif, vlan->vlan_itf, po_state); + + if (!local_if_is_l3_mode(vlan->vlan_itf)) + continue; + + /*NOTE + * assume only one mlag per vlan + * need to add rules for per mlag per vlan later (arp list?) + */ + set_l3_vlan_if = vlan->vlan_itf; + if (po_state != lif->po_active + || MLACP(csm).current_state != set_l3_vlan_if->mlacp_state) + { + if (po_state == 1) + { + route_type = ROUTE_DEL; + } + else if (po_state == 0 + && MLACP(csm).current_state == MLACP_STATE_EXCHANGE) + { + if (peer_po_is_alive(csm, lif->ifindex) == 1) + route_type = ROUTE_ADD; + } + } + + /*update_po_arp_list(csm, set_l3_vlan_if);*/ + set_l3_itf_state(csm, set_l3_vlan_if, route_type); + update_l3_if_info(csm, lif, set_l3_vlan_if, po_state); + } + + return; +} + +static void update_l3_po_state(struct CSM *csm, + struct LocalInterface *lif, + int po_state) +{ + ROUTE_MANIPULATE_TYPE_E route_type = ROUTE_NONE; + struct LocalInterface *set_l3_lif = NULL; + + /*L3 po*/ + set_l3_lif = lif; + + if (!csm || !lif) + return; + + if (po_state != lif->po_active + && po_state == 1) + { + /* po alive, clean static route & recover the ARP*/ + route_type = ROUTE_DEL; + } + else if (po_state != lif->po_active + && po_state == 0 + && MLACP(csm).current_state == MLACP_STATE_EXCHANGE) + { + /* po is not alive & peer-link alive, set static route*/ + if (peer_po_is_alive(csm, lif->po_id) == 1) + route_type = ROUTE_ADD; + } + else if (MLACP(csm).current_state != lif->mlacp_state + && MLACP(csm).current_state == MLACP_STATE_EXCHANGE + && po_state == 0) + { + /* when peer-pair link ready, set static route for broken po link*/ + if (peer_po_is_alive(csm, lif->po_id) == 1) + route_type = ROUTE_ADD; + } + + /*update_po_arp_list(csm, set_l3_lif);*/ + set_l3_itf_state(csm, set_l3_lif, route_type); + update_l3_if_info(csm, lif, set_l3_lif, po_state); + + return; +} + +int is_local_vlan_on(struct VLAN_ID* vlan_id_list) +{ + if (!vlan_id_list->vlan_itf) + return 0; + + return 1; +} + +void syn_arp_info_to_peer(struct CSM *csm, struct LocalInterface *local_if) +{ + struct Msg *msg = NULL; + struct ARPMsg *arp_msg = NULL, *arp_info = NULL; + struct Msg *msg_send = NULL; + + if (!csm || !local_if) + return; + + if (!TAILQ_EMPTY(&(MLACP(csm).arp_list))) + { + TAILQ_FOREACH(msg, &MLACP(csm).arp_list, tail) + { + arp_info = (struct ARPMsg*)msg->buf; + + if (strcmp(arp_info->ifname, local_if->name) != 0) + continue; + + arp_msg = (struct ARPMsg*)msg->buf; + arp_msg->op_type = ARP_SYNC_ADD; + + if (iccp_csm_init_msg(&msg_send, (char*)arp_msg, sizeof(struct ARPMsg)) == 0) + { + TAILQ_INSERT_TAIL(&(MLACP(csm).arp_msg_list), msg_send, tail); + /*ICCPD_LOG_DEBUG( __FUNCTION__, "Enqueue ARP[ADD] for %s", + show_ip_str(htonl(arp_msg->ipv4_addr)));*/ + } + else + ICCPD_LOG_WARN(__FUNCTION__, "Failed to enqueue ARP[ADD] for %s", + show_ip_str(htonl(arp_msg->ipv4_addr))); + } + } + + return; +} + +void update_stp_peer_link(struct CSM *csm, + struct PeerInterface *pif, + int po_state, int new_create) +{ + struct LocalInterface *lif = NULL; + struct VLAN_ID *vlan = NULL; + + if (!csm || !pif) + return; + if (new_create == 0 && po_state == pif->po_active) + return; + + LIST_FOREACH(lif, &(MLACP(csm).lif_list), mlacp_next) + { + if (strcmp(lif->name, pif->name) != 0) + continue; + + /* update lif route if pif link status changes */ + if (local_if_is_l3_mode(lif)) + { + if (po_state == 1 && lif->po_active == 0) + set_l3_itf_state(csm, lif, ROUTE_ADD); + else if (po_state == 0 && lif->po_active == 0) + set_l3_itf_state(csm, lif, ROUTE_DEL); + + /*If pif change to active, and local is also active, syn arp to peer*/ + if (po_state == 1 && lif->po_active == 1) + { + syn_arp_info_to_peer(csm, lif); + } + } + else + { + LIST_FOREACH(vlan, &(lif->vlan_list), port_next) + { + if (!is_local_vlan_on(vlan)) + continue; + if (!local_if_is_l3_mode(vlan->vlan_itf)) + continue; + + /*NOTE + * assume only one mlag per bridge + * need to add rules for per mlag per bridge later (arp list?) + */ + if (po_state == 1 && lif->po_active == 0) + set_l3_itf_state(csm, vlan->vlan_itf, ROUTE_ADD); + else if (po_state == 0 && lif->po_active == 0) + set_l3_itf_state(csm, vlan->vlan_itf, ROUTE_DEL); + + /*If pif change to active, and local is also active, syn arp to peer*/ + if (po_state == 1 && lif->po_active == 1) + { + syn_arp_info_to_peer(csm, vlan->vlan_itf); + } + } + } + + break; + } + + return; +} + +void iccp_get_fdb_change_from_syncd( void) +{ + struct IccpSyncdHDr * msg_hdr; + char msg_buf[512]; + struct System *sys; + + sys = system_get_instance(); + if (sys == NULL) + return; + + memset(msg_buf, 0, 512); + + msg_hdr = (struct IccpSyncdHDr *)msg_buf; + msg_hdr->ver = 1; + msg_hdr->type = MCLAG_MSG_TYPE_GET_FDB_CHANGES; + msg_hdr->len = sizeof(struct IccpSyncdHDr); + + ICCPD_LOG_DEBUG(__FUNCTION__, "Send get fdb change msg to mclagsyncd"); + + /*send msg*/ + if (sys->sync_fd > 0) + write(sys->sync_fd, msg_buf, msg_hdr->len); + + return; +} + +void iccp_send_fdb_entry_to_syncd( struct MACMsg* mac_msg, uint8_t mac_type) +{ + struct IccpSyncdHDr * msg_hdr; + char msg_buf[512]; + struct System *sys; + struct mclag_fdb_info * mac_info; + + sys = system_get_instance(); + if (sys == NULL) + return; + + memset(msg_buf, 0, 512); + + msg_hdr = (struct IccpSyncdHDr *)msg_buf; + msg_hdr->ver = 1; + msg_hdr->type = MCLAG_MSG_TYPE_SET_FDB; + + /*mac msg */ + mac_info = (struct mclag_fdb_info *)&msg_buf[sizeof(struct IccpSyncdHDr)]; + mac_info->vid = mac_msg->vid; + memcpy(mac_info->port_name, mac_msg->ifname, MAX_L_PORT_NAME); + memcpy(mac_info->mac, mac_msg->mac_str, ETHER_ADDR_STR_LEN); + mac_info->type = mac_type; + mac_info->op_type = mac_msg->op_type; + msg_hdr->len = sizeof(struct IccpSyncdHDr) + sizeof(struct mclag_fdb_info); + + ICCPD_LOG_DEBUG(__FUNCTION__, "Send mac %s msg to mclagsyncd, vid %d ; ifname %s ; mac %s; type %s", + mac_info->op_type == MAC_SYNC_ADD ? "add" : "del", mac_info->vid, mac_info->port_name, mac_info->mac, mac_info->type == MAC_TYPE_STATIC ? "static" : "dynamic"); + + /*send msg*/ + if (sys->sync_fd > 0 ) + write(sys->sync_fd, msg_buf, msg_hdr->len); + + return; +} + +void add_mac_to_chip(struct MACMsg* mac_msg, uint8_t mac_type) +{ + mac_msg->op_type = MAC_SYNC_ADD; + iccp_send_fdb_entry_to_syncd( mac_msg, mac_type); + + return; +} + +void del_mac_from_chip(struct MACMsg* mac_msg) +{ + mac_msg->op_type = MAC_SYNC_DEL; + iccp_send_fdb_entry_to_syncd( mac_msg, mac_msg->fdb_type); + + return; +} + +uint8_t set_mac_local_age_flag(struct CSM *csm, struct MACMsg* mac_msg, uint8_t set ) +{ + uint8_t new_age_flag = 0; + struct Msg *msg = NULL; + + new_age_flag = mac_msg->age_flag; + + if (set == 0)/*remove age flag*/ + { + new_age_flag &= ~MAC_AGE_LOCAL; + + ICCPD_LOG_DEBUG(__FUNCTION__, "Remove local age flag: %d ifname %s, add %s vlan-id %d, age_flag %d", + new_age_flag, mac_msg->ifname, mac_msg->mac_str, mac_msg->vid, mac_msg->age_flag); + + /*send mac MAC_SYNC_ADD message to peer*/ + if (MLACP(csm).current_state == MLACP_STATE_EXCHANGE) + { + mac_msg->op_type = MAC_SYNC_ADD; + if (iccp_csm_init_msg(&msg, (char*)mac_msg, sizeof(struct MACMsg)) == 0) + { + TAILQ_INSERT_TAIL(&(MLACP(csm).mac_msg_list), msg, tail); + /*ICCPD_LOG_DEBUG(__FUNCTION__, "MAC-msg-list enqueue: %s, add %s vlan-id %d, age_flag %d", + mac_msg->ifname, mac_msg->mac_str, mac_msg->vid, mac_msg->age_flag);*/ + } + else + { + ICCPD_LOG_WARN(__FUNCTION__, "Failed to enqueue MAC-msg-list: %s, add %s vlan-id %d, age_flag %d", + mac_msg->ifname, mac_msg->mac_str, mac_msg->vid, mac_msg->age_flag); + } + } + } + else/*set age flag*/ + { + new_age_flag |= MAC_AGE_LOCAL; + + ICCPD_LOG_DEBUG(__FUNCTION__, "Add local age flag: %s, add %s vlan-id %d, age_flag %d", + mac_msg->ifname, mac_msg->mac_str, mac_msg->vid, mac_msg->age_flag); + + /*send mac MAC_SYNC_DEL message to peer*/ + if (MLACP(csm).current_state == MLACP_STATE_EXCHANGE) + { + mac_msg->op_type = MAC_SYNC_DEL; + if (iccp_csm_init_msg(&msg, (char*)mac_msg, sizeof(struct MACMsg)) == 0) + { + TAILQ_INSERT_TAIL(&(MLACP(csm).mac_msg_list), msg, tail); + /*ICCPD_LOG_DEBUG(__FUNCTION__, "MAC-msg-list enqueue: %s, add %s vlan-id %d, age_flag %d", + mac_msg->ifname, mac_msg->mac_str, mac_msg->vid, mac_msg->age_flag);*/ + } + else + { + ICCPD_LOG_WARN(__FUNCTION__, "Failed to enqueue MAC-msg-list: %s, del %s vlan-id %d, age_flag %d", + mac_msg->ifname, mac_msg->mac_str, mac_msg->vid, mac_msg->age_flag); + } + } + } + + return new_age_flag; +} + +/*Deal with mac add,del,move when portchannel up or down*/ +static void update_l2_mac_state(struct CSM *csm, + struct LocalInterface *lif, + int po_state) +{ + struct Msg* msg = NULL; + struct MACMsg* mac_msg = NULL; + + if (!csm || !lif) + return; + + TAILQ_FOREACH(msg, &MLACP(csm).mac_list, tail) + { + mac_msg = (struct MACMsg*)msg->buf; + + /* find the MAC for this interface*/ + if (strcmp(lif->name, mac_msg->origin_ifname) != 0) + continue; + + /*portchannel down*/ + if (po_state == 0) + { + ICCPD_LOG_DEBUG(__FUNCTION__, "Intf %s down, age local MAC %s vlan-id %d", + mac_msg->ifname, mac_msg->mac_str, mac_msg->vid); + + mac_msg->age_flag = set_mac_local_age_flag(csm, mac_msg, 1); + + if (mac_msg->age_flag == (MAC_AGE_LOCAL | MAC_AGE_PEER)) + { + /*send mac del message to mclagsyncd.*/ + if (mac_msg->fdb_type != MAC_TYPE_STATIC) + del_mac_from_chip(mac_msg); + + ICCPD_LOG_DEBUG(__FUNCTION__, "Intf %s down, del MAC %s vlan-id %d", + mac_msg->ifname, mac_msg->mac_str, mac_msg->vid); + + /*If local and peer both aged, del the mac*/ + TAILQ_REMOVE(&(MLACP(csm).mac_list), msg, tail); + free(msg->buf); + free(msg); + } + else + { + /*If local is aged but peer is not aged, redirect the mac to peer-link*/ + if (strlen(csm->peer_itf_name) != 0) + { + /*Send mac add message to mclagsyncd. fdb_type is not changed*/ + /*Is need to delete the old item before add?(Old item probably is static)*/ + if (csm->peer_link_if && csm->peer_link_if->state == PORT_STATE_UP) + { + memcpy(mac_msg->ifname, csm->peer_itf_name, IFNAMSIZ); + add_mac_to_chip(mac_msg, MAC_TYPE_DYNAMIC); + } + else + { + /*must redirect but peerlink is down, del mac from ASIC*/ + /*if peerlink change to up, mac will add back to ASIC*/ + del_mac_from_chip(mac_msg); + memcpy(mac_msg->ifname, csm->peer_itf_name, IFNAMSIZ); + } + + ICCPD_LOG_DEBUG(__FUNCTION__, "Intf %s down, redirect MAC %s vlan-id %d to peer-link %s", + mac_msg->ifname, mac_msg->mac_str, mac_msg->vid, csm->peer_itf_name); + } + else + { + /*peer-link is not configured, del mac from ASIC, mac still in mac_list*/ + del_mac_from_chip(mac_msg); + + ICCPD_LOG_DEBUG(__FUNCTION__, "Intf %s down, peer-link is not configured: MAC %s vlan-id %d", + mac_msg->ifname, mac_msg->mac_str, mac_msg->vid); + } + } + } + else /*portchannel up*/ + { + /*the old item is redirect to peerlink for portchannel down*/ + /*when this portchannel up, recover the mac back*/ + if (strcmp(mac_msg->ifname, csm->peer_itf_name) == 0) + { + ICCPD_LOG_DEBUG(__FUNCTION__, "Intf %s up, redirect MAC %s vlan-id %d from peerlink to %s", + mac_msg->origin_ifname, mac_msg->mac_str, mac_msg->vid, mac_msg->origin_ifname); + + /*Remove MAC_AGE_LOCAL flag*/ + mac_msg->age_flag = set_mac_local_age_flag(csm, mac_msg, 0); + + /*Reverse interface from peer-link to the original portchannel*/ + memcpy(mac_msg->ifname, mac_msg->origin_ifname, MAX_L_PORT_NAME); + + /*Send dynamic or static mac add message to mclagsyncd*/ + add_mac_to_chip(mac_msg, mac_msg->fdb_type); + } + else + { + /*this may be peerlink is not configured and portchannel is down*/ + /*when this portchannel up, add the mac back to ASIC*/ + ICCPD_LOG_DEBUG(__FUNCTION__, "Intf %s up, add MAC %s vlan-id %d to ASIC", + mac_msg->ifname, mac_msg->mac_str, mac_msg->vid); + + /*Remove MAC_AGE_LOCAL flag*/ + mac_msg->age_flag = set_mac_local_age_flag(csm, mac_msg, 0); + + /*Send dynamic or static mac add message to mclagsyncd*/ + add_mac_to_chip(mac_msg, mac_msg->fdb_type); + } + } + } + + return; +} + +void mlacp_portchannel_state_handler(struct CSM* csm, + struct LocalInterface* local_if, + int po_state) +{ + if (!csm || !local_if) + return; + + update_peerlink_isolate_from_lif(csm, local_if, po_state); + + update_l2_mac_state(csm, local_if, po_state); + + if (!local_if_is_l3_mode(local_if)) + update_l2_po_state(csm, local_if, po_state); + else + update_l3_po_state(csm, local_if, po_state); + + update_po_if_info(csm, local_if, po_state); + + return; +} + +static void mlacp_conn_handler_fdb(struct CSM* csm) +{ + struct Msg* msg = NULL; + struct MACMsg* mac_msg = NULL; + struct Msg *msg_send = NULL; + + if (!csm) + return; + + if (!TAILQ_EMPTY(&(MLACP(csm).mac_list))) + { + TAILQ_FOREACH(msg, &MLACP(csm).mac_list, tail) + { + mac_msg = (struct MACMsg*)msg->buf; + + /*Wait the ACK from peer?*/ + /*mac_msg->age_flag &= ~MAC_AGE_PEER;*/ + + /*If MAC with local age flag, dont sync to peer. Such MAC only exist when peer is warm-reboot. + If peer is warm-reboot, peer age flag is not set when connection is lost. + When MAC is aged in local switch, this MAC is not deleted for no peer age flag. + After warm-reboot, this MAC must be learnt by peer and sync to local switch*/ + if (!(mac_msg->age_flag & MAC_AGE_LOCAL)) + { + /*Send mac add message to peer*/ + mac_msg->op_type = MAC_SYNC_ADD; + if (iccp_csm_init_msg(&msg_send, (char*)mac_msg, sizeof(struct MACMsg)) == 0) + { + mac_msg->age_flag &= ~MAC_AGE_PEER; + TAILQ_INSERT_TAIL(&(MLACP(csm).mac_msg_list), msg_send, tail); + ICCPD_LOG_DEBUG(__FUNCTION__, "MAC-msg-list enqueue: %s, add %s vlan-id %d", + mac_msg->ifname, mac_msg->mac_str, mac_msg->vid); + } + } + else + { + /*If MAC with local age flag and is point to MCLAG enabled port, reomove local age flag*/ + if (strcmp(mac_msg->ifname, csm->peer_itf_name) != 0) + { + ICCPD_LOG_DEBUG(__FUNCTION__, "MAC-msg-list not enqueue for local age flag: %s, mac %s vlan-id %d, remove local age flag", + mac_msg->ifname, mac_msg->mac_str, mac_msg->vid); + mac_msg->age_flag &= ~MAC_AGE_LOCAL; + } + } + } + } + + return; +} + +static void mlacp_fix_bridge_mac(struct CSM* csm) +{ + char syscmd[128]; + int ret = 0; + char macaddr[64]; + uint8_t null_mac[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + if (memcmp(MLACP(csm).system_id, null_mac, ETHER_ADDR_LEN) != 0) + { + memset(macaddr, 0, 64); + snprintf(macaddr, 64, "%02x:%02x:%02x:%02x:%02x:%02x", + MLACP(csm).system_id[0], MLACP(csm).system_id[1], MLACP(csm).system_id[2], + MLACP(csm).system_id[3], MLACP(csm).system_id[4], MLACP(csm).system_id[5]); + + /*When changing the mac of a vlan member port, the mac of Bridge will be changed.*/ + /*The Bridge mac can not be the same as peer system id, so fix the Bridge MAC address here.*/ + sprintf(syscmd, "ip link set dev Bridge address %s > /dev/null 2>&1", macaddr); + ret = system(syscmd); + ICCPD_LOG_DEBUG(__FUNCTION__, " %s ret = %d", syscmd, ret); + } + + return; +} + +/***************************************** +* Peer connect/disconnect handler +* +* ***************************************/ +void mlacp_peer_conn_handler(struct CSM* csm) +{ + struct LocalInterface *lif = NULL; + static int once_connected = 0; + struct System* sys = NULL; + + if (!csm) + return; + + if ((sys = system_get_instance()) == NULL) + return; + + if (csm->warm_reboot_disconn_time != 0) + { + /*If peer reconnected, reset peer disconnect time*/ + csm->warm_reboot_disconn_time = 0; + ICCPD_LOG_DEBUG(__FUNCTION__, "Peer warm reboot and reconnect, reset peer disconnect time!"); + } + + if (csm->peer_link_if) + { + set_peerlink_mlag_port_learn(csm->peer_link_if, 0); + } + + /*If peer connect again, don't flush FDB*/ + if (once_connected == 0) + { + once_connected = 1; + mlacp_fix_bridge_mac(csm); + /*If warm reboot, don't flush FDB*/ + if (sys->warmboot_start != WARM_REBOOT) + mlacp_clean_fdb(); + } + + iccp_get_fdb_change_from_syncd(); + sys->csm_trans_time = time(NULL); + + mlacp_conn_handler_fdb(csm); + +#if 0 + // When peer-link ready, suppose all MLAG link are alive + LIST_FOREACH(pif, &(MLACP(csm).pif_list), mlacp_next) + { + pif->po_active = 1; + } +#endif + LIST_FOREACH(lif, &(MLACP(csm).lif_list), mlacp_next) + { + if (lif->type == IF_T_PORT_CHANNEL) + { +#if 0 + if (local_if_is_l3_mode(lif)) + { + set_route_by_linux_route( csm, lif, 1); + } + else + { + LIST_FOREACH(vlan, &(lif->vlan_list), port_next) + { + if (!vlan->vlan_itf) + continue; + if (!local_if_is_l3_mode(vlan->vlan_itf)) + continue; + + set_route_by_linux_route(csm, vlan->vlan_itf, 1); /* add static route by linux route tool*/ + } + } +#endif + + mlacp_portchannel_state_handler(csm, lif, (lif->state == PORT_STATE_UP) ? 1 : 0); + } + } + + return; +} + +extern void recover_if_ipmac_on_standby(struct LocalInterface* lif_po); +void mlacp_peer_disconn_handler(struct CSM* csm) +{ + uint8_t null_mac[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + struct LocalInterface* lif = NULL; + struct Msg* msg = NULL; + struct MACMsg* mac_msg = NULL; + struct System* sys = NULL; + + if (!csm) + return; + + if ((sys = system_get_instance()) == NULL) + return; + + /*If warm reboot, don't change FDB and MAC address*/ + if (sys->warmboot_exit == WARM_REBOOT) + return; + + /*If peer is warm reboot, don't change FDB*/ + if (csm->peer_warm_reboot_time != 0) + { + /*If peer disconnected, recover peer to normal reboot for next time*/ + csm->peer_warm_reboot_time = 0; + /*peer connection must be establised again within 90s + from last disconnection for peer warm reboot*/ + time(&csm->warm_reboot_disconn_time); + ICCPD_LOG_DEBUG(__FUNCTION__, "Peer warm reboot and disconnect, recover to normal reboot for next time!"); + return; + } + + TAILQ_FOREACH(msg, &MLACP(csm).mac_list, tail) + { + mac_msg = (struct MACMsg*)msg->buf; + + mac_msg->age_flag |= MAC_AGE_PEER; + ICCPD_LOG_DEBUG(__FUNCTION__, "Add peer age flag: %s, MAC %s vlan-id %d", + mac_msg->ifname, mac_msg->mac_str, mac_msg->vid); + + /* find the MAC that the port is peer-link or local and peer both aged, to be deleted*/ + if (strcmp(mac_msg->ifname, csm->peer_itf_name) != 0 && mac_msg->age_flag != (MAC_AGE_LOCAL | MAC_AGE_PEER)) + continue; + + ICCPD_LOG_DEBUG(__FUNCTION__, "Peer disconnect, del MAC for peer-link: %s, MAC %s vlan-id %d", + mac_msg->ifname, mac_msg->mac_str, mac_msg->vid); + + /*Send mac del message to mclagsyncd, may be already deleted*/ + del_mac_from_chip(mac_msg); + + TAILQ_REMOVE(&(MLACP(csm).mac_list), msg, tail); + free(msg->buf); + free(msg); + } + + /* Clean all port block*/ + peerlink_port_isolate_cleanup(csm); + + memcpy(MLACP(csm).remote_system.system_id, null_mac, ETHER_ADDR_LEN); + + /*If peer is disconnected, recover the MAC address.*/ + if (csm->role_type == STP_ROLE_STANDBY) + { + LIST_FOREACH(lif, &(MLACP(csm).lif_list), mlacp_next) + { + recover_if_ipmac_on_standby(lif); + } + } + + return; +} + +void mlacp_peerlink_up_handler(struct CSM* csm) +{ + struct Msg* msg = NULL; + struct MACMsg* mac_msg = NULL; + + if (!csm) + return; + + /*If peer link up, set all the mac that point to the peer-link in ASIC*/ + TAILQ_FOREACH(msg, &MLACP(csm).mac_list, tail) + { + mac_msg = (struct MACMsg*)msg->buf; + + /* Find the MAC that the port is peer-link to be added*/ + if (strcmp(mac_msg->ifname, csm->peer_itf_name) != 0) + continue; + + ICCPD_LOG_DEBUG(__FUNCTION__, "Peer link up, add MAC to ASIC for peer-link: %s, MAC %s vlan-id %d", + mac_msg->ifname, mac_msg->mac_str, mac_msg->vid); + + /*Send mac add message to mclagsyncd, local age flag is already set*/ + add_mac_to_chip(mac_msg, MAC_TYPE_DYNAMIC); + } + + return; +} + +void mlacp_peerlink_down_handler(struct CSM* csm) +{ + struct Msg* msg = NULL; + struct MACMsg* mac_msg = NULL; + + if (!csm) + return; + + /*If peer link down, remove all the mac that point to the peer-link*/ + TAILQ_FOREACH(msg, &MLACP(csm).mac_list, tail) + { + mac_msg = (struct MACMsg*)msg->buf; + + /* Find the MAC that the port is peer-link to be deleted*/ + if (strcmp(mac_msg->ifname, csm->peer_itf_name) != 0) + continue; + + ICCPD_LOG_DEBUG(__FUNCTION__, "Peer link down, del MAC for peer-link: %s, MAC %s vlan-id %d", + mac_msg->ifname, mac_msg->mac_str, mac_msg->vid); + + mac_msg->age_flag = set_mac_local_age_flag(csm, mac_msg, 1); + + /*Send mac del message to mclagsyncd*/ + del_mac_from_chip(mac_msg); + + /*If peer is not age, keep the MAC in mac_list, but ASIC is deleted*/ + if (mac_msg->age_flag == (MAC_AGE_LOCAL | MAC_AGE_PEER)) + { + /*If local and peer both aged, del the mac*/ + TAILQ_REMOVE(&(MLACP(csm).mac_list), msg, tail); + free(msg->buf); + free(msg); + } + } + + return; +} + +/***************************************** +* Po add/remove handler +* +*****************************************/ +void mlacp_mlag_link_add_handler(struct CSM *csm, struct LocalInterface *lif) +{ + if (!csm || !lif) + return; + if (MLACP(csm).current_state != MLACP_STATE_EXCHANGE) + return; + + set_peerlink_mlag_port_isolate(csm, lif, 1); + + return; +} + +void mlacp_mlag_link_del_handler(struct CSM *csm, struct LocalInterface *lif) +{ + if (!csm || !lif) + return; + + if (MLACP(csm).current_state != MLACP_STATE_EXCHANGE) + return; + + set_peerlink_mlag_port_isolate(csm, lif, 0); + + return; +} + +int iccp_connect_syncd() +{ + struct System* sys = NULL; + int ret = 0; + int fd = 0; + struct sockaddr_in serv; + static int count = 0; + struct epoll_event event; + + if ((sys = system_get_instance()) == NULL) + goto conn_fail; + + if (sys->sync_fd >= 0) + return 0; + + /*Print the fail log message every 60s*/ + if (count >= 600) + { + count = 0; + } + + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd < 0) + { + if (count == 0) + ICCPD_LOG_WARN(__FUNCTION__, "Failed to create unix socket: %s", strerror(errno)); + goto conn_fail; + } + + /* Make server socket. */ + memset(&serv, 0, sizeof(serv)); + serv.sin_family = AF_INET; + serv.sin_port = htons(2626); +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + serv.sin_len = sizeof(struct sockaddr_in); +#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ + serv.sin_addr.s_addr = htonl(0x7f000006); + + ret = connect(fd, (struct sockaddr *)&serv, sizeof(serv)); + if (ret < 0) + { + if (count == 0) + ICCPD_LOG_WARN(__FUNCTION__, "Failed to connect to mclag syncd: errno str %s", strerror(errno)); + close(fd); + goto conn_fail; + } + + ICCPD_LOG_NOTICE(__FUNCTION__, "Success to link syncd"); + sys->sync_fd = fd; + + event.data.fd = fd; + event.events = EPOLLIN; + ret = epoll_ctl(sys->epoll_fd, EPOLL_CTL_ADD, fd, &event); + + count = 0; + return 0; + + conn_fail: + if (count == 0) + ICCPD_LOG_DEBUG(__FUNCTION__, "Mclag syncd socket connect fail"); + + count++; + + return MCLAG_ERROR; +} + +void syncd_info_close() +{ + struct System* sys = NULL; + + if ((sys = system_get_instance()) == NULL) + return; + + if (sys->sync_fd > 0) + { + close(sys->sync_fd); + sys->sync_fd = -1; + } + + return; +} + +int iccp_get_receive_fdb_sock_fd(struct System *sys) +{ + return sys->sync_fd; +} + +/*When received MAC add and del packets from mclagsyncd, update mac information*/ +void do_mac_update_from_syncd(char mac_str[ETHER_ADDR_STR_LEN], uint16_t vid, char *ifname, uint8_t fdb_type, uint8_t op_type) +{ + struct System *sys = NULL; + struct CSM *csm = NULL; + struct Msg *msg = NULL; + struct MACMsg *mac_msg = NULL, *mac_info = NULL; + uint8_t mac_exist = 0; + char buf[MAX_BUFSIZE]; + size_t msg_len = 0; + uint8_t from_mclag_intf = 0;/*0: orphan port, 1: MCLAG port*/ + struct CSM *first_csm = NULL; + + struct LocalInterface *lif_po = NULL, *mac_lif = NULL; + + if (!(sys = system_get_instance())) + return; + + /* create MAC msg*/ + memset(buf, 0, MAX_BUFSIZE); + msg_len = sizeof(struct MACMsg); + mac_msg = (struct MACMsg*)buf; + mac_msg->op_type = op_type; + mac_msg->fdb_type = fdb_type; + sprintf(mac_msg->mac_str, "%s", mac_str); + mac_msg->vid = vid; + + mac_msg->age_flag = 0; + + ICCPD_LOG_DEBUG(__FUNCTION__, "Recv MAC msg from mclagsyncd, vid %d mac %s port %s optype %s ", vid, mac_str, ifname, op_type == MAC_SYNC_ADD ? "add" : "del"); + /*Debug*/ + #if 0 + /* dump receive MAC info*/ + fprintf(stderr, "\n======== MAC Update==========\n"); + fprintf(stderr, " MAC = %s\n", mac_str); + fprintf(stderr, " vlan id = %d\n", vid); + fprintf(stderr, " fdb type = %s\n", fdb_type == MAC_TYPE_STATIC ? "static" : "dynamic"); + fprintf(stderr, " op type = %s\n", op_type == MAC_SYNC_ADD ? "add" : "del"); + fprintf(stderr, "==============================\n"); + #endif + + /* Find MLACP itf, may be mclag enabled port-channel*/ + LIST_FOREACH(csm, &(sys->csm_list), next) + { + if (csm && !first_csm) + { + /*Record the first CSM, only one CSM in the system currently*/ + first_csm = csm; + } + + /*If MAC is from peer-link, break; peer-link is not in MLACP(csm).lif_list*/ + if (strcmp(ifname, csm->peer_itf_name) == 0) + break; + + LIST_FOREACH(lif_po, &(MLACP(csm).lif_list), mlacp_next) + { + if (lif_po->type != IF_T_PORT_CHANNEL) + continue; + + if (strcmp(lif_po->name, ifname) == 0) + { + from_mclag_intf = 1; + break; + } + } + + if (from_mclag_intf == 1) + break; + } + + if (!first_csm) + return; + + /*If support multiple CSM, the MAC list of orphan port must be moved to sys->mac_list*/ + csm = first_csm; + + /* find lif MAC+vid*/ + TAILQ_FOREACH(msg, &MLACP(csm).mac_list, tail) + { + mac_info = (struct MACMsg*)msg->buf; + + /*MAC and vid are equal*/ + if (strcmp(mac_info->mac_str, mac_str) == 0 && mac_info->vid == vid) + { + mac_exist = 1; + break; + } + } + + /*handle mac add*/ + if (op_type == MAC_SYNC_ADD) + { + /* Find local itf*/ + if (!(mac_lif = local_if_find_by_name(ifname))) + return; + + sprintf(mac_msg->ifname, "%s", ifname); + sprintf(mac_msg->origin_ifname, "%s", ifname); + + /*same MAC exist*/ + if (mac_exist) + { + /*If the recv mac port is peer-link, that is add from iccpd, no need to handle*/ + if (strcmp(csm->peer_itf_name, mac_msg->ifname) == 0) + { + return; + } + + /*If the current mac port is peer-link, it will handle by port up event*/ + /*if(strcmp(csm->peer_itf_name, mac_info->ifname) == 0) + { + return; + }*/ + + /* update MAC*/ + if (mac_info->fdb_type != mac_msg->fdb_type + || strcmp(mac_info->ifname, mac_msg->ifname) != 0 + || strcmp(mac_info->origin_ifname, mac_msg->ifname) != 0) + { + mac_info->fdb_type = mac_msg->fdb_type; + sprintf(mac_info->ifname, "%s", mac_msg->ifname); + sprintf(mac_info->origin_ifname, "%s", mac_msg->ifname); + + /*Remove MAC_AGE_LOCAL flag*/ + mac_info->age_flag = set_mac_local_age_flag(csm, mac_info, 0); + + ICCPD_LOG_DEBUG(__FUNCTION__, "Update MAC for %s, ifname %s", mac_msg->mac_str, mac_msg->ifname); + } + else + { + /*All info are the same, Remove MAC_AGE_LOCAL flag, then return*/ + /*In theory, this will be happened that mac age and then learn*/ + mac_info->age_flag = set_mac_local_age_flag(csm, mac_info, 0); + + return; + } + } + else/*same MAC not exist*/ + { + /*If the port the mac learn is change to down before the mac + sync to iccp, this mac must be deleted */ + if (mac_lif->state == PORT_STATE_DOWN) + { + del_mac_from_chip(mac_msg); + + return; + } + + /*set MAC_AGE_PEER flag before send this item to peer*/ + mac_msg->age_flag |= MAC_AGE_PEER; + /*ICCPD_LOG_DEBUG(__FUNCTION__, "Add peer age flag: %s, add %s vlan-id %d, age_flag %d", + mac_msg->ifname, mac_msg->mac_str, mac_msg->vid, mac_msg->age_flag);*/ + mac_msg->op_type = MAC_SYNC_ADD; + + if (MLACP(csm).current_state == MLACP_STATE_EXCHANGE) + { + struct Msg *msg_send = NULL; + if (iccp_csm_init_msg(&msg_send, (char*)mac_msg, msg_len) == 0) + { + mac_msg->age_flag &= ~MAC_AGE_PEER; + TAILQ_INSERT_TAIL(&(MLACP(csm).mac_msg_list), msg_send, tail); + + /*ICCPD_LOG_DEBUG(__FUNCTION__, "MAC-msg-list enqueue: %s, add %s vlan-id %d, age_flag %d", + mac_msg->ifname, mac_msg->mac_str, mac_msg->vid, mac_msg->age_flag);*/ + } + else + { + ICCPD_LOG_WARN(__FUNCTION__, "Failed to enqueue MAC-msg-list: %s, MAC %s vlan-id %d", + mac_msg->ifname, mac_msg->mac_str, mac_msg->vid); + } + } + + /*enqueue mac to mac-list*/ + if (iccp_csm_init_msg(&msg, (char*)mac_msg, msg_len) == 0) + { + TAILQ_INSERT_TAIL(&(MLACP(csm).mac_list), msg, tail); + + /*ICCPD_LOG_DEBUG(__FUNCTION__, "MAC-list enqueue: %s, add %s vlan-id %d", + mac_msg->ifname, mac_msg->mac_str, mac_msg->vid);*/ + } + else + ICCPD_LOG_DEBUG(__FUNCTION__, "Failed to enqueue MAC %s, MAC %s vlan-id %d", + mac_msg->ifname, mac_msg->mac_str, mac_msg->vid); + } + } + else/*handle mac del*/ + { + /*same MAC exist*/ + if (mac_exist) + { + /*orphan port mac or origin from_mclag_intf but state is down*/ + if (strcmp(mac_info->ifname, csm->peer_itf_name) == 0) + { + /*Set MAC_AGE_LOCAL flag*/ + mac_info->age_flag = set_mac_local_age_flag(csm, mac_info, 1); + + if (mac_info->age_flag == (MAC_AGE_LOCAL | MAC_AGE_PEER)) + { + ICCPD_LOG_DEBUG(__FUNCTION__, "Recv MAC del msg: %s(peer-link), del %s vlan-id %d", + mac_info->ifname, mac_info->mac_str, mac_info->vid); + + /*If peer link is down, del the mac*/ + TAILQ_REMOVE(&(MLACP(csm).mac_list), msg, tail); + free(msg->buf); + free(msg); + } + else if (csm->peer_link_if && csm->peer_link_if->state != PORT_STATE_DOWN) + { + /*peer-link learn mac is control by iccpd, ignore the chip del info*/ + add_mac_to_chip(mac_info, MAC_TYPE_DYNAMIC); + + ICCPD_LOG_DEBUG(__FUNCTION__, "Recv MAC del msg: %s(peer-link is up), add back %s vlan-id %d", + mac_info->ifname, mac_info->mac_str, mac_info->vid); + } + + return; + } + + /*Add MAC_AGE_LOCAL flag*/ + mac_info->age_flag = set_mac_local_age_flag(csm, mac_info, 1); + + if (mac_info->age_flag == (MAC_AGE_LOCAL | MAC_AGE_PEER)) + { + ICCPD_LOG_DEBUG(__FUNCTION__, "Recv MAC del msg: %s, del %s vlan-id %d", + mac_info->ifname, mac_info->mac_str, mac_info->vid); + + /*If local and peer both aged, del the mac (local orphan mac is here)*/ + TAILQ_REMOVE(&(MLACP(csm).mac_list), msg, tail); + free(msg->buf); + free(msg); + } + else + { + ICCPD_LOG_DEBUG(__FUNCTION__, "Recv MAC del msg: %s, del %s vlan-id %d, peer is not age, add back to chip", + mac_info->ifname, mac_info->mac_str, mac_info->vid); + + mac_info->fdb_type = MAC_TYPE_DYNAMIC; + + if (from_mclag_intf && lif_po && lif_po->state == PORT_STATE_DOWN) + { + /*If local if is down, redirect the mac to peer-link*/ + if (strlen(csm->peer_itf_name) != 0) + { + memcpy(&mac_info->ifname, csm->peer_itf_name, IFNAMSIZ); + + if (csm->peer_link_if && csm->peer_link_if->state == PORT_STATE_UP) + { + add_mac_to_chip(mac_info, MAC_TYPE_DYNAMIC); + ICCPD_LOG_DEBUG(__FUNCTION__, "Recv MAC del msg: %s(down), del %s vlan-id %d, redirect to peer-link", + mac_info->ifname, mac_info->mac_str, mac_info->vid); + } + } + + return; + } + + /*If local is aged but peer is not aged, Send mac add message to mclagsyncd*/ + /*it is from_mclag_intf and port state is up, local orphan mac can not be here*/ + /* Find local itf*/ + if (!(mac_lif = local_if_find_by_name(mac_info->ifname))) + return; + if (mac_lif->state == PORT_STATE_UP) + add_mac_to_chip(mac_info, MAC_TYPE_DYNAMIC); + } + } + } + + return; +} + +int iccp_receive_fdb_handler_from_syncd(struct System *sys) +{ + char *msg_buf = g_csm_buf; + struct IccpSyncdHDr *msg_hdr; + struct mclag_fdb_info * mac_info; + size_t pos = 0; + int count = 0; + int i = 0; + int n = 0; + + if (sys == NULL) + return MCLAG_ERROR; + + memset(msg_buf, 0, CSM_BUFFER_SIZE); + + n = read(sys->sync_fd, msg_buf, CSM_BUFFER_SIZE); + if (n <= 0) + { + ICCPD_LOG_ERR(__FUNCTION__, "read msg error!!!" ); + return MCLAG_ERROR; + } + + while (pos < n) + { + msg_hdr = (struct IccpSyncdHDr *)&msg_buf[pos]; + if (msg_hdr->ver != 1 || msg_hdr->type != MCLAG_SYNCD_MSG_TYPE_FDB_OPERATION ) + { + ICCPD_LOG_ERR(__FUNCTION__, "msg version or type wrong!!!!! "); + return MCLAG_ERROR; + } + + count = ( msg_hdr->len - sizeof(struct IccpSyncdHDr )) / sizeof(struct mclag_fdb_info); + ICCPD_LOG_DEBUG(__FUNCTION__, "recv msg fdb count %d ", count); + + for (i = 0; i < count; i++) + { + mac_info = (struct mclag_fdb_info *)&msg_buf[pos + sizeof(struct IccpSyncdHDr ) + i * sizeof(struct mclag_fdb_info)]; + /*ICCPD_LOG_DEBUG(__FUNCTION__, "recv msg fdb count %d vid %d mac %s port %s optype %s ", i, mac_info->vid, mac_info->mac, mac_info->port_name, mac_info->op_type == MAC_SYNC_ADD ? "add" : "del");*/ + do_mac_update_from_syncd(mac_info->mac, mac_info->vid, mac_info->port_name, mac_info->type, mac_info->op_type); + } + + pos += msg_hdr->len; + } + + return 0; +} + +char * mclagd_ctl_cmd_str(int req_type) +{ + switch (req_type) + { + case INFO_TYPE_DUMP_STATE: + return "dump config"; + + case INFO_TYPE_DUMP_ARP: + return "dump arp"; + + case INFO_TYPE_DUMP_MAC: + return "dump mac"; + + case INFO_TYPE_DUMP_LOCAL_PORTLIST: + return "dump local portlist"; + + case INFO_TYPE_DUMP_PEER_PORTLIST: + return "dump peer portlist"; + + case INFO_TYPE_CONFIG_LOGLEVEL: + return "config loglevel"; + default: + break; + } + + return "error req type"; +} + +int mclagd_ctl_sock_create() +{ + struct sockaddr_un addr; + struct System* sys = NULL; + struct epoll_event event; + int addr_len; + int ret = 0; + + if ((sys = system_get_instance()) == NULL) + return MCLAG_ERROR; + + if (sys->sync_ctrl_fd > 0) + return sys->sync_ctrl_fd; + + sys->sync_ctrl_fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (sys->sync_ctrl_fd < 0) + { + ICCPD_LOG_WARN(__FUNCTION__, "Failed to create mclagd ctl sock"); + return sys->sync_ctrl_fd; + } + + unlink(sys->mclagdctl_file_path); + + memset((void*)&addr, 0, sizeof(struct sockaddr_un)); + addr.sun_family = AF_UNIX; + snprintf(addr.sun_path, 107, "%s", sys->mclagdctl_file_path); + addr_len = sizeof(addr.sun_family) + strlen(sys->mclagdctl_file_path); + + if ((ret = bind(sys->sync_ctrl_fd, (struct sockaddr*)&addr, addr_len)) < 0) + { + ICCPD_LOG_WARN(__FUNCTION__, "Failed to bind mclagd ctl socket %s:%s", sys->mclagdctl_file_path, strerror(errno)); + close(sys->sync_ctrl_fd); + return MCLAG_ERROR; + } + + if (listen(sys->sync_ctrl_fd, 5) < 0) + { + ICCPD_LOG_WARN(__FUNCTION__, "Failed to listen unix mclagd ctl socket%s:%s", sys->mclagdctl_file_path, strerror(errno)); + close(sys->sync_ctrl_fd); + return MCLAG_ERROR; + } + + event.data.fd = sys->sync_ctrl_fd; + event.events = EPOLLIN; + epoll_ctl(sys->epoll_fd, EPOLL_CTL_ADD, sys->sync_ctrl_fd, &event); + FD_SET(sys->sync_ctrl_fd, &(sys->readfd)); + sys->readfd_count++; + + return sys->sync_ctrl_fd; +} + +int mclagd_ctl_sock_accept(int fd) +{ + struct sockaddr_in client_addr; + int client_fd = 0; + unsigned int addr_len = 0; + + client_fd = accept(fd, (struct sockaddr*)&client_addr, &addr_len); + if (client_fd < 0) + { + ICCPD_LOG_WARN(__FUNCTION__, "Failed to accept a client from mclagdctl"); + return MCLAG_ERROR; + } + + return client_fd; +} + +int mclagd_ctl_sock_read(int fd, char *r_buf, int total_len) +{ + int read_len = 0; + int ret = 0; + struct timeval tv = { 0 }; + fd_set read_fd; + + while (read_len < total_len) + { + FD_ZERO(&read_fd); + FD_SET(fd, &read_fd); + tv.tv_sec = 5; + tv.tv_usec = 0; + + switch ((ret = select(fd + 1, &read_fd, NULL, NULL, &tv))) + { + case -1: + /* error*/ + case 0: + /* timeout*/ + return MCLAG_ERROR; + + default: + break; + } + + if (FD_ISSET(fd, &read_fd)) + ret = read(fd, r_buf + read_len, total_len - read_len); + if (ret <= 0) + { + return MCLAG_ERROR; + } + read_len += ret; + } + + return read_len; +} + +int mclagd_ctl_sock_write(int fd, char *w_buf, int total_len) +{ + int write_len = 0; + int ret = 0; + + while (write_len < total_len) + { + ret = write(fd, w_buf + write_len, total_len - write_len); + if (ret <= 0) + { + return 0; + } + write_len += ret; + } + + return write_len; +} + +void mclagd_ctl_handle_dump_state(int client_fd, int mclag_id) +{ + char * Pbuf = NULL; + char buf[512] = { 0 }; + int state_num = 0; + int ret = 0; + struct mclagd_reply_hdr *hd = NULL; + int len_tmp = 0; + + ret = iccp_mclag_config_dump(&Pbuf, &state_num, mclag_id); + if (ret != EXEC_TYPE_SUCCESS) + { + len_tmp = sizeof(struct mclagd_reply_hdr); + memcpy(buf, &len_tmp, sizeof(int)); + hd = (struct mclagd_reply_hdr *)(buf + sizeof(int)); + hd->exec_result = ret; + hd->info_type = INFO_TYPE_DUMP_STATE; + hd->data_len = 0; + mclagd_ctl_sock_write(client_fd, buf, MCLAGD_REPLY_INFO_HDR); + + if (Pbuf) + free(Pbuf); + + return; + } + hd = (struct mclagd_reply_hdr *)(Pbuf + sizeof(int)); + hd->exec_result = EXEC_TYPE_SUCCESS; + hd->info_type = INFO_TYPE_DUMP_STATE; + hd->data_len = state_num * sizeof(struct mclagd_state); + + len_tmp = (hd->data_len + sizeof(struct mclagd_reply_hdr)); + memcpy(Pbuf, &len_tmp, sizeof(int)); + mclagd_ctl_sock_write(client_fd, Pbuf, MCLAGD_REPLY_INFO_HDR + hd->data_len); + + if (Pbuf) + free(Pbuf); + + return; +} + +void mclagd_ctl_handle_dump_arp(int client_fd, int mclag_id) +{ + char * Pbuf = NULL; + char buf[512] = { 0 }; + int arp_num = 0; + int ret = 0; + struct mclagd_reply_hdr *hd = NULL; + int len_tmp = 0; + + ret = iccp_arp_dump(&Pbuf, &arp_num, mclag_id); + if (ret != EXEC_TYPE_SUCCESS) + { + len_tmp = sizeof(struct mclagd_reply_hdr); + memcpy(buf, &len_tmp, sizeof(int)); + hd = (struct mclagd_reply_hdr *)(buf + sizeof(int)); + hd->exec_result = ret; + hd->info_type = INFO_TYPE_DUMP_ARP; + hd->data_len = 0; + mclagd_ctl_sock_write(client_fd, buf, MCLAGD_REPLY_INFO_HDR); + + if (Pbuf) + free(Pbuf); + + return; + } + + hd = (struct mclagd_reply_hdr *)(Pbuf + sizeof(int)); + hd->exec_result = EXEC_TYPE_SUCCESS; + hd->info_type = INFO_TYPE_DUMP_ARP; + hd->data_len = arp_num * sizeof(struct mclagd_arp_msg); + len_tmp = (hd->data_len + sizeof(struct mclagd_reply_hdr)); + memcpy(Pbuf, &len_tmp, sizeof(int)); + mclagd_ctl_sock_write(client_fd, Pbuf, MCLAGD_REPLY_INFO_HDR + hd->data_len); + + if (Pbuf) + free(Pbuf); + + return; +} + +void mclagd_ctl_handle_dump_mac(int client_fd, int mclag_id) +{ + char * Pbuf = NULL; + char buf[512] = { 0 }; + int mac_num = 0; + int ret = 0; + struct mclagd_reply_hdr *hd = NULL; + int len_tmp = 0; + + ret = iccp_mac_dump(&Pbuf, &mac_num, mclag_id); + if (ret != EXEC_TYPE_SUCCESS) + { + len_tmp = sizeof(struct mclagd_reply_hdr); + memcpy(buf, &len_tmp, sizeof(int)); + hd = (struct mclagd_reply_hdr *)(buf + sizeof(int)); + hd->exec_result = ret; + hd->info_type = INFO_TYPE_DUMP_MAC; + hd->data_len = 0; + mclagd_ctl_sock_write(client_fd, buf, MCLAGD_REPLY_INFO_HDR); + + if (Pbuf) + free(Pbuf); + + return; + } + + hd = (struct mclagd_reply_hdr *)(Pbuf + sizeof(int)); + hd->exec_result = EXEC_TYPE_SUCCESS; + hd->info_type = INFO_TYPE_DUMP_MAC; + hd->data_len = mac_num * sizeof(struct mclagd_mac_msg); + + len_tmp = (hd->data_len + sizeof(struct mclagd_reply_hdr)); + memcpy(Pbuf, &len_tmp, sizeof(int)); + + mclagd_ctl_sock_write(client_fd, Pbuf, MCLAGD_REPLY_INFO_HDR + hd->data_len); + + if (Pbuf) + free(Pbuf); + + return; +} + +void mclagd_ctl_handle_dump_local_portlist(int client_fd, int mclag_id) +{ + char * Pbuf = NULL; + char buf[512] = { 0 }; + int lif_num = 0;; + int ret = 0; + struct mclagd_reply_hdr *hd = NULL; + int len_tmp = 0; + + ret = iccp_local_if_dump(&Pbuf, &lif_num, mclag_id); + if (ret != EXEC_TYPE_SUCCESS) + { + len_tmp = sizeof(struct mclagd_reply_hdr); + memcpy(buf, &len_tmp, sizeof(int)); + hd = (struct mclagd_reply_hdr *)(buf + sizeof(int)); + hd->exec_result = ret; + hd->info_type = INFO_TYPE_DUMP_LOCAL_PORTLIST; + hd->data_len = 0; + mclagd_ctl_sock_write(client_fd, buf, MCLAGD_REPLY_INFO_HDR); + + if (Pbuf) + free(Pbuf); + + return; + } + + hd = (struct mclagd_reply_hdr *)(Pbuf + sizeof(int)); + hd->exec_result = EXEC_TYPE_SUCCESS; + hd->info_type = INFO_TYPE_DUMP_LOCAL_PORTLIST; + hd->data_len = lif_num * sizeof(struct mclagd_local_if); + len_tmp = (hd->data_len + sizeof(struct mclagd_reply_hdr)); + memcpy(Pbuf, &len_tmp, sizeof(int)); + mclagd_ctl_sock_write(client_fd, Pbuf, MCLAGD_REPLY_INFO_HDR + hd->data_len); + + if (Pbuf) + free(Pbuf); + + return; +} + +void mclagd_ctl_handle_dump_peer_portlist(int client_fd, int mclag_id) +{ + char * Pbuf = NULL; + char buf[512] = { 0 }; + int pif_num = 0; + int ret = 0; + struct mclagd_reply_hdr *hd = NULL; + int len_tmp = 0; + + ret = iccp_peer_if_dump(&Pbuf, &pif_num, mclag_id); + if (ret != EXEC_TYPE_SUCCESS) + { + len_tmp = sizeof(struct mclagd_reply_hdr); + memcpy(buf, &len_tmp, sizeof(int)); + hd = (struct mclagd_reply_hdr *)(buf + sizeof(int)); + hd->exec_result = ret; + hd->info_type = INFO_TYPE_DUMP_PEER_PORTLIST; + hd->data_len = 0; + mclagd_ctl_sock_write(client_fd, buf, MCLAGD_REPLY_INFO_HDR); + + if (Pbuf) + free(Pbuf); + + return; + } + + hd = (struct mclagd_reply_hdr *)(Pbuf + sizeof(int)); + hd->exec_result = EXEC_TYPE_SUCCESS; + hd->info_type = INFO_TYPE_DUMP_PEER_PORTLIST; + hd->data_len = pif_num * sizeof(struct mclagd_peer_if); + len_tmp = (hd->data_len + sizeof(struct mclagd_reply_hdr)); + memcpy(Pbuf, &len_tmp, sizeof(int)); + mclagd_ctl_sock_write(client_fd, Pbuf, MCLAGD_REPLY_INFO_HDR + hd->data_len); + + if (Pbuf) + free(Pbuf); + + return; +} + +void mclagd_ctl_handle_config_loglevel(int client_fd, int log_level) +{ + char buf[sizeof(struct mclagd_reply_hdr)+sizeof(int)]; + struct mclagd_reply_hdr *hd = NULL; + int len_tmp = 0; + + logger_set_configuration(log_level); + + len_tmp = sizeof(struct mclagd_reply_hdr); + memcpy(buf, &len_tmp, sizeof(int)); + hd = (struct mclagd_reply_hdr *)(buf + sizeof(int)); + hd->exec_result = EXEC_TYPE_SUCCESS; + hd->info_type = INFO_TYPE_CONFIG_LOGLEVEL; + hd->data_len = 0; + mclagd_ctl_sock_write(client_fd, buf, MCLAGD_REPLY_INFO_HDR); + + return; +} + +int mclagd_ctl_interactive_process(int client_fd) +{ + char buf[512] = { 0 }; + int ret = 0; + + struct mclagdctl_req_hdr* req = NULL; + + if (client_fd < 0) + return MCLAG_ERROR; + + ret = mclagd_ctl_sock_read(client_fd, buf, sizeof(struct mclagdctl_req_hdr)); + + if (ret < 0) + return MCLAG_ERROR; + + req = (struct mclagdctl_req_hdr*)buf; + + ICCPD_LOG_DEBUG(__FUNCTION__, "Receive request %s from mclagdctl", mclagd_ctl_cmd_str(req->info_type)); + + switch (req->info_type) + { + case INFO_TYPE_DUMP_STATE: + mclagd_ctl_handle_dump_state(client_fd, req->mclag_id); + break; + + case INFO_TYPE_DUMP_ARP: + mclagd_ctl_handle_dump_arp(client_fd, req->mclag_id); + break; + + case INFO_TYPE_DUMP_MAC: + mclagd_ctl_handle_dump_mac(client_fd, req->mclag_id); + break; + + case INFO_TYPE_DUMP_LOCAL_PORTLIST: + mclagd_ctl_handle_dump_local_portlist(client_fd, req->mclag_id); + break; + + case INFO_TYPE_DUMP_PEER_PORTLIST: + mclagd_ctl_handle_dump_peer_portlist(client_fd, req->mclag_id); + break; + + case INFO_TYPE_CONFIG_LOGLEVEL: + mclagd_ctl_handle_config_loglevel(client_fd, req->mclag_id); + break; + + default: + return MCLAG_ERROR; + } + + return 0; +} + + diff --git a/src/iccpd/src/mlacp_sync_prepare.c b/src/iccpd/src/mlacp_sync_prepare.c new file mode 100644 index 000000000000..f40a59884e49 --- /dev/null +++ b/src/iccpd/src/mlacp_sync_prepare.c @@ -0,0 +1,624 @@ +/* + * MLACP Sync Infomation Preparation + * mlacp_sync_prepare.c + + * Copyright(c) 2016-2019 Nephos/Estinet. + * + * 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, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintainer: jianjun, grace Li from nephos + * + */ + +#include +#include + +#include + +#include "../include/system.h" +#include "../include/logger.h" +#include "../include/mlacp_fsm.h" +#include "../include/mlacp_tlv.h" +#include "../include/mlacp_link_handler.h" +#include "../include/iccp_ifm.h" +#include "../include/iccp_csm.h" + +/***************************************** +* Static Function +* +* ***************************************/ +static int mlacp_fill_icc_header(struct CSM* csm, ICCHdr* icc_hdr, size_t msg_len); + +/***************************************** +* Create Sync Request TLV +* +* ***************************************/ +int mlacp_prepare_for_sync_request_tlv(struct CSM* csm, char* buf, size_t max_buf_size) +{ + struct System* sys = NULL; + ICCHdr* icc_hdr = NULL; + mLACPSyncReqTLV* tlv = NULL; + size_t msg_len = sizeof(ICCHdr) + sizeof(mLACPSyncReqTLV); + + if (csm == NULL) + return MCLAG_ERROR; + + if (buf == NULL) + return MCLAG_ERROR; + + if (msg_len > max_buf_size) + return MCLAG_ERROR; + + if ((sys = system_get_instance()) == NULL) + return MCLAG_ERROR; + + /* Prepare for sync request */ + memset(buf, 0, max_buf_size); + + icc_hdr = (ICCHdr*)buf; + tlv = (mLACPSyncReqTLV*)&buf[sizeof(ICCHdr)]; + + /* ICC header */ + mlacp_fill_icc_header(csm, icc_hdr, msg_len); + + /* mLACP Synchronization Request TLV */ + tlv->icc_parameter.u_bit = 0; + tlv->icc_parameter.f_bit = 0; + tlv->icc_parameter.type = htons(TLV_T_MLACP_SYNC_REQUEST); + tlv->icc_parameter.len = htons(sizeof(mLACPSyncReqTLV) - sizeof(ICCParameter)); + + tlv->req_num = 0; + MLACP(csm).sync_req_num = 0; + + tlv->c_bit = 1; + tlv->s_bit = 1; + tlv->req_type = 0x3FFF; + *(uint16_t *)((uint8_t *)tlv + sizeof(ICCParameter) + sizeof(uint16_t)) = htons(*(uint16_t *)((uint8_t *)tlv + sizeof(ICCParameter) + sizeof(uint16_t))); + + tlv->port_num_agg_id = 0; + tlv->actor_key = 0; + + return msg_len; +} + +/***************************************** +* Prprare Sync Data TLV +* +* ***************************************/ +int mlacp_prepare_for_sync_data_tlv(struct CSM* csm, char* buf, size_t max_buf_size, int end) +{ + struct System* sys = NULL; + ICCHdr* icc_hdr = (ICCHdr*)buf; + mLACPSyncDataTLV* tlv = (mLACPSyncDataTLV*)&buf[sizeof(ICCHdr)]; + size_t msg_len = sizeof(ICCHdr) + sizeof(mLACPSyncDataTLV); + + if (csm == NULL) + return MCLAG_ERROR; + + if (buf == NULL) + return MCLAG_ERROR; + + if (msg_len > max_buf_size) + return MCLAG_ERROR; + + if ((sys = system_get_instance()) == NULL) + return MCLAG_ERROR; + + /* Prepare for sync request */ + memset(buf, 0, max_buf_size); + + icc_hdr = (ICCHdr*)buf; + tlv = (mLACPSyncDataTLV*)&buf[sizeof(ICCHdr)]; + + /* ICC header */ + mlacp_fill_icc_header(csm, icc_hdr, msg_len); + + /* mLACP Synchronization Data TLV */ + tlv->icc_parameter.u_bit = 0; + tlv->icc_parameter.f_bit = 0; + tlv->icc_parameter.type = htons(TLV_T_MLACP_SYNC_DATA); + tlv->icc_parameter.len = htons(sizeof(mLACPSyncDataTLV) - sizeof(ICCParameter)); + + tlv->req_num = htons(MLACP(csm).sync_req_num); + if (end == 0) + tlv->flags = 0x00; + else + tlv->flags = htons(0x01); + + return msg_len; +} + +/***************************************** +* Prprare Sync System-Config TLV +* +* ***************************************/ +int mlacp_prepare_for_sys_config(struct CSM* csm, char* buf, size_t max_buf_size) +{ + struct System* sys = NULL; + ICCHdr* icc_hdr = (ICCHdr*)buf; + mLACPSysConfigTLV* tlv = (mLACPSysConfigTLV*)&buf[sizeof(ICCHdr)]; + size_t msg_len = sizeof(ICCHdr) + sizeof(mLACPSysConfigTLV); + + if (csm == NULL) + return MCLAG_ERROR; + + if (buf == NULL) + return MCLAG_ERROR; + + if (msg_len > max_buf_size) + return MCLAG_ERROR; + + if ((sys = system_get_instance()) == NULL) + return MCLAG_ERROR; + + /* Prepare for sync request */ + memset(buf, 0, max_buf_size); + + icc_hdr = (ICCHdr*)buf; + tlv = (mLACPSysConfigTLV*)&buf[sizeof(ICCHdr)]; + + /* ICC header */ + mlacp_fill_icc_header(csm, icc_hdr, msg_len); + + /* System Config TLV */ + tlv->icc_parameter.u_bit = 0; + tlv->icc_parameter.f_bit = 0; + tlv->icc_parameter.type = htons(TLV_T_MLACP_SYSTEM_CONFIG); + tlv->icc_parameter.len = htons(sizeof(mLACPSysConfigTLV) - sizeof(ICCParameter)); + + memcpy(tlv->sys_id, MLACP(csm).system_id, ETHER_ADDR_LEN); + tlv->sys_priority = htons(MLACP(csm).system_priority); + tlv->node_id = MLACP(csm).node_id; + return msg_len; +} + +/*Prprare Sync AggPort-State TLV */ +int mlacp_prepare_for_Aggport_state(struct CSM* csm, char* buf, size_t max_buf_size, struct LocalInterface* local_if) +{ + struct System* sys = NULL; + ICCHdr* icc_hdr = (ICCHdr*)buf; + mLACPAggPortStateTLV* tlv = (mLACPAggPortStateTLV*)&buf[sizeof(ICCHdr)]; + size_t msg_len = sizeof(ICCHdr) + sizeof(mLACPAggPortStateTLV); + + if (csm == NULL) + return MCLAG_ERROR; + + if (buf == NULL) + return MCLAG_ERROR; + + if (local_if == NULL) + return MCLAG_ERROR; + + if (local_if->type != IF_T_PORT_CHANNEL) + return MCLAG_ERROR; + + if (msg_len > max_buf_size) + return MCLAG_ERROR; + + if ((sys = system_get_instance()) == NULL) + return MCLAG_ERROR; + + /* Prepare for sync request */ + memset(buf, 0, max_buf_size); + + icc_hdr = (ICCHdr*)buf; + tlv = (mLACPAggPortStateTLV*)&buf[sizeof(ICCHdr)]; + + /* ICC header */ + mlacp_fill_icc_header(csm, icc_hdr, msg_len); + + /* Port State TLV */ + tlv->icc_parameter.u_bit = 0; + tlv->icc_parameter.f_bit = 0; + tlv->icc_parameter.type = htons(TLV_T_MLACP_AGGREGATOR_STATE); + tlv->icc_parameter.len = htons(sizeof(mLACPAggPortStateTLV) - sizeof(ICCParameter)); + + tlv->partner_sys_priority = 0; + tlv->partner_key = 0; + tlv->agg_id = htons(local_if->po_id); + tlv->actor_key = 0; + tlv->agg_state = local_if->state; + + return msg_len; +} + +/***************************************** +* Prprare Sync Purge Port +* +* ***************************************/ +int mlacp_prepare_for_Aggport_config(struct CSM* csm, + char* buf, size_t max_buf_size, + struct LocalInterface* lif, int purge_flag) +{ + ICCHdr* icc_hdr = (ICCHdr*)buf; + mLACPAggConfigTLV* tlv = (mLACPAggConfigTLV*)&buf[sizeof(ICCHdr)]; + size_t msg_len = sizeof(ICCHdr) + sizeof(mLACPAggConfigTLV); + + if (csm == NULL) + return MCLAG_ERROR; + + if (buf == NULL) + return MCLAG_ERROR; + + if (msg_len > max_buf_size) + return MCLAG_ERROR; + + /* Prepare for sync request */ + memset(buf, 0, max_buf_size); + + icc_hdr = (ICCHdr*)buf; + tlv = (mLACPAggConfigTLV*)&buf[sizeof(ICCHdr)]; + + /* ICC header */ + mlacp_fill_icc_header(csm, icc_hdr, msg_len); + + /* Port Config TLV */ + tlv->icc_parameter.u_bit = 0; + tlv->icc_parameter.f_bit = 0; + tlv->icc_parameter.type = htons(TLV_T_MLACP_AGGREGATOR_CONFIG); + + tlv->icc_parameter.len = htons(sizeof(mLACPAggConfigTLV) - sizeof(ICCParameter)); + tlv->agg_id = htons(lif->po_id); + if (purge_flag == 1) + tlv->flags = 0x02; /*purge*/ + else + tlv->flags = 0x1; + tlv->agg_name_len = strlen(lif->name); + memcpy(tlv->agg_name, lif->name, MAX_L_PORT_NAME); + memcpy(tlv->mac_addr, lif->mac_addr, ETHER_ADDR_LEN); + + return msg_len; +} + +/***************************************** +* Preprare Sync MAC-Info TLV +* +* ***************************************/ +int mlacp_prepare_for_mac_info_to_peer(struct CSM* csm, char* buf, size_t max_buf_size, struct MACMsg* mac_msg, int count) +{ + struct mLACPMACInfoTLV* tlv = NULL; + size_t msg_len = 0; + size_t tlv_len = 0; + ICCHdr* icc_hdr = NULL; + struct mLACPMACData *MacData; + + if (!csm) + return MCLAG_ERROR; + if (!buf) + return MCLAG_ERROR; + + tlv_len = sizeof(struct mLACPMACInfoTLV) + sizeof(struct mLACPMACData) * (count + 1); + + if ((msg_len = sizeof(ICCHdr) + tlv_len) > max_buf_size) + return MCLAG_ERROR; + + /* ICC header */ + icc_hdr = (ICCHdr*)buf; + mlacp_fill_icc_header(csm, icc_hdr, msg_len); + /* Prepare for MAC information TLV */ + tlv = (struct mLACPMACInfoTLV*)&buf[sizeof(ICCHdr)]; + tlv->icc_parameter.len = htons(tlv_len - sizeof(ICCParameter)); + tlv->num_of_entry = htons(count + 1); + + if (count == 0) + { + tlv->icc_parameter.u_bit = 0; + tlv->icc_parameter.f_bit = 0; + tlv->icc_parameter.type = htons(TLV_T_MLACP_MAC_INFO); + } + + MacData = (struct mLACPMACData *)&buf[sizeof(ICCHdr) + sizeof(struct mLACPMACInfoTLV) + sizeof(struct mLACPMACData) * count]; + MacData->type = mac_msg->op_type; + sprintf(MacData->mac_str, "%s", mac_msg->mac_str); + sprintf(MacData->ifname, "%s", mac_msg->origin_ifname); + MacData->vid = htons(mac_msg->vid); + + ICCPD_LOG_DEBUG(__FUNCTION__, "Send MAC messge to peer, port %s mac = %s, vid = %d, type = %s count %d ", mac_msg->origin_ifname, + mac_msg->mac_str, mac_msg->vid, mac_msg->op_type == MAC_SYNC_ADD ? "add" : "del", count); + + return msg_len; +} + +/***************************************** +* Preprare Sync ARP-Info TLV +* +* ***************************************/ +int mlacp_prepare_for_arp_info(struct CSM* csm, char* buf, size_t max_buf_size, struct ARPMsg* arp_msg, int count) +{ + struct mLACPARPInfoTLV* tlv = NULL; + size_t msg_len = 0; + size_t tlv_len = 0; + ICCHdr* icc_hdr = NULL; + struct ARPMsg* ArpData; + + if (!csm) + return MCLAG_ERROR; + if (!buf) + return MCLAG_ERROR; + + tlv_len = sizeof(struct mLACPARPInfoTLV) + sizeof(struct ARPMsg) * (count + 1); + + if ((msg_len = sizeof(ICCHdr) + tlv_len) > max_buf_size) + return MCLAG_ERROR; + + /* ICC header */ + icc_hdr = (ICCHdr*)buf; + mlacp_fill_icc_header(csm, icc_hdr, msg_len); + + /* Prepare for ARP information TLV */ + tlv = (struct mLACPARPInfoTLV*)&buf[sizeof(ICCHdr)]; + tlv->icc_parameter.len = htons(tlv_len - sizeof(ICCParameter)); + tlv->num_of_entry = htons(count + 1); + + if (count == 0) + { + tlv->icc_parameter.u_bit = 0; + tlv->icc_parameter.f_bit = 0; + tlv->icc_parameter.type = htons(TLV_T_MLACP_ARP_INFO); + } + + ArpData = (struct mLACPMACData *)&buf[sizeof(ICCHdr) + sizeof(struct mLACPARPInfoTLV) + sizeof(struct ARPMsg) * count]; + + ArpData->op_type = arp_msg->op_type; + sprintf(ArpData->ifname, "%s", arp_msg->ifname); + ArpData->ipv4_addr = htonl(arp_msg->ipv4_addr); + memcpy(ArpData->mac_addr, arp_msg->mac_addr, ETHER_ADDR_LEN); + + ICCPD_LOG_DEBUG(__FUNCTION__, "Send ARP messge to peer, if name %s mac %02x:%02x:%02x:%02x:%02x:%02x IP %s", ArpData->ifname, ArpData->mac_addr[0], ArpData->mac_addr[1], ArpData->mac_addr[2], + ArpData->mac_addr[3], ArpData->mac_addr[4], ArpData->mac_addr[5], show_ip_str( ArpData->ipv4_addr)); + + return msg_len; +} + +/***************************************** +* Prprare Send portchannel info +* +* ***************************************/ +int mlacp_prepare_for_port_channel_info(struct CSM* csm, char* buf, + size_t max_buf_size, + struct LocalInterface* port_channel) +{ + struct System* sys = NULL; + ICCHdr* icc_hdr = NULL; + struct mLACPPortChannelInfoTLV* tlv = NULL; + size_t msg_len; + size_t tlv_len; + size_t name_len = MAX_L_PORT_NAME; + struct VLAN_ID* vlan_id = NULL; + int num_of_vlan_id = 0; + + if (csm == NULL ) + return MCLAG_ERROR; + if (buf == NULL ) + return MCLAG_ERROR; + if (port_channel == NULL ) + return MCLAG_ERROR; + if (port_channel->type == IF_T_PORT) + return MCLAG_ERROR; + if ((sys = system_get_instance()) == NULL ) + return MCLAG_ERROR; + + /* Calculate VLAN ID Length */ + LIST_FOREACH(vlan_id, &(port_channel->vlan_list), port_next) + if (vlan_id != NULL) + num_of_vlan_id++; + + tlv_len = sizeof(struct mLACPPortChannelInfoTLV) + sizeof(struct mLACPVLANData) * num_of_vlan_id; + + if ((msg_len = sizeof(ICCHdr) + tlv_len) > max_buf_size) + return MCLAG_ERROR; + + /* Prepare for port channel info */ + memset(buf, 0, max_buf_size); + + icc_hdr = (ICCHdr*)buf; + tlv = (struct mLACPPortChannelInfoTLV*)&buf[sizeof(ICCHdr)]; + + /* ICC header */ + mlacp_fill_icc_header(csm, icc_hdr, msg_len); + + /* Port Channel Info TLV */ + tlv->icc_parameter.u_bit = 0; + tlv->icc_parameter.f_bit = 0; + tlv->icc_parameter.type = htons(TLV_T_MLACP_PORT_CHANNEL_INFO); + tlv->icc_parameter.len = htons(sizeof(struct mLACPPortChannelInfoTLV) - sizeof(ICCParameter) + sizeof(struct mLACPVLANData) * num_of_vlan_id); + tlv->agg_id = htons(port_channel->po_id); + tlv->ipv4_addr = htonl(port_channel->ipv4_addr); + tlv->l3_mode = port_channel->l3_mode; + tlv->po_id = htons(port_channel->po_id); + + if (strlen(port_channel->name) < name_len) + name_len = strlen(port_channel->name); + memcpy(tlv->if_name, port_channel->name, name_len); + tlv->if_name_len = name_len; + tlv->num_of_vlan_id = htons(num_of_vlan_id); + + num_of_vlan_id = 0; + LIST_FOREACH(vlan_id, &(port_channel->vlan_list), port_next) + { + if (vlan_id != NULL ) + { + tlv->vlanData[num_of_vlan_id].vlan_id = htons(vlan_id->vid); + + num_of_vlan_id++; + ICCPD_LOG_DEBUG(__FUNCTION__, "PortChannel%d: ipv4 addr = %s vlan id %d num %d ", port_channel->po_id, show_ip_str( tlv->ipv4_addr), vlan_id->vid, num_of_vlan_id ); + } + } + + ICCPD_LOG_DEBUG(__FUNCTION__, "PortChannel%d: ipv4 addr = %s l3 mode %d", port_channel->po_id, show_ip_str( tlv->ipv4_addr), tlv->l3_mode); + + return msg_len; +} + +/***************************************** +* Prprare Send port peerlink info +* +* ***************************************/ +int mlacp_prepare_for_port_peerlink_info(struct CSM* csm, char* buf, + size_t max_buf_size, + struct LocalInterface* peerlink_port) +{ + struct System* sys = NULL; + ICCHdr* icc_hdr = NULL; + struct mLACPPeerLinkInfoTLV* tlv = NULL; + size_t msg_len; + size_t tlv_len; + + if (csm == NULL ) + return MCLAG_ERROR; + if (buf == NULL ) + return MCLAG_ERROR; + if (peerlink_port == NULL ) + return MCLAG_ERROR; + if ((sys = system_get_instance()) == NULL ) + return MCLAG_ERROR; + + /* Prepare for port channel info */ + memset(buf, 0, max_buf_size); + + tlv_len = sizeof(struct mLACPPeerLinkInfoTLV); + + if ((msg_len = sizeof(ICCHdr) + tlv_len) > max_buf_size) + return MCLAG_ERROR; + + icc_hdr = (ICCHdr*)buf; + tlv = (struct mLACPPeerLinkInfoTLV*)&buf[sizeof(ICCHdr)]; + + /* ICC header */ + mlacp_fill_icc_header(csm, icc_hdr, msg_len); + + /* Port Channel Info TLV */ + tlv->icc_parameter.u_bit = 0; + tlv->icc_parameter.f_bit = 0; + tlv->icc_parameter.type = htons(TLV_T_MLACP_PEERLINK_INFO); + + tlv->icc_parameter.len = htons(tlv_len - sizeof(ICCParameter)); + memcpy(tlv->if_name, peerlink_port->name, MAX_L_PORT_NAME); + tlv->port_type = peerlink_port->type; + + ICCPD_LOG_DEBUG(__FUNCTION__, "Peerlink port is %s, type = %d", tlv->if_name, tlv->port_type); + + return msg_len; +} + + +/***************************************** +* Prprare Send Heartbeat +* +* ***************************************/ +int mlacp_prepare_for_heartbeat(struct CSM* csm, char* buf, size_t max_buf_size) +{ + struct System* sys = NULL; + ICCHdr* icc_hdr = (ICCHdr*)buf; + struct mLACPHeartbeatTLV* tlv = (struct mLACPHeartbeatTLV*)&buf[sizeof(ICCHdr)]; + size_t msg_len = sizeof(ICCHdr) + sizeof(struct mLACPHeartbeatTLV); + + if (csm == NULL) + return MCLAG_ERROR; + + if (buf == NULL) + return MCLAG_ERROR; + + if (msg_len > max_buf_size) + return MCLAG_ERROR; + + if ((sys = system_get_instance()) == NULL) + return MCLAG_ERROR; + + /* Prepare for sync request */ + memset(buf, 0, max_buf_size); + + icc_hdr = (ICCHdr*)buf; + tlv = (struct mLACPHeartbeatTLV*)&buf[sizeof(ICCHdr)]; + + /* ICC header */ + mlacp_fill_icc_header(csm, icc_hdr, msg_len); + + /* System Config TLV */ + tlv->icc_parameter.u_bit = 0; + tlv->icc_parameter.f_bit = 0; + tlv->icc_parameter.type = htons(TLV_T_MLACP_HEARTBEAT); + + tlv->icc_parameter.len = htons(sizeof(struct mLACPHeartbeatTLV) - sizeof(ICCParameter)); + tlv->heartbeat = 0xFF; + return msg_len; +} + +/***************************************** +* Prepare Send warm-reboot flag +* +* ***************************************/ +int mlacp_prepare_for_warm_reboot(struct CSM* csm, char* buf, size_t max_buf_size) +{ + struct System* sys = NULL; + ICCHdr* icc_hdr = (ICCHdr*)buf; + struct mLACPWarmbootTLV* tlv = (struct mLACPWarmbootTLV*)&buf[sizeof(ICCHdr)]; + size_t msg_len = sizeof(ICCHdr) + sizeof(struct mLACPWarmbootTLV); + + if (csm == NULL) + return MCLAG_ERROR; + + if (buf == NULL) + return MCLAG_ERROR; + + if (msg_len > max_buf_size) + return MCLAG_ERROR; + + if ((sys = system_get_instance()) == NULL) + return MCLAG_ERROR; + + /* Prepare for sync request */ + memset(buf, 0, max_buf_size); + + icc_hdr = (ICCHdr*)buf; + tlv = (struct mLACPWarmbootTLV*)&buf[sizeof(ICCHdr)]; + + /* ICC header */ + mlacp_fill_icc_header(csm, icc_hdr, msg_len); + + /* System Config TLV */ + tlv->icc_parameter.u_bit = 0; + tlv->icc_parameter.f_bit = 0; + tlv->icc_parameter.type = htons(TLV_T_MLACP_WARMBOOT_FLAG); + + tlv->icc_parameter.len = htons(sizeof(struct mLACPWarmbootTLV) - sizeof(ICCParameter)); + tlv->warmboot = 0x1; + + ICCPD_LOG_DEBUG(__FUNCTION__, "Send warm reboot notification to peer!"); + return msg_len; +} + +/***************************************** +* Tool : Prepare ICC Header +* +* ***************************************/ +static int mlacp_fill_icc_header(struct CSM* csm, ICCHdr* icc_hdr, size_t msg_len) +{ + if (csm == NULL || icc_hdr == NULL) + return MCLAG_ERROR; + + /* ICC header */ + icc_hdr->ldp_hdr.u_bit = 0x0; + icc_hdr->ldp_hdr.msg_type = htons(MSG_T_RG_APP_DATA); + + icc_hdr->ldp_hdr.msg_len = htons(msg_len - MSG_L_INCLUD_U_BIT_MSG_T_L_FIELDS); + icc_hdr->ldp_hdr.msg_id = htonl(ICCP_MSG_ID); + ICCP_MSG_ID++; + iccp_csm_fill_icc_rg_id_tlv(csm, icc_hdr); + + return 0; +} + diff --git a/src/iccpd/src/mlacp_sync_update.c b/src/iccpd/src/mlacp_sync_update.c new file mode 100644 index 000000000000..40dc33187885 --- /dev/null +++ b/src/iccpd/src/mlacp_sync_update.c @@ -0,0 +1,740 @@ +/******************************************************************************** + * mlacp_sync_update.c + * Copyright(c) 2016-2019 Nephos/Estinet. + * + * 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, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintainer: jianjun, grace Li from nephos + * + *******************************************************************************/ +#include +#include + +#include +#include + +#include "../include/system.h" +#include "../include/logger.h" +#include "../include/mlacp_tlv.h" +#include "../include/iccp_csm.h" +#include "../include/mlacp_link_handler.h" +#include "../include/iccp_consistency_check.h" +#include "../include/port.h" +/***************************************** +* Port-Conf Update +* +* ***************************************/ +extern void update_if_ipmac_on_standby(struct LocalInterface* lif_po); +int mlacp_fsm_update_system_conf(struct CSM* csm, mLACPSysConfigTLV*sysconf) +{ + struct LocalInterface* lif = NULL; + + /*NOTE + a little tricky, we change the NodeID local side if collision happened first time*/ + if (sysconf->node_id == MLACP(csm).node_id) + MLACP(csm).node_id++; + + memcpy(MLACP(csm).remote_system.system_id, sysconf->sys_id, ETHER_ADDR_LEN); + MLACP(csm).remote_system.system_priority = ntohs(sysconf->sys_priority); + MLACP(csm).remote_system.node_id = sysconf->node_id; + + ICCPD_LOG_DEBUG(__FUNCTION__, "SystemID [%02X:%02X:%02X:%02X:%02X:%02X], SystemPriority [%d], Remote NodeID [%d], NodeID [%d]", + MLACP(csm).remote_system.system_id[0], MLACP(csm).remote_system.system_id[1], MLACP(csm).remote_system.system_id[2], + MLACP(csm).remote_system.system_id[3], MLACP(csm).remote_system.system_id[4], MLACP(csm).remote_system.system_id[5], + MLACP(csm).remote_system.system_priority, + MLACP(csm).remote_system.node_id, + MLACP(csm).node_id); + + LIST_FOREACH(lif, &(MLACP(csm).lif_list), mlacp_next) + { + update_if_ipmac_on_standby(lif); + } + + return 0; +} + +/***************************************** +* Port-Conf Update +* +* ***************************************/ +int mlacp_fsm_update_Agg_conf(struct CSM* csm, mLACPAggConfigTLV* portconf) +{ + struct PeerInterface* pif = NULL; + uint8_t po_active; + uint8_t new_create = 0; + + ICCPD_LOG_DEBUG(__FUNCTION__, "Port name %s, po id %d flag %d MAC[%02x:%02x:%02x:%02x:%02x:%02x] ", + portconf->agg_name, ntohs(portconf->agg_id), portconf->flags, portconf->mac_addr[0], portconf->mac_addr[1], portconf->mac_addr[2], + portconf->mac_addr[3], portconf->mac_addr[4], portconf->mac_addr[5] ); + + /* Looking for the peer port instance, is any peer if exist?*/ + pif = peer_if_find_by_name(csm, portconf->agg_name); + + /* Process purge*/ + if (portconf->flags & 0x02) + { + /*Purge*/ + if (pif != NULL ) + peer_if_destroy(pif); + else + MLACP(csm).need_to_sync = 1; + /*ICCPD_LOG_INFO("mlacp_fsm", + " Peer port %s is removed from port-channel member.",portconf->port_name);*/ + + return 0; + } + + if (pif == NULL && portconf->flags & 0x01) + { + pif = peer_if_create(csm, ntohs(portconf->agg_id), IF_T_PORT_CHANNEL); + if (pif == NULL) + return MCLAG_ERROR; + + new_create = 1; + } + + pif->po_id = ntohs(portconf->agg_id); + memcpy(pif->name, portconf->agg_name, portconf->agg_name_len); + memcpy(pif->mac_addr, portconf->mac_addr, ETHER_ADDR_LEN); + + po_active = (pif->state == PORT_STATE_UP); + update_stp_peer_link(csm, pif, po_active, new_create); + update_peerlink_isolate_from_pif(csm, pif, po_active, new_create); + pif->po_active = po_active; + + return 0; +} + +/***************************************** +* Agg Port-State Update +* +* ***************************************/ +int mlacp_fsm_update_Aggport_state(struct CSM* csm, mLACPAggPortStateTLV* tlv) +{ + struct PeerInterface* peer_if = NULL; + uint8_t po_active; + + if (csm == NULL || tlv == NULL) + return MCLAG_ERROR; + ICCPD_LOG_DEBUG(__FUNCTION__, "Portchannel id %d state %d", ntohs(tlv->agg_id), tlv->agg_state); + + po_active = (tlv->agg_state == PORT_STATE_UP); + + LIST_FOREACH(peer_if, &(MLACP(csm).pif_list), mlacp_next) + { + if (peer_if->type != IF_T_PORT_CHANNEL) + continue; + + if (peer_if->po_id != ntohs(tlv->agg_id)) + continue; + + peer_if->state = tlv->agg_state; + + update_stp_peer_link(csm, peer_if, po_active, 0); + update_peerlink_isolate_from_pif(csm, peer_if, po_active, 0); + + peer_if->po_active = po_active; + ICCPD_LOG_DEBUG(__FUNCTION__, "Update peer interface %s to state %s", peer_if->name, tlv->agg_state ? "down" : "up"); + + break; + } + + return 0; +} + +/***************************************** +* Recv from peer, MAC-Info Update +* ***************************************/ +int mlacp_fsm_update_mac_entry_from_peer( struct CSM* csm, struct mLACPMACData *MacData) +{ + + struct Msg* msg = NULL; + struct MACMsg *mac_msg = NULL, mac_data; + struct LocalInterface* local_if = NULL; + uint8_t from_mclag_intf = 0;/*0: orphan port, 1: MCLAG port*/ + + ICCPD_LOG_INFO(__FUNCTION__, + "Received MAC Info, port[%s] vid[%d] MAC[%s] type[%s]", + MacData->ifname, ntohs(MacData->vid), MacData->mac_str, MacData->type == MAC_SYNC_ADD ? "add" : "del"); + + /*Find the interface in MCLAG interface list*/ + LIST_FOREACH(local_if, &(MLACP(csm).lif_list), mlacp_next) + { + if (local_if->type == IF_T_PORT_CHANNEL && strcmp(local_if->name, MacData->ifname) == 0) + { + from_mclag_intf = 1; + break; + } + } + + /* update MAC list*/ + TAILQ_FOREACH(msg, &(MLACP(csm).mac_list), tail) + { + mac_msg = (struct MACMsg*)msg->buf; + + /*Same MAC is exist in local switch, this may be mac move*/ + if (strcmp(mac_msg->mac_str, MacData->mac_str) == 0 && mac_msg->vid == ntohs(MacData->vid)) + { + if (MacData->type == MAC_SYNC_ADD) + { + mac_msg->age_flag &= ~MAC_AGE_PEER; + ICCPD_LOG_DEBUG(__FUNCTION__, "Recv ADD, Remove peer age flag:%d ifname %s, MAC %s vlan-id %d", + mac_msg->age_flag, mac_msg->ifname, mac_msg->mac_str, mac_msg->vid); + + /*mac_msg->fdb_type = tlv->fdb_type;*/ + /*The port ifname is different to the local item*/ + if (from_mclag_intf == 0 || strcmp(mac_msg->ifname, MacData->ifname) != 0 || strcmp(mac_msg->origin_ifname, MacData->ifname) != 0) + { + if (mac_msg->fdb_type != MAC_TYPE_STATIC) + { + /*Update local item*/ + memcpy(&mac_msg->origin_ifname, MacData->ifname, MAX_L_PORT_NAME); + } + + /*If the MAC is learned from orphan port, or from MCLAG port but the local port is down*/ + if (from_mclag_intf == 0 || (local_if->state == PORT_STATE_DOWN && strcmp(mac_msg->ifname, csm->peer_itf_name) != 0)) + { + /*Set MAC_AGE_LOCAL flag*/ + mac_msg->age_flag = set_mac_local_age_flag(csm, mac_msg, 1); + + if (strlen(csm->peer_itf_name) != 0) + { + if (strcmp(mac_msg->ifname, csm->peer_itf_name) == 0) + { + /* This MAC is already point to peer-link */ + return 0; + } + + if (csm->peer_link_if && csm->peer_link_if->state == PORT_STATE_UP) + { + /*Redirect the mac to peer-link*/ + memcpy(&mac_msg->ifname, csm->peer_itf_name, IFNAMSIZ); + + /*Send mac add message to mclagsyncd*/ + add_mac_to_chip(mac_msg, MAC_TYPE_DYNAMIC); + } + else + { + /*must redirect but peerlink is down, del mac from ASIC*/ + /*if peerlink change to up, mac will add back to ASIC*/ + del_mac_from_chip(mac_msg); + + /*Redirect the mac to peer-link*/ + memcpy(&mac_msg->ifname, csm->peer_itf_name, IFNAMSIZ); + } + } + else + { + /*must redirect but no peerlink, del mac from ASIC*/ + del_mac_from_chip(mac_msg); + + /*Update local item*/ + memcpy(&mac_msg->ifname, MacData->ifname, MAX_L_PORT_NAME); + + /*if orphan port mac but no peerlink, don't keep this mac*/ + if (from_mclag_intf == 0) + { + TAILQ_REMOVE(&(MLACP(csm).mac_list), msg, tail); + free(msg->buf); + free(msg); + return 0; + } + } + } + else + { + /*Remove MAC_AGE_LOCAL flag*/ + mac_msg->age_flag = set_mac_local_age_flag(csm, mac_msg, 0); + + /*Update local item*/ + memcpy(&mac_msg->ifname, MacData->ifname, MAX_L_PORT_NAME); + + /*from MCLAG port and the local port is up, add mac to ASIC to update port*/ + add_mac_to_chip(mac_msg, MAC_TYPE_DYNAMIC); + } + } + + #if 0 + mac_msg->op_type = MAC_SYNC_ACK; + if (iccp_csm_init_msg(&msg_send, (char*)mac_msg, sizeof(struct MACMsg)) == 0) + { + /*Reply mac ack message to peer, peer will clean MAC_AGE_PEER flag*/ + TAILQ_INSERT_TAIL(&(MLACP(csm).mac_msg_list), msg_send, tail); + ICCPD_LOG_DEBUG(__FUNCTION__, "Recv ADD, MAC-msg-list enqueue: %s, add %s vlan-id %d, op_type %d", + mac_msg->ifname, mac_msg->mac_str, mac_msg->vid, mac_msg->op_type); + } + #endif + } + #if 0 + else if (tlv->type == MAC_SYNC_ACK) + { + /*Clean the MAC_AGE_PEER flag*/ + mac_msg->age_flag &= ~MAC_AGE_PEER; + ICCPD_LOG_DEBUG(__FUNCTION__, "Recv ACK, Remove peer age flag:%d ifname %s, add %s vlan-id %d, op_type %d", + mac_msg->age_flag, mac_msg->ifname, mac_msg->mac_str, mac_msg->vid, mac_msg->op_type); + } + #endif + + break; + } + } + + /* delete/add MAC list*/ + if (msg && MacData->type == MAC_SYNC_DEL) + { + mac_msg->age_flag |= MAC_AGE_PEER; + ICCPD_LOG_DEBUG(__FUNCTION__, "Recv DEL, Add peer age flag: %d ifname %s, MAC %s vlan-id %d", + mac_msg->age_flag, mac_msg->ifname, mac_msg->mac_str, mac_msg->vid); + + if (mac_msg->age_flag == (MAC_AGE_LOCAL | MAC_AGE_PEER)) + { + /*send mac del message to mclagsyncd.*/ + del_mac_from_chip(mac_msg); + + /*If local and peer both aged, del the mac*/ + TAILQ_REMOVE(&(MLACP(csm).mac_list), msg, tail); + free(msg->buf); + free(msg); + } + else + { + return 0; + } + } + else if (!msg && MacData->type == MAC_SYNC_ADD) + { + mac_msg = (struct MACMsg*)&mac_data; + mac_msg->fdb_type = MAC_TYPE_DYNAMIC; + mac_msg->vid = ntohs(MacData->vid); + sprintf(mac_msg->mac_str, "%s", MacData->mac_str); + sprintf(mac_msg->ifname, "%s", MacData->ifname); + sprintf(mac_msg->origin_ifname, "%s", MacData->ifname); + mac_msg->age_flag = 0; + + /*If the MAC is learned from orphan port, or from MCLAG port but the local port is down*/ + if (from_mclag_intf == 0 || local_if->state == PORT_STATE_DOWN) + { + /*Set MAC_AGE_LOCAL flag*/ + mac_msg->age_flag = set_mac_local_age_flag(csm, mac_msg, 1); + + if (strlen(csm->peer_itf_name) == 0) + { + ICCPD_LOG_DEBUG(__FUNCTION__, "From orphan port or portchannel is down, but peer-link is not configured: ifname %s, MAC %s vlan-id %d", + mac_msg->ifname, mac_msg->mac_str, mac_msg->vid); + + /*if orphan port mac but no peerlink, don't keep this mac*/ + if (from_mclag_intf == 0) + return 0; + } + else + { + /*Redirect the mac to peer-link*/ + memcpy(&mac_msg->ifname, csm->peer_itf_name, IFNAMSIZ); + + ICCPD_LOG_DEBUG(__FUNCTION__, "Redirect to peerlink for orphan port or portchannel is down, Add local age flag: %d ifname %s, MAC %s vlan-id %d", + mac_msg->age_flag, mac_msg->ifname, mac_msg->mac_str, mac_msg->vid); + } + } + else + { + /*Remove MAC_AGE_LOCAL flag*/ + mac_msg->age_flag = set_mac_local_age_flag(csm, mac_msg, 0); + } + + if (iccp_csm_init_msg(&msg, (char*)mac_msg, sizeof(struct MACMsg)) == 0) + { + TAILQ_INSERT_TAIL(&(MLACP(csm).mac_list), msg, tail); + /*ICCPD_LOG_INFO(__FUNCTION__, "add mac queue successfully");*/ + + /*If the mac is from orphan port, or from MCLAG port but the local port is down*/ + if (strcmp(mac_msg->ifname, csm->peer_itf_name) == 0) + { + /*Send mac add message to mclagsyncd*/ + if (csm->peer_link_if && csm->peer_link_if->state == PORT_STATE_UP) + add_mac_to_chip(mac_msg, mac_msg->fdb_type); + } + else + { + /*from MCLAG port and the local port is up*/ + add_mac_to_chip(mac_msg, mac_msg->fdb_type); + } + + #if 0 + mac_msg->op_type = MAC_SYNC_ACK; + if (iccp_csm_init_msg(&msg_send, (char*)mac_msg, sizeof(struct MACMsg)) == 0) + { + /*Reply mac ack message to peer, peer will clean MAC_AGE_PEER flag*/ + TAILQ_INSERT_TAIL(&(MLACP(csm).mac_msg_list), msg_send, tail); + ICCPD_LOG_DEBUG(__FUNCTION__, "MAC-msg-list enqueue: %s, add %s vlan-id %d, op_type %d", + mac_msg->ifname, mac_msg->mac_str, mac_msg->vid, mac_msg->op_type); + } + #endif + } + } + + return 0; +} + +int mlacp_fsm_update_mac_info_from_peer(struct CSM* csm, struct mLACPMACInfoTLV* tlv) +{ + int count = 0; + int i; + + if (!csm || !tlv) + return MCLAG_ERROR; + count = ntohs(tlv->num_of_entry); + ICCPD_LOG_INFO(__FUNCTION__, "Received MAC Info count %d", count ); + + for (i = 0; i < count; i++) + { + mlacp_fsm_update_mac_entry_from_peer(csm, &(tlv->MacEntry[i])); + } +} + +/***************************************** + * Tool : Add ARP Info into ARP list + * + ****************************************/ +void mlacp_enqueue_arp(struct CSM* csm, struct Msg* msg) +{ + struct ARPMsg *arp_msg = NULL; + + if (!csm) + { + if (msg) + free(msg); + return; + } + if (!msg) + return; + + arp_msg = (struct ARPMsg*)msg->buf; + if (arp_msg->op_type != ARP_SYNC_DEL) + { + TAILQ_INSERT_TAIL(&(MLACP(csm).arp_list), msg, tail); + } + + return; +} + +/***************************************** +* ARP-Info Update +* ***************************************/ +int mlacp_fsm_update_arp_entry(struct CSM* csm, struct ARPMsg *arp_entry) +{ + struct Msg* msg = NULL; + struct ARPMsg *arp_msg = NULL, arp_data; + struct LocalInterface* local_if; + struct LocalInterface *peer_link_if = NULL; + struct VLAN_ID *vlan_id_list = NULL; + int set_arp_flag = 0; + char mac_str[18] = ""; + + if (!csm || !arp_entry) + return MCLAG_ERROR; + + sprintf(mac_str, "%02x:%02x:%02x:%02x:%02x:%02x", arp_entry->mac_addr[0], arp_entry->mac_addr[1], arp_entry->mac_addr[2], + arp_entry->mac_addr[3], arp_entry->mac_addr[4], arp_entry->mac_addr[5]); + + ICCPD_LOG_INFO(__FUNCTION__, + "Received ARP Info, intf[%s] IP[%s], MAC[%02x:%02x:%02x:%02x:%02x:%02x]", + arp_entry->ifname, show_ip_str(arp_entry->ipv4_addr), + arp_entry->mac_addr[0], arp_entry->mac_addr[1], arp_entry->mac_addr[2], + arp_entry->mac_addr[3], arp_entry->mac_addr[4], arp_entry->mac_addr[5]); + + if (strncmp(arp_entry->ifname, "Vlan", 4) == 0) + { + peer_link_if = local_if_find_by_name(csm->peer_itf_name); + + if (peer_link_if && !local_if_is_l3_mode(peer_link_if)) + { + /* Is peer-linlk itf belong to a vlan the same as peer?*/ + LIST_FOREACH(vlan_id_list, &(peer_link_if->vlan_list), port_next) + { + if (!vlan_id_list->vlan_itf) + continue; + if (strcmp(vlan_id_list->vlan_itf->name, arp_entry->ifname) != 0) + continue; + if (!local_if_is_l3_mode(vlan_id_list->vlan_itf)) + continue; + + ICCPD_LOG_DEBUG(__FUNCTION__, "ARP is learnt from intf %s, peer-link %s is the member of this vlan", + vlan_id_list->vlan_itf->name, peer_link_if->name); + + /* Peer-link belong to L3 vlan is alive, set the ARP info*/ + set_arp_flag = 1; + + break; + } + } + } + + if (set_arp_flag == 0) + { + LIST_FOREACH(local_if, &(MLACP(csm).lif_list), mlacp_next) + { + if (local_if->type == IF_T_PORT_CHANNEL) + { + if (!local_if_is_l3_mode(local_if)) + { + /* Is the L2 MLAG itf belong to a vlan the same as peer?*/ + LIST_FOREACH(vlan_id_list, &(local_if->vlan_list), port_next) + { + if (!vlan_id_list->vlan_itf) + continue; + if (strcmp(vlan_id_list->vlan_itf->name, arp_entry->ifname) != 0) + continue; + if (!local_if_is_l3_mode(vlan_id_list->vlan_itf)) + continue; + + ICCPD_LOG_DEBUG(__FUNCTION__, "ARP is learnt from intf %s, mclag %s is the member of this vlan", + vlan_id_list->vlan_itf->name, local_if->name); + break; + } + + if (vlan_id_list && local_if->po_active == 1) + { + /* Any po of L3 vlan is alive, set the ARP info*/ + set_arp_flag = 1; + break; + } + } + else + { + /* Is the ARP belong to a L3 mode MLAG itf?*/ + if (strcmp(local_if->name, arp_entry->ifname) == 0) + { + ICCPD_LOG_DEBUG(__FUNCTION__, "ARP is learnt from mclag L3 intf %s", local_if->name); + if (local_if->po_active == 1) + { + /* po is alive, set the ARP info*/ + set_arp_flag = 1; + break; + } + } + else + { + continue; + } + } + } + } + } + + /* set dynamic ARP*/ + if (set_arp_flag == 1) + { + if (arp_entry->op_type == ARP_SYNC_ADD) + { + if (mlacp_fsm_arp_set(arp_entry->ifname, ntohl(arp_entry->ipv4_addr), mac_str) < 0) + { + ICCPD_LOG_WARN(__FUNCTION__, "ARP add failure for %s %s %s", + arp_entry->ifname, show_ip_str(arp_entry->ipv4_addr), mac_str); + return MCLAG_ERROR; + } + } + else + { + if (mlacp_fsm_arp_del(arp_entry->ifname, ntohl(arp_entry->ipv4_addr)) < 0) + { + ICCPD_LOG_WARN(__FUNCTION__, "ARP delete failure for %s %s %s", + arp_entry->ifname, show_ip_str(arp_entry->ipv4_addr), mac_str); + return MCLAG_ERROR; + } + } + + /*ICCPD_LOG_DEBUG(__FUNCTION__, "%s: ARP update for %s %s %s", + __FUNCTION__, arp_entry->ifname, show_ip_str(arp_entry->ipv4_addr), mac_str);*/ + } + else + { + ICCPD_LOG_DEBUG(__FUNCTION__, "Failure: port-channel is not alive"); + /*TODO Set static route through peer-link or just skip it?*/ + } + + /* update ARP list*/ + TAILQ_FOREACH(msg, &(MLACP(csm).arp_list), tail) + { + arp_msg = (struct ARPMsg*)msg->buf; + if (arp_msg->ipv4_addr == ntohl(arp_entry->ipv4_addr)) + { + /*arp_msg->op_type = tlv->type;*/ + sprintf(arp_msg->ifname, "%s", arp_entry->ifname); + memcpy(arp_msg->mac_addr, arp_entry->mac_addr, ETHER_ADDR_LEN); + break; + } + } + + /* delete/add ARP list*/ + if (msg && arp_entry->op_type == ARP_SYNC_DEL) + { + TAILQ_REMOVE(&(MLACP(csm).arp_list), msg, tail); + free(msg->buf); + free(msg); + /*ICCPD_LOG_INFO(__FUNCTION__, "Del arp queue successfully");*/ + } + else if (!msg && arp_entry->op_type == ARP_SYNC_ADD) + { + arp_msg = (struct ARPMsg*)&arp_data; + sprintf(arp_msg->ifname, "%s", arp_entry->ifname); + arp_msg->ipv4_addr = ntohl(arp_entry->ipv4_addr); + arp_msg->op_type = arp_entry->op_type; + memcpy(arp_msg->mac_addr, arp_entry->mac_addr, ETHER_ADDR_LEN); + if (iccp_csm_init_msg(&msg, (char*)arp_msg, sizeof(struct ARPMsg)) == 0) + { + mlacp_enqueue_arp(csm, msg); + /*ICCPD_LOG_INFO(__FUNCTION__, "Add arp queue successfully");*/ + } + } + + /* remove all ARP msg queue, when receive peer's ARP list at the same time*/ + TAILQ_FOREACH(msg, &(MLACP(csm).arp_msg_list), tail) + { + arp_msg = (struct ARPMsg*)msg->buf; + if (arp_msg->ipv4_addr == ntohl(arp_entry->ipv4_addr)) + break; + } + + while (msg) + { + arp_msg = (struct ARPMsg*)msg->buf; + TAILQ_REMOVE(&(MLACP(csm).arp_msg_list), msg, tail); + free(msg->buf); + free(msg); + TAILQ_FOREACH(msg, &(MLACP(csm).arp_msg_list), tail) + { + arp_msg = (struct ARPMsg*)msg->buf; + if (arp_msg->ipv4_addr == ntohl(arp_entry->ipv4_addr)) + break; + } + } + + return 0; +} + +int mlacp_fsm_update_arp_info(struct CSM* csm, struct mLACPARPInfoTLV* tlv) +{ + int count = 0; + int i; + + if (!csm || !tlv) + return MCLAG_ERROR; + count = ntohs(tlv->num_of_entry); + ICCPD_LOG_INFO(__FUNCTION__, "Received ARP Info count %d ", count ); + + for (i = 0; i < count; i++) + { + mlacp_fsm_update_arp_entry(csm, &(tlv->ArpEntry[i])); + } +} + +/***************************************** +* Port-Channel-Info Update +* ***************************************/ +int mlacp_fsm_update_port_channel_info(struct CSM* csm, + struct mLACPPortChannelInfoTLV* tlv) +{ + struct PeerInterface* peer_if = NULL; + struct VLAN_ID* peer_vlan_id = NULL; + int i = 0; + + if (csm == NULL || tlv == NULL ) + return MCLAG_ERROR; + + LIST_FOREACH(peer_if, &(MLACP(csm).pif_list), mlacp_next) + { + if (peer_if->type != IF_T_PORT_CHANNEL) + continue; + + if (peer_if->po_id != ntohs(tlv->agg_id)) + continue; + + LIST_FOREACH(peer_vlan_id, &(peer_if->vlan_list), port_next) + { + peer_vlan_id->vlan_removed = 1; + } + + /* Record peer info*/ + peer_if->ipv4_addr = ntohl(tlv->ipv4_addr); + peer_if->l3_mode = tlv->l3_mode; + + for (i = 0; i < ntohs(tlv->num_of_vlan_id); i++) + { + peer_if_add_vlan(peer_if, ntohs(tlv->vlanData[i].vlan_id)); + } + + peer_if_clean_unused_vlan(peer_if); + + iccp_consistency_check(peer_if->name); + + ICCPD_LOG_DEBUG(__FUNCTION__, "Peer intf %s info: ipv4 addr %s l3 mode %d", peer_if->name, show_ip_str( tlv->ipv4_addr), peer_if->l3_mode); + break; + } + + return 0; +} + +/***************************************** +* Peerlink port Update +* ***************************************/ +int mlacp_fsm_update_peerlink_info(struct CSM* csm, + struct mLACPPeerLinkInfoTLV* tlv) +{ + if (csm == NULL || tlv == NULL ) + return MCLAG_ERROR; + + if (!csm->peer_link_if) + { + ICCPD_LOG_WARN(__FUNCTION__, "Peerlink port info recv from peer, local peerlink is not exist!"); + return 0; + } + + if (csm->peer_link_if->type != tlv->port_type) + ICCPD_LOG_DEBUG(__FUNCTION__, "Peerlink port type of peer %d is not same with local %d !", tlv->port_type, csm->peer_link_if->type); + + if (tlv->port_type == IF_T_VXLAN && strncmp(csm->peer_itf_name, tlv->if_name, strlen(csm->peer_itf_name))) + ICCPD_LOG_DEBUG(__FUNCTION__, "Peerlink port is vxlan port, but peerlink port of peer %s is not same with local %s !", tlv->if_name, csm->peer_itf_name); + + return 0; +} + +/***************************************** +* Heartbeat Update +*****************************************/ +int mlacp_fsm_update_heartbeat(struct CSM* csm, struct mLACPHeartbeatTLV* tlv) +{ + if (!csm || !tlv) + return MCLAG_ERROR; + + time(&csm->heartbeat_update_time); + + return 0; +} + +/***************************************** +* warm-reboot flag Update +*****************************************/ +int mlacp_fsm_update_warmboot(struct CSM* csm, struct mLACPWarmbootTLV* tlv) +{ + if (!csm || !tlv) + return MCLAG_ERROR; + + time(&csm->peer_warm_reboot_time); + ICCPD_LOG_DEBUG(__FUNCTION__, "Receive warm reboot notification from peer!"); + + return 0; +} + diff --git a/src/iccpd/src/port.c b/src/iccpd/src/port.c new file mode 100644 index 000000000000..1414a1f86c87 --- /dev/null +++ b/src/iccpd/src/port.c @@ -0,0 +1,665 @@ +/* + * port.c + * + * Copyright(c) 2016-2019 Nephos/Estinet. + * + * 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, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintainer: jianjun, grace Li from nephos + */ + +#include +#include + +#include "../include/logger.h" +#include "../include/port.h" +#include "../include/system.h" +#include "../include/iccp_csm.h" + +void local_if_init(struct LocalInterface* local_if) +{ + if (local_if == NULL) + return; + + memset(local_if, 0, sizeof(struct LocalInterface)); + local_if->po_id = -1; + local_if->po_active = 1; //always guess the po is active + local_if->mlacp_state = MLACP_STATE_INIT; + local_if->type = IF_T_UNKNOW; + local_if->changed = 1; + local_if->port_config_sync = 0; + local_if->is_peer_link = 0; + local_if->is_arp_accept = 0; + local_if->l3_mode = 0; + local_if->state = PORT_STATE_DOWN; + local_if->prefixlen = 32; + local_if->csm = NULL; + local_if->isolate_to_peer_link = 0; + LIST_INIT(&local_if->vlan_list); + + return; +} + +void vlan_info_init(struct VLAN_ID* vlan) +{ + vlan->vid = -1; + vlan->vlan_removed = 0; + vlan->vlan_itf = NULL; + + return; +} + +struct LocalInterface* local_if_create(int ifindex, char* ifname, int type) +{ + struct System* sys = NULL; + struct LocalInterface* local_if = NULL; + struct CSM* csm; + struct If_info * cif = NULL; + + if (!ifname) + return NULL; + + if (!(sys = system_get_instance())) + return NULL; + + if (ifindex < 0) + return NULL; + + if ((local_if = local_if_find_by_ifindex(ifindex))) + return local_if; + + if (!(local_if = (struct LocalInterface*)malloc(sizeof(struct LocalInterface)))) + { + ICCPD_LOG_WARN(__FUNCTION__, "Port ifindex = %d, malloc failed", ifindex); + return NULL; + } + + local_if_init(local_if); + local_if->ifindex = ifindex; + local_if->type = type; + + if (local_if->type == IF_T_PORT_CHANNEL) + { + int i; + int len; + len = strlen(ifname); + + for (i = 0; i < len; ++i) + if (ifname[i] >= '0' && ifname[i] <= '9') + break; + + if (i >= len) + return NULL; + + local_if->po_id = atoi(&ifname[i]); + } + + if (ifname) + snprintf(local_if->name, MAX_L_PORT_NAME, "%s", ifname); + + switch (type) + { + case IF_T_PORT_CHANNEL: + break; + + case IF_T_PORT: + break; + + case IF_T_VLAN: + /* do nothing currently. */ + break; + + case IF_T_VXLAN: + /* do nothing currently. */ + break; + + default: + ICCPD_LOG_WARN(__FUNCTION__, "The type of local interface (%s) is not acceptable", ifname); + if (local_if) + free(local_if); + return NULL; + } + + ICCPD_LOG_NOTICE(__FUNCTION__, + "Create a local_if = %s ifindex = %d MAC = %02x:%02x:%02x:%02x:%02x:%02x, state = %s", + ifname, local_if->ifindex, local_if->mac_addr[0], local_if->mac_addr[1], local_if->mac_addr[2], + local_if->mac_addr[3], local_if->mac_addr[4], local_if->mac_addr[5], local_if->state ? "down" : "up"); + + LIST_INSERT_HEAD(&(sys->lif_list), local_if, system_next); + + /*Check the intf is peer-link? Only support PortChannel and Ethernet currently*/ + /*When set peer-link, the local-if is probably not created*/ + LIST_FOREACH(csm, &(sys->csm_list), next) + { + if (strcmp(local_if->name, csm->peer_itf_name) == 0) + { + local_if->is_peer_link = 1; + csm->peer_link_if = local_if; + break; + } + /*check the intf is bind with csm*/ + LIST_FOREACH(cif, &(csm->if_bind_list), csm_next) + { + if (strcmp(ifname, cif->name) == 0) + mlacp_bind_port_channel_to_csm(csm, ifname); + } + } + + return local_if; +} + +struct LocalInterface* local_if_find_by_name(const char* ifname) +{ + struct System* sys = NULL; + struct LocalInterface* local_if = NULL; + + if (!ifname) + return NULL; + + if (!(sys = system_get_instance())) + return NULL; + + LIST_FOREACH(local_if, &(sys->lif_list), system_next) + { + if (strcmp(local_if->name, ifname) == 0) + return local_if; + } + + return NULL; +} + +struct LocalInterface* local_if_find_by_ifindex(int ifindex) +{ + struct System* sys = NULL; + struct LocalInterface* local_if = NULL; + + if ((sys = system_get_instance()) == NULL) + return NULL; + + LIST_FOREACH(local_if, &(sys->lif_list), system_next) + { + if (local_if->ifindex == ifindex) + return local_if; + } + + return NULL; +} + +struct LocalInterface* local_if_find_by_po_id(int po_id) +{ + struct System* sys = NULL; + struct LocalInterface* local_if = NULL; + + if ((sys = system_get_instance()) == NULL) + return NULL; + + LIST_FOREACH(local_if, &(sys->lif_list), system_next) + { + if (local_if->type == IF_T_PORT_CHANNEL && local_if->po_id == po_id) + return local_if; + } + + return NULL; +} + +static void local_if_vlan_remove(struct LocalInterface *lif_vlan) +{ + struct System *sys = NULL; + struct LocalInterface *lif = NULL; + struct VLAN_ID *vlan = NULL; + + if ((sys = system_get_instance()) != NULL) + { + LIST_FOREACH(lif, &(sys->lif_list), system_next) + { + LIST_FOREACH(vlan, &(lif->vlan_list), port_next) + { + if (lif_vlan != vlan->vlan_itf) + continue; + + vlan->vlan_itf = NULL; + } + } + } + + return; +} + +static void local_if_po_remove(struct LocalInterface *lif_po) +{ + struct System *sys = NULL; + struct CSM *csm = NULL; + struct LocalInterface *lif = NULL; + + /* remove all po member*/ + if ((sys = system_get_instance()) != NULL) + { + csm = lif_po->csm; + if (csm) + { + LIST_FOREACH(lif, &(MLACP(csm).lif_list), mlacp_next) + { + if (lif->type != IF_T_PORT) + continue; + if (lif->po_id != lif_po->po_id) + continue; + + mlacp_unbind_local_if(lif); + } + } + } + + return; +} + +static void local_if_remove(struct LocalInterface *lif) +{ + mlacp_unbind_local_if(lif); + lif->po_id = -1; + + return; +} + +void local_if_destroy(char *ifname) +{ + struct LocalInterface* lif = NULL; + struct CSM *csm = NULL; + struct System *sys = NULL; + + if (!(sys = system_get_instance())) + return; + + lif = local_if_find_by_name(ifname); + if (!lif) + return; + + ICCPD_LOG_WARN(__FUNCTION__, "Destroy interface %s, %d\n", lif->name, lif->ifindex); + + if (lif->type == IF_T_VLAN) + local_if_vlan_remove(lif); + else if (lif->type == IF_T_PORT_CHANNEL) + local_if_po_remove(lif); + else + local_if_remove(lif); + + csm = lif->csm; + if (csm && csm->peer_link_if && strcmp(csm->peer_link_if->name, ifname) == 0) + { + /*if the peerlink interface is not created, peer connection can not establish*/ + scheduler_session_disconnect_handler(csm); + csm->peer_link_if->is_peer_link = 0; + csm->peer_link_if = NULL; + } + + if (csm && MLACP(csm).current_state == MLACP_STATE_EXCHANGE) + goto to_mlacp_purge; + else + goto to_sys_purge; + + to_sys_purge: + /* sys purge */ + LIST_REMOVE(lif, system_next); + if (lif->csm) + LIST_REMOVE(lif, mlacp_next); + LIST_INSERT_HEAD(&(sys->lif_purge_list), lif, system_purge_next); + return; + + to_mlacp_purge: + /* sys & mlacp purge */ + LIST_REMOVE(lif, system_next); + LIST_REMOVE(lif, mlacp_next); + LIST_INSERT_HEAD(&(sys->lif_purge_list), lif, system_purge_next); + LIST_INSERT_HEAD(&(MLACP(csm).lif_purge_list), lif, mlacp_purge_next); + return; +} + +int local_if_is_l3_mode(struct LocalInterface* local_if) +{ + int ret = 0; + + if (local_if == NULL) + return 0; + + if (local_if->ipv4_addr != 0) + ret = 1; + + return ret; +} + +void local_if_change_flag_clear(void) +{ + struct System* sys = NULL; + struct LocalInterface* lif = NULL; + + if ((sys = system_get_instance()) == NULL) + return; + + LIST_FOREACH(lif, &(sys->lif_list), system_next) + { + if (lif->changed == 1) + { + lif->changed = 0; + } + } + + return; +} + +void local_if_purge_clear(void) +{ + struct System* sys = NULL; + struct LocalInterface* lif = NULL; + + if ((sys = system_get_instance()) == NULL) + return; + + /* destroy purge if*/ + while (!LIST_EMPTY(&(sys->lif_purge_list))) + { + lif = LIST_FIRST(&(sys->lif_purge_list)); + ICCPD_LOG_DEBUG(__FUNCTION__, "Purge %s", lif->name); + LIST_REMOVE(lif, system_purge_next); + if (lif->mlacp_purge_next.le_next != 0 && lif->mlacp_purge_next.le_prev != 0) + LIST_REMOVE(lif, mlacp_purge_next); + local_if_del_all_vlan(lif); + free(lif); + } + + LIST_INIT(&(sys->lif_purge_list)); + + return; +} + +void local_if_finalize(struct LocalInterface* lif) +{ + if (lif == NULL) + return; + + local_if_del_all_vlan(lif); + + free(lif); + + return; +} + +struct PeerInterface* peer_if_create(struct CSM* csm, + int peer_if_number, int type) +{ + struct PeerInterface* peer_if = NULL; + + /* check csm*/ + if (csm == NULL) + return NULL; + + /* check id*/ + if (peer_if_number < 0) + { + ICCPD_LOG_WARN(__FUNCTION__, "peer interface id < 0"); + return NULL; + } + + /* check type*/ + if (type != IF_T_PORT && type != IF_T_PORT_CHANNEL) + { + ICCPD_LOG_WARN(__FUNCTION__, + "The type(%) of peer interface(%d) is not acceptable", + type, peer_if_number); + return NULL; + } + + /* create a new peer if*/ + if ((peer_if = (struct PeerInterface*)malloc(sizeof(struct PeerInterface))) == NULL) + { + ICCPD_LOG_WARN(__FUNCTION__, "Peer port id = %d, malloc failed", peer_if_number); + return NULL; + } + memset(peer_if, 0, sizeof(struct PeerInterface)); + + if (type == IF_T_PORT) + { + peer_if->ifindex = peer_if_number; + peer_if->type = IF_T_PORT; + peer_if->csm = csm; + } + else if (type == IF_T_PORT_CHANNEL) + { + peer_if->ifindex = peer_if_number; + peer_if->type = IF_T_PORT_CHANNEL; + } + + LIST_INSERT_HEAD(&(MLACP(csm).pif_list), peer_if, mlacp_next); + + return peer_if; +} + +struct PeerInterface* peer_if_find_by_name(struct CSM* csm, char* name) +{ + struct System* sys = NULL; + struct PeerInterface* peer_if = NULL; + + if ((sys = system_get_instance()) == NULL) + return NULL; + + if (csm == NULL) + return NULL; + + LIST_FOREACH(peer_if, &(csm->app_csm.mlacp.pif_list), mlacp_next) + { + if (strcmp(peer_if->name, name) == 0) + return peer_if; + } + + return NULL; +} + +void peer_if_del_all_vlan(struct PeerInterface* pif) +{ + struct VLAN_ID *pvlan = NULL; + + while (!LIST_EMPTY(&(pif->vlan_list))) + { + pvlan = LIST_FIRST(&(pif->vlan_list)); + ICCPD_LOG_DEBUG(__FUNCTION__, "Remove peer intf %s from VLAN %d", + pif->name, pvlan->vid); + LIST_REMOVE(pvlan, port_next); + free(pvlan); + } + + return; +} + +void peer_if_destroy(struct PeerInterface* pif) +{ + ICCPD_LOG_WARN(__FUNCTION__, "Destroy peer's interface %s, %d", + pif->name, pif->ifindex); + + /* destroy if*/ + LIST_REMOVE(pif, mlacp_next); + peer_if_del_all_vlan(pif); + + free(pif); + return; +} + +int local_if_add_vlan(struct LocalInterface* local_if, uint16_t vid) +{ + struct VLAN_ID *vlan = NULL; + char vlan_name[16] = ""; + + sprintf(vlan_name, "Vlan%d", vid); + + /* traverse 1 time */ + LIST_FOREACH(vlan, &(local_if->vlan_list), port_next) + { + if (vlan->vid == vid) + break; + } + + if (!vlan) + { + vlan = (struct VLAN_ID*)malloc(sizeof(struct VLAN_ID)); + if (!vlan) + return MCLAG_ERROR; + + ICCPD_LOG_DEBUG(__FUNCTION__, "Add %s to VLAN %d", local_if->name, vid); + local_if->port_config_sync = 1; + LIST_INSERT_HEAD(&(local_if->vlan_list), vlan, port_next); + } + + vlan_info_init(vlan); + vlan->vid = vid; + vlan->vlan_removed = 0; + vlan->vlan_itf = local_if_find_by_name(vlan_name); + + update_if_ipmac_on_standby(local_if); + + return 0; +} + +void local_if_del_vlan(struct LocalInterface* local_if, uint16_t vid) +{ + struct VLAN_ID *vlan = NULL; + + /* traverse 1 time */ + LIST_FOREACH(vlan, &(local_if->vlan_list), port_next) + { + if (vlan->vid == vid) + break; + } + + if (vlan != NULL) + { + LIST_REMOVE(vlan, port_next); + free(vlan); + local_if->port_config_sync = 1; + } + + ICCPD_LOG_DEBUG(__FUNCTION__, "Remove %s from VLAN %d", local_if->name, vid); + + return; +} + +void local_if_del_all_vlan(struct LocalInterface* lif) +{ + struct VLAN_ID* vlan = NULL; + + while (!LIST_EMPTY(&(lif->vlan_list))) + { + vlan = LIST_FIRST(&(lif->vlan_list)); + ICCPD_LOG_DEBUG(__FUNCTION__, "Remove %s from VLAN %d", lif->name, vlan->vid); + LIST_REMOVE(vlan, port_next); + free(vlan); + } + + return; +} + +/* Add VLAN from peer-link*/ +int peer_if_add_vlan(struct PeerInterface* peer_if, uint16_t vlan_id) +{ + struct VLAN_ID *peer_vlan = NULL; + char vlan_name[16] = ""; + + sprintf(vlan_name, "Vlan%d", vlan_id); + + /* traverse 1 time */ + LIST_FOREACH(peer_vlan, &(peer_if->vlan_list), port_next) + { + if (peer_vlan->vid == vlan_id) + { + ICCPD_LOG_DEBUG(__FUNCTION__, "Update VLAN ID %d for peer intf %s", peer_vlan->vid, peer_if->name); + break; + } + } + + if (!peer_vlan) + { + peer_vlan = (struct VLAN_ID*)malloc(sizeof(struct VLAN_ID)); + if (!peer_vlan) + return MCLAG_ERROR; + + ICCPD_LOG_DEBUG(__FUNCTION__, "Add peer intf %s to VLAN %d", peer_if->name, vlan_id); + LIST_INSERT_HEAD(&(peer_if->vlan_list), peer_vlan, port_next); + } + + vlan_info_init(peer_vlan); + peer_vlan->vid = vlan_id; + peer_vlan->vlan_removed = 0; + + return 0; +} + +/* Used by sync update*/ +int peer_if_clean_unused_vlan(struct PeerInterface* peer_if) +{ + struct VLAN_ID *peer_vlan = NULL; + struct VLAN_ID *peer_vlan_next = NULL; + + /* traverse 1 time */ + LIST_FOREACH(peer_vlan_next, &(peer_if->vlan_list), port_next) + { + if (peer_vlan != NULL) + { + ICCPD_LOG_DEBUG(__FUNCTION__, "Remove peer intf %s from VLAN %d", peer_if->name, peer_vlan->vid); + LIST_REMOVE(peer_vlan, port_next); + free(peer_vlan); + peer_vlan = NULL; + + } + if (peer_vlan_next->vlan_removed == 1) + peer_vlan = peer_vlan_next; + } + + if (peer_vlan != NULL) + { + ICCPD_LOG_DEBUG(__FUNCTION__, "Remove peer intf %s from VLAN %d", peer_if->name, peer_vlan->vid); + LIST_REMOVE(peer_vlan, port_next); + free(peer_vlan); + } + + return 0; +} + +int set_sys_arp_accept_flag(char* ifname, int flag) +{ + FILE *file_ptr = NULL; + char cmd[64]; + char arp_file[64]; + char buf[2]; + int result = MCLAG_ERROR; + + memset(arp_file, 0, 64); + snprintf(arp_file, 63, "/proc/sys/net/ipv4/conf/%s/arp_accept", ifname); + if (!(file_ptr = fopen(arp_file, "r"))) + { + ICCPD_LOG_WARN(__func__, "Failed to find device %s from %s", ifname, arp_file); + return result; + } + + fgets(buf, sizeof(buf), file_ptr); + if (atoi(buf) == flag) + result = 0; + else + { + memset(cmd, 0, 64); + snprintf(cmd, 63, "echo %d > /proc/sys/net/ipv4/conf/%s/arp_accept", flag, ifname); + if (system(cmd)) + ICCPD_LOG_WARN(__func__, "Failed to execute cmd = %s", flag, cmd); + } + + fclose(file_ptr); + return result; +} diff --git a/src/iccpd/src/scheduler.c b/src/iccpd/src/scheduler.c new file mode 100644 index 000000000000..9d08e137dce0 --- /dev/null +++ b/src/iccpd/src/scheduler.c @@ -0,0 +1,770 @@ +/* + * scheduler.c + * + * Copyright(c) 2016-2019 Nephos/Estinet. + * + * 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, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintainer: jianjun, grace Li from nephos + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../include/logger.h" +#include "../include/system.h" +#include "../include/scheduler.h" +#include "../include/iccp_csm.h" +#include "../include/iccp_ifm.h" +#include "../include/iccp_cmd.h" +#include "../include/mlacp_link_handler.h" +#include "../include/iccp_netlink.h" + +/****************************************************** +* +* Global Variable +* +******************************************************/ + +static int session_conn_thread_lock(pthread_mutex_t *conn_mutex) +{ + return 1; /*pthread_mutex_lock(conn_mutex);*/ +} + +static int session_conn_thread_trylock(pthread_mutex_t *conn_mutex) +{ + return 0; /*pthread_mutex_trylock(conn_mutex);*/ +} + +static int session_conn_thread_unlock(pthread_mutex_t *conn_mutex) +{ + return 1;/* pthread_mutex_unlock(conn_mutex);*/ +} + +static void heartbeat_check(struct CSM *csm) +{ + if (csm->heartbeat_update_time == 0) + { + time(&csm->heartbeat_update_time); + return; + } + + if ( (time(NULL) - csm->heartbeat_update_time) > HEARTBEAT_TIMEOUT_SEC) + { + /* hearbeat timeout*/ + ICCPD_LOG_WARN(__FUNCTION__, "iccpd connection timeout (heartbeat)"); + scheduler_session_disconnect_handler(csm); + } + + return; +} + +static void heartbeat_update(struct CSM *csm) +{ + if (csm->sock_fd > 0) + { + heartbeat_check(csm); + } + + return; +} + +/* Transit FSM of all connections */ +static int scheduler_transit_fsm() +{ + struct CSM* csm = NULL; + struct System* sys = NULL; + + if ((sys = system_get_instance()) == NULL) + return MCLAG_ERROR; + + LIST_FOREACH(csm, &(sys->csm_list), next) + { + heartbeat_update(csm); + iccp_csm_transit(csm); + app_csm_transit(csm); + mlacp_fsm_transit(csm); + + if (MLACP(csm).current_state == MLACP_STATE_EXCHANGE && (time(NULL) - sys->csm_trans_time) >= 60) + { + iccp_get_fdb_change_from_syncd(); + sys->csm_trans_time = time(NULL); + } + } + + local_if_change_flag_clear(); + local_if_purge_clear(); + + return 1; +} + +/* Receive packets call back function */ +int scheduler_csm_read_callback(struct CSM* csm) +{ + struct Msg* msg = NULL; + /*peer message*/ + char *peer_msg = g_csm_buf; + LDPHdr* ldp_hdr = (LDPHdr*)peer_msg; + char* data = &peer_msg[sizeof(LDPHdr)]; + size_t data_len = 0; + size_t pos = 0; + int recv_len = 0, len = 0, retval; + + if (csm->sock_fd <= 0) + return MCLAG_ERROR; + + memset(peer_msg, 0, CSM_BUFFER_SIZE); + + recv_len = 0; + + while (recv_len != sizeof(LDPHdr)) + { + len = recv(csm->sock_fd, peer_msg + recv_len, sizeof(LDPHdr) - recv_len, 0); + if (len == -1) + { + perror("recv(). Error"); + goto recv_err; + } + else if (len == 0) + { + ICCPD_LOG_WARN(__FUNCTION__, "Peer disconnect for receive error"); + goto recv_err; + } + recv_len += len; + /*usleep(100);*/ + } + + data_len = ntohs(ldp_hdr->msg_len) - MSG_L_INCLUD_U_BIT_MSG_T_L_FIELDS; + pos = 0; + + while (data_len > 0) + { + recv_len = recv(csm->sock_fd, &data[pos], data_len, 0); + if (recv_len == -1) + { + perror("continue recv(). Error"); + goto recv_err; + } + else if (recv_len == 0) + { + ICCPD_LOG_WARN(__FUNCTION__, "Peer disconnect for read error"); + goto recv_err; + } + data_len -= recv_len; + pos += recv_len; + /*usleep(100);*/ + } + + retval = iccp_csm_init_msg(&msg, peer_msg, ntohs(ldp_hdr->msg_len) + MSG_L_INCLUD_U_BIT_MSG_T_L_FIELDS); + if (retval == 0) + { + iccp_csm_enqueue_msg(csm, msg); + ++csm->icc_msg_in_count; + } + else + ++csm->i_msg_in_count; + + return 1; + + recv_err: + scheduler_session_disconnect_handler(csm); + return MCLAG_ERROR; +} + +/* Handle server accept client */ +int scheduler_server_accept() +{ + int new_fd; + int ret = MCLAG_ERROR; + struct CSM* csm = NULL; + struct System* sys = NULL; + struct sockaddr_in client_addr; + socklen_t addr_len; + + if ((sys = system_get_instance()) == NULL ) + { + return MCLAG_ERROR; + } + if (sys->server_fd <= 0) + { + return MCLAG_ERROR; + } + + addr_len = sizeof(struct sockaddr_in); + new_fd = accept(sys->server_fd, (struct sockaddr *)&client_addr, &addr_len); + if (new_fd == -1) + { + goto reject_client; + } + else + { + csm = system_get_csm_by_peer_ip(inet_ntoa(client_addr.sin_addr)); + if (!csm) + { + /* can't find csm with peer ip*/ + ICCPD_LOG_INFO(__FUNCTION__, "csm null with peer ip [%s]", inet_ntoa(client_addr.sin_addr)); + goto reject_client; + } + + if (csm->sock_fd > 0) + { + /* peer already connected*/ + ICCPD_LOG_INFO(__FUNCTION__, "csm sock is connected with peer ip [%s]", inet_ntoa(client_addr.sin_addr)); + goto reject_client; + } + + if ((ret = scheduler_check_csm_config(csm)) < 0) + { + /* csm config error*/ + ICCPD_LOG_INFO(__FUNCTION__, "csm config error with peer ip [%s]", inet_ntoa(client_addr.sin_addr)); + goto reject_client; + } + } + + /* Accept*/ + goto accept_client; + + reject_client: + if (new_fd >= 0) + close(new_fd); + return MCLAG_ERROR; + + accept_client: + session_conn_thread_lock(&csm->conn_mutex); + ICCPD_LOG_INFO(__FUNCTION__, "Server Accept, SocketFD [%d], %p", new_fd, csm); + + struct epoll_event event; + int err; + event.data.fd = new_fd; + event.events = EPOLLIN; + err = epoll_ctl(sys->epoll_fd, EPOLL_CTL_ADD, new_fd, &event); + if (err) + { + session_conn_thread_unlock(&csm->conn_mutex); + goto reject_client; + } + + csm->sock_fd = new_fd; + csm->current_state = ICCP_NONEXISTENT; + FD_SET(new_fd, &(sys->readfd)); + sys->readfd_count++; + session_conn_thread_unlock(&csm->conn_mutex); + return 0; +} + +void iccp_get_start_type(struct System* sys) +{ + FILE* fp; + + memset(g_csm_buf, 0, CSM_BUFFER_SIZE); + + fp = fopen("/proc/cmdline", "r"); + if (!fp) + { + ICCPD_LOG_WARN(__FUNCTION__, "Error: Can't open file /proc/cmdline!"); + return; + } + + fread(g_csm_buf, CSM_BUFFER_SIZE, 1, fp); + (void)fclose(fp); + + if (strstr(g_csm_buf, "SONIC_BOOT_TYPE=warm")) + sys->warmboot_start = WARM_REBOOT; + + return; +} + +/* scheduler initialization */ +void scheduler_init() +{ + struct System* sys = NULL; + + if (!(sys = system_get_instance())) + return; + + iccp_get_start_type(sys); + /*Get kernel interface and port */ + iccp_sys_local_if_list_get_init(); + iccp_sys_local_if_list_get_addr(); + /*Interfaces must be created before this func called*/ + iccp_config_from_file(sys->config_file_path); + + /*Get kernel ARP info */ + iccp_arp_get_init(); + + if (iccp_connect_syncd() < 0) + { + ICCPD_LOG_WARN(__FUNCTION__, "Syncd info socket connect fail"); + } + else + { + ICCPD_LOG_DEBUG(__FUNCTION__, "Syncd info socket connect success"); + } + + if (mclagd_ctl_sock_create() < 0) + { + ICCPD_LOG_WARN(__FUNCTION__, "Mclagd ctl info socket connect fail"); + } + + return; +} + +extern int mlacp_prepare_for_warm_reboot(struct CSM* csm, char* buf, size_t max_buf_size); +void mlacp_sync_send_warmboot_flag() +{ + struct System* sys = NULL; + struct CSM* csm = NULL; + int msg_len = 0; + + if ((sys = system_get_instance()) == NULL) + return; + + LIST_FOREACH(csm, &(sys->csm_list), next) + { + if (MLACP(csm).current_state == MLACP_STATE_EXCHANGE) + { + memset(g_csm_buf, 0, CSM_BUFFER_SIZE); + msg_len = mlacp_prepare_for_warm_reboot(csm, g_csm_buf, CSM_BUFFER_SIZE); + iccp_csm_send(csm, g_csm_buf, msg_len); + } + } + + return; +} + +int iccp_receive_signal_handler(struct System* sys) +{ + char ctrl_byte; + int err = 0; + + err = read(sys->sig_pipe_r, &ctrl_byte, 1); + if (err == -1) + { + ICCPD_LOG_DEBUG(__FUNCTION__, "Read sig_pipe_r fail !"); + return err; + } + + switch (ctrl_byte) + { + case 'w': + /*send packet to peer*/ + mlacp_sync_send_warmboot_flag(); + sys->warmboot_exit = WARM_REBOOT; + break; + + default: + break; + } + + return 0; +} + +/* Thread fetch to call */ +void scheduler_loop() +{ + struct System* sys = NULL; + + if ((sys = system_get_instance()) == NULL) + return; + + while (1) + { + if (sys->sync_fd <= 0) + { + iccp_connect_syncd(); + } + + /*handle socket slelect event ,If no message received, it will block 0.1s*/ + iccp_handle_events(sys); + /*csm, app state machine transit */ + scheduler_transit_fsm(); + + if (sys->warmboot_exit == WARM_REBOOT) + { + ICCPD_LOG_DEBUG(__FUNCTION__, "Warm reboot exit ......"); + return; + } + } + + return; +} + +/***************************************** +* Sync portchannel MAC with kernel +* +* ***************************************/ +int mlacp_sync_with_kernel_callback() +{ + struct System* sys = NULL; + struct CSM* csm = NULL; + struct LocalInterface* local_if = NULL; + + if ((sys = system_get_instance()) == NULL) + { + goto out; + } + + /* traverse all CSM */ + LIST_FOREACH(csm, &(sys->csm_list), next) + { + /* Sync MLAG po state with kernel*/ + LIST_FOREACH(local_if, &(MLACP(csm).lif_list), mlacp_next) + { + if (local_if->type == IF_T_PORT_CHANNEL) + { + /* sync system info from one port-channel device*/ + if (memcmp(MLACP(csm).system_id, local_if->mac_addr, ETHER_ADDR_LEN) != 0) + { + memcpy(MLACP(csm).system_id, local_if->mac_addr, ETHER_ADDR_LEN); + MLACP(csm).system_config_changed = 1; + break; + } + } + } + } + + out: + return 0; +} + +/* Scheduler start while loop */ +void scheduler_start() +{ + /*mlacp_sync_with_kernel_callback();*/ + + scheduler_loop(); + + return; +} + +/* Scheduler tear down */ +void scheduler_finalize() +{ + struct System* sys = NULL; + + if ((sys = system_get_instance()) == NULL) + return; + + syncd_info_close(); + + log_finalize(); + + ICCPD_LOG_INFO(__FUNCTION__, "Scheduler is terminated."); + + return; +} + +void session_client_conn_handler(struct CSM *csm) +{ + struct System* sys = NULL; + struct sockaddr_in peer_addr; + int connFd = -1, connStat = -1; + struct timeval con_tv; + socklen_t len = sizeof(con_tv); + int err = 0; + + struct sockaddr_in src_addr; + bzero(&(src_addr), sizeof(src_addr)); + src_addr.sin_family = PF_INET; + src_addr.sin_port = 0; + src_addr.sin_addr.s_addr = inet_addr(csm->sender_ip); + + /* Lock the thread*/ + session_conn_thread_lock(&csm->conn_mutex); + + sys = system_get_instance(); + if (!sys) + goto conn_fail; + + /* Create sock*/ + connFd = socket(PF_INET, SOCK_STREAM, 0); + bzero(&peer_addr, sizeof(peer_addr)); + peer_addr.sin_family = PF_INET; + peer_addr.sin_port = htons(ICCP_TCP_PORT); + peer_addr.sin_addr.s_addr = inet_addr(csm->peer_ip); + if (connFd == -1) + { + ICCPD_LOG_DEBUG(__FUNCTION__, "Peer IP:%s Socket FD creation failed.", + csm->peer_ip); + goto conn_fail; + } + + /* Set connect timeout secs*/ + con_tv.tv_sec = 0; + con_tv.tv_usec = CONNECT_TIMEOUT_MSEC * 1000; + if (setsockopt(connFd, SOL_SOCKET, SO_SNDTIMEO, &con_tv, len) == -1) + { + ICCPD_LOG_INFO(__FUNCTION__, "Set socket timeout fail"); + } + + err = bind(connFd, (struct sockaddr*)&(src_addr), sizeof(src_addr)); + if (err < 0) + { + ICCPD_LOG_INFO(__FUNCTION__, "Bind socket failed. Error = %d errno = %d ",err,errno); + goto conn_fail; + } + + /* Try conn*/ + ICCPD_LOG_INFO(__FUNCTION__, "Connecting. peer ip = [%s], %p", csm->peer_ip, csm); + connStat = connect(connFd, (struct sockaddr*)&(peer_addr), sizeof(peer_addr)); + ICCPD_LOG_INFO(__FUNCTION__, "Connection. fd = [%d], status = [%d], %p", + connFd, connStat, csm); + + if (connStat != 0) + { + /* Conn Fail*/ + goto conn_fail; + } + else + { + /* Conn OK*/ + struct epoll_event event; + int err; + event.data.fd = connFd; + event.events = EPOLLIN; + err = epoll_ctl(sys->epoll_fd, EPOLL_CTL_ADD, connFd, &event); + if (err) + goto conn_fail; + csm->sock_fd = connFd; + FD_SET(connFd, &(sys->readfd)); + sys->readfd_count++; + ICCPD_LOG_INFO(__FUNCTION__, "Connect to server %s sucess .", csm->peer_ip); + goto conn_ok; + } + + conn_fail: + if (connFd >= 0) + { + csm->sock_fd = -1; + close(connFd); + } + conn_ok: + time(&csm->connTimePrev); + session_conn_thread_unlock(&csm->conn_mutex); + return; +} + +/* Create socket connect to peer */ +int scheduler_prepare_session(struct CSM* csm) +{ + int ret = MCLAG_ERROR; + uint32_t local_ip = 0; + uint32_t peer_ip = 0; + + /* Init time_t*/ + if (csm->connTimePrev == 0) + { + time(&csm->connTimePrev); + } + + /* Don't conn to svr continously*/ + if ((time(NULL) - csm->connTimePrev) < CONNECT_INTERVAL_SEC) + { + goto no_time_update; + } + + /* Already conn?*/ + if (csm->sock_fd > 0) + { + goto time_update; + } + + if ((ret = scheduler_check_csm_config(csm)) < 0) + goto time_update; + + /* Who is client*/ + local_ip = inet_addr(csm->sender_ip); + peer_ip = inet_addr(csm->peer_ip); + if (local_ip > peer_ip) + { + goto time_update; + } + else if (local_ip == peer_ip) + { + ICCPD_LOG_WARN(__FUNCTION__, "Local IP must not be the same as the peer IP."); + goto time_update; + } + + if (session_conn_thread_trylock(&csm->conn_mutex) == 0) + { + session_client_conn_handler(csm); + session_conn_thread_unlock(&csm->conn_mutex); + } + + time_update: + time(&csm->connTimePrev); + return 0; + + no_time_update: + return 0; +} + +/* Server socket initialization */ +void scheduler_server_sock_init() +{ + int optval = 1; + struct System* sys = NULL; + struct sockaddr_in src_addr; + + if ((sys = system_get_instance()) == NULL) + return; + + sys->server_fd = socket(PF_INET, SOCK_STREAM, 0); + bzero(&(src_addr), sizeof(src_addr)); + src_addr.sin_family = PF_INET; + src_addr.sin_port = htons(ICCP_TCP_PORT); + src_addr.sin_addr.s_addr = INADDR_ANY; + + if (sys->server_fd == -1) + { + ICCPD_LOG_ERR(__FUNCTION__, "Server Socket FD creation failed."); + return; + } + + if (setsockopt(sys->server_fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) == -1) + { + ICCPD_LOG_INFO(__FUNCTION__, "Set socket option failed. Error"); + /*return;*/ + } + + if (bind(sys->server_fd, (struct sockaddr*)&(src_addr), sizeof(src_addr)) < 0) + { + ICCPD_LOG_INFO(__FUNCTION__, "Bind socket failed. Error"); + return; + } + + if (listen(sys->server_fd, MAX_ACCEPT_CONNETIONS) == -1) + { + ICCPD_LOG_INFO(__FUNCTION__, "Listen failed. Error"); + return; + } + + ICCPD_LOG_INFO(__FUNCTION__, "Server socket init done."); + + return; +} + +int iccp_get_server_sock_fd() +{ + struct System* sys = NULL; + + if ((sys = system_get_instance()) == NULL) + return 0; + + return sys->server_fd; +} + +/* Server socket initialization */ +int scheduler_check_csm_config(struct CSM* csm) +{ + int ret = 1; + struct LocalInterface* lif = NULL; + struct System* sys = NULL; + + if ((sys = system_get_instance()) == NULL) + return MCLAG_ERROR; + + if (csm == NULL ) + return MCLAG_ERROR; + + if (csm->mlag_id <= 0) + ret = MCLAG_ERROR; + else if (strlen(csm->peer_ip) <= 0) + ret = MCLAG_ERROR; + else if (strlen(csm->sender_ip) <= 0) + ret = MCLAG_ERROR; + else if (strlen(csm->peer_itf_name) != 0) + { + lif = local_if_find_by_name(csm->peer_itf_name); + if (lif == NULL) + { + /*if peer-link is configured but the interface is not created, peer connection can not establish*/ + return MCLAG_ERROR; + } + else + { + lif->is_peer_link = 1; + csm->peer_link_if = lif; + } + } + + if (ret == MCLAG_ERROR) + ICCPD_LOG_INFO(__FUNCTION__, "mclag config is not complete or conflicting, please check!"); + + /* Decide STP role*/ + iccp_csm_stp_role_count(csm); + + return ret; +} + +int scheduler_unregister_sock_read_event_callback(struct CSM* csm) +{ + struct System* sys = NULL; + + if ((sys = system_get_instance()) == NULL ) + return MCLAG_ERROR; + + if (csm == NULL ) + { + return MCLAG_ERROR; + } + + FD_CLR(csm->sock_fd, &(sys->readfd)); + + return 0; +} + +void scheduler_session_disconnect_handler(struct CSM* csm) +{ + struct System* sys = NULL; + + if ((sys = system_get_instance()) == NULL ) + return; + + struct epoll_event event; + + if (csm == NULL) + return; + + session_conn_thread_lock(&csm->conn_mutex); + scheduler_unregister_sock_read_event_callback(csm); + if (csm->sock_fd > 0) + { + event.data.fd = csm->sock_fd; + event.events = EPOLLIN; + epoll_ctl(sys->epoll_fd, EPOLL_CTL_DEL, csm->sock_fd, &event); + + close(csm->sock_fd); + csm->sock_fd = -1; + } + + mlacp_peer_disconn_handler(csm); + MLACP(csm).current_state = MLACP_STATE_INIT; + iccp_csm_status_reset(csm, 0); + time(&csm->connTimePrev); + session_conn_thread_unlock(&csm->conn_mutex); + + return; +} diff --git a/src/iccpd/src/system.c b/src/iccpd/src/system.c new file mode 100644 index 000000000000..9665b416ca52 --- /dev/null +++ b/src/iccpd/src/system.c @@ -0,0 +1,200 @@ +/* + * system.c + * + * Copyright(c) 2016-2019 Nephos/Estinet. + * + * 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, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintainer: jianjun, grace Li from nephos + */ + +#include + +#include "../include/iccp_csm.h" +#include "../include/logger.h" +#include "../include/iccp_netlink.h" +#include "../include/scheduler.h" + +/* Singleton */ +struct System* system_get_instance() +{ + static struct System* sys = NULL; + + if (sys == NULL ) + { + sys = (struct System*)malloc(sizeof(struct System)); + if (sys == NULL ) + { + ICCPD_LOG_WARN(__FUNCTION__, "Failed to obtain system instance."); + return NULL; + } + system_init(sys); + } + + return sys; +} + +/* System instance initialization */ +void system_init(struct System* sys) +{ + if (sys == NULL ) + return; + + sys->server_fd = -1; + sys->sync_fd = -1; + sys->sync_ctrl_fd = -1; + sys->arp_receive_fd = -1; + sys->epoll_fd = -1; + sys->family = -1; + sys->warmboot_start = 0; + sys->warmboot_exit = 0; + LIST_INIT(&(sys->csm_list)); + LIST_INIT(&(sys->lif_list)); + LIST_INIT(&(sys->lif_purge_list)); + + sys->log_file_path = strdup("/var/log/iccpd.log"); + sys->cmd_file_path = strdup("/var/run/iccpd/iccpd.vty"); + sys->config_file_path = strdup("/etc/iccpd/iccpd.conf"); + sys->mclagdctl_file_path = strdup("/var/run/iccpd/mclagdctl.sock"); + sys->pid_file_fd = 0; + sys->telnet_port = 2015; + FD_ZERO(&(sys->readfd)); + sys->readfd_count = 0; + sys->csm_trans_time = 0; + sys->need_sync_team_again = 0; + sys->need_sync_netlink_again = 0; + scheduler_server_sock_init(); + iccp_system_init_netlink_socket(); + iccp_init_netlink_event_fd(sys); +} + +/* System instance tear down */ +void system_finalize() +{ + struct System* sys = NULL; + struct CSM* csm = NULL; + struct LocalInterface* local_if = NULL; + + if ((sys = system_get_instance()) == NULL ) + return; + + ICCPD_LOG_INFO(__FUNCTION__, "System resource pool is destructing."); + + while (!LIST_EMPTY(&(sys->csm_list))) + { + csm = LIST_FIRST(&(sys->csm_list)); + iccp_csm_finalize(csm); + } + + /* Release all port objects */ + while (!LIST_EMPTY(&(sys->lif_list))) + { + local_if = LIST_FIRST(&(sys->lif_list)); + LIST_REMOVE(local_if, system_next); + local_if_finalize(local_if); + } + + while (!LIST_EMPTY(&(sys->lif_purge_list))) + { + local_if = LIST_FIRST(&(sys->lif_purge_list)); + LIST_REMOVE(local_if, system_purge_next); + local_if_finalize(local_if); + } + + iccp_system_dinit_netlink_socket(); + + if (sys->log_file_path != NULL ) + free(sys->log_file_path); + if (sys->cmd_file_path != NULL ) + free(sys->cmd_file_path); + if (sys->config_file_path != NULL ) + free(sys->config_file_path); + if (sys->pid_file_fd > 0) + close(sys->pid_file_fd); + if (sys->server_fd > 0) + close(sys->server_fd); + if (sys->sync_fd > 0) + close(sys->sync_fd); + if (sys->sync_ctrl_fd > 0) + close(sys->sync_ctrl_fd); + if (sys->arp_receive_fd > 0) + close(sys->arp_receive_fd); + if (sys->sig_pipe_r > 0) + close(sys->sig_pipe_r); + if (sys->sig_pipe_w > 0) + close(sys->sig_pipe_w); + + if (sys->epoll_fd) + close(sys->epoll_fd); + + free(sys); + ICCPD_LOG_INFO(__FUNCTION__, "System resource pool destructed successfully..."); +} + +struct CSM* system_create_csm() +{ + struct System* sys = NULL; + struct CSM* csm = NULL; + + if ((sys = system_get_instance()) == NULL ) + return NULL; + + /* Create a new csm */ + csm = (struct CSM*)malloc(sizeof(struct CSM)); + if (csm == NULL ) + return NULL; + else + memset(csm, 0, sizeof(struct CSM)); + iccp_csm_init(csm); + LIST_INSERT_HEAD(&(sys->csm_list), csm, next); + + return csm; +} + +/* Get connect state machine instance by peer ip */ +struct CSM* system_get_csm_by_peer_ip(const char* peer_ip) +{ + struct System* sys = NULL; + struct CSM* csm = NULL; + + if ((sys = system_get_instance()) == NULL ) + return NULL; + + LIST_FOREACH(csm, &(sys->csm_list), next) + { + if (strcmp(csm->peer_ip, peer_ip) == 0) + return csm; + } + + return NULL; +} + +struct CSM* system_get_csm_by_mlacp_id(int id) +{ + struct System* sys = NULL; + struct CSM* csm = NULL; + + if ((sys = system_get_instance()) == NULL ) + return NULL; + + LIST_FOREACH(csm, &(sys->csm_list), next) + { + if (csm->app_csm.mlacp.id == id) + return csm; + } + + return NULL; +}