diff --git a/Makefile.deb b/Makefile.deb new file mode 100644 index 0000000..4f4d568 --- /dev/null +++ b/Makefile.deb @@ -0,0 +1,67 @@ + +# Example custom makefile for creating .deb without using dpkg tools. +# +# Author: Tim Wegener +# +# This requires deb_hand.mak + +# Sources: +# SOURCE_DIR - directory containing files to be packaged +# ICON_SOURCE - 26x26 icon file for maemo +# description.txt - description with summary on first line +# preinst, postinst, prerm, postrm - optional control shell scripts + +# usage: +# sudo make PACKAGE=lanforge-xorp -f Makefile.deb create_deb + + +# These fields are used to build the control file. +PACKAGE ?= xorp +VERSION = 1.0.0 +ARCH = i386 +SECTION = net +PRIORITY = optional +MAINTAINER = Ben Greear +DEPENDS = libpcap0.8,libgcrypt11 + +#ICON_SOURCE = +PACKAGE_DIR ?= tmp + +create_deb: + echo "#!/bin/bash" > preinst + echo "set -x" >> preinst + echo "adduser xorp || true" >> preinst + echo "adduser xorp xorp || true" >> preinst + # I compile xorp on FC5, and it has a hard dependency on libpcap.0.9.4 and libcrypto.so.6 + # Fake out the link here so that it will start OK. This works on Ubuntu 8.0.4, no idea + # if it will work elsewhere. --Ben + echo "if [ ! -f /usr/lib/libpcap.so.0.9.4 ]; then ln -s /usr/lib/libpcap.so.0.9.* /usr/lib/libpcap.so.0.9.4; fi" >> preinst + echo "if [ ! -f /usr/lib/libcrypto.so.6 ]; then ln -s /usr/lib/libcrypto.so.0.9.* /usr/lib/libcrypto.so.6; fi" >> preinst + echo "#!/bin/bash" > postinst + echo "echo \"Xorp is installed in /usr/local/xorp.\"" >> postinst + echo "#!/bin/bash" > prerm + echo "# nothing to do here." >> prerm + echo "XORP modular router with improvements to better support LANforge." > description.txt + echo "More information: http://www.xorp.org" >> description.txt + chmod a+x preinst prerm postinst + make -f Makefile.deb clobber + make -f Makefile.deb ARCH=i386 deb + mv tmp/$(PACKAGE)_$(VERSION)_i386.deb ./ + cp $(PACKAGE)_$(VERSION)_i386.deb /mnt/d2/pub/candela_cdrom.5.0.7/debs/dists/candela/multiverse/binary-i386/ + @echo "On a Ubunto machine:" + @echo "cd /mnt/d2/pub/candela_cdrom.5.0.7/debs" + @echo "dpkg-scanpackages . /dev/null | gzip -9c > dists/candela/multiverse/binary-i386/Packages.gz" + @echo "apt-get update" + @echo "apt-get install $(PACKAGE)" + + +# This assumes a 'make' has already happened, and that make install will deposit files in /usr/local/xorp +# This most likely will have to be run as root user. +${PACKAGE_DIR}/data: + rm -rf $@ + mkdir -p $@/usr/local/ + (make install; cd /usr/local; find xorp -name "*" -print|xargs strip; \ + tar -cvzf /tmp/xorp.tgz xorp) + (cd $@/usr/local; tar -xvzf /tmp/xorp.tgz) + +include deb_hand.mak diff --git a/deb_hand.mak b/deb_hand.mak new file mode 100644 index 0000000..3a8eabd --- /dev/null +++ b/deb_hand.mak @@ -0,0 +1,108 @@ +# Create .deb without using dpkg tools. +# +# Author: Tim Wegener +# +# Use 'include deb_hand.mak' after defining the user variables in a local +# makefile. +# +# The 'data' rule must be customised in the local make file. +# This rule should make a 'data' directory containing the full file +# layout of the installed package. +# +# This makefile will create a debian-binary file a control directory and a +# a build directory in the current directory. +# Do 'make clobber' to remove these generated files. +# +# Destination: +# PACKAGE_DIR - directory where package (and support files) will be built +# defaults to the current directory +# +# Sources: +# SOURCE_DIR - directory containing files to be packaged +# ICON_SOURCE - 26x26 icon file for maemo +# description.txt - description with summary on first line +# preinst, postinst, prerm, postrm - optional control shell scripts + +# These fields are used to build the control file: +# PACKAGE = +# VERSION = +# ARCH = +# SECTION = +# PRIORITY = +# MAINTAINER = +# DEPENDS = +# +# SOURCE_DIR = +# ICON_SOURCE = +# (ICON_SOURCE is optional) + +# *** NO USER CHANGES REQUIRED BEYOND THIS POINT *** + +CONTROL_EXTRAS ?= ${wildcard preinst postinst prerm postrm} + +${PACKAGE_DIR}/control: ${PACKAGE_DIR}/data ${CONTROL_EXTRAS} description.txt \ + ${ICON_SOURCE} + rm -rf $@ + mkdir $@ +ifneq (${CONTROL_EXTRAS},) + cp ${CONTROL_EXTRAS} $@ +endif +# Make control file. + echo "Package: ${PACKAGE}" > $@/control + echo "Version: ${VERSION}" >> $@/control + echo "Section: ${SECTION}" >> $@/control + echo "Priority: ${PRIORITY}" >> $@/control + echo "Architecture: ${ARCH}" >> $@/control + echo "Depends: ${DEPENDS}" >> $@/control + echo "Installed-Size: ${shell du -s ${PACKAGE_DIR}/data|cut -f1}" \ + >> $@/control + echo "Maintainer: ${MAINTAINER}" >> $@/control + echo -n "Description:" >> $@/control + cat description.txt | gawk '{print " "$$0;}' >> $@/control +ifneq (${ICON_SOURCE},) + echo "Maemo-Icon-26:" >> $@/control + base64 ${ICON_SOURCE} | gawk '{print " "$$0;}' >> $@/control +endif +# Make md5sums. + cd ${PACKAGE_DIR}/data && find . -type f -exec md5sum {} \; \ + | sed -e 's| \./||' \ + > ../md5sums + +${PACKAGE_DIR}/debian-binary: + mkdir -p ${PACKAGE_DIR} + echo "2.0" > $@ + +${PACKAGE_DIR}/build: ${PACKAGE_DIR}/debian-binary ${PACKAGE_DIR}/control \ + ${PACKAGE_DIR}/data + rm -rf $@ + mkdir $@ + cp ${PACKAGE_DIR}/debian-binary $@/ + cd ${PACKAGE_DIR}/control && tar czvf ../build/control.tar.gz ./* + cd ${PACKAGE_DIR}/data && tar czvf ../build/data.tar.gz ./* + +# Convert GNU ar to BSD ar that debian requires. +# Note: Order of files within ar archive is important! +# NOTE: This doesn't actually work, and doesn't seem to be generally required. --Ben +${PACKAGE_DIR}/${PACKAGE}_${VERSION}_${ARCH}.deb: ${PACKAGE_DIR}/build + ar -rc $@tmp $ $@fail + #rm -f $@tmp + #mv $@fail $@ + mv $@tmp $@ + +.PHONY: data +data: ${PACKAGE_DIR}/data + +.PHONY: control +control: ${PACKAGE_DIR}/control + +.PHONY: build +build: ${PACKAGE_DIR}/build + +.PHONY: deb +deb: ${PACKAGE_DIR}/${PACKAGE}_${VERSION}_${ARCH}.deb + + +clobber:: + rm -rf ${PACKAGE_DIR}/debian_binary ${PACKAGE_DIR}/control \ + ${PACKAGE_DIR}/data ${PACKAGE_DIR}/build diff --git a/fea/data_plane/control_socket/netlink_socket_utilities.cc b/fea/data_plane/control_socket/netlink_socket_utilities.cc index 22572ad..a1a034e 100644 --- a/fea/data_plane/control_socket/netlink_socket_utilities.cc +++ b/fea/data_plane/control_socket/netlink_socket_utilities.cc @@ -15,6 +15,7 @@ #ident "$XORP: xorp/fea/data_plane/control_socket/netlink_socket_utilities.cc,v 1.14 2008/03/09 00:21:17 pavlin Exp $" #include "fea/fea_module.h" +#include "fea/fibconfig.hh" #include "libxorp/xorp.h" #include "libxorp/xlog.h" @@ -164,7 +165,7 @@ NlmUtils::get_rtattr(const struct rtattr* rtattr, int rta_len, int NlmUtils::nlm_get_to_fte_cfg(const IfTree& iftree, FteX& fte, const struct nlmsghdr* nlh, - const struct rtmsg* rtmsg, int rta_len) + const struct rtmsg* rtmsg, int rta_len, const FibConfig& fibconfig) { const struct rtattr *rtattr; const struct rtattr *rta_array[RTA_MAX + 1]; @@ -182,6 +183,36 @@ NlmUtils::nlm_get_to_fte_cfg(const IfTree& iftree, FteX& fte, if (nlh->nlmsg_type == RTM_DELROUTE) is_deleted = true; + + // + // Get the attributes + // + memset(rta_array, 0, sizeof(rta_array)); + rtattr = RTM_RTA(const_cast(rtmsg)); + NlmUtils::get_rtattr(rtattr, rta_len, rta_array, + sizeof(rta_array) / sizeof(rta_array[0])); + + // Discard the route if we are using a specific table. + if (fibconfig.unicast_forwarding_table_id_is_configured(family)) { + int rttable = fibconfig.unicast_forwarding_table_id(family); + int rtmt = rtmsg->rtm_table; +#ifdef HAVE_NETLINK_SOCKET_ATTRIBUTE_RTA_TABLE + if (rta_array[RTA_TABLE] != NULL) { + const uint8_t* p = static_cast( + RTA_DATA(const_cast(rta_array[RTA_TABLE]))); + rtmt = extract_host_int(p); + } +#endif + if ((rtmt != RT_TABLE_UNSPEC) && (rtmt != rttable)) { + //XLOG_WARNING("Ignoring route from table: %i\n", rtmt); + return XORP_ERROR; + } + else { + //XLOG_WARNING("Accepting route from table: %i\n", rtmt); + } + } + + IPvX nexthop_addr(family); IPvX dst_addr(family); int dst_mask_len = 0; @@ -272,14 +303,6 @@ NlmUtils::nlm_get_to_fte_cfg(const IfTree& iftree, FteX& fte, return (XORP_ERROR); // Invalid address family // - // Get the attributes - // - memset(rta_array, 0, sizeof(rta_array)); - rtattr = RTM_RTA(const_cast(rtmsg)); - NlmUtils::get_rtattr(rtattr, rta_len, rta_array, - sizeof(rta_array) / sizeof(rta_array[0])); - - // // Get the destination // if (rta_array[RTA_DST] == NULL) { @@ -353,7 +376,10 @@ NlmUtils::nlm_get_to_fte_cfg(const IfTree& iftree, FteX& fte, } else { // // XXX: A route was added, but we cannot find the corresponding - // interface/vif. This might happen because of a race + // interface/vif. + // This is probably because the interface in question is not in our + // local config. + // Another possibility: This might happen because of a race // condition. E.g., an interface was added and then // immediately deleted, but the processing for the addition of // the corresponding connected route was delayed. @@ -363,12 +389,13 @@ NlmUtils::nlm_get_to_fte_cfg(const IfTree& iftree, FteX& fte, // For the time being make it a fatal error until there is // enough evidence and the issue is understood. // - IPvXNet dst_subnet(dst_addr, dst_mask_len); - XLOG_FATAL("Decoding for route %s next hop %s failed: " - "could not find interface and vif for index %d", - dst_subnet.str().c_str(), - nexthop_addr.str().c_str(), - if_index); + //IPvXNet dst_subnet(dst_addr, dst_mask_len); + //XLOG_WARNING("WARNING: Decoding for route %s next hop %s failed: " + // "could not find interface and vif for index %d", + // dst_subnet.str().c_str(), + // nexthop_addr.str().c_str(), + // if_index); + return XORP_ERROR; } } } diff --git a/fea/data_plane/control_socket/netlink_socket_utilities.hh b/fea/data_plane/control_socket/netlink_socket_utilities.hh index 0ef7dc0..ecfb1ac 100644 --- a/fea/data_plane/control_socket/netlink_socket_utilities.hh +++ b/fea/data_plane/control_socket/netlink_socket_utilities.hh @@ -76,6 +76,7 @@ class NetlinkSocket; class NetlinkSocketReader; +class FibConfig; /** * @short Helper class for various NETLINK-format related utilities. @@ -116,7 +117,8 @@ public: */ static int nlm_get_to_fte_cfg(const IfTree& iftree, FteX& fte, const struct nlmsghdr* nlh, - const struct rtmsg* rtmsg, int rta_len); + const struct rtmsg* rtmsg, int rta_len, + const FibConfig& fibconfig); /** * Check that a previous netlink request has succeeded. diff --git a/fea/data_plane/fibconfig/fibconfig_entry_get_netlink_socket.cc b/fea/data_plane/fibconfig/fibconfig_entry_get_netlink_socket.cc index 00f6e85..3938c82 100644 --- a/fea/data_plane/fibconfig/fibconfig_entry_get_netlink_socket.cc +++ b/fea/data_plane/fibconfig/fibconfig_entry_get_netlink_socket.cc @@ -296,7 +296,7 @@ FibConfigEntryGetNetlinkSocket::lookup_route_by_dest(const IPvX& dst, return (XORP_ERROR); } if (parse_buffer_netlink_socket(fibconfig().system_config_iftree(), fte, - _ns_reader.buffer(), true) + _ns_reader.buffer(), true, fibconfig()) != XORP_OK) { return (XORP_ERROR); } diff --git a/fea/data_plane/fibconfig/fibconfig_entry_get_netlink_socket.hh b/fea/data_plane/fibconfig/fibconfig_entry_get_netlink_socket.hh index 0634547..d57a09b 100644 --- a/fea/data_plane/fibconfig/fibconfig_entry_get_netlink_socket.hh +++ b/fea/data_plane/fibconfig/fibconfig_entry_get_netlink_socket.hh @@ -106,7 +106,7 @@ public: */ static int parse_buffer_netlink_socket(const IfTree& iftree, FteX& fte, const vector& buffer, - bool is_nlm_get_only); + bool is_nlm_get_only, const FibConfig& fibconfig); private: /** diff --git a/fea/data_plane/fibconfig/fibconfig_entry_parse_netlink_socket.cc b/fea/data_plane/fibconfig/fibconfig_entry_parse_netlink_socket.cc index 8358717..283362c 100644 --- a/fea/data_plane/fibconfig/fibconfig_entry_parse_netlink_socket.cc +++ b/fea/data_plane/fibconfig/fibconfig_entry_parse_netlink_socket.cc @@ -51,7 +51,7 @@ FibConfigEntryGetNetlinkSocket::parse_buffer_netlink_socket( const IfTree& iftree, FteX& fte, const vector& buffer, - bool is_nlm_get_only) + bool is_nlm_get_only, const FibConfig& fibconfig) { size_t buffer_bytes = buffer.size(); AlignData align_data(buffer); @@ -114,7 +114,7 @@ FibConfigEntryGetNetlinkSocket::parse_buffer_netlink_socket( break; // XXX: ignore broadcast entries return (NlmUtils::nlm_get_to_fte_cfg(iftree, fte, nlh, rtmsg, - rta_len)); + rta_len, fibconfig)); } break; diff --git a/fea/data_plane/fibconfig/fibconfig_entry_set_netlink_socket.cc b/fea/data_plane/fibconfig/fibconfig_entry_set_netlink_socket.cc index a343205..f3aad6d 100644 --- a/fea/data_plane/fibconfig/fibconfig_entry_set_netlink_socket.cc +++ b/fea/data_plane/fibconfig/fibconfig_entry_set_netlink_socket.cc @@ -275,9 +275,9 @@ FibConfigEntrySetNetlinkSocket::add_entry(const FteX& fte) if (if_index != NULL) break; - XLOG_FATAL("Could not find interface index for interface %s vif %s", + XLOG_ERROR("Could not find interface index for interface %s vif %s", fte.ifname().c_str(), fte.vifname().c_str()); - break; + return XORP_ERROR; } while (false); // diff --git a/fea/data_plane/fibconfig/fibconfig_table_get_netlink_socket.cc b/fea/data_plane/fibconfig/fibconfig_table_get_netlink_socket.cc index 61581a4..752178f 100644 --- a/fea/data_plane/fibconfig/fibconfig_table_get_netlink_socket.cc +++ b/fea/data_plane/fibconfig/fibconfig_table_get_netlink_socket.cc @@ -182,6 +182,46 @@ FibConfigTableGetNetlinkSocket::get_table(int family, list& fte_list) nlh->nlmsg_pid = ns.nl_pid(); rtgenmsg = reinterpret_cast(NLMSG_DATA(nlh)); rtgenmsg->rtgen_family = family; + + struct rtmsg* rtmsg = reinterpret_cast(NLMSG_DATA(nlh)); + uint32_t table_id = RT_TABLE_UNSPEC; // Default value for lookup + struct rtattr* rtattr = RTM_RTA(rtmsg); + + // + // Set the routing/forwarding table ID. + // If the table ID is <= 0xff, then we set it in rtmsg->rtm_table, + // otherwise we set rtmsg->rtm_table to RT_TABLE_UNSPEC and add the + // real value as an RTA_TABLE attribute. + // + if (fibconfig().unicast_forwarding_table_id_is_configured(family)) + table_id = fibconfig().unicast_forwarding_table_id(family); + if (table_id <= 0xff) + rtmsg->rtm_table = table_id; + else + rtmsg->rtm_table = RT_TABLE_UNSPEC; + +#ifdef HAVE_NETLINK_SOCKET_ATTRIBUTE_RTA_TABLE + // Add the table ID as an attribute + if (table_id > 0xff) { + uint8_t* data; + uint32_t uint32_table_id = table_id; + int rta_len = RTA_LENGTH(sizeof(uint32_table_id)); + if (NLMSG_ALIGN(nlh->nlmsg_len) + rta_len > sizeof(buffer)) { + XLOG_FATAL("AF_NETLINK buffer size error: %u instead of %u", + XORP_UINT_CAST(sizeof(buffer)), + XORP_UINT_CAST(NLMSG_ALIGN(nlh->nlmsg_len) + rta_len)); + } + void* rta_align_data = reinterpret_cast(rtattr) + + RTA_ALIGN(rtattr->rta_len); + rtattr = static_cast(rta_align_data); + rtattr->rta_type = RTA_TABLE; + rtattr->rta_len = rta_len; + data = static_cast(RTA_DATA(rtattr)); + memcpy(data, &uint32_table_id, sizeof(uint32_table_id)); + nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + rta_len; + } +#endif // HAVE_NETLINK_SOCKET_ATTRIBUTE_RTA_TABLE + if (ns.sendto(&buffer, nlh->nlmsg_len, 0, reinterpret_cast(&snl), @@ -210,8 +250,9 @@ FibConfigTableGetNetlinkSocket::get_table(int family, list& fte_list) } // XXX: reset the multipart message read hackish flag ns.set_multipart_message_read(false); + if (parse_buffer_netlink_socket(family, fibconfig().system_config_iftree(), - fte_list, _ns_reader.buffer(), true) + fte_list, _ns_reader.buffer(), true, fibconfig()) != XORP_OK) { return (XORP_ERROR); } diff --git a/fea/data_plane/fibconfig/fibconfig_table_get_netlink_socket.hh b/fea/data_plane/fibconfig/fibconfig_table_get_netlink_socket.hh index 44e72a2..90b4d49 100644 --- a/fea/data_plane/fibconfig/fibconfig_table_get_netlink_socket.hh +++ b/fea/data_plane/fibconfig/fibconfig_table_get_netlink_socket.hh @@ -91,7 +91,7 @@ public: static int parse_buffer_netlink_socket(int family, const IfTree& iftree, list& fte_list, const vector& buffer, - bool is_nlm_get_only); + bool is_nlm_get_only, const FibConfig& fibconfig); private: int get_table(int family, list& fte_list); diff --git a/fea/data_plane/fibconfig/fibconfig_table_observer_netlink_socket.cc b/fea/data_plane/fibconfig/fibconfig_table_observer_netlink_socket.cc index f24af45..4a07a46 100644 --- a/fea/data_plane/fibconfig/fibconfig_table_observer_netlink_socket.cc +++ b/fea/data_plane/fibconfig/fibconfig_table_observer_netlink_socket.cc @@ -128,7 +128,7 @@ FibConfigTableObserverNetlinkSocket::receive_data(const vector& buffer) fibconfig().system_config_iftree(), fte_list, buffer, - false); + false, fibconfig()); if (! fte_list.empty()) { fibconfig().propagate_fib_changes(fte_list, this); fte_list.clear(); @@ -145,7 +145,7 @@ FibConfigTableObserverNetlinkSocket::receive_data(const vector& buffer) fibconfig().system_config_iftree(), fte_list, buffer, - false); + false, fibconfig()); if (! fte_list.empty()) { fibconfig().propagate_fib_changes(fte_list, this); fte_list.clear(); diff --git a/fea/data_plane/fibconfig/fibconfig_table_parse_netlink_socket.cc b/fea/data_plane/fibconfig/fibconfig_table_parse_netlink_socket.cc index cd90fdf..5719db9 100644 --- a/fea/data_plane/fibconfig/fibconfig_table_parse_netlink_socket.cc +++ b/fea/data_plane/fibconfig/fibconfig_table_parse_netlink_socket.cc @@ -52,7 +52,7 @@ FibConfigTableGetNetlinkSocket::parse_buffer_netlink_socket( const IfTree& iftree, list& fte_list, const vector& buffer, - bool is_nlm_get_only) + bool is_nlm_get_only, const FibConfig& fibconfig) { size_t buffer_bytes = buffer.size(); AlignData align_data(buffer); @@ -119,7 +119,7 @@ FibConfigTableGetNetlinkSocket::parse_buffer_netlink_socket( break; // XXX: ignore broadcast entries FteX fte(family); - if (NlmUtils::nlm_get_to_fte_cfg(iftree, fte, nlh, rtmsg, rta_len) + if (NlmUtils::nlm_get_to_fte_cfg(iftree, fte, nlh, rtmsg, rta_len, fibconfig) == XORP_OK) { fte_list.push_back(fte); } diff --git a/fea/data_plane/ifconfig/ifconfig_get_click.cc b/fea/data_plane/ifconfig/ifconfig_get_click.cc index fc35425..0494007 100644 --- a/fea/data_plane/ifconfig/ifconfig_get_click.cc +++ b/fea/data_plane/ifconfig/ifconfig_get_click.cc @@ -86,8 +86,9 @@ IfConfigGetClick::stop(string& error_msg) } int -IfConfigGetClick::pull_config(IfTree& iftree) +IfConfigGetClick::pull_config(const IfTree* local_config, IfTree& iftree) { + UNUSED(local_config); return read_config(iftree); } diff --git a/fea/data_plane/ifconfig/ifconfig_get_click.hh b/fea/data_plane/ifconfig/ifconfig_get_click.hh index 98c32a7..d2d9ac0 100644 --- a/fea/data_plane/ifconfig/ifconfig_get_click.hh +++ b/fea/data_plane/ifconfig/ifconfig_get_click.hh @@ -59,7 +59,7 @@ public: * @param iftree the IfTree storage to store the pulled information. * @return XORP_OK on success, otherwise XORP_ERROR. */ - virtual int pull_config(IfTree& iftree); + virtual int pull_config(const IfTree* local_config, IfTree& iftree); private: int read_config(IfTree& iftree); diff --git a/fea/data_plane/ifconfig/ifconfig_get_dummy.cc b/fea/data_plane/ifconfig/ifconfig_get_dummy.cc index bb98206..21b5114 100644 --- a/fea/data_plane/ifconfig/ifconfig_get_dummy.cc +++ b/fea/data_plane/ifconfig/ifconfig_get_dummy.cc @@ -76,8 +76,9 @@ IfConfigGetDummy::stop(string& error_msg) } int -IfConfigGetDummy::pull_config(IfTree& iftree) +IfConfigGetDummy::pull_config(const IfTree* local_config, IfTree& iftree) { + UNUSED(local_config); iftree = ifconfig().system_config(); return (XORP_OK); diff --git a/fea/data_plane/ifconfig/ifconfig_get_dummy.hh b/fea/data_plane/ifconfig/ifconfig_get_dummy.hh index bf2916a..31fb81b 100644 --- a/fea/data_plane/ifconfig/ifconfig_get_dummy.hh +++ b/fea/data_plane/ifconfig/ifconfig_get_dummy.hh @@ -57,7 +57,7 @@ public: * @param iftree the IfTree storage to store the pulled information. * @return XORP_OK on success, otherwise XORP_ERROR. */ - virtual int pull_config(IfTree& iftree); + virtual int pull_config(const IfTree* local_config, IfTree& iftree); private: }; diff --git a/fea/data_plane/ifconfig/ifconfig_get_getifaddrs.cc b/fea/data_plane/ifconfig/ifconfig_get_getifaddrs.cc index 2d0ca7b..95c4abb 100644 --- a/fea/data_plane/ifconfig/ifconfig_get_getifaddrs.cc +++ b/fea/data_plane/ifconfig/ifconfig_get_getifaddrs.cc @@ -80,8 +80,9 @@ IfConfigGetGetifaddrs::stop(string& error_msg) } int -IfConfigGetGetifaddrs::pull_config(IfTree& iftree) +IfConfigGetGetifaddrs::pull_config(const IfTree* local_config, IfTree& iftree) { + UNUSED(local_config); return read_config(iftree); } @@ -101,10 +102,11 @@ IfConfigGetGetifaddrs::read_config(IfTree& iftree) // // Get the VLAN vif info // + bool modified = false; IfConfigVlanGet* ifconfig_vlan_get; ifconfig_vlan_get = fea_data_plane_manager().ifconfig_vlan_get(); if (ifconfig_vlan_get != NULL) { - if (ifconfig_vlan_get->pull_config(iftree) != XORP_OK) + if (ifconfig_vlan_get->pull_config(iftree, modified) != XORP_OK) return (XORP_ERROR); } diff --git a/fea/data_plane/ifconfig/ifconfig_get_getifaddrs.hh b/fea/data_plane/ifconfig/ifconfig_get_getifaddrs.hh index 3af91b7..127ed51 100644 --- a/fea/data_plane/ifconfig/ifconfig_get_getifaddrs.hh +++ b/fea/data_plane/ifconfig/ifconfig_get_getifaddrs.hh @@ -57,7 +57,7 @@ public: * @param iftree the IfTree storage to store the pulled information. * @return XORP_OK on success, otherwise XORP_ERROR. */ - virtual int pull_config(IfTree& iftree); + virtual int pull_config(const IfTree* local_config, IfTree& iftree); /** * Parse information about network interface configuration change from diff --git a/fea/data_plane/ifconfig/ifconfig_get_ioctl.cc b/fea/data_plane/ifconfig/ifconfig_get_ioctl.cc index 4adfe37..56693a1 100644 --- a/fea/data_plane/ifconfig/ifconfig_get_ioctl.cc +++ b/fea/data_plane/ifconfig/ifconfig_get_ioctl.cc @@ -136,8 +136,9 @@ IfConfigGetIoctl::stop(string& error_msg) } int -IfConfigGetIoctl::pull_config(IfTree& iftree) +IfConfigGetIoctl::pull_config(const IfTree* local_config, IfTree& iftree) { + UNUSED(local_config); return read_config(iftree); } @@ -177,10 +178,11 @@ IfConfigGetIoctl::read_config(IfTree& iftree) // // Get the VLAN vif info // + bool modified = false; IfConfigVlanGet* ifconfig_vlan_get; ifconfig_vlan_get = fea_data_plane_manager().ifconfig_vlan_get(); if (ifconfig_vlan_get != NULL) { - if (ifconfig_vlan_get->pull_config(iftree) != XORP_OK) + if (ifconfig_vlan_get->pull_config(iftree, modified) != XORP_OK) return (XORP_ERROR); } diff --git a/fea/data_plane/ifconfig/ifconfig_get_ioctl.hh b/fea/data_plane/ifconfig/ifconfig_get_ioctl.hh index 20e87f5..d0dd501 100644 --- a/fea/data_plane/ifconfig/ifconfig_get_ioctl.hh +++ b/fea/data_plane/ifconfig/ifconfig_get_ioctl.hh @@ -57,7 +57,7 @@ public: * @param iftree the IfTree storage to store the pulled information. * @return XORP_OK on success, otherwise XORP_ERROR. */ - virtual int pull_config(IfTree& iftree); + virtual int pull_config(const IfTree* local_config, IfTree& iftree); /** * Parse information about network interface configuration change from diff --git a/fea/data_plane/ifconfig/ifconfig_get_iphelper.cc b/fea/data_plane/ifconfig/ifconfig_get_iphelper.cc index 7921031..e72b61b 100644 --- a/fea/data_plane/ifconfig/ifconfig_get_iphelper.cc +++ b/fea/data_plane/ifconfig/ifconfig_get_iphelper.cc @@ -88,8 +88,9 @@ IfConfigGetIPHelper::stop(string& error_msg) } int -IfConfigGetIPHelper::pull_config(IfTree& iftree) +IfConfigGetIPHelper::pull_config(const IfTree* local_config, IfTree& iftree) { + UNUSED(local_config); return read_config(iftree); } diff --git a/fea/data_plane/ifconfig/ifconfig_get_iphelper.hh b/fea/data_plane/ifconfig/ifconfig_get_iphelper.hh index 4f385a3..621ca64 100644 --- a/fea/data_plane/ifconfig/ifconfig_get_iphelper.hh +++ b/fea/data_plane/ifconfig/ifconfig_get_iphelper.hh @@ -57,7 +57,7 @@ public: * @param iftree the IfTree storage to store the pulled information. * @return XORP_OK on success, otherwise XORP_ERROR. */ - virtual int pull_config(IfTree& iftree); + virtual int pull_config(const IfTree* local_config, IfTree& iftree); private: int read_config(IfTree& iftree); diff --git a/fea/data_plane/ifconfig/ifconfig_get_netlink_socket.cc b/fea/data_plane/ifconfig/ifconfig_get_netlink_socket.cc index b1007cd..708557c 100644 --- a/fea/data_plane/ifconfig/ifconfig_get_netlink_socket.cc +++ b/fea/data_plane/ifconfig/ifconfig_get_netlink_socket.cc @@ -25,6 +25,8 @@ #endif #ifdef HAVE_LINUX_RTNETLINK_H #include +#include +#include #endif #ifdef HAVE_NET_IF_H #include @@ -91,13 +93,116 @@ IfConfigGetNetlinkSocket::stop(string& error_msg) } int -IfConfigGetNetlinkSocket::pull_config(IfTree& iftree) +IfConfigGetNetlinkSocket::pull_config(const IfTree* local_cfg, IfTree& iftree) { - return read_config(iftree); + return read_config(local_cfg, iftree); } int -IfConfigGetNetlinkSocket::read_config(IfTree& iftree) +IfConfigGetNetlinkSocket::pull_config_one(IfTree& iftree, const char* ifname, int ifindex) +{ + return read_config_one(iftree, ifname, ifindex); +} + + +int findDeviceIndex(const char* device_name) { + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, device_name, IFNAMSIZ); //max length of a device name. + + int ioctl_sock = socket(AF_INET, SOCK_STREAM, 0); + if (ioctl_sock >= 0) { + if (ioctl(ioctl_sock, SIOCGIFINDEX, &ifr) >= 0) { + close(ioctl_sock); + return ifr.ifr_ifindex; + } + else { + close(ioctl_sock); + } + } + return -1; +}//findDeviceIndex + + +int +IfConfigGetNetlinkSocket::read_config_one(IfTree& iftree, + const char* ifname, int if_index) +{ + static const size_t buffer_size = sizeof(struct nlmsghdr) + + sizeof(struct ifinfomsg) + sizeof(struct ifaddrmsg) + 512; + union { + uint8_t data[buffer_size]; + struct nlmsghdr nlh; + } buffer; + struct nlmsghdr* nlh = &buffer.nlh; + struct sockaddr_nl snl; + struct ifinfomsg* ifinfomsg; + NetlinkSocket& ns = *this; + + //XLOG_WARNING("read_config_one: tree: %s ifname: %s if_index: %i\n", + // iftree.getName().c_str(), ifname, if_index); + + + if ((if_index < 1) && ifname) { + // Try to resolve it based on ifname + if_index = findDeviceIndex(ifname); + //XLOG_WARNING("tried to resolve ifindex: %i\n", if_index); + } + if (if_index < 1) { + return XORP_ERROR; + } + + // Set the socket + memset(&snl, 0, sizeof(snl)); + snl.nl_family = AF_NETLINK; + snl.nl_pid = 0; // nl_pid = 0 if destination is the kernel + snl.nl_groups = 0; + + // + // Set the request for network interfaces + // + memset(&buffer, 0, sizeof(buffer)); + nlh->nlmsg_len = NLMSG_LENGTH(sizeof(*ifinfomsg)); + nlh->nlmsg_type = RTM_GETLINK; + nlh->nlmsg_flags = NLM_F_REQUEST; // | NLM_F_ROOT; // Get the whole table (NO, not now. --Ben ) + nlh->nlmsg_seq = ns.seqno(); + nlh->nlmsg_pid = ns.nl_pid(); + ifinfomsg = reinterpret_cast(NLMSG_DATA(nlh)); + ifinfomsg->ifi_family = AF_UNSPEC; + ifinfomsg->ifi_type = IFLA_UNSPEC; + ifinfomsg->ifi_index = if_index; + ifinfomsg->ifi_flags = 0; + ifinfomsg->ifi_change = 0xffffffff; + + if (ns.sendto(&buffer, nlh->nlmsg_len, 0, + reinterpret_cast(&snl), + sizeof(snl)) + != (ssize_t)nlh->nlmsg_len) { + XLOG_ERROR("Error writing to netlink socket: %s", strerror(errno)); + return (XORP_ERROR); + } + + // + // Force to receive data from the kernel, and then parse it + // + // + string error_msg; + if (_ns_reader.receive_data(ns, nlh->nlmsg_seq, error_msg) != XORP_OK) { + XLOG_ERROR("Error reading from netlink socket: %s", error_msg.c_str()); + return (XORP_ERROR); + } + + bool modified = false; + if (parse_buffer_netlink_socket(ifconfig(), iftree, _ns_reader.buffer(), modified) + != XORP_OK) { + return (XORP_ERROR); + } + return XORP_OK; +} + + +int +IfConfigGetNetlinkSocket::read_config_all(IfTree& iftree) { static const size_t buffer_size = sizeof(struct nlmsghdr) + sizeof(struct ifinfomsg) + sizeof(struct ifaddrmsg) + 512; @@ -316,7 +421,207 @@ IfConfigGetNetlinkSocket::read_config(IfTree& iftree) IfConfigVlanGet* ifconfig_vlan_get; ifconfig_vlan_get = fea_data_plane_manager().ifconfig_vlan_get(); if (ifconfig_vlan_get != NULL) { - if (ifconfig_vlan_get->pull_config(iftree) != XORP_OK) + bool modified = false; + if (ifconfig_vlan_get->pull_config(iftree, modified) != XORP_OK) + return (XORP_ERROR); + } + + return (XORP_OK); +}//read_config_all + + +int +IfConfigGetNetlinkSocket::read_config(const IfTree* local_cfg, IfTree& iftree) +{ + if (!local_cfg) { + return read_config_all(iftree); + } + + static const size_t buffer_size = sizeof(struct nlmsghdr) + + sizeof(struct ifinfomsg) + sizeof(struct ifaddrmsg) + 512; + union { + uint8_t data[buffer_size]; + struct nlmsghdr nlh; + } buffer; + struct nlmsghdr* nlh = &buffer.nlh; + struct sockaddr_nl snl; + struct ifaddrmsg* ifaddrmsg; + NetlinkSocket& ns = *this; + + // + // Set the request. First the socket, then the request itself. + // + // Note that first we send a request for the network interfaces, + // then a request for the IPv4 addresses, and then a request + // for the IPv6 addresses. + // + + IfTree::IfMap::const_iterator if_iter; + for (if_iter = local_cfg->interfaces().begin(); + if_iter != local_cfg->interfaces().end(); + ++if_iter) { + const IfTreeInterface& iface = *(if_iter->second); + IfTreeInterface::VifMap::const_iterator vif_iter; + for (vif_iter = iface.vifs().begin(); + vif_iter != iface.vifs().end(); + ++vif_iter) { + const IfTreeVif& vif = *(vif_iter->second); + read_config_one(iftree, vif.vifname().c_str(), vif.pif_index()); + } + } + + // + // Create a list with the interface indexes + // + list if_index_list; + uint32_t if_index; + + for (if_iter = iftree.interfaces().begin(); + if_iter != iftree.interfaces().end(); + ++if_iter) { + const IfTreeInterface& iface = *(if_iter->second); + IfTreeInterface::VifMap::const_iterator vif_iter; + for (vif_iter = iface.vifs().begin(); + vif_iter != iface.vifs().end(); + ++vif_iter) { + const IfTreeVif& vif = *(vif_iter->second); + if_index = vif.pif_index(); + if_index_list.push_back(if_index); + } + } + + // Set the socket + memset(&snl, 0, sizeof(snl)); + snl.nl_family = AF_NETLINK; + snl.nl_pid = 0; // nl_pid = 0 if destination is the kernel + snl.nl_groups = 0; + + // + // Send requests for the addresses of each interface we just found + // + list::const_iterator if_index_iter; + for (if_index_iter = if_index_list.begin(); + if_index_iter != if_index_list.end(); + ++if_index_iter) { + if_index = *if_index_iter; + + // + // Set the request for IPv4 addresses + // + if (fea_data_plane_manager().have_ipv4()) { + memset(&buffer, 0, sizeof(buffer)); + nlh->nlmsg_len = NLMSG_LENGTH(sizeof(*ifaddrmsg)); + nlh->nlmsg_type = RTM_GETADDR; + // Get the whole table + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT; + nlh->nlmsg_seq = ns.seqno(); + nlh->nlmsg_pid = ns.nl_pid(); + ifaddrmsg = reinterpret_cast(NLMSG_DATA(nlh)); + ifaddrmsg->ifa_family = AF_INET; + ifaddrmsg->ifa_prefixlen = 0; + ifaddrmsg->ifa_flags = 0; + ifaddrmsg->ifa_scope = 0; + ifaddrmsg->ifa_index = if_index; + + if (ns.sendto(&buffer, nlh->nlmsg_len, 0, + reinterpret_cast(&snl), + sizeof(snl)) + != (ssize_t)nlh->nlmsg_len) { + XLOG_ERROR("Error writing to netlink socket: %s", + strerror(errno)); + return (XORP_ERROR); + } + + // + // Force to receive data from the kernel, and then parse it + // + // + // XXX: setting the flag below is a work-around hack because of a + // Linux kernel bug: when we read the whole table the kernel + // may not set the NLM_F_MULTI flag for the multipart messages. + // + string error_msg; + ns.set_multipart_message_read(true); + if (_ns_reader.receive_data(ns, nlh->nlmsg_seq, error_msg) + != XORP_OK) { + ns.set_multipart_message_read(false); + XLOG_ERROR("Error reading from netlink socket: %s", + error_msg.c_str()); + return (XORP_ERROR); + } + // XXX: reset the multipart message read hackish flag + ns.set_multipart_message_read(false); + if (parse_buffer_netlink_socket(ifconfig(), iftree, + _ns_reader.buffer()) + != XORP_OK) { + return (XORP_ERROR); + } + } + +#ifdef HAVE_IPV6 + // + // Set the request for IPv6 addresses + // + if (fea_data_plane_manager().have_ipv6()) { + memset(&buffer, 0, sizeof(buffer)); + nlh->nlmsg_len = NLMSG_LENGTH(sizeof(*ifaddrmsg)); + nlh->nlmsg_type = RTM_GETADDR; + // Get the whole table + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT; + nlh->nlmsg_seq = ns.seqno(); + nlh->nlmsg_pid = ns.nl_pid(); + ifaddrmsg = reinterpret_cast(NLMSG_DATA(nlh)); + ifaddrmsg->ifa_family = AF_INET6; + ifaddrmsg->ifa_prefixlen = 0; + ifaddrmsg->ifa_flags = 0; + ifaddrmsg->ifa_scope = 0; + ifaddrmsg->ifa_index = if_index; + + if (ns.sendto(&buffer, nlh->nlmsg_len, 0, + reinterpret_cast(&snl), + sizeof(snl)) + != (ssize_t)nlh->nlmsg_len) { + XLOG_ERROR("Error writing to netlink socket: %s", + strerror(errno)); + return (XORP_ERROR); + } + + // + // Force to receive data from the kernel, and then parse it + // + // + // XXX: setting the flag below is a work-around hack because of a + // Linux kernel bug: when we read the whole table the kernel + // may not set the NLM_F_MULTI flag for the multipart messages. + // + string error_msg; + ns.set_multipart_message_read(true); + if (_ns_reader.receive_data(ns, nlh->nlmsg_seq, error_msg) + != XORP_OK) { + ns.set_multipart_message_read(false); + XLOG_ERROR("Error reading from netlink socket: %s", + error_msg.c_str()); + return (XORP_ERROR); + } + // XXX: reset the multipart message read hackish flag + ns.set_multipart_message_read(false); + if (parse_buffer_netlink_socket(ifconfig(), iftree, + _ns_reader.buffer()) + != XORP_OK) { + return (XORP_ERROR); + } + } +#endif // HAVE_IPV6 + } + + // + // Get the VLAN vif info + // + IfConfigVlanGet* ifconfig_vlan_get; + ifconfig_vlan_get = fea_data_plane_manager().ifconfig_vlan_get(); + if (ifconfig_vlan_get != NULL) { + bool modified = false; + if (ifconfig_vlan_get->pull_config(iftree, modified) != XORP_OK) return (XORP_ERROR); } diff --git a/fea/data_plane/ifconfig/ifconfig_get_netlink_socket.hh b/fea/data_plane/ifconfig/ifconfig_get_netlink_socket.hh index 2385db5..fb3bd65 100644 --- a/fea/data_plane/ifconfig/ifconfig_get_netlink_socket.hh +++ b/fea/data_plane/ifconfig/ifconfig_get_netlink_socket.hh @@ -59,7 +59,14 @@ public: * @param iftree the IfTree storage to store the pulled information. * @return XORP_OK on success, otherwise XORP_ERROR. */ - virtual int pull_config(IfTree& iftree); + virtual int pull_config(const IfTree* local_cfg, IfTree& iftree); + + + virtual bool can_pull_one() { return true; } + + /** If_index can be -1 if unknown: We will try to resolve it from ifname. + */ + virtual int pull_config_one(IfTree& iftree, const char* ifname, int if_index); /** * Parse information about network interface configuration change from @@ -75,10 +82,20 @@ public: * @see IfTree. */ static int parse_buffer_netlink_socket(IfConfig& ifconfig, IfTree& iftree, - const vector& buffer); + const vector& buffer) { + bool modified = false; + return parse_buffer_netlink_socket(ifconfig, iftree, buffer, modified); + } + + /** Same as above, but also return whether or not something was actually modified in iftree. + */ + static int parse_buffer_netlink_socket(IfConfig& ifconfig, IfTree& iftree, + const vector& buffer, bool& modified); private: - int read_config(IfTree& iftree); + int read_config(const IfTree* local_config, IfTree& iftree); + int read_config_all(IfTree& iftree); + int read_config_one(IfTree& iftree, const char* ifname, int if_index); NetlinkSocketReader _ns_reader; }; diff --git a/fea/data_plane/ifconfig/ifconfig_get_proc_linux.cc b/fea/data_plane/ifconfig/ifconfig_get_proc_linux.cc index 09f8e9e..b1af85a 100644 --- a/fea/data_plane/ifconfig/ifconfig_get_proc_linux.cc +++ b/fea/data_plane/ifconfig/ifconfig_get_proc_linux.cc @@ -125,16 +125,16 @@ IfConfigGetProcLinux::stop(string& error_msg) } int -IfConfigGetProcLinux::pull_config(IfTree& iftree) +IfConfigGetProcLinux::pull_config(const IfTree* local_config, IfTree& iftree) { - return read_config(iftree); + return read_config(local_config, iftree); } int -IfConfigGetProcLinux::read_config(IfTree& iftree) +IfConfigGetProcLinux::read_config(const IfTree* local_config, IfTree& iftree) { // XXX: this method relies on the ioctl() method - _ifconfig_get_ioctl->pull_config(iftree); + _ifconfig_get_ioctl->pull_config(local_config, iftree); // // The IPv4 information @@ -164,7 +164,8 @@ IfConfigGetProcLinux::read_config(IfTree& iftree) IfConfigVlanGet* ifconfig_vlan_get; ifconfig_vlan_get = fea_data_plane_manager().ifconfig_vlan_get(); if (ifconfig_vlan_get != NULL) { - if (ifconfig_vlan_get->pull_config(iftree) != XORP_OK) + bool modified = false; + if (ifconfig_vlan_get->pull_config(iftree, modified) != XORP_OK) return (XORP_ERROR); } diff --git a/fea/data_plane/ifconfig/ifconfig_get_proc_linux.hh b/fea/data_plane/ifconfig/ifconfig_get_proc_linux.hh index 6d92173..c39d11e 100644 --- a/fea/data_plane/ifconfig/ifconfig_get_proc_linux.hh +++ b/fea/data_plane/ifconfig/ifconfig_get_proc_linux.hh @@ -58,10 +58,10 @@ public: * @param iftree the IfTree storage to store the pulled information. * @return XORP_OK on success, otherwise XORP_ERROR. */ - virtual int pull_config(IfTree& iftree); + virtual int pull_config(const IfTree* local_config, IfTree& iftree); private: - int read_config(IfTree& iftree); + int read_config(const IfTree* local_config, IfTree& iftree); IfConfigGetIoctl* _ifconfig_get_ioctl; diff --git a/fea/data_plane/ifconfig/ifconfig_get_sysctl.cc b/fea/data_plane/ifconfig/ifconfig_get_sysctl.cc index bbbfb08..bcd9c4e 100644 --- a/fea/data_plane/ifconfig/ifconfig_get_sysctl.cc +++ b/fea/data_plane/ifconfig/ifconfig_get_sysctl.cc @@ -87,8 +87,9 @@ IfConfigGetSysctl::stop(string& error_msg) } int -IfConfigGetSysctl::pull_config(IfTree& iftree) +IfConfigGetSysctl::pull_config(const IfTree* local_config, IfTree& iftree) { + UNUSED(local_config); return read_config(iftree); } diff --git a/fea/data_plane/ifconfig/ifconfig_get_sysctl.hh b/fea/data_plane/ifconfig/ifconfig_get_sysctl.hh index f9500de..838f8d2 100644 --- a/fea/data_plane/ifconfig/ifconfig_get_sysctl.hh +++ b/fea/data_plane/ifconfig/ifconfig_get_sysctl.hh @@ -57,7 +57,7 @@ public: * @param iftree the IfTree storage to store the pulled information. * @return XORP_OK on success, otherwise XORP_ERROR. */ - virtual int pull_config(IfTree& iftree); + virtual int pull_config(const IfTree* local_config, IfTree& iftree); /** * Parse information about network interface configuration change from diff --git a/fea/data_plane/ifconfig/ifconfig_observer_netlink_socket.cc b/fea/data_plane/ifconfig/ifconfig_observer_netlink_socket.cc index 57d1a3b..42f7b9f 100644 --- a/fea/data_plane/ifconfig/ifconfig_observer_netlink_socket.cc +++ b/fea/data_plane/ifconfig/ifconfig_observer_netlink_socket.cc @@ -115,35 +115,40 @@ IfConfigObserverNetlinkSocket::stop(string& error_msg) void IfConfigObserverNetlinkSocket::receive_data(const vector& buffer) { + bool modified = false; // Pre-processing cleanup ifconfig().system_config().finalize_state(); if (IfConfigGetNetlinkSocket::parse_buffer_netlink_socket( - ifconfig(), ifconfig().system_config(), buffer) + ifconfig(), ifconfig().system_config(), buffer, modified) != XORP_OK) { return; } + // TODO: If the parse_buffer call above didn't modify anything, is the vlan pull + // below actually needed? // // Get the VLAN vif info // IfConfigVlanGet* ifconfig_vlan_get; ifconfig_vlan_get = fea_data_plane_manager().ifconfig_vlan_get(); if (ifconfig_vlan_get != NULL) { - if (ifconfig_vlan_get->pull_config(ifconfig().system_config()) + if (ifconfig_vlan_get->pull_config(ifconfig().system_config(), modified) != XORP_OK) { XLOG_ERROR("Unknown error while pulling VLAN information"); } } - // - // Propagate the changes from the system config to the merged config - // - IfTree& merged_config = ifconfig().merged_config(); - merged_config.align_with_observed_changes(ifconfig().system_config(), - ifconfig().user_config()); - ifconfig().report_updates(merged_config); - merged_config.finalize_state(); + if (modified) { + // + // Propagate the changes from the system config to the merged config + // + IfTree& merged_config = ifconfig().merged_config(); + merged_config.align_with_observed_changes(ifconfig().system_config(), + ifconfig().user_config()); + ifconfig().report_updates(merged_config); + merged_config.finalize_state(); + } } void diff --git a/fea/data_plane/ifconfig/ifconfig_parse_netlink_socket.cc b/fea/data_plane/ifconfig/ifconfig_parse_netlink_socket.cc index 0a0e0d4..1f0f6ee 100644 --- a/fea/data_plane/ifconfig/ifconfig_parse_netlink_socket.cc +++ b/fea/data_plane/ifconfig/ifconfig_parse_netlink_socket.cc @@ -54,28 +54,28 @@ #ifdef HAVE_NETLINK_SOCKETS -static void nlm_newlink_to_fea_cfg(IfTree& iftree, - const struct ifinfomsg* ifinfomsg, - int rta_len); +static void nlm_cond_newlink_to_fea_cfg(const IfTree& user_config, IfTree& iftree, + const struct ifinfomsg* ifinfomsg, + int rta_len, bool& modified); static void nlm_dellink_to_fea_cfg(IfTree& iftree, const struct ifinfomsg* ifinfomsg, - int rta_len); -static void nlm_newdeladdr_to_fea_cfg(IfTree& iftree, - const struct ifaddrmsg* ifaddrmsg, - int rta_len, bool is_deleted); + int rta_len, bool& modified); +static void nlm_cond_newdeladdr_to_fea_cfg(const IfTree& user_config, IfTree& iftree, + const struct ifaddrmsg* ifaddrmsg, + int rta_len, bool is_deleted, + bool& modified); int IfConfigGetNetlinkSocket::parse_buffer_netlink_socket(IfConfig& ifconfig, IfTree& iftree, - const vector& buffer) + const vector& buffer, + bool& modified) { size_t buffer_bytes = buffer.size(); AlignData align_data(buffer); const struct nlmsghdr* nlh; bool recognized = false; - UNUSED(ifconfig); - for (nlh = align_data.payload(); NLMSG_OK(nlh, buffer_bytes); nlh = NLMSG_NEXT(const_cast(nlh), buffer_bytes)) { @@ -119,9 +119,10 @@ IfConfigGetNetlinkSocket::parse_buffer_netlink_socket(IfConfig& ifconfig, } ifinfomsg = reinterpret_cast(nlmsg_data); if (nlh->nlmsg_type == RTM_NEWLINK) - nlm_newlink_to_fea_cfg(iftree, ifinfomsg, rta_len); + nlm_cond_newlink_to_fea_cfg(ifconfig.user_config(), iftree, + ifinfomsg, rta_len, modified); else - nlm_dellink_to_fea_cfg(iftree, ifinfomsg, rta_len); + nlm_dellink_to_fea_cfg(iftree, ifinfomsg, rta_len, modified); recognized = true; } break; @@ -138,9 +139,11 @@ IfConfigGetNetlinkSocket::parse_buffer_netlink_socket(IfConfig& ifconfig, } ifaddrmsg = reinterpret_cast(nlmsg_data); if (nlh->nlmsg_type == RTM_NEWADDR) { - nlm_newdeladdr_to_fea_cfg(iftree, ifaddrmsg, rta_len, false); + nlm_cond_newdeladdr_to_fea_cfg(ifconfig.user_config(), iftree, + ifaddrmsg, rta_len, false, modified); } else { - nlm_newdeladdr_to_fea_cfg(iftree, ifaddrmsg, rta_len, true); + nlm_cond_newdeladdr_to_fea_cfg(ifconfig.user_config(), iftree, + ifaddrmsg, rta_len, true, modified); } recognized = true; } @@ -249,8 +252,8 @@ nlm_decode_ipvx_interface_address(const struct ifinfomsg* ifinfomsg, } static void -nlm_newlink_to_fea_cfg(IfTree& iftree, const struct ifinfomsg* ifinfomsg, - int rta_len) +nlm_cond_newlink_to_fea_cfg(const IfTree& user_cfg, IfTree& iftree, const struct ifinfomsg* ifinfomsg, + int rta_len, bool& modified) { const struct rtattr *rtattr; const struct rtattr *rta_array[IFLA_MAX + 1]; @@ -283,7 +286,14 @@ nlm_newlink_to_fea_cfg(IfTree& iftree, const struct ifinfomsg* ifinfomsg, } caddr_t rta_data = reinterpret_cast(RTA_DATA(const_cast(rta_array[IFLA_IFNAME]))); if_name = string(reinterpret_cast(rta_data)); - debug_msg("interface: %s\n", if_name.c_str()); + //XLOG_WARNING("newlink, interface: %s tree: %s\n", if_name.c_str(), iftree.getName().c_str()); + + if (! user_cfg.find_interface(if_name)) { + //XLOG_WARNING("Ignoring interface: %s as it is not found in the local config.\n", if_name.c_str()); + return; + } + + modified = true; // this is just for optimization..so if we somehow don't modify things, it's not a big deal. // // Get the interface index @@ -501,7 +511,7 @@ nlm_newlink_to_fea_cfg(IfTree& iftree, const struct ifinfomsg* ifinfomsg, static void nlm_dellink_to_fea_cfg(IfTree& iftree, const struct ifinfomsg* ifinfomsg, - int rta_len) + int rta_len, bool& modified) { const struct rtattr *rtattr; const struct rtattr *rta_array[IFLA_MAX + 1]; @@ -523,7 +533,7 @@ nlm_dellink_to_fea_cfg(IfTree& iftree, const struct ifinfomsg* ifinfomsg, } caddr_t rta_data = reinterpret_cast(RTA_DATA(const_cast(rta_array[IFLA_IFNAME]))); if_name = string(reinterpret_cast(rta_data)); - debug_msg("Deleting interface name: %s\n", if_name.c_str()); + XLOG_WARNING("dellink, interface: %s tree: %s\n", if_name.c_str(), iftree.getName().c_str()); // // Get the interface index @@ -541,17 +551,23 @@ nlm_dellink_to_fea_cfg(IfTree& iftree, const struct ifinfomsg* ifinfomsg, // IfTreeInterface* ifp = iftree.find_interface(if_index); if (ifp != NULL) { - ifp->mark(IfTree::DELETED); + if (!ifp->is_marked(IfTree::DELETED)) { + iftree.markIfaceDeleted(ifp); + modified = true; + } } IfTreeVif* vifp = iftree.find_vif(if_index); if (vifp != NULL) { - vifp->mark(IfTree::DELETED); + if (!vifp->is_marked(IfTree::DELETED)) { + iftree.markVifDeleted(vifp); + modified = true; + } } } static void -nlm_newdeladdr_to_fea_cfg(IfTree& iftree, const struct ifaddrmsg* ifaddrmsg, - int rta_len, bool is_deleted) +nlm_cond_newdeladdr_to_fea_cfg(const IfTree& user_config, IfTree& iftree, const struct ifaddrmsg* ifaddrmsg, + int rta_len, bool is_deleted, bool& modified) { const struct rtattr *rtattr; const struct rtattr *rta_array[IFA_MAX + 1]; @@ -605,12 +621,21 @@ nlm_newdeladdr_to_fea_cfg(IfTree& iftree, const struct ifaddrmsg* ifaddrmsg, // return; } - XLOG_FATAL("Could not find vif with index %u in IfTree", if_index); + else { + const IfTreeVif* vifpc = user_config.find_vif(if_index); + if (vifpc) { + XLOG_FATAL("Could not find vif with index %u in IfTree", if_index); + } + //else, not in local config, so ignore it + return; + } } debug_msg("Address event on interface %s vif %s with interface index %u\n", vifp->ifname().c_str(), vifp->vifname().c_str(), vifp->pif_index()); + modified = true; // this is just for optimization..so if we somehow don't modify things, it's not a big deal. + // // Get the IP address, netmask, broadcast address, P2P destination // diff --git a/fea/data_plane/ifconfig/ifconfig_set.cc b/fea/data_plane/ifconfig/ifconfig_set.cc index 956c67c..6a70762 100644 --- a/fea/data_plane/ifconfig/ifconfig_set.cc +++ b/fea/data_plane/ifconfig/ifconfig_set.cc @@ -85,9 +85,9 @@ copy_vif_state(const IfTreeVif* pulled_vifp, IfTreeVif& config_vif) } int -IfConfigSet::push_config(IfTree& iftree) +IfConfigSet::push_config(const IfTree& iftree) { - IfTree::IfMap::iterator ii; + IfTree::IfMap::const_iterator ii; IfTreeInterface::VifMap::iterator vi; IfConfigErrorReporterBase& error_reporter = ifconfig().ifconfig_error_reporter(); @@ -249,7 +249,7 @@ IfConfigSet::push_config(IfTree& iftree) // 2. Pull the config from the system (e.g., to obtain information // such as interface indexes for newly created interfaces/vifs). // - ifconfig().pull_config(); + ifconfig().pull_config(NULL, -1); // // 3. Push the interface/vif/address configuration. @@ -342,7 +342,7 @@ IfConfigSet::push_config(IfTree& iftree) } void -IfConfigSet::push_iftree_begin(IfTree& iftree) +IfConfigSet::push_iftree_begin(const IfTree& iftree) { string error_msg; IfConfigErrorReporterBase& error_reporter = @@ -366,7 +366,7 @@ IfConfigSet::push_iftree_begin(IfTree& iftree) } void -IfConfigSet::push_iftree_end(IfTree& iftree) +IfConfigSet::push_iftree_end(const IfTree& iftree) { string error_msg; IfConfigErrorReporterBase& error_reporter = diff --git a/fea/data_plane/ifconfig/ifconfig_set_click.cc b/fea/data_plane/ifconfig/ifconfig_set_click.cc index f446d47..a9b54b2 100644 --- a/fea/data_plane/ifconfig/ifconfig_set_click.cc +++ b/fea/data_plane/ifconfig/ifconfig_set_click.cc @@ -46,6 +46,7 @@ IfConfigSetClick::IfConfigSetClick(FeaDataPlaneManager& fea_data_plane_manager) : IfConfigSet(fea_data_plane_manager), ClickSocket(fea_data_plane_manager.eventloop()), _cs_reader(*(ClickSocket *)this), + _iftree("click-set"), _kernel_click_config_generator(NULL), _user_click_config_generator(NULL), _has_kernel_click_config(false), diff --git a/fea/data_plane/ifconfig/ifconfig_set_dummy.cc b/fea/data_plane/ifconfig/ifconfig_set_dummy.cc index 89c1e9e..9517abc 100644 --- a/fea/data_plane/ifconfig/ifconfig_set_dummy.cc +++ b/fea/data_plane/ifconfig/ifconfig_set_dummy.cc @@ -78,7 +78,7 @@ IfConfigSetDummy::stop(string& error_msg) } int -IfConfigSetDummy::push_config(IfTree& iftree) +IfConfigSetDummy::push_config(const IfTree& iftree) { ifconfig().set_system_config(iftree); diff --git a/fea/data_plane/ifconfig/ifconfig_set_dummy.hh b/fea/data_plane/ifconfig/ifconfig_set_dummy.hh index 9be2742..7d67996 100644 --- a/fea/data_plane/ifconfig/ifconfig_set_dummy.hh +++ b/fea/data_plane/ifconfig/ifconfig_set_dummy.hh @@ -60,7 +60,7 @@ public: * @param iftree the interface tree configuration to push. * @return XORP_OK on success, otherwise XORP_ERROR. */ - virtual int push_config(IfTree& iftree); + virtual int push_config(const IfTree& iftree); private: /** diff --git a/fea/data_plane/ifconfig/ifconfig_vlan_get_bsd.cc b/fea/data_plane/ifconfig/ifconfig_vlan_get_bsd.cc index 34c17a6..041f81c 100644 --- a/fea/data_plane/ifconfig/ifconfig_vlan_get_bsd.cc +++ b/fea/data_plane/ifconfig/ifconfig_vlan_get_bsd.cc @@ -113,13 +113,13 @@ IfConfigVlanGetBsd::stop(string& error_msg) } int -IfConfigVlanGetBsd::pull_config(IfTree& iftree) +IfConfigVlanGetBsd::pull_config(IfTree& iftree, bool& modified) { - return read_config(iftree); + return read_config(iftree, bool& modified); } int -IfConfigVlanGetBsd::read_config(IfTree& iftree) +IfConfigVlanGetBsd::read_config(IfTree& iftree, bool& modified) { IfTree::IfMap::iterator ii; string error_msg; @@ -178,6 +178,7 @@ IfConfigVlanGetBsd::read_config(IfTree& iftree) // Find or add the VLAN vif IfTreeVif* parent_vifp = parent_ifp->find_vif(ifp->ifname()); if (parent_vifp == NULL) { + modified = true; parent_ifp->add_vif(ifp->ifname()); parent_vifp = parent_ifp->find_vif(ifp->ifname()); } @@ -185,12 +186,23 @@ IfConfigVlanGetBsd::read_config(IfTree& iftree) // Copy the vif state IfTreeVif* vifp = ifp->find_vif(ifp->ifname()); - if (vifp != NULL) + if (vifp != NULL) { + modified = true; /* TODO: this could be a lie, but better safe than sorry until + * the copy-recursive method takes 'modified'. + */ parent_vifp->copy_recursive_vif(*vifp); + } // Set the VLAN vif info - parent_vifp->set_vlan(true); - parent_vifp->set_vlan_id(vlan_id); + // Set the VLAN vif info + if (!parent_vifp->is_vlan()) { + parent_vifp->set_vlan(true); + modified = true; + } + if (parent_vifp->vlan_id() != vlan_id) { + parent_vifp->set_vlan_id(vlan_id); + modified = true; + } } return (XORP_OK); diff --git a/fea/data_plane/ifconfig/ifconfig_vlan_get_bsd.hh b/fea/data_plane/ifconfig/ifconfig_vlan_get_bsd.hh index 35166bc..e401949 100644 --- a/fea/data_plane/ifconfig/ifconfig_vlan_get_bsd.hh +++ b/fea/data_plane/ifconfig/ifconfig_vlan_get_bsd.hh @@ -59,10 +59,10 @@ public: * @param iftree the IfTree storage to store the pulled information. * @return XORP_OK on success, otherwise XORP_ERROR. */ - virtual int pull_config(IfTree& iftree); + virtual int pull_config(IfTree& iftree, bool& modified); private: - int read_config(IfTree& iftree); + int read_config(IfTree& iftree, bool& modified); int _s4; }; diff --git a/fea/data_plane/ifconfig/ifconfig_vlan_get_dummy.cc b/fea/data_plane/ifconfig/ifconfig_vlan_get_dummy.cc index 2589345..cbf056d 100644 --- a/fea/data_plane/ifconfig/ifconfig_vlan_get_dummy.cc +++ b/fea/data_plane/ifconfig/ifconfig_vlan_get_dummy.cc @@ -75,9 +75,9 @@ IfConfigVlanGetDummy::stop(string& error_msg) } int -IfConfigVlanGetDummy::pull_config(IfTree& iftree) +IfConfigVlanGetDummy::pull_config(IfTree& iftree, bool& modified) { UNUSED(iftree); - + UNUSED(modified); return (XORP_OK); } diff --git a/fea/data_plane/ifconfig/ifconfig_vlan_get_dummy.hh b/fea/data_plane/ifconfig/ifconfig_vlan_get_dummy.hh index c54fd1a..2cb2355 100644 --- a/fea/data_plane/ifconfig/ifconfig_vlan_get_dummy.hh +++ b/fea/data_plane/ifconfig/ifconfig_vlan_get_dummy.hh @@ -59,7 +59,7 @@ public: * @param iftree the IfTree storage to store the pulled information. * @return XORP_OK on success, otherwise XORP_ERROR. */ - virtual int pull_config(IfTree& iftree); + virtual int pull_config(IfTree& iftree, bool& modified); private: }; diff --git a/fea/data_plane/ifconfig/ifconfig_vlan_get_linux.cc b/fea/data_plane/ifconfig/ifconfig_vlan_get_linux.cc index 61f11b4..26366f4 100644 --- a/fea/data_plane/ifconfig/ifconfig_vlan_get_linux.cc +++ b/fea/data_plane/ifconfig/ifconfig_vlan_get_linux.cc @@ -110,16 +110,17 @@ IfConfigVlanGetLinux::stop(string& error_msg) } int -IfConfigVlanGetLinux::pull_config(IfTree& iftree) +IfConfigVlanGetLinux::pull_config(IfTree& iftree, bool& modified) { - return read_config(iftree); + return read_config(iftree, modified); } int -IfConfigVlanGetLinux::read_config(IfTree& iftree) +IfConfigVlanGetLinux::read_config(IfTree& iftree, bool& modified) { IfTree::IfMap::iterator ii; string error_msg; + bool mod_on_entry = modified; if (! _is_running) { error_msg = c_format("Cannot read VLAN interface intormation: " @@ -145,6 +146,22 @@ IfConfigVlanGetLinux::read_config(IfTree& iftree) if (ifp->is_marked(IfTreeItem::DELETED)) continue; + // If the iface list was modified on entry (ie, we added/deleted an ifp somewhere + // when reading iface config, then it's possible a parent has appeared and so we must + // reprobe everything just in case. + if (mod_on_entry) { + ifp->set_probed_vlan(false); + } + + // If we've already probed this device for vlan-ness, then + // no need to probe again I think. + if (ifp->probed_vlan()) { + continue; + } + + /** we'll have probed it when we return. */ + ifp->set_probed_vlan(true); + struct vlan_ioctl_args vlanreq; // Test whether a VLAN interface @@ -152,8 +169,9 @@ IfConfigVlanGetLinux::read_config(IfTree& iftree) strncpy(vlanreq.device1, ifp->ifname().c_str(), sizeof(vlanreq.device1) - 1); vlanreq.cmd = GET_VLAN_REALDEV_NAME_CMD; - if (ioctl(_s4, SIOCGIFVLAN, &vlanreq) < 0) - continue; // XXX: Most likely not a VLAN interface + if (ioctl(_s4, SIOCGIFVLAN, &vlanreq) < 0) { + continue; // not a vlan + } // // XXX: VLAN interface @@ -161,8 +179,11 @@ IfConfigVlanGetLinux::read_config(IfTree& iftree) // Get the parent device string parent_ifname = vlanreq.u.device2; - if (parent_ifname.empty()) + if (parent_ifname.empty()) { + // BUG + XLOG_ERROR("Could not find parent ifname for iface: %s\n", ifp->ifname().c_str()); continue; + } // Get the VLAN ID memset(&vlanreq, 0, sizeof(vlanreq)); @@ -177,26 +198,52 @@ IfConfigVlanGetLinux::read_config(IfTree& iftree) } uint16_t vlan_id = vlanreq.u.VID; + // NOTE: It's possible for VLANs to be configured and not + // have the parent device configured (or even visible at all, when linux namespace + // code is completed.) Since VLANs are 'real' devices in Linux, this might not really + // matter if Xorp doesn't notice... --Ben IfTreeInterface* parent_ifp = iftree.find_interface(parent_ifname); - if ((parent_ifp == NULL) || parent_ifp->is_marked(IfTreeItem::DELETED)) - continue; - - // Find or add the VLAN vif - IfTreeVif* parent_vifp = parent_ifp->find_vif(ifp->ifname()); - if (parent_vifp == NULL) { - parent_ifp->add_vif(ifp->ifname()); + IfTreeVif* parent_vifp = NULL; + if ((parent_ifp == NULL) || parent_ifp->is_marked(IfTreeItem::DELETED)) { + // It could appear later, go set the vifp if we can find it + } + else { + // Find or add the VLAN vif parent_vifp = parent_ifp->find_vif(ifp->ifname()); + if (parent_vifp == NULL) { + modified = true; + parent_ifp->add_vif(ifp->ifname()); + parent_vifp = parent_ifp->find_vif(ifp->ifname()); + } + XLOG_ASSERT(parent_vifp != NULL); } - XLOG_ASSERT(parent_vifp != NULL); // Copy the vif state IfTreeVif* vifp = ifp->find_vif(ifp->ifname()); - if (vifp != NULL) - parent_vifp->copy_recursive_vif(*vifp); - - // Set the VLAN vif info - parent_vifp->set_vlan(true); - parent_vifp->set_vlan_id(vlan_id); + if (vifp != NULL) { + if (parent_vifp) { + modified = true; /* TODO: this could be a lie, but better safe than sorry until + * the copy-recursive method takes 'modified'. + */ + parent_vifp->copy_recursive_vif(*vifp); + } + else { + // Well, lie and set the info in the vifp instead, since it's actually + // a vlan. + parent_vifp = vifp; + } + } + if (parent_vifp) { + // Set the VLAN vif info + if (!parent_vifp->is_vlan()) { + parent_vifp->set_vlan(true); + modified = true; + } + if (parent_vifp->vlan_id() != vlan_id) { + parent_vifp->set_vlan_id(vlan_id); + modified = true; + } + } } return (XORP_OK); diff --git a/fea/data_plane/ifconfig/ifconfig_vlan_get_linux.hh b/fea/data_plane/ifconfig/ifconfig_vlan_get_linux.hh index 1a90124..e2ab45e 100644 --- a/fea/data_plane/ifconfig/ifconfig_vlan_get_linux.hh +++ b/fea/data_plane/ifconfig/ifconfig_vlan_get_linux.hh @@ -59,10 +59,10 @@ public: * @param iftree the IfTree storage to store the pulled information. * @return XORP_OK on success, otherwise XORP_ERROR. */ - virtual int pull_config(IfTree& iftree); + virtual int pull_config(IfTree& iftree, bool& modified); private: - int read_config(IfTree& iftree); + int read_config(IfTree& iftree, bool& modified); int _s4; }; diff --git a/fea/data_plane/io/io_ip_dummy.hh b/fea/data_plane/io/io_ip_dummy.hh index 21318c9..e652f7e 100644 --- a/fea/data_plane/io/io_ip_dummy.hh +++ b/fea/data_plane/io/io_ip_dummy.hh @@ -183,7 +183,9 @@ public: * @return a reference to the file descriptor for receiving protocol * messages. */ - XorpFd& protocol_fd_in() { return (_dummy_protocol_fd_in); } + XorpFd* mcast_protocol_fd_in() { + return (&_dummy_protocol_fd_in); + } private: // Private state diff --git a/fea/data_plane/io/io_ip_socket.cc b/fea/data_plane/io/io_ip_socket.cc index 0a339b0..32947a3 100644 --- a/fea/data_plane/io/io_ip_socket.cc +++ b/fea/data_plane/io/io_ip_socket.cc @@ -239,8 +239,8 @@ static uint8_t ra_opt6[IP6OPT_RTALERT_LEN]; IoIpSocket::IoIpSocket(FeaDataPlaneManager& fea_data_plane_manager, - const IfTree& iftree, int family, uint8_t ip_protocol) - : IoIp(fea_data_plane_manager, iftree, family, ip_protocol), + const IfTree& ift, int family, uint8_t ip_protocol) + : IoIp(fea_data_plane_manager, ift, family, ip_protocol), _is_ip_hdr_included(false), _ip_id(xorp_random()) { @@ -297,12 +297,19 @@ IoIpSocket::IoIpSocket(FeaDataPlaneManager& fea_data_plane_manager, _rcvmh.msg_controllen = CMSG_BUF_SIZE; _sndmh.msg_controllen = 0; #endif // ! HOST_OS_WINDOWS + + XLOG_WARNING("Registering with iftree: %s\n", iftree().getName().c_str()); + // Register interest in interface deletions. + iftree().registerListener(this); } IoIpSocket::~IoIpSocket() { string error_msg; + // Register interest in interface deletions. + iftree().unregisterListener(this); + if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the I/O IP raw socket mechanism: %s", error_msg.c_str()); @@ -516,26 +523,37 @@ IoIpSocket::join_multicast_group(const string& if_name, string& error_msg) { const IfTreeVif* vifp; + XorpFd* _proto_socket_in = NULL; // Find the vif vifp = iftree().find_vif(if_name, vif_name); if (vifp == NULL) { - error_msg = c_format("Joining multicast group %s failed: " + error_msg += c_format("Joining multicast group %s failed: " "interface %s vif %s not found", cstring(group), if_name.c_str(), vif_name.c_str()); - return (XORP_ERROR); + goto out_err; + } + + _proto_socket_in = findOrCreateInputSocket(if_name, vif_name, error_msg); + + if (! _proto_socket_in) { + string em = c_format("ERROR: Could not find or create input socket, if_name: %s vif_name: %s error_msg: %s", + if_name.c_str(), vif_name.c_str(), error_msg.c_str()); + XLOG_WARNING("%s", em.c_str()); + error_msg += em; + goto out_err; } #if 0 // TODO: enable or disable the enabled() check? if (! vifp->enabled()) { - error_msg = c_format("Cannot join group %s on interface %s vif %s: " + error_msg += c_format("Cannot join group %s on interface %s vif %s: " "interface/vif is DOWN", cstring(group), if_name.c_str(), vif_name.c_str()); - return (XORP_ERROR); + goto out_err; } #endif // 0/1 @@ -548,12 +566,12 @@ IoIpSocket::join_multicast_group(const string& if_name, // Find the first address IfTreeVif::IPv4Map::const_iterator ai = vifp->ipv4addrs().begin(); if (ai == vifp->ipv4addrs().end()) { - error_msg = c_format("Cannot join group %s on interface %s vif %s: " + error_msg += c_format("Cannot join group %s on interface %s vif %s: " "interface/vif has no address", cstring(group), if_name.c_str(), vif_name.c_str()); - return (XORP_ERROR); + goto out_err; } const IfTreeAddr4& fa = *(ai->second); @@ -561,14 +579,19 @@ IoIpSocket::join_multicast_group(const string& if_name, group.copy_out(mreq.imr_multiaddr); mreq.imr_interface.s_addr = in_addr.s_addr; - if (setsockopt(_proto_socket_in, IPPROTO_IP, IP_ADD_MEMBERSHIP, + if (setsockopt(*_proto_socket_in, IPPROTO_IP, IP_ADD_MEMBERSHIP, XORP_SOCKOPT_CAST(&mreq), sizeof(mreq)) < 0) { - error_msg = c_format("Cannot join group %s on interface %s vif %s: %s", + error_msg += c_format("Cannot join group %s on interface %s vif %s: %s", cstring(group), if_name.c_str(), vif_name.c_str(), strerror(errno)); - return (XORP_ERROR); + goto out_err; + } + else { + XLOG_INFO("Joined IPv4 group: %s on interface %s vif %s socket: %i", + cstring(group), if_name.c_str(), vif_name.c_str(), + (int)(*_proto_socket_in)); } } break; @@ -577,22 +600,27 @@ IoIpSocket::join_multicast_group(const string& if_name, case AF_INET6: { #ifndef HAVE_IPV6_MULTICAST - error_msg = c_format("join_multicast_group() failed: " + error_msg += c_format("join_multicast_group() failed: " "IPv6 multicast not supported"); - return (XORP_ERROR); + goto out_err; #else struct ipv6_mreq mreq6; group.copy_out(mreq6.ipv6mr_multiaddr); mreq6.ipv6mr_interface = vifp->pif_index(); - if (setsockopt(_proto_socket_in, IPPROTO_IPV6, IPV6_JOIN_GROUP, + if (setsockopt(*_proto_socket_in, IPPROTO_IPV6, IPV6_JOIN_GROUP, XORP_SOCKOPT_CAST(&mreq6), sizeof(mreq6)) < 0) { - error_msg = c_format("Cannot join group %s on interface %s vif %s: %s", + error_msg += c_format("Cannot join group %s on interface %s vif %s: %s", cstring(group), if_name.c_str(), vif_name.c_str(), strerror(errno)); - return (XORP_ERROR); + goto out_err; + } + else { + XLOG_INFO("Joined IPv6 group: %s on interface %s vif %s socket: %i", + cstring(group), if_name.c_str(), vif_name.c_str(), + (int)(*_proto_socket_in)); } #endif // HAVE_IPV6_MULTICAST } @@ -601,13 +629,74 @@ IoIpSocket::join_multicast_group(const string& if_name, default: XLOG_UNREACHABLE(); - error_msg = c_format("Invalid address family %d", family()); - return (XORP_ERROR); + error_msg += c_format("Invalid address family %d", family()); + goto out_err; } return (XORP_OK); + + out_err: + if (error_msg.size()) { + XLOG_ERROR("ERROR in join_multicast_group: %s", error_msg.c_str()); + } + return XORP_ERROR; +} + + +XorpFd* IoIpSocket::mcast_protocol_fd_in() { +#ifdef USE_SOCKET_PER_IFACE + if (! _mcast_proto_socket_in.is_valid()) { + // Try to open the socket. + _mcast_proto_socket_in = socket(family(), SOCK_RAW, ip_protocol()); + if (!_mcast_proto_socket_in.is_valid()) { + char *errstr; + +#ifdef HAVE_STRERROR + errstr = strerror(errno); +#else + errstr = "unknown error"; +#endif + XLOG_WARNING("Cannot open multicast IP protocol %u raw socket: %s", + ip_protocol(), errstr); + } + else { + string err_msg; + initializeInputSocket(&_mcast_proto_socket_in, err_msg); + if (err_msg.size()) { + XLOG_WARNING("%s", err_msg.c_str()); + } + } + } + return &_mcast_proto_socket_in; +#else + string nll; + // Just grabs first and only socket when USE_SOCKET_PER_IFACE is not defined. + return findExistingInputSocket(nll, nll); +#endif } + +XorpFd* IoIpSocket::findExistingInputSocket(const string& if_name, const string& vif_name) { +#ifdef USE_SOCKET_PER_IFACE + string k(if_name); + k += " "; + k += vif_name; + map::iterator i = _proto_sockets_in.find(k); +#else + UNUSED(if_name); + UNUSED(vif_name); + map::iterator i = _proto_sockets_in.begin(); +#endif + if (i == _proto_sockets_in.end()) { + return NULL; + } + else { + return i->second; + } + +} + + int IoIpSocket::leave_multicast_group(const string& if_name, const string& vif_name, @@ -619,18 +708,28 @@ IoIpSocket::leave_multicast_group(const string& if_name, // Find the vif vifp = iftree().find_vif(if_name, vif_name); if (vifp == NULL) { - error_msg = c_format("Leaving multicast group %s failed: " - "interface %s vif %s not found", + error_msg += c_format("Leaving multicast group %s failed: " + "interface %s vif %s not found\n", cstring(group), if_name.c_str(), vif_name.c_str()); return (XORP_ERROR); } + + XorpFd* _proto_socket_in = findExistingInputSocket(if_name, vif_name); + if (!_proto_socket_in) { + error_msg += c_format("Leaving multicast group %s failed: " + "interface %s vif %s does not have a socket assigned.\n", + cstring(group), + if_name.c_str(), + vif_name.c_str()); + return (XORP_ERROR); + } #if 0 // TODO: enable or disable the enabled() check? if (! vifp->enabled()) { - error_msg = c_format("Cannot leave group %s on interface %s vif %s: " - "interface/vif is DOWN", + error_msg += c_format("Cannot leave group %s on interface %s vif %s: " + "interface/vif is DOWN\n", cstring(group), if_name.c_str(), vif_name.c_str()); @@ -647,8 +746,8 @@ IoIpSocket::leave_multicast_group(const string& if_name, // Find the first address IfTreeVif::IPv4Map::const_iterator ai = vifp->ipv4addrs().begin(); if (ai == vifp->ipv4addrs().end()) { - error_msg = c_format("Cannot leave group %s on interface %s vif %s: " - "interface/vif has no address", + error_msg += c_format("Cannot leave group %s on interface %s vif %s: " + "interface/vif has no address\n", cstring(group), if_name.c_str(), vif_name.c_str()); @@ -659,15 +758,20 @@ IoIpSocket::leave_multicast_group(const string& if_name, fa.addr().copy_out(in_addr); group.copy_out(mreq.imr_multiaddr); mreq.imr_interface.s_addr = in_addr.s_addr; - if (setsockopt(_proto_socket_in, IPPROTO_IP, IP_DROP_MEMBERSHIP, + if (setsockopt(*_proto_socket_in, IPPROTO_IP, IP_DROP_MEMBERSHIP, XORP_SOCKOPT_CAST(&mreq), sizeof(mreq)) < 0) { - error_msg = c_format("Cannot leave group %s on interface %s vif %s: %s", + error_msg += c_format("Cannot leave group %s on interface %s vif %s socket: %i: %s\n", cstring(group), if_name.c_str(), - vif_name.c_str(), + vif_name.c_str(), (int)(*_proto_socket_in), strerror(errno)); return (XORP_ERROR); } + else { + XLOG_INFO("Left group: %s on interface %s vif %s socket: %i", + cstring(group), if_name.c_str(), vif_name.c_str(), + (int)(*_proto_socket_in)); + } } break; @@ -675,20 +779,20 @@ IoIpSocket::leave_multicast_group(const string& if_name, case AF_INET6: { #ifndef HAVE_IPV6_MULTICAST - error_msg = c_format("leave_multicast_group() failed: " - "IPv6 multicast not supported"); + error_msg += c_format("leave_multicast_group() failed: " + "IPv6 multicast not supported\n"); return (XORP_ERROR); #else struct ipv6_mreq mreq6; group.copy_out(mreq6.ipv6mr_multiaddr); mreq6.ipv6mr_interface = vifp->pif_index(); - if (setsockopt(_proto_socket_in, IPPROTO_IPV6, IPV6_LEAVE_GROUP, + if (setsockopt(*_proto_socket_in, IPPROTO_IPV6, IPV6_LEAVE_GROUP, XORP_SOCKOPT_CAST(&mreq6), sizeof(mreq6)) < 0) { - error_msg = c_format("Cannot leave group %s on interface %s vif %s: %s", + error_msg += c_format("Cannot leave V6 group %s on interface %s vif %s socket: %i: %s\n", cstring(group), if_name.c_str(), - vif_name.c_str(), + vif_name.c_str(), (int)(*_proto_socket_in), strerror(errno)); return (XORP_ERROR); } @@ -699,25 +803,27 @@ IoIpSocket::leave_multicast_group(const string& if_name, default: XLOG_UNREACHABLE(); - error_msg = c_format("Invalid address family %d", family()); + error_msg += c_format("Invalid address family %d\n", family()); return (XORP_ERROR); } return (XORP_OK); } -int -IoIpSocket::open_proto_sockets(string& error_msg) -{ - string dummy_error_msg; - if (_proto_socket_in.is_valid() && _proto_socket_out.is_valid()) - return (XORP_OK); - - // If necessary, open the protocol sockets - if (! _proto_socket_in.is_valid()) { - _proto_socket_in = socket(family(), SOCK_RAW, ip_protocol()); - if (!_proto_socket_in.is_valid()) { +XorpFd* IoIpSocket::findOrCreateInputSocket(const string& if_name, const string& vif_name, + string& error_msg) { + XorpFd* rv = findExistingInputSocket(if_name, vif_name); + + string key(if_name); + key += " "; + key += vif_name; + + if (!rv) { + // Create a new one + rv = new XorpFd(); + *rv = socket(family(), SOCK_RAW, ip_protocol()); + if (!rv->is_valid()) { char *errstr; #ifdef HAVE_STRERROR @@ -725,28 +831,49 @@ IoIpSocket::open_proto_sockets(string& error_msg) #else errstr = "unknown error"; #endif - error_msg = c_format("Cannot open IP protocol %u raw socket: %s", - ip_protocol(), errstr); - return (XORP_ERROR); + error_msg += c_format("Cannot open IP protocol %u raw socket: %s", + ip_protocol(), errstr); + delete rv; + return NULL; + } + else { + _proto_sockets_in[key] = rv; } - } - if (! _proto_socket_out.is_valid()) { - _proto_socket_out = socket(family(), SOCK_RAW, ip_protocol()); - if (!_proto_socket_out.is_valid()) { - char *errstr; + int rslt = initializeInputSocket(rv, error_msg); + if (rslt != XORP_OK) { + _proto_sockets_in.erase(key); + cleanupXorpFd(rv); + return NULL; + } + // Bind to a particular interface. +#ifdef USE_SOCKET_PER_IFACE +#ifdef SO_BINDTODEVICE + if (setsockopt(*rv, SOL_SOCKET, SO_BINDTODEVICE, + vif_name.c_str(), vif_name.size() + 1)) { + error_msg += c_format("ERROR: IoIpSocket::open_proto_socket, setsockopt (BINDTODEVICE): failed: %s", #ifdef HAVE_STRERROR - errstr = strerror(errno); + strerror(errno) #else - errstr = "unknown error"; -#endif - error_msg = c_format("Cannot open IP protocol %u raw socket: %s", - ip_protocol(), errstr); - return (XORP_ERROR); + "unknown error" +#endif + ); } + else { + XLOG_INFO("Successfully bound socket: %i to interface: %s input sockets size: %i\n", + (int)(*rv), vif_name.c_str(), _proto_sockets_in.size()); + } +#endif +#endif } + return rv; +} + + +int IoIpSocket::initializeInputSocket(XorpFd* rv, string& error_msg) { + #ifdef HOST_OS_WINDOWS switch (family()) { case AF_INET: @@ -759,7 +886,7 @@ IoIpSocket::open_proto_sockets(string& error_msg) int result; DWORD nbytes; - result = WSAIoctl(_proto_socket_in, + result = WSAIoctl(*rv, SIO_GET_EXTENSION_FUNCTION_POINTER, const_cast(&guidWSARecvMsg), sizeof(guidWSARecvMsg), @@ -778,7 +905,7 @@ IoIpSocket::open_proto_sockets(string& error_msg) int result; DWORD nbytes; - result = WSAIoctl(_proto_socket_in, + result = WSAIoctl(*rv, SIO_GET_EXTENSION_FUNCTION_POINTER, const_cast(&guidWSASendMsg), sizeof(guidWSASendMsg), @@ -795,8 +922,8 @@ IoIpSocket::open_proto_sockets(string& error_msg) #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); - error_msg = c_format("Invalid address family %d", family()); - return (XORP_ERROR); + error_msg += c_format("Invalid address family %d", family()); + return XORP_ERROR; } #endif // HOST_OS_WINDOWS @@ -805,43 +932,21 @@ IoIpSocket::open_proto_sockets(string& error_msg) // // Lots of input buffering - if (comm_sock_set_rcvbuf(_proto_socket_in, SO_RCV_BUF_SIZE_MAX, + if (comm_sock_set_rcvbuf(*rv, SO_RCV_BUF_SIZE_MAX, SO_RCV_BUF_SIZE_MIN) < SO_RCV_BUF_SIZE_MIN) { - error_msg = c_format("Cannot set the receiver buffer size: %s", - comm_get_last_error_str()); - close_proto_sockets(dummy_error_msg); - return (XORP_ERROR); - } - // Lots of output buffering - if (comm_sock_set_sndbuf(_proto_socket_out, SO_SND_BUF_SIZE_MAX, - SO_SND_BUF_SIZE_MIN) - < SO_SND_BUF_SIZE_MIN) { - error_msg = c_format("Cannot set the sender buffer size: %s", - comm_get_last_error_str()); - close_proto_sockets(dummy_error_msg); - return (XORP_ERROR); - } - // Include IP header when sending (XXX: doesn't do anything for IPv6) - if (enable_ip_hdr_include(true, error_msg) != XORP_OK) { - close_proto_sockets(dummy_error_msg); - return (XORP_ERROR); + error_msg += c_format("Cannot set the receiver buffer size: %s", + comm_get_last_error_str()); + // doesn't seem fatal....continue on. + //close_proto_sockets(error_msg); + //return (XORP_ERROR); } + // Show interest in receiving information from IP header - if (enable_recv_pktinfo(true, error_msg) != XORP_OK) { - close_proto_sockets(dummy_error_msg); - return (XORP_ERROR); - } - // Restrict multicast TTL - if (set_multicast_ttl(MINTTL, error_msg) != XORP_OK) { - close_proto_sockets(dummy_error_msg); - return (XORP_ERROR); - } - // Disable mcast loopback - if (enable_multicast_loopback(false, error_msg) != XORP_OK) { - close_proto_sockets(dummy_error_msg); - return (XORP_ERROR); + if (enable_recv_pktinfo(rv, true, error_msg) != XORP_OK) { + return XORP_ERROR; } + // Protocol-specific setup switch (family()) { case AF_INET: @@ -871,12 +976,11 @@ IoIpSocket::open_proto_sockets(string& error_msg) } #endif // 0 #endif // HAVE_IPV6_MULTICAST_ROUTING - if (setsockopt(_proto_socket_in, ip_protocol(), ICMP6_FILTER, + if (setsockopt(*rv, ip_protocol(), ICMP6_FILTER, XORP_SOCKOPT_CAST(&filter), sizeof(filter)) < 0) { - close_proto_sockets(dummy_error_msg); - error_msg = c_format("setsockopt(ICMP6_FILTER) failed: %s", - strerror(errno)); - return (XORP_ERROR); + error_msg += c_format("setsockopt(ICMP6_FILTER) failed: %s", + strerror(errno)); + return XORP_ERROR; } } } @@ -884,8 +988,8 @@ IoIpSocket::open_proto_sockets(string& error_msg) #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); - error_msg = c_format("Invalid address family %d", family()); - return (XORP_ERROR); + error_msg += c_format("Invalid address family %d", family()); + return XORP_ERROR; } #ifdef HOST_OS_WINDOWS @@ -899,20 +1003,82 @@ IoIpSocket::open_proto_sockets(string& error_msg) sin.sin_family = AF_INET; sin.sin_addr.s_addr = INADDR_ANY; - if (SOCKET_ERROR == bind(_proto_socket_in, (sockaddr *)&sin, + if (SOCKET_ERROR == bind(*rv, (sockaddr *)&sin, sizeof(sockaddr_in))) { XLOG_WARNING("bind() failed: %s\n", win_strerror(GetLastError())); } #endif // HOST_OS_WINDOWS // Assign a method to read from this socket - if (eventloop().add_ioevent_cb(_proto_socket_in, IOT_READ, + if (eventloop().add_ioevent_cb(*rv, IOT_READ, callback(this, &IoIpSocket::proto_socket_read)) == false) { + error_msg += c_format("Cannot add protocol socket: %i to the set of " + "sockets to read from in the event loop", (int)(*rv)); + return XORP_ERROR; + } + + return XORP_OK; +}//initializeInputSocket + + +int +IoIpSocket::open_proto_sockets(string& error_msg) +{ + string dummy_error_msg; + + // We will open input sockets as interfaces are registered (due to listening for mcast addrs) + if (_proto_socket_out.is_valid()) + return (XORP_OK); + + if (! _proto_socket_out.is_valid()) { + _proto_socket_out = socket(family(), SOCK_RAW, ip_protocol()); + if (!_proto_socket_out.is_valid()) { + char *errstr; + +#ifdef HAVE_STRERROR + errstr = strerror(errno); +#else + errstr = "unknown error"; +#endif + error_msg = c_format("Cannot open IP protocol %u raw socket: %s", + ip_protocol(), errstr); + return (XORP_ERROR); + } + } + + // + // Set various socket options + // + + // Lots of output buffering + if (comm_sock_set_sndbuf(_proto_socket_out, SO_SND_BUF_SIZE_MAX, + SO_SND_BUF_SIZE_MIN) + < SO_SND_BUF_SIZE_MIN) { + error_msg = c_format("Cannot set the sender buffer size: %s", + comm_get_last_error_str()); + close_proto_sockets(dummy_error_msg); + return (XORP_ERROR); + } + + // Very small input buffering, as we never read this, but don't fail if + // we can't...it just wastes a bit of memory. + comm_sock_set_rcvbuf(_proto_socket_out, 2000, 2000); + + // Include IP header when sending (XXX: doesn't do anything for IPv6) + if (enable_ip_hdr_include(true, error_msg) != XORP_OK) { + close_proto_sockets(dummy_error_msg); + return (XORP_ERROR); + } + // Restrict multicast TTL + if (set_multicast_ttl(MINTTL, error_msg) != XORP_OK) { + close_proto_sockets(dummy_error_msg); + return (XORP_ERROR); + } + // Disable mcast loopback + if (enable_multicast_loopback(false, error_msg) != XORP_OK) { close_proto_sockets(dummy_error_msg); - error_msg = c_format("Cannot add a protocol socket to the set of " - "sockets to read from in the event loop"); return (XORP_ERROR); } @@ -932,12 +1098,32 @@ IoIpSocket::close_proto_sockets(string& error_msg) _proto_socket_out.clear(); } +#ifdef USE_SOCKET_PER_IFACE + if (_mcast_proto_socket_in.is_valid()) { + comm_close(_mcast_proto_socket_in); + _mcast_proto_socket_in.clear(); + } +#endif + + map::iterator i; + for (i = _proto_sockets_in.begin(); i != _proto_sockets_in.end(); i++) { + + XorpFd* fd = i->second; + + cleanupXorpFd(fd); + } + _proto_sockets_in.clear(); + + return (XORP_OK); +} + +int IoIpSocket::cleanupXorpFd(XorpFd* fd) { // // Close the incoming protocol socket // - if (_proto_socket_in.is_valid()) { + if (fd->is_valid()) { // Remove it just in case, even though it may not be select()-ed - eventloop().remove_ioevent_cb(_proto_socket_in); + eventloop().remove_ioevent_cb(*fd); #ifdef HOST_OS_WINDOWS switch (family()) { @@ -956,14 +1142,15 @@ IoIpSocket::close_proto_sockets(string& error_msg) return (XORP_ERROR); } #endif // HOST_OS_WINDOWS - - comm_close(_proto_socket_in); - _proto_socket_in.clear(); + + comm_close(*fd); + fd->clear(); } - - return (XORP_OK); + delete fd; + return XORP_OK; } + int IoIpSocket::enable_ip_hdr_include(bool is_enabled, string& error_msg) { @@ -1003,7 +1190,7 @@ IoIpSocket::enable_ip_hdr_include(bool is_enabled, string& error_msg) } int -IoIpSocket::enable_recv_pktinfo(bool is_enabled, string& error_msg) +IoIpSocket::enable_recv_pktinfo(XorpFd* input_fd, bool is_enabled, string& error_msg) { switch (family()) { case AF_INET: @@ -1016,7 +1203,7 @@ IoIpSocket::enable_recv_pktinfo(bool is_enabled, string& error_msg) // #ifdef IP_RECVIF // XXX: BSD - if (setsockopt(_proto_socket_in, IPPROTO_IP, IP_RECVIF, + if (setsockopt(*input_fd, IPPROTO_IP, IP_RECVIF, XORP_SOCKOPT_CAST(&bool_flag), sizeof(bool_flag)) < 0) { XLOG_ERROR("setsockopt(IP_RECVIF, %u) failed: %s", bool_flag, strerror(errno)); @@ -1026,7 +1213,7 @@ IoIpSocket::enable_recv_pktinfo(bool is_enabled, string& error_msg) #ifdef IP_PKTINFO // XXX: Linux - if (setsockopt(_proto_socket_in, IPPROTO_IP, IP_PKTINFO, + if (setsockopt(*input_fd, IPPROTO_IP, IP_PKTINFO, XORP_SOCKOPT_CAST(&bool_flag), sizeof(bool_flag)) < 0) { XLOG_ERROR("setsockopt(IP_PKTINFO, %u) failed: %s", bool_flag, strerror(errno)); @@ -1049,7 +1236,7 @@ IoIpSocket::enable_recv_pktinfo(bool is_enabled, string& error_msg) // #ifdef IPV6_RECVPKTINFO // The new option (applies to receiving only) - if (setsockopt(_proto_socket_in, IPPROTO_IPV6, IPV6_RECVPKTINFO, + if (setsockopt(*input_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, XORP_SOCKOPT_CAST(&bool_flag), sizeof(bool_flag)) < 0) { error_msg = c_format("setsockopt(IPV6_RECVPKTINFO, %u) failed: %s", bool_flag, strerror(errno)); @@ -1057,7 +1244,7 @@ IoIpSocket::enable_recv_pktinfo(bool is_enabled, string& error_msg) } #else // The old option (see RFC-2292) - if (setsockopt(_proto_socket_in, IPPROTO_IPV6, IPV6_PKTINFO, + if (setsockopt(*input_fd, IPPROTO_IPV6, IPV6_PKTINFO, XORP_SOCKOPT_CAST(&bool_flag), sizeof(bool_flag)) < 0) { error_msg = c_format("setsockopt(IPV6_PKTINFO, %u) failed: %s", bool_flag, strerror(errno)); @@ -1069,14 +1256,14 @@ IoIpSocket::enable_recv_pktinfo(bool is_enabled, string& error_msg) // Hop-limit field // #ifdef IPV6_RECVHOPLIMIT - if (setsockopt(_proto_socket_in, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, + if (setsockopt(*input_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, XORP_SOCKOPT_CAST(&bool_flag), sizeof(bool_flag)) < 0) { error_msg = c_format("setsockopt(IPV6_RECVHOPLIMIT, %u) failed: %s", bool_flag, strerror(errno)); return (XORP_ERROR); } #else - if (setsockopt(_proto_socket_in, IPPROTO_IPV6, IPV6_HOPLIMIT, + if (setsockopt(*input_fd, IPPROTO_IPV6, IPV6_HOPLIMIT, XORP_SOCKOPT_CAST(&bool_flag), sizeof(bool_flag)) < 0) { error_msg = c_format("setsockopt(IPV6_HOPLIMIT, %u) failed: %s", bool_flag, strerror(errno)); @@ -1088,7 +1275,7 @@ IoIpSocket::enable_recv_pktinfo(bool is_enabled, string& error_msg) // Traffic class value // #ifdef IPV6_RECVTCLASS - if (setsockopt(_proto_socket_in, IPPROTO_IPV6, IPV6_RECVTCLASS, + if (setsockopt(*input_fd, IPPROTO_IPV6, IPV6_RECVTCLASS, XORP_SOCKOPT_CAST(&bool_flag), sizeof(bool_flag)) < 0) { error_msg = c_format("setsockopt(IPV6_RECVTCLASS, %u) failed: %s", bool_flag, strerror(errno)); @@ -1100,14 +1287,14 @@ IoIpSocket::enable_recv_pktinfo(bool is_enabled, string& error_msg) // Hop-by-hop options // #ifdef IPV6_RECVHOPOPTS - if (setsockopt(_proto_socket_in, IPPROTO_IPV6, IPV6_RECVHOPOPTS, + if (setsockopt(*input_fd, IPPROTO_IPV6, IPV6_RECVHOPOPTS, XORP_SOCKOPT_CAST(&bool_flag), sizeof(bool_flag)) < 0) { error_msg = c_format("setsockopt(IPV6_RECVHOPOPTS, %u) failed: %s", bool_flag, strerror(errno)); return (XORP_ERROR); } #else - if (setsockopt(_proto_socket_in, IPPROTO_IPV6, IPV6_HOPOPTS, + if (setsockopt(*input_fd, IPPROTO_IPV6, IPV6_HOPOPTS, XORP_SOCKOPT_CAST(&bool_flag), sizeof(bool_flag)) < 0) { error_msg = c_format("setsockopt(IPV6_HOPOPTS, %u) failed: %s", bool_flag, strerror(errno)); @@ -1119,14 +1306,14 @@ IoIpSocket::enable_recv_pktinfo(bool is_enabled, string& error_msg) // Routing header options // #ifdef IPV6_RECVRTHDR - if (setsockopt(_proto_socket_in, IPPROTO_IPV6, IPV6_RECVRTHDR, + if (setsockopt(*input_fd, IPPROTO_IPV6, IPV6_RECVRTHDR, XORP_SOCKOPT_CAST(&bool_flag), sizeof(bool_flag)) < 0) { error_msg = c_format("setsockopt(IPV6_RECVRTHDR, %u) failed: %s", bool_flag, strerror(errno)); return (XORP_ERROR); } #else - if (setsockopt(_proto_socket_in, IPPROTO_IPV6, IPV6_RTHDR, + if (setsockopt(*input_fd, IPPROTO_IPV6, IPV6_RTHDR, XORP_SOCKOPT_CAST(&bool_flag), sizeof(bool_flag)) < 0) { error_msg = c_format("setsockopt(IPV6_RTHDR, %u) failed: %s", bool_flag, strerror(errno)); @@ -1138,14 +1325,14 @@ IoIpSocket::enable_recv_pktinfo(bool is_enabled, string& error_msg) // Destination options // #ifdef IPV6_RECVDSTOPTS - if (setsockopt(_proto_socket_in, IPPROTO_IPV6, IPV6_RECVDSTOPTS, + if (setsockopt(*input_fd, IPPROTO_IPV6, IPV6_RECVDSTOPTS, XORP_SOCKOPT_CAST(&bool_flag), sizeof(bool_flag)) < 0) { error_msg = c_format("setsockopt(IPV6_RECVDSTOPTS, %u) failed: %s", bool_flag, strerror(errno)); return (XORP_ERROR); } #else - if (setsockopt(_proto_socket_in, IPPROTO_IPV6, IPV6_DSTOPTS, + if (setsockopt(*input_fd, IPPROTO_IPV6, IPV6_DSTOPTS, XORP_SOCKOPT_CAST(&bool_flag), sizeof(bool_flag)) < 0) { error_msg = c_format("setsockopt(IPV6_DSTOPTS, %u) failed: %s", bool_flag, strerror(errno)); @@ -1165,6 +1352,64 @@ IoIpSocket::enable_recv_pktinfo(bool is_enabled, string& error_msg) return (XORP_OK); } + +void IoIpSocket::notifyDeletingIface(const string& ifname) { +// Only clean up here if we are using multiple input sockets. + XLOG_INFO("IoIpSocket::notifyDeletingIface: %s\n", ifname.c_str()); + +#ifdef USE_SOCKET_PER_IFACE + const IfTreeInterface* ifp = iftree().find_interface(ifname); + if (ifp) { + for (IfTreeInterface::VifMap::const_iterator i = ifp->vifs().begin(); i != ifp->vifs().end(); i++) { + string ifn = i->second->ifname(); + string vn = i->second->vifname(); + XorpFd* fd = findExistingInputSocket(ifn, vn); + if (fd) { + string key(ifn); + key += " "; + key += vn; + int _fd = (int)(*fd); + + _proto_sockets_in.erase(key); + cleanupXorpFd(fd); + + XLOG_INFO("Closed socket: %i on interface: %s:%s because its interface is being deleted, input sockets count: %i\n", + _fd, ifn.c_str(), vn.c_str(), _proto_sockets_in.size()); + + } + } + } +#else + UNUSED(ifname); +#endif +} + +void IoIpSocket::notifyDeletingVif(const string& ifn, const string& vn) { +// Only clean up here if we are using multiple input sockets. + XLOG_INFO("IoIpSocket::notifyDeletingVif: %s:%s\n", ifn.c_str(), vn.c_str()); + +#ifdef USE_SOCKET_PER_IFACE + XorpFd* fd = findExistingInputSocket(ifn, vn); + if (fd) { + string key(ifn); + key += " "; + key += vn; + int _fd = (int)(*fd); + + _proto_sockets_in.erase(key); + cleanupXorpFd(fd); + + XLOG_INFO("Closed socket: %i on interface: %s:%s because it is being deleted, input sockets count: %i\n", + _fd, ifn.c_str(), vn.c_str(), _proto_sockets_in.size()); + + } +#else + UNUSED(ifn); + UNUSED(vn); +#endif +} + + void IoIpSocket::proto_socket_read(XorpFd fd, IoEventType type) { @@ -1211,12 +1456,12 @@ IoIpSocket::proto_socket_read(XorpFd fd, IoEventType type) } // Read from the socket - nbytes = recvmsg(_proto_socket_in, &_rcvmh, 0); + nbytes = recvmsg(fd, &_rcvmh, 0); if (nbytes < 0) { if (errno == EINTR) return; // OK: restart receiving XLOG_ERROR("recvmsg() on socket %s failed: %s", - _proto_socket_in.str().c_str(), strerror(errno)); + fd.str().c_str(), strerror(errno)); return; // Error } @@ -1228,12 +1473,12 @@ IoIpSocket::proto_socket_read(XorpFd fd, IoEventType type) struct sockaddr_storage from; socklen_t from_len = sizeof(from); - nbytes = recvfrom(_proto_socket_in, XORP_BUF_CAST(_rcvbuf), + nbytes = recvfrom(fd, XORP_BUF_CAST(_rcvbuf), IO_BUF_SIZE, 0, reinterpret_cast(&from), &from_len); debug_msg("Read fd %s, %d bytes\n", - _proto_socket_in.str().c_str(), XORP_INT_CAST(nbytes)); + fd.str().c_str(), XORP_INT_CAST(nbytes)); if (nbytes < 0) { // XLOG_ERROR("recvfrom() failed: %s", strerror(errno)); return; // Error @@ -1259,10 +1504,10 @@ IoIpSocket::proto_socket_read(XorpFd fd, IoEventType type) XLOG_ERROR("lpWSARecvMsg is NULL"); return; // Error } - error = lpWSARecvMsg(_proto_socket_in, &mh, &nrecvd, NULL, NULL); + error = lpWSARecvMsg(fd, &mh, &nrecvd, NULL, NULL); nbytes = (ssize_t)nrecvd; debug_msg("Read fd %s, %d bytes\n", - _proto_socket_in.str().c_str(), XORP_INT_CAST(nbytes)); + fd.str().c_str(), XORP_INT_CAST(nbytes)); if (nbytes < 0) { // XLOG_ERROR("lpWSARecvMsg() failed: %s", strerror(errno)); return; // Error @@ -1749,10 +1994,23 @@ IoIpSocket::proto_socket_read(XorpFd fd, IoEventType type) if ((ifp == NULL) || (vifp == NULL)) { // No vif found. Ignore this packet. +#ifdef USE_SOCKET_PER_IFACE + // Don't expect this to happen, so log all errors. XLOG_WARNING("proto_socket_read() failed: " "RX packet from %s to %s pif_index %u: no vif found", cstring(src_address), cstring(dst_address), pif_index); - return; // Error +#else + // On a multi-port system, one can rx pkts for devices not in use by this system, + // so don't print the error messages so often. + static int bad_vifs = 0; + if ((bad_vifs++ % 1000) == 0) { + XLOG_WARNING("proto_socket_read(), total bad_vifs: %i: " + "RX packets from %s to %s pif_index %u: no vif found", + bad_vifs, cstring(src_address), cstring(dst_address), + pif_index); + } +#endif + return;// Error } if (! (ifp->enabled() || vifp->enabled())) { // This vif is down. Silently ignore this packet. @@ -1803,7 +2061,7 @@ IoIpSocket::send_packet(const string& if_name, ifp = iftree().find_interface(if_name); if (ifp == NULL) { - error_msg = c_format("No interface %s", if_name.c_str()); + error_msg = c_format("No interface %s in tree: %s", if_name.c_str(), iftree().getName().c_str()); return (XORP_ERROR); } vifp = ifp->find_vif(vif_name); @@ -1957,6 +2215,7 @@ IoIpSocket::send_packet(const string& if_name, if (enable_ip_hdr_include(do_ip_hdr_include, error_msg) != XORP_OK) { XLOG_ERROR("%s", error_msg.c_str()); + assert(error_msg.size()); return (XORP_ERROR); } } @@ -2086,6 +2345,7 @@ IoIpSocket::send_packet(const string& if_name, ip4.set_ip_id(_ip_id); if (ip4.fragment(ifp->mtu(), fragments, false, error_msg) != XORP_OK) { + assert(error_msg.size()); return (XORP_ERROR); } XLOG_ASSERT(! fragments.empty()); @@ -2434,6 +2694,8 @@ IoIpSocket::proto_socket_transmit(const IfTreeInterface* ifp, // TODO: check the interface status. // E.g., vif_state_check(family()); // + error_msg = c_format("sendmsg failed, error: %s socket: %i", + strerror(errno), (int)(_proto_socket_out)); } else { error_msg = c_format("sendmsg(proto %d size %u from %s to %s " "on interface %s vif %s) failed: %s", @@ -2513,6 +2775,9 @@ IoIpSocket::proto_socket_transmit(const IfTreeInterface* ifp, } } + if (ret_value != XORP_OK) { + assert(error_msg.size()); + } return (ret_value); } diff --git a/fea/data_plane/io/io_ip_socket.hh b/fea/data_plane/io/io_ip_socket.hh index 0ef796d..1211c8e 100644 --- a/fea/data_plane/io/io_ip_socket.hh +++ b/fea/data_plane/io/io_ip_socket.hh @@ -32,6 +32,12 @@ #include "fea/io_ip.hh" +// Shall we use one socket per interface for receiving? Defaulting +// to enabled for Linux (its the only thing that supports SO_BINDTODEVICE it seems.) +#ifdef SO_BINDTODEVICE +#define USE_SOCKET_PER_IFACE +#endif + /** * @short A base class for I/O IP raw socket communication. @@ -39,7 +45,7 @@ * Each protocol 'registers' for I/O and gets assigned one object * of this class. */ -class IoIpSocket : public IoIp { +class IoIpSocket : public IoIp, public IfTreeListener { public: /** * Constructor for a given address family and protocol. @@ -180,12 +186,29 @@ public: string& error_msg); /** - * Get the file descriptor for receiving protocol messages. + * Get the file descriptor for receiving protocol messages on the specified vif. * * @return a reference to the file descriptor for receiving protocol * messages. */ - XorpFd& protocol_fd_in() { return (_proto_socket_in); } + XorpFd* findExistingInputSocket(const string& if_name, const string& vif_name); + + /** Get the input file descriptor to be used for multicast routing. This + * should not be bound to any specific interface. Returns NULL if cannot + * find or create one. + */ + XorpFd* mcast_protocol_fd_in(); + + /** Interface is going away..clean up the sockets for the associated vif(s) */ + void notifyDeletingIface(const string& ifname); + void notifyErasingIface(const string& ifname) { notifyDeletingIface(ifname); } + + /** VIF is going away..clean up the socket */ + void notifyDeletingVif(const string& ifname, const string& vifname); + void notifyErasingVif(const string& ifname, const string& vifname) { + notifyDeletingVif(ifname, vifname); + } + private: /** @@ -199,6 +222,16 @@ private: */ int open_proto_sockets(string& error_msg); + + /** Returns XORP_ERROR on error. */ + int initializeInputSocket(XorpFd* rv, string& error_msg); + + /** Find or create an input socket for the specified VIF. Returns NULL + * if the socket cannot be found or created. + */ + XorpFd* findOrCreateInputSocket(const string& if_name, const string& vif_name, + string& error_msg); + /** * Close the protocol sockets. * @@ -207,6 +240,12 @@ private: */ int close_proto_sockets(string& error_msg); + /** Helper method to close a single socket. Does not remove from + * socket map structure. Deletes memory associated with 'fd'. + */ + int cleanupXorpFd(XorpFd* fd); + + /** * Enable/disable the "Header Included" option (for IPv4) on the outgoing * protocol socket. @@ -239,7 +278,7 @@ private: * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ - int enable_recv_pktinfo(bool is_enabled, string& error_msg); + int enable_recv_pktinfo(XorpFd* input_fd, bool is_enabled, string& error_msg); /** * Read data from a protocol socket, and then call the appropriate protocol @@ -269,7 +308,12 @@ private: string& error_msg); // Private state - XorpFd _proto_socket_in; // The socket to receive protocol message + //XorpFd _proto_socket_in; // The socket to receive protocol message + // The key is "if_name vif_name" + map _proto_sockets_in; +#ifdef USE_SOCKET_PER_IFACE + XorpFd _mcast_proto_socket_in; +#endif XorpFd _proto_socket_out; // The socket to end protocol message bool _is_ip_hdr_included; // True if IP header is included on send uint16_t _ip_id; // IPv4 Header ID diff --git a/fea/ifconfig.cc b/fea/ifconfig.cc index 8a977f1..e641906 100644 --- a/fea/ifconfig.cc +++ b/fea/ifconfig.cc @@ -52,7 +52,7 @@ map_changes(const IfTreeItem::State& fci, default: XLOG_FATAL("Unknown IfTreeItem::State"); break; - } + } return true; } @@ -61,6 +61,11 @@ IfConfig::IfConfig(FeaNode& fea_node) _eventloop(fea_node.eventloop()), _nexthop_port_mapper(fea_node.nexthop_port_mapper()), _itm(NULL), + //_live_config("live-config"), + _user_config("user-config"), + _system_config("system-config"), + _merged_config("pushed-config"), + _original_config("original-config"), _restore_original_config_on_shutdown(false), _ifconfig_update_replicator(merged_config()), _is_running(false) @@ -141,6 +146,48 @@ IfConfig::add_transaction_operation(uint32_t tid, return (XORP_OK); } +int IfConfig::add_interface(const char* ifname) { + IfTreeInterface* ifpl = user_config().find_interface(ifname); + //XLOG_WARNING("Add interface: %s current local_cfg: %p\n", ifname, ifpl); + if (!ifpl) { + // Add to our local config + user_config().add_interface(ifname); + + // Read in the OS's information for this interface. + pull_config(ifname, -1); + + IfTreeInterface* ifp = system_config().find_interface(ifname); + if (ifp) { + //XLOG_WARNING("Added new interface: %s, found it in pulled config.\n", ifname); + user_config().update_interface(*ifp); + } + else { + //XLOG_WARNING("Added new interface: %s, but not found in pulled config.\n", ifname); + } + } + + // Add this to our original config if it's not there already. + if (!_original_config.find_interface(ifname)) { + IfTreeInterface* ifp = _system_config.find_interface(ifname); + if (ifp) { + _original_config.update_interface(*ifp); + } + } + return XORP_OK; +} + +int IfConfig::remove_interface(const char* ifname) { + //XLOG_WARNING("Remove interface: %s\n", ifname); + user_config().remove_interface(ifname); + system_config().remove_interface(ifname); + return XORP_OK; +} + +int IfConfig::update_interface(const IfTreeInterface& iface) { + return _user_config.update_interface(iface); +} + + int IfConfig::commit_transaction(uint32_t tid, string& error_msg) { @@ -151,7 +198,7 @@ IfConfig::commit_transaction(uint32_t tid, string& error_msg) // XXX: Pull in advance the current system config, in case it is needed // by some of the transaction operations. // - IfTree old_system_config = pull_config(); + IfTree old_system_config = pull_config(NULL, -1); if (_itm->commit(tid) != true) { error_msg = c_format("Expired or invalid transaction ID presented"); @@ -195,7 +242,7 @@ IfConfig::commit_transaction(uint32_t tid, string& error_msg) // // Pull the new device configuration // - pull_config(); + pull_config(NULL, -1); debug_msg("SYSTEM CONFIG %s\n", system_config().str().c_str()); debug_msg("USER CONFIG %s\n", user_config().str().c_str()); debug_msg("MERGED CONFIG %s\n", merged_config().str().c_str()); @@ -231,7 +278,7 @@ IfConfig::restore_config(const IfTree& old_user_config, // Restore the config set_user_config(old_user_config); set_merged_config(old_user_config); - pull_config(); // Get the current system config + pull_config(NULL, -1); // Get the current system config iftree.prepare_replacement_state(system_config()); // Push the config @@ -241,7 +288,7 @@ IfConfig::restore_config(const IfTree& old_user_config, } // Align the state - pull_config(); + pull_config(NULL, -1); merged_config().align_with_pulled_changes(system_config(), user_config()); user_config().finalize_state(); merged_config().finalize_state(); @@ -567,7 +614,7 @@ IfConfig::start(string& error_msg) return (XORP_ERROR); } - pull_config(); + pull_config(NULL, -1); _system_config.finalize_state(); _original_config = _system_config; @@ -581,6 +628,19 @@ IfConfig::start(string& error_msg) return (XORP_OK); } +const IfTree& IfConfig::full_pulled_config() { + // XXX: We pull the configuration by using only the first method. + // In the future we need to rething this and be more flexible. + // + if (! _ifconfig_gets.empty()) { + IfConfigGet* ifconfig_get = _ifconfig_gets.front(); + + ifconfig_get->pull_config(NULL, _system_config); + } + return _system_config; +} + + int IfConfig::stop(string& error_msg) { @@ -708,7 +768,7 @@ IfConfig::stop(string& error_msg) } int -IfConfig::push_config(IfTree& iftree) +IfConfig::push_config(const IfTree& iftree) { list::const_iterator ifconfig_set_iter; @@ -726,19 +786,38 @@ IfConfig::push_config(IfTree& iftree) return (XORP_OK); } +// TODO: This can be costly when you have lots of interfaces, so try to +// optimize things so this is called rarely. Currently it is called +// several times on startup of fea. const IfTree& -IfConfig::pull_config() +IfConfig::pull_config(const char* ifname, int if_index) { - // Clear the old state - _system_config.clear(); - // // XXX: We pull the configuration by using only the first method. // In the future we need to rething this and be more flexible. // if (! _ifconfig_gets.empty()) { IfConfigGet* ifconfig_get = _ifconfig_gets.front(); - ifconfig_get->pull_config(_system_config); + + if (ifname && ifconfig_get->can_pull_one()) { + // Just request a pull of this interface + // + //XLOG_WARNING("Pull_config_one for interface: %s\n", ifname); + int rv = ifconfig_get->pull_config_one(_system_config, ifname, if_index); + if (rv != XORP_OK) { + XLOG_WARNING("ERROR: pull_config_one for interface: %s failed: %i\n", ifname, rv); + } + if (!_system_config.find_interface(ifname)) { + XLOG_WARNING("ERROR: Could not find interface: %s after pull_config_one.\n", ifname); + } + } + else { + // Pull all + // Clear the old state + _system_config.clear(); + + ifconfig_get->pull_config(&_user_config, _system_config); + } } return _system_config; diff --git a/fea/ifconfig.hh b/fea/ifconfig.hh index 0d6c499..af90300 100644 --- a/fea/ifconfig.hh +++ b/fea/ifconfig.hh @@ -109,6 +109,15 @@ public: const TransactionManager::Operation& op, string& error_msg); + /** These are used by the Transaction Operation callbacks to actually do work. + * All of these apply to the user_config unless otherwise specified. + */ + int add_interface(const char* ifname); + int add_interface(const string& ifname) { return add_interface(ifname.c_str()); } + int remove_interface(const char* ifname); + int remove_interface(const string& ifname) { return remove_interface(ifname.c_str()); } + int update_interface(const IfTreeInterface& iface); + /** * Get a reference to the @ref NexthopPortMapper instance. * @@ -343,7 +352,7 @@ public: * @param iftree the interface configuration to be pushed down. * @return XORP_OK on success, otherwise XORP_ERROR. */ - int push_config(IfTree& iftree); + int push_config(const IfTree& iftree); /** * Get the error message associated with a push operation. @@ -353,11 +362,24 @@ public: const string& push_error() const; /** - * Pull up current interface configuration from the system. + * Pull up current interface configuration from the system, but only for interfaces + * currently configured by the user. + * If ifname is specified, pull only this interface's information. Use NULL to ignore. + * if if_index is specified, pull only this interface's information. Use -1 to ignore. * * @return the current interface configuration from the system. */ - const IfTree& pull_config(); + const IfTree& pull_config(const char* ifname, int if_index); + + + /** NOTE: This will cause the system to read *all* interface information into + * the _pulled_config list. If you are trying to optimize by only pulling information + * for interfaces configured in the local_config, do not use this method. + * + * @return a reference to the pulled interface configuration from the + * system. + */ + const IfTree& full_pulled_config(); /** * Check IfTreeInterface and report updates to IfConfigUpdateReporter. @@ -432,7 +454,8 @@ private: IfConfigTransactionManager* _itm; // The interface transaction manager IfTree _user_config; // The IfTree with the user config - IfTree _system_config; // The IfTree with the system config + IfTree _system_config; /* The IfTree with the system config. + * This usually only holds info for devices in _user_config */ IfTree _merged_config; // The merged system-user config IfTree _original_config; // The IfTree on startup bool _restore_original_config_on_shutdown; // If true, then diff --git a/fea/ifconfig_get.hh b/fea/ifconfig_get.hh index 6554df1..62ad3d2 100644 --- a/fea/ifconfig_get.hh +++ b/fea/ifconfig_get.hh @@ -82,10 +82,27 @@ public: /** * Pull the network interface information from the underlying system. * + * @param local_config If not NULL, optimized ifconfig-get subclasses + * may pull interface config for only interfaces found in local_config. + * Set to NULl to pull all information from the kernel. * @param iftree the IfTree storage to store the pulled information. * @return XORP_OK on success, otherwise XORP_ERROR. */ - virtual int pull_config(IfTree& iftree) = 0; + virtual int pull_config(const IfTree* local_config, IfTree& iftree) = 0; + + /** Child classes that *can* do this should over-ride. + */ + virtual bool can_pull_one() { return false; } + + /** If_index can be -1 if unknown: We will try to resolve it from ifname. + * Child classes that can do this should implement the method. + */ + virtual int pull_config_one(IfTree& iftree, const char* ifname, int if_index) { + UNUSED(iftree); + UNUSED(ifname); + UNUSED(if_index); + return XORP_ERROR; + } protected: // Misc other state diff --git a/fea/ifconfig_set.hh b/fea/ifconfig_set.hh index 8d4b0af..2aa4bdd 100644 --- a/fea/ifconfig_set.hh +++ b/fea/ifconfig_set.hh @@ -89,7 +89,7 @@ public: * @param iftree the interface tree configuration to push. * @return XORP_OK on success, otherwise XORP_ERROR. */ - virtual int push_config(IfTree& iftree); + virtual int push_config(const IfTree& iftree); protected: /** @@ -309,8 +309,8 @@ protected: bool _is_running; private: - void push_iftree_begin(IfTree& iftree); - void push_iftree_end(IfTree& iftree); + void push_iftree_begin(const IfTree& iftree); + void push_iftree_end(const IfTree& iftree); void push_interface_begin(const IfTreeInterface* pulled_ifp, IfTreeInterface& config_iface); void push_interface_end(const IfTreeInterface* pulled_ifp, diff --git a/fea/ifconfig_transaction.cc b/fea/ifconfig_transaction.cc index d97063c..a868979 100644 --- a/fea/ifconfig_transaction.cc +++ b/fea/ifconfig_transaction.cc @@ -38,3 +38,9 @@ IfConfigTransactionManager::operation_result(bool success, flush(_tid_exec); } } + +bool RemoveInterface::dispatch() { + if (ifconfig().remove_interface(ifname()) != XORP_OK) + return (false); + return (true); +} diff --git a/fea/ifconfig_transaction.hh b/fea/ifconfig_transaction.hh index 72baac9..d88cc15 100644 --- a/fea/ifconfig_transaction.hh +++ b/fea/ifconfig_transaction.hh @@ -130,7 +130,7 @@ public: : IfConfigTransactionOperation(ifconfig, ifname) {} bool dispatch() { - if (iftree().add_interface(ifname()) != XORP_OK) + if (ifconfig().add_interface(ifname()) != XORP_OK) return (false); return (true); } @@ -144,13 +144,9 @@ public: class RemoveInterface : public IfConfigTransactionOperation { public: RemoveInterface(IfConfig& ifconfig, const string& ifname) - : IfConfigTransactionOperation(ifconfig, ifname) {} + : IfConfigTransactionOperation(ifconfig, ifname) { } - bool dispatch() { - if (iftree().remove_interface(ifname()) != XORP_OK) - return (false); - return (true); - } + bool dispatch(); string str() const { return string("RemoveInterface: ") + ifname(); @@ -169,6 +165,10 @@ public: {} bool dispatch() { + // Force a read of ALL interfaces, not just ones we are configured + // to care about. + ifconfig().full_pulled_config(); + if (_enable) { // // Configure all interfaces @@ -179,7 +179,7 @@ public: iter != dev_config.interfaces().end(); ++iter) { const IfTreeInterface& iface = *(iter->second); - if (iftree().update_interface(iface) != XORP_OK) + if (ifconfig().update_interface(iface) != XORP_OK) return (false); } } diff --git a/fea/ifconfig_vlan_get.hh b/fea/ifconfig_vlan_get.hh index 924a224..ed97c2c 100644 --- a/fea/ifconfig_vlan_get.hh +++ b/fea/ifconfig_vlan_get.hh @@ -85,9 +85,10 @@ public: * The VLAN information is added to the existing state in the iftree. * * @param iftree the IfTree storage to store the pulled information. + * @param modified Will be false if it is guaranteed that nothing changed in iftree or any of it's objects. * @return XORP_OK on success, otherwise XORP_ERROR. */ - virtual int pull_config(IfTree& iftree) = 0; + virtual int pull_config(IfTree& iftree, bool& modified) = 0; protected: // Misc other state diff --git a/fea/iftree.cc b/fea/iftree.cc index 8544523..6b998b5 100644 --- a/fea/iftree.cc +++ b/fea/iftree.cc @@ -54,15 +54,18 @@ IfTreeItem::str() const /* ------------------------------------------------------------------------- */ /* IfTree code */ -IfTree::IfTree() +IfTree::IfTree(const char* tree_name) : IfTreeItem() { + name = tree_name; } IfTree::IfTree(const IfTree& other) : IfTreeItem() { *this = other; + name = "copy of "; + name += other.name; } IfTree::~IfTree() @@ -91,8 +94,10 @@ IfTree::operator=(const IfTree& other) void IfTree::clear() { + XLOG_WARNING("Clearing iftree: %s\n", name.c_str()); while (! _interfaces.empty()) { IfTreeInterface* ifp = _interfaces.begin()->second; + sendEvent(IFTREE_ERASE_IFACE, ifp); _interfaces.erase(_interfaces.begin()); delete ifp; } @@ -117,6 +122,8 @@ IfTree::add_recursive_interface(const IfTreeInterface& other_iface, else ifp->mark(CREATED); + //XLOG_WARNING("Adding interface: %s recursively to tree: %s\n", ifname.c_str(), name.c_str()); + // Add recursively all vifs from the other interface IfTreeInterface::VifMap::const_iterator ov; for (ov = other_iface.vifs().begin(); @@ -140,9 +147,20 @@ IfTree::add_interface(const string& ifname) ifp = new IfTreeInterface(*this, ifname); _interfaces.insert(IfMap::value_type(ifname, ifp)); + //XLOG_WARNING("Adding interface: %s to tree: %s\n", ifname.c_str(), name.c_str()); + return (XORP_OK); } +void IfTree::registerListener(IfTreeListener* l) const { + listeners.push_back(l); +} + +void IfTree::unregisterListener(IfTreeListener* l) const { + listeners.remove(l); +} + + IfTreeInterface* IfTree::find_interface(const string& ifname) { @@ -435,15 +453,63 @@ IfTree::find_interface_vif_same_subnet_or_p2p(const IPvX& addr, return (false); } +void IfTree::sendEvent(IfTreeIfaceEventE e, IfTreeInterface* ifp) { + list::iterator i; + for (i = listeners.begin(); i != listeners.end(); i++) { + IfTreeListener* l = *i; + switch (e) { + case IFTREE_DELETE_IFACE: + l->notifyDeletingIface(ifp->ifname()); + break; + case IFTREE_ERASE_IFACE: + l->notifyErasingIface(ifp->ifname()); + break; + default: + // Should never be here + XLOG_ASSERT(0); + }//switch + } +} + +void IfTree::sendEvent(IfTreeVifEventE e, IfTreeVif* vifp) { + list::iterator i; + for (i = listeners.begin(); i != listeners.end(); i++) { + IfTreeListener* l = *i; + switch (e) { + case IFTREE_DELETE_VIF: + l->notifyDeletingVif(vifp->ifname(), vifp->vifname()); + break; + case IFTREE_ERASE_VIF: + l->notifyErasingVif(vifp->ifname(), vifp->vifname()); + break; + default: + // Should never be here + XLOG_ASSERT(0); + }//switch + } +} + +void IfTree::markIfaceDeleted(IfTreeInterface* ifp) { + sendEvent(IFTREE_DELETE_IFACE, ifp); + ifp->mark(DELETED); +} + +void IfTree::markVifDeleted(IfTreeVif* vifp) { + sendEvent(IFTREE_DELETE_VIF, vifp); + vifp->mark(DELETED); +} + int IfTree::remove_interface(const string& ifname) { IfTreeInterface* ifp = find_interface(ifname); + XLOG_WARNING("Marking interface: %s in tree: %s DELETED\n", ifname.c_str(), name.c_str()); + if (ifp == NULL) return (XORP_ERROR); - ifp->mark(DELETED); + markIfaceDeleted(ifp); return (XORP_OK); } @@ -486,7 +552,7 @@ IfTree::update_interface(const IfTreeInterface& other_iface) other_vifp = other_iface.find_vif(this_vifp->vifname()); if ((other_vifp == NULL) || (other_vifp->is_marked(DELETED))) { - this_vifp->mark(DELETED); + markVifDeleted(this_vifp); continue; } @@ -605,7 +671,9 @@ IfTree::finalize_state() // If interface is marked as deleted, delete it. if (ifp->is_marked(DELETED)) { + sendEvent(IFTREE_ERASE_IFACE, ifp); _interfaces.erase(ii++); + XLOG_WARNING("Deleting interface: %s from tree: %s\n", ifp->ifname().c_str(), name.c_str()); delete ifp; continue; } @@ -619,7 +687,8 @@ IfTree::finalize_state() string IfTree::str() const { - string r; + string r(name); + r += "\n"; IfMap::const_iterator ii; // @@ -734,7 +803,7 @@ IfTree::align_with_pulled_changes(const IfTree& other, vi != this_ifp->vifs().end(); ++vi) { IfTreeVif* this_vifp = vi->second; - this_vifp->mark(DELETED); + markVifDeleted(this_vifp); } } continue; @@ -1410,7 +1479,7 @@ IfTree::prepare_replacement_state(const IfTree& other) ifp = find_interface(ifname); XLOG_ASSERT(ifp != NULL); ifp->copy_state(other_iface, false); - ifp->mark(DELETED); + markIfaceDeleted(ifp); } // @@ -1431,7 +1500,7 @@ IfTree::prepare_replacement_state(const IfTree& other) vifp = ifp->find_vif(vifname); XLOG_ASSERT(vifp != NULL); vifp->copy_state(other_vif); - vifp->mark(DELETED); + markVifDeleted(vifp); } // @@ -1510,6 +1579,7 @@ IfTree::prune_bogus_deleted_state(const IfTree& old_iftree) old_ifp = old_iftree.find_interface(ifname); if (old_ifp == NULL) { // Remove this item from the local tree + sendEvent(IFTREE_ERASE_IFACE, ifp); _interfaces.erase(ii++); delete ifp; continue; @@ -1531,6 +1601,7 @@ IfTree::prune_bogus_deleted_state(const IfTree& old_iftree) old_vifp = old_ifp->find_vif(vifname); if (old_vifp == NULL) { // Remove this item from the local tree + sendEvent(IFTREE_ERASE_VIF, vifp); ifp->vifs().erase(vi++); delete vifp; continue; @@ -1683,6 +1754,7 @@ IfTreeInterface::IfTreeInterface(IfTree& iftree, const string& ifname) _iftree(iftree), _ifname(ifname), _pif_index(0), + _probed_vlan(false), _enabled(false), _discard(false), _unreachable(false), @@ -1697,6 +1769,7 @@ IfTreeInterface::~IfTreeInterface() { while (! _vifs.empty()) { IfTreeVif* vifp = _vifs.begin()->second; + iftree().sendEvent(IFTREE_ERASE_VIF, vifp); _vifs.erase(_vifs.begin()); delete vifp; } @@ -1762,7 +1835,7 @@ IfTreeInterface::remove_vif(const string& vifname) if (vifp == NULL) return (XORP_ERROR); - vifp->mark(DELETED); + iftree().markVifDeleted(vifp); return (XORP_OK); } @@ -1871,6 +1944,7 @@ IfTreeInterface::finalize_state() // If interface is marked as deleted, delete it. if (vifp->is_marked(DELETED)) { + iftree().sendEvent(IFTREE_ERASE_VIF, vifp); _vifs.erase(vi++); delete vifp; continue; diff --git a/fea/iftree.hh b/fea/iftree.hh index 1013ccb..6499cdb 100644 --- a/fea/iftree.hh +++ b/fea/iftree.hh @@ -55,7 +55,7 @@ public: State state() const { return _st; } - int mark(State st) { + virtual int mark(State st) { if (bits(st) > 1) { return (XORP_ERROR); } @@ -105,6 +105,35 @@ class IfTreeVif; class IfTreeAddr4; class IfTreeAddr6; + +enum IfTreeIfaceEventE { + IFTREE_DELETE_IFACE, // marked deleted + IFTREE_ERASE_IFACE //erased entirely +}; + +enum IfTreeVifEventE { + IFTREE_DELETE_VIF, // marked deleted + IFTREE_ERASE_VIF //erased entirely +}; + +/** IfTree will make these callbacks to listeners when certain actions + * occur. + */ +class IfTreeListener { +public: + virtual ~IfTreeListener() { } + + virtual void notifyDeletingIface(const string& ifname) = 0; // marking iface deleted + virtual void notifyErasingIface(const string& ifname) = 0; // erasing iface memory + + // These may not be called if the parrent iface is being deleted, so + // alwaysclean up all VIFs in the iface handler. + virtual void notifyDeletingVif(const string& ifname, const string& vifname) = 0; // marking vif deleted + virtual void notifyErasingVif(const string& ifname, const string& vifname) = 0; // erasing vif memory + + // Add more as are needed. +}; + /** * Container class for FEA Interface objects in a system. */ @@ -122,7 +151,7 @@ public: /** * Default constructor. */ - IfTree(); + IfTree(const char* tree_name); /** * Constructor from another IfTree. @@ -148,6 +177,17 @@ public: */ void clear(); + /** Not really const, but better than having to pass non-const + * references to other classes. + */ + void registerListener(IfTreeListener* l) const; + + /** Not really const, but better than having to pass non-const + * references to other classes. + */ + void unregisterListener(IfTreeListener* l) const; + + /** * Add recursively a new interface. * @@ -508,6 +548,11 @@ public: */ string str() const; + const string& getName() const { return name; } + + void markVifDeleted(IfTreeVif* ifp); + void markIfaceDeleted(IfTreeInterface* ifp); + protected: friend class IfTreeInterface; friend class IfTreeVif; @@ -517,10 +562,18 @@ protected: void insert_vifindex(IfTreeVif* vifp); void erase_vifindex(IfTreeVif* vifp); + void sendEvent(IfTreeVifEventE e, IfTreeVif* vifp); + void sendEvent(IfTreeIfaceEventE e, IfTreeInterface* ifp); + private: + string name; // identifier for this tree IfMap _interfaces; IfIndexMap _ifindex_map; // Map of pif_index to interface VifIndexMap _vifindex_map; // Map of pif_index to vif + + // Make this mutable so that we can past const references to other classes, + // but still let them be listeners. + mutable list listeners; }; @@ -545,6 +598,17 @@ public: iftree().insert_ifindex(this); } + virtual int mark(State st) { + int rv = IfTreeItem::mark(st); + if (st == DELETED) { + set_probed_vlan(false); // need to reprobe if this ever goes un-deleted + } + return rv; + } + + bool probed_vlan() const { return _probed_vlan; } + void set_probed_vlan(bool b) { _probed_vlan = b; } + bool enabled() const { return _enabled; } void set_enabled(bool en) { _enabled = en; mark(CHANGED); } @@ -755,6 +819,7 @@ private: IfTree& _iftree; const string _ifname; uint32_t _pif_index; + bool _probed_vlan; bool _enabled; bool _discard; bool _unreachable; diff --git a/fea/io_ip.hh b/fea/io_ip.hh index a83fb7b..5a9b847 100644 --- a/fea/io_ip.hh +++ b/fea/io_ip.hh @@ -255,10 +255,10 @@ public: /** * Get the file descriptor for receiving protocol messages. * - * @return a reference to the file descriptor for receiving protocol - * messages. + * @return a pointer to the file descriptor for receiving protocol + * messages, or NULL if it cannot be found. */ - virtual XorpFd& protocol_fd_in() = 0; + virtual XorpFd* mcast_protocol_fd_in() = 0; protected: /** diff --git a/fea/io_ip_manager.cc b/fea/io_ip_manager.cc index 8f6e7ad..7226f1c 100644 --- a/fea/io_ip_manager.cc +++ b/fea/io_ip_manager.cc @@ -324,6 +324,10 @@ IoIpComm::send_packet(const string& if_name, ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; + error_msg += c_format("Error while sending to vif: %s:%s src: %s dest: %s: ", + if_name.c_str(), vif_name.c_str(), + src_address.str().c_str(), + dst_address.str().c_str()); error_msg += error_msg2; } } @@ -457,6 +461,45 @@ IoIpComm::join_multicast_group(const string& if_name, return (ret_value); } + +int +IoIpComm::leave_all_multicast_groups(const string& if_name, + const string& vif_name, + string& error_msg) +{ + //XLOG_ERROR("IoIpComm::leave_all_multicast_groups, if: %s vif: %s\n", + //if_name.c_str(), vif_name.c_str()); + start: + { + JoinedGroupsTable::iterator joined_iter; + for (joined_iter = _joined_groups_table.begin(); + joined_iter != _joined_groups_table.end(); joined_iter++) { + //XLOG_ERROR("Found group, if: %s vif: %s\n", + // joined_iter->second.if_name().c_str(), + // joined_iter->second.vif_name().c_str()); + if ((joined_iter->second.if_name() == if_name) && + ((vif_name.size() == 0) || (joined_iter->second.vif_name() == vif_name))) { + + string tmp_vifn(joined_iter->second.vif_name()); + + //XLOG_ERROR("Matched, looking for receivers.\n"); + set::iterator ri = joined_iter->second.get_receivers().begin(); + while (ri != joined_iter->second.get_receivers().end()) { + //XLOG_ERROR("Found receiver: %s\n", ri->c_str()); + leave_multicast_group(if_name, tmp_vifn, joined_iter->second.group_address(), + *ri, error_msg); + // Maybe there are more...back out to top and search again. This should keep our iterators + // from being corrupted by the leave_multicast_group call. + goto start; + }//for all receivers + }//if this joined group matches our interface and vif (if specified) + }//for all joined groups + }//scoping brace + // We delete all we can find...whatever we found must have been OK. + return XORP_OK; +} + + int IoIpComm::leave_multicast_group(const string& if_name, const string& vif_name, @@ -643,7 +686,7 @@ IoIpComm::stop_io_ip_plugins() } XorpFd -IoIpComm::first_valid_protocol_fd_in() +IoIpComm::first_valid_mcast_protocol_fd_in() { XorpFd xorp_fd; @@ -651,7 +694,7 @@ IoIpComm::first_valid_protocol_fd_in() IoIpPlugins::iterator iter; for (iter = _io_ip_plugins.begin(); iter != _io_ip_plugins.end(); ++iter) { IoIp* io_ip = iter->second; - xorp_fd = io_ip->protocol_fd_in(); + xorp_fd = *(io_ip->mcast_protocol_fd_in()); if (xorp_fd.is_valid()) return (xorp_fd); } @@ -1005,6 +1048,19 @@ IoIpManager::join_multicast_group(const string& receiver_name, return (XORP_ERROR); } + +int IoIpManager::leave_all_multicast_groups(const string& if_name, + const string& vif_name, + string& error_msg) { + CommTable::iterator cti; + for (cti = _comm_table4.begin(); cti != _comm_table4.end(); cti++) { + IoIpComm* c = cti->second; + c->leave_all_multicast_groups(if_name, vif_name, error_msg); + } + return XORP_OK; +} + + int IoIpManager::leave_multicast_group(const string& receiver_name, const string& if_name, @@ -1053,7 +1109,7 @@ IoIpManager::register_system_multicast_upcall_receiver( int family, uint8_t ip_protocol, IoIpManager::UpcallReceiverCb receiver_cb, - XorpFd& receiver_fd, + XorpFd& mcast_receiver_fd, string& error_msg) { SystemMulticastUpcallFilter* filter; @@ -1093,7 +1149,7 @@ IoIpManager::register_system_multicast_upcall_receiver( if (filter->ip_protocol() == ip_protocol) { // Already have this filter filter->set_receiver_cb(receiver_cb); - receiver_fd = io_ip_comm->first_valid_protocol_fd_in(); + mcast_receiver_fd = io_ip_comm->first_valid_mcast_protocol_fd_in(); return (XORP_OK); } } @@ -1110,7 +1166,7 @@ IoIpManager::register_system_multicast_upcall_receiver( // Add the filter to those associated with empty receiver_name filters.insert(FilterBag::value_type(receiver_name, filter)); - receiver_fd = io_ip_comm->first_valid_protocol_fd_in(); + mcast_receiver_fd = io_ip_comm->first_valid_mcast_protocol_fd_in(); return (XORP_OK); } diff --git a/fea/io_ip_manager.hh b/fea/io_ip_manager.hh index 4ce0140..0f1d86a 100644 --- a/fea/io_ip_manager.hh +++ b/fea/io_ip_manager.hh @@ -196,6 +196,8 @@ public: */ bool empty() const { return _receivers.empty(); } + set& get_receivers() { return _receivers; } + private: string _if_name; string _vif_name; @@ -398,6 +400,19 @@ public: string& error_msg); /** + * Leave all IP multicast groups on this interface. + * + * @param if_name the interface through which packets should not be + * accepted. + * @param vif_name the vif through which packets should not be accepted. + * @param error_msg the error message (if error). + * @return XORP_OK on success, otherwise XORP_ERROR. + */ + int leave_all_multicast_groups(const string& if_name, + const string& vif_name, + string& error_msg); + + /** * Get the IP protocol. * * @return the IP protocol. @@ -410,7 +425,7 @@ public: * @return the first valid file descriptor for receiving protocol * messages. */ - XorpFd first_valid_protocol_fd_in(); + XorpFd first_valid_mcast_protocol_fd_in(); private: IoIpComm(const IoIpComm&); // Not implemented. @@ -609,6 +624,11 @@ public: const IPvX& group_address, string& error_msg); + /** Leave all multicast groups on this vif */ + int leave_all_multicast_groups(const string& if_name, + const string& vif_name, + string& error_msg); + /** * Register to receive multicast forwarding related upcalls from the * system. @@ -628,7 +648,7 @@ public: int register_system_multicast_upcall_receiver(int family, uint8_t ip_protocol, IoIpManager::UpcallReceiverCb receiver_cb, - XorpFd& receiver_fd, + XorpFd& mcast_receiver_fd, string& error_msg); /** diff --git a/fea/io_link_manager.hh b/fea/io_link_manager.hh index 18710ff..bed31ca 100644 --- a/fea/io_link_manager.hh +++ b/fea/io_link_manager.hh @@ -202,6 +202,8 @@ public: */ bool empty() const { return _receivers.empty(); } + set& get_receivers() { return _receivers; } + private: Mac _group_address; set _receivers; diff --git a/fea/mfea_node.cc b/fea/mfea_node.cc index fcf0df5..f42a34d 100644 --- a/fea/mfea_node.cc +++ b/fea/mfea_node.cc @@ -76,6 +76,7 @@ MfeaNode::MfeaNode(FeaNode& fea_node, int family, xorp_module_id module_id, _fea_node(fea_node), _mfea_mrouter(*this), _mfea_dft(*this), + _mfea_iftree("mfea-tree"), _mfea_iftree_update_replicator(_mfea_iftree), _is_log_trace(false) { diff --git a/fea/xrl_fea_target.cc b/fea/xrl_fea_target.cc index fb2b47f..3fd8824 100644 --- a/fea/xrl_fea_target.cc +++ b/fea/xrl_fea_target.c